• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

G3 Blue and White custom startup sound ROM hack -success!!

dougg3

Well-known member
OK, so now I'm thinking of my next big startup sound hack and I want it to be a New World Mac. I have several New World G3/G4 Macs, but the Yosemite G3 seems like a nice candidate due to its logic board being so easy to access. It's the first brand-new Mac I ever bought. I still have the original box, packing materials, restore disk, manuals, keyboard, awful hockey puck mouse, power cord, internal 56k modem and Zip drive install kit boxes, etc. (yeah, I'm a pack rat) so I want to preserve it and don't exactly want to modify the stock hardware at all. With that said, I'm definitely up for grabbing a logic board on eBay to hack.

My hardware hacking capabilities start to diminish as we move toward miniature surface mount parts and BGA packages, but this one still seems pretty doable (if any hardware hacking is even necessary -- read on). The Blue and White G3 developer note points out that there is 1 MB of ROM on the logic board containing POST software, Open Firmware, and hardware specific drivers. In particular, the POST software contains a "boot beep" -- the startup chime.

I've found the chip on the logic board. It's a MIcron MT28F008B3 8 megabit (1 megabyte) flash chip in a Type I TSOP-40 package. I'm hoping I don't have to find any of these chips -- Digi-Key doesn't stock them anymore. The good news is that with the right adapter, my Willem programmer seems to support it.

ROMChip.JPG

Looks like there might be pads for a different type of flash chip too...

I also got sneaky and downloaded the Power Macintosh G3 (Blue and White) Firmware Update 1.1. I have no idea if I actually ever installed this update on my G3, but its contents are interesting. After you mount the SMI file, you will find an installer and a file called G3 Firmware. The G3 Firmware file's data fork is plain text with Mac line endings. It appears to be a Forth script, which is probably run by Open Firmware. Unfortunately, I know nothing about Forth except that it uses reverse polish notation. Still, I can find some useful info in the file:

Lines 102 and 103:

Code:
 /L field >dir.BOOT-BEEP
/L field >dir.BOOT-BEEP-size
Code:
cr ." loading ROM-IMAGE, offset=00080000, size=00080000"
(followed by a ton of data that appears to be encoded in some kind of ASCII format (maybe Ascii85?) -- it looks like it's calling a decode function called dc85 which is defined earlier in the file)

It also includes code for controlling AMD, Intel, Micron, Atmel, and Sharp flash chips (I think!).

So if I go through with this, I'll probably need to learn Forth (it's way different from anything I'm familiar with, but I'm still very curious to learn how it works--can't be too hard to learn it!) in order to figure out how to decode what appears to be the new ROM contents. If I can do this and modify the script, it might be possible to flash a custom startup chime without doing anything to the hardware...of course, if I fail, I'll have to manually desolder and reflash the chip with my programmer (no big deal there -- Chip Quik should work). I suppose I could find a TSOP ZIF socket to put on the logic board...I'm not sure if there's room for it because of how close some of the other components are, though.

Anyway, that's my plan right now! I don't know if/when I'll attack it, but I really wanted to get this all written down so I don't forget it and as info for anybody else who's been considering a similar feat.

 

olePigeon

Well-known member
Awesome! Please keep us posted. Could this potentially open up the possibility of enabling native OS 9 booting on newer machines? Just curious.

 

dougg3

Well-known member
Could this potentially open up the possibility of enabling native OS 9 booting on newer machines? Just curious.
Sadly, I doubt it -- the newer machines would need new hardware drivers to work in OS 9 (I think). I'm trying to avoid playing with code at all. It's likely beyond my capabilities...there's always a chance that the work could help someone with more intimate knowledge get closer to that goal, though. I dunno.

I did play around a little bit more with the ROM today. I was able to decode the data I mentioned earlier using this program. It's definitely Ascii85 encoding.

Code:
./ascii85 -d -n -y input.txt > output.bin
I don't know if it's perfect decoding or if there's something I'm doing wrong. Near the beginning of the decoded binary file I'm definitely finding some complete ASCII strings. Later on, I'm also finding some other ASCII strings that have characters like 0xFF in strange unexpected places in the middle of them. I can't figure out if this is a decoding error or intentional. The decoded length doesn't seem to match the length that the script specifies, so it's possible I'm missing something. There's also an Adler-32 checksum involved, so I should be able to make sure I have the right data by calculating the checksum. I haven't played extensively with this yet.

The startup chime is at the end of the decoded binary file. I can tell it's sound data because it sounds like the startup chime, but I can't get the pitch to match up and it's pretty scratchy. I really hope it's not compressed...

Edit: Got the decoded binary length to match the length listed in the G3 Firmware file after running the decoder on Linux instead of Windows. I'm thinking redirecting stdout to a file in Windows does LF to CRLF conversions, so that probably broke it. Even with the match, there are still weird characters and the sound data is still strange. Next step is to get the Adler checksum to match...

 

dougg3

Well-known member
Got the Adler checksum to match. it needed a small change to the Ascii85 algorithm. Ordinarily in Ascii85 encoding, a 'z' character means four 0s (NULL bytes) and a 'y' character means four spaces. Well, in Apple's implementation of Ascii85, a 'y' character means four 0xFFs. Probably makes sense considering unprogrammed flash is 0xFF. I was barely able to comprehend that from the Forth code. It really is a foreign syntax to me!

Plus, the checksum algorithm runs past the end of the decoded data. The decoded ROM image is 264,868 bytes, but the checksum is run for 0x7FFFC (524,284) bytes. Once I padded the rest of the decoded data with zerosn so the total length is 524,284, the checksum came out perfectly.

The sound data at the end is still not sounding correct, so it must be compressed or encoded in some way...

 

dougg3

Well-known member
I was able to dump my G3's ROM and my G4 Mac mini's ROM, and I've confirmed that my decode of the G3 firmware update worked correctly.

I found two instances of an IMA ADPCM step table in the Blue and White G3's ROM. (It's also in the Mac mini's ROM twice in similar locations...) I'm now pretty sure the startup chime is IMA compressed, and the fact that the table is in ROM suggests to me that the decompression is most likely done in software.

I'm not sure why there are *two* step tables. One of them seems to be in a recovery section of the ROM, so it might just be different code for playing a startup sound in recovery mode.

The step table is probably a useful clue for where to look in ROM for the decoding algorithm. Chances are the code is nearby, or I can at least use the step table's known location to find code that references it. There are many implementations of IMA compression, but they all use the same basic algorithm -- it's the little details of the implementation like packet size and the format of the first few bytes per packet that I have to iron out. Hopefully some PowerPC disassembly will help with that. }:)

 

Dennis Nedry

Well-known member
dougg3, you amaze me - very cool project. That would be AWESOME to change the startup sound via firmware update. Maybe the next step after this is current Macs????? I smell bold disclaimers and shareware funds close by.

 

mcdermd

Well-known member
Doug - I'm down in Albany and I have a couple of B&W G3 parts machines. About the only good thing left on them are the logic boards and processors. If you want them, I'd be more than happy to donate them. PM me if you're interested.

 

dougg3

Well-known member
Thanks Dennis Nedry! I think it would be AWESOME to hack the startup chime of current Macs. For me it becomes a monetary thing...once the technology gets cheap enough that it's easy to get a new motherboard when something goes wrong, then I'm comfortable hacking it :)

I have decoded the sound.

I started messing with disassembling, but I quickly realized that PowerPC assembly is a complete mess to try to read. There are so many freakin' instructions that do really crazy things! I started seeing instructions like rlwinm (Rotate Left Word Immediate then AND with Mask) and think "WTF?" Maybe it's just because I'm not familiar with the syntax like I am with Motorola and Intel code, but it turned me off pretty quickly. I found a place where it's writing to the sound chip but lost my patience and decided to try some other methods.

I figured Apple would probably use their own IMA 4:1 encoding standard (34-byte chunks -- 2 bytes of starting info followed by 64 nibbles of data), and it turns out that's exactly what they did. See this page for the header info, and this page for generic IMA decoding info. I took the code I had already written from experimenting with your Quadra 700 project, and made it work with Apple's IMA 4:1.

I didn't know exactly where the sound started, so I played with various starting points until I found one that didn't show any weird "index out of bounds" errors during decoding. Then I played back the resulting sound and BINGO. It's a little scratchy for my tastes, but I think it's correct. I'll post a recording and code later...

mcdermd -- WOW, thank you! I may indeed take you up on that offer once I'm comfortable with trying to inject my own sound :)

Edit: Here's the extracted chime. There may be some improvements needed to the decode algorithm because I don't think it's supposed to sound that scratchy. But it's pretty much correct.

 

dougg3

Well-known member
I discovered the reason for the scratchiness -- I had a typo in my code that decodes the nibbles. I think I screwed it up when I was copying the code over from the original IMA documentation. Here's the original code:

Code:
    int diff = 0;
   if (s & 4)
       diff += step;
   if (s & 2)
       diff += step >> 1;
   if (s & 4)
       diff += step >> 2;
   diff += step >> 3;
   if (s & 
       diff = -diff;
Here's the fixed code:

Code:
    int diff = 0;
   if (s & 4)
       diff += step;
   if (s & 2)
       diff += step >> 1;
   if (s & 1)
       diff += step >> 2;
   diff += step >> 3;
   if (s & 
       diff = -diff;
Stupid typos!

Here's the fixed chime, sounding MUCH better and probably perfect. Below is the complete working code:

Code:
#include 
#include 

static const int ima_index_table[] = {
 -1, -1, -1, -1, 2, 4, 6, 8,
 -1, -1, -1, -1, 2, 4, 6, 8
}; 

static const int ima_step_table[] = { 
 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 
 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 
 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 
 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 
 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 
 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 
};
#define NUM_STEP_TABLE_ENTRIES (sizeof(ima_step_table)/sizeof(ima_step_table[0]))

static int predictor = 0;
static int step_index = 0;
static int step = 7;

void do_header(uint8_t h)
{
   predictor = (h << ;
   if (predictor & 0x8000)
   {
       predictor |= 0xFFFF0000;
   }
}

void do_header2(uint8_t h)
{   
   predictor |= (h & 0x80);
   step_index = h & 0x7F;

   if (step_index >= NUM_STEP_TABLE_ENTRIES)
   {
       printf("Header out of bounds\n");
       step_index = NUM_STEP_TABLE_ENTRIES - 1;
   }

   step = ima_step_table[step_index];
}

void do_sample(uint8_t s, FILE *w)
{
   step_index += ima_index_table[(uint8_t)s];
   if (step_index < 0)
   {
       printf("Sample out of bounds < 0\n");
       step_index = 0;
   }
   if (step_index >= NUM_STEP_TABLE_ENTRIES)
   {
       printf("Sample out of bounds >= %d\n", NUM_STEP_TABLE_ENTRIES);
       step_index = NUM_STEP_TABLE_ENTRIES - 1;
   }

   // Sign-extend the nibble
   if (s & 0x08){ s |= 0xF0; }

   //int diff = (s + 0.5) * step / 4;
   int diff = 0;
   if (s & 4)
       diff += step;
   if (s & 2)
       diff += step >> 1;
   if (s & 1)
       diff += step >> 2;
   diff += step >> 3;
   if (s & 
       diff = -diff;

   predictor = predictor + diff;
   if (predictor < -32768) predictor = -32768;
   if (predictor > 32767) predictor = 32767;
   step = ima_step_table[step_index];

   int16_t value = predictor;

   // This is me cheating to convert it back to big-endian for AIFF.
   // Probably don't need to do it the way I did it, but whatever...
   uint16_t tmpVal = (uint16_t)value;
   uint16_t tmpVal2 = ((tmpVal & 0xFF) <<  | (tmpVal >> ;

   fwrite(&tmpVal2, 2, 1, w);
}

int main(int argc, char *argv[])
{
  uint32_t headersFound = 0;
  uint32_t nibblesFound = 0;
  uint32_t upperHeadersFound = 0;

  FILE *f = fopen("SoundFile2.bin", "rb");
  FILE *w = fopen("SoundFile.out", "wb");
  int counter = 0;

  uint8_t s;

   while (fread(&s, 1, 1, f) > 0)
   {     
       if ((counter % 34) == 0)
       {
           do_header(s);
       }
       else if ((counter % 34) == 1)
       {
           do_header2(s);
       }
       else
       {
           uint8_t s1 = s >> 4;  // Most significant nibble
           uint8_t s2 = s & 0x0F; // Least significant nibble

           do_sample(s2, w);
           do_sample(s1, w);
       }

       counter++;
   }

   fclose(f);
   fclose(w);
}
I still get some warning printouts telling me the step index is less than 0 (which forces me to clamp it up to 0), but I think that's OK -- it's probably normal and just means the encoding wanted to keep it at 0. Anyway, great success on a Tuesday morning!

 

dougg3

Well-known member
Ha, yeah! There are so many different variants of New World Macs. I hope that getting the G3 to play a custom chime will help open up the door for people to figure out the rest of the New World Macs.

Some of them have firmware updates that Apple released. I'm assuming those will be the ones with the best chance of getting a custom chime. The ones that have never had a firmware update are probably capable of accepting a firmware update, but the jury is still out on whether it will actually be possible to flash them without sample Forth code showing how to update their firmware.

I'm in the middle of learning Forth. It's not half bad of a language! I can see why it's used -- it's pretty flexible and easy to parse. Once I'm done with learning Forth and completely figuring out what every line of the firmware update script does, I'll try to flash a custom chime to a G3 B&W. I already know exactly what I want to try, but I want to make sure there isn't a checksum somewhere that would ruin everything.

I've dumped the 1 MB boot ROM on all of my New World Macs (G3 Blue and White, Lombard PB G3, iMac G4, Mac mini G4) and they all store the startup chime in the same format--in fact, the bytes are exactly the same on all of them. That's a good sign that this should be possible on other Macs! I think for starters it will probably be best to stick with replacing the old chime with a chime that's exactly the same length. (We can pad a shorter sound with silence to make it the same length)

 

olePigeon

Well-known member
I'm sure you're aware that Sun developed Open Firmware (they call it OpenBoot) and licensed it to Apple and IBM. So after you learn some Forth, you can also apply your knowledge to non-Intel based Sun and IBM boxen. :)

 

olePigeon

Well-known member
Going off on a tangent here, but Open Firmware is a million times better than BIOS. After you compile Forth, FCode is platform independent. It doesn't care what architecture you're running, what instruction set you're using, or what memory hierarchy you're implementing. It just works. After reading Wikipedia, it's too bad Apple couldn't just continue using Open Firmware and leave Intel to their own idiocy.

 

dougg3

Well-known member
I knew it was used by other stuff too...I doubt I'll ever need to know it for anything else though! :) It looks like the One Laptop Per Child project uses it also...

Open Firmware does seem pretty nice! You're right about the platform independence. Well, I have found a couple of places in the firmware updater where it reads a PowerPC-specific register, but that's kind of an edge case. There's no way I'll become a Forth expert during this whole project, but I'd at least like to have a decent idea about what the firmware updater is doing. Some of the syntax is challenging, but I'm slowly picking it up.

The basic structure of the update procedure seems to be:

  • Some basic setup
  • Verify Adler32 checksum of entire firmware updater file
  • Decode and load boot block, recovery, and ROM images into RAM, verifying individual adler32 checksum of each
  • Some more setup
  • Determine manufacturer of flash chip on logic board
  • Do actual flashing of chips, drawing progress bar stuff while doing it


I found an interesting error message in the update file that it prints if it can't find a flash chip it knows how to control...

I'm sorry, Dave, but I don't grok this part!!
:)

 

kite210

Well-known member
This is absolutely amazing, I might have to pull my B&W out of the basement if this becomes a sure thing. Keep up the great work doug. :)

Also, that is a very interesting reference to 2001: A Space Odyssey.

 

dougg3

Well-known member
Thank you! I'd definitely like to make it so people can choose their own chime, patch the firmware update, run it, and have a new chime :)

Thanks to mcdermd, I now have two G3 logic boards to try to patch with a custom chime! I'm thinking that I should be able to test this out fairly soon...thank you so much, mcdermd!

The two logic boards have Sharp flash chips as opposed to mine, which has a Micron flash chip. So Apple definitely used different brands throughout the production. That explains without a doubt why the firmware update code has so many cases for different chips. Anyway, just another little tidbit as I figure this stuff out!

 

CC_333

Well-known member
Hi,

As you become more experienced, perhaps maybe you can add patches which could restore OS 9 boot-ability on newer Macs (such as the FW800 MDD G4).

Keep up the good work!

c

 
Top