From 26042708236d2d2594638dd3d7a2c46372e1e78e Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 15 Oct 2025 19:17:41 -0500 Subject: [PATCH] pvs_disassemble: add more concise disassembly --- regs/build.sh | 1 + regs/pvs_bits/pvs_source_operand_bits.txt | 24 +-- regs/pvs_disassemble.py | 210 +++++++++++++++++++++- regs/pvs_src_bits.py | 22 +++ 4 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 regs/pvs_src_bits.py diff --git a/regs/build.sh b/regs/build.sh index 9a14d02..110568c 100644 --- a/regs/build.sh +++ b/regs/build.sh @@ -3,4 +3,5 @@ set -eux python parse_pvs.py PVS_DST pvs_bits/pvs_opcode_and_destination_operand.txt > pvs_dst.py python parse_pvs.py PVS_SRC pvs_bits/pvs_source_operand.txt > pvs_src.py python parse_pvs_bits.py pvs_bits/pvs_opcode_and_destination_operand_bits.txt > pvs_dst_bits.py +python parse_pvs_bits.py pvs_bits/pvs_source_operand_bits.txt > pvs_src_bits.py python 3d_registers.py python 3d_registers.txt > registers_lookup.py diff --git a/regs/pvs_bits/pvs_source_operand_bits.txt b/regs/pvs_bits/pvs_source_operand_bits.txt index e1b19f9..3d63729 100644 --- a/regs/pvs_bits/pvs_source_operand_bits.txt +++ b/regs/pvs_bits/pvs_source_operand_bits.txt @@ -1,15 +1,15 @@ -PVS_SRC_OFFSET: -PVS_SRC_REG_TEMPORARY = 0 Intermediate storage -PVS_SRC_REG_INPUT = 1 Input Vertex Storage -PVS_SRC_REG_CONSTANT = 2 Constant State Storage -PVS_SRC_REG_ALT_TEMPORARY = 3 Alternate Intermediate Storage -PVS_SRC_ADDR_SEL: -PVS_SRC_SELECT_X = 0 Select X Component -PVS_SRC_SELECT_Y = 1 Select Y Component -PVS_SRC_SELECT_Z = 2 Select Z Component -PVS_SRC_SELECT_W = 3 Select W Component -PVS_SRC_SELECT_FORCE_0 = 4 Force Component to 0.0 -PVS_SRC_SELECT_FORCE_1 = 5 Force Component to 1.0 +PVS_SRC_REG_TYPE: +PVS_SRC_REG_TEMPORARY = 0 +PVS_SRC_REG_INPUT = 1 +PVS_SRC_REG_CONSTANT = 2 +PVS_SRC_REG_ALT_TEMPORARY = 3 +PVS_SRC_SWIZZLE_SEL: +PVS_SRC_SELECT_X = 0 +PVS_SRC_SELECT_Y = 1 +PVS_SRC_SELECT_Z = 2 +PVS_SRC_SELECT_W = 3 +PVS_SRC_SELECT_FORCE_0 = 4 +PVS_SRC_SELECT_FORCE_1 = 5 PVS_SRC_ADDR_MODE: Absolute addressing = 0 Relative addressing using A0 register = 1 diff --git a/regs/pvs_disassemble.py b/regs/pvs_disassemble.py index ccde6a1..0fac1c3 100644 --- a/regs/pvs_disassemble.py +++ b/regs/pvs_disassemble.py @@ -1,7 +1,10 @@ import pvs_src +import pvs_src_bits import pvs_dst import pvs_dst_bits from pprint import pprint +import itertools +from functools import partial code = [ 0x00f00203, @@ -33,11 +36,84 @@ code = [ 0x01248001, ] +code = [ + 0x00f00003, + 0x00d10022, + 0x01248022, + 0x01248022, + 0x00f02003, + 0x00d10022, + 0x01248022, + 0x01248022, + 0x00100004, + 0x01ff0002, + 0x01ff0020, + 0x01ff2000, + 0x00100006, + 0x01ff0000, + 0x01248000, + 0x01248000, + 0x00100004, + 0x01ff0000, + 0x01ff4022, + 0x01ff6022, + 0x00100050, + 0x00000000, + 0x01248000, + 0x01248000, + 0x00f00204, + 0x0165a000, + 0x01690001, + 0x01240000, +] + +code = [ + 0x00f00003, + 0x00d10022, + 0x01248022, + 0x01248022, + 0x00f02003, + 0x00d10022, + 0x01248022, + 0x01248022, + 0x00100004, + 0x01ff0002, + 0x01ff0020, + 0x01ff2000, + 0x00100006, + 0x01ff0000, + 0x01248000, + 0x01248000, + 0x00100004, + 0x01ff0000, + 0x01ff4022, + 0x01ff6022, + 0x00200051, + 0x00000000, + 0x01248000, + 0x01248000, + 0x00100050, + 0x00000000, + 0x01248000, + 0x01248000, + 0x00600002, + 0x01c8e001, + 0x01c9e000, + 0x01248000, + 0x00500204, + 0x1fe72001, + 0x01e70000, + 0x01e72000, + 0x00a00204, + 0x0138e001, + 0x0138e000, + 0x017ae000, +] + def out(level, *args): sys.stdout.write(" " * level + " ".join(args)) def parse_code(code): - ix = 0 pvs_dst_maxlen = max(len(k) for k, _ in pvs_dst.table) @@ -75,4 +151,134 @@ def parse_code(code): ix += 4 -parse_code(code) +#parse_code(code) + +def dst_swizzle_from_we(dst_op): + table = [ + (pvs_dst.WE_X, "x"), + (pvs_dst.WE_Y, "y"), + (pvs_dst.WE_Z, "z"), + (pvs_dst.WE_W, "w"), + ] + return ''.join( + c for func, c in table + if func(dst_op) + ) + +_op_substitutions = [ + ("DOT_PRODUCT", "DOT"), + ("MULTIPLY_ADD", "MAD"), + ("FRACTION", "FRAC"), + ("MULTIPLY", "MUL"), + ("MAXMIUM", "MAX"), + ("MINIMUM", "MIN"), +] + +def op_substitutions(s): + for k, v in _op_substitutions: + if k in s: + s = s.replace(k, v) + return s + +def parse_dst_op(dst_op): + opcode = pvs_dst.OPCODE(dst_op) + math_inst = pvs_dst.MATH_INST(dst_op) + macro_inst = pvs_dst.MACRO_INST(dst_op) + reg_type = pvs_dst.REG_TYPE(dst_op) + addr_mode = ( (pvs_dst.ADDR_MODE_1(dst_op) << 1) + | (pvs_dst.ADDR_MODE_0(dst_op) << 0)) + offset = pvs_dst.OFFSET(dst_op) + pred_enable = pvs_dst.PRED_ENABLE(dst_op) + pred_sense = pvs_dst.PRED_SENSE(dst_op) + dual_math_op = pvs_dst.DUAL_MATH_OP(dst_op) + addr_sel = pvs_dst.ADDR_SEL(dst_op) + + assert addr_mode == 0 + assert macro_inst == 0 + assert pred_enable == 0 + assert pred_sense == 0 + assert dual_math_op == 0 + assert addr_sel == 0 + + parts = [] + + reg_str = pvs_dst_bits.PVS_DST_REG[reg_type] + reg_str = reg_str.replace("TEMPORARY", "TEMP").lower() + we_swizzle = dst_swizzle_from_we(dst_op) + parts.append(f"{reg_str}[{offset}].{we_swizzle}") + + if math_inst: + parts.append(op_substitutions(pvs_dst_bits.MATH_OPCODE[opcode])) + else: + parts.append(op_substitutions(pvs_dst_bits.VECTOR_OPCODE[opcode])) + + return parts + +def src_swizzle_from_src_op(src_op): + swizzle_x = pvs_src.SWIZZLE_X(src_op) + swizzle_y = pvs_src.SWIZZLE_Y(src_op) + swizzle_z = pvs_src.SWIZZLE_Z(src_op) + swizzle_w = pvs_src.SWIZZLE_W(src_op) + modifier_x = pvs_src.MODIFIER_X(src_op) + modifier_y = pvs_src.MODIFIER_Y(src_op) + modifier_z = pvs_src.MODIFIER_Z(src_op) + modifier_w = pvs_src.MODIFIER_W(src_op) + + modifiers = [ + '' if modifier == 0 else '-' + for modifier + in [modifier_x, modifier_y, modifier_z, modifier_w] + ] + src_swizzle_select = [ + 'x', 'y', 'z', 'w', '0', '1', 'h', '_' + ] + swizzles = [ + src_swizzle_select[swizzle] + for swizzle + in [swizzle_x, swizzle_y, swizzle_z, swizzle_w] + ] + + return ''.join(map(''.join, zip(modifiers, swizzles))) + +def parse_src_op(src_op): + reg_type = pvs_src.REG_TYPE(src_op) + abs_xyzw = pvs_src.ABS_XYZW(src_op) + addr_mode = ( (pvs_src.ADDR_MODE_1(src_op) << 1) + | (pvs_src.ADDR_MODE_0(src_op) << 0)) + offset = pvs_src.OFFSET(src_op) + addr_sel = pvs_src.ADDR_SEL(src_op) + + assert addr_mode == 0 + assert addr_sel == 0 + + reg_type_str = pvs_src_bits.PVS_SRC_REG_TYPE[reg_type] + reg_type_str = reg_type_str.removeprefix("PVS_SRC_REG_") + reg_type_str = reg_type_str.replace("TEMPORARY", "TEMP") + reg_type_str = reg_type_str.replace("CONSTANT", "CONST") + reg_type_str = reg_type_str.lower() + + src_swizzle = src_swizzle_from_src_op(src_op) + if abs_xyzw: + src_swizzle = f"abs({src_swizzle})" + + return f"{reg_type_str}[{offset}].{src_swizzle}" + +def parse_instruction(instruction): + dst_op = instruction[0] + src_op0 = instruction[1] + src_op1 = instruction[2] + src_op2 = instruction[3] + + dst, op, *rest = itertools.chain( + parse_dst_op(dst_op), + [ + parse_src_op(src_op0), + parse_src_op(src_op1), + parse_src_op(src_op2), + ] + ) + + print(dst.ljust(12), "=", op.ljust(9), " ".join(map(lambda s: s.ljust(17), rest))) + +for i in range(len(code) // 4): + parse_instruction(code[i*4:i*4+4]) diff --git a/regs/pvs_src_bits.py b/regs/pvs_src_bits.py new file mode 100644 index 0000000..75e59a0 --- /dev/null +++ b/regs/pvs_src_bits.py @@ -0,0 +1,22 @@ +PVS_SRC_REG_TYPE = { + 0: "PVS_SRC_REG_TEMPORARY", + 1: "PVS_SRC_REG_INPUT", + 2: "PVS_SRC_REG_CONSTANT", + 3: "PVS_SRC_REG_ALT_TEMPORARY", +} + +PVS_SRC_SWIZZLE_SEL = { + 0: "PVS_SRC_SELECT_X", + 1: "PVS_SRC_SELECT_Y", + 2: "PVS_SRC_SELECT_Z", + 3: "PVS_SRC_SELECT_W", + 4: "PVS_SRC_SELECT_FORCE_0", + 5: "PVS_SRC_SELECT_FORCE_1", +} + +PVS_SRC_ADDR_MODE = { + 0: "Absolute addressing", + 1: "Relative addressing using A0 register", + 2: "Relative addressing using I0 register", +} +