Files
project-solana/games/rac1/tools/vu_isa.py
T
2026-10-13 20:04:31 -04:00

149 lines
6.5 KiB
Python

"""vu_isa.py: decoding of PS2 VU micro-mode instructions (shared by vu_recomp.py).
A VU micro instruction is a 64-bit pair: the lower word (at the lower address)
holds the lower-unit op (load/store, integer, branch, DIV/EFU...), the upper
word the FMAC op plus the I/E/M/D/T bits. With the I bit set the lower word is
a float immediate for the I register (LOI).
"""
XYZW = "xyzw"
def dest(w):
d = (w >> 21) & 15
return [i for i in range(4) if d & (8 >> i)]
def dstr(w):
return "".join(XYZW[i] for i in dest(w))
class Up:
"""decoded upper op: name, kind, fd/fs/ft, comps, bc ('x'..'w', 'q', 'i' or None), acc (writes ACC)"""
def __init__(self, name, w, **kw):
self.name, self.w = name, w
self.comps = dest(w)
self.ft, self.fs, self.fd = (w >> 16) & 31, (w >> 11) & 31, (w >> 6) & 31
self.bc = None
self.__dict__.update(kw)
def text(self):
return "%s.%s ft=%d fs=%d fd=%d%s" % (self.name, dstr(self.w), self.ft, self.fs, self.fd,
(" bc=" + self.bc) if self.bc else "")
_ARITH1 = {0x00: "add", 0x04: "sub", 0x08: "madd", 0x0C: "msub", 0x10: "max", 0x14: "mini", 0x18: "mul"}
_S1 = {0x1C: ("mul", "q"), 0x1D: ("max", "i"), 0x1E: ("mul", "i"), 0x1F: ("mini", "i"),
0x20: ("add", "q"), 0x21: ("madd", "q"), 0x22: ("add", "i"), 0x23: ("madd", "i"),
0x24: ("sub", "q"), 0x25: ("msub", "q"), 0x26: ("sub", "i"), 0x27: ("msub", "i"),
0x28: ("add", None), 0x29: ("madd", None), 0x2A: ("mul", None), 0x2B: ("max", None),
0x2C: ("sub", None), 0x2D: ("msub", None), 0x2F: ("mini", None)}
_ARITH2 = {0x00: "add", 0x04: "sub", 0x08: "madd", 0x0C: "msub", 0x18: "mul"}
_S2 = {0x1C: ("mul", "q"), 0x20: ("add", "q"), 0x21: ("madd", "q"), 0x22: ("add", "i"), 0x23: ("madd", "i"),
0x24: ("sub", "q"), 0x25: ("msub", "q"), 0x26: ("sub", "i"), 0x27: ("msub", "i"), 0x1E: ("mul", "i"),
0x28: ("add", None), 0x29: ("madd", None), 0x2A: ("mul", None), 0x2C: ("sub", None), 0x2D: ("msub", None)}
def decode_upper(w):
op = w & 0x3F
if op < 0x3C:
if op < 0x1C:
base = op & 0x3C
if base in _ARITH1:
return Up(_ARITH1[base], w, kind="arith", bc=XYZW[op & 3], acc=False)
if op in _S1:
n, bc = _S1[op]
return Up(n, w, kind="arith", bc=bc, acc=False)
if op == 0x2E:
return Up("opmsub", w, kind="opmsub", acc=False)
return Up("?up%02X" % op, w, kind="bad")
op2 = ((w >> 6) & 31) << 2 | (w & 3)
if op2 < 0x1C and (op2 & 0x3C) in _ARITH2 and op2 not in range(0x10, 0x18):
return Up(_ARITH2[op2 & 0x3C] + "a", w, kind="arith", bc=XYZW[op2 & 3], acc=True, base=_ARITH2[op2 & 0x3C])
if op2 in _S2:
n, bc = _S2[op2]
return Up(n + "a", w, kind="arith", bc=bc, acc=True, base=n)
if 0x10 <= op2 <= 0x13:
return Up("itof%d" % (0, 4, 12, 15)[op2 & 3], w, kind="itof", shift=(0, 4, 12, 15)[op2 & 3])
if 0x14 <= op2 <= 0x17:
return Up("ftoi%d" % (0, 4, 12, 15)[op2 & 3], w, kind="ftoi", shift=(0, 4, 12, 15)[op2 & 3])
if op2 == 0x1D: return Up("abs", w, kind="abs")
if op2 == 0x1F: return Up("clipw", w, kind="clip")
if op2 == 0x2E: return Up("opmula", w, kind="opmula")
if op2 == 0x2F: return Up("nop", w, kind="nop")
return Up("?up2_%02X" % op2, w, kind="bad")
class Lo:
def __init__(self, name, w, **kw):
self.name, self.w = name, w
self.comps = dest(w)
self.it, self.is_, self.id_ = (w >> 16) & 31, (w >> 11) & 31, (w >> 6) & 31
self.ft, self.fs, self.fd = self.it, self.is_, self.id_
self.fsf, self.ftf = (w >> 21) & 3, (w >> 23) & 3
self.__dict__.update(kw)
def text(self):
return "%s.%s it=%d is=%d id=%d %s" % (self.name, dstr(self.w), self.it, self.is_, self.id_,
" ".join("%s=%s" % (k, getattr(self, k)) for k in ("imm",) if hasattr(self, k)))
def s11(w):
v = w & 0x7FF
return v - 0x800 if v & 0x400 else v
_LOWER_S2 = {0x30: "move", 0x31: "mr32", 0x34: "lqi", 0x35: "sqi", 0x36: "lqd", 0x37: "sqd", 0x38: "div",
0x39: "sqrt", 0x3A: "rsqrt", 0x3B: "waitq", 0x3C: "mtir", 0x3D: "mfir", 0x3E: "ilwr", 0x3F: "iswr",
0x40: "rnext", 0x41: "rget", 0x42: "rinit", 0x43: "rxor", 0x64: "mfp", 0x68: "xtop", 0x69: "xitop",
0x6C: "xgkick", 0x70: "esadd", 0x71: "ersadd", 0x72: "eleng", 0x73: "erleng", 0x74: "eatanxy",
0x75: "eatanxz", 0x76: "esum", 0x78: "esqrt", 0x79: "ersqrt", 0x7A: "ercpr", 0x7B: "waitp",
0x7C: "esin", 0x7D: "eatan", 0x7E: "eexp"}
_LOWER = {0x00: "lq", 0x01: "sq", 0x04: "ilw", 0x05: "isw", 0x08: "iaddiu", 0x09: "isubiu", 0x10: "fceq",
0x11: "fcset", 0x12: "fcand", 0x13: "fcor", 0x14: "fseq", 0x15: "fsset", 0x16: "fsand", 0x17: "fsor",
0x18: "fmeq", 0x1A: "fmand", 0x1B: "fmor", 0x1C: "fcget", 0x20: "b", 0x21: "bal", 0x24: "jr",
0x25: "jalr", 0x28: "ibeq", 0x29: "ibne", 0x2C: "ibltz", 0x2D: "ibgtz", 0x2E: "iblez", 0x2F: "ibgez"}
BRANCHES = {"b", "bal", "jr", "jalr", "ibeq", "ibne", "ibltz", "ibgtz", "iblez", "ibgez"}
def decode_lower(w):
op = w >> 25
if op == 0x40:
f = w & 0x3F
if f < 0x3C:
n = {0x30: "iadd", 0x31: "isub", 0x32: "iaddi", 0x34: "iand", 0x35: "ior"}.get(f)
if n is None:
return Lo("?lo40_%02X" % f, w)
l = Lo(n, w)
if n == "iaddi":
imm = (w >> 6) & 31
l.imm = imm - 32 if imm & 16 else imm
return l
op2 = ((w >> 6) & 31) << 2 | (w & 3)
n = _LOWER_S2.get(op2)
if n is None:
return Lo("?lo2_%02X" % op2, w)
return Lo(n, w)
n = _LOWER.get(op)
if n is None:
return Lo("?lo%02X" % op, w)
l = Lo(n, w)
if n in ("lq", "sq", "ilw", "isw", "b", "bal", "ibeq", "ibne", "ibltz", "ibgtz", "iblez", "ibgez"):
l.imm = s11(w)
elif n in ("iaddiu", "isubiu"):
l.imm = ((w >> 10) & 0x7800) | (w & 0x7FF)
elif n in ("fceq", "fcset", "fcand", "fcor"):
l.imm = w & 0xFFFFFF
elif n in ("fseq", "fsset", "fsand", "fsor"):
l.imm = ((w >> 10) & 0x800) | (w & 0x7FF)
return l
def decode_pair(lo, hi):
"""returns (upper, lower or None, flags dict, i_imm or None)"""
up = decode_upper(hi)
fl = {"I": (hi >> 31) & 1, "E": (hi >> 30) & 1, "M": (hi >> 29) & 1, "D": (hi >> 28) & 1, "T": (hi >> 27) & 1}
if fl["I"]:
return up, None, fl, lo
return up, decode_lower(lo), fl, None