Skip to content

Commit

Permalink
Add tests, fix bugs, as ever, and ever, and ever...
Browse files Browse the repository at this point in the history
  • Loading branch information
smoynes committed Oct 9, 2023
1 parent 9e094e8 commit 28f528e
Show file tree
Hide file tree
Showing 3 changed files with 320 additions and 4 deletions.
75 changes: 75 additions & 0 deletions internal/asm/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ func TestLDR_Generate(tt *testing.T) {
t := generatorHarness{tt}
tcs := []generateCase{
{oper: &LDR{DR: "R0", SR: "R5", OFFSET: 0x10}, want: 0x6150, wantErr: nil},
{oper: &LDR{DR: "R7", SR: "R7", OFFSET: 0xffff}, want: 0x6fff, wantErr: nil},
{oper: &LDR{DR: "R7", SR: "R4", SYMBOL: "LABEL"}, want: 0x6f05, wantErr: nil},
{oper: &LDR{DR: "R5", SR: "R1", SYMBOL: "BACK"}, want: 0x6a40, wantErr: nil},
{oper: &LDR{DR: "R3", SR: "R2", SYMBOL: "GONE"}, want: 0, wantErr: &SymbolError{0x3000, "GONE"}},
Expand All @@ -189,6 +190,8 @@ func TestLDR_Generate(tt *testing.T) {
func TestLD_Generate(tt *testing.T) {
t := generatorHarness{tt}
tcs := []generateCase{
{oper: &LD{DR: "R0", OFFSET: 0x2f}, want: 0x202f},
{oper: &LD{DR: "R0", OFFSET: 0xffff}, want: 0x20ff},
{oper: &LD{DR: "R0", OFFSET: 0x10}, want: 0x2010, wantErr: nil},
{oper: &LD{DR: "R7", SYMBOL: "LABEL"}, want: 0x2e05, wantErr: nil},
}
Expand Down Expand Up @@ -226,6 +229,78 @@ func TestLEA_Generate(tt *testing.T) {
t.Run(pc, symbols, tcs)
}

func TestST_Generate(tt *testing.T) {
pc := uint16(0x3000)
symbols := SymbolTable{
"LABEL": 0x2fff,
"THERE": 0x3080,
"WAYBACK": 0x2c00,
"OVERTHERE": 0x3200,
}

t := generatorHarness{tt}
tcs := []generateCase{
{oper: &ST{SR: "R0", OFFSET: 0x1ff}, want: 0x31ff},
{oper: &ST{SR: "R0", OFFSET: 0x00}, want: 0x3000},
{oper: &ST{SR: "R0", OFFSET: 0xffff}, want: 0x31ff},
{oper: &ST{SR: "R0", OFFSET: 0x10}, want: 0x3010, wantErr: nil},
{oper: &ST{SR: "R7", SYMBOL: "LABEL"}, want: 0x3fff, wantErr: nil},
{oper: &ST{SR: "R2", SYMBOL: "THERE"}, want: 0x3480},
{oper: &ST{SR: "R3", SYMBOL: "WAYBACK"}, wantErr: &OffsetError{0xfc00}},
{oper: &ST{SR: "R4", SYMBOL: "OVERTHERE"}, wantErr: &OffsetError{0x0200}},
{oper: &ST{SR: "R5", SYMBOL: "DNE"}, wantErr: &SymbolError{Loc: 0x3000, Symbol: "DNE"}},
}

t.Run(pc, symbols, tcs)
}

func TestSTI_Generate(tt *testing.T) {
pc := uint16(0x3000)
symbols := SymbolTable{
"LABEL": 0x2fff,
"THERE": 0x3080,
"WAYBACK": 0x2c00,
"OVERTHERE": 0x3200,
}

t := generatorHarness{tt}
tcs := []generateCase{
{oper: &STI{SR: "R0", OFFSET: 0x10}, want: 0xb010, wantErr: nil},
{oper: &STI{SR: "R7", SYMBOL: "LABEL"}, want: 0xbfff, wantErr: nil},
{oper: &STI{SR: "R2", SYMBOL: "THERE"}, want: 0xb480},
{oper: &STI{SR: "R3", SYMBOL: "WAYBACK"}, wantErr: &OffsetError{0xfc00}},
{oper: &STI{SR: "R4", SYMBOL: "OVERTHERE"}, wantErr: &OffsetError{0x0200}},
{oper: &STI{SR: "R5", SYMBOL: "DNE"}, wantErr: &SymbolError{Loc: 0x3000, Symbol: "DNE"}},
}

t.Run(pc, symbols, tcs)
}

func TestSTR_Generate(tt *testing.T) {
pc := uint16(0x3000)
symbols := SymbolTable{
"LABEL": 0x2fff, // -1
"THERE": 0x301f, // 64
"BACK": 0x2fe0, // -64
"WAYBACK": 0x2fd0,
"OVERTHERE": 0x3040,
}

t := generatorHarness{tt}
tcs := []generateCase{
{oper: &STR{SR1: "R0", SR2: "R1", OFFSET: 0x2f}, want: 0x706f},
{oper: &STR{SR1: "R0", SR2: "R0", OFFSET: 0x00}, want: 0x7000},
{oper: &STR{SR1: "R0", SR2: "R1", OFFSET: 0xffff}, want: 0x707f},
{oper: &STR{SR1: "R7", SR2: "R2", SYMBOL: "LABEL"}, want: 0x7e9f},
{oper: &STR{SR1: "R2", SR2: "R3", SYMBOL: "THERE"}, want: 0x74df},
{oper: &STR{SR1: "R3", SR2: "R4", SYMBOL: "WAYBACK"}, wantErr: &OffsetError{0xffd0}},
{oper: &STR{SR1: "R4", SR2: "R5", SYMBOL: "OVERTHERE"}, wantErr: &OffsetError{0x0040}},
{oper: &STR{SR1: "R5", SR2: "R6", SYMBOL: "DNE"}, wantErr: &SymbolError{Loc: 0x3000, Symbol: "DNE"}},
}

t.Run(pc, symbols, tcs)
}

func TestADD_Generate(tt *testing.T) {
t := generatorHarness{tt}
tcs := []generateCase{
Expand Down
143 changes: 139 additions & 4 deletions internal/asm/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type BR struct {
OFFSET uint16
}

func (br BR) String() string { return fmt.Sprintf("BR(%#v)", br) }
func (br BR) String() string { return fmt.Sprintf("%#v", br) }

// Parse parses all variations of the BR* instruction based on the opcode.
func (br *BR) Parse(opcode string, opers []string) error {
Expand Down Expand Up @@ -116,7 +116,7 @@ type AND struct {
OFFSET uint16 // Otherwise.
}

func (and AND) String() string { return fmt.Sprintf("AND(%#v)", and) }
func (and AND) String() string { return fmt.Sprintf("%#v", and) }

// Parse parses an AND instruction from its opcode and operands.
func (and *AND) Parse(oper string, opers []string) error {
Expand Down Expand Up @@ -200,7 +200,7 @@ type LD struct {
SYMBOL string
}

func (ld LD) String() string { return fmt.Sprintf("LD(%#v)", ld) }
func (ld LD) String() string { return fmt.Sprintf("%#v", ld) }

func (ld *LD) Parse(opcode string, operands []string) error {
var err error
Expand Down Expand Up @@ -265,7 +265,7 @@ type LDR struct {
SYMBOL string
}

func (ldr LDR) String() string { return fmt.Sprintf("LDR(%#v)", ldr) }
func (ldr LDR) String() string { return fmt.Sprintf("%#v", ldr) }

func (ldr *LDR) Parse(opcode string, operands []string) error {
var err error
Expand Down Expand Up @@ -446,6 +446,141 @@ func (st ST) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) {
return []uint16{code.Encode()}, nil
}

// STI: Store Indirect.
//
// STI SR,LABEL
// STI SR,#OFFSET9
//
// | 1011 | SR | OFFSET9 |
// |------+-----+---------|
// |15 12|11 9|8 0|
//
// .
type STI struct {
SourceInfo
SR string
SYMBOL string
OFFSET uint16
}

func (sti STI) String() string { return fmt.Sprintf("%#v", sti) }

func (sti *STI) Parse(opcode string, operands []string) error {
var err error

if opcode != "STI" {
return errors.New("sti: opcode error")
} else if len(operands) != 2 {
return errors.New("sti: operand error")
}

*sti = STI{
SourceInfo: sti.SourceInfo,
SR: operands[0],
}

sti.OFFSET, sti.SYMBOL, err = parseImmediate(operands[1], 9)
if err != nil {
return fmt.Errorf("sti: operand error: %w", err)
}

return nil
}

func (sti STI) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) {
dr := registerVal(sti.SR)

if dr == badGPR {
return nil, &RegisterError{"sti", sti.SR}
}

code := vm.NewInstruction(vm.STI, dr<<9)

switch {
case sti.SYMBOL != "":
offset, err := symbols.Offset(sti.SYMBOL, pc, 9)
if err != nil {
return nil, fmt.Errorf("sti: %w", err)
}

code.Operand(offset)
default:
code.Operand(sti.OFFSET & 0x01ff)
}

return []uint16{code.Encode()}, nil
}

// STR: Store Relative.
//
// STR SR1,SR2,LABEL
// STR SR1,SR2,#OFFSET6
//
// | 0111 | SR1 | SR2 | OFFSET6 |
// |------+-----+-----+---------|
// |15 12|11 9|8 6|5 0|
//
// .
type STR struct {
SourceInfo
SR1 string
SR2 string
SYMBOL string
OFFSET uint16
}

func (str STR) String() string { return fmt.Sprintf("%#v", str) }

func (str *STR) Parse(opcode string, operands []string) error {
var err error

if opcode != "STR" {
return errors.New("str: opcode error")
} else if len(operands) != 3 {
return errors.New("str: operand error")
}

*str = STR{
SourceInfo: str.SourceInfo,
SR1: operands[0],
SR2: operands[1],
}

str.OFFSET, str.SYMBOL, err = parseImmediate(operands[2], 6)
if err != nil {
return fmt.Errorf("str: operand error: %w", err)
}

return nil
}

func (str STR) Generate(symbols SymbolTable, pc uint16) ([]uint16, error) {
sr1 := registerVal(str.SR1)
sr2 := registerVal(str.SR2)

if sr1 == badGPR {
return nil, &RegisterError{"str", str.SR1}
} else if sr2 == badGPR {
return nil, &RegisterError{"str", str.SR2}
}

code := vm.NewInstruction(vm.STR, sr1<<9|sr2<<6)

switch {
case str.SYMBOL != "":
offset, err := symbols.Offset(str.SYMBOL, pc, 5)
if err != nil {
return nil, fmt.Errorf("str: %w", err)
}

code.Operand(offset)
default:
code.Operand(str.OFFSET & 0x003f)
}

return []uint16{code.Encode()}, nil
}

// ADD: Arithmetic addition operator.
//
// ADD DR,SR1,SR2
Expand Down
106 changes: 106 additions & 0 deletions internal/asm/ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,112 @@ func TestST_Parse(t *testing.T) {
}
}

func TestSTI_Parse(t *testing.T) {
tests := []parserCase{
{
name: "bad oper",
opcode: "OP", operands: []string{"IDENT"},
want: nil,
wantErr: &SyntaxError{},
},
{
name: "STI label",
opcode: "STI", operands: []string{"SR", "LABEL"},
want: &STI{SR: "SR", OFFSET: 0, SYMBOL: "LABEL"},
wantErr: nil,
},
{
name: "STI literal",
opcode: "STI", operands: []string{"SR", "#-1"},
want: &STI{SR: "SR", OFFSET: 0x01ff},
wantErr: nil,
},
{
name: "STI literal too large",
opcode: "STI", operands: []string{"SR", "#x0200"},
want: &STI{SR: "SR", OFFSET: 0x00},
wantErr: &SyntaxError{},
},
{
name: "STI literal too negative",
opcode: "STI", operands: []string{"SR", "#xff00"},
want: &STI{SR: "SR", OFFSET: 0x3f},
wantErr: &SyntaxError{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := &STI{}
err := got.Parse(tt.opcode, tt.operands)

if (tt.wantErr != nil && err == nil) || err != nil && tt.wantErr == nil {
t.Fatalf("not expected: %#v, want: %#v", err, tt.wantErr)

t.Errorf("STI.Parse() error = %v, wantErr %v", err, tt.wantErr)
return
}

if (err == nil) && !reflect.DeepEqual(got, tt.want) {
t.Errorf("STI.Parse() = %#v, want %#v", got, tt.want)
}
})
}
}

func TestSTR_Parse(t *testing.T) {
tests := []parserCase{
{
name: "bad oper",
opcode: "OP", operands: []string{"IDENT"},
want: nil,
wantErr: &SyntaxError{},
},
{
name: "STR label",
opcode: "STR", operands: []string{"SR", "SR2", "LABEL"},
want: &STR{SR1: "SR", SR2: "SR2", OFFSET: 0, SYMBOL: "LABEL"},
wantErr: nil,
},
{
name: "STR literal",
opcode: "STR", operands: []string{"SR", "SR2", "#-1"},
want: &STR{SR1: "SR", SR2: "SR2", OFFSET: 0x03f},
wantErr: nil,
},
{
name: "STR literal too large",
opcode: "STR", operands: []string{"SR", "SR2", "#x0200"},
want: &STR{SR1: "SR", SR2: "SR2", OFFSET: 0x00},
wantErr: &SyntaxError{},
},
{
name: "STR literal too negative",
opcode: "STR", operands: []string{"SR", "SR2", "#xff00"},
want: &STR{SR1: "SR", SR2: "SR2", OFFSET: 0x3f},
wantErr: &SyntaxError{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := &STR{}
err := got.Parse(tt.opcode, tt.operands)

if (tt.wantErr != nil && err == nil) || err != nil && tt.wantErr == nil {
t.Fatalf("not expected: %#v, want: %#v", err, tt.wantErr)

t.Errorf("STR.Parse() error = %v, wantErr %v", err, tt.wantErr)
return
}

if (err == nil) && !reflect.DeepEqual(got, tt.want) {
t.Errorf("STR.Parse() = %#v, want %#v", got, tt.want)
}
})
}
}

func TestADD_Parse(t *testing.T) {
tcs := []parserCase{
{
Expand Down

0 comments on commit 28f528e

Please sign in to comment.