Home Journal Contact Me Tools Comments

NES Zelda

Contents
Code List
RAM Contents[raw]
Bank 0 – Audio[raw]650216K90%
Bank 1[raw]650216K1%
Bank 2[raw]650216K1%
Bank 3[raw]650216K1%
Bank 4[raw]650216K1%
Bank 5[raw]650216K1%
Bank 6[raw]650216K1%
Bank 7 - Fixed[raw]650216K1%

Extracting the Code

December 7, 2009

It starts with the NES emulator file ZELDA.NES, which is 128K ROM data plus the 16 byte header that describes the cartridge hardware. Based on the header, Zelda turns out to be a simple configuration. There is only program ROM (no extra video data) organized into 8 16K banks. There is battery-backed RAM from 6000 to 7FFF. I wrote a java tool to extract the header and the individual ROM banks.

ROM banks are controlled by a custom memory-mapping chip called MMC1. The NES maps ROM to half (32K) of the address space from 8000 to FFFF. The MMC1 allows this area to be switched in as full 32K pages or two 16K pages with either the top or bottom page fixed.

Each of the 8 banks must be disassembled with a fixed origin. The 6502 branch instructions are relative but some instructions (like JSR) use an absolute two-byte address. I ran the 8 banks through the disassembler with an origin of 8000 to start with. The bottom of each bank is identical with a reset vector to BF50 (FF50 for bank 7).

The RESET vector sets up the MMC1 registers with 16K bank swapping (confirmed by the NES header). The top bank from C000 to FFFF is left fixed (bank 7). The other banks swap in and out of the lower half from 8000 to BFFF.

I ran the individual banks through the disassembler at their correct origin. Note that the disassembler I used identifies NES hardware registers in comments beside the code.

The game configures the ROM banks as:

8000 – BFFF 16K (Banks 0-6 swapped in and out as needed)
C000 – FFFF 16K (Bank 7 always)

The game starts with the RESET vector in bank 7. Thus the Legend of Zelda begins at FF50 in ROM bank 7.

December 10, 2009

I took a cursory glance through the code and copied the RESET vector comments through all banks. 128K of ROM is quite formidable. I expected to find a lot of data for graphics images, and there are lots of “???” opcodes (data areas). I didn’t expect to find the huge empty blocks of FF’s in all the banks. I estimate less than 1/4th of the 128K ROM is actual code. That’s a good thing!

I wrote a java tool to help me format data and blank areas more densely. This shrunk the size of the disassembly text considerably.

Image Quest

December 14, 2009

I did a little interactive-disassembly. Using the “patch” tool I filled data areas with a pattern of “AA” (alternating pixels) and looked for graphics changes in the emulator. When I got to Bank2 I bumped into the images of the letters and numbers. I wrote a tool to extract images in a text format and applied it to Bank2. There are all the letters, numbers, and lots of misc images like the treasure pictures used in the splash screen explanation.

There is a small section of code above the images at the beginning of Bank2. The code contains a block-copy algorithm to fill VRAM with the image data. There is also an “init” routine to copy the images as three separate chunks into VRAM. The init routine reads from a table of eighteen bytes at the very beginning of Bank2. I hope this structure is duplicated in other banks.

The massive block of images at the beginning of Bank2 is divided into two large and one smaller chunks:

807F – 877E    Treasure picture images
877F – 8E7E    Numbers, letters, punctuation, and symbols
8E7F – 8F5E    Misc pictures (??)

With a little more POKEing around I found the letters for the treasure-descriptions in the splash sequence. They are offset exactly as they appear in the 877F area with ‘0’ having a value of 0 and ‘A’ having a value of 10. The space character is 36 (one after ‘Z’).

Strings

December 22, 2009

Strings of text are not represented in ASCII. Now that I know the text-character mapping values I wrote a program to search through the binary for text. The program displays the starting address and the text itself (mapped to ASCII). There are some interesting strings in the game! Next step is to document those in the code.

Game Flow

December 25, 2009

Just a few instructions into the startup sequence the program makes it to E45B in bank 7. This is an endless loop where the main program spins for ever more. The game is driven by the NMI interrupt tick.

E45B: 4C 5B E4    JMP   $E45B         ; ENDLESS LOOP ... interrupt driven

Battery Backed RAM

December 26, 2009

At startup the program checks locations 6001 and 7FFF (battery backed ram). If the program does not find 5A in both, it clears a large chunk of the RAM.

Sound

December 29, 2009

The code in Bank 0 is filled with writes to the sound hardware. This bank seems to be exclusively sound. I will patch some of the data areas and hear the changes.

January 5, 2010

There are seven quick one-shot sound effects that can be played (one at a time) on the square-wave-1 channel. These include:

1 Anything bouncing off shield
2 Enemy death
3 ??
4 ??
5 Letters popping up when someone is talking
6 Picking a letter when entering a name
7 The near-death beep (played over and over)

http://computerarcheology.com/zelda/zelda0.txt.html#MiscSounds

Whenever one of these seven is requested it replaces any other effect being played with one exception. The near-heart beeping will NOT preempt an effect in play. The near-heart beeping is an ongoing effect that needs to run behind other effects.

It is possible (though rare) that two sound effects will be requested at the exact same time. In this case the table gives the priority. If (1) and (6) are requested at the same time, (1) will play. However, if (1) is requested and then a fraction of a second later (6) is requested then (6) will replace (1). The priority is only for this very rare simultaneous picking, and thus the special preemption logic hardcoded for the near-death beeping.

January 24, 2010

I’ve been working on the sound code on and off. I just haven’t been diligent in updating this journal.

Much thanks to James Vanderhyde for decoding two of the miscellaneous sound effects. He changed the “bouncing off shield” sound to the others and recognized Number3 is a wizzrobe magic attack and 4 is picking up a heart.

I have been focusing on the music data. I wrote a tool to decompose the music sequences into note values and beat counts.

January 18, 2011

Almost a year later. I did beat Zelda: Twilight Princess since last here. Time to continue here. I’ll take the next hobby session to reorient myself to the site.