bird-vm/ops.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
}