[Docs] Land the PPC / Xbox 360 export reference tables
These were sitting in stash@{0} ("On canary_experimental: doc", 2026-04-11) as
untracked files only -- no tracked change was ever stashed with them, so they
were one `git stash drop` away from being lost:
docs/ppc_instructions.{json,md} PPC instruction reference
docs/xbox360_exports.{json,md} XAM / xboxkrnl export tables
docs/xex2_format.md XEX2 container format
tools/generate_export_docs.py the generator behind them
xex2_format.md in particular is the spec a static XEX reader would need to
pull the title's XACH achievement table and .rdata tables off the disc.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
13577
docs/ppc_instructions.json
Normal file
13577
docs/ppc_instructions.json
Normal file
File diff suppressed because it is too large
Load Diff
547
docs/ppc_instructions.md
Normal file
547
docs/ppc_instructions.md
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
# Xenia Canary — PowerPC Instruction Reference
|
||||||
|
|
||||||
|
Complete catalog of all **455 PowerPC instructions** implemented in the Xenia (canary)
|
||||||
|
Xbox 360 emulator. These instructions are defined in `tools/ppc-instructions.xml` and
|
||||||
|
code-generated into the emitter/decoder/disassembler pipeline.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
PPC Binary → Decoder (ppc_opcode_lookup_gen.cc)
|
||||||
|
→ Frontend Emit (ppc_emit_*.cc) → HIR (intermediate representation)
|
||||||
|
→ Backend Sequences (x64_seq_*.cc / a64_seq_*.cc) → Host Machine Code
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Files
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
|------|------|
|
||||||
|
| `tools/ppc-instructions.xml` | Master instruction definitions (source of truth) |
|
||||||
|
| `tools/ppc-table-gen.py` | Code generator |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_opcode.h` | Generated enum (`PPCOpcode`) |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_opcode_table_gen.cc` | Generated opcode info table |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_opcode_lookup_gen.cc` | Generated instruction decoder |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_opcode_disasm_gen.cc` | Generated disassembler |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_emit_alu.cc` | Integer/ALU emission (~68 handlers) |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_emit_control.cc` | Branch/control emission (~28 handlers) |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_emit_memory.cc` | Load/store emission (~80 handlers) |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_emit_fpu.cc` | Floating-point emission (~40 handlers) |
|
||||||
|
| `src/xenia/cpu/ppc/ppc_emit_altivec.cc` | Vector/VMX emission (~306 handlers) |
|
||||||
|
|
||||||
|
### Backend Targets
|
||||||
|
|
||||||
|
| Backend | Directory |
|
||||||
|
|---------|-----------|
|
||||||
|
| x86-64 | `src/xenia/cpu/backend/x64/` |
|
||||||
|
| ARM64 | `src/xenia/cpu/backend/a64/` |
|
||||||
|
|
||||||
|
### Instruction Encoding Formats
|
||||||
|
|
||||||
|
**27 formats:** `A`, `B`, `D`, `DCBZ`, `DS`, `I`, `M`, `MD`, `MDS`, `SC`, `VA`, `VC`, `VX`, `VX128`, `VX128_1`, `VX128_2`, `VX128_3`, `VX128_4`, `VX128_5`, `VX128_P`, `VX128_R`, `X`, `XFL`, `XFX`, `XL`, `XO`, `XS`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instruction Catalog
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Category | Count |
|
||||||
|
|----------|------:|
|
||||||
|
| Integer / ALU | 70 |
|
||||||
|
| Branch / System | 9 |
|
||||||
|
| Condition Register / SPR | 26 |
|
||||||
|
| Floating-Point | 33 |
|
||||||
|
| Memory (Load/Store/Cache) | 112 |
|
||||||
|
| Vector / VMX (AltiVec) | 205 |
|
||||||
|
| **Total** | **455** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Integer / ALU (70 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `addx` | Add | XO | `7c000214` |
|
||||||
|
| 2 | `addcx` | Add Carrying | XO | `7c000014` |
|
||||||
|
| 3 | `addex` | Add Extended | XO | `7c000114` |
|
||||||
|
| 4 | `addi` | Add Immediate | D | `38000000` |
|
||||||
|
| 5 | `addic` | Add Immediate Carrying | D | `30000000` |
|
||||||
|
| 6 | `addic.` | Add Immediate Carrying and Record | D | `34000000` |
|
||||||
|
| 7 | `addis` | Add Immediate Shifted | D | `3c000000` |
|
||||||
|
| 8 | `addmex` | Add to Minus One Extended | XO | `7c0001d4` |
|
||||||
|
| 9 | `addzex` | Add to Zero Extended | XO | `7c000194` |
|
||||||
|
| 10 | `andx` | AND | X | `7c000038` |
|
||||||
|
| 11 | `andcx` | AND with Complement | X | `7c000078` |
|
||||||
|
| 12 | `andi.` | AND Immediate | D | `70000000` |
|
||||||
|
| 13 | `andis.` | AND Immediate Shifted | D | `74000000` |
|
||||||
|
| 14 | `cmp` | Compare | X | `7c000000` |
|
||||||
|
| 15 | `cmpi` | Compare Immediate | D | `2c000000` |
|
||||||
|
| 16 | `cmpl` | Compare Logical | X | `7c000040` |
|
||||||
|
| 17 | `cmpli` | Compare Logical Immediate | D | `28000000` |
|
||||||
|
| 18 | `cntlzdx` | Count Leading Zeros Doubleword | X | `7c000074` |
|
||||||
|
| 19 | `cntlzwx` | Count Leading Zeros Word | X | `7c000034` |
|
||||||
|
| 20 | `divdx` | Divide Doubleword | XO | `7c0003d2` |
|
||||||
|
| 21 | `divdux` | Divide Doubleword Unsigned | XO | `7c000392` |
|
||||||
|
| 22 | `divwx` | Divide Word | XO | `7c0003d6` |
|
||||||
|
| 23 | `divwux` | Divide Word Unsigned | XO | `7c000396` |
|
||||||
|
| 24 | `eieio` | Enforce In-Order Execution of I/O | X | `7c0006ac` |
|
||||||
|
| 25 | `eqvx` | Equivalent | X | `7c000238` |
|
||||||
|
| 26 | `extsbx` | Extend Sign Byte | X | `7c000774` |
|
||||||
|
| 27 | `extshx` | Extend Sign Half Word | X | `7c000734` |
|
||||||
|
| 28 | `extswx` | Extend Sign Word | X | `7c0007B4` |
|
||||||
|
| 29 | `isync` | Instruction Synchronize | XL | `4c00012c` |
|
||||||
|
| 30 | `mulhdx` | Multiply High Doubleword | XO | `7c000092` |
|
||||||
|
| 31 | `mulhdux` | Multiply High Doubleword Unsigned | XO | `7c000012` |
|
||||||
|
| 32 | `mulhwx` | Multiply High Word | XO | `7c000096` |
|
||||||
|
| 33 | `mulhwux` | Multiply High Word Unsigned | XO | `7c000016` |
|
||||||
|
| 34 | `mulldx` | Multiply Low Doubleword | XO | `7c0001d2` |
|
||||||
|
| 35 | `mulli` | Multiply Low Immediate | D | `1c000000` |
|
||||||
|
| 36 | `mullwx` | Multiply Low Word | XO | `7c0001d6` |
|
||||||
|
| 37 | `nandx` | NAND | X | `7c0003b8` |
|
||||||
|
| 38 | `negx` | Negate | XO | `7c0000d0` |
|
||||||
|
| 39 | `norx` | NOR | X | `7c0000f8` |
|
||||||
|
| 40 | `orx` | OR | X | `7c000378` |
|
||||||
|
| 41 | `orcx` | OR with Complement | X | `7c000338` |
|
||||||
|
| 42 | `ori` | OR Immediate | D | `60000000` |
|
||||||
|
| 43 | `oris` | OR Immediate Shifted | D | `64000000` |
|
||||||
|
| 44 | `rldclx` | Rotate Left Doubleword then Clear Left | MDS | `78000010` |
|
||||||
|
| 45 | `rldcrx` | Rotate Left Doubleword then Clear Right | MDS | `78000012` |
|
||||||
|
| 46 | `rldicx` | Rotate Left Doubleword Immediate then Clear | MD | `78000008` |
|
||||||
|
| 47 | `rldiclx` | Rotate Left Doubleword Immediate then Clear Left | MD | `78000000` |
|
||||||
|
| 48 | `rldicrx` | Rotate Left Doubleword Immediate then Clear Right | MD | `78000004` |
|
||||||
|
| 49 | `rldimix` | Rotate Left Doubleword Immediate then Mask Insert | MD | `7800000C` |
|
||||||
|
| 50 | `rlwimix` | Rotate Left Word Immediate then Mask Insert | M | `50000000` |
|
||||||
|
| 51 | `rlwinmx` | Rotate Left Word Immediate then AND with Mask | M | `54000000` |
|
||||||
|
| 52 | `rlwnmx` | Rotate Left Word then AND with Mask | M | `5c000000` |
|
||||||
|
| 53 | `sldx` | Shift Left Doubleword | X | `7c000036` |
|
||||||
|
| 54 | `slwx` | Shift Left Word | X | `7c000030` |
|
||||||
|
| 55 | `sradx` | Shift Right Algebraic Doubleword | X | `7c000634` |
|
||||||
|
| 56 | `sradix` | Shift Right Algebraic Doubleword Immediate | XS | `7c000674` |
|
||||||
|
| 57 | `srawx` | Shift Right Algebraic Word | X | `7c000630` |
|
||||||
|
| 58 | `srawix` | Shift Right Algebraic Word Immediate | X | `7c000670` |
|
||||||
|
| 59 | `srdx` | Shift Right Doubleword | X | `7c000436` |
|
||||||
|
| 60 | `srwx` | Shift Right Word | X | `7c000430` |
|
||||||
|
| 61 | `subfx` | Subtract From | XO | `7c000050` |
|
||||||
|
| 62 | `subfcx` | Subtract From Carrying | XO | `7c000010` |
|
||||||
|
| 63 | `subfex` | Subtract From Extended | XO | `7c000110` |
|
||||||
|
| 64 | `subficx` | Subtract From Immediate Carrying | D | `20000000` |
|
||||||
|
| 65 | `subfmex` | Subtract From Minus One Extended | XO | `7c0001d0` |
|
||||||
|
| 66 | `subfzex` | Subtract From Zero Extended | XO | `7c000190` |
|
||||||
|
| 67 | `sync` | Synchronize | X | `7c0004ac` |
|
||||||
|
| 68 | `xorx` | XOR | X | `7c000278` |
|
||||||
|
| 69 | `xori` | XOR Immediate | D | `68000000` |
|
||||||
|
| 70 | `xoris` | XOR Immediate Shifted | D | `6c000000` |
|
||||||
|
|
||||||
|
### Branch / System (9 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `bx` | Branch | I | `48000000` |
|
||||||
|
| 2 | `bcx` | Branch Conditional | B | `40000000` |
|
||||||
|
| 3 | `bcctrx` | Branch Conditional to Count Register | XL | `4c000420` |
|
||||||
|
| 4 | `bclrx` | Branch Conditional to Link Register | XL | `4c000020` |
|
||||||
|
| 5 | `sc` | System Call | SC | `44000002` |
|
||||||
|
| 6 | `td` | Trap Doubleword | X | `7c000088` |
|
||||||
|
| 7 | `tdi` | Trap Doubleword Immediate | D | `08000000` |
|
||||||
|
| 8 | `tw` | Trap Word | X | `7c000008` |
|
||||||
|
| 9 | `twi` | Trap Word Immediate | D | `0c000000` |
|
||||||
|
|
||||||
|
### Condition Register / SPR (26 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `crand` | Condition Register AND | XL | `4c000202` |
|
||||||
|
| 2 | `crandc` | Condition Register AND with Complement | XL | `4c000102` |
|
||||||
|
| 3 | `creqv` | Condition Register Equivalent | XL | `4c000242` |
|
||||||
|
| 4 | `crnand` | Condition Register NAND | XL | `4c0001c2` |
|
||||||
|
| 5 | `crnor` | Condition Register NOR | XL | `4c000042` |
|
||||||
|
| 6 | `cror` | Condition Register OR | XL | `4c000382` |
|
||||||
|
| 7 | `crorc` | Condition Register OR with Complement | XL | `4c000342` |
|
||||||
|
| 8 | `crxor` | Condition Register XOR | XL | `4c000182` |
|
||||||
|
| 9 | `mcrf` | Move Condition Register Field | XL | `4c000000` |
|
||||||
|
| 10 | `mcrfs` | Move to Condition Register from FPSCR | X | `fc000080` |
|
||||||
|
| 11 | `mcrxr` | Move to Condition Register from XER | X | `7c000400` |
|
||||||
|
| 12 | `mfcr` | Move from Condition Register | X | `7c000026` |
|
||||||
|
| 13 | `mffsx` | Move from FPSCR | X | `fc00048e` |
|
||||||
|
| 14 | `mfmsr` | Move from Machine State Register | X | `7c0000a6` |
|
||||||
|
| 15 | `mfspr` | Move from Special-Purpose Register | XFX | `7c0002a6` |
|
||||||
|
| 16 | `mftb` | Move from Time Base | XFX | `7c0002e6` |
|
||||||
|
| 17 | `mtcrf` | Move to Condition Register Fields | XFX | `7c000120` |
|
||||||
|
| 18 | `mtfsb0x` | Move to FPSCR Bit 0 | X | `fc00008c` |
|
||||||
|
| 19 | `mtfsb1x` | Move to FPSCR Bit 1 | X | `fc00004c` |
|
||||||
|
| 20 | `mtfsfx` | Move to FPSCR Fields | XFL | `fc00058e` |
|
||||||
|
| 21 | `mtfsfix` | Move to FPSCR Field Immediate | X | `fc00010c` |
|
||||||
|
| 22 | `mtmsr` | Move to Machine State Register | X | `7c000124` |
|
||||||
|
| 23 | `mtmsrd` | Move to Machine State Register Doubleword | X | `7c000164` |
|
||||||
|
| 24 | `mtspr` | Move to Special-Purpose Register | XFX | `7c0003a6` |
|
||||||
|
| 25 | `mfvscr` | Move from VSCR | VX | `10000604` |
|
||||||
|
| 26 | `mtvscr` | Move to VSCR | VX | `10000644` |
|
||||||
|
|
||||||
|
### Floating-Point (33 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `fabsx` | Floating Absolute Value | X | `fc000210` |
|
||||||
|
| 2 | `faddx` | Floating Add | A | `fc00002a` |
|
||||||
|
| 3 | `faddsx` | Floating Add Single | A | `ec00002a` |
|
||||||
|
| 4 | `fcfidx` | Floating Convert From Integer Doubleword | X | `FC00069C` |
|
||||||
|
| 5 | `fcmpo` | Floating Compare Ordered | X | `fc000040` |
|
||||||
|
| 6 | `fcmpu` | Floating Compare Unordered | X | `fc000000` |
|
||||||
|
| 7 | `fctidx` | Floating Convert to Integer Doubleword | X | `fc00065c` |
|
||||||
|
| 8 | `fctidzx` | Floating Convert to Integer Doubleword with Round Toward Zero | X | `fc00065e` |
|
||||||
|
| 9 | `fctiwx` | Floating Convert to Integer Word | X | `fc00001c` |
|
||||||
|
| 10 | `fctiwzx` | Floating Convert to Integer Word with Round Toward Zero | X | `fc00001e` |
|
||||||
|
| 11 | `fdivx` | Floating Divide | A | `fc000024` |
|
||||||
|
| 12 | `fdivsx` | Floating Divide Single | A | `ec000024` |
|
||||||
|
| 13 | `fmaddx` | Floating Multiply-Add | A | `fc00003a` |
|
||||||
|
| 14 | `fmaddsx` | Floating Multiply-Add Single | A | `ec00003a` |
|
||||||
|
| 15 | `fmrx` | Floating Move Register | X | `fc000090` |
|
||||||
|
| 16 | `fmsubx` | Floating Multiply-Subtract | A | `fc000038` |
|
||||||
|
| 17 | `fmsubsx` | Floating Multiply-Subtract Single | A | `ec000038` |
|
||||||
|
| 18 | `fmulx` | Floating Multiply | A | `fc000032` |
|
||||||
|
| 19 | `fmulsx` | Floating Multiply Single | A | `ec000032` |
|
||||||
|
| 20 | `fnabsx` | Floating Negative Absolute Value | X | `fc000110` |
|
||||||
|
| 21 | `fnegx` | Floating Negate | X | `fc000050` |
|
||||||
|
| 22 | `fnmaddx` | Floating Negative Multiply-Add | A | `fc00003e` |
|
||||||
|
| 23 | `fnmaddsx` | Floating Negative Multiply-Add Single | A | `ec00003e` |
|
||||||
|
| 24 | `fnmsubx` | Floating Negative Multiply-Subtract | A | `fc00003c` |
|
||||||
|
| 25 | `fnmsubsx` | Floating Negative Multiply-Subtract Single | A | `ec00003c` |
|
||||||
|
| 26 | `fresx` | Floating Reciprocal Estimate Single | A | `ec000030` |
|
||||||
|
| 27 | `frspx` | Floating Round to Single | X | `fc000018` |
|
||||||
|
| 28 | `frsqrtex` | Floating Reciprocal Square Root Estimate | A | `fc000034` |
|
||||||
|
| 29 | `fselx` | Floating Select | A | `fc00002e` |
|
||||||
|
| 30 | `fsqrtx` | Floating Square Root | A | `fc00002c` |
|
||||||
|
| 31 | `fsqrtsx` | Floating Square Root Single | A | `ec00002c` |
|
||||||
|
| 32 | `fsubx` | Floating Subtract | A | `fc000028` |
|
||||||
|
| 33 | `fsubsx` | Floating Subtract Single | A | `ec000028` |
|
||||||
|
|
||||||
|
### Memory (Load/Store/Cache) (112 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `dcbf` | Data Cache Block Flush | X | `7c0000ac` |
|
||||||
|
| 2 | `dcbi` | Data Cache Block Invalidate | X | `7c0003ac` |
|
||||||
|
| 3 | `dcbst` | Data Cache Block Store | X | `7c00006c` |
|
||||||
|
| 4 | `dcbt` | Data Cache Block Touch | X | `7c00022c` |
|
||||||
|
| 5 | `dcbtst` | Data Cache Block Touch for Store | X | `7c0001ec` |
|
||||||
|
| 6 | `dcbz` | Data Cache Block Clear to Zero | DCBZ | `7c0007ec` |
|
||||||
|
| 7 | `dcbz128` | Data Cache Block Clear to Zero 128 | DCBZ | `7c2007ec` |
|
||||||
|
| 8 | `icbi` | Instruction Cache Block Invalidate | X | `7c0007ac` |
|
||||||
|
| 9 | `lbz` | Load Byte and Zero | D | `88000000` |
|
||||||
|
| 10 | `lbzu` | Load Byte and Zero with Update | D | `8c000000` |
|
||||||
|
| 11 | `lbzux` | Load Byte and Zero with Update Indexed | X | `7c0000ee` |
|
||||||
|
| 12 | `lbzx` | Load Byte and Zero Indexed | X | `7c0000ae` |
|
||||||
|
| 13 | `ld` | Load Doubleword | DS | `E8000000` |
|
||||||
|
| 14 | `ldarx` | Load Doubleword and Reserve Indexed | X | `7C0000A8` |
|
||||||
|
| 15 | `ldbrx` | Load Doubleword Byte-Reverse Indexed | X | `7C000428` |
|
||||||
|
| 16 | `ldu` | Load Doubleword with Update | DS | `E8000001` |
|
||||||
|
| 17 | `ldux` | Load Doubleword with Update Indexed | X | `7c00006a` |
|
||||||
|
| 18 | `ldx` | Load Doubleword Indexed | X | `7c00002a` |
|
||||||
|
| 19 | `lfd` | Load Floating-Point Double | D | `c8000000` |
|
||||||
|
| 20 | `lfdu` | Load Floating-Point Double with Update | D | `cc000000` |
|
||||||
|
| 21 | `lfdux` | Load Floating-Point Double with Update Indexed | X | `7c0004ee` |
|
||||||
|
| 22 | `lfdx` | Load Floating-Point Double Indexed | X | `7c0004ae` |
|
||||||
|
| 23 | `lfs` | Load Floating-Point Single | D | `c0000000` |
|
||||||
|
| 24 | `lfsu` | Load Floating-Point Single with Update | D | `c4000000` |
|
||||||
|
| 25 | `lfsux` | Load Floating-Point Single with Update Indexed | X | `7c00046e` |
|
||||||
|
| 26 | `lfsx` | Load Floating-Point Single Indexed | X | `7c00042e` |
|
||||||
|
| 27 | `lha` | Load Half Word Algebraic | D | `a8000000` |
|
||||||
|
| 28 | `lhau` | Load Half Word Algebraic with Update | D | `ac000000` |
|
||||||
|
| 29 | `lhaux` | Load Half Word Algebraic with Update Indexed | X | `7c0002ee` |
|
||||||
|
| 30 | `lhax` | Load Half Word Algebraic Indexed | X | `7c0002ae` |
|
||||||
|
| 31 | `lhbrx` | Load Half Word Byte-Reverse Indexed | X | `7c00062c` |
|
||||||
|
| 32 | `lhz` | Load Half Word and Zero | D | `a0000000` |
|
||||||
|
| 33 | `lhzu` | Load Half Word and Zero with Update | D | `a4000000` |
|
||||||
|
| 34 | `lhzux` | Load Half Word and Zero with Update Indexed | X | `7c00026e` |
|
||||||
|
| 35 | `lhzx` | Load Half Word and Zero Indexed | X | `7c00022e` |
|
||||||
|
| 36 | `lmw` | Load Multiple Word | D | `b8000000` |
|
||||||
|
| 37 | `lswi` | Load String Word Immediate | X | `7c0004aa` |
|
||||||
|
| 38 | `lswx` | Load String Word Indexed | X | `7c00042a` |
|
||||||
|
| 39 | `lwa` | Load Word Algebraic | DS | `e8000002` |
|
||||||
|
| 40 | `lwarx` | Load Word and Reserve Indexed | X | `7c000028` |
|
||||||
|
| 41 | `lwaux` | Load Word Algebraic with Update Indexed | X | `7c0002ea` |
|
||||||
|
| 42 | `lwax` | Load Word Algebraic Indexed | X | `7c0002aa` |
|
||||||
|
| 43 | `lwbrx` | Load Word Byte-Reverse Indexed | X | `7c00042c` |
|
||||||
|
| 44 | `lwz` | Load Word and Zero | D | `80000000` |
|
||||||
|
| 45 | `lwzu` | Load Word and Zero with Update | D | `84000000` |
|
||||||
|
| 46 | `lwzux` | Load Word and Zero with Update Indexed | X | `7c00006e` |
|
||||||
|
| 47 | `lwzx` | Load Word and Zero Indexed | X | `7c00002e` |
|
||||||
|
| 48 | `stb` | Store Byte | D | `98000000` |
|
||||||
|
| 49 | `stbu` | Store Byte with Update | D | `9c000000` |
|
||||||
|
| 50 | `stbux` | Store Byte with Update Indexed | X | `7c0001ee` |
|
||||||
|
| 51 | `stbx` | Store Byte Indexed | X | `7c0001ae` |
|
||||||
|
| 52 | `std` | Store Doubleword | DS | `f8000000` |
|
||||||
|
| 53 | `stdbrx` | Store Doubleword Byte-Reverse Indexed | X | `7c000528` |
|
||||||
|
| 54 | `stdcx` | Store Doubleword Conditional Indexed | X | `7c0001ad` |
|
||||||
|
| 55 | `stdu` | Store Doubleword with Update | DS | `f8000001` |
|
||||||
|
| 56 | `stdux` | Store Doubleword with Update Indexed | X | `7c00016a` |
|
||||||
|
| 57 | `stdx` | Store Doubleword Indexed | X | `7c00012a` |
|
||||||
|
| 58 | `stfd` | Store Floating-Point Double | D | `d8000000` |
|
||||||
|
| 59 | `stfdu` | Store Floating-Point Double with Update | D | `dc000000` |
|
||||||
|
| 60 | `stfdux` | Store Floating-Point Double with Update Indexed | X | `7c0005ee` |
|
||||||
|
| 61 | `stfdx` | Store Floating-Point Double Indexed | X | `7c0005ae` |
|
||||||
|
| 62 | `stfiwx` | Store Floating-Point as Integer Word Indexed | X | `7c0007ae` |
|
||||||
|
| 63 | `stfs` | Store Floating-Point Single | D | `d0000000` |
|
||||||
|
| 64 | `stfsu` | Store Floating-Point Single with Update | D | `d4000000` |
|
||||||
|
| 65 | `stfsux` | Store Floating-Point Single with Update Indexed | X | `7c00056e` |
|
||||||
|
| 66 | `stfsx` | Store Floating-Point Single Indexed | X | `7c00052e` |
|
||||||
|
| 67 | `sth` | Store Half Word | D | `b0000000` |
|
||||||
|
| 68 | `sthbrx` | Store Half Word Byte-Reverse Indexed | X | `7c00072c` |
|
||||||
|
| 69 | `sthu` | Store Half Word with Update | D | `b4000000` |
|
||||||
|
| 70 | `sthux` | Store Half Word with Update Indexed | X | `7c00036e` |
|
||||||
|
| 71 | `sthx` | Store Half Word Indexed | X | `7c00032e` |
|
||||||
|
| 72 | `stmw` | Store Multiple Word | D | `bc000000` |
|
||||||
|
| 73 | `stswi` | Store String Word Immediate | X | `7c0005aa` |
|
||||||
|
| 74 | `stswx` | Store String Word Indexed | X | `7c00052a` |
|
||||||
|
| 75 | `stw` | Store Word | D | `90000000` |
|
||||||
|
| 76 | `stwbrx` | Store Word Byte-Reverse Indexed | X | `7c00052c` |
|
||||||
|
| 77 | `stwcx` | Store Word Conditional Indexed | X | `7c00012d` |
|
||||||
|
| 78 | `stwu` | Store Word with Update | D | `94000000` |
|
||||||
|
| 79 | `stwux` | Store Word with Update Indexed | X | `7c00016e` |
|
||||||
|
| 80 | `stwx` | Store Word Indexed | X | `7c00012e` |
|
||||||
|
| 81 | `lvebx` | Load Vector Element Byte Indexed | X | `7c00000e` |
|
||||||
|
| 82 | `lvehx` | Load Vector Element Half Word Indexed | X | `7c00004e` |
|
||||||
|
| 83 | `lvewx` | Load Vector Element Word Indexed | X | `7c00008e` |
|
||||||
|
| 84 | `lvewx128` | Load Vector Element Word Indexed 128 | VX128_1 | `10000083` |
|
||||||
|
| 85 | `lvx` | Load Vector Indexed | X | `7c0000ce` |
|
||||||
|
| 86 | `lvx128` | Load Vector Indexed 128 | VX128_1 | `100000C3` |
|
||||||
|
| 87 | `lvxl` | Load Vector Indexed LRU | X | `7c0002ce` |
|
||||||
|
| 88 | `lvxl128` | Load Vector Indexed LRU 128 | VX128_1 | `100002C3` |
|
||||||
|
| 89 | `lvlx` | Load Vector Left Indexed | X | `7C00040E` |
|
||||||
|
| 90 | `lvlx128` | Load Vector Left Indexed 128 | VX128_1 | `10000403` |
|
||||||
|
| 91 | `lvlxl` | Load Vector Left Indexed LRU | X | `7C00060E` |
|
||||||
|
| 92 | `lvlxl128` | Load Vector Left Indexed LRU 128 | VX128_1 | `10000603` |
|
||||||
|
| 93 | `lvrx` | Load Vector Right Indexed | X | `7C00044E` |
|
||||||
|
| 94 | `lvrx128` | Load Vector Right Indexed 128 | VX128_1 | `10000443` |
|
||||||
|
| 95 | `lvrxl` | Load Vector Right Indexed LRU | X | `7C00064E` |
|
||||||
|
| 96 | `lvrxl128` | Load Vector Right Indexed LRU 128 | VX128_1 | `10000643` |
|
||||||
|
| 97 | `stvebx` | Store Vector Element Byte Indexed | X | `7c00010e` |
|
||||||
|
| 98 | `stvehx` | Store Vector Element Half Word Indexed | X | `7c00014e` |
|
||||||
|
| 99 | `stvewx` | Store Vector Element Word Indexed | X | `7c00018e` |
|
||||||
|
| 100 | `stvewx128` | Store Vector Element Word Indexed 128 | VX128_1 | `10000183` |
|
||||||
|
| 101 | `stvx` | Store Vector Indexed | X | `7c0001ce` |
|
||||||
|
| 102 | `stvx128` | Store Vector Indexed 128 | VX128_1 | `100001c3` |
|
||||||
|
| 103 | `stvxl` | Store Vector Indexed LRU | X | `7c0003ce` |
|
||||||
|
| 104 | `stvxl128` | Store Vector Indexed LRU 128 | VX128_1 | `100003c3` |
|
||||||
|
| 105 | `stvlx` | Store Vector Left Indexed | X | `7c00050e` |
|
||||||
|
| 106 | `stvlx128` | Store Vector Left Indexed 128 | VX128_1 | `10000503` |
|
||||||
|
| 107 | `stvlxl` | Store Vector Left Indexed LRU | X | `7c00070e` |
|
||||||
|
| 108 | `stvlxl128` | Store Vector Left Indexed LRU 128 | VX128_1 | `10000703` |
|
||||||
|
| 109 | `stvrx` | Store Vector Right Indexed | X | `7c00054e` |
|
||||||
|
| 110 | `stvrx128` | Store Vector Right Indexed 128 | VX128_1 | `10000543` |
|
||||||
|
| 111 | `stvrxl` | Store Vector Right Indexed LRU | X | `7c00074e` |
|
||||||
|
| 112 | `stvrxl128` | Store Vector Right Indexed LRU 128 | VX128_1 | `10000743` |
|
||||||
|
|
||||||
|
### Vector / VMX (AltiVec) (205 instructions)
|
||||||
|
|
||||||
|
| # | Mnemonic | Description | Form | Opcode |
|
||||||
|
|--:|----------|-------------|------|--------|
|
||||||
|
| 1 | `lvsl` | Load Vector for Shift Left Indexed | X | `7c00000c` |
|
||||||
|
| 2 | `lvsl128` | Load Vector for Shift Left Indexed 128 | VX128_1 | `10000003` |
|
||||||
|
| 3 | `lvsr` | Load Vector for Shift Right Indexed | X | `7c00004c` |
|
||||||
|
| 4 | `lvsr128` | Load Vector for Shift Right Indexed 128 | VX128_1 | `10000043` |
|
||||||
|
| 5 | `vaddcuw` | Vector Add Carryout Unsigned Word | VX | `10000180` |
|
||||||
|
| 6 | `vaddfp` | Vector Add Floating Point | VX | `1000000A` |
|
||||||
|
| 7 | `vaddfp128` | Vector128 Add Floating Point | VX128 | `14000010` |
|
||||||
|
| 8 | `vaddsbs` | Vector Add Signed Byte Saturate | VX | `10000300` |
|
||||||
|
| 9 | `vaddshs` | Vector Add Signed Half Word Saturate | VX | `10000340` |
|
||||||
|
| 10 | `vaddsws` | Vector Add Signed Word Saturate | VX | `10000380` |
|
||||||
|
| 11 | `vaddubm` | Vector Add Unsigned Byte Modulo | VX | `10000000` |
|
||||||
|
| 12 | `vaddubs` | Vector Add Unsigned Byte Saturate | VX | `10000200` |
|
||||||
|
| 13 | `vadduhm` | Vector Add Unsigned Half Word Modulo | VX | `10000040` |
|
||||||
|
| 14 | `vadduhs` | Vector Add Unsigned Half Word Saturate | VX | `10000240` |
|
||||||
|
| 15 | `vadduwm` | Vector Add Unsigned Word Modulo | VX | `10000080` |
|
||||||
|
| 16 | `vadduws` | Vector Add Unsigned Word Saturate | VX | `10000280` |
|
||||||
|
| 17 | `vand` | Vector Logical AND | VX | `10000404` |
|
||||||
|
| 18 | `vand128` | Vector128 Logical AND | VX128 | `14000210` |
|
||||||
|
| 19 | `vandc` | Vector Logical AND with Complement | VX | `10000444` |
|
||||||
|
| 20 | `vandc128` | Vector128 Logical AND with Complement | VX128 | `14000250` |
|
||||||
|
| 21 | `vavgsb` | Vector Average Signed Byte | VX | `10000502` |
|
||||||
|
| 22 | `vavgsh` | Vector Average Signed Half Word | VX | `10000542` |
|
||||||
|
| 23 | `vavgsw` | Vector Average Signed Word | VX | `10000582` |
|
||||||
|
| 24 | `vavgub` | Vector Average Unsigned Byte | VX | `10000402` |
|
||||||
|
| 25 | `vavguh` | Vector Average Unsigned Half Word | VX | `10000442` |
|
||||||
|
| 26 | `vavguw` | Vector Average Unsigned Word | VX | `10000482` |
|
||||||
|
| 27 | `vcfsx` | Vector Convert from Signed Fixed-Point Word | VX | `1000034A` |
|
||||||
|
| 28 | `vcfux` | Vector Convert from Unsigned Fixed-Point Word | VX | `1000030A` |
|
||||||
|
| 29 | `vcmpbfp` | Vector Compare Bounds Floating Point | VC | `100003C6` |
|
||||||
|
| 30 | `vcmpbfp128` | Vector128 Compare Bounds Floating Point | VX128_R | `18000180` |
|
||||||
|
| 31 | `vcmpeqfp` | Vector Compare Equal-to Floating Point | VC | `100000C6` |
|
||||||
|
| 32 | `vcmpeqfp128` | Vector128 Compare Equal-to Floating Point | VX128_R | `18000000` |
|
||||||
|
| 33 | `vcmpequb` | Vector Compare Equal-to Unsigned Byte | VC | `10000006` |
|
||||||
|
| 34 | `vcmpequh` | Vector Compare Equal-to Unsigned Half Word | VC | `10000046` |
|
||||||
|
| 35 | `vcmpequw` | Vector Compare Equal-to Unsigned Word | VC | `10000086` |
|
||||||
|
| 36 | `vcmpequw128` | Vector128 Compare Equal-to Unsigned Word | VX128_R | `18000200` |
|
||||||
|
| 37 | `vcmpgefp` | Vector Compare Greater-Than-or-Equal-to Floating Point | VC | `100001C6` |
|
||||||
|
| 38 | `vcmpgefp128` | Vector128 Compare Greater-Than-or-Equal-to Floating Point | VX128_R | `18000080` |
|
||||||
|
| 39 | `vcmpgtfp` | Vector Compare Greater-Than Floating Point | VC | `100002C6` |
|
||||||
|
| 40 | `vcmpgtfp128` | Vector128 Compare Greater-Than Floating-Point | VX128_R | `18000100` |
|
||||||
|
| 41 | `vcmpgtsb` | Vector Compare Greater-Than Signed Byte | VC | `10000306` |
|
||||||
|
| 42 | `vcmpgtsh` | Vector Compare Greater-Than Signed Half Word | VC | `10000346` |
|
||||||
|
| 43 | `vcmpgtsw` | Vector Compare Greater-Than Signed Word | VC | `10000386` |
|
||||||
|
| 44 | `vcmpgtub` | Vector Compare Greater-Than Unsigned Byte | VC | `10000206` |
|
||||||
|
| 45 | `vcmpgtuh` | Vector Compare Greater-Than Unsigned Half Word | VC | `10000246` |
|
||||||
|
| 46 | `vcmpgtuw` | Vector Compare Greater-Than Unsigned Word | VC | `10000286` |
|
||||||
|
| 47 | `vctsxs` | Vector Convert to Signed Fixed-Point Word Saturate | VX | `100003CA` |
|
||||||
|
| 48 | `vctuxs` | Vector Convert to Unsigned Fixed-Point Word Saturate | VX | `1000038A` |
|
||||||
|
| 49 | `vexptefp` | Vector 2 Raised to the Exponent Estimate Floating Point | VX | `1000018A` |
|
||||||
|
| 50 | `vexptefp128` | Vector128 Log2 Estimate Floating Point | VX128_3 | `180006B0` |
|
||||||
|
| 51 | `vlogefp` | Vector Log2 Estimate Floating Point | VX | `100001CA` |
|
||||||
|
| 52 | `vlogefp128` | Vector128 Log2 Estimate Floating Point | VX128_3 | `180006F0` |
|
||||||
|
| 53 | `vmaddfp` | Vector Multiply-Add Floating Point | VA | `1000002E` |
|
||||||
|
| 54 | `vmaddfp128` | Vector128 Multiply Add Floating Point | VX128 | `140000D0` |
|
||||||
|
| 55 | `vmaxfp` | Vector Maximum Floating Point | VX | `1000040A` |
|
||||||
|
| 56 | `vmaxfp128` | Vector128 Maximum Floating Point | VX128 | `18000280` |
|
||||||
|
| 57 | `vmaxsb` | Vector Maximum Signed Byte | VX | `10000102` |
|
||||||
|
| 58 | `vmaxsh` | Vector Maximum Signed Half Word | VX | `10000142` |
|
||||||
|
| 59 | `vmaxsw` | Vector Maximum Signed Word | VX | `10000182` |
|
||||||
|
| 60 | `vmaxub` | Vector Maximum Unsigned Byte | VX | `10000002` |
|
||||||
|
| 61 | `vmaxuh` | Vector Maximum Unsigned Half Word | VX | `10000042` |
|
||||||
|
| 62 | `vmaxuw` | Vector Maximum Unsigned Word | VX | `10000082` |
|
||||||
|
| 63 | `vmhaddshs` | Vector Multiply-High and Add Signed Signed Half Word Saturate | VA | `10000020` |
|
||||||
|
| 64 | `vmhraddshs` | Vector Multiply-High Round and Add Signed Signed Half Word Saturate | VA | `10000021` |
|
||||||
|
| 65 | `vminfp` | Vector Minimum Floating Point | VX | `1000044A` |
|
||||||
|
| 66 | `vminfp128` | Vector128 Minimum Floating Point | VX128 | `180002C0` |
|
||||||
|
| 67 | `vminsb` | Vector Minimum Signed Byte | VX | `10000302` |
|
||||||
|
| 68 | `vminsh` | Vector Minimum Signed Half Word | VX | `10000342` |
|
||||||
|
| 69 | `vminsw` | Vector Minimum Signed Word | VX | `10000382` |
|
||||||
|
| 70 | `vminub` | Vector Minimum Unsigned Byte | VX | `10000202` |
|
||||||
|
| 71 | `vminuh` | Vector Minimum Unsigned Half Word | VX | `10000242` |
|
||||||
|
| 72 | `vminuw` | Vector Minimum Unsigned Word | VX | `10000282` |
|
||||||
|
| 73 | `vmladduhm` | Vector Multiply-Low and Add Unsigned Half Word Modulo | VA | `10000022` |
|
||||||
|
| 74 | `vmrghb` | Vector Merge High Byte | VX | `1000000C` |
|
||||||
|
| 75 | `vmrghh` | Vector Merge High Half Word | VX | `1000004C` |
|
||||||
|
| 76 | `vmrghw` | Vector Merge High Word | VX | `1000008C` |
|
||||||
|
| 77 | `vmrghw128` | Vector128 Merge High Word | VX128 | `18000300` |
|
||||||
|
| 78 | `vmrglb` | Vector Merge Low Byte | VX | `1000010C` |
|
||||||
|
| 79 | `vmrglh` | Vector Merge Low Half Word | VX | `1000014C` |
|
||||||
|
| 80 | `vmrglw` | Vector Merge Low Word | VX | `1000018C` |
|
||||||
|
| 81 | `vmrglw128` | Vector128 Merge Low Word | VX128 | `18000340` |
|
||||||
|
| 82 | `vmsummbm` | Vector Multiply-Sum Mixed-Sign Byte Modulo | VA | `10000025` |
|
||||||
|
| 83 | `vmsumshm` | Vector Multiply-Sum Signed Half Word Modulo | VA | `10000028` |
|
||||||
|
| 84 | `vmsumshs` | Vector Multiply-Sum Signed Half Word Saturate | VA | `10000029` |
|
||||||
|
| 85 | `vmsumubm` | Vector Multiply-Sum Unsigned Byte Modulo | VA | `10000024` |
|
||||||
|
| 86 | `vmsumuhm` | Vector Multiply-Sum Unsigned Half Word Modulo | VA | `10000026` |
|
||||||
|
| 87 | `vmsumuhs` | Vector Multiply-Sum Unsigned Half Word Saturate | VA | `10000027` |
|
||||||
|
| 88 | `vmulesb` | Vector Multiply Even Signed Byte | VX | `10000308` |
|
||||||
|
| 89 | `vmulesh` | Vector Multiply Even Signed Half Word | VX | `10000348` |
|
||||||
|
| 90 | `vmuleub` | Vector Multiply Even Unsigned Byte | VX | `10000208` |
|
||||||
|
| 91 | `vmuleuh` | Vector Multiply Even Unsigned Half Word | VX | `10000248` |
|
||||||
|
| 92 | `vmulosb` | Vector Multiply Odd Signed Byte | VX | `10000108` |
|
||||||
|
| 93 | `vmulosh` | Vector Multiply Odd Signed Half Word | VX | `10000148` |
|
||||||
|
| 94 | `vmuloub` | Vector Multiply Odd Unsigned Byte | VX | `10000008` |
|
||||||
|
| 95 | `vmulouh` | Vector Multiply Odd Unsigned Half Word | VX | `10000048` |
|
||||||
|
| 96 | `vnmsubfp` | Vector Negative Multiply-Subtract Floating Point | VA | `1000002F` |
|
||||||
|
| 97 | `vnmsubfp128` | Vector128 Negative Multiply-Subtract Floating Point | VX128 | `14000150` |
|
||||||
|
| 98 | `vnor` | Vector Logical NOR | VX | `10000504` |
|
||||||
|
| 99 | `vnor128` | Vector128 Logical NOR | VX128 | `14000290` |
|
||||||
|
| 100 | `vor` | Vector Logical OR | VX | `10000484` |
|
||||||
|
| 101 | `vor128` | Vector128 Logical OR | VX128 | `140002D0` |
|
||||||
|
| 102 | `vperm` | Vector Permute | VA | `1000002B` |
|
||||||
|
| 103 | `vperm128` | Vector128 Permute | VX128_2 | `14000000` |
|
||||||
|
| 104 | `vpkpx` | Vector Pack Pixel | VX | `1000030E` |
|
||||||
|
| 105 | `vpkshss` | Vector Pack Signed Half Word Signed Saturate | VX | `1000018E` |
|
||||||
|
| 106 | `vpkshss128` | Vector128 Pack Signed Half Word Signed Saturate | VX128 | `14000200` |
|
||||||
|
| 107 | `vpkshus` | Vector Pack Signed Half Word Unsigned Saturate | VX | `1000010E` |
|
||||||
|
| 108 | `vpkshus128` | Vector128 Pack Signed Half Word Unsigned Saturate | VX128 | `14000240` |
|
||||||
|
| 109 | `vpkswss` | Vector Pack Signed Word Signed Saturate | VX | `100001CE` |
|
||||||
|
| 110 | `vpkswss128` | Vector128 Pack Signed Word Signed Saturate | VX128 | `14000280` |
|
||||||
|
| 111 | `vpkswus` | Vector Pack Signed Word Unsigned Saturate | VX | `1000014E` |
|
||||||
|
| 112 | `vpkswus128` | Vector128 Pack Signed Word Unsigned Saturate | VX128 | `140002C0` |
|
||||||
|
| 113 | `vpkuhum` | Vector Pack Unsigned Half Word Unsigned Modulo | VX | `1000000E` |
|
||||||
|
| 114 | `vpkuhum128` | Vector128 Pack Unsigned Half Word Unsigned Modulo | VX128 | `14000300` |
|
||||||
|
| 115 | `vpkuhus` | Vector Pack Unsigned Half Word Unsigned Saturate | VX | `1000008E` |
|
||||||
|
| 116 | `vpkuhus128` | Vector128 Pack Unsigned Half Word Unsigned Saturate | VX128 | `14000340` |
|
||||||
|
| 117 | `vpkuwum` | Vector Pack Unsigned Word Unsigned Modulo | VX | `1000004E` |
|
||||||
|
| 118 | `vpkuwum128` | Vector128 Pack Unsigned Word Unsigned Modulo | VX128 | `14000380` |
|
||||||
|
| 119 | `vpkuwus` | Vector Pack Unsigned Word Unsigned Saturate | VX | `100000CE` |
|
||||||
|
| 120 | `vpkuwus128` | Vector128 Pack Unsigned Word Unsigned Saturate | VX128 | `140003C0` |
|
||||||
|
| 121 | `vrefp` | Vector Reciprocal Estimate Floating Point | VX | `1000010A` |
|
||||||
|
| 122 | `vrefp128` | Vector128 Reciprocal Estimate Floating Point | VX128_3 | `18000630` |
|
||||||
|
| 123 | `vrfim` | Vector Round to Floating-Point Integer toward -Infinity | VX | `100002CA` |
|
||||||
|
| 124 | `vrfim128` | Vector128 Round to Floating-Point Integer toward -Infinity | VX128_3 | `18000330` |
|
||||||
|
| 125 | `vrfin` | Vector Round to Floating-Point Integer Nearest | VX | `1000020A` |
|
||||||
|
| 126 | `vrfin128` | Vector128 Round to Floating-Point Integer Nearest | VX128_3 | `18000370` |
|
||||||
|
| 127 | `vrfip` | Vector Round to Floating-Point Integer toward +Infinity | VX | `1000028A` |
|
||||||
|
| 128 | `vrfip128` | Vector128 Round to Floating-Point Integer toward +Infinity | VX128_3 | `180003B0` |
|
||||||
|
| 129 | `vrfiz` | Vector Round to Floating-Point Integer toward Zero | VX | `1000024A` |
|
||||||
|
| 130 | `vrfiz128` | Vector128 Round to Floating-Point Integer toward Zero | VX128_3 | `180003F0` |
|
||||||
|
| 131 | `vrlb` | Vector Rotate Left Integer Byte | VX | `10000004` |
|
||||||
|
| 132 | `vrlh` | Vector Rotate Left Integer Half Word | VX | `10000044` |
|
||||||
|
| 133 | `vrlw` | Vector Rotate Left Integer Word | VX | `10000084` |
|
||||||
|
| 134 | `vrlw128` | Vector128 Rotate Left Word | VX128 | `18000050` |
|
||||||
|
| 135 | `vrsqrtefp` | Vector Reciprocal Square Root Estimate Floating Point | VX | `1000014A` |
|
||||||
|
| 136 | `vrsqrtefp128` | Vector128 Reciprocal Square Root Estimate Floating Point | VX128_3 | `18000670` |
|
||||||
|
| 137 | `vsel` | Vector Conditional Select | VA | `1000002A` |
|
||||||
|
| 138 | `vsel128` | Vector128 Conditional Select | VX128 | `14000350` |
|
||||||
|
| 139 | `vsl` | Vector Shift Left | VX | `100001C4` |
|
||||||
|
| 140 | `vslb` | Vector Shift Left Integer Byte | VX | `10000104` |
|
||||||
|
| 141 | `vsldoi` | Vector Shift Left Double by Octet Immediate | VA | `1000002C` |
|
||||||
|
| 142 | `vsldoi128` | Vector128 Shift Left Double by Octet Immediate | VX128_5 | `10000010` |
|
||||||
|
| 143 | `vslh` | Vector Shift Left Integer Half Word | VX | `10000144` |
|
||||||
|
| 144 | `vslo` | Vector Shift Left by Octet | VX | `1000040C` |
|
||||||
|
| 145 | `vslo128` | Vector128 Shift Left Octet | VX128 | `14000390` |
|
||||||
|
| 146 | `vslw` | Vector Shift Left Integer Word | VX | `10000184` |
|
||||||
|
| 147 | `vslw128` | Vector128 Shift Left Integer Word | VX128 | `180000D0` |
|
||||||
|
| 148 | `vspltb` | Vector Splat Byte | VX | `1000020C` |
|
||||||
|
| 149 | `vsplth` | Vector Splat Half Word | VX | `1000024C` |
|
||||||
|
| 150 | `vspltisb` | Vector Splat Immediate Signed Byte | VX | `1000030C` |
|
||||||
|
| 151 | `vspltish` | Vector Splat Immediate Signed Half Word | VX | `1000034C` |
|
||||||
|
| 152 | `vspltisw` | Vector Splat Immediate Signed Word | VX | `1000038C` |
|
||||||
|
| 153 | `vspltisw128` | Vector128 Splat Immediate Signed Word | VX128_3 | `18000770` |
|
||||||
|
| 154 | `vspltw` | Vector Splat Word | VX | `1000028C` |
|
||||||
|
| 155 | `vspltw128` | Vector128 Splat Word | VX128_3 | `18000730` |
|
||||||
|
| 156 | `vsr` | Vector Shift Right | VX | `100002C4` |
|
||||||
|
| 157 | `vsrab` | Vector Shift Right Algebraic Byte | VX | `10000304` |
|
||||||
|
| 158 | `vsrah` | Vector Shift Right Algebraic Half Word | VX | `10000344` |
|
||||||
|
| 159 | `vsraw` | Vector Shift Right Algebraic Word | VX | `10000384` |
|
||||||
|
| 160 | `vsraw128` | Vector128 Shift Right Arithmetic Word | VX128 | `18000150` |
|
||||||
|
| 161 | `vsrb` | Vector Shift Right Byte | VX | `10000204` |
|
||||||
|
| 162 | `vsrh` | Vector Shift Right Half Word | VX | `10000244` |
|
||||||
|
| 163 | `vsro` | Vector Shift Right Octet | VX | `1000044C` |
|
||||||
|
| 164 | `vsro128` | Vector128 Shift Right Octet | VX128 | `140003D0` |
|
||||||
|
| 165 | `vsrw` | Vector Shift Right Word | VX | `10000284` |
|
||||||
|
| 166 | `vsrw128` | Vector128 Shift Right Word | VX128 | `180001D0` |
|
||||||
|
| 167 | `vsubcuw` | Vector Subtract Carryout Unsigned Word | VX | `10000580` |
|
||||||
|
| 168 | `vsubfp` | Vector Subtract Floating Point | VX | `1000004A` |
|
||||||
|
| 169 | `vsubfp128` | Vector128 Subtract Floating Point | VX128 | `14000050` |
|
||||||
|
| 170 | `vsubsbs` | Vector Subtract Signed Byte Saturate | VX | `10000700` |
|
||||||
|
| 171 | `vsubshs` | Vector Subtract Signed Half Word Saturate | VX | `10000740` |
|
||||||
|
| 172 | `vsubsws` | Vector Subtract Signed Word Saturate | VX | `10000780` |
|
||||||
|
| 173 | `vsububm` | Vector Subtract Unsigned Byte Modulo | VX | `10000400` |
|
||||||
|
| 174 | `vsububs` | Vector Subtract Unsigned Byte Saturate | VX | `10000600` |
|
||||||
|
| 175 | `vsubuhm` | Vector Subtract Unsigned Half Word Modulo | VX | `10000440` |
|
||||||
|
| 176 | `vsubuhs` | Vector Subtract Unsigned Half Word Saturate | VX | `10000640` |
|
||||||
|
| 177 | `vsubuwm` | Vector Subtract Unsigned Word Modulo | VX | `10000480` |
|
||||||
|
| 178 | `vsubuws` | Vector Subtract Unsigned Word Saturate | VX | `10000680` |
|
||||||
|
| 179 | `vsumsws` | Vector Sum Across Signed Word Saturate | VX | `10000788` |
|
||||||
|
| 180 | `vsum2sws` | Vector Sum Across Partial (1/2) Signed Word Saturate | VX | `10000688` |
|
||||||
|
| 181 | `vsum4sbs` | Vector Sum Across Partial (1/4) Signed Byte Saturate | VX | `10000708` |
|
||||||
|
| 182 | `vsum4shs` | Vector Sum Across Partial (1/4) Signed Half Word Saturate | VX | `10000648` |
|
||||||
|
| 183 | `vsum4ubs` | Vector Sum Across Partial (1/4) Unsigned Byte Saturate | VX | `10000608` |
|
||||||
|
| 184 | `vupkhpx` | Vector Unpack High Pixel | VX | `1000034E` |
|
||||||
|
| 185 | `vupkhsb` | Vector Unpack High Signed Byte | VX | `1000020E` |
|
||||||
|
| 186 | `vupkhsb128` | Vector128 Unpack High Signed Byte | VX128 | `18000380` |
|
||||||
|
| 187 | `vupkhsh` | Vector Unpack High Signed Half Word | VX | `1000024E` |
|
||||||
|
| 188 | `vupklpx` | Vector Unpack Low Pixel | VX | `100003CE` |
|
||||||
|
| 189 | `vupklsb` | Vector Unpack Low Signed Byte | VX | `1000028E` |
|
||||||
|
| 190 | `vupklsb128` | Vector128 Unpack Low Signed Byte | VX128 | `180003C0` |
|
||||||
|
| 191 | `vupklsh` | Vector Unpack Low Signed Half Word | VX | `100002CE` |
|
||||||
|
| 192 | `vxor` | Vector Logical XOR | VX | `100004C4` |
|
||||||
|
| 193 | `vxor128` | Vector128 Logical XOR | VX128 | `14000310` |
|
||||||
|
| 194 | `vmulfp128` | Vector128 Multiply Floating-Point | VX128 | `14000090` |
|
||||||
|
| 195 | `vmaddcfp128` | Vector128 Multiply Add Floating Point | VX128 | `14000110` |
|
||||||
|
| 196 | `vmsum3fp128` | Vector128 Multiply Sum 3-way Floating Point | VX128 | `14000190` |
|
||||||
|
| 197 | `vmsum4fp128` | Vector128 Multiply Sum 4-way Floating-Point | VX128 | `140001D0` |
|
||||||
|
| 198 | `vpermwi128` | Vector128 Permutate Word Immediate | VX128_P | `18000210` |
|
||||||
|
| 199 | `vcfpsxws128` | Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate | VX128_3 | `18000230` |
|
||||||
|
| 200 | `vcfpuxws128` | Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate | VX128_3 | `18000270` |
|
||||||
|
| 201 | `vcsxwfp128` | Vector128 Convert From Signed Fixed-Point Word to Floating-Point | VX128_3 | `180002B0` |
|
||||||
|
| 202 | `vcuxwfp128` | Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point | VX128_3 | `180002F0` |
|
||||||
|
| 203 | `vpkd3d128` | Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert | VX128_4 | `18000610` |
|
||||||
|
| 204 | `vrlimi128` | Vector128 Rotate Left Immediate and Mask Insert | VX128_4 | `18000710` |
|
||||||
|
| 205 | `vupkd3d128` | Vector128 Unpack D3Dtype | VX128_3 | `180007F0` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Generated from `tools/ppc-instructions.xml` — 455 instructions across 6 categories.*
|
||||||
33562
docs/xbox360_exports.json
Normal file
33562
docs/xbox360_exports.json
Normal file
File diff suppressed because it is too large
Load Diff
2947
docs/xbox360_exports.md
Normal file
2947
docs/xbox360_exports.md
Normal file
File diff suppressed because it is too large
Load Diff
967
docs/xex2_format.md
Normal file
967
docs/xex2_format.md
Normal file
@@ -0,0 +1,967 @@
|
|||||||
|
# XEX2 File Format Documentation
|
||||||
|
|
||||||
|
This document describes the XEX2 (Xbox 360 Executable) file format as implemented in the Xenia emulator. All multi-byte values in the XEX2 file are **big-endian** unless otherwise noted. The contained PE image uses big-endian values as well (PowerPC BE target).
|
||||||
|
|
||||||
|
**Terminology:**
|
||||||
|
- **XEX offset**: byte offset from the start of the `.xex` file on disk
|
||||||
|
- **PE offset**: byte offset from the start of the decompressed/decrypted PE image (which begins at `xex2_header.header_size` in the raw file, but after decryption/decompression is loaded at the base address)
|
||||||
|
- **Memory address**: Xbox 360 virtual address (typically starting at the `load_address` from security info, e.g. `0x82000000`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Top-Level XEX2 File Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
+==================================+ XEX offset 0x00
|
||||||
|
| xex2_header |
|
||||||
|
| (magic, flags, header_size, |
|
||||||
|
| security_offset, opt headers) |
|
||||||
|
+----------------------------------+ XEX offset 0x18
|
||||||
|
| Optional Headers Array |
|
||||||
|
| (header_count entries of |
|
||||||
|
| xex2_opt_header, 8 bytes each) |
|
||||||
|
+----------------------------------+ XEX offset = security_offset
|
||||||
|
| xex2_security_info |
|
||||||
|
| (RSA sig, AES key, pages, ...) |
|
||||||
|
+----------------------------------+ XEX offset varies
|
||||||
|
| Optional Header Data |
|
||||||
|
| (pointed to by opt headers) |
|
||||||
|
+==================================+ XEX offset = header_size
|
||||||
|
| Encrypted/Compressed PE Image |
|
||||||
|
| (the actual executable payload) |
|
||||||
|
+==================================+ XEX offset = end of file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Main XEX2 Header (`xex2_header`)
|
||||||
|
|
||||||
|
Located at **XEX offset 0x00**.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `magic` | Magic bytes: `XEX2` (0x58455832) |
|
||||||
|
| 0x04 | 4 | `module_flags` | Bitfield of `xex2_module_flags` (see below) |
|
||||||
|
| 0x08 | 4 | `header_size` | Total size of all headers in bytes. **The PE image data starts at this XEX offset.** |
|
||||||
|
| 0x0C | 4 | `reserved` | Reserved (typically 0) |
|
||||||
|
| 0x10 | 4 | `security_offset` | XEX offset to the `xex2_security_info` structure (from start of file) |
|
||||||
|
| 0x14 | 4 | `header_count` | Number of optional header entries following |
|
||||||
|
| 0x18 | 8 * N | `headers[N]` | Array of `xex2_opt_header` entries |
|
||||||
|
|
||||||
|
### Module Flags (`xex2_module_flags`, bitmask)
|
||||||
|
|
||||||
|
| Value | Name | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| 0x00000001 | `XEX_MODULE_TITLE` | Main game/app executable |
|
||||||
|
| 0x00000002 | `XEX_MODULE_EXPORTS_TO_TITLE` | Module exports functions to titles |
|
||||||
|
| 0x00000004 | `XEX_MODULE_SYSTEM_DEBUGGER` | System debugger module |
|
||||||
|
| 0x00000008 | `XEX_MODULE_DLL_MODULE` | DLL module |
|
||||||
|
| 0x00000010 | `XEX_MODULE_MODULE_PATCH` | Module patch |
|
||||||
|
| 0x00000020 | `XEX_MODULE_PATCH_FULL` | Full patch (replaces entire module) |
|
||||||
|
| 0x00000040 | `XEX_MODULE_PATCH_DELTA` | Delta patch (applies diffs) |
|
||||||
|
| 0x00000080 | `XEX_MODULE_USER_MODE` | User-mode module |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Optional Header Entry (`xex2_opt_header`)
|
||||||
|
|
||||||
|
Each entry is 8 bytes, located starting at **XEX offset 0x18**.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `key` | Header key identifier (`xex2_header_keys` enum) |
|
||||||
|
| 0x04 | 4 | `value` / `offset` | Interpretation depends on low byte of `key` |
|
||||||
|
|
||||||
|
### How the `value`/`offset` field is interpreted
|
||||||
|
|
||||||
|
The **low byte** (`key & 0xFF`) determines the meaning:
|
||||||
|
|
||||||
|
| Low byte | Meaning |
|
||||||
|
|----------|---------|
|
||||||
|
| `0x00` | The 4-byte `value` field **is** the data itself (inline uint32_t). |
|
||||||
|
| `0x01` | The `value` field **is** the data itself (stored in-place, pointer to the 4-byte field within the header). |
|
||||||
|
| Any other | `offset` is an XEX offset (from start of file) pointing to the actual header data structure. |
|
||||||
|
|
||||||
|
### Optional Header Keys (`xex2_header_keys`)
|
||||||
|
|
||||||
|
| Key Value | Name | Data Size/Type | Description |
|
||||||
|
|-----------|------|---------------|-------------|
|
||||||
|
| 0x000002FF | `XEX_HEADER_RESOURCE_INFO` | Variable | Embedded resource descriptors |
|
||||||
|
| 0x000003FF | `XEX_HEADER_FILE_FORMAT_INFO` | Variable | Encryption + compression info |
|
||||||
|
| 0x000005FF | `XEX_HEADER_DELTA_PATCH_DESCRIPTOR` | Variable | Delta patch descriptor |
|
||||||
|
| 0x00000405 | `XEX_HEADER_BASE_REFERENCE` | Variable | Base reference for patches |
|
||||||
|
| 0x00004304 | `XEX_HEADER_DISC_PROFILE_ID` | 4 bytes | Disc profile ID |
|
||||||
|
| 0x000080FF | `XEX_HEADER_BOUNDING_PATH` | Variable string | Bounding path |
|
||||||
|
| 0x00008105 | `XEX_HEADER_DEVICE_ID` | 20 bytes | Device ID |
|
||||||
|
| 0x00010001 | `XEX_HEADER_ORIGINAL_BASE_ADDRESS` | Inline u32 | Original PE base address |
|
||||||
|
| 0x00010100 | `XEX_HEADER_ENTRY_POINT` | Inline u32 | Program entry point (memory address) |
|
||||||
|
| 0x00010201 | `XEX_HEADER_IMAGE_BASE_ADDRESS` | Inline u32 | Load base address override |
|
||||||
|
| 0x000103FF | `XEX_HEADER_IMPORT_LIBRARIES` | Variable | Import library table |
|
||||||
|
| 0x00018002 | `XEX_HEADER_CHECKSUM_TIMESTAMP` | 8 bytes | Checksum + timestamp |
|
||||||
|
| 0x00018102 | `XEX_HEADER_ENABLED_FOR_CALLCAP` | 8 bytes | Callcap thunk addresses |
|
||||||
|
| 0x00018200 | `XEX_HEADER_ENABLED_FOR_FASTCAP` | Inline u32 | Fastcap enabled |
|
||||||
|
| 0x000183FF | `XEX_HEADER_ORIGINAL_PE_NAME` | Variable string | Original PE file name |
|
||||||
|
| 0x000200FF | `XEX_HEADER_STATIC_LIBRARIES` | Variable | Linked static library info |
|
||||||
|
| 0x00020104 | `XEX_HEADER_TLS_INFO` | 16 bytes | Thread-Local Storage info |
|
||||||
|
| 0x00020200 | `XEX_HEADER_DEFAULT_STACK_SIZE` | Inline u32 | Default stack size |
|
||||||
|
| 0x00020301 | `XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE` | Inline u32 | FS cache size |
|
||||||
|
| 0x00020401 | `XEX_HEADER_DEFAULT_HEAP_SIZE` | Inline u32 | Default heap size |
|
||||||
|
| 0x00028002 | `XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS` | 8 bytes | Page heap config |
|
||||||
|
| 0x00030000 | `XEX_HEADER_SYSTEM_FLAGS` | Inline u32 | System privilege flags |
|
||||||
|
| 0x00030100 | `XEX_HEADER_SYSTEM_FLAGS_32` | Inline u32 | Extended system flags (Kinect, etc.) |
|
||||||
|
| 0x00030200 | `XEX_HEADER_SYSTEM_FLAGS_64` | Inline u32 | 64-bit privilege flags |
|
||||||
|
| 0x00040006 | `XEX_HEADER_EXECUTION_INFO` | 24 bytes | Title ID, media ID, disc info |
|
||||||
|
| 0x00040201 | `XEX_HEADER_TITLE_WORKSPACE_SIZE` | Inline u32 | Title workspace size |
|
||||||
|
| 0x00040310 | `XEX_HEADER_GAME_RATINGS` | 64 bytes | Game content ratings |
|
||||||
|
| 0x00040404 | `XEX_HEADER_LAN_KEY` | 16 bytes | LAN encryption key |
|
||||||
|
| 0x000405FF | `XEX_HEADER_XBOX360_LOGO` | Variable | Xbox 360 logo bitmap |
|
||||||
|
| 0x000406FF | `XEX_HEADER_MULTIDISC_MEDIA_IDS` | Variable | Multi-disc media IDs |
|
||||||
|
| 0x000407FF | `XEX_HEADER_ALTERNATE_TITLE_IDS` | Variable | Alternate title IDs |
|
||||||
|
| 0x00040801 | `XEX_HEADER_ADDITIONAL_TITLE_MEMORY` | Inline u32 | Extra title memory |
|
||||||
|
| 0x00E10402 | `XEX_HEADER_EXPORTS_BY_NAME` | 8 bytes | PE export directory info |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Security Info (`xex2_security_info`)
|
||||||
|
|
||||||
|
Located at **XEX offset = `xex2_header.security_offset`** (from start of file).
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x000 | 4 | `header_size` | Size of this security info structure |
|
||||||
|
| 0x004 | 4 | `image_size` | Size of the decompressed PE image |
|
||||||
|
| 0x008 | 256 (0x100) | `rsa_signature` | RSA-2048 signature over the header |
|
||||||
|
| 0x108 | 4 | `unk_108` | Unknown (length field?) |
|
||||||
|
| 0x10C | 4 | `image_flags` | `xex2_image_flags` bitmask |
|
||||||
|
| 0x110 | 4 | `load_address` | Virtual memory address where the PE is loaded (e.g. 0x82000000) |
|
||||||
|
| 0x114 | 20 (0x14) | `section_digest` | SHA-1 digest of section data |
|
||||||
|
| 0x128 | 4 | `import_table_count` | Number of import table entries |
|
||||||
|
| 0x12C | 20 (0x14) | `import_table_digest` | SHA-1 digest of import table |
|
||||||
|
| 0x140 | 16 (0x10) | `xgd2_media_id` | XGD2 media identifier |
|
||||||
|
| 0x150 | 16 (0x10) | `aes_key` | **Encrypted AES-128 session key** (see Encryption section) |
|
||||||
|
| 0x160 | 4 | `export_table` | Memory address of the XEX export table (0 if none) |
|
||||||
|
| 0x164 | 20 (0x14) | `header_digest` | SHA-1 digest of header |
|
||||||
|
| 0x178 | 4 | `region` | Allowed regions (`xex2_region_flags`) |
|
||||||
|
| 0x17C | 4 | `allowed_media_types` | Allowed media types (`xex2_media_flags`) |
|
||||||
|
| 0x180 | 4 | `page_descriptor_count` | Number of page descriptors following |
|
||||||
|
| 0x184 | 24 * N | `page_descriptors[N]` | Array of `xex2_page_descriptor` entries |
|
||||||
|
|
||||||
|
### Image Flags (`xex2_image_flags`, bitmask)
|
||||||
|
|
||||||
|
| Value | Name |
|
||||||
|
|-------|------|
|
||||||
|
| 0x00000002 | Manufacturing utility |
|
||||||
|
| 0x00000004 | Manufacturing support tools |
|
||||||
|
| 0x00000008 | XGD2 media only |
|
||||||
|
| 0x00000100 | Cardea key |
|
||||||
|
| 0x00000200 | Xeika key |
|
||||||
|
| 0x00000400 | Usermode title |
|
||||||
|
| 0x00000800 | Usermode system |
|
||||||
|
| 0x10000000 | **4KB page size** (otherwise 64KB) |
|
||||||
|
| 0x20000000 | Region free |
|
||||||
|
| 0x40000000 | Revocation check optional |
|
||||||
|
| 0x80000000 | Revocation check required |
|
||||||
|
|
||||||
|
### Region Flags (`xex2_region_flags`, bitmask)
|
||||||
|
|
||||||
|
| Value | Region |
|
||||||
|
|-------|--------|
|
||||||
|
| 0x000000FF | NTSC/U (North America) |
|
||||||
|
| 0x0000FF00 | NTSC/J (Japan + Asia) |
|
||||||
|
| 0x00000100 | NTSC/J - Japan |
|
||||||
|
| 0x00000200 | NTSC/J - China |
|
||||||
|
| 0x00FF0000 | PAL (Europe) |
|
||||||
|
| 0x00010000 | PAL - Australia/New Zealand |
|
||||||
|
| 0xFF000000 | Other regions |
|
||||||
|
| 0xFFFFFFFF | All regions (region-free) |
|
||||||
|
|
||||||
|
### Media Flags (`xex2_media_flags`, bitmask)
|
||||||
|
|
||||||
|
| Value | Media Type |
|
||||||
|
|-------|------------|
|
||||||
|
| 0x00000001 | Hard disk |
|
||||||
|
| 0x00000002 | DVD X2 |
|
||||||
|
| 0x00000004 | DVD/CD |
|
||||||
|
| 0x00000008 | DVD-5 |
|
||||||
|
| 0x00000010 | DVD-9 |
|
||||||
|
| 0x00000020 | System flash |
|
||||||
|
| 0x00000080 | Memory unit |
|
||||||
|
| 0x00000100 | USB mass storage |
|
||||||
|
| 0x00000200 | Network |
|
||||||
|
| 0x00000400 | Direct from memory |
|
||||||
|
| 0x00000800 | RAM drive |
|
||||||
|
| 0x00001000 | SVOD |
|
||||||
|
| 0x01000000 | Insecure package |
|
||||||
|
| 0x02000000 | Savegame package |
|
||||||
|
| 0x04000000 | Locally signed package |
|
||||||
|
| 0x08000000 | LIVE signed package |
|
||||||
|
| 0x10000000 | Xbox package |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Page Descriptors (`xex2_page_descriptor`)
|
||||||
|
|
||||||
|
Each page descriptor is **24 bytes** and immediately follows `page_descriptor_count` in the security info (starting at **XEX offset = security_offset + 0x184**).
|
||||||
|
|
||||||
|
```
|
||||||
|
+----------------------------------+
|
||||||
|
| Bits 31-28 | Bits 27-0 | 0x00 (4 bytes, combined bitfield)
|
||||||
|
| info (4b) | page_count (28b) |
|
||||||
|
+----------------------------------+
|
||||||
|
| data_digest (20 bytes, SHA-1) | 0x04
|
||||||
|
+----------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `value` | Combined bitfield (big-endian, must be byte-swapped before reading bits) |
|
||||||
|
| | | `.info` (bits 31-28) | Section type: 1=Code, 2=Data, 3=Read-only data |
|
||||||
|
| | | `.page_count` (bits 27-0) | Number of pages in this section |
|
||||||
|
| 0x04 | 20 | `data_digest` | SHA-1 hash of the page data |
|
||||||
|
|
||||||
|
**Page size** is determined by `XEX_IMAGE_PAGE_SIZE_4KB` in image flags:
|
||||||
|
- If set: **4 KB** (0x1000) pages
|
||||||
|
- If not set: **64 KB** (0x10000) pages
|
||||||
|
|
||||||
|
**Memory mapping**: Pages are mapped sequentially starting at `load_address`. For page descriptor `i`, the memory address is:
|
||||||
|
```
|
||||||
|
address = load_address + (sum of all previous page_counts) * page_size
|
||||||
|
size = desc.page_count * page_size
|
||||||
|
```
|
||||||
|
|
||||||
|
Section types determine memory protection:
|
||||||
|
| Type | Value | Protection |
|
||||||
|
|------|-------|------------|
|
||||||
|
| `XEX_SECTION_CODE` | 1 | Read-only (or Read+Write if writable_code_segments) |
|
||||||
|
| `XEX_SECTION_DATA` | 2 | Read + Write |
|
||||||
|
| `XEX_SECTION_READONLY_DATA` | 3 | Read-only |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Encryption
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
XEX2 uses a **two-level AES-128-CBC** encryption scheme:
|
||||||
|
|
||||||
|
1. The **session key** (per-XEX) is stored encrypted in `xex2_security_info.aes_key` (at security info offset 0x150).
|
||||||
|
2. This session key is itself encrypted with one of the well-known **master keys**.
|
||||||
|
3. The session key is then used to decrypt the PE image payload.
|
||||||
|
|
||||||
|
### Master AES-128 Keys
|
||||||
|
|
||||||
|
| Key | Value (hex) | Usage |
|
||||||
|
|-----|-------------|-------|
|
||||||
|
| **XEX2 Retail** | `20 B1 85 A5 9D 28 FD C3 40 58 3F BB 08 96 BF 91` | Production/retail XEX2 files |
|
||||||
|
| **XEX2 DevKit** | `00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00` | Development kit XEX2 files (null key) |
|
||||||
|
| **XEX1 Retail** | `A2 6C 10 F7 1F D9 35 E9 8B 99 92 2C E9 32 15 72` | Legacy XEX1 format |
|
||||||
|
|
||||||
|
### Key Derivation Process
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Read encrypted_session_key from xex2_security_info.aes_key[0x10]
|
||||||
|
2. Decrypt encrypted_session_key using master_key with AES-128-CBC (IV = 0)
|
||||||
|
→ This yields the session_key[16]
|
||||||
|
3. Use session_key to decrypt the PE image payload with AES-128-CBC (IV = 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### AES-128-CBC Decryption Algorithm
|
||||||
|
|
||||||
|
The decryption used is standard **AES-128 in CBC mode** with a **zero IV** (16 bytes of 0x00):
|
||||||
|
|
||||||
|
```
|
||||||
|
Input: session_key[16], ciphertext, length
|
||||||
|
State: IV[16] = {0, 0, ..., 0}
|
||||||
|
rk[] = rijndaelKeySetupDec(session_key, 128) // 128-bit key
|
||||||
|
|
||||||
|
For each 16-byte block:
|
||||||
|
plaintext_block = rijndaelDecrypt(rk, Nr, ciphertext_block)
|
||||||
|
plaintext_block ^= IV // XOR with previous ciphertext (or IV for first block)
|
||||||
|
IV = ciphertext_block // Update IV to current ciphertext
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation**: Uses the Rijndael reference implementation (`rijndael-alg-fst.c`), with `Nr` rounds returned by `rijndaelKeySetupDec()` (10 rounds for AES-128).
|
||||||
|
|
||||||
|
### Key Trial Order
|
||||||
|
|
||||||
|
The loader tries keys in this order, falling back on failure:
|
||||||
|
1. XEX2 Retail key
|
||||||
|
2. XEX2 DevKit key (all zeros)
|
||||||
|
3. XEX1 Retail key
|
||||||
|
|
||||||
|
Success is determined by checking if the decrypted image begins with a valid PE signature (`MZ` / 0x5A4D).
|
||||||
|
|
||||||
|
### Encryption Type (`xex2_encryption_type`)
|
||||||
|
|
||||||
|
Stored in `xex2_opt_file_format_info.encryption_type`:
|
||||||
|
|
||||||
|
| Value | Name | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| 0 | `XEX_ENCRYPTION_NONE` | PE image is not encrypted |
|
||||||
|
| 1 | `XEX_ENCRYPTION_NORMAL` | PE image is AES-128-CBC encrypted |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Compression
|
||||||
|
|
||||||
|
Compression type is stored in `xex2_opt_file_format_info.compression_type`.
|
||||||
|
|
||||||
|
### File Format Info (`xex2_opt_file_format_info`)
|
||||||
|
|
||||||
|
Pointed to by optional header key `0x000003FF` (`XEX_HEADER_FILE_FORMAT_INFO`).
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `info_size` | Total size of this structure |
|
||||||
|
| 0x04 | 2 | `encryption_type` | 0=None, 1=Normal (AES-128-CBC) |
|
||||||
|
| 0x06 | 2 | `compression_type` | 0=None, 1=Basic, 2=Normal, 3=Delta |
|
||||||
|
| 0x08 | ... | `compression_info` | Union: basic or normal compression info |
|
||||||
|
|
||||||
|
### Compression Types
|
||||||
|
|
||||||
|
| Value | Name | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| 0 | `XEX_COMPRESSION_NONE` | No compression; raw PE image data |
|
||||||
|
| 1 | `XEX_COMPRESSION_BASIC` | Block-based zero-fill compression |
|
||||||
|
| 2 | `XEX_COMPRESSION_NORMAL` | LZX (Lempel-Ziv extended) compression with SHA-1 block chaining |
|
||||||
|
| 3 | `XEX_COMPRESSION_DELTA` | Delta patch compression (for update patches) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7a. No Compression (`XEX_COMPRESSION_NONE`)
|
||||||
|
|
||||||
|
The PE image starts at **XEX offset = `header_size`** and extends to end of file. The raw data length is `xex_file_size - header_size`. If encrypted, the entire payload is decrypted in-place with AES-128-CBC using the session key.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7b. Basic Compression (`XEX_COMPRESSION_BASIC`)
|
||||||
|
|
||||||
|
The compression info contains an array of block descriptors that describe alternating data and zero-filled regions.
|
||||||
|
|
||||||
|
#### Basic Compression Block (`xex2_file_basic_compression_block`)
|
||||||
|
|
||||||
|
Located at `xex2_opt_file_format_info` offset 0x08. The number of blocks is `(info_size - 8) / 8`.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `data_size` | Bytes of real data to copy from the XEX payload |
|
||||||
|
| 0x04 | 4 | `zero_size` | Bytes of zeros to append after the data |
|
||||||
|
|
||||||
|
**Decompression process** (after AES-128-CBC decryption if `encryption_type == NORMAL`):
|
||||||
|
|
||||||
|
```
|
||||||
|
source_ptr = XEX file + header_size // start of PE payload in XEX file
|
||||||
|
dest_ptr = base_address in memory // Xbox 360 virtual memory
|
||||||
|
|
||||||
|
For each block[i]:
|
||||||
|
Copy block[i].data_size bytes from source_ptr to dest_ptr
|
||||||
|
Advance source_ptr by data_size
|
||||||
|
Zero-fill block[i].zero_size bytes at dest_ptr + data_size
|
||||||
|
Advance dest_ptr by (data_size + zero_size)
|
||||||
|
```
|
||||||
|
|
||||||
|
The total uncompressed size = sum of all `(data_size + zero_size)` across all blocks.
|
||||||
|
|
||||||
|
**Note on encryption with basic compression**: When encryption is `NORMAL`, the AES-128-CBC decryption is performed **inline per block** — the CBC IV state carries across block boundaries (it is NOT reset per block). The same `session_key` and continuous CBC state are used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7c. Normal Compression (`XEX_COMPRESSION_NORMAL`)
|
||||||
|
|
||||||
|
This is a two-stage process: de-blocking, then LZX decompression.
|
||||||
|
|
||||||
|
#### Normal Compression Info (`xex2_file_normal_compression_info`)
|
||||||
|
|
||||||
|
Located at `xex2_opt_file_format_info` offset 0x08:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `window_size` | LZX decompression window size in bytes (must be power of 2) |
|
||||||
|
| 0x04 | 4 | `first_block.block_size` | Size of the first compressed block in bytes |
|
||||||
|
| 0x08 | 20 | `first_block.block_hash` | SHA-1 hash of the first block's data |
|
||||||
|
|
||||||
|
#### Compressed Block Info (`xex2_compressed_block_info`)
|
||||||
|
|
||||||
|
Each block in the compressed stream is described by a chained structure:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `block_size` | Total size of *this* block in bytes (0 = end of chain) |
|
||||||
|
| 0x04 | 20 | `block_hash` | SHA-1 hash of *this* block's data |
|
||||||
|
|
||||||
|
**Block chaining**: The `block_size` and `block_hash` of the *next* block are stored at the **beginning** of the *current* block's data. This creates a hash chain for integrity verification.
|
||||||
|
|
||||||
|
#### Decompression Process
|
||||||
|
|
||||||
|
```
|
||||||
|
1. DECRYPT (if encrypted):
|
||||||
|
Decrypt the entire PE payload (XEX file + header_size, length = file_size - header_size)
|
||||||
|
using AES-128-CBC with session_key and zero IV.
|
||||||
|
|
||||||
|
2. DE-BLOCK:
|
||||||
|
current_block_info = first_block from compression_info header
|
||||||
|
source_ptr = start of decrypted payload
|
||||||
|
dest_buffer = temporary buffer
|
||||||
|
|
||||||
|
While current_block_info.block_size != 0:
|
||||||
|
a. Verify SHA-1(source_ptr, current_block_info.block_size) == current_block_info.block_hash
|
||||||
|
b. Read next_block_info from source_ptr:
|
||||||
|
next_block_size = bytes [0..3] (4 bytes)
|
||||||
|
next_block_hash = bytes [4..23] (20 bytes)
|
||||||
|
c. Skip past block header (4 + 20 = 24 bytes)
|
||||||
|
d. Read data chunks:
|
||||||
|
While true:
|
||||||
|
chunk_size = read 2 bytes (big-endian uint16)
|
||||||
|
If chunk_size == 0: break (end of block)
|
||||||
|
Copy chunk_size bytes to dest_buffer
|
||||||
|
e. Advance source_ptr to: previous source_ptr + current_block_info.block_size
|
||||||
|
f. current_block_info = next_block_info
|
||||||
|
|
||||||
|
3. LZX DECOMPRESS:
|
||||||
|
Decompress dest_buffer using LZX algorithm:
|
||||||
|
- Input: de-blocked data
|
||||||
|
- Output size: image_size (from page descriptors sum)
|
||||||
|
- Window size: compression_info.normal.window_size
|
||||||
|
- Reset interval: 0 (no reset)
|
||||||
|
- Frame size: 0x8000 (32 KB)
|
||||||
|
|
||||||
|
Output is written to memory at base_address.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### LZX Algorithm Details
|
||||||
|
|
||||||
|
- **Algorithm**: LZX (Lempel-Ziv Extended), the same algorithm used in Microsoft CAB files
|
||||||
|
- **Implementation**: mspack library (`lzxd.c`)
|
||||||
|
- **Window size**: Specified per-XEX in `window_size` field (typically a power of 2; common values include 0x20000 = 128KB)
|
||||||
|
- **Window bits**: `log2(window_size)` — computed via bit scan
|
||||||
|
- **Frame size**: Fixed at `0x8000` (32,768 bytes)
|
||||||
|
- **Reset interval**: 0 (no periodic state reset)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7d. Delta Compression / Patching (`XEX_COMPRESSION_DELTA`)
|
||||||
|
|
||||||
|
Used for XEX patches (XEXP files). The patch XEX has `XEX_MODULE_PATCH_DELTA` set in module_flags.
|
||||||
|
|
||||||
|
#### Delta Patch Descriptor (`xex2_opt_delta_patch_descriptor`)
|
||||||
|
|
||||||
|
Pointed to by optional header key `0x000005FF`:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Size of the header patch data |
|
||||||
|
| 0x04 | 4 | `target_version_value` | Target version after patch (xex2_version bitfield) |
|
||||||
|
| 0x08 | 4 | `source_version_value` | Source version required (xex2_version bitfield) |
|
||||||
|
| 0x0C | 20 | `digest_source` | SHA-1 digest of source image |
|
||||||
|
| 0x20 | 16 | `image_key_source` | Key verification data |
|
||||||
|
| 0x30 | 4 | `size_of_target_headers` | Size of target XEX headers after patch |
|
||||||
|
| 0x34 | 4 | `delta_headers_source_offset` | Offset within source XEX headers to copy from |
|
||||||
|
| 0x38 | 4 | `delta_headers_source_size` | Size of source header data to copy |
|
||||||
|
| 0x3C | 4 | `delta_headers_target_offset` | Offset within target XEX headers to copy to |
|
||||||
|
| 0x40 | 4 | `delta_image_source_offset` | Offset within source PE image to copy from |
|
||||||
|
| 0x44 | 4 | `delta_image_source_size` | Size of source image data to copy |
|
||||||
|
| 0x48 | 4 | `delta_image_target_offset` | Offset within target PE image to copy to |
|
||||||
|
| 0x4C | ... | `info` | First `xex2_delta_patch` entry (inline) |
|
||||||
|
|
||||||
|
#### Delta Patch Entry (`xex2_delta_patch`)
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `old_addr` | Offset in the **existing memory image** to read source data from |
|
||||||
|
| 0x04 | 4 | `new_addr` | Offset in the **existing memory image** to write patched data to |
|
||||||
|
| 0x08 | 2 | `uncompressed_len` | Size of decompressed output |
|
||||||
|
| 0x0A | 2 | `compressed_len` | Size of compressed patch data (special values below) |
|
||||||
|
| 0x0C | ... | `patch_data` | Compressed patch data (variable length) |
|
||||||
|
|
||||||
|
**Special `compressed_len` values:**
|
||||||
|
| Value | Action |
|
||||||
|
|-------|--------|
|
||||||
|
| 0 | Zero-fill: `memset(dest + new_addr, 0, uncompressed_len)` |
|
||||||
|
| 1 | Copy: `memcpy(dest + new_addr, dest + old_addr, uncompressed_len)` |
|
||||||
|
| >= 2 | LZX delta decompress: decompress `patch_data` using `old_addr` data as window reference |
|
||||||
|
|
||||||
|
#### Delta Patch Key Handling
|
||||||
|
|
||||||
|
Delta patches use a three-level key scheme:
|
||||||
|
1. **Base module's session key** is decrypted using the master key (as normal)
|
||||||
|
2. The **patch's encrypted AES key** is then decrypted using the **base module's session key** (not the master key)
|
||||||
|
3. Verification: `AES_Decrypt(base_session_key, patch_descriptor.image_key_source)` must equal the **original** session key of the base module
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Import Libraries
|
||||||
|
|
||||||
|
Located via optional header key `0x000103FF` (`XEX_HEADER_IMPORT_LIBRARIES`).
|
||||||
|
|
||||||
|
### Import Libraries Container (`xex2_opt_import_libraries`)
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Total size of the import libraries structure |
|
||||||
|
| 0x04 | 4 | `string_table.size` | Size of the string table in bytes |
|
||||||
|
| 0x08 | 4 | `string_table.count` | Number of strings in the table |
|
||||||
|
| 0x0C | N | `string_table.data` | Null-terminated strings, 4-byte aligned with padding |
|
||||||
|
|
||||||
|
Library entries follow immediately after the string table (at offset `string_table.size + 12`).
|
||||||
|
|
||||||
|
### Import Library (`xex2_import_library`)
|
||||||
|
|
||||||
|
Each library entry:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Size of this library entry in bytes |
|
||||||
|
| 0x04 | 20 | `next_import_digest` | SHA-1 digest of next import entry |
|
||||||
|
| 0x18 | 4 | `id` | Library ID |
|
||||||
|
| 0x1C | 4 | `version_value` | Library version (xex2_version bitfield) |
|
||||||
|
| 0x20 | 4 | `version_min_value` | Minimum required version |
|
||||||
|
| 0x24 | 2 | `name_index` | Index (low byte) into the string table |
|
||||||
|
| 0x26 | 2 | `count` | Number of import records |
|
||||||
|
| 0x28 | 4 * N | `import_table[N]` | Array of import record **memory addresses** |
|
||||||
|
|
||||||
|
### Import Record Format (in memory)
|
||||||
|
|
||||||
|
Each entry in `import_table` is a **memory address** pointing to a location within the loaded PE image. At that memory address, the value is:
|
||||||
|
|
||||||
|
```
|
||||||
|
Bits 31-24 (byte 0): record_type
|
||||||
|
0x00 = Variable import
|
||||||
|
0x01 = Thunk (function) import
|
||||||
|
Bits 15-0 (bytes 2-3): ordinal number
|
||||||
|
```
|
||||||
|
|
||||||
|
**Variable imports** (record_type == 0): The memory slot is overwritten with:
|
||||||
|
- For kernel exports (implemented): the variable's address
|
||||||
|
- For kernel exports (not implemented): `0xD000BEEF | (ordinal & 0xFFF) << 16`
|
||||||
|
- For user module exports: the export address
|
||||||
|
- For unresolved imports: `0xF00DF00D`
|
||||||
|
|
||||||
|
**Thunk imports** (record_type == 1): The 16-byte thunk in memory is originally:
|
||||||
|
```
|
||||||
|
+0x00: li r3, 0 // 0x38600000
|
||||||
|
+0x04: li r4, <ordinal> // 0x38800000 | ordinal
|
||||||
|
+0x08: mtspr CTR, r11 // 0x7D6903A6
|
||||||
|
+0x0C: bctr // 0x4E800420
|
||||||
|
```
|
||||||
|
|
||||||
|
For user module imports, this is rewritten to:
|
||||||
|
```
|
||||||
|
+0x00: lis r11, <addr_hi> // 0x3D600000 | (addr >> 16)
|
||||||
|
+0x04: ori r11, r11, <addr_lo>// 0x616B0000 | (addr & 0xFFFF)
|
||||||
|
+0x08: mtspr CTR, r11 // (unchanged)
|
||||||
|
+0x0C: bctr // (unchanged)
|
||||||
|
```
|
||||||
|
|
||||||
|
Import records alternate: variable descriptor, then thunk address, then next variable descriptor, etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Export Table (`xex2_export_table`)
|
||||||
|
|
||||||
|
Located at the **memory address** specified in `xex2_security_info.export_table`. This is a virtual address, NOT a file offset.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 12 | `magic[3]` | Magic identifier (3 uint32_t values) |
|
||||||
|
| 0x0C | 8 | `modulenumber[2]` | Module number (2 uint32_t values) |
|
||||||
|
| 0x14 | 12 | `version[3]` | Version info (3 uint32_t values) |
|
||||||
|
| 0x20 | 4 | `imagebaseaddr` | Image base address (must be shifted left 16 bits to get actual address) |
|
||||||
|
| 0x24 | 4 | `count` | Number of exports |
|
||||||
|
| 0x28 | 4 | `base` | Base ordinal number |
|
||||||
|
| 0x2C | 4 * N | `ordOffset[N]` | Array of ordinal offsets |
|
||||||
|
|
||||||
|
**Resolving an export address:**
|
||||||
|
```
|
||||||
|
function_address = ordOffset[ordinal - base] + (imagebaseaddr << 16)
|
||||||
|
```
|
||||||
|
|
||||||
|
### PE Export Directory (`X_IMAGE_EXPORT_DIRECTORY`)
|
||||||
|
|
||||||
|
An alternative export mechanism via the PE header (optional header key `XEX_HEADER_EXPORTS_BY_NAME`). The `xex2_opt_data_directory` at that key contains:
|
||||||
|
- `offset`: RVA from PE base to the `X_IMAGE_EXPORT_DIRECTORY`
|
||||||
|
- `size`: Size of the export directory
|
||||||
|
|
||||||
|
The export directory is standard PE format (little-endian within the Xbox PE):
|
||||||
|
|
||||||
|
| Offset | Size | Field |
|
||||||
|
|--------|------|-------|
|
||||||
|
| 0x00 | 4 | Characteristics |
|
||||||
|
| 0x04 | 4 | TimeDateStamp |
|
||||||
|
| 0x08 | 2 | MajorVersion |
|
||||||
|
| 0x0A | 2 | MinorVersion |
|
||||||
|
| 0x0C | 4 | Name (RVA) |
|
||||||
|
| 0x10 | 4 | Base ordinal |
|
||||||
|
| 0x14 | 4 | NumberOfFunctions |
|
||||||
|
| 0x18 | 4 | NumberOfNames |
|
||||||
|
| 0x1C | 4 | AddressOfFunctions (RVA from export directory) |
|
||||||
|
| 0x20 | 4 | AddressOfNames (RVA from export directory) |
|
||||||
|
| 0x24 | 4 | AddressOfNameOrdinals (RVA from export directory) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Specific Optional Header Structures
|
||||||
|
|
||||||
|
### Execution Info (`xex2_opt_execution_info`) — Key 0x00040006
|
||||||
|
|
||||||
|
24 bytes (0x18). All offsets relative to start of structure.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `media_id` | Media identifier |
|
||||||
|
| 0x04 | 4 | `version_value` | Module version (xex2_version bitfield) |
|
||||||
|
| 0x08 | 4 | `base_version_value` | Base version |
|
||||||
|
| 0x0C | 4 | `title_id` | Title ID (e.g. 0x415607D1) |
|
||||||
|
| 0x10 | 1 | `platform` | Platform identifier |
|
||||||
|
| 0x11 | 1 | `executable_table` | Executable table index |
|
||||||
|
| 0x12 | 1 | `disc_number` | Current disc number |
|
||||||
|
| 0x13 | 1 | `disc_count` | Total disc count |
|
||||||
|
| 0x14 | 4 | `savegame_id` | Savegame identifier |
|
||||||
|
|
||||||
|
### Version Bitfield (`xex2_version`)
|
||||||
|
|
||||||
|
Packed into a 32-bit big-endian value:
|
||||||
|
|
||||||
|
| Bits | Field | Width |
|
||||||
|
|------|-------|-------|
|
||||||
|
| 31-28 | `major` | 4 bits |
|
||||||
|
| 27-24 | `minor` | 4 bits |
|
||||||
|
| 23-8 | `build` | 16 bits |
|
||||||
|
| 7-0 | `qfe` | 8 bits |
|
||||||
|
|
||||||
|
### TLS Info (`xex2_opt_tls_info`) — Key 0x00020104
|
||||||
|
|
||||||
|
16 bytes (0x10):
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `slot_count` | Number of TLS slots |
|
||||||
|
| 0x04 | 4 | `raw_data_address` | Memory address of TLS raw data |
|
||||||
|
| 0x08 | 4 | `data_size` | Total TLS data size |
|
||||||
|
| 0x0C | 4 | `raw_data_size` | Size of initialized TLS data |
|
||||||
|
|
||||||
|
### Checksum / Timestamp (`xex2_opt_checksum_timedatestamp`) — Key 0x00018002
|
||||||
|
|
||||||
|
8 bytes:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `checksum` | Module checksum |
|
||||||
|
| 0x04 | 4 | `timedatestamp` | Unix timestamp of build |
|
||||||
|
|
||||||
|
### Resource Info (`xex2_opt_resource_info`) — Key 0x000002FF
|
||||||
|
|
||||||
|
Variable size. Resource count = `(size - 4) / 16`.
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Total size of resource info |
|
||||||
|
| 0x04 | 16 * N | `resources[N]` | Array of `xex2_resource` |
|
||||||
|
|
||||||
|
Each `xex2_resource` (16 bytes):
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 8 | `name` | Resource name (null-padded) |
|
||||||
|
| 0x08 | 4 | `address` | Memory address of resource |
|
||||||
|
| 0x0C | 4 | `size` | Size of resource in bytes |
|
||||||
|
|
||||||
|
### Static Libraries (`xex2_opt_static_libraries`) — Key 0x000200FF
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Total size. Library count = (size - 4) / 16 |
|
||||||
|
| 0x04 | 16 * N | `libraries[N]` | Array of `xex2_opt_static_library` |
|
||||||
|
|
||||||
|
Each `xex2_opt_static_library` (16 bytes / 0x10):
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 8 | `name` | Library name (null-padded) |
|
||||||
|
| 0x08 | 2 | `version_major` | Major version |
|
||||||
|
| 0x0A | 2 | `version_minor` | Minor version |
|
||||||
|
| 0x0C | 2 | `version_build` | Build number |
|
||||||
|
| 0x0E | 1 | `approval_type` | 0=Unapproved, 1=Possible, 2=Approved, 3=Expired |
|
||||||
|
| 0x0F | 1 | `version_qfe` | QFE version |
|
||||||
|
|
||||||
|
### LAN Key (`xex2_opt_lan_key`) — Key 0x00040404
|
||||||
|
|
||||||
|
16 bytes: raw AES-128 key used for LAN multiplayer encryption.
|
||||||
|
|
||||||
|
### Game Ratings (`xex2_game_ratings_t`) — Key 0x00040310
|
||||||
|
|
||||||
|
64 bytes (0x40) containing age ratings for various regional rating boards:
|
||||||
|
|
||||||
|
| Offset | Size | Board |
|
||||||
|
|--------|------|-------|
|
||||||
|
| 0x00 | 1 | ESRB (North America) |
|
||||||
|
| 0x01 | 1 | PEGI (Europe) |
|
||||||
|
| 0x02 | 1 | PEGI Finland |
|
||||||
|
| 0x03 | 1 | PEGI Portugal |
|
||||||
|
| 0x04 | 1 | BBFC (UK/Ireland) |
|
||||||
|
| 0x05 | 1 | CERO (Japan) |
|
||||||
|
| 0x06 | 1 | USK (Germany) |
|
||||||
|
| 0x07 | 1 | OFLC Australia |
|
||||||
|
| 0x08 | 1 | OFLC New Zealand |
|
||||||
|
| 0x09 | 1 | KMRB (South Korea) |
|
||||||
|
| 0x0A | 1 | Brazil |
|
||||||
|
| 0x0B | 1 | FPB (South Africa) |
|
||||||
|
| 0x0C | 52 | Reserved / Unknown |
|
||||||
|
|
||||||
|
Each rating value is 0xFF for "Unrated".
|
||||||
|
|
||||||
|
### Callcap Imports (`xex2_opt_call_cap_imports`) — Key 0x00018102
|
||||||
|
|
||||||
|
8 bytes:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `start_func_thunk_addr` | Memory address of start function thunk |
|
||||||
|
| 0x04 | 4 | `end_func_thunk_addr` | Memory address of end function thunk |
|
||||||
|
|
||||||
|
### Data Directory (`xex2_opt_data_directory`) — Key 0x00E10402
|
||||||
|
|
||||||
|
8 bytes, used for PE exports-by-name:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `offset` | RVA from PE image base |
|
||||||
|
| 0x04 | 4 | `size` | Size of the directory |
|
||||||
|
|
||||||
|
### Bound Path (`xex2_opt_bound_path`) — Key 0x000080FF
|
||||||
|
|
||||||
|
Variable length:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Total size including this field |
|
||||||
|
| 0x04 | N | `path` | Null-terminated path string |
|
||||||
|
|
||||||
|
### Original PE Name (`xex2_opt_original_pe_name`) — Key 0x000183FF
|
||||||
|
|
||||||
|
Variable length:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `size` | Total size including this field |
|
||||||
|
| 0x04 | N | `name` | Null-terminated original PE filename |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. PE Image (After Decryption/Decompression)
|
||||||
|
|
||||||
|
After decryption and decompression, the PE image is loaded into Xbox 360 virtual memory at `load_address` (from security info, or overridden by `XEX_HEADER_IMAGE_BASE_ADDRESS`).
|
||||||
|
|
||||||
|
### PE Headers (in memory at `load_address`)
|
||||||
|
|
||||||
|
The PE image is a standard 32-bit PE executable for PowerPC Big-Endian:
|
||||||
|
|
||||||
|
#### DOS Header (at memory base address)
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 2 | `e_magic` | `MZ` signature (0x5A4D) — note: stored as 0x905A4D with byte swap check |
|
||||||
|
| ... | ... | ... | Standard DOS header fields |
|
||||||
|
| 0x3C | 4 | `e_lfanew` | Offset to NT headers (from start of PE image) |
|
||||||
|
|
||||||
|
#### NT Headers (at PE offset `e_lfanew`)
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 4 | `Signature` | `PE\0\0` (0x00004550) |
|
||||||
|
| 0x04 | 20 | `FileHeader` | COFF file header |
|
||||||
|
| 0x18 | 224 | `OptionalHeader` | PE32 optional header |
|
||||||
|
|
||||||
|
#### File Header Validation
|
||||||
|
|
||||||
|
| Field | Expected Value | Description |
|
||||||
|
|-------|---------------|-------------|
|
||||||
|
| `Machine` | 0x01F2 | `IMAGE_FILE_MACHINE_POWERPCBE` |
|
||||||
|
| `Characteristics` | bit 0x0100 set | `IMAGE_FILE_32BIT_MACHINE` |
|
||||||
|
| `SizeOfOptionalHeader` | 224 (0xE0) | Standard PE32 optional header size |
|
||||||
|
|
||||||
|
#### Optional Header Validation
|
||||||
|
|
||||||
|
| Field | Expected Value |
|
||||||
|
|-------|---------------|
|
||||||
|
| `Magic` | 0x10B (`IMAGE_NT_OPTIONAL_HDR32_MAGIC`) |
|
||||||
|
| `Subsystem` | 14 (`IMAGE_SUBSYSTEM_XBOX`) |
|
||||||
|
|
||||||
|
#### Section Headers
|
||||||
|
|
||||||
|
Located immediately after the optional header. Each section header is 40 bytes:
|
||||||
|
|
||||||
|
| Offset | Size | Field | Description |
|
||||||
|
|--------|------|-------|-------------|
|
||||||
|
| 0x00 | 8 | `Name` | Section name (e.g. `.text`, `.rdata`, `.data`) |
|
||||||
|
| 0x08 | 4 | `VirtualSize` | Size in memory |
|
||||||
|
| 0x0C | 4 | `VirtualAddress` | RVA from PE image base |
|
||||||
|
| 0x10 | 4 | `SizeOfRawData` | Size of raw data |
|
||||||
|
| 0x14 | 4 | `PointerToRawData` | PE offset to raw data |
|
||||||
|
| 0x18 | 4 | `PointerToRelocations` | (not used) |
|
||||||
|
| 0x1C | 4 | `PointerToLinenumbers` | (not used) |
|
||||||
|
| 0x20 | 2 | `NumberOfRelocations` | (not used) |
|
||||||
|
| 0x22 | 2 | `NumberOfLinenumbers` | (not used) |
|
||||||
|
| 0x24 | 4 | `Characteristics` | Section flags |
|
||||||
|
|
||||||
|
Section characteristics relevant to Xbox 360:
|
||||||
|
|
||||||
|
| Value | Name |
|
||||||
|
|-------|------|
|
||||||
|
| 0x00000020 | Contains code |
|
||||||
|
| 0x00000040 | Contains initialized data |
|
||||||
|
| 0x00000080 | Contains uninitialized data |
|
||||||
|
| 0x20000000 | Memory execute |
|
||||||
|
| 0x40000000 | Memory read |
|
||||||
|
| 0x80000000 | Memory write |
|
||||||
|
|
||||||
|
The in-memory address of a section is: `load_address + VirtualAddress`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. System Flags (`xex2_system_flags`)
|
||||||
|
|
||||||
|
Inline u32 at optional header key `0x00030000`. Bitmask of system privileges:
|
||||||
|
|
||||||
|
| Value | Name | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| 0x00000001 | NO_FORCED_REBOOT | |
|
||||||
|
| 0x00000002 | FOREGROUND_TASKS | |
|
||||||
|
| 0x00000004 | NO_ODD_MAPPING | |
|
||||||
|
| 0x00000008 | HANDLE_MCE_INPUT | |
|
||||||
|
| 0x00000010 | RESTRICTED_HUD_FEATURES | |
|
||||||
|
| 0x00000020 | HANDLE_GAMEPAD_DISCONNECT | |
|
||||||
|
| 0x00000040 | INSECURE_SOCKETS | |
|
||||||
|
| 0x00000080 | XBOX1_INTEROPERABILITY | |
|
||||||
|
| 0x00000100 | DASH_CONTEXT | |
|
||||||
|
| 0x00000200 | USES_GAME_VOICE_CHANNEL | |
|
||||||
|
| 0x00000400 | PAL50_INCOMPATIBLE | |
|
||||||
|
| 0x00000800 | INSECURE_UTILITY_DRIVE | |
|
||||||
|
| 0x00001000 | XAM_HOOKS | |
|
||||||
|
| 0x00002000 | ACCESS_PII | |
|
||||||
|
| 0x00004000 | CROSS_PLATFORM_SYSTEM_LINK | |
|
||||||
|
| 0x00008000 | MULTIDISC_SWAP | |
|
||||||
|
| 0x00010000 | MULTIDISC_INSECURE_MEDIA | |
|
||||||
|
| 0x00020000 | AP25_MEDIA | Anti-piracy 2.5 media check |
|
||||||
|
| 0x00040000 | NO_CONFIRM_EXIT | |
|
||||||
|
| 0x00080000 | ALLOW_BACKGROUND_DOWNLOAD | |
|
||||||
|
| 0x00100000 | CREATE_PERSISTABLE_RAMDRIVE | |
|
||||||
|
| 0x00200000 | INHERIT_PERSISTENT_RAMDRIVE | |
|
||||||
|
| 0x00400000 | ALLOW_HUD_VIBRATION | |
|
||||||
|
| 0x00800000 | ACCESS_UTILITY_PARTITIONS | |
|
||||||
|
| 0x01000000 | IPTV_INPUT_SUPPORTED | |
|
||||||
|
| 0x02000000 | PREFER_BIG_BUTTON_INPUT | |
|
||||||
|
| 0x04000000 | ALLOW_EXTENDED_SYSTEM_RESERVATION | |
|
||||||
|
| 0x08000000 | MULTIDISC_CROSS_TITLE | |
|
||||||
|
| 0x10000000 | INSTALL_INCOMPATIBLE | |
|
||||||
|
| 0x20000000 | ALLOW_AVATAR_GET_METADATA_BY_XUID | |
|
||||||
|
| 0x40000000 | ALLOW_CONTROLLER_SWAPPING | |
|
||||||
|
| 0x80000000 | DASH_EXTENSIBILITY_MODULE | |
|
||||||
|
|
||||||
|
### Extended System Flags (32-bit) — Key 0x00030100
|
||||||
|
|
||||||
|
| Value | Name |
|
||||||
|
|-------|------|
|
||||||
|
| 0x00000001 | ALLOW_NETWORK_READ_CANCEL |
|
||||||
|
| 0x00000002 | UNINTERRUPTABLE_READS |
|
||||||
|
| 0x00000004 | REQUIRE_FULL_EXPERIENCE |
|
||||||
|
| 0x00000008 | GAME_VOICE_REQUIRED_UI |
|
||||||
|
| 0x00000010 | TITLE_SET_PRESENCE_STRING |
|
||||||
|
| 0x00000020 | CAMERA_ANGLE_CONTROL |
|
||||||
|
| 0x00000040 | SKELETAL_TRACKING_REQUIRED |
|
||||||
|
| 0x00000080 | SKELETAL_TRACKING_SUPPORTED |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Complete Loading Sequence
|
||||||
|
|
||||||
|
Here is the full loading process as implemented by Xenia:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. READ HEADER
|
||||||
|
a. Read xex2_header from offset 0
|
||||||
|
b. Verify magic == "XEX2" (0x58455832)
|
||||||
|
c. Copy entire header region (header_size bytes) into memory
|
||||||
|
|
||||||
|
2. PARSE SECURITY INFO
|
||||||
|
a. Navigate to xex2_header.security_offset
|
||||||
|
b. Extract: RSA signature, encrypted AES key, load_address, image_flags,
|
||||||
|
export_table address, page_descriptors
|
||||||
|
c. Determine base_address: use XEX_HEADER_IMAGE_BASE_ADDRESS if present,
|
||||||
|
otherwise security_info.load_address
|
||||||
|
|
||||||
|
3. DECRYPT & DECOMPRESS PE IMAGE
|
||||||
|
a. Determine encryption/compression from XEX_HEADER_FILE_FORMAT_INFO
|
||||||
|
b. Derive session key:
|
||||||
|
session_key = AES-128-CBC-Decrypt(master_key, security_info.aes_key)
|
||||||
|
Try retail key first, then devkit, then XEX1 key
|
||||||
|
c. Based on compression_type:
|
||||||
|
- NONE: decrypt payload directly to base_address
|
||||||
|
- BASIC: decrypt + zero-fill blocks to base_address
|
||||||
|
- NORMAL: decrypt → de-block → LZX decompress to base_address
|
||||||
|
d. For patches: store raw patch data for later application
|
||||||
|
|
||||||
|
4. VERIFY PE IMAGE
|
||||||
|
a. Check for MZ signature (0x5A4D) at base_address
|
||||||
|
b. If not valid PE and not a patch, loading fails
|
||||||
|
|
||||||
|
5. APPLY PATCHES (if applicable)
|
||||||
|
a. Patch XEX headers using LZX delta
|
||||||
|
b. Re-derive session keys for patched module
|
||||||
|
c. Decrypt and apply image delta patches block by block
|
||||||
|
d. Verify block hashes (SHA-1) at each step
|
||||||
|
|
||||||
|
6. PARSE PE HEADERS (LoadContinue)
|
||||||
|
a. Verify DOS header (MZ), NT headers (PE\0\0)
|
||||||
|
b. Verify Machine == POWERPCBE, Subsystem == XBOX
|
||||||
|
c. Extract all PE sections (name, VA, size, flags)
|
||||||
|
|
||||||
|
7. SETUP MEMORY PROTECTION
|
||||||
|
a. For each page_descriptor:
|
||||||
|
- CODE / READONLY_DATA → Read-only
|
||||||
|
- DATA → Read + Write
|
||||||
|
b. Track low_address (first code page) and high_address (last code page)
|
||||||
|
|
||||||
|
8. RESOLVE IMPORTS
|
||||||
|
a. Parse XEX_HEADER_IMPORT_LIBRARIES
|
||||||
|
b. For each import library:
|
||||||
|
- Parse string table for library names
|
||||||
|
- Load dependent user modules if not already loaded
|
||||||
|
- For each import record:
|
||||||
|
* Variable (type 0): write resolved address to memory slot
|
||||||
|
* Thunk (type 1): declare function, optionally rewrite PPC branch code
|
||||||
|
|
||||||
|
9. SETUP EXPORTS
|
||||||
|
a. If security_info.export_table != 0: XEX export table is in memory
|
||||||
|
b. If XEX_HEADER_EXPORTS_BY_NAME present: PE export directory is available
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Integrity Verification (SHA-1)
|
||||||
|
|
||||||
|
SHA-1 is used throughout the format for data integrity:
|
||||||
|
|
||||||
|
| Location | What is Hashed | Hash Location |
|
||||||
|
|----------|---------------|---------------|
|
||||||
|
| Page descriptors | Each page's data in memory | `xex2_page_descriptor.data_digest` (20 bytes) |
|
||||||
|
| Security info | Section data | `xex2_security_info.section_digest` |
|
||||||
|
| Security info | Import table | `xex2_security_info.import_table_digest` |
|
||||||
|
| Security info | Header data | `xex2_security_info.header_digest` |
|
||||||
|
| Normal compression | Each compressed block | `xex2_compressed_block_info.block_hash` (chain) |
|
||||||
|
| Delta patches | Each patch block | `xex2_compressed_block_info.block_hash` |
|
||||||
|
| Import libraries | Next import entry | `xex2_import_library.next_import_digest` |
|
||||||
|
| Delta patches | Source image | `xex2_opt_delta_patch_descriptor.digest_source` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Key Source Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `src/xenia/kernel/util/xex2_info.h` | All XEX2 structure definitions, enums, and flags |
|
||||||
|
| `src/xenia/cpu/xex_module.h` | XexModule class, SecurityInfoContext, ImportLibrary structures |
|
||||||
|
| `src/xenia/cpu/xex_module.cc` | Main loading logic, AES keys, decryption, decompression dispatch |
|
||||||
|
| `src/xenia/cpu/lzx.h` / `lzx.cc` | LZX decompression and delta patch application |
|
||||||
|
| `src/xenia/base/pe_image.h` | PE (DOS/NT/Section) header structures |
|
||||||
|
| `third_party/crypto/rijndael-alg-fst.c` | AES (Rijndael) cipher implementation |
|
||||||
|
| `third_party/mspack/lzxd.c` | LZX decompression engine |
|
||||||
338
tools/generate_export_docs.py
Normal file
338
tools/generate_export_docs.py
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate documentation of all Xbox 360 system exports in Xenia Canary.
|
||||||
|
|
||||||
|
Parses the export table .inc files and implementation .cc files to produce
|
||||||
|
a comprehensive listing of all exports with their implementation status.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 tools/generate_export_docs.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
|
||||||
|
TABLE_FILES = {
|
||||||
|
"xboxkrnl": REPO_ROOT / "src/xenia/kernel/xboxkrnl/xboxkrnl_table.inc",
|
||||||
|
"xam": REPO_ROOT / "src/xenia/kernel/xam/xam_table.inc",
|
||||||
|
"xbdm": REPO_ROOT / "src/xenia/kernel/xbdm/xbdm_table.inc",
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPL_DIRS = {
|
||||||
|
"xboxkrnl": REPO_ROOT / "src/xenia/kernel/xboxkrnl",
|
||||||
|
"xam": REPO_ROOT / "src/xenia/kernel/xam",
|
||||||
|
"xbdm": REPO_ROOT / "src/xenia/kernel/xbdm",
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_NAMES = {
|
||||||
|
"xboxkrnl": "xboxkrnl.exe",
|
||||||
|
"xam": "xam.xex",
|
||||||
|
"xbdm": "xbdm.xex",
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_MAP = {"XBOXKRNL": "xboxkrnl", "XAM": "xam", "XBDM": "xbdm"}
|
||||||
|
|
||||||
|
# Regex patterns
|
||||||
|
XE_EXPORT_RE = re.compile(
|
||||||
|
r"XE_EXPORT\(\s*(\w+)\s*,\s*(0x[0-9A-Fa-f]+)\s*,\s*(\w+)\s*,\s*k(\w+)\s*\)"
|
||||||
|
)
|
||||||
|
|
||||||
|
DECLARE_RE = re.compile(
|
||||||
|
r"DECLARE_(XBOXKRNL|XAM|XBDM)_EXPORT\d*\(\s*(\w+)\s*,"
|
||||||
|
r"\s*k(\w+)\s*,\s*(.+?)\)\s*;",
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
TAG_RE = re.compile(r"k(\w+)")
|
||||||
|
|
||||||
|
SHIM_RE = re.compile(r'SHIM_SET_MAPPING\(\s*"[^"]+"\s*,\s*(\w+)\s*,')
|
||||||
|
|
||||||
|
VAR_MAPPING_RE = re.compile(r"ordinals::(\w+)")
|
||||||
|
EXPORT_KVAR_RE = re.compile(r"EXPORT_KVAR\((\w+)\)")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_table_files():
|
||||||
|
"""Phase A: Parse all XE_EXPORT entries from *_table.inc files."""
|
||||||
|
exports = OrderedDict()
|
||||||
|
|
||||||
|
for module, path in TABLE_FILES.items():
|
||||||
|
content = path.read_text()
|
||||||
|
for match in XE_EXPORT_RE.finditer(content):
|
||||||
|
mod, ordinal_hex, name, typ = match.groups()
|
||||||
|
exports[(mod, name)] = {
|
||||||
|
"module": mod,
|
||||||
|
"ordinal": int(ordinal_hex, 16),
|
||||||
|
"ordinal_hex": ordinal_hex.upper().replace("0X", "0x"),
|
||||||
|
"name": name,
|
||||||
|
"type": "variable" if typ == "Variable" else "function",
|
||||||
|
"status": "not_implemented",
|
||||||
|
"tags": [],
|
||||||
|
"category": "",
|
||||||
|
"source_file": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
return exports
|
||||||
|
|
||||||
|
|
||||||
|
def scan_declare_macros(exports):
|
||||||
|
"""Phase B: Scan DECLARE_*_EXPORT macros in .cc files."""
|
||||||
|
for mod_key, dir_path in IMPL_DIRS.items():
|
||||||
|
for cc_file in sorted(dir_path.glob("*.cc")):
|
||||||
|
content = cc_file.read_text()
|
||||||
|
for match in DECLARE_RE.finditer(content):
|
||||||
|
mod_prefix, name, category, tags_str = match.groups()
|
||||||
|
module = MODULE_MAP[mod_prefix]
|
||||||
|
tags = TAG_RE.findall(tags_str)
|
||||||
|
key = (module, name)
|
||||||
|
|
||||||
|
if key in exports:
|
||||||
|
entry = exports[key]
|
||||||
|
entry["category"] = category
|
||||||
|
entry["tags"] = tags
|
||||||
|
entry["source_file"] = str(
|
||||||
|
cc_file.relative_to(REPO_ROOT)
|
||||||
|
)
|
||||||
|
|
||||||
|
if "Implemented" in tags:
|
||||||
|
entry["status"] = "implemented"
|
||||||
|
elif "Stub" in tags:
|
||||||
|
entry["status"] = "stub"
|
||||||
|
elif "Sketchy" in tags:
|
||||||
|
entry["status"] = "sketchy"
|
||||||
|
else:
|
||||||
|
entry["status"] = "stub"
|
||||||
|
|
||||||
|
|
||||||
|
def scan_legacy_patterns(exports):
|
||||||
|
"""Phase C: Handle SHIM_SET_MAPPING and SetVariableMapping."""
|
||||||
|
# SHIM_SET_MAPPING in xboxkrnl_strings.cc
|
||||||
|
strings_file = IMPL_DIRS["xboxkrnl"] / "xboxkrnl_strings.cc"
|
||||||
|
if strings_file.exists():
|
||||||
|
content = strings_file.read_text()
|
||||||
|
for match in SHIM_RE.finditer(content):
|
||||||
|
name = match.group(1)
|
||||||
|
key = ("xboxkrnl", name)
|
||||||
|
if key in exports:
|
||||||
|
exports[key]["status"] = "implemented"
|
||||||
|
exports[key]["source_file"] = str(
|
||||||
|
strings_file.relative_to(REPO_ROOT)
|
||||||
|
)
|
||||||
|
if not exports[key]["tags"]:
|
||||||
|
exports[key]["tags"] = ["Implemented"]
|
||||||
|
|
||||||
|
# SetVariableMapping / EXPORT_KVAR in xboxkrnl_module.cc
|
||||||
|
module_file = IMPL_DIRS["xboxkrnl"] / "xboxkrnl_module.cc"
|
||||||
|
if module_file.exists():
|
||||||
|
content = module_file.read_text()
|
||||||
|
for match in VAR_MAPPING_RE.finditer(content):
|
||||||
|
name = match.group(1)
|
||||||
|
key = ("xboxkrnl", name)
|
||||||
|
if key in exports:
|
||||||
|
exports[key]["status"] = "implemented"
|
||||||
|
exports[key]["source_file"] = str(
|
||||||
|
module_file.relative_to(REPO_ROOT)
|
||||||
|
)
|
||||||
|
if not exports[key]["tags"]:
|
||||||
|
exports[key]["tags"] = ["Implemented"]
|
||||||
|
|
||||||
|
for match in EXPORT_KVAR_RE.finditer(content):
|
||||||
|
name = match.group(1)
|
||||||
|
key = ("xboxkrnl", name)
|
||||||
|
if key in exports:
|
||||||
|
exports[key]["status"] = "implemented"
|
||||||
|
exports[key]["source_file"] = str(
|
||||||
|
module_file.relative_to(REPO_ROOT)
|
||||||
|
)
|
||||||
|
if not exports[key]["tags"]:
|
||||||
|
exports[key]["tags"] = ["Implemented"]
|
||||||
|
|
||||||
|
|
||||||
|
def compute_statistics(exports):
|
||||||
|
"""Compute per-module statistics."""
|
||||||
|
stats = {}
|
||||||
|
for module in TABLE_FILES:
|
||||||
|
module_exports = [e for e in exports.values() if e["module"] == module]
|
||||||
|
stats[module] = {
|
||||||
|
"total": len(module_exports),
|
||||||
|
"implemented": sum(
|
||||||
|
1 for e in module_exports if e["status"] == "implemented"
|
||||||
|
),
|
||||||
|
"stub": sum(1 for e in module_exports if e["status"] == "stub"),
|
||||||
|
"sketchy": sum(
|
||||||
|
1 for e in module_exports if e["status"] == "sketchy"
|
||||||
|
),
|
||||||
|
"not_implemented": sum(
|
||||||
|
1
|
||||||
|
for e in module_exports
|
||||||
|
if e["status"] == "not_implemented"
|
||||||
|
),
|
||||||
|
"functions": sum(
|
||||||
|
1 for e in module_exports if e["type"] == "function"
|
||||||
|
),
|
||||||
|
"variables": sum(
|
||||||
|
1 for e in module_exports if e["type"] == "variable"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def generate_markdown(exports, stats):
|
||||||
|
"""Generate the Markdown documentation."""
|
||||||
|
lines = []
|
||||||
|
lines.append("# Xbox 360 System Exports - Xenia Canary")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(
|
||||||
|
"Auto-generated by `tools/generate_export_docs.py`. "
|
||||||
|
"Do not edit manually."
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Overall summary
|
||||||
|
lines.append("## Summary")
|
||||||
|
lines.append("")
|
||||||
|
total_all = sum(s["total"] for s in stats.values())
|
||||||
|
impl_all = sum(s["implemented"] for s in stats.values())
|
||||||
|
stub_all = sum(s["stub"] for s in stats.values())
|
||||||
|
sketchy_all = sum(s["sketchy"] for s in stats.values())
|
||||||
|
not_impl_all = sum(s["not_implemented"] for s in stats.values())
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f"**Total exports: {total_all}** | "
|
||||||
|
f"Implemented: {impl_all} | "
|
||||||
|
f"Stub: {stub_all} | "
|
||||||
|
f"Sketchy: {sketchy_all} | "
|
||||||
|
f"Not implemented: {not_impl_all}"
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
"| Module | File | Total | Implemented | Stub | Sketchy | Not Implemented | Functions | Variables |"
|
||||||
|
)
|
||||||
|
lines.append(
|
||||||
|
"|--------|------|------:|------------:|-----:|--------:|----------------:|----------:|----------:|"
|
||||||
|
)
|
||||||
|
for module in TABLE_FILES:
|
||||||
|
s = stats[module]
|
||||||
|
lines.append(
|
||||||
|
f"| {module} | {MODULE_NAMES[module]} | {s['total']} | "
|
||||||
|
f"{s['implemented']} | {s['stub']} | {s['sketchy']} | "
|
||||||
|
f"{s['not_implemented']} | {s['functions']} | {s['variables']} |"
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Per-module tables
|
||||||
|
for module in TABLE_FILES:
|
||||||
|
s = stats[module]
|
||||||
|
lines.append(f"## {module} ({MODULE_NAMES[module]})")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(
|
||||||
|
f"Total: {s['total']} | "
|
||||||
|
f"Implemented: {s['implemented']} | "
|
||||||
|
f"Stub: {s['stub']} | "
|
||||||
|
f"Sketchy: {s['sketchy']} | "
|
||||||
|
f"Not implemented: {s['not_implemented']}"
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
"| Ordinal | Name | Type | Status | Category | Source |"
|
||||||
|
)
|
||||||
|
lines.append(
|
||||||
|
"|--------:|------|------|--------|----------|--------|"
|
||||||
|
)
|
||||||
|
|
||||||
|
module_exports = sorted(
|
||||||
|
[e for e in exports.values() if e["module"] == module],
|
||||||
|
key=lambda e: e["ordinal"],
|
||||||
|
)
|
||||||
|
|
||||||
|
for e in module_exports:
|
||||||
|
ordinal = f"0x{e['ordinal']:03X}"
|
||||||
|
status_icon = {
|
||||||
|
"implemented": "implemented",
|
||||||
|
"stub": "stub",
|
||||||
|
"sketchy": "sketchy",
|
||||||
|
"not_implemented": "-",
|
||||||
|
}.get(e["status"], e["status"])
|
||||||
|
|
||||||
|
source = e["source_file"]
|
||||||
|
if source:
|
||||||
|
# Show just the filename for brevity
|
||||||
|
source = pathlib.Path(source).name
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f"| {ordinal} | {e['name']} | {e['type']} | "
|
||||||
|
f"{status_icon} | {e['category']} | {source} |"
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_json(exports, stats):
|
||||||
|
"""Generate the JSON documentation."""
|
||||||
|
data = {
|
||||||
|
"description": "Xbox 360 system exports implemented in Xenia Canary",
|
||||||
|
"generator": "tools/generate_export_docs.py",
|
||||||
|
"statistics": stats,
|
||||||
|
"modules": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for module in TABLE_FILES:
|
||||||
|
module_exports = sorted(
|
||||||
|
[e for e in exports.values() if e["module"] == module],
|
||||||
|
key=lambda e: e["ordinal"],
|
||||||
|
)
|
||||||
|
data["modules"][module] = {
|
||||||
|
"file": MODULE_NAMES[module],
|
||||||
|
"exports": module_exports,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.dumps(data, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Parsing export tables...")
|
||||||
|
exports = parse_table_files()
|
||||||
|
print(f" Found {len(exports)} exports across {len(TABLE_FILES)} modules")
|
||||||
|
|
||||||
|
print("Scanning DECLARE_*_EXPORT macros...")
|
||||||
|
scan_declare_macros(exports)
|
||||||
|
|
||||||
|
print("Scanning legacy registration patterns...")
|
||||||
|
scan_legacy_patterns(exports)
|
||||||
|
|
||||||
|
stats = compute_statistics(exports)
|
||||||
|
|
||||||
|
for module, s in stats.items():
|
||||||
|
print(
|
||||||
|
f" {module}: {s['total']} total, "
|
||||||
|
f"{s['implemented']} implemented, "
|
||||||
|
f"{s['stub']} stub, "
|
||||||
|
f"{s['sketchy']} sketchy, "
|
||||||
|
f"{s['not_implemented']} not implemented"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate outputs
|
||||||
|
docs_dir = REPO_ROOT / "docs"
|
||||||
|
docs_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
md_path = docs_dir / "xbox360_exports.md"
|
||||||
|
md_content = generate_markdown(exports, stats)
|
||||||
|
md_path.write_text(md_content)
|
||||||
|
print(f"Written: {md_path.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
json_path = docs_dir / "xbox360_exports.json"
|
||||||
|
json_content = generate_json(exports, stats)
|
||||||
|
json_path.write_text(json_content)
|
||||||
|
print(f"Written: {json_path.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user