From 07a8d64377c9e3611cd1b1f0e1e7965f300eea48 Mon Sep 17 00:00:00 2001 From: Zellyn Hunter Date: Fri, 22 Apr 2016 06:40:00 -0400 Subject: [PATCH] Tidying, getting ready to add Sweet16 opcodes - Getting tests to pass - Adding flavors of opcodes and passing them around - Rearranging a2as flags a little --- asm/asm.go | 7 ++- asm/cmd/a2as/a2as.go | 59 ++++++++++++---------- asm/cmd/a2as/runtest.sh | 6 +++ asm/flavors/as65/as65.go | 11 ++-- asm/flavors/merlin/merlin.go | 4 +- asm/flavors/oldschool/oldschool.go | 7 +-- asm/flavors/redbook/redbook.go | 12 +++-- asm/flavors/scma/scma.go | 4 +- asm/flavors/tests/assemble_test.go | 7 +-- asm/flavors/tests/simple_parse_test.go | 17 ++++--- asm/inst/instruction_test.go | 69 -------------------------- opcodes/opcodes.go | 17 +++++-- tests/functional_test.go | 2 +- 13 files changed, 95 insertions(+), 127 deletions(-) delete mode 100644 asm/inst/instruction_test.go diff --git a/asm/asm.go b/asm/asm.go index 2c08902..f88992d 100644 --- a/asm/asm.go +++ b/asm/asm.go @@ -13,6 +13,8 @@ import ( "github.com/zellyn/go6502/asm/membuf" ) +// Assembler is the basic assembler struct. It keeps track of its +// options, the stream of instructions, and defined macros. type Assembler struct { Flavor flavors.F Opener lines.Opener @@ -21,15 +23,18 @@ type Assembler struct { Ctx *context.SimpleContext } +// NewAssembler creates a new assembler with the given flavor and +// file-opener. func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler { ctx := &context.SimpleContext{} flavor.InitContext(ctx) - return &Assembler{ + a := &Assembler{ Flavor: flavor, Opener: opener, Macros: make(map[string]macros.M), Ctx: ctx, } + return a } type ifdef struct { diff --git a/asm/cmd/a2as/a2as.go b/asm/cmd/a2as/a2as.go index 3aa0493..5e82f2f 100644 --- a/asm/cmd/a2as/a2as.go +++ b/asm/cmd/a2as/a2as.go @@ -13,25 +13,14 @@ import ( "github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/ihex" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) -var flavorsByName map[string]flavors.F -var flavor string - -func init() { - flavorsByName = map[string]flavors.F{ - "merlin": merlin.New(), - "scma": scma.New(), - "redbooka": redbook.NewRedbookA(), - "redbookb": redbook.NewRedbookB(), - } - var names []string - for name := range flavorsByName { - names = append(names, name) - } - usage := fmt.Sprintf("assembler flavor: %s", strings.Join(names, ",")) - flag.StringVar(&flavor, "flavor", "", usage) - +var flavorNames = []string{ + "merlin", + "scma", + "redbooka", + "redbookb", } var infile = flag.String("in", "", "input file") @@ -40,6 +29,8 @@ var listfile = flag.String("listing", "", "listing file") var format = flag.String("format", "binary", "output format: binary/ihex") var fill = flag.Uint("fillbyte", 0x00, "byte value to use when filling gaps between assmebler output regions") var prefix = flag.Int("prefix", -1, "length of prefix to skip past addresses and bytes, -1 to guess") +var sweet16 = flag.Bool("sw16", false, "assemble sweet16 opcodes") +var flavorName = flag.String("flavor", "", fmt.Sprintf("assemble flavor: %s", strings.Join(flavorNames, ","))) func main() { flag.Parse() @@ -51,16 +42,6 @@ func main() { fmt.Fprintln(os.Stderr, "no output file specified") os.Exit(1) } - - if flavor == "" { - fmt.Fprintln(os.Stderr, "no flavor specified") - os.Exit(1) - } - f, ok := flavorsByName[flavor] - if !ok { - fmt.Fprintf(os.Stderr, "invalid flavor: '%s'\n", flavor) - os.Exit(1) - } if *format != "binary" && *format != "ihex" { fmt.Fprintf(os.Stderr, "format must be binary or ihex; got '%s'\n", *format) os.Exit(1) @@ -70,6 +51,30 @@ func main() { os.Exit(1) } + if *flavorName == "" { + fmt.Fprintln(os.Stderr, "no flavor specified") + os.Exit(1) + } + + var f flavors.F + var flavor opcodes.Flavor + if *sweet16 { + flavor |= opcodes.FlavorSweet16 + } + switch *flavorName { + case "merlin": + f = merlin.New(flavor) + case "scma": + f = scma.New(flavor) + case "redbooka": + f = redbook.NewRedbookA(flavor) + case "redbookb": + f = redbook.NewRedbookB(flavor) + default: + fmt.Fprintf(os.Stderr, "invalid flavor: '%s'\n", flavor) + os.Exit(1) + } + var o lines.OsOpener a := asm.NewAssembler(f, o) diff --git a/asm/cmd/a2as/runtest.sh b/asm/cmd/a2as/runtest.sh index 6ad067d..8020208 100755 --- a/asm/cmd/a2as/runtest.sh +++ b/asm/cmd/a2as/runtest.sh @@ -6,6 +6,12 @@ MD5_MONITOR=$(md5 -q monitor.rom) [[ $MD5_MONITOR == 'bc0163ca04c463e06f99fb029ad21b1f' ]] || (echo 'Wrong checksum for monitor.rom'; false) || exit 1 rm -f monitor.rom monitor.lst +echo autostart.rom +./a2as --in ../../../../goapple2/source/redbook/autostart.asm --out autostart.rom --flavor redbooka --listing autostart.lst --prefix=-1 +MD5_AUTOSTART=$(md5 -q autostart.rom) +[[ $MD5_AUTOSTART == '8925b695ae0177dd3919dbea2f2f202b' ]] || (echo 'Wrong checksum for autostart.rom'; false) || exit 1 +rm -f autostart.rom autostart.lst + echo miniasm.rom ./a2as --in ../../../../goapple2/source/redbook/miniasm.asm --out miniasm.rom --flavor redbooka --listing miniasm.lst --prefix=-1 MD5_MINIASM=$(md5 -q miniasm.rom) diff --git a/asm/flavors/as65/as65.go b/asm/flavors/as65/as65.go index 9c77a7c..cecf0b3 100644 --- a/asm/flavors/as65/as65.go +++ b/asm/flavors/as65/as65.go @@ -7,15 +7,20 @@ import ( "github.com/zellyn/go6502/asm/flavors" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) // AS65 implements the AS65-compatible assembler flavor. // See http://www.kingswood-consulting.co.uk/assemblers/ -type AS65 struct{} +type AS65 struct { + OpcodesByName map[string]opcodes.OpSummary +} -func New() *AS65 { - return &AS65{} +func New(flavors opcodes.Flavor) *AS65 { + return &AS65{ + OpcodesByName: opcodes.ByName(flavors), + } } // Parse an entire instruction, or return an appropriate error. diff --git a/asm/flavors/merlin/merlin.go b/asm/flavors/merlin/merlin.go index 400fd8c..5dbb144 100644 --- a/asm/flavors/merlin/merlin.go +++ b/asm/flavors/merlin/merlin.go @@ -9,6 +9,7 @@ import ( "github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) // Merlin implements the Merlin-compatible assembler flavor. @@ -21,9 +22,10 @@ type Merlin struct { const whitespace = " \t" const macroNameChars = oldschool.Letters + oldschool.Digits + "_" -func New() *Merlin { +func New(flavors opcodes.Flavor) *Merlin { m := &Merlin{} m.Name = "merlin" + m.OpcodesByName = opcodes.ByName(flavors) m.LabelChars = oldschool.Letters + oldschool.Digits + ":" m.LabelColons = oldschool.ReqDisallowed m.ExplicitARegister = oldschool.ReqOptional diff --git a/asm/flavors/oldschool/oldschool.go b/asm/flavors/oldschool/oldschool.go index b3f9808..2ce89b7 100644 --- a/asm/flavors/oldschool/oldschool.go +++ b/asm/flavors/oldschool/oldschool.go @@ -44,6 +44,7 @@ const ( type Base struct { Name string Directives map[string]DirectiveInfo + OpcodesByName map[string]opcodes.OpSummary Operators map[string]expr.Operator EquateDirectives map[string]bool LabelChars string @@ -239,15 +240,15 @@ func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode fl return a.parseSetting(ctx, in, lp) } - if summary, ok := opcodes.ByName[in.Command]; ok { + if summary, ok := a.OpcodesByName[in.Command]; ok { in.Type = inst.TypeOp return a.parseOpArgs(ctx, in, lp, summary, false) } // Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page. - if a.SuffixForWide { + if a.SuffixForWide && len(in.Command) == 4 { prefix := in.Command[:len(in.Command)-1] - if summary, ok := opcodes.ByName[prefix]; ok { + if summary, ok := a.OpcodesByName[prefix]; ok { in.Command = prefix in.Type = inst.TypeOp return a.parseOpArgs(ctx, in, lp, summary, true) diff --git a/asm/flavors/redbook/redbook.go b/asm/flavors/redbook/redbook.go index 959a22e..48afb0b 100644 --- a/asm/flavors/redbook/redbook.go +++ b/asm/flavors/redbook/redbook.go @@ -8,6 +8,7 @@ import ( "github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) // RedBook implements a Redbook-listing-compatible-ish assembler flavor. @@ -15,21 +16,22 @@ type RedBook struct { oldschool.Base } -func NewRedbookA() *RedBook { - r := newRedbook("redbook-a") +func NewRedbookA(flavors opcodes.Flavor) *RedBook { + r := newRedbook("redbook-a", flavors) return r } -func NewRedbookB() *RedBook { - r := newRedbook("redbook-b") +func NewRedbookB(flavors opcodes.Flavor) *RedBook { + r := newRedbook("redbook-b", flavors) r.ExplicitARegister = oldschool.ReqRequired r.SpacesForComment = 3 return r } -func newRedbook(name string) *RedBook { +func newRedbook(name string, flavors opcodes.Flavor) *RedBook { r := &RedBook{} r.Name = name + r.OpcodesByName = opcodes.ByName(flavors) r.LabelChars = oldschool.Letters + oldschool.Digits + "." r.LabelColons = oldschool.ReqOptional r.ExplicitARegister = oldschool.ReqRequired diff --git a/asm/flavors/scma/scma.go b/asm/flavors/scma/scma.go index 26d9f5f..096c6ac 100644 --- a/asm/flavors/scma/scma.go +++ b/asm/flavors/scma/scma.go @@ -8,6 +8,7 @@ import ( "github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) // 40 spaces = comment column @@ -19,9 +20,10 @@ type SCMA struct { oldschool.Base } -func New() *SCMA { +func New(flavors opcodes.Flavor) *SCMA { a := &SCMA{} a.Name = "scma" + a.OpcodesByName = opcodes.ByName(flavors) a.LabelChars = oldschool.Letters + oldschool.Digits + ".:" a.LabelColons = oldschool.ReqDisallowed a.ExplicitARegister = oldschool.ReqDisallowed diff --git a/asm/flavors/tests/assemble_test.go b/asm/flavors/tests/assemble_test.go index 6f669dd..608e92f 100644 --- a/asm/flavors/tests/assemble_test.go +++ b/asm/flavors/tests/assemble_test.go @@ -12,6 +12,7 @@ import ( "github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/lines" "github.com/zellyn/go6502/asm/membuf" + "github.com/zellyn/go6502/opcodes" ) // h converts from hex or panics. @@ -29,13 +30,13 @@ func TestMultiline(t *testing.T) { o := lines.NewTestOpener() ss := asmFactory(func() *asm.Assembler { - return asm.NewAssembler(scma.New(), o) + return asm.NewAssembler(scma.New(opcodes.FlavorSweet16), o) }) ra := asmFactory(func() *asm.Assembler { - return asm.NewAssembler(redbook.NewRedbookA(), o) + return asm.NewAssembler(redbook.NewRedbookA(opcodes.FlavorSweet16), o) }) mm := asmFactory(func() *asm.Assembler { - return asm.NewAssembler(merlin.New(), o) + return asm.NewAssembler(merlin.New(opcodes.FlavorSweet16), o) }) tests := []struct { diff --git a/asm/flavors/tests/simple_parse_test.go b/asm/flavors/tests/simple_parse_test.go index a1d6b60..ff06463 100644 --- a/asm/flavors/tests/simple_parse_test.go +++ b/asm/flavors/tests/simple_parse_test.go @@ -12,14 +12,15 @@ import ( "github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" + "github.com/zellyn/go6502/opcodes" ) func TestSimpleCommonFunctions(t *testing.T) { - ss := scma.New() - ra := redbook.NewRedbookA() - rb := redbook.NewRedbookB() - // aa := as65.New() - mm := merlin.New() + ss := scma.New(opcodes.FlavorSweet16) + ra := redbook.NewRedbookA(opcodes.FlavorSweet16) + rb := redbook.NewRedbookB(opcodes.FlavorSweet16) + // aa := as65.New(opcodes.FlavorSweet16) + mm := merlin.New(opcodes.FlavorSweet16) tests := []struct { f flavors.F // assembler flavor @@ -278,9 +279,9 @@ func TestSimpleCommonFunctions(t *testing.T) { } func TestSimpleErrors(t *testing.T) { - ss := scma.New() - aa := as65.New() - mm := merlin.New() + ss := scma.New(opcodes.FlavorSweet16) + aa := as65.New(opcodes.FlavorSweet16) + mm := merlin.New(opcodes.FlavorSweet16) tests := []struct { f flavors.F // assembler flavor diff --git a/asm/inst/instruction_test.go b/asm/inst/instruction_test.go deleted file mode 100644 index 58b0284..0000000 --- a/asm/inst/instruction_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package inst - -import ( - "testing" - - "github.com/zellyn/go6502/asm/context" - "github.com/zellyn/go6502/asm/expr" - "github.com/zellyn/go6502/asm/lines" -) - -func TestComputeLabel(t *testing.T) { - i := I{ - Label: "L1", - } - c := &context.SimpleContext{} - i.computeLabel(c, false) -} - -func TestWidthDoesNotChange(t *testing.T) { - i := I{ - Type: TypeOp, - Command: "LDA", - Exprs: []*expr.E{ - &expr.E{ - Op: expr.OpMinus, - Left: &expr.E{Op: expr.OpLeaf, Text: "L1"}, - Right: &expr.E{Op: expr.OpLeaf, Text: "L2"}, - }, - }, - Width: 0x2, - Final: false, - Op: 0xad, - Mode: 0x2, - ZeroMode: 0x80, - ZeroOp: 0xa5, - Line: &lines.Line{}, - } - c := &context.SimpleContext{} - c.Set("L1", 0x102) - final, err := i.Compute(c, false) - if err != nil { - t.Fatal(err) - } - if final { - t.Fatal("Second pass shouldn't be able to finalize expression with unknown variable.") - } - if !i.WidthKnown { - t.Fatal("Second pass should have set width.") - } - if i.Width != 3 { - t.Fatalf("i.Width should be 3; got %d", i.Width) - } - - c.Set("L2", 0x101) - - final, err = i.Compute(c, true) - if err != nil { - t.Fatal(err) - } - if !final { - t.Fatal("Third pass should be able to finalize expression.") - } - if !i.WidthKnown { - t.Fatal("Third pass should left width unchanged.") - } - if i.Width != 3 { - t.Fatalf("i.Width should still be 3; got %d", i.Width) - } -} diff --git a/opcodes/opcodes.go b/opcodes/opcodes.go index cff9fdc..3f0a211 100644 --- a/opcodes/opcodes.go +++ b/opcodes/opcodes.go @@ -273,17 +273,24 @@ type OpSummary struct { Ops []OpInfo } -var ByName map[string]OpSummary +type Flavor uint16 -func init() { - ByName = make(map[string]OpSummary) +const ( + FlavorUnknown Flavor = iota + FlavorSweet16 +) + +func ByName(flavors Flavor) map[string]OpSummary { + m := make(map[string]OpSummary) for b, oc := range Opcodes { info := OpInfo{oc.Mode, ModeLengths[oc.Mode], b} - summary := ByName[oc.Name] + summary := m[oc.Name] summary.Modes |= oc.Mode summary.Ops = append(summary.Ops, info) - ByName[oc.Name] = summary + m[oc.Name] = summary } + + return m } func (s OpSummary) AnyModes(modes AddressingMode) bool { diff --git a/tests/functional_test.go b/tests/functional_test.go index b35c2d4..a3afb0a 100644 --- a/tests/functional_test.go +++ b/tests/functional_test.go @@ -39,7 +39,7 @@ func randomize(k *K64) { // status prints out the current CPU instruction and register status. func status(c cpu.Cpu, m *[65536]byte, cc uint64) string { - bytes, text, _ := asm.Disasm(c.PC(), m[c.PC()], m[c.PC()+1], m[c.PC()+2]) + bytes, text, _ := asm.Disasm(c.PC(), m[c.PC()], m[c.PC()+1], m[c.PC()+2], nil, 0) return fmt.Sprintf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b - %d\n", c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P(), cc) }