Skip to content

fix(crypto/shared): mul_tables.s stub size .res 256 -> .res 512 - #46

Merged
JC-000 merged 1 commit into
masterfrom
fix/mul-tables-stub-size
May 21, 2026
Merged

fix(crypto/shared): mul_tables.s stub size .res 256 -> .res 512#46
JC-000 merged 1 commit into
masterfrom
fix/mul-tables-stub-size

Conversation

@JC-000

Copy link
Copy Markdown
Owner

Summary

Single-file fix on src/crypto/shared/mul_tables.s: change the Phase-C stub reservations from .res 256 to .res 512 per table. Header comment block updated to document the 1 KB total / sqtab_hi = sqtab_lo + $0200 layout and the formula (sqtab_hi[n] << 8) | sqtab_lo[n] = floor(n²/4) for n ∈ 0..510.

Why now

Surfaced in JC-000/c64-lib-contract#5 (audit of the same primitive across five sibling libs). Every in-the-wild implementation — including this repo's own src/data.s:136 — uses 512 B per table. The stub's .res 256 would silently truncate at n ≥ 256 the moment any sibling lib redirected its sqtab_init to mul_tables_init per the Phase-C plan in the file header. Fixing it now keeps the precondition for the first sibling migration cheap.

Contract guarantees this preserves

Aligns the stub size with the canonical sqtab shape pinned in JC-000/c64-lib-contract#TBD (SPEC §8.1):

  • 512 B per table.
  • Layout sqtab_hi = sqtab_lo + $0200 (page-delta constraint).
  • Page-aligned base (TABLES_BSS segment placement preserved).

Scope

No segment changes. No API changes. No runtime change in Phase C.0 — CANONICAL_SQTAB is not defined in the default profile, so the labels in this file stay dormant; the larger reservation only takes effect once a sibling enables CANONICAL_SQTAB.

Build verified: make (default BACKEND=ip65) completes; build/c64-https.prg size unchanged.

Adoption pacing

Sibling-lib migration to the shared sqtab is opt-in per lib (c64-nist-curves has anchored its shape in the contract thread). This PR just removes the size-truncation tripwire before any redirect lands.

Refs: JC-000/c64-lib-contract#5, JC-000/c64-ChaCha20-Poly1305#34.

Each quarter-square multiply table is 512 bytes (n in 0..510 -> 511
entries, padded to 512), not 256. Every in-the-wild implementation
across sibling libs uses 512 B per table — including this repo's own
src/data.s:136 — but the Phase C.0 stub at .res 256 would silently
truncate at n >= 256 the moment any sibling redirected its init here.
Fixes the stub before that redirect lands. No segment changes, no
API changes, no runtime change in Phase C.0 (CANONICAL_SQTAB is
not defined; the labels here stay dormant). Build verified with
default profile (BACKEND=ip65).
Refs c64-lib-contract issue #5 (size bug confirmed in audit there)
and c64-ChaCha20-Poly1305 issue #34 (the originating
sibling-ingestion thread).
JC-000 added a commit to JC-000/c64-nist-curves that referenced this pull request May 20, 2026
Rewrites the sqtab address from a fixed equate to the SPEC §8.1
LIB_SHARED_SQTAB_BASE-derived shape so a consumer linking multiple
sqtab-using sibling libs (e.g. c64-wireguard ingesting both
c64-nist-curves and c64-chacha20-poly1305) supplies one base and the
libraries agree. Defaults preserve the historical $9c00 standalone
build; symbol addresses and PRG bytes are unchanged.
Changes in src/mul_8x8.s:
- sqtab_lo / sqtab_hi now derive from LIB_SHARED_SQTAB_BASE
(.ifndef-guarded default $9c00). Consumer override via
`ca65 --asm-define LIB_SHARED_SQTAB_BASE=$<addr>`.
- Two .assert guards per SPEC §8.1: page-aligned base
(cycle-stable abs,x indexing) and exact $0200 lo->hi delta
(SMC dispatch invariant). Catches the 2026-05-17 corruption
failure mode at assemble time rather than at boot.
- sqtab_init body + its scratch gated on .ifndef SHARED_SQTAB_INIT
so a shared-primitives module can supply the canonical
mul_tables_init without source patching.
Changes in src/lib_manifest.s:
- Adds LIB_SHARED_PRIMITIVES_SQTAB = $0001 bit constant + the
LIB_NISTCURVES_SHARED_PRIMITIVES manifest equate per SPEC §5 +
§8.0. Lets consumers .assert AND-of-bitmasks = 0 to catch
duplicate ownership at assemble time. Append-only.
CLAUDE.md "Known issues" sqtab paragraph reframed: the failure mode
description stays as load-bearing context, but the entry now points
at the .assert guards instead of describing the hard-coded equate
that no longer exists.
Build: 37302 B PRG, byte-identical to master.
sqtab_lo still $9c00, sqtab_hi still $9e00, sqtab_init unchanged.
Tests: tools/test_fp256.py 471/471 (smoke gate for multiply
correctness; same sqtab serves P-256 + P-384).
Refs: JC-000/c64-lib-contract#5, JC-000/c64-lib-contract#6,
JC-000/c64-https#46.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JC-000
JC-000 merged commit a1702c7 into masterMay 21, 2026
JC-000 added a commit that referenced this pull request Jul 20, 2026
…te (#56)
CLAUDE.md was partially updated during the session that produced
PRs #51-#55; several sections survived with pre-bump claims that no
longer match reality. This commit refreshes them in one pass:
- Crypto ABI: bump x25519 sibling pin v0.4.0 -> v0.6.0; mark P-256
as c64-lib-contract SPEC-aligned with the make -C libs/<X>
lib-VARIANT integration pattern.
- MEMORY requirements: rename CRYPTO -> CRYPTO_HOT (UCI) /
CRYPTO_RESIDENT (ip65) per the W1 hot/cold split; cross-link
c64-lib-contract repo + docs/library-ingestion-architecture.md.
- Build vars: document USE_X25519_SIBLING + EMBED_P256_OVERLAY.
- UCI Memory layout: collapse to a pointer at the post-W1 table
below.
- Known issues: rewrite the X25519 sibling block for v0.6.0 + the
post-W1 split; replace MemoryPolicy note with the centralized
VICE helper (tools/_vice_helpers.py::default_vice_config).
- ECDSA P-256 wall-clock: update to 82.1 s under v0.3.0 (was 86.7 s
under v0.2.0); drop the v0.2.0-era defensive-init footnote.
- Memory layout: replace pre-W1 monolithic CRYPTO table with the
full UCI + ip65 post-W1 layout; document the ip65 CRYPTO_COLD_SHADOW
1,662 B overflow tracked at c64-nist-curves#54.
docs/library-ingestion-architecture.md gets a "Status as of 2026-05-23"
appendix snapshotting which §3 library-side issues and §4 c64-https-side
work items landed via PRs #51-#55 vs. are still in-flight (Task #12 /
P-384 wall-clock / CI bot).
No source / cfg / integration script / submodule changes.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JC-000 added a commit that referenced this pull request Jul 20, 2026
…te (#57)
CLAUDE.md was partially updated during the session that produced
PRs #51-#55; several sections survived with pre-bump claims that no
longer match reality. This commit refreshes them in one pass:
- Crypto ABI: bump x25519 sibling pin v0.4.0 -> v0.6.0; mark P-256
as c64-lib-contract SPEC-aligned with the make -C libs/<X>
lib-VARIANT integration pattern.
- MEMORY requirements: rename CRYPTO -> CRYPTO_HOT (UCI) /
CRYPTO_RESIDENT (ip65) per the W1 hot/cold split; cross-link
c64-lib-contract repo + docs/library-ingestion-architecture.md.
- Build vars: document USE_X25519_SIBLING + EMBED_P256_OVERLAY.
- UCI Memory layout: collapse to a pointer at the post-W1 table
below.
- Known issues: rewrite the X25519 sibling block for v0.6.0 + the
post-W1 split; replace MemoryPolicy note with the centralized
VICE helper (tools/_vice_helpers.py::default_vice_config).
- ECDSA P-256 wall-clock: update to 82.1 s under v0.3.0 (was 86.7 s
under v0.2.0); drop the v0.2.0-era defensive-init footnote.
- Memory layout: replace pre-W1 monolithic CRYPTO table with the
full UCI + ip65 post-W1 layout; document the ip65 CRYPTO_COLD_SHADOW
1,662 B overflow tracked at c64-nist-curves#54.
docs/library-ingestion-architecture.md gets a "Status as of 2026-05-23"
appendix snapshotting which §3 library-side issues and §4 c64-https-side
work items landed via PRs #51-#55 vs. are still in-flight (Task #12 /
P-384 wall-clock / CI bot).
No source / cfg / integration script / submodule changes.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JC-000 added a commit that referenced this pull request Jul 20, 2026
* docs: refresh CLAUDE.md + ingestion plan for PR #46/#51-55 merged state
CLAUDE.md was partially updated during the session that produced
PRs #51-#55; several sections survived with pre-bump claims that no
longer match reality. This commit refreshes them in one pass:
- Crypto ABI: bump x25519 sibling pin v0.4.0 -> v0.6.0; mark P-256
as c64-lib-contract SPEC-aligned with the make -C libs/<X>
lib-VARIANT integration pattern.
- MEMORY requirements: rename CRYPTO -> CRYPTO_HOT (UCI) /
CRYPTO_RESIDENT (ip65) per the W1 hot/cold split; cross-link
c64-lib-contract repo + docs/library-ingestion-architecture.md.
- Build vars: document USE_X25519_SIBLING + EMBED_P256_OVERLAY.
- UCI Memory layout: collapse to a pointer at the post-W1 table
below.
- Known issues: rewrite the X25519 sibling block for v0.6.0 + the
post-W1 split; replace MemoryPolicy note with the centralized
VICE helper (tools/_vice_helpers.py::default_vice_config).
- ECDSA P-256 wall-clock: update to 82.1 s under v0.3.0 (was 86.7 s
under v0.2.0); drop the v0.2.0-era defensive-init footnote.
- Memory layout: replace pre-W1 monolithic CRYPTO table with the
full UCI + ip65 post-W1 layout; document the ip65 CRYPTO_COLD_SHADOW
1,662 B overflow tracked at c64-nist-curves#54.
docs/library-ingestion-architecture.md gets a "Status as of 2026-05-23"
appendix snapshotting which §3 library-side issues and §4 c64-https-side
work items landed via PRs #51-#55 vs. are still in-flight (Task #12 /
P-384 wall-clock / CI bot).
No source / cfg / integration script / submodule changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(uci): C64 Ultimate support — 64 MHz e2e + WiFi DHCP + fence retune (#58)
* fix(uci): widen uci_fence to keep 35% margin at 64 MHz
The C64 Ultimate tops out at 64 MHz, where the old 2525-cycle fence
(INNER=100, tuned for 48 MHz) shrinks to ~39.5 us — only ~4% over the
FPGA's ~38 us floor. Bump UCI_FENCE_INNER to 131 (~3300 cycles):
51.6 us at 64 MHz (35.7% margin), 68.8 us at 48 MHz, ~3.3 ms at
1 MHz. Still negligible for networking at every speed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
* feat(uci): interface-fallback DHCP acquire for C64 Ultimate WiFi
The C64 Ultimate has Ethernet AND WiFi; GET_IPADDR(iface=0) returns
0.0.0.0 when the box is on WiFi, which the old single-shot
net_dhcp_acquire surfaced as DHCP FAILED (UCI_ERR_NO_IP). Probe
interface indices 0..3 and take the first non-zero lease; clean up
(drain + ack) after a CMD_FAILED probe so the next one starts from
idle. Clear net_last_error on success so no-lease residue from
earlier probes doesn't sit next to a good acquire.
Costs ~38 B in NET_CODE — boundary with NET_BSS_TAIL shifted by $40
($1B26 -> $1B66); NET_BSS_TAIL occupancy is $625 so $75 slack
remains. Verified on C64 Ultimate firmware 1.1.0 (WiFi): phase2_check
PASS, IP acquired on iface 1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
* fix(uci): retune uci_fence for C64 Ultimate burst floor
The C64U (firmware 1.1.0, core 1.49) needs more inter-access time
than the U64E's empirical ~38 us, and only under sustained CMD_DATA
bursts: at 51.6 us spacing (INNER=131 @ 64 MHz) GET_IPADDR works but
TCP_CONNECT's ~15-byte hostname push is silently lost
(UCI_ERR_NO_SOCKET, no SYN ever leaves the box). Floor bracketed at
64 MHz: 51.6 us FAIL / 62.9 us PASS / 100 us PASS. Ship INNER=217
(5450 cycles = 85.2 us @ 64 MHz) for 35% margin over the worst-case
floor; 113.5 us @ 48 MHz, ~5.5 ms @ 1 MHz — negligible for
networking on both devices.
E2e verified on C64U: 64 MHz PASS (64.7 s), 48 MHz PASS (73.0 s).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
* test(uci): set turbo before boot — C64U runtime-switch quirk
On the C64 Ultimate, a REST config CPU-speed change while the PRG is
running can glitch the UCI bridge so the next pushed command is
silently lost — reproduced 2x as UCI_ERR_NO_SOCKET on the first
TCP_CONNECT after a 1->64 MHz switch, even with a 100 us fence
(1->48 happened to survive). Move set_turbo_mhz before
reset/run_prg so the machine boots at target speed and never
switches mid-session; also makes boot speed deterministic instead
of inheriting the previous run's config.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
* docs: C64 Ultimate notes + 48/64 MHz benchmarks
New bench device: C64 Ultimate Starlight (10.53.21.158, fw 1.1.0,
core 1.49, NTSC, WiFi). Documents the 64 MHz speed enum, the
runtime speed-switch quirk, the wider fence floor, multi-interface
GET_IPADDR, REU-disabled default + set_reu() incompatibility, and
the new P-256 e2e numbers: 73.0 s @ 48 MHz, 64.7 s @ 64 MHz.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
* bench+docs: 48/64 MHz verify scaling analysis — REU DMA is the wall
bench_ecdsa_u64e.py: the post-W1 MemoryPolicy blocks the bench's DMA
writes of vector inputs into CRYPTO_BSS (now in CRYPTO_COLD_SHADOW);
pass override= with a reason instead of widening the policy.
Measured on C64U: ecdsa_verify_256 53.8 s @ 48 MHz / 47.4 s @ 64 MHz.
T(f) = D + C/f fits both e2e and verify pairs to 0.1 s:
verify D = 28.4 s speed-invariant vs C = 1.22 Gcycles CPU-scaled.
D matches fp_mul's REU row-fetch volume (~27 MB/verify) at the
stock ~1 MB/s DMA rate that turbo does not scale (consistent with
the 16 ms overlay-swap datapoint). e2e adds ~11.4 s of UCI/network
D. Conclusion documented in CLAUDE.md: >48 MHz clocks are mostly
wasted on this fp_mul; the fix is library-side.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZmMaxyyykvYbCvJAc4QVW
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@JC-000