Repository navigation
Integrate ip65 TCP/IP stack with RR-Net driver - #1
Merged
Merged
Conversation
- Add ip65 as git submodule (cc65/ip65) - Build ip65 TCP + RR-Net driver into binary blob at $2000 via ca65/ld65 - ip65_stub.s: jump table (11 entries) + variable address table - ip65.cfg: custom linker config placing code at $2000, BSS at $4000 - Wire net.asm to call ip65 through the jump table with ZP save/restore - net_init, net_dhcp, net_poll, net_dns_resolve, net_tcp_connect/send/close - net_print_ip: decimal IP display from ip65 config area - TCP receive ring buffer (256 bytes) for callback data - Update constants.asm with ip65 jump table offsets and variable addresses - Change boot.asm to menu-driven startup (I=init, G=get, Q=quit) - No auto-init on startup — prevents crash without RR-Net hardware - Update Makefile for two-stage build: ca65/ld65 (ip65) then ACME (our code) - Add test suite: tools/test_net.py (56 tests) - Build integrity (14 labels) - ip65 jump table validation (22 entries) - ZP save/restore round-trip (10 random patterns) - TCP receive ring buffer (drain + wrap-around) - ip65_init without hardware (graceful failure + ZP preservation) Binary: 15.4 KB ($0801-$458F), ip65 blob: 6.8 KB at $2000-$3B26 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive per-address documentation of all 256 ZP bytes: - 5-tier safety classification (always safe, BASIC-only, KERNAL I/O, IRQ-clobbered, system-reserved) - Complete IRQ handler ($EA31) footprint with code paths - KERNAL call side effects for CHROUT, CHRIN, GETIN, OPEN, CLOSE, LOAD, SAVE, SETLFS, SETNAM, CLRCHN, CHKIN, CHKOUT - cc65/ip65 ZP convention ($02-$1B) - Cursor blink disable optimization ($CC) - Recommended allocation strategy for multi-module projects - Test harness jsr() BRK artifact documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy and adapt 6 crypto modules from sibling projects: - word32.asm (505 lines) — 32-bit arithmetic for ChaCha20 - chacha20.asm (326 lines) — ChaCha20 stream cipher (RFC 7539) - poly1305.asm (610 lines) — Poly1305 MAC with quarter-square multiply - aead.asm (311 lines) — ChaCha20-Poly1305 AEAD (RFC 7539 S2.8) - sha256.asm (1029 lines) — SHA-256 with optimized byte-rotate decomposition - hmac_drbg.asm (621 lines) — HMAC-SHA256, HMAC-DRBG, entropy collection Adaptations: strip menu UI code, rename zp_ptr1->zp_ptr, add extra_sid stubs for entropy, add sqtab/SID equates to constants.asm, merge all crypto data buffers into data.asm (~1.1 KB). Test suites (85 tests total): - test_sha256.py: 7 tests (init IV, NIST "abc", empty, boundary 1/55/56/63) - test_crypto.py: 22 tests (sqtab_init, ChaCha20 block+encrypt RFC vectors, Poly1305 MAC RFC vector, AEAD encrypt/decrypt RFC vector + 5 random) - test_net.py: 56 tests (unchanged, regression pass) Binary: 22 KB, 406 labels. All crypto at $3B27-$4E88. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hkdf.asm stubs with working implementations: - hkdf_extract: HMAC-SHA256(salt, IKM) with zero-salt fallback - hkdf_expand: HMAC-SHA256(PRK, info || 0x01) for L ≤ 32 - hkdf_expand_label: builds TLS 1.3 HkdfLabel struct in hkdf_info_buf, copies label via ZP indirect addressing, then calls hkdf_expand - tls_derive_secret: convenience wrapper (unchanged) Optimizations: ZP pointer ($FB) for indirect copies in expand_label, tight DEX/BPL loops for 32-byte copies, direct info_buf construction. Test suite: tools/test_hkdf.py (12 tests) - RFC 5869 Extract cases 1 + 3 (empty salt) - RFC 5869 Expand cases 1 + 3 (L=32 truncated) - TLS 1.3 early_secret derivation - HKDF-Expand-Label with "derived" label + empty hash context - 3 random Extract + 3 random Expand-Label vs Python hmac reference All 97 tests pass (56 net + 7 SHA-256 + 22 crypto + 12 HKDF). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mark ip65 integration, crypto modules, and HKDF as complete. Update test section with all 4 suites (97 tests total). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Sep 30, 2026
…consume the load; refuse data on non-read replies (adv-l2 #1-#4) #1 (MAJOR) A valid slot beside one whose open/read failed (DOS, NOPATH, IO) or whose version is unknown was loaded as VALID. That slot can be the NEWER one, so the host looked absent and a save could write over it: a rollback (reproduced; the write landed). Only ABSENT and torn damage (FORMAT, CHECKSUM) are tolerated now (ts_tolerable); the Python mirror and its test follow. #2 stage made the unsaved record visible to lookup, and a failed save kept it there (and allowed a blind retry after CHANGED). stage now consumes the load (state NONE, lookup C=1); every save attempt consumes load and staged record (ts_consume). #3 test gap: a stale-but-valid read-back case now fails if the read-back generation compare is removed. #4 dos_finish stored reply data for OPEN/CLOSE/WRITE at the stale dos_store whenever dos_cnt was nonzero; a non-read reply with data is now refused before anything is stored. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Oct 1, 2026
A key refused mid-entry, or a host refused at RETURN, returned straight to main_loop, so the rest of what the operator typed ran as menu commands: 'H' dialled zimmers.net over plain HTTP, 'I' re-initialised the network, 'Q' quit. Now a refused key's line is swallowed unechoed up to RETURN, a refused host still gets the PATH prompt, the KERNAL key buffer is flushed, and only then is INVALID TARGET printed. test_typed_target no longer hides this: every refusal case types the rest of its entry, lets main_loop run afterwards, and fails on a dial, an HTTP GET, a re-init, a quit or keys left queued. Four cases type h/i/q after the refused key. Current head before this commit: 112/132; after: 132/132. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Oct 1, 2026
A key refused mid-entry, or a host refused at RETURN, returned straight to main_loop, so the rest of what the operator typed ran as menu commands: 'H' dialled zimmers.net over plain HTTP, 'I' re-initialised the network, 'Q' quit. Now a refused key's line is swallowed unechoed up to RETURN, a refused host still gets the PATH prompt, the KERNAL key buffer is flushed, and only then is INVALID TARGET printed. test_typed_target no longer hides this: every refusal case types the rest of its entry, lets main_loop run afterwards, and fails on a dial, an HTTP GET, a re-init, a quit or keys left queued. Four cases type h/i/q after the refused key. Current head before this commit: 112/132; after: 132/132. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Oct 6, 2026
…K_CODE) 1. MEDIUM-HIGH. The hook stored tp_bwarn from A after a failed tp_cmp_got, and A is then the bundle pin's byte where the hashes first differ (from byte 31 down). A $00 there left tp_bwarn clear: BUNDLE MISMATCH printed, RECORD KEY? was never asked, and trust_post recorded the server's key as TOFU -- free when the pin's byte 31 is $00, ~2^8 / 2^16 grinds for byte 30 / 29. Now `lda #1`. Regression: pins with byte 31 = $00, and byte 30 = $00 with 31 equal (red on 04af8b9: recorded without A); mutant. Audit: tp_bwarn was the only flag stored from an incidental A; the other stores are explicit, or clear (the safe way). 2. LOW. TRUST_POLICY_BSS is not boot-zeroed, so an accept armed before 'Q' survived RUN (or a reset and SYS). start now calls trust_state_init, which clears it and the bundle cache. Test + mutant (drop the call). 3. LOW, comb. A cold-bank refusal of trust_post (socket ERROR, latched DMA timeout) skipped its clears, keeping tp_ovr_armed and tp_mode. The resident stubs now clear both whenever the bank itself refuses. Red on 04af8b9 (armed=1 mode=1 survived); mutant. Under TRUST_STORE the hook's code is TRUST_HOOK_CODE, not CERT_PIN_CODE: the comb cfgs place it in NET_CODE (CRYPTO_HOT has no room for it beside #273's body-sink bound), the uci cfg where CERT_PIN_CODE already was. The plain pin keeps CERT_PIN_CODE, so non-trust pinned builds do not move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Oct 6, 2026
…K_CODE) 1. MEDIUM-HIGH. The hook stored tp_bwarn from A after a failed tp_cmp_got, and A is then the bundle pin's byte where the hashes first differ (from byte 31 down). A $00 there left tp_bwarn clear: BUNDLE MISMATCH printed, RECORD KEY? was never asked, and trust_post recorded the server's key as TOFU -- free when the pin's byte 31 is $00, ~2^8 / 2^16 grinds for byte 30 / 29. Now `lda #1`. Regression: pins with byte 31 = $00, and byte 30 = $00 with 31 equal (red on 04af8b9: recorded without A); mutant. Audit: tp_bwarn was the only flag stored from an incidental A; the other stores are explicit, or clear (the safe way). 2. LOW. TRUST_POLICY_BSS is not boot-zeroed, so an accept armed before 'Q' survived RUN (or a reset and SYS). start now calls trust_state_init, which clears it and the bundle cache. Test + mutant (drop the call). 3. LOW, comb. A cold-bank refusal of trust_post (socket ERROR, latched DMA timeout) skipped its clears, keeping tp_ovr_armed and tp_mode. The resident stubs now clear both whenever the bank itself refuses. Red on 04af8b9 (armed=1 mode=1 survived); mutant. Under TRUST_STORE the hook's code is TRUST_HOOK_CODE, not CERT_PIN_CODE: the comb cfgs place it in NET_CODE (CRYPTO_HOT has no room for it beside #273's body-sink bound), the uci cfg where CERT_PIN_CODE already was. The plain pin keeps CERT_PIN_CODE, so non-trust pinned builds do not move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
JC-000
added a commit
that referenced
this pull request
Oct 14, 2026
…e read budget Amendments from the adv-296 review: - test_copy_counts_at_page_edges: headers 1/255/256/257/512/768/893 at ring tails $0000/$0E7D/$0F00/$0FFF, plus short, exact-request and over-claim cases. The mutant count-always-iny (an unconditional iny at @p_copy_init) killed every session whose header is a multiple of 256 and no test fed one. - _pacing_flows traces a CLOSE whose PUSH is refused once, so the rejected path's CLR_ERR write fence is checked (rejected-clr-unfenced). - test_read_pacing's read side was SBC #1-shaped only; it now also caps the instructions after each read (READ_GAP_BUDGET, from the measured maxima), whatever the delay's shape (statline-nopsled). The docstring says what it detects and on which flows. check 53/53 (default 44648860, M3_READ_FENCE f5d9278b); mutants 74/74. 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.
Summary
Test plan
make clean && make— two-stage build (ca65/ld65 → ACME)python3 tools/test_net.py— 56/56 tests pass🤖 Generated with Claude Code
Originally posted by @JC-000 on 2026-03-14