mirror of
https://github.com/zellyn/go6502.git
synced 2025-02-11 12:32:07 +00:00
most merlin tests passing
This commit is contained in:
parent
006f18e51d
commit
a31ee8b1d1
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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},
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user