Running it in the emulator¶
You do not need an iPod to work on Chikuma. The machine in qemu-ipod/ is a QEMU model of the
S5L8702 that boots both Apple's RetailOS and Chikuma, against the same disk image -- which is the
whole reason claims in this project are checkable.
The two commands¶
cd qemu-ipod
./retailos.sh # Apple's firmware, in a window
./run.sh --payload ../retailos_re/payload.elf # Chikuma instead
retailos.sh is run.sh with the device's identity and the savestate conveniences added. Go to
run.sh directly for a payload or an unusual configuration. --help lists everything.
Headless, for a single frame:
It reaches the Language screen in about 5 seconds.
Driving it¶
Live, in the window: two-finger scroll or a mouse wheel turns the click wheel, Up / Down step it, Enter or Space is SELECT, Left / Right are PREVIOUS / NEXT, M or Esc is MENU, P is PLAY/PAUSE. Left and right click are SELECT and MENU. The pointer is absolute, so the window never grabs your mouse.
Scripted, from Python -- tools/ipodmachine.py is the interface, and tools/drive.py is a thin CLI
on top of it. Import it rather than re-implementing the QMP handshake; half a dozen throwaway scripts
have done that and each got some of it wrong.
from ipodmachine import Machine
with Machine(audio="coreaudio", wav="/tmp/out.wav", debug="sm1") as m:
m.wait_for_screen() # boot, then the first frame with real content
m.press("select") # a real 120 ms press
m.scroll(24) # a dozen notches move one menu row
m.shot("/tmp/menu.png")
print(m.uart()) # what the guest printed
Sockets, logs and the gdb port are per process, so a probe never disturbs an emulator you already have running, and two probes can run at once.
Three things that will catch you¶
A press must have a duration
Sending down and up in one batch is a zero-length press, and the firmware's debounce never sees
it. About 120 ms of guest time is a tap; a second is a press-and-hold. The monitor's sendkey
gets this right on its own.
The wheel is coarse, and its gain is not fixed
A menu row costs somewhere between six and a dozen notches depending on the list, so an
open-loop scroll(N) that works on one screen overshoots on the next. Drive to a known row by
screenshotting and checking, not by counting notches. tools/menu_walk.py walks a path by row
for exactly this reason.
--warp breaks interaction
icount keeps the instruction count exact, and by default virtual time is tied to real time. With
--warp (sleep=off) QEMU skips the idle and the guest clock runs about 35× fast -- a
100 ms press arrives as a three-second hold, and the firmware's 150-second idle sleep lands about
four seconds after boot. Use --warp only for a batch boot that takes no input.
Which disk you boot decides what is there to play¶
The library lives on the disk image. The default (retailos_re/build/ipod_apm.img) is a small test
volume. A larger one with movies and podcasts is used for the media work and is passed explicitly.
If the library reads as empty -- menus present, songs absent -- the FireWire GUID is wrong: the retail database is bound to the GUID it was synced with, and the machine has to present the same one.
Debugging¶
tools/hook.sh attaches gdb hooks using the Ghidra project's own function names:
Savestates are worth the habit. Anything that takes an hour of guest time to reach should be frozen just before the interesting moment; one such savestate turned a 90-minute end-of-track experiment into a three-minute one.
The retired emulator¶
emu/ is a Python + Unicorn machine that was the oracle until 2026-08-09. It is reference, not a
tool -- read it, do not run it.
It was retired for one reason, learned twice: Unicorn delivers interrupts only at slice boundaries, while QEMU raises the line the instant a device asserts it, as the hardware does. Three bugs in a single session lived exactly in that gap, including an interrupt landing in the middle of a kernel service -- which faulted on every QEMU boot and passed the Unicorn gate every time. A model that cannot produce the hardware's timing cannot be the oracle for behaviour that depends on it.
What emu/ still is: the record. Its device models carry the measured comments the QEMU port was
checked against, and the reports cite it throughout.