ROM Header: Difference between revisions
(24 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
== Standard header == |
== Standard header == |
||
The following table shows the standard contents of the ROM header. Most of the structure of the header is defined by the [[IPL3]] routines commonly used in commercial games (which are actually contained in the ROM itself); only a few fields are accessed by IPL2 (which is burned in [[PIF-NUS|PIF ROM]]) and are thus hard-coded for all possible valid N64 ROMs. |
The following table shows the standard contents of the ROM header. Most of the structure of the header is defined by the [[IPL3]] routines commonly used in commercial games (which are actually contained in the ROM itself); only a few fields are accessed by IPL2 (which is burned in [[PIF-NUS|PIF ROM]]) and are thus hard-coded for all possible valid N64 ROMs. |
||
{| class="wikitable" |
{| class="wikitable" |
||
|+ |
|||
!Offset |
!Offset |
||
!Size |
|||
!Bytes |
|||
!Name |
!Name |
||
!Description |
!Description |
||
|- |
|- |
||
|0x00 |
|0x00 |
||
| |
|0x4 |
||
|PI DOM1 |
|PI BSD DOM1 Configuration Flags |
||
|These flags are used by IPL2 to configure access to the ROM (which is mapped in the PI DOM1 space, see [[memory map]]). IPL2 first configure the PI to its slowest speed to be able to read these 4 bytes, and then use them to configure the correct speed to access the ROM. |
|These flags are used by IPL2 to configure access to the ROM (which is mapped in the PI DOM1 space, see [[memory map]]). IPL2 first configure the PI to its slowest speed to be able to read these 4 bytes, and then use them to configure the correct speed to access the ROM. |
||
All known commercial games use |
All known commercial games use <code>80 37 12 40</code> in these bytes. |
||
These fields could be tuned in replica cartridges to adapt to the actual maximum physical speed that the ROM can be accessed, but please pay attention that many emulators expect to find the 4 "standard" values here and actually use them as a "fixed ID" to detect if the ROM dump was byte-swapped. |
These fields could be tuned in replica cartridges to adapt to the actual maximum physical speed that the ROM can be accessed, but please pay attention that many emulators expect to find the 4 "standard" values here and actually use them as a "fixed ID" to detect if the ROM dump was byte-swapped. |
||
If you are an emulator author, please make sure that your emulator can load a ROM with arbitrary values in these first 4 bytes, and assume the ROM is in plain "big-endian" format (not byte swapped, not endian swapped). All modern homebrew ROMs should ship in plain format anyway, as the other formats are obsolete and strongly discouraged. |
|||
|- |
|- |
||
|0x04 |
|0x04 |
||
| |
|0x4 |
||
|Clock |
|Clock Rate |
||
|Constant value used by libultra versions 2.0I and earlier, to naively calculate how much real-time has passed based on the CPU's Count register. |
|||
|Usually 0 |
|||
The value is masked by 0xFFFFFFF0, then multiplied by 0.75 to account for the CPU clock's multiplier (1.5x) and that the Count register increments every 2 CPU cycles. |
|||
However, if the masked value equals zero, libultra defaults to 0x03B9ACA0 (62,500,000) before multiplying by 0.75. |
|||
All known ROMs, except those listed below, have this field set to 0x0000000F. |
|||
{| class="wikitable" |
|||
! Clock Rate !! libultra ver. !! Game Code !! Title |
|||
|- |
|- |
||
| 0x03B9ACAF || 2.0D || NCUP || Cruis'n USA (Europe) |
|||
|0x08 |
|||
| |
|- |
||
| 0x03B9ACAF || 2.0D || NCUE || Cruis'n USA (USA) (Rev A) |
|||
|Initial PC |
|||
|- |
|||
|Initial PC in RDRAM. IPL3 will jump to this address when it has finished initializing the hardware, to boot the ROM. |
|||
| 0x03B9ACAF || 2.0D || NCUE || Cruis'n USA (USA) (Rev B) |
|||
|- |
|||
| 0x03B9ACAF || 2.0D || NCUE || Cruis'n USA (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0G || NDMP || Doom 64 (Europe) |
|||
|- |
|||
| 0x03A07F5F || 2.0H || NDMJ || Doom 64 (Japan) |
|||
|- |
|||
| 0x03A07F5F || 2.0G || NDME || Doom 64 (USA) (Rev A) |
|||
|- |
|||
| 0x03A07F5F || 2.0G || NDME || Doom 64 (USA) |
|||
|- |
|||
| 0x03B9ACAF || 2.0G || NXGP || NBA Hangtime (Europe) |
|||
|- |
|||
| 0x03B9ACAF || 2.0G || NXGE || NBA Hangtime (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0I || NRIP || New Tetris, The (Europe) |
|||
|- |
|||
| 0x03A07F5F || 2.0I || NRIE || New Tetris, The (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0H || NQKP || Quake 64 (Europe) |
|||
|- |
|||
| 0x03A07F5F || 2.0H || NQKE || Quake 64 (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0F || NFXJ || Star Fox 64 (Japan) |
|||
|- |
|||
| 0x03A07F5F || 2.0H || NFXE || Star Fox 64 (USA) (Rev A) |
|||
|- |
|||
| 0x03A07F5F || 2.0H || NFXE || Star Fox 64 (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWP || Star Wars - Shadows of the Empire (Europe) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWE || Star Wars - Shadows of the Empire (USA) (Beta) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWE || Star Wars - Shadows of the Empire (USA) (Rev A) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWE || Star Wars - Shadows of the Empire (USA) (Rev B) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWE || Star Wars - Shadows of the Empire (USA) |
|||
|- |
|||
| 0x03A07F5F || 2.0D || NSWJ || Star Wars - Teikoku no Kage (Japan) |
|||
|- |
|||
| 0x03A07F5F || 2.0I || NEPP || Star Wars Episode I - Racer (Europe) (En,Fr,De) |
|||
|- |
|||
| 0x03A07F5F || 2.0I || NEPJ || Star Wars Episode I - Racer (Japan) |
|||
|- |
|||
| 0x03A07F5F || 2.0I || NEPE || Star Wars Episode I - Racer (USA) |
|||
|} |
|||
Using Doom 64 as an example, 0x03A07F5F is masked to 0x03A07F50 (60,850,000), and then multiplied by 0.75 equals 0x02B85F7C (45,637,500). |
|||
Keep in mind that there is no known way for software to change the clock frequency used by either the RCP or CPU. So if/when games use the Count register divided by 46,875,000 to measure a real-time second, the calculated result will be inaccurate. |
|||
[[User:Polprzewodnikowy|Polprzewodnikowy]] from the N64brew Discord, experimented with Doom 64 and Star Fox 64 on real hardware. They found that increasing this value increases the delay before the copyright screen or Nintendo logo (respectively) shows up on screen. |
|||
|- |
|||
|0x08 |
|||
|0x4 |
|||
|Boot Address |
|||
|Initial PC in RDRAM. IPL3 will DMA 1 MiB of ROM code from offset 0x1000 to this address when it has finished initializing the hardware, and then jump here to boot the ROM. The most common value for this field is 0x80000400. |
|||
Line 40: | Line 103: | ||
|- |
|- |
||
|0x0C |
|0x0C |
||
| |
|0x4 |
||
|Libultra Version |
|||
|? |
|||
|These 4 bytes are meant to indicate the version of the libultra SDK the ROM was compiled with. |
|||
|? |
|||
{| class="wikitable" |
|||
|+ |
|||
!Offset |
|||
!Size |
|||
!Description |
|||
|- |
|||
|0x0 |
|||
|0x2 |
|||
|Reserved, or possibly used to store the patch version. |
|||
|- |
|||
|0x2 |
|||
|0x1 |
|||
|Major and minor version of libultra as decimal value. For instance, version "2.0" is encoded as decimal 20 (hex 0x14). |
|||
|- |
|||
|0x3 |
|||
|0x1 |
|||
|Revision version of libultra, as ASCII letter. |
|||
|} |
|||
For instance, libultra version "2.0L" is reported as <code>00 00 14 4C</code> |
|||
This field is not used by IPL or anything else, it is just for information; moreover, many games report the wrong version here. |
|||
|- |
|- |
||
|0x10 |
|0x10 |
||
| |
|0x8 |
||
|Check Code |
|||
|CRC |
|||
| |
|64-bit check code calculated on 1 Mbyte of ROM contents starting from offset 0x1000 (after IPL3). This check code is computed with a custom algorithm implemented by IPL3 that verifies the integrity of the ROM before booting it. If the check code doesn't match, IPL3 refuses to boot the ROM and hangs the console. |
||
Sometimes, these 8 bytes are referred to as "CRC HI/LO" or "CRC1/2", but the check code algorithm is not a CRC, and splitting it into two 4-byte words seem confusing when it is really a 64-bit value. |
|||
Line 53: | Line 141: | ||
|- |
|- |
||
|0x18 |
|0x18 |
||
| |
|0x8 |
||
|Reserved |
|||
|? |
|||
|This field is unused by Nintendo, but has seen usage by other developers. |
|||
|? |
|||
For the Conker's Bad Fur Day debug ROM, this field is set to <code>6F 23 01 3A 2F C9 CB 36</code>. For the Perfect Dark debug ROM, this field is set to <code>54 E4 D8 38 2F BD 95 36</code>. Since both of these games were made by RAREWARE, this field could be a check code for their Security Dongle hardware, which was required to be able to boot these games on the N64. |
|||
Another ROM, the StarCraft 64 beta, also has data in this field. However, it seems to just be random, as the remaining header fields are also set to random values. |
|||
|- |
|- |
||
|0x20 |
|0x20 |
||
|0x14 |
|||
|20 |
|||
|Game |
|Game Title |
||
| |
|String that contains the name of the game. The encoding is usually either ASCII or JIS X 0201 (a subset of Shift-JIS). Padding Is usually performed with 0x20 (ASCII space). |
||
|- |
|- |
||
|0x34 |
|0x34 |
||
| |
|0x7 |
||
|Reserved |
|||
|Unused |
|||
|This field is unused by Nintendo, but saw accidental usage in the Tommy Thunder prototype ROM, where the first 4 bytes are set to 0x20. This is possibly due to the developers thinking that the game title field was 0x18 bytes rather than 0x14. |
|||
| |
|||
|- |
|- |
||
|0x3B |
|0x3B |
||
| |
|0x4 |
||
|Game Code |
|||
|Media format |
|||
|The game code is 4 ASCII characters that are split up into 3 parts - the category code, the unique code and the destination code. |
|||
|An ASCII character identifying the physical support that this ROM was extracted from. |
|||
{| class="wikitable" |
|||
|+ |
|||
!Offset |
|||
!Size |
|||
!Name |
|||
!Description |
|||
|- |
|||
|0x0 |
|||
|0x1 |
|||
|Category Code |
|||
|An alphanumeric character that specifies the kind of media the game is stored on. |
|||
{| class="wikitable" |
{| class="wikitable" |
||
|+ |
|+ |
||
!N |
!N |
||
|Game Pak |
|||
|Standard gamepad (cartridge) |
|||
|- |
|- |
||
!D |
!D |
||
|64DD |
|64DD Disk |
||
|- |
|- |
||
!C |
!C |
||
|Expandable |
|Expandable Game: Game Pak Part |
||
|- |
|- |
||
!E |
!E |
||
|Expandable |
|Expandable Game: 64DD Disk Part |
||
|- |
|- |
||
!Z |
!Z |
||
|Aleck64 |
|Aleck64 Game Pak |
||
|} |
|} |
||
|- |
|- |
||
|0x1 |
|||
|0x3C |
|||
| |
|0x2 |
||
|Unique Code |
|||
|Game ID |
|||
|Two |
|Two alphanumeric characters that identify the game. |
||
|- |
|- |
||
|0x3 |
|||
|0x3E |
|||
| |
|0x1 |
||
|Destination Code |
|||
|Country code |
|||
|An |
|An alphanumeric character that specifies the destination the game is intended for. |
||
{| class="wikitable" |
{| class="wikitable" |
||
|+ |
|+ |
||
|A |
|||
!7 |
|||
|All |
|||
|Beta |
|||
| |
| |
||
|H |
|||
!G |
|||
|Netherlands |
|||
|Gateway 64 (NTSC) |
|||
| |
| |
||
|S |
|||
!P |
|||
|Spain |
|||
|European |
|||
|- |
|- |
||
|B |
|||
!A |
|||
|Brazil |
|||
|Asian |
|||
| |
| |
||
|I |
|||
!H |
|||
|Italy |
|||
|Dutch |
|||
| |
| |
||
|U |
|||
!S |
|||
|Australia |
|||
|Spanish |
|||
|- |
|- |
||
|C |
|||
!B |
|||
|China |
|||
|Brazilian |
|||
| |
| |
||
|J |
|||
!I |
|||
|Japan |
|||
|Italian |
|||
| |
| |
||
|W |
|||
!U |
|||
|Scandinavia |
|||
|Australian |
|||
|- |
|- |
||
|D |
|||
!C |
|||
|Germany |
|||
|Chinese |
|||
| |
| |
||
|K |
|||
!J |
|||
|Korea |
|||
|Japanese |
|||
| |
| |
||
|X |
|||
!W |
|||
|Europe |
|||
|Scandinavian |
|||
|- |
|- |
||
|E |
|||
!D |
|||
|German |
|||
| |
|||
!K |
|||
|Korean |
|||
| |
|||
!X |
|||
|European |
|||
|- |
|||
!E |
|||
|North America |
|North America |
||
| |
| |
||
|L |
|||
|Gateway 64 (PAL) |
|Gateway 64 (PAL) |
||
| |
| |
||
|Y |
|||
|Europe |
|||
|European |
|||
|- |
|- |
||
|F |
|||
|France |
|||
|French |
|||
| |
| |
||
|N |
|||
|Canada |
|||
|Canadian |
|||
| |
| |
||
|Z |
|||
! |
|||
|Europe |
|||
|- |
|||
|G |
|||
|Gateway 64 (NTSC) |
|||
| |
| |
||
|P |
|||
|Europe |
|||
|} |
|||
|} |
|} |
||
|- |
|- |
||
|0x3F |
|0x3F |
||
| |
|0x1 |
||
|ROM Version |
|||
|Unused |
|||
|This byte is used to identify the version of the game. Normally the value is 0 for the first version, 1 for the second and so on. |
|||
| |
|||
|- |
|- |
||
|0x40 |
|0x40 |
||
|0xFC0 |
|||
|4032 |
|||
|IPL3 |
|IPL3 |
||
|This area contains the [[IPL3|IPL3 boot code]]. Each ROM ships its own IPL3 that is meant to work together with the [[CIC]] installed in the ROM. Before running IPL3, IPL2 checks its checksum also using a seed coming from the CIC, which binds each IPL3 code to a specific version of CIC. |
|This area contains the [[IPL3|IPL3 boot code]]. Each ROM ships its own IPL3 that is meant to work together with the [[CIC]] installed in the ROM. Before running IPL3, IPL2 checks its checksum also using a seed coming from the CIC, which binds each IPL3 code to a specific version of CIC. |
||
|} |
|} |
||
== |
==Advanced Homebrew ROM Header== |
||
The Advanced Homebrew ROM Header format is a convention that has been agreed upon in the N64 homebrew community to add additional information in the header, using unused fields. The goal of this convention is to let homebrew ROMs declare the correct saveype and controllers that are expected to play the game. The format has been introduced by the EverDrive 64 flashcard and has been later enhanced by the homebrew community. |
The Advanced Homebrew ROM Header format is a convention that has been agreed upon in the N64 homebrew community to add additional information in the header, using unused fields. The goal of this convention is to let homebrew ROMs declare the correct saveype and controllers that are expected to play the game. The format has been introduced by the EverDrive 64 flashcard and has been later enhanced by the homebrew community. |
||
Line 184: | Line 285: | ||
Instead, by using the Advanced Hombrew ROM Header, emulators can automatically configure the required emulated hardware as expected without having to keep any additional database, by simply decoding specific fields of the ROM header. In addition to emulators, also development flashcarts and loaders can use this format to automatically configure the correct savetype when the ROM is loaded. |
Instead, by using the Advanced Hombrew ROM Header, emulators can automatically configure the required emulated hardware as expected without having to keep any additional database, by simply decoding specific fields of the ROM header. In addition to emulators, also development flashcarts and loaders can use this format to automatically configure the correct savetype when the ROM is loaded. |
||
=== |
===Homebrew ROM Header special flags=== |
||
{| class="wikitable" |
{| class="wikitable" |
||
!Offset |
!Offset |
||
Line 191: | Line 292: | ||
!Description |
!Description |
||
|- |
|- |
||
|0x34 |
|||
|0x36 |
|||
|1 |
|1 |
||
|Controller 1 |
|Controller 1 |
||
|This byte contains the suggested / expected controller hardware that should be attached to port 1. |
| This byte contains the suggested / expected controller hardware that should be attached to port 1. |
||
Values 0x01-0x7F indicate a standard N64 controller, possibly with some installed pak. |
Values 0x01-0x7F indicate a standard N64 controller, possibly with some installed pak. |
||
Line 214: | Line 315: | ||
|N64 controller with Controller Pak |
|N64 controller with Controller Pak |
||
!0x82 |
!0x82 |
||
|Gamecube controller |
|Gamecube controller |
||
|- |
|- |
||
!0x03 |
!0x03 |
||
Line 222: | Line 323: | ||
|- |
|- |
||
!0xFF |
!0xFF |
||
|Nothing attached to this port |
|Nothing attached to this port |
||
!0x84 |
!0x84 |
||
|Gamecube keyboard |
|Gamecube keyboard |
||
Line 229: | Line 330: | ||
|- |
|- |
||
|0x35 |
|||
|0x37 |
|||
|1 |
|1 |
||
|Controller 2 |
|Controller 2 |
||
|This byte contains the suggested / expected controller hardware that should be attached to port 2. See byte |
| This byte contains the suggested / expected controller hardware that should be attached to port 2. See byte 0x34 for more information. |
||
|- |
|- |
||
|0x36 |
|||
|0x38 |
|||
|1 |
|1 |
||
|Controller 3 |
|Controller 3 |
||
|This byte contains the suggested / expected controller hardware that should be attached to port 3. See byte |
| This byte contains the suggested / expected controller hardware that should be attached to port 3. See byte 0x34 for more information. |
||
|- |
|- |
||
|0x37 |
|||
|0x39 |
|||
|1 |
|1 |
||
|Controller 4 |
|Controller 4 |
||
|This byte contains the suggested / expected controller hardware that should be attached to port 4. See byte |
| This byte contains the suggested / expected controller hardware that should be attached to port 4. See byte 0x34 for more information. |
||
|- |
|- |
||
|0x3C |
|0x3C |
||
|2 |
|2 |
||
|Game ID |
|Game ID |
||
|This must contain the ASCII characters "ED". It is used as an ID to identify that the Advanced Homebrew ROM header format Is being used by this ROM. |
|This must contain the ASCII characters "ED". It is used as an ID to identify that the Advanced Homebrew ROM header format Is being used by this ROM. |
||
|- |
|- |
||
|0x3F |
|0x3F |
||
Line 291: | Line 392: | ||
|} |
|} |
||
=== |
===Support by emulators=== |
||
Emulators not listed here do not support the advanced homebrew ROM header format. |
Emulators not listed here do not support the advanced homebrew ROM header format. |
||
{| class="wikitable" |
{| class="wikitable" |
||
|+ |
|+ |
||
!Emulator |
!Emulator |
||
!Savetype |
|||
!Current support |
|||
!Controllers |
|||
|- |
|- |
||
|Ares |
|Ares |
||
|Yes |
|||
|Supports Savetype only |
|||
|Yes |
|||
|- |
|- |
||
|cen64 |
|cen64 |
||
|Yes |
|||
|Supports Savetype only |
|||
|No |
|||
|- |
|||
|Parallel Launcher |
|||
|Yes |
|||
|Yes |
|||
|- |
|||
|mupen64plus (core) |
|||
|Yes |
|||
|No |
|||
|- |
|||
|Rosalie's Mupen GUI |
|||
|Yes |
|||
|No |
|||
|} |
|} |
||
=== |
===Support by flashcarts=== |
||
Support by flashcarts can vary depending on the USB loader being used and/or the flashcart operating system. Notice that flashcarts can emulate a specific savetype but have nothing to do with controllers, so the maximum expected support is related to savetype. |
Support by flashcarts can vary depending on the USB loader being used and/or the flashcart operating system. Notice that flashcarts can emulate a specific savetype but have nothing to do with controllers, so the maximum expected support is related to savetype. |
||
{| class="wikitable" |
{| class="wikitable" |
||
Line 314: | Line 430: | ||
|- |
|- |
||
| rowspan="4" |64drive |
| rowspan="4" |64drive |
||
|64drive official C tool |
|64drive official C tool |
||
|None |
|None |
||
|- |
|- |
||
|64drive menu |
|64drive menu |
||
|None |
|None |
||
|- |
|- |
||
Line 324: | Line 440: | ||
|- |
|- |
||
|UNFLoader |
|UNFLoader |
||
|Savetype supported |
|||
|None |
|||
|- |
|- |
||
| rowspan="3" |Everdrive 64 |
| rowspan="3" |Everdrive 64 |
||
Line 334: | Line 450: | ||
|- |
|- |
||
|UNFLoader |
|UNFLoader |
||
|Savetype supported |
|||
|None |
|||
|- |
|||
| rowspan="3" |SummerCart64 |
|||
|N64FlashcartMenu |
|||
|Savetype supported |
|||
|- |
|||
|sc64deployer |
|||
|Savetype supported |
|||
|- |
|||
|UNFLoader |
|||
|Savetype supported |
|||
|} |
|} |
Latest revision as of 07:08, 3 April 2024
Standard header
The following table shows the standard contents of the ROM header. Most of the structure of the header is defined by the IPL3 routines commonly used in commercial games (which are actually contained in the ROM itself); only a few fields are accessed by IPL2 (which is burned in PIF ROM) and are thus hard-coded for all possible valid N64 ROMs.
Offset | Size | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x00 | 0x4 | PI BSD DOM1 Configuration Flags | These flags are used by IPL2 to configure access to the ROM (which is mapped in the PI DOM1 space, see memory map). IPL2 first configure the PI to its slowest speed to be able to read these 4 bytes, and then use them to configure the correct speed to access the ROM.
If you are an emulator author, please make sure that your emulator can load a ROM with arbitrary values in these first 4 bytes, and assume the ROM is in plain "big-endian" format (not byte swapped, not endian swapped). All modern homebrew ROMs should ship in plain format anyway, as the other formats are obsolete and strongly discouraged. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x04 | 0x4 | Clock Rate | Constant value used by libultra versions 2.0I and earlier, to naively calculate how much real-time has passed based on the CPU's Count register.
The value is masked by 0xFFFFFFF0, then multiplied by 0.75 to account for the CPU clock's multiplier (1.5x) and that the Count register increments every 2 CPU cycles. However, if the masked value equals zero, libultra defaults to 0x03B9ACA0 (62,500,000) before multiplying by 0.75. All known ROMs, except those listed below, have this field set to 0x0000000F.
Using Doom 64 as an example, 0x03A07F5F is masked to 0x03A07F50 (60,850,000), and then multiplied by 0.75 equals 0x02B85F7C (45,637,500). Keep in mind that there is no known way for software to change the clock frequency used by either the RCP or CPU. So if/when games use the Count register divided by 46,875,000 to measure a real-time second, the calculated result will be inaccurate. Polprzewodnikowy from the N64brew Discord, experimented with Doom 64 and Star Fox 64 on real hardware. They found that increasing this value increases the delay before the copyright screen or Nintendo logo (respectively) shows up on screen. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x08 | 0x4 | Boot Address | Initial PC in RDRAM. IPL3 will DMA 1 MiB of ROM code from offset 0x1000 to this address when it has finished initializing the hardware, and then jump here to boot the ROM. The most common value for this field is 0x80000400.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x0C | 0x4 | Libultra Version | These 4 bytes are meant to indicate the version of the libultra SDK the ROM was compiled with.
For instance, libultra version "2.0L" is reported as
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x10 | 0x8 | Check Code | 64-bit check code calculated on 1 Mbyte of ROM contents starting from offset 0x1000 (after IPL3). This check code is computed with a custom algorithm implemented by IPL3 that verifies the integrity of the ROM before booting it. If the check code doesn't match, IPL3 refuses to boot the ROM and hangs the console.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x18 | 0x8 | Reserved | This field is unused by Nintendo, but has seen usage by other developers.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x20 | 0x14 | Game Title | String that contains the name of the game. The encoding is usually either ASCII or JIS X 0201 (a subset of Shift-JIS). Padding Is usually performed with 0x20 (ASCII space). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x34 | 0x7 | Reserved | This field is unused by Nintendo, but saw accidental usage in the Tommy Thunder prototype ROM, where the first 4 bytes are set to 0x20. This is possibly due to the developers thinking that the game title field was 0x18 bytes rather than 0x14. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x3B | 0x4 | Game Code | The game code is 4 ASCII characters that are split up into 3 parts - the category code, the unique code and the destination code.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x3F | 0x1 | ROM Version | This byte is used to identify the version of the game. Normally the value is 0 for the first version, 1 for the second and so on. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x40 | 0xFC0 | IPL3 | This area contains the IPL3 boot code. Each ROM ships its own IPL3 that is meant to work together with the CIC installed in the ROM. Before running IPL3, IPL2 checks its checksum also using a seed coming from the CIC, which binds each IPL3 code to a specific version of CIC. |
Advanced Homebrew ROM Header
The Advanced Homebrew ROM Header format is a convention that has been agreed upon in the N64 homebrew community to add additional information in the header, using unused fields. The goal of this convention is to let homebrew ROMs declare the correct saveype and controllers that are expected to play the game. The format has been introduced by the EverDrive 64 flashcard and has been later enhanced by the homebrew community.
This is useful because emulators normally work using a game database which matches games using checksum to find out which savetype and controllers are expected by the game, to help gamers play the game. For instance, when a N64 emulator detects that a Perfect Dark ROM is loaded, it will automatically emulate a 16Kb EEPROM to save the game (that was the original support present in the physical cartridge for the original game), and will possibly also emulate a preinstalled Transfer PAK, as the accessory can be used with the game. This is done purely by matching the Perfect Dark ROM checksum in a database, so a homebrew game, which would probably not be present in game databases, would suffer from non-working saves and possibly wouldn't be able to use special controllers or accessories.
Instead, by using the Advanced Hombrew ROM Header, emulators can automatically configure the required emulated hardware as expected without having to keep any additional database, by simply decoding specific fields of the ROM header. In addition to emulators, also development flashcarts and loaders can use this format to automatically configure the correct savetype when the ROM is loaded.
Homebrew ROM Header special flags
Offset | Bytes | Name | Description | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x34 | 1 | Controller 1 | This byte contains the suggested / expected controller hardware that should be attached to port 1.
Values 0x01-0x7F indicate a standard N64 controller, possibly with some installed pak. Values 0x80-0xFE indicate a different kind of controller.
| ||||||||||||||||||||
0x35 | 1 | Controller 2 | This byte contains the suggested / expected controller hardware that should be attached to port 2. See byte 0x34 for more information. | ||||||||||||||||||||
0x36 | 1 | Controller 3 | This byte contains the suggested / expected controller hardware that should be attached to port 3. See byte 0x34 for more information. | ||||||||||||||||||||
0x37 | 1 | Controller 4 | This byte contains the suggested / expected controller hardware that should be attached to port 4. See byte 0x34 for more information. | ||||||||||||||||||||
0x3C | 2 | Game ID | This must contain the ASCII characters "ED". It is used as an ID to identify that the Advanced Homebrew ROM header format Is being used by this ROM. | ||||||||||||||||||||
0x3F | 1 | Savetype | This byte mostly contains information on the savetype. It must be decoded as a bitfield:
|
Support by emulators
Emulators not listed here do not support the advanced homebrew ROM header format.
Emulator | Savetype | Controllers |
---|---|---|
Ares | Yes | Yes |
cen64 | Yes | No |
Parallel Launcher | Yes | Yes |
mupen64plus (core) | Yes | No |
Rosalie's Mupen GUI | Yes | No |
Support by flashcarts
Support by flashcarts can vary depending on the USB loader being used and/or the flashcart operating system. Notice that flashcarts can emulate a specific savetype but have nothing to do with controllers, so the maximum expected support is related to savetype.
Flashcart | Loader | Current support |
---|---|---|
64drive | 64drive official C tool | None |
64drive menu | None | |
g64drive | Savetype supported | |
UNFLoader | Savetype supported | |
Everdrive 64 | Menu | Savetype supported |
ed64 | None | |
UNFLoader | Savetype supported | |
SummerCart64 | N64FlashcartMenu | Savetype supported |
sc64deployer | Savetype supported | |
UNFLoader | Savetype supported |