# comodore-iec-emu — IEC listener PoC A proof-of-concept Commodore **IEC listener** device on a Raspberry Pi Zero 2 W. It behaves like a printer (default device address **4**): it never talks back, it only **listens**, and it debug-prints in real time everything the C64 sends. The timing-critical IEC handshake (ATN ack, per-bit sampling, EOI) runs in a **Linux kernel module**; a **Python** userspace program decodes and pretty-prints. See [`_plans/poc-listener-printer-PLAN.md`](_plans/poc-listener-printer-PLAN.md) for the full design and [`_research/`](_research/) for the grounding research. ## Layout ``` kernel/ # the kernel module (real-time half) — builds on the Pi iec_listener.c ISR, IRQ-off bit loop, listener state machine, /dev/iec0 iec_listener.h shared struct iec_record + ioctls (mirrors device.py) iec_lines.h logical line layer, direct BCM register access (hot path) iec_timing.h handshake timing constants Makefile out-of-tree build + load/unload/overlay targets dts/ device-tree overlay (pin reservation / pulls) iecpoc/ # Python userspace (everything else) — host-testable device.py /dev/iec0 record stream (wire format, replay) decode.py command-byte + event decoder petscii.py PETSCII -> display glyphs log.py the annotated trace formatter (PLAN §8) main.py CLI (--address, --raw, --logfile, --replay) tests/ # host-runnable, no Pi/kernel needed docs/ # wiring.md, kernel-notes.md ``` ## Userspace (Phase 0 — works on any host) ```bash python3 -m venv .venv && .venv/bin/pip install -e '.[dev]' .venv/bin/pytest # run the tests .venv/bin/python -m tests.fixtures # (re)generate the capture .venv/bin/python -m iecpoc.main --replay tests/data/hello_world_session.bin ``` On the Pi, point it at the live device instead: ```bash .venv/bin/python -m iecpoc.main --address 4 # reads /dev/iec0 ``` Example trace (the `OPEN 1,4 / PRINT# / CLOSE` session, abbreviated): ``` [IDLE] waiting for ATN [ATN] asserted -> DATA low (ack) [CMD] $24 LISTEN 4 (addressed: ME) [CMD] $60 DATA SA=0 [ATN] released -> LISTENER [DATA] $48 'H' ... [DATA] $0D [ATN] released -> not addressed -> IDLE ``` ## Kernel module (on the Pi) ```bash sudo apt install raspberrypi-kernel-headers cd kernel && make sudo insmod iec_listener.ko address=4 # creates /dev/iec0 sudo rmmod iec_listener # releases DATA on unload ``` See [`docs/wiring.md`](docs/wiring.md) for the level-shifter circuit and the pre-C64 bring-up checklist, and [`docs/kernel-notes.md`](docs/kernel-notes.md) for the resolved kernel real-time decisions. ## Running the frontend on the Pi A tagged build publishes a release zip that bundles everything needed to run on a Pi without building anything: ``` comodore-iec-emu/ selftest.sh one-shot wiring self-test (no C64 connected) launch.sh load module → run iecpoc → unload pyproject.toml iecpoc packaging metadata README.md iecpoc/ the Python frontend modules/ iec_listener_.ko, one per supported kernel ``` Unzip it on the Pi and run **`launch.sh`**. It installs the frontend into a local `.venv`, detects the running kernel and picks the matching `.ko` (by *vermagic*, not filename), `insmod`s it, runs `iecpoc`, and **always `rmmod`s on exit** — so the Pi is left exactly as before: ```bash unzip comodore-iec-emu-*.zip && cd comodore-iec-emu sudo ./selftest.sh # first: check the wiring (want 0x1F) sudo ./launch.sh # then: load + run the frontend (address 4) sudo ./launch.sh --address 8 # listen as device 8 sudo ./launch.sh -- --raw # forward extra args (after --) to iecpoc ``` The module is picked automatically; pass `--ko PATH` only to force a specific file. `launch.sh` needs `python3` plus `python3-venv` (and network on first run for the build backend). See [`kernel/README.md`](kernel/README.md) for the self-test details and the per-kernel module selection. ## Status - **Phase 0 (userspace + tests):** complete, runs on any host. - **Phase 1 (kernel command phase):** skeleton present; needs Pi bring-up. - **Phase 2 (data-phase reception):** the real experiment; tune timing on real hardware. See PLAN.md §9 for the phased milestones.