diff options
author | Dimitri Sokolyuk <demon@dim13.org> | 2018-01-15 00:32:37 +0100 |
---|---|---|
committer | Dimitri Sokolyuk <demon@dim13.org> | 2018-01-15 00:32:37 +0100 |
commit | 3705ec154f2700d1b3c60ce56626cc520fb3e068 (patch) | |
tree | 413f995245533e627a6f78d983c77b1fe2b7a268 /core.go | |
parent | d4c791fe8e48463a9b47d3afc65b613ca61883da (diff) |
rename
Diffstat (limited to 'core.go')
-rw-r--r-- | core.go | 184 |
1 files changed, 184 insertions, 0 deletions
@@ -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 bool2int(int16(N) < int16(T)) + case opNrshiftT: // 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<<T + return N << (T & 0xf) + case opDepth: // depth (dsp) + return (c.r.depth() << 8) | c.d.depth() + case opNuleT: // Nu<T + return bool2int(N < T) + default: + panic("invalid instruction") + } +} + +func bool2int(b bool) uint16 { + if b { + return ^uint16(0) + } + return 0 +} |