esp32emu / js /xtensa.js
HelloSun's picture
ESP32 emulator update
8d577f5 verified
Raw History Blame Contribute Delete
53.7 kB
// 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;
}
}