Christian Werner 0d6034702a test(iecpoc): add plain-mode tests for TraceFormatter
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
2026-06-20 18:19:47 +02:00
2026-06-18 22:25:42 +02:00
2026-06-20 17:52:41 +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%