D73+D74: intro logo fixed (sinf/cosf endianness + texture import/sub-tile wrap)

D73 (root cause of old D72.3): GE's sinf/cosf build their double
constants through the du bit-union with big-endian word pairs {hi,lo};
on LE PC the .d read has the words swapped (rpi.d ~ 2^733), range
reduction overflows, and FTOFIX32 saturates to -32768 for every
sin/cos entry of guMtxF2L — so the Rareware logo's rotation matrix was
garbage and all its triangles projected off-screen. Fix: DVAL() macro
in src/libultra/gu/guint.h (PORT-only; #else branch is the original
expression) re-packing hi/lo into the correct LE double; sinf.c/cosf.c
.d reads replaced. N64 build untouched.

D74 (texture side, three port-layer bugs in fast3d):
- import_texture's fallback condition (rdp.tex_lod && tile >=
  first+detail) overwrote valid gDPLoadBlock tmem data with
  line*tile.height: letter mip chains 2744B -> 2048B (mips 1-5 dropped),
  D_02005FF0 32x32 -> 32x3 (stale tile.height from gsDPSetTileSize).
  Now falls back only when the tmem slot was never written.
- TextureCacheKey gains size_bytes so a truncated first import cannot
  poison later full imports of the same address.
- VBO UV path now wraps by TILE size for WRAP sub-tiles (N64 semantics);
  the logo's 20x3 tile at offset (11.5,29) was sampling row 0 instead
  of rows 29-31. Per-vertex pre-wrap; known limits: half-texel edge
  bleed, mirror sub-tiles unhandled.

Verified: clean runs reach frame ~2100 then hit the D69 load_bg_file
fault (unchanged — still the milestone blocker); PPM frame ~555 shows
the four gold RARE letters on the dark-blue plate (final pixel-perfect
check vs N64 footage left as a low-priority task). All env-gated probes
from this session (GE_DBGUV/DBGTRI/DBGTALL/DBGMAT/D74IMP/D74DUMP/
GE_DBGLOAD) stripped; previously committed TEMP D63 blocks + GE_D71LOG
remain on the HANDOFF strip list.

Docs: PCPortResearch.md §F D73/D74 entries, §G/§H status updated;
HANDOFF.md rewritten for Session I (D69 primary); AGENTS.md phase line.
This commit is contained in:
James Dansereau committed 2026-08-24 22:32:27 -04:00
1 parent 5f4e90ddfc
commit 978306d14f
8 files changed
+237 -173

No files matched your search

+1 -1
View File
@@ -48,6 +48,6 @@ Run `/linkcheck` for this sweep. Record new findings in `docs/PCPortResearch.md`
- **Compile+link milestone:** all ~235 TUs compile and `ge007.x86_64.exe` links clean (`ninja ge007 -k 0`, 236/236). Findings D7–D17 in `docs/PCPortResearch.md` §11; asset stubs in `port/src/assetstubs.c`, fast3d no-ops in `port/src/gfxstub.c` (delete when real fast3d lands).
- **Phase 1 (boot to window):** done — ROM loads/validates/maps at cart base 0x10000000 (`romdata.c`), SDL2 window + GL clear loop (`video.c`), absolute asset symbols from `scripts/gen_romassets.py` → `port/src/romassets_<r>.s` (findings D18–D23).
- **Phase 1.5 (boot to first frame):** DONE — boot chain complete AND real frames render (`[NOTE] frame N rendered in … us`). D34–D42 closed the blockers on top of D31–D33: ANIM_DATA lvalues, music seq-table ABI+endianness, music-heap sizing, libaudio bank-tree re-layout, implicit-declaration prototype shim (D38), Globalimagetable rebasing + true segment size (D39), ModelHitEntry pool sizing (D40), cross-thread GL context release (D41), rsp task-settings toggle (D42). ROM dump verified against the No-Intro good dump (byte-identical, header CRCs re-checked — `tools_pc/romverify.c`).
- **Phase 2 (rendering):** IN PROGRESS — fast3d integrated + real frames render. **Plan B EXECUTED (D50, committed):** all 512 NTSC model files converted offline by `tools_pc/d43_emit.py` → `data/pcmodels-ntsc-final/pcmodels.bin` + manifest.csv (single concatenated RZ image; regenerate per region with the script — sidecars are gitignored), served through the existing load path via a port-layer table patch (`port/src/pcmodels.c`, romdata cart-extension, one-shot `pcmodelsPatchTable()` + poolRemaining reset hook in `load_object_fill_header`); runtime C fixups for language-bank BE offset tables + font re-layout (D50.3/D50.4); legal-screen UB seed (D50.5); **D50.6 fixed the first model-render crash** — `texCopyGdls` copied only w0 of each 16-byte Gfx slot on PC (the union's trailing `long long` is 8 bytes), leaving stale mempool w1s in the compacted GDLs; byte-proven against PlegalpageZ, fixed with a full-slot copy under #ifdef PORT. Model GDLs now execute to completion (PlegalpageZ sub-DLs). **D51 RESOLVED** (font `pixeldata` fixup wrote the pointer at the wrong offsets — frame-5 `import_texture_i8` crash gone; GE opcode space decoded along the way, G_TEXRECT=0xE4/0xE5 non-F3DEX numbering), **D52 RESOLVED** (`osGetCount` now advances at the N64 RSP counter rate ≈ 46.5525 ticks/µs — fixed the non-deterministic post-frame-2 hang), **D53 RESOLVED** (model RW-data pool addressing: D53.1 `Model.datas` word-stride → `u32 *` under PORT + casts; D53.2 ModelSlot/AnimModelSlot re-laid on top of the PC struct Model — in-use@0x10 / pool@0x20, slot size = sizeof(Model) = 0xE8 — plus PORT-guarded heap-fallback bumps in model.c), **D54 RESOLVED** (cseq ALCMidiHdr BE header: port-layer `romdataFixupCseq` bswap32 of the first 17 u32s, called from music.c after each decompress), **D54b RESOLVED** (synth param slots sized for ALStartParamAlt = 0x28 in synthesizer.c alSynInit under PORT), **D55 RESOLVED** (RLE folder-menu background BE w/h header: bswap32 of the first word at `(u32 *)&unknown2` at cart-map time in romdataInit, guarded). Build GREEN; game boots, plays intro music, renders through logo/gun-barrel, instantiates Brosnan, and executes the gun-barrel hole DL clean to ENDDL. **D56 RESOLVED** (watch Model/RW-pool raw offsets: field-name Model access + static 0xC8-byte pool under PORT in bondview2.c), **D57 RESOLVED** (pointer-grown rwdata records overflowed N64-sized spare pools — Brosnan PC numRecords 153 > 140: capacities grown in initunk_005520.c + dynamic slot+pool fallback in model.c), **D58 RESOLVED** (gun-barrel DL: vertex-pointer K0 idiom rebuilt via `OS_K0_TO_PHYSICAL(…) | 0x80000000` in title.c + DL reserve 0x100→0x200 because `sizeof(Gfx)` is 16 on PC). **D59–D68 RESOLVED (committed):** the "GL DLL" crash was D64's unbounded blood-RLE write (sentinel section-adjacency lost on PC) clobbering the gun-barrel sub-DL; then D60–D62 (DMA target validation, OSMesgQueue layout), D65/D65b (HEADS/BODIES enum signed sentinels under PORT), D66 (romCopyAligned + ramrom replay 64-bit pointer width), D67 (struct image_entry N64 layout), **D68 (Globalimagetable BE→LE fixup** — `port/src/gimgfixup.c` bswaps IMAGESEG Gfx w1 words + sImageTableEntry.index after texReset's romCopy; compiled globalDL shadows synced for explosion.c). The game now renders the **entire ~3.5-minute intro** (logo → gun barrel w/ Brosnan → cast) at ~59 fps. **Current blocker D69:** `load_bg_file` (bg.c:830) faults on first stage load — BG-file headers/records are N64 big-endian segment-0x0F offsets, read LE on PC; the bg `.seg` + `Tbg_*_stanZ` format work is next (decode from bg.c/stan.c → offline sidecar conversion per the D43 pattern or runtime port-layer fixup; PD `preprocess/filebg.c` is approach-reference only — its BG format differs). **D70–D72 (intro-logo pixels):** D70 env-gated PPM frame capture (`GE_PCDUMP` → `./ppm/`, gitignored); **D71 RESOLVED** — C-array texture sources (the four rarewarelogo.c RGBA16 images) byte-swapped on LE PC → pink/green logo; port-layer per-source bswap in `import_texture`; **D72 RESOLVED** the UV path (GE always uses authored `tc[]` UVs — no G_TT_BASE/CLAMP anywhere; `lookat_enabled` defaults false since N64 boots with RSP mem zeroed) but **D72.3 OPEN**: all logo triangles project off-screen and a flat dark-blue checkerboard triangle rasterizes instead (identity pending via `GE_DBGUV`/`GE_DBGTALL` traces); D69 remains the milestone blocker. Committed through D72. Recurring sub-task: ABI/layout fixes per the D32 procedure (§H) as more asset types are first touched (PD's `preprocess/` module already solves most of them; prefer the offline pattern for format work — D48; full-struct Gfx copies on PC — D50.6; audit struct type-puns after pointer-width changes — D53.2; raw byte offsets into pointer-containing structs shift on PC — D56; N64-sized reservations must be re-checked against 16-byte Gfx/Vtx widths — D58).
- **Phase 2 (rendering):** IN PROGRESS — fast3d integrated + real frames render. **Plan B EXECUTED (D50, committed):** all 512 NTSC model files converted offline by `tools_pc/d43_emit.py` → `data/pcmodels-ntsc-final/pcmodels.bin` + manifest.csv (single concatenated RZ image; regenerate per region with the script — sidecars are gitignored), served through the existing load path via a port-layer table patch (`port/src/pcmodels.c`, romdata cart-extension, one-shot `pcmodelsPatchTable()` + poolRemaining reset hook in `load_object_fill_header`); runtime C fixups for language-bank BE offset tables + font re-layout (D50.3/D50.4); legal-screen UB seed (D50.5); **D50.6 fixed the first model-render crash** — `texCopyGdls` copied only w0 of each 16-byte Gfx slot on PC (the union's trailing `long long` is 8 bytes), leaving stale mempool w1s in the compacted GDLs; byte-proven against PlegalpageZ, fixed with a full-slot copy under #ifdef PORT. Model GDLs now execute to completion (PlegalpageZ sub-DLs). **D51 RESOLVED** (font `pixeldata` fixup wrote the pointer at the wrong offsets — frame-5 `import_texture_i8` crash gone; GE opcode space decoded along the way, G_TEXRECT=0xE4/0xE5 non-F3DEX numbering), **D52 RESOLVED** (`osGetCount` now advances at the N64 RSP counter rate ≈ 46.5525 ticks/µs — fixed the non-deterministic post-frame-2 hang), **D53 RESOLVED** (model RW-data pool addressing: D53.1 `Model.datas` word-stride → `u32 *` under PORT + casts; D53.2 ModelSlot/AnimModelSlot re-laid on top of the PC struct Model — in-use@0x10 / pool@0x20, slot size = sizeof(Model) = 0xE8 — plus PORT-guarded heap-fallback bumps in model.c), **D54 RESOLVED** (cseq ALCMidiHdr BE header: port-layer `romdataFixupCseq` bswap32 of the first 17 u32s, called from music.c after each decompress), **D54b RESOLVED** (synth param slots sized for ALStartParamAlt = 0x28 in synthesizer.c alSynInit under PORT), **D55 RESOLVED** (RLE folder-menu background BE w/h header: bswap32 of the first word at `(u32 *)&unknown2` at cart-map time in romdataInit, guarded). Build GREEN; game boots, plays intro music, renders through logo/gun-barrel, instantiates Brosnan, and executes the gun-barrel hole DL clean to ENDDL. **D56 RESOLVED** (watch Model/RW-pool raw offsets: field-name Model access + static 0xC8-byte pool under PORT in bondview2.c), **D57 RESOLVED** (pointer-grown rwdata records overflowed N64-sized spare pools — Brosnan PC numRecords 153 > 140: capacities grown in initunk_005520.c + dynamic slot+pool fallback in model.c), **D58 RESOLVED** (gun-barrel DL: vertex-pointer K0 idiom rebuilt via `OS_K0_TO_PHYSICAL(…) | 0x80000000` in title.c + DL reserve 0x100→0x200 because `sizeof(Gfx)` is 16 on PC). **D59–D68 RESOLVED (committed):** the "GL DLL" crash was D64's unbounded blood-RLE write (sentinel section-adjacency lost on PC) clobbering the gun-barrel sub-DL; then D60–D62 (DMA target validation, OSMesgQueue layout), D65/D65b (HEADS/BODIES enum signed sentinels under PORT), D66 (romCopyAligned + ramrom replay 64-bit pointer width), D67 (struct image_entry N64 layout), **D68 (Globalimagetable BE→LE fixup** — `port/src/gimgfixup.c` bswaps IMAGESEG Gfx w1 words + sImageTableEntry.index after texReset's romCopy; compiled globalDL shadows synced for explosion.c). The game now renders the **entire intro** (logo → gun barrel w/ Brosnan → cast; ~frame 2100, ≈2 min wall-clock at the current ~20 fps clean-run rate). **Current blocker D69:** `load_bg_file` (bg.c:830) faults on first stage load — BG-file headers/records are N64 big-endian segment-0x0F offsets, read LE on PC; the bg `.seg` + `Tbg_*_stanZ` format work is next (decode from bg.c/stan.c → offline sidecar conversion per the D43 pattern or runtime port-layer fixup; PD `preprocess/filebg.c` is approach-reference only — its BG format differs). **D70–D74 (intro-logo pixels, RESOLVED):** D70 env-gated PPM frame capture (`GE_PCDUMP` → `./ppm/`, gitignored — kept as permanent dev tooling); **D71** C-array texture sources (the four rarewarelogo.c RGBA16 images) byte-swapped on LE PC → port-layer per-source bswap in `import_texture`; **D72** the UV path (GE always uses authored `tc[]` UVs; `lookat_enabled` defaults false); **D73 = root cause of old D72.3** — sinf/cosf `du` double constants are big-endian word pairs, garbage on LE PC → guMtxF2L emitted −32768 for every sin/cos entry → logo triangles projected off-screen; fixed with the PORT-only `DVAL()` macro in `src/libultra/gu/guint.h` (sinf.c/cosf.c `.d` reads replaced; N64 build untouched); **D74** texture import fallback no longer truncates valid gDPLoadBlock data (was dropping letter mip chains + reducing D_02005FF0 to 32×3), `TextureCacheKey` gained `size_bytes`, and the VBO path wraps UVs by tile size for WRAP sub-tiles (N64 semantics). The Rareware logo now renders its four gold letters on the dark-blue plate (PPM-verified ~frame 555; final pixel-perfect check vs N64 footage optional). D69 remaLine truncated
- **Phase 3 (audio + input):** libaudio → SDL — largely copy-and-adapt from PD (`mixer.c` 722 lines vs our 31-line stub; `input.c` SDL_GameController backend; §2.4); decide the ASP strategy (C2: `aspMain*` dummies in `ucode.c`).
- **Phase 4 (saves + polish):** file-backed EEPROM — build on PD's `fs.c` (file-backed save dir); PFS/motor already no-op stubs.
+71 -68
View File
@@ -1,62 +1,55 @@
# Handoff brief — GoldenEye 007 PC port (Phase 2: Session H — D72.3 intro-logo pixels + D69 BG/stan stage loading)
# Handoff brief — GoldenEye 007 PC port (Phase 2: Session I — D69 BG/stan stage loading)
_Paste-ready briefing. Authoritative detail lives in `AGENTS.md` and
`docs/PCPortResearch.md` (§F/D59–D69 findings, §G status, §H procedure); this
`docs/PCPortResearch.md` (§F/D59–D74 findings, §G status, §H procedure); this
is the summary + the immediate tasks._
## Your job
- **Session H:** two live threads, both verified reproducible:
1. **D72.3 (intro logo pixels):** after D71 (logo byte order) + D72 (tc[]
UVs), the Rareware logo still doesn't appear — all its triangles project
off-screen, and a flat dark-blue checkerboard triangle (broken
rasterization of something else) fills the lower screen instead. Tools
are in place: `GE_PCDUMP` frame capture + `GE_DBGUV`/`GE_DBGTALL`
traces in gfx_pc.cpp. Find which triangle draws the checkerboard, then
check MP matrix + viewport at logo time (title.c
load_display_rare_logo).
2. **D69 (milestone):** make the first stage (BUNKER1) load and render —
BG-file data is N64 big-endian; reverse-engineer GE's bg `.seg` +
`Tbg_*_stanZ` formats from the decompiled consumers, then convert/fix
for PC.
Work agentically: decode → implement → build (~5 s) → verify → commit at
each sub-milestone (document findings as D6x/D7x in §F).
- **Session I — D69 (the milestone blocker):** make the first stage (BUNKER1)
load and render. BG-file data is N64 big-endian; reverse-engineer GE's bg
`.seg` + `Tbg_*_stanZ` formats from the decompiled consumers, then convert
or fix them for PC. This is the only thing standing between the intro and
gameplay (verified: clean runs reach frame ~2100 in ≈2 min wall-clock, then
fault deterministically in `load_bg_file`, bg.c:830).
- **Secondary (low priority):** final pixel check of the intro logo. D73 +
D74 fixed it at the data level and PPM frames ~550–560 show four gold
letters on the dark-blue plate; compare against N64 reference footage if
time allows (reference screenshots are at repo root: `Graphics Screenshot
2026-08-24 121659.jpg`, `gun barrel.png`).
- Work agentically: decode → implement → build (~5 s) → verify → commit at
each sub-milestone (document findings as D7x in §F).
## Current state (verified this session)
- **Build GREEN.** `export PATH="/c/msys64/mingw64/bin:$PATH" && ./build-pc.sh ntsc-final`.
- **D59–D68 all resolved** (details in PCPortResearch.md §F). Highlights:
- **D59/D64** — the old "GL DLL" crash was an unbounded blood-RLE write
(sentinel `die_blood_image_end` lost its N64 section adjacency on PC)
clobbering the gun-barrel sub-DL. Sentinel now defined as one-past-the-
end of the array under PORT.
- **D65/D65b** — `enum HEADS`/`BODIES` sentinels: N64 semantics were signed
(`HEAD_FIXED == -1`); PC GCC 16 picks unsigned underlying types, deleting
the guard branches. Negative literals under PORT restore the bit pattern
with signed semantics.
- **D66** — `romCopyAligned` + ramrom replay did s32 pointer math; targets
in `.bss` > 4 GiB truncated to wild addresses. 64-bit version under PORT,
returns `void *`.
- **D67** — `struct image_entry` re-laid (all-u32 bitfields, `dataoffset:24`
first) so texLoad's raw word read + chrprop.c's 8-byte stride agree;
sizeof == 8 on both targets.
- **D68** — Globalimagetable BE→LE fixup (`port/src/gimgfixup.c`): bswap the
IMAGESEG-marked Gfx w1 words (17 DLs) + `sImageTableEntry.index` (32
tables, counts from D39 layout) after texReset's romCopy; marker scan in
`texLoadFromDisplayList` moved to bytes 6..7 under PORT; compiled
globalDL shadows synced after texLoad (`gimgSyncCompiledGlobalDLs`).
- **D70–D72 (intro logo pixels, latest session):** D70 env-gated PPM frame
capture (`GE_PCDUMP` → `./ppm/`, gitignored); D71 RESOLVED — C-array
texture sources (the four rarewarelogo.c RGBA16 images) were byte-swapped
on LE PC → pink/green logo; port-layer per-source bswap normalizes them.
D72 RESOLVED the UV path (GE always uses authored tc[] UVs; lookat defaults
off) but **D72.3 OPEN**: all logo triangles project off-screen and a flat
dark-blue checkerboard triangle rasterizes instead — see Your job #1.
- **Runtime:** boots → intro music → full intro (logo, gun barrel w/ Brosnan,
cast screen) at ~59 fps → **crash in `load_bg_file` (bg.c:830)** on first
stage load. EXCEPTION 0xc0000005, FAULT ADDR ≈ header buffer + 0x500000F.
- **Committed through D72** (see git log). TEMP diagnostics from the
D51–D72 sessions are still in the tree — strip list in Task 3.
- **D59–D68 resolved** (details in PCPortResearch.md §F): blood-RLE sentinel,
HEADS/BODIES signed sentinels, romCopyAligned/ramrom 64-bit, image_entry
layout, Globalimagetable BE→LE fixup (`port/src/gimgfixup.c`), etc.
- **D70–D74 (intro logo) resolved:**
- D70: `GE_PCDUMP` PPM frame capture (kept — permanent dev tooling).
- D71: C-array texture sources (rarewarelogo.c RGBA16 images) bswapped on
LE PC → port-layer per-source normalization in `import_texture`.
- D72: GE always uses authored `tc[]` UVs; `lookat_enabled` defaults false.
- **D73 (root cause of the old D72.3):** sinf/cosf `du` double constants
are big-endian word pairs → garbage on LE PC → guMtxF2L emitted −32768
for every sin/cos entry → logo triangles projected off-screen. Fixed with
the `DVAL()` macro in `src/libultra/gu/guint.h` (PORT-only; N64 build
untouched). All scenes using guRotate/guLookAt-derived matrices were
affected, not just the logo.
- **D74:** texture import fallback no longer truncates valid gDPLoadBlock
data (was dropping letter mip chains and reducing D_02005FF0 to 32×3);
`TextureCacheKey` gained `size_bytes`; VBO path now wraps UVs by tile
size for WRAP sub-tiles (N64 semantics; the logo's 20×3 sub-tile at
offset (11.5, 29) was sampling row 0 instead of rows 29–31).
- **Runtime:** boots → intro music → full intro (Rareware logo with gold
letters, gun barrel w/ Brosnan, cast screen) → **crash in `load_bg_file`
(bg.c:830)** on first stage load. EXCEPTION 0xc0000005; the crash log at
repo root (`ge007.crash.log`, gitignored) is from this exact fault:
PC=0x14000627c, FAULT ADDR = Rax+0x28 (reading `pPointTableBin` through the
mis-rebased room-fileposition pointer).
- **Committed through D74.** This session's TEMP probes are all stripped;
previously committed TEMP diagnostics remain — strip list in Task 3.
## The blocker, precisely (D69)
@@ -98,7 +91,8 @@ GE's own consumers (bg.c, stan.c, and the N64 disasm where in doubt).
1. `AGENTS.md` — non-negotiables, critical files, build, verification ritual.
2. `docs/PCPortResearch.md` **§F/D69** (blocker + strategy), then D68
(Globalimagetable fixup — the template for BE data in this segment family),
§G status, and the environment reminders at the end of §H.
D73–D74 (this session), §G status, and the environment reminders at the end
of §H.
3. The consumers: `src/game/bg.c` (`load_bg_file` ~line 795+, levelinfotable
line 183, BG_SEG_TO_PTR in bg.h:23), `src/game/stan.c` (stanLoadFile /
stanDetermineEOF), `src/game/ob.c` (obLoadBGFileBytesAtOffset — note the
@@ -135,24 +129,23 @@ GE's own consumers (bg.c, stan.c, and the N64 disasm where in doubt).
## Task 3 — strip TEMP diagnostics before committing
Env-gated probes to remove (grep `GE_D` + `TEMP D`):
- ob.c: D69 probe (both obLoadBGFileBytesAtOffset versions) + its stdio/stdlib
includes.
- image.c / image_bank.c: D66 texLoad probe if still present.
- gfx_pc.cpp: D59 seg probes, D63 dram-branch/modelRenderNodeGundl/rspGfxTaskStart.
- gfx_pc.cpp: D70/D71/D72 TEMP — `GE_DBGUV`/`GE_DBGTALL` UV+triangle loggers
(gfx_dbg_uv_enabled + the DBGTRI/DBGTALL blocks in gfx_sp_vertex/gfx_sp_tri1)
and the `GE_D71LOG` normalize log. Keep the D71 normalization itself — it's
the fix, not a probe.
- video.c + gfx_opengl.cpp: D70 `GE_PCDUMP` frame capture (videoEndFrame dump
hook, gfx_opengl_pcdump_* implementations).
- blood_animation.c: D63 g_GfxMemPos probe.
- front.c: D63 menu probes (d63MenuProbe/After + call sites).
This session's probes (GE_DBGUV/DBGTRI/DBGTALL/DBGMAT/D74IMP/D74DUMP/DBGLOAD)
are already stripped. Previously committed TEMP diagnostics still in the tree
(grep `TEMP D` + `GE_D`):
- gfx_pc.cpp: **TEMP D63** — the dram-branch trail (`s_d63taskcount`,
`d63RecordBranch`/`d63DumpTrail`/`d63LogLastBranchReread` externs near the
top of gfx_run_dl, the call in the G_DL case that also spams `[NOTE] D63
dram-branch` every frame, the GE_D63 dump block, and the function
definitions at the end of the file); D59 seg probes if still present.
- gfx_pc.cpp: `GE_D71LOG` one-shot normalize log (keep the normalization
itself — it's the D71 fix).
- front.c: `d63MenuProbe`/After + call sites. blood_animation.c: D63
g_GfxMemPos probe. ob.c: D69 probe (both obLoadBGFileBytesAtOffset
versions) + its stdio/stdlib includes — **strip only after D69 is solved**.
- rsp.c, model.c, initanitable.c, language.c, title.c, ramromreplay.c,
n64stubs.c, libultra.c, romdata.c: leftover TEMP blocks from D51–D66
(D51 vi-post logging, D54 music, D56 watch, D57 pool, D60/D61 DMA logging —
decide per item whether the guard stays permanent; D60's target validation
is a permanent safety net and should stay).
(decide per item; D60's DMA target validation is a permanent safety net and
should stay).
- Scratch files at repo root: `run_*.log`, `d62mesg.log`, `all.txt`, `b3.c`,
`btest*.c`, `buildtest.txt`, `err.txt`, `gcout.txt`, `preproc.txt`,
`vsize.c/.exe`, `scratch-logs/` — delete (add to .gitignore if they recur).
@@ -166,16 +159,21 @@ Env-gated probes to remove (grep `GE_D` + `TEMP D`):
duplicates, syntax (`./build-pc.sh` is the final word).
- **Run the game from the repo root** (`cd REPO_ROOT &&
./build-pc/ge007.x86_64.exe`) — running from build-pc/ picks up the wrong
ROM (baserom.u.z64) and misses the sidecar.
ROM (baserom.u.z64) and misses the pcmodels.bin sidecar → spurious frame-4
crash in modelPromoteNodeOffsetsToPointers.
- `Gfx.words.w0/w1` are both uintptr_t (16-byte Gfx on PC); ROM-copied N64
Gfx DLs stay 8 bytes in memory — walk them at 8-byte stride when scanning.
- N64 CPU/RDP data in ROM is big-endian; any raw `*(u32*)` read of ROM-copied
buffers needs a bswap under PORT (D68 pattern) or offline conversion.
- Heavy env-gated logging perturbs tick-driven timing (a full-triangle trace
hung the kernel heartbeat at ~frame 107 while clean runs run fine past
frame 2100) — use short targeted probe windows, and diagnose stability only
with clean runs.
## Environment reminders
- MSYS2 tools: `export PATH="/c/msys64/mingw64/bin:$PATH"`. Build ~5 s.
- Crash log `ge007.crash.log` now includes FAULT ADDR, STACK@RSP window, and
- Crash log `ge007.crash.log` includes FAULT ADDR, STACK@RSP window, and
module list — first stop for any fault. Symbolicate offline:
`addr2line -e build-pc/ge007.x86_64.exe -f -C <pc>`.
- gdb is launch-mode only and far too slow for timing-dependent crashes —
@@ -186,3 +184,8 @@ Env-gated probes to remove (grep `GE_D` + `TEMP D`):
- ROM: data/ge007.ntsc-final.z64 (byte-identical to the No-Intro good dump);
sidecar: data/pcmodels-ntsc-final/ (gitignored, regenerate with
tools_pc/d43_emit.py per region).
- Artifacts from this session: `ppm/` frames ~510–590 are the logo sequence
(frame ~555 is the best front-facing "RARE" shot); `tools_pc/mtxtest.c` is a
scratch matrix-convention harness (kept for reference). PPM files from
timeout-killed runs can be truncated — validate header + pixel count before
parsing.
+108 -30
View File
@@ -2214,28 +2214,40 @@ was D64's unbounded blood-RLE write clobbering the barrel sub-DL),
**D67 resolved** (struct image_entry N64 layout reconstruction),
**D68 resolved** (Globalimagetable BE→LE fixup for IMAGESEG Gfx words +
sImageTableEntry.index; compiled globalDL shadows synced after texLoad).
The game now boots, plays the intro music, and renders the **entire ~3.5-
minute intro** (Nintendo logo → gun barrel with Brosnan → cast screen) at
~59 fps, then crashes in `load_bg_file` on the first stage load —
The game now boots, plays the intro music, and renders the **entire intro**
(Nintendo logo → gun barrel with Brosnan → cast screen; ~frame 2100, ≈2 min
wall-clock at the current ~20 fps clean-run rate), then crashes in
`load_bg_file` on the first stage load —
**D69, the milestone blocker**: BG-file headers are N64 big-endian
(segment-0x0F offsets) and PC reads them LE. Details in §F/D59–D69.
**D70–D72 (intro-logo pixel work):** D70 env-gated PPM frame capture
**D70–D74 (intro-logo pixel work):** D70 env-gated PPM frame capture
(`GE_PCDUMP` → `./ppm/`) for numerical visual debugging; **D71 resolved** —
C-array texture sources (the four rarewarelogo.c RGBA16 images) were
byte-swapped on LE PC (pink/green logo); port-layer per-source bswap in
`import_texture`. **D72 resolved** the UV path (GE always uses authored tc[]
UVs; `lookat_enabled` defaults false) but **D72.3 is OPEN**: all logo
triangles project off-screen and a flat dark-blue checkerboard triangle
rasterizes instead — identity pending (GE_DBGUV/GE_DBGTALL traces).
UVs; `lookat_enabled` defaults false). **D73 resolved** — root cause of
D72.3: sinf/cosf `du` double constants are big-endian word pairs, garbage on
LE PC → guMtxF2L emitted −32768 for every sin/cos entry → logo triangles
projected off-screen; DVAL() macro fix in guint.h/sinf.c/cosf.c under PORT.
**D74 resolved** — texture import fallback no longer truncates valid
gDPLoadBlock data (mip chains + sub-tiled textures), TextureCacheKey gains
`size_bytes`, and the VBO path now wraps UVs by tile size for WRAP sub-tiles
(N64 semantics). The logo now renders its four gold letters on the dark-blue
plate (PPM-verified at frame ~555); a final pixel-perfect comparison against
N64 reference footage is still open. **D69 remains the milestone blocker**
(stage load).
**Committed through D72**: D51–D72 fixes + TEMP diagnostics still present
(strip list in HANDOFF Task 3); build is GREEN.
**Committed through D74**: D51–D74 fixes; this session's TEMP probes are
stripped, but previously committed TEMP diagnostics (D63 blocks, GE_D71LOG,
and the older D51–D66 leftovers) are still in the tree — strip list in
HANDOFF Task 3. Build is GREEN.
### H. Handoff & plan (current session)
Full paste-ready brief: **docs/HANDOFF.md** (two live threads: D72.3
intro-logo pixels, then D69 BG/stan stage loading). For D69:
Full paste-ready brief: **docs/HANDOFF.md** (primary thread: D69 BG/stan
stage loading — the milestone blocker; secondary: final pixel check of the
now-rendering intro logo). For D69:
reverse-engineer GE's bg .seg + Tbg_*_stanZ formats from the decompiled
consumers (bg.c, stan.c), then choose offline sidecar conversion (Plan-B
pattern, D43) vs runtime port-layer fixup; PD's preprocess/filebg.c is a
@@ -2243,25 +2255,28 @@ reference for the *approach* only — its BG format is different (zipped
multi-section).
Summary:
**State.** D50–D72 resolved and verified (except D72.3, open); committed
through the D72 milestone. The game boots, plays intro music, and renders
the entire ~3.5-minute intro (logo → gun barrel with Brosnan → cast screen)
at ~59 fps — logo lettering now gold-on-blue (D71/D72) but not yet visible
(D72.3). TEMP diagnostics from the D51–D72 sessions are still in the tree
(strip list in HANDOFF Task 3); visual-debug tooling: `GE_PCDUMP` frame
capture, `GE_DBGUV`/`GE_DBGTALL` UV+triangle traces. Build is GREEN.
**State.** D50–D74 resolved and verified (D73 = sinf/cosf endianness, D74 =
texture import fallback + sub-tile UV wrap); committed through the D74
milestone. The game boots, plays intro music, and renders the entire intro
(logo → gun barrel with Brosnan → cast screen) — the Rareware logo now shows
its four gold letters on the dark-blue plate (D71–D74; PPM-verified at frame
~555, final pixel-perfect check vs N64 footage still open). TEMP diagnostics
from earlier sessions are still in the tree (strip list in HANDOFF Task 3);
visual-debug tooling: `GE_PCDUMP` frame capture. Build is GREEN.
**Thread 1 — D72.3 (intro logo).** All logo triangles project off-screen
(clip x=y≈−1.6e11, w<0) while a flat dark-blue (0,0,64) checkerboard
triangle rasterizes in the lower screen; matrix-format mismatches ruled out
(§F/D72). Next: identify the checkerboard's triangle via GE_DBGTALL, then
verify MP matrix + viewport at logo time (title.c load_display_rare_logo).
**Thread 2 — D69 (milestone blocker).** `load_bg_file` (src/game/bg.c:830)
faults on the first stage load (BUNKER1): BG-file header words are N64
big-endian segment-0x0F offsets; PC reads them LE, so the
**Thread 1 — D69 (milestone blocker, now primary).** `load_bg_file`
(src/game/bg.c:830) faults on the first stage load (BUNKER1): BG-file header
words are N64 big-endian segment-0x0F offsets; PC reads them LE, so the
room-fileposition-list pointer lands ~5 MB past the stack header buffer.
Full analysis + strategy options in §F/D69.
Full analysis + strategy options in §F/D69. This is the next milestone:
decode the bg .seg + Tbg_*_stanZ formats, convert/fix for PC, get a stage to
load and render.
**Thread 2 — logo final check (low priority).** Compare the rendered logo
frames (ppm/, frame ~550–560) against N64 reference footage; if any letter
is still off, re-add a lightweight triangle/texture attribution probe (the
D74 probes are stripped; the import sizes and sub-tile wrap are verified at
the data level).
**After D69:** get a stage to load + render (bg .seg + Tbg_*_stanZ format
work), then continue the diagnose→fix→verify loop through gameplay; strip
@@ -2354,8 +2369,71 @@ gated by `GE_D71LOG`.
modified variant writing the interleaved hi16/lo16 s32 Q15.16 packed format
that gfx_sp_matrix decodes exactly (FTOFIX32=×65536); `D_8002A7D0` is a
zero-init u32 so `[D_8002A7D0]`==`[0]`; alloc_intro_matrices() runs from
initmenus. Next: find the checkerboard's triangle via GE_DBGTALL, then
verify MP matrix + viewport at logo time (title.c load_display_rare_logo).
initmenus. **SUPERSEDED by D73** — the off-screen projection was never a
matrix-format or UV problem: guMtxF2L's sinf/cosf inputs were garbage on PC.
**D73 (RESOLVED — root cause of D72.3):** GE's `sinf`/`cosf`
(src/libultra/gu/sinf.c, cosf.c) build their double constants through the
`du` bit-union (`{ struct { u32 hi; u32 lo; } word; double d; }`) with
big-endian word pairs `{hi, lo}` (rpi, pihi, and the P[] polynomial tables).
On an LE PC the `.d` read has the two words swapped: `rpi.d` ≈ 2^733,
`pihi.d` ≈ 2^257. Range reduction then overflows: `dn = dx*rpi.d` huge →
ROUND(dn) saturates in cvttsd2si (n = −2^31) → `dx = dx − dn*pihi.d` ≈ 2^288
→ xsq ≈ 2^576 → the result overflows float → sinf/cosf return ±inf/NaN.
FTOFIX32's `(int)(±inf·65536)` is another cvttss2si saturation →
0x80000000 (−32768). Every sin/cos-derived entry of guMtxF2L became −32768,
so the Rareware logo's guRotate matrix was garbage and all its triangles
projected off-screen (the D72.3 symptom; the "checkerboard" was a separate
large triangle rasterizing over the empty logo region). The logo scene is the
first visually verifiable consumer of guMtxF2L sin/cos output in the intro;
other scenes that build matrices via guRotate/guLookAt are affected by the
same bug and were fixed by this change. Fix (narrow ABI exception — constant
interpretation only, algorithm verbatim): `duD()` static-inline + `DVAL(x)`
macro in src/libultra/gu/guint.h under #ifdef PORT re-pack hi/lo into the
correct LE double; the 5 `.d` reads in sinf.c and 4 in cosf.c now use
`DVAL(...)`. The N64 build is untouched (#else branch = original expression).
Only sinf.c/cosf.c use `du` (grep-verified); `fu` (single u32) constants are
endian-safe. Verified numerically: P decodes to the expected perspective
matrix, Rot(−40°Y) gives clean cos=0.766/sin=−0.643, and logo vertices clip
to small finite on-screen NDC values. (tools_pc/mtxtest.c is a standalone
scratch harness for the matrix-convention question that was ruled out along
the way — GE stores all matrices transposed; row-vector ≡ column pipeline.)
**D74 (RESOLVED — final pixel check pending):** with D73 in, logo geometry
was correct but the texture side was still wrong: a dim red-brown blob plus
a flat vertical bar at the right edge, one letter visible at a time. Three
port-layer bugs, all in port/fast3d/gfx_pc.cpp (with gfx_pc.h):
- **Import fallback truncation.** `import_texture`'s old condition
`(rdp.tex_lod && tile >= rdp.first_tile_index + rdp.tex_detail) ||
!loaded_texture.addr` overwrote valid gDPLoadBlock tmem data with
`line_size_bytes * tile.height`. For the logo: letter mip chains 2744 B →
2048 B (mips 1–5 dropped), and D_02005FF0's 32×32 → 192 B (32×3, stale
tile.height=3 left by `gsDPSetTileSize(0, 46, 116, 124, 124)` in DL
D_02004758). Fix: fall back only when `!loaded_texture.addr` (N64 TMem is
persistent — a populated slot is the faithful source).
- **Cache poisoning.** TextureCacheKey lacked the upload size, so the first
(truncated) import of an address poisoned every later import. Added
`size_bytes` to the key (gfx_pc.h + both key initializers + the bucket-only
aggregate in gfx_texture_cache_delete).
- **Sub-tile UV wrap period.** N64 wraps UVs by the TILE size when a render
tile is a sub-region of the image; the port wrapped at the full uploaded
image size (GL_REPEAT). D_02004758's 20×3 tile at offset (11.5, 29)
therefore sampled row 0 instead of rows 29–31 — the flat bar's color
matched texel [0,4] of D_02005FF0 exactly. Fix: per-vertex pre-wrap in the
VBO path — for a WRAP sub-tile (`tex_width2 < tex_width`, where
`tex_width2 = (lrs−uls+4)/4` is the tile window in texels),
`u = fmodf(u, tw); if (u<0) u += tw; u += uls/4.0f` (same for v). Known
limitations: half-texel edge bleed at the sub-tile window edges (GL
bilinear neighbors may sample just outside [O, O+W)); mirror sub-tiles are
not handled (the logo doesn't use them; audit if another scene regresses).
Verified: runtime import sizes correct (D_02005FF0 = 2048 B, imgRAre =
2744 B); PPM frame ~555 shows the four-letter RARE band with gold/warm
colors and per-letter segmentation; clean `GE_PCDUMP` runs are stable past
1156 frames. Final pixel-perfect confirmation against N64 reference footage
is still open (see HANDOFF Task 1). All env-gated probes added for D72.3/D74
(GE_DBGUV/GE_DBGTRI/GE_DBGTALL/GE_DBGMAT/GE_D74IMP/GE_D74DUMP/GE_DBGLOAD)
were stripped with this change; the previously committed TEMP D63 blocks and
the GE_D71LOG normalize log remain on the HANDOFF strip list.
**Non-negotiable #2 refinement (applied to AGENTS.md).** The original "game code
compiles unmodified / fix belongs in port/" is too absolute: pointer-width layout
+29 -65
View File
@@ -568,7 +568,7 @@ void gfx_texture_cache_delete(const uint8_t* orig_addr) {
}
while (gfx_texture_cache.map.bucket_count() > 0) {
TextureCacheKey key = { orig_addr, { 0 }, 0, 0 }; // bucket index only depends on the address
TextureCacheKey key = { orig_addr, { 0 }, 0, 0, 0 }; // bucket index only depends on the address
size_t bucket = gfx_texture_cache.map.bucket(key);
bool again = false;
for (auto it = gfx_texture_cache.map.begin(bucket); it != gfx_texture_cache.map.end(bucket); ++it) {
@@ -914,7 +914,12 @@ static void import_texture(int i, int tile, bool importReplacement) {
const uint32_t tex_flags = loaded_texture.tex_flags;
const uint8_t palette_index = rdp.texture_tile[tile].palette;
if ((rdp.tex_lod && tile >= rdp.first_tile_index + rdp.tex_detail) || !loaded_texture.addr) {
// D74: only fall back when the tmem slot was never written by a load
// command. The old `rdp.tex_lod && tile >= first+detail` branch also
// overwrote valid gDPLoadBlock data with line*tile.height, which (a) dropped
// mip chains and (b) truncated sub-tiled textures to the sub-tile's row
// count (e.g. the Rare-logo D_02005FF0 20x3 tile uploaded as 32x3).
if (!loaded_texture.addr) {
// set up miplevel 0; also acts as a catch-all for when .addr is NULL because my texture loader sucks
loaded_texture.addr = rdp.texture_to_load.addr;
loaded_texture.line_size_bytes = rdp.texture_tile[tile].line_size_bytes;
@@ -935,9 +940,10 @@ static void import_texture(int i, int tile, bool importReplacement) {
TextureCacheKey key;
if (fmt == G_IM_FMT_CI) {
key = { orig_addr, { rdp.palette_addrs[0], rdp.palette_addrs[1] }, fmt, siz, palette_index };
key = { orig_addr, { rdp.palette_addrs[0], rdp.palette_addrs[1] }, fmt, siz, palette_index,
loaded_texture.size_bytes };
} else {
key = { orig_addr, {}, fmt, siz, palette_index };
key = { orig_addr, {}, fmt, siz, palette_index, loaded_texture.size_bytes };
}
if (gfx_texture_cache_lookup(i, key)) {
@@ -1255,57 +1261,12 @@ static inline int gfx_lod_tile_offset(const int i) {
return (rdp.tex_lod ? rdp.tex_detail : i);
}
// TEMP D72: env-gated UV pipeline logging (GE_DBGUV=1).
static bool gfx_dbg_uv_enabled(void) {
static int e = -1;
if (e < 0) e = getenv("GE_DBGUV") != nullptr;
return e;
}
static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bool is_rect) {
struct LoadedVertex* v1 = &rsp.loaded_vertices[vtx1_idx];
struct LoadedVertex* v2 = &rsp.loaded_vertices[vtx2_idx];
struct LoadedVertex* v3 = &rsp.loaded_vertices[vtx3_idx];
struct LoadedVertex* v_arr[3] = { v1, v2, v3 };
// TEMP D72: GE_DBGTRI=1 — trace triangles; GE_DBGTALL=1 logs all.
static int dbg_tri_count = 0;
if (gfx_dbg_uv_enabled() && getenv("GE_DBGTALL")) {
float sx[3] = { v1->x / (v1->w), v2->x / (v2->w), v3->x / (v3->w) };
float sy[3] = { v1->y / (v1->w), v2->y / (v2->w), v3->y / (v3->w) };
if (dbg_tri_count++ < 4000) {
fprintf(stderr, "[DBGTALL] n=%d scr=(%.0f,%.0f)(%.0f,%.0f)(%.0f,%.0f) col=(%d,%d,%d,%d) clip=(%x,%x,%x)\n",
dbg_tri_count, sx[0], sy[0], sx[1], sy[1], sx[2], sy[2],
v1->color.r, v1->color.g, v1->color.b, v1->color.a,
v1->clip_rej, v2->clip_rej, v3->clip_rej);
}
}
if (gfx_dbg_uv_enabled()) {
bool gold = false, blue = false;
for (int i = 0; i < 3; i++) {
if (v_arr[i]->color.b == 0 && v_arr[i]->color.r > 90) gold = true;
if (v_arr[i]->color.b > 100 && v_arr[i]->color.r < 50) blue = true;
}
if (gold || blue) {
float dx1 = v1->x / (v1->w) - v2->x / (v2->w);
float dy1 = v1->y / (v1->w) - v2->y / (v2->w);
float dx2 = v3->x / (v3->w) - v2->x / (v2->w);
float dy2 = v3->y / (v3->w) - v2->y / (v2->w);
if (dbg_tri_count++ < 64) {
fprintf(stderr, "[DBGTRI] %s cull=%d clip=(%x,%x,%x) cross=%.3f scr=(%.1f,%.1f)(%.1f,%.1f)(%.1f,%.1f) clip4=(%.0f,%.0f,%.0f,%.2f) col=(%d,%d,%d)\n",
gold ? "GOLD" : "BLUE",
rsp.geometry_mode & G_CULL_BOTH,
v1->clip_rej, v2->clip_rej, v3->clip_rej,
dx1 * dy2 - dy1 * dx2,
v1->x / (v1->w), v1->y / (v1->w),
v2->x / (v2->w), v2->y / (v2->w),
v3->x / (v3->w), v3->y / (v3->w),
v1->x, v1->y, v1->z, v1->w,
v1->color.r, v1->color.g, v1->color.b);
}
}
}
if ((rsp.extra_geometry_mode & G_NO_CLIPPING_EXT) == 0) {
if (v1->clip_rej & v2->clip_rej & v3->clip_rej) {
// The whole triangle lies outside the visible area
@@ -1567,6 +1528,25 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
u -= rdp.texture_tile[rdp.first_tile_index + tile].uls / 4.0f;
v -= rdp.texture_tile[rdp.first_tile_index + tile].ult / 4.0f;
// D74: N64 wraps UVs by the TILE size when a render tile is a
// sub-region of the image (uls/ult offset + lrs/lrt window); GL
// would wrap at the full uploaded image size instead. Pre-wrap per
// vertex (GE/PD triangles never span more than one tile period).
{
const uint32_t tw = tex_width2[i];
const uint32_t th = tex_height2[i];
if ((rdp.texture_tile[rdp.first_tile_index + tile].cms & G_TX_WRAP) && tw < tex_width[i]) {
u = fmodf(u, (float)tw);
if (u < 0.0f) u += (float)tw;
u += rdp.texture_tile[rdp.first_tile_index + tile].uls / 4.0f;
}
if ((rdp.texture_tile[rdp.first_tile_index + tile].cmt & G_TX_WRAP) && th < tex_height[i]) {
v = fmodf(v, (float)th);
if (v < 0.0f) v += (float)th;
v += rdp.texture_tile[rdp.first_tile_index + tile].ult / 4.0f;
}
}
if (!is_rect) {
if (!(rdp.other_mode_h & G_TP_PERSP)) {
u *= 0.5f;
@@ -1583,22 +1563,6 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
buf_vbo[buf_vbo_len++] = u / tex_width[t];
buf_vbo[buf_vbo_len++] = v / tex_height[t];
// TEMP D72: GE_DBGUV=1 — log the UV pipeline per vertex, but
// only for C-array (logo) textures so the cap isn't exhausted.
if (gfx_dbg_uv_enabled() &&
gfx_tex_source_is_c_array(
rdp.loaded_texture[rdp.texture_tile[rdp.first_tile_index + tile].tmem].addr)) {
static int dbg_uv_count = 0;
if (dbg_uv_count++ < 30000) {
fprintf(stderr, "[DBGUV] v.u=%d v.v=%d shifts=%d shiftt=%d uls=%d ult=%d -> u=%.4f v=%.4f (tex %dx%d)\n",
v_arr[i]->u, v_arr[i]->v, shifts, shiftt,
rdp.texture_tile[rdp.first_tile_index + tile].uls,
rdp.texture_tile[rdp.first_tile_index + tile].ult,
u / tex_width[t], v / tex_height[t],
tex_width[t], tex_height[t]);
}
}
bool clampS = tm & (1 << 2 * t);
bool clampT = tm & (1 << (2 * t + 1));
+4
View File
@@ -25,6 +25,10 @@ struct TextureCacheKey {
const uint8_t* palette_addrs[2];
uint8_t fmt, siz;
uint8_t palette_index;
// D74: the same source address can be loaded with different sizes
// (gDPLoadBlock mip chains vs partial loads); include the size so a
// truncated first import cannot poison later full imports.
uint32_t size_bytes;
bool operator==(const TextureCacheKey&) const noexcept = default;
+4 -4
View File
@@ -96,18 +96,18 @@ int ix, xpt;
dx = absx;
dn = dx*rpi.d + 0.5;
dn = dx*DVAL(rpi) + 0.5;
n = ROUND(dn);
dn = n;
dn -= 0.5;
dx = dx - dn*pihi.d;
dx = dx - dn*pilo.d; /* dx = x - (n - 0.5)*pi */
dx = dx - dn*DVAL(pihi);
dx = dx - dn*DVAL(pilo); /* dx = x - (n - 0.5)*pi */
xsq = dx*dx;
poly = ((P[4].d*xsq + P[3].d)*xsq + P[2].d)*xsq + P[1].d;
poly = ((DVAL(P[4])*xsq + DVAL(P[3]))*xsq + DVAL(P[2]))*xsq + DVAL(P[1]);
result = dx + (dx*xsq)*poly;
+15
View File
@@ -24,6 +24,21 @@ typedef union
double d;
} du;
#ifdef PORT
/* The {hi,lo} initializers of du constants are written in big-endian word
* order (as on the N64). On a little-endian host the .d member would read
* the two words swapped; re-pack them to recover the same double value. */
static inline double duD(const du u)
{
union { unsigned long long b; double d; } t;
t.b = ((unsigned long long)u.word.hi << 32) | (unsigned long long)u.word.lo;
return t.d;
}
#define DVAL(x) duD((x))
#else
#define DVAL(x) ((x).d)
#endif
typedef union
{
unsigned int i;
+5 -5
View File
@@ -96,7 +96,7 @@ int ix, xpt;
xsq = dx*dx;
poly = ((P[4].d*xsq + P[3].d)*xsq + P[2].d)*xsq + P[1].d;
poly = ((DVAL(P[4])*xsq + DVAL(P[3]))*xsq + DVAL(P[2]))*xsq + DVAL(P[1]);
result = dx + (dx*xsq)*poly;
@@ -114,20 +114,20 @@ int ix, xpt;
/* reduce argument to +/- pi/2 */
dn = dx*rpi.d;
dn = dx*DVAL(rpi);
n = ROUND(dn);
dn = n;
dx = dx - dn*pihi.d;
dx = dx - dn*pilo.d; /* dx = x - n*pi */
dx = dx - dn*DVAL(pihi);
dx = dx - dn*DVAL(pilo); /* dx = x - n*pi */
/* compute sin(dx) as before, negating result if n is odd
*/
xsq = dx*dx;
poly = ((P[4].d*xsq + P[3].d)*xsq + P[2].d)*xsq + P[1].d;
poly = ((DVAL(P[4])*xsq + DVAL(P[3]))*xsq + DVAL(P[2]))*xsq + DVAL(P[1]);
result = dx + (dx*xsq)*poly;