aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2018-01-09 01:29:38 +0100
committerDimitri Sokolyuk <demon@dim13.org>2018-01-09 01:29:38 +0100
commitf8a2141c0776a28c6a7107849102154f9a959f48 (patch)
tree49c6743aeadea35218d5dfa7353b381baa3a9569
parent15eda60829ae0b4b084fe93bf419bf014a82a21c (diff)
guard stack and memory
-rw-r--r--eval.go66
-rw-r--r--eval_test.go82
-rw-r--r--stack.go36
3 files changed, 109 insertions, 75 deletions
diff --git a/eval.go b/eval.go
index 072c976..06b350f 100644
--- a/eval.go
+++ b/eval.go
@@ -8,15 +8,18 @@ import (
"io/ioutil"
)
+const (
+ memSize = 0x4000
+ stackSize = 0x20
+)
+
// J1 Forth processor VM
type J1 struct {
- pc uint16 // 13 bit
- st0 uint16 // top of data stack
- dsp int8 // 5 bit data stack pointer
- rsp int8 // 5 bit retrun stack pointer
- dstack [0x20]uint16 // data stack
- rstack [0x20]uint16 // return stack
- memory [0x4000]uint16 // 0..0x3fff main memory, 0x4000 .. 0x7fff mem-mapped i/o
+ pc uint16 // 13 bit
+ st0 uint16 // top of data stack
+ d stack
+ r stack
+ memory [memSize]uint16 // 0..0x3fff main memory, 0x4000 .. 0x7fff mem-mapped i/o
console io.ReadWriter
}
@@ -26,13 +29,13 @@ func New() *J1 {
// Reset VM
func (j1 *J1) Reset() {
- j1.pc, j1.st0, j1.dsp, j1.rsp = 0, 0, 0, 0
+ j1.pc, j1.st0, j1.d.sp, j1.r.sp = 0, 0, 0, 0
}
// LoadBytes into memory
func (j1 *J1) LoadBytes(data []byte) error {
size := len(data) >> 1
- if size > len(j1.memory) {
+ if size >= memSize {
return fmt.Errorf("too big")
}
return binary.Read(bytes.NewReader(data), binary.LittleEndian, j1.memory[:size])
@@ -61,36 +64,38 @@ func (j1 *J1) Eval() {
func (j1 *J1) String() string {
s := fmt.Sprintf("\tPC=%0.4X ST=%0.4X\n", j1.pc, j1.st0)
- s += fmt.Sprintf("\tD=%0.4X\n", j1.dstack[:j1.dsp+1])
- s += fmt.Sprintf("\tR=%0.4X\n", j1.rstack[:j1.rsp+1])
+ s += fmt.Sprintf("\tD=%0.4X\n", j1.d.dump())
+ s += fmt.Sprintf("\tR=%0.4X\n", j1.r.dump())
return s
}
func (j1 *J1) push(v uint16) {
- j1.dsp++
- j1.dstack[j1.dsp] = j1.st0
+ j1.d.push(j1.st0)
j1.st0 = v
}
func (j1 *J1) pop() uint16 {
v := j1.st0
- j1.st0 = j1.dstack[j1.dsp]
- j1.dsp--
+ j1.st0 = j1.d.pop()
return v
}
func (j1 *J1) write(addr, value uint16) {
+ if off := int(addr >> 1); off < memSize {
+ j1.memory[addr>>1] = value
+ }
switch addr {
case 0xf000: // key
fmt.Fprintf(j1.console, "%c", value)
case 0xf002: // bye
j1.Reset()
- default:
- j1.memory[addr>>1] = value
}
}
func (j1 *J1) read(addr uint16) uint16 {
+ if off := int(addr >> 1); off < memSize {
+ return j1.memory[off]
+ }
switch addr {
case 0xf000: // tx!
var b uint16
@@ -99,7 +104,7 @@ func (j1 *J1) read(addr uint16) uint16 {
case 0xf001: // ?rx
return 1
default:
- return j1.memory[addr>>1]
+ return 0
}
}
@@ -111,8 +116,7 @@ func (j1 *J1) eval(ins Instruction) {
case Jump:
j1.pc = v.Value()
case Call:
- j1.rsp++
- j1.rstack[j1.rsp] = j1.pc << 1
+ j1.r.push(j1.pc << 1)
j1.pc = v.Value()
case Cond:
if j1.pop() == 0 {
@@ -120,19 +124,19 @@ func (j1 *J1) eval(ins Instruction) {
}
case ALU:
if v.RtoPC {
- j1.pc = j1.rstack[j1.rsp] >> 1
+ j1.pc = j1.r.peek() >> 1
}
if v.NtoAtT {
- j1.write(j1.st0, j1.dstack[j1.dsp])
+ j1.write(j1.st0, j1.d.peek())
}
st0 := j1.newST0(v.Opcode)
- j1.dsp += v.Ddir
- j1.rsp += v.Rdir
+ j1.d.move(v.Ddir)
+ j1.r.move(v.Rdir)
if v.TtoN {
- j1.dstack[j1.dsp] = j1.st0
+ j1.d.set(j1.st0)
}
if v.TtoR {
- j1.rstack[j1.rsp] = j1.st0
+ j1.r.set(j1.st0)
}
j1.st0 = st0
}
@@ -146,13 +150,7 @@ func bool2int(b bool) uint16 {
}
func (j1 *J1) newST0(opcode uint16) uint16 {
- if j1.dsp < 0 {
- j1.dsp = 0
- }
- if j1.rsp < 0 {
- j1.rsp = 0
- }
- T, N, R := j1.st0, j1.dstack[j1.dsp], j1.rstack[j1.rsp]
+ T, N, R := j1.st0, j1.d.peek(), j1.r.peek()
switch opcode {
case opT: // T
return T
@@ -183,7 +181,7 @@ func (j1 *J1) newST0(opcode uint16) uint16 {
case opNlshiftT: // N<<T
return N << (T & 0xf)
case opDepth: // depth (dsp)
- return (uint16(j1.rsp) << 8) | uint16(j1.dsp)
+ return (j1.r.depth() << 8) | j1.d.depth()
case opNuleT: // Nu<T
return bool2int(N < T)
default:
diff --git a/eval_test.go b/eval_test.go
index 1b46179..60d3e1b 100644
--- a/eval_test.go
+++ b/eval_test.go
@@ -13,17 +13,17 @@ func cmp(t *testing.T, got, want J1) {
if got.st0 != want.st0 {
t.Errorf("st0: got %0.4X, want %0.4X", got.st0, want.st0)
}
- if got.dsp != want.dsp {
- t.Errorf("dsp: got %0.4X, want %0.4X", got.dsp, want.dsp)
+ if got.d.sp != want.d.sp {
+ t.Errorf("dsp: got %0.4X, want %0.4X", got.d.sp, want.d.sp)
}
- if got.rsp != want.rsp {
- t.Errorf("rsp: got %0.4X, want %0.4X", got.rsp, want.rsp)
+ if got.r.sp != want.r.sp {
+ t.Errorf("rsp: got %0.4X, want %0.4X", got.r.sp, want.r.sp)
}
- if got.dstack != want.dstack {
- t.Errorf("dstack: got %0.4X, want %0.4X", got.dstack, want.dstack)
+ if got.d.data != want.d.data {
+ t.Errorf("d:stack: got %0.4X, want %0.4X", got.d.data, want.d.data)
}
- if got.rstack != want.rstack {
- t.Errorf("rstack: got %0.4X, want %0.4X", got.rstack, want.rstack)
+ if got.r.data != want.r.data {
+ t.Errorf("rstack: got %0.4X, want %0.4X", got.r.data, want.r.data)
}
}
@@ -46,59 +46,59 @@ func TestEval(t *testing.T) {
},
{
ins: []Instruction{Call(0xff)},
- end: J1{pc: 0xff, rstack: [0x20]uint16{0x00, 0x02}, rsp: 1},
+ end: J1{pc: 0xff, r: stack{data: [0x20]uint16{0x00, 0x02}, sp: 1}},
},
{
ins: []Instruction{Lit(0xff)},
- end: J1{pc: 1, st0: 0xff, dsp: 1},
+ end: J1{pc: 1, st0: 0xff, d: stack{sp: 1}},
},
{
ins: []Instruction{Lit(0xff), Lit(0xfe)},
- end: J1{pc: 2, st0: 0xfe, dstack: [0x20]uint16{0x00, 0x00, 0xff}, dsp: 2},
+ end: J1{pc: 2, st0: 0xfe, d: stack{data: [0x20]uint16{0x00, 0x00, 0xff}, sp: 2}},
},
{ // dup
ins: []Instruction{Lit(0xff), ALU{Opcode: opT, TtoN: true, Ddir: 1}},
- end: J1{pc: 2, st0: 0xff, dstack: [0x20]uint16{0x00, 0x00, 0xff}, dsp: 2},
+ end: J1{pc: 2, st0: 0xff, d: stack{data: [0x20]uint16{0x00, 0x00, 0xff}, sp: 2}},
},
{ // over
ins: []Instruction{Lit(0xaa), Lit(0xbb), ALU{Opcode: opN, TtoN: true, Ddir: 1}},
- end: J1{pc: 3, st0: 0xaa, dstack: [0x20]uint16{0x00, 0x00, 0xaa, 0xbb}, dsp: 3},
+ end: J1{pc: 3, st0: 0xaa, d: stack{data: [0x20]uint16{0x00, 0x00, 0xaa, 0xbb}, sp: 3}},
},
{ // invert
ins: []Instruction{Lit(0x00ff), ALU{Opcode: opNotT}},
- end: J1{pc: 2, st0: 0xff00, dsp: 1},
+ end: J1{pc: 2, st0: 0xff00, d: stack{sp: 1}},
},
{ // +
ins: []Instruction{Lit(1), Lit(2), ALU{Opcode: opTplusN, Ddir: -1}},
- end: J1{pc: 3, st0: 3, dsp: 1, dstack: [0x20]uint16{0, 0, 1}},
+ end: J1{pc: 3, st0: 3, d: stack{data: [0x20]uint16{0, 0, 1}, sp: 1}},
},
{ // swap
ins: []Instruction{Lit(2), Lit(3), ALU{Opcode: opN, TtoN: true}},
- end: J1{pc: 3, st0: 2, dsp: 2, dstack: [0x20]uint16{0, 0, 3}},
+ end: J1{pc: 3, st0: 2, d: stack{data: [0x20]uint16{0, 0, 3}, sp: 2}},
},
{ // nip
ins: []Instruction{Lit(2), Lit(3), ALU{Opcode: opT, Ddir: -1}},
- end: J1{pc: 3, st0: 3, dsp: 1, dstack: [0x20]uint16{0, 0, 2}},
+ end: J1{pc: 3, st0: 3, d: stack{data: [0x20]uint16{0, 0, 2}, sp: 1}},
},
{ // drop
ins: []Instruction{Lit(2), Lit(3), ALU{Opcode: opN, Ddir: -1}},
- end: J1{pc: 3, st0: 2, dsp: 1, dstack: [0x20]uint16{0, 0, 2}},
+ end: J1{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: J1{pc: 11, rsp: 1, rstack: [0x20]uint16{0, 2, 22}},
+ end: J1{pc: 11, r: stack{data: [0x20]uint16{0, 2, 22}, sp: 1}},
},
{ // >r
ins: []Instruction{Lit(10), ALU{Opcode: opN, TtoR: true, Ddir: -1, Rdir: 1}},
- end: J1{pc: 2, rsp: 1, rstack: [0x20]uint16{0, 10}},
+ end: J1{pc: 2, r: stack{data: [0x20]uint16{0, 10}, sp: 1}},
},
{ // r>
ins: []Instruction{Lit(10), Call(20), ALU{Opcode: opR, TtoN: true, TtoR: true, Ddir: 1, Rdir: -1}},
- end: J1{pc: 21, st0: 4, dsp: 2, dstack: [0x20]uint16{0, 0, 10}, rsp: 0, rstack: [0x20]uint16{10, 4}},
+ end: J1{pc: 21, st0: 4, d: stack{data: [0x20]uint16{0, 0, 10}, sp: 2}, r: stack{data: [0x20]uint16{10, 4}}},
},
{ // r@
ins: []Instruction{Lit(10), ALU{Opcode: opR, TtoN: true, TtoR: true, Ddir: 1}},
- end: J1{pc: 2, dsp: 2, dstack: [0x20]uint16{0, 0, 10}, rstack: [0x20]uint16{10}},
+ end: J1{pc: 2, d: stack{data: [0x20]uint16{0, 0, 10}, sp: 2}, r: stack{data: [0x20]uint16{10}}},
},
{ // @
ins: []Instruction{ALU{Opcode: opAtT}},
@@ -106,7 +106,7 @@ func TestEval(t *testing.T) {
},
{ // !
ins: []Instruction{Lit(1), Lit(0), ALU{Opcode: opN, NtoAtT: true, Ddir: -1}},
- end: J1{pc: 3, st0: 1, dsp: 1, dstack: [0x20]uint16{0, 0, 1}, memory: [0x4000]uint16{1}},
+ end: J1{pc: 3, st0: 1, d: stack{data: [0x20]uint16{0, 0, 1}, sp: 1}, memory: [0x4000]uint16{1}},
},
}
@@ -128,24 +128,24 @@ func TestNextST0(t *testing.T) {
state J1
}{
{ins: ALU{Opcode: opT}, st0: 0xff, state: J1{st0: 0xff}},
- {ins: ALU{Opcode: opN}, st0: 0xbb, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opTplusN}, st0: 0x01ba, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opTandN}, st0: 0xbb, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opTorN}, st0: 0xff, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opTxorN}, st0: 0x44, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
+ {ins: ALU{Opcode: opN}, st0: 0xbb, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opTplusN}, st0: 0x01ba, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opTandN}, st0: 0xbb, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opTorN}, st0: 0xff, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opTxorN}, st0: 0x44, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
{ins: ALU{Opcode: opNotT}, st0: 0xff55, state: J1{st0: 0xaa}},
- {ins: ALU{Opcode: opNeqT}, st0: 0x00, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opNeqT}, st0: 0xffff, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xff}, dsp: 2}},
- {ins: ALU{Opcode: opNleT}, st0: 0xffff, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opNleT}, st0: 0x00, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xff}, dsp: 2}},
- {ins: ALU{Opcode: opNrshiftT}, st0: 0x3f, state: J1{st0: 0x02, dstack: [0x20]uint16{0, 0xaa, 0xff}, dsp: 2}},
+ {ins: ALU{Opcode: opNeqT}, st0: 0x00, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opNeqT}, st0: 0xffff, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
+ {ins: ALU{Opcode: opNleT}, st0: 0xffff, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opNleT}, st0: 0x00, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
+ {ins: ALU{Opcode: opNrshiftT}, st0: 0x3f, state: J1{st0: 0x02, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
{ins: ALU{Opcode: opTminus1}, st0: 0x54, state: J1{st0: 0x55}},
- {ins: ALU{Opcode: opR}, st0: 0x5, state: J1{rstack: [0x20]uint16{0, 0x05}, rsp: 1}},
+ {ins: ALU{Opcode: opR}, st0: 0x5, state: J1{r: stack{data: [0x20]uint16{0, 0x05}, sp: 1}}},
{ins: ALU{Opcode: opAtT}, st0: 0x5, state: J1{st0: 0x02, memory: [0x4000]uint16{0, 5, 10}}},
- {ins: ALU{Opcode: opNlshiftT}, st0: 0x3fc, state: J1{st0: 0x02, dstack: [0x20]uint16{0, 0xaa, 0xff}, dsp: 2}},
- {ins: ALU{Opcode: opDepth}, st0: 0x305, state: J1{rsp: 3, dsp: 5}},
- {ins: ALU{Opcode: opNuleT}, st0: 0xffff, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xbb}, dsp: 2}},
- {ins: ALU{Opcode: opNuleT}, st0: 0x00, state: J1{st0: 0xff, dstack: [0x20]uint16{0, 0xaa, 0xff}, dsp: 2}},
+ {ins: ALU{Opcode: opNlshiftT}, st0: 0x3fc, state: J1{st0: 0x02, d: stack{data: [0x20]uint16{0, 0xaa, 0xff}, sp: 2}}},
+ {ins: ALU{Opcode: opDepth}, st0: 0x305, state: J1{r: stack{sp: 3}, d: stack{sp: 5}}},
+ {ins: ALU{Opcode: opNuleT}, st0: 0xffff, state: J1{st0: 0xff, d: stack{data: [0x20]uint16{0, 0xaa, 0xbb}, sp: 2}}},
+ {ins: ALU{Opcode: opNuleT}, st0: 0x00, state: J1{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) {
@@ -170,10 +170,10 @@ func TestLoadBytes(t *testing.T) {
}
}
-func TestRest(t *testing.T) {
- j1 := &J1{pc: 100, dsp: 2, rsp: 3, st0: 5}
+func TestReset(t *testing.T) {
+ j1 := &J1{pc: 100, d: stack{sp: 2}, r: stack{sp: 3}, st0: 5}
j1.Reset()
- if j1.pc != 0 || j1.dsp != 0 || j1.rsp != 0 || j1.st0 != 0 {
+ if j1.pc != 0 || j1.d.sp != 0 || j1.r.sp != 0 || j1.st0 != 0 {
t.Errorf("got %v", j1)
}
}
diff --git a/stack.go b/stack.go
new file mode 100644
index 0000000..c192878
--- /dev/null
+++ b/stack.go
@@ -0,0 +1,36 @@
+package j1
+
+type stack struct {
+ data [stackSize]uint16 // stack
+ sp int8 // 5 bit stack pointer
+}
+
+func (s *stack) move(dir int8) {
+ s.sp = (s.sp + dir + stackSize) % stackSize
+}
+
+func (s *stack) push(v uint16) {
+ s.move(1)
+ s.set(v)
+}
+
+func (s *stack) pop() uint16 {
+ defer s.move(-1)
+ return s.peek()
+}
+
+func (s *stack) peek() uint16 {
+ return s.data[s.sp]
+}
+
+func (s *stack) set(v uint16) {
+ s.data[s.sp] = v
+}
+
+func (s *stack) depth() uint16 {
+ return uint16(s.sp)
+}
+
+func (s *stack) dump() []uint16 {
+ return s.data[:s.sp+1]
+}