Nintendo header: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(More caveats)
(Adds information on Nintendo header FamicomBox compatibility.)
Line 1: Line 1:
About 33% of licensed Nintendo NES games have an header present at their last bank, at addresses $FFE0-$FFF9, right before the interrupt vectors. The [[FamicomBox]] reads this header to determine the title of the inserted game.
About 33% of licensed Nintendo NES games have a header present at their last bank, at addresses $FFE0-$FFF9, right before the interrupt vectors. The [[FamicomBox]] reads this header to determine the validity and title of an inserted game.


The info in them is frequently incomplete and/or inaccurate. When info on a particular filed is not present, a byte $00 or $FF is used for padding.
The info in them is frequently incomplete and/or inaccurate. When info on a particular filed is not present, a byte $00 or $FF is used for padding.


* '''$FFE0-$FFEF''' : Name of the game, in ASCII. (sometimes the name is abbreviated or replaced by Nintendo's code). ASCII codes 20h-5Ah allowed. Should be right-justified.
* '''$FFE0-$FFEF''' : Name of the game, in ASCII. (sometimes the name is abbreviated or replaced by Nintendo's code). ASCII codes 20h-5Ah allowed. Should be right-justified.
* '''$FFF0-$FFF1''' : PRG checksum (either the last 16k bank, or the whole PRG-ROM). Sum of all bytes in the relevant area, excluding the two checksum bytes. Big-endian.
* '''$FFF0-$FFF1''' : PRG checksum (the whole PRG-ROM, the last 16 KiB, or each 32 KiB bank, depending on specified mapper). Sum of all bytes in the relevant area, excluding the two checksum bytes. Big-endian.
* '''$FFF2-$FFF3''' : CHR checksum. 0x00 if RAM. Big-endian.
* '''$FFF2-$FFF3''' : CHR checksum. 0x00 if RAM. Big-endian.
* '''$FFF4''' : Data sizes. Values: 0 = 8 KiB or 64 KiB based on board type, 1 = 16 KiB, 2 = 32 KiB, 3 = 128 KiB, 4 = 256 KiB, 5 = 512 KiB
* '''$FFF4''' : Data sizes. Values: 0 = 8 KiB or 64 KiB based on board type, 1 = 16 KiB, 2 = 32 KiB, 3 = 128 KiB, 4 = 256 KiB, 5 = 512 KiB
Line 12: Line 12:
* '''$FFF5''' : Board Type.
* '''$FFF5''' : Board Type.
**D7: 0 = Horizontal [[Mirroring|nametable arrangement]], 1 = Vertical arrangement (inverse of [[iNES#Flags 6|iNES mirroring bit]])
**D7: 0 = Horizontal [[Mirroring|nametable arrangement]], 1 = Vertical arrangement (inverse of [[iNES#Flags 6|iNES mirroring bit]])
**D6-D0:  0 = NROM, 1 = CNROM, 2 = UNROM/AOROM, 3 = GNROM, 4 = MMC (any). Determines which area to checksum.
**D6-D0:  0 = NROM, 1 = CNROM, 2 = UNROM, 3 = GNROM, 4 = MMC (any). Determines which area to checksum.
* '''$FFF6''' : Title encoding : 0 = No title entered, 1 = ASCII, 2 = Another encoding
* '''$FFF6''' : Title encoding : 0 = No title entered, 1 = ASCII, 2 = Another encoding
* '''$FFF7''' : Valid Title Length - 1. 0 if no title entered.
* '''$FFF7''' : Valid Title Length - 1. 0 if no title entered.
Line 18: Line 18:
* '''$FFF9''' : Header Validation Byte. 8-bit checksum of $FFF2-$FFF9 should = 0
* '''$FFF9''' : Header Validation Byte. 8-bit checksum of $FFF2-$FFF9 should = 0


== Calculating the checksum ==
== FamicomBox compatibility ==


[https://problemkaputt.de/everynes.htm#famicomboxromheaderatffe0h Everynes] claims that the FamicomBox system software applies these rules:
The FamicomBox is the only device known to use the Nintendo header. Its firmware cartridge validates game cartridges using the header validation byte and the PRG checksum. If these are both correct, the game is marked as valid and the header title is copied, and otherwise, the game is looked up in the firmware cartridge's database for a match.
;NROM and CNROM: Sum of $E000-$FFFF, $C000-$FFFF, or $8000-$FFFF. The system software detects the size.
;UNROM/AOROM: Sum of $8000-$BFFF in banks 0-7.
;GNROM: Each bank has a header with its own checksum of $8000-$FFFF. All checksums must pass.
;MMC: Sum of $C000-$FFFF, as if NROM-128. No bank switching is attempted.


A checksum passes if the sum of bytes, minus the bytes at $FFF0 and $FFF1, matches the big-endian value at $FFF0.
As part of validation, the FamicomBox performs writes to the game cartridge's mapper. It does this by searching forward from $8000-FFFF, ANDing each byte by a write mask and comparing against the write value. If they match, the value at that ROM address is read and then written back. This is done to avoid bus conflicts. If no match is found, $0000 is read and written, instead.


Caveats for particular mappers, to be verified:
The sequence of operations for validating a cartridge are as follows:


;UNROM: The checksum routine avoids bus conflicts by searching ROM for the first byte in $8000-$FFFF whose low bits match the low bits of the bank number, reading that byte, and writing it back. This causes problems with UOROM, as this a write with low nibble as a write intended to set bank 0 may instead set bank 8.
* The slot is checked for whether it contains a cartridge by checking 17 addresses for any non-$FF value. These addresses are: $0000, $8000, $8001, $8002, $8004, $8008, $8010, $8020, $8040, $8080, $8100, $8200, $8400, $8800, $9000, $A000, $C000. If only $FF is found, the slot is skipped. Note that the check on $0000 may result in false positives.
:For a 64 KiB UNROM, such as [[240p Test Suite]], the checksum routine reads each of the four banks twice. Thus it double-counts most bytes and (net) single-counts the bytes at $FFF0-$FFF1 because they also appear at $BFF0-$BFF1. A ROM will usually need an extra byte somewhere (such as at $FFDF) whose value is chosen to cancel the lower byte of the checksum of the rest of the ROM.
* The mapper is written with value $00 and mask $FF.
;AxROM and BxROM: 128 KiB ROM has four banks and thus has a similar double-counting caveat to 64 KiB UNROM. The checksum is in the last bank (bank 3 or bank 7) and, as usual, both bytes are subtracted before comparison despite not having been included in the original sum.
* The header is validated by verifying that $FFF2-FFF9 sum to 0. If they do not, the validation process is stopped and the database is checked.
;MMC1: Sum of $C000-$FFFF at power on, as if NROM-128. No bank switching is attempted. Any 16 KiB bank that can be switched into $C000-$FFFF needs its own header with a checksum of that bank.
* The PRG checksum is calculated based on the cartridge's mapper type. The bytes at $FFF0 and $FFF1 are each subtracted from this sum and the result is compared to $FFF0-FFF1 (big endian). If they do not match, the validation process is stopped and the database is checked. Note that the bank is not changed between completing the sum and using the header's PRG checksum values.
;MMC3, most VRCs, FME-7: Sum of $C000-$FFFF at power on, as if NROM-128. These mappers have a fixed bank at $E000-$FFFF and an unpredictable switchable bank at $C000-$DFFF. In order to get the checksum to match regardless of state, the sum of bytes in all 8 KiB banks other than the last must be identical. Given the overall lack of mapper IRQ support in the FamicomBox, we seek evidence of games on these mappers having been released for FamicomBox at all.
** '''NROM/CNROM''': The PRG size is determined by looking for the largest range containing unique data ($E000-FFFF if $C000-DFFF and $E000-FFFF match, $C000-FFFF if $8000-BFFF and $C000-EFFF match, and $8000-FFFF otherwise). This range is summed.
** '''UNROM''': A single checksum is created by summing 8 banks together. The 8 banks are loaded by writing $00-07 with mask $07 to the mapper. For each bank, $8000-BFFF is summed into the combined total.
** '''GNROM''': Each of 4 banks is individually checked, so each requires its own checksum at $FFF0-FFF1. The 4 banks are loaded by writing $00, $10, $20, and $30 with mask $30 to the mapper. For each bank, $8000-FFFF is summed.
** '''MMC''': $C000-FFFF is summed.
** '''Other''': The match automatically fails.
* The title length at $FFF7 is checked. If it isn't in the range $01-0F, the title is considered invalid, so a 16-byte title of value $20 (space) is used and the slot is marked as valid with an invalid title. Otherwise, the right-aligned title is copied from $FFE0-FFEF and stored left-aligned, the remaining space on the right is padded with value $20, and the slot is marked as valid with a valid title. Note that 1-character titles will be seen as invalid, but this can be fixed by incrementing the length and suffixing the title with a space. Any bytes to the left of the used portion of the title are free for general-purpose use.
 
For database lookups, $00 with mask $FF is written to the mapper and then the NROM/CNROM-style checksum is calculated.
 
=== Caveats for particular mappers ===
 
;UNROM: The checksum routine avoids bus conflicts by searching ROM for the first byte in the currently-loaded $8000-FFFF region whose low 3 bits match the target bank number, reading that byte, and writing it back. This causes problems with variants supporting more than 8 banks, such as UOROM, because the remaining bits will depend on the value that was found by the search, so a checksum generator must perform this search to find which of the 8 banks are visited when calculating the checksum.
:For 64 KiB UNROM, such as [[240p Test Suite]], the checksum routine reads each of the four banks twice. Thus, it double counts every byte, but still subtracts the checksum bytes only once. The checksum value must account for this. A potential strategy is to generate a checksum, and then reduce the value of an unused byte by half the sum of the two checksum bytes.
;AxROM and BxROM: The header does not support these mapper types. The Japanese release of Battletoads uses AOROM and has a header that claims to be UNROM, but its checksum does not adhere to UNROM rules. These are best supported using the NROM or MMC types and placing the header information in the first bank.
;MMC1: The mapper is not reset before the checksum is read, so the last bank may not be fixed to $C000-FFFF. Therefore, every bank that could be mapped there at power-on needs its own header with its own checksum.
;MMC3, most VRCs, FME-7: These mappers have a fixed bank at $E000-$FFFF and an unpredictable switchable bank at $C000-$DFFF. To get the checksum to match, either the $00 mapper write needs to be used to make this region predictable, or the sum of all 8 KiB banks that can be mapped there must be identical. For MMC3, placing $00 at the beginning of each bank will send this write to the bank select register at $8000, fixing $C000-DFFF to the second-last bank.
 
In general, the MMCx mapper type is likely the best fit for any given project, with the header and checksum included in any place that can be loaded after a write of $00 to first of any address that can contain $00.

Revision as of 07:04, 16 August 2021

About 33% of licensed Nintendo NES games have a header present at their last bank, at addresses $FFE0-$FFF9, right before the interrupt vectors. The FamicomBox reads this header to determine the validity and title of an inserted game.

The info in them is frequently incomplete and/or inaccurate. When info on a particular filed is not present, a byte $00 or $FF is used for padding.

  • $FFE0-$FFEF : Name of the game, in ASCII. (sometimes the name is abbreviated or replaced by Nintendo's code). ASCII codes 20h-5Ah allowed. Should be right-justified.
  • $FFF0-$FFF1 : PRG checksum (the whole PRG-ROM, the last 16 KiB, or each 32 KiB bank, depending on specified mapper). Sum of all bytes in the relevant area, excluding the two checksum bytes. Big-endian.
  • $FFF2-$FFF3 : CHR checksum. 0x00 if RAM. Big-endian.
  • $FFF4 : Data sizes. Values: 0 = 8 KiB or 64 KiB based on board type, 1 = 16 KiB, 2 = 32 KiB, 3 = 128 KiB, 4 = 256 KiB, 5 = 512 KiB
    • D7-D4: PRG size
    • D3: 0 = CHR ROM, 1 = CHR RAM
    • D2-D0: CHR size
  • $FFF5 : Board Type.
    • D7: 0 = Horizontal nametable arrangement, 1 = Vertical arrangement (inverse of iNES mirroring bit)
    • D6-D0: 0 = NROM, 1 = CNROM, 2 = UNROM, 3 = GNROM, 4 = MMC (any). Determines which area to checksum.
  • $FFF6 : Title encoding : 0 = No title entered, 1 = ASCII, 2 = Another encoding
  • $FFF7 : Valid Title Length - 1. 0 if no title entered.
  • $FFF8 : Maker's code, the same used for the FDS, GB, GBC and SNES headers : 1 = Nintendo, 2-254 = everyone else. 255 must be reserved.
  • $FFF9 : Header Validation Byte. 8-bit checksum of $FFF2-$FFF9 should = 0

FamicomBox compatibility

The FamicomBox is the only device known to use the Nintendo header. Its firmware cartridge validates game cartridges using the header validation byte and the PRG checksum. If these are both correct, the game is marked as valid and the header title is copied, and otherwise, the game is looked up in the firmware cartridge's database for a match.

As part of validation, the FamicomBox performs writes to the game cartridge's mapper. It does this by searching forward from $8000-FFFF, ANDing each byte by a write mask and comparing against the write value. If they match, the value at that ROM address is read and then written back. This is done to avoid bus conflicts. If no match is found, $0000 is read and written, instead.

The sequence of operations for validating a cartridge are as follows:

  • The slot is checked for whether it contains a cartridge by checking 17 addresses for any non-$FF value. These addresses are: $0000, $8000, $8001, $8002, $8004, $8008, $8010, $8020, $8040, $8080, $8100, $8200, $8400, $8800, $9000, $A000, $C000. If only $FF is found, the slot is skipped. Note that the check on $0000 may result in false positives.
  • The mapper is written with value $00 and mask $FF.
  • The header is validated by verifying that $FFF2-FFF9 sum to 0. If they do not, the validation process is stopped and the database is checked.
  • The PRG checksum is calculated based on the cartridge's mapper type. The bytes at $FFF0 and $FFF1 are each subtracted from this sum and the result is compared to $FFF0-FFF1 (big endian). If they do not match, the validation process is stopped and the database is checked. Note that the bank is not changed between completing the sum and using the header's PRG checksum values.
    • NROM/CNROM: The PRG size is determined by looking for the largest range containing unique data ($E000-FFFF if $C000-DFFF and $E000-FFFF match, $C000-FFFF if $8000-BFFF and $C000-EFFF match, and $8000-FFFF otherwise). This range is summed.
    • UNROM: A single checksum is created by summing 8 banks together. The 8 banks are loaded by writing $00-07 with mask $07 to the mapper. For each bank, $8000-BFFF is summed into the combined total.
    • GNROM: Each of 4 banks is individually checked, so each requires its own checksum at $FFF0-FFF1. The 4 banks are loaded by writing $00, $10, $20, and $30 with mask $30 to the mapper. For each bank, $8000-FFFF is summed.
    • MMC: $C000-FFFF is summed.
    • Other: The match automatically fails.
  • The title length at $FFF7 is checked. If it isn't in the range $01-0F, the title is considered invalid, so a 16-byte title of value $20 (space) is used and the slot is marked as valid with an invalid title. Otherwise, the right-aligned title is copied from $FFE0-FFEF and stored left-aligned, the remaining space on the right is padded with value $20, and the slot is marked as valid with a valid title. Note that 1-character titles will be seen as invalid, but this can be fixed by incrementing the length and suffixing the title with a space. Any bytes to the left of the used portion of the title are free for general-purpose use.

For database lookups, $00 with mask $FF is written to the mapper and then the NROM/CNROM-style checksum is calculated.

Caveats for particular mappers

UNROM
The checksum routine avoids bus conflicts by searching ROM for the first byte in the currently-loaded $8000-FFFF region whose low 3 bits match the target bank number, reading that byte, and writing it back. This causes problems with variants supporting more than 8 banks, such as UOROM, because the remaining bits will depend on the value that was found by the search, so a checksum generator must perform this search to find which of the 8 banks are visited when calculating the checksum.
For 64 KiB UNROM, such as 240p Test Suite, the checksum routine reads each of the four banks twice. Thus, it double counts every byte, but still subtracts the checksum bytes only once. The checksum value must account for this. A potential strategy is to generate a checksum, and then reduce the value of an unused byte by half the sum of the two checksum bytes.
AxROM and BxROM
The header does not support these mapper types. The Japanese release of Battletoads uses AOROM and has a header that claims to be UNROM, but its checksum does not adhere to UNROM rules. These are best supported using the NROM or MMC types and placing the header information in the first bank.
MMC1
The mapper is not reset before the checksum is read, so the last bank may not be fixed to $C000-FFFF. Therefore, every bank that could be mapped there at power-on needs its own header with its own checksum.
MMC3, most VRCs, FME-7
These mappers have a fixed bank at $E000-$FFFF and an unpredictable switchable bank at $C000-$DFFF. To get the checksum to match, either the $00 mapper write needs to be used to make this region predictable, or the sum of all 8 KiB banks that can be mapped there must be identical. For MMC3, placing $00 at the beginning of each bank will send this write to the bank select register at $8000, fixing $C000-DFFF to the second-last bank.

In general, the MMCx mapper type is likely the best fit for any given project, with the header and checksum included in any place that can be loaded after a write of $00 to first of any address that can contain $00.