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
- 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
- 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 isacacec7595eaae5175b15253a63345daeae5b1613b17865779cf6c06818d37f9; the frame-43108 PPM SHA-256 ise47c9b0685d7a81bb4526a86b846bdecca7b79ff13d770002907182e2016253f. The matched control-follow-up has the same PPM hashes at both frames and the same 600-frame raw capture hash0bcf7126e458782169b9d73b59325790643acf01962b32c909327c573c073f8f. 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:
/Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-startup-bitmap-20261024//Volumes/Extern-disk/nexus-saturn-capture/run-ymir-vdp2-startup-cram-20261024/
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.