diff --git a/asm/flavors/common/common.go b/asm/flavors/common/common.go index c22abd7..d8db9b0 100644 --- a/asm/flavors/common/common.go +++ b/asm/flavors/common/common.go @@ -10,7 +10,7 @@ import ( // DecodeOp contains the common code that decodes an Opcode, once we // have fully parsed it. -func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect bool, xy rune) (inst.I, error) { +func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect bool, xy rune, forceWide bool) (inst.I, error) { i := inst.I{} ex := in.Exprs[0] @@ -20,7 +20,10 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect case 'x': op, ok := summary.OpForMode(opcodes.MODE_INDIRECT_X) if !ok { - return i, fmt.Errorf("%s doesn't support indexed indirect (addr,X) mode", in.Command) + return i, in.Errorf("%s doesn't support indexed indirect (addr,X) mode", in.Command) + } + if forceWide { + return i, in.Errorf("%s (addr,X) doesn't have a wide variant", in.Command) } in.Op = op.Byte in.WidthKnown = true @@ -31,7 +34,10 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect case 'y': op, ok := summary.OpForMode(opcodes.MODE_INDIRECT_Y) if !ok { - return i, fmt.Errorf("%s doesn't support indirect indexed (addr),Y mode", in.Command) + return i, in.Errorf("%s doesn't support indirect indexed (addr),Y mode", in.Command) + } + if forceWide { + return i, fmt.Errorf("%s (addr),Y doesn't have a wide variant", in.Command) } in.WidthKnown = true in.MinWidth = 2 @@ -42,7 +48,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect default: op, ok := summary.OpForMode(opcodes.MODE_INDIRECT) if !ok { - return i, fmt.Errorf("%s doesn't support indirect (addr) mode", in.Command) + return i, in.Errorf("%s doesn't support indirect (addr) mode", in.Command) } in.Op = op.Byte in.WidthKnown = true @@ -59,6 +65,10 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect if !ok { panic(fmt.Sprintf("opcode error: %s has no MODE_RELATIVE opcode", in.Command)) } + if forceWide { + return i, fmt.Errorf("%s doesn't have a wide variant", in.Command) + } + in.Op = op.Byte in.WidthKnown = true in.MinWidth = 2 @@ -68,7 +78,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect } // No ,X or ,Y, and width is forced to 1-byte: immediate mode. - if xy == '-' && ex.Width() == 1 && summary.AnyModes(opcodes.MODE_IMMEDIATE) { + if xy == '-' && ex.Width() == 1 && summary.AnyModes(opcodes.MODE_IMMEDIATE) && !forceWide { op, ok := summary.OpForMode(opcodes.MODE_IMMEDIATE) if !ok { panic(fmt.Sprintf("opcode error: %s has no MODE_IMMEDIATE opcode", in.Command)) @@ -99,7 +109,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect opZp, zpOk := summary.OpForMode(zp) if !summary.AnyModes(zp | wide) { - return i, fmt.Errorf("%s opcode doesn't support %s or %s modes.", zpS, wideS) + return i, in.Errorf("%s opcode doesn't support %s or %s modes.", zpS, wideS) } if !summary.AnyModes(zp) { @@ -117,6 +127,9 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect if !zpOk { panic(fmt.Sprintf("opcode error: %s has no %s opcode", in.Command, zpS)) } + if forceWide { + return i, fmt.Errorf("%s doesn't have a wide variant", in.Command) + } in.Op = opZp.Byte in.WidthKnown = true in.MinWidth = 2 @@ -125,6 +138,15 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect return in, nil } + if forceWide { + in.Op = opWide.Byte + in.WidthKnown = true + in.MinWidth = 3 + in.MaxWidth = 3 + in.Mode = wide + return in, nil + } + // Okay, we don't know whether it's wide or narrow: store enough info for either. if !zpOk { panic(fmt.Sprintf("opcode error: %s has no %s opcode", in.Command, zpS)) diff --git a/asm/flavors/merlin/merlin.go b/asm/flavors/merlin/merlin.go index 8e1e4e6..4c9bd9f 100644 --- a/asm/flavors/merlin/merlin.go +++ b/asm/flavors/merlin/merlin.go @@ -36,6 +36,7 @@ func New() *Merlin { m.CharChars = "'" m.InvCharChars = `"` m.MacroArgSep = ";" + m.SuffixForWide = true m.Directives = map[string]oldschool.DirectiveInfo{ "ORG": {inst.TypeOrg, m.ParseAddress, 0}, diff --git a/asm/flavors/oldschool/oldschool.go b/asm/flavors/oldschool/oldschool.go index 69d0b2a..ee5d53d 100644 --- a/asm/flavors/oldschool/oldschool.go +++ b/asm/flavors/oldschool/oldschool.go @@ -20,7 +20,7 @@ const Digits = "0123456789" const binarydigits = "01" const hexdigits = Digits + "abcdefABCDEF" const Whitespace = " \t" -const cmdChars = Letters + Digits + ".>_" +const cmdChars = Letters + Digits + ".<>_:@" const fileChars = Letters + Digits + "." type DirectiveInfo struct { @@ -50,6 +50,7 @@ type Base struct { HexCommas Requiredness SpacesForComment int // this many spaces after command means it's the comment field StringEndOptional bool // can omit closing delimeter from string args? + SuffixForWide bool // is eg. "LDA:" a force-wide on "LDA"? (Merlin) CommentChar rune BinaryChar rune MsbChars string @@ -166,7 +167,17 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) { if summary, ok := opcodes.ByName[in.Command]; ok { in.Type = inst.TypeOp - return a.ParseOpArgs(in, lp, summary) + return a.ParseOpArgs(in, lp, summary, false) + } + + // Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page. + if a.SuffixForWide { + prefix := in.Command[:len(in.Command)-1] + if summary, ok := opcodes.ByName[prefix]; ok { + in.Command = prefix + in.Type = inst.TypeOp + return a.ParseOpArgs(in, lp, summary, true) + } } return inst.I{}, in.Errorf(`unknown command/instruction: "%s"`, in.Command) @@ -229,9 +240,9 @@ func (a *Base) ParseQuoted(in inst.I, lp *lines.Parse) (string, error) { return strings.Replace(s, `""`, `"`, -1), nil } -// ParseOpArgs parses the arguments to an assembly op. We expect to be looking at the first -// non-op character (probably whitespace) -func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary) (inst.I, error) { +// ParseOpArgs parses the arguments to an assembly op. We expect to be +// looking at the first non-op character (probably whitespace) +func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) { i := inst.I{} // MODE_IMPLIED: we don't really care what comes next: it's a comment. @@ -338,7 +349,7 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary } } - return common.DecodeOp(a, in, summary, indirect, xy) + return common.DecodeOp(a, in, summary, indirect, xy, forceWide) } func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) { diff --git a/asm/flavors/tests/simple_parse_test.go b/asm/flavors/tests/simple_parse_test.go index 46d69e3..de9fe4a 100644 --- a/asm/flavors/tests/simple_parse_test.go +++ b/asm/flavors/tests/simple_parse_test.go @@ -49,6 +49,7 @@ func TestSimpleCommonFunctions(t *testing.T) { // {aa, ` include "FILE.NAME"`, "{inc 'FILE.NAME'}", ""}, // {aa, ` title "Title here"`, "{-}", ""}, // {ss, " .TA *-1234", "{target (- * $04d2)}", ""}, + {mm, " <<<", `{endm}`, ""}, {mm, " >>> M1,$42 ;$43", `{call M1 {"$42"}}`, ""}, {mm, " >>> M1.$42", `{call M1 {"$42"}}`, ""}, {mm, " >>> M1/$42;$43", `{call M1 {"$42", "$43"}}`, ""}, @@ -61,6 +62,7 @@ func TestSimpleCommonFunctions(t *testing.T) { {mm, " DFB $12,$34,$1234", "{data/b $0012,$0034,$1234}", "123434"}, {mm, " DFB $34,100,$81A2-$77C4,%1011,>$81A2-$77C4", "{data/b $0034,$0064,(- $81a2 $77c4),$000b,(msb (- $81a2 $77c4))}", "3464de0b09"}, {mm, " DSK OUTFILE", "{-}", ""}, + {mm, " EOM", `{endm}`, ""}, {mm, " HEX 00,01,FF,AB", "{data/b}", "0001ffab"}, {mm, " HEX 0001FFAB", "{data/b}", "0001ffab"}, {mm, " INCW $42;$43", `{call INCW {"$42", "$43"}}`, ""}, @@ -79,9 +81,9 @@ func TestSimpleCommonFunctions(t *testing.T) { {mm, " LDA $1234,X", "{LDA/absX $1234}", "bd3412"}, {mm, " LDA ($12),Y", "{LDA/indY $0012}", "b112"}, {mm, " LDA ($12,X)", "{LDA/indX $0012}", "a112"}, - {mm, " LDA: $12", "{LDA/zp $0012}", "ad1200"}, - {mm, " LDA@ $12", "{LDA/zp $0012}", "ad1200"}, - {mm, " LDAX $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDA: $12", "{LDA/abs $0012}", "ad1200"}, + {mm, " LDA@ $12", "{LDA/abs $0012}", "ad1200"}, + {mm, " LDAX $12", "{LDA/abs $0012}", "ad1200"}, {mm, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, {mm, " ORG $D000", "{org $d000}", ""}, {mm, " PMC M1($42", `{call M1 {"$42"}}`, ""}, @@ -107,6 +109,7 @@ func TestSimpleCommonFunctions(t *testing.T) { {mm, "Label", "{- 'Label'}", ""}, {mm, "Label;Comment", "{- 'Label'}", ""}, {mm, "MacroName MAC", `{macro "MacroName"}`, ""}, + {mm, "MacroName MAC", `{macro "MacroName"}`, ""}, {mm, ` ASC !ABC!`, "{data/b}", "c1c2c3"}, {mm, ` ASC "ABC"`, "{data/b}", "c1c2c3"}, {mm, ` ASC #ABC#`, "{data/b}", "c1c2c3"},