Files
BFM-decomp/cookbook/C0296.md
T

2.5 KiB

§275 — THE LEFTOVER-REGISTER READ

A function whose entry block consumes $v0/$v1 before ANY EARLIER INSTRUCTION IN THAT FUNCTION writes it is reading the CALLER's last return value, not a missing rand()/helper call. (Later jals are irrelevant and usually present — func_80181F54 has five. What matters is that no write to the register precedes the read.) (P31 S60; byte-proven func_80181F54, ov_SC03_104, wave bw, MATCH 57/57.)

MECHANISM. PSX/MIPS o32 does not clear $v0/$v1 across a jal-less function entry, and gcc-2.7.2 gives every function a fresh allocation for its own values — it never treats an incoming $v0 as meaningful unless the C body says so. When the ORIGINAL source read that stale value on purpose — almost certainly a copy-paste from a sibling that genuinely called something with an s32 return, with the call itself dropped in the shipped game code — the only way to reproduce the bytes is to declare a register-pinned local with no assignment and consume it immediately:

register s32 vin __asm__("$2");    /* never assigned — reads whatever is in $v0 at entry */
...
(s16)(((s16)vin) % 128)

THE TELL. The function's disassembly opens with an operation on $v0/$v1 (sll $v0,$v0,16, an andi, a comparison, etc.) before any jal, lw, or argument-register use establishes a value for it — the read has no producer anywhere in the function. Do NOT spend turns hunting for a dropped call to explain it. Check a structurally-similar sibling first: if it genuinely has a jal at the analogous slot (its own .s shows the call), that sibling's C cannot be copied verbatim for this target — only its skeleton transfers, and the target's own .s (no jal anywhere, an entry-block $v0/$v1 read) is the ground truth that this is the leftover-register class, not a missing-call defect. Cast the value at each use exactly as the register width implies ((s16) for a halfword-looking use) — no promotion instructions exist in the target because none were emitted for a value that was never loaded.

BOUNDARY. Distinct from §223's delay-slot corollary (which reads a value the SAME function's caller-of-a-callee definitely set on purpose, one instruction earlier in the same function) and from §176-A (a store's C statement dominating an upcoming call). This law applies only at a function's true entry block, before any control transfer of its own, and only when no assignment to the consumed register exists anywhere in the target .s.