Nexus runtime capture contract

This document describes the boundary between decoded Saturn source files and runtime evidence. MENU.BPK, TITLE.BIN/TITLE.CG, STABG.BIN, FACE.BIN, DM.BIN, LEV##.DGN, SLEV##.BIN, SNDLEV##.SAL/.MAP and SDDRVS.TSK may be parsed and hash-verified from user-supplied data, but those facts do not identify the Saturn VDP1/VDP2 destination, CLUT owner, command order, runtime selector, or SH-2 consumer by themselves.

Verified external VDP1 sequence witness

The authenticated operator capture /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-long-20260809f/runtime-vdp12.raw contains the 900 frames declared by its external manifest. The bounded VDP1 sequence verifier reports 390 complete command chains and 510 valid idle/END frames. With the real Nexus corpus at /Volumes/Extern-disk/FirestaffUserData/data/nexus, the DGN sequence join at frame 899 reports 204/204 source matches, 204/204 palette matches, and 175 face-owner matches.

Reproduction:

FIRESTAFF_NEXUS_RUNTIME_CAPTURE=/Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-long-20260809f/runtime-vdp12.raw \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_FRAMES=900 \
bash tests/test_nexus_v1_vdp1_command_sequence.sh

FIRESTAFF_NEXUS_DGN_SEQUENCE_CAPTURE=/Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-long-20260809f/runtime-vdp12.raw \
FIRESTAFF_NEXUS_DGN_SEQUENCE_DATA_DIR=/Volumes/Extern-disk/FirestaffUserData/data/nexus \
FIRESTAFF_NEXUS_DGN_SEQUENCE_CAPTURE_FRAMES=900 \
FIRESTAFF_NEXUS_DGN_SEQUENCE_FRAME=899 \
bash tests/test_nexus_v1_vdp1_dgn_command_sequence_join.sh

These are bounded capture/source-join witnesses only. They do not identify the retail scene owner or authorize the production renderer; transform, culling, VDP1/VDP2 consumer ownership and production admission remain closed.

Required producer

The capture launchers under probes/nexus/ are operator-only. A capture-capable instrumented Saturn emulator must create the binary artifact itself. Firestaff does not copy BIOS, disc, source, or guessed trace bytes into an artifact.

The launchers now check that the selected Mednafen binary advertises the corresponding FIRESTAFF_NEXUS_*_OUTPUT hook before writing a manifest or starting the emulator. Stock Mednafen is therefore rejected early (exit 78): its normal video snapshots or movie recording are useful visual evidence, but they do not prove VDP register/VRAM ownership or SLEV/SAL event dispatch. The raw Saturn launcher uses a non-quiet string scan for this check because set -o pipefail makes strings | grep -q report a false failure when grep closes the pipe after the first match.

The BIOS/media region must also be compatible. The supplied English merged disc reports SGAREA=J; the external capture disk now contains the matching Japanese BIOS 1.01 (SHA-256 dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac). The European E-BIOS remains a separate, valid pairing for the French SGAREA=E data-only disc. Stock Mednafen or a mismatched pair is still rejected; any resulting corrupted desktop frame is not a valid startup/menu witness and must not be admitted to Firestaff.

The external Mednafen 1.32.1 build also advertises the NXSLSC01 SLEV/SAL producer. Its hook records only authenticated SH-2 WorkRAM writes, both SH-2 program counters, and an incremental payload hash. The payload is deliberately opaque: this producer does not establish the SLEV selector, SAL codec, MAP-row ownership, or host playback. A real capture must still be imported and joined to the source-owned runtime corridor before any event or audio behavior opens.

For startup/input experiments the launcher can request an operator-owned controller window with --press-start-frame N --press-start-length N and choose the active-low Saturn button mask with --press-button-mask. The default 0x10 is START, 0x20 is A, and 0x30 tests both. The external Mednafen SMPC hook drives that real gamepad bit window and releases it on the following frame. This is input provenance only: it does not write SH-2 state, VDP memory, host pixels, or a guessed menu. A resulting menu/HUD/viewport claim still requires the corresponding source identity and VDP1/VDP2 consumer artifact.

An input-request log alone is not an input receipt: it can record a requested mask after the emulator has already sampled the gamepad. The producer applies its mask before the original IODevice::UpdateInput pass, and a valid capture must additionally show the changed SMPC output-register values at the same frame. A V2 receipt records the reading CPU plus its ID/IF pipeline PCs and opcodes. The analyzer only accepts an instruction identity when the producer exposes a nonzero ID PC and its captured opcode exactly matches hash-verified retail DM.BIN; otherwise it reports pipeline_unavailable. Neither case assigns a menu action, game state, or host-rendering admission.

A fresh matched cold-start pair on TRV2 tested frames 0–279 with the Japanese retail BIOS/disc. The input run requested START (0x10) at frames 140–199 and A (0x20) at 210–239; the control held the injected pad mask at zero over both windows. Both runs validated as complete 280-frame captures, and their entire raw VDP1/VDP2 streams were byte-identical with SHA-256 ea2eb96dc56ce9505d67062b2a5f98141d2a456d413c74c19997c04346e27af3. The ten session-bound PPM samples (frames 130, 139, 140, 159, 199, 209, 210, 239, 240, and 279) were also identical between runs. SMPC register 0x10, read at RPC 0x060103e6, returned 0x10 during the scheduled input run and 0x00 in the control; the observation does not assign a separate meaning to either button mask. This confirms a live input-read path, but no input-correlated VDP presentation change in this cold-start interval, so the title/menu route stays blocked. The private manifests and traces remain outside the repository at /home/trv2/work/firestaff-nexus-capture-20260917/runs/nexus-startup-input-f140-20260928b/ and /home/trv2/work/firestaff-nexus-capture-20260917/runs/nexus-startup-input-control-f140-20260928a/.

The reproducible producer is the version-locked mednafen_1.32.1_nexus_smpc_read_trace.patch, applied by build_mednafen_nexus_saturn_capture.sh. Set FIRESTAFF_NEXUS_TRACE_SMPC_READS to an operator-owned output file; optional FIRESTAFF_NEXUS_TRACE_SMPC_READ_FRAME_MIN, FIRESTAFF_NEXUS_TRACE_SMPC_READ_FRAME_MAX, and FIRESTAFF_NEXUS_TRACE_SMPC_READ_LIMIT bound it. V2 records emulation frame, reading CPU, SMPC output register and value, plus the SH-2 ID/IF pipeline PCs and opcodes. It never copies game data, extracts media, or becomes a Firestaff runtime dependency. The raw-capture launcher forwards this optional trace path to the external emulator and records its SHA-256 in the capture manifest when the trace is produced.

scripts/analyze_nexus_smpc_read_trace.py is the corresponding fail-closed consumer receipt. It reads DM.BIN directly from the supplied CUE in memory, requires its Japanese retail SHA-256 3bbca125e0bfb486897e4926541e7c31adbff010d01a9b0c736637f432aad124, and rejects every available static master-SH-2 V2 ID PC outside that member’s load range or whose captured ID opcode differs from the corresponding retail word. For code relocated into WorkRAMH, --ram-snapshot requires the exact 1 MiB snapshot for every traced frame and compares the ID opcode to that runtime word instead; malformed, absent, duplicate, or wrong-size snapshots fail. When the normal producer has no ID-PC value, it reports pipeline_unavailable; V1 receipts remain source-PC snapshots. Neither format alone identifies an instruction’s input semantics or controller action. Its deliberately broad L/R/X/Up/C/Left/L/Right sequence is byte-identical to the same no-input control in all eight captured VDP1/VDP2 regions for the 80-frame window, so it admits neither a debug feature nor a menu/gameplay transition.

A matched JP retail control/START pair at frames 1000–1079 exercised a 20-frame 0x10 START pulse at frames 1020–1039. The matched SMPC read traces show address 0x10 returning 0x00 in the control, 0x10 during the pulse, and 0x00 again at frame 1040. The raw VDP capture hash and all three rendered frame hashes (1000, 1020, 1079) are identical between the pair. This confirms that the external input hook changes the observed SMPC return byte in this window, but not that the game interprets it as a menu action: the source consumer receipt rejects frame 1020 because RPC 0x06014514 has captured PIF 0x2310 while retail DM.BIN contains 0x0009 there, and both ID/IF pipeline PCs are zero. The sampled images remain an intro-logo sequence with no input-correlated presentation change; semantic admission stays blocked.

The shared 80-frame raw SHA-256 is c1996a2b2fab4599d275945fd98194f894941d541e992593d98ef31b6cb246db; the START-run input-event trace is 37f69607da48713cec5eeab996a5efdfbc58f5686b11c34db52638c670e37fca and its bounded SMPC-read trace is bdb7b1568de800c44122e6f60a73557c66422e93d04b761c12eedbc3066e0f5b. The matching rendered-frame hashes are ef488068746129d499325a1ff8f301f7768d36a5af860e41903332276c995b1d (1000), b21d75a28fdbe57d7a22a24aff2a4270671f069d19c3b1780fbd08963c5503a9 (1020), and f316d3c4410475102b15b185ed24b35f22e63668059631e7622bdd96848df4e8 (1079).

A second matched JP retail pair at frames 40000–40079 applies START at 40020–40039. Its read trace records SMPC address 0x10 returning 0x10 during the pulse and 0x00 at frame 40040. Despite that observed register value, the complete raw VDP1/VDP2 capture is byte-identical to the no-input control, as are rendered frames 40000, 40020, and 40079. The consumer analyzer again rejects frame 40020 (RPC=0x06014514, captured PIF=0x2310, retail word 0x0009, PC_ID=PC_IF=0), so this is negative transition evidence only; it does not establish what code owns or interprets the input.

The shared raw SHA-256 is 627a1055274abbd164bc1bab7a9253ae2760e0a1a039ef0c587748ff10083681; the START-run input-event trace is 5884585948e08780a276ea709ccfd611c1c5f3b2a71485fed532536bc061f799 and its SMPC-read trace is 1ae8be3f310a03e35ac0886f70fd5b7d779a4d5442cf46d456f241d910b64c52. The matching rendered-frame hashes are 99fd3ff47bf58af36c021be7695edb78498805df5346a80a70bd70f72e1870d0 (40000), 206ac085bc5e2e0f56f2f621c28abd81fdd5a4fa451f57016d1de9ac542ad50d (40020), and fd2ecae82077ad19818ad4de1e9d86278d71a0519667a05621651cfab2ea5eb5 (40079).

A further matched JP pair at frames 40000–40079 schedules the nine remaining digital-pad masks not covered by the earlier START/A/B/C trials: Up/Down/Left/ Right (0x0001, 0x0002, 0x0004, 0x0008) and X/Y/Z/L/R (0x0100, 0x0200, 0x0400, 0x0800, 0x1000), each for two frames inside the captured window. The external input-event receipt d41b8de558e270255d0ee5e7eb995f3ddc4349c2bf2b2084c26ad35bba197fb7 records every scheduled mask. The complete 80-frame raw VDP1/VDP2 capture is byte-identical to the no-input control; both have SHA-256 627a1055274abbd164bc1bab7a9253ae2760e0a1a039ef0c587748ff10083681. Rendered frames 40000 and 40079 also compare byte-identically. This closes only the tested masks/window: the event trace does not prove the Saturn game consumed those signals, and no menu or gameplay meaning is inferred.

For a diagnostic pass, set FIRESTAFF_NEXUS_TRACE_SMPC_PIPELINE_DEBUG=1 when invoking the raw-capture launcher. It selects Mednafen’s debugger CPU loop in the external capture build so PC_ID and PC_IF are populated. This is not an admission override: the analyzer still requires the captured pipeline opcode to match the authenticated DM.BIN word. The first JP smoke receipt populated nonzero PCs but failed that match, so it remains rejected rather than being reinterpreted as an input consumer.

A follow-up JP capture at frames 1000–1079 enabled the version-locked WorkRAMH snapshot hook for frame 1020, during the same 20-frame START pulse. The 1 MiB snapshot is hash-bound in the run manifest, and analyze_nexus_smpc_read_trace.py --frame-min 1020 --frame-max 1020 --ram-snapshot ... --expect-read 1020:0x10:0x10 validates all 21 master-SH-2 SMPC reads against their PC_ID words in that same-session runtime snapshot. The previously captured 0x0601456c fetch is 0xd223 in WorkRAMH, confirming the sampled pipeline bytes even though the direct retail DM.BIN address does not match. The analyzer reports retirement_unavailable and input_consumer_semantics=unbound: this establishes the live low-level SMPC read path, not a START-to-menu/gameplay interpretation. Its raw capture SHA is still c1996a2b2fab4599d275945fd98194f894941d541e992593d98ef31b6cb246db, and its rendered frames 1000, 1020, and 1079 retain the previously recorded hashes, so the input pulse still has no observed presentation effect in this window. The snapshot SHA-256 is 90f4db8b94246570e3c6902e451b1f01e4ea9eba81620ee9a54601b8cfe05fee and the debug SMPC-read trace SHA-256 is 6af9979cb99776e705b65a2330ea4e18ecac04fe4810010d42745c2540a2b6e0.

When the input must be observed inside the raw capture, pass --require-input-window. The launcher then rejects a plan unless the complete button interval lies between skip_frames and skip_frames + frame_limit; this prevents a pre-input witness from being mistaken for a post-input menu capture.

For an external SH-2 WorkRAMH-consumer diagnostic, the version-locked mednafen_1.32.1_nexus_sh2_ram_read_trace.patch records the frame, address, width, returned value, both SH-2 PCs, master-SH-2 R0–R15, and PR. FIRESTAFF_NEXUS_TRACE_SH2_RAM_READS enables it; address, PC, row-limit, and inclusive frame-window filters keep a receipt small. This is a producer-only inspection aid: register values identify an observed copy/normalization path, but do not assign controller semantics, mutate media, or add a Firestaff runtime dependency.

scripts/analyze_nexus_sh2_ram_read_trace.py validates a one-frame V3 receipt against the observed JP chain: linked buffer reads, SMPC-byte copy, nibble-normalization and the post-normalization table reader. It requires the complete live register bank on every row and rejects a slave-SH-2 or multi-frame receipt. Its only positive result is workram_input_consumer_chain=verified; it always keeps semantic admission blocked.

The same producer hook also accepts an optional inclusive FIRESTAFF_NEXUS_TRACE_SH2_RAM_READ_PC_MIN/_PC_MAX filter. This bounds a follow-up scan to a selected SH-2 code corridor while retaining master/slave PC and register ownership. The analysis script accepts --pc-min and --pc-max (half-open range) and defaults to master reads in the observed retail TM.BIN code corridor (0x06014000..0x060147ff) before validating its known five-read chain. A different range can be selected when following a captured transfer into another retail code owner. Reads outside the selected corridor remain raw observations and cannot be promoted to menu semantics by this filter.

scripts/compare_nexus_render_frames.py compares specified external P6 PPM render receipts byte-for-byte. It rejects missing, malformed, or non-identical frames. An identical sequence proves only that the injected input has no observed renderer effect in that sampled interval; its successful result keeps input/menu semantics and native admission blocked.

For a multi-step Saturn startup route, the operator launcher also forwards the optional environment variable FIRESTAFF_NEXUS_TRACE_PRESS_SEQUENCE. Its format is a comma-separated list of frame:length:mask entries, for example 1250:60:0x10,1350:60:0x10,1450:60:0x20. The sequence is recorded in the manifest and is applied to the real SMPC input stream in one emulator session; the masks are the Saturn pad bits exposed by the instrumented SMPC hook; the hook clears those bits during each interval because the Saturn pad bus is active-low. They follow Mednafen 1.32.1’s standard digital-pad layout: Up=0x1, Down=0x2, Left=0x4, Right=0x8, Start=0x10, A=0x20, B=0x40, C=0x80, X=0x100, Y=0x200, Z=0x400, L=0x800, and R=0x1000. The 13-bit path therefore includes the right-shoulder bit (0x1000). The documented Nexus debug sequence L, R, X, Up, C, Left, L, Right is 0x800,0x1000,0x100,0x1,0x80,0x4,0x800,0x8; do not substitute the adjacent B, Y, or shoulder bits. This sequence is an emulator investigation aid and input-provenance record only. The resulting frame still requires an exact MENU.BPK/FONT256 or other source-consumer join before startup or menu presentation can open.

For long operator-only scans, the capture patch also accepts the inherited FIRESTAFF_NEXUS_NO_WAITING=1 environment flag. It requests Mednafen’s no-wait scheduler path only; it does not alter the Saturn input, VDP or SCSP capture payload.

For targeted VDP2 investigation, the external producer accepts FIRESTAFF_NEXUS_TRACE_VDP2_WRITER_FRAME_MIN and FIRESTAFF_NEXUS_TRACE_VDP2_WRITER_FRAME_MAX together with FIRESTAFF_NEXUS_TRACE_VDP2_WRITER_REGS. These bounds use the actual SMPC emulation frame number, rather than the optional raw-capture frame counter. They prevent reset and boot writes from exhausting the register-trace budget before a later input or menu sequence is reached. This is an external development witness only: it never changes Firestaff’s runtime dependencies or admits a render path without an authenticated source-consumer join. Each writer row carries R0–R15 and, as of capture patch V14, pr= with the SH-2 procedure register. Retain pr when tracing the title corridor: it is the return-address witness needed to connect the disassembled byte-copy loop to its real caller. It is provenance only, not a rendering admission. scripts/verify_nexus_title_nbg0_copy_callchain.py --cue <retail.cue> --writer-registers <receipt> scans every aligned SH-2 word of the hash-verified DM.BIN in memory for direct BSR call edges. It requires the measured 0x0602312c loop to retain the real mov.b @r5+,r1 / mov.b r1,@r4 instructions and requires one complete, internally consistent live PR value across the captured copy rows. It separately reports the direct 0x06022772 BSR to 0x060230c0; that outer return address is not imposed on a later helper loop because SH-2 PR is live state and can be replaced by nested work. A missing or inconsistent PR stays blocked; it is never filled in from static analysis. --static-only reports the static result separately and never promotes it to an observed call.

The paired VDP2 write stream accepts the corresponding FIRESTAFF_NEXUS_TRACE_VDP2_WRITE_FRAME_MIN and FIRESTAFF_NEXUS_TRACE_VDP2_WRITE_FRAME_MAX bounds. Set both filter pairs when correlating a writer-register receipt with its VDP2 destination write; otherwise early boot writes can still consume the write-stream budget. The write stream records transport and source-register evidence only and remains semantically blocked until its bytes are joined to authenticated game media.

The raw witness can be inspected with scripts/analyze_nexus_saturn_runtime_capture.py. It reports SHA-256 values for each captured VDP1/VDP2 region and can require a region to differ between adjacent frames. This proves observed runtime change in the producer without identifying a menu, HUD, viewport, CLUT, or consumer; the tool always keeps semantic admission blocked.

The external short J-region run /Volumes/Extern-disk/nexus-capture-20260813/run-attachment-j-short-snapshot-20260813/ also demonstrates the snapshot profile. Its authentic 60-frame raw witness has SHA-256 b7d09f7103f21202392533f454acc8fe9839b790e7e30d03d85ff6e615b62d13, and memory.snapshot contains four V1 records (frames 0, 20, 40 and 59), each covering the 2 MiB SH-2 WorkRAM range 0x06000000..0x061fffff; the artifact SHA-256 is 837e15c76b206c59ab2a4bf27bdd87322067619223d51e4a8055ebdb99e1c8b9. The snapshot-capable binary did not emit the separate RAM-write trace in this run. The external RAM-trace-capable binary is retained as a separate profile; the launcher must not combine or synthesize outputs from the two binaries. The snapshot is raw memory evidence only and does not bind LEV01, a save record, a pose, or a presentation consumer.

Authenticated VDP1 DGN material join

The external European run /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-window-20260809/runtime-vdp12.raw has SHA-256 685ae90896cf72deeac0a98de5fb1fb4f0bd90e89ace73c5e7631daf44b8faa7. In frame 0, the real command at 0x0e180 is a VDP1 colour-mode-1 draw with COLR=0x32a4, source offset 0x4c580, and 1,952 source bytes. The word-swapped source is an exact match for the hash-verified LEV00.DGN Structure2 descriptor 72 (64x61). The captured 16-word VDP1 CLUT at ((COLR & ~3) << 2) = 0xca90 also exactly matches that descriptor’s 16-entry big-endian BGR555 palette after the capture’s word order is restored.

This is the first source-plus-CLUT material receipt for the live Nexus viewport. Reproduce it with scripts/analyze_nexus_vdp1_dgn_material_join.py. The same receipt now also matches the canonical raw fill_selector face rows in LEV00.DGN Structure3: Structure2=72 is owned by entry 15 face 1. This is source-level face/mesh provenance, not proof of runtime face selection, command ordering, transform, culling, or complete scene assembly. The script therefore reports semantic_admission=blocked and the production renderer remains fail-closed.

The material probe accepts both the newer VDP1+VDP2 frame container and the earlier VDP1-only witness. For the retained four-frame V1 capture, reproduce the join with: python3 scripts/analyze_nexus_vdp1_dgn_material_join.py \ /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-window-20260809/runtime-vdp12.raw \ --data-dir <local-home>/.firestaff/data/nexus --frame 0 --capture-frames 4 \ --command-offset 0xe180.

The same legacy witness also passes the C runtime handoff when linked against the normal firestaff_nexus target: the first resolved mode-1 command reports source_join_verified=1, palette_join_verified=1, structure3_face_owner_join_verified=1, and renderer_permitted=1. This is one source-bound capture command only; it does not promote the complete DGN scene, camera transform, command-list ownership, menu/HUD identity, or VDP2 composition.

The same frame also contains four additional type-2 draws. Their exact word-swapped sources and CLUTs bind to LEV00.DGN Structure2=60, 64, 68 and 71, plus Structure2=36 for the fifth draw. The observed VDP1 quadrilaterals are reported as eight signed Saturn coordinates per command. Their source materials now also have canonical LEV00.DGN Structure3 raw selector owners: 60 → entries 11/13 faces 5/9, 64 → faces 6/10, 68 → faces 7/11, 71 → entry 14 face 0, and 36 → entries 8/9 faces 0..6. This closes the source-level material-to-face-selector join and proves hardware destination coordinates for the captured commands, but not runtime face selection, command ordering, transform/culling, or complete scene assembly.

The code now contains a separate capture-only VDP1 mode-1 compositor in nexus_v1_vdp1_capture_compositor.c. It accepts one command only when its texture span and 32-byte palette state exactly match the canonical DGN image and palette after Saturn word-order restoration, and when the caller supplies an authenticated original-capture receipt plus the display origin. It applies the documented high-nibble-first texel order and transparent/end-code rules. This is a bounded replay consumer, not permission to use the ordinary DGN mesh route: complete command-list order, local/system clipping, dynamic face selection and VDP2 composition remain separate gates.

The capture compositor is atomic at both levels: a single command that is fully off-screen or otherwise produces no pixel cannot publish its palette or partial framebuffer, and a failed first VDP1 pass in the VDP2-over-VDP1 composition restores the complete viewport. The retail DGN face/material receipt likewise keeps no_draw_only=1 and blocks_real_dgn_mesh_render=1 on its successful source-bound state; these flags describe a validated source boundary and do not authorize normal rendering.

An input-free pre-Start run at runtime frame 10000 independently captured active VDP2 NBG1 bitmap mode (BGON=0x0002, CHCTLA=0x1211) with stable VRAM and CRAM. Its decoded MENU.BPK, FONT256.S2D, TITLE, and STABG source join is negative, so it is hardware-layer evidence only and is not called a menu capture.

A time-corrected authenticated E-BIOS/French session at /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-window-20260809-f/runtime-vdp12.raw captured 512 consecutive frames with skip_frames=10000, so raw frame 500 corresponds to runtime frame 10500, where the real START input is injected. Its SHA-256 is decf7dbd3a327cb5623fe7c12b4820f5037dc0e977c50ec3aac38645fc353d30. All 512 frames have VDP1 activity. The first frame retains the active NBG1 bitmap tuple; after the input window VDP2 changes to other register states, but no MENU.BPK/FONT256/TITLE/STABG exact source join is found. This is positive input/transition evidence, not menu, HUD, or text-consumer admission, so semantic admission remains blocked.

A parallel 512-frame run with Saturn A (0x20) instead of START (0x10) produced the same raw SHA-256 and the same unbound VDP2 state. The button-mask change therefore did not produce a verifiable menu window; this is not a reason to open the host text consumer or to infer a menu route.

The validator also accepts --require-vdp1-activity for a V2 witness. This requires a non-idle PTMR/EDSR state and a nonzero VDP1 VRAM or framebuffer payload. It is only an active-engine observation. It does not prove which MENU.BPK, DGN, ITEM, HUD or viewport record owns the bytes.

The first external European gameplay witness is retained at /Volumes/Extern-disk/nexus-saturn-capture/run-french-gameplay-skip18000-2/runtime-vdp12.raw. It has SHA-256 549e03856163899381d4b6a03f65ef989fadbeccb338579eb87876e00f30e362, with two non-idle frames at PTMR=02, EDSR=03, COPR=00000c. The VDP1 command window is stable across both frames: system clip, local coordinate, one type-2 textured command (PMOD=0x0028, SRCa=0xc7c0, SIZE=0x28b4) and END. The draw-buffer selector and VDP1 VRAM/framebuffer payload change between frames, which proves a real gameplay redraw. The command source is not yet joined to an authenticated DGN/ITEM/MNS source span, so no menu, HUD, CLUT, source-asset or production viewport binding is admitted from it yet.

An additional negative startup-input run is retained outside the repository at /Volumes/Extern-disk/nexus-saturn-capture/run-french-start100x60-skip6000-next/runtime-vdp12.raw. It uses the authenticated E-BIOS/French media, drives START+A for frames 100–159, then captures eight active VDP1 frames beginning at runtime frame 6000 (ce800662…). VDP2 registers, VRAM and CRAM remain byte-stable and the observed framebuffer is still intro/dungeon imagery. This narrows neither the menu transition nor its consumer; semantic admission remains blocked.

An operator-only VDP1 write trace was also run against the same European image with an SH-2 PC attached to each VRAM write. The first bounded source probe was dominated by framebuffer/colour fills at PC0=0x06026260/0x06026270; this is not a DGN texture-owner join. scripts/analyze_nexus_vdp1_write_trace.py records that negative result and remains fail-closed. The reproducible Mednafen instrumentation is kept in scripts/mednafen_1.32.1_nexus_saturn_vdp1_pc_trace.patch; it is diagnostic only and does not authorize production drawing.

A second source-span-specific run captured 4,601 writes from runtime PC 0x06013098 into the beginning of the type-2 command’s source window 0x47c00..0x49ffe; the same bounded trace separately identifies the colour/ framebuffer writers at 0x06026260, 0x06026270, 0x060262c4 and 0x060262d4. The enhanced analyzer can require both a PC and an address range. This is a positive runtime writer corridor, not yet a DM.BIN/TM.BIN source join or a proof that the bytes are DMV, TITLE, MENU, HUD or DGN material, so semantic admission remains blocked.

The authenticated eight-frame E-BIOS/French startup run now also joins the bounded command window to this writer corridor. COPR=0x00000c exposes four records at 0x00000, 0x00020, 0x00040 and 0x00060: system records 0x09/0x0a, one type-2 bitmap command, and END. In frame 7 the draw command is PMOD=0x0028, SRCa=0x8f80, SIZE=0x28b4; the Saturn encoding maps SRCa to VDP1 byte offset 0x47c00, the same destination selected by the runtime writer PC 0x06013098. The captured source span is 0x47c00..0x4fe00 (33,280 bytes). This is a real command-to-VRAM join, but the source span is still not joined to a decoded MENU/DGN/ITEM record or a VDP2 CLUT/tile owner, so production menu/HUD/viewport drawing remains gated.

The reproducible scripts/analyze_nexus_vdp1_source_join.py comparator now checks that source span against every bounded TEXT surface in the real MNS corpus. The two-frame gameplay witness has one 16bpp draw command and 815 retail MNS surfaces in the supplied data root; neither native-byte nor word-swapped comparison produces an exact owner. This is a negative source join, not permission to use a host texture: the source may still belong to a relocated/decompressed runtime buffer or another retail asset class, and the CLUT/placement/command-order gates remain open.

The same comparator now also checks the raw image spans of all hash-verified LEV00.DGN through LEV15.DGN Structure2 descriptors. The European frame-1 witness contains 1,678 bounded Structure2 candidates; its 33,280-byte 16bpp source span has no native or word-swapped exact match. Frame 7 of the separate eight-frame witness has the same negative result. These are source-byte joins only: an exact match would still need the original command order, CLUT and placement relation before any DGN material could be admitted.

The longer J-BIOS/English-media startup-input witness run-codex-start-menu-long-20260810/runtime-vdp12.raw adds 1,800 frames with the authenticated Start window at capture frames 300–359. Frame 300 has a complete VDP1 chain (User/System Clip → Local Coordinate → type-2 draw → END) whose draw consumes 0x63e00..0x6bf80 (33,280 bytes, 16bpp). The command-sequence, material-resolver and capture-compositor tests accept that hardware framing, while the source comparator finds no exact native or word-swapped owner among 126 verified retail files, MNS surfaces or DGN Structure2 spans. It is therefore stronger startup-input transport evidence, not a startup/menu asset identity or production-renderer admission.

The isolated rerun on 2026-08-11 keeps the same J-BIOS/English-media pair and captures 361 frames with SGAREA=J and Region=0x1. Its bounded VDP1 write trace observes 18,318 writes in 0x63e00..0x6bf80; 18,277 carry PC=0x06029e84. This is authenticated runtime-writer evidence for the destination corridor, not proof of the source asset, CLUT owner or menu identity. The source-join and production gates therefore remain closed.

The comparator also scans the extracted corpus as whole files, but admits a file to this scan only when its SHA-256 matches the selected retail manifest or the authenticated European startup-asset identity. Frames 1 and 7 each scan 126 verified files (five variant identities are rejected rather than silently treated as canonical); neither native nor word-swapped bytes contain the captured source span. This closes another false-positive route without claiming that relocated or decompressed runtime data has been recovered.

A second authenticated E-BIOS/French run with START+A held at frames 18000–18119 and capture beginning at frame 18500 is retained outside the repository at /Volumes/Extern-disk/nexus-saturn-capture/run-french-start18000x120-skip18500-live2/runtime-vdp12.raw. Its SHA-256 is d648bd88…, and the eight-frame validator reports active VDP1 draws in every frame. Frames 0–1 retain a 16bpp type-2 source at 0x63e00 whose 33280-byte span hashes 5cca9793…; frames 3–7 retain the same command shape and span address but hash 58afb9c9…. A bounded exact-byte scan of the authenticated Nexus data directory finds no owning file for either span. VDP2 registers, VRAM and CRAM remain byte-stable with the earlier witness, so this run adds VDP1 source-span evidence only; it does not authorize a DGN, MENU, ITEM, HUD or viewport interpretation.

The same producer run also emitted a bounded VDP2 write witness. The trace contains 15,365 register writes in 0x180000..0x18011e, 183,355 VRAM writes in 0x000000..0x0743fe, and 1,280 CRAM writes in 0x100000..0x1007fe. Nonzero SH-2 PCs are now captured with the writes; the dominant VRAM writers are 0x06011924 (65,538), 0x060118fc (40,448) and 0x06002fc4 (22,914), and the dominant register writer is 0x0600231c (14,400). This binds the VDP2 activity to executing Saturn code, but not yet to a decoded tilemap, CLUT bank, menu asset or final layer placement; semantic admission remains blocked.

Session-bound SCSP/SLEV corridor

The short clean J-region diagnostic run at /Volumes/Extern-disk/nexus-capture-20260813/run-attachment-j-short-20260813/ validated 60 raw frames and 16 active VDP1 observations, but produced no SH-2 RAM-write or memory-snapshot artifact despite the launcher receiving those output paths. This is a capture-tooling limitation: the witness remains transport-only and cannot identify a LEV01 pose, save write, or runtime consumer.

A separate operator-only J-BIOS/English-media run is retained outside the repository at /Volumes/Extern-disk/nexus-saturn-capture/run-codex-scsp-menu-bound-20260810/. The manifest binds the Japanese BIOS SHA-256 dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac, the English merged-disc SHA-256 5ab2fbd572d454e4a9df024e2bb6b3cd30f84018c3e650bd945bd52742edd83d, and the same trace session token scsp-menu-bound-20260810 into both the sound-CPU and main-SH-2 traces. The raw VDP1/VDP2 capture is independently validated; its SHA-256 is 86bc7cddb23c012edebd1f8ca93fe019737cc8e1fb59a5c68f39fb2a60ffd63a.

The source-bound corridor analyzer reports all 16 SLEV files, all 16 MAP files, all 16 SAL files, and the authenticated SDDRVS.TSK identity. It observes mailbox value 0x02 at the expected mailbox and binds the producer and consumer traces to the same session. It does not observe the verified SDDRVS command handler at PC=0x3224, an event selector, a MAP row, a SAL codec/sample, or a SCSP voice write. The C runtime join test therefore passes only as a correctly blocked, playback-blocked result for this capture; it is not a positive source-bound corridor receipt or an audio playback admission.

Reproduce the source-bound check with:

python3 scripts/analyze_nexus_slev_sal_runtime_corridor.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-codex-scsp-menu-bound-20260810/scsp-writes.trace \
  --main-trace /Volumes/Extern-disk/nexus-saturn-capture/run-codex-scsp-menu-bound-20260810/main-scsp-writes.trace \
  --data-dir <local-home>/.firestaff/data/nexus \
  --driver <local-home>/.firestaff/data/nexus/SDDRVS.TSK

The missing handler and event-to-SAL relation keep SLEV/SAL/SDDRVS playback capture-gated.

For the next operator capture, the external Mednafen diagnostic build can also record sound-CPU reads. Set the trace path and a bounded SCSP range before launching the existing raw-capture launcher:

FIRESTAFF_NEXUS_TRACE_SCSP_READS=/Volumes/Extern-disk/nexus-saturn-capture/run-scsp-read/scsp-reads.trace
FIRESTAFF_NEXUS_TRACE_SCSP_READ_MIN=0x100000
FIRESTAFF_NEXUS_TRACE_SCSP_READ_MAX=0x101000
FIRESTAFF_NEXUS_TRACE_SCSP_READ_LIMIT=200000

The launcher propagates these variables and records the resulting trace hash in the manifest. This trace is still observational: a read of a SCSP address does not by itself identify a SLEV selector, SAL codec or host-playback route. It is intended to provide the missing source-to-voice evidence needed before those runtime gates can be reconsidered.

The corrected external build produced a valid 300-frame menu-window witness at /Volumes/Extern-disk/nexus-saturn-capture/run-scsp-read-menu-20260811c/. The raw capture SHA-256 is 13a7cd31ad1fe1eaa1368e7b8c4403fe43d8f03a6541f25ac86bc0c44a96b9b3. The read trace is session-bound and contains 200,000 reads, but only the mailbox/status addresses 0x100420 and 0x100408; it contains no SAL-window or voice-parameter read. The paired write trace reaches the same authenticated mailbox corridor, while event-selector semantics, SAL codec and playback stay blocked. This is a negative runtime result, not a decoder admission.

A matching deterministic 300-frame menu witness with the focused sound-RAM window 0x40000..0x4ffff recorded zero sound-CPU reads and zero writes in that window. Its raw SHA-256 is the same 13a7cd31ad1fe1eaa1368e7b8c4403fe43d8f03a6541f25ac86bc0c44a96b9b3. This rules out menu initialization as evidence for a sample-RAM consumer; a gameplay witness is still required.

The producer also records 64 unique VDP2 writer code windows. The primary window at PC=0x06011924 contains the runtime words 25fe 0000 25fe 007c ..., while the initial register setup at PC=0x06001416 contains the expected VDP-register literal pairs. The exact 48-word windows do not occur verbatim in the hash-verified TM.BIN or DM.BIN files, so the current result is an execution/code receipt with source identity still unbound; no decompression or relocation interpretation is promoted to production semantics. scripts/analyze_nexus_vdp2_writer_candidates.py also reports the longest aligned partial matches: the primary window reaches only four words in DM.BIN and three in TM.BIN; the longest observed ten-word match belongs to another runtime PC and is shared by both retail files. These are review leads only, not source ownership or tilemap/CLUT consumer proof.

The authentic raw witness can now be decoded at the VDP2 register-field level with scripts/analyze_nexus_vdp2_composition.py. In both the one-frame and eight-frame European samples, TVMD=0x0080 and BGON=0x0002 show display enabled with only NBG1 enabled. CHCTLA=0x1211 selects NBG1 bitmap mode, colour code 1 (the 256-colour mode in Mednafen’s VDP2 renderer), bitmap-size code 0, and BMPNA=0x0000 selects bitmap palette 0; PNCN1=0x00c0 and MPOFN=0x3000 are present but are not consumed as NBG1 tilemap selectors in this bitmap-mode frame. The remaining observed values are CRAOFA=0x1000 and PRINA=0x0503. This is a bounded hardware composition observation, not a retail asset join: the bitmap VRAM source, CLUT owner and text/menu/HUD meaning remain blocked.

The reproducible NBG1 hardware receipt additionally reads the captured NBG1 scroll registers and requires origin (0,0), along with enabled bitmap mode, colour code 1 and BMPNA palette bank 0. It proves the observed VDP2 palette bank and hardware origin, not the screen-space placement of STABG.BIN or any other retail source asset.

nexus_v1_vdp2_nbg1_bitmap_decode_real separately decodes the same authentic frame’s full 512×256 indexed NBG1 bitmap and 256-entry CRAM lane in native code. It verifies the hardware pixel/palette span is readable with the producer’s recorded byte order, but is explicitly capture-only: no retail asset ownership, placement or production renderer admission follows.

scripts/analyze_nexus_vdp2_bitmap_source.py binds that geometry to the hash-verified local corpus and compares the 131072-byte NBG1 span against 162 decoded MENU.BPK PRS3 surfaces, 242 real FONT256.S2D character-generator tiles, and five authentic TITLE.BIN MAPD/TIBG maps expanded through TITLE.CG, plus the first authentic 40×21 STABG.BIN map expanded through its 791-tile pixel region. It also compares the authenticated 256-entry MENU.BPK PALT record. The one-frame and frame-7 eight-frame samples both produce zero non-zero exact matches; MENU, title and STABG palettes have no exact byte or word-swapped position in captured VDP2 CRAM. This is bounded negative evidence for the captured gameplay/intro state, not proof of another source owner. Dungeon bitmap and CLUT joins remain open.

The analyser accepts either a hash-verified loose corpus (--data-dir) or a retail Track 1 CUE (--cue). The CUE path reads just the requested ISO members into memory and validates their recorded hashes; it neither extracts game files nor makes a copy on disk. This keeps capture/source investigation reproducible against the user’s original media while preserving the native runtime’s no-extraction contract.

The source-side STABG work also has a separate capture-only join adapter. Given an explicitly identified Saturn crop, it compares the DMWeb first-map decode row by row and all 512 raw STABG palette bytes before writing the 320×168 indexed surface to a framebuffer. This strengthens the source/capture join for a future HUD witness, but does not reinterpret the negative gameplay capture above or open VDP2 layer ownership and normal HUD presentation.

The atomic VDP1/VDP2 composition lane now accepts that STABG receipt as its VDP2 source and records either explicitly attested order (VDP1 over VDP2 or VDP2 over VDP1). It rejects an ambiguous order and restores the framebuffer on a failed subroute. This establishes the handoff contract needed for HUD over viewport, not the missing Saturn frame that would select the order.

The VDP2 bitmap comparator now also validates every nonzero 32-byte palette anchor in the canonical LEV00–LEV15 Structure2 descriptors. The real corpus contains 1,266 such anchors. Neither the European frame-1 witness nor the independent eight-frame frame-7 witness contains a native or word-swapped exact CRAM match for any of them; the previously checked MENU, TITLE and STABG palettes remain negative as well. This is a bounded CLUT-source result, not permission to select a palette bank or compose a layer.

Title-character data is not a title-screen binding

scripts/analyze_nexus_title_vdp2_source.py checks the authenticated English TITLE.BIN and TITLE.CG pair against raw VDP2 frames. It retains the five DMWeb MAPD/TIBG 64×28 cell maps, the 32-byte title palette and the character generator payload as separate exact byte spans. Native and Saturn word-swapped positions are reported independently.

The tool is intentionally stricter than a character-data search: a resident TITLE.CG payload does not identify a title screen unless a title map and palette also occur in the same observed VDP2 state. Even that source-level combination leaves display-list ownership, active layer selection and final composition unbound, so the tool always reports title_consumer_identity=unbound and semantic_admission=blocked.

The authenticated English/Japanese-BIOS early boot witness captured on 2026-08-14 (frames 0–199, SHA-256 1f1b63b7917ecc347eb2e931fefe2881d64faf96338e0fd04312c1e5887a2008) contains neither title character data nor any of the five title maps. This is negative evidence for that boot window only; it does not identify a later title owner or authorise a fallback renderer.

The longer authentic visual-start witness from the same English CUE and Japanese BIOS (2,400 frames, SHA-256 7220f2fd6ace842dd7de1e269e533b6dc4a942db8e9d286eb93fcf493019e9c1) was also scanned frame by frame. It contains none of the title character generator span, five MAPD cell maps, or title palette in VDP2. Its timeout termination does not weaken the raw transport validation, but the result is strictly negative for this recorded window. The analyzer now streams capture frames from disk so multi-gigabyte witnesses are checked without loading the whole raw file into memory.

The same witness was sampled in VDP1 immediately before the scheduled Start input (frame 1,250), at the next input boundary (frame 1,400), and at its final frame (2,399). Each frame has one observed mode-5 draw reading 0x47c00..0x4fe00 (33,280 bytes). The bytes change between samples, but no exact or Saturn-word-swapped span matches the 126 hash-verified retail files, 815 MNS surfaces, or 1,678 bounded Structure2 surfaces. This is a negative source-owner result for that interval; it neither identifies a title nor permits a texture fallback. The VDP1 source-join tool now streams the selected frame rather than allocating the complete multi-gigabyte witness.

Conversely, the retained four-frame menu-window witness contains the complete word-swapped TITLE.CG payload at VDP2 VRAM 0x24020 and the word-swapped 32-byte title palette at CRAM 0x400, while all five title MAPD cell spans are absent. That is a source-residency receipt, not a title presentation receipt: the map/layer consumer remains unbound.

Both VDP2 analyzers accept --capture-frames N and can now inspect any frame inside an authenticated multi-frame witness. The eight-frame European gameplay witness was rechecked at frame 7: it retains the same NBG1 bitmap composition (BGON=0x0002, CHCTLA=0x1211, BMPNA=0x0000) and still has zero exact retail-source joins. This extends animation-state observation only; it does not turn a frame into menu, HUD or viewport evidence.

The same producer has an independent SCSP trace patch for the audio lane.

Native-runtime boundary while capture remains incomplete

The Firestaff native Nexus route remains blocked: the observed TITLE.CG residency is not a display-consumer or title-input proof, and the visual receipt shows a Saturn-composed 3D title sequence rather than a flat TITLE.CG framebuffer. Firestaff must not substitute an inferred renderer.

The CLI and start menu remain blocked until native capture evidence proves the display consumer and title input contract. Firestaff never opens a retail CUE in Mednafen as a fallback. Mednafen may be instrumented outside Firestaff to produce read-only traces, but those traces must be imported and verified before they can unlock a native Nexus route.

Render-frame receipt binding

The external-only Saturn capture launcher records every requested post-render PPM as render_frame_NNNNNN_ppm_sha256 and render_frame_NNNNNN_ppm_bytes in the raw-capture manifest. This receipt is bound to the capture’s disc hash, BIOS hash, input plan, absolute frame window and raw VDP trace; the launcher rejects a requested render frame outside that raw window. When input observation is enabled, its trace also receives an input_events_trace_sha256 receipt; an input plan alone is not an observed button event. A PPM without those companion receipts is useful visual reference only: it cannot prove a title, menu, HUD, viewport or compositor route and must not unlock Firestaff’s native Nexus startup.

When M12 has matched physical Track 1 instead of a CUE, M11 reopens sibling CUE sheets with the native ISO reader and retains one only if it resolves to that exact physical Nexus data track. The CUE remains the engine input, so its authored AUDIO declarations are retained for the future native consumer; ambiguous ownership deliberately remains blocked.

Firestaff now has a C receipt for the emitted FIRESTAFF_NEXUS_SCSP_WRITE_TRACE_V1 schema. It validates the raw trace hash, mailbox writes, the SDDRVS.TSK command-handler PC 0x3224, and the SCSP voice-register corridor. The real external trace passes this structural receipt, while event→MAP semantics, SAL decoding, and playback remain false. The C receipt also accepts the producer-side FIRESTAFF_NEXUS_MAIN_SCSP_WRITE_TRACE_V1 schema as a separate receipt. It keeps the SH-2 mailbox values 0x02 and 0x0200 distinct from the sound-CPU trace and requires both on the authenticated external producer trace. This joins the producer observation to the mailbox corridor only; it still does not assign a gameplay event, MAP row, SAL sample, SCSP voice, or playback. Against the European gameplay window, the authenticated 68K task observed nonzero mailbox writes at 0x100400 from PCs inside SDDRVS.TSK when loaded at 0x1000; the main SH-2 trace observed 0x06001652 -> 0x100400 = 0x02. scripts/analyze_nexus_scsp_write_trace.py verifies the trace envelope and driver SHA-256 (68890ee4…). This is an authentic runtime handoff corridor, but it does not identify the SLEV event selector, MAP row, SAL sample, or SCSP voice ABI, so semantic admission and host playback remain blocked.

The two trace files are bindable only when the producer writes the same non-empty session= token into both headers. Set FIRESTAFF_NEXUS_TRACE_SESSION to a capture-local token before starting Mednafen. Older traces without this field remain valid structural receipts, but are deliberately rejected as a cross-trace runtime join because they do not prove that the producer and sound-CPU observations came from one session.

The observed 68K PC 0x3224 is now checked by scripts/analyze_nexus_scsp_driver_owner.py against the authenticated driver window at SDDRVS.TSK+0x2220. The source bytes read a command byte, limit it against 0x12, update driver state and write the SCSP per-channel register family at offset $17 from a5=0x100000. This proves a runtime command-to-driver corridor, but does not bind the SLEV selector, MAP row or SAL sample; playback remains blocked.

scripts/analyze_nexus_slev_sal_runtime_corridor.py now joins that runtime corridor to the complete real European sound corpus without promoting any meaning. It verifies all 16 SLEV##.BIN, SNDLEV##.MAP, SNDLEV##.SAL files and SDDRVS.TSK, then applies the same DMWeb eight-byte MAP record grammar as the C loader. The corpus contains 154 terminated MAP rows. The observed mailbox trace contains four non-zero 68K writes, all raw value 0x02, from SDDRVS.TSK PCs 0x3224, 0x1090, 0x34aa and 0x108e; the main trace has five mailbox records (0x0002 and 0x0200). The analyzer also preserves the chronological raw sequence: sound-CPU writes 0x02 to 0x100400, 0x0f to 0x100401 and 0x0118 to 0x100402, repeated at the observed PCs; the main CPU writes 0x02 and 0x0200 to 0x00100400. These are address/value/PC observations only and are not renamed to event IDs, MAP rows or samples.

Fifty-four parsed MAP windows extend beyond the extracted SAL file length when treated as direct file intervals. The source code already identifies these fields as an opaque sound-driver memory area, so the audit records this as a boundary fact rather than an error or a guessed relocation. It does not prove which event selects a row, how SAL bytes are decoded, or which SCSP voice owns the data. The receipt therefore reports event_selector_semantics=unproven, sal_codec=unproven and host_playback=blocked.

The C integration test nexus_v1_slev_scsp_runtime_join now requires the same two trace streams, the authenticated SDDRVS.TSK corridor, and a real SNDLEV00.SAL/.MAP load in one receipt. It passes only with the source directory present and still requires blocked-unsupported-decode; this joins provenance without turning mailbox values into event or sample identities.

The external capture inventory is reproducible with scripts/analyze_nexus_capture_inventory.py. The current operator corpus has 34 valid raw witnesses and 136 frame observations: 8 reset/no-layer frames, 14 RBG0-only frames from the Saturn CD-player state, 100 NBG1-only frames from the dungeon witness, and 14 other active VDP2 states. The inventory does not call any of these states a menu, HUD or viewport. The latest frame-0 startup attempt is a one-frame reset witness (TVMD=0, BGON=0) and supplies no startup asset-consumer evidence.

Latest J/J startup-to-menu attempt

The external run /Volumes/Extern-disk/nexus-saturn-capture/run-authentic-merged-startup-menu-20260811d/ contains 200 frames from the merged English disc with the authenticated J BIOS. The bounded active-low START+A input window is frames 120–129. The raw VDP1/VDP2 stream is SHA-256 1f1b63b7917ecc347eb2e931fefe2881d64faf96338e0fd04312c1e5887a2008 and contains 156 frames with observed VDP1 activity.

The post-input window changes VDP state and produces a mode-5 VDP1 source span at 0x10000, but the span has no exact match in the verified MENU.BPK, MNS, DGN or other retail source corpus. The VDP2 character lane reports font256_vram_span_matches=1/4 and font256_cram_palette_matches=0/1, with the text consumer still unbound. The paired SCSP traces record a producer mailbox write of 0x02 and the sound-CPU mailbox/voice-register corridor, but do not identify a menu event, MAP row or SAL sample. This is authenticated transition evidence only; startup→menu, menu text, HUD and viewport admission remain blocked.

JP controller-reader retail-source join

A matched Japanese retail run at emulation frame 10500 now identifies the controller-reader overlay from original disc bytes instead of treating its relocated WorkRAM PCs as if they were in DM.BIN. The source-write receipt from session nexus-menu-source-reader-10500-20260928 joins the complete 512-byte destination span 0x06014500..0x060146ff to TM.BIN at file offset 0x4500, with source-writer PC 0x00205f18. The exact chunk is admitted by scripts/analyze_nexus_sh2_source_trace.py against the retail CUE; Track 1 is normalized in memory from raw 2352-byte sectors and no game member is written to disk. The receipt has 384 rows, three contiguous destination chunks and two exact retail-member matches (0DMSTRT.BIN and TM.BIN). Its source-trace SHA-256 is 012a0b757556688ac8e868cb0c45b6cf1ccd3b469a1557570eb23c9715f0191b. The matching ISO byte offset 0xdaed00 is sector 0x1b5d plus 0x500 bytes, matching the trace’s source LBA for all rows in this chunk. This was a partial instrumentation run: the source-write trace was flushed and hash-receipted, but the separate raw-frame capture did not finish (capture_exit_status=1).

Disassembly of the matched retail TM.BIN bytes places the live pipeline PCs at offsets 0x456c, 0x4582, 0x4594 and 0x45b0. The containing code reads SMPC OREG bytes starting at 0x20100021 and processes a controller packet through a RAM-backed state buffer. A same-frame matched RAM snapshot pair (frame 10500, 1 MiB WorkRAMH each) differs in only six bytes: three at 0x0602c8f9..0x0602c8fb and the corresponding three at 0x0602c900..0x0602c902. The input snapshot carries 17 41 07 at both spans; the forced-no-button control carries 57 03 08. The input-side SMPC trace independently verifies the relocated instruction words against that session’s RAM snapshot (master_sh2_instruction_identity=runtime_snapshot_verified).

This now binds the live input reader to exact Japanese retail code and proves that Start changes its controller-state bytes. It still does not decode the specific bytes into a Start action or connect that action to a title/menu transition; the trace analyzer correctly reports input_consumer_semantics=unbound and semantic_admission=blocked. The first attempted retail-to-runtime join against DM.BIN was invalid because this routine is loaded from TM.BIN; do not reuse that earlier static disassembly as the reader’s source.

A follow-up matched pair at frames 10500–10551 sampled WorkRAMH every four frames while scheduling 13 two-frame button pulses in the input run (Start, A/B/C, the four directions, X/Y/Z, L and R). Both captures completed with status 0, and all eight captured VDP1/VDP2 regions were byte-identical. The sampled RAM deltas were limited to two corresponding two-byte ranges (0x0602c8f9..0x0602c8fa and 0x0602c900..0x0602c901): control 09 33 versus input 29 38 through frame 10528, then control 10 33 versus input 30 38 from frame 10532 onward. This sampling does not distinguish the individual masks or establish a button-to-action mapping; short pulses and four-frame sampling may miss the reader’s scan cadence. Semantic admission remains blocked. A useful next capture needs longer button dwell and finer-grained RAM samples, still paired with a forced-no-button control.

A higher-resolution matched pair was captured on TRV2 at frames 10500–10694 with the same authenticated Japanese BIOS and retail CUE. The control run forced no buttons; the input run scheduled Start, A/B/C, Up/Down/Left/Right, X/Y/Z/L/R in that order, each held for 12 frames followed by three release frames. Each run completed with capture_exit_status=0, a 195-frame raw VDP capture and 195 exact 1 MiB WorkRAMH snapshots. Their raw VDP captures have the same SHA-256 144d7a92c54a046d76be666ab735fb6e2c2d03a5502281f15af5424f4c75ea3c; the comparator confirms all eight VDP1/VDP2 regions are identical across all 195 frames. The input event receipt contains 312 rows for the 13 scheduled masks.

The memory-stream comparison reports a difference in all 195 frame pairs, including all 39 frames with no active mask in the input run. The recurring two-byte spans at 0x0602c8f9..0x0602c8fa and 0x0602c900..0x0602c901 drift through several values over the run rather than uniquely following each mask. Two other bytes (0x0602c90c and 0x0602c910) show 00 versus 10 during Start/A windows and 00 versus 20 during B/C windows; the other nine masks do not produce those same-byte deltas. This is an observed byte pattern, not a decoded controller structure or action mapping: the input schedule is sequential, deltas persist during release frames, and the raw presentation never changes. The capture therefore does not authorize a title/menu transition or gameplay input semantics.

The bounded comparison tool is scripts/compare_nexus_sh2_memory_snapshots.py; it reads one 1 MiB snapshot from each stream at a time and emits address/value deltas without treating synthetic fixtures as game data. Its CTest parser regression is nexus_sh2_memory_snapshot_compare. On TRV2 the matched inputs remain under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/button-map-fine-10500-20260928/. Control WorkRAMH SHA-256: 90d4380bce8115607a6f85e80bfbfcece5da0a163782be88f6ce0d3c15ddbf53; input WorkRAMH SHA-256: 7fb3f54321e56cc73e8442c9d58a6c69e3c9e2f2d824362ca45353ef9f0e7ab1; input event SHA-256: e34cbbfc8ecb5ad6d84b2880ef04d70845f46f32f9fcab3caef91002098888e2. The comparator’s complete textual receipt has SHA-256 d557e3011d2eaae940e2488c634611d208c89662b47db8c26967ed04f1e3ae6f. The next useful input experiment is one mask per independently reset matched pair, joined to the retail consumer’s exact state reads and later presentation.

A one-mask independent-start pair was then captured on TRV2 at frames 10500–10559 with the same JP BIOS and retail CUE. The input run scheduled only mask 0x0010 for 12 frames; its input receipt has 24 rows (two observations per active frame) and no other mask. Both cold-start runs completed with capture_exit_status=0, 60 WorkRAMH snapshots and 60 raw VDP frames. All eight VDP1/VDP2 regions are identical across the pair, and the rendered PPMs for frames 10500, 10512 and 10559 are byte-identical. The shared raw capture SHA-256 is b64c09d0ac4a677bb10ec68f2058dca27b07d37e4d949a526bce870cc5c28c63.

At the instrumented SH-2 WorkRAMH read site, address 0x0602c90c is observed as 0x1000f001 in the Start-input run during frames 10507–10511, while the matched forced-no-button run reads 0x0000f001; after release, the input run also reads 0x0000f001 by frame 10559. This joins the external Start mask to a transient controller-state byte pattern, not to a menu command. Separately, the memory comparator reports two differing byte spans at 0x0602c8f9..0x0602c8fa and 0x0602c900..0x0602c901 in all 60 snapshots, including all 48 input-release frames; their bytes are 13 22 in control and 56 27 in input. Those persistent differences do not identify button/action semantics. The memory receipt SHA-256 is e29a35b834d4b792112f9513e1427f94aace281b57bcf9b81933ec0dacf6265d.

The control and input WorkRAMH stream SHA-256 values are 2df0204269388d817632b78939ee6450cb9798bfc80f4bdd81a8a7c9e78237f9 and ddd1554a9ec3ab7554a12b356c39af4b4aa38cd7869234896ee424888c05041e; the input event trace SHA-256 is 446caae2da8e1c0848c8922a828d834c99de2e66aaa67787ab26e3f492fe57ed. Original BIOS, disc and all capture artifacts remain on TRV2 under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/button-start-independent-10500-20260928/. This independently reset pair narrows controller transport evidence but still does not show a title/menu transition or authorize interactive startup. The next capture must follow the packet past the bounded read region into the retail event consumer and sample an actually interactive title/menu interval.

A source-bounded follow-up reused this exact 10500–10559 cold-start window with frame-stamped SH-2 WorkRAMH reads/writes for addresses 0x0602c8f0..0x0602ca00 and runtime PCs 0x06014000..0x06014a20. Both runs completed with capture_exit_status=0; their 60-frame VDP streams have the same SHA-256 b64c09d0ac4a677bb10ec68f2058dca27b07d37e4d949a526bce870cc5c28c63, and all four rendered frames (10507, 10512, 10520 and 10559) are byte-identical. The input-event trace records Start mask 0x0010 twice per active frame from 10500 through 10511. In the SH-2 read trace, 0x0602c90c at reported PC 0x06014388 is observed as 0x1000f001 in the input run versus 0x0000f001 in control on 11 sampled reads; reads from 0x0602c910 show the same difference on six samples. The write trace also records input/control deltas in the 0x0602c90c..0x0602c911 area. This strengthens the evidence that the external Start pulse reaches transient controller-related WorkRAMH state, but does not identify an action, menu dispatch, or presentation consumer. Raw VDP output remains unchanged. Control/input read-trace SHA-256: 7497dfc02ad7833335178eae1a3c90a47138d11f992d32e894606b8732e73f8a / 431a6b33fa1da3f0862617ba63d770908142db1d61b9caa98c75db96f505f4b5; write trace SHA-256: f0d6b7b130ce62247be1198e4a3a0d444c9c604cecd10e5c2a7c242613134ba9 / 985f6cddd253c6e7d17a1130bd5e557997fa617f41b49871d75b4a97de5bb2c1. The input-event SHA-256 is 446caae2da8e1c0848c8922a828d834c99de2e66aaa67787ab26e3f492fe57ed. BIOS, disc and capture artifacts remain on TRV2 under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/controller-state-depth-10500-20260928/.

A paired all-PC read trace sampled only WorkRAMH addresses 0x0602c90c and 0x0602c910 over the same 60-frame window. Each run recorded 105 reads, all from the same four SH-2 PCs (0x06014388, 0x0601439a, 0x06014510, and 0x0601457e); the Start run introduced no additional observed reader PC. During the scheduled pulse the input trace includes 0x1000f001 at 0x0602c90c (11 reads) and at 0x0602c910 (6 reads), where the control records 0x0000f001. The complete raw VDP hashes still match, and the four sampled rendered frames are byte-identical. This wider PC scope therefore confirms only the bounded controller-state reads and their changed values; it does not show a new consumer, action dispatch, or menu transition. Control and input read-trace SHA-256 values are b7924dde76da6a8a7607eb3f07cc94279c94478129b7b547ed854a68489433cf and e3a93335fdb349e2e1340ed1bb8507306b0366ac7cad814343d762d4a0e30acf; the input event receipt has SHA-256 446caae2da8e1c0848c8922a828d834c99de2e66aaa67787ab26e3f492fe57ed. The capture artifacts remain on TRV2 under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/controller-consumer-allpc-10500-20260928/.

The analyzer now accepts both the older V1 receipt and the instrumented producer’s V2 register-owner receipt; --frame selects a single frame from a bounded multi-frame trace. The captures above intentionally stop their read filter at 0x0602c911, so they do not contain the linked-buffer/table reads needed for the analyzer’s complete consumer-chain check. That chain remains unverified until a wider, source-bounded trace is captured.

A subsequent paired cold-start capture on TRV2 widened that bounded read filter through 0x0602c940 and captured frames 10500–10507 in control and one-frame Start-input runs. Their raw VDP12 streams and rendered frame 10507 are byte-identical. Both traces reach the same table read at address 0x0602c940, PC 0x0601462c; the register R4 is 0 in control and 0x10 with Start input. The analyzer reports the observed register value without requiring either value, and calls the resulting chain observed rather than verified. This does not establish the table’s semantics, an action mapping, or a menu transition; semantic admission remains blocked. Both capture receipts and original media remain on TRV2 under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/button-start-consumer-frame-10507-20260928/.

The Japanese retail TM.BIN was read directly from the authenticated CUE on TRV2 and has SHA-256 d87485fe6eba1f6e9fbbf487f5fcdd994911136905e6172e5bb5bc0122407eb6. Its bytes at runtime 0x0601462c (TM.BIN offset 0x462c, with the observed load base 0x06010000) decode as CMP/EQ #1,R0; the following instruction branches to 0x060146e8 when that comparison is false. In the same-session input and control read receipts, the fetched value at 0x0602c940 is 0x01000000 in both runs. Only the sampled R4 differs (0x10 versus 0), and the rendered/raw VDP outputs are identical. Therefore this receipt does not show a Start-dependent condition or presentation transition: R4 is an observed live register, not an action code. The nearby branch remains an unidentified TM.BIN state dispatch until its compared field and downstream state/presentation consumer are independently bound.

The operator capture launcher now forwards the SH-2 WorkRAMH write producer’s inclusive absolute-frame bounds and records them in its manifest. A follow-up input/control pass can therefore pair a narrow WorkRAM address window and row limit with the exact captured interval, without letting unrelated earlier startup writes consume the trace budget. Such writes remain observations until they are joined to the authentic TM.BIN consumer and a downstream state or presentation effect.

The producer also has a bounded SCSP-read trace with an optional sound-CPU PC filter. In the retained 100-record European gameplay window, reads were observed from shared sound RAM and driver setup tables, but none from the 0x100400..0x100401 mailbox range and none while filtered to 0x3224. scripts/analyze_nexus_scsp_read_trace.py records this negative result. It does not authorize an inferred event, selector, sample or playback route.

The capture patch now emits FIRESTAFF_NEXUS_SATURN_VDP1_RAW_V2 with an explicit VDP1 state line (TVMR, FBCR, PTMR, EDSR, LOPR, COPR, the return pointer and framebuffer selector) before the unchanged raw VRAM/FB payload. V1 witnesses remain readable. The state line is an observation of the emulator’s VDP1 model, not by itself proof that a particular MENU.BPK, DGN, HUD or viewport record owns the command stream.

Generic Mednafen capture handoff

The upstream review candidate on the external disk uses the neutral markers MDFN_SS_SATURN_RUNTIME_CAPTURE_V1 and VDP1_RAW. Firestaff’s transport reader accepts those markers as well as the historical Firestaff markers. The payload order is unchanged: VDP1 VRAM, both VDP1 framebuffers, the optional draw-buffer byte, then VDP2 registers, VRAM and CRAM. This is intentionally a transport compatibility boundary only; generic Mednafen output still has no Nexus asset owner and therefore cannot pass semantic admission on its own. The generic Mednafen container serializes Saturn words explicitly big-endian. Historical Firestaff V1/V2 containers have no byte-order field, so the reader resolves their order from a nonzero VDP2 register witness; ambiguous raw registers remain transport-only. The regression test uses nonzero TVMD and BGON words in both forms, and the external Firestaff witness is verified as big-endian by that receipt.

To reproduce the generic dump, build the review candidate on the external disk with Saturn enabled and provide user-owned BIOS/media paths at runtime:

cd /Volumes/Extern-disk/mednafen-nexus-upstream-pr-v1
./configure --enable-ss
make -j2

HOME=/Volumes/Extern-disk/mednafen-nexus-upstream-pr-v1/runtime-home \
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
./src/mednafen -sound 0 \
  -ss.bios_jp "/path/to/Sega Saturn BIOS (J) (1.01).bin" \
  -ss.capture.path "/Volumes/Extern-disk/mednafen-nexus-upstream-pr-v1/nexus-test.capture" \
  -ss.capture.frames 1 "/path/to/Dungeon Master Nexus.cue"

The command is allowed to stop after the requested frame has been emitted; the resulting file stays outside the repository. Validate its neutral stream layout first, then run the Firestaff transport gate:

python3 scripts/validate_mednafen_saturn_capture.py \
  /Volumes/Extern-disk/mednafen-nexus-upstream-pr-v1/nexus-test.capture
FIRESTAFF_NEXUS_RUNTIME_CAPTURE=\
/Volumes/Extern-disk/mednafen-nexus-upstream-pr-v1/nexus-test.capture \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_FRAME=0 \
./build/test_nexus_v1_saturn_runtime_capture

Both checks prove only that the VDP1/VDP2 byte stream is readable. A frame with TVMD=0 or BGON=0 is a reset/idle observation and must not be counted as startup, menu, HUD or viewport evidence.

In the 600-frame Japanese-BIOS/English-media witness, frame 350 is an active NBG1 tilemap frame (TVMD=0x8000, BGON=0x0103, CHCTLA=0x1010). The capture-only NBG1 tilemap decoder accepts that frame after restoring the candidate’s explicit big-endian VDP2 word order. This proves raw tilemap decoding only; no Nexus source span, menu identity or production compositor is admitted by that result.

The source-bound NBG1 bitmap compositor now consumes the same explicit byte order carried by the raw-frame receipt for both VDP2 registers and CRAM. An older fixture with no producer order keeps the previous scoring fallback; a real Firestaff or Mednafen frame does not guess from register values.

Fresh reset witness

An external reset-only run using the authenticated J BIOS and the merged English/European disc cue is retained at /Volumes/Extern-disk/nexus-saturn-capture/run-reset-title-menu-20260811b/. Its 300-frame raw stream has SHA-256 7756900f79ed3b7c4e680918a1f9604943fb96398b068866c77bc91b37d3f0ad and passes the raw-layout validator. VDP2 is reset through the early frames, becomes display-active around frame 120, and then holds the observed TVMD=0x8000, BGON=0x000f, CHCTLA=0x1010 state. No input was injected. This is useful reset/runtime evidence, but it does not identify a retail TITLE, MENU.BPK, FONT256 or host display consumer, so startup admission stays closed.

On 2026-08-10 a bounded J-BIOS/English-media source-trace attempt reached 500,000 SH-2 RAM-source rows but timed out before producing a complete frame capture. After rejecting zero-filled RAM and unmapped ISO padding, the trace has exact_iso_chunk_matches=0 and retail_runtime_source_join=missing. It is retained as a negative transport/provenance witness, not as menu evidence.

scripts/analyze_nexus_vdp1_command_window.py can then inspect the bounded record window ending at the captured COPR. It reports raw command words and requires an observed END record when requested. Its output is still a state receipt: command type 0x08 (User Clip), 0x09 (System Clip), 0x0A (Local Coordinate), or END does not establish a game-asset owner or authorize a host draw.

Authenticated VDP1 command-list framing

scripts/analyze_nexus_vdp1_command_sequence.py follows the captured CMDLINK chain instead of treating the live COPR cursor as the end of the list. It distinguishes User Clip, System Clip and Local Coordinate records, handles a cursor captured during an active draw, and accepts reset/idle frames as idle observations rather than pretending they contain a scene.

Against the external 300-frame European witness /Volumes/Extern-disk/nexus-saturn-capture/run-codex-long-menu-20260809b/runtime-vdp12.raw (manifest SHA-256 6ba86952b3fe1cbf9e2ebbf20f52345e7df15c270a90e00e09867a7fff0c9611), the verifier covers all 300 frames: 290 active command chains and 10 idle END frames. Every active chain reaches an observed END and contains draw records plus a clip-state record and Local Coordinate state. The witness uses both VDP1 command buffers; for example, frame 104 resolves 0x0c3c0→0x0d6c0 with 153 records, 147 draws, two User Clip records, no System Clip record and three Local Coordinate records. This is hardware command-order evidence only; it does not identify the chain as startup, menu, HUD or a DGN scene.

Full gameplay-chain DGN join

The longer European E-BIOS/French-data-only witness /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-long-20260809f/runtime-vdp12.raw has 900 captured frames and SHA-256 cd167dabeaedd02f8555a4e1d4eaa1818b51b5fb6ebb3479c9a2869faf6bbdc9. At frame 899 the authenticated chain contains 220 records, including 209 colour-mode-1 textured draws. scripts/analyze_nexus_vdp1_dgn_command_sequence_join.py matches 204/209 captured source spans and CLUTs to canonical LEV00.DGN Structure2 materials; 175 of those matches also have Structure3 face-selector owners. The five unmatched source spans and 34 selector-less material uses are reported, not replaced by guessed HUD/menu assets. This closes a bounded gameplay material/order receipt while leaving the unresolved cases visible. Transform/culling, display origin, HUD/menu ownership and VDP2 composition are still unproven, so semantic_admission remains blocked.

The runtime writer/source join is reproducible with scripts/analyze_nexus_vdp1_runtime_writer_join.py. On the authenticated European DM.BIN/TM.BIN pair, the captured writer window beginning at PC=0x06013058 and writing 0x47c00 has no whole native owner. With the explicit 0x06010000 load-base hypothesis, its direct DM.BIN offset 0x3058 is a byte mismatch; the best retail-file overlap is one SH-2 word. The tool records this as negative evidence and leaves relocation, decompression and runtime-code ownership unbound. It does not treat a short word overlap as a source join or unlock menu, DGN, HUD or viewport drawing.

scripts/analyze_nexus_tm_bin_vdp_owner.py records a separate static source receipt. It accepts a real CUE and reads the selected DM.BIN or TM.BIN member in memory, retaining its load base when reporting the SH-2 instruction and literal addresses. Both authenticated binaries have PC-relative literal loads reaching VDP2 register space; TM.BIN also reaches the VDP1 register window (0x25d00000 through the observed register offsets). This narrows code ownership, but a literal corridor is not an execution trace and does not join either executable to a captured command or source span.

The first positive runtime source join is retained outside the repository at /Volumes/Extern-disk/nexus-saturn-capture/run-codex-vdp1-source-read-20260810. The SH-2 source-write trace SHA-256 is 91819bcdad6ee9c34f3cced80926d0efc855e2fc09c4bceeb48d0ea2944431b4; the English merged ISO used for the runtime join has SHA-256 00431dfad48b44d4457300bcc62d8af6bfb5edcad149313e75db8de6bbddd581. The authenticated J-BIOS/English-media capture has 600 raw VDP1/VDP2 frames. Its bounded SH-2 source-write trace contains a complete 16,384-byte TM.BIN chunk loaded to 0x06027000 by runtime PC 0x00205f18; the strict analyzer resolves the chunk to ISO offset 0xd89800, member offset 0x17000, and reports retail_runtime_source_join=verified. Reproduce it with:

python3 scripts/analyze_nexus_sh2_source_trace.py \
  --require-member TM.BIN \
  --require-destination-range 0x06027000:0x0602b000 \
  --require-pc 0x00205f18 \
  "/Volumes/Extern-disk/nexus-saturn-capture/media/Dungeon Master Nexus (English) - Merged.iso" \
  "/Volumes/Extern-disk/nexus-saturn-capture/run-codex-vdp1-source-read-20260810/sh2-source-writes.trace"

The same exact 16 KiB chunk also occurs in the authenticated Japanese retail CUE at ISO offset 0xdc1800; this alternate source-corpus lookup does not change the capture’s English-media provenance and does not prove the Japanese runtime executed this chunk.

This proves retail TM.BIN bytes entered runtime RAM during the capture. The same witness still has no exact VDP1 command-to-retail-file owner (source_join=unbound), so it does not unlock the startup/menu text consumer, HUD, viewport or SLEV/SAL/SDDRVS playback.

scripts/analyze_nexus_slev_sh2_owner.py provides the corresponding static receipt for all 16 hash-authenticated SLEV##.BIN tasks. It scans the big-endian SH-2 word stream, binds the shared 0x2fe6 entry and reports the exact PC-relative literal rows that touch the observed 0x25/0x26 address corridors. The scan is source evidence only; the task body remains opaque and cannot authorize event dispatch, selector order, callback writes or sound playback.

It accepts the original Track 1 CUE directly as well as a loose corpus. For example, this reads all sixteen requested ISO members in memory, validates their recorded hashes, and never extracts them:

python3 scripts/analyze_nexus_slev_sh2_owner.py \
  --cue "$HOME/.firestaff/data/nexus/Dungeon Master Nexus (Japan).cue"

When a later frame contains a texture command, the same tool reports its bounded SRCa-derived VRAM byte span and SHA-256. That span is the join key for a future source-owned capture; it is not permission to reinterpret the bytes as PRS3, TITLE.CG, MENU.BPK, STABG, FACE, or DGN material.

The VDP1 writer join also accepts the frame-scoped V2 trace emitted by the current capture producer. Use the same frame number for both artifacts; for example, the startup-source witness can be checked at frame 106 with:

python3 scripts/analyze_nexus_vdp1_source_write_join.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-authentic-merged-startup-source-20260811b/runtime-vdp12.raw \
  /Volumes/Extern-disk/nexus-saturn-capture/run-authentic-merged-startup-source-20260811b/vdp1-writes.trace \
  --capture-frames 300 --frame 106

This removes the former V1-only parser failure. It still reports semantic_admission=blocked when no exact source owner is found.

Artifact families

The capture consumer now also has a bounded VDP2 NBG1 tilemap lane. It admits only an exact name-table/character-generator/full-CRAM join, the observed NBG1 tilemap register tuple, 8x8 cells and two-word name entries. Mednafen’s verified fields are used directly: PNDSize=0, palno=tmp&0x7f, flip bits 0x4000/0x8000, charno=pnd[1]&0x7fff, and the 4/8bpp CG addressing used by TileFetcher::Fetch. This is a source-bound replay primitive, not a general VDP2 emulator. It does not admit PNDSize=1, 16x16 cells, inferred source-map crops or a host text layout. A positive FONT256/TEXT4/TABL consumer capture is still required before the startup/menu text gate can change.

The transport-only tilemap decoder can be exercised against an operator-supplied authenticated capture at frame 300. The capture is development evidence only: Firestaff neither ships nor loads a Saturn BIOS at runtime.

cmake --build build/nexus-capture \
  --target test_nexus_v1_vdp2_runtime_tilemap -j2
FIRESTAFF_NEXUS_RUNTIME_CAPTURE=\
  /path/to/authenticated-nexus-capture/runtime-vdp12.raw \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_FRAME=300 \
  build/nexus-capture/test_nexus_v1_vdp2_runtime_tilemap

The test passes with NBG1 enabled in character/tilemap mode and keeps renderer_permitted=0. The same witness is not a bitmap-mode frame, so the separate 512x256/8bpp bitmap decoder correctly rejects it; this prevents a character map from being mislabeled as a bitmap or a menu surface.

The corrected TileFetcher address units were checked in the open PR #19 against an authentic Japanese Nexus startup capture. For NBG1, MAP1A/MAP1B at register offsets 0x44/0x46 select plane 0x17; with PLSZ=0 and MPOFN=0, the name-table base is 0x5c000 bytes after wrapping the 18-bit Saturn word address and converting it to bytes. The visible 64×64-pixel window visits 64 unique cells. The observed charno=0x1000 selects character bytes at 0x20000 using Saturn’s 32-byte character-number stride. The runtime decoder test checks the 64×64 window at (0, 0) and its 64 unique name-table cells. It does not establish full-frame traversal or authenticate the capture by itself.

This is capture transport/address validation only. It does not bind an asset, text code, CLUT owner, menu placement or display consumer. renderer_permitted remains zero and production readiness remains unchanged.

VDP1 replay also has an atomic sequence lane for a complete bounded command window. Every mode-1 command must pass the existing exact DGN image/CLUT join; the sequence additionally requires captured clip state (User Clip or System Clip), local-coordinate state, command order and an observed END record. Replay is staged into a temporary framebuffer and published only after all commands pass. This closes the single-command-to-command-list handoff mechanically, but does not claim that the current European traces are a complete retail viewport scene.

Separate authenticated VDP1 witness windows

The external VDP1 compositor verifier accepts separate capture paths for the two real witness windows. This is required because the retained authentic captures do not place the direct-colour and indexed DGN/Mode-1 observations in the same frame:

FIRESTAFF_NEXUS_RUNTIME_CAPTURE=\
  /Volumes/Extern-disk/nexus-saturn-capture/run-codex-sysclip-gameplay-20260809/runtime-vdp12.raw \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_FRAME=756 \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_DIRECT_COLOR=\
  /Volumes/Extern-disk/nexus-saturn-capture/run-codex-j-menu-long-20260809/runtime-vdp12.raw \
FIRESTAFF_NEXUS_RUNTIME_CAPTURE_FRAME_DIRECT_COLOR=500 \
FIRESTAFF_NEXUS_DGN_SOURCE=/Volumes/Extern-disk/FirestaffUserData/data/nexus/LEV00.DGN \
  build/test_nexus_v1_vdp1_capture_compositor

The command passes with the gameplay witness’s System Clip (319,223), 237 draw records, 219 resolved DGN source/palette joins, and an atomic replay receipt. The independent J frame 500 passes the direct-colour decoder and viewport adapter. The VDP2 bitmap compositor passes against that same J frame

  1. These receipts prove bounded, source-bound capture replay only; they do not identify the retail scene owner or authorize the production renderer.

nexus_v1_vdp1_command_sequence_frame() now performs the same bounded CMDLINK traversal in C. On the authenticated European frame 899 snapshot it reports 220 records, 215 draw records, two User Clip records, two Local-Coordinate records and the first display origin (160,112). The origin is carried into the sequence compositor and every command must match it. This proves command-list framing and display-origin state only; mesh camera transform, face selection and culling remain blocked.

Ymir debug tooling

The external Ymir PR #961 adds a bounded VDP1 command-trace window, CSV export, and frame/command-context memory dumps. It was exercised with the authentic Japanese Nexus disc under --debug; one date-setup run exported 305 command rows across 105 frames. The trace is useful for disassembly and frame selection, but it has not been joined to a same-frame source asset, Firestaff renderer output or a retail menu/HUD/viewport owner. Ymir remains an external development tool, never a Firestaff runtime dependency. The PR is open and has not been released.

The follow-up Ymir PR #966 adds --load-state SLOT and --dump-memory for unattended capture. The frontend queues a requested save-state load before the dump on the emulation event queue, avoiding host-window focus and synthetic keyboard input. It is intended to reproduce an already hash-pinned, authentic state with an isolated profile; it neither packages game bytes nor proves a hardware presentation route. Any resulting dump still requires the normal same-session source, palette, VDP command, and consumer admission checks.

Mednafen generic VDP1 state and bus-order handoff

The generic Mednafen candidate now emits an optional state= line after VDP1_RAW. It records TVMR/FBCR/PTMR/EDSR, LOPR/COPR, return address, framebuffer selector and system-clip bounds. The VDP1 payload remains big-endian Saturn bus words. Firestaff records that order in the transport receipt and swaps only a temporary replay copy before feeding the existing little-endian VDP1 command parser; the raw artifact is never rewritten.

The external candidate run /Volumes/Extern-disk/nexus-saturn-capture/run-mednafen-generic-state-20260811i/runtime-vdp12.raw contains 120 frames and 76 non-idle VDP1 observations. The transport validator, VDP1 command-sequence probe and VDP2 tilemap capture-only probe pass against it. This proves a replayable VDP1 command-state corridor, not yet a MENU.BPK/FONT256, HUD or DGN scene owner; semantic admission remains blocked.

Japanese FONT256 character-source identity

The VDP2 character-source join accepts both the hash-verified European FONT256.S2D extraction and the authentic Japanese retail file recorded in scripts/fixtures/nexus_v1_disc_file_hashes.py. The Japanese file is parsed using its own SCR descriptors; hash acceptance does not assert that its font is present in a captured frame. In the authenticated Japanese capture, frame 142 yields only one of four exact VRAM span matches and no CRAM palette match. The source join therefore remains unbound, text-consumer ownership is unknown, and semantic admission remains blocked. DMWeb’s SCR format notes guide parsing; they do not supply the missing runtime binding.

Route Magic Evidence still required
PRS3/menu replay NXSPRS3M source stream, SH-2 execution trace, PALT/VDP1 consumer and frame identity
VDP1 material NXSVDP1C DGN/face/descriptor/image/palette identity, command words, VRAM window and destination
SLEV/SAL runtime NXSLSC01 task body, event selector, MAP row, SAL window and SDDRVS consumer
Structure3 topology NXS3TOP1 face/vertex ownership, transform/culling route and frame order

Each verifier checks the artifact header, source hashes, bounded payload and identity lanes. A manifest alone is not a capture; a source decoder alone is not a presentation or gameplay proof. Until the corresponding artifact is admitted, production routes remain no-draw or playback-gated.

Streaming VDP1 source-buffer verification

scripts/analyze_nexus_vdp1_source_to_vram.py reads a raw capture sequentially and verifies the complete frame envelope through end of file. It provides the same strict transport proof for the selected VDP1 copy without retaining a long retail capture in memory in its entirety. A truncated envelope, incorrect frame number, or data after the final frame is rejected. The check still proves only the source buffer, writer PC, VDP1 destination, and Saturn byte order. It does not assign pixel-format, palette, command, or resource semantics.

Retail title MAPD CD-to-RAM receipt, 2026-09-28

A current instrumented JP Saturn run captured 162 frames beginning at 13294, using BIOS SHA-256 dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac and CUE SHA-256 878b3bc223bb56e99626dbaa2946d17fb349a2a67a10cd4423742d8ab30b8699. The raw VDP12 receipt is SHA-256 3a1ceef218c8acf5f44e8fd47dac0f6fc7764b1a72890203e20d42b02a774bdc; the FIFO-word and SH-2 source-write traces are bound in the manifest with SHA-256 values 13fd555f194efbc276c2f1d729ea1cdafbbd54f621a930565fd47b682244e301 and 6327a77b26cd8f832e87fa1d067a1cb867f60be03318e2667e6b31da560ffba5. Against the supplied Track 1, the verifier accepts all 27,441 bounded FIFO words, confirms 13,312 contiguous retail-loader writes from LBAs 6063–6088 into 0x060b7d80..0x060c4d7f, and locates the MAPD record and palette at 0x060b7ff8 and 0x060c0c4c respectively. The real-media CTest now checks both the legacy flat trace layout and the launcher’s traces/ subdirectory layout; it passes against this receipt. This establishes title MAPD CD-to-RAM transport only. RAM-to-VDP2 ownership and semantic admission remain blocked. The BIOS, disc, raw capture and traces remain on TRV2 under /home/trv2/work/firestaff-nexus-menu-transition-20260928/runs/title-mapd-cd-ram-source-13294-20260928/.

JP title-screen START follow-up and SH-2 branch trace, 2026-10-02

A separate authentic-media run shows the retail title screen after a single START pulse. The input capture uses the Japanese BIOS and Japanese retail CUE hashes above, starts at frame 42509, and schedules mask 0x10 for one frame at

  1. The rendered frames at 42808, 42809, 42810, 42869, and 43108 are retained in the capture manifest; frame 42869 shows the Nexus title and PRESS START BUTTON, and frame 43108 still shows the title after 299 frames. The frame-42869 PPM SHA-256 is acacec7595eaae5175b15253a63345daeae5b1613b17865779cf6c06818d37f9; the frame-43108 PPM SHA-256 is e47c9b0685d7a81bb4526a86b846bdecca7b79ff13d770002907182e2016253f. The matched control-follow-up has the same PPM hashes at both frames and the same 600-frame raw capture hash 0bcf7126e458782169b9d73b59325790643acf01962b32c909327c573c073f8f. The control-follow-up’s only START pulse is at its initial frame, so this comparison establishes no lasting presentation change from the later input pulse, but does not identify the menu action consumer.

An independent cold-start pair captured 70 raw frames from absolute frames 42860–42929, with START mask 0x10 for one frame at 42869 in the input run and no input in the control. Both manifests bind the same authenticated Japanese BIOS and retail CUE. Their complete raw VDP1/VDP2 streams are byte-identical (SHA-256 f6e905f19a34125414f74e3daab64fd91b744361d2c80084e7d08e9d6734ae57, 110,434,202 bytes). The input branch trace records R2=0x10 at frame 42870 versus R2=0 in control; at the observed PC=0x06039290 both runs retain T=0, taken=0, and the same opcode=0x8d5d. The trace therefore shows a transient register difference, not a taken START branch or a visual transition. Its branch-trace SHA-256 values are 1d3f0ec113b0383cfc65c76482f89220ff4878572628ebf20bead43245a6ad01 (input) and 8933f0358b0ffcb943e40bd4700eb5e56bf9da83b46b250564f3291663e74c30 (control). Under scripts/analyze_nexus_tm_bin_vdp_owner.py’s historical static DM.BIN code mapping 0x06010040, this PC maps to file offset 0x29250. This mapping is a code-location hypothesis for that older trace, not a global raw-image placement. The hash-verified Japanese DM.BIN (3bbca125e0bfb486897e4926541e7c31adbff010d01a9b0c736637f432aad124) contains opcode 0x8903 at that offset, not the runtime-traced 0x8d5d. This direct mapping therefore does not bind the branch to a retail code span; relocation or runtime code provenance remains unknown. The retail input/action consumer and Firestaff’s production title-to-menu parity remain unverified.

The rendered frames are in /home/trv2/work/firestaff-nexus-title-start-pair-real15/; the branch pair is in /home/trv2/work/firestaff-nexus-start-branch-trace-20261002/. These captures are supporting negative evidence only; they do not authorize title, menu, HUD, or viewport renderer admission.

Matched A-input and no-input title-window capture, 2026-10-23

A fresh 40-frame comparison used the authenticated Japanese BIOS dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac, the Japanese retail CUE 878b3bc223bb56e99626dbaa2946d17fb349a2a67a10cd4423742d8ab30b8699, and instrumented Mednafen 1.32.1 builds on the Mac mini M4. The A run scheduled mask 0x20 for frames 10510–10511; the control scheduled no input in frames 10500–10539. Both raw captures contain 40 frames and have the same SHA-256 5f038b424de4564e7a354190d0f24ab79fee3b989d02e0297a6ac3693b82affd. The comparator confirms all eight captured VDP1/VDP2 regions are identical.

The A-side SMPC trace records four reads of controller port 0x10 per frame from master-SH-2 PCs 0x0601456c, 0x06014582, 0x06014594 and 0x060145b0. All four return 0x10 while A is held (frames 10510–10511) and return 0x00 before and after. The matched no-input trace returns 0x00 at all four PCs for those frames. Each observed pipeline instruction word matches the corresponding bytes in retail TM.BIN extracted from the CUE; TM.BIN SHA-256 is d87485fe6eba1f6e9fbbf487f5fcdd994911136905e6172e5bb5bc0122407eb6. This binds the controller read to the authentic retail reader. It does not show a menu action: the VDP state remains byte-identical during this title window, so the Nexus title-to-menu handoff is still blocked pending a capture of the actual interactive menu and its source-owned transition.

The A capture and traces remain under /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-a-state-20261019/; the no-input control remains under /Volumes/Extern-disk/nexus-saturn-capture/run-codex-menu-noinput-smpc-control-20261023/. These external reference captures are not Firestaff renderer output.

Ymir VDP2 bitmap/source join, frame 278, 2026-10-24

The new Ymir memory bundle reader accepts a Ymir dump directory directly in analyze_nexus_vdp2_bitmap_source.py. It checks the frame receipt against the register snapshot, requires a completed trace with commands for that frame, and validates the VDP2 register, VRAM, and CRAM lengths before decoding. Run it with the authenticated Japanese retail corpus:

python3 scripts/analyze_nexus_vdp2_bitmap_source.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp1-bundle-20261024 \
  --data-dir /Volumes/Extern-disk/FirestaffUserData/data/nexus

The source CUE SHA-256 is 878b3bc223bb56e99626dbaa2946d17fb349a2a67a10cd4423742d8ab30b8699. The authentic frame receipt identifies frame 278; its VDP2 registers enable NBG1 8-bit bitmap mode at VRAM byte offset 0x020000 for a 512x256 span. The bounded join finds no exact match among the decoded retail menu, font, title, and level palette sources. The longest reported partial spans are 118 bytes from TITLE.BIN map 4 and 99 bytes from MENU.BPK; palettes do not match CRAM. The analyzer therefore reports source_join=unbound and semantic_admission=blocked. This Ymir boot state does not identify the date screen, menu, HUD, or viewport owner, and does not authorize rendering.

Ymir synchronized VDP2 memory bundle, frame 295, 2026-10-24

The standalone vdp2-memory-write-bundle.txt format is accepted directly by analyze_nexus_vdp2_bitmap_source.py. The reader checks the bundle-complete receipt, exact CSV row count, trace frame interval no later than the snapshot, memory address, width, and value bounds, plus VDP2 register, VRAM, and CRAM sizes. Ymir captures the receipt, registers, VRAM, CRAM, and trace in one emulator-thread callback. The authentic Japanese retail run used --debug and the Japanese IPL SHA-256 dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac, and the retail CUE SHA-256 878b3bc223bb56e99626dbaa2946d17fb349a2a67a10cd4423742d8ab30b8699.

The exported receipt binds frame 295 to 17,280 trace rows across frames 205–295, with zero dropped writes; 172 rows belong to frame 295. The final VRAM and CRAM snapshots match replay of the last write to each of 192 unique written byte locations. The dump decodes NBG1 as an 8-bit bitmap at byte offset 0x020000, 512×256. Against the authentic Japanese retail corpus, the longest exact byte runs remain partial (118 bytes from TITLE.BIN map 4 and 99 bytes from MENU.BPK); there are no exact bitmap or title, menu, STABG, or DGN palette matches in CRAM. Firestaff reports source_join=unbound and semantic_admission=blocked. The emulator showed the Japanese date/time setup screen, but no date-screen, menu, HUD, or viewport ownership is inferred from the coincident bitmap contents.

The complete external bundle and screenshot are retained under /Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-memory-bundle-20261024-frame295/. Re-run the analysis against the authentic local corpus with:

python3 scripts/analyze_nexus_vdp2_bitmap_source.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-memory-bundle-20261024-frame295 \
  --data-dir /Volumes/Extern-disk/FirestaffUserData/data/nexus

Ymir Japanese date/time setup bitmap and CRAM traces, frames 105 and 118, 2026-10-24

Two additional bundles were captured from the authentic Japanese retail CUE and IPL identified above using Ymir --debug. The frame-105 bundle filters VRAM writes to 0x020000–0x040000; it contains 164,924 rows from frames 24–31, zero dropped rows, and a complete 128 KiB NBG1 bitmap snapshot. Replaying all trace writes byte by byte in trace order reproduces that region exactly. The snapshot is later than the last filtered write, so the receipt correctly binds the full trace interval to the snapshot frame rather than requiring a write in the snapshot frame.

The frame-118 bundle filters CRAM writes to 0x000–0x1000; it contains 272 rows from frame 24 and zero dropped rows. The two snapshots have identical NBG1 VRAM regions and CRAM bytes. Interpreting the captured registers identifies NBG0 as a 512×256 8bpp bitmap at VRAM 0x000000 with BMPNA palette selector 0, and NBG1 as a 512×256 8bpp bitmap at 0x020000 with selector 1. The captured NBG0 bytes, decoded with the captured CRAM, visibly form the Japanese date/time setup text and cursor; NBG1 contains the corresponding box shapes. The trace does not bind this screen to Nexus code or assets and does not prove that the retail game booted; treat it as a pre-game/system setup state until a source-PC or disc-read trace establishes otherwise. These observations do not provide Nexus startup/menu parity or authorize renderer admission.

analyze_nexus_vdp2_bitmap_source.py now compares both active bitmap layers against the hash-verified retail source corpus. For both frames 105 and 118 it reports zero exact source matches on either layer, source_join=unbound, and semantic_admission=blocked. Its longest byte runs are partial only (35 bytes from TITLE.BIN maps 1/3 on NBG0; 118 bytes from TITLE.BIN map 4 and 99 bytes from MENU.BPK on NBG1). The tested TITLE, MENU, STABG, and 1,266 DGN palette anchors have no exact or word-swapped CRAM match. This advances emulator-state inspection but leaves game boot, source identity, palette owner, and production rendering unresolved. Do not count this as a game startup/menu witness.

The complete bitmap bundle and CRAM bundle, including raw register, VRAM, CRAM, trace, and screenshots, are retained at:

Re-run against the authentic corpus with:

python3 scripts/analyze_nexus_vdp2_bitmap_source.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-startup-bitmap-20261024 \
  --data-dir /Volumes/Extern-disk/FirestaffUserData/data/nexus
python3 scripts/analyze_nexus_vdp2_bitmap_source.py \
  /Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-startup-cram-20261024 \
  --data-dir /Volumes/Extern-disk/FirestaffUserData/data/nexus

Source references

The byte-format baseline is the DMWeb Nexus file-format documentation, including the ITEM.IBS description and DGN documentation. Use Greatstone SCK to cross-check authentic DM-family asset extraction and documented byte formats; the reviewed page inventory is in DMWeb and Greatstone Reference. Neither reference establishes Saturn VDP register behavior or proves a retail runtime consumer. The NBG1 address-unit correction above is instead grounded in the Ymir/Mednafen hardware trace and authentic capture. Disassembly and runtime ownership must be established from the Saturn program and an execution capture; inferred offsets or host screenshots must not be promoted to production semantics.