Kid Icarus Revealed: 2-2

An introduction to ROM hacking and NES architecture.

Understanding VRAM

Before I tell you how we can do some magic in the title screen, you need to understand how VRAM works, and what it is used for. I’ll give a general overview, and experienced people may want to skip this section.

You already know that all graphics displayed on screen must be directly accessible by the PPU (picture processor). This is what the VRAM is used for. Everything loaded in this memory can be read or written by the PPU with no external help. VRAM it’s not a different kind of memory, It’s exactly the same as the main RAM. The difference between one and another is the use you make of it in the system. While main RAM is controlled by the CPU, VRAM is controlled by the PPU. If the CPU wants to access the VRAM, it has to tell the PPU to do it. This is done using what it’s called ports.

There’s an onboard 2Kb VRAM chip, used for Name Tables, and another chip placed in cartridges, to store Pattern Tables, which holds the graphics. If you don’t know very well what these tables are, read the first part of this article, especially section “Tiles”. I’ll talk a bit more on Name Tables later on, for those who are not familiarized with NES architecture.

Game cartridges usually have a CHR-ROM, where all graphics used in the game are stored, and like any other ROM, cannot be written. The PPU can only access 8Kb of CHR-ROM, so it can also be mapped, as it happens with the PGR-ROM (the ROM where the game code is stored), to allow the use of much larger CHR-ROMs, and only accessing to an area of 8Kb at a time.

But Kid Icarus cartridge has an 8Kb CHR-RAM instead of a CHR-ROM. It has the same purpose, but it is writeable, and it’s empty when the game starts. Both, code and graphics are stored in a single ROM. The game transfers the patterns needed from this ROM to CHR-RAM.

Like the CPU has a memory map, the PPU also has a memory map.

PPU memory map

Memory ranges
$3F10-$3F1F . Sprite palette
$3F00-$3F0F . Background palette
$2000-$3EFF . Name tables
$1000-$1FFF . Pattern table 1
$0000-$0FFF . Pattern table 0

The pattern tables are 4Kb each, and are always stored in a CHR-ROM or CHR-RAM in the cartridge.
The Name Tables are stored in the system’s VRAM. Really, there’s only space for 2 Name Tables, which are 1Kb each, but the programmer sees 4 Name Tables. What you need to know is that only 2 of them really exist. The other ones are just mirrors. But you can choose at any time which one is mirror of which one.

This will be clear with an example.

The first and third world use horizontal mirroring. The content of the Name Tables at the beginning of the game is:

Keep in mind that only Name Tables 0 and 2 exist.
Table 1 is a mirror of 0.
Table 3 is a mirror of 2.

This arrangement lets do a simple scroll upward. I recommend you to open up a NES emulator with a name table viewer, and play a bit , going up, and looking how name tables work.

Second world uses a vertical mirroring instead.
Now table 2 is a mirror of table 0, and table 3 a mirror of table 1.
This arrangement makes an horizontal scrolling easier.

Ok, but how does a Name Table look like in terms of bytes? How is it stores in VRAM?
Name table 0 starts at PPU address $2000
Name table 1 starts at PPU address $2400
Name table 2 starts at PPU address $2800
Name table 3 starts at PPU address $2C00

Remember that if you are using horizontal mirroring, Name Table 0 and 1 are the same, so writing to $2000 is the same as writing to $2400. And so on.

A Name Table is a chunk of 960 bytes, each one representing one area of 8x8 pixels of the screen. If the first byte is a $43, then, pattern in position $43 of background Pattern Table is drawn at the top-left 8x8 pixels area. If a $12 comes next, then, pattern in position $12 of patter table is drawn to the right. An so on, until the screen is filled…

This is the least you must know to understand what comes next. But I encourage you to learn more about this, to get a good knowledge.

How we CAN do it

Again YES, it can be done. You ARE able to make the PUSH START BUTTON sentence longer, and also move it to a different position on screen. This required some ROM debugging and disassembling, to study how code loads and manages this data.

I’ll go to the conclusions for now, and explain later all the process, because it’s a little complicated as it is, for non experienced people. And even if you’re a master, you might find it interesting.

Look at the following image. There’s a table of data starting at ROM offset 0x628A (where the E7 marked in blue is), that specifies what will be loaded into the name tables, and thus, be shown on screen. Believe it or not, the 5 bytes marked in colour, will tell the game about PUSH START BUTTON sentence, and will let us do our magic. But does this, in a some weird way. Let’s see how.

Blue bytes E7 21 will be interpreted as a VRAM memory 0x21E7, which corresponds to name table 0, and determine the screen position where it will be shown.

Green bytes E8 A2 will be interpreted as a CPU memory address 0xA2E8, which falls inside mapped ROM bank at 0x8000, which is bank 1 at the moment this code is used, as it should be obvious. Why?

We know ROM is divided in chunks of 16Kb that can be mapped for the CPU to be able to access it.
First ROM chunk will be from 0x0000 to 0x3FFF. This will be bank 0.
Second ROM chunk will be from 0x4000 to 0x7FFF. This will be bank 1.
And so on.

If you recall, these coloured bytes are stored in ROM address 0x628A. It falls in bank 1, which starts at 0x4000. And thus, 0x628A has an offset of 0x228A inside bank 1.

Now, 0xA2E8 address, specified by these green bytes, point exactly to the beginning of PUSH string (in yellow), where we were working before.
If you don’t see this, read the following.
Remember how to swing from ROM addresses to REAL addresses. I already explained in the first part of this document, so now I’ll only refresh the main ideas.

0xA2E8 is a ROM offset. We know ROM is divided in chunks of 16Kb that can be mapped in real address 0x8000, for the CPU to be able to access it. 0xA2E8 has a bank offset of 0x22E8. Because we know it is bank 1 which is mapped at this time, this address corresponds to an offset of 0x22E8 inside bank 1. As bank 1 starts at ROM address 0x4000, final address is 0x62E8.

Now add 0x10, because all NES ROM files stored in computer have a 0x10 added header, for emulators to know which mapper the cartridge uses, etc. This leads us to 0x62F8, where PUSH START BUTTON starts.
So, the green bytes tells the game where the PUSH STRART BUTTON sentence is stored inside the ROM. So it can be move elsewhere if we want to, with the only restriction, that it must be in bank 1, that is, inside 4000-7FFF, or the CPU won’t be able to access it (remember that bank 1 is mapped at this time, and banks not mapped are not accessible). Of course, bank 7 is always mapped at C000-FFFF, and will do the job too.

Red byte 12 will be interpreted as the number of bytes that will be transferred. Realize that 12 is eighteen in decimal form, which is the maximum number of characters that we were allow to use for the PUSH START BUTTON sentence.

Great. Isn’t it? We can now change the maximum number of characters, and the screen position, by changing the VRAM destination address (which requires knowledge about VRAM memory map).

To illustrate a sentence enlargement, see this:

Below you can see the changes made. Red byte is increased in 3, to accommodate 3 more characters at the end (marked in blue).

Remember I said that there was a table at 0x628A, starting with this 5 bytes (first, marked in black) that told us about PUSH START BUTTON sentence?

There’re much more 5-byte-groups in this table. After the red byte, each 5 bytes do the same we’ve just seen, but for other elements in the title screen. They tell the game to copy some data, of a certain size, to name table in VRAM. These groups of 5 bytes make up the whole table. And this table determines the arrangement of the title screen.

Let’s find and change the year under the game title. Ok?
1986 is coded a 01 09 08 06, because those are its pattern table positions.
We find them a little bit under the place we’ve just been (marked in yellow)

Marked in green is the symbol of copyright, and in red, string Nintendo.
So, we found the title screen element at offset 0x64E0:

Imagine this game is so great, that it was programmed by gods in year 10352 B.C, while the pyramids of Gizeh were built, and kept in secret until 1986, when Gumpei Yokoi, a great master, decided to make a conversion for Famicom/NES, and show the world pure true power. Just joking. But let’s see how it’d look!

Just change,
0A 01 09 08 06 20 21 22 23 24 25 26
Into,
12 01 00 03 05 02 12 65 0A 12 12 12

(I had to use © instead of C because there’s no C loaded in the pattern table, as you see)

Now, we can find which of the 5-bytes-group in the table refers to this element, so we can change its position on screen, and its length. How?

We know the element is stored in 0x64E0, so we need to find a 5-bytes-group which CPU address pointer points to this address. First, we need to translate it to a real address.

0x64E0 – 0x10 (from the file header) = 0x64D0
0x64D0 is at bank 1 offset 0x24D0
As bank 1 is mapped at real address 0x8000
0x24D0 offset in real address 0x8000 leads us to a final address of 0xA4D0

Because pointers in 5-bytes-groups are inverted, we need to search for D0 A4. And we’ve got it, shaded in grey below.

This element will be copied to VRAM memory address 0x2A89 (which falls in name table 2), took from real address 0xA4D0 (which corresponds to ROM offset 0x64E0 as we’ve just seen, where the element is stored), and it will $C in size (that is, twelve, in decimal base).

VRAM memory address 0x2A89 is a “name table 2” address. As you should know, this name table starts at 0x2800 of VRAM memory map. So, the element is copied at offset $289 of this table. $20 bytes are required for a complete row of 32 patterns in screen. So, with basic maths, we can get that “C 1986 Nintendo” is displayed at screen position (8, 14)

Knowing this, let’s move PUSH START BUTTON sentence below it.

It’s enough to move the element PUSH START BUTTON from its original position, to VRAM address 0x2AA5, in its 5-bytes-group.

I won’t give more details, as the reader should try its own changes, if he wants to, and I gave a lot of material for him/her to try to understand this.

You can now change the whole title screen :-)

David Senabre Albujer. 2007