Consolidated technical knowledge from the multi-session R&D effort. Read this before attacking any of the open blockers — most hypotheses have already been tested.
Game CPU code (recompiled)
│
│ submit_rsp_task(OSTask*)
▼
ultramodern/events.cpp ───── shadow-snapshots DL bytes + sub-DL targets
│ (deep_shadow heuristic, see lib/N64ModernRuntime/ultramodern/src/events.cpp)
│ enqueue SpTaskAction
▼
gfx_thread (action_queue)
│
▼
RT64 Interpreter (DL walker) ─── lib/rt64/src/hle/rt64_interpreter.cpp
│
│ per-cmd dispatch via gbi->map[opCode]
▼
RSP::* / RDP::* handlers ─── lib/rt64/src/hle/{rt64_rsp.cpp, rt64_rdp.cpp}
│ (matrix, setVertex, drawIndexedTri, setTextureImage, loadBlock, …)
▼
fbPair (color/depth target + draw rect)
│
│ State::fullSync()
▼
copyRenderTargetToNative → RDRAM at colorFb->addressStart
│
▼
update_screen() reads VI_ORIGIN
│
▼
PPM dump @ /tmp/ge_fb_NNNN.ppm
This pipeline is fully validated — GE_FORCE_FB_MARKER=1 writes a magenta block at colorFb->addressStart and that block appears in the PPM dump (see docs/screenshots/fb-marker-validation.png). The mystery is upstream: why aren't real triangles producing pixels?
Extracted from n64decomp/007 rsp/graphics/gmain.s (1545 lines RSP MIPS asm) via subagent disasm. Confirmed against n64decomp/007/include/PR/gbi.h C-side macros.
- C-side uses stock libultra
gbi.h. Decomp Makefile does NOT defineF3DEX_GBIorF3DEX_GBI_2. Stock F3D opcode set:G_MTX=0x01, G_DL=0x06, G_VTX=0x04, G_MOVEMEM=0x03, G_ENDDL=0xB8, G_SETTIMG=0xFD, G_SETTILE=0xF5, G_LOADBLOCK=0xF3, G_SETCIMG=0xFF. - All
gSPMatrixcalls wrap withosVirtualToPhysical()— matrices are plain physical RDRAM addresses (/tmp/ge_decomp/src/fr.c:1696-1698,model.c:6486). - Matrices produced by
guMtxF2L→ standard libultra format (16× s16 int + 16× u16 frac, BE). - All matrix calls use
G_MTX_NOPUSH | (LOAD|MUL) | (MODELVIEW|PROJECTION). PUSH never used.gSPPopMatrixnever called. - Vertices use segment 4 (
SPSEGMENT_MODEL_VTX), set per DL block (/tmp/ge_decomp/src/game/model.c:6121,6146,6198). - Active segments per
/tmp/ge_decomp/src/bondconstants.h:2466-2478: 0(NULL), 1(font), 2(GETITLE), 4(MODEL_VTX), 5/6(MODEL_COL), 13-15(BG). - Microcode entry symbol:
gsp3DTextStart(/tmp/ge_decomp/src/game/rsp.c:208,245).
segmented_to_physical(IMEM 0x10a8): masks with DMEM-residentmem[0xb8]before segment lookup. RT64 hardcodes0x00FFFFF8. If GE writes a different mask via MOVEWORD, addresses resolve wrong.- Segment table at DMEM 0x160, indexed by
(addr>>22) & 0x3c(==((addr>>24)&0xf)*4byte offset). - Opcode 0xBD is allegedly a bitfield-MOVEWORD per agent disasm. Tested: NEVER FIRES in level 10 boot+intro. Hypothesis ruled out.
- G_TRIX (0xB1) packs ≤4 tris in 4-bit nibbles. RT64's impl uses raw nibbles (no remap table) — appears to work (20 visible tris reach drawTri).
- G_DL (0x06): segment byte from w0 top byte, not w1. Values 0xFD..0xFF bypass translation.
- Vertex DMEM stride 0x50 post-transform, base 0x420; ≤32 vertices.
- Matrix stack: 9 levels at DMEM 0x336.
Observed pattern across matrix/vertex/texture calls:
seg=0x00264503for matrix (low 3 bits = 0x3 = exactly the params byte 0x03)seg=0x0026450Bfor vertex (low 4 bits = 0xB)seg=0x00264503for texture (same)
Per gmain.s: F3D_Gold ucode is tolerant of low 3 bits in w1 — DMA hardware truncates them and ucode never reads them as flags. But in our run the bytes at the masked address (0x00264500) decode as a DL stream (opcodes E7, BA, B9, BB, FC, FD, F5), not as matrix/vertex/texture data.
Conclusion: the address resolution is NOT the bug. The bug is upstream — something is passing a non-Mtx pointer to gSPMatrix, similar for vtx/texture. Hypothesis: heap aliasing in g_GfxMemPos (the bump allocator in /tmp/ge_decomp/src/game/dyn.c:85) where DL data and matrix data share storage.
| Var | Default | Effect |
|---|---|---|
GE_DEEP_SHADOW |
off | Snapshot DL+matrix+vertex+movemem references into shadow region at submit time. Required for stable boot. |
GE_REMAP_VTX |
off | Redirect vertex addresses through shadow snapshot. Required (without it, NaN vertex positions). |
GE_LOCK_MATRICES |
off | Drop all matrix loads except injected ortho/identity at 0x007F0000-0x007F01FF. Recommended; without it, NaN tri explosion. |
GE_ORTHO_SCALE=N |
15 | Inverse-power-of-2 ortho scale: 1/2^N. Default 1/32768. Try 12-14 for wider scale. |
GE_FORCE_SHADE |
off | Force `geometryMode |
GE_RAW_VTX_COLOR |
off | Force lightCount=0 so compute shader uses raw vertex rgba (bypass lights producing zero RGB). |
GE_DUMP_ALL |
off | Dump every FB to PPM (default: sparse 1/30). |
GE_FORCE_FB_MARKER |
off | Stamp magenta block at colorFb->addressStart AFTER copyNativeToRAM. Validates FB write path. |
GE_INJECT_PERSPECTIVE |
off | Replace ortho with real perspective. Currently produces NaN due to z=0 vertices → div-by-zero. |
GE_PERSP_SCALE_DIV=N, GE_PERSP_FOVY/_NEAR/_FAR |
2048/60/10/30000 | Perspective tuning. |
GE_MV_TZ=N |
0 | Inject translate(0,0,-N) into modelview. For perspective without div/0. |
GE_NO_BD |
off | Gate 0xBD opcode to no-op. Effectively does nothing (0xBD never fires in level 10 boot). |
GE_NO_MTX_FILTER |
off | Disable malformed-matrix detector. |
GE_SHADOW_MATRICES |
off | Restore old behavior of routing matrix addresses through shadow. |
GE_SHADOW_TEXTURES |
off | Re-enable shadow remap for textures (was causing stale-DL reads). |
GE_LOG_WALK |
off | Log every DL command dispatched. |
Best-known visible-content combo:
GE_FORCE_SHADE=1 GE_RAW_VTX_COLOR=1 GE_DEEP_SHADOW=1 GE_REMAP_VTX=1 GE_LOCK_MATRICES=1 GE_DUMP_ALL=1
| Hypothesis | Status | Evidence |
|---|---|---|
| RT64 dispatches 0x01 as G_MTX but it's actually G_DL in F3D_Gold | ❌ ruled out | Switching produced cycle detection + crash. Decomp Makefile uses stock F3D opcodes (G_MTX=1, G_DL=6). |
| F3D_Gold uses paletted Vtx layout like F3D_PD | ❌ ruled out | GE's Vertex struct in bondtypes.h:711 is "Binary compatible with gbi Vtx" — stock libultra 16-byte layout. |
| Opcode 0xBD bitfield-MOVEWORD corrupts DMEM state | ❌ ruled out | Logging shows 0 invocations in level 10 boot+intro. |
| RT64 reads matrix from wrong offset (e.g. w1±64) | ❌ ruled out | Offset scanner found no affine matrix in ±192 bytes around target address. |
| GE_DEEP_SHADOW captures matrix bytes from stale snapshot | ❌ ruled out | Removing shadow remap for matrices: bytes still decode as DL stream, not Mtx. |
| Vertex colors zero kills alpha cascade | ❌ ruled out | Forcing vertex.rgba = (255,255,255,255) did not produce visible content. |
| Combiner produces zero output | ✅ confirmed | Decoded 0xFC121824FF33FFFF = RGB=TEXEL0², ALPHA=(SHADE-0)·TEXEL0+PRIM. With shade=0 + missing texture + primAlpha=0, output is zero. |
| FB write path is broken | ❌ ruled out | GE_FORCE_FB_MARKER produces visible turquoise block in dumps. |
| Triangle rasterization is broken | ❌ ruled out | FORCE_MAGENTA early-return in pixel shader fills lower half of screen with magenta — full-screen triangle rasterization works. |
| Stall heal corrupts game memory at hardcoded 0x803B38B8 | gen.type stamping at that address sometimes makes things worse; address might not be stable. |
Symptom: bossMainloop receives 1 DONE per run sometimes, 152 DONEs other times. Same env vars, same code.
What we know (UPDATED 2026-05-05 via orchestrator dogfood):
__scTaskComplete(/tmp/ge_decomp/src/sched.c:406-433) forwardst->msgto clientQ.cur_msg = 0x803B38B8in 100% of observed GFX submissions — the hardcoded fallback IS the right address. Agent A's earlier hypothesis (that real address is stack-local&localGfxDoneMsg) was wrong.learn_count = 0is misleading: the assign happens, but the "learned" log only fires when value CHANGES; since initial == observed, no log.sp_completefires ~26 times per 60s run (audio + gfx);dp_completefires ~3 times (gfx only).- bossMainloop receives 20× type=1 (RETRACE), 0× type=2 (DONE) — sp/dp complete fire correctly but DONEs don't reach clientQ.
Real cause: the recompiled game's __scHandleSP → __scTaskComplete → osSendMesg(clientQ, doneMsg) chain doesn't successfully forward DONEs even when sp/dp completion signals arrive. This is a recompilation-level bug, not a heal-message-corruption bug.
Hypothesis 1 (FALSIFIED 2026-05-05): Remove hardcoded fallback g_known_done_msg_ptr = 0 → made it WORSE (dones=[1,3,1,0,2] vs baseline [5,3,2,2,1]). Reverted.
Hypothesis 2 (PARTIAL 2026-05-05): Bypass game's __scTaskComplete entirely — inject DONE msg directly into clientQ at 0x80141C90 with DONE_MSG=0x803B38B8 after each gfx task. Result: 2/8 wins (25%) vs 1/5 baseline (20%). Marginal improvement, not deterministic. The injection succeeds (osSendMesg returns 0) but bossMainloop's NOBLOCK drain at workbench_theboy.c:616 eats it before pendingGfx > 0. Code: lib/N64ModernRuntime/ultramodern/src/events.cpp:368+. Disable via GE_NO_INJECT_DONE=1.
Try next (specialist-level):
- Game-side
__scHandleSPand__scTaskCompleterecompiled versions need inspection — these are inRecompiledFuncs/(specifically the recompiled sched.c) OS_EVENT_SPregisters msg=0x29Bwhich is the SP-DONE message ID (not a pointer); verify recompiled__scHandleSPcorrectly handles this case- Trace why DONE msgs aren't being osSendMesg'd to clientQ from the recompiled binary
- Possible: spawn a continuous "DONE heartbeat" thread that injects every 16ms (VI rate), but won't help if bossMainloop never reaches the pendingGfx>0 state
This requires N64Recomp / N64 OS scheduler expertise.
Symptom: When game advances frames (DONEs > 100), the rainbow-test pixel shader (which colors each fragment by screen position) produces ZERO visible output. But FORCE_MAGENTA does work in earlier-stage runs.
Hypothesis: Stall-recovered frames render only fillrects (boot transitions), not scene triangles. The actual game's bondviewMain → bgLevelRender → model_render chain hasn't been reached yet — game is still in some prelevel state.
Try next:
- Force game to skip to a level (look at
/tmp/ge_decomp/src/main.cfor boot fast-path arguments) - Confirm rainbow test in a config that DOES produce scene tris (need to find that config)
- Add per-tri log of
renderFlagRect(rp.flags)to count rect-vs-tri pixel shader invocations
See "The 'w1 low-bits' mystery" section above.
Try next: Read n64decomp/007 rsp/graphics/gmain.s more carefully. Find the EXACT instruction that consumes w1 in matrix/vertex/texture handlers. Verify low-bits truly are masked vs extracted as flags.
Symptom: Combiner is RGB=TEXEL0², ALPHA=(SHADE-0)·TEXEL0+PRIM. With shade=0 + texture-not-loaded + primAlpha=0, output alpha=0 → coverage discard.
Try next:
- Find where game emits
gSPSetGeometryMode(G_SHADE)and verify RT64 catches it - Find where game emits
gDPSetPrimColor(...)with non-zero alpha - If neither fires in current run, the issue is upstream DL processing
Symptom: loadBlock src=0x0060FFFB (formerly), or src=0x00264500 (post-mask-fix) — but bytes there are DL stream, not texture data.
Same root cause as matrix mystery — heap aliasing in g_GfxMemPos.
Symptom: GE_INJECT_PERSPECTIVE=1 produces NaN tris because z=0 vertices → w_out=0 → division by zero.
Try next: GE_MV_TZ=2000 injects translate(0,0,-2000) so all vertices have non-zero w. Then tune scale + near/far for sane NDC.
docs/screenshots/scene-magenta-l.png— magenta L-shape + white rectangle (multi-color, possibly real boot transition)docs/screenshots/scene-magenta-full.png— uniform magenta (clear/transition)docs/screenshots/force-magenta-validation.png—FORCE_MAGENTAshader bypass (rasterizer proof)docs/screenshots/fb-marker-validation.png— turquoise marker (FB write path proof, color is byte-swapped magenta)
| Component | File |
|---|---|
| Stall heal | lib/N64ModernRuntime/ultramodern/src/events.cpp:568-700 |
| RDP texture handlers | lib/rt64/src/hle/rt64_rdp.cpp (setTile, loadBlock, loadTLUT) |
| RSP matrix/vertex/texture image | lib/rt64/src/hle/rt64_rsp.cpp |
| F3D_Gold opcode setup | lib/rt64/src/gbi/rt64_gbi_f3dgolden.cpp |
| Pixel shader | lib/rt64/src/shaders/RasterPS.hlsl |
| Compute shader (vertex transform) | lib/rt64/src/shaders/RSPProcessCS.hlsl |
| Vertex shader | lib/rt64/src/shaders/RasterVS.hlsl |
| Render context (matrix/vp/mv injection) | src/main/rt64_render_context.cpp |
| FB dump path | src/main/rt64_render_context.cpp::update_screen |
GE_FORCE_SHADE=1 GE_RAW_VTX_COLOR=1 \
GE_DEEP_SHADOW=1 GE_REMAP_VTX=1 GE_LOCK_MATRICES=1 \
GE_DUMP_ALL=1 \
./build/GoldenRecomp.app/Contents/MacOS/GoldenRecomp -level_10 &
APP_PID=$!; sleep 90; kill $APP_PID
# Find FBs with content
for f in /tmp/ge_fb_*.ppm; do
NZ=$(tail -c +20 "$f" | od -An -tx1 | tr -d ' \n' | tr -d '0' | wc -c)
[ "$NZ" -gt 100 ] && echo "$f: $NZ"
done
# Convert to PNG (macOS native tool)
sips -s format png /tmp/ge_fb_NNNN.ppm --out frame.pngExpect ~40-50% of runs to produce a frame with 250k+ non-zero pixels. The rest stall in the boot loop and produce only black FBs.
Pipeline reverse-engineering and instrumentation done with assistance from Claude (Anthropic) — see commit history for full attribution.