most merlin tests passing

This commit is contained in:
Zellyn Hunter 2014-06-18 21:57:17 -07:00
parent 006f18e51d
commit a31ee8b1d1
5 changed files with 130 additions and 32 deletions

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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},

View File

@ -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 {