// Xtensa LX6 (ESP32) CPU core interpreter. Windowed registers, loops, MAC16, // 32-bit mul/div, S32C1I, single-precision FPU, boolean option, XEA2 exceptions. export const CAUSE = { ILLEGAL: 0, SYSCALL: 1, IFETCH: 2, LOADSTORE: 3, LEVEL1: 4, ALLOCA: 5, DIV0: 6, PRIV: 8, ALIGN: 9, COPROC0: 32, }; // SR numbers const SR = { LBEG: 0, LEND: 1, LCOUNT: 2, SAR: 3, BR: 4, LITBASE: 5, SCOMPARE1: 12, ACCLO: 16, ACCHI: 17, M0: 32, M1: 33, M2: 34, M3: 35, WINDOWBASE: 72, WINDOWSTART: 73, IBREAKENABLE: 96, MEMCTL: 97, ATOMCTL: 99, DDR: 104, EPC1: 177, EPC7: 183, DEPC: 192, EPS2: 194, EPS7: 199, EXCSAVE1: 209, EXCSAVE7: 215, CPENABLE: 224, INTERRUPT: 226, INTCLEAR: 227, INTENABLE: 228, PS: 230, VECBASE: 231, EXCCAUSE: 232, DEBUGCAUSE: 233, CCOUNT: 234, PRID: 235, ICOUNT: 236, ICOUNTLEVEL: 237, EXCVADDR: 238, CCOMPARE0: 240, CCOMPARE1: 241, CCOMPARE2: 242, MISC0: 244, MISC1: 245, MISC2: 246, MISC3: 247, }; export { SR }; const LEVEL_MASK = [0, 0x000637FF, 0x00380000, 0x28C08800, 0x53000000, 0x84010000, 0, 0x00004000]; const INT_LEVEL = new Uint8Array(32); for (let l = 1; l < 8; l++) for (let b = 0; b < 32; b++) if ((LEVEL_MASK[l] >>> b) & 1) INT_LEVEL[b] = l; const B4CONST = [-1, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 32, 64, 128, 256]; const B4CONSTU = [32768, 65536, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 32, 64, 128, 256]; const PS_MASK = 0x70F3F; const SLOW = 1; const f32 = new Float32Array(1), i32f = new Int32Array(f32.buffer); function mulhu(a, b) { a >>>= 0; b >>>= 0; const a0 = a & 0xFFFF, a1 = a >>> 16, b0 = b & 0xFFFF, b1 = b >>> 16; const t = a1 * b0 + ((a0 * b0) >>> 16); const w1 = (t & 0xFFFF) + a0 * b1; return (a1 * b1 + Math.floor(t / 65536) + Math.floor(w1 / 65536)) | 0; } function mulhs(a, b) { let r = mulhu(a, b); if (a < 0) r = (r - b) | 0; if (b < 0) r = (r - a) | 0; return r; } export class XtensaCPU { constructor(soc, id) { this.soc = soc; this.id = id; this.ar = new Int32Array(64); this.f = new Float32Array(16); this.fi = new Int32Array(this.f.buffer); this.epc = new Int32Array(8); this.eps = new Int32Array(8); this.excsave = new Int32Array(8); this.sr = new Int32Array(256); // misc storage for rarely used SRs this.ccompare = new Int32Array(3); this.rd = null; this.wr = null; this.h8 = null; this.h16 = null; this.h16s = null; this.h32 = null; this.trace = null; this.trp = 0; this.bpAddr = -1; this.bpHit = false; this.bpSkip = false; this.reset(); } reset() { this.ar.fill(0); this.f.fill(0); this.pc = 0x40000400; this.ps = 0x1F; // INTLEVEL=15? ISA reset: INTLEVEL=15, EXCM=1 this.ps = 0x1F; this.wb = 0; this.ws = 1; this.wb4 = 0; this.sar = 0; this.br = 0; this.lbeg = 0; this.lend = 0; this.lcount = 0; this.scompare1 = 0; this.acclo = 0; this.acchi = 0; this.m = new Int32Array(4); this.depc = 0; this.exccause = 0; this.excvaddr = 0; this.vecbase = 0x40000000; this.intenable = 0; this.intSet = 0; this.intPins = 0; this.cpenable = 0; this.threadptr = 0; this.fcr = 0; this.fsr = 0; this.atomctl = 0; this.memctl = 0; this.ccompare.fill(0); this.ccountOff = 0; this.cyc = 0; this.cycBase = 0; this.sliceLen = 0; this.n = 0; this.halted = false; this.stalled = false; this.chkInt = true; this.winLim = 3; this.misc = new Int32Array(4); this.ddr = 0; this.icount = 0; this.debugcause = 0; this.timerNext = -1; this.sr.fill(0); } enableTrace() { this.trace = new Int32Array(65536); this.trp = 0; } getTrace(n) { const out = []; for (let i = Math.max(0, this.trp - n); i < this.trp; i++) out.push(this.trace[i & 0xFFFF]); return out; } // ---- time ---- curCycles() { return this.cycBase + this.sliceLen - this.n; } get ccount() { return (this.curCycles() + this.ccountOff) >>> 0; } // ---- interrupts ---- get interrupt() { return (this.intPins | this.intSet) | 0; } setIrqPin(n, level) { if (level) this.intPins |= (1 << n); else this.intPins &= ~(1 << n); this.chkInt = true; if (this.halted) this.checkWake(); } raiseIntSet(mask) { this.intSet |= mask; this.chkInt = true; if (this.halted) this.checkWake(); } deliverNow() { if (this.stalled) return; if (this.pendingLevel()) { this.halted = false; this.pc = this.takeInterrupt(this.pc | 0) | 0; this.wb4 = this.wb << 2; this.chkInt = false; } } checkWake() { if (this.pendingLevel() > 0) this.halted = false; } cintLevel() { let l = this.ps & 15; if ((this.ps & 0x10) && l < 3) l = 3; return l; } pendingLevel() { let p = (this.intPins | this.intSet) & this.intenable; if (!p) return 0; const cl = this.cintLevel(); let best = 0; while (p) { const b = 31 - Math.clz32(p); p &= ~(1 << b); const lv = INT_LEVEL[b]; if (lv > best) best = lv; } return best > cl ? best : 0; } updateWinLim() { // number of free quadrants beyond current window if ((this.ps & 0x40010) !== 0x40000) { this.winLim = 3; return; } const ws = this.ws, wb = this.wb; for (let k = 1; k <= 3; k++) if ((ws >>> ((wb + k) & 15)) & 1) { this.winLim = k - 1; return; } this.winLim = 3; } // ---- exceptions ---- // returns new pc exception(pc, cause, vaddr) { if (vaddr !== undefined) this.excvaddr = vaddr | 0; this.exccause = cause; let vec; if (this.ps & 0x10) { this.depc = pc; vec = 0x3C0; } else { this.epc[1] = pc; vec = (this.ps & 0x20) ? 0x340 : 0x300; } this.ps |= 0x10; this.updateWinLim(); return (this.vecbase + vec) | 0; } windowException(pc, kind, rot) { // kind: 0x00 OF4, 0x40 UF4, 0x80 OF8, 0xC0 UF8, 0x100 OF12, 0x140 UF12 this.rotateTo(this.wb + rot); // PS.OWB = old window base (before rotation): caller passes old in this.oldWb this.ps = (this.ps & ~0xF00) | (this.owb << 8) | 0x10; this.epc[1] = pc; this.updateWinLim(); return (this.vecbase + kind) | 0; } rotateTo(wb) { this.wb = wb & 15; this.wb4 = this.wb << 2; } // check window overflow for an instruction touching registers up to quadrant q; returns -1 if OK else vector pc winCheck(pc, q) { const ws = this.ws, wb = this.wb; let n = 0; for (let k = 1; k <= 15; k++) { if ((ws >>> ((wb + k) & 15)) & 1) { n = k; break; } } if (n === 0 || n > q) return -1; // overflow: rotate by n this.owb = wb; let kind; const next = (() => { for (let k = 1; k <= 15; k++) if ((ws >>> ((wb + n + k) & 15)) & 1) return k; return 15; })(); if (next === 1) kind = 0x00; else if (next === 2) kind = 0x80; else kind = 0x100; return this.windowException(pc, kind, n); } takeInterrupt(pc) { const level = this.pendingLevel(); if (!level) return pc; if (level > 1) { this.epc[level] = pc; this.eps[level] = this.ps; this.ps = (this.ps & ~15) | level | 0x10; this.updateWinLim(); const vec = [0, 0, 0x180, 0x1C0, 0x200, 0x240, 0x280, 0x2C0][level]; if (level === 7) this.intSet &= ~0x4000; return (this.vecbase + vec) | 0; } return this.exception(pc, CAUSE.LEVEL1); } // ---- special registers ---- rsr(n, pc) { switch (n) { case SR.LBEG: return this.lbeg; case SR.LEND: return this.lend; case SR.LCOUNT: return this.lcount; case SR.SAR: return this.sar; case SR.BR: return this.br; case SR.LITBASE: return this.sr[5]; case SR.SCOMPARE1: return this.scompare1; case SR.ACCLO: return this.acclo; case SR.ACCHI: return (this.acchi << 24) >> 24; case 32: case 33: case 34: case 35: return this.m[n - 32]; case SR.WINDOWBASE: return this.wb; case SR.WINDOWSTART: return this.ws; case SR.MEMCTL: return this.memctl; case SR.ATOMCTL: return this.atomctl; case SR.DDR: return this.ddr; case 177: case 178: case 179: case 180: case 181: case 182: case 183: return this.epc[n - 176]; case SR.DEPC: return this.depc; case 194: case 195: case 196: case 197: case 198: case 199: return this.eps[n - 192]; case 209: case 210: case 211: case 212: case 213: case 214: case 215: return this.excsave[n - 208]; case SR.CPENABLE: return this.cpenable; case SR.INTERRUPT: return this.interrupt; case SR.INTENABLE: return this.intenable; case SR.PS: return this.ps; case SR.VECBASE: return this.vecbase; case SR.EXCCAUSE: return this.exccause; case SR.DEBUGCAUSE: return this.debugcause; case SR.CCOUNT: return this.ccount | 0; case SR.PRID: return this.id === 0 ? 0xCDCD : 0xABAB; case SR.ICOUNT: return this.icount; case SR.ICOUNTLEVEL: return this.sr[237]; case SR.EXCVADDR: return this.excvaddr; case 240: case 241: case 242: return this.ccompare[n - 240]; case 244: case 245: case 246: case 247: return this.misc[n - 244]; default: return this.sr[n & 255]; } } wsr(n, v) { v |= 0; switch (n) { case SR.LBEG: this.lbeg = v; break; case SR.LEND: this.lend = v; break; case SR.LCOUNT: this.lcount = v; break; case SR.SAR: this.sar = v & 63; break; case SR.BR: this.br = v & 0xFFFF; break; case SR.LITBASE: this.sr[5] = v; break; case SR.SCOMPARE1: this.scompare1 = v; break; case SR.ACCLO: this.acclo = v; break; case SR.ACCHI: this.acchi = (v << 24) >> 24; break; case 32: case 33: case 34: case 35: this.m[n - 32] = v; break; case SR.WINDOWBASE: this.wb = v & 15; this.wb4 = this.wb << 2; this.updateWinLim(); break; case SR.WINDOWSTART: this.ws = v & 0xFFFF; this.updateWinLim(); break; case SR.MEMCTL: this.memctl = v; break; case SR.ATOMCTL: this.atomctl = v; break; case SR.DDR: this.ddr = v; break; case 177: case 178: case 179: case 180: case 181: case 182: case 183: this.epc[n - 176] = v; break; case SR.DEPC: this.depc = v; break; case 194: case 195: case 196: case 197: case 198: case 199: this.eps[n - 192] = v; break; case 209: case 210: case 211: case 212: case 213: case 214: case 215: this.excsave[n - 208] = v; break; case SR.CPENABLE: this.cpenable = v & 0xFF; break; case SR.INTERRUPT: this.intSet |= v; this.chkInt = true; break; // INTSET case SR.INTCLEAR: this.intSet &= ~v; this.chkInt = true; break; case SR.INTENABLE: this.intenable = v; this.chkInt = true; break; case SR.PS: this.ps = v & PS_MASK; this.updateWinLim(); this.chkInt = true; break; case SR.VECBASE: this.vecbase = v; break; case SR.EXCCAUSE: this.exccause = v; break; case SR.DEBUGCAUSE: this.debugcause = v; break; case SR.CCOUNT: this.ccountOff = (v - this.curCycles()) | 0; this.soc.cpuTimerChanged(this); break; case SR.ICOUNT: this.icount = v; break; case SR.EXCVADDR: this.excvaddr = v; break; case 240: case 241: case 242: this.ccompare[n - 240] = v; this.intSet &= ~(1 << [6, 15, 16][n - 240]); this.chkInt = true; this.soc.cpuTimerChanged(this); break; case 244: case 245: case 246: case 247: this.misc[n - 244] = v; break; case SR.PRID: break; default: this.sr[n & 255] = v; } } // ---- slow memory path ---- ldSlow(ea, size) { ea |= 0; if (size === 1) { const d = this.rd[ea >>> 12]; return d !== SLOW ? this.h8[(ea + d) | 0] : this.slowLoad(ea, 1); } const al = (size - 1); if ((ea & al) === 0) { const d = this.rd[ea >>> 12]; if (d !== SLOW) return size === 4 ? this.h32[((ea + d) | 0) >> 2] : this.h16[((ea + d) | 0) >> 1]; return this.slowLoad(ea, size); } let v = 0; for (let i = size - 1; i >= 0; i--) v = (v << 8) | this.ldSlow((ea + i) | 0, 1); return v; } stSlow(ea, size, val) { ea |= 0; if (size === 1) { const d = this.wr[ea >>> 12]; if (d !== SLOW) this.h8[(ea + d) | 0] = val; else this.slowStore(ea, 1, val & 255); return; } const al = (size - 1); if ((ea & al) === 0) { const d = this.wr[ea >>> 12]; if (d !== SLOW) { if (size === 4) this.h32[((ea + d) | 0) >> 2] = val; else this.h16[((ea + d) | 0) >> 1] = val; return; } this.slowStore(ea, size, val); return; } for (let i = 0; i < size; i++) this.stSlow((ea + i) | 0, 1, (val >>> (8 * i)) & 255); } slowLoad(addr, size) { return this.soc.busRead(this, addr >>> 0, size); } slowStore(addr, size, v) { this.soc.busWrite(this, addr >>> 0, size, v); } fetchSlow(pc) { // assemble instruction bytes through page tables (page crossing) let v = 0; for (let k = 0; k < 3; k++) { const a = (pc + k) >>> 0; const d = this.rd[a >>> 12]; if (d === SLOW) return -1; v |= this.h8[(a + d) | 0] << (8 * k); } return v; } // Run for up to `budget` cycles (instructions). Returns when budget used or halted. run(budget) { const h8 = this.h8, h16 = this.h16, h16s = this.h16s, h32 = this.h32, rd = this.rd, wr = this.wr; const ar = this.ar, f = this.f, fi = this.fi; let pc = this.pc | 0, wb4 = this.wb4, n = budget; this.sliceLen = budget; this.cycBase = this.cyc; this.n = n; const self = this; let inst = 0, op0 = 0, t = 0, s = 0, r = 0, ea = 0, d = 0, v = 0; let tr = this.trace; if (this.halted) { this.cyc += budget; this.n = 0; return; } if (this.stalled) { this.cyc += budget; return; } for (;;) { if (this.chkInt) { this.chkInt = false; if (this.pendingLevel()) { this.pc = pc; this.n = n; pc = this.takeInterrupt(pc) | 0; wb4 = this.wb4; } } if (n <= 0) break; n--; // ---------- fetch ---------- d = rd[pc >>> 12]; if (d === SLOW || (pc & 0xFFF) > 0xFFC) { inst = this.fetchSlow(pc); if (inst < 0) { this.n = n; pc = this.exception(pc, CAUSE.IFETCH, pc) | 0; wb4 = this.wb4; continue; } } else { ea = (pc + d) | 0; inst = h8[ea] | (h8[ea + 1] << 8) | (h8[ea + 2] << 16); } if (tr) { tr[this.trp++ & 0xFFFF] = pc; if (pc === this.bpAddr && !this.bpSkip) { this.bpHit = true; this.pc = pc; this.wb4 = wb4; this.cyc = this.cycBase + (budget - n) - 1; this.n = n + 1; return; } this.bpSkip = false; } op0 = inst & 15; let npc; let jmp = false; if (op0 >= 8) { // ---------------- narrow ---------------- npc = pc + 2; t = (inst >> 4) & 15; s = (inst >> 8) & 15; r = (inst >> 12) & 15; if (this.winLim < 3 && (op0 !== 13 || r === 0)) { const q = (op0 === 12 ? s : op0 === 11 ? Math.max(r, s) : op0 === 8 || op0 === 9 || op0 === 13 ? Math.max(s, t) : Math.max(s, t, r)) >> 2; if (q > this.winLim) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } switch (op0) { case 8: { // L32I.N ea = (ar[(wb4 + s) & 63] + (r << 2)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) v = h32[((ea + d) | 0) >> 2]; else { this.n = n; v = this.ldSlow(ea, 4); } ar[(wb4 + t) & 63] = v; break; } case 9: { // S32I.N ea = (ar[(wb4 + s) & 63] + (r << 2)) | 0; d = wr[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = ar[(wb4 + t) & 63]; else { this.n = n; this.stSlow(ea, 4, ar[(wb4 + t) & 63]); } break; } case 10: ar[(wb4 + r) & 63] = (ar[(wb4 + s) & 63] + ar[(wb4 + t) & 63]) | 0; break; case 11: ar[(wb4 + r) & 63] = (ar[(wb4 + s) & 63] + (t === 0 ? -1 : t)) | 0; break; case 12: { // ST2 if ((t & 8) === 0) { // MOVI.N let imm = ((t & 7) << 4) | r; if (imm > 95) imm -= 128; ar[(wb4 + s) & 63] = imm; } else { const imm6 = ((t & 3) << 4) | r; v = ar[(wb4 + s) & 63]; if (((t >> 2) & 1) === 0 ? v === 0 : v !== 0) { jmp = true; npc = pc + 4 + imm6; } } break; } case 13: { // ST3 if (r === 0) ar[(wb4 + t) & 63] = ar[(wb4 + s) & 63]; // MOV.N else if (r === 15) { if (t === 0) { jmp = true; npc = ar[wb4 & 63]; } // RET.N else if (t === 1) { // RETW.N const a0 = ar[wb4 & 63]; const nn = (a0 >>> 30) & 3; const wbq = this.wb; // illegal check if (nn === 0) { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } this.ws &= ~(1 << wbq); const owb = wbq; // underflow test: WindowStart bit of (wb - n) const tgt = (wbq - nn) & 15; jmp = true; npc = ((pc & 0xC0000000) | (a0 & 0x3FFFFFFF)) | 0; // QEMU computes: top bits of pc kept jmp = true; npc = ((pc & 0xC0000000) | (a0 & 0x3FFFFFFF)) | 0; if (!((this.ws | (1 << wbq)) & (1 << tgt))) { // window underflow: restore ws bit for current (retw is re-executed after handler) this.ws |= (1 << wbq); this.owb = wbq; this.rotateTo(tgt); this.ps = (this.ps & ~0xF00) | (owb << 8) | 0x10; this.epc[1] = pc; this.updateWinLim(); const kind = nn === 1 ? 0x40 : nn === 2 ? 0xC0 : 0x140; pc = (this.vecbase + kind) | 0; wb4 = this.wb4; continue; } this.rotateTo(tgt); wb4 = this.wb4; this.updateWinLim(); } else if (t === 2) { /* BREAK.N */ } else if (t === 3) { /* NOP.N */ } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } // loop end check if (npc === this.lend && this.lcount !== 0 && !jmp) { this.lcount--; npc = this.lbeg; } pc = npc | 0; continue; } // ---------------- 24-bit ---------------- npc = pc + 3; t = (inst >> 4) & 15; s = (inst >> 8) & 15; r = (inst >> 12) & 15; switch (op0) { case 0: { // QRST const op1 = (inst >> 16) & 15, op2 = (inst >> 20) & 15; if (this.winLim < 3) { const q = this.qrstQuad(op1, op2, r, s, t); if (q > this.winLim) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } switch (op1) { case 0: { // RST0 switch (op2) { case 0: { // ST0 switch (r) { case 0: { // SNM0 const m = (inst >> 6) & 3, nn = (inst >> 4) & 3; if (m === 0) { if (this.soc.hooks.size && this.soc.runHook(this, pc, wb4)) { pc = this.hookPc | 0; continue; } this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } if (m === 2) { if (nn === 0) { jmp = true; npc = ar[wb4 & 63]; } // RET else if (nn === 1) { // RETW const a0 = ar[wb4 & 63]; const nb = (a0 >>> 30) & 3; const wbq = this.wb; if (nb === 0) { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } this.ws &= ~(1 << wbq); const tgt = (wbq - nb) & 15; jmp = true; npc = ((pc & 0xC0000000) | (a0 & 0x3FFFFFFF)) | 0; if (!((this.ws | (1 << wbq)) & (1 << tgt))) { this.ws |= (1 << wbq); this.owb = wbq; this.rotateTo(tgt); this.ps = (this.ps & ~0xF00) | (wbq << 8) | 0x10; this.epc[1] = pc; this.updateWinLim(); const kind = nb === 1 ? 0x40 : nb === 2 ? 0xC0 : 0x140; pc = (this.vecbase + kind) | 0; wb4 = this.wb4; continue; } this.rotateTo(tgt); wb4 = this.wb4; this.updateWinLim(); } else if (nn === 2) { jmp = true; npc = ar[(wb4 + s) & 63]; } // JX else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } } else if (m === 3) { // CALLX const tg = ar[(wb4 + s) & 63]; if (nn === 0) { ar[wb4 & 63] = npc; jmp = true; npc = tg; } else { if (this.winLim < 3 && nn > this.winLim) { const nv = this.winCheck(pc, nn); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } this.ps = (this.ps & ~0x30000) | (nn << 16); ar[(wb4 + nn * 4) & 63] = (npc & 0x3FFFFFFF) | (nn << 30); jmp = true; npc = tg; } } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 1: { // MOVSP if ((this.ws & ((1 << ((this.wb - 1) & 15)) | (1 << ((this.wb - 2) & 15)) | (1 << ((this.wb - 3) & 15)))) === 0) { this.n = n; pc = this.exception(pc, CAUSE.ALLOCA) | 0; wb4 = this.wb4; continue; } ar[(wb4 + t) & 63] = ar[(wb4 + s) & 63]; break; } case 2: break; // SYNC group (ISYNC, RSYNC, ESYNC, DSYNC, EXCW, MEMW, EXTW, NOP) case 3: { // RFEI group if (t === 0) { if (s === 0) { this.ps &= ~0x10; jmp = true; npc = this.epc[1]; this.updateWinLim(); this.chkInt = true; } // RFE else if (s === 2) { this.ps &= ~0x10; jmp = true; npc = this.depc; this.updateWinLim(); this.chkInt = true; } // RFDE else if (s === 4 || s === 5) { // RFWO / RFWU this.ps &= ~0x10; if (s === 4) this.ws &= ~(1 << this.wb); else this.ws |= (1 << this.wb); this.rotateTo((this.ps >> 8) & 15); wb4 = this.wb4; jmp = true; npc = this.epc[1]; this.updateWinLim(); this.chkInt = true; } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } } else if (t === 1) { // RFI if (s < 2) { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } this.ps = this.eps[s]; jmp = true; npc = this.epc[s]; this.updateWinLim(); this.chkInt = true; } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 4: // BREAK break; case 5: // SYSCALL this.n = n; pc = this.exception(pc, CAUSE.SYSCALL) | 0; wb4 = this.wb4; continue; case 6: // RSIL ar[(wb4 + t) & 63] = this.ps; this.ps = (this.ps & ~15) | s; this.chkInt = true; break; case 7: { // WAITI this.ps = (this.ps & ~15) | s; this.chkInt = true; this.pc = npc | 0; this.n = n; if (this.pendingLevel() === 0) { this.halted = true; // consume the remainder of the slice this.cyc = this.cycBase + this.sliceLen; this.n = 0; this.pc = npc | 0; this.wb4 = wb4; return; } break; } case 8: case 9: case 10: case 11: { const len = (r === 8 || r === 9) ? 4 : 8; const mask = ((1 << len) - 1) << s; const bits = this.br & mask; const res = (r === 8 || r === 10) ? bits !== 0 : bits === mask; this.br = (this.br & ~(1 << t)) | (res ? (1 << t) : 0); break; } default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 1: ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63] & ar[(wb4 + t) & 63]; break; case 2: ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63] | ar[(wb4 + t) & 63]; break; case 3: ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63] ^ ar[(wb4 + t) & 63]; break; case 4: { // ST1 switch (r) { case 0: this.sar = ar[(wb4 + s) & 63] & 31; break; // SSR case 1: this.sar = 32 - (ar[(wb4 + s) & 63] & 31); break; // SSL case 2: this.sar = (ar[(wb4 + s) & 63] & 3) << 3; break; // SSA8L case 3: this.sar = 32 - ((ar[(wb4 + s) & 63] & 3) << 3); break; // SSA8B case 4: this.sar = s | ((t & 1) << 4); break; // SSAI case 6: ar[(wb4 + t) & 63] = this.soc.rer(this, ar[(wb4 + s) & 63]); break; // RER case 7: this.soc.wer(this, ar[(wb4 + s) & 63], ar[(wb4 + t) & 63]); break; // WER case 8: { // ROTW let k = t; if (k & 8) k -= 16; this.rotateTo(this.wb + k); wb4 = this.wb4; this.updateWinLim(); break; } case 14: { v = ar[(wb4 + s) & 63]; ar[(wb4 + t) & 63] = v === 0 || v === -1 ? 31 : Math.clz32(v < 0 ? ~v : v) - 1; break; } // NSA case 15: { v = ar[(wb4 + s) & 63]; ar[(wb4 + t) & 63] = Math.clz32(v); break; } // NSAU default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 5: { // TLB / region protection ops: no-ops (reads return 0) if (r === 3 || r === 5 || r === 7 || r === 11 || r === 13 || r === 15) ar[(wb4 + t) & 63] = 0; break; } case 6: { // RT0 v = ar[(wb4 + t) & 63]; if (s === 0) ar[(wb4 + r) & 63] = (-v) | 0; else if (s === 1) ar[(wb4 + r) & 63] = v < 0 ? (-v) | 0 : v; else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 8: ar[(wb4 + r) & 63] = (ar[(wb4 + s) & 63] + ar[(wb4 + t) & 63]) | 0; break; case 9: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 1) + ar[(wb4 + t) & 63]) | 0; break; case 10: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 2) + ar[(wb4 + t) & 63]) | 0; break; case 11: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 3) + ar[(wb4 + t) & 63]) | 0; break; case 12: ar[(wb4 + r) & 63] = (ar[(wb4 + s) & 63] - ar[(wb4 + t) & 63]) | 0; break; case 13: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 1) - ar[(wb4 + t) & 63]) | 0; break; case 14: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 2) - ar[(wb4 + t) & 63]) | 0; break; case 15: ar[(wb4 + r) & 63] = ((ar[(wb4 + s) & 63] << 3) - ar[(wb4 + t) & 63]) | 0; break; default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 1: { // RST1 switch (op2) { case 0: case 1: ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63] << (32 - (t | ((op2 & 1) << 4))); break; // SLLI (shift 32 -> sa==0 ->?) case 2: case 3: ar[(wb4 + r) & 63] = ar[(wb4 + t) & 63] >> (s | ((op2 & 1) << 4)); break; // SRAI case 4: ar[(wb4 + r) & 63] = ar[(wb4 + t) & 63] >>> s; break; // SRLI case 6: { // XSR const sn = (r << 4) | s; v = this.rsr(sn, pc); this.n = n; this.wsr(sn, ar[(wb4 + t) & 63]); wb4 = this.wb4; ar[(wb4 + t) & 63] = v; break; } case 8: { // SRC const sh = this.sar; const hi = ar[(wb4 + s) & 63], lo = ar[(wb4 + t) & 63]; ar[(wb4 + r) & 63] = sh === 0 ? lo : sh === 32 ? hi : ((hi << (32 - sh)) | (lo >>> sh)); break; } case 9: ar[(wb4 + r) & 63] = this.sar >= 32 ? 0 : ar[(wb4 + t) & 63] >>> this.sar; break; // SRL case 10: { const sh = 32 - this.sar; ar[(wb4 + r) & 63] = sh >= 32 ? 0 : ar[(wb4 + s) & 63] << sh; break; } // SLL case 11: ar[(wb4 + r) & 63] = ar[(wb4 + t) & 63] >> (this.sar > 31 ? 31 : this.sar); break; // SRA case 12: ar[(wb4 + r) & 63] = Math.imul(ar[(wb4 + s) & 63] & 0xFFFF, ar[(wb4 + t) & 63] & 0xFFFF); break; // MUL16U case 13: ar[(wb4 + r) & 63] = Math.imul((ar[(wb4 + s) & 63] << 16) >> 16, (ar[(wb4 + t) & 63] << 16) >> 16); break; // MUL16S case 15: break; // IMP (cache etc.) nop default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 2: { // RST2 switch (op2) { case 0: case 1: case 2: case 3: case 4: { // boolean ops: ANDB ANDBC ORB ORBC XORB const a = (this.br >> s) & 1, b = (this.br >> t) & 1; let res; if (op2 === 0) res = a & b; else if (op2 === 1) res = a & (b ^ 1); else if (op2 === 2) res = a | b; else if (op2 === 3) res = a | (b ^ 1); else res = a ^ b; this.br = (this.br & ~(1 << r)) | (res << r); break; } case 8: ar[(wb4 + r) & 63] = Math.imul(ar[(wb4 + s) & 63], ar[(wb4 + t) & 63]); break; case 10: ar[(wb4 + r) & 63] = mulhu(ar[(wb4 + s) & 63], ar[(wb4 + t) & 63]); break; case 11: ar[(wb4 + r) & 63] = mulhs(ar[(wb4 + s) & 63], ar[(wb4 + t) & 63]); break; case 12: case 13: case 14: case 15: { const a = ar[(wb4 + s) & 63], b = ar[(wb4 + t) & 63]; if (b === 0) { this.n = n; pc = this.exception(pc, CAUSE.DIV0) | 0; wb4 = this.wb4; continue; } if (op2 === 12) ar[(wb4 + r) & 63] = ((a >>> 0) / (b >>> 0)) | 0; else if (op2 === 13) ar[(wb4 + r) & 63] = (a === -2147483648 && b === -1) ? a : ((a / b) | 0); else if (op2 === 14) ar[(wb4 + r) & 63] = ((a >>> 0) % (b >>> 0)) | 0; else ar[(wb4 + r) & 63] = (a === -2147483648 && b === -1) ? 0 : (a % b) | 0; break; } default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 3: { // RST3 switch (op2) { case 0: { // RSR this.n = n; ar[(wb4 + t) & 63] = this.rsr((r << 4) | s, pc); break; } case 1: { // WSR this.n = n; this.wsr((r << 4) | s, ar[(wb4 + t) & 63]); wb4 = this.wb4; break; } case 2: { // SEXT const sh = 31 - (t + 7); ar[(wb4 + r) & 63] = (ar[(wb4 + s) & 63] << sh) >> sh; break; } case 3: { // CLAMPS const lo = -(1 << (t + 7)), hi = (1 << (t + 7)) - 1; v = ar[(wb4 + s) & 63]; ar[(wb4 + r) & 63] = v < lo ? lo : v > hi ? hi : v; break; } case 4: { const a = ar[(wb4 + s) & 63], b = ar[(wb4 + t) & 63]; ar[(wb4 + r) & 63] = a < b ? a : b; break; } case 5: { const a = ar[(wb4 + s) & 63], b = ar[(wb4 + t) & 63]; ar[(wb4 + r) & 63] = a > b ? a : b; break; } case 6: { const a = ar[(wb4 + s) & 63], b = ar[(wb4 + t) & 63]; ar[(wb4 + r) & 63] = (a >>> 0) < (b >>> 0) ? a : b; break; } case 7: { const a = ar[(wb4 + s) & 63], b = ar[(wb4 + t) & 63]; ar[(wb4 + r) & 63] = (a >>> 0) > (b >>> 0) ? a : b; break; } case 8: if (ar[(wb4 + t) & 63] === 0) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 9: if (ar[(wb4 + t) & 63] !== 0) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 10: if (ar[(wb4 + t) & 63] < 0) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 11: if (ar[(wb4 + t) & 63] >= 0) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 12: if (((this.br >> t) & 1) === 0) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 13: if (((this.br >> t) & 1) === 1) ar[(wb4 + r) & 63] = ar[(wb4 + s) & 63]; break; case 14: { // RUR const un = (s << 4) | t; ar[(wb4 + r) & 63] = un === 231 ? this.threadptr : un === 232 ? this.fcr : un === 233 ? this.fsr : 0; break; } case 15: { // WUR const un = (r << 4) | s; v = ar[(wb4 + t) & 63]; if (un === 231) this.threadptr = v; else if (un === 232) this.fcr = v; else if (un === 233) this.fsr = v; break; } } break; } case 4: case 5: { // EXTUI const sh = s | ((op1 & 1) << 4); const mask = (1 << (op2 + 1)) - 1; ar[(wb4 + r) & 63] = (ar[(wb4 + t) & 63] >>> sh) & mask; break; } case 8: { // LSCX (FP indexed load/store) if (!(this.cpenable & 1)) { this.n = n; pc = this.exception(pc, CAUSE.COPROC0) | 0; wb4 = this.wb4; continue; } ea = (ar[(wb4 + s) & 63] + ar[(wb4 + t) & 63]) | 0; const isStore = (op2 & 4) !== 0; if (!isStore) { d = rd[ea >>> 12]; fi[r] = (d !== SLOW && (ea & 3) === 0) ? h32[((ea + d) | 0) >> 2] : (this.n = n, this.ldSlow(ea, 4)); } else { d = wr[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = fi[r]; else { this.n = n; this.stSlow(ea, 4, fi[r]); } } if (op2 & 1) ar[(wb4 + s) & 63] = ea; // update forms break; } case 9: { // L32E / S32E ea = (ar[(wb4 + s) & 63] + (-64 + (r << 2))) | 0; if (op2 === 0) { d = rd[ea >>> 12]; ar[(wb4 + t) & 63] = (d !== SLOW && (ea & 3) === 0) ? h32[((ea + d) | 0) >> 2] : (this.n = n, this.ldSlow(ea, 4)); } else if (op2 === 4) { d = wr[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = ar[(wb4 + t) & 63]; else { this.n = n; this.stSlow(ea, 4, ar[(wb4 + t) & 63]); } } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 10: { // FP0 if (!(this.cpenable & 1)) { this.n = n; pc = this.exception(pc, CAUSE.COPROC0) | 0; wb4 = this.wb4; continue; } this.fp0(op2, r, s, t, wb4, pc); if (this.fpIllegal) { this.fpIllegal = false; this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 11: { // FP1 (compare/move) if (!(this.cpenable & 1)) { this.n = n; pc = this.exception(pc, CAUSE.COPROC0) | 0; wb4 = this.wb4; continue; } this.fp1(op2, r, s, t, wb4); if (this.fpIllegal) { this.fpIllegal = false; this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 1: { // L32R if (this.winLim < 3 && (t >> 2) > this.winLim) { const nv = this.winCheck(pc, t >> 2); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } const imm16 = inst >>> 8 & 0xFFFF; ea = (((pc + 3) & ~3) + (imm16 << 2) - 0x40000) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) v = h32[((ea + d) | 0) >> 2]; else { this.n = n; v = this.ldSlow(ea, 4); } ar[(wb4 + t) & 63] = v; break; } case 2: { // LSAI if (this.winLim < 3) { const q = (((inst >> 12) & 15) === 10 ? t : Math.max(s, t)) >> 2; if (q > this.winLim) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } const imm8 = (inst >> 16) & 255; switch (r) { case 0: // L8UI ea = (ar[(wb4 + s) & 63] + imm8) | 0; d = rd[ea >>> 12]; if (d !== SLOW) v = h8[(ea + d) | 0]; else { this.n = n; v = this.ldSlow(ea, 1); } ar[(wb4 + t) & 63] = v; break; case 1: // L16UI ea = (ar[(wb4 + s) & 63] + (imm8 << 1)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 1) === 0) v = h16[((ea + d) | 0) >> 1]; else { this.n = n; v = this.ldSlow(ea, 2); } ar[(wb4 + t) & 63] = v; break; case 2: // L32I ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) v = h32[((ea + d) | 0) >> 2]; else { this.n = n; v = this.ldSlow(ea, 4); } ar[(wb4 + t) & 63] = v; break; case 4: // S8I ea = (ar[(wb4 + s) & 63] + imm8) | 0; d = wr[ea >>> 12]; v = ar[(wb4 + t) & 63]; if (d !== SLOW) h8[(ea + d) | 0] = v; else { this.n = n; this.stSlow(ea, 1, v & 255); } break; case 5: // S16I ea = (ar[(wb4 + s) & 63] + (imm8 << 1)) | 0; d = wr[ea >>> 12]; v = ar[(wb4 + t) & 63]; if (d !== SLOW && (ea & 1) === 0) h16[((ea + d) | 0) >> 1] = v; else { this.n = n; this.stSlow(ea, 2, v & 0xFFFF); } break; case 6: // S32I ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; d = wr[ea >>> 12]; v = ar[(wb4 + t) & 63]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = v; else { this.n = n; this.stSlow(ea, 4, v); } break; case 7: break; // cache ops: nop case 9: // L16SI ea = (ar[(wb4 + s) & 63] + (imm8 << 1)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 1) === 0) v = h16s[((ea + d) | 0) >> 1]; else { this.n = n; v = (this.ldSlow(ea, 2) << 16) >> 16; } ar[(wb4 + t) & 63] = v; break; case 10: { // MOVI let imm = imm8 | (s << 8); if (imm & 0x800) imm -= 4096; ar[(wb4 + t) & 63] = imm; break; } case 11: // L32AI ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) v = h32[((ea + d) | 0) >> 2]; else { this.n = n; v = this.ldSlow(ea, 4); } ar[(wb4 + t) & 63] = v; break; case 12: ar[(wb4 + t) & 63] = (ar[(wb4 + s) & 63] + ((imm8 << 24) >> 24)) | 0; break; // ADDI case 13: ar[(wb4 + t) & 63] = (ar[(wb4 + s) & 63] + (((imm8 << 24) >> 24) << 8)) | 0; break; // ADDMI case 14: { // S32C1I ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; d = rd[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) v = h32[((ea + d) | 0) >> 2]; else { this.n = n; v = this.ldSlow(ea, 4); } if (v === this.scompare1) { const nv = ar[(wb4 + t) & 63]; d = wr[ea >>> 12]; if (d !== SLOW) h32[((ea + d) | 0) >> 2] = nv; else { this.n = n; this.slowStore(ea, 4, nv); } } ar[(wb4 + t) & 63] = v; break; } case 15: // S32RI ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; d = wr[ea >>> 12]; v = ar[(wb4 + t) & 63]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = v; else { this.n = n; this.stSlow(ea, 4, v); } break; default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 3: { // LSCI (FP load/store immediate) if (!(this.cpenable & 1)) { this.n = n; pc = this.exception(pc, CAUSE.COPROC0) | 0; wb4 = this.wb4; continue; } if (this.winLim < 3 && (s >> 2) > this.winLim) { const nv = this.winCheck(pc, s >> 2); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } const imm8 = (inst >> 16) & 255; ea = (ar[(wb4 + s) & 63] + (imm8 << 2)) | 0; if (r === 0 || r === 8) { d = rd[ea >>> 12]; fi[t] = (d !== SLOW && (ea & 3) === 0) ? h32[((ea + d) | 0) >> 2] : (this.n = n, this.ldSlow(ea, 4)); } else if (r === 4 || r === 12) { d = wr[ea >>> 12]; if (d !== SLOW && (ea & 3) === 0) h32[((ea + d) | 0) >> 2] = fi[t]; else { this.n = n; this.stSlow(ea, 4, fi[t]); } } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } if (r >= 8) ar[(wb4 + s) & 63] = ea; break; } case 4: { // MAC16 if (this.mac16(inst, wb4, n)) { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } break; } case 5: { // CALLN const nn = (inst >> 4) & 3; let off = inst >> 6; off = (off << 14) >> 14; // sign-extend 18 bits const target = ((pc & ~3) + 4 + (off << 2)) | 0; if (nn === 0) { ar[wb4 & 63] = npc; } else { if (this.winLim < 3 && nn > this.winLim) { const nv = this.winCheck(pc, nn); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } this.ps = (this.ps & ~0x30000) | (nn << 16); ar[(wb4 + nn * 4) & 63] = (npc & 0x3FFFFFFF) | (nn << 30); } jmp = true; npc = target; break; } case 6: { // SI const nn = (inst >> 4) & 3, m = (inst >> 6) & 3; if (nn === 0) { let off = inst >> 6; off = (off << 14) >> 14; jmp = true; npc = (pc + 4 + off) | 0; break; } // J if (this.winLim < 3) { const q = s >> 2; if (q > this.winLim && !(nn === 3 && (m === 0 || (m === 1 && r < 2)))) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } if (nn === 1) { // BZ v = ar[(wb4 + s) & 63]; let off = inst >> 12; off = (off << 20) >> 20; let take; if (m === 0) take = v === 0; else if (m === 1) take = v !== 0; else if (m === 2) take = v < 0; else take = v >= 0; if (take) { jmp = true; npc = (pc + 4 + off) | 0; } break; } if (nn === 2) { // BI0 v = ar[(wb4 + s) & 63]; const off = ((inst >> 16) << 24) >> 24; const c = B4CONST[r]; let take; if (m === 0) take = v === c; else if (m === 1) take = v !== c; else if (m === 2) take = v < c; else take = v >= c; if (take) { jmp = true; npc = (pc + 4 + off) | 0; } break; } // BI1 if (m === 0) { // ENTRY const imm = (inst >> 12) & 0xFFF; // imm12 *8 const callinc = (this.ps >> 16) & 3; if (this.winLim < 3) { // check as access & callinc quadrant const q = Math.max(s >> 2, callinc); if (q > this.winLim) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } const sp = (ar[(wb4 + s) & 63] - (imm << 3)) | 0; ar[(wb4 + (callinc << 2) + (s & 3)) & 63] = sp; this.rotateTo(this.wb + callinc); wb4 = this.wb4; this.ws |= (1 << this.wb); this.updateWinLim(); } else if (m === 1) { // B1 const off = ((inst >> 16) << 24) >> 24; if (r === 0 || r === 1) { // BF/BT const bit = (this.br >> s) & 1; if (bit === r) { jmp = true; npc = (pc + 4 + off) | 0; } } else if (r === 8 || r === 9 || r === 10) { // LOOP, LOOPNEZ, LOOPGTZ v = ar[(wb4 + s) & 63]; this.lcount = (v - 1) | 0; this.lbeg = npc; this.lend = (pc + 4 + (inst >>> 16 & 255)) | 0; if ((r === 9 && v === 0) || (r === 10 && v <= 0)) { npc = this.lend; jmp = true; } } else { this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } } else { // BLTUI / BGEUI v = ar[(wb4 + s) & 63] >>> 0; const off = ((inst >> 16) << 24) >> 24; const c = B4CONSTU[r]; if (m === 2 ? v < c : v >= c) { jmp = true; npc = (pc + 4 + off) | 0; } } break; } case 7: { // B if (this.winLim < 3) { const q = (r === 6 || r === 7 || r === 14 || r === 15 ? s : Math.max(s, t)) >> 2; if (q > this.winLim) { const nv = this.winCheck(pc, q); if (nv >= 0) { pc = nv | 0; wb4 = this.wb4; continue; } } } const a = ar[(wb4 + s) & 63]; const off = ((inst >> 16) << 24) >> 24; let take; switch (r) { case 0: take = (a & ar[(wb4 + t) & 63]) === 0; break; case 1: take = a === ar[(wb4 + t) & 63]; break; case 2: take = a < ar[(wb4 + t) & 63]; break; case 3: take = (a >>> 0) < (ar[(wb4 + t) & 63] >>> 0); break; case 4: take = ((~a) & ar[(wb4 + t) & 63]) === 0; break; case 5: take = ((a >>> (ar[(wb4 + t) & 63] & 31)) & 1) === 0; break; case 6: case 7: take = ((a >>> (t | ((r & 1) << 4))) & 1) === 0; break; case 8: take = (a & ar[(wb4 + t) & 63]) !== 0; break; case 9: take = a !== ar[(wb4 + t) & 63]; break; case 10: take = a >= ar[(wb4 + t) & 63]; break; case 11: take = (a >>> 0) >= (ar[(wb4 + t) & 63] >>> 0); break; case 12: take = ((~a) & ar[(wb4 + t) & 63]) !== 0; break; case 13: take = ((a >>> (ar[(wb4 + t) & 63] & 31)) & 1) === 1; break; default: take = ((a >>> (t | ((r & 1) << 4))) & 1) === 1; break; } if (take) { jmp = true; npc = (pc + 4 + off) | 0; } break; } default: this.n = n; pc = this.exception(pc, CAUSE.ILLEGAL) | 0; wb4 = this.wb4; continue; } if (npc === this.lend && this.lcount !== 0 && !jmp) { this.lcount--; npc = this.lbeg; } pc = npc | 0; } this.pc = pc; this.wb4 = wb4; this.cyc = this.cycBase + (budget - n); this.n = n; } qrstQuad(op1, op2, r, s, t) { let m = 0; switch (op1) { case 0: if (op2 === 0) { m = r === 0 ? s : r === 1 ? Math.max(s, t) : r === 6 ? t : 0; } else if (op2 === 4) { m = r <= 3 ? s : (r === 6 || r === 7 || r === 14 || r === 15) ? Math.max(s, t) : 0; } else if (op2 === 6) m = Math.max(r, t); else m = Math.max(r, s, t); break; case 1: if (op2 <= 1) m = Math.max(r, s); else if (op2 <= 4) m = Math.max(r, t); else if (op2 === 6) m = t; else if (op2 === 9 || op2 === 11) m = Math.max(r, t); else if (op2 === 10) m = Math.max(r, s); else m = Math.max(r, s, t); break; case 2: m = op2 <= 4 ? 0 : Math.max(r, s, t); break; case 3: if (op2 <= 1) m = t; else if (op2 <= 3) m = Math.max(r, s); else if (op2 === 12 || op2 === 13) m = Math.max(r, s); else if (op2 === 14) m = r; else if (op2 === 15) m = t; else m = Math.max(r, s, t); break; case 4: case 5: m = Math.max(r, t); break; case 8: case 9: m = Math.max(s, t); break; case 10: if (op2 === 15) m = t === 4 ? r : t === 5 ? s : 0; else if (op2 >= 8 && op2 <= 11 || op2 === 14) m = r; else if (op2 === 12 || op2 === 13) m = s; break; case 11: m = op2 >= 8 && op2 <= 11 ? t : 0; break; } return m >> 2; } fp0(op2, r, s, t, wb4, pc) { const f = this.f, ar = this.ar; switch (op2) { case 0: f[r] = f[s] + f[t]; break; case 1: f[r] = f[s] - f[t]; break; case 2: f[r] = f[s] * f[t]; break; case 4: f[r] = f[r] + f[s] * f[t]; break; // MADD.S (double product exact, single rounding of sum) case 5: f[r] = f[r] - f[s] * f[t]; break; case 8: case 9: case 10: case 11: case 14: { // ROUND TRUNC FLOOR CEIL UTRUNC const x = f[s] * Math.pow(2, t); let y; if (x !== x) y = 0x7FFFFFFF; else { let z; if (op2 === 8) { z = Math.floor(x + 0.5); if (z - x === 0.5 && z % 2 !== 0) z -= 1; } else if (op2 === 9 || op2 === 14) z = Math.trunc(x); else if (op2 === 10) z = Math.floor(x); else z = Math.ceil(x); if (op2 === 14) y = z >= 4294967296 ? -1 : z < 0 ? 0 : z | 0; else y = z >= 2147483648 ? 0x7FFFFFFF : z < -2147483648 ? -2147483648 : z | 0; } ar[(wb4 + r) & 63] = y; break; } case 12: f[r] = ar[(wb4 + s) & 63] * Math.pow(2, -t); break; // FLOAT.S case 13: f[r] = (ar[(wb4 + s) & 63] >>> 0) * Math.pow(2, -t); break; // UFLOAT.S case 15: { // FP1OP switch (t) { case 0: f[r] = f[s]; break; case 1: f[r] = Math.abs(f[s]); break; case 4: ar[(wb4 + r) & 63] = this.fi[s]; break; // RFR case 5: this.fi[r] = ar[(wb4 + s) & 63]; break; // WFR case 6: f[r] = -f[s]; break; default: this.fpIllegal = true; } break; } default: this.fpIllegal = true; } } fp1(op2, r, s, t, wb4) { const f = this.f, ar = this.ar; const a = f[s], b = f[t]; let res = -1; switch (op2) { case 1: res = (a !== a || b !== b) ? 1 : 0; break; // UN.S case 2: res = (a === b) ? 1 : 0; break; // OEQ case 3: res = (a === b || a !== a || b !== b) ? 1 : 0; break; // UEQ case 4: res = (a < b) ? 1 : 0; break; // OLT case 5: res = (a < b || a !== a || b !== b) ? 1 : 0; break; // ULT case 6: res = (a <= b) ? 1 : 0; break; // OLE case 7: res = (a <= b || a !== a || b !== b) ? 1 : 0; break; // ULE case 8: if (ar[(wb4 + t) & 63] === 0) f[r] = f[s]; return; case 9: if (ar[(wb4 + t) & 63] !== 0) f[r] = f[s]; return; case 10: if (ar[(wb4 + t) & 63] < 0) f[r] = f[s]; return; case 11: if (ar[(wb4 + t) & 63] >= 0) f[r] = f[s]; return; case 12: if (((this.br >> t) & 1) === 0) f[r] = f[s]; return; case 13: if (((this.br >> t) & 1) === 1) f[r] = f[s]; return; default: this.fpIllegal = true; return; } this.br = (this.br & ~(1 << r)) | (res << r); } mac16(inst, wb4, n) { const ar = this.ar; const t = (inst >> 4) & 15, s = (inst >> 8) & 15, r = (inst >> 12) & 15; const op1 = (inst >> 16) & 15, op2 = (inst >> 20) & 15; const xr = (r >> 2) & 1, yr = 2 + ((t >> 2) & 1); if (op2 === 8 || op2 === 9) { // LDINC / LDDEC let a = ar[(wb4 + s) & 63]; if (op2 === 9) a = (a - 4) | 0; const d = this.rd[a >>> 12]; this.m[r & 3] = d !== SLOW ? this.h32[((a + d) | 0) >> 2] : this.slowLoad(a, 4); if (op2 === 8) a = (a + 4) | 0; ar[(wb4 + s) & 63] = a; return false; } const kind = op2 & 7; // 2 dd, 3 ad, 6 da, 7 aa; 0/1 dd+ld ; 4/5 da+ld let x, y; let ld = -1; switch (op2) { case 2: x = this.m[xr]; y = this.m[yr]; break; case 3: x = ar[(wb4 + s) & 63]; y = this.m[yr]; break; case 6: x = this.m[xr]; y = ar[(wb4 + t) & 63]; break; case 7: x = ar[(wb4 + s) & 63]; y = ar[(wb4 + t) & 63]; break; case 0: case 1: x = this.m[xr]; y = this.m[yr]; ld = op2; break; case 4: case 5: x = this.m[xr]; y = ar[(wb4 + t) & 63]; ld = op2 - 4; break; default: return true; } const hx = (op1 & 1) ? (x >> 16) : ((x << 16) >> 16); const hy = (op1 & 2) ? (y >> 16) : ((y << 16) >> 16); const kindOp = op1 >> 2; let p; if (kindOp === 0) { p = ((hx & 0xFFFF) * (hy & 0xFFFF)); } else p = hx * hy; let acc = this.acchi * 4294967296 + (this.acclo >>> 0); if (kindOp === 0 || kindOp === 1) acc = p; else if (kindOp === 2) acc += p; else acc -= p; // wrap to 40 bit signed acc = ((acc % 1099511627776) + 1099511627776) % 1099511627776; if (acc >= 549755813888) acc -= 1099511627776; const lo = acc % 4294967296; const lo32 = ((lo + 4294967296) % 4294967296) | 0; this.acclo = lo32; this.acchi = Math.floor(acc / 4294967296) << 24 >> 24; if (ld >= 0) { // LDINC (op2 0 / 4) or LDDEC (1 / 5) on as into m[r&3] let a = ar[(wb4 + s) & 63]; if (ld === 1) a = (a - 4) | 0; const d = this.rd[a >>> 12]; this.m[r & 3] = d !== SLOW ? this.h32[((a + d) | 0) >> 2] : this.slowLoad(a, 4); if (ld === 0) a = (a + 4) | 0; ar[(wb4 + s) & 63] = a; } return false; } }