# vu-disasm — standalone PS2 VU0/VU1 microcode disassembler

A minimal, dependency-free disassembler for Sony PlayStation 2 Vector
Unit microcode (VU0 and VU1). Reads raw bytes, prints annotated
assembly. No emulator, no runtime, no Qt — just a CLI binary you can
drop into a reverse-engineering pipeline.

Built for the `xenosaga3-decompiled` project to cover the gap that
Ghidra's stock MIPS SLEIGH leaves around the `.vutext` section. Generic
enough to use against any PS2 ELF's VU microcode region or any VU1
microprogram extracted from a bigfile.

## Lineage and licence

The decoding tables and per-opcode dispatch are lifted from
[PCSX2](https://github.com/PCSX2/pcsx2) (`pcsx2/DebugTools/DisVU*`),
with the live-emulator-state hooks (`VU0.VF[i].f.w`, `CpuVU1->IsInterpreter`,
etc.) replaced by name-only register printing. The decode logic itself
is unchanged. SPDX-License-Identifier: **GPL-3.0+**, inherited from
PCSX2.

Every modified source file carries a header noting the PCSX2 origin
and the nature of the standalone modification.

## Build

```sh
make             # → ./vu-disasm
make smoke       # runs tests/smoke_test.sh
```

Requires a C++17 compiler. No external libraries.

## Usage

```sh
./vu-disasm <bytes-file> [--base-va 0x003172f0] [--vu 0|1]
```

`<bytes-file>` is raw VU microcode bytes (typically the `.vutext`
section of an EE ELF, or a microprogram blob from a bigfile). VU
instructions are 64-bit pairs: lower 32 bits = LOWER op, upper 32 bits
= UPPER op (executed in parallel).

`--base-va` sets the virtual address printed in the first column.
Defaults to 0.

`--vu` chooses the VU unit (0 or 1). Both use the same instruction
set but PCSX2 keeps separate dispatch tables; the output is
indistinguishable in practice, but VU1 specifies different immediate
sign-extension for a few opcodes. Defaults to 1 (the more common case
for microprograms).

## Output format

One annotated line per 64-bit instruction pair:

```
0x003172f0: 70000000 00000000  | NOP    | NOP
0x003172f8: 6c0410dc 00f00000  | ADD.xyzw  vf02, vf03, vf04 | NOP
```

Columns:
1. Virtual address.
2. Raw instruction bytes (upper 32 then lower 32, little-endian on disc → printed in machine order).
3. UPPER mnemonic (parallel-issued, runs in the upper pipeline).
4. LOWER mnemonic (parallel-issued, runs in the lower pipeline).

The E/M/D/T flag bits in the upper instruction's top nybble are decoded
inline with the UPPER mnemonic when set.

## What it doesn't do

* No control-flow analysis — emits a flat dump. Wrap with a script if
  you want branch/call following.
* No symbol resolution — your pipeline should annotate the output
  separately if you have a symbol map.
* No live register values — that requires a running emulator, which
  defeats the purpose of a standalone disassembler. If you want that,
  use PCSX2's debugger directly.

## Files

```
vu-disasm/
├── LICENSE                  # GPL-3.0
├── Makefile
├── README.md                # this file
├── src/
│   ├── DisVU0Micro.cpp      } from PCSX2, modified
│   ├── DisVU1Micro.cpp      }
│   ├── DisVUmicro.h         }
│   ├── DisVUops.h           }
│   ├── DisASM.h             }
│   ├── debug_shim.h         # our Debug.h replacement
│   ├── vu_state_stub.h      # our VU.h / VUmicro.h replacement
│   ├── cop2_tables.cpp      # COP2_REG_FP / _CTL / VFnames (from PCSX2 DisR5900asm.cpp)
│   └── main.cpp             # CLI driver
└── tests/
    └── smoke_test.sh        # known-good VU encodings
```
