| """
|
| mips_core.py -- R4300i (MIPS III) integer core, every data operation through
|
| the mips_units API (golden or neural). Brick 1 scope: full integer ISA with
|
| delay slots and branch-likely nullification, 64-bit registers with MIPS III
|
| 32-bit sign-extension semantics, HI/LO, COP0 stubs (Count/Compare/Status),
|
| big-endian memory through a pluggable bus. No TLB (KSEG0/KSEG1 direct map),
|
| no FPU (brick 2), overflow traps treated as non-trapping adds.
|
| """
|
| from mips_units import GoldenUnits, ALU, OPC_CLASSES, FUNCT_CLASSES, REGIMM_CLASSES
|
| from fpu import SoftFP, F32, F64
|
|
|
| M32 = 0xFFFFFFFF
|
| M64 = 0xFFFFFFFFFFFFFFFF
|
|
|
|
|
| def sx8(v): return v - 0x100 if v & 0x80 else v
|
| def sx16(v): return v - 0x10000 if v & 0x8000 else v
|
| def sx32(v): return (v | 0xFFFFFFFF00000000) if v & 0x80000000 else (v & M32)
|
| def sx32i(v): return v - 0x100000000 if v & 0x80000000 else v
|
|
|
|
|
| class CPUError(Exception):
|
| pass
|
|
|
|
|
| class TLBMiss(Exception):
|
| pass
|
|
|
|
|
| class R4300:
|
| def __init__(self, bus, units=None):
|
| self.bus = bus
|
| self.u = units or GoldenUnits()
|
| self.alu = ALU(self.u)
|
| self.r = [0] * 32
|
| self.hi = self.lo = 0
|
| self.pc = 0; self.next_pc = 4
|
| self.cop0 = {9: 0, 11: 0, 12: 0, 13: 0}
|
| self.ll_bit = 0
|
| self.instr_count = 0
|
| self.tlb = [None] * 32
|
| self.int_pending = 0
|
|
|
| self.fp = SoftFP(self.alu, self.u)
|
| self.fpr = [0] * 32
|
| self.fcsr = 0
|
| self.fcr0 = 0x00000B00
|
|
|
|
|
| self._rdram = bus.rdram
|
| self._rdram_len = len(bus.rdram)
|
|
|
|
|
| def set_r(self, i, v):
|
| if i:
|
| self.r[i] = v & M64
|
|
|
| def sgn64(self, v): return v - (1 << 64) if v >> 63 else v
|
|
|
| def slt_signed(self, a, b):
|
| """signed a < b via subtract wiring over verified slices"""
|
| sa, sb = a >> 63, b >> 63
|
| if sa != sb:
|
| return int(sa == 1)
|
| diff, _ = self.alu.sub(a, b, 64)
|
| return int(diff >> 63)
|
| def slt_unsigned(self, a, b):
|
| _, borrow = self.alu.sub(a, b, 64)
|
| return borrow
|
|
|
| def mrd(self, a, n):
|
| return self.bus.read(self.translate(a), n)
|
| def mwr(self, a, v, n):
|
| self.bus.write(self.translate(a), v, n)
|
|
|
|
|
| def exception(self, code, vaddr=None, refill=False):
|
| st = self.cop0.get(12, 0)
|
| cause = self.cop0.get(13, 0) & ~0x8000007C
|
| cause |= (code & 31) << 2
|
| if not (st & 2):
|
|
|
|
|
|
|
| epc = getattr(self, "exc_pc", self.pc)
|
| if getattr(self, "exc_bd", False):
|
| epc = (epc - 4) & M64
|
| cause |= 0x80000000
|
| self.cop0[14] = epc
|
| self.cop0[12] = st | 2
|
| vec = 0x80000000 if refill else 0x80000180
|
| else:
|
| vec = 0x80000180
|
| self.cop0[13] = cause
|
| if vaddr is not None:
|
| self.cop0[8] = vaddr & M32
|
| self.cop0[10] = (self.cop0.get(10, 0) & 0x1FFF) | (vaddr & 0xFFFFE000)
|
| self.pc = vec
|
| self.next_pc = (vec + 4) & M64
|
|
|
| def check_interrupts(self):
|
| st = self.cop0[12]
|
| if (st & 7) != 1:
|
| return False
|
| cause = self.cop0[13]
|
|
|
| ip = ((cause >> 8) & 0x03) | self.int_pending
|
|
|
| if (ip & ((st >> 8) & 0xFF)):
|
| self.cop0[13] = (cause & ~0xFF00) | ((ip & 0xFF) << 8)
|
| self.exc_pc = self.pc
|
| self.exc_bd = self.next_pc != ((self.pc + 4) & M64)
|
| self.exception(0)
|
| return True
|
| return False
|
|
|
| def tick_timer(self):
|
| cnt = (self.cop0.get(9, 0) + 1) & M32
|
| self.cop0[9] = cnt
|
| if cnt == self.cop0.get(11, 0):
|
| self.int_pending |= 0x80
|
|
|
|
|
| def translate(self, va):
|
|
|
|
|
|
|
| if (va >> 62) == 0b10:
|
| return va & 0xFFFFFFFF
|
| va &= M32
|
| if 0x80000000 <= va < 0xC0000000:
|
| return va
|
| vpn = va >> 13
|
| for ent in self.tlb:
|
| if ent is None:
|
| continue
|
| hi, mask, lo0, lo1 = ent
|
| pm = (mask >> 13) | 0
|
| if (hi >> 13) & ~pm == vpn & ~pm:
|
| odd = (va >> 12) & 1 if mask == 0 else (va >> (13 + (mask.bit_length() - 13))) & 1
|
| lo = lo1 if odd else lo0
|
| if not (lo & 2):
|
| break
|
| pfn = (lo >> 6) & 0xFFFFF
|
| off_bits = 12 if mask == 0 else mask.bit_length()
|
| return ((pfn << 12) | (va & ((1 << off_bits) - 1))) | 0x80000000
|
| self.exception(2, vaddr=va, refill=True)
|
| raise TLBMiss()
|
|
|
|
|
| def step(self):
|
| self.instr_count += 1
|
|
|
| c0 = self.cop0
|
| cnt = (c0[9] + 1) & M32
|
| c0[9] = cnt
|
| if cnt == c0[11]:
|
| self.int_pending |= 0x80
|
| if self.check_interrupts():
|
| return
|
| try:
|
| self._step_body()
|
| except TLBMiss:
|
| pass
|
|
|
| def _step_body(self):
|
| self.exc_pc = self.pc
|
| self.exc_bd = self.next_pc != ((self.pc + 4) & M64)
|
|
|
|
|
| pc32 = self.pc & M32
|
| if 0x80000000 <= pc32 < 0xC0000000:
|
| p = pc32 & 0x1FFFFFFF
|
| if p + 4 <= self._rdram_len:
|
| rd = self._rdram
|
| instr = (rd[p] << 24) | (rd[p + 1] << 16) | (rd[p + 2] << 8) | rd[p + 3]
|
| else:
|
| instr = self.mrd(pc32, 4)
|
| else:
|
| instr = self.mrd(pc32, 4)
|
| cur_pc = self.pc
|
| self.pc = self.next_pc
|
| self.next_pc = (self.pc + 4) & M64
|
| if instr == 0:
|
| return
|
| op = instr >> 26
|
| rs, rt = (instr >> 21) & 31, (instr >> 16) & 31
|
| rd, sa = (instr >> 11) & 31, (instr >> 6) & 31
|
| imm = instr & 0xFFFF
|
| simm = sx16(imm)
|
| cls = OPC_CLASSES[self.u.opc(op)]
|
| R = self.r; u = self.u; alu = self.alu
|
|
|
| if cls == "SPECIAL":
|
| f = FUNCT_CLASSES[self.u.funct(instr & 0x3F)]
|
| self.special(f, instr, rs, rt, rd, sa, cur_pc)
|
| elif cls == "REGIMM":
|
| g = REGIMM_CLASSES[self.u.regimm(rt)]
|
| cond = {"BLTZ": R[rs] >> 63, "BGEZ": 1 - (R[rs] >> 63),
|
| "BLTZL": R[rs] >> 63, "BGEZL": 1 - (R[rs] >> 63),
|
| "BLTZAL": R[rs] >> 63, "BGEZAL": 1 - (R[rs] >> 63)}.get(g)
|
| if cond is None:
|
| raise CPUError(f"regimm {rt:#x}")
|
| if g.endswith("AL"):
|
| self.set_r(31, (cur_pc + 8) & M64)
|
| if cond:
|
| self.next_pc = (cur_pc + 4 + (simm << 2)) & M64
|
| elif g.endswith("L") and not g.endswith("AL"):
|
| self.pc = self.next_pc
|
| self.next_pc = (self.pc + 4) & M64
|
| elif cls == "J" or cls == "JAL":
|
| if cls == "JAL":
|
| self.set_r(31, (cur_pc + 8) & M64)
|
| self.next_pc = ((cur_pc + 4) & ~0x0FFFFFFF) | ((instr & 0x3FFFFFF) << 2)
|
| elif cls in ("BEQ", "BNE", "BLEZ", "BGTZ", "BEQL", "BNEL", "BLEZL", "BGTZL"):
|
| a, b = R[rs], R[rt]
|
| taken = {"BEQ": a == b, "BNE": a != b,
|
| "BLEZ": (a >> 63) or a == 0, "BGTZ": not ((a >> 63) or a == 0),
|
| "BEQL": a == b, "BNEL": a != b,
|
| "BLEZL": (a >> 63) or a == 0, "BGTZL": not ((a >> 63) or a == 0)}[cls]
|
| if taken:
|
| self.next_pc = (cur_pc + 4 + (simm << 2)) & M64
|
| elif cls.endswith("L"):
|
| self.pc = self.next_pc
|
| self.next_pc = (self.pc + 4) & M64
|
| elif cls in ("ADDI", "ADDIU"):
|
| res, _ = alu.add(R[rs] & M32, simm & M32, 32)
|
| self.set_r(rt, sx32(res))
|
| elif cls in ("DADDI", "DADDIU"):
|
| res, _ = alu.add(R[rs], simm & M64, 64)
|
| self.set_r(rt, res)
|
| elif cls == "SLTI":
|
| self.set_r(rt, self.slt_signed(R[rs], simm & M64))
|
| elif cls == "SLTIU":
|
| self.set_r(rt, self.slt_unsigned(R[rs], simm & M64))
|
| elif cls == "ANDI":
|
| self.set_r(rt, alu.logic("AND8", R[rs], imm, 64))
|
| elif cls == "ORI":
|
| self.set_r(rt, alu.logic("OR8", R[rs], imm, 64))
|
| elif cls == "XORI":
|
| self.set_r(rt, alu.logic("XOR8", R[rs], imm, 64))
|
| elif cls == "LUI":
|
| self.set_r(rt, sx32(imm << 16))
|
| elif cls == "COP0":
|
| self.cop0_op(instr, rs, rt, rd)
|
| elif cls == "COP1":
|
| self.cop1_op(instr, rs, rt, rd, cur_pc)
|
| elif cls in ("CACHE", "LL", "SC", "LLD", "SCD"):
|
| if cls == "CACHE":
|
|
|
|
|
|
|
|
|
|
|
|
|
| if rt == 0x0D:
|
| line = ((R[rs] + simm) & M32) & ~0x1F
|
| for off in range(0, 32, 4):
|
| self.mwr((line + off) & M32, 0, 4)
|
| elif cls == "LL":
|
| a = (R[rs] + simm) & M64
|
| self.set_r(rt, sx32(self.mrd(a, 4))); self.ll_bit = 1
|
| elif cls == "SC":
|
| a = (R[rs] + simm) & M64
|
| if self.ll_bit:
|
| self.mwr(a, R[rt] & M32, 4)
|
| self.set_r(rt, self.ll_bit); self.ll_bit = 0
|
| elif cls == "LLD":
|
| a = (R[rs] + simm) & M64
|
| self.set_r(rt, self.mrd(a, 8)); self.ll_bit = 1
|
| elif cls == "SCD":
|
| a = (R[rs] + simm) & M64
|
| if self.ll_bit:
|
| self.mwr(a, R[rt], 8)
|
| self.set_r(rt, self.ll_bit); self.ll_bit = 0
|
| elif cls in ("LB", "LBU", "LH", "LHU", "LW", "LWU", "LD",
|
| "SB", "SH", "SW", "SD"):
|
| a = (R[rs] + simm) & M64
|
| if cls == "LB": self.set_r(rt, sx8(self.mrd(a, 1)) & M64)
|
| elif cls == "LBU": self.set_r(rt, self.mrd(a, 1))
|
| elif cls == "LH": self.set_r(rt, sx16(self.mrd(a, 2)) & M64)
|
| elif cls == "LHU": self.set_r(rt, self.mrd(a, 2))
|
| elif cls == "LW": self.set_r(rt, sx32(self.mrd(a, 4)))
|
| elif cls == "LWU": self.set_r(rt, self.mrd(a, 4))
|
| elif cls == "LD": self.set_r(rt, self.mrd(a, 8))
|
| elif cls == "SB": self.mwr(a, R[rt] & 0xFF, 1)
|
| elif cls == "SH": self.mwr(a, R[rt] & 0xFFFF, 2)
|
| elif cls == "SW": self.mwr(a, R[rt] & M32, 4)
|
| else: self.mwr(a, R[rt], 8)
|
| elif cls in ("LWL", "LWR", "SWL", "SWR"):
|
| a = (R[rs] + simm) & M64
|
| al = a & ~3; off = a & 3
|
| word = self.mrd(al, 4)
|
| if cls == "LWL":
|
| v = ((word << (8 * off)) & M32)
|
| keep = (1 << (8 * off)) - 1 if off else 0
|
| self.set_r(rt, sx32((R[rt] & keep) | v))
|
| elif cls == "LWR":
|
| v = word >> (8 * (3 - off))
|
| keep = (M32 << (8 * (off + 1))) & M32 if off != 3 else 0
|
| self.set_r(rt, sx32(((R[rt] & M32) & keep) | v))
|
| elif cls == "SWL":
|
| v = (R[rt] & M32) >> (8 * off)
|
| keep = ~((M32 >> (8 * off))) & M32
|
| self.mwr(al, (word & keep) | v, 4)
|
| else:
|
| v = ((R[rt] & M32) << (8 * (3 - off))) & M32
|
| keep = (1 << (8 * (3 - off))) - 1
|
| self.mwr(al, (word & keep) | v, 4)
|
| elif cls in ("LDL", "LDR", "SDL", "SDR"):
|
| a = (R[rs] + simm) & M64
|
| al = a & ~7; off = a & 7
|
| dw = self.mrd(al, 8)
|
| if cls == "LDL":
|
| v = (dw << (8 * off)) & M64
|
| keep = (1 << (8 * off)) - 1 if off else 0
|
| self.set_r(rt, (R[rt] & keep) | v)
|
| elif cls == "LDR":
|
| v = dw >> (8 * (7 - off))
|
| keep = (M64 << (8 * (off + 1))) & M64 if off != 7 else 0
|
| self.set_r(rt, (R[rt] & keep) | v)
|
| elif cls == "SDL":
|
| v = R[rt] >> (8 * off)
|
| keep = ~(M64 >> (8 * off)) & M64
|
| self.mwr(al, (dw & keep) | v, 8)
|
| else:
|
| v = (R[rt] << (8 * (7 - off))) & M64
|
| keep = (1 << (8 * (7 - off))) - 1
|
| self.mwr(al, (dw & keep) | v, 8)
|
| elif cls in ("LWC1", "SWC1", "LDC1", "SDC1"):
|
| a = (R[rs] + simm) & M64
|
| if cls == "LWC1": self.set_fpr32(rt, self.mrd(a, 4))
|
| elif cls == "LDC1": self.set_fpr64(rt, self.mrd(a, 8))
|
| elif cls == "SWC1": self.mwr(a, self.get_fpr32(rt), 4)
|
| else: self.mwr(a, self.get_fpr64(rt), 8)
|
| else:
|
| raise CPUError(f"opcode {op:#04x} ({cls}) at {cur_pc:#x}")
|
|
|
| def special(self, f, instr, rs, rt, rd, sa, cur_pc):
|
| R = self.r; alu = self.alu
|
| if f == "SLL": self.set_r(rd, sx32(alu.shl(R[rt] & M32, sa, 32)))
|
| elif f == "SRL": self.set_r(rd, sx32(alu.shr(R[rt] & M32, sa, 32)))
|
| elif f == "SRA": self.set_r(rd, sx32(alu.shr(R[rt] & M32, sa, 32, arith=True)))
|
| elif f == "SLLV": self.set_r(rd, sx32(alu.shl(R[rt] & M32, R[rs] & 31, 32)))
|
| elif f == "SRLV": self.set_r(rd, sx32(alu.shr(R[rt] & M32, R[rs] & 31, 32)))
|
| elif f == "SRAV": self.set_r(rd, sx32(alu.shr(R[rt] & M32, R[rs] & 31, 32, arith=True)))
|
| elif f == "DSLL": self.set_r(rd, alu.shl(R[rt], sa, 64))
|
| elif f == "DSRL": self.set_r(rd, alu.shr(R[rt], sa, 64))
|
| elif f == "DSRA": self.set_r(rd, alu.shr(R[rt], sa, 64, arith=True))
|
| elif f == "DSLL32": self.set_r(rd, alu.shl(R[rt], sa + 32, 64))
|
| elif f == "DSRL32": self.set_r(rd, alu.shr(R[rt], sa + 32, 64))
|
| elif f == "DSRA32": self.set_r(rd, alu.shr(R[rt], sa + 32, 64, arith=True))
|
| elif f == "DSLLV": self.set_r(rd, alu.shl(R[rt], R[rs] & 63, 64))
|
| elif f == "DSRLV": self.set_r(rd, alu.shr(R[rt], R[rs] & 63, 64))
|
| elif f == "DSRAV": self.set_r(rd, alu.shr(R[rt], R[rs] & 63, 64, arith=True))
|
| elif f == "JR": self.next_pc = R[rs]
|
| elif f == "JALR":
|
| self.set_r(rd, (cur_pc + 8) & M64); self.next_pc = R[rs]
|
| elif f in ("ADD", "ADDU"):
|
| res, _ = alu.add(R[rs] & M32, R[rt] & M32, 32)
|
| self.set_r(rd, sx32(res))
|
| elif f in ("SUB", "SUBU"):
|
| res, _ = alu.sub(R[rs] & M32, R[rt] & M32, 32)
|
| self.set_r(rd, sx32(res))
|
| elif f in ("DADD", "DADDU"):
|
| res, _ = alu.add(R[rs], R[rt], 64); self.set_r(rd, res)
|
| elif f in ("DSUB", "DSUBU"):
|
| res, _ = alu.sub(R[rs], R[rt], 64); self.set_r(rd, res)
|
| elif f == "AND": self.set_r(rd, alu.logic("AND8", R[rs], R[rt], 64))
|
| elif f == "OR": self.set_r(rd, alu.logic("OR8", R[rs], R[rt], 64))
|
| elif f == "XOR": self.set_r(rd, alu.logic("XOR8", R[rs], R[rt], 64))
|
| elif f == "NOR": self.set_r(rd, alu.logic("NOR8", R[rs], R[rt], 64))
|
| elif f == "SLT": self.set_r(rd, self.slt_signed(R[rs], R[rt]))
|
| elif f == "SLTU": self.set_r(rd, self.slt_unsigned(R[rs], R[rt]))
|
| elif f == "MFHI": self.set_r(rd, self.hi)
|
| elif f == "MFLO": self.set_r(rd, self.lo)
|
| elif f == "MTHI": self.hi = R[rs]
|
| elif f == "MTLO": self.lo = R[rs]
|
| elif f in ("MULT", "MULTU"):
|
| a, b = R[rs] & M32, R[rt] & M32
|
| if f == "MULT":
|
| sa_, sb_ = sx32i(a), sx32i(b)
|
| full = self.alu.mul(abs(sa_), abs(sb_), 32)
|
| if (sa_ < 0) != (sb_ < 0):
|
| full = (-full) & M64
|
| else:
|
| full = self.alu.mul(a, b, 32)
|
| self.lo = sx32(full & M32); self.hi = sx32(full >> 32)
|
| elif f in ("DMULT", "DMULTU"):
|
| a, b = R[rs], R[rt]
|
| if f == "DMULT":
|
| sa_ = self.sgn64(a); sb_ = self.sgn64(b)
|
| full = self.alu.mul(abs(sa_), abs(sb_), 64)
|
| if (sa_ < 0) != (sb_ < 0):
|
| full = (-full) & ((1 << 128) - 1)
|
| else:
|
| full = self.alu.mul(a, b, 64)
|
| self.lo = full & M64; self.hi = (full >> 64) & M64
|
| elif f in ("DIV", "DIVU"):
|
| a, b = R[rs] & M32, R[rt] & M32
|
| if f == "DIV":
|
| sa_, sb_ = sx32i(a), sx32i(b)
|
| if sb_ == 0:
|
| self.lo = self.r[rs]; self.hi = 0
|
| else:
|
| q0, r0 = self.alu.divmod_(abs(sa_), abs(sb_), 32)
|
| q = -q0 if (sa_ < 0) != (sb_ < 0) else q0
|
| r2 = -r0 if sa_ < 0 else r0
|
| self.lo = sx32(q & M32); self.hi = sx32(r2 & M32)
|
| else:
|
| if b == 0:
|
| self.lo = self.r[rs]; self.hi = 0
|
| else:
|
| q, r2 = self.alu.divmod_(a, b, 32)
|
| self.lo = sx32(q); self.hi = sx32(r2)
|
| elif f in ("DDIV", "DDIVU"):
|
| a, b = R[rs], R[rt]
|
| if f == "DDIV":
|
| sa_, sb_ = self.sgn64(a), self.sgn64(b)
|
| if sb_ == 0:
|
| self.lo = a; self.hi = 0
|
| else:
|
| q0, r0 = self.alu.divmod_(abs(sa_), abs(sb_), 64)
|
| q = -q0 if (sa_ < 0) != (sb_ < 0) else q0
|
| r2 = -r0 if sa_ < 0 else r0
|
| self.lo = q & M64; self.hi = r2 & M64
|
| else:
|
| if b == 0:
|
| self.lo = a; self.hi = 0
|
| else:
|
| q, r2 = self.alu.divmod_(a, b, 64)
|
| self.lo = q; self.hi = r2
|
| elif f == "SYNC": pass
|
| elif f == "TEQ":
|
| if R[rs] == R[rt]:
|
| self.exception(13)
|
| elif f == "SYSCALL":
|
| self.exception(8)
|
| elif f == "BREAK":
|
| self.exception(9)
|
| else:
|
| raise CPUError(f"funct {instr & 0x3F:#04x} ({f}) at {cur_pc:#x}")
|
|
|
|
|
| def _fr(self): return (self.cop0.get(12, 0) >> 26) & 1
|
|
|
| def get_fpr32(self, i):
|
| if self._fr():
|
| return self.fpr[i] & M32
|
| base = self.fpr[i & ~1]
|
| return (base >> 32) & M32 if i & 1 else base & M32
|
| def set_fpr32(self, i, v):
|
| v &= M32
|
| if self._fr():
|
| self.fpr[i] = (self.fpr[i] & 0xFFFFFFFF00000000) | v
|
| else:
|
| j = i & ~1
|
| if i & 1:
|
| self.fpr[j] = (self.fpr[j] & M32) | (v << 32)
|
| else:
|
| self.fpr[j] = (self.fpr[j] & 0xFFFFFFFF00000000) | v
|
| def get_fpr64(self, i):
|
| return self.fpr[i if self._fr() else i & ~1]
|
| def set_fpr64(self, i, v):
|
| self.fpr[i if self._fr() else i & ~1] = v & M64
|
|
|
| def cop1_op(self, instr, rs, rt, rd, cur_pc):
|
| ft, fs, fd = rt, rd, (instr >> 6) & 31
|
| funct = instr & 0x3F
|
| fp = self.fp
|
| fp.rm = self.fcsr & 3
|
| if rs == 0:
|
| self.set_r(rt, sx32(self.get_fpr32(rd))); return
|
| if rs == 1:
|
| self.set_r(rt, self.get_fpr64(rd)); return
|
| if rs == 4:
|
| self.set_fpr32(rd, self.r[rt]); return
|
| if rs == 5:
|
| self.set_fpr64(rd, self.r[rt]); return
|
| if rs == 2:
|
| self.set_r(rt, sx32(self.fcr0 if rd == 0 else self.fcsr)); return
|
| if rs == 6:
|
| if rd == 31: self.fcsr = self.r[rt] & M32
|
| return
|
| if rs == 8:
|
| d = sx16(instr & 0xFFFF)
|
| taken = bool(self.fcsr & (1 << 23)) == bool(rt & 1)
|
| if taken:
|
| self.next_pc = (cur_pc + 4 + (d << 2)) & M64
|
| elif rt & 2:
|
| self.pc = self.next_pc
|
| self.next_pc = (self.pc + 4) & M64
|
| return
|
| if rs in (16, 17):
|
| f = F32 if rs == 16 else F64
|
| get = self.get_fpr32 if rs == 16 else self.get_fpr64
|
| put = self.set_fpr32 if rs == 16 else self.set_fpr64
|
| a = get(fs)
|
| if funct in (0, 1, 2, 3):
|
| b = get(ft)
|
| r = (fp.add(a, b, f) if funct == 0 else
|
| fp.add(a, b, f, sub=True) if funct == 1 else
|
| fp.mul(a, b, f) if funct == 2 else fp.div(a, b, f))
|
| put(fd, r); return
|
| if funct == 4: put(fd, fp.sqrt(a, f)); return
|
| if funct == 5: put(fd, a & ~(1 << (f["W"] - 1))); return
|
| if funct == 6: put(fd, a); return
|
| if funct == 7: put(fd, a ^ (1 << (f["W"] - 1))); return
|
| if 8 <= funct <= 15:
|
| width = 64 if funct < 12 else 32
|
| rm = (funct - 8) & 3
|
| v = fp.f2i(a, f, width, rm)
|
| (self.set_fpr64 if width == 64 else self.set_fpr32)(fd, v)
|
| return
|
| if funct == 32:
|
| self.set_fpr32(fd, fp.cvt_f2f(a, f, F32)); return
|
| if funct == 33:
|
| self.set_fpr64(fd, fp.cvt_f2f(a, f, F64)); return
|
| if funct == 36:
|
| self.set_fpr32(fd, fp.f2i(a, f, 32, self.fcsr & 3)); return
|
| if funct == 37:
|
| self.set_fpr64(fd, fp.f2i(a, f, 64, self.fcsr & 3)); return
|
| if funct >= 0x30:
|
| lt, eq, un = fp.cmp(a, get(ft), f)
|
| cond = funct & 0xF
|
| c = ((cond >> 2) & 1 & lt) | ((cond >> 1) & 1 & eq) | (cond & 1 & un)
|
| self.fcsr = (self.fcsr & ~(1 << 23)) | (c << 23)
|
| return
|
| raise CPUError(f"cop1 {('S','D')[rs-16]} funct {funct:#x}")
|
| if rs in (20, 21):
|
| width = 32 if rs == 20 else 64
|
| v = self.get_fpr32(fs) if rs == 20 else self.get_fpr64(fs)
|
| if funct == 32:
|
| self.set_fpr32(fd, fp.i2f(v, width, F32)); return
|
| if funct == 33:
|
| self.set_fpr64(fd, fp.i2f(v, width, F64)); return
|
| raise CPUError(f"cop1 W/L funct {funct:#x}")
|
| raise CPUError(f"cop1 rs={rs}")
|
|
|
| def cop0_op(self, instr, rs, rt, rd):
|
| if rs == 0:
|
| v = self.cop0.get(rd, 0) & M32
|
| if rd == 13:
|
| v = (v & ~0xFC00) | ((self.int_pending & 0xFC) << 8)
|
| self.set_r(rt, sx32(v))
|
| elif rs == 4:
|
| self.cop0[rd] = self.r[rt] & M32
|
| if rd == 11:
|
| self.int_pending &= ~0x80
|
| elif rs == 1:
|
| self.set_r(rt, self.cop0.get(rd, 0))
|
| elif rs == 5:
|
| self.cop0[rd] = self.r[rt]
|
| elif rs & 0x10:
|
| f = instr & 0x3F
|
| if f == 0x18:
|
| self.pc = self.cop0.get(14, 0) & M64
|
| self.next_pc = (self.pc + 4) & M64
|
| self.cop0[12] = self.cop0.get(12, 0) & ~2
|
| elif f == 0x02:
|
| i = self.cop0.get(0, 0) & 31
|
| self.tlb[i] = (self.cop0.get(10, 0), self.cop0.get(5, 0),
|
| self.cop0.get(2, 0), self.cop0.get(3, 0))
|
| elif f == 0x06:
|
| i = (self.instr_count % 24) + 8
|
| self.tlb[i & 31] = (self.cop0.get(10, 0), self.cop0.get(5, 0),
|
| self.cop0.get(2, 0), self.cop0.get(3, 0))
|
| elif f == 0x08:
|
| self.cop0[0] = 0x80000000
|
| vpn = self.cop0.get(10, 0) >> 13
|
| for i, ent in enumerate(self.tlb):
|
| if ent and (ent[0] >> 13) == vpn:
|
| self.cop0[0] = i
|
| break
|
| elif f == 0x01:
|
| ent = self.tlb[self.cop0.get(0, 0) & 31]
|
| if ent:
|
| (self.cop0[10], self.cop0[5],
|
| self.cop0[2], self.cop0[3]) = ent
|
| else:
|
| raise CPUError(f"cop0 funct {f:#x}")
|
| else:
|
| raise CPUError(f"cop0 rs={rs}")
|
|
|