(This is a visitant article by Rairii)

So today I reached a nice milestone in one of my projects: my activity porting traditional Mac OS to PowerPC systems alongside what I affectionately call ArtX chipsets (Nintendo GameCube, Wii, eventually Wii U; the GameCube’s Flipper unified chipset was extremely much designed by ArtX as it was efficiently completed whenever ATI acquired the company, and the Wii’s Vegas (and its afterward revision Bollywood) and Wii U’s Latte are efficiently direct successors) reached the desktop for the archetypal time, most apt the archetypal period since the CHRP copy era that person exterior of Apple brought up traditional Mac OS on a new scheme to the desktop. Despite motionless being nowhere near prepared for a community release, I desire to conversation concerning the challenges I faced alongside this project so far.
But first, why harbor traditional Mac OS to this hardware in the archetypal place? I’d already ported NT of course, and during that activity I acquired several PowerPC Mac laptops which I additionally ended up porting NT to, activity on one harbor improved the others and so on. This got me acquainted alongside the traditional Mac OS, and I cut in affection alongside the jank as it were (hacks upon hacks upon hacks going rear to the 1980s is item I’d certainly depict as “jank”). Not to citation the m68k part of the TBXI is called “SuperMario”, and was for the complete PowerPC era. I amazement if a developer was a fan in the first 1990s?
Also, for the record: I started this project on 2025-06-22 (when I forked upstream openbios), no benevolent of coding delegate or LLM was used, all coding, reversing and debugging activity done by myself. I do these projects for fun and to learn, if I used “AI” it wouldn’t be fun and I wouldn’t study anything.
Let’s commencement alongside the first boot. “New World” traditional Mac OS starts out alongside a bootloader called the “Trampoline”, which runs on an Open Firmware environment. So I needed to harbor OpenBIOS. Luckily, about this period Goldfish64 had started his own harbor of Mac OS X to the Wii, and he’d made an OpenBIOS harbor that I could use several drivers from, although for assorted reasons I didn’t fair desire to use his harbor immediately (which was lone targeting Wii/Wii U, I wanted to mark GameCube too). I additionally decided to reuse several existing code to create several controller betterment easier (specifically the display driver), and thus changed my existing NT ARC firmware loader to burden a big endian ELF instead.
I needed several small figure of disk drivers for first bringup, so decided to harbor my two drivers for devices on the EXI bus (this is fair Nintendo and Macronix’s name for SPI), the “SD Gecko” (which is fair a direct through adapter for a GameCube recollection cardstock slot allowing for SD complete SPI), and the “EXI-IDE” (my controller is really not accurate to genuine hardware, but to my inaccurate emulation in my Dolphin tree allowing for evaluation under emulator alongside the low flat debugging assistance that implies).
I wasn’t certain what benevolent of on-disk format I wanted to use (these drivers on my before NT harbor ran entirely using disk images on a FAT filesystem, because of the necessity for compatibiltiy alongside existing homebrew loaders that burden executables from FAT filesystems, but this would naturally be constricted to 4GB images for FAT32, and afterward versions of traditional Mac OS needed more), so for first evaluation I fair took the “Mac OS ROM” TBXI document and placed it immediately on a FAT filesystem as \ppc\bootinfo.txt for OpenBIOS to burden it on boot.
So alongside that I could get the Trampoline running. I decided that I should try and use as many patches to the depiction as imaginable as near to run-time as possible, so mostly untouched existing CD images could be used eventually.
The archetypal matter I had was that the Trampoline makes several assumptions of the bodily location area layout that are not true for this system. The bodily location of a buffer gets calculated as an invalid address, so I patched that to an location guaranteed to be valid, and additionally the corresponding code environment up the recollection chunk list/page array entries for the kernel.
Additionally, the Trampoline expects that bodily addresses from 0x8000_0000 onwards corresponds to recollection mapped I/O (on existing systems this code ran on, this would equivalent to PCI-bus location space). This is not the case here, and in fact on Wii U, this location would equivalent to part of RAM. There was additionally another assumption that bankrupt on these systems, alongside holes between areas of RAM in the bodily location space, several of which were partially filled by memory-mapped I/O.
The way I fixed this was to place the initialisation of this MMIO mapping in the kernel recollection chunk list, to a map of bodily location 0x0100_0000 (partway through one area of RAM) to virtual location 0xE000_0000, alongside dimension 0x1000_0000. This specifically maps the entire of the MMIO, affirmative the end of one RAM area and (for post-GameCube systems) the commencement of another. (I may alter this in future, as on the Wii U, bodily location range 0xD000_0000-0xEFFF_FFFF is mapped to a part of the Radeon R7xx GPU called “HDP”, which can be used to map a perspective of bodily recollection alongside particular swizzling and/or endianness swapping, specified that a texture in several GPU-specific format can appear to be a linear framebuffer. It depends on whether I need to use this obstacle ) The function used for initialising a mapping in the Trampoline changed at several item and I ended up detecting the older type and hooking the function so I could activity about it (earlier versions used a 16-bit leaf count, afterward versions used 32-bit). Additionally, older versions of the Trampoline sets up unconditional 1:1 mappings for bodily recollection complete 0x8000_0000, which needs to be removed as they battle alongside the mappings set up earlier.
There follows assorted various patches that assume particular hardware. These include: patching out environment several bitflags which equivalent to the existence of a microcontroller established on all PowerPC Macintosh hardware, attempting to recalculate the bus frequence using a particular hardware timer, and assorted enroll writes and fixups connected to a particular interrupt controller equipment (it’s small endian since it’s on the PCI bus, and naturally, not current here).
Finally, the final call to jump into the loaded kernel initialisation code is hooked so additional patches can be made afterward following everything is loaded into memory.
Additionally, the equipment tree must be hacked up a bit to be in a form the Trampoline volition accept. The interrupt controller is faked to be among the recognised ones (the small endian one from above, since at a enroll flat it’s closest to the Flipper interrupt controller current on all the ArtX chipsets, albeit on Wii U it was changed slightly to add assistance for the additional PowerPC cores). The location of the example buffer in the equipment tree is changed to the uncached virtual location (in the 0xExxx_xxxx mapping), and eventually a counterfeit Mac99-compatible power administration equipment is added to the equipment tree, so the Trampoline prepares a power administration controller for it, that can be replaced later.
As mentioned above, the kernel initialisation is hooked so additional patches can be made. On the kernel side, there is lone one place to be made: the kernel contains multiple interrupt controller drivers, the one chosen is patched to equivalent the Flipper interrupt controller, alongside among the unused ones sacrificed to acquire additional area for this. The chief difference current is that the expected interrupt controller (whose controller is patched) has a bequest manner anywhere it raises a PowerPC interrupt whenever the interrupts alter (not fair whenever interrupts are raised) so this needs to be worked about by masking the raised interrupts in the kernel and afterward unmasking them afterward on in drivers’ interrupt handlers.
Additional patches are made in the “SuperMario” m68k code, and in PowerPC native executables. The m68k code has assorted codepaths that peruse and compose immediately to native interrupt controller registers, these need to be patched. The function pointers for enabling and disabling audio interrupts need to be stubbed out (there’s already an admission in that array that points to a stub, which is helpful). A function called first in the footwear procedure to initialise the ADB and power administration microcontroller does not inspect if the hardware really exists, and if it doesn’t it clobbers parts of low memory. A function for initialising hardware interrupts has a bug because of a copy-paste blunder, the intent was to skip former the code for initialising the interrupt for serial harbor 2 if the hardware isn’t present, but the developers forgot to alter the jump tag whenever copy-pasting the code from serial harbor 1, which causes an infinite iteration if serial harbor 2 doesn’t exist. As for native code, there’s additional code that says and writes to interrupt controller registers which needs to be correctly patched. The USBShimKeyboard controller initialisation code fails to grip the province anywhere no USB controller exists and in that case volition call a NULL pointer inner the m68k emulator. The power administration controller mentioned complete is additionally replaced entirely current (currently I use the stub controller from BlueBox but scheme to compose my own afterward to add appropriate assistance for shutting downward and restarting).
All of this is adequate to get the TBXI to footwear to blinking floppy – following that I wrote a display controller (thanks to Elliott Nunn for his virtio driver, it was extremely helpful) so I could see it correctly, followed by a controller for the gamecube controller ports (porting from my NT controller for the same) so I could move the mouse cursor about without USB.
At this item I started to execute a obstacle IO controller (for EXI devices), but ended up getting stuck thinking concerning how to agreement alongside how the disk format have to be laid out and how the controller should work. Then Mac OS for CHRP got dumped, and I got sidetracked alongside patching up the afterward TBXI images to footwear it, thinking operating 7.x would be helpful on a GameCube which doesn’t equal have adequate RAM to assistance 8.0 at minimum requirements, and following that I got sidetracked alongside another projects. Almost a twelvemonth afterward I came rear to it, implemented my preferred disk format in OpenBIOS and decided to hack up a controller faking SCSI (some another drivers did this, most notably traditional Mac OS drivers for assorted SATA controllers). There were a few issues I established whenever penning the fake-SCSI controller too: I had to add another place to the m68k code to power loading the disk controller (and I average this, a controller loaded from the disk itself, basically implementing the genuine disk devices on top of several partitioned obstacle device) as a newer category fairly than the older category (the older category was efficiently obsolete whenever the archetypal PowerPC systems released anyway, and I don’t equal assistance the old-style calls from my counterfeit SCSI driver, which is likely why it wrecked trying to burden it as an older category driver), and additionally established an engaging quirk anywhere the optical disc controller (loaded from the obstacle equipment itself) emulates 512-byte sectors on 2048- or 2352-byte field obstacle devices by making a peruse call environment up 3 scatter-gather buffers, apart from it never checked if the underlying SCSI controller really supports scatter-gather, so I had to execute assistance for that on reads.
I motionless saw a blinking floppy though, several debugging showed me what was being rejected, a PowerPC function StartLib!GetStartupDevice was being called to get the footwear device, and it made several assumptions of the two the equipment tree and that there was several benevolent of built in SCSI equipment that needed distinct handling. The archetypal one was uncomplicated adequate (fix the equipment tree in my driver’s initialisation code), the second one was additional engaging in that I had to grovel into inner structures to fix an indicator in 3 distinct places (including the equipment tree) to justify it was non-zero and so the accurate code way would be executed.
With this done I saw a blessed Mac icon for the archetypal time, but afterward got rejected by the HFS footwear field (something errored and it returned). This turned out to be a bug in my own driver: I was debugging in dolphin alongside a hacked up EXI-IDE device; study and penning data in PIO manner for IDE devices deals in 16-bit words, and I didn’t anticipate my code to get passed a entirely unaligned odd buffer location (low 4 bits equal to 1), so I had to modify my low flat controller to not equal attempt EXI DMA for specified an address. The bug manifested as a byte efficiently failing to be written out, alongside remaining data getting offset by one byte; at smallest this happened whenever study the header of a asset fork, and what unsuccessful was the validity checks for this data, making it much easier to debug (I ended up dumping RAM and comparing alongside the expected data from the disk depiction I was using).
After this, Mac OS 9.2.2 started to footwear in my hacked up Dolphin tree, extremely slowly. It took an hr before it eventually crashed, and afterward I tried 7.6, which additionally crashed, but earlier. I realised that it was likely crashing trying to burden MacsBug, removed it, and following concerning 20 minutes I was staring at the desktop.

The following dawn I tested on genuine hardware (using SD complete SPI). This basically booted 7.6 inside 40 seconds, and this was expected to be the slowest access manner for an SD card! My former cognition was penning an NT controller for SD complete SPI though, and perchance that is additional of an outlier. See this video installing NT on a GameCube using specified a obstacle device, and note that the NT content setup document copy is sped up 64 times – it’s VERY slow. The difference current is that I get to hog the entire CPU whenever performing a transfer – in fact there’s no another option, if I come back a value specifying the transfer is asynchronous, the visitant proceeds to busy-wait for interrupts in a iteration waiting for the transfer to complete (although the busy-wait does inspect for all controllers current on the system).
Later on I added several hacked up collision handler so I could try to debug what was going on alongside MacsBug… it turned out to be an emulation error, no specified collision occured whenever loading MacsBug on genuine hardware. I afterward established out the logic Mac OS 9.2.2 was crashing on footwear was since of Open Transport, removing it from the Extensions directory caused it to footwear absolutely fine. My conjecture is that Open Transport can’t grip no serial or network equipment being current at all.
As I mentioned, my harbor is motionless nowhere near release. Next up volition be bringing up the Wii’s SD controller, afterward the USB controller (I’ve already done several reversing activity connected to that, I cognize there’s a vibecoded EHCI controller now, that’ll likely lone aid in reversing activity really), afterward getting the harbor brought up on the Wii U. At several item I desire to perform about alongside patches for blowing through at smallest several of the RAM barriers too.