Skip to content

Commit

Permalink
Write multiple sections by encoding different data records
Browse files Browse the repository at this point in the history
  • Loading branch information
smoynes committed Nov 22, 2023
1 parent fc9942d commit a167b9e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 15 deletions.
25 changes: 15 additions & 10 deletions internal/asm/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func NewGenerator(symbols SymbolTable, syntax SyntaxTable) *Generator {
}

// Encode generates object code and encodes it as hex-encoded ASCII object code.
//
// Multiple sections are supported if the symbol table has multiple ORIG directives.
func (gen *Generator) Encode() ([]byte, error) {
if len(gen.syntax) == 0 {
return nil, nil
Expand All @@ -52,21 +54,22 @@ func (gen *Generator) Encode() ([]byte, error) {

// We expect the .ORIG directive to be the first operation in the syntax table. TODO: We should
// be able to support multiple origins if the encoder does.
if orig, ok := origin(gen.syntax[0]); ok {
gen.pc = orig.LITERAL
obj.Orig = vm.Word(orig.LITERAL)
} else {
if _, ok := origin(gen.syntax[0]); !ok {
return nil, fmt.Errorf(".ORIG should be first operation; was: %T", gen.syntax[0])
}

for i, op := range gen.syntax {
for _, op := range gen.syntax {
if op == nil {
continue
} else if _, ok := origin(op); i == 0 {
continue
} else if ok {
err = errors.New(".ORIG directive may only be the first operation")
break
} else if orig, ok := origin(op); ok {
if obj.Code != nil {
gen.encoding.Code = append(gen.encoding.Code, obj)
}

gen.pc = orig.LITERAL
obj = vm.ObjectCode{Orig: vm.Word(gen.pc)}

continue // We don't need to generate code.
}

genWords, genErr := op.Generate(gen.symbols, gen.pc+1)
Expand Down Expand Up @@ -96,6 +99,8 @@ func (gen *Generator) Encode() ([]byte, error) {
}

// WriteTo writes generated binary machine-code to an output stream. It implements io.WriteTo.
//
// Unlinke Encode, WriteTo does not support writing more than a single section of code.
func (gen *Generator) WriteTo(out io.Writer) (int64, error) {
if len(gen.syntax) == 0 {
return 0, nil
Expand Down
65 changes: 60 additions & 5 deletions internal/asm/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,12 +556,67 @@ func Test_CaseInsensitiveLabels(tt *testing.T) {
tcs := []generateCase{
{oper: &JSR{OFFSET: 0x00ff}, want: 0x48ff},
{oper: &JSR{OFFSET: 0xffff}, want: 0x4bff},
{oper: &JSR{SYMBOL: "label"}, want: 0x4fff},
{oper: &JSR{SYMBOL: "there"}, want: 0x49ff},
{oper: &JSR{SYMBOL: "back"}, want: 0x4800},
{oper: &JSR{SYMBOL: "wayback"}, wantErr: &OffsetRangeError{Offset: 0xf7ff}},
{oper: &JSR{SYMBOL: "overthere"}, wantErr: &OffsetRangeError{Offset: 0x0800}},
{oper: &JSR{SYMBOL: "lAbEl"}, want: 0x4fff},
{oper: &JSR{SYMBOL: "thErE"}, want: 0x49ff},
{oper: &JSR{SYMBOL: "bAck"}, want: 0x4800},
{oper: &JSR{SYMBOL: "wAybAck"}, wantErr: &OffsetRangeError{Offset: 0xf7ff}},
{oper: &JSR{SYMBOL: "ovErthEre"}, wantErr: &OffsetRangeError{Offset: 0x0800}},
}

t.Run(pc, symbols, tcs)
}

func TestORIG_Generate(tt *testing.T) {
t := generatorHarness{tt}

tcs := []generateCase{
{
oper: &ORIG{LITERAL: 0x3000},
wantCode: []vm.Word{0x3000},
},
{
oper: &ORIG{LITERAL: 0x0030},
wantCode: []vm.Word{0x0030},
},
}

pc := uint16(0x3000)
symbols := SymbolTable{}

for tc := range tcs {
op := tcs[tc].oper
wantCode := tcs[tc].wantCode

code, err := op.Generate(symbols, pc)
if err != nil {
t.Fatalf("unexpected error: %#v", err)
}

if code == nil {
t.Error("invalid machine code")
}

// Convert []uint16 to []byte...
codeBuffer := new(bytes.Buffer)
err = binary.Write(codeBuffer, binary.BigEndian, code)

if err != nil {
t.Error(err)
return
}

wantBytes := new(bytes.Buffer)
err = binary.Write(wantBytes, binary.BigEndian, wantCode)

if err != nil {
t.Error(err)
return
}

if bytes.Compare(codeBuffer.Bytes(), wantBytes.Bytes()) != 0 {
t.Error("code differs")
t.Errorf("%s", codeBuffer.Bytes())
t.Errorf("%s", wantBytes.Bytes())
}
}
}
4 changes: 4 additions & 0 deletions internal/asm/gold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ func TestAssembler_Gold(tt *testing.T) {
input: t.inputStream("parser7.asm"),
expectedHex: t.expectOutput("parser7.hex"),
},
{
input: t.inputStream("parser9.asm"),
expectedHex: t.expectOutput("parser9.hex"),
},
}

for i, tc := range tcs {
Expand Down
8 changes: 8 additions & 0 deletions internal/asm/testdata/parser9.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
;;; "Code" section.
.ORIG x3000
.DW x2364
.END
;;; Data section.
.ORIG x3100
.DW x2365
.END
3 changes: 3 additions & 0 deletions internal/asm/testdata/parser9.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:02300000236447
:0231000023658c
:00000001ff
1 change: 1 addition & 0 deletions internal/cli/cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func (ex *executor) Run(ctx context.Context, args []string, stdout io.Writer, lo
case err != nil:
logger.Error(err.Error())
cancel(err)

return
default:
cancel(context.Canceled)
Expand Down
6 changes: 6 additions & 0 deletions internal/encoding/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ func (h *HexEncoding) MarshalText() ([]byte, error) {
_ = buf.WriteByte(':')

l := len(code.Code)

if l*2 > 0xff {
panic("object code too long")
}

val[0] = byte(l * 2)

_, _ = hexEnc.Write(val[:1])
check += val[0]

Expand Down

0 comments on commit a167b9e

Please sign in to comment.