582 lines
9.8 KiB
Go
582 lines
9.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
func opNop(longOp bool, s *Stack){}
|
|
|
|
// ( [val] val high low -- )
|
|
func opSet(longOp bool, s *Stack) {
|
|
addr := s.PopLong()
|
|
if longOp {
|
|
h, l := s.Pop2()
|
|
vm.Mem.Write2(addr, h, l)
|
|
return
|
|
}
|
|
vm.Mem.Write(addr, s.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.Prog.Read2(vm.PC))
|
|
vm.PC++
|
|
return
|
|
}
|
|
s.Push(vm.Prog.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 := s.PopLong(), s.PopLong()
|
|
h16 = h16>>l16
|
|
vm.Data.PushLong(h16)
|
|
return
|
|
}
|
|
n1, n2 := s.Pop2()
|
|
vm.Data.Push(n1>>n2)
|
|
}
|
|
|
|
func opJmp(longOp bool, s *Stack){
|
|
jump = true
|
|
if longOp {
|
|
vm.PC = s.PopLong()
|
|
} else {
|
|
vm.PC = uint16(s.Pop())
|
|
}
|
|
}
|
|
|
|
func opCal(longOp bool, s *Stack){
|
|
// No short version
|
|
vm.Return.PushLong(vm.PC)
|
|
opJmp(true, s)
|
|
}
|
|
|
|
|
|
func opJcd(longOp bool, s *Stack){
|
|
if longOp {
|
|
if s.PopLong() == 0 {
|
|
s.PopLong()
|
|
} else {
|
|
opJmp(true, s)
|
|
}
|
|
} else {
|
|
if s.Pop() == 0 {
|
|
s.PopLong()
|
|
} else {
|
|
opJmp(false, s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func opRet(longOp bool, s *Stack){
|
|
// No short or return version version
|
|
vm.PC = vm.Return.PopLong()
|
|
}
|
|
|
|
// ( -- ch )
|
|
func opRch(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])
|
|
}
|
|
|
|
// Read a string from stdin until delim byte
|
|
// ( delim -- 0 [b ...] hiLen loLen)
|
|
func opRst(longOp bool, s *Stack){
|
|
delim := s.Pop()
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
text, err := reader.ReadString(delim)
|
|
if err != nil {
|
|
vm.Data.Push(0)
|
|
return
|
|
}
|
|
length := len(text)+1
|
|
s.Push(0)
|
|
for i := len(text)-2; i >= 0; i-- {
|
|
if text[i] > 255 {
|
|
s.Push(byte('?'))
|
|
}
|
|
s.Push(byte(text[i]))
|
|
}
|
|
s.PushLong(uint16(length))
|
|
}
|
|
|
|
func opPut(longOp bool, s *Stack) {
|
|
format, dest := s.Pop2()
|
|
d := os.Stdout
|
|
if dest == 2 {
|
|
d = os.Stderr
|
|
}
|
|
if format == 'c' || format == 'C' {
|
|
if longOp {
|
|
hi, lo := s.Pop2()
|
|
fmt.Fprint(d, string([]byte{hi, lo}))
|
|
return
|
|
}
|
|
ch := s.Pop()
|
|
fmt.Fprint(d, string(ch))
|
|
return
|
|
}
|
|
if format < 2 || format > 36 {
|
|
return
|
|
}
|
|
var o string
|
|
if longOp {
|
|
o = strconv.FormatUint(uint64(s.PopLong()), int(format))
|
|
} else {
|
|
o = strconv.FormatUint(uint64(s.Pop()), int(format))
|
|
}
|
|
fmt.Fprint(d, o)
|
|
}
|
|
|
|
func opPst(longOp bool, s *Stack){
|
|
st := make([]byte, 0)
|
|
dest := s.Pop()
|
|
d := os.Stdout
|
|
if dest == 2 {
|
|
d = os.Stderr
|
|
}
|
|
var b byte
|
|
for {
|
|
b = s.Pop()
|
|
if b == 0 {
|
|
break
|
|
}
|
|
st = append(st, b)
|
|
}
|
|
d.Write(st)
|
|
}
|
|
|
|
// Halt execution
|
|
// ( exitCode -- )
|
|
func opHlt(longOp bool, s *Stack){
|
|
halt = int(vm.Data.Pop())
|
|
}
|
|
|
|
// Set string (set a string to a memory address)
|
|
// Reads from the stack until a zero bye has been
|
|
// reached and added to the string in memory.
|
|
//
|
|
// Memory space should be created in advance.
|
|
// ( b ... -- )
|
|
func opSst(longOp bool, s *Stack) {
|
|
addr := s.PopLong()
|
|
var b byte
|
|
for {
|
|
b = s.Pop()
|
|
vm.Prog[addr] = b
|
|
if b == 0 {
|
|
break
|
|
}
|
|
addr++
|
|
}
|
|
}
|
|
|
|
// Get a string from memory onto a stack. Leaves
|
|
// the memory intact (does not clear it).
|
|
//
|
|
// ( hi lo -- b ... hi lo )
|
|
// | addr | | data | | len |
|
|
//
|
|
// The len will include the zero byte. This allows
|
|
// allot to use the len to fill memory space to make
|
|
// a copy
|
|
func opGst(longOp bool, s *Stack) {
|
|
start := s.PopLong()
|
|
end := start
|
|
for {
|
|
if vm.Prog[end] == 0 {
|
|
break
|
|
}
|
|
end++
|
|
}
|
|
for i := end; i >= start; i-- {
|
|
s.Push(vm.Prog[i])
|
|
}
|
|
s.PushLong(uint16(end-start+1))
|
|
}
|
|
|
|
func opGrt(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() > l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h > l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
func opLst(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() < l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h < l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
func opGte(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() >= l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h >= l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
func opLte(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() < l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h < l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
func opEql(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() == l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h == l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
func opNeq(longOp bool, s *Stack){
|
|
if longOp {
|
|
l := s.PopLong()
|
|
if s.PopLong() != l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
return
|
|
}
|
|
h, l := s.Pop2()
|
|
if h != l {
|
|
s.Push(1)
|
|
} else {
|
|
s.Push(0)
|
|
}
|
|
}
|
|
|
|
// Allocate memory in the heap area _count_ long
|
|
// in bytes. Return the address as a long.
|
|
// ( [count] count -- addrHigh addrLow )
|
|
func opAll(longOp bool, s *Stack){
|
|
if longOp {
|
|
count := PopAddress()
|
|
s.PushLong(vm.HP)
|
|
vm.HP += count
|
|
return
|
|
}
|
|
count := uint16(vm.Data.Pop())
|
|
vm.Data.PushLong(vm.HP)
|
|
vm.HP += count
|
|
}
|
|
|