From NES Hacker Wiki
Revision as of 16:11, 28 December 2018 by TheAlmightyGuru (talk | contribs) (Multiple-Byte, Hex Value)
Jump to: navigation, search

This guide is to make you better acquainted with the way various games store variables in the NES.

Remember that most games store all of their memory in the basic RAM of the NES, which is located from 0x0000 to 0x07FF.

Single-Byte, Hex Value

Variables of this type are stored as a hex value in a single byte of memory. This is the native way the CPU handles values, so it is the easiest way, and by far the most commonly used in games. No special code is needed to perform math on variable stored in this way. Each byte can hold 256 discrete values, and they are displayed in a hex editor as 0x00-0xFF.

In The Legend of Zelda, Link's horizontal position on the screen is stored as a single byte hex value in memory address 0x0070. If you load up the game and move Link horizontally across the screen, you'll see this number grow and shrink as he moves.

As long as the value never needs to be displayed to the player, this is the best way to handle such values. However, if the player needs to see the value in number form, it must first be converted to decimal, which takes extra CPU time. A good example of this in action is Link's rupees in The Legend of Zelda. They are stored in memory as a hex value, but they are displayed to the player in decimal form. If you load The Legend of Zelda and start a new game, you can adjust the number of rupees (memory address 0x066D) using a hex editor and see the value update immediately on the screen. For example, type "01" into the address, and you'll suddenly have 1 rupee. Type "0A" (the hex value for 10), and you'll have 10 rupees. Type "FF" (the hex value for 255) and you'll have a full compliment of 255 rupees. The game does this by running a conversion from hex to decimal every time the value updates.

Single-Byte, Decimal Value

Variables of this type are stored as a decimal value in a single byte of memory. Because the NES CPU doesn't have native decimal mode, programmers must write a special routine to perform decimal math on these values every time they add or subtract on them. Because of this extra overhead, programmers usually only type of variable for values that will be seen by the player.

For example, the player's scores in Tecmo Super Bowl are stored as decimal values in memory. P1 is at address 0x0399 and P2 is at address 0x039E. When a player scores 14 points, the value won't read 0x0E (the hex value for 14), but rather 14 in decimal. This way, the value won't need to be converted when it's displayed on the screen. So, if you want to have a score of 99 points, you need only type "99" into a hex editor, not 0x63, the hex equivalent.

The programmers handled this by writing special addition routines when a player scores. Instead of simply using the native ADC opcode, which adds in hex, the game has to convert the decimal value into hex, then add the points, and then convert it back to decimal every time a score changes.

The CPU used by the NES originally had decimal mode, but Nintendo engineers removed it so it wouldn't have to pay royalties on the patent. If decimal mode remained, programmers wouldn't have had to write all the extra code to convert between hex and decimal and add and subtract decimal values.

Multiple-Byte, Hex Value

When a game needs a variable to store more than 256 values, two or more bytes must be used. But, since the CPU of the NES only supports 8-bit, any variables that are two or more bytes must be handled manually through code. The most common way of handling this is to have each byte be a multiple of 256. Using this method, one byte can store 256 values, two bytes can store 65536 values, three bytes can store 16,777,216 values, four bytes can store 4,294,967,296, and so forth. Most games that use this method only use two bytes.

Because 16-bit (and higher) math is not a built-in operation of the CPU, every game has the option of handling it differently. Sometimes the multiple bytes go left-to-right, sometimes right-to-left, but they're almost always next to each other.

Dragon Warrior is a good example of 2-byte hex values. The player's gold pieces are stored in two addresses, 0x00BC and 0x00BD. But any value below 256 can be stored in a single byte as normal. For example, 200 GP would look like this in a hex editor:

C8 00

Because 0xC8 is equal to 200 in decimal. But, if the player gets another 100 gold pieces, bringing the total up to 300, we have surpassed the 256 values that a single byte can hold, so the second byte must be employed. 300 GP is represented as:

2C 01

The first byte is the ×1 multiplier and second is the ×256 multiplier. So, the variable's value is determined by adding together the second byte ×256 and the first byte ×1. To better illustrate this, I'll first convert the hex into decimal:

0x2C = 44
0x01 = 1

Perform the multiplication:

44 × 1   = 44
1  × 256 = 256

And then we add the two:

+ 44

Since the player's gold pieces are displayed to the player, the game must convert the hex value into decimal before it is displayed on the screen, but the variable itself is stored in memory as hex. Because two bytes can only hold 65,536 unique values, the player will max out gold at 65,535. In fact, if you wanted the max gold, simply type "FF FF" into memory addresses 0x0BC-0x0BD, and you will have 65,535.

The actual addition needed for 2-byte variables must be programmed custom for each game, and different routines are often created for adding a 1-byte variable to a 2-byte variable and adding together two 2-byte variables.

You can determine the necessary hex value from a decimal value by performing a similar equation. Lets say you wanted to have 20,000 GP. First, divide 20,000 by 256:

20,000 / 256 = 78.125

Next, floor the quotient. In mathematics, "floor" means to discard any decimal values without rounding. In mathematical notation, flooring is represented with these special brackets, ⌊⌋:

⌊78.125⌋ = 78

This is the number that will have to go into the second byte. If there was no decimal value, the first byte will be 0, but if you had to floor off the decimal, you'll need to do an extra step to determine the first byte. Multiply the floored value by 256. This will give you a number that is less than 256 away from your original number:

256 * 78 = 19,968

Finally, subtract your original number from that product:

20,000 - 19,968 = 32

This is the number that will go into the first byte. However, you must first convert them into hex:

78 = 0x4E
32 = 0x20

Thus, by typing "20 4E" into the memory addresses 0x0BC-0x0BD, your player will have 20,000 GP.

Multiple-Byte, Decimal Values

Bit Flags