Initial commit
This commit is contained in:
commit
40cd1d855a
|
@ -0,0 +1,90 @@
|
|||
# Op Layout
|
||||
|
||||
The first three bits are the category, the next four are the op, the last one is whether or not the op should operate on two bytes (1 for 2byte/16bit, 0 for 1byte/8bit)
|
||||
|
||||
```
|
||||
[ 000 0000 0 ]
|
||||
| \/ |
|
||||
Cat Op 2byte
|
||||
```
|
||||
|
||||
1. memory - 000
|
||||
1. set - 000
|
||||
2. get - 001
|
||||
3. sei - 010 - set indirect (use a pointer stores elswhere)
|
||||
4. gei - 011
|
||||
5. .
|
||||
6. .
|
||||
7. .
|
||||
2. stack - 001
|
||||
1. psh - 000
|
||||
2. pop - 001
|
||||
3. ovr - 010
|
||||
4. swp - 011
|
||||
5. rot - 100
|
||||
6. dup - 101
|
||||
7. .
|
||||
3. math - 010
|
||||
1. add - 000
|
||||
2. sub - 001
|
||||
3. mul - 010
|
||||
4. div - 011
|
||||
5. inc - 100
|
||||
6. dec - 101
|
||||
7. .
|
||||
4. bits - 011
|
||||
1. and - 000
|
||||
2. bor - 001 (bitwise or)
|
||||
3. xor - 010
|
||||
4. not - 011
|
||||
5. shr - 100
|
||||
6. shl - 101
|
||||
7. eql - 111
|
||||
5. jump - 100
|
||||
1. jmp - 000
|
||||
2. sub - 001
|
||||
3. jcd - 010
|
||||
4. ret - 011
|
||||
5. .
|
||||
6. .
|
||||
7. .
|
||||
7. sys - 101
|
||||
1. gch - 000
|
||||
2. gst - 001
|
||||
3. pch - 010
|
||||
4. pst - 011
|
||||
5. hlt - 100
|
||||
6. .
|
||||
7. .
|
||||
8. .
|
||||
9. .
|
||||
10. .
|
||||
11. .
|
||||
12. .
|
||||
13. .
|
||||
14. .
|
||||
15. .
|
||||
8. dev - 111
|
||||
1. .
|
||||
2. .
|
||||
3. .
|
||||
4. .
|
||||
5. .
|
||||
6. .
|
||||
7. .
|
||||
8. .
|
||||
9. .
|
||||
10. .
|
||||
11. .
|
||||
12. .
|
||||
13. .
|
||||
14. .
|
||||
15. .
|
||||
|
||||
## Memory Layout
|
||||
|
||||
```
|
||||
var memory = [MAX_uint16]byte{}
|
||||
```
|
||||
|
||||
## Stack
|
|
@ -0,0 +1,7 @@
|
|||
module tildegit.org/sloum/bird-vm
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require golang.org/x/term v0.15.0
|
||||
|
||||
require golang.org/x/sys v0.15.0 // indirect
|
|
@ -0,0 +1,4 @@
|
|||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
|
@ -0,0 +1,66 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const MaxUint16 uint16 = uint16(0xFFFF)
|
||||
const MaxByte byte = byte(0xFF)
|
||||
|
||||
func IsLongOp(b byte) bool {
|
||||
if b>>7 == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsRetStackOp(b byte) bool {
|
||||
if b&0x40 == 0x40 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func PopAddress() uint16 {
|
||||
return To16(vm.Data.Pop2())
|
||||
}
|
||||
|
||||
func ReadAddress(a uint16) uint16 {
|
||||
return To16(vm.Mem.Read2(a))
|
||||
}
|
||||
|
||||
func IsTrue(b byte) bool {
|
||||
if b == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func To16(high, low byte) uint16 {
|
||||
return (uint16(high)<<8)|uint16(low)
|
||||
}
|
||||
|
||||
func To8s(n uint16) (byte, byte) {
|
||||
return byte(n>>8), byte(n & uint16(0xFF))
|
||||
}
|
||||
|
||||
func LoadMemory(b []byte) {
|
||||
for i := range b {
|
||||
vm.Mem[i] = b[i]
|
||||
}
|
||||
vm.HP = uint16(len(b))
|
||||
vm.MemOffset = uint16(len(b))
|
||||
}
|
||||
|
||||
func DumpVm() {
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"\n<%03d>Data : %v\n<%03d>Return: %v\nPC: %d\nHP: %d\n",
|
||||
vm.Data.pointer,
|
||||
vm.Data.data[:vm.Data.pointer],
|
||||
vm.Return.pointer,
|
||||
vm.Return.data[:vm.Return.pointer],
|
||||
vm.PC,
|
||||
vm.HP-vm.MemOffset)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
)
|
||||
|
||||
// This has room for up to 255 ops, but just uses these right now.
|
||||
// Once a screen device is enable there will likely be more. Screen
|
||||
// and sound, really.
|
||||
var ops = []func(bool, *Stack){
|
||||
opNop, opSet, opGet, opSei, opGei, opAlo, opNop,
|
||||
opPsh, opPop, opOvr, opSwp, opRot, opDup, opSsw, // <-13
|
||||
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
|
||||
opAnd, opBor, opXor, opShr, opShl, opNop, opNop, // <-27
|
||||
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
|
||||
opGch, opGst, opPch, opPst, opHlt, opPhx, opNop, //<-41
|
||||
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop,
|
||||
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
|
||||
opNop, opNop,
|
||||
|
||||
opNop, opSet, opGet, opSei, opGei, opAlo, opNop,
|
||||
opPsh, opPop, opOvr, opSwp, opRot, opDup, opNop,
|
||||
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
|
||||
opAnd, opBor, opXor, opShr, opShl, opNop, opNop,
|
||||
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
|
||||
opNop, opNop, opNop, opNop, opNop, opPhx, opNop,
|
||||
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop,
|
||||
}
|
||||
|
||||
var vm Vm = Vm{0, 0, 0, [65535]byte{0}, Stack{0, [255]byte{0}}, Stack{0, [255]byte{0}}}
|
||||
var dumpVmData bool
|
||||
var halt = -1
|
||||
|
||||
|
||||
func main() {
|
||||
flag.BoolVar(&dumpVmData, "dump", false, "Dump stacks on exit")
|
||||
flag.Parse()
|
||||
test := []byte{
|
||||
0x87, 0x02, 0x08, // Push 0x02 and 0x08 to the stack
|
||||
0x0e, 0x28, // add them together and print the hex value
|
||||
0x07, 0x02, 0x27, // Then exit with 0x02 as the exit code
|
||||
}
|
||||
|
||||
LoadMemory(test)
|
||||
|
||||
// Loop
|
||||
for ;vm.PC < MaxUint16 && halt < 0;vm.PC++{
|
||||
o := vm.Mem.Read(vm.PC)
|
||||
if IsRetStackOp(o) {
|
||||
ops[o](IsLongOp(o), &(vm.Return))
|
||||
} else {
|
||||
ops[o](IsLongOp(o), &(vm.Data))
|
||||
}
|
||||
}
|
||||
if halt < 0 {
|
||||
halt = 9
|
||||
}
|
||||
if dumpVmData {
|
||||
DumpVm()
|
||||
}
|
||||
os.Exit(halt)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package main
|
||||
|
||||
// https://www.andreinc.net/2021/12/01/writing-a-simple-vm-in-less-than-125-lines-of-c
|
||||
|
||||
type Memory [65536]uint16
|
||||
|
||||
func (m *Memory) Read(address uint16) uint16 {
|
||||
return m[address]
|
||||
}
|
||||
|
||||
func (m *Memory) Write(address, value uint16) {
|
||||
m[address] = value
|
||||
}
|
||||
|
||||
const (
|
||||
FlagFp uint16 = 1 << 0
|
||||
FlagFz uint16 = 1 << 1
|
||||
FlagFn uint16 = 1 << 2
|
||||
R0 uint16 = 0
|
||||
R2 = iota
|
||||
R3
|
||||
R4
|
||||
R5
|
||||
R6
|
||||
R7
|
||||
RPC
|
||||
RCND
|
||||
RCNT
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
PcStart uint16 = 0x3000
|
||||
Reg [RCNT]uint16
|
||||
OpMap [16]func(uint16){opBr,opAdd,opLd,opSt,opJsr,opAnd,opLdr,opStr,opRti,opNot,opLdi,opSti,opJump,opNop,opLea,opTrap}
|
||||
)
|
||||
|
||||
func Uf(r uint16) {
|
||||
if (Reg[r] == 0) {
|
||||
reg[RCND] = FlagFz
|
||||
} else if (Reg[r]>>15 == 1) {
|
||||
reg[RCND] = FlagFn
|
||||
} else {
|
||||
reg[RCND] = FlagFp
|
||||
}
|
||||
}
|
||||
|
||||
func FIMM(n uint16) uint16 {
|
||||
return (n>>5)&1
|
||||
}
|
||||
|
||||
func DR(n uint16) uint16 {
|
||||
return (n>>9)&0x7
|
||||
}
|
||||
|
||||
func SR1(n uint16) uint16 {
|
||||
return (i>>6)&0x7
|
||||
}
|
||||
|
||||
func SR2(n uint16) uint16 {
|
||||
return n&0x7
|
||||
}
|
||||
|
||||
func IMM(n uint16) uint16 {
|
||||
return n&0x1F
|
||||
}
|
||||
|
||||
func SextImm(n uint16) uint16 {
|
||||
return Sext(IMM(n), 5)
|
||||
}
|
||||
|
||||
func Sext(n uint16, b int) uint16 {
|
||||
if ((n>>(b-1))&1 == 1) { // if the bth bit of n is 1 (number is negative):
|
||||
return (n|(0xFFFF << b)) // fill up with 1s the remaining bits (15)
|
||||
} else { // else:
|
||||
return n // return the number as is
|
||||
}
|
||||
}
|
||||
|
||||
func opAdd(i uint16) {
|
||||
reg[DR(i)] = reg[SR1(i)]
|
||||
if FIMM(i) == 1 { // If the 5th bit is 1:
|
||||
reg[DR[i]] += SextImm(i) // we sign extend IMM5 and we add it to SR1 (add2)
|
||||
} else { // else:
|
||||
reg[DR[i]] += reg[SR2(i)] // add the value of SR2 to SR1 (add1)
|
||||
}
|
||||
Uf(DR(i)); // !! Update the conditional register depending on the value of DR1
|
||||
}
|
||||
|
||||
func opAnd(i uint16) {
|
||||
if FIMM(i) == 1 { // If the 5th bit is 1
|
||||
reg[DR(i)] = reg[SR1(i)] & SextImm(i) // We sign extend IMM5 and we & it to SR1 (and1)
|
||||
} else {
|
||||
reg[DR(i)] = reg[SR1(i)] & reg[SR2(i)] // Otherwise we & the value of SR2 to SR1
|
||||
}
|
||||
Uf(DR(i)); // Update the conditional register
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func opNop(longOp bool, s *Stack){}
|
||||
|
||||
// ( [val] val high low -- )
|
||||
func opSet(longOp bool, s *Stack) {
|
||||
addr := PopAddress()
|
||||
if longOp {
|
||||
h, l := vm.Data.Pop2()
|
||||
vm.Mem.Write2(addr, h, l)
|
||||
return
|
||||
}
|
||||
vm.Mem.Write(addr, vm.Data.Pop())
|
||||
}
|
||||
|
||||
// ( high low -- val [val] )
|
||||
func opGet(longOp bool, s *Stack) {
|
||||
addr := PopAddress()
|
||||
if longOp {
|
||||
vm.Data.Push2(vm.Mem.Read2(addr))
|
||||
return
|
||||
}
|
||||
vm.Data.Push(vm.Mem.Read(addr))
|
||||
}
|
||||
|
||||
// Set indirect
|
||||
// ( [val] val high low -- )
|
||||
func opSei(longOp bool, s *Stack) {
|
||||
ptr := PopAddress()
|
||||
vm.Data.Push2(vm.Mem.Read2(ptr))
|
||||
opSet(longOp, s)
|
||||
}
|
||||
|
||||
// ( high low -- val [val] )
|
||||
func opGei(longOp bool, s *Stack) {
|
||||
ptr := PopAddress()
|
||||
vm.Data.Push2(vm.Mem.Read2(ptr))
|
||||
opGet(longOp, s)
|
||||
}
|
||||
|
||||
// ( -- [val] val )
|
||||
func opPsh(longOp bool, s *Stack) {
|
||||
vm.PC++
|
||||
if longOp {
|
||||
s.Push2(vm.Mem.Read2(vm.PC))
|
||||
vm.PC++
|
||||
return
|
||||
}
|
||||
s.Push(vm.Mem.Read(vm.PC))
|
||||
}
|
||||
|
||||
// ( v1 -- )
|
||||
// ( v1 v2 -- )
|
||||
func opPop(longOp bool, s *Stack) {
|
||||
s.Pop()
|
||||
if longOp {
|
||||
s.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
// ( v1 v2 -- v1 v2 v1 )
|
||||
// ( v1 v2 v3 v4 -- v1 v2 v3 v4 v1 v2 )
|
||||
func opOvr(longOp bool, s *Stack) {
|
||||
hTop, lTop := vm.Data.Pop2()
|
||||
if longOp {
|
||||
hOvr, lOvr := vm.Data.Pop2()
|
||||
vm.Data.Push2(hOvr, lOvr)
|
||||
vm.Data.Push2(hTop, lTop)
|
||||
vm.Data.Push2(hOvr, lOvr)
|
||||
return
|
||||
}
|
||||
vm.Data.Push2(hTop, lTop)
|
||||
vm.Data.Push(hTop)
|
||||
}
|
||||
|
||||
// ( v1 v2 -- v2 v1 )
|
||||
// ( v1 v2 v3 v4 -- v3 v4 v1 v2 )
|
||||
func opSwp(longOp bool, s *Stack) {
|
||||
hTop, lTop := vm.Data.Pop2()
|
||||
if longOp {
|
||||
hSwp, lSwp := vm.Data.Pop2()
|
||||
vm.Data.Push2(hTop, lTop)
|
||||
vm.Data.Push2(hSwp, lSwp)
|
||||
return
|
||||
}
|
||||
vm.Data.Push2(lTop, hTop)
|
||||
}
|
||||
|
||||
|
||||
func opRot(longOp bool, s *Stack) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// ( v -- v v )
|
||||
// ( v1 v2 -- v1 v2 v1 v2 )
|
||||
func opDup(longOp bool, s *Stack) {
|
||||
if longOp {
|
||||
vm.Data.Push2(vm.Data.Peek2())
|
||||
return
|
||||
}
|
||||
vm.Data.Push(vm.Data.Peek())
|
||||
}
|
||||
|
||||
// Swaps data from one stack to another
|
||||
// In return mode goes from ret->data
|
||||
// normally goes data->ret
|
||||
func opSsw(longOp bool, s *Stack) {
|
||||
var from, to *Stack
|
||||
if s == &(vm.Data) {
|
||||
from = s
|
||||
to = &(vm.Return)
|
||||
} else {
|
||||
to = s
|
||||
from = &(vm.Return)
|
||||
}
|
||||
if longOp {
|
||||
to.PushLong(from.PopLong())
|
||||
} else {
|
||||
to.Push(from.Pop())
|
||||
}
|
||||
}
|
||||
|
||||
// ( v1 v2 -- v1+v2 )
|
||||
// ( v1 v2 v3 v4 -- v1v2+v3v4 )
|
||||
func opAdd(longOp bool, s *Stack) {
|
||||
if longOp {
|
||||
vm.Data.PushLong(PopAddress()+PopAddress())
|
||||
return
|
||||
}
|
||||
vm.Data.Push(vm.Data.Pop()+vm.Data.Pop())
|
||||
}
|
||||
|
||||
// ( v1 v2 -- v1-v2 )
|
||||
// ( v1 v2 v3 v4 -- v1v2-v3v4 )
|
||||
func opSub(longOp bool, s *Stack) {
|
||||
if longOp {
|
||||
l := PopAddress()
|
||||
h := PopAddress()
|
||||
vm.Data.PushLong(h-l)
|
||||
return
|
||||
}
|
||||
h, l := vm.Data.Pop2()
|
||||
vm.Data.Push(h-l)
|
||||
}
|
||||
|
||||
// ( v1 v2 == v1*v2 )
|
||||
// ( v1 v2 v3 v4 == v1v2*v3v4 )
|
||||
func opMul(longOp bool, s *Stack) {
|
||||
if longOp {
|
||||
vm.Data.PushLong(PopAddress()*PopAddress())
|
||||
return
|
||||
}
|
||||
vm.Data.Push(vm.Data.Pop()*vm.Data.Pop())
|
||||
}
|
||||
|
||||
// ( v1 v2 -- v1/v2 )
|
||||
// ( v1 v2 v3 v4 -- v1v2/v3v4 )
|
||||
func opDiv(longOp bool, s *Stack) {
|
||||
if longOp {
|
||||
h := PopAddress()
|
||||
l := PopAddress()
|
||||
if l == 0 {
|
||||
vm.Data.Push(byte(0))
|
||||
return
|
||||
}
|
||||
vm.Data.PushLong(h/l)
|
||||
return
|
||||
}
|
||||
h := vm.Data.Pop()
|
||||
l := vm.Data.Pop()
|
||||
if l == 0 {
|
||||
vm.Data.Push(byte(0))
|
||||
return
|
||||
}
|
||||
vm.Data.Push(h/l)
|
||||
}
|
||||
|
||||
func opInc(longOp bool, s *Stack){
|
||||
addr := PopAddress()
|
||||
if longOp {
|
||||
v := vm.Mem.ReadLong(addr)
|
||||
if v == MaxUint16 {
|
||||
v = 0
|
||||
} else {
|
||||
v++
|
||||
}
|
||||
vm.Mem.WriteLong(addr, v)
|
||||
return
|
||||
}
|
||||
v := vm.Mem.Read(addr)
|
||||
if v == MaxByte {
|
||||
v = 0
|
||||
} else {
|
||||
v++
|
||||
}
|
||||
vm.Mem.Write(addr, v)
|
||||
}
|
||||
|
||||
func opDec(longOp bool, s *Stack){
|
||||
addr := PopAddress()
|
||||
if longOp {
|
||||
v := vm.Mem.ReadLong(addr)
|
||||
if v == 0 {
|
||||
v = MaxUint16
|
||||
} else {
|
||||
v--
|
||||
}
|
||||
vm.Mem.WriteLong(addr, v)
|
||||
return
|
||||
}
|
||||
v := vm.Mem.Read(addr)
|
||||
if v == 0 {
|
||||
v = MaxByte
|
||||
} else {
|
||||
v--
|
||||
}
|
||||
vm.Mem.Write(addr, v)
|
||||
}
|
||||
|
||||
func opAnd(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
l16, h16 := PopAddress(), PopAddress()
|
||||
h16 = h16&l16
|
||||
vm.Data.PushLong(h16)
|
||||
return
|
||||
}
|
||||
n1, n2 := vm.Data.Pop2()
|
||||
vm.Data.Push(n1&n2)
|
||||
}
|
||||
|
||||
func opBor(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
l16, h16 := PopAddress(), PopAddress()
|
||||
h16 = h16|l16
|
||||
vm.Data.PushLong(h16)
|
||||
return
|
||||
}
|
||||
n1, n2 := vm.Data.Pop2()
|
||||
vm.Data.Push(n1|n2)
|
||||
}
|
||||
|
||||
func opXor(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
l16, h16 := PopAddress(), PopAddress()
|
||||
h16 = h16^l16
|
||||
vm.Data.PushLong(h16)
|
||||
return
|
||||
}
|
||||
n1, n2 := vm.Data.Pop2()
|
||||
vm.Data.Push(n1^n2)
|
||||
}
|
||||
|
||||
func opShr(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
l16, h16 := PopAddress(), PopAddress()
|
||||
h16 = h16>>l16
|
||||
vm.Data.PushLong(h16)
|
||||
return
|
||||
}
|
||||
n1, n2 := vm.Data.Pop2()
|
||||
vm.Data.Push(n1>>n2)
|
||||
}
|
||||
|
||||
func opShl(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
l16, h16 := PopAddress(), PopAddress()
|
||||
h16 = h16>>l16
|
||||
vm.Data.PushLong(h16)
|
||||
return
|
||||
}
|
||||
n1, n2 := vm.Data.Pop2()
|
||||
vm.Data.Push(n1>>n2)
|
||||
}
|
||||
|
||||
func opJmp(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
vm.PC = vm.Data.PopLong()
|
||||
} else {
|
||||
vm.PC = uint16(vm.Data.Pop())
|
||||
}
|
||||
}
|
||||
|
||||
func opCal(longOp bool, s *Stack){
|
||||
// No short version
|
||||
vm.Return.PushLong(vm.PC)
|
||||
opJmp(longOp, s)
|
||||
}
|
||||
|
||||
|
||||
func opJcd(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
if vm.Data.PopLong() == 0 {
|
||||
vm.Data.PopLong()
|
||||
} else {
|
||||
opJmp(true, s)
|
||||
}
|
||||
} else {
|
||||
if vm.Data.Pop() == 0 {
|
||||
vm.Data.PopLong()
|
||||
} else {
|
||||
opJmp(false, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func opRet(longOp bool, s *Stack){
|
||||
// No short version
|
||||
vm.Data.PushLong(vm.Return.PopLong())
|
||||
opJmp(true, s)
|
||||
}
|
||||
|
||||
// ( -- ch )
|
||||
func opGch(longOp bool, s *Stack) {
|
||||
oldTerm, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
vm.Data.Push(byte(0))
|
||||
return
|
||||
}
|
||||
defer term.Restore(int(os.Stdin.Fd()), oldTerm)
|
||||
var count = 1
|
||||
if longOp {
|
||||
count = 2
|
||||
}
|
||||
b := make([]byte, count)
|
||||
os.Stdin.Read(b)
|
||||
if longOp {
|
||||
vm.Data.Push2(b[0], b[1])
|
||||
return
|
||||
}
|
||||
vm.Data.Push(b[0])
|
||||
}
|
||||
|
||||
// Get string placeholder
|
||||
// Maybe use scanline instead?
|
||||
func opGst(longOp bool, s *Stack){
|
||||
b := make([]byte, 1)
|
||||
_, err := os.Stdin.Read(b)
|
||||
if err != nil {
|
||||
vm.Data.Push(0)
|
||||
return
|
||||
}
|
||||
vm.Data.Push(b[0])
|
||||
}
|
||||
func opPch(longOp bool, s *Stack){}
|
||||
func opPst(longOp bool, s *Stack){}
|
||||
|
||||
// Halt execution
|
||||
// ( exitCode -- )
|
||||
func opHlt(longOp bool, s *Stack){
|
||||
halt = int(vm.Data.Pop())
|
||||
}
|
||||
|
||||
// Print hex
|
||||
func opPhx(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
fmt.Printf("%04x", PopAddress())
|
||||
return
|
||||
}
|
||||
fmt.Printf("%02x", vm.Data.Pop())
|
||||
}
|
||||
func opGrt(longOp bool, s *Stack){}
|
||||
func opLst(longOp bool, s *Stack){}
|
||||
func opGte(longOp bool, s *Stack){}
|
||||
func opLte(longOp bool, s *Stack){}
|
||||
func opEql(longOp bool, s *Stack){}
|
||||
func opNeq(longOp bool, s *Stack){}
|
||||
|
||||
// ( [count] count -- addrHigh addrLow )
|
||||
func opAlo(longOp bool, s *Stack){
|
||||
if longOp {
|
||||
count := PopAddress()
|
||||
vm.Data.PushLong(vm.HP)
|
||||
vm.HP += count
|
||||
return
|
||||
}
|
||||
count := uint16(vm.Data.Pop())
|
||||
vm.Data.PushLong(vm.HP)
|
||||
vm.HP += count
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
0000 0000 set
|
||||
0000 0001 get
|
||||
0000 0010 sei
|
||||
0000 0011 gei
|
||||
0000 0100 nop
|
||||
0000 0101 nop
|
||||
0000 0111 nop
|
||||
0000 1000 psh
|
||||
0000 1001 pop
|
||||
0000 1010 ovr
|
||||
0000 1011 swp
|
||||
0000 1100 rot
|
||||
0000 1101 dup
|
||||
0000 1111 nop
|
||||
0001 0000 add
|
||||
0001 0001 sub
|
||||
0001 0010 mul
|
||||
0001 0011 div
|
||||
0001 0100 inc
|
||||
0001 0101 dec
|
||||
0001 0111 nop
|
||||
0001 1000 and
|
||||
0001 1001 bor
|
||||
0001 1010 xor
|
||||
0001 1011 not
|
||||
0001 1100 shr
|
||||
0001 1101 shl
|
||||
0001 1111 eql
|
||||
0010 0000 jmp
|
||||
0010 0001 cal
|
||||
0010 0010 jcd
|
||||
0010 0011 ret
|
||||
0010 0100 nop
|
||||
0010 0101 nop
|
||||
0010 0111 nop
|
||||
0010 1000 gch
|
||||
0010 1001 gst
|
||||
0010 1010 pch
|
||||
0010 1011 pst
|
||||
0010 1100 hlt
|
||||
0010 1101 nop
|
||||
0010 1111 nop
|
||||
0011 0000 grt
|
||||
0011 0001 lst
|
||||
0011 0010 gte
|
||||
0011 0011 lte
|
||||
0011 0100 eql
|
||||
0011 0101 neq
|
||||
...
|
||||
1000 0000 set2
|
||||
1000 0001 get2
|
||||
1000 0010 sei2
|
||||
1000 0011 gei2
|
||||
1000 1000 psh2
|
||||
1000 1001 pop2
|
||||
1000 1010 ovr2
|
||||
1000 1011 swp2
|
||||
1000 1100 rot2
|
||||
1000 1101 dup2
|
||||
1001 0000 add2
|
||||
1001 0001 sub2
|
||||
1001 0010 mul2
|
||||
1001 0011 div2
|
||||
1001 0100 inc2
|
||||
1001 0101 dec2
|
||||
1001 1000 and2
|
||||
1001 1001 bor2
|
||||
1001 1010 xor2
|
||||
1001 1011 not2
|
||||
1001 1100 shr2
|
||||
1001 1101 shl2
|
||||
1001 1111 eql2
|
||||
1010 0000 jmp2
|
||||
1010 0001 cal2
|
||||
1010 0010 jcd2
|
||||
1010 0011 ret2
|
||||
1010 0100 nop
|
||||
1010 0101 nop
|
||||
1010 0111 nop
|
||||
...
|
||||
*/
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package main
|
||||
|
||||
type Memory [65535]byte
|
||||
|
||||
func (m *Memory) Read(address uint16) byte {
|
||||
return m[address]
|
||||
}
|
||||
|
||||
func (m *Memory) ReadLong(address uint16) uint16 {
|
||||
return To16(m[address], m[address+1])
|
||||
}
|
||||
|
||||
func (m *Memory) Read2(address uint16) (byte, byte) {
|
||||
return m[address], m[address+1] // May error if called at max index for the low
|
||||
}
|
||||
|
||||
func (m *Memory) Write(address uint16, value byte) {
|
||||
m[address] = value
|
||||
}
|
||||
|
||||
func (m *Memory) WriteLong(address uint16, value uint16) {
|
||||
h, l := To8s(value)
|
||||
m.Write(address, h)
|
||||
address++
|
||||
if address >= MaxUint16-1 {
|
||||
address = 0
|
||||
}
|
||||
m.Write(address, l)
|
||||
}
|
||||
|
||||
func (m *Memory) Write2(address uint16, high, low byte) {
|
||||
m.Write(address, high)
|
||||
address++
|
||||
if address >= MaxUint16-1 {
|
||||
address = 0
|
||||
}
|
||||
m.Write(address, low)
|
||||
}
|
||||
|
||||
type Stack struct {
|
||||
pointer byte
|
||||
data [255]byte
|
||||
}
|
||||
|
||||
func (s Stack) Peek() byte {
|
||||
if s.pointer == 0 {
|
||||
return s.data[len(s.data)-1]
|
||||
}
|
||||
return s.data[s.pointer-1]
|
||||
}
|
||||
|
||||
func (s Stack) Peek2() (byte, byte) {
|
||||
low := s.Peek()
|
||||
var high byte
|
||||
if s.pointer == 1 {
|
||||
high = s.data[len(s.data)-1]
|
||||
} else if s.pointer == 0 {
|
||||
high = s.data[len(s.data)-2]
|
||||
} else {
|
||||
high = s.data[s.pointer-2]
|
||||
}
|
||||
return high, low
|
||||
}
|
||||
|
||||
func (s *Stack) Push(b byte) {
|
||||
s.data[s.pointer] = b
|
||||
if s.pointer == MaxByte-1 {
|
||||
s.pointer = 0
|
||||
} else {
|
||||
s.pointer++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stack) Push2(h, l byte) {
|
||||
s.Push(h)
|
||||
s.Push(l)
|
||||
}
|
||||
|
||||
func (s *Stack) PushLong(n uint16) {
|
||||
h, l := To8s(n)
|
||||
s.Push2(h, l)
|
||||
}
|
||||
|
||||
func (s *Stack) Pop() byte {
|
||||
if s.pointer == 0 {
|
||||
s.pointer = 254
|
||||
} else {
|
||||
s.pointer--
|
||||
}
|
||||
return s.data[s.pointer]
|
||||
}
|
||||
|
||||
func (s *Stack) Pop2() (byte, byte) {
|
||||
low := s.Pop()
|
||||
return s.Pop(), low
|
||||
}
|
||||
|
||||
func (s *Stack) PopLong() (uint16) {
|
||||
h, l := s.Pop2()
|
||||
return To16(h,l)
|
||||
}
|
||||
|
||||
/* The idea of the heap pointer is that after all
|
||||
* data and ops are written to the memory space the
|
||||
* heap space will start to get used up. However,
|
||||
* when a subroutine is called with `cal`, the current
|
||||
* HP will be added to the return stack along with the
|
||||
* return address. When `ret` is called, it will change
|
||||
* the PC to the return address and HP to its previous
|
||||
* position. This allows for temporary variable usage
|
||||
* within the heap space and clear things up as needed.
|
||||
* The only issue is how to do something like dynamically
|
||||
* read in a string to a memory space. Since the length is
|
||||
* not known in advance, the space cannot be reserved in
|
||||
* advance. As a result, there is a case to be made for
|
||||
* not doing this at all, and essentially just fillling
|
||||
* up space without freeing it, especially since many of
|
||||
* the references in question will have their sizes known
|
||||
* prior to runtime. Doing all sized references in a "data"
|
||||
* section makes sense. That would leave the heap section
|
||||
* for large dynamic runtime things...
|
||||
*/
|
||||
|
||||
type Vm struct {
|
||||
PC uint16 // Current Position
|
||||
HP uint16 // Current start of heap
|
||||
MemOffset uint16 // Start of user memory
|
||||
Mem Memory
|
||||
Data Stack
|
||||
Return Stack
|
||||
}
|
Loading…
Reference in New Issue