fix(kernel): allow command-phase byte reception while ATN is asserted
All checks were successful
Build kernel module / build (1:6.12.93-1+rpt1, bookworm) (pull_request) Successful in 1m18s
Build kernel module / build (1:6.18.34-1+rpt1, trixie) (pull_request) Successful in 1m13s
Build kernel module / release (pull_request) Has been skipped

wait_clk() aborted on any asserted ATN, but ATN is held low for the
entire command phase by design. Reception therefore aborted on the
first poll before clocking a single bit, so the listener never decoded
the LISTEN command and always fell through to "not addressed -> IDLE".

Make the ATN abort phase-relative via IEC_RX_ATN meaning "ATN changed":
in the data phase abort when ATN asserts (C64 interrupts with a new
command); in the command phase abort when ATN releases (command
sequence finished). The check collapses to
`iec_atn_asserted() == data_phase`.

Closes #2

Generated by Clanker

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Christian Werner 2026-06-20 03:11:51 +02:00
parent 62dff1264d
commit c9cfebafda

View File

@ -108,16 +108,26 @@ static void emit_record(u8 kind, u8 value, u8 flags)
/* --- busy-poll helper ---------------------------------------------------- */
/*
* Spin until CLK reaches `want_asserted`, or timeout/ATN/RESET. Caller holds
* local IRQs disabled. Uses a microsecond budget rather than a fixed udelay so
* a slightly-fast C64 is handled correctly (research §1.3).
* Spin until CLK reaches `want_asserted`, or timeout/ATN-change/RESET. Caller
* holds local IRQs disabled. Uses a microsecond budget rather than a fixed
* udelay so a slightly-fast C64 is handled correctly (research §1.3).
*
* The ATN abort is phase-relative, and IEC_RX_ATN means "ATN changed, abort":
* - data_phase: ATN is released; abort if it ASSERTS (C64 interrupts data
* transfer with a new command).
* - command phase: ATN is asserted for the whole command byte; abort if it
* RELEASES (the command sequence is over -> hand back to the state machine).
* Treating any asserted ATN as an abort (the previous behaviour) made command
* reception impossible: ATN is legitimately low the entire command phase, so
* the very first poll aborted before a single bit was clocked.
*/
static enum iec_rx wait_clk(bool want_asserted, unsigned int timeout_us)
static enum iec_rx wait_clk(bool want_asserted, unsigned int timeout_us,
bool data_phase)
{
unsigned int waited = 0;
while (iec_clk_asserted() != want_asserted) {
if (iec_atn_asserted())
if (iec_atn_asserted() == data_phase)
return IEC_RX_ATN;
if (iec_reset_asserted())
return IEC_RX_RESET;
@ -143,7 +153,7 @@ static enum iec_rx receive_byte(u8 *out, bool *eoi, bool data_phase)
*eoi = false;
/* 1. READY-FOR-DATA: wait for talker to release CLK, then release DATA */
rc = wait_clk(false /* released */, IEC_CLK_TIMEOUT_US);
rc = wait_clk(false /* released */, IEC_CLK_TIMEOUT_US, data_phase);
if (rc != IEC_RX_OK)
return rc;
iec_data_release();
@ -171,10 +181,12 @@ static enum iec_rx receive_byte(u8 *out, bool *eoi, bool data_phase)
/* 3. RECEIVE 8 BITS, LSB first */
for (i = 0; i < 8; i++) {
rc = wait_clk(true /* asserted: data invalid/setup */, IEC_CLK_TIMEOUT_US);
rc = wait_clk(true /* asserted: data invalid/setup */,
IEC_CLK_TIMEOUT_US, data_phase);
if (rc != IEC_RX_OK)
goto out;
rc = wait_clk(false /* released: data valid -> sample */, IEC_CLK_TIMEOUT_US);
rc = wait_clk(false /* released: data valid -> sample */,
IEC_CLK_TIMEOUT_US, data_phase);
if (rc != IEC_RX_OK)
goto out;
/* released(high) = bit 1, asserted(low) = bit 0 */