Skip to content

Phase 3 WIP: layout fix + TLS receive-path fixes (hits architectural limits) - #13

Merged
JC-000 merged 13 commits into
masterfrom
feature/e2e-bridge-tests
May 6, 2026
Merged

Phase 3 WIP: layout fix + TLS receive-path fixes (hits architectural limits)#13
JC-000 merged 13 commits into
masterfrom
feature/e2e-bridge-tests

Conversation

@JC-000

Copy link
Copy Markdown
Owner

Summary

  • End-to-end bridge test framework for c64-https running in VICE over RR-Net (Phase 1 DHCP and Phase 2 plain HTTP GET pass on this branch).
  • Load-bearing memory-layout fix: relocate the entire crypto blob above ip65's BSS window to eliminate a CPU JAM caused by DHCP writing packet bytes on top of sha256_rotr8 code.
  • TLS 1.3 receive-path chain-of-bugs repaired (plaintext/encrypted dispatch, X-wrap in TCP recv callback, missing clc, never-called ECDH shared-secret, CCS filter).
  • TCP receive buffer grown to 4 KB with 16-bit indices, aead_data_len widened to 16-bit so TLS 1.3 Certificate records no longer overflow or truncate.
  • Phase 3 diagnostic instrumentation (screen markers, 16-bit net_poll entry/return counters, dynamic label lookup in the test harness) preserved for the next session.

What's included

5feb0ce — Add end-to-end bridge tests for DHCP and HTTP GET

Vendors the c64-test-harness bridge networking scripts (setup + cleanup) and extends them with dnsmasq (DHCP + DNS overrides). Builds a reusable tools/https_e2e/ library with a BridgeEnv context manager, single-VICE launcher, boot-menu helpers, and HTTP listener. Two passing e2e tests drive the real c64-https binary inside VICE over RR-Net at normal speed: Phase 1 verifies DHCP, Phase 2 verifies a plain HTTP GET to a local listener. This is the scaffolding every later phase rides on.

d973531 — Relocate crypto above ip65 BSS + multiple TLS receive-path fixes

The original CPU JAM at $4DE0 was caused by c64-https crypto code streaming from $3B28 straight through ip65's BSS window at $4000-$5FFF: when DHCP ran, it overwrote sha256_rotr8 with packet bytes. Inserting * = $6000 before crypto/word32.asm in src/main.asm moves every crypto unit (word32/chacha/poly1305/aead/sha256/hmac_drbg/fe25519/x25519/ecdsa/ecdsa_verify/der/tls_cert/tls_ecdh) above the BSS. To fit in the space below $7C00 three compromises were required: sqtab_lo/sqtab_hi moved from hardcoded $7800/$7A00 equates to labels inside data.asm BSS; P-384 ECDSA was stubbed in crypto/ecdsa_verify.asm (tech debt tracked in project_p384_stubbed, must be restored before real CA chains); and the old * = $7C00 barrier was removed. With the JAM gone, a chain of follow-on TLS receive-path bugs became diagnosable and were fixed in the same commit (see "TLS receive-path fixes" below). Also renames the demo URL from www.apple.com to www.foo.bar throughout boot.asm, dnsmasq overrides, HTTPS listener cert subject/SAN, and test screen text, so a stray packet can never escape the bridge to a real host. The bridge setup/cleanup scripts are hardened (no blanket pkill, specific PID tracking, idempotent re-entry, stale-rc-file handling). Adds tests/test_phase3_https.py with granular post-mortem diagnostics via dynamic label lookup from build/labels.txt.

c8ca348 — Grow tcp_recv_buf to 4KB + widen aead_data_len to 16-bit

Two coordinated architectural changes that unblock the TLS 1.3 handshake past ServerHello. The previous 256-byte ring buffer with 8-bit head/tail silently overwrote unread data whenever ip65 delivered a TCP segment larger than the remaining space — the post-ServerHello flight (CCS + EncryptedExtensions + Certificate + CertVerify + Finished, 600–800 bytes) always overflowed. tcp_recv_buf is now a 4096-byte buffer pinned to $C000-$CFFF (always-RAM region on the C64 with BASIC ROM banked out) declared as an equate in constants.asm, so it consumes zero BSS. Head/tail are 16-bit with mask-on-access ($0FFF), and a tcp_recv_overflow sticky flag is set by the producer in net_tcp_recv_cb if it would lap the consumer. net.asm consumer/producer paths and http.asm's feed loop are updated to 16-bit indexing. Separately, aead_data_len in data.asm is widened from 1 byte to 16-bit because the previous 8-bit cap truncated any AEAD record larger than 255 bytes — TLS 1.3 Certificate ciphertext is ~353 bytes, so every Poly1305 tag check failed. crypto/aead.asm now uses 16-bit counter semantics throughout aead_encrypt/aead_decrypt/aead_compute_tag, the length block stores both bytes, chacha20_encrypt's block loop runs on a 16-bit cc20_remain:cc20_remain_hi, and tls_record.asm copies both bytes of tls_enc_aead_len into aead_data_len.

7eb8b8a — Phase 3 screen markers + net_poll counters + diagnostic instrumentation

Adds on-screen progress markers (CH, SH, HK1, KEYS, ENC1, RX, GOT2, DEC, PROC, EE, CERT, CV, FIN, CFIN) at each TLS handshake state transition in tls_connect, plus 16-bit entry/exit counters on net_poll so the gap net_poll_entry_count - net_poll_return_count directly exposes how many ip65_process frames are parked on the stack. The branch-target trampoline pattern (bcc @okN / jmp @error / @okN:) is used where the inserted print calls would push @error out of bcs range. tests/test_phase3_https.py is extended with dynamic label lookup via _label_addr() against build/labels.txt (so diagnostic reads always hit the current build regardless of data.asm layout shifts), a file-logged post-mortem at /tmp/c64-https-phase3-diag.log with explicit flush that survives hard-kill, per-heartbeat sampling of tcp_recv_head/tcp_recv_tail/net_poll_*, CPU-register reads via transport.read_registers(), and a top-of-stack return-address chain with label resolution. This instrumentation is what localized the remaining stall to ip65, not c64-https (see "What still doesn't work").

Memory-layout fixes (the load-bearing part)

The * = $6000 directive inserted before crypto/word32.asm in src/main.asm is the single most important change in this PR. Before it, the c64-https binary had crypto code mapped linearly from $3B28 upward, which means sha256_rotr8 and its neighbors sat inside ip65's BSS region at $4000-$5FFF. ip65 allocates DHCP/ARP/IP/TCP packet buffers in that window, so the first DHCP request issued from the boot menu wrote packet payload bytes directly on top of sha256_rotr8, which then took a $02 opcode on next entry and CPU-JAMed at $4DE0. Relocating the crypto blob above the BSS ends that. The three adjustments that come with it — sqtab_lo/sqtab_hi moved from hardcoded $7800/$7A00 equates into data.asm BSS labels, P-384 ECDSA stubbed in crypto/ecdsa_verify.asm, and the * = $7C00 barrier removed — are all necessary to fit everything below $7C00 and are tracked in project_p384_stubbed as known tech debt that must be restored before the client can validate real CA chains.

tcp_recv_buf is separately pinned to $C000 as an equate in constants.asm. That region ($C000-$CFFF) is always RAM when the $01 processor port is set to $36 (BASIC ROM out, KERNAL in) as boot.asm does, so it's 4 KB of completely free buffer space that costs zero BSS. The buffer's 16-bit head/tail live in data.asm BSS (tcp_recv_head, tcp_recv_tail, tcp_recv_overflow), so no data.asm label shifts affect the ring's backing store. Widening aead_data_len to a !word in data.asm is the matching fix on the crypto side: without it, the 8-bit counter truncated AEAD decrypt at 255 bytes and the ~353-byte Certificate ciphertext body always failed Poly1305.

TLS receive-path fixes

All landed in d973531:

  • src/net.asmnet_tcp_recv_cb had an 8-bit X register wrap when ip65 delivered packets >255 bytes. cb_remaining is now clamped to 255 per invocation so the callback re-enters cleanly on the next slice.
  • src/tls_record_io.asm — plaintext/encrypted dispatch was comparing against TLS_STATE_SERVER_HELLO ($02), but tls_state is already $02 when the ServerHello is being received, so the code was trying to AEAD-decrypt a plaintext record. Changed to TLS_STATE_ENCRYPTED_EXT ($03). Also adds a CCS filter: RFC 8446 §5 requires TLS 1.3 clients to ignore ChangeCipherSpec records during handshake, and the record receive loop now retries past them.
  • src/tls_keyschedule.asmtls_derive_handshake_keys fell off its final loop without a clc, leaving carry set from the last hmac_sha256. The caller's bcs @error then fired on success. Added clc before rts.
  • src/tls13.asm (tls_recv_server_hello)tls_ecdh_compute_shared was defined in tls_ecdh.asm but never called anywhere in the codebase, so tls_shared_secret stayed all zeros and HKDF derived handshake keys from zeros. Added the missing jsr tls_ecdh_compute_shared right after tls_parse_server_hello succeeds.

What still doesn't work

The Phase 3 HTTPS end-to-end test (tests/test_phase3_https.py) still fails at runtime, but the failure is inside ip65, not c64-https. The on-screen markers reach CH → SH → HK1 → KEYS → ENC1 → RX cleanly (ClientHello sent, ServerHello parsed, X25519 shared secret computed, HKDF handshake keys derived, entered tls_recv_encrypted, first net_poll call inside the encrypted-wait loop) and then hang. The net_poll_entry_count / net_poll_return_count 16-bit counters show a gap of exactly 1 — one ip65_process call on the stack, never returning — and tcp_recv_tail sits frozen at $0198 (408 bytes delivered) across 55+ heartbeats / 1800 seconds. PC samples during the hang hit different addresses inside ip65 code ($20BE, $9E8D), so ip65 is executing, just caught in an inner loop that doesn't yield — most likely the CS8900a driver or TCP reassembly path choking on a burst of retransmits that arrive after ~3.6 minutes of CPU silence during the X25519 shared-secret computation.

This is tracked separately. Four candidate mitigations are written up in the project_phase3_handoff session memory (ip65 upstream patch, c64-https-side close/reopen workaround, server-side pacing, or warp mode during X25519 to eliminate the long silence). None of them require reverting anything in this PR.

Phase 1 (DHCP) and Phase 2 (HTTP GET) end-to-end tests pass on this branch. The memory-layout fix, the TLS receive-path fixes, and the buffer-widening changes are all independently load-bearing for future progress, and nothing in them depends on the ip65 stall being resolved first.

Testing

  • PASSsudo PYTHONPATH=tools python3 tests/test_phase1_dhcp.py (DHCP end-to-end over bridge + RR-Net)
  • PASSsudo PYTHONPATH=tools python3 tests/test_phase2_http.py (HTTP GET to local listener)
  • KNOWN FAILUREsudo PYTHONPATH=tools python3 tests/test_phase3_https.py hangs after the RX marker inside ip65_process; see "What still doesn't work" above. The failure is preserved at the ip65 boundary by design — all c64-https markers light up cleanly first.
  • Unit tests still pass: tools/test_crypto.py, tools/test_sha256.py, tools/test_x25519.py, tools/test_hkdf.py, tools/test_keyschedule_steps.py, tools/test_tls_handshake.py, tools/test_tls_record.py, tools/test_net.py, tools/test_http.py, tools/test_dns.py, tools/test_x509.py, tools/test_entropy.py, tools/test_chained_hmac.py.

Review notes for merger

  • tools/https_e2e/ (the bridge test library) is checked in on this branch: __init__.py, env.py, c64_menu.py, http_listener.py, https_listener.py, vice_on_bridge.py. The only other tracked diagnostic scaffold is tools/_diag_carry.py (a small carry-flag probe from the tls_derive_handshake_keys bug hunt).
  • Ad-hoc tools/diag_4de0_*.py / tools/diag_read_live.py scripts that appeared during the CPU-JAM post-mortem are not tracked anywhere — they live only in working trees and are not part of this PR.
  • project_phase3_handoff.md in the session memory is the primary session-handoff document for whoever picks up the ip65 investigation next. It has the full list of addresses, the four mitigation options, and the gotchas (X25519 timing, $01 processor port and BASIC ROM shadowing, label shifts in data.asm, buffered-stdout pitfalls with tee).
  • * = $6000 in src/main.asm is load-bearing. Do not move it.
  • P-384 ECDSA is stubbed (crypto/ecdsa_verify.asm returns error). Tracked in project_p384_stubbed; must be restored before the client can validate any real CA chain.

🤖 Generated with Claude Code


Originally posted by @JC-000 on 2026-04-14

JC-000and others added 13 commits March 23, 2026 15:39
…erged
The harness wait_for_text() now calls transport.resume() between polls
internally, so the inline polling loops are no longer needed. This
replaces 13 copies of the same ~10-line loop with single wait_for_text()
calls, reducing total code by 120 lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r reuse
The parallel runner had two bugs: (1) as_completed() didn't see futures added
mid-iteration, so only the first N suites were collected, and (2) reusing VICE
instances across suites caused state contamination (HKDF 0/12 on reused workers).
Fix: allocate a fresh VICE instance per suite via run_suite_in_own_instance().
Add all 10 suites (was 5). Add --skip-slow and --seed flags. 193/193 pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix parallel test runner: all 10 suites, instance-per-suite.
… tests
net_dns_resolve and net_set_tcp_dest both passed A/X parameters through
net_save_zp, which uses X as a loop counter and clobbers both registers.
This caused DNS resolution and TCP destination setup to receive garbage
pointers instead of the caller's intended addresses. Fixed by pushing
A/X to the stack before the ZP save and restoring after.
Added test_dns.py (4 tests) exercising net_dns_resolve over TAP with
dnsmasq, and test_http_integration.py (5 tests) for end-to-end plain
HTTP GET (DNS → TCP → request → response). Both use ViceInstanceManager
with ethernet_mode="rrnet" and run unprivileged (only dnsmasq via sudo).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ion-tests
Fix A/X register clobbering in net.asm, add DNS + HTTP integration tests
Replace baseline fe25519/x25519 with optimized versions from the c64-x25519
performance tuning project, achieving ~30% speedup (12,782 jiffies / 3.6 min
per key generation vs 18,005 baseline).
Optimizations imported:
- REU DMA multiplication tables (128KB REU, 4 cyc/product vs mul_8x8)
- mult66 indirect-indexed quarter-square multiply for fe_sqr
- Self-modifying accumulation addresses in fe_mul/fe_sqr inner loops
- 4x unrolled constant-time fe_cswap (38 cyc/byte vs 49)
- Shift-before-accumulate for fe_sqr cross terms
- mul_by_38 lookup tables for fe_reduce_wide
Key integration fixes:
- Optimization tables (mul_dma, sqtab2, mul38) placed early in data.asm to
stay below $A000 and avoid BASIC ROM shadow region
- BASIC ROM banked out at boot and kept off during runtime (data buffers at
$A000+ need direct RAM access)
- VICE launched with -reu -reusize 512 for all test suites
- Zero page lmul0/lmul1 pointers time-shared with ChaCha20 vars
New files:
- tools/test_x25519.py: 71 unit tests (fe25519 field ops + x25519_clamp +
optional --slow RFC 7748 scalarmult vectors)
- tools/bench_x25519.py: key generation benchmark with jiffy clock timing
and Python X25519 verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Import optimized X25519 (~30% faster key generation)
Introduces tools/net_test_env.py with NetworkTestEnv context manager
that handles TAP interface, dnsmasq, and optional HTTPS server lifecycle.
Replaces duplicated inline setup/teardown code across network test files
and guarantees cleanup via __exit__, signal handlers, and atexit.
Migrates test_dns.py as proof-of-concept. Includes 14 unit tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add consolidated network test environment
Vendor the c64-test-harness bridge networking scripts (setup + cleanup)
and extend them with dnsmasq (DHCP + DNS overrides). Build a reusable
tools/https_e2e/ library with BridgeEnv context manager, single-VICE
launcher, boot menu helpers, and HTTP listener. Two passing e2e tests
drive the real c64-https binary in VICE over RR-Net at normal speed:
Phase 1 verifies DHCP, Phase 2 verifies plain HTTP GET to a local
listener. HTTPS (Phase 3) to follow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The original CPU JAM at $4DE0 was caused by ip65 writing DHCP packets on
top of sha256_rotr8 code: c64-https crypto code streamed from $3B28 right
through ip65's BSS window at $4000-$5FFF. Fix the layout by inserting
`* = $6000` before word32.asm so all of word32/chacha/poly/aead/sha256/
hmac_drbg/fe25519/x25519/ecdsa + ecdsa_verify/der/tls_cert/tls_ecdh land
above the BSS. Requires three adjacent compromises to fit in the space
below $7C00:
- Relocate sqtab from the hardcoded $7800/$7A00 equates to labels inside
data.asm BSS, freeing the $7800-$7BFF region
- Stub P-384 ECDSA (src/crypto/ecdsa_verify.asm) to return an error
instead of dispatching. P-384 isn't needed for the self-signed P-256
cert used in tests but MUST be restored before real CA chains. Tech
debt tracked in project_p384_stubbed memory
- Remove the `* = $7C00` barrier now that sqtab is gone
With the layout fix, the CPU JAM is gone entirely and the TLS receive
path progresses far enough to expose several follow-on bugs that a
clean walkthrough diagnosed and fixed in sequence:
- tls_record_io.asm: plaintext/encrypted dispatch was comparing against
TLS_STATE_SERVER_HELLO ($02) but tls_state is already $02 when the
ServerHello arrives, causing the code to try to AEAD-decrypt a plaintext
record. Change to TLS_STATE_ENCRYPTED_EXT ($03)
- net.asm: TCP recv callback had an 8-bit X register wrap when ip65
delivered packets >255 bytes, causing re-reads of source bytes.
Clamp cb_remaining to 255 per invocation
- tls_keyschedule.asm: tls_derive_handshake_keys fell off the end of
its final loop without a clc, leaving the carry flag set from the
last hmac_sha256 call. Caller's `bcs @error` then fired on success.
Add clc before rts
- tls13.asm: tls_ecdh_compute_shared was defined in tls_ecdh.asm but
never called anywhere in the codebase, so tls_shared_secret stayed
zeros. HKDF then derived handshake keys from zeros. Add the jsr call
in tls_recv_server_hello right after tls_parse_server_hello succeeds
- tls_record_io.asm: RFC 8446 §5 says TLS 1.3 clients MUST ignore CCS
records during handshake. Add a retry that skips CCS content type
Rename the demo URL from www.apple.com to www.foo.bar throughout boot.asm,
dnsmasq DNS overrides, HTTPS listener cert subject/SAN, and test screen
text expectations, to avoid any risk of real-world impact if the test
environment escapes the bridge.
Bridge setup/cleanup scripts hardened: no blanket pkill, specific PID
tracking, idempotent re-entry, handling of stale vice_eth rc files and
legacy tap-c64 interfaces from older test harness setups.
New test tests/test_phase3_https.py drives the full Phase 3 e2e flow:
BridgeEnv + HTTPS listener + VICE + DHCP + HTTPS GET. Captures granular
post-mortem diagnostics (tls_state, tls_last_state, tls_recv_progress,
tls_recv_sub_progress, tls_rec_header, tls_rec_buf, tls_hs_buf,
tcp_recv_buf, tls_recv_state/count) via dynamic label lookup from
build/labels.txt so diagnostic addresses track the current build.
Instrumentation added to src/data.asm (tls_last_state, tls_recv_progress,
tls_recv_sub_progress, tls_recv_poll_count) and to tls_recv_server_hello,
tls_record_recv_and_decrypt, tls_recv_record for precise failure-site
identification. These are permanent diagnostic helpers.
Current status: handshake reaches tls_state=$03 (ENCRYPTED_EXT) and dies
trying to receive the first encrypted handshake record (EncryptedExtensions).
Two remaining architectural bugs require multi-file refactoring and are
tracked as follow-on work:
1. tcp_recv_buf is 256 bytes with 8-bit indices. TLS 1.3 Certificate
records (~374 bytes) don't fit. Needs 2KB ring with 16-bit indices
and producer overflow check in net_tcp_recv_cb
2. aead_data_len is 1 byte. Caps AEAD decrypt at 255 bytes, so the
~353-byte Certificate ciphertext body is truncated and tag always
fails. Needs widening to 16-bit with updated ChaCha20/Poly1305 loops
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two coordinated architectural changes to unblock TLS 1.3 handshake
past the ServerHello state:
**tcp_recv_buf at \$C000, 4096 bytes, 16-bit indices**
The previous 256-byte ring with 8-bit head/tail silently overwrote
unread data whenever ip65 delivered a TCP segment larger than the
empty space — the TLS 1.3 post-ServerHello flight (CCS + Encrypted-
Extensions + Certificate + CertVerify + Finished, ~600-800 bytes)
always overflowed.
Move the buffer to the 4KB free-RAM region at \$C000-\$CFFF (always
RAM on a C64, never ROM-shadowed) via an equate in constants.asm —
zero impact on data.asm BSS footprint. Convert head/tail to 16-bit
words. Add a tcp_recv_overflow sticky flag set by the producer when
it would lap the consumer.
Update all consumer and producer paths in net.asm to use 16-bit
indexing with mask-on-access (\$0FFF = 4095). http.asm feed loop
also updated. Add producer overflow check in net_tcp_recv_cb so
ip65's callback cannot silently corrupt the ring.
**aead_data_len widened to 16-bit**
Was a single byte, which capped AEAD encrypt/decrypt at 255 bytes.
TLS 1.3 Certificate records carry ~350+ bytes of ciphertext, so
decrypt was always truncated and the Poly1305 tag always failed.
Change aead_data_len to !word in data.asm. Update the aead_encrypt /
aead_decrypt / aead_compute_tag paths in crypto/aead.asm to 16-bit
counter semantics (compare-with-ora for zero, decrement with borrow,
length block stored in both bytes of the 64-bit field).
Update chacha20_encrypt block loop to a 16-bit counter over
cc20_remain:cc20_remain_hi.
Update tls_record.asm where tls_enc_aead_len is copied into
aead_data_len — both bytes are stored now.
poly1305_update in poly1305.asm is unchanged because AEAD
exclusively drives Poly1305 via aead_process_padded now; the
standalone poly1305_update path is dead code and doesn't need
widening for this fix.
**Verification**
After this change, Phase 3 test reaches tls_state=\$03
(ENCRYPTED_EXT), confirms the full post-ServerHello server flight
is visible and intact in the ring at \$C000 (ServerHello, CCS,
EncryptedExtensions record 50B, Certificate record 369B),
tcp_recv_head/tail 16-bit values working. No regression on DHCP,
DNS, TCP connect, or ServerHello parse.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds on-screen progress markers at each TLS handshake state transition
in tls_connect plus entry/exit counters on net_poll to definitively
localize the Phase 3 HTTPS stall.
**Markers** (src/tls13.asm + src/boot.asm):
CH/SH/HK1/KEYS/ENC1/RX/GOT2/DEC/PROC/EE/CERT/CV/FIN/CFIN printed on
screen as each step of tls_connect completes. The branch-target
trampoline pattern (`bcc @okn / jmp @error / @okn:`) is used because
the inserted print calls push @error out of bcs range.
**Counters** (src/data.asm + src/net.asm):
net_poll_entry_count and net_poll_return_count 16-bit counters
increment at entry and exit of net_poll. Gap between them = number
of active ip65_process calls currently on the stack.
**Test diagnostics** (tests/test_phase3_https.py):
- Dynamic label lookup via _label_addr() against build/labels.txt so
diagnostic reads always hit the current build regardless of BSS
layout shifts
- File-logged post-mortem at /tmp/c64-https-phase3-diag.log with
explicit flush(), survives hard-kill
- Per-heartbeat sampling of tcp_recv_head, tcp_recv_tail,
net_poll_entry/return_count, printed inline
- CPU register read (PC, SP, A, X, Y) via transport.read_registers()
- Top-of-stack return-address chain with label resolution
- Full dumps of tls_rec_buf, tls_hs_buf, tcp_recv_buf in hex+ASCII
**Conclusion from this instrumentation**: Phase 3 handshake progresses
successfully through CH, SH, HK1, KEYS, ENC1, RX markers — i.e., all
of X25519 keygen, X25519 shared secret, HKDF handshake-key derivation,
and transition into tls_recv_encrypted. It then stalls permanently
while net_poll_entry - net_poll_return = 1 (exactly one ip65_process
call on the stack, never returning) and tcp_recv_tail frozen at
0x0198 (408 bytes) across 1800 seconds / 55+ heartbeats.
The stall is inside ip65 — likely the CS8900a driver or IP/TCP
receive path — triggered by the second TCP segment carrying
Certificate record bytes arriving after the multi-minute X25519
silence. None of the c64-https TLS code has a bug at this point;
the TLS layer never gets a CPU quantum because net_poll doesn't
return.
This commit preserves the instrumentation for future sessions to
investigate the ip65 stall with PC sampling and ip65 code inspection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JC-000
JC-000 merged commit 0def607 into masterMay 6, 2026
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