From 1754dcf7a3e4fd8f3270892c1c2f0280af9c92f6 Mon Sep 17 00:00:00 2001 From: Zellyn Hunter Date: Thu, 12 Jun 2014 17:39:48 -0700 Subject: [PATCH] wip: merlin assembler --- asm/a2as/a2as.go | 3 + asm/flavors/merlin/merlin.go | 140 ++++++++++-- asm/flavors/oldschool/oldschool.go | 93 ++++++-- asm/flavors/redbook/redbook.go | 2 + asm/flavors/scma/scma.go | 2 + asm/flavors/tests/assemble_test.go | 29 ++- asm/flavors/tests/simple_parse_test.go | 282 +++++++++++++++---------- asm/inst/instruction.go | 2 +- asm/lines/lineparse.go | 2 +- 9 files changed, 396 insertions(+), 159 deletions(-) diff --git a/asm/a2as/a2as.go b/asm/a2as/a2as.go index 2379292..c0b09a6 100644 --- a/asm/a2as/a2as.go +++ b/asm/a2as/a2as.go @@ -8,6 +8,7 @@ import ( "github.com/zellyn/go6502/asm" "github.com/zellyn/go6502/asm/flavors" + "github.com/zellyn/go6502/asm/flavors/merlin" "github.com/zellyn/go6502/asm/flavors/redbook" "github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/ihex" @@ -19,6 +20,7 @@ var flavor string func init() { flavorsByName = map[string]flavors.F{ + "merlin": merlin.New(), "scma": scma.New(), "redbooka": redbook.NewRedbookA(), "redbookb": redbook.NewRedbookB(), @@ -79,6 +81,7 @@ func main() { fmt.Fprintf(os.Stderr, "Error trying to determine prefix length for file '%s'", *infile, err) os.Exit(1) } + fmt.Fprintf(os.Stderr, "Prefix guessed to be %d\n", p) } if err := a.AssembleWithPrefix(*infile, p); err != nil { diff --git a/asm/flavors/merlin/merlin.go b/asm/flavors/merlin/merlin.go index 0916b15..e6a409a 100644 --- a/asm/flavors/merlin/merlin.go +++ b/asm/flavors/merlin/merlin.go @@ -2,8 +2,11 @@ package merlin import ( "errors" + "fmt" + "strings" - "github.com/zellyn/go6502/asm/context" + "github.com/zellyn/go6502/asm/expr" + "github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/lines" ) @@ -11,41 +14,140 @@ import ( // Merlin implements the Merlin-compatible assembler flavor. // See http://en.wikipedia.org/wiki/Merlin_(assembler) and // http://www.apple-iigs.info/doc/fichiers/merlin816.pdf‎ - type Merlin struct { - context.SimpleContext - context.LabelerBase + oldschool.Base } +const whitespace = " \t" + func New() *Merlin { - return &Merlin{} + m := &Merlin{} + m.LabelChars = oldschool.Letters + oldschool.Digits + ":" + m.LabelColons = oldschool.ReqDisallowed + m.ExplicitARegister = oldschool.ReqOptional + m.StringEndOptional = false + m.CommentChar = ';' + m.BinaryChar = '%' + m.LsbChars = "<" + m.MsbChars = ">/" + m.ImmediateChars = "#" + m.HexCommas = oldschool.ReqOptional + + m.Directives = map[string]oldschool.DirectiveInfo{ + "ORG": {inst.TypeOrg, m.ParseAddress, 0}, + "OBJ": {inst.TypeNone, nil, 0}, + "ENDASM": {inst.TypeEnd, m.ParseNoArgDir, 0}, + "=": {inst.TypeEqu, m.ParseEquate, inst.EquNormal}, + "HEX": {inst.TypeData, m.ParseHexString, inst.DataBytes}, + "DFB": {inst.TypeData, m.ParseData, inst.DataBytes}, + "DA": {inst.TypeData, m.ParseData, inst.DataWordsLe}, + "DDB": {inst.TypeData, m.ParseData, inst.DataWordsBe}, + "ASC": {inst.TypeData, m.ParseAscii, inst.DataAscii}, + "DCI": {inst.TypeData, m.ParseAscii, inst.DataAsciiFlip}, + ".DO": {inst.TypeIfdef, m.ParseDo, 0}, + ".ELSE": {inst.TypeIfdefElse, m.ParseNoArgDir, 0}, + ".FIN": {inst.TypeIfdefEnd, m.ParseNoArgDir, 0}, + ".MA": {inst.TypeMacroStart, m.ParseMacroStart, 0}, + ".EM": {inst.TypeMacroEnd, m.ParseNoArgDir, 0}, + ".US": {inst.TypeNone, m.ParseNotImplemented, 0}, + "PAGE": {inst.TypeNone, nil, 0}, // New page + "TTL": {inst.TypeNone, nil, 0}, // Title + "SAV": {inst.TypeNone, nil, 0}, // Save + "DSK": {inst.TypeNone, nil, 0}, // Assemble to disk + "PUT": {inst.TypeInclude, m.ParseInclude, 0}, + "USE": {inst.TypeInclude, m.ParseInclude, 0}, + } + m.Operators = map[string]expr.Operator{ + "*": expr.OpMul, + "/": expr.OpDiv, + "+": expr.OpPlus, + "-": expr.OpMinus, + "<": expr.OpLt, + ">": expr.OpGt, + "=": expr.OpEq, + } + + m.OnOff = map[string]bool{ + "LST": true, // Display listing: not used + "XC": false, // Extended commands: not implemented yet + "EXP": false, // How to print macro calls + "LSTDO": false, // List conditional code? + "TR": false, // truncate listing to 3 bytes? + "CYC": false, // print cycle times? + } + + m.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) { + if in.Command != "ASC" && in.Command != "DCI" { + panic(fmt.Sprintf("Unimplemented/unknown ascii directive: '%s'", in.Command)) + } + invert := lp.Peek() < '\'' + invertLast := in.Command == "DCI" + switch { + case !invert && !invertLast: + in.Var = inst.DataAscii + case !invert && invertLast: + in.Var = inst.DataAsciiFlip + case invert && !invertLast: + in.Var = inst.DataAsciiHi + case invert && invertLast: + in.Var = inst.DataAsciiHiFlip + } + } + + return m } -// Parse an entire instruction, or return an appropriate error. -func (a *Merlin) ParseInstr(line lines.Line) (inst.I, error) { - return inst.I{}, nil -} - -func (a *Merlin) Zero() (uint16, error) { +func (m *Merlin) Zero() (uint16, error) { return 0, errors.New("Division by zero.") } -func (a *Merlin) DefaultOrigin() (uint16, error) { +func (m *Merlin) DefaultOrigin() (uint16, error) { return 0x8000, nil } -func (a *Merlin) SetWidthsOnFirstPass() bool { - panic("don't know yet") +func (m *Merlin) SetWidthsOnFirstPass() bool { + // TODO(zellyn): figure this out + return true } -func (a *Merlin) ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error) { +func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) { + lp.IgnoreRun(whitespace) + lp.AcceptUntil(";") + filename := strings.TrimSpace(lp.Emit()) + prefix := "T." + if len(filename) > 0 && filename[0] < '@' { + prefix = "" + filename = strings.TrimSpace(filename[1:]) + } + if filename == "" { + return inst.I{}, in.Errorf("%s expects filename", in.Command) + } + in.TextArg = prefix + filename + in.WidthKnown = true + in.MinWidth = 0 + in.MaxWidth = 0 + in.Final = true + return in, nil +} + +func (m *Merlin) ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error) { panic("Merlin.ReplaceMacroArgs not implemented yet.") } -func (a *Merlin) IsNewParentLabel(label string) bool { - return label != "" && label[0] != '.' +func (m *Merlin) IsNewParentLabel(label string) bool { + return label != "" && label[0] != ':' } -func (a *Merlin) FixLabel(label string, macroCount int) (string, error) { - panic("Merlin.FixLabel not implemented yet.") +func (m *Merlin) FixLabel(label string, macroCount int) (string, error) { + switch { + case label == "": + return label, nil + case label[0] == ':': + if last := m.LastLabel(); last == "" { + return "", fmt.Errorf("sublabel '%s' without previous label", label) + } else { + return fmt.Sprintf("%s/%s", last, label), nil + } + } + return label, nil } diff --git a/asm/flavors/oldschool/oldschool.go b/asm/flavors/oldschool/oldschool.go index 1ea566a..2db1747 100644 --- a/asm/flavors/oldschool/oldschool.go +++ b/asm/flavors/oldschool/oldschool.go @@ -15,8 +15,9 @@ import ( "github.com/zellyn/go6502/opcodes" ) -const Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +const Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" const Digits = "0123456789" +const binarydigits = "01" const hexdigits = Digits + "abcdefABCDEF" const whitespace = " \t" const cmdChars = Letters + "." @@ -33,9 +34,9 @@ type DirectiveInfo struct { type Requiredness int const ( - ReqOptional Requiredness = iota + ReqDisallowed Requiredness = iota + ReqOptional ReqRequired - ReqDisallowed ) // Base implements the S-C Macro Assembler-compatible assembler flavor. @@ -48,11 +49,16 @@ type Base struct { LabelChars string LabelColons Requiredness ExplicitARegister Requiredness + HexCommas Requiredness ExtraCommenty func(string) bool SpacesForComment int // this many spaces after command means it's the comment field StringEndOptional bool // can omit closing delimeter from string args? SetAsciiVariation func(*inst.I, *lines.Parse) CommentChar rune + BinaryChar rune + MsbChars string + LsbChars string + ImmediateChars string } // Parse an entire instruction, or return an appropriate error. @@ -107,7 +113,7 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) { // Ignore whitespace at the start or after the label. lp.IgnoreRun(whitespace) - if lp.Peek() == lines.Eol { + if lp.Peek() == lines.Eol || lp.Peek() == a.CommentChar { in.Type = inst.TypeNone return in, nil } @@ -128,7 +134,7 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) { if lp.Consume(">") { return a.ParseMacroCall(in, lp) } - if !lp.AcceptRun(cmdChars) { + if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) { c := lp.Next() return inst.I{}, in.Errorf("expecting instruction, found '%c' (%d)", c, c) } @@ -453,17 +459,26 @@ func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) { } func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) { - lp.IgnoreRun(whitespace) - if !lp.AcceptRun(hexdigits) { - return inst.I{}, in.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next()) - } - hs := lp.Emit() - if len(hs)%2 != 0 { - return inst.I{}, in.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs)) - } - var err error - if in.Data, err = hex.DecodeString(hs); err != nil { - return inst.I{}, in.Errorf("%s: error decoding hex string: %s", in.Command, err) + lp.AcceptRun(whitespace) + for { + lp.Ignore() + if !lp.AcceptRun(hexdigits) { + return inst.I{}, in.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next()) + } + hs := lp.Emit() + if len(hs)%2 != 0 { + return inst.I{}, in.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs)) + } + data, err := hex.DecodeString(hs) + if err != nil { + return inst.I{}, in.Errorf("%s: error decoding hex string: %s", in.Command, err) + } + in.Data = append(in.Data, data...) + + // Keep going if we allow commas and have one + if a.HexCommas == ReqDisallowed || !lp.Accept(",") { + break + } } return in, nil } @@ -508,12 +523,30 @@ func (a *Base) ParseNotImplemented(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) { var outer *expr.E - if lp.Accept("#/") { - switch lp.Emit() { - case "#": - outer = &expr.E{Op: expr.OpLsb} - case "/": - outer = &expr.E{Op: expr.OpMsb} + if lp.AcceptRun(a.MsbChars + a.LsbChars + a.ImmediateChars) { + pc := lp.Emit() + switch len(pc) { + case 1: + switch { + case strings.Contains(a.MsbChars, pc[:1]): + outer = &expr.E{Op: expr.OpMsb} + case strings.Contains(a.LsbChars+a.ImmediateChars, pc[:1]): + outer = &expr.E{Op: expr.OpLsb} + } + case 2: + err := in.Errorf("Got strange number prefix: '%s'", pc) + switch { + case !strings.Contains(a.ImmediateChars, pc[:1]): + return &expr.E{}, err + case strings.Contains(a.MsbChars, pc[1:]): + outer = &expr.E{Op: expr.OpMsb} + case strings.Contains(a.LsbChars, pc[1:]): + outer = &expr.E{Op: expr.OpLsb} + default: + return &expr.E{}, err + } + default: + return &expr.E{}, in.Errorf("Expected one or two number prefixes, got '%s'", pc) } } @@ -570,6 +603,22 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) { return top, nil } + // Hex + if lp.Consume(string(a.BinaryChar)) { + if !lp.AcceptRun(binarydigits) { + c := lp.Next() + return &expr.E{}, in.Errorf("expecting binary number, found '%c' (%d)", c, c) + } + s := lp.Emit() + i, err := strconv.ParseUint(s, 2, 16) + if err != nil { + return &expr.E{}, in.Errorf("invalid binary number: %s: %s", s, err) + } + ex.Op = expr.OpLeaf + ex.Val = uint16(i) + return top, nil + } + // Decimal if lp.AcceptRun(Digits) { s := lp.Emit() diff --git a/asm/flavors/redbook/redbook.go b/asm/flavors/redbook/redbook.go index 3805740..6a0f35c 100644 --- a/asm/flavors/redbook/redbook.go +++ b/asm/flavors/redbook/redbook.go @@ -34,6 +34,8 @@ func newRedbook() *RedBook { r.ExplicitARegister = oldschool.ReqRequired r.StringEndOptional = true r.CommentChar = ';' + r.MsbChars = "/" + r.ImmediateChars = "#" r.Directives = map[string]oldschool.DirectiveInfo{ "ORG": {inst.TypeOrg, r.ParseAddress, 0}, diff --git a/asm/flavors/scma/scma.go b/asm/flavors/scma/scma.go index b80492e..43e18f3 100644 --- a/asm/flavors/scma/scma.go +++ b/asm/flavors/scma/scma.go @@ -24,6 +24,8 @@ func New() *SCMA { a.LabelColons = oldschool.ReqDisallowed a.ExplicitARegister = oldschool.ReqDisallowed a.SpacesForComment = 2 + a.MsbChars = "/" + a.ImmediateChars = "#" a.Directives = map[string]oldschool.DirectiveInfo{ ".IN": {inst.TypeInclude, a.ParseInclude, 0}, diff --git a/asm/flavors/tests/assemble_test.go b/asm/flavors/tests/assemble_test.go index 0dac837..ec69bfc 100644 --- a/asm/flavors/tests/assemble_test.go +++ b/asm/flavors/tests/assemble_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/zellyn/go6502/asm" + "github.com/zellyn/go6502/asm/flavors/merlin" "github.com/zellyn/go6502/asm/flavors/redbook" "github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/lines" @@ -27,9 +28,7 @@ func TestMultiline(t *testing.T) { ss := asm.NewAssembler(scma.New(), o) ra := asm.NewAssembler(redbook.NewRedbookA(), o) - // rb := asm.NewAssembler(redbook.NewRedbookB(), o) - // aa := asm.NewAssembler(as65.New(), o) - // mm := asm.NewAssembler(merlin.New(), o) + mm := asm.NewAssembler(merlin.New(), o) tests := []struct { a *asm.Assembler // assembler @@ -54,7 +53,7 @@ func TestMultiline(t *testing.T) { }, nil, "adff00ea", nil, true}, // Sub-labels - {ss, "Sublabels", []string{ + {ss, "ss:Sublabels", []string{ "L1 BEQ .1", ".1 NOP", "L2 BEQ .2", @@ -62,6 +61,15 @@ func TestMultiline(t *testing.T) { ".2 NOP", }, nil, "f000eaf001eaea", nil, true}, + // Sub-labels + {mm, "mm:Sublabels", []string{ + "L1 BEQ :ONE", + ":ONE NOP", + "L2 BEQ :TWO", + ":ONE NOP", + ":TWO NOP", + }, nil, "f000eaf001eaea", nil, true}, + // Includes: one level deep {ss, "Include A", []string{ " BEQ OVER", @@ -228,6 +236,19 @@ func TestMultiline(t *testing.T) { " MSB ON", " ASC 'AB'", }, nil, "c1c24142c1c2", nil, true}, + + // Merlin: macros, local labels + {mm, "Macros: local labels", []string{ + "L1 NOP", + "M1 MAC", + " INC ]1", + " BNE L1", + "L1 NOP", + " <<<", + " >>> M1.$42", + " PMC M1($43;$44", + " M1 $44", + }, nil, "eae642d000eae643d000eae644d000ea", nil, true}, } for i, tt := range tests { diff --git a/asm/flavors/tests/simple_parse_test.go b/asm/flavors/tests/simple_parse_test.go index 42747a5..c509ee7 100644 --- a/asm/flavors/tests/simple_parse_test.go +++ b/asm/flavors/tests/simple_parse_test.go @@ -17,7 +17,7 @@ func TestSimpleCommonFunctions(t *testing.T) { ra := redbook.NewRedbookA() rb := redbook.NewRedbookB() // aa := as65.New() - // mm := merlin.New() + mm := merlin.New() tests := []struct { a flavors.F // assembler flavor @@ -25,131 +25,187 @@ func TestSimpleCommonFunctions(t *testing.T) { p string // printed instruction, expected b string // bytes, expected }{ - {ss, "* Comment", "{-}", ""}, - {ra, "* Comment", "{-}", ""}, - {ra, " ; Comment", "{-}", ""}, - // {aa, "; Comment", "{-}", ""}, - // {mm, "* Comment", "{-}", ""}, - {ss, " far-out-comment", "{-}", ""}, - {ss, "Label", "{- 'Label'}", ""}, - {ra, "Label", "{- 'Label'}", ""}, - {ra, "Label:", "{- 'Label'}", ""}, - // {aa, "Label", "{- 'Label'}", ""}, - // {mm, "Label", "{- 'Label'}", ""}, - {ss, " .IN FILE.NAME", "{inc 'FILE.NAME'}", ""}, - {ss, " .IN S.DEFS", "{inc 'S.DEFS'}", ""}, - // {aa, ` include "FILE.NAME"`, "{inc 'FILE.NAME'}", ""}, - // {mm, " PUT !FILE.NAME", "{inc 'FILE.NAME'}", ""}, - {ss, " .TI 76,Title here", "{-}", ""}, - {ra, ` SBTL Title here`, "{-}", ""}, - {ra, ` TITLE Title here`, "{-}", ""}, - // {aa, ` title "Title here"`, "{-}", ""}, - // {mm, ` TTL "Title here"`, "{-}", ""}, - {ss, " .TF OUT.BIN", "{-}", ""}, - // {mm, " DSK OUTFILE", "{-}", ""}, - // {mm, " SAV OUTFILE", "{-}", ""}, - {ss, " .OR $D000", "{org $d000}", ""}, - {ra, " ORG $D000", "{org $d000}", ""}, - // {aa, " org $D000", "{org $d000}", ""}, - // {mm, " ORG $D000", "{org $d000}", ""}, - // {ss, " .TA *-1234", "{target (- * $04d2)}", ""}, - {ss, " .DA $1234", "{data $1234}", "3412"}, + // {aa, " beq $2343", "{BEQ/rel $2343}", "f0fc"}, + // {aa, " beq $2345", "{BEQ/rel $2345}", "f0fe"}, + // {aa, " beq $2347", "{BEQ/rel $2347}", "f000"}, // {aa, " dw $1234", "{data/wle $1234}", "3412"}, - // {mm, " DW $1234", "{data/wle $1234}", "3412"}, - {ss, " .DA/$1234,#$1234,$1234", "{data (msb $1234),(lsb $1234),$1234}", "12343412"}, + // {aa, " jmp $1234", "{JMP/abs $1234}", "4c3412"}, + // {aa, " jmp ($1234)", "{JMP/ind $1234}", "6c3412"}, + // {aa, " lda #$12", "{LDA/imm (lsb $0012)}", "a912"}, + // {aa, " lda $12", "{LDA/zp $0012}", "a512"}, + // {aa, " lda $12,x", "{LDA/zpX $0012}", "b512"}, + // {aa, " lda $1234", "{LDA/abs $1234}", "ad3412"}, + // {aa, " lda $1234,x", "{LDA/absX $1234}", "bd3412"}, + // {aa, " lda ($12),y", "{LDA/indY $0012}", "b112"}, + // {aa, " lda ($12,x)", "{LDA/indX $0012}", "a112"}, + // {aa, " ldx $12,y", "{LDX/zpY $0012}", "b612"}, + // {aa, " org $D000", "{org $d000}", ""}, + // {aa, " rol $12", "{ROL/zp $0012}", "2612"}, + // {aa, " rol $1234", "{ROL/abs $1234}", "2e3412"}, + // {aa, " rol a", "{ROL/a}", "2a"}, + // {aa, " sta $1234,y", "{STA/absY $1234}", "993412"}, + // {aa, "; Comment", "{-}", ""}, + // {aa, "Label", "{- 'Label'}", ""}, + // {aa, ` include "FILE.NAME"`, "{inc 'FILE.NAME'}", ""}, + // {aa, ` title "Title here"`, "{-}", ""}, + // {ss, " .TA *-1234", "{target (- * $04d2)}", ""}, + {mm, " >>> M1,$42 ;$43", `{call M1 {"$42"}}`, ""}, + {mm, " >>> M1.$42", `{call M1 {"$42"}}`, ""}, + {mm, " >>> M1/$42;$43", `{call M1 {"$42", "$43"}}`, ""}, + {mm, " BEQ $2343", "{BEQ/rel $2343}", "f0fc"}, + {mm, " BEQ $2345", "{BEQ/rel $2345}", "f0fe"}, + {mm, " BEQ $2347", "{BEQ/rel $2347}", "f000"}, + {mm, " DA $12,$34,$1234", "{data/wle $0012,$0034,$1234}", "120034003412"}, + {mm, " DDB $12,$34,$1234", "{data/wbe $0012,$0034,$1234}", "001200341234"}, + {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, " DA $1234", "{data/wle $1234}", "3412"}, + {mm, " HEX 00,01,FF,AB", "{data/b}", "0001ffab"}, + {mm, " HEX 0001FFAB", "{data/b}", "0001ffab"}, + {mm, " JMP $1234", "{JMP/abs $1234}", "4c3412"}, + {mm, " JMP ($1234)", "{JMP/ind $1234}", "6c3412"}, + {mm, " LDA #$12", "{LDA/imm (lsb $0012)}", "a912"}, + {mm, " LDA $12", "{LDA/zp $0012}", "a512"}, + {mm, " LDA $12,X", "{LDA/zpX $0012}", "b512"}, + {mm, " LDA $1234", "{LDA/abs $1234}", "ad3412"}, + {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, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, + {mm, " ORG $D000", "{org $d000}", ""}, + {mm, " PMC M1($42", `{call M1 {"$42"}}`, ""}, + {mm, " PMC M1-$42", `{call M1 {"$42"}}`, ""}, + {mm, " PUT !FILE.NAME", "{inc 'FILE.NAME'}", ""}, + {mm, " ROL $12", "{ROL/zp $0012}", "2612"}, + {mm, " ROL $1234", "{ROL/abs $1234}", "2e3412"}, + {mm, " ROL", "{ROL/a}", "2a"}, + {mm, " SAV OUTFILE", "{-}", ""}, + {mm, " STA $1234,Y", "{STA/absY $1234}", "993412"}, + {mm, "* Comment", "{-}", ""}, + {mm, "ABC = $800", "{= 'ABC' $0800}", ""}, + {mm, "Label ;Comment", "{- 'Label'}", ""}, + {mm, "Label", "{- 'Label'}", ""}, + {mm, "Label", "{- 'Label'}", ""}, + {mm, "Label;Comment", "{- 'Label'}", ""}, + {mm, ` ASC !ABC!`, "{data/b}", "c1c2c3"}, + {mm, ` ASC "ABC"`, "{data/b}", "c1c2c3"}, + {mm, ` ASC #ABC#`, "{data/b}", "c1c2c3"}, + {mm, ` ASC $ABC$`, "{data/b}", "c1c2c3"}, + {mm, ` ASC %ABC%`, "{data/b}", "c1c2c3"}, + {mm, ` ASC &ABC&`, "{data/b}", "c1c2c3"}, + {mm, ` ASC 'ABC'`, "{data/b}", "414243"}, + {mm, ` ASC (ABC(`, "{data/b}", "414243"}, + {mm, ` ASC )ABC)`, "{data/b}", "414243"}, + {mm, ` ASC +ABC+`, "{data/b}", "414243"}, + {mm, ` ASC ?ABC?`, "{data/b}", "414243"}, + {mm, ` DCI !ABC!`, "{data/b}", "c1c243"}, + {mm, ` DCI "ABC"`, "{data/b}", "c1c243"}, + {mm, ` DCI #ABC#`, "{data/b}", "c1c243"}, + {mm, ` DCI $ABC$`, "{data/b}", "c1c243"}, + {mm, ` DCI %ABC%`, "{data/b}", "c1c243"}, + {mm, ` DCI &ABC&`, "{data/b}", "c1c243"}, + {mm, ` DCI 'ABC'`, "{data/b}", "4142c3"}, + {mm, ` DCI (ABC(`, "{data/b}", "4142c3"}, + {mm, ` DCI )ABC)`, "{data/b}", "4142c3"}, + {mm, ` DCI +ABC+`, "{data/b}", "4142c3"}, + {mm, ` DCI ?ABC?`, "{data/b}", "4142c3"}, + {mm, ` TTL "Title here"`, "{-}", ""}, + {mm, " LDA #$1234", "{LDA/imm (lsb $1234)}", "a934"}, + {mm, " LDA #<$1234", "{LDA/imm (lsb $1234)}", "a934"}, + {mm, " LDA #>$1234", "{LDA/imm (msb $1234)}", "a912"}, + {mm, " LDA #/$1234", "{LDA/imm (msb $1234)}", "a912"}, + {mm, " LDA $12", "{LDA/zp $0012}", "a512"}, + {mm, " LDA: $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDA@ $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDAX $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDA $1234", "{LDA/abs $1234}", "ad3412"}, + + {mm, "L1 = L2-L3", "{= 'L1' (- L2 L3)}", ""}, + {mm, "L1 = 2*L2+$231", "{= 'L1' (+ (* $0002 L2) $0231)}", ""}, + {mm, "L1 = 1234+%10111", "{= 'L1' (+ $04d2 $0017)}", ""}, + {mm, "L1 = L2&$7F", "{= 'L1' (& L2 $007f)}", ""}, + {mm, "L1 = *-2", "{= 'L1' (- * $0002)}", ""}, + {mm, "L1 = L2.%10000000", "{= 'L1' (| L2 $0080)}", ""}, + {mm, `L1 = L2!"A"`, "{= 'L1' (^ L2 $00c1)}", ""}, + {mm, "L1 = L2!'A'", "{= 'L1' (^ L2 $0041)}", ""}, + {mm, `L1 = "A+3`, "{= 'L1' (+ $00c1 $0003)}", ""}, + {mm, "L1 = 'A.2", "{= 'L1' (| $0041 $0002)}", ""}, + + {ra, " ; Comment", "{-}", ""}, + {ra, " DDB $12,$34,$1234", "{data/wbe $0012,$0034,$1234}", "001200341234"}, {ra, " DFB $12", "{data/b $0012}", "12"}, {ra, " DFB $12,$34,$1234", "{data/b $0012,$0034,$1234}", "123434"}, {ra, " DW $12,$34,$1234", "{data/wle $0012,$0034,$1234}", "120034003412"}, - {ra, " DDB $12,$34,$1234", "{data/wbe $0012,$0034,$1234}", "001200341234"}, - {ss, " ROL", "{ROL/a}", "2a"}, - // {aa, " rol a", "{ROL/a}", "2a"}, - {ra, " ROL A", "{ROL/a}", "2a"}, + {ra, " LST OFF", "{set LST OFF}", ""}, + {ra, " LST ON", "{set LST ON}", ""}, + {ra, " MSB OFF", "{set MSB OFF}", ""}, + {ra, " MSB ON", "{set MSB ON}", ""}, + {ra, " ORG $D000", "{org $d000}", ""}, {ra, " ROL A", "{ROL/a}", "2a"}, // two spaces is no big deal - // {mm, " ROL", "{ROL/a}", "2a"}, - {ss, " ROL Comment after two spaces", "{ROL/a}", "2a"}, - {ss, " ROL X", "{ROL/a}", "2a"}, // two spaces = comment - {rb, " ROL", "{ROL/a}", "2a"}, - {rb, " ROL Comment after three spaces", "{ROL/a}", "2a"}, - {rb, " ROL X", "{ROL/a}", "2a"}, // two spaces = comment - {ss, " ROL $1234", "{ROL/abs $1234}", "2e3412"}, - // {aa, " rol $1234", "{ROL/abs $1234}", "2e3412"}, - // {mm, " ROL $1234", "{ROL/abs $1234}", "2e3412"}, - {ss, " ROL $12", "{ROL/zp $0012}", "2612"}, - // {aa, " rol $12", "{ROL/zp $0012}", "2612"}, - // {mm, " ROL $12", "{ROL/zp $0012}", "2612"}, - {ss, " LDA #$12", "{LDA/imm (lsb $0012)}", "a912"}, - // {aa, " lda #$12", "{LDA/imm (lsb $0012)}", "a912"}, - // {mm, " LDA #$12", "{LDA/imm (lsb $0012)}", "a912"}, - {ss, " JMP $1234", "{JMP/abs $1234}", "4c3412"}, - // {aa, " jmp $1234", "{JMP/abs $1234}", "4c3412"}, - // {mm, " JMP $1234", "{JMP/abs $1234}", "4c3412"}, - {ss, " JMP ($1234)", "{JMP/ind $1234}", "6c3412"}, - // {aa, " jmp ($1234)", "{JMP/ind $1234}", "6c3412"}, - // {mm, " JMP ($1234)", "{JMP/ind $1234}", "6c3412"}, - {ss, " BEQ $2345", "{BEQ/rel $2345}", "f0fe"}, - // {aa, " beq $2345", "{BEQ/rel $2345}", "f0fe"}, - // {mm, " BEQ $2345", "{BEQ/rel $2345}", "f0fe"}, - {ss, " BEQ $2347", "{BEQ/rel $2347}", "f000"}, - // {aa, " beq $2347", "{BEQ/rel $2347}", "f000"}, - // {mm, " BEQ $2347", "{BEQ/rel $2347}", "f000"}, - {ss, " BEQ $2343", "{BEQ/rel $2343}", "f0fc"}, - // {aa, " beq $2343", "{BEQ/rel $2343}", "f0fc"}, - // {mm, " BEQ $2343", "{BEQ/rel $2343}", "f0fc"}, - {ss, " LDA $1234", "{LDA/abs $1234}", "ad3412"}, - // {aa, " lda $1234", "{LDA/abs $1234}", "ad3412"}, - // {mm, " LDA $1234", "{LDA/abs $1234}", "ad3412"}, - {ss, " LDA $1234,X", "{LDA/absX $1234}", "bd3412"}, - // {aa, " lda $1234,x", "{LDA/absX $1234}", "bd3412"}, - // {mm, " LDA $1234,X", "{LDA/absX $1234}", "bd3412"}, - {ss, " STA $1234,Y", "{STA/absY $1234}", "993412"}, - // {aa, " sta $1234,y", "{STA/absY $1234}", "993412"}, - // {mm, " STA $1234,Y", "{STA/absY $1234}", "993412"}, - {ss, " LDA $12", "{LDA/zp $0012}", "a512"}, - // {aa, " lda $12", "{LDA/zp $0012}", "a512"}, - // {mm, " LDA $12", "{LDA/zp $0012}", "a512"}, - {ss, " LDA $12,X", "{LDA/zpX $0012}", "b512"}, - // {aa, " lda $12,x", "{LDA/zpX $0012}", "b512"}, - // {mm, " LDA $12,X", "{LDA/zpX $0012}", "b512"}, - {ss, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, - // {aa, " ldx $12,y", "{LDX/zpY $0012}", "b612"}, - // {mm, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, - {ss, " LDA ($12),Y", "{LDA/indY $0012}", "b112"}, - // {aa, " lda ($12),y", "{LDA/indY $0012}", "b112"}, - // {mm, " LDA ($12),Y", "{LDA/indY $0012}", "b112"}, - {ss, " LDA ($12,X)", "{LDA/indX $0012}", "a112"}, - // {aa, " lda ($12,x)", "{LDA/indX $0012}", "a112"}, - // {mm, " LDA ($12,X)", "{LDA/indX $0012}", "a112"}, - {ss, ` .AS "ABC"`, "{data/b}", "414243"}, - {ss, ` .AT "ABC"`, "{data/b}", "4142c3"}, - {ss, ` .AS /ABC/`, "{data/b}", "414243"}, - {ss, ` .AT /ABC/`, "{data/b}", "4142c3"}, - {ss, ` .AS -"ABC"`, "{data/b}", "c1c2c3"}, - {ss, ` .AT -"ABC"`, "{data/b}", "c1c243"}, - {ss, ` .AS -dABCd`, "{data/b}", "c1c2c3"}, - {ss, ` .AT -dABCd`, "{data/b}", "c1c243"}, + {ra, " ROL A", "{ROL/a}", "2a"}, + {ra, "* Comment", "{-}", ""}, + {ra, "Label", "{- 'Label'}", ""}, + {ra, "Label:", "{- 'Label'}", ""}, {ra, ` ASC "ABC"`, "{data/b}", "c1c2c3"}, {ra, ` ASC $ABC$ ;comment`, "{data/b}", "c1c2c3"}, {ra, ` ASC $ABC`, "{data/b}", "c1c2c3"}, - {ra, ` DCI "ABC"`, "{data/b}", "4142c3"}, {ra, ` ASC -ABC-`, "{data/b}", "c1c2c3"}, - {ss, " .HS 0001ffAb", "{data/b}", "0001ffab"}, - {ss, "A.B .EQ *-C.D", "{= 'A.B' (- * C.D)}", ""}, + {ra, ` DCI "ABC"`, "{data/b}", "4142c3"}, + {ra, ` SBTL Title here`, "{-}", ""}, + {ra, ` TITLE Title here`, "{-}", ""}, + {rb, " ROL Comment after three spaces", "{ROL/a}", "2a"}, + {rb, " ROL X", "{ROL/a}", "2a"}, // two spaces = comment + {rb, " ROL", "{ROL/a}", "2a"}, + {ss, " far-out-comment", "{-}", ""}, {ss, " .BS $8", "{block $0008}", "xxxxxxxxxxxxxxxx"}, + {ss, " .DA $1234", "{data $1234}", "3412"}, + {ss, " .DA/$1234,#$1234,$1234", "{data (msb $1234),(lsb $1234),$1234}", "12343412"}, {ss, " .DO A<$3", "{if (< A $0003)}", ""}, {ss, " .ELSE", "{else}", ""}, - {ss, " .FIN", "{endif}", ""}, - {ss, " .MA MacroName", "{macro 'MacroName'}", ""}, {ss, " .EM", "{endm}", ""}, {ss, " .EN", "{end}", ""}, - {ss, `>SAM AB,$12,"A B","A, B, "" C"`, - `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""}, - {ss, " LDX #']+$80", "{LDX/imm (lsb (+ $005d $0080))}", "a2dd"}, - + {ss, " .FIN", "{endif}", ""}, + {ss, " .HS 0001FFAB", "{data/b}", "0001ffab"}, + {ss, " .IN FILE.NAME", "{inc 'FILE.NAME'}", ""}, + {ss, " .IN S.DEFS", "{inc 'S.DEFS'}", ""}, + {ss, " .MA MacroName", "{macro 'MacroName'}", ""}, + {ss, " .OR $D000", "{org $d000}", ""}, + {ss, " .TF OUT.BIN", "{-}", ""}, + {ss, " .TI 76,Title here", "{-}", ""}, + {ss, " BEQ $2343", "{BEQ/rel $2343}", "f0fc"}, + {ss, " BEQ $2345", "{BEQ/rel $2345}", "f0fe"}, + {ss, " BEQ $2347", "{BEQ/rel $2347}", "f000"}, {ss, " CMP #';'+1", "{CMP/imm (lsb (+ $003b $0001))}", "c93c"}, - {ra, " LST ON", "{set LST ON}", ""}, - {ra, " LST OFF", "{set LST OFF}", ""}, - {ra, " MSB ON", "{set MSB ON}", ""}, - {ra, " MSB OFF", "{set MSB OFF}", ""}, + {ss, " JMP $1234", "{JMP/abs $1234}", "4c3412"}, + {ss, " JMP ($1234)", "{JMP/ind $1234}", "6c3412"}, + {ss, " LDA #$12", "{LDA/imm (lsb $0012)}", "a912"}, + {ss, " LDA $12", "{LDA/zp $0012}", "a512"}, + {ss, " LDA $12,X", "{LDA/zpX $0012}", "b512"}, + {ss, " LDA $1234", "{LDA/abs $1234}", "ad3412"}, + {ss, " LDA $1234,X", "{LDA/absX $1234}", "bd3412"}, + {ss, " LDA ($12),Y", "{LDA/indY $0012}", "b112"}, + {ss, " LDA ($12,X)", "{LDA/indX $0012}", "a112"}, + {ss, " LDX #']+$80", "{LDX/imm (lsb (+ $005d $0080))}", "a2dd"}, + {ss, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, + {ss, " ROL Comment after two spaces", "{ROL/a}", "2a"}, + {ss, " ROL X", "{ROL/a}", "2a"}, // two spaces = comment + {ss, " ROL $12", "{ROL/zp $0012}", "2612"}, + {ss, " ROL $1234", "{ROL/abs $1234}", "2e3412"}, + {ss, " ROL", "{ROL/a}", "2a"}, + {ss, " STA $1234,Y", "{STA/absY $1234}", "993412"}, + {ss, "* Comment", "{-}", ""}, + {ss, "A.B .EQ *-C.D", "{= 'A.B' (- * C.D)}", ""}, + {ss, "Label", "{- 'Label'}", ""}, + {ss, ` .AS "ABC"`, "{data/b}", "414243"}, + {ss, ` .AS -"ABC"`, "{data/b}", "c1c2c3"}, + {ss, ` .AS -DABCD`, "{data/b}", "c1c2c3"}, + {ss, ` .AS /ABC/`, "{data/b}", "414243"}, + {ss, ` .AT "ABC"`, "{data/b}", "4142c3"}, + {ss, ` .AT -"ABC"`, "{data/b}", "c1c243"}, + {ss, ` .AT -DABCD`, "{data/b}", "c1c243"}, + {ss, ` .AT /ABC/`, "{data/b}", "4142c3"}, + {ss, `>SAM AB,$12,"A B","A, B, "" C"`, `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""}, } // TODO(zellyn): Add tests for finalization of four SCMA directives: @@ -165,6 +221,8 @@ func TestSimpleCommonFunctions(t *testing.T) { tt.a.SetAddr(0x2345) tt.a.Set("A.B", 0x6789) tt.a.Set("C.D", 0x789a) + tt.a.Set("L2", 0x6789) + tt.a.Set("L3", 0x789a) inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i)) if err != nil { @@ -176,7 +234,7 @@ func TestSimpleCommonFunctions(t *testing.T) { } _, err = inst.Compute(tt.a, true, true) if err != nil { - t.Errorf(`%d. %s.Compute(tt.a, true, true) => error: %s`, i, inst, err) + t.Errorf(`%d. %T.ParseInstr("%s"): %s.Compute(tt.a, true, true) => error: %s`, i, tt.a, tt.i, inst, err) continue } if inst.String() != tt.p { diff --git a/asm/inst/instruction.go b/asm/inst/instruction.go index 12fa622..8201374 100644 --- a/asm/inst/instruction.go +++ b/asm/inst/instruction.go @@ -43,8 +43,8 @@ const ( DataWordsLe // Data: expressions, but forced to one word per, little-endian DataWordsBe // Data: expressions, but forced to one word per, big-endian DataAscii // Data: from ASCII strings, high bit clear - DataAsciiHi // Data: from ASCII strings, high bit set DataAsciiFlip // Data: from ASCII strings, high bit clear, except last char + DataAsciiHi // Data: from ASCII strings, high bit set DataAsciiHiFlip // Data: from ASCII strings, high bit set, except last char ) diff --git a/asm/lines/lineparse.go b/asm/lines/lineparse.go index 7074b3c..e88e6d9 100644 --- a/asm/lines/lineparse.go +++ b/asm/lines/lineparse.go @@ -93,7 +93,7 @@ func (lp *Parse) AcceptRun(valid string) bool { } func (lp *Parse) AcceptUntil(until string) bool { - until += "\n" + until += string(Eol) some := false for strings.IndexRune(until, lp.Next()) < 0 { some = true