esop

Essential Stack-Operated Phone (concept)
git clone git://git.luxferre.top/esop.git
Log | Files | Refs | README | LICENSE

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 }