diff --git a/asm/expr/expression.go b/asm/expr/expression.go index 3c6d047..858d218 100644 --- a/asm/expr/expression.go +++ b/asm/expr/expression.go @@ -31,6 +31,9 @@ const ( OpGt // Greater than OpEq // Equal to OpByte // A single byte of storage + OpAnd + OpOr + OpXor ) var OpStrings = map[Operator]string{ @@ -44,6 +47,9 @@ var OpStrings = map[Operator]string{ OpLt: "<", OpGt: ">", OpEq: "=", + OpAnd: "&", + OpOr: "|", + OpXor: "^", } type E struct { @@ -62,7 +68,7 @@ func (e E) String() string { } return fmt.Sprintf("$%04x", e.Val) - case OpPlus, OpMinus, OpMul, OpDiv, OpLsb, OpMsb, OpByte, OpLt, OpGt, OpEq: + case OpPlus, OpMinus, OpMul, OpDiv, OpLsb, OpMsb, OpByte, OpLt, OpGt, OpEq, OpAnd, OpOr, OpXor: if e.Right != nil { return fmt.Sprintf("(%s %s %s)", OpStrings[e.Op], *e.Left, *e.Right) } @@ -142,7 +148,7 @@ func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) { return l & 0xff, nil case OpByte: return e.Val, nil - case OpPlus, OpMul, OpDiv, OpLt, OpGt, OpEq: + case OpPlus, OpMul, OpDiv, OpLt, OpGt, OpEq, OpAnd, OpOr, OpXor: l, err := e.Left.Eval(ctx, ln) if err != nil { return 0, err @@ -176,6 +182,12 @@ func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) { return ctx.Zero() } return l / r, nil + case OpAnd: + return l & r, nil + case OpOr: + return l | r, nil + case OpXor: + return l ^ r, nil } panic(fmt.Sprintf("bad code - missing switch case: %d", e.Op)) } diff --git a/asm/flavors/merlin/merlin.go b/asm/flavors/merlin/merlin.go index 2487748..8e1e4e6 100644 --- a/asm/flavors/merlin/merlin.go +++ b/asm/flavors/merlin/merlin.go @@ -19,6 +19,7 @@ type Merlin struct { } const whitespace = " \t" +const macroNameChars = oldschool.Letters + oldschool.Digits + "_" func New() *Merlin { m := &Merlin{} @@ -32,6 +33,9 @@ func New() *Merlin { m.MsbChars = ">/" m.ImmediateChars = "#" m.HexCommas = oldschool.ReqOptional + m.CharChars = "'" + m.InvCharChars = `"` + m.MacroArgSep = ";" m.Directives = map[string]oldschool.DirectiveInfo{ "ORG": {inst.TypeOrg, m.ParseAddress, 0}, @@ -47,9 +51,9 @@ func New() *Merlin { ".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}, + "MAC": {inst.TypeMacroStart, m.MarkMacroStart, 0}, + "EOM": {inst.TypeMacroEnd, m.ParseNoArgDir, 0}, + "<<<": {inst.TypeMacroEnd, m.ParseNoArgDir, 0}, "PAGE": {inst.TypeNone, nil, 0}, // New page "TTL": {inst.TypeNone, nil, 0}, // Title "SAV": {inst.TypeNone, nil, 0}, // Save @@ -65,6 +69,9 @@ func New() *Merlin { "<": expr.OpLt, ">": expr.OpGt, "=": expr.OpEq, + ".": expr.OpOr, + "&": expr.OpAnd, + "!": expr.OpXor, } m.SetOnOffDefaults(map[string]bool{ @@ -94,6 +101,55 @@ func New() *Merlin { } } + // ParseMacroCall parses a macro call. We expect in.Command to hold + // the "command column" value, which caused isMacroCall to return + // true, and the lp to be pointing to the following character + // (probably whitespace). + m.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) { + if in.Command == "" { + return in, false, nil + } + byName := m.HasMacroName(in.Command) + + // It's not a macro call. + if in.Command != ">>>" && in.Command != "PMC" && !byName { + return in, false, nil + } + + in.Type = inst.TypeMacroCall + lp.IgnoreRun(oldschool.Whitespace) + if !byName { + if !lp.AcceptRun(macroNameChars) { + c := lp.Next() + return in, true, in.Errorf("Expected macro name, got char '%c'", c) + } + in.Command = lp.Emit() + if !m.HasMacroName(in.Command) { + return in, true, in.Errorf("Unknown macro: '%s'", in.Command) + } + if !lp.Consume(" ./,-(") { + c := lp.Next() + if c == lines.Eol || c == ';' { + return in, true, nil + } + return in, true, in.Errorf("Expected macro name/args separator [ ./,-(], got '%c'", c) + + } + } + for { + s, err := m.ParseMacroArg(in, lp) + if err != nil { + return inst.I{}, true, err + } + in.MacroArgs = append(in.MacroArgs, s) + if !lp.Consume(";") { + break + } + } + + return in, true, nil + } + return m } diff --git a/asm/flavors/oldschool/oldschool.go b/asm/flavors/oldschool/oldschool.go index 7d27d5e..69d0b2a 100644 --- a/asm/flavors/oldschool/oldschool.go +++ b/asm/flavors/oldschool/oldschool.go @@ -22,7 +22,6 @@ const hexdigits = Digits + "abcdefABCDEF" const Whitespace = " \t" const cmdChars = Letters + Digits + ".>_" const fileChars = Letters + Digits + "." -const operatorChars = "+-*/<>=" type DirectiveInfo struct { Type inst.Type @@ -59,6 +58,10 @@ type Base struct { ExtraCommenty func(string) bool SetAsciiVariation func(*inst.I, *lines.Parse) ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error) + operatorChars string + CharChars string + InvCharChars string + MacroArgSep string } // Parse an entire instruction, or return an appropriate error. @@ -195,7 +198,7 @@ func (a *Base) ParseMacroArg(in inst.I, lp *lines.Parse) (string, error) { if lp.Peek() == '"' { return a.ParseQuoted(in, lp) } - lp.AcceptUntil(Whitespace + ",") + lp.AcceptUntil(Whitespace + a.MacroArgSep) return lp.Emit(), nil } @@ -480,6 +483,7 @@ func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) { return in, nil } +// For assemblers where the macro name follows the macro directive. func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) { lp.IgnoreRun(Whitespace) if !lp.AcceptRun(cmdChars) { @@ -493,6 +497,16 @@ func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) { return in, nil } +// For assemblers where the macro name is the label, followed by the directive. +func (a *Base) MarkMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) { + in.TextArg, in.Label = in.Label, "" + in.WidthKnown = true + in.MinWidth = 0 + in.MaxWidth = 0 + in.Final = true + return in, nil +} + func (a *Base) ParseNoArgDir(in inst.I, lp *lines.Parse) (inst.I, error) { in.WidthKnown = true in.MinWidth = 0 @@ -506,6 +520,13 @@ 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) { + + if a.operatorChars == "" { + for k, _ := range a.Operators { + a.operatorChars += k + } + } + var outer *expr.E if lp.AcceptRun(a.MsbChars + a.LsbChars + a.ImmediateChars) { pc := lp.Emit() @@ -539,7 +560,7 @@ func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) { return &expr.E{}, err } - for lp.Accept(operatorChars) { + for lp.Accept(a.operatorChars) { c := lp.Emit() right, err := a.ParseTerm(in, lp) if err != nil { @@ -587,7 +608,7 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) { return top, nil } - // Hex + // Binary if lp.Consume(string(a.BinaryChar)) { if !lp.AcceptRun(binarydigits) { c := lp.Next() @@ -616,14 +637,19 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) { } // Character - if lp.Consume("'") { + allChars := a.CharChars + a.InvCharChars + if allChars != "" && lp.Accept(allChars) { + quote := lp.Emit() c := lp.Next() if c == lines.Eol { return &expr.E{}, in.Errorf("end of line after quote") } ex.Op = expr.OpLeaf ex.Val = uint16(c) - lp.Consume("'") // optional closing quote + if strings.Contains(a.InvCharChars, quote[:1]) { + ex.Val |= 0x80 + } + lp.Consume(quote) // optional closing quote lp.Ignore() return top, nil } diff --git a/asm/flavors/scma/scma.go b/asm/flavors/scma/scma.go index 95d2dae..44226cb 100644 --- a/asm/flavors/scma/scma.go +++ b/asm/flavors/scma/scma.go @@ -26,6 +26,8 @@ func New() *SCMA { a.SpacesForComment = 2 a.MsbChars = "/" a.ImmediateChars = "#" + a.CharChars = "'" + a.MacroArgSep = "," a.Directives = map[string]oldschool.DirectiveInfo{ ".IN": {inst.TypeInclude, a.ParseInclude, 0}, diff --git a/asm/flavors/tests/simple_parse_test.go b/asm/flavors/tests/simple_parse_test.go index 05c2c1b..46d69e3 100644 --- a/asm/flavors/tests/simple_parse_test.go +++ b/asm/flavors/tests/simple_parse_test.go @@ -56,22 +56,32 @@ func TestSimpleCommonFunctions(t *testing.T) { {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, " DA $1234", "{data/wle $1234}", "3412"}, {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, " INCW $42;$43", `{call INCW {"$42", "$43"}}`, ""}, {mm, " JMP $1234", "{JMP/abs $1234}", "4c3412"}, {mm, " JMP ($1234)", "{JMP/ind $1234}", "6c3412"}, {mm, " LDA #$12", "{LDA/imm (lsb $0012)}", "a912"}, + {mm, " LDA #$1234", "{LDA/imm (lsb $1234)}", "a934"}, + {mm, " LDA #/$1234", "{LDA/imm (msb $1234)}", "a912"}, + {mm, " LDA #<$1234", "{LDA/imm (lsb $1234)}", "a934"}, + {mm, " LDA #>$1234", "{LDA/imm (msb $1234)}", "a912"}, + {mm, " LDA $12", "{LDA/zp $0012}", "a512"}, {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", "{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, " LDA: $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDA@ $12", "{LDA/zp $0012}", "ad1200"}, + {mm, " LDAX $12", "{LDA/zp $0012}", "ad1200"}, {mm, " LDX $12,Y", "{LDX/zpY $0012}", "b612"}, {mm, " ORG $D000", "{org $d000}", ""}, {mm, " PMC M1($42", `{call M1 {"$42"}}`, ""}, @@ -84,10 +94,19 @@ func TestSimpleCommonFunctions(t *testing.T) { {mm, " STA $1234,Y", "{STA/absY $1234}", "993412"}, {mm, "* Comment", "{-}", ""}, {mm, "ABC = $800", "{= 'ABC' $0800}", ""}, + {mm, "L1 = 'A.2", "{= 'L1' (| $0041 $0002)}", ""}, + {mm, "L1 = *-2", "{= 'L1' (- * $0002)}", ""}, + {mm, "L1 = 1234+%10111", "{= 'L1' (+ $04d2 $0017)}", ""}, + {mm, "L1 = 2*L2+$231", "{= 'L1' (+ (* $0002 L2) $0231)}", ""}, + {mm, "L1 = L2!'A'", "{= 'L1' (^ L2 $0041)}", ""}, + {mm, "L1 = L2&$7F", "{= 'L1' (& L2 $007f)}", ""}, + {mm, "L1 = L2-L3", "{= 'L1' (- L2 L3)}", ""}, + {mm, "L1 = L2.%10000000", "{= 'L1' (| L2 $0080)}", ""}, {mm, "Label ;Comment", "{- 'Label'}", ""}, {mm, "Label", "{- 'Label'}", ""}, {mm, "Label", "{- 'Label'}", ""}, {mm, "Label;Comment", "{- 'Label'}", ""}, + {mm, "MacroName MAC", `{macro "MacroName"}`, ""}, {mm, ` ASC !ABC!`, "{data/b}", "c1c2c3"}, {mm, ` ASC "ABC"`, "{data/b}", "c1c2c3"}, {mm, ` ASC #ABC#`, "{data/b}", "c1c2c3"}, @@ -111,27 +130,8 @@ func TestSimpleCommonFunctions(t *testing.T) { {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)}", ""}, - + {mm, `L1 = L2!"A"`, "{= 'L1' (^ L2 $00c1)}", ""}, {ra, " ; Comment", "{-}", ""}, {ra, " DDB $12,$34,$1234", "{data/wbe $0012,$0034,$1234}", "001200341234"}, {ra, " DFB $12", "{data/b $0012}", "12"}, @@ -223,6 +223,8 @@ func TestSimpleCommonFunctions(t *testing.T) { tt.a.Set("C.D", 0x789a) tt.a.Set("L2", 0x6789) tt.a.Set("L3", 0x789a) + tt.a.AddMacroName("INCW") + tt.a.AddMacroName("M1") inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i)) if err != nil {