uxncore.js (6926B)
1 /** 2 * Uxn core JS implementation for ESOP VM 3 * @version 0.0.1 4 * @author Luxferre 5 * @license Unlicense <https://unlicense.org> 6 */ 7 8 function UxnCore() { 9 10 function initMem() { 11 return { 12 ram: new Uint8Array(65536), //main RAM 13 wst: {ptr: 0, pk: 0, dat: new Uint8Array(256)}, //working stack 14 rst: {ptr: 0, pk: 0, dat: new Uint8Array(256)}, //return stack 15 dev: new Uint8Array(256) //device memory 16 } 17 } 18 19 var mem = null, 20 bs = 0, //byte/short flag 21 km = 0, //keep mode flag 22 pc = 0, //program counter 23 devWriteHooks = {}, 24 devReadHooks = {} 25 26 //helper methods 27 28 function trapout(errcode) { //error reporting with vector execution 29 console.error('Error code: '+errcode+' at instruction #'+pc) 30 console.log('Working stack: ', mem.wst.dat) 31 console.log('Return stack: ', mem.rst.dat) 32 pc = 0 33 } 34 35 function rel(x) {return x > 0x80 ? x - 256 : x} 36 function PUSH8(s, x) {if(s.ptr === 255) trapout(2);s.dat[s.ptr++] = x} 37 function PUSH16(s, x) {PUSH8(s, x>>8);PUSH8(s, x&255)} 38 function PUSH(s, x) {if(bs) PUSH16(s, x);else PUSH8(s, x)} 39 function POP8(s) { 40 var ptr = km ? --s.pk : --s.ptr 41 if(ptr < 0) trapout(1) 42 return s.dat[ptr] 43 } 44 function POP16(s) {return POP8(s) | (POP8(s)<<8)} 45 function POP(s) {return bs ? POP16(s) : POP8 (s)} 46 function JUMP(x, pc) {return bs ? x : (pc + rel(x))} 47 function POKE(x, y) { 48 if(bs) { 49 mem.ram[x] = y>>8 50 mem.ram[x+1] = y&255 51 } 52 else mem.ram[x] = y 53 } 54 function PEEK16(x) {return (mem.ram[x]<<8) | mem.ram[x+1]} 55 function PEEK(x) {return bs ? PEEK16(x) : mem.ram[x]} 56 function DEVR(x) {if(x in devReadHooks) devReadHooks[x]();return bs ? (mem.dev[x] << 8)|mem.dev[x+1] : mem.dev[x]} 57 function DEVW(x, y) { 58 var p, p2 59 if(bs) { 60 p = mem.dev[x] 61 p2 = mem.dev[x+1] 62 mem.dev[x] = y>>8 63 if(x in devWriteHooks) 64 devWriteHooks[x](x, mem.dev[x], p) 65 mem.dev[x+1] = y&255 66 if((x+1) in devWriteHooks) 67 devWriteHooks[x+1](x+1, mem.dev[x+1], p2) 68 } 69 else { 70 p = mem.dev[x] 71 mem.dev[x] = y 72 if(x in devWriteHooks) 73 devWriteHooks[x](x, y, p) 74 } 75 } 76 77 // main eval loop 78 79 function uxnstart(startaddr, vmTrace) { 80 pc = startaddr & 65535 81 var instr, src, dst, a, b, c, tmpTrace = !!vmTrace 82 if(!pc) { 83 this.active = false 84 return 0 85 } 86 this.active = true 87 while(instr = mem.ram[pc++]) { 88 bs = (instr & 0x20) ? 1 : 0 //short mode 89 if(instr & 0x40) { //return mode 90 src = mem.rst 91 dst = mem.wst 92 } else { 93 src = mem.wst 94 dst = mem.rst 95 } 96 if(instr & 0x80) { //keep mode 97 km = 1 98 src.pk = src.ptr 99 dst.pk = dst.ptr 100 } else km = 0 101 // main instruction selector 102 switch(instr & 0x1f) { 103 // Stack 104 case 0x00: /* LIT */ PUSH(src, PEEK(pc)); pc += bs + 1; break; 105 case 0x01: /* INC */ PUSH(src, POP(src) + 1); break; 106 case 0x02: /* POP */ POP(src); break; 107 case 0x03: /* NIP */ a = POP(src); POP(src); PUSH(src, a); break; 108 case 0x04: /* SWP */ a = POP(src); b = POP(src); PUSH(src, a); PUSH(src, b); break; 109 case 0x05: /* ROT */ a = POP(src); b = POP(src); c = POP(src); PUSH(src, b); PUSH(src, a); PUSH(src, c); break; 110 case 0x06: /* DUP */ a = POP(src); PUSH(src, a); PUSH(src, a); break; 111 case 0x07: /* OVR */ a = POP(src); b = POP(src); PUSH(src, b); PUSH(src, a); PUSH(src, b); break; 112 // Logic 113 case 0x08: /* EQU */ a = POP(src); b = POP(src); PUSH8(src, 0|(b == a)); break; 114 case 0x09: /* NEQ */ a = POP(src); b = POP(src); PUSH8(src, 0|(b != a)); break; 115 case 0x0a: /* GTH */ a = POP(src); b = POP(src); PUSH8(src, 0|(b > a)); break; 116 case 0x0b: /* LTH */ a = POP(src); b = POP(src); PUSH8(src, 0|(b < a)); break; 117 case 0x0c: /* JMP */ pc = JUMP(POP(src), pc); break; 118 case 0x0d: /* JCN */ a = POP(src); if(POP8(src)) pc = JUMP(a, pc); break; 119 case 0x0e: /* JSR */ PUSH16(dst, pc); pc = JUMP(POP(src), pc); break; 120 case 0x0f: /* STH */ PUSH(dst, POP(src)); break; 121 // Memory 122 case 0x10: /* LDZ */ PUSH(src, PEEK(POP8(src))); break; 123 case 0x11: /* STZ */ POKE(POP8(src), POP(src)); break; 124 case 0x12: /* LDR */ PUSH(src, PEEK(pc + rel(POP8(src)))); break; 125 case 0x13: /* STR */ POKE(pc + rel(POP8(src)), POP(src)); break; 126 case 0x14: /* LDA */ PUSH(src, PEEK(POP16(src))); break; 127 case 0x15: /* STA */ POKE(POP16(src), POP(src)); break; 128 case 0x16: /* DEI */ PUSH(src, DEVR(POP8(src))); break; 129 case 0x17: /* DEO */ a = POP8(src); b = POP(src); if(a===this.tracePort) tmpTrace=true;else DEVW(a, b); break; 130 // Arithmetic 131 case 0x18: /* ADD */ a = POP(src); b = POP(src); PUSH(src, b + a); break; 132 case 0x19: /* SUB */ a = POP(src); b = POP(src); PUSH(src, b - a); break; 133 case 0x1a: /* MUL */ a = POP(src); b = POP(src); PUSH(src, b * a); break; 134 case 0x1b: /* DIV */ a = POP(src); b = POP(src); if(!a) trapout(3); PUSH(src, 0|(b / a)); break; 135 case 0x1c: /* AND */ a = POP(src); b = POP(src); PUSH(src, b & a); break; 136 case 0x1d: /* ORA */ a = POP(src); b = POP(src); PUSH(src, b | a); break; 137 case 0x1e: /* EOR */ a = POP(src); b = POP(src); PUSH(src, b ^ a); break; 138 case 0x1f: /* SFT */ a = POP8(src); b = POP(src); PUSH(src, b >> (a & 0x0f) << ((a & 0xf0) >> 4)); break; 139 } 140 if(vmTrace || tmpTrace) { 141 console.log('Step: ', pc, 'Instruction:', instr.toString(16), 'Base instr:', (instr&0x1f).toString(16), 'stackptr:', src.ptr, 'stack:', src.dat) 142 tmpTrace = vmTrace 143 } 144 } 145 } 146 147 return { 148 active: false, 149 boot: function() { 150 this.active = false 151 this.tracePort = 0x0e 152 this.memDevOffset = 0 153 mem = initMem() 154 bs = 0 155 pc = 0 156 km = 0 157 devWriteHooks = {} 158 devReadHooks = {} 159 }, 160 load: function(prog, addr) { 161 if(!addr) addr = 0x100 162 for(var i=0, l=prog.length;i<l;i++) 163 mem.ram[addr+i] = prog[i] 164 }, 165 start: uxnstart, 166 setdev: function(port, val) { 167 mem.dev[port] = val 168 if(this.memDevOffset) 169 mem.ram[this.memDevOffset+port] = val 170 }, 171 getdev: function(port) {return mem.dev[port]}, 172 getram: function(start, length) {return mem.ram.subarray(start, start+length)}, 173 setram: function(addr, bVal) {mem.ram[addr] = bVal}, 174 setramblk: function(addr, blk) {mem.ram.set(blk, addr)}, 175 setWriteHook: function(port, cb) { 176 if(!cb) cb = function(){} 177 devWriteHooks[port] = cb 178 }, 179 setReadHook: function(devPort, cb) { 180 if(!cb) cb = function(){} 181 devReadHooks[devPort] = cb 182 }, 183 triggerVector: function(devId) { 184 var basePos = (devId >> 4) << 4 185 var targetAddr = (mem.dev[basePos] << 8) | mem.dev[basePos + 1] 186 if(targetAddr) uxnstart(targetAddr) 187 } 188 } 189 }