fix(kernel): allow command-phase byte reception while ATN is asserted #3

Merged
chris merged 1 commits from fix/command-phase-atn-abort into master 2026-06-20 03:36:35 +02:00

View File

@ -108,16 +108,26 @@ static void emit_record(u8 kind, u8 value, u8 flags)
/* --- busy-poll helper ---------------------------------------------------- */ /* --- busy-poll helper ---------------------------------------------------- */
/* /*
* Spin until CLK reaches `want_asserted`, or timeout/ATN/RESET. Caller holds * Spin until CLK reaches `want_asserted`, or timeout/ATN-change/RESET. Caller
* local IRQs disabled. Uses a microsecond budget rather than a fixed udelay so * holds local IRQs disabled. Uses a microsecond budget rather than a fixed
* a slightly-fast C64 is handled correctly (research §1.3). * 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; unsigned int waited = 0;
while (iec_clk_asserted() != want_asserted) { while (iec_clk_asserted() != want_asserted) {
if (iec_atn_asserted()) if (iec_atn_asserted() == data_phase)
return IEC_RX_ATN; return IEC_RX_ATN;
if (iec_reset_asserted()) if (iec_reset_asserted())
return IEC_RX_RESET; return IEC_RX_RESET;
@ -143,7 +153,7 @@ static enum iec_rx receive_byte(u8 *out, bool *eoi, bool data_phase)
*eoi = false; *eoi = false;
/* 1. READY-FOR-DATA: wait for talker to release CLK, then release DATA */ /* 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) if (rc != IEC_RX_OK)
return rc; return rc;
iec_data_release(); 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 */ /* 3. RECEIVE 8 BITS, LSB first */
for (i = 0; i < 8; i++) { 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) if (rc != IEC_RX_OK)
goto out; 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) if (rc != IEC_RX_OK)
goto out; goto out;
/* released(high) = bit 1, asserted(low) = bit 0 */ /* released(high) = bit 1, asserted(low) = bit 0 */