Makes things work better (worst commit message ever)

This commit is contained in:
sloum 2024-01-03 22:35:18 -08:00
parent b69d1500bf
commit 55715ba55e
4 changed files with 100 additions and 27 deletions

View File

@ -1,10 +1,15 @@
# bird, a vm
This is a learning project. Many mistakes are being made, but lots of fun is being had. Inspired by varvara/uxn, but without looking at their implementation details and mostly just taking my memory of their project as a point of inspiration.
bird-vm is a mostly stack based vm. It uses 8-bit ops and 8-bit stacks, but has 16-bit addressing and the ability to do 16 bit math and work with the stacks in "long mode" (pop a high and low byte to work with 16-bit numbers).
This is a learning project. Many mistakes are being made, but lots of fun is being had. It is inspired by varvara/uxn, but without looking at their implementation details and mostly just taking my memory of their project as a point of inspiration. I do not expect it to be generally useful, or a particularly good design (it is my first time dipping a toe into writing a bytecode interpreter/vm).
Coming soon:
1. An assembler that produces bird bytecode
2. More ops filled in within the vm
3. A higher level language that compiles down to either the assembler or to bytecode
- [X] An assembler that produces bird bytecode (see: [bird-asm](https://tildegit.org/sloum/bird-asm) )
- [ ] A few string handling ops that will make working with null terminated strings more functional
- [ ] A higher level language that compiles down to either the assembler or to bytecode
- I am thinking about something vaguely Pascal-like

View File

@ -48,26 +48,27 @@ func To8s(n uint16) (byte, byte) {
return byte(n>>8), byte(n & uint16(0xFF))
}
func LoadProg(b []byte) {
func LoadProg(b []byte) int{
for i := range b {
vm.Prog[i] = b[i]
}
if len(b) < 2 {
return
return 0
}
vm.HP = To16(vm.Prog[0], vm.Prog[1])
return len(b)
}
func DumpVm() {
func DumpVm(l int) {
fmt.Fprintf(
os.Stderr,
"\n<%03d>Data : %v\n<%03d>Return: %v\nPC: %d\nData: %v",
"\n<%03d>Data : %v\n<%03d>Return: %v\nPC: %d\nMemory: %v",
vm.Data.pointer,
vm.Data.data[:vm.Data.pointer],
vm.Return.pointer,
vm.Return.data[:vm.Return.pointer],
vm.PC,
vm.Prog[:vm.PC])
vm.Prog[:l])
}

14
main.go
View File

@ -8,8 +8,6 @@ import (
/*
TODO
- Change GCH and GST to RCH and RST (From G/Get to R/Read)
- Remove PHX
- Add SST (set string: move a string from the stack to memory)
- Add a flag that lets the heap pointer move after the write?
- If this isn't present then GST and RST have to add a length
@ -28,7 +26,7 @@ var ops = []func(bool, *Stack){
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
opAnd, opBor, opXor, opShr, opShl, opNop, opNop, // <-27
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
opGch, opGst, opPut, opPst, opHlt, opNop, opNop,
opRch, opRst, opPut, opPst, opHlt, opSst, opGst,
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop, //<-48
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
@ -40,7 +38,7 @@ var ops = []func(bool, *Stack){
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
opAnd, opBor, opXor, opShr, opShl, opNop, opNop,
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
opGch, opGst, opPut, opPst, opHlt, opNop, opNop,
opRch, opRst, opPut, opPst, opHlt, opSst, opGst,
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop, //<-112
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
@ -52,7 +50,7 @@ var ops = []func(bool, *Stack){
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
opAnd, opBor, opXor, opShr, opShl, opNop, opNop,
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
opGch, opGst, opPut, opPst, opHlt, opNop, opNop,
opRch, opRst, opPut, opPst, opHlt, opSst, opGst,
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop, //<-176
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
@ -64,7 +62,7 @@ var ops = []func(bool, *Stack){
opAdd, opSub, opMul, opDiv, opInc, opDec, opNop,
opAnd, opBor, opXor, opShr, opShl, opNop, opNop,
opJmp, opCal, opJcd, opRet, opNop, opNop, opNop,
opGch, opGst, opPut, opPst, opHlt, opNop, opNop,
opRch, opRst, opPut, opPst, opHlt, opSst, opGst,
opGrt, opLst, opGte, opLte, opEql, opNeq, opNop, //<-176
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
opNop, opNop, opNop, opNop, opNop, opNop, opNop,
@ -96,7 +94,7 @@ func main() {
os.Exit(1)
}
LoadProg(in)
l := LoadProg(in)
vm.PC = 2
// Loop
@ -120,7 +118,7 @@ func main() {
halt = 9
}
if dumpVmData {
DumpVm()
DumpVm(l)
}
os.Exit(halt)
}

89
ops.go
View File

@ -1,6 +1,7 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
@ -12,13 +13,13 @@ func opNop(longOp bool, s *Stack){}
// ( [val] val high low -- )
func opSet(longOp bool, s *Stack) {
addr := PopAddress()
addr := s.PopLong()
if longOp {
h, l := vm.Data.Pop2()
h, l := s.Pop2()
vm.Mem.Write2(addr, h, l)
return
}
vm.Mem.Write(addr, vm.Data.Pop())
vm.Mem.Write(addr, s.Pop())
}
// ( high low -- val [val] )
@ -318,7 +319,7 @@ func opRet(longOp bool, s *Stack){
}
// ( -- ch )
func opGch(longOp bool, s *Stack) {
func opRch(longOp bool, s *Stack) {
oldTerm, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
vm.Data.Push(byte(0))
@ -338,16 +339,26 @@ func opGch(longOp bool, s *Stack) {
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)
// 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
}
vm.Data.Push(b[0])
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) {
@ -379,6 +390,21 @@ func opPut(longOp bool, s *Stack) {
}
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
@ -387,6 +413,49 @@ 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()