diff --git a/internal/asm/asm.go b/internal/asm/asm.go index 097066f..b95225c 100644 --- a/internal/asm/asm.go +++ b/internal/asm/asm.go @@ -190,17 +190,22 @@ type Operation interface { // Generate encodes an operation as machine code. Using the values from Parse, the operation is // converted to one (or more) words. Generate(symbols SymbolTable, pc uint16) ([]uint16, error) - - // Source returns information about the source code location of an operation. - Source() SourceInfo } -// SourceInfo holds information on the source of an operation. +// SourceInfo wraps an operation to annotate it with parser metadata. type SourceInfo struct { Filename string Pos uint16 Line string + + Operation } -// Source returns a copy of the source information. -func (s SourceInfo) Source() SourceInfo { return s } +// Unwrap returns the operation which the source info wraps. +func (si *SourceInfo) Unwrap() Operation { + if si.Operation == nil { + return nil + } + + return si.Operation +} diff --git a/internal/asm/gen.go b/internal/asm/gen.go index c15c83e..0498386 100644 --- a/internal/asm/gen.go +++ b/internal/asm/gen.go @@ -48,32 +48,37 @@ func (gen *Generator) WriteTo(out io.Writer) (int64, error) { return 0, nil } - // Write the object-code header: the origin offset. The .ORIG directive should be the first - // operation in the syntax table. - if orig := origin(gen.syntax[0]); orig == nil { - return 0, errors.New("gen: .ORIG directive must be the first operation") - } else { + // Write the origin offset as the leader of the object file. The .ORIG directive should be the + // first operation in the syntax table. However, operations may be wrapped, so we unwrap to the + // base case, first. + if orig, ok := unwrap(gen.syntax[0]).(*ORIG); ok { gen.pc = orig.LITERAL + gen.log.Debug("Wrote object header", "ORIG", fmt.Sprintf("%0#4x", orig.LITERAL)) + } else { + return 0, fmt.Errorf(".ORIG should be first operation; was: %T", gen.syntax[0]) } for i, code := range gen.syntax { if code == nil { continue - } else if origin(code) != nil && i != 0 { - err = errors.New("gen: .ORIG directive may only be the first operation") + } else if _, ok := (unwrap(code)).(*ORIG); ok && i != 0 { + err = errors.New(".ORIG directive may only be the first operation") break } encoded, err = code.Generate(gen.symbols, gen.pc) if err != nil { - src := code.Source() - err = &SyntaxError{ - File: src.Filename, - Loc: gen.pc, - Pos: src.Pos, - Line: src.Line, - Err: err, + // If code generation caused an error, we try to get the source of the operation and + // covert annotate the error with the source code annotation. + if src, ok := code.(*SourceInfo); ok { + err = &SyntaxError{ + File: src.Filename, + Loc: gen.pc, + Pos: src.Pos, + Line: src.Line, + Err: err, + } } break @@ -94,10 +99,13 @@ func (gen *Generator) WriteTo(out io.Writer) (int64, error) { return count, nil } -func origin(op Operation) *ORIG { - if op, ok := op.(*ORIG); ok { - return op +// unwrap returns the base operation from possibly wrapped operation. +func unwrap(oper Operation) Operation { + for { + if wrap, ok := oper.(interface{ Unwrap() Operation }); ok { + oper = wrap.Unwrap() + } else { + return oper + } } - - return nil } diff --git a/internal/asm/gen_test.go b/internal/asm/gen_test.go index e866b81..527c126 100644 --- a/internal/asm/gen_test.go +++ b/internal/asm/gen_test.go @@ -89,6 +89,7 @@ func TestGenerator(tt *testing.T) { var buf bytes.Buffer syntax := make(SyntaxTable, 0) + syntax.Add(&ORIG{LITERAL: 0x3000}) syntax.Add(&NOT{DR: "R0", SR: "R7"}) syntax.Add(&AND{DR: "R3", SR1: "R4", SR2: "R6"}) @@ -97,7 +98,6 @@ func TestGenerator(tt *testing.T) { symbols.Add("LABEL", 0x2ff0) gen := NewGenerator(symbols, syntax) - count, err := gen.WriteTo(&buf) if err != nil { t.Error(err) diff --git a/internal/asm/ops.go b/internal/asm/ops.go index 48293e2..9818fb8 100644 --- a/internal/asm/ops.go +++ b/internal/asm/ops.go @@ -27,7 +27,6 @@ import ( // |------+-----+---------| // |15 12|11 9|8 0| type BR struct { - SourceInfo NZP uint8 SYMBOL string OFFSET uint16 @@ -68,10 +67,9 @@ func (br *BR) Parse(opcode string, opers []string) error { } *br = BR{ - SourceInfo: br.SourceInfo, - NZP: uint8(nzp), - SYMBOL: sym, - OFFSET: off, + NZP: uint8(nzp), + SYMBOL: sym, + OFFSET: off, } return nil @@ -108,7 +106,6 @@ func (br *BR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // |------+----+-----+---+----------| // |15 12|11 9|8 6| 5 |4 0| type AND struct { - SourceInfo DR string SR1 string SR2 string // Register mode. @@ -125,9 +122,8 @@ func (and *AND) Parse(oper string, opers []string) error { } *and = AND{ - SourceInfo: and.SourceInfo, - DR: parseRegister(opers[0]), - SR1: parseRegister(opers[1]), + DR: parseRegister(opers[0]), + SR1: parseRegister(opers[1]), } if sr2 := parseRegister(opers[2]); sr2 != "" { @@ -194,7 +190,6 @@ func (and *AND) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // |------+----+---------| // |15 12|11 9|8 0| type LD struct { - SourceInfo DR string OFFSET uint16 SYMBOL string @@ -212,8 +207,7 @@ func (ld *LD) Parse(opcode string, operands []string) error { } *ld = LD{ - SourceInfo: ld.SourceInfo, - DR: operands[0], + DR: operands[0], } ld.OFFSET, ld.SYMBOL, err = parseImmediate(operands[1], 9) @@ -258,7 +252,6 @@ func (ld LD) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type LDR struct { - SourceInfo DR string SR string OFFSET uint16 @@ -277,9 +270,8 @@ func (ldr *LDR) Parse(opcode string, operands []string) error { } *ldr = LDR{ - SourceInfo: ldr.SourceInfo, - DR: operands[0], - SR: operands[1], + DR: operands[0], + SR: operands[1], } ldr.OFFSET, ldr.SYMBOL, err = parseImmediate(operands[2], 6) @@ -328,7 +320,6 @@ func (ldr LDR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type LEA struct { - SourceInfo DR string SYMBOL string OFFSET uint16 @@ -346,8 +337,7 @@ func (lea *LEA) Parse(opcode string, operands []string) error { } *lea = LEA{ - SourceInfo: lea.SourceInfo, - DR: operands[0], + DR: operands[0], } lea.OFFSET, lea.SYMBOL, err = parseImmediate(operands[1], 9) @@ -393,7 +383,6 @@ func (lea LEA) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type LDI struct { - SourceInfo SR string SYMBOL string OFFSET uint16 @@ -411,8 +400,7 @@ func (ldi *LDI) Parse(opcode string, operands []string) error { } *ldi = LDI{ - SourceInfo: ldi.SourceInfo, - SR: operands[0], + SR: operands[0], } ldi.OFFSET, ldi.SYMBOL, err = parseImmediate(operands[1], 9) @@ -457,7 +445,6 @@ func (ldi LDI) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // |15 12|11 9|8 0| type ST struct { - SourceInfo SR string SYMBOL string OFFSET uint16 @@ -475,8 +462,7 @@ func (st *ST) Parse(opcode string, operands []string) error { } *st = ST{ - SourceInfo: st.SourceInfo, - SR: operands[0], + SR: operands[0], } st.OFFSET, st.SYMBOL, err = parseImmediate(operands[1], 9) @@ -522,7 +508,6 @@ func (st ST) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type STI struct { - SourceInfo SR string SYMBOL string OFFSET uint16 @@ -540,8 +525,7 @@ func (sti *STI) Parse(opcode string, operands []string) error { } *sti = STI{ - SourceInfo: sti.SourceInfo, - SR: operands[0], + SR: operands[0], } sti.OFFSET, sti.SYMBOL, err = parseImmediate(operands[1], 9) @@ -587,7 +571,6 @@ func (sti STI) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type STR struct { - SourceInfo SR1 string SR2 string SYMBOL string @@ -606,9 +589,8 @@ func (str *STR) Parse(opcode string, operands []string) error { } *str = STR{ - SourceInfo: str.SourceInfo, - SR1: operands[0], - SR2: operands[1], + SR1: operands[0], + SR2: operands[1], } str.OFFSET, str.SYMBOL, err = parseImmediate(operands[2], 6) @@ -656,7 +638,6 @@ func (str STR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type JMP struct { - SourceInfo SR string } @@ -670,8 +651,7 @@ func (jmp *JMP) Parse(opcode string, operands []string) error { } *jmp = JMP{ - SourceInfo: jmp.SourceInfo, - SR: operands[0], + SR: operands[0], } return nil @@ -698,9 +678,7 @@ func (jmp *JMP) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // |15 12|11 9|8 6|5 0| // // . -type RET struct { - SourceInfo -} +type RET struct{} func (ret *RET) String() string { return fmt.Sprintf("%#v", ret) } @@ -711,9 +689,7 @@ func (ret *RET) Parse(opcode string, operands []string) error { return errors.New("ret: operand error") } - *ret = RET{ - SourceInfo: ret.SourceInfo, - } + *ret = RET{} return nil } @@ -739,11 +715,10 @@ func (ret *RET) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type ADD struct { - SourceInfo // TODO: This might be cleaner as a decorator instead of embedded. - DR string - SR1 string - SR2 string // Not empty when register mode. - LITERAL uint16 // Literal value otherwise, immediate mode. + DR string + SR1 string + SR2 string // Not empty when register mode. + LITERAL uint16 // Literal value otherwise, immediate mode. } func (add ADD) String() string { return fmt.Sprintf("%#v", add) } @@ -759,9 +734,8 @@ func (add *ADD) Parse(opcode string, operands []string) error { sr1 := parseRegister(operands[1]) *add = ADD{ - SourceInfo: add.SourceInfo, - DR: dr, - SR1: sr1, + DR: dr, + SR1: sr1, } if sr2 := parseRegister(operands[2]); sr2 != "" { @@ -815,7 +789,6 @@ func (add ADD) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type TRAP struct { - SourceInfo LITERAL uint16 } @@ -834,8 +807,7 @@ func (trap *TRAP) Parse(opcode string, operands []string) error { } *trap = TRAP{ - SourceInfo: trap.SourceInfo, - LITERAL: lit, + LITERAL: lit, } return nil @@ -855,9 +827,7 @@ func (trap TRAP) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // |15 12|11 0| // // . -type RTI struct { - SourceInfo -} +type RTI struct{} func (rti RTI) String() string { return fmt.Sprintf("%#v", rti) } @@ -886,7 +856,6 @@ func (rti RTI) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type NOT struct { - SourceInfo DR string SR string } @@ -904,9 +873,8 @@ func (not *NOT) Parse(opcode string, operands []string) error { sr := parseRegister(operands[1]) *not = NOT{ - SourceInfo: not.SourceInfo, - DR: dr, - SR: sr, + DR: dr, + SR: sr, } return nil @@ -942,7 +910,6 @@ func (not *NOT) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type JSR struct { - SourceInfo SYMBOL string OFFSET uint16 } @@ -962,9 +929,8 @@ func (jsr *JSR) Parse(opcode string, operands []string) error { } *jsr = JSR{ - SourceInfo: jsr.SourceInfo, - OFFSET: off, - SYMBOL: sym, + OFFSET: off, + SYMBOL: sym, } return nil @@ -999,7 +965,6 @@ func (jsr *JSR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // . type JSRR struct { - SourceInfo SR string } @@ -1013,8 +978,7 @@ func (jsrr *JSRR) Parse(opcode string, operands []string) error { } *jsrr = JSRR{ - SourceInfo: jsrr.SourceInfo, - SR: operands[0], + SR: operands[0], } return nil @@ -1036,7 +1000,6 @@ func (jsrr *JSRR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // .FILL x1234 // .FILL 0 type FILL struct { - SourceInfo LITERAL uint16 // Literal constant. } @@ -1055,7 +1018,6 @@ func (fill *FILL) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // .BLKW 1 type BLKW struct { - SourceInfo ALLOC uint16 // Number of words allocated. } @@ -1075,10 +1037,19 @@ func (blkw *BLKW) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // .ORIG x1234 // .ORIG 0 type ORIG struct { - SourceInfo LITERAL uint16 // Literal constant. } +func (orig *ORIG) Is(target Operation) bool { + if _, ok := target.(*ORIG); ok { + return true + } else if target, ok := target.(interface{ Is(Operation) bool }); ok { + return target.Is(orig) + } + + return false +} + func (orig *ORIG) Parse(opcode string, operands []string) error { if len(operands) != 1 { return errors.New("argument error") @@ -1114,7 +1085,6 @@ func (orig *ORIG) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) { // // HELLO .STRINGZ "Hello, world!" type STRINGZ struct { - SourceInfo LITERAL string // Literal constant. } diff --git a/internal/asm/parser.go b/internal/asm/parser.go index 6403f08..c8859f1 100644 --- a/internal/asm/parser.go +++ b/internal/asm/parser.go @@ -227,56 +227,63 @@ func (p *Parser) parseInstruction(opcode string, operands []string) error { return fmt.Errorf("parse: %s: %w", opcode, err) } - p.syntax.Add(oper) + p.AddSyntax(oper) p.loc++ return nil } +// AddSyntax adds an operation to the syntax table. The operation is wrapped with source metadata in +// SourceInfo. +func (p *Parser) AddSyntax(oper Operation) { + op := &SourceInfo{ + Operation: oper, + Pos: p.pos, + Line: p.line, + Filename: p.filename, + } + + p.syntax.Add(op) +} + // parseOperator returns the operation for the given opcode or an error if there is no such // operation. func (p *Parser) parseOperator(opcode string) Operation { - source := SourceInfo{ - Filename: p.filename, - Pos: p.pos, - Line: p.line, - } - switch strings.ToUpper(opcode) { case "ADD": - return &ADD{SourceInfo: source} + return &ADD{} case "AND": - return &AND{SourceInfo: source} + return &AND{} case "BR", "BRNZP", "BRN", "BRZ", "BRP", "BRZN", "BRNP", "BRZP": - return &BR{SourceInfo: source} + return &BR{} case "JMP": - return &JMP{SourceInfo: source} + return &JMP{} case "RET": - return &RET{SourceInfo: source} + return &RET{} case "JSR": - return &JSR{SourceInfo: source} + return &JSR{} case "JSRR": - return &JSRR{SourceInfo: source} + return &JSRR{} case "NOT": - return &NOT{SourceInfo: source} + return &NOT{} case "LD": - return &LD{SourceInfo: source} + return &LD{} case "LDI": - return &LDI{SourceInfo: source} + return &LDI{} case "LDR": - return &LDR{SourceInfo: source} + return &LDR{} case "LEA": - return &LEA{SourceInfo: source} + return &LEA{} case "ST": - return &ST{SourceInfo: source} + return &ST{} case "STR": - return &STR{SourceInfo: source} + return &STR{} case "STI": - return &STI{SourceInfo: source} + return &STI{} case "TRAP": - return &TRAP{SourceInfo: source} + return &TRAP{} case "RTI": - return &RTI{SourceInfo: source} + return &RTI{} case p.probeOpcode: return p.probeInstr default: @@ -300,52 +307,46 @@ func (p *Parser) isReservedKeyword(word string) bool { func (p *Parser) parseDirective(ident string, arg string) error { var err error - source := SourceInfo{ - Filename: p.filename, - Pos: p.pos, - Line: p.line, - } - switch ident { case ".ORIG": - orig := ORIG{SourceInfo: source} + orig := ORIG{} err = orig.Parse(ident, []string{arg}) if err != nil { break } - p.syntax.Add(&orig) + p.AddSyntax(&orig) p.loc = orig.LITERAL case ".BLKW": - blkw := BLKW{SourceInfo: source} + blkw := BLKW{} err = blkw.Parse(ident, []string{arg}) if err != nil { break } - p.syntax.Add(&blkw) + p.AddSyntax(&blkw) p.loc += blkw.ALLOC case ".FILL", ".DW": - fill := FILL{SourceInfo: source} + fill := FILL{} err = fill.Parse(ident, []string{arg}) if err != nil { break } - p.syntax.Add(&fill) + p.AddSyntax(&fill) p.loc++ case ".STRINGZ": - strz := STRINGZ{SourceInfo: source} + strz := STRINGZ{} err = strz.ParseString(ident, arg) if err != nil { break } - p.syntax.Add(&strz) + p.AddSyntax(&strz) p.loc += uint16(len(strz.LITERAL) + 1) case ".END": // TODO: add to syntax table diff --git a/internal/asm/parser_test.go b/internal/asm/parser_test.go index d22e4dc..426721c 100644 --- a/internal/asm/parser_test.go +++ b/internal/asm/parser_test.go @@ -279,6 +279,12 @@ func TestParser_FILL(tt *testing.T) { code := syntax[1] + if source, ok := code.(*SourceInfo); ok { + code = source.Operation + } else { + t.Error("Source is not wrapped") + } + if fill, ok := code.(*FILL); !ok || fill.LITERAL != 0xdada { t.Errorf("data: 0x1234 %#v != %0#4x", code, 0xdada) } @@ -306,6 +312,12 @@ func TestParser_STRINGZ(tt *testing.T) { code := syntax[1] + if source, ok := code.(*SourceInfo); ok { + code = source.Operation + } else { + t.Error("code is not wrapped") + } + if fill, ok := code.(*STRINGZ); !ok || fill.LITERAL != want { t.Errorf("data: %#v != %0#4x", code, want) }