Skip to content

UCI: drop the trailing zero pad; the PRG no longer loads into I/O (#288) [re-land of #295] - #298

Merged
JC-000 merged 8 commits into
masterfrom
fix/288-drop-trailing-pad
Oct 14, 2026
Merged

JC-000 merged 8 commits into
masterfrom
fix/288-drop-trailing-pad

Conversation

@JC-000

@JC-000 JC-000 commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Fixes #288.

Why this PR exists. This re-lands #295 on master. #295 was stacked on #294, with base retire/p384-lane. #294 merged first, and #295 then merged into the retire/p384-lane branch (76269e5) instead of master. So the fix never reached master, and #288 stayed open. My mistake: I had said I would retarget #295 to master after #294 merged, and I didn't.

The head is the same reviewed commit, 053003b, adversary CLEAR after 2 rounds. Its diff against master is exactly #295's change, and it merges cleanly onto master b9aaac0, which already holds #294 and #296.

Checked on the merged tree (master b9aaac0 + 053003b), 2026-10-14. The hashes are identical to those in #295's body:

build PRG
uci-onchip store (TRUST_STORE=1 TRUST_RELEASE=1) 47,105 B fd37600b…
uci-comb store 51,201 B 025af934…
ip65-onchip 810f1158… (unchanged)
uci-m3 c64267c4…, same as master b9aaac0 (unchanged by this PR)

#296 touched only the uci-m3 adapter and rigs, so it does not interact with this change.


The original #295 description follows.

The UCI cfgs carried a fill = yes zero pad (OVERLAY_FILE_PAD) whose original job was positioning the $E000 P-384 blob, which #294 retires. It put zeros in the PRG file through $D000-$DFFF, so a real KERNAL LOAD wrote them into the VIC/SID/CIA registers.

Result

product (store build, TRUST_STORE=1 TRUST_RELEASE=1) before (17654bf) after
uci-onchip 55,297 B 7059930a… 47,105 B, ends $BFFF, fd37600b…
uci-comb 55,297 B 936884e9… 51,201 B, ends $CFFF, 025af934…
  • All seven UCI profiles: each new PRG is a byte-prefix of the old one, and the dropped tails are all zero. ip65 (810f1158…) and uci-m3 (e7b143ed…) are unchanged.
  • Zero-reliance audit: nothing reads $C000-$DFFF before writing it (TCP ring is an equate with head/tail zeroed at connect; viewer tables written first; trust bundle gated on tb_loaded; comb's COLD_IMAGE stays file-backed; $01 never maps RAM under I/O).

The disk-load hang ("TDE load never completes")

Measured in VICE via c64-test-harness, true drive emulation on, read-then-resume probe:

  • old store images froze in CIA1's page ($DC06 / $DC09 / $DC0A observed across runs) for the full 300-900 s;
  • new images reach the banner in 10-21 s, same as the ip65 control.
  • A second, independent cause: verify_release.py's poll loop (sleep -> resume -> read) left VICE halted between reads. Review reproduced both halves: old image + new loop freezes; new image + old loop crawls.

Guards

  • tools/check_prg_io_range.py computes each cfg area's load range in ld65 file order (expressions evaluated, ld65 booleans, fail-closed on anything it cannot parse or a duplicate MEMORY/SEGMENTS block); tools/test_prg_io_range.py (pytest testpaths) holds every review mutant as a red case.
  • Makefile post-link check refuses any PRG loading into the I/O page and leaves no PRG (every profile goes through the one $(PRG) recipe; real hashes unchanged).
  • make package-verify: step 1b range-checks every dist/*.prg; boots now go through a real TDE serial load (-drive8truedrive +trapdevice8 +autostart-handle-tde) and read back Drive8TrueEmulation=1, TrapDevice8=0, AutostartHandleTrueDriveEmulation=0. 14/14, RELEASE ARTIFACTS VERIFIED on 053003b.
  • tools/uci/_memory_policy.py reserves the TCP ring and $D000-$DFFF explicitly (the pad region used to do it implicitly).

Evidence

  • BACKEND=uci run_all_tests.py 440/440; test_cold_bank 117; test_rig_scratch --build 18/18; test_package_verify 41/41; measure_margins --build exit 0; pytest green apart from the known ip65-check-on-a-uci-build refusal.
  • U64E @ 48 MHz via the DeviceLock queue: boot_check.py PASS on fd37600b… and 025af934…; rig_https_local.py PASS on the non-store onchip SNI build (a665e6f9…) and comb SNI build (6e4c6c51…). Those runs predate the guard/doc commits, which do not move any PRG hash.
  • Adversarial review: two rounds; round-1 findings F1-F6 and round-2 G1 fixed with red-green evidence (see commit messages). The final G1 commit (053003b: checker + test + notes) was not separately re-attacked.

Commits (messages verbatim)

967c5a5 UCI cfgs: drop the trailing zero pad; the PRG no longer loads into I/O (#288)

OVERLAY_FILE_PAD ran the UCI images on to $DFFF with zeros. It existed
to put the P-384 curve blob at $E000, which the P-384 retirement
removed, and it made every KERNAL LOAD of a UCI image (disk, .d64, a
real 1541) store zeros into the VIC/SID/colour RAM/CIA registers at
$D000-$DFFF, CIA2 (the IEC bus the load is running over) included.

Each image now ends at its last real byte. Every new PRG is a byte
prefix of the old one (head -c old | cmp - new: exit 0) and
the dropped tail held no non-zero byte; segment lists are identical:

profile old B sha256 new B end sha256
uci 55297 bac5d2ef8885.. 47105 $BFFF 9abf7d069cad..
uci-onchip 55297 d5fe4a546336.. 47105 $BFFF 426d501cfa71..
uci-comb 55297 662d7fd9db1f.. 51201 $CFFF 36626d5c77d9..
uci-comb COLD_BANK=0
55297 aefed6c732fd.. 47105 $BFFF e94117ba1f94..
onchip-store* 55297 a3fdc7728e77.. 47105 $BFFF 5571c41c5d5a..
comb-store* 55297 6a5a2993ba6c.. 51201 $CFFF 4c23bc3d5dde..
comb demo-store 55297 b96e1a4a4797.. 51201 $CFFF c4b50ecdbf81..
ip65-onchip, uci-m3: unchanged (b262f3f9.., e7b143ed..); neither cfg
had the pattern.
(* = the package matrix args incl. TRUST_RELEASE=1)

Nothing relied on load-time zeros above $BFFF: no segment and no
label other than the pad's own _OVERLAY_FILE_PAD* symbols lies in
$C000-$DFFF in any of the seven maps (comb's COLD_IMAGE stays
file-backed to $CFFF, unchanged); the TCP ring is an equate whose
head/tail net_tcp_connect zeroes and whose reads stop at the tail;
the viewer's $CE00 tables are written before read and sit in the ring
the fetch has already filled; trust_bundle reads TB_BUF only after a
file was read into it; $01 is only ever toggled for BASIC, so nothing
reads RAM under I/O.

New guard tools/test_prg_io_range.py (pytest testpaths): every cfg's
MEMORY areas must stay off $D000-$DFFF unless file = "", plus a PRG
load-range check standalone. Red-green: with the base branch's three
UCI cfgs restored it fails
AssertionError: c64-https-uci-onchip-cold.cfg:
['OVERLAY_FILE_PAD $D000-$DFFF file=%O'] (1 failed, 1 passed)
and standalone on the base PRGs it FAILs all seven UCI images
("loads $0801-$DFFF, into $D000-$DFFF", exit 1); with this change both
pass (exit 0). test_cold_bank.py's cfg pin follows the comb cfg (118
checks passed).

b34f3dd package-verify: boot every .d64 through a real TDE serial load (#288)

The boot step loaded through the KERNAL traps because "under true drive
emulation the load never completes". Measured for #288, that had two
causes:

  1. The poll halted the machine. Every binary-monitor command halts the
    6510 until resume(); the loop did sleep, resume, read, so VICE sat
    stopped through each sleep (0.0% CPU) and a TDE load crawled at
    ~0.5 B/s on every image, the ip65 control included. It now reads,
    then resumes.
  2. The UCI images ran to $DFFF. With the machine running, the old
    55,297 B images froze under TDE with the load pointer at $DC06
    (uci-onchip, a3fdc772.., 900 s) and $DC09 (uci-comb, 6a5a2993..),
    inside CIA1's page; the ip65 image (ends $BFFF) loaded and showed
    the menu in 21 s, as did the new uci-onchip (5571c41c..) and
    uci-comb (4c23bc3d..) images.

Red-green: base-branch UCI images under TDE: no banner in 900 s (load
frozen in $DC0x); this branch's images: banner in 21 s. make
package-verify with the new boot step: 11/11 checks, RELEASE ARTIFACTS
VERIFIED; banners at 10 s (ip65-onchip), 15 s (uci-comb), 10 s
(uci-onchip); all three PRG hashes reproduce.

The #288 hypothesis named CIA2 ($DD00/$DD02); the measured stop is in
CIA1's page, before $DD00. Which write stops it was not isolated.

Docs: CLAUDE.md and README memory layout ($C000-$CFFF ring by equate,
$D000-$DFFF never in the PRG), README PRG sizes, engineering-notes
measurement record under Packaging.

dac5e6e README: product table re-measured at the nistcurves v0.15.0 pin

Package matrix args (TRUST_RELEASE=1), make clean before each:
ip65-onchip 47,105 B / 2,420 labels (810f1158..), uci-onchip 47,105 B /
2,939 (fd37600b..), uci-comb 51,201 B / 3,181 (025af934..).

ec36d4e rig_scratch: regenerate the profile fixture after dropping the pad (#288)

python3 tools/test_rig_scratch.py --build --write-fixtures on this
branch (nistcurves v0.15.0): the captured labels lose the
_OVERLAY_FILE_PAD* rows and comb's COLD_RUN*FILEOFFS moves
$D801 -> $C801; 17/17 passed. COMB_SEGMENTS inside the test is a dated
capture of the old carve-out (it still carries OVERLAY_BLOB_CURVE_RAM
too) and is left as the historical input it documents.

ec108f7 I/O-range guard: compute load addresses, check at link and release (#288 review F1, F2)

F1. The cfg half of tools/test_prg_io_range.py decided by an area's
START address with single-line regexes. Three shapes beat it with
pytest green (old guard returned [] for each):
(a) size = $1000 + $1000 (relinks the 55,297 B image)
(b) the old pad split over two lines
(c) HIGHPAD: start = $E000, size = $1100, file = %O, fill = yes after
the last area: its bytes follow $BFFF in the file, so the PRG
loads $0801-$D0FF
New tools/check_prg_io_range.py computes where each output-writing
area LOADS from file order (header = first writing area, 2 bytes; fill
areas write their size; others write up to their size only if a non
bss/zp segment loads into them), evaluates size/start expressions, and
raises on any entry, attribute or expression it cannot parse. Against
the three mutants: OVERLAY_FILE_PAD loads $C000-$DFFF (a, b), HIGHPAD
loads $C000-$D0FF (c); 17654bf's three UCI cfgs: OVERLAY_FILE_PAD at
$C000-$DFFF / $C000-$DFFF / $D000-$DFFF. Real cfgs map to the linked
ends: ip65/uci/uci-onchip $BFFF, onchip-cold $CFFF, m3 $9FFF (upper
bound). The test now holds the mutants and three fail-closed cases as
red cases.

The PRG half now runs automatically:

  • Makefile, after every link (shell, no new build dependency):
    the load address from the PRG's header + its length. Red: with
    17654bf's uci.cfg, make BACKEND=uci USE_NISTCURVES_ONCHIP=1 TRUST_STORE=1 stops with "ERROR: build/c64-https.prg loads
    $0801-$DFFF, into the I/O page ..." and no PRG is left. Green: the
    same build is fd37600b... (byte-identical to before the check).
  • make package-verify: new step 1b range-checks every dist/*.prg.

F2. The boot gate now reads the drive setup back from the running VICE
(resource_get) and fails the image unless Drive8TrueEmulation=1,
TrapDevice8=0, AutostartHandleTrueDriveEmulation=0 (names and values
probed: TDE flags give 1/0/0, the old trap flags 0/1/0). Its docstring
says what it cannot see: an I/O write that does not hang the load; the
range check is what catches that. test_package_verify.py pins TDE_ARGS,
that the gate launches with them and reads the resources back, and
tde_resource_problems on held/trap/handled/unreadable values. Red:
reverting TDE_ARGS to the trap flags -> 2 FAIL (39/41); dropping the
readback in the gate -> 1 FAIL (40/41); restored 41/41.

60295c9 rig write guard: reserve the TCP ring and the I/O page (#288 review F3)

The dropped OVERLAY_FILE_PAD region was what reserved $C000-$DFFF in
the rigs' MemoryPolicy; without it a write there was 'unknown' (a
warning, not a refusal) on every UCI profile: $C000/$C800/$CF00/$D000/
$DC00 on onchip-store, $D000/$DC00 on comb. build_policy now reserves
tcp_recv_buf .. +$1000 (the label, $C000 by default) and $D000-$DFFF
on every profile.

test_rig_scratch.py::test_policy_reserves_the_ring_and_the_io_page
probes $C000 $C800 $CF00 $CFFF $D000 $D020 $DC00 $DFFF on every
captured profile. Red with the previous _memory_policy.py: 'uci: a
write at $C000 is allowed' (17/18). Green: 18/18.

bf521e4 docs: re-cite the #288 TDE record with build args; prose fixes (review F4-F6)

F4. engineering-notes cited a3fdc772/6a5a2993/b262f3f9/5571c41c/4c23bc3d,
which are pre-rebase images (3f420f2 / b4a88b1, nistcurves v0.14.0) and
reproduce at neither 17654bf nor this branch. The record is now a table
naming tree, build args and hash for each image, plus the rebased pair,
re-measured with the same read-then-resume probe: 17654bf onchip-store
7059930a... and comb-store 936884e9... froze at $DC0A (300 s); this
branch's fd37600b... and 025af934... reached the banner in 21 s.

F5. The stop is stated as observed: in CIA1's page, $DC06/$DC09/$DC0A
(verify_release.py docstring, check/test headers). The test header no
longer leads with the refuted CIA2/IEC story. The U64 claim is now only
what was measured in review: its DMA load of the old image left
$D020/$D021/$DC02/$DC03/$DD02 unchanged, so only RAM changes there.

F6. library-ingestion-architecture.md marks the $C000-$DFFF zero fill
historical; _prg_load.py's RAM_WINDOW comment no longer says other
images carry zeros there; CLAUDE.md's $CFFF end is the COLD_BANK=1 comb
only.

053003b check_prg_io_range: ld65 fill booleans, one MEMORY/SEGMENTS block; notes count (#288 re-attack G1)

G1. load_map took only fill = yes as fill, and read only the first
MEMORY/SEGMENTS block. fill now evaluates as an ld65 boolean
(yes/no/true/false or a numeric expression; anything else is a
CfgError), and a second MEMORY or SEGMENTS block is a CfgError. The
docstring now lists exactly what fails closed and what is ignored.

Red, against bf521e4's checker -> this one:
old pad with fill = true [] -> OVERLAY_FILE_PAD loads $C000-$DFFF
old pad with fill = 1 [] -> OVERLAY_FILE_PAD loads $C000-$DFFF
pad in a 2nd MEMORY block [] -> CfgError: 2 MEMORY blocks
test_prg_io_range.py holds them (plus fill = maybe and a 2nd SEGMENTS
block) as red cases: with bf521e4's checker 2 failed, 2 passed; with
this one 4 passed.

Doc: engineering-notes cited package-verify as 11/11 for this branch;
the run on the final images is 14/14 (3 reproducibility, 3 step-1b
load ranges, 3 d64 contents, 3 TDE boots, 2 listener).

🤖 Generated with Claude Code

JC-000 and others added 8 commits October 12, 2026 15:05
#288)

OVERLAY_FILE_PAD ran the UCI images on to $DFFF with zeros. It existed
to put the P-384 curve blob at $E000, which the P-384 retirement
removed, and it made every KERNAL LOAD of a UCI image (disk, .d64, a
real 1541) store zeros into the VIC/SID/colour RAM/CIA registers at
$D000-$DFFF, CIA2 (the IEC bus the load is running over) included.

Each image now ends at its last real byte. Every new PRG is a byte
prefix of the old one (head -c <new size> old | cmp - new: exit 0) and
the dropped tail held no non-zero byte; segment lists are identical:

  profile          old B  sha256          new B  end    sha256
  uci              55297  bac5d2ef8885..  47105  $BFFF  9abf7d069cad..
  uci-onchip       55297  d5fe4a546336..  47105  $BFFF  426d501cfa71..
  uci-comb         55297  662d7fd9db1f..  51201  $CFFF  36626d5c77d9..
  uci-comb COLD_BANK=0
                   55297  aefed6c732fd..  47105  $BFFF  e94117ba1f94..
  onchip-store*    55297  a3fdc7728e77..  47105  $BFFF  5571c41c5d5a..
  comb-store*      55297  6a5a2993ba6c..  51201  $CFFF  4c23bc3d5dde..
  comb demo-store  55297  b96e1a4a4797..  51201  $CFFF  c4b50ecdbf81..
  ip65-onchip, uci-m3: unchanged (b262f3f9.., e7b143ed..); neither cfg
  had the pattern.
  (* = the package matrix args incl. TRUST_RELEASE=1)

Nothing relied on load-time zeros above $BFFF: no segment and no
label other than the pad's own __OVERLAY_FILE_PAD_* symbols lies in
$C000-$DFFF in any of the seven maps (comb's COLD_IMAGE stays
file-backed to $CFFF, unchanged); the TCP ring is an equate whose
head/tail net_tcp_connect zeroes and whose reads stop at the tail;
the viewer's $CE00 tables are written before read and sit in the ring
the fetch has already filled; trust_bundle reads TB_BUF only after a
file was read into it; $01 is only ever toggled for BASIC, so nothing
reads RAM under I/O.

New guard tools/test_prg_io_range.py (pytest testpaths): every cfg's
MEMORY areas must stay off $D000-$DFFF unless file = "", plus a PRG
load-range check standalone. Red-green: with the base branch's three
UCI cfgs restored it fails
  AssertionError: c64-https-uci-onchip-cold.cfg:
    ['OVERLAY_FILE_PAD $D000-$DFFF file=%O']   (1 failed, 1 passed)
and standalone on the base PRGs it FAILs all seven UCI images
("loads $0801-$DFFF, into $D000-$DFFF", exit 1); with this change both
pass (exit 0). test_cold_bank.py's cfg pin follows the comb cfg (118
checks passed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The boot step loaded through the KERNAL traps because "under true drive
emulation the load never completes". Measured for #288, that had two
causes:

1. The poll halted the machine. Every binary-monitor command halts the
   6510 until resume(); the loop did sleep, resume, read, so VICE sat
   stopped through each sleep (0.0% CPU) and a TDE load crawled at
   ~0.5 B/s on every image, the ip65 control included. It now reads,
   then resumes.
2. The UCI images ran to $DFFF. With the machine running, the old
   55,297 B images froze under TDE with the load pointer at $DC06
   (uci-onchip, a3fdc772.., 900 s) and $DC09 (uci-comb, 6a5a2993..),
   inside CIA1's page; the ip65 image (ends $BFFF) loaded and showed
   the menu in 21 s, as did the new uci-onchip (5571c41c..) and
   uci-comb (4c23bc3d..) images.

Red-green: base-branch UCI images under TDE: no banner in 900 s (load
frozen in $DC0x); this branch's images: banner in 21 s. make
package-verify with the new boot step: 11/11 checks, RELEASE ARTIFACTS
VERIFIED; banners at 10 s (ip65-onchip), 15 s (uci-comb), 10 s
(uci-onchip); all three PRG hashes reproduce.

The #288 hypothesis named CIA2 ($DD00/$DD02); the measured stop is in
CIA1's page, before $DD00. Which write stops it was not isolated.

Docs: CLAUDE.md and README memory layout ($C000-$CFFF ring by equate,
$D000-$DFFF never in the PRG), README PRG sizes, engineering-notes
measurement record under Packaging.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Package matrix args (TRUST_RELEASE=1), make clean before each:
ip65-onchip 47,105 B / 2,420 labels (810f1158..), uci-onchip 47,105 B /
2,939 (fd37600b..), uci-comb 51,201 B / 3,181 (025af934..).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
)

python3 tools/test_rig_scratch.py --build --write-fixtures on this
branch (nistcurves v0.15.0): the captured labels lose the
__OVERLAY_FILE_PAD_* rows and comb's __COLD_RUN_*_FILEOFFS__ moves
$D801 -> $C801; 17/17 passed. COMB_SEGMENTS inside the test is a dated
capture of the old carve-out (it still carries OVERLAY_BLOB_CURVE_RAM
too) and is left as the historical input it documents.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
 review F1, F2)

F1. The cfg half of tools/test_prg_io_range.py decided by an area's
START address with single-line regexes. Three shapes beat it with
pytest green (old guard returned [] for each):
  (a) size = $1000 + $1000          (relinks the 55,297 B image)
  (b) the old pad split over two lines
  (c) HIGHPAD: start = $E000, size = $1100, file = %O, fill = yes after
      the last area: its bytes follow $BFFF in the file, so the PRG
      loads $0801-$D0FF
New tools/check_prg_io_range.py computes where each output-writing
area LOADS from file order (header = first writing area, 2 bytes; fill
areas write their size; others write up to their size only if a non
bss/zp segment loads into them), evaluates size/start expressions, and
raises on any entry, attribute or expression it cannot parse. Against
the three mutants: OVERLAY_FILE_PAD loads $C000-$DFFF (a, b), HIGHPAD
loads $C000-$D0FF (c); 17654bf's three UCI cfgs: OVERLAY_FILE_PAD at
$C000-$DFFF / $C000-$DFFF / $D000-$DFFF. Real cfgs map to the linked
ends: ip65/uci/uci-onchip $BFFF, onchip-cold $CFFF, m3 $9FFF (upper
bound). The test now holds the mutants and three fail-closed cases as
red cases.

The PRG half now runs automatically:
  * Makefile, after every link (shell, no new build dependency):
    the load address from the PRG's header + its length. Red: with
    17654bf's uci.cfg, `make BACKEND=uci USE_NISTCURVES_ONCHIP=1
    TRUST_STORE=1` stops with "ERROR: build/c64-https.prg loads
    $0801-$DFFF, into the I/O page ..." and no PRG is left. Green: the
    same build is fd37600b... (byte-identical to before the check).
  * make package-verify: new step 1b range-checks every dist/*.prg.

F2. The boot gate now reads the drive setup back from the running VICE
(resource_get) and fails the image unless Drive8TrueEmulation=1,
TrapDevice8=0, AutostartHandleTrueDriveEmulation=0 (names and values
probed: TDE flags give 1/0/0, the old trap flags 0/1/0). Its docstring
says what it cannot see: an I/O write that does not hang the load; the
range check is what catches that. test_package_verify.py pins TDE_ARGS,
that the gate launches with them and reads the resources back, and
tde_resource_problems on held/trap/handled/unreadable values. Red:
reverting TDE_ARGS to the trap flags -> 2 FAIL (39/41); dropping the
readback in the gate -> 1 FAIL (40/41); restored 41/41.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The dropped OVERLAY_FILE_PAD region was what reserved $C000-$DFFF in
the rigs' MemoryPolicy; without it a write there was 'unknown' (a
warning, not a refusal) on every UCI profile: $C000/$C800/$CF00/$D000/
$DC00 on onchip-store, $D000/$DC00 on comb. build_policy now reserves
tcp_recv_buf .. +$1000 (the label, $C000 by default) and $D000-$DFFF
on every profile.

test_rig_scratch.py::test_policy_reserves_the_ring_and_the_io_page
probes $C000 $C800 $CF00 $CFFF $D000 $D020 $DC00 $DFFF on every
captured profile. Red with the previous _memory_policy.py: 'uci: a
write at $C000 is allowed' (17/18). Green: 18/18.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…w F4-F6)

F4. engineering-notes cited a3fdc772/6a5a2993/b262f3f9/5571c41c/4c23bc3d,
which are pre-rebase images (3f420f2 / b4a88b1, nistcurves v0.14.0) and
reproduce at neither 17654bf nor this branch. The record is now a table
naming tree, build args and hash for each image, plus the rebased pair,
re-measured with the same read-then-resume probe: 17654bf onchip-store
7059930a... and comb-store 936884e9... froze at $DC0A (300 s); this
branch's fd37600b... and 025af934... reached the banner in 21 s.

F5. The stop is stated as observed: in CIA1's page, $DC06/$DC09/$DC0A
(verify_release.py docstring, check/test headers). The test header no
longer leads with the refuted CIA2/IEC story. The U64 claim is now only
what was measured in review: its DMA load of the old image left
$D020/$D021/$DC02/$DC03/$DD02 unchanged, so only RAM changes there.

F6. library-ingestion-architecture.md marks the $C000-$DFFF zero fill
historical; _prg_load.py's RAM_WINDOW comment no longer says other
images carry zeros there; CLAUDE.md's $CFFF end is the COLD_BANK=1 comb
only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tes count (#288 re-attack G1)

G1. load_map took only fill = yes as fill, and read only the first
MEMORY/SEGMENTS block. fill now evaluates as an ld65 boolean
(yes/no/true/false or a numeric expression; anything else is a
CfgError), and a second MEMORY or SEGMENTS block is a CfgError. The
docstring now lists exactly what fails closed and what is ignored.

Red, against bf521e4's checker -> this one:
  old pad with fill = true     []  -> OVERLAY_FILE_PAD loads $C000-$DFFF
  old pad with fill = 1        []  -> OVERLAY_FILE_PAD loads $C000-$DFFF
  pad in a 2nd MEMORY block    []  -> CfgError: 2 MEMORY blocks
test_prg_io_range.py holds them (plus fill = maybe and a 2nd SEGMENTS
block) as red cases: with bf521e4's checker 2 failed, 2 passed; with
this one 4 passed.

Doc: engineering-notes cited package-verify as 11/11 for this branch;
the run on the final images is 14/14 (3 reproducibility, 3 step-1b
load ranges, 3 d64 contents, 3 TDE boots, 2 listener).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@JC-000
JC-000 merged commit a7bccc9 into master Oct 14, 2026
@JC-000
JC-000 deleted the fix/288-drop-trailing-pad branch October 14, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UCI PRG load writes zeros through $D000-$DFFF I/O on KERNAL LOAD (possible cause of the TDE .d64 'never completes')

1 participant