We've just updated MediaWiki and its underlying software. If anything doesn't look or work quite right, please mention it to us. --RanAS

PPU Registers

From SnesLab
Revision as of 05:10, 19 July 2019 by Vitor Vilela (talk | contribs) (Vitor Vilela moved page SNES's PPU Registers to PPU Registers)
Jump to: navigation, search

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.

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.