Repository navigation
UCI: drop the trailing zero pad; the PRG no longer loads into I/O (#288) [re-land of #295] - #298
Merged
Merged
Conversation
#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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 theretire/p384-lanebranch (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:
TRUST_STORE=1 TRUST_RELEASE=1)fd37600b…025af934…810f1158…(unchanged)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 = yeszero pad (OVERLAY_FILE_PAD) whose original job was positioning the$E000P-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
TRUST_STORE=1 TRUST_RELEASE=1)7059930a…$BFFF,fd37600b…936884e9…$CFFF,025af934…810f1158…) and uci-m3 (e7b143ed…) are unchanged.$C000-$DFFFbefore writing it (TCP ring is an equate with head/tail zeroed at connect; viewer tables written first; trust bundle gated ontb_loaded; comb'sCOLD_IMAGEstays file-backed;$01never 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:
$DC06/$DC09/$DC0Aobserved across runs) for the full 300-900 s;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.pycomputes 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.$(PRG)recipe; real hashes unchanged).make package-verify: step 1b range-checks everydist/*.prg; boots now go through a real TDE serial load (-drive8truedrive +trapdevice8 +autostart-handle-tde) and read backDrive8TrueEmulation=1,TrapDevice8=0,AutostartHandleTrueDriveEmulation=0. 14/14, RELEASE ARTIFACTS VERIFIED on 053003b.tools/uci/_memory_policy.pyreserves the TCP ring and$D000-$DFFFexplicitly (the pad region used to do it implicitly).Evidence
BACKEND=uci run_all_tests.py440/440;test_cold_bank117;test_rig_scratch --build18/18;test_package_verify41/41;measure_margins --buildexit 0; pytest green apart from the known ip65-check-on-a-uci-build refusal.boot_check.pyPASS onfd37600b…and025af934…;rig_https_local.pyPASS 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.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:
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.
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:
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=1stops 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).
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