From 3705ec154f2700d1b3c60ce56626cc520fb3e068 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Mon, 15 Jan 2018 00:32:37 +0100 Subject: rename --- core.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ core_test.go | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ eval.go | 187 ----------------------------------------------------------- eval_test.go | 179 -------------------------------------------------------- 4 files changed, 363 insertions(+), 366 deletions(-) create mode 100644 core.go create mode 100644 core_test.go delete mode 100644 eval.go delete mode 100644 eval_test.go diff --git a/core.go b/core.go new file mode 100644 index 0000000..5ffe7cb --- /dev/null +++ b/core.go @@ -0,0 +1,184 @@ +package j1 + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "io/ioutil" +) + +const memSize = 0x4000 + +type Console interface { + Read() uint16 + Write(uint16) + Len() uint16 +} + +// Core of J1 Forth CPU +type Core struct { + memory [memSize]uint16 // 0..0x3fff main memory, 0x4000 .. 0x7fff mem-mapped i/o + pc uint16 // 13 bit + st0 uint16 // top of data stack + d, r stack // data and return stacks + tty Console // console i/o + stop context.CancelFunc +} + +func New() *Core { + return new(Core) +} + +// Reset VM +func (c *Core) Reset() { + c.pc, c.st0, c.d.sp, c.r.sp = 0, 0, 0, 0 +} + +// LoadBytes into memory +func (c *Core) LoadBytes(data []byte) error { + size := len(data) >> 1 + if size >= memSize { + return fmt.Errorf("too big") + } + return binary.Read(bytes.NewReader(data), binary.LittleEndian, c.memory[:size]) +} + +// LoadFile into memory +func (c *Core) LoadFile(fname string) error { + data, err := ioutil.ReadFile(fname) + if err != nil { + return err + } + return c.LoadBytes(data) +} + +// Run evaluates content of memory +func (c *Core) Run() { + ctx, cancel := context.WithCancel(context.Background()) + c.tty = NewConsole(ctx) + c.stop = cancel + for { + select { + case <-ctx.Done(): + return + default: + c.Eval(Decode(c.memory[c.pc])) + } + } +} + +func (c *Core) String() string { + s := fmt.Sprintf("\tPC=%0.4X ST=%0.4X\n", c.pc, c.st0) + s += fmt.Sprintf("\tD=%0.4X\n", c.d.dump()) + s += fmt.Sprintf("\tR=%0.4X\n", c.r.dump()) + return s +} + +func (c *Core) writeAt(addr, value uint16) { + if off := int(addr >> 1); off < memSize { + c.memory[addr>>1] = value + } + switch addr { + case 0xf000: // key + c.tty.Write(value) + case 0xf002: // bye + c.stop() + } +} + +func (c *Core) readAt(addr uint16) uint16 { + if off := int(addr >> 1); off < memSize { + return c.memory[off] + } + switch addr { + case 0xf000: // tx! + return c.tty.Read() + case 0xf001: // ?rx + return c.tty.Len() + } + return 0 +} + +func (c *Core) Eval(ins Instruction) { + c.pc++ + switch v := ins.(type) { + case Lit: + c.d.push(c.st0) + c.st0 = v.Value() + case Jump: + c.pc = v.Value() + case Call: + c.r.push(c.pc << 1) + c.pc = v.Value() + case Cond: + if c.st0 == 0 { + c.pc = v.Value() + } + c.st0 = c.d.pop() + case ALU: + if v.RtoPC { + c.pc = c.r.get() >> 1 + } + if v.NtoAtT { + c.writeAt(c.st0, c.d.get()) + } + st0 := c.newST0(v.Opcode) + c.d.move(v.Ddir) + c.r.move(v.Rdir) + if v.TtoN { + c.d.set(c.st0) + } + if v.TtoR { + c.r.set(c.st0) + } + c.st0 = st0 + } +} + +func (c *Core) newST0(opcode uint16) uint16 { + T, N, R := c.st0, c.d.get(), c.r.get() + switch opcode { + case opT: // T + return T + case opN: // N + return N + case opTplusN: // T+N + return T + N + case opTandN: // T&N + return T & N + case opTorN: // T|N + return T | N + case opTxorN: // T^N + return T ^ N + case opNotT: // ~T + return ^T + case opNeqT: // N==T + return bool2int(N == T) + case opNleT: // N>T + return N >> (T & 0xf) + case opTminus1: // T-1 + return T - 1 + case opR: // R (rT) + return R + case opAtT: // [T] + return c.readAt(T) + case opNlshiftT: // N<r + ins: []Instruction{Lit(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{Lit(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{Lit(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{Lit(1), Lit(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: [0x4000]uint16{1}}, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprint(tc.ins), func(t *testing.T) { + state := New() + for _, ins := range tc.ins { + state.Eval(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: [0x4000]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() + if err := j1.LoadBytes(data); err != nil { + t.Fatal(err) + } + expect := [0x4000]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) + } +} diff --git a/eval.go b/eval.go deleted file mode 100644 index c879d7a..0000000 --- a/eval.go +++ /dev/null @@ -1,187 +0,0 @@ -package j1 - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "io/ioutil" -) - -const memSize = 0x4000 - -type Console interface { - Read() uint16 - Write(uint16) - Len() uint16 -} - -// J1 Forth processor VM -type J1 struct { - memory [memSize]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 - console Console - stop context.CancelFunc -} - -func New() *J1 { - return new(J1) -} - -// Reset VM -func (j1 *J1) Reset() { - 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 >= memSize { - return fmt.Errorf("too big") - } - return binary.Read(bytes.NewReader(data), binary.LittleEndian, j1.memory[:size]) -} - -// LoadFile into memory -func (j1 *J1) LoadFile(fname string) error { - data, err := ioutil.ReadFile(fname) - if err != nil { - return err - } - return j1.LoadBytes(data) -} - -// Run evaluates content of memory -func (j1 *J1) Run() { - ctx, cancel := context.WithCancel(context.Background()) - j1.console = NewConsole(ctx) - j1.stop = cancel - for { - select { - case <-ctx.Done(): - return - default: - ins := Decode(j1.memory[j1.pc]) - //fmt.Printf("%v\n%v", ins, j1) - j1.Eval(ins) - } - } -} - -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.d.dump()) - s += fmt.Sprintf("\tR=%0.4X\n", j1.r.dump()) - return s -} - -func (j1 *J1) writeAt(addr, value uint16) { - if off := int(addr >> 1); off < memSize { - j1.memory[addr>>1] = value - } - switch addr { - case 0xf000: // key - j1.console.Write(value) - case 0xf002: // bye - j1.stop() - } -} - -func (j1 *J1) readAt(addr uint16) uint16 { - if off := int(addr >> 1); off < memSize { - return j1.memory[off] - } - switch addr { - case 0xf000: // tx! - return j1.console.Read() - case 0xf001: // ?rx - return j1.console.Len() - } - return 0 -} - -func (j1 *J1) Eval(ins Instruction) { - j1.pc++ - switch v := ins.(type) { - case Lit: - j1.d.push(j1.st0) - j1.st0 = v.Value() - case Jump: - j1.pc = v.Value() - case Call: - j1.r.push(j1.pc << 1) - j1.pc = v.Value() - case Cond: - if j1.st0 == 0 { - j1.pc = v.Value() - } - j1.st0 = j1.d.pop() - case ALU: - if v.RtoPC { - j1.pc = j1.r.get() >> 1 - } - if v.NtoAtT { - j1.writeAt(j1.st0, j1.d.get()) - } - st0 := j1.newST0(v.Opcode) - j1.d.move(v.Ddir) - j1.r.move(v.Rdir) - if v.TtoN { - j1.d.set(j1.st0) - } - if v.TtoR { - j1.r.set(j1.st0) - } - j1.st0 = st0 - } -} - -func (j1 *J1) newST0(opcode uint16) uint16 { - T, N, R := j1.st0, j1.d.get(), j1.r.get() - switch opcode { - case opT: // T - return T - case opN: // N - return N - case opTplusN: // T+N - return T + N - case opTandN: // T&N - return T & N - case opTorN: // T|N - return T | N - case opTxorN: // T^N - return T ^ N - case opNotT: // ~T - return ^T - case opNeqT: // N==T - return bool2int(N == T) - case opNleT: // N>T - return N >> (T & 0xf) - case opTminus1: // T-1 - return T - 1 - case opR: // R (rT) - return R - case opAtT: // [T] - return j1.readAt(T) - case opNlshiftT: // N<r - ins: []Instruction{Lit(10), ALU{Opcode: opN, TtoR: true, Ddir: -1, Rdir: 1}}, - 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, 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, d: stack{data: [0x20]uint16{0, 0, 10}, sp: 2}, r: stack{data: [0x20]uint16{10}}}, - }, - { // @ - ins: []Instruction{ALU{Opcode: opAtT}}, - end: J1{pc: 1}, - }, - { // ! - ins: []Instruction{Lit(1), Lit(0), ALU{Opcode: opN, NtoAtT: true, Ddir: -1}}, - end: J1{pc: 3, st0: 1, d: stack{data: [0x20]uint16{0, 0, 1}, sp: 1}, memory: [0x4000]uint16{1}}, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprint(tc.ins), func(t *testing.T) { - state := New() - for _, ins := range tc.ins { - state.Eval(ins) - } - cmp(t, *state, tc.end) - }) - } -} - -func TestNextST0(t *testing.T) { - testCases := []struct { - ins ALU - st0 uint16 - state J1 - }{ - {ins: ALU{Opcode: opT}, st0: 0xff, state: J1{st0: 0xff}}, - {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, 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{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, 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) { - 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() - if err := j1.LoadBytes(data); err != nil { - t.Fatal(err) - } - expect := [0x4000]uint16{0x0201, 0x0804} - if j1.memory != expect { - t.Errorf("got %v, want %v", j1.memory[:2], expect) - } -} - -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.d.sp != 0 || j1.r.sp != 0 || j1.st0 != 0 { - t.Errorf("got %v", j1) - } -} -- cgit v1.2.3