Skip to content

Load the Lim-Lee comb table from disk instead of computing it at boot #120

Description

@JC-000

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:

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions