diff --git a/internal/cmd/avogen/main.go b/internal/cmd/avogen/main.go
new file mode 100644
index 0000000..84a7b64
--- /dev/null
+++ b/internal/cmd/avogen/main.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "flag"
+ "go/build"
+ "log"
+ "os"
+ "path/filepath"
+
+ "github.com/mmcloughlin/avo/internal/gen"
+ "github.com/mmcloughlin/avo/internal/load"
+)
+
+var generators = map[string]gen.Interface{
+ "asmtest": gen.NewAsmTest(),
+ "godata": gen.NewGoData(),
+}
+
+var datadir = flag.String(
+ "data",
+ filepath.Join(build.Default.GOPATH, "src/github.com/mmcloughlin/avo/internal/data"),
+ "path to data directory",
+)
+
+var output = flag.String("output", "", "path to output file (default stdout)")
+
+func main() {
+ flag.Parse()
+
+ // Build generator.
+ t := flag.Arg(0)
+ g := generators[t]
+ if g == nil {
+ log.Fatalf("unknown generator type '%s'", t)
+ }
+
+ // Determine output writer.
+ w := os.Stdout
+ if *output != "" {
+ f, err := os.Open(*output)
+ if err != nil {
+ log.Fatal(err)
+ }
+ w = f
+ }
+
+ // Load instructions.
+ l := load.NewLoaderFromDataDir(*datadir)
+ is, err := l.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Generate output.
+ b, err := g.Generate(is)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Write.
+ if _, err := w.Write(b); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/internal/gen/loadertest.go b/internal/gen/asmtest.go
similarity index 82%
rename from internal/gen/loadertest.go
rename to internal/gen/asmtest.go
index b53d949..b61258f 100644
--- a/internal/gen/loadertest.go
+++ b/internal/gen/asmtest.go
@@ -2,7 +2,6 @@ package gen
import (
"fmt"
- "io"
"math"
"strconv"
"strings"
@@ -10,61 +9,65 @@ import (
"github.com/mmcloughlin/avo/internal/inst"
)
-type LoaderTest struct {
+type asmtest struct {
sym string // reference to the test function symbol
rel8 string // label to be used for near jumps
rel32 string // label for far jumps
}
-func (l *LoaderTest) Generate(w io.Writer, is []*inst.Instruction) error {
- p := &printer{w: w}
+func NewAsmTest() Interface {
+ return &asmtest{}
+}
- l.sym = "\u00b7loadertest(SB)"
- p.printf("TEXT %s, 0, $0\n", l.sym)
+func (a *asmtest) Generate(is []*inst.Instruction) ([]byte, error) {
+ p := &printer{}
+
+ a.sym = "\u00b7loadertest(SB)"
+ p.Printf("TEXT %s, 0, $0\n", a.sym)
// Define a label for far jumps.
- p.printf("rel32:\n")
- l.rel32 = "rel32"
+ p.Printf("rel32:\n")
+ a.rel32 = "rel32"
counts := map[string]int{}
for _, i := range is {
- p.printf("\t// %s %s\n", i.Opcode, i.Summary)
- if skip, msg := l.skip(i.Opcode); skip {
- p.printf("\t// SKIP: %s\n", msg)
+ p.Printf("\t// %s %s\n", i.Opcode, i.Summary)
+ if skip, msg := a.skip(i.Opcode); skip {
+ p.Printf("\t// SKIP: %s\n", msg)
counts["skip"]++
continue
}
if i.Opcode[0] == 'J' {
label := fmt.Sprintf("rel8_%s", strings.ToLower(i.Opcode))
- p.printf("%s:\n", label)
- l.rel8 = label
+ p.Printf("%s:\n", label)
+ a.rel8 = label
}
for _, f := range i.Forms {
- as := l.args(i.Opcode, f.Operands)
+ as := a.args(i.Opcode, f.Operands)
if as == nil {
- p.printf("\t// TODO: %s %#v\n", i.Opcode, f.Operands)
+ p.Printf("\t// TODO: %s %#v\n", i.Opcode, f.Operands)
counts["todo"]++
continue
}
- p.printf("\t%s\t%s\n", i.Opcode, strings.Join(as, ", "))
+ p.Printf("\t%s\t%s\n", i.Opcode, strings.Join(as, ", "))
counts["total"]++
}
- p.printf("\n")
+ p.Printf("\n")
}
- p.printf("\tRET\n")
+ p.Printf("\tRET\n")
for m, c := range counts {
- p.printf("// %s: %d\n", m, c)
+ p.Printf("// %s: %d\n", m, c)
}
- return p.Err()
+ return p.Result()
}
-func (l LoaderTest) skip(opcode string) (bool, string) {
+func (a asmtest) skip(opcode string) (bool, string) {
prefixes := map[string]string{
"PUSH": "PUSH can produce 'unbalanced PUSH/POP' assembler error",
"POP": "POP can produce 'unbalanced PUSH/POP' assembler error",
@@ -77,15 +80,15 @@ func (l LoaderTest) skip(opcode string) (bool, string) {
return false, ""
}
-func (l LoaderTest) args(opcode string, ops []inst.Operand) []string {
+func (a asmtest) args(opcode string, ops []inst.Operand) []string {
// Special case for CALL, since it needs a different type of rel32 argument than others.
if opcode == "CALL" {
- return []string{l.sym}
+ return []string{a.sym}
}
as := make([]string, len(ops))
for i, op := range ops {
- a := l.arg(op.Type, i)
+ a := a.arg(op.Type, i)
if a == "" {
return nil
}
@@ -95,7 +98,7 @@ func (l LoaderTest) args(opcode string, ops []inst.Operand) []string {
}
// arg generates an argument for an operand of the given type.
-func (l LoaderTest) arg(t string, i int) string {
+func (a asmtest) arg(t string, i int) string {
m := map[string]string{
"1": "$1", //
"3": "$3", //
@@ -165,8 +168,8 @@ func (l LoaderTest) arg(t string, i int) string {
//
//
//
- "rel8": l.rel8, //
- "rel32": l.rel32, //
+ "rel8": a.rel8, //
+ "rel32": a.rel32, //
//
//
diff --git a/internal/gen/gen.go b/internal/gen/gen.go
index acc26a5..2f47255 100644
--- a/internal/gen/gen.go
+++ b/internal/gen/gen.go
@@ -1,34 +1,46 @@
package gen
import (
+ "bytes"
"fmt"
- "io"
+ "go/format"
"github.com/mmcloughlin/avo/internal/inst"
)
type Interface interface {
- Generate(io.Writer, []*inst.Instruction) error
+ Generate([]*inst.Instruction) ([]byte, error)
}
-type Func func(io.Writer, []*inst.Instruction) error
+type Func func([]*inst.Instruction) ([]byte, error)
-func (f Func) Generate(w io.Writer, is []*inst.Instruction) error {
- return f(w, is)
+func (f Func) Generate(is []*inst.Instruction) ([]byte, error) {
+ return f(is)
+}
+
+// GoFmt formats Go code produced from the given generator.
+func GoFmt(i Interface) Interface {
+ return Func(func(is []*inst.Instruction) ([]byte, error) {
+ b, err := i.Generate(is)
+ if err != nil {
+ return nil, err
+ }
+ return format.Source(b)
+ })
}
type printer struct {
- w io.Writer
+ buf bytes.Buffer
err error
}
-func (p *printer) printf(format string, args ...interface{}) {
+func (p *printer) Printf(format string, args ...interface{}) {
if p.err != nil {
return
}
- _, p.err = fmt.Fprintf(p.w, format, args...)
+ _, p.err = fmt.Fprintf(&p.buf, format, args...)
}
-func (p *printer) Err() error {
- return p.err
+func (p *printer) Result() ([]byte, error) {
+ return p.buf.Bytes(), p.err
}
diff --git a/internal/gen/godata.go b/internal/gen/godata.go
new file mode 100644
index 0000000..2a2b8e4
--- /dev/null
+++ b/internal/gen/godata.go
@@ -0,0 +1,61 @@
+package gen
+
+import (
+ "github.com/mmcloughlin/avo/internal/inst"
+)
+
+type godata struct {
+}
+
+func NewGoData() Interface {
+ return GoFmt(godata{})
+}
+
+func (g godata) Generate(is []*inst.Instruction) ([]byte, error) {
+ p := &printer{}
+
+ p.Printf("package inst\n\n")
+
+ p.Printf("var Instructions = []Instruction{\n")
+
+ for _, i := range is {
+ p.Printf("{\n")
+
+ p.Printf("Opcode: %#v,\n", i.Opcode)
+ p.Printf("Summary: %#v,\n", i.Summary)
+
+ p.Printf("Forms: []Form{\n")
+ for _, f := range i.Forms {
+ p.Printf("{\n")
+
+ if f.ISA != nil {
+ p.Printf("ISA: %#v,\n", f.ISA)
+ }
+
+ if f.Operands != nil {
+ p.Printf("Operands: []Operand{\n")
+ for _, op := range f.Operands {
+ p.Printf("{Type: %#v, Action: %#v},\n", op.Type, op.Action)
+ }
+ p.Printf("},\n")
+ }
+
+ if f.ImplicitOperands != nil {
+ p.Printf("ImplicitOperands: []ImplicitOperand{\n")
+ for _, op := range f.ImplicitOperands {
+ p.Printf("{Register: %#v, Action: %#v},\n", op.Register, op.Action)
+ }
+ p.Printf("},\n")
+ }
+
+ p.Printf("},\n")
+ }
+ p.Printf("},\n")
+
+ p.Printf("},\n")
+ }
+
+ p.Printf("}\n")
+
+ return p.Result()
+}