Skip to content

Instantly share code, notes, and snippets.

@tangentstorm
Last active July 2, 2021 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tangentstorm/1520860029844d1459f62140608f2036 to your computer and use it in GitHub Desktop.
Save tangentstorm/1520860029844d1459f62140608f2036 to your computer and use it in GitHub Desktop.
ASM vm... This started as an attempt at a bootstrapping virtual machine, then I got caught up with the idea that bytecode could be human readable, and it probably got a bit out of hand.
const DATA=0, CALL=1, WORK=2, TEMP=3
class Worker {
vm = null // virtual machine
w = 0 // worker number
f = 0 // function pointer
e = 0 // execution pointer
t = 0 // current token in function
s = "" // source (input string/data)
i = -1 // read pointer
x = 0 // first arg
y = 0 // second arg
z = 0 // third arg
constructor(vm, n) {
this.vm = vm
this.n = n
this.d = [[],[],[],[],[]] // stacks: DATA, CALL (for system), WORK, TEMP (for user)
}
// push and pop to the stacks:
push(s,v) { this.d[s].push(v) } pop(s) { return this.d[s].pop() }
dx(v) { this.push(DATA, v) } xd() { return this.pop(DATA) }
cx(v) { this.push(CALL, v) } xc() { return this.pop(CALL) }
wx(v) { this.push(WORK, v) } xw() { return this.pop(WORK) }
tx(v) { this.push(TEMP, v) } xt() { return this.pop(TEMP) }
// -- parser support ------------------------------------------------------
// move execution pointer and get new instruction
vmi(j) { this.t=this.vm.f[this.d][this.e+=j]; return this.t }
// // process a hex number
// hex() {
// let ix=-1, r = 0; const hex = "0123456789ABCDEF_"
// while(0>=(ix=hex.indexOf(this.vmi(1)))) { r = ix===16? -r : ix + r << 4 }
// this.vmi(-1);
// return t }
ops = {
// read and write the registers
'x':()=> this.dx(this.x), 'X':()=> this.x=this.xd(),
'y':()=> this.dx(this.y), 'Y':()=> this.y=this.xd(),
'z':()=> this.dx(this.z), 'Z':()=> this.y=this.xd(),
'i':()=> this.dx(this.i), 'I':()=> this.i=this.xd(),
's':()=> this.dx(this.s), 'S':()=> this.s=this.xd(),
// current and next values from src
'v':()=> this.dx(this.s[this.i]||null),
'V':()=> this.dx(this.s[this.i+1]||null),
// worker id
'W':()=> this.dx(this.n),
// truthy/falsy (we use 0 and 1)
'?':()=> this.dx(+!!this.xd()),
'~':()=> this.dx(~this.xd()),
// hex digits
'_':()=> this.dx(-this.xd()),
'$':()=> this.dx(0),
'0':()=> this.dx(0+this.xd()<<4), '8':()=> this.dx( 8+this.xd()<<4),
'1':()=> this.dx(1+this.xd()<<4), '9':()=> this.dx( 9+this.xd()<<4),
'2':()=> this.dx(2+this.xd()<<4), 'A':()=> this.dx(10+this.xd()<<4),
'3':()=> this.dx(3+this.xd()<<4), 'B':()=> this.dx(11+this.xd()<<4),
'4':()=> this.dx(4+this.xd()<<4), 'C':()=> this.dx(12+this.xd()<<4),
'5':()=> this.dx(5+this.xd()<<4), 'D':()=> this.dx(13+this.xd()<<4),
'6':()=> this.dx(6+this.xd()<<4), 'E':()=> this.dx(14+this.xd()<<4),
'7':()=> this.dx(7+this.xd()<<4), 'F':()=> this.dx(15+this.xd()<<4),
// jump to TOS if NOS is truthy. otherwise drop both.
'j':()=> { let rel = 0|this.dx(); if(!!this.dx()) this.i += rel },
// unconditional relative jump
'J':()=> { this.i += 0|this.dx() },
// dup
"d":()=> { let w=this.d[WORK],l=w.length; if(l){ this.dx(d[l-1]) }},
// temporary list
"[":()=> { this.tx(this.d[DATA]); this.d[DATA]=[] },
"]":()=> { let t = this.d[DATA]; this.d[DATA]=this.xt(); this.dx(t) },
// write item to work buffer
",":()=> this.wx(this.xd()),
// length of the work buffer (like "HERE" in forth)
'#':()=> this.dx(this.wx.length),
// call/return
'c':()=> this.cx(this.p), 'r':()=> this.p = this.xc(),
}
// -- functions -----------------------------------------------------------
// -- arithmetic ----------------------------------------------------------
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment