Status¶
Last updated 2026-09-14. Everything claimed here has a gate behind it, and what does not work is listed beside what does.
The target device is the iPod classic 6G, S5L8702. Nothing here has been tried on any other model.
What fidelity means here¶
As close as possible to the original's behaviour, not a byte-for-byte copy of Apple's compiler output. Every line that is RetailOS's behaviour comes from the reverse - the archive's records, the firmware's own code, or a measurement on it - and where the firmware does not decide something (how the port is laid out, what a file is called, where shared code lives) the choice is ours and says so in the file. A byte-match would forbid recompiling and changing anything, which is the point of the exercise.
So screens are compared by subtraction and some come out byte-identical, and a reassembly gate once proved the boot chain's disassembly was read correctly. Those are evidence, not the target.
Works, with a gate¶
Each of these has a test or a measurement behind it that fails if the behaviour changes. The full
table, with the gate for every row, is chikuma/ROADMAP.md.
Kernel. The firmware's own RTXC design, not a stand-in: tasks, priorities, preemption, mutexes with priority inheritance, timers, slots and events, a heap with boundary tags, and the service dispatch. All 49 dispatch slots are read and classified. The 34 tasks carry the firmware's own names, ids and priorities: 32 of them recovered from the image's own strings, with two independent naming mechanisms agreeing on the 20 they share.
The boot chain, reproduced whole. The mask boot ROM and Apple's EFI NOR bootloader (the ONB, 40 modules) are both C in this tree, built from source, signed with Chikuma's own certificate chain and booted on the emulated machine: NOR read, X.509 verify, AES decrypt, SEC, PEI, DXE, BDS, hand-off. The control is that the same chain boots Apple's own unmodified RetailOS to a working UI. See the boot chain, in C.
Storage. ATA (LBA28/48), a partition layer, FAT16 and the HFS+ reader. The filesystem does not
call the disk directly: it posts to a queue and blocks, because that is what the ATAWorkLoopTask /
ATAWorkLoopIRQTask pair in the image does.
Display. The real LCD pipe, driven with the firmware's own register values, including the panel tear-effect interrupt and the second hardware window that composites album art under a hole in the UI surface. The compositor above it has its name back: Granite, six layer slots and two backends (the display pipe, and the hardware scaler), pushed from its own thread that the tear-effect interrupt signals rather than from whoever painted last. Granite's layers are what this tree already called display windows, so nothing was renamed to make the mapping true - it needed saying.
Transitions on their own surfaces. A screen's slots - menu, status bar, preview pane, media list, Now Playing - render into retained surfaces and are composed the way the firmware composes them, so a push slides and the fade family runs through a slot's own alpha instead of being redrawn frame by frame.
UI, built from the archive. Screens are not constructed in code. A screen names its controller
through the archive's SCST record; its rows, labels and value properties come from ITEM and
SORC; its key and event bindings come from CEVT and SEVT. All eight navigator verbs dispatch
on the archive's own bindings, including the two dead bindings the device also ignores. Text renders
through FreeType, taken from upstream.
Menus that go somewhere. Home, Settings, Music, and the media lists drilling down Artists →
Albums → Songs → Now Playing, with the browse scope kept on the model the way the firmware keeps it.
MENU walks back up and restores the level you left. Every distinct-value list opens with the
device's own aggregate row too ("All Songs", "All Albums"), naming the level below and counting in
that level's units, verified row for row against the original.
Genius. The whole path, not just the reader. The Music menu's Genius row pushes the Genius
list (or the intro screen when the database is absent), the list carries its own Refresh and Save
Playlist command rows with the row classes the archive names for them, and Now Playing's Genius
layout runs the slider with the firmware's own gates - a wheel turn while the finger is down moves
the switch and nothing else; the release starts the run or raises the matching error. A run publishes
its playlist as the playback source and lands on the list, with Now Playing arriving over it once the
firmware's 3000 ms floor expires - which is a floor under the generator's work, not a timeout.
Saving writes a real Genius playlist, seed and all. The recommendation walk runs on the Extras.itdb
graph in the host gate, against a real device's database.
Playlists. The Playlists screen lists what the device lists: a podcast playlist is not a music
playlist, and the temporary On-The-Go list is always the last row, with the row class the archive
names for it, its own icon per kind (Genius, smart, podcast, ordinary) and the song count beneath the
name - except on a Genius row, where the line beneath is the seed track's artist, measured on the
original against a real library rather than guessed (a small test library cannot tell the two
strings apart). On-The-Go survives a reboot: the file the device writes is reproduced byte for byte, and
fsck_hfs calls the volume it was written into clean.
Row order. Songs, Albums, Artists, Genres and Composers come out in the order the database states rather than one Chikuma computes: see Storage. Verified against the original on the same disk, row for row.
Localization. English is the resource archive itself and a language is an override run keyed by resource id, which is how the firmware does it; Français and Japanese render from the image's own bytes, and the strings can be edited and repacked.
Volume, from the wheel to the codec register. The whole chain is recovered rather than
approximated: the wheel's two-per-notch step, the object's 256-level domain with the region ceiling a
European unit ships with, the limit rules behind the padlock, and the Wolfson curve
0x3E + level * 0x3B / 0xF2 that lands in the part's LOUT1/ROUT1 registers over two-wire. It
reproduces an earlier bus capture exactly, including why 255 levels fold onto 63 distinct codes.
Pause is what the device's pause is: one byte written, with no DMA teardown at all.
Audio. Music plays. The sm1 block at 0x38500000 is a bus-mastering hardware decoder: packets
in over a DRAM descriptor chain, PCM out into fixed SRAM buffers, so there is no Apple codec in the
image to recover, and the specification is written down. The feeder blocks on the DMA terminal count
instead of spinning, which is what lets the window manager keep painting while a track plays: Now
Playing shows title, album, position, progress and cover with playback running.
Video. Both test films decode with zero errors, including the two-layer GOP, after the model was corrected to start on the run-bit 0→1 edge the way the hardware does - and the picture reaches the panel now, full screen, with the backlight held on the way the device holds it (by repeated assertion, measured off the original's own PMU traffic) and the video window taken down when the film is left, so the list behind it is clean.
Cover Flow, drawn by the CPU. The OpenGL ES in this image is Vincent with Apple's JIT turned on - a software rasteriser generating ARM at run time - so the tilted, reflected covers are the CPU's work. Ours runs on the same library, with the device's own 21-tile ring where a tile carries its texture as it travels, the fan's animations timed to the firmware's own 3000 ms window, and the frame loop paced at the firmware's three ticks. Measured against retail on the same flick: a median 33 ms between published frames against retail's 23, and no spike over 60 ms in either direction.
Artwork. The ArtworkDB → .ithmb join runs on the device, cells are cached, and album rows draw
with the correct split, separator and stride.
Podcasts. The Podcasts screen opens and its rows drill into a show's episodes, the same generic media-list machinery the Videos family uses, grouped by album rather than by artist because that is the field a podcast's own tracks carry it on. The caption under Home's watermark is pixel-identical to the original once the count is published on every highlight move rather than once at boot.
Extras. Several of the Extras apps open now, each built from the archive and gated by a host test that reads the real records: the World Clock (its list face, the analog city clocks with the firmware's own sprite-mirrored hands, and the Add/Edit/Delete option bar with its selection pill), Alarms and the alarm-properties screen, the Sleep Timer borrowed from Now Playing, the Stopwatch with its Start/Stop/Lap and the newest-three-laps cap, and Notes. The wheel and keys route to whichever control the layout makes active - on the World Clock's edit layouts that is the option bar, not the list underneath. Two of these carry declared stand-ins: the World Clock's added-clocks list is a RAM fixture (Cupertino and Tokyo, two real cities from the firmware's own table) with no persistence or city-picker wired yet, and the Sleep Timer reaches expiry but the power/idle sleep it would trigger is not reproduced.
The click-wheel games. All three shipped titles - iPod Quiz, Klondike and Vortex - launch from the Games menu and run: the loader binds each against the firmware's own eleven interfaces and 486 functions, and each reaches its name-entry screen and keeps rendering, taking wheel and key input, with no fault and no quit. Their sound is stubbed and a game still has no writable state, so this is "they boot and play frames", not "they are finished". See click-wheel games.
The disk, addressed whole. ATA reads wait on the completion interrupt instead of polling the
status register, files created in the HFS+ catalog carry the three date fields the firmware stamps
(and macOS is the judge of them: the test mounts the volume and compares stat), and 48-bit
addressing reaches past the 2^28-sector wall - checked with a sparse 160 GiB image carrying one
marker at the last sector and a different one where a 28-bit truncation would wrap.
Fidelity checking. The retail firmware and Chikuma boot the same disk on the same emulated machine, so screens are compared by subtraction. Several screens are byte-identical to the original; where they are not, the differing pixel count is the number that gets quoted.
Does not work yet¶
- Not every Extras app is served, and the built ones have unfinished depths. Clock, Alarms, Sleep Timer, Stopwatch, Notes and Games open (above); Calendars and Contacts are not served, and Screen Lock is a dispatch branch rather than a full screen. Inside the built ones the deeper paths are stand-ins: adding or persisting a world clock (only removing one already in the fixture works), the Sleep Timer's actual sleep, and the Stopwatch's Clear Logs.
- Most screens are unserved. Of 114 push targets in the archive, 43 are registered and 19 are served by the generic menu list. 50 are unserved and 2 have names that do not resolve at all.
- Compilations and Audiobooks are still unbound rows on the Music menu, and the reason is a missing count rather than a missing binding: this tree's iTunesDB reader has no compilation flag, and the audiobook and video rows want the media-kind masks read out of their own handlers. Those rows say they are unbound rather than answering from a count we do not have.
- The now-playing list's playlist icon. Three of the four kinds are answered from the database (Genius from the seed, smart from the rules, podcast from its flag); the now-playing one is not a database field at all - the firmware's is the runtime list the player owns, so it waits on that.
- No preview pane follows a highlight below Home: every
*Hilitedbinding on those menus is still unbound. - Photos: the database is read and the controllers are named, but nothing is reproduced yet.
- USB is not started. Five USB tasks exist in the image. Mass storage is the goal (a car head
unit playing from the device needs MSC, not iAP), and it is a reproduction job because
USB MSCis already a task there. - Catalog node split. Disk writes work now: the ATA model carries the firmware's WRITE DMA, and
FS_CREATEreaches a reproduced HFS+ catalog insert thatfsck_hfspasses. What is not yet reproduced is splitting a full B-tree leaf, so a create into an already-full node is refused rather than performed. - A game has no sound and no writable state. The three shipped games run, but every
SoundEffectslot they ask for is a stub, andoptions/statsmiss because this port's FAT16 has no writer yet. - No installable release. See below.
Deliberately out of scope¶
- Byte-matching Apple's compiler output. See what fidelity means here.
- Re-typing published libraries. FreeType, SQLite, zlib and the Vincent OpenGL ES implementation
are used from upstream at the version the image advertises. What Apple grew onto them (a vendor
extension like
GL_APPLE_binary_shader, for instance) is recovered, because that could have been different.
Wanted, once music is solid¶
A night mode: a time-based dark theme, because the stock UI's white backgrounds at night are
painful. It is the first feature that is Chikuma's rather than Apple's, and most of it lands as
data. The archive carries 84 named COLR records, semantic and id-keyed (Background_Color,
System_Text_Color, Genius_Background_Color), which is a single choke point for a colour change.
Better than that, the firmware already ships two skins - not as a flag, which it does not have,
but as paired resources the screen chooses between: StatusBarWhite_* against StatusBarBlack_*,
the black option-bar caps, the black transport glyphs (property id 0x7F0C white against 0x7F0D
black, and the getter compares the id itself), and Now Playing's dark colour set beside its light
one - Apple's _White_ means "for the white skin", which is why NowPlaying_White_Text_Color is
black. 122 resource names carry White against 44 carrying Black, so the pairing covers the status
bar, the option bar and Now Playing, and not the menus. A faithful dark mode asks for the black twin
where the device has one, and declares the rest as ours.
Installing it¶
There isn't a release. The install path exists (a two-stage payload, a DFU upload path, and a bundle format), and the boot chain it would install is now reproduced end to end - but it is developer plumbing and it touches flash on a device that is no longer manufactured. On the device says what the path is and why you should think twice.