Skip to content

Commit

Permalink
Merge branch 'tuning'
Browse files Browse the repository at this point in the history
  • Loading branch information
koron committed Aug 25, 2020
2 parents 4342ef6 + 92f7448 commit bd34758
Show file tree
Hide file tree
Showing 25 changed files with 3,050 additions and 1,431 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ clean:
zexdoc:
$(MAKE) -C cmd/zexdoc run

switch.go: op_*.go gen_switch.go ./cmd/gen_switch/*.go
rm -f switch.go switch.go.new
cp switch.go.dummy switch.go
go run ./cmd/gen_switch | goimports > switch.go.new
mv switch.go.new switch.go

# based on: github.com/koron-go/_skeleton/Makefile
5 changes: 5 additions & 0 deletions cmd/extractf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# extractf

Tool to extract `F` filed of `OPcode` as top-level function.

`OPCode``F` を独立した関数に抜き出すツール
170 changes: 170 additions & 0 deletions cmd/extractf/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package main

import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
)

func invalidRune(r rune) bool {
// valid: 0-9 A-Z a-z
// invalid: others
return r < '0' ||
(r > '9' && r < 'A') ||
(r > 'Z' && r < 'a') ||
r > 'z'
}

func mapping(r rune) rune {
if r == ')' {
return 'P'
}
if invalidRune(r) {
return -1
}
return r
}

func mangleN(s string) string {
return "op" + strings.Map(mapping, s)
}

type item struct {
n string
f []string
}

func extractF(w *bufio.Writer, r *bufio.Reader) error {
var items []*item

L:
for {
var (
n string
f []string
indent string
fEnd string
)
for {
s, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
break L
}
return err
}
_, err = w.WriteString(s)
if err != nil {
return err
}
m := rxN.FindStringSubmatch(s)
if len(m) > 0 {
n = mangleN(m[1])
break
}
}
for {
s, err := r.ReadString('\n')
if err != nil {
return err
}
m := rxF.FindStringSubmatch(s)
if len(m) > 0 {
indent = m[1]
fEnd = indent + "},\n"
break
}
_, err = w.WriteString(s)
if err != nil {
return err
}
}
_, err := fmt.Fprintf(w, "%sF: %s,\n", indent, n)
if err != nil {
return err
}

for {
s, err := r.ReadString('\n')
if err != nil {
return err
}
if s == fEnd {
break
}
f = append(f, s[len(indent):])
}
items = append(items, &item{n: n, f: f})
}

for _, i := range items {
_, err := fmt.Fprintf(w, "\nfunc %s(cpu *CPU, codes []uint8) {\n", i.n)
if err != nil {
return err
}
for _, f := range i.f {
_, err := fmt.Fprint(w, f)
if err != nil {
return err
}
}
_, err = w.WriteString("}\n")
if err != nil {
return err
}
}

return w.Flush()
}

func (i item) name() string {
return strings.Map(mapping, i.n)
}

var rxN = regexp.MustCompile(`^\s*N: "([^"]*)",`)

var rxF = regexp.MustCompile(`^(\t+)F: func\(.*\) \{\n$`)

func rewrite(name string) error {
in, err := ioutil.ReadFile(name)
if err != nil {
return err
}
r := bufio.NewReader(bytes.NewBuffer(in))

out := &bytes.Buffer{}
w := bufio.NewWriter(out)
err = extractF(w, r)
if err != nil {
return err
}

return ioutil.WriteFile(name, out.Bytes(), 0666)
}

func main() {
flag.Parse()

if flag.NArg() == 0 {
r := bufio.NewReader(os.Stdin)
w := bufio.NewWriter(os.Stdout)
err := extractF(w, r)
if err != nil {
log.Fatal(err)
}
}

for _, name := range flag.Args() {
err := rewrite(name)
if err != nil {
log.Fatal(err)
}
}
}
15 changes: 15 additions & 0 deletions cmd/gen_switch/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"log"
"os"

"github.com/koron-go/z80"
)

func main() {
err := z80.GenerateSwitchDecoder(os.Stdout)
if err != nil {
log.Fatal(err)
}
}
34 changes: 0 additions & 34 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,37 +152,3 @@ func DumpDecodeLayer(w io.Writer) error {
e.SetIndent("", " ")
return e.Encode(defaultDecodeLayer().mapTo())
}

func decode(l *decodeLayer, buf []byte, f fetcher) (*OPCode, []uint8, error) {
var op *OPCode
for l != nil {
b, err := f.fetch()
if err != nil {
return nil, buf, fmt.Errorf("fetch failed: %w", err)
}
buf = append(buf, b)
n := l.get(b)
if n == nil {
break
}
if n.opcode != nil {
op = n.opcode
for len(buf) < len(op.C) {
b, err := f.fetch()
if err != nil {
return nil, buf, fmt.Errorf("fetch remains failed: %w", err)
}
buf = append(buf, b)
}
break
}
l = n.next
}
if op == nil {
return nil, buf, ErrInvalidCodes
}
if op.F == nil {
return nil, buf, fmt.Errorf("OPCode:%s %w", op.N, ErrNotImplemented)
}
return op, buf, nil
}
12 changes: 0 additions & 12 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,3 @@ func TestDecodeLayer_CheckAllOPCodes(t *testing.T) {
t.Errorf("unseen OPCode: %s", c)
}
}

func TestDecodeLayer_DD7E00(t *testing.T) {
f := memSrc{0xdd, 0x7e, 0x00}
l := defaultDecodeLayer()
c, _, err := decode(l, nil, &f)
if err != nil {
t.Fatal(err)
}
if c.N != "LD r, (IX+d)" {
t.Fatalf("unexpected opcode: %s", c.N)
}
}
21 changes: 21 additions & 0 deletions fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package z80

type fetcher interface {
fetch() uint8
fetchLabel() string
}

type memSrc []uint8

func (m *memSrc) fetch() uint8 {
if len(*m) == 0 {
return 0
}
var b uint8
b, *m = (*m)[0], (*m)[1:]
return b
}

func (m *memSrc) fetchLabel() string {
return "IM0"
}
112 changes: 112 additions & 0 deletions gen_switch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package z80

import (
"bufio"
"fmt"
"io"

"github.com/koron-go/z80/internal/opname"
)

// GenerateSwitchDecoder generate decoder `switch` statements.
func GenerateSwitchDecoder(w io.Writer) error {
bw := bufio.NewWriter(w)
_, err := bw.WriteString(`package z80
func decodeExec(cpu *CPU, f fetcher) error {
var b uint8
buf := cpu.decodeBuf[:4]`)
if err != nil {
return err
}
l := defaultDecodeLayer()
nr := 0
err = writeLayerCode(bw, l, nr)
if err != nil {
return err
}
_, err = fmt.Fprintf(bw, "\n}\n")
if err != nil {
return err
}
return bw.Flush()
}

type nextItem struct {
d uint8
l *decodeLayer
}

func writeLayerCode(w *bufio.Writer, l *decodeLayer, nr int) error {
if l.anyNode != nil {
fmt.Fprintf(w, "\nbuf[%d] = f.fetch()", nr)
if l.anyNode.next == nil {
return fmt.Errorf("invalid any node: %+v", l.anyNode)
}
return writeLayerCode(w, l.anyNode.next, nr+1)
}

fmt.Fprintf(w, `
b = f.fetch()
buf[%d] = b`, nr)
nr++
w.WriteString("\nswitch b {")

var singleOps []*OPCode
var codesMap = map[string][]uint8{}
var nexts []*nextItem
for i, n := range l.nodes {
if n == nil {
continue
}
if n.next != nil {
nexts = append(nexts, &nextItem{d: uint8(i), l: n.next})
continue
}
if n.opcode == nil {
return fmt.Errorf("node without opcode: %+v", n)
}
// store opcode to group.
op := n.opcode
codes, ok := codesMap[op.N]
if !ok {
singleOps = append(singleOps, op)
}
codes = append(codes, uint8(i))
codesMap[op.N] = codes
}

for _, op := range singleOps {
codes, ok := codesMap[op.N]
if !ok || len(codes) == 0 {
panic("something wrong: failed to build codesMap")
}
w.WriteString("\ncase")
for i, code := range codes {
fmt.Fprintf(w, " 0x%02x", code)
if i+1 < len(codes) {
w.WriteRune(',')
} else {
w.WriteRune(':')
}
}
if d := len(op.C) - nr; d > 0 {
for i := 0; i < d; i++ {
fmt.Fprintf(w, "\nbuf[%d] = f.fetch()", nr+i)
}
}
name := opname.Mangle(op.N)
fmt.Fprintf(w, "\n%s(cpu, buf[:%d])\nreturn nil", name, len(op.C))
}

for _, n := range nexts {
fmt.Fprintf(w, "\ncase 0x%02x:", n.d)
writeLayerCode(w, n.l, nr)
}

w.WriteString(`
default:
return ErrInvalidCodes
}`)
return nil
}
Loading

0 comments on commit bd34758

Please sign in to comment.