docs(reference): adopt the four homeless reference files, and a LICENSE

CONSOLIDATION.md Phase 2. Each of these existed only in a repository about to
be retired, or as an untracked file on one disk -- which, for anything that
has to survive a machine, is the same as not existing.

  xex2-format.md            39 KB   the XEX2 container format
  xbox360-exports.{json,md} 1.1 MB  2,913 exports: xboxkrnl, xam, xbdm
  ppc-instructions.{json,md} 340 KB the PowerPC instruction reference
  LICENSE                            MIT -- this repo had none

The first two are byte-identical to xex2tractor's copies, verified with cmp,
and byte-identical again to the loose untracked copies in the project root:
three copies, zero of them tracked.

docs/reference/ is deliberately its own directory with a README that says
what it is NOT: nothing here is a finding, and no docs/re/ page should cite it
as evidence. Reference material filed beside measurements is how a borrowed
table starts getting read as a result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-13 19:20:30 +02:00
parent 5aeca0a55a
commit 444776ae1a
7 changed files with 51639 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Fabian
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18
docs/reference/README.md Normal file
View File

@@ -0,0 +1,18 @@
# Reference data — not ours, and not findings
Third-party or generated reference material the RE work reads. **Nothing here is
a finding**: no page in `docs/re/` should cite this directory as evidence, and
nothing here was measured by this project.
It is here because it was homeless. Every file below lived only in a retired
repository or as an untracked file on one disk, which is the same as not existing
— see [`../agents/CONSOLIDATION.md`](../agents/CONSOLIDATION.md).
| file | what it is | from |
|---|---|---|
| `xex2-format.md` | the XEX2 container format | `xex2tractor`, byte-identical |
| `xbox360-exports.{json,md}` | 2,913 system exports across `xboxkrnl.exe`, `xam.xex`, `xbdm.xex` | `xex2tractor`, byte-identical |
| `ppc-instructions.{json,md}` | the PowerPC instruction reference | untracked, project root |
`LICENSE` at the repository root came from the same place — MIT, and this
repository had none at all.

File diff suppressed because it is too large Load Diff

View 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.*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 |