diff --git a/asm/asm.go b/asm/asm.go index 8fbcac3..35ada0f 100644 --- a/asm/asm.go +++ b/asm/asm.go @@ -71,6 +71,10 @@ func (a *Assembler) Load(filename string, prefix int) error { return parseErr } + if mode == flavors.ParseModeNormal && !in.WidthKnown { + return in.Errorf("Width unknown") + } + if _, err := a.passInst(&in, false); err != nil { return err } diff --git a/asm/flavors/common/common.go b/asm/flavors/common/common.go index a870561..3871777 100644 --- a/asm/flavors/common/common.go +++ b/asm/flavors/common/common.go @@ -32,6 +32,11 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.WidthKnown = true in.Width = 2 in.Mode = opcodes.MODE_INDIRECT_X + in.Var = inst.VarOpByte + if valKnown { + in.Final = true + in.Data = []byte{in.Op, byte(val)} + } return in, nil case 'y': op, ok := summary.OpForMode(opcodes.MODE_INDIRECT_Y) @@ -45,6 +50,11 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.Width = 2 in.Mode = opcodes.MODE_INDIRECT_Y in.Op = op.Byte + in.Var = inst.VarOpByte + if valKnown { + in.Final = true + in.Data = []byte{in.Op, byte(val)} + } return in, nil default: op, ok := summary.OpForMode(opcodes.MODE_INDIRECT) @@ -55,6 +65,11 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.WidthKnown = true in.Width = 3 in.Mode = opcodes.MODE_INDIRECT + in.Var = inst.VarOpWord + if valKnown { + in.Final = true + in.Data = []byte{in.Op, byte(val), byte(val >> 8)} + } return in, nil } } @@ -73,7 +88,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.WidthKnown = true in.Width = 2 in.Mode = opcodes.MODE_RELATIVE - in.Var = inst.VarRelative + in.Var = inst.VarOpBranch if valKnown { b, err := RelativeAddr(c, in, val) if err != nil { @@ -82,7 +97,6 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.Data = []byte{in.Op, b} in.Final = true } - return in, nil } @@ -96,8 +110,11 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect in.WidthKnown = true in.Width = 2 in.Mode = opcodes.MODE_IMMEDIATE - in.Var = inst.VarBytes - //xyzzy() + in.Var = inst.VarOpByte + if valKnown { + in.Data = []byte{in.Op, byte(val)} + in.Final = true + } return in, nil } diff --git a/asm/flavors/oldschool/oldschool.go b/asm/flavors/oldschool/oldschool.go index 155055e..ce46a30 100644 --- a/asm/flavors/oldschool/oldschool.go +++ b/asm/flavors/oldschool/oldschool.go @@ -101,6 +101,9 @@ func (a *Base) ParseInstr(ctx context.Context, line lines.Line, mode flavors.Par // Flavor considers this line extra commenty for some reason? if a.ExtraCommenty != nil && a.ExtraCommenty(lp.Rest()) { in.Type = inst.TypeNone + in.Final = true + in.WidthKnown = true + in.Width = 0 return in, nil } @@ -108,6 +111,9 @@ func (a *Base) ParseInstr(ctx context.Context, line lines.Line, mode flavors.Par trimmed := strings.TrimSpace(lp.Rest()) if trimmed == "" || trimmed[0] == '*' || rune(trimmed[0]) == a.CommentChar { in.Type = inst.TypeNone + in.Final = true + in.WidthKnown = true + in.Width = 0 return in, nil } @@ -136,6 +142,9 @@ func (a *Base) ParseInstr(ctx context.Context, line lines.Line, mode flavors.Par return in, err } } + in.Final = true + in.WidthKnown = true + in.Width = 0 return in, nil } return a.parseCmd(ctx, in, lp, mode) @@ -179,9 +188,7 @@ func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode fl if mode == flavors.ParseModeMacroSave { // all we care about is labels (already covered) and end-of-macro. if dir, ok := a.Directives[in.Command]; ok { - if dir.Type == inst.TypeMacroEnd { - in.Type = inst.TypeMacroEnd - } + in.Type = dir.Type } return in, nil } @@ -189,8 +196,8 @@ func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode fl if mode == flavors.ParseModeInactive { // all we care about are endif and else. if dir, ok := a.Directives[in.Command]; ok { + in.Type = dir.Type if dir.Type == inst.TypeIfdefElse || dir.Type == inst.TypeIfdefEnd { - in.Type = dir.Type // It's weird, but handle labels on else/endif lines. if err := a.handleLabel(ctx, in); err != nil { return in, err @@ -214,6 +221,9 @@ func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode fl return in, err } if isMacro { + i.WidthKnown = true + i.Width = 0 + i.Final = true return i, nil } } @@ -222,6 +232,9 @@ func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode fl in.Type = dir.Type in.Var = dir.Var if dir.Func == nil { + in.WidthKnown = true + in.Width = 0 + in.Final = true return in, nil } return dir.Func(ctx, in, lp) @@ -265,6 +278,9 @@ func (a *Base) parseSetting(ctx context.Context, in inst.I, lp *lines.Parse) (in default: return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg) } + in.WidthKnown = true + in.Width = 0 + in.Final = true return in, nil } @@ -466,6 +482,9 @@ func (a *Base) ParseAscii(ctx context.Context, in inst.I, lp *lines.Parse) (inst in.Data[i] ^= invertLast } } + in.Width = uint16(len(in.Data)) + in.WidthKnown = true + in.Final = true return in, nil } @@ -476,6 +495,14 @@ func (a *Base) ParseBlockStorage(ctx context.Context, in inst.I, lp *lines.Parse return in, err } in.Exprs = append(in.Exprs, ex) + val, err := ex.Eval(ctx, in.Line) + if err != nil { + return in, in.Errorf("Cannot evaluate size of block storage on first pass") + } + + in.WidthKnown = true + in.Final = true + in.Width = val return in, nil } @@ -491,6 +518,60 @@ func (a *Base) ParseData(ctx context.Context, in inst.I, lp *lines.Parse) (inst. break } } + switch in.Var { + case inst.VarBytes: + in.WidthKnown = true + in.Width = uint16(len(in.Exprs)) + in.Final = true + for _, expr := range in.Exprs { + val, err := expr.Eval(ctx, in.Line) + if err != nil { + in.Final = false + in.Data = nil + } + in.Data = append(in.Data, byte(val)) + } + case inst.VarWordsLe, inst.VarWordsBe: + in.WidthKnown = true + in.Width = 2 * uint16(len(in.Exprs)) + in.Final = true + for _, expr := range in.Exprs { + val, err := expr.Eval(ctx, in.Line) + if err != nil { + in.Final = false + in.Data = nil + } + if in.Var == inst.VarWordsLe { + in.Data = append(in.Data, byte(val), byte(val>>8)) + } else { + in.Data = append(in.Data, byte(val>>8), byte(val)) + } + } + case inst.VarMixed: + in.WidthKnown = true + in.Final = true + for _, expr := range in.Exprs { + in.Width += expr.Width() + val, err := expr.Eval(ctx, in.Line) + if err != nil { + in.Final = false + in.Data = nil + } else { + if in.Final { + switch expr.Width() { + case 1: + in.Data = append(in.Data, byte(val)) + case 2: + in.Data = append(in.Data, byte(val), byte(val>>8)) + default: + return in, in.Errorf("Unsupported expression width: %d", expr.Width()) + } + } + } + } + default: + return in, in.Errorf("Unknown Var(%d) with ParseData for %s", in.Var, in.Command) + } return in, nil } @@ -548,6 +629,9 @@ func (a *Base) ParseHexString(ctx context.Context, in inst.I, lp *lines.Parse) ( break } } + in.WidthKnown = true + in.Width = uint16(len(in.Data)) + in.Final = true return in, nil } diff --git a/asm/flavors/tests/simple_parse_test.go b/asm/flavors/tests/simple_parse_test.go index 3c1197c..5b122fe 100644 --- a/asm/flavors/tests/simple_parse_test.go +++ b/asm/flavors/tests/simple_parse_test.go @@ -239,6 +239,10 @@ func TestSimpleCommonFunctions(t *testing.T) { t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.f, tt.i, err) continue } + if !in.WidthKnown { + t.Errorf(`%d. %s.ParseInstr("%s") => WidthKnown=false`, i, tt.f, tt.i) + continue + } if in.Line.Parse == nil { t.Errorf("Got empty in.Line.Parse on input '%s'", tt.i) } diff --git a/asm/inst/instruction.go b/asm/inst/instruction.go index 7f8f5ad..28933f6 100644 --- a/asm/inst/instruction.go +++ b/asm/inst/instruction.go @@ -53,6 +53,9 @@ const ( VarRelative // For branches: a one-byte relative address VarEquNormal // Equ: a normal equate VarEquPageZero // Equ: a page-zero equate + VarOpByte // An op with a one-byte argument + VarOpWord // An op with a one-word argument + VarOpBranch // An op with a one-byte relative address argument ) type I struct {