Christian Werner dafcdfbd7b
All checks were successful
Build kernel module / build (1:6.18.34-1+rpt1, trixie) (pull_request) Successful in 1m13s
Build kernel module / build (1:6.12.93-1+rpt1, bookworm) (pull_request) Successful in 1m16s
Build kernel module / release (pull_request) Has been skipped
feat(kernel): Add launch.sh for streamlined module loading and frontend execution
Introduce `launch.sh` as a single execution entry point for the IEC listener module and Python frontend. Automates kernel module selection, virtual environment setup, and cleanup to simplify usage. Ensures consistency by unloading modules on script exit.
2026-06-20 05:31:21 +02:00
2026-06-18 22:25:42 +02:00
2026-06-18 22:25:42 +02:00
2026-06-18 22:25:42 +02:00
2026-06-18 22:25:42 +02:00
2026-06-18 22:25:42 +02:00
2026-06-18 22:25:42 +02:00

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 for the full design and _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)

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:

.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 <CR>   <EOI>
[ATN]    released -> not addressed -> IDLE

Kernel module (on the Pi)

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 for the level-shifter circuit and the pre-C64 bring-up checklist, and 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_<kernel_version>.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), insmods it, runs iecpoc, and always rmmods on exit — so the Pi is left exactly as before:

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 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.

Description
No description provided
Readme 356 KiB
Languages
Python 39.1%
C 32.3%
Shell 22.3%
Dockerfile 4.4%
Makefile 1.9%