• 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.

Mac ROM Checksum Computation?

Dennis Nedry

Well-known member
I have a legal collection Mac ROM images (i.e., I own each respective Mac.). Amongst this collection are some copies. As many of us know, each ROM starts with a 32-bit checksum. I have one specific pair of ROMs from the same model of Mac, that start with the same checksum, but they are not identical. In fact they are VERY different.

So I played with "sum" and "cksum" on my OS X machine, experimenting with things like zeroing the stored checksum, etc. But I was not able to get any ROM to sum out to the checksum. Does anybody have any idea of exactly how this checksum is computed? Even a general description if anybody knows how this works would do because I could write a quick dirty app to check it.

 

Dennis Nedry

Well-known member
I believe I found the checksum subroutine in the Mac SE ROM, with the help of FDisasm.

Code:
                      P_ChecksumRom:
26C8   7000                      MoveQ.L   $0, D0
26CA   7200                      MoveQ.L   $0, D1
26CC   41F9 0040 0000            Lea.L     ($400000), A0
26D2   2818                      Move.L    (A0)+, D4
26D4   263C 0001 FFFE            Move.L    $1FFFE, D3
26DA   3018           L335:      Move      (A0)+, D0
26DC   D280                      Add.L     D0, D1
26DE   5383                      SubQ.L    $1, D3
26E0   66F8                      BNE       L335
26E2   4E71                      Nop
26E4   4E71                      Nop
26E6   B981                      Eor.L     D4, D1
26E8   6704                      BEQ       L336
26EA   3C3C FFFF                 Move      $-1, D6
26EE   4ED6           L336:      Jmp       (A6)
Is anyone fluent enough in 68000 ASM to describe what's going on here? I have some knowledge of ASM but it pertains mostly to much more simple processors/microcontrollers.

 

Dennis Nedry

Well-known member
Here's my take:

Code:
                     P_ChecksumRom:
26C8   7000                      MoveQ.L   $0, D0         ; D0 = 0.
26CA   7200                      MoveQ.L   $0, D1         ; D1 = 0.
26CC   41F9 0040 0000            Lea.L     ($400000), A0  ; Load A0 with the address $40 0000. (middle of ROM)
26D2   2818                      Move.L    (A0)+, D4      ; Load D4 with memory contents at A0 (location $40 0000).
                                                         ; D4 = $6656 2270.  This is the actual checksum?
                                                         ; A0 = A0 + 4 = $40 0004.
26D4   263C 0001 FFFE            Move.L    $1FFFE, D3     ; D3 = $1 FFFE.

                                                         ; LOOP
                                                         ; D0 fetches each 32-bit word.
                                                         ; D1 keeps track of the 32-bit sum.
                                                         ; D3 is a decrementing counter for the loop, which tells it when to exit.
                                                         ; When the loop exits, D1 is holding the sum.

26DA   3018           L335:      Move      (A0)+, D0      ; D0 = memory contents at A0. (location $40 0004 = $8161 0002).  A0=A0+4=$40 0008.
26DC   D280                      Add.L     D0, D1         ; D1 = D1 + D0 = 0 + 8161 0002.
26DE   5383                      SubQ.L    $1, D3         ; D3 = D3 - 1 = $1 FFFD
26E0   66F8                      BNE       L335           ; If (D3 != 0), goto LOOP.  (values above are for first time through loop only.)

26E2   4E71                      Nop                      ; Wait 1 cycle.
26E4   4E71                      Nop                      ; Wait 1 cycle.
26E6   B981                      Eor.L     D4, D1         ; D1 = D1 XOR D4. (i.e. verify checksum)
26E8   6704                      BEQ       L336           ; If (D1 = 0), goto L336 (i.e. checksum PASSED.)
26EA   3C3C FFFF                 Move      $-1, D6        ; Else set flag for checksum FAIL
26EE   4ED6           L336:      Jmp       (A6)           ; Exit Subroutine.
It would appear that the checksum is actually stored at $40 0000, not the very beginning (location $0000 0000)? Maybe I'm seeing it wrong; is there some sort of paging I'm not knowledgeable about? It also appears that it loops around the ROM, starting somewhere in the middle, getting to the end, jumping to the beginning, then back to the middle. What facilitates this looping? This doesn't really appear to be a simple overflow-type loop.

 

Dennis Nedry

Well-known member
I guess it makes sense to me that the beginning of ROM is not mapped to address $0000 0000. These very low addresses are often reserved for internal registers and ports of the processor. Maybe we can assume that the ROM starts at address location $40 0000. That seems logical.

It looks like it skips the first 32-bit word by incrementing A0 before the loop. (i.e. skips the recorded checksum at the beginning) Then it adds all following words, except it also skips the last word as well by setting D3 8 bytes less than the length of ROM. (4 bytes go to what was skipped at the beginning, the remaining 4 skip the last word in ROM.)

I'm going to see if I can whip something together that can verify the checksum successfully. Maybe I'll make it pretty and share it so people can verify their Mac ROM dumps!

 

Dog Cow

Well-known member
Sorry to butt in to your one man conversation, ( :lol: ) but I'm wondering how you ended up with two different ROM files from the same model of Mac. Do you have two of this particular model, or was one downloaded from elsewhere?

 

Dennis Nedry

Well-known member
I can't quite get the checksum to work. Either I have a bad ROM dump or I'm doing something wrong. Any ideas? This is a IIfx ROM that I dumped myself just weeks ago.

 

Dennis Nedry

Well-known member
I'm wondering how you ended up with two different ROM files from the same model of Mac. Do you have two of this particular model, or was one downloaded from elsewhere?
These were downloaded, however I do own the respective Mac somewhere where I can't readily get to it.

 

Dennis Nedry

Well-known member
I think part of the problem here is the ROMs I'm using. The disassembled checksum routine that I commented up is from the 256kiB Mac SE ROM (68000) and I'm trying to do the actual checksum on the "known good" 512kiB IIfx ROM (68030), which adds extra variables to this experiment. Both size and architecture are different...

What I find interesting is that D3 (the loop counter) is 1/4 the size of the IIfx ROM (~128k), as one might expect, but it's 1/2 the size of the SE ROM, which suggests that it's taking 2 bytes at a time, i.e. 16-bit, which puzzles me. Modifying my dirty checksum program to accommodate this, I do not come up with the correct checksum. The sum is not ever big enough even to loop all 32-bits and it's quite less than the recorded checksum. So that does not look good.

If anyone could take a fresh pair of eyes to this and try to find any discrepancies to my commented code, that would be appreciated.

 

Dennis Nedry

Well-known member
OMG I got it to work... There was a small problem with my program. I think pretending to have a conversation with people helped! :lol: It is indeed retrieving 16 bits at a time for the checksum.

I didn't give anyone a chance, it all happened so fast.

I'm going to try to generalize this program so it can be used to verify any ROM against the recorded checksum. The source code is being written in C++.

 

Trash80toHP_Mini

NIGHT STALKER
OMG I got it to work... There was a small problem with my program. I think pretending to have a conversation with people helped! :lol: It is indeed retrieving 16 bits at a time for the checksum.
I didn't give anyone a chance, it all happened so fast.
Way to go! Keep it up, comrade! [;)] ]'> [:D] ]'>

I only do the ROM thing from a Hardware Hacker/PCB Adapter & Emulation Standpoint, it's great so see somebody else is busy bangin' on the bits!

. . . and I seem to find myself, talking to myself here alla' time! :eek:)

p.s. If there's any info you need again from GttMFH: Second Edition again, just ask! }:)

 

Dennis Nedry

Well-known member
p.s. If there's any info you need again from GttMFH: Second Edition again, just ask! }:)
Will do! ;)

I have some alpha quality code going here. This command line app takes one argument, the path to the ROM file. So drag the app onto a terminal window, then drag a ROM onto the same window, then hit return.

It displays this stuff right now:

-Usage (if arguments were invalid)

-ROM length in kiB

-Warning if this is a strange length, i.e. length % 64kiB != 0

-Statement whether checksum passed or failed

-Tells what Mac the ROM came from, based on: http://minivmac.sourceforge.net/mac68k.html

-Still needs:

-- check if file exists

-- add more models to the checksum/name of Mac table.

-- a couple known-good PPC 4MiB ROMs fail. Need to figure out why.

Otherwise it worked for all of my ROMs. 6100, 6400 failed, 6500 ok, etc. And I identified which of my two different Quadra 650 ROMs was bad.

Here's what I have so far. You can paste it over the main.cpp file if you create a new C++ Tool project in Xcode. Then build. The binary will be in /Build/Debug of your project's folder by default.

Code:
#include 
#include 
using namespace std;

int main (int argc, char * const argv[]) {
if (argc != 2) {
	cout << "\nUsage:  MacROMVerify [path to ROM file]\n";
	return(0);
}	
ifstream inFile;
inFile.open(argv[1]);

unsigned int sum = 0;
long int length = -1;	// -1 is used so that the EOF character is not counted.
while (!inFile.eof()) {
	inFile.get();
	length++;
}

cout << "\nROM Length: " << length/1024.0 << " kiB\n";
if (length % 65536 != 0) cout << "The ROM appears to be the wrong length.\n";  // Checks if the file size is divisible by 64kiB

inFile.close();
inFile.open(argv[1]);

unsigned int recorded = inFile.get()*16777216+inFile.get()*65536+inFile.get()*256+inFile.get();

//cout << hex << "\nRecorded Checksum: 0x" << recorded;

for (int i=4; i		sum += inFile.get() * 256;
	sum += inFile.get();
}

//cout << hex << "\nComputed Checksum: 0x" << sum << endl;

if (recorded == sum) cout << "\nROM Checksum PASSED.\n";
else cout << "\nROM Checksum FAILED.\n";

cout << "\nThis is a";
switch (recorded) {
	case 0x28BA61CE: cout << " Macintosh 128k or 512k"; break;
	case 0x28BA4E50: cout << " Macintosh 128k or 512k"; break;
	case 0x4D1EEEE1: cout << " Macintosh 512ke or Plus"; break;
	case 0x4D1EEAE1: cout << " Macintosh 512ke or Plus"; break;
	case 0x4D1F8172: cout << " Macintosh 512ke or Plus"; break;
	case 0xB2E362A8: cout << " Macintosh SE"; break;
	case 0x9779D2C4: cout << " Macintosh II"; break;
	case 0x97851DB6: cout << " Macintosh II"; break;
	case 0x97221136: cout << " Macintosh II FDHD, IIx, IIcx, or SE/30"; break;
	case 0xB306E171: cout << " Macintosh SE FDHD"; break;
	case 0x96645F9C: cout << " Macintosh Portable, Backlit Portable, or PowerBook 100"; break;
	case 0x368CADFE: cout << " Macintosh IIci"; break;
	case 0x4147DD77: cout << " Macintosh IIfx"; break;
	case 0x35C28C8F: cout << " Macintosh IIfx"; break;
	case 0x36B7FB6C: cout << " Macintosh IIsi"; break;
	case 0xA49F9914: cout << " Macintosh Classic"; break;
	case 0x350EACF0: cout << " Macintosh LC"; break;
	case 0x3193670E: cout << " Macintosh Classic II or Performa 200"; break;
	case 0x420DBFF3: cout << " Macintosh Quadra 700 or 900"; break;
	case 0x35C28F5F: cout << " Macintosh LC III, Performa 400, 405, 410 or 430"; break;
	case 0x3DC27823: cout << " Macintosh Quadra 950"; break;
	case 0x49579803: cout << " Macintosh IIvx or Performa 600"; break;
	case 0xE33B2724: cout << " Macintosh PowerBook 160, 165, 165c, 180 or 180c"; break;
	case 0xECFA989B: cout << " Macintosh PowerBook Duo 230 or 250"; break;
	case 0xECFA89A8: cout << " Macintosh PowerBook Duo 230 or 250"; break;
	case 0xECD99DC0: cout << " Macintosh Color Classic or Performa 250"; break;
	case 0xECBBC41C: cout << " Macintosh LC III, III+, 520, Performa 460 or 520"; break;
	case 0xF1A6F343: cout << " Macintosh Centris 610 or Quadra 610"; break;
	case 0xF1ACAD13: cout << " Macintosh Centris 650, Quadra 650 or 800"; break;
	case 0x5BF10FD1: cout << " Macintosh Centris 660AV, Quadra 660AV, 840AV"; break;
	case 0xFF7439EE: cout << " Macintosh LC 475, 575, Performa 475, 476, 575, 577, 578 or Quadra 605"; break;
	case 0x0024D346: cout << " Macintosh PowerBook Duo 270c"; break;
	case 0xEDE66CBD: cout << " Macintosh Colour Classic II, LC 550, Performa 275, 550, 560 or TV"; break;
	case 0xEAF1678D: cout << " Macintosh Colour Classic II, LC 550, Performa 275, 550, 560 or TV"; break;
	case 0x015621D7: cout << " Macintosh PowerBook Duo 280 or 280c"; break;
	case 0xB6909089: cout << " Macintosh PoweBook 520, 520c, 540 or 540c"; break;
	case 0x06684214: cout << " Macintosh LC 630, Performa 630 or Quadra 630"; break;
	case 0xFDA22562: cout << " Macintosh PowerBook 150"; break;
	case 0x064DC91D: cout << " Macintosh LC 580, Performa 580 or 588"; break;
	case 0x4D27039C: cout << " Macintosh PowerBook 190 or 190cs"; break;
	default: cout << "n unknown";
}
cout << " ROM.\n";

return 0;
}
Please feel free to suggest changes and/or optimizations. Maybe I'll learn something!

 

techfury90

Well-known member
I guess it makes sense to me that the beginning of ROM is not mapped to address $0000 0000. These very low addresses are often reserved for internal registers and ports of the processor. Maybe we can assume that the ROM starts at address location $40 0000. That seems logical.
It looks like it skips the first 32-bit word by incrementing A0 before the loop. (i.e. skips the recorded checksum at the beginning) Then it adds all following words, except it also skips the last word as well by setting D3 8 bytes less than the length of ROM. (4 bytes go to what was skipped at the beginning, the remaining 4 skip the last word in ROM.)

I'm going to see if I can whip something together that can verify the checksum successfully. Maybe I'll make it pretty and share it so people can verify their Mac ROM dumps!
Just a heads up, the beginning of address space in 68ks is used for vector addresses. In particular, the first 32 bits are used to hold the initial value of the A7 register, then the next 32 (addresses 4- 8) are used to hold the initial value of the program counter. For this reason, many 68k systems have their ROM at 0x0. (At least Apollo workstations do)

Disclaimer: I only know this from writing an Apollo DN3500 emulator.

 

sentient06

Member
Sorry to ressurect such an old topic, but while researching for an application I came to this code and I am using this logic. Since the link seems to be long gone I decided to compile a C version of this code, maybe it interests some of you.

Also, I increased the number of ROM checksums to get also some 2MB and 4MB images.

https://gist.github.com/sentient06/7303670

Cheers!

 

onlyonemac

Well-known member
Ask dougg3. If anyone knows, it's him. And the reason I say this is because he had to recalculate the checksum of his ROMs every time he changed them.

 
Top