...just another vision... Studios Return to Challenge Games

This is a road map to the NES' address space ($0000-$FFFF).
First, here is a simplified map:
ADDRESSFUNCTION
$0000-$07FFRAM
$2000-$2007PPU communication registers
$4000-$4013pAPU communication registers
$4014-$4017Miscellaneous registers
$5000-$5FFFMMC5 Expansion RAM and registers
$6000-$7FFFSave RAM
$8000-$FFFFROM
$FFF9-$FFFF(6502 vectors)

And now, the hitchhiker's guide to the NES galaxy:

ADDRESSUSAGEFUNCTION
$0000-$07FFRead/ Write RAM
You are given a mere Two Kilobytes of RAM to work with (and an eighth of it is stack space). Use it well!
$0800-$1FFFn/a Shadow RAM
Every $400 bytes of this is simply a duplicate of $0000-$07FF. Yes, it's stupid.
The following are used to communicate with the PPU
$2000Write PPU Control Register #1
This is used to control PPU Functions. It is a bitwise register.
NMSbsATT
||||||++--- Name Table Address
||||||      (I'm not sure what this represents)
|||||+----- PPU Address Increment (on read/write of $2007)
|||||       0 = Increment by 1
|||||       1 = Increment by $20 (32)
||||+------ Sprite Pattern Table Address
||||        0 = VRAM $0000 ("left" half)
||||        1 = VRAM $1000 ("right" half)
|||+------- Background Pattern Table Address
|||         0 = VRAM $0000 ("left" half)
|||         1 = VRAM $1000 ("right" half)
||+-------- Sprite Size
||          0 = 8x8 pixels
||          1 = 8x16 pixels
|+--------- (unused; leave it at 0)
+---------- Execute NMI on VBlank
            0 = Disabled
            1 = Enabled
$2001Write PPU Control Register #2
This is used to control display settings
CCCSBsbT
|||||||+--- Colour mode
|||||||     0 = Colour (normal palette)
|||||||     1 = Monochrome (all palette entries mapped to $x0)
||||||+---- Background Clipping
||||||      0 = BG not displayed in left-/right-hand 8-pixel column
||||||      1 = No clipping
|||||+----- Sprite Clipping
|||||       0 = (as above, but for Sprites)
|||||       1 = No clipping
||||+------ Background visibility
||||        0 = Background not displayed (all black)
||||        1 = Background visible
|||+------- Sprite visibility
|||         0 = Sprites invisible
|||         1 = Sprites visible
+++-------- CMYK colour mask
            000 = do not alter colours
            001 = darken Magenta (make palette Greener)
            010 = darken Yellow (make palette Bluer)
            100 = darken Cyan (make palette Redder)
Notes:
  • The colour mask settings can be combined, but doing so will make the display even darker. If you set all three, the display will lose 75% of it's brightness.
  • Clipping is useful for masking glitches that occur on the edges of the screen. BG Clipping can be used to mask tile changes when Scrolling horizontally (see Ultima: Exodus to see what I mean); Sprite Clipping can be used so Sprites that move off-screen (left/right) will disappear normally (without it, they will just suddenly blink out of existence and reappear on the other side of the screen).
$2002Read PPU Status Register
This register reports the PPU Status
V08W....
|||+------- VRAM Write Flag
|||         0 = Writes to VRAM Respected
|||         1 = Writes to VRAM Ignored
||+-------- Scanline sprite count
||          0 = Eight or fewer Sprites are on the current scanline
||          1 = More than eight Sprites on the current scanline
|+--------- Sprite #0 Occurence
|           0 = Sprite #0 not found
|           1 = PPU Has hit Sprite #0
|           (see below)
+---------- Vertical Blanking Impulse (VBlank) status
            0 = Not occuring
            1 = PPU is in VBlank
Notes:
  • The highest bit is reset to 0 when you read $2002.
  • The function of the Sprite #0 hit flag is not completely understood. Here is the generally accepted description:
    When the PPU encounters a non-transparent sprite pixel that overlaps a non-transparent background pixel, this flag is set.
  • I'm not sure what the VRAM Write Flag means.
  • $2005 and $2006 are reset when you read $2002, so the next write to $2005 will be Horizontal, and the next write to $2006 will be the High byte.
$2003Write Sprite-RAM Address Register
Use this to set the address to start I/O for Sprite-RAM.
$2004Write Sprite-RAM I/O Register
This is where you write data to Sprite-RAM (set the starting address via $2003).
I do not know if you can also read data through this. I've never needed to.
$2005Write x2 VRAM Address Register #1
This controls PPU Addressing lines. It is generally used (by you) to pan/scroll the background display. The first write is Horizontal, and the second write is Vertical. I do not completely understand this register, so I will leave it at that. For more information, consult the NESDEV site.
$2006Write x2 VRAM Address Register #2
This is used to specify the 16-bit address in VRAM to access via $2007. It also controls PPU Addressing Lines, so unless you know what you're doing, you should first wait for VBlank before setting this.

You write the high byte FIRST, and then the low byte.
$2007Read/ Write VRAM I/O Register
This is where you will access VRAM (read/write). The VRAM address is auto-incremented (by either 1 or 32, set via $2000) upon each access.

Notes:
  • When reading from VRAM, the correct data will be returned starting with the second read. Therefore, when reading from VRAM, you should conduct a "throw-away" read first, before performing the main read.
  • This does not apply to VRAM $3F00-$3F1F (which is where the palettes are located), and does not apply to writes to VRAM.
$2008-$3FFFn/a Shadow PPU Registers
Every eight bytes is a mirror of $2000-$2007. Yes, it's stupid.
The following are pAPU (audio) registers. This information taken nearly verbatim from Y0sHi's document.
$4000WritePulse #1 Control Register
$4001WritePulse #1 Ramp Control Register
$4002WritePulse #1 Fine-Tune Register
$4003WritePulse #1 Coarse-Tune Register
$4004WritePulse #2 Control Register
$4005WritePulse #2 Ramp Control Register
$4006WritePulse #2 Fine-Tune Register
$4007WritePulse #2 Coarse-Tune Register
$4008WriteTriangle Control Register #1
$4009"?"Triangle Control Register #2
$400AWriteTriangle Frequency Register #1
$400BWriteTriangle Frequency Register #2
$400CWriteNoise Control Register #1
$400D???"Unused (???)"
$400EWriteNoise Frequency Register #1
$400FWriteNoise Frequency Register #2
$4010WriteDelta Modulation Control Register
$4011WriteDelta Modulation "D/A" Register
$4012WriteDelta Modulation Address Register
$4013WriteDelta Modulation Data Length Register
The following are "miscellaneous" registers.
$4014Write Sprite-RAM DMA Register
This is a very handy way to deal with Sprites. When you write a value here, 256 bytes are automagically transferred to Sprite-RAM. The address read from is $xx00-$xxFF, where xx is the value written.
You can make the most of this by setting aside $100 bytes of RAM (such as $0700-$07FF) as a Sprite DMA table. You can then modify the data there instead of manually via $2003/$2004. When everything's ready, just write the page number (in this case, $07) to $4014. Einfach! (Nothing to it!)
$4015Read/ Write pAPU Master Control
On read:
..IDNT21
  |||||+--- Pulse #1 ---------+
  ||||+---- Pulse #2          |
  |||+----- Triangle          +-- 0 = Not in use
  ||+------ Noise             |   1 = In use
  |+------- Delta Modulation -+
  +-------- "Vertical Clock Signal IRQ Avaliability"
            0 = One frame occuring, thus IRQ cannot occur
            1 = One frame is being interrupted via IRQ
On write:
...DNT21
   ||||+--- Pulse #1 ---------+
   |||+---- Pulse #2          |
   ||+----- Triangle          +-- 0 = Disabled
   |+------ Noise             |   1 = Enabled
   +------- Delta Modulation -+
(This information taken nearly verbatim from Y0sHi's NESTECH.TXT document)
$4016Read/ Write Joystick #1
On read:
...TS..D
   ||  +--- Joystick data (see below)
   |+------ Zapper Sprite Detection
   |        0 = Sprite not in position
   |        1 = Sprite in front of cross-hair
   +------- Zapper Trigger
            0 = Pulled
            1 = Released
On write:
.......S
       +--- Joystick strobe
            0 = Clear strobe
            1 = Reset strobe
Notes:
  • You must "strobe" the joystick before attempting to read the data. This is done by resetting, then clearing the strobe. You do this by writing $01 and then $00 to $4016. This resets the joystick hardware and it will begin sending out the button-press status one bit at a time.
  • Once you've strobed the joystick, you can start reading the data. On each read, the joystick will return the status one button at a time in the lowest bit of $4016. On each successive read, it will return the status of the successive buttons. The button status is returned in this order:
    1. A Button
    2. B Button
    3. Select
    4. Start
    5. Up arrow
    6. Down arrow
    7. Left arrow
    8. Right arrow
Sample joystick-reading routine:
a.read = $00
b.read = $01
.
.
.
right.read = $07

readjoy:    LDX #$01
            STX $4016
            DEX
            STX $4016
            LDY #$00
do_readjoy: LDX $4016
            STX a.read,Y
            INY
            CPY #$08
            BNE do_readjoy
            RTS
$4017Read/ Write Joystick #2
This works essentially the same as for Joystick #1.
$5000-$5FFFRead/ Write MMC5 Registers and Expansion RAM
This section of memory is only used with the Nintendo MMC5 memory mapper.
$6000-$7FFFRead/ Write Save-RAM
This is where saved games live and frolick and make merry, assuming the cardridge supports it. If it does, you may as well use this as an extension to the regular work RAM. I mean, you're born with 2K, and here's 8K of space (minus whatever you use for the actual saved games) right here!
$8000-$FFFFRead ROM
This is the area in which the game ROM is loaded.

Notes:
  • If the game uses a mapper (ie., if there's more than 32K of PRG-ROM), it varies which sections are mapped into this space. For games with 32K PRG-ROM space, then simply all of it is mapped into this space. For games with only 16K of PRG-ROM space, it is mapped to both the $8000-$BFFF and $C000-$FFFF regions.
  • For all mappers except MMC5, you communicate with it by writing values to mapper registers mapped in the $8000-$FFFF. The actual registers, their locations, and their functions vary from mapper to mapper, of course.
  • Writing values to the $8000-$FFFF area does not affect the ROM. After all, it's Read-Only Memory, right?
The following are 6502 Vectors.
$FFFA-$FFFBn/a Non-Maskable Interrupt (NMI) Vector
This contains the address to jump to when the PPU generates a Vertical Blanking Impulse.

Notes:
  • You can control whether or not the PPU generates NMI on VBlank, via the highest bit of $2000
  • You cannot mask an NMI by executing the SEI instruction, thus the name "Non-maskable".
$FFFC-$FFFD Reset Vector
This contains the address to jump to when the 6502 hardware is initialized. This occurs when you first turn on the power, or when you press the Reset button. In other words, this contains the address of the "start" of the program.
$FFFE-$FFFF Interrupt Request / CPU Break (IRQ/BRK) Vector
This contains the address to jump to when a hardware interrupt (IRQ) or software interrupt (BRK) is executed.

The PPU has it's own 16K area, known as VRAM. It is accessed by writing the address (high byte first) to $2006, then accessing the data in question via $2007. So, here is a PPU Memory map:

ADDRESSFUNCTION
$0000-$1FFF Pattern Tables
This is where the graphic tiles are stored. You can view these in NESticle by pressing F2.

Notes:
  • Bits 3 and 4 (---XX---) of $2000 control which half of the Pattern Table is used by the Sprites and the Background. For the tile index numbers used for Sprites/BG, they refer to that half of the pattern tables. You can map both the Sprites and the Background to the same half (such as the controls screen of Chris Covell's Solar Wars).
  • For CHR-RAM based games, this is where you access CHR-RAM.
  • Each tile is $10 bytes in size; each row of 16 tiles is $100 bytes.
$2000-$23BF Name Table #0 (lower-left)
The Name Table data controls which tiles go where in the Background. It starts with the upper-left tile, and each successive byte represents the next tile. Each byte represents the tile number in the pattern tables ($0xx0 or $1xx0 depending on PPU Control Register #1 ($2000)).

The background (per name table) is 32x30 tiles in size.
$23C0-$23FF Attribute Table #0 (lower-left)
The Attribute Table data controls which set of colours is used where. Each Attr. Table byte covers a 4x4-tile area, and these areas are laid out left-to-right, top-to-bottom. Each such section is divided into 2x2-tile areas, which is the finest resolution of Attribute Table data. Now, for each byte:
44332211
||||||++--- Upper-left 2x2-tile section
||||++----- Upper-right 2x2-tile section
||++------- Lower-left 2x2-tile section
++--------- Lower-right 2x2-tile section
For each:
00 = First set of four colours (of the BG palette)
01 = Second set of four colours
10 = Third set of four colours
11 = Fourth set of four colours
Notes:
  • There are only tiles defined for a 32x30-tile area (in the Name Table), but the Attribute Table data is treated as covering a complete 32x32-tile area. It's a bit confusing, perhaps a diagram may help:

    Each light/dark area represents a different tile; the differently-coloured areas represent each 2x2-tile part of the Attribute Tables.
  • The same phenomenon occurs at the bottom of the screen.
$2400-$27BFName Table #1 (lower-right)
$27C0-$27FFAttribute Table #1 (lower-right)
$2800-$2BBFName Table #2 (upper-left)
$2BC0-$2BFFAttribute Table #2 (upper-left)
$2C00-$2FBFName Table #3 (upper-right)
$2FC0-$2FFFAttribute Table #3 (upper-right)
$3000-$3EFFUnknown
This is probably mirrored from something. Whoever laid out the NES memory map sure likes to waste space with "mirroring", doesn't he?
$3F00-$3F0F Background Colour-Index Array (ie. Palette)
Each byte written here is not an absolute colour value, but rather an offset in the colour index used by the PPU to draw the screen. This image will serve as a very valuable reference:

$0F is generally used to represent the colour Black.
Feel free to download and save that image. Right-click on it and select "Save Image As...".

Notes:
  • $3F00 controls the screen background colour. This colour is mirrored every four bytes in the $3F00-$3F1C range. (However, if (via PPU Control Register #2 ($2001)) you tell the PPU not to display the background, it will still always display Black, regardless of the palette BG colour).
  • You can read the palette data from VRAM without having to conduct a "throw-away" read. I think it has something to do with where the palette data is physically stored in relation to the PPU. Regardless of why, it just IS. I remember that there's a document at the NESDEV site explaining why, if you're interested.
$3F10-$3F1F Sprite Colour-Index Array (ie. Palette)
This is the same as the Background palette, except it's used for Sprites.

Notes:
  • The background colour ($3F00) does not affect sprite display at all; colour #0 of any Sprite tile is simply not drawn at all (it is transparent).
$3F20-$3FFF Shadow Palette
Every $20 bytes of this is simply a mirror of $3F00-$3F1F.
$4000-$FFFF Shadow VRAM
Every $4000 bytes of this is simply a mirror of $0000-$3FFF.

Return to JAVS NES Development page