aboutsummaryrefslogtreecommitdiff
path: root/core_test.go
blob: 22c4fdb0111cecea986283245ce85a9c18732e1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package j1

import (
	"fmt"
	"testing"
)

func cmp(t *testing.T, got, want Core) {
	t.Helper()
	if got.pc != want.pc {
		t.Errorf("pc: got %0.4X, want %0.4X", got.pc, want.pc)
	}
	if got.st0 != want.st0 {
		t.Errorf("st0: got %0.4X, want %0.4X", got.st0, want.st0)
	}
	if got.d.sp != want.d.sp {
		t.Errorf("dsp: got %0.4X, want %0.4X", got.d.sp, want.d.sp)
	}
	if got.r.sp != want.r.sp {
		t.Errorf("rsp: got %0.4X, want %0.4X", got.r.sp, want.r.sp)
	}
	if got.d.data != want.d.data {
		t.Errorf("dstack: got %0.4X, want %0.4X", got.d.data, want.d.data)
	}
	if got.r.data != want.r.data {
		t.Errorf("rstack: got %0.4X, want %0.4X", got.r.data, want.r.data)
	}
}

type mocConsole struct{}

func (m *mocConsole) Read() uint16 { return 0 }
func (m *mocConsole) Write(uint16) {}
func (m *mocConsole) Len() uint16  { return 0 }

func TestEval(t *testing.T) {
	testCases := []struct {
		ins []Instruction
		end Core
	}{
		{
			ins: []Instruction{Jump(0xff)},
			end: Core{pc: 0xff},
		},
		{
			ins: []Instruction{Literal(1), Conditional(0xff)},
			end: Core{pc: 2},
		},
		{
			ins: []Instruction{Literal(0), Conditional(0xff)},
			end: Core{pc: 0xff},
		},
		{
			ins: []Instruction{Call(0xff)},
			end: Core{pc: 0xff, r: stack{data: [0x20]uint16{0x00, 0x02}, sp: 1}},
		},
		{
			ins: []Instruction{Literal(0xff)},
			end: Core{pc: 1, st0: 0xff, d: stack{sp: 1}},
		},
		{
			ins: []Instruction{Literal(0xff), Literal(0xfe)},
			end: Core{pc: 2, st0: 0xfe, d: stack{data: [0x20]uint16{0x00, 0x00, 0xff}, sp: 2}},
		},
		{ // dup
			ins: []Instruction{Literal(0xff), ALU{Opcode: opT, TtoN: true, Ddir: 1}},
			end: Core{pc: 2, st0: 0xff, d: stack{data: [0x20]uint16{0x00, 0x00, 0xff}, sp: 2}},
		},
		{ // over
			ins: []Instruction{Literal(0xaa), Literal(0xbb), ALU{Opcode: opN, TtoN: true, Ddir: 1}},
			end: Core{pc: 3, st0: 0xaa, d: stack{data: [0x20]uint16{0x00, 0x00, 0xaa, 0xbb}, sp: 3}},
		},
		{ // invert
			ins: []Instruction{Literal(0x00ff), ALU{Opcode: opNotT}},
			end: Core{pc: 2, st0: 0xff00, d: stack{sp: 1}},
		},
		{ // +
			ins: []Instruction{Literal(1), Literal(2), ALU{Opcode: opTplusN, Ddir: -1}},
			end: Core{pc: 3, st0: 3, d: stack{data: [0x20]uint16{0, 0, 1}, sp: 1}},
		},
		{ // swap
			ins: []Instruction{Literal(2), Literal(3), ALU{Opcode: opN, TtoN: true}},
			end: Core{pc: 3, st0: 2, d: stack{data: [0x20]uint16{0, 0, 3}, sp: 2}},
		},
		{ // nip
			ins: []Instruction{Literal(2), Literal(3), ALU{Opcode: opT, Ddir: -1}},
			end: Core{pc: 3, st0: 3, d: stack{data: [0x20]uint16{0, 0, 2}, sp: 1}},
		},
		{ // drop
			ins: []Instruction{Literal(2), Literal(3), ALU{Opcode: opN, Ddir: -1}},
			end: Core{pc: 3, st0: 2, d: stack{data: [0x20]uint16{0, 0, 2}, sp: 1}},
		},
		{ // ;
			ins: []Instruction{Call(10), Call(20), ALU{Opcode: opT, RtoPC: true, Rdir: -1}},
			end: Core{pc: 11, r: stack{data: [0x20]uint16{0, 2, 22}, sp: 1}},
		},
		{ // >r
			ins: []Instruction{Literal(10), ALU{Opcode: opN, TtoR: true, Ddir: -1, Rdir: 1}},
			end: Core{pc: 2, r: stack{data: [0x20]uint16{0, 10}, sp: 1}},
		},
		{ // r>
			ins: []Instruction{Literal(10), Call(20), ALU{Opcode: opR, TtoN: true, TtoR: true, Ddir: 1, Rdir: -1}},
			end: Core{pc: 21, st0: 4, d: stack{data: [0x20]uint16{0, 0, 10}, sp: 2}, r: stack{data: [0x20]uint16{10, 4}}},
		},
		{ // r@
			ins: []Instruction{Literal(10), ALU{Opcode: opR, TtoN: true, TtoR: true, Ddir: 1}},
			end: Core{pc: 2, d: stack{data: [0x20]uint16{0, 0, 10}, sp: 2}, r: stack{data: [0x20]uint16{10}}},
		},
		{ // @
			ins: []Instruction{ALU{Opcode: opAtT}},
			end: Core{pc: 1},
		},
		{ // !
			ins: []Instruction{Literal(1), Literal(0), ALU{Opcode: opN, NtoAtT: true, Ddir: -1}},
			end: Core{pc: 3, st0: 1, d: stack{data: [0x20]uint16{0, 0, 1}, sp: 1}, memory: [0x2000]uint16{1}},
		},
	}

	for _, tc := range testCases {
		t.Run(fmt.Sprint(tc.ins), func(t *testing.T) {
			state := New(&mocConsole{})
			for _, ins := range tc.ins {
				state.Evaluate(ins)
			}
			cmp(t, *state, tc.end)
		})
	}
}

func TestNextST0(t *testing.T) {
	testCases := []struct {
		ins   ALU
		st0   uint16
		state Core
	}{
		{ins: ALU{Opcode: opT}, st0: 0xff, state: Core{st0: 0xff}},
		{ins: ALU{Opcode: opN}, st0: 0xbb, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opTplusN}, st0: 0x01ba, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opTandN}, st0: 0xbb, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opTorN}, st0: 0xff, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opTxorN}, st0: 0x44, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opNotT}, st0: 0xff55, state: Core{st0: 0xaa}},
		{ins: ALU{Opcode: opNeqT}, st0: 0x00, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opNeqT}, st0: 0xffff, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
		{ins: ALU{Opcode: opNleT}, st0: 0xffff, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opNleT}, st0: 0x00, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
		{ins: ALU{Opcode: opNrshiftT}, st0: 0x3f, state: Core{st0: 0x02, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
		{ins: ALU{Opcode: opTminus1}, st0: 0x54, state: Core{st0: 0x55}},
		{ins: ALU{Opcode: opR}, st0: 0x5, state: Core{r: stack{data: [0x20]uint16{0, 0x05}, sp: 1}}},
		{ins: ALU{Opcode: opAtT}, st0: 0x5, state: Core{st0: 0x02, memory: [0x2000]uint16{0, 5, 10}}},
		{ins: ALU{Opcode: opNlshiftT}, st0: 0x3fc, state: Core{st0: 0x02, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
		{ins: ALU{Opcode: opDepth}, st0: 0x305, state: Core{r: stack{sp: 3}, d: stack{sp: 5}}},
		{ins: ALU{Opcode: opNuleT}, st0: 0xffff, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
		{ins: ALU{Opcode: opNuleT}, st0: 0x00, state: Core{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
	}
	for _, tc := range testCases {
		t.Run(fmt.Sprint(tc.ins), func(t *testing.T) {
			state := &tc.state
			st0 := state.newST0(tc.ins.Opcode)
			if st0 != tc.st0 {
				t.Errorf("got %x, want %x", st0, tc.st0)
			}
		})
	}
}

func TestLoadBytes(t *testing.T) {
	data := []byte{1, 2, 4, 8}
	j1 := New(&mocConsole{})
	if err := j1.LoadBytes(data); err != nil {
		t.Fatal(err)
	}
	expect := [0x2000]uint16{0x0201, 0x0804}
	if j1.memory != expect {
		t.Errorf("got %v, want %v", j1.memory[:2], expect)
	}
}

func TestReset(t *testing.T) {
	j1 := &Core{pc: 100, d: stack{sp: 2}, r: stack{sp: 3}, st0: 5}
	j1.Reset()
	if j1.pc != 0 || j1.d.sp != 0 || j1.r.sp != 0 || j1.st0 != 0 {
		t.Errorf("got %v", j1)
	}
}