SNES ROM Checksums.

JadussD

New member
I was reading the http://www.emulatronia.com/doctec/consolas/snes/sneskart.html#embededcartridgeSNESKart</A> document, which is excellent, except for some ambiguity on the subject of the checksum. It says that SNES ROM checksums are 16-bit, but they are obtained by adding together all the bytes in 4Mbit chunks, then adding these chunks together and taking the lower 32-bits. Obviously, the checksum cannot be 32-bits, if there is only 16-bits to store it in. What gives?

It also says some confusing things about what to do whenever the ROM size is not evenly divided by 4Mbit. So, if I have a 10Mbit ROM (Were any commercial games actually of this size or is worrying about this a waste of time?), would I have to multiply the result of the last 2Mbit by itself, or simply add 0s where the non-existant space is? The document is ambiguous on the matter. Also, could anyone tell me any games that were of a size indivisible by 4Mbit (524,288 bytes) if any?
 
> I was reading the SNESKart document,

One more thing. Since the checksum is contained within the ROM, how do you factor that in? Do you just skip the checksum bytes, or do you take them into the checksum as well?
 
> One more thing. Since the checksum is contained within the
> ROM, how do you factor that in? Do you just skip the
> checksum bytes, or do you take them into the checksum as
> well?

There's an offset byte (or is it bytes?) that make sure it adds up properly. Like, if your checksum is 0xFFF1, the offset would be 0x000F. That way they would add up to 0x0000 and not affect the checksum.
 
> There's an offset byte (or is it bytes?) that make sure it
> adds up properly. Like, if your checksum is 0xFFF1, the
> offset would be 0x000F. That way they would add up to
> 0x0000 and not affect the checksum.
>

Ohh, the inverse checksum byte, that's what that's for. I was using that to detect if the game was HiROM or LoROM and if the ROM had a header by ORing the 4 places it could be...should have thought of that. Thanks!
 
> Ohh, the inverse checksum byte, that's what that's for. I
> was using that to detect if the game was HiROM or LoROM and
> if the ROM had a header by ORing the 4 places it could
> be...should have thought of that. Thanks!

Watch out for the beta carts. Their checksums are utterly fucked.
 
here's how you do it

it's as simple as this. take a rom, remove any header if present (the first 512 bytes, they aren't the original rom, it's added information copiers put there to store extra data about the rom.) if it's 8, 16, or 32mbit, just add all the bytes together, then AND it with 0xFFFF (to keep it 16 bits.) that is the checksum, which is the second part in the rom's actual header. then inverse the checksum and that's the compliment. so if the checksum is 0x1234 then XOR it with 0xFFFF (0x1234 ^= 0xFFFF) and your compliment is 0xEDCB.

when a raw rom is compiled before they insert the checksum, the checksum and compliment are 0x0000 and 0xFFFF so when they do calculate the checksum the resulting checksum and compliment will be the same. 0x0000 + 0xFFFF is the same as 0x1234 + 0xEDCB.

now if you have a rom not 8, 16, or 32mbits, you add up 8mbit sections, and the last section that's less than 8mbit you simply double until it is 8mbit. so a 12mbit rom, you add the first 8mbits byte by byte together. then add the next 4mbit byte by byte together, then double the number for the last part. or if you wish duplicate the last 4mbits onto itself, then add it all together byte by byte. so 24mbits = 8mbit + 8mbit + (8mbit x 2.) since there is no 4th section of 8mbits, you fake it by taking the 3rd section of 8mbits and duplicating it.

4mbit rom = checksum of 4mbits * 2
8mbit rom = checksum
12mbit rom = checksum of first 8mbits + checksum of remaining 4mbits * 2
16mbit rom = checksum
20mbit rom = checksum of first 16mbits + checksum of remaining 4mbits * 2
24mbit rom = checksum of first 16mbits + checksum of remaining 8mbits * 2
28 mbit rom = checksum of first 16mbits + checksum of remaining 8mbits * 2 (yeah it doesn't cover the very last 4mbits as far as I know.)
32mbit rom = checksum

this is as much as I know. since there is no commercial 28mbit rom (they mine as well pay for 32mbit mask rom) then I don't know if it's done this way. try it out, it'd be a simple c program to make, and compare how different rom tools and emulators calculate such things. also, ToP is 48mbit, which is actually a 32mbit and a separate 16mbit rom combined into one file. one of the roms has a checksum for that rom only, the other one doesn't have a checksum written for itself. so it can be handled in different ways.

again the checksum is the nintendo standard, so you must not have a copier header on the rom (copiers obviously are not nintendo standards.)
 
Re: here's how you do it

> 4mbit rom = checksum of 4mbits * 2

Alright, thanks a lot for that reply that was very helpful, but I think you're wrong about one thing, because I just plain added up all the bytes in F-Zero without multiplying by 2, and the program spit out the same checksum that's in the ROM. I'm thinking that it has to be divisible by 4Mbit, not 8Mbit like you said, which is what the SNES Kart document seemed to be getting at, although I wasn't sure. I'm going to try out the Ms. PacMan ROM, which I think was 2Mbit and see if multiplying by 2 gives the correct checksum. Anyways, thanks!
 
10mbit

> 10Mbit ROM (Were any commercial games actually of this size
> or is worrying about this a waste of time?)

Aladdin, Equinox, Jinsei Game (The Game of Life) 1/2 are a few I know of. Odd thing about Equinox is that the Japanese version (Solstice 2) is 8mbit, yet the extra 2mbit in the US ROM image seems completely empty. Didn't look at the PAL version, though.

Also, SGB is 2mbit if you want another odd size ROM example.
 
Re: 10mbit

> Aladdin, Equinox, Jinsei Game (The Game of Life) 1/2 are a
> few I know of. Odd thing about Equinox is that the Japanese
> version (Solstice 2) is 8mbit, yet the extra 2mbit in the US
> ROM image seems completely empty. Didn't look at the PAL
> version, though.

What did they report in their headers for ROM size?
 
Re: here's how you do it

> it's as simple as this. take a rom, remove any header if present (the first 512 bytes, they aren't the original rom, it's added information copiers put there to store extra data about the rom.) if it's 8, 16, or 32mbit, just add all the bytes together, then AND it with 0xFFFF (to keep it 16 bits.) that is the checksum, which is the second part in the rom's actual header. then inverse the checksum and that's the compliment. so if the checksum is 0x1234 then XOR it with 0xFFFF (0x1234 ^= 0xFFFF) and your compliment is 0xEDCB.

Do you have a code snippet for this? I'm trying to understand how this works and am just not getting it from your description. How is this different from CRC16 with the last banks mirrored out to make 4/8/16/32Mbit?
 
Re: here's how you do it

I might write up some code later, it's real simple. think of it like this. the rom data is like 1 2 3 4 5 6 7 8 so you add them up (in groups of 4mbit or 8mbit I don't remember, sneskart may have it wrong.) so the checksum is 36. now for even sized roms of 8, 16, 32mbits (the value in the header) this works fine like this example of 8 numbers being added. but let's say we have 12 numbers not 8. you'd add them like this.

1 2 3 4 5 6 7 8 | 9 10 11 12 9 10 11 12

you duplicate the part that's less than 8mbit. 8mbit adds fine (don't count the copier header.) 16mbit, 32mbit works fine, but an odd sized rom like 10 or 12mbit, you need to use the remaining data and duplicate it until you reach an even size.

it might be a fun little learning experience to program something like this, but it's been done a million times snes9x, zsnes, smc, nsrt. if you're really curious about it get a hex editor and play around with some rawms.
 
What I just figured out.

In order to now have to multiply the result of an unfinished ROM bank, the game's size has to be a power of 2mbit. Well, not QUITE, but effectively for all commerical ROMs, I'll get to that in a second. Ms. Pac-Man is a 2mbit game, and it's checksum is calculated perfectly by just adding the byte.. This is because how many bytes are counted into the checksum is based on the size byte in the embedded ROM data, which works like this, for those who don't know, for common sizes (lazy coding, could have used an algorithm, and will recode it soon):

<tt>
switch (romsize)
{
case 0x08:
printf("2 MBit\n");
break;
case 0x09:
printf("4 MBit\n");
break;
case 0x0A:
printf("8 MBit\n");
break;
case 0x0B:
printf("16 MBit\n");
break;
case 0x0C:
printf("32 MBit\n");
break;
default:
printf("Unknown/Other\n");
break;
}</tt>


Obviously, this size byte is wrong for any game that doesn't fall into one of those sizes. Therefore, "extra" data needs to be counted until it fits that size.

Theoretically, the checksum for ROMs that are below 2mbit (No such commercial ROMs), would be counted as if they were of the size 32kbit, 64kbit, 128kbit, 256kbit, 512kbit, or 1mbit, IMO.

I tested my program, which only counts as is right now, and here's the sizes it's correct and incorrect on:

2mbit=right
4mbit=wrong
8mbit=right
12mbit=wrong
16mbit=right
20mbit=wrong
24mbit=wrong
32mbit=right

That's what made me think it was based on the size byte in the embedded data.

This is just a fun exercise like PhonyMike said. I might use the code I make in the future, if I ever get good enough to make an emulator (also for my amusement) and I'll probably release it after I clean it up and add some more features (automaticly splitting and renaming ROMs so they'll work on Game Doctor 7/Professor SF 2 is one I want) If anyone isn't having fun figuring this out or interested in this, no problem, don't want to waste your time. I'm going to watch the World Series now, I'll get back to this afterward.
 
Re: What I just figured out.

I'm getting correct results on 12Mbit games without any mirroring ...

Breath of Fire (U) - 12Mbit - 0b11, 0b11
Dragon Quest I&II (J) - 12Mbit - 2d8a, 2d8a
Gradius III (U) - 4Mbit - 27a2, 27a2
NP Power Lode Runner (J) -12Mbit - 420f, 420f

function checksum($string) {
$crc = "";
for ($x = 0; $x < strlen ($string); $x++) {
$crc += ord($string[$x]);
}
return($crc & 0xffff);
}
 
Re: What I just figured out.

> Breath of Fire (U) - 12Mbit - 0b11, 0b11
> Dragon Quest I&II (J) - 12Mbit - 2d8a, 2d8a
> Gradius III (U) - 4Mbit - 27a2, 27a2
> NP Power Lode Runner (J) -12Mbit - 420f, 420f

Breath of Fire (U) - 12mbit - 8511, 0b11
Dragon Quest 1&2 (J) = 12mbit - 3971, 2d8a
Gradius III (U) - 4mbit - 27A2, 27A2
NP Power Lode Runner (J) - 12mbit - 97df, 042f

Excuse the horrible mess. And no, I don't remember why I malloc'ed a one byte buffer (obbuffer) and made a pointer to it. Like I said, I'm a n00b, etc. And yeah it's inefficient as fuck.

rhoffset is 0x200 if there's a header.
endofrom was obtained by doing ftell(f)...it's endofrom - 1 because it doesn't work without that, which was the source of a hard to find bug...

<tt>
for (romoffset=0; (romoffset+rhoffset)<=endofrom-1; romoffset++)
{
fseek(f, romoffset+rhoffset, SEEK_SET);
fread(obbuffer, 1, 1, f);
checksumvalue=(checksumvalue+*obbuffer);
}

free(obbuffer);

checksumvalue=checksumvalue&0x0000FFFF;


printf("Checksum calculated: %x", checksumvalue);

fseek(f,hiromoffset+0x7fde, SEEK_SET);
fread(&hb, 1, 1, f);
fread(&lb, 1, 1, f);
printf("\nROM Checksum=%x%x",lb, hb);

if ((((checksumvalue&0x0000FF00) >> 8)==lb)&&((checksumvalue&0x000000FF)==hb))
{
printf(" (Checksum OK)\n");
}
else printf(" (Checksum does not match)\n");</tt>
 
Re: What I just figured out.

> Excuse the horrible mess. And no, I don't remember why I
> malloc'ed a one byte buffer (obbuffer) and made a pointer to
> it. Like I said, I'm a n00b, etc. And yeah it's inefficient
> as fuck.

Jesus fuck C is ugly.

--snip--

Doh, I found the glitch in my code. Now I get the same problems as you.<P ID="edit"><FONT class="small">Edited by D-BOY on 10/23/05 01:54 AM.</FONT></P>
 
Re: What I just figured out.

> Doh, I found the glitch in my code. Now I get the same
> problems as you.
>
Aw man, how much longer after you posted that did you edit it? :p

I was going to ask you if you were somehow accidentally assigning the value read from the ROM in the embedded SNES data to the same variable as the calculated or something like that before displaying it, but I figured that kind of question might piss an experienced coder off hahaha...the silver lining is that I went and recoded the checksum calculating thing in a separate program in a much less horrid manner because I was going to post all of my code, and wanted to eliminate any other possibilities except the problem at hand, so it would be easier on anyone who'd look at it. ..well, now I have better code, so I guess that's cool, haha.
 
Re: What I just figured out.

Did you find out what to do with 2Mbit games or the 256Kbit BIOS type files yet? That's the last thing I have left to get working. Do you just keep mirroring the whole thing up to 4Mbit?
 
Re: What I just figured out.

> Did you find out what to do with 2Mbit games or the 256Kbit
> BIOS type files yet? That's the last thing I have left to
> get working. Do you just keep mirroring the whole thing up
> to 4Mbit?

2Mbit games you just count. You don't mirror them at all. Any game that's actual size matches its size byte isn't mirrored at all...that's the determining factor I do believe. Let me fetch a BIOS file and look at it. I doubt they use checksums...
 
Re: What I just figured out.

> 2Mbit games you just count. You don't mirror them at all.
> Any game that's actual size matches its size byte isn't
> mirrored at all...that's the determining factor I do
> believe. Let me fetch a BIOS file and look at it. I doubt
> they use checksums...

Just figured out the Super GameBoy BIOS V1.0 (J).smc

Just add the bytes! No mirroring, nothing. However, it doesn't correspond to its size byte (which would be 0x06), so I was wrong about that.
 
Game Genie

Edit: Nevermind. Fixed it. This is becoming a recurring pattern isn't it?

In my eternal quest to re-invent the wheel (God this is fun), I have a question about the SNES's Game Genie. It seems that to convert a code from Pro Action Replay to Game Genie, one would have to use a look-up table to get the values the Game Genie uses for various 4-bit hex digits. I've done this, and I'm 100% certain this part is correct. Then, one must shift around the bits doing a lot of bitwise stuff, according to the document I read, so that they are in a different place. I've coded this, and I've looked it over a dozen times and looked at what's going on in the debugger, and the bits do appear to be shifting to the right place. But the code is not correct.

Well, I've tried doing the look-up first and then switching the bits around, and that doesn't work either. Does anyone know if there's anything "weird" about the Game Genie that I haven't mentioned? Do I need to convert endianness? Does it refer to a physical ROM address rather than an address in the SNES's memory map? Something like this?<P ID="edit"><FONT class="small">Edited by JadussD on 11/01/05 09:56 AM.</FONT></P>
 
Back
Top Bottom