PPU Registers
PPU (also knowing as Picture Processing Unit) is a co-processor of SNES used to send video data to TV. It includes a lot of registers that can be used to do graphic effects.
Almost all the text of this document was extracted from Anomie's Register Doc "regs.txt"
Registers $2100~$21FF
$2100: Screen Display
- It can be read/written at any time: Yes
- It can be read/written during H-Blank: Yes
- It can be read/written during V-Blank: Yes
- It can be read/written during force-blank: Yes
- Register is writable for an effect: Yes
- Register is readable for a value or effect (i.e. not open bus): Yes
- Read/Write style: Byte
Format:
x---bbbb
x = Force blank on when set. bbbb = Screen brightness, F=max, 0="off".
Note that force blank CAN be disabled mid-scanline. However, this can result in glitched graphics on that scanline, as the internal rendering buffers will not have been updated during force blank. Current theory is that BGs will be glitched for a few tiles (depending on how far in advance the PPU operates), and OBJ will be glitched for the entire scanline. Also, writing this register on the first line of V-Blank (225 or 240, depending on overscan) when force blank is currently active causes the OAM Address Reset to occur.
$2101: Object Size and Chr Address
- It can be read/written at any time: No
- It can be read/written during H-Blank: ???
- It can be read/written during V-Blank: Yes
- It can be read/written during force-blank: Yes
- Register is writable for an effect: Yes
- Register is readable for a value or effect (i.e. not open bus): No
- Read/Write style: Byte
Format:
sssnnbbb
sss = Object size:
- 000 = 8x8 and 16x16 sprites
- 001 = 8x8 and 32x32 sprites
- 010 = 8x8 and 64x64 sprites
- 011 = 16x16 and 32x32 sprites
- 100 = 16x16 and 64x64 sprites
- 101 = 32x32 and 64x64 sprites
- 110 = 16x32 and 32x64 sprites ('undocumented')
- 111 = 16x32 and 32x32 sprites ('undocumented')
nn = Name Select
bbb = Name Base Select (Addr>>14)
- See the section "SPRITES" below for details.
$2102: OAM Address Low Byte and $2103: OAM Address High Byte and Object Priority
- It can be read/written at any time: No
- It can be read/written during H-Blank: ???
- It can be read/written during V-Blank: Yes
- It can be read/written during force-blank: Yes
- Register is writable for an effect: Yes
- Register is readable for a value or effect (i.e. not open bus): Yes
- Read/Write style: Low/High
Format:
p------b aaaaaaaa
p = Obj Priority activation bit
- When this bit is set, an Obj other than Sprite 0 may be given priority. See the section "SPRITES" below for details.
b aaaaaaaa = OAM address
- This can be thought of in two ways, depending on your conception of OAM. If you consider OAM as a 544-byte table, baaaaaaaa is the word address into that table. If you consider OAM to be a 512-byte table and a 32-byte table, b is the table selector and aaaaaaaa is the word address in the table. See the section "SPRITES" below for details.
- The internal OAM address is invalidated when scanlines are being rendered. This invalidation is deterministic, but we do not know how it is determined. Thus, the last value written to these registers is reloaded into the internal OAM address at the beginning of V-Blank if that occurs outside of a force-blank period. This is known as 'OAM reset'. 'OAM reset' also occurs on certain writes to $2100.
- Writing to either $2102 or $2103 resets the entire internal OAM Address to the values last written to this register. E.g., if you set $104 to this register, write 4 bytes, then write $1 to $2103, the internal OAM address will point to word 4, not word 6.
$2104: Data for OAM Write
- It can be read/written at any time: No
- It can be read/written during H-Blank: No
- It can be read/written during V-Blank: Yes
- It can be read/written during force-blank: Yes
- Register is writable for an effect: Yes
- Register is readable for a value or effect (i.e. not open bus): Yes
- Read/Write style: Byte
Format:
dddddddd
- Note that OAM writes are done in an odd manner, in particular the low table of OAM is not affected until the high byte of a word is written (however, the high table is affected immediately). Thus, if you set the address, then alternate writes and reads, OAM will never be affected until you reach the high table!
- Similarly, if you set the address to 0, then write 1, 2, read, then write 3, OAM will end up as "01 02 01 03", rather than "01 02 xx 03" as you might expect.
- Technically, this register CAN be written during H-blank (and probably mid-scanline as well). However, due to OAM address invalidation the actual OAM byte written will probably not be what you expect. Note that writing during force-blank will only work as expected if that force-blank was begun during V-Blank, or (probably) if $2102/3 have been reset during that force-blank period.
- See the section "SPRITES" below for details.
$2105: BG Mode and Character Size
- It can be read/written at any time: No
- It can be read/written during H-Blank: Yes
- It can be read/written during V-Blank: Yes
- It can be read/written during force-blank: Yes
- Register is writable for an effect: Yes
- Register is readable for a value or effect (i.e. not open bus): Yes
- Read/Write style: Byte
Format
DCBAemmm
A/B/C/D = BG character size for BG1/BG2/BG3/BG4
- If the bit is set, then the BG is made of 16x16 tiles. Otherwise, 8x8 tiles are used. However, note that Modes 5 and 6 always use 16-pixel wide tiles, and Mode 7 always uses 8x8 tiles. See the section "BACKGROUNDS" below for details.
mmm = BG Mode
e = Mode 1 BG3 priority bit
Mode | BG Depth 1 | BG Depth 2 | BG Depth 3 | BG Depth 4 | OPT | Priorities Front -> Back |
---|---|---|---|---|---|---|
0 | 2 | 2 | 2 | 2 | no | 3:AB 2:ab 1:CD 0:cd |
1 | 4 | 4 | 2 | 2 | no | 3:AB 2:ab 1:C 0:c / if e set: 3:C 2:AB 1:ab 0:c |
2 | 4 | 4 | yes | 3:A 2:B 1:a 0:b | ||
3 | 8 | 4 | no | 3:A 2:B 1:a 0:b | ||
4 | 8 | 2 | yes | 3:A 2:B 1:a 0:b | ||
5 | 4 | 2 | no | 3:A 2:B 1:a 0:b | ||
6 | 4 | yes | 3:A 1:a | |||
7 | 8 | no | 1:a | |||
7+EXTBG | 8 | 7 | no | 2:B 1:a 0:b |
- "OPT" means "Offset-per-tile mode". For the priorities, numbers mean sprites with that priority. Letters correspond to BGs (A=1, B=2, etc), with upper/lower case indicating tile priority 1/0. See the section "BACKGROUNDS" below for details.
- Mode 7's EXTBG mode allows you to enable BG2, which uses the same tilemap and character data as BG1 but interprets bit 7 of the pixel data as a priority bit. BG2 also has some oddness to do with some of the per-BG registers below. See the Mode 7 section under BACKGROUNDS for details.
$2106: Screen Pixelization
$2107: BG1 Tilemap Address and Size
$2108: BG2 Tilemap Address and Size
$2109: BG3 Tilemap Address and Size
$210A: BG4 Tilemap Address and Size
$210B: BG1&2 Tilemap Character Address
$210C: BG3&4 Tilemap Character Address
$210D: BG1 Horizontal Scroll / Mode 7 BG Horizontal Scroll
$210E: BG1 Vertical Scroll / Mode 7 BG Vertical Scroll
$210F: BG2 Horizontal Scroll
$2110: BG2 Vertical Scroll
$2111: BG3 Horizontal Scroll
$2112: BG3 Vertical Scroll
$2113: BG4 Horizontal Scroll
$2114: BG4 Vertical Scroll
$2115: Video Port Control
$2116: VRAM Address Low Byte
$2117: VRAM Address High Byte
$2118: VRAM Data Write Low Byte
$2119: VRAM Data Write High Byte
$211A: Mode 7 Settings
$211B: Mode 7 Matrix A
$211C: Mode 7 Matrix B
$211D: Mode 7 Matrix C
$211E: Mode 7 Matrix D
$211F: Mode 7 Center X
$2120: Mode 7 Center Y
$2121: CGRAM Address
$2122: CGRAM Data Write
$2123: Window Mask Settings for BG1 and BG2
$2124: Window Mask Settings for BG3 and BG4
$2125: Window Mask Settings for Objects and Color Window
$2126: Window 1 Left Position
$2127: Window 1 Right Position
$2128: Window 2 Left Position
$2129: Window 2 Right Position
$212A: Window Mask Logic for Backgrounds
$212B: Window Mask Logic for Objects and Color Window
$212C: Main Screen Designation
$212D: Subscreen Designation
$212E: Window Mask Designation for the Main Screen
$212F: Window Mask Designation for the Subscreen
$2130: Color Addition Select
$2131: Color Math Designation
$2132: Fixed Color Data
$2133: Screen Mode/Video Select
$2134: Multiplication Result Low Byte
$2135: Multiplication Result Middle Byte
$2136: Multiplication Result High Byte
$2137: Software Latch for the H/V Counter
$2138: Data for OAM Read
$2139: Vram Data Read Low Byte
$213A: Vram Data Read High Byte
$213B: CGRAM Data Read
$213C: Horizontal Scanline Location
$213D: Vertical Scanline Location
$213E: PPU Status Flag and Version
$213F: PPU Status Flag and Version
$2140: APU I/O Register 0
$2141: APU I/O Register 1
$2142: APU I/O Register 2
$2143: APU I/O Register 3
$2180: WRAM Data Read/Write
$2181: WRAM Address Low Byte
$2182: WRAM Address Middle Byte
$2183: WRAM Address High Byte
SPRITES
The SNES has 128 independant sprites. The sprite definitions are stored in Object Attribute Memory, or OAM.
OAM
OAM consists of 544 bytes, organized into a low table of 512 bytes and a high table of 32 bytes. Both tables are made up of 128 records. OAM is accessed by setting the word address in register $2102, the "table select" in bit 0 of $2103, then writing to $2104 or reading from $2138. Since the high table is only 32 bytes long, only the low 4 bits of $2102 are significant for indexing this table.
The internal OAM address is invalidated during the rendering of a scanline; this invalidation is deterministic, but we do not know how or when the value is determined. Current theory is that it is invalidated more-or-less continuously and has something to do with the current OAM address and possibly which sprites are on the current scanline. The internal OAM address is reloaded from $2102/3 at the beginning of V-Blank, if this occurs outside of a force-blank period. The reload also occurs on a 1->0 transition of $2100.7.
Each read/write increments the address by one byte (the internal address has 10 bits, with bit 9 selecting the table and bits 0-8 indexing). Reads simply read the current byte. Writes to the low table go into a word-sized buffer, which is written to the appropriate word of OAM when the high byte of the word is written. Thus, if alternating reads and writes occur such that the high byte of the word is always read instead of written, none of the writes will actually affect OAM. If the alternation happens such that the writes always occur to the high byte, not only the high bytes but whatever garbage is left in the low byte will be written as well!
Pictorally: Start OAM filled with all zeros. Write 1, read, read, Write 2, read, write 3 => OAM is 00 00 01 02 01 03, rather than 01 00 00 02 00 03 as you might expect.
Writes to the high table, on the other hand, work exactly as expected.
The record format for the low table is 4 bytes:
- byte OBJ*4+0: xxxxxxxx
- byte OBJ*4+1: yyyyyyyy
- byte OBJ*4+2: cccccccc
- byte OBJ*4+3: vhoopppN
The record format for the high table is 2 bits:
- bit 0/2/4/6 of byte OBJ/4: X
- bit 1/3/5/7 of byte OBJ/4: s
The values are:
- Xxxxxxxxx = X position of the sprite. Basically, consider this signed but see below.
- yyyyyyyy = Y position of the sprite. Values 0-239 are on-screen. -63 through -1 are "off the top", so the bottom part of the sprite comes in at the top of the screen. Note that this implies a really big sprite can go off the bottom and come back in the top.
- cccccccc = First tile of the sprite. See below for the calculation of the VRAM address. Note that this could also be considered as 'rrrrcccc' specifying the row and column of the tile in the 16x16 character table.
- N = Name table of the sprite. See below for the calculation of the VRAM address.
- ppp = Palette of the sprite. The first palette index is 128+ppp*16.
- oo = Sprite priority. See below for details.
- h/v = Horizontal/Veritcal flip flags. Note this flips the whole sprite, not just the individual tiles. However, the rectangular sprites are flipped vertically as if they were two square sprites (i.e. rows "01234567" flip to "32107654", not "76543210").
- s = Sprite size flag. See below for details.
The sprite size is controlled by bits 5-7 of $2101, and the Size bit of OAM. $2101 determines the two possible sizes for all sprites. If the OAM Size flag is 0, the sprite uses the smaller size, otherwise it uses the larger size.