Seam Demo — Three-Act Boundary Cartridge

Cartridge & Mapping Tests

Six megabytes of generated, seam-aware payload on a 48 Mbit ExHiROM cartridge (map mode $25, two mask ROMs), read three different ways in a loop. The centrepiece is one instruction whose opcode is the last byte of the first device and whose operand is the first byte of the second — the non-monotonic $FF:FFFF$40:0000 seam, executed for real.

loading core…

Self-running — no controls. Press Verify fidelity to read the three-act fold out of WRAM; a full cycle takes about 100 seconds.

Click the screen, then play. (Tab away and it pauses.)

What it is

One 48 Mbit ExHiROM cartridge — the same 32 + 16 Mbit mask-ROM pair as the 48 Mbit canary — whose entire six megabytes are generated, seam-aware payload. Three acts loop forever, and each reads the cartridge a different way, because a decode defect that a sequential read never touches is exactly the kind that ships.

The three acts

Act 1 — the cartridge is the program. A 16-opcode bytecode stream spans the whole image and a tiny VM executes it as generative line art. The 24-bit program counter is on screen the whole time; the centrepiece is the moment it rolls file $3FFFFF to $400000 — CPU $FF:FFFF to $40:0000, the physical boundary between the two mask ROMs. That instruction is hand-laid so its opcode is the last byte of device 1 and its operand is the first byte of device 2: one instruction, split across the seam, executed for real.

Act 2 — the boundary-hostile graph walk. A pointer-linked graph baked across the image, with edge targets placed adversarially from the address model so that every one of the 374 reachable 32 KiB decode cells is pointed at by something. 1,122 edges, of which 755 cross the device seam and 748 arrive through a mirror rather than a canonical window. The walk draws itself as a spreading web.

Act 3 — Mode 7 flyover. A camera path over a texture atlas that spans the cartridge, crossing seam-mapped pages as it flies. Mode 7 character data is tiled while the payload is linear, so the reorder happens on the way to VRAM: 64 eight-byte DMAs, one tile-row per v-blank, across the eight frames the camera spends on each page.

How it proves itself

Every act folds what it read into its own CRC; the cycle then folds those three into corpus_result. Nothing is checked against a hard-coded constant baked by hand — the payload, the expected traversals and the oracle all come from tools/snes_cartmap.py, a port of bsnes-jg's own bus decode, so the cartridge is measured against a model of the hardware rather than against itself. Host Python, host C and the console agree on all three: $F0E2, $36B6, $6D21, folding to $3277. Press Verify fidelity to read that fold out of WRAM.

A full cycle runs about 100 seconds; the verdict strip at the bottom carries each act's CRC as it completes.

The map it exercises

Ordinary LoROM and HiROM both stop at 4 MiB. The extended map splits the image in two and selects between the halves on the bank's high bit, inverted — which is why the boot vectors live in bank $40 and the file is not monotonic in CPU space.

seamdemo — 48 Mbit ExHiROM decode map — CPU bank decode mapmapping exhirom (map mode $25, slow ROM) — ROM 1 = 32Mbit @ file $000000, ROM 2 = 16Mbit @file $4000000123456789ABCDEF$00$10$20$30$40$50$60$70$80$90$A0$B0$C0$D0$E0$F0ROM 1 canonical — 32Mbit @ file $000000–$3FFFFFROM 1 mirror — same bytes, alternate CPU addressROM 2 canonical — 16Mbit @ file $400000–$5FFFFFROM 2 mirror — same bytes, alternate CPU addressWRAM ($7E/$7F) — hard-wired, never cartridgesystem area / I-O — bank low half below $C0, not cartridge512 half-bank cells (256 banks × 2 halves): 380 decode to cartridge ROM (192 canonical +188 mirror), 4 WRAM, 128 system-area.canonical windows$C0-$FF:0000-FFFF <- file $000000-$3FFFFF (ROM 1)$40-$5F:0000-FFFF <- file $400000-$5FFFFF (ROM 2)file→CPU seam(s)seam at file $400000: CPU bank drops $FF→$40 while the file offset keeps increasing — a segmentcursor must reload the bank byte here, never increment across it.device split2 physical mask ROMs: ROM 1 = 32Mbit @ file $000000–$3FFFFF, ROM 2 = 16Mbit @ file$400000–$5FFFFF