Add row to "Decisions baked into the code" table explaining the DATA line
control design: all DATA assert/release operations in the worker and ATN ISR
go through iec_data_*_sync() under iec_data_lock to serialize the GPFSEL0
read-modify-write across CPUs. The release wrapper refuses to release while
ATN is asserted, preventing the ready-for-data release from erasing the ISR's
presence acknowledge — the bug that caused intermittently missed
LISTEN/UNLISTEN commands. Init/exit/self-test keep raw unconditional helpers.
Generated by Clanker 🤖
The worker kthread and ATN ISR both modify the DATA line by changing GPIO2's
direction (open-drain emulation). On quad-core Pi they run simultaneously on
different CPUs, causing their read-modify-write operations on the shared GPFSEL0
register to clobber each other. The ISR's ATN presence-acknowledge could be
erased by the worker's ready-for-data release, causing the C64 to see "device
not present" and drop LISTEN/UNLISTEN command bytes.
Add iec_data_lock spinlock and two synchronized wrappers:
- iec_data_assert_sync(): serializes DATA assert
- iec_data_release_sync(): serializes DATA release and refuses to release while
ATN is asserted, preventing the acknowledge from being undone
Convert all worker and ISR DATA accesses to use the _sync() wrappers. Keep raw
unconditional helpers for init/exit/self-test (exit must free the bus
unconditionally; self-test probes directly).
Generated by Clanker 🤖
Added a 'Frontend output modes' section explaining plain text (default),
--debug (annotated trace), and --raw (hex), with launch.sh examples for
each.
Generated by Clanker
Covers the plain property, per-record-type output (printable chars,
CR/LF newlines, LISTEN/UNLISTEN dividers, silenced events and other
commands), and a full-session integration check.
Generated by Clanker
Default output now shows only the received characters; CR/LF become real
newlines and LISTEN/UNLISTEN print a clearly visible divider. Pass --debug
to restore the previous full annotated trace.
Generated by Clanker
Adopt the confirmed-working reference's pin assignment so the module runs
on that proven wiring: DATA=BCM2 (pin 3), CLK=BCM3 (pin 5), ATN=BCM4
(pin 7). RESET is kept and relocated to BCM17 (pin 11), the pin freed by
moving CLK. All five GPIOs stay in bank 0, so the direct-register hot
path and ATN IRQ are unchanged -- only the IEC_GPIO_* defines, the
device-tree overlay, and the docs/self-test move.
DATA/CLK now sit on the ARM I2C pins (GPIO2/3) with the SoC's fixed
~1.8k pull-ups; keep dtparam=i2c_arm off. The bare-board self-test
expectation changes from 0x15 to 0x0b accordingly.
Generated by Clanker
The IEC bit loop sampled GPIO with a single register read, so a glitch
on a level-shifted 5V<->3.3V bus could corrupt a bit with no chance to
retry (the loop runs with IRQs off). Add a stable-read filter that only
believes a level change after it holds IEC_DEBOUNCE_US (now 5 us); the
fast path is a single read so tight CLK polls stay cheap. Used across
the receive hot path; the ATN ISR and self-test keep raw reads.
Generated by Clanker
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>