▲ 80 r/EmuDev+2 crossposts

iPod Video 5.5G emulated: Apple's retail firmware boots, draws its own UI, and runs a game

github.com/siggifly/ipod-emulator

Apple's retail iPod 5.5G firmware boots on an emulator I have been building for four days. It formats its own filesystem, reads the click wheel, draws its own menus, and runs Brick.

https://i.redd.it/jxyu7lp3odjh1.gif

The 5.5G shipped on 12 September 2006. I bought one that year, at twelve, and it was the first Apple product I ever owned.

I chose this model deliberately, because it is the one every clickwheel game runs on. That it is also the one I grew up with is a coincidence I have enjoyed more than I expected: my first Apple product, now running on a MacBook Air M4, twenty years later.

Working at Apple was always the dream. Having now spent four days taking their firmware apart, I have no idea whether that brought it closer or ruined it, and I find that funny either way.

What it actually is

Not a reimplementation of the interface. Apple's own code the whole way down.

The emulator enters at address 0, where the CPU fetches out of reset. Apple's bootloader brings up SDRAM, talks to the power chip over I2C, uploads firmware to the video co-processor, reads the partition table, DMAs 7.5 MB of RetailOS into memory, checksums it and jumps. RetailOS then remaps memory, starts its RTXC kernel and 61 tasks, mounts a FAT12 volume hidden inside the firmware partition, formats and populates its own FAT32 volume, spins the drive down, and draws.

Then you scroll to Extras, open Games, and play.

https://i.redd.it/4wma2f58odjh1.gif

There is a window too — a drawn iPod whose wheel, buttons and hold switch drive the machine, with a debug mode that shows instruction counts, both clocks, and a does the input reach RetailOS? panel carrying arrival counters at the real addresses. It proves its own claim rather than asserting it.

https://preview.redd.it/ks4euy0eodjh1.png?width=2940&format=png&auto=webp&s=8ceaada6695d0836a72585cda58fb2078b216410

Before anything else: I did not write this

I am not an engineer. I am a tinkerer, and I did not write a single line of code in this project.

It was written with Claude Opus 5 over four days. The model wrote all of it and did the reverse engineering: bisecting a boot to the exact instruction where a function stops returning, reading forty instructions of ARM to recover a structure layout, running the same experiment four ways to check a result held.

What I did was steer. Decide what was worth chasing and what was a rabbit hole. Push back when an answer sounded too convenient. Come up with ideas when it got stuck, which happened plenty. Go find the prior art that unstuck it. Say "that cannot be right, look again" — which was worth more than it sounds, because a confident wrong answer costs hours and sometimes only a human going hang on catches it.

That is not nothing. It is also not writing an emulator, and I would rather say so than let anyone assume otherwise.

Why

Apple sold 54 clickwheel games and delisted them in 2011. For years, if you had not bought them then, that was that.

Olsro's Clickwheel Games Preservation Project changed that, and it is why I got interested. It preserves the games and works out the authorisation. I used it on my own iPod last year and left it there.

The catch is that it needs the hardware, and a used 5.5G in good condition is not cheap now. So the games are being looked after while the machine they need gets scarcer. That seemed backwards.

I was also abroad on holiday with my iPod at home, which ruled out the obvious approach. And I had just seen someone running Tahoe 26.5's kernel natively on a Galaxy A55 a few days earlier, which is most of the reason I tried at all.

Four days, roughly

Day 1 — my iPod was at home, so the first problem was having anything for iTunes to talk to. That worked the same night: a virtual iPod that iTunes accepts as a real device, with the USB identity rebuilt out of real firmware artifacts. (The Palm Pre did something similar in 2009 and Apple kept breaking it until Palm gave up. It stuck with me as proof the idea was possible.) The ARM7TDMI core was written the same day — about 1,900 lines, zero dependencies.

Day 2 — the disk. RetailOS will not finish booting until it can write, which I had not allowed: it blocks on a one-sector write, times out after 3.9 simulated seconds, and retries forever. A read-only disk looks exactly like a deadlock and is not one.

Day 3, 02:06 — Apple's bootloader loaded RetailOS, verified it, and handed over. 02:16 — I set up a headless Ghidra server. That ten-minute gap is the whole story of the day: until then most questions could be answered by running the thing and watching, and once RetailOS was booting and then halting somewhere inside itself, they became "what calls this", which does not yield to watching.

Day 4 — the display, which was the hard part and has its own section below.

Full timeline with commit counts: docs/HOW-IT-WAS-BUILT.md

The display, and the thing I would tell you if you only read one part

The iPod 5.5G has a Broadcom BCM2722 next to the PortalPlayer SoC. Rockbox treats it as a framebuffer — write pixels, kick an update. RetailOS does not. It reads a service directory out of the co-processor's memory, binds an RPC channel, creates a layer, and uploads dirty scanlines to a surface. None of that is documented anywhere I could find.

What cracked it: the resource volume on the iPod's own disk holds the co-processor's firmware and a library of .vll codec plugins, and those plugins are ordinary ELF shared objectse_machine = 0x5f, EM_VIDEOCORE, an officially assigned ELF machine number. Standard tooling reads their symbol tables, and their undefined symbols are by definition what the co-processor's runtime exports. All 183 of them, including dispman_object_create, gencmd_register, and 25 Nucleus PLUS internals.

dispman_* is DispmanX — the same display manager Broadcom would later ship on the Raspberry Pi. The later chip is publicly documented and the concepts carry.

I never emulated the VideoCore. The transport layout came out of RetailOS's own parser, on the principle that code which reads a structure is a specification of that structure.

The whole thing is written up here: research/21-the-videocore-runtime.md

A small one, afterwards

Once RetailOS was drawing, the boot still opened on a black screen with what looked like diagonal noise. That frame had been dismissed twice as fragments. It was the Apple logo — sitting in the co-processor's buffer since the very first run at 62-halfword pitch, waiting for a rectangle placement the model never executed. A 62-pixel-wide logo in a buffer being read 320 pixels wide. The fix was to stop looking at the panel in screen order and look at it in address order.

the Apple boot logo · write-up

What does not work

  • No audio. The Wolfson codec is unmodelled.
  • 30% of real time headless, 19% with the window open. A JIT is the answer and is not written.
  • Purchased titles do not launch. Apple's DRM refuses them. The identity it binds to is understood — the FireWire GUID, not the serial — the keystore is not.
  • Four values in the co-processor transport are chosen rather than measured, and there is no timing model, so a bug that only appears when a reply is late is invisible to me.
  • The boot takes ~300 seconds of simulated time where hardware takes five or ten. Something waits far longer than it should and I do not know what.

Everything faked has a row in a ledger with a written condition for retiring it. Nothing is faked without one: research/12-bypass-ledger.md

Where it goes

Running the games with no RetailOS at all is the real goal — host the framework yourself and you do not need Apple's OS in the loop. I did not know what that framework did, and there are around 98 entry points. Booting RetailOS is how you find out, because Apple's implementation of it is inside.

Then audio, a JIT, and eventually every non-iOS iPod. I picked the 5.5G because it is the superset: 14 of the 54 titles are iPod Video exclusives and do not run on a Classic at all.

Credit

Rockbox first and by a distance. pp5020.h and the iPod target code are where most of the register semantics came from, and it doubled as an oracle — a known-good OS to boot when something broke and I needed to know whether it was me. iPodLinux underneath it.

dreamlayers, who identified those .vll files as ELF DLLs on the Rockbox forums in 2009. I worked it out independently and then found the post, sixteen years late.

Also Olsro, daniel5151/clicky (which independently needed the same two undocumented register bits), freemyipod and q3k's wInd3x writeup, qemu-ios, giek2000, Xlinka, dstaley, raspberrypi/userland, and the Internet Archive. The full list, with what each one gave, is in the repo and it is longer than this.

If you feel like giving money, give it to Rockbox or to Olsro before me. Rockbox has been documenting this hardware for twenty years so that anyone could use it. Olsro spent a year working out the authorisation so the games could be played again at all, and without that I would never have started.

reddit.com
u/siggifly — 5 days ago