Request
Ship the Lim-Lee comb table as data and load it into REU bank 2 at boot, instead of computing it on the C64 with ec_precompute_256.
Feature request, not a defect — the comb profile works today. This is about making it a viable product rather than a benchmark configuration.
Why
ec_precompute_256 costs roughly 40 min at 1 MHz, 3.5 min at 16 MHz, and 50 s at 64 MHz. That one-time cost is large enough to invert comb's advantage for short sessions. Break-even against the no-comb onchip profile, from the v0.6.0 measured verify times (C64U):
| MHz |
onchip |
comb |
gain/handshake |
fill |
break-even |
| 16 |
88.3 s |
49.4 s |
38.9 s |
200 s |
5.1 handshakes |
| 32 |
43.3 s |
24.9 s |
18.4 s |
100 s |
5.4 |
| 48 |
30.9 s |
16.5 s |
14.4 s |
67 s |
4.6 |
| 64 |
22.9 s |
12.4 s |
10.5 s |
50 s |
4.8 |
So today comb loses a single cold handshake at every clock, and only pulls ahead from roughly the fifth connection in one session. With the table preloaded it wins from connection #1, by 42-47%:
16 MHz 88.3 s -> 49.4 s 48 MHz 30.9 s -> 16.5 s
32 MHz 43.3 s -> 24.9 s 64 MHz 22.9 s -> 12.4 s
That matters because 31.0 s end-to-end (C64U, 64 MHz) is the only handshake time this client has produced that sits inside the window a normal internet server will hold a connection open for.
It fits, and loading is much cheaper than computing
The table is 256 slots x 64-byte affine = 16,384 B in REU bank 2 at $0000-$3FFF (points256_comb.s:168). It is a pure constant — Lim-Lee anchors A_p = 2^(32*(p-1)) * G over the fixed P-256 generator — so it is identical on every machine and every run, and can be generated once at build time.
Capacity on a standard D64 (664 free blocks):
uci-comb.prg 62,977 B = 248 blocks
comb table 16,384 B = 65 blocks
---------
313 of 664 -> 351 blocks spare (87 KB)
No D81 is needed. (It will not compress — the contents are effectively uniform field elements.)
Load time is clock-invariant (the serial bus is its own clock domain) while the precompute scales with the CPU, so loading wins everywhere, and by more the slower the machine:
| MHz |
compute |
stock 1541 |
fastloader |
Ultimate virtual drive |
| 1 |
2,400 s |
46.8 s |
5.5 s |
0.7 s |
| 48 |
67 s |
46.8 s |
5.5 s |
0.7 s |
| 64 |
50 s |
46.8 s |
5.5 s |
0.7 s |
The only close case is stock-serial-at-turbo (47 s load vs 50 s compute), and that combination does not physically exist — there is no 64 MHz without an Ultimate, and an Ultimate's drive is its own fast virtual device.
Check this first — it may need no 6502 code at all
The Ultimate can restore REU contents from an image directly. If that covers bank 2 the way it appears to, the shipped artifact is just the table file plus a config note, with no loader, no chunking and no new failure modes — for exactly the machines comb targets. Verify this before writing a loader. I have not tested it against the firmware's REU-image handling.
If a loader is needed
Two things worth knowing before estimating it:
- There is no KERNAL file I/O anywhere in this codebase today.
grep for SETLFS/SETNAM/CHKIN/LOAD returns only comments about PRG-load-time segment placement. This is new code, not a call to something existing.
- The REU-write half already exists:
src/boot.s:771 issues execute + autoload + STASH (C64 -> REU). So the DMA side is a known pattern.
- Chunking is required. 16 KB will not sit in spare C64 RAM alongside a ~63 KB image; it wants ~4 KB at a time through an existing buffer, STASHed to bank 2 per chunk.
Generating the table
Two routes, both cheap:
- Capture — run
ec_precompute_256 once (VICE or hardware), DMA-read REU bank 2 $0000-$3FFF, ship those bytes. No new maths, and it is by construction exactly what the on-C64 path produces.
- Host-side generation — we already own pure-Python P-256 point arithmetic in
tools/package/listener/gen_certs.py (_mul at :110, curve params at :221), so the anchors and table are straightforward to emit and diff against route 1.
Doing both and comparing is the natural self-check, and pins the artifact with a sha256 thereafter.
Verification
The table being wrong is a silent-wrong-crypto failure, not a link error, so this needs a behavioural gate rather than a build one: tools/test_ecdsa_kat_oracle.py against a comb PRG is the test that catches it (6/6 today at the current pin, including 3 negative CAVP vectors; a corrupt table fails the 3 positive ones). Any change here must run that, not just check that the image links.
Related
Request
Ship the Lim-Lee comb table as data and load it into REU bank 2 at boot, instead of computing it on the C64 with
ec_precompute_256.Feature request, not a defect — the comb profile works today. This is about making it a viable product rather than a benchmark configuration.
Why
ec_precompute_256costs roughly 40 min at 1 MHz, 3.5 min at 16 MHz, and 50 s at 64 MHz. That one-time cost is large enough to invert comb's advantage for short sessions. Break-even against the no-comb onchip profile, from the v0.6.0 measured verify times (C64U):So today comb loses a single cold handshake at every clock, and only pulls ahead from roughly the fifth connection in one session. With the table preloaded it wins from connection #1, by 42-47%:
That matters because 31.0 s end-to-end (C64U, 64 MHz) is the only handshake time this client has produced that sits inside the window a normal internet server will hold a connection open for.
It fits, and loading is much cheaper than computing
The table is 256 slots x 64-byte affine = 16,384 B in REU bank 2 at
$0000-$3FFF(points256_comb.s:168). It is a pure constant — Lim-Lee anchorsA_p = 2^(32*(p-1)) * Gover the fixed P-256 generator — so it is identical on every machine and every run, and can be generated once at build time.Capacity on a standard D64 (664 free blocks):
No D81 is needed. (It will not compress — the contents are effectively uniform field elements.)
Load time is clock-invariant (the serial bus is its own clock domain) while the precompute scales with the CPU, so loading wins everywhere, and by more the slower the machine:
The only close case is stock-serial-at-turbo (47 s load vs 50 s compute), and that combination does not physically exist — there is no 64 MHz without an Ultimate, and an Ultimate's drive is its own fast virtual device.
Check this first — it may need no 6502 code at all
The Ultimate can restore REU contents from an image directly. If that covers bank 2 the way it appears to, the shipped artifact is just the table file plus a config note, with no loader, no chunking and no new failure modes — for exactly the machines comb targets. Verify this before writing a loader. I have not tested it against the firmware's REU-image handling.
If a loader is needed
Two things worth knowing before estimating it:
grepforSETLFS/SETNAM/CHKIN/LOAD returns only comments about PRG-load-time segment placement. This is new code, not a call to something existing.src/boot.s:771issuesexecute + autoload + STASH(C64 -> REU). So the DMA side is a known pattern.Generating the table
Two routes, both cheap:
ec_precompute_256once (VICE or hardware), DMA-read REU bank 2$0000-$3FFF, ship those bytes. No new maths, and it is by construction exactly what the on-C64 path produces.tools/package/listener/gen_certs.py(_mulat :110, curve params at :221), so the anchors and table are straightforward to emit and diff against route 1.Doing both and comparing is the natural self-check, and pins the artifact with a sha256 thereafter.
Verification
The table being wrong is a silent-wrong-crypto failure, not a link error, so this needs a behavioural gate rather than a build one:
tools/test_ecdsa_kat_oracle.pyagainst a comb PRG is the test that catches it (6/6 today at the current pin, including 3 negative CAVP vectors; a corrupt table fails the 3 positive ones). Any change here must run that, not just check that the image links.Related