Downtown Doug Brown » Customizing the startup chime on a 1999 G3 iMac
In the event you’ve been studying my weblog for some time, you would possibly bear in mind again in 2012 after I changed the startup sound on my Power Mac G3 (Blue and White). That was a enjoyable introduction to the Forth programming language. I needed to reverse-engineer simply sufficient of Apple’s firmware replace script to know what was happening.
Just lately, Aidan Halpin, a reader of this website, requested me if I might do the identical sort of startup sound customization on his iMac. This specific iMac is formally referred to as the “iMac (Slot Loading)” and has a mannequin identifier of PowerMac2,1. As you may guess from the title, it has a slot-loading CD-ROM drive not like the unique iMac that had a laptop-style tray-loading drive. By the best way, Aidan’s iMac is particular as a result of it has a PowerPC G4 processor soldered onto the logic board as an alternative of the unique G3.
He despatched me Apple’s final firmware replace for this mannequin: iMac Firmware Update 4.1.9. I went to work wanting on the replace contents to see if I might determine easy methods to modify the chime the identical method I did with my Energy Mac G3. I assumed it could be enjoyable to take everybody alongside for a trip and present precisely what was concerned in altering the sound. And naturally, this put up wouldn’t be full with out additionally sharing the code for the utility I created to inject the brand new chime into the firmware replace file.
Earlier than we get began, I want to offer some background information from my 2012 work on customizing the startup sound in my Energy Mac G3 as a result of a superb portion of it was related for the iMac too. Once I dumped my G3’s ROM, I imported it into Audacity as a uncooked file. I selected signed 16-bit PCM, big-endian, 44,100 Hz because the format. Then I performed your entire 1 MB ROM as an enormous sound. It principally gave the impression of a scratchy mess, however there have been two areas that each clearly contained the startup sound knowledge. It sounded very staticky and performed method too rapidly, nevertheless it was clearly the startup chime. Headphone/earbud customers beware, it’s fairly loud:
I might get it nearer to the proper size and pitch by decreasing the pattern charge, nevertheless it nonetheless sounded scratchy. See the part depicted beneath from about 2.75 seconds to three.5 seconds or so for an instance of what it appeared like in Audacity.
On the time, I had been doing a variety of digging into sound codecs for other Mac ROM investigations, so a number of the widespread sound compression algorithms have been already contemporary in my head. I rapidly stumbled upon a few IMA ADPCM step tables within the ROM. Frequent sense prompt that Apple most likely would have used their own QuickTime IMA ADPCM format, which teams the info into 34-byte packets. That data allowed me to determine precisely the place the sound knowledge started and ended within the ROM, and I used to be capable of decode it.
So when Aidan requested if I might look into altering the sound on the iMac, I took the compressed sound knowledge I had discovered 10 years earlier in my Energy Mac’s ROM and searched for a similar chunk of knowledge within the iMac’s firmware replace file. Certain sufficient, the sound was the very same compressed sound, proper down to each byte. On this replace file, there is just one copy of the startup sound. It’s positioned right here:
Nevertheless, it wasn’t as simple as simply doing the very same patch to the firmware replace file. Though the iMac’s firmware replace file is a Forth script similar to the Energy Mac G3’s, it’s organized in another way. With that in thoughts, I pulled up some Forth tutorials and faked my method via understanding the code. What follows is my evaluation of the firmware replace file and the method I adopted to change the sound.
The Forth code begins out by defining a bunch of buildings. This one is fascinating as a result of it refers to a boot beep and corresponding measurement:
Extra on that later. Additional down within the code, there are some some section-related buildings. It creates area for the general header and 6 part headers: sbb, srec, sboot, ssys, stst, and snv. Every part has a checksum. This replace script is tremendous helpful! It tells me the that means of a variety of the info within the replace file.
I’m not going to undergo the Forth code that really makes use of these buildings and units all the pieces up — partially as a result of it’s sort of lengthy, and partially as a result of I don’t absolutely perceive all the pieces it does. The essential half is that after the tip of the script (doit adopted by a carriage return), a bunch of uncooked non-ASCII knowledge instantly follows. The primary chunk of this knowledge seems to be the content material of the >h and >s structs described above.
Utilizing the struct definitions, you may decode all the highlighted knowledge above (remember that not less than on Macs, PowerPC is big-endian):
- Header:
- file-size = 0x000E11C0 = 922048 (that is the precise measurement of the firmware file)
- rom-size = 0x0010 = 16
- header-size = 0x00A4 (so the header ends at 0x68DC)
- build-version = 0x000419F1 = 4.1.9f1
- build-date = 0x20010914 = September 14, 2001
- mannequin = 0xFFFF
- fill-byte = 0xFF
- num-sections = 6
- sbb:
- kind = 0x00
- flags = 0x81
- reserved = 0x0000
- place = 0x000068DC (instantly after the header)
- offset = 0x00000000
- measurement = 0x00003F00
- precise = 0x00003A00
- checksum = 0x43B8671B
- srec:
- kind = 0x01
- flags = 0x81
- reserved = 0x0000
- place = 0x0000A2DC (instantly after the sbb knowledge)
- offset = 0x00008000
- measurement = 0x00078000
- precise = 0x00063DA0
- checksum = 0xF05F6B03
- sboot:
- kind = 0x02
- flags = 0x81
- reserved = 0x0000
- place = 0x0006E07C (instantly after the srec knowledge)
- offset = 0x00080000
- measurement = 0x00080000
- precise = 0x00072280
- checksum = 0xD85D3F5A
- I’m going to cease right here, however you may decode the opposite sections by persevering with within the knowledge.
It’s clear to me after staring extra on the content material of those structs that place is the beginning location of the info for that part within the firmware replace file, offset might be the placement the place it’s saved within the flash chip, measurement is the reserved measurement for the part within the flash chip, and precise is the precise quantity of knowledge included for that part within the firmware replace.
Up above, I decided the info for the startup sound was positioned from 0xD1E2C to 0xE02DF within the file. This places it proper inside the sboot part, virtually on the finish. The sboot part runs from 0x6E07C to 0xE02FC within the file.
Trying again on the >dir struct containing the BOOT-BEEP discipline, I guessed that perhaps the sboot part would begin with that construction. It might include 20 32-bit phrases for a complete of 0x50 bytes. The info is highlighted within the image beneath:
Decoding it utilizing the struct definition above, we get:
- inst0 = 0x48000080
- inst1 = 0x000419F1
- filler0 = 0x20010914
- filler1 = 0x00100000
- HWINIT = 0x00000000
- HWINIT-size = 0x00006BF8
- NUB = 0x00000000
- NUB-size = 0x00000000
- OF = 0x00006C01
- OF-size = 0x0005D195
- unused{0,1,2,3} = 0x00000000
- unused{0,1,2,3}-size = 0x00000000 (and unused3-size is lacking the -size within the struct definition, most likely a typo)
- BOOT-BEEP = 0x00063DA0
- BOOT-BEEP-size = 0x0000E4C4
This knowledge appears right! 0xE4C4 is an inexpensive size for the sound. Within the picture above the place I confirmed the sound knowledge, it stated that the size of knowledge I had extracted was 0xE4B4, which is 16 bytes smaller. The offset of 0x63DA0 for the sound can be affordable. Including it to the sboot place of 0x6E07C, we get 0xD1E1C, which is 16 bytes earlier within the file than the sound knowledge I discovered. So it seems that the sound additionally has a 16-byte header:
I’m undecided precisely what all the content material of this header is. The primary 32-bit worth of 0x00000010 would possibly characterize the size of the header. A wild guess is that 0x0000002C = 44 is meant to point that the pattern charge of the ultimate sound is 44.1 kHz. I might undoubtedly be flawed on that one although. I’m extra assured concerning the remaining 32-bit worth although: 0x0001AE80 is the variety of samples. The sound knowledge is 0xE4B4 bytes lengthy, and every block of IMA knowledge is 34 bytes lengthy. Which means there are 0xE4B4 / 34 = 1,722 blocks. Every block represents 64 samples, so there are 64 * 1,722 = 110,208 = 0x1AE80 samples within the sound.
Anyway, this served as affirmation that I undoubtedly found the startup sound’s location in ROM. I believe that this even provides me sufficient info to elongate or shorten the sound, however perhaps another person who’s feeling extra courageous can experiment with that!
So at this level, the method of injecting a brand new chime was wanting like this:
- Begin with a sound that’s precisely the identical size and pattern charge of the unique sound: 110,208 samples at 44.1 kHz = just below 2.5 seconds.
- Compress the sound utilizing Apple’s IMA ADPCM format.
- Exchange the sound knowledge within the firmware replace with the brand new sound knowledge, which ought to come out to precisely the identical compressed size as the unique.
The one factor this checklist is lacking is updating any checksums. Recall that every part header comprises a checksum as the ultimate 32-bit worth. Trying on the Forth code originally of the replace file, it’s fairly clear that it’s an Adler-32 checksum.
It appears to be checking if the part’s flags have the checksum flag set, and in that case, computing the Adler-32 checksum of your entire part measurement minus 4 bytes. This is smart, as a result of the ultimate 4 bytes could be reserved for really storing the checksum within the flash chip.
I performed round a bit with this to attempt to recalculate the unique firmware replace’s current checksum. My first few makes an attempt at simply checksumming the “precise” size of the part failed miserably. I ought to have been wanting nearer on the code, as a result of it made it clear that the “measurement” size was the essential one. The general header struct even comprises a fill-byte member (0xFF) so I knew that I ought to pad the part with that worth. To calculate the proper checksum, I began with the bytes for that part within the file (the “precise” bytes), then appended 0xFF till your entire part was precisely “measurement – 4” in size. This lastly prompted the checksum calculation to completely match the unique.
Whereas I used to be looking via the firmware replace file, I discovered another reference to the “adler32” phrase within the script. The script additionally checks the checksum of itself to ensure your entire firmware replace file is safely intact.
Attempting my greatest to know this Forth code with out spending an excessive amount of time learning the syntax, I can say that it’s calculating the checksum of your entire firmware replace file minus the final 4 bytes (which include the anticipated checksum), and evaluating the calculated checksum to the anticipated checksum.
This try at replicating the present checksum succeeded on the primary strive. Yay! I felt excellent concerning the checksum state of affairs after this little experiment. I knew the proper method to calculate each checksums. The one worry in my thoughts was that there might probably be one other checksum involving the sound someplace, however I thought-about that unlikely as a result of my unique Energy Mac G3 startup chime patch additionally concerned updating two checksums just like those I discovered right here and nothing else.
I bundled this whole process right into a C++ program that ensures the substitute chime knowledge is the proper size, compresses it with IMA ADPCM, sticks it into the firmware replace file within the right location, and recalculates the checksum. It’s only a modified model of the same firmware patching utility I made for altering my G3’s startup sound.
After operating my new utility with Aidan’s desired sound and the unique firmware replace file as inputs, I used to be outfitted with a patched firmware replace file to strive. I carried out this patching course of on Home windows. To be able to get the modified file again onto a Mac with out dropping the resource fork, I went via my Linux server operating netatalk and Samba. I can entry it via Samba on Home windows to change the info fork of a file, and it’ll protect the useful resource fork info when accessed via netatalk on a Mac. The useful resource fork is saved by netatalk in a particular .AppleDouble folder.
There’s most likely a method I might have instructed Aidan to run this patched firmware replace manually by booting into Open Firmware and running a command, however I actually didn’t really feel snug sufficient in OF to try it. So the subsequent order of enterprise was to determine easy methods to modify Apple’s firmware updater program to patch the firmware when it’s already updated.
I had additionally gone via this course of on my G3 in 2012. My G3’s firmware updater had an enable checklist of firmware variations it was capable of replace. This meant it was tremendous simple to patch; I simply modified one of many entries within the enable checklist to match the most recent firmware model.
No such checklist existed within the iMac updater. I appeared all around the knowledge fork and useful resource fork, however there was nothing. This mainly meant one factor: it was time to open up Ghidra!
I can not less than sort of learn Intel and ARM meeting language, however PowerPC has at all times been intimidating to me. Happily, Ghidra features a decompiler so I can have a look at one thing a bit extra acquainted. Right here’s the highest of foremost():
Ghidra was capable of mechanically decide the perform names, which made my job lots simpler. Many of the code displayed above is doing varied sanity checks to ensure the replace is definitely allowed to be put in. For instance, IsBlueBox() is ensuring that the replace isn’t being run inside the Basic setting in Mac OS X. Right here’s what ALRT useful resource 138 seems like in ResEdit:
I made a decision that CompareWithCurrentVersion() could be a superb perform to patch. If it returns 5 it shows ALRT useful resource 131, which matches the message depicted earlier:
Right here’s a more in-depth have a look at CompareWithCurrentVersion(). I did manually give a reputation to the worldwide variable firmwareVersion to make it clearer what’s happening:
This perform compares the parameter handed in (0x419f1 within the earlier decompilation of foremost()) to a worldwide variable and returns one in every of three values: 6 if the firmware model matches, 7 if the present firmware is sufficiently old to be up to date, or 5 if the firmware is simply too new to be up to date. There’s additionally a particular case that I don’t perceive the place it modifies each variations by on the lookout for a “d” within the location the place the “f” is in 0x419f1 and turning it right into a “9” previous to evaluating the variations, however that doesn’t matter.
I initially opted to change the perform to return 7 as an alternative of 5 by turning “39 80 00 05” into “39 80 00 07”, however Aidan reported that it didn’t work. In the event you learn the earlier paragraph carefully, you’re most likely questioning, “WTF is Doug pondering? The firmware model matches.” I spotted my mistake the subsequent day — I ought to have been modifying the case the place it returns 6, not 5. In hindsight, this could have been apparent. My confusion stemmed from the disassembly of foremost(), the place a return worth of 5 prompted ALRT 131 to be displayed. I knew ALRT 131 was being displayed, so I assumed that was the patch wanted. It seems that FirmwareIsUpdated(), which is named when CompareWithCurrentVersion() returns 6, can be able to displaying ALRT 131. I ought to have paid extra consideration to what CompareWithCurrentVersion() was really doing.
Though this doesn’t matter, perhaps you’re inquisitive about what’s happening on this perform. It is aware of whether or not that is the primary boot since a firmware flash try, and in that case, it exhibits ALRT 141 as an alternative, which says that the replace was efficiently put in.
To summarize the patch I settled on for the firmware updater, I merely wanted to vary the “39 80 00 06” in CompareWithCurrentVersion() to “39 80 00 07”. This modifies it to permit the firmware to be up to date even when the present firmware model matches the replace model. This does imply that on the subsequent boot a message will pop up erroneously claiming that the firmware didn’t replace appropriately, however that’s only a minor inconvenience.
In the event you plan on trying this patch your self, beware that there are literally two “39 80 00 06” hex sequences within the firmware updater program. The second is the one which wants patching.
I’m certain a few of you want to expertise the ultimate outcome. I bundled the recordsdata right into a Disk Copy disk picture, encoded it as MacBinary with a purpose to protect the useful resource fork of the disk picture file, and despatched it off to Aidan. Right here is his video exhibiting the firmware replace set up and the following boot.
For a enjoyable little trivia truth, that is the short-lived Energy Mac 6100/7100/8100 12-string guitar startup sound created by Stanley Jordan [source].
I used to be a bit of nervous about compatibility with the customized G4 soldered onto this specific iMac’s logic board, nevertheless it labored simply effective. Aidan identified that it was already operating the inventory firmware, so in hindsight I had nothing to fret about. I simply knew that the G3 Blue and White firmware, for instance, had been intentionally patched by Apple to remove compatibility with the G4, and required a customized firmware patch to re-enable G4 assist. To be protected, I requested him if he might check on a distinct iMac first, as a result of I didn’t wish to smash his uncommon G4 iMac in case I made a mistake throughout the patching course of. That check was additionally profitable.
I’ve uploaded the patcher to GitHub in case anybody is enthusiastic about trying this. I additionally included the patcher I created for the Energy Mac G3 (Blue and White). In the event you do that, be sure to protect the useful resource fork of each the replace file and the updater utility once you patch them. Like I stated earlier, a method of carrying out that is to make use of netatalk on a Linux or NetBSD server to behave because the middleman between a Mac and your growth laptop.
So there you’ve gotten it! That’s the method I adopted to patch the startup sound on an iMac (Slot Loading). Once I look again on it, it wasn’t actually that troublesome. The Forth code was basically serving me the answer on a silver platter. There’s sufficient spare room within the sboot part that theoretically you possibly can most likely insert a chime that’s about 4.5 seconds lengthy or so, however it could get extra difficult to patch the firmware replace file since you’d must shift the positions within the part buildings after sboot. I’ll go away it as much as another person to try.