phase5: identify the compiler as egcs-2.91.66 (PSX) / PsyQ 4.5 CC1PSX

P5-T3 revised: obtained the real PsyQ 4.4/4.5/4.6 SDK compilers (proprietary,
kept ignored) and ran CC1PSX 4.5 under the ignored wibo Win32 loader. The SDK's
own README and the binary agree: PsyQ 4.5 CC1PSX = egcs-2.91.66 (egcs-1.1.2),
target mips-sony-psx, assembler ASPSX 2.81.

The open decompals/old-gcc gcc-2.91.66-psx produces instruction-identical
output to the real CC1PSX across ~990 instructions in twelve probe files, so no
proprietary compiler is needed for the matching build. Five reconstructed
original functions are byte-identical with -O2 -G0 -mno-split-addresses.

Corrects the earlier gcc-2.7.2-psx selection, which was an artifact of the
invalid -mcpu=3000 spelling. Records the unresolved 0x8005DEF8 reconstruction.
This commit is contained in:
Christopher Williams
2026-09-23 20:23:05 -04:00
parent f80ada2e07
commit 147eb4abf8
4 changed files with 114 additions and 55 deletions
+1
View File
@@ -12,6 +12,7 @@ tools/splat/
tools/maspsx/
tools/mipsel-none-elf-binutils/
tools/old-gcc/
tools/wibo/
# Generated reverse-engineering and build state
.run/*
+66 -44
View File
@@ -1,65 +1,87 @@
# Phase 5 Toolchain Fingerprint
**Scope:** byte-evidence fingerprint of the original USA build compiler using self-authored probes and reconstructed instruction ranges.
**Task:** P5-T3
**Status:** complete — `gcc-2.7.2-psx` selected, with one bounded unresolved divergence.
**Scope:** identification of the original USA build compiler, verified against the SDK's own binary.
**Task:** P5-T3 (revised — supersedes the earlier draft conclusion).
**Status:** complete — **`egcs-2.91.66` (PSX), i.e. PsyQ 4.5 `CC1PSX`**, with an open substitute verified byte-identical.
## Method
## Result
1. Acquired the `decompals/old-gcc` 0.17 ladder (P5-T2) into ignored `tools/old-gcc/`.
2. Wrote self-authored C probes expressing only constructs observable in the original, compiled each with every candidate, assembled with the ignored GNU `mipsel-none-elf-as`, and compared mnemonic/operand output.
3. Reconstructed five tiny original functions exactly and compared the assembled **bytes** against the validated USA ranges.
The original compiler is **`egcs-2.91.66 19990314` (egcs-1.1.2), target `mips-sony-psx`** — the
compiler shipped as **`CC1PSX.EXE` in PsyQ SDK 4.5**, which is the SDK version the executable's
loader-detected signature reports.
All probe sources are self-authored reconstructions, not ROM-derived source. Raw candidate outputs and probe files live in ignored `.run/p5-t3/`.
Two independent confirmations:
## Discriminating results
1. The SDK's own `psyq4.5/README.md` records the compiler banner:
`GNU C version egcs-2.91.66 19990314 (egcs-1.1.2 release) (PSX)`, and `SDevTC ASPSX version 2.81`.
2. The real `CC1PSX.EXE` was executed (under `wibo`) and its output compared against the original
executable.
| # | Probe | 2.6.3-psx | 2.7.2 | 2.7.2-psx | 2.7.2-cdk | 2.8.0 | 2.8.1 | 2.91.66 | 2.95.2 | **USA original** |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Symbolic store `g = a;` | `lui at` | `lui at` | `lui at` | `lui v0` | `lui v0` | `lui v0` | `lui v0` | `lui v0` | **`lui at`** |
| 2 | Symbolic load `return g;` | `lui Rn;lw Rn` | same | same | same | same | same | `lui v1;lw v0` | `lui v1;lw v0` | **`lui Rn;lw Rn`** |
| 3 | `char` load from `char *` | `lbu` | `lb` | `lbu` | — | — | — | — | — | **`lbu`** |
| 4 | Signed magic division scratch | `mfhi v0` (direct) | `mfhi t0` | `mfhi t0` | `mfhi t0` | `mfhi t0` | `mfhi t0` | `mfhi a3` | `mfhi a3` | **`mfhi t0`** |
**The open `decompals/old-gcc` 0.17 `gcc-2.91.66-psx` build produces instruction-identical output
to the real Sony `CC1PSX` 4.5** across every probe tried — twelve probe files, roughly 990
instructions covering address materialization, calls, delay-slot filling, frames, division and
modulo (variable and by constant), `long long`, switch/jump tables, bitfields, loops, struct
return/copy, soft-float, and `char` handling. No proprietary compiler is needed for the matching
build.
Eliminations:
- **2.8.0 / 2.8.1 / 2.91.66 / 2.95.2** — probe 1 (they emit the address into the destination register instead of the ASPSX-style `$at` macro expansion) and probe 2 for the egcs/2.95 pair.
- **2.7.2-cdk** (`cygnus-2.7.2-970404`, the base of PsyQ 4.0/4.1 `CC1PSX`) — probe 1.
- **2.7.2 (vanilla, `BSD Mips` target)** — probe 3 (signed `char`), and it is not a PlayStation target.
- **2.6.3-psx** — probe 4: it always computes the magic-division high word directly into the destination register (`mfhi v0; sra v0,v0,1`), while the original uses a distinct scratch (`mfhi t0; sra v0,t0,1`). Across roughly twenty probes 2.6.3-psx never produced `mfhi t0`.
## Provisional flags
**Selected: `gcc-2.7.2-psx`** — the only candidate consistent with every discriminator.
```
-O2 -G0 -mno-split-addresses (plus the mips-sony-psx defaults)
```
## Byte-identical confirmation
- **`-mno-split-addresses` is essential.** The real `CC1PSX` defaults to `-msplit-addresses`, which
emits `lui $3,%hi(sym)` / `lw $2,%lo(sym)($3)`; the game's code matches `-mno-split-addresses`,
which emits the ASPSX-style macro form that the assembler expands through `$at`
(`lui $at,…` / `sw $4,%lo(sym)($at)`).
- `-mgas`, `-msoft-float`, `-mcpu=r3000` are the target defaults and are consistent with observation.
- Input must be **preprocessed**: `cc1`/`CC1PSX` reject comments and `#` directives, so a `cpp`
stage is required (as in the peer project's pipeline).
- `-G` is **not** 0 for the whole program: the original contains `gp`-relative small-data accesses,
so a per-module `-G` value must still be determined.
Five reconstructed original functions assembled from `gcc-2.7.2-psx` output are **byte-identical** to the validated USA executable:
## Byte-identical original ranges
| Function | USA address | Length | Content (mnemonics) |
|---|---|---|---|
| `q_get220` | `0x800321EC` | 12 B | struct-field load, `jr ra` |
| `q_set22` | `0x800179D4` | 12 B | halfword + word store, last store in the delay slot |
| `q_set20` | `0x80017AE8` | 16 B | three word stores, last store in the delay slot |
| `q_sym_load` | `0x80085B80` | 16 B | symbolic load via `lui`/`lw` with sign-adjusted low half |
| `q_sym_store` | `0x800F3160` | 12 B | symbolic store expanded through `$at` |
Five reconstructed original functions assemble from this compiler to the exact original bytes:
| Function | USA address | Length |
|---|---|---|
| struct-field load | `0x800321EC` | 12 B |
| halfword + word store | `0x800179D4` | 12 B |
| three word stores | `0x80017AE8` | 16 B |
| symbolic load (`lui`/`lw`, sign-adjusted low half) | `0x80085B80` | 16 B |
| symbolic store (`$at` macro form) | `0x800F3160` | 12 B |
Symbol values used: `g_load = 0x8013845C`, `g_store = 0x80142EDC`.
## Bounded unresolved divergence
## Method correction (recorded so it is not repeated)
One reconstructed function (`0x8005DEF8`, a `/12`-magic expression) does **not** reproduce byte-for-byte. Every candidate synthesizes the constant multiply `* 68` into a shift/add chain (`sll`/`addu`/`sll`), whereas the original emits a real `li v0,68` + `mult` + `mflo`.
An earlier draft of this record eliminated candidates using `-mcpu=3000`. That spelling is not the
target's accepted CPU name: it changes `2.91.66`'s code generation while leaving older builds
apparently unaffected, which produced a spurious discriminator table and a wrong "selected"
candidate (`gcc-2.7.2-psx`). With the SDK's own spelling `-mcpu=r3000`, **all ten acquired
candidates reproduce the five simple oracles byte-identically**, so those oracles do not
discriminate at all. The selection above rests on the SDK banner plus the real-binary differential,
not on that table.
Tested without effect: `-O0`, `-O1`, `-O3`, `-fno-strength-reduce`, `-fno-expensive-optimizations`, `-fno-schedule-insns`, `-mcpu=3000`/`r3000`/unset, `-mips2`.
## Bounded open items
Interpretation and limits:
- This is a multiply-cost/synthesis difference, not a version or family difference: the same candidate matches probes 1–4 and the five byte-identical ranges above.
- Possible causes, none established: the reconstructed expression is not exactly the original source; the original build used a per-file flag variant; or the original compiler is Sony's own `CC1PSX` 4.5, whose multiply-cost tuning differs from the community 2.7.2-psx build.
- It does not change the selection, but any function whose source contains a constant multiply may need this resolved before it can match.
## Selection and limits
- **Selected compiler:** `gcc-2.7.2-psx` (`GNU C 2.7.2 [AL 1.1, MM 40] Sony Playstation`), sha256 `500a459b3485e885a8d302cac23c2a4632f3900e03a09153f6190699fd723571`.
- **Provisional flags:** `-quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker`, fed **preprocessed** input (cc1 rejects comments and directives).
- **Not established:** the assembler (the `$at` expansion is consistent with an ASPSX-style macro assembler; Maspsx already cloned in Phase 3 is the candidate), the linker, the small-data `-G` threshold (the original clearly uses `gp`-relative small data in some functions, so `-G` is not 0 for the whole program), and per-module flag variation.
- **`0x8005DEF8` does not reproduce.** The original emits a real `li v0,68` + `mult` + `mflo` for a
constant multiply, while every compiler tested — including the real `CC1PSX` — synthesizes the
multiply into a shift/add chain. Tested without effect: `-O0/-O1/-O2/-O3/-Os`,
`-fno-expensive-optimizations`, `-fno-strength-reduce`, `-fno-inline`, `-fno-delayed-branch`,
`-mcpu` variants, and several source spellings. Most likely the C reconstruction of that function
is wrong; a per-module compiler difference is not excluded. It does not affect the identification,
but must be resolved before that function can be claimed.
- **Assembler:** `ASPSX` 2.81 per the SDK. The open `maspsx` (already cloned in Phase 3) is the
candidate emulator; not yet exercised.
- **Linker and library set:** not investigated.
- **Per-module flag variation:** not excluded (the peer project documents compilers being mixed
within one executable).
## Firewall note
No game bytes, disassembly listings, or ROM-derived strings were added to tracked files. Raw disassembly, probe sources, candidate outputs, and comparison data remain in ignored `.run/p5-t3/`.
The PsyQ SDK binaries are proprietary and remain under ignored `tools/psyq/`; `wibo` is under
ignored `tools/wibo/`. Only sha256 values and provenance are recorded in `docs/SETUP.md`. No game
bytes, disassembly listings, or ROM-derived strings were added to tracked files; raw probe sources,
candidate outputs, and comparison data remain in ignored `.run/p5-t3/`.
+26
View File
@@ -139,3 +139,29 @@ release containing the **actual Sony PsyQ SDK compilers** (`psyq3.3` … `psyq4.
`psyq-compilers.tar.gz`). Those are proprietary Sony material: if ever used they must stay in an
ignored path and must never be committed. As of P5-T3 none has been downloaded.
## Phase 5 PsyQ 4.5 SDK and Win32 loader (2026-09-23)
P5-T3 obtained the real SDK compilers to resolve the compiler identity against the SDK's own binary.
Both components stay ignored (`tools/psyq/` and `tools/wibo/`).
| Component | Identity | sha256 |
|---|---|---|
| PsyQ SDK 4.4 compilers | `CC1PSX.EXE`, `ASPSX.EXE`, `PSYLINK.EXE`, … (PE32) | `72e73934bab0d51933eb95af514afb14f3d432f01530eac3ddb16dfbb57ab66c` |
| PsyQ SDK 4.5 compilers | `CC1PSX.EXE`, `ASPSX.EXE`, `PSYLINK.EXE`, … (PE32) | `75f28034f6844f0f7633e3f17443727865c8955da1cd19147db2c760b40f14f7` |
| PsyQ SDK 4.6 compilers | `CC1PSX.EXE`, `ASPSX.EXE`, `PSYLINK.EXE`, … (PE32) | `635603e09a452c9c2923492fea8b8eb051959dd94f240933b88e94866814b894` |
| wibo 1.2.0 (`wibo-x86_64`) | Minimal Win32 command-line binary loader for Linux (MIT) | `13f86a2d618f0dbe67179d349625345eabf9b46450295cb4c904e49f6aff85af` |
- Source: `https://github.com/mkst/esa/releases/download/psyq-binaries/<name>.tar.gz` and
`https://github.com/decompals/wibo/releases/download/1.2.0/wibo-x86_64`.
- The SDK tarballs are **proprietary Sony material**. They are kept only under the already-ignored
`tools/psyq/` and must never be committed or published.
- `CC1PSX.EXE` is a PE32 Windows console binary; it runs on this host under `wibo`, which is how
the compiler was executed. Invocation:
`tools/wibo/wibo tools/psyq/psyq4.5/CC1PSX.EXE -quiet -O2 -G0 -mno-split-addresses <preprocessed.c> -o <out.s>`
- `psyq4.5/README.md` (shipped with the SDK) records the compiler banner:
`GNU C version egcs-2.91.66 19990314 (egcs-1.1.2 release) (PSX)`, and `SDevTC ASPSX version 2.81`.
**Result (P5-T3):** the real `CC1PSX` 4.5 and the open `gcc-2.91.66-psx` produce **identical**
instruction output across every probe tested (~990 instructions, twelve probe files). The open
`gcc-2.91.66-psx` is therefore the working compiler; the proprietary binary is only a verification
reference and is not required for the matching build.
+21 -11
View File
@@ -2,8 +2,8 @@
**Phase:** Phase 5 — Original Toolchain Identification and First C Match
**Plan:** `phase-ends/Phase5_PLAN.md`
**Status:** active; developer approved the ladder approach for P5-T2/P5-T3
**Active task:** P5-T3 complete — `gcc-2.7.2-psx` selected; next is P5-T4 (comparator harness)
**Status:** active; P5-T3 complete after obtaining and running the real SDK compiler
**Active task:** P5-T3 complete — compiler identified as `egcs-2.91.66` (PSX) / PsyQ 4.5 `CC1PSX`
> Approval note: `Phase5_PLAN.md` was authored as a DRAFT requiring explicit developer
> approval. The developer's instruction to begin Phase 5 is being treated as that approval
@@ -39,10 +39,8 @@ were copied into tracked files.
## Next task
P5-T4 — reproducible instruction-range comparator harness, then P5-T5 (first isolated C match) using
`gcc-2.7.2-psx` with `-quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker`.
Open item carried from P5-T3: constant-multiply synthesis diverges from the original in one
reconstructed function (`0x8005DEF8`); see `docs/PHASE5_TOOLCHAIN_FINGERPRINT.md`.
`gcc-2.91.66-psx` with `-O2 -G0 -mno-split-addresses` (or the real `CC1PSX` under `wibo` for
verification), and a `cpp` stage before the compiler.
## P5-T2 completed evidence
@@ -52,8 +50,20 @@ independent project also records matched exactly. The ignored GNU `as` accepts C
## P5-T3 completed evidence
`docs/PHASE5_TOOLCHAIN_FINGERPRINT.md` records the fingerprint. `gcc-2.7.2-psx` is selected: it
is the only candidate matching the ASPSX-style `$at` symbolic store, the same-register symbolic
load, the unsigned-`char` default, and the `mfhi <scratch>` magic-division allocation. Five
reconstructed original functions are byte-identical. 2.6.3-psx, 2.7.2 vanilla, 2.7.2-cdk,
2.8.0-psx, 2.8.1-psx, 2.91.66-psx and 2.95.2-psx are eliminated.
`docs/PHASE5_TOOLCHAIN_FINGERPRINT.md` records the result. The compiler is **`egcs-2.91.66`
(egcs-1.1.2), target `mips-sony-psx`** — the `CC1PSX.EXE` of PsyQ SDK 4.5, matching the SDK's own
README banner and the executable's detected SDK signature.
The real `CC1PSX` was executed under the ignored `wibo` Win32 loader and compared with the open
`gcc-2.91.66-psx`: their instruction output is **identical across ~990 instructions** in twelve
probe files. Five reconstructed original functions are byte-identical with
`-O2 -G0 -mno-split-addresses`. The open compiler is therefore the working tool; the proprietary
binary is only a verification reference.
A method correction is recorded: the earlier `gcc-2.7.2-psx` selection came from `-mcpu=3000`, an
invalid CPU spelling that perturbs `2.91.66` only. With the SDK's `-mcpu=r3000`, all ten candidates
reproduce the simple oracles, so the earlier discriminator table was invalid.
Open item carried forward: `0x8005DEF8` does not reproduce (the original emits a real `mult` for a
constant multiply that every compiler, including the real `CC1PSX`, synthesizes); the C
reconstruction is the likely cause.