https://snes.nesdev.org/w/api.php?action=feedcontributions&user=Fiskbit&feedformat=atomSNESdev Wiki - User contributions [en]2024-03-28T12:25:13ZUser contributionsMediaWiki 1.39.0https://snes.nesdev.org/w/index.php?title=PPU_registers&diff=1187PPU registers2024-02-08T22:12:13Z<p>Fiskbit: /* CGDATAREAD - CGRAM data read ($213B read twice) */ Formatting.</p>
<hr />
<div>The SNES PPU is accessed through [[MMIO register table|memory-mapped registers]] at $2100-213F.<br />
<br />
{| class="wikitable"<br />
|+ PPU register summary<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
{{:MMIO register table/PPU}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/PPU|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
* '''8x2''' - An internal 2-byte state accessed by two 8-bit read or writes (LSB first).<br />
<br />
==Display configuration==<br />
{{Anchor|INIDISP}}<br />
===INIDISP - Screen display ($2100 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
F... BBBB<br />
| ||||<br />
| ++++- Screen brightness (linear steps from 0 = none to $F = full)<br />
+--------- Force blanking<br />
<br />
{{Anchor|BGMODE}}<br />
===BGMODE - BG mode and Character size ($2105 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
4321 PMMM<br />
|||| ||||<br />
|||| |+++- BG mode (see below)<br />
|||| +---- Mode 1 BG3 priority (0 = normal, 1 = high)<br />
|||+------ BG1 character size (0 = 8x8, 1 = 16x16)<br />
||+------- BG2 character size (0 = 8x8, 1 = 16x16)<br />
|+-------- BG3 character size (0 = 8x8, 1 = 16x16)<br />
+--------- BG4 character size (0 = 8x8, 1 = 16x16)<br />
<br />
'''BG Modes'''<br />
Mode| BG bit depth |Offsets | Priorities (front -> back) | Notes <u><br />
|BG1 BG2 BG3 BG4|per tile| | <br />
0 | 2 2 2 2 | No | S3 1H 2H S2 1L 2L S1 3H 4H S0 3L 4L| </u><br />
1 | 4 4 2 | No | S3 1H 2H S2 1L 2L S1 3H S0 3L |BG3 priority = 0 <u><br />
| | |3H S3 1H 2H S2 1L 2L S1 S0 3L |BG3 priority = 1 <br />
2 | 4 4 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
3 | 8 4 | No | S3 1H S2 2H S1 1L S0 2L | <br />
4 | 8 2 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
5 | 4 2 | No | S3 1H S2 2H S1 1L S0 2L |Fixed 16 pixel char width. Forced high-res mode.<br />
6 | 4 | Yes | S3 1H S2 S1 1L S0 |Fixed 16 pixel char width. Forced high-res mode.<br />
7 | 8 | No | S3 S2 S1 1L S0 |Fixed 8x8 char size. </u><br />
7EXT| 8 7 | No | S3 S2 2H S1 1L S0 2L |Fixed 8x8 char size. BG2 bit 7 acts as priority.<br />
<br />
See: [[Backgrounds]].<br />
<br />
{{Anchor|MOSAIC}}<br />
<br />
===MOSAIC - Screen pixelation ($2106 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSS 4321<br />
|||| ||||<br />
|||| |||+- Enable BG1 mosaic<br />
|||| ||+-- Enable BG2 mosaic<br />
|||| |+--- Enable BG3 mosaic<br />
|||| +---- Enable BG4 mosaic<br />
++++------ Mosaic size in pixels (0 = 1x1, ..., 15 = 16x16)<br />
<br />
{{Anchor|BGnSC}}<br />
===BGnSC - BG1-4 tilemap address and size ($2107-$210A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAYX<br />
|||| ||||<br />
|||| |||+- Horizontal tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
|||| ||+-- Vertical tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
++++-++--- Tilemap VRAM address (word address = AAAAAA << 10)<br />
<br />
Tilemaps may be placed at any 2 KiB (1 KiW) page.<br />
<br />
===CHR word base address===<br />
----<br />
<br />
The tile base address for background CHR can start at any 8 KiB (4 KiW) page.<br />
<br />
Tilemap offsets that go past the end of VRAM are allowed to wrap around to the beginning.<br />
<br />
{{Anchor|BG12NBA}}<br />
====BG12NBA - BG1 and BG2 CHR word base address ($210B write)====<br />
7 bit 0<br />
---- ----<br />
BBBB AAAA<br />
|||| ||||<br />
|||| ++++- BG1 CHR word base address (word address = AAAA << 12)<br />
++++------ BG2 CHR word base address (word address = BBBB << 12)<br />
<br />
{{Anchor|BG34NBA}}<br />
====BG34NBA - BG3 and BG4 CHR word base address ($210C write)====<br />
7 bit 0<br />
---- ----<br />
DDDD CCCC<br />
|||| ||||<br />
|||| ++++- BG3 CHR word base address (word address = CCCC << 12)<br />
++++------ BG4 CHR word base address (word address = DDDD << 12)<br />
<br />
===Scroll===<br />
----<br />
<br />
Each of these scroll registers is normally updated by two single-byte writes to the same address. After two consecutive writes the scroll value is fully updated.<br />
<br />
The two-write mechanism internally keeps shared latch values, so these registers should not normally be written in mixed order. Complete both writes to one register before moving on to the next.<br />
<br />
The scroll offset is always relative to the top-left of the screen, even when updating mid-frame with HDMA.<br />
<br />
Because the first line of rendering is always a blank line, with vertical scroll of 0 the top line of the BG will be hidden. In the default [[#SETINI|224-lines mode]] an extra (224th) line of BG is also visible at the bottom to compensate.<br />
<br />
{{Anchor|BGnHOFS}}<br />
====BGnHOFS - BG1-4 horizontal scroll offset ($210D, $210F, $2111, $2113 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..XX XXXX XXXX<br />
|| |||| ||||<br />
++---++++-++++- BGn horizontal scroll<br />
<br />
On write: BGnHOFS = (value << 8) | (bgofs_latch & ~7) | (bghofs_latch & 7)<br />
bgofs_latch = value<br />
bghofs_latch = value<br />
<br />
Note: BG1HOFS uses the same address as M7HOFS<br />
<br />
{{Anchor|BGnVOFS}}<br />
====BGnVOFS - BG1-4 vertical scroll offset ($210E, $2110, $2112, $2114 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..YY YYYY YYYY<br />
|| |||| ||||<br />
++---++++-++++- BGn vertical scroll<br />
<br />
On write: BGnVOFS = (value << 8) | bgofs_latch<br />
bgofs_latch = value<br />
<br />
Note: BG1VOFS uses the same address as M7VOFS<br />
<br />
===Layer enable===<br />
----<br />
<br />
{{Anchor|TM}}<br />
====TM - Main screen layer enable ($212C write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on main screen<br />
| ||+-- Enable BG2 on main screen<br />
| |+--- Enable BG3 on main screen<br />
| +---- Enable BG4 on main screen<br />
+------ Enable OBJ on main screen<br />
<br />
{{Anchor|TS}}<br />
====TS - Subscreen layer enable ($212D write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on subscreen<br />
| ||+-- Enable BG2 on subscreen<br />
| |+--- Enable BG3 on subscreen<br />
| +---- Enable BG4 on subscreen<br />
+------ Enable OBJ on subscreen<br />
<br />
{{Anchor|SETINI}}<br />
===SETINI - Screen Mode/Video Select ($2133 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
EX.. HOiI<br />
|| ||||<br />
|| |||+- Screen interlacing<br />
|| ||+-- OBJ interlacing<br />
|| |+--- Overscan mode<br />
|| +---- High-res mode<br />
|+-------- EXTBG mode<br />
+--------- External sync<br />
<br />
* '''Screen interlacing''' causes every odd frame to lower its picture scanlines half a line between the even frames. When enabled, this produces a 480i picture composed of 2 frames (fields), instead of the default 240p progressive picture where each frame appears at the same vertical level.<br />
** [[#STAT78 - PPU2 status flags and version ($213F read)|'''STAT78''']] ($213F) can be used to check whether the current frame is an even or odd field.<br />
** When interlacing is enabled for BG mode 5 or 6, the BG layers are automatically interlaced to give a view of the background that has double the vertical resolution in 480i, effectively making every BG pixel half as tall.<br />
* '''OBJ interlacing''' interlaces the sprites to double their vertical resolution in 480i. Sprite pixels will appear half as tall.<br />
* '''Overscan mode''' enables the full 239 line picture when set, instead of only 224. On NTSC televisions this extra area is not normally visible, but on PAL it is very visible. Setting this causes NMI/vblank to begin 8 lines later, and end 8 lines earlier, dramatically reducing the vblank length in NTSC. Sprite and scroll positions are relative to the end of the blanking period, so enabling this automatically shifts everything up 8 lines. Using this feature makes the SNES drawing positions similar to the NES.<br />
* '''High-res mode''' doubles the horizontal output resolution from 256 to 512 pixels.<br />
** In most BG modes this causes the sub screen to render pixels on even columns (assuming zero-based column indices), and the main screen to render on odd columns. This is sometimes called "pseudo-hires". Some games use this for a transparency effect (''Kirby's Dreamland 3'', ''Jurassic Park''), relying on blurring from the composite video signal to blend the columns.<br />
** In BG modes 5 and 6, this high-res is forced, but the BG layers are automatically interleaved to double their horizontal resolution, making every BG pixel half as wide.<br />
* '''EXTBG''' controls a second-layer effect in BG [[Mode 7|mode 7]] only. In other modes, enabling EXTBG will display garbage.<br />
* '''External sync''' is used for super-imposing images from an external device. Normally 0.<br />
<br />
==VRAM==<br />
{{Anchor|VMAIN}}<br />
===VMAIN - Video Port Control ($2115 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
M... RRII<br />
| ||||<br />
| ||++- Address increment amount:<br />
| || 0: Increment by 1 word<br />
| || 1: Increment by 32 words<br />
| || 2: Increment by 128 words<br />
| || 3: Increment by 128 words<br />
| ++--- Address remapping: (VMADD -> Internal)<br />
| 0: None<br />
| 1: Remap rrrrrrrr YYYccccc -> rrrrrrrr cccccYYY (2bpp)<br />
| 2: Remap rrrrrrrY YYcccccP -> rrrrrrrc ccccPYYY (4bpp)<br />
| 3: Remap rrrrrrYY YcccccPP -> rrrrrrcc cccPPYYY (8bpp)<br />
+--------- Address increment mode:<br />
0: Increment after writing $2118 or reading $2139<br />
1: Increment after writing $2119 or reading $213A<br />
<br />
* '''Address remapping''' allows redirection of the write address to update 32-tile rows horizontally when using <code>II</code> = 0. Within a 32-tile group, sequential access iterates through the same 8-pixel row of each tile horizontally. After 32 spans, it will reach the second row of the first tile. Finally after a group of 32 tiles has been updated, it advances to the next group of 32 tiles..<br />
** This is suitable for a 32x32 tilemap in 8x8 tile mode. By filling each row of the tilemap with sequential values, each group of 32 tiles now corresponds to a contiguous horizontal span of pixels.<br />
** P = tile bitplane-word, c = group column, Y = tile pixel row, r = group row.<br />
** When setting the starting address, the starting tile of a 32-tile group will always be the at the same position as its remapped address.<br />
** With 4bpp or 8bpp modes, each increment advances through the 2 or 4 plane-words of a single tile before advancing to the next tile.<br />
** Simplified explanation:<br />
*** 1. Write all planes for an 8 pixel span before proceeding horizontally to the next.<br />
*** 2. After completing a row of 256 pixels (32 spans), proceed vertically to the next.<br />
<br />
===VRAM address===<br />
----<br />
{{Anchor|VMADD|VMADDL|VMADDH}}<br />
====VMADDL, VMADDH - VRAM word address ($2116, $2117 write)====<br />
VMADDH VMADDL<br />
$2117 $2116<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
hHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM word address<br />
<br />
On write: Update VMADD<br />
vram_latch = [VMADD]<br />
<br />
Because the SNES only has 64 KiB of VRAM, VRAM address bit 15 has no effect.<br />
<br />
The VRAM can only be read during vertical-blank or force-blank. If the PPU is in horizontal-blank or active-display then the VRAM will not be read and <tt>vram_latch</tt> will contain invalid data.<br />
<br />
===VRAM data===<br />
----<br />
{{Anchor|VMDATA|VMDATAL|VMDATAH}}<br />
====VMDATAL, VMDATAH - VRAM data write ($2118, $2119 write)====<br />
VMDATAH VMDATAL<br />
$2119 $2118<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word<br />
<br />
On $2118 write: If address increment mode == 0: increment VMADD<br />
On $2119 write: If address increment mode == 1: increment VMADD<br />
<br />
The VRAM can only be written to in vertical-blank or force-blank. Any VRAM writes during horizontal-blank or active-display will be ignored.<br />
<br />
<tt>[[#VMADD|VMADD]]</tt> will always increment, depending on the state of <tt>[[#VMAIN|VMAIN]]</tt>, even if the VRAM write is ignored.<br />
<br />
{{Anchor|VMDATAREAD|VMDATALREAD|VMDATAHREAD}}<br />
====VMDATALREAD, VMDATAHREAD - VRAM data read ($2139, $213A read)====<br />
VMDATAHREAD VMDATALREAD<br />
$213A $2139<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word from vram_latch<br />
<br />
On $2139 read: value = vram_latch.low<br />
If address increment mode == 0:<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
On $213A read: value = vram_latch.high<br />
If address increment mode == 1:<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
<br />
When reading multiple bytes/words with increment, we normally have to do 1 extra read at the start to account for the <tt>vram_latch</tt> behaviour.<br />
<br />
The <tt>vram_latch</tt> is loaded immediately after you set an address with <tt>[[#VMADD|VMADD]]</tt>, and the word value at that address will be available for the next reads from <tt>VMDATAxREAD</tt>.<br />
<br />
When incrementing due to <tt>VMDATAxREAD</tt>, the next word value is loaded into <tt>vram_latch</tt> ''before ''the increment. This means that the first 2 reads after setting <tt>VMADD</tt> will ''both'' return the same word stored at that address, before the increment takes effect and allows you to read the subsequent bytes/words.<br />
<br />
So:<br />
* When reading a single byte/word of data: simply set the address with <tt>VMADD</tt>, and then read the data via <tt>VMDATAxREAD</tt>.<br />
* When reading a block of contiguous data: after writing <tt>VMADD</tt> do one dummy read to <tt>VMDATAxREAD</tt> to pre-load the <tt>vram_latch</tt>. After this you can simply reach each byte/word sequentially with auto-increment.<br />
<br />
<br />
The VRAM can only be read during vertical-blank or force-blank. If the PPU is in horizontal-blank or active-display then the VRAM will not be read and <tt>vram_latch</tt> will contain invalid data.<br />
<br />
<tt>[[#VMADD|VMADD]]</tt> will always increment, depending on the state of <tt>[[#VMAIN|VMAIN]]</tt>, even if the VRAM is not read.<br />
<br />
==CGRAM==<br />
{{Anchor|CGADD}}<br />
===CGADD - CGRAM word address ($2121 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- CGRAM word address<br />
<br />
On write: cgram_byte = 0<br />
<br />
===CGRAM data===<br />
----<br />
{{Anchor|CGDATA}}<br />
====CGDATA - CGRAM data write ($2122 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.BBB BBGG GGGR RRRR<br />
||| |||| |||| ||||<br />
||| |||| |||+-++++- Red component <br />
||| ||++---+++------- Green component<br />
+++-++--------------- Blue component<br />
<br />
On write: If cgram_byte == 0: cgram_latch = value<br />
If cgram_byte == 1: CGDATA = (value << 8) | cgram_latch<br />
cgram_byte = ~cgram_byte<br />
<br />
Two single-byte writes to this register will update a single CGRAM word. The effect is applied only once the second byte is written.<br />
<br />
Each write will increment the internal byte address. After two writes it will automatically have incremented to the next word.<br />
<br />
{{Anchor|CGDATAREAD}}<br />
<br />
====CGDATAREAD - CGRAM data read ($213B read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xBBB BBGG GGGR RRRR<br />
|||| |||| |||| ||||<br />
|||| |||| |||+-++++- Red component <br />
|||| ||++---+++------- Green component<br />
|+++-++--------------- Blue component<br />
+--------------------- PPU2 [[open bus]]<br />
<br />
On read: If cgram_byte == 0: value = CGDATA.low<br />
If cgram_byte == 1: value = CGDATA.high<br />
cgram_byte = ~cgram_byte<br />
<br />
==OAM==<br />
{{Anchor|OBJSEL|OBSEL}}<br />
===OBJSEL - Object size and Character address ($2101 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSN NbBB<br />
|||| ||||<br />
|||| |+++- Name base address (word address = bBB << 13)<br />
|||+-+---- Name select (word offset = (NN+1) << 12)<br />
+++------- Object size:<br />
0: 8x8 and 16x16<br />
1: 8x8 and 32x32<br />
2: 8x8 and 64x64<br />
3: 16x16 and 32x32<br />
4: 16x16 and 64x64<br />
5: 32x32 and 64x64<br />
6: 16x32 and 32x64<br />
7: 16x32 and 32x32<br />
<br />
* '''Name base address''' selects a 16 KiB-aligned quarter of VRAM for the first 8 KiB of available sprite tiles. Bit 2 was reserved for a planned but never implemented expansion to 128 KiB VRAM, so is normally 0.<br />
* '''Name select''' controls a relative offset from the name base address in NN+1 8 KiB increments, selecting a second 8 KiB of available sprite tiles. With name select of 0, the second half follows the base 8 KiB contiguously.<br />
* '''Object size''' controls the sizes available for sprites. The two modes featuring rectangular sizes (6, 7) were not documented by the SNES development manual.<br />
<br />
Fullsnes refers to this register as '''OBSEL'''.<br />
<br />
===OAM address===<br />
----<br />
{{Anchor|OAMADD|OAMADDL|OAMADDH}}<br />
====OAMADDL, OAMADDH - OAM word address ($2102, $2103 write)====<br />
OAMADDH OAMADDL<br />
$2103 $2102<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
P... ...B AAAA AAAA<br />
| | |||| ||||<br />
| | ++++-++++- OAM word address<br />
| | ++++-+++0- OAM priority rotation index<br />
| +------------- OAM table select (0 = 256 word table, 1 = 16 word table)<br />
+--------------------- OAM priority rotation (1 = enable)<br />
<br />
On write: Update OAMADD<br />
internal_oamadd = (OAMADD & $1FF) << 1<br />
<br />
* '''Priority rotation''' causes the highest priority sprite to be at the last OAMADD set before the visible picture (bits 1-7 only). Otherwise OAM 0 is the highest priority sprite. This can be used for a simple sprite priority rotation.<br />
<br />
===OAM data===<br />
----<br />
{{Anchor|OAMDATA}}<br />
====OAMDATA - OAM data write ($2104 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On write: If (internal_oamadd & 1) == 0: oam_latch = value<br />
If internal_oamadd < $200 and (internal_oamadd & 1) == 1:<br />
[internal_oamadd-1] = oam_latch<br />
[internal_oamadd] = value<br />
If internal_oamadd >= $200: [internal_oamadd] = value<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
When the OAM byte address is less than 512:<br />
:Two single-byte writes to this register will update a single OAM word. The effect is applied only once the second byte is written.<br />
When the OAM byte address is 512 or above:<br />
:Each write immediately applies to the current byte.<br />
<br />
Each write will increment the internal byte address.<br />
<br />
{{Anchor|OAMDATAREAD}}<br />
====OAMDATAREAD - OAM data read ($2138 read)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On read: value = [internal_oamadd]<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
==Mode 7==<br />
{{Anchor|M7SEL}}<br />
===M7SEL - Mode 7 settings ($211A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
RF.. ..YX<br />
|| ||<br />
|| |+- Flip screen horizontally (backgrounds only)<br />
|| +-- Flip screen vertically (backgrounds only)<br />
|+-------- Non-tilemap fill (0 = transparent, 1 = character 0)<br />
+--------- Tilemap repeat (0 = tilemap repeats, 1 = Non-tilemap fill beyond tilemap boundaries)<br />
<br />
===Scroll===<br />
----<br />
{{Anchor|M7HOFS}}<br />
====M7HOFS - Mode 7 horizontal scroll offset ($210D write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 horizontal scroll (signed)<br />
<br />
On write: M7HOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1HOFS<br />
<br />
{{Anchor|M7VOFS}}<br />
====M7VOFS - Mode 7 vertical scroll offset ($210E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 vertical scroll (signed)<br />
<br />
On write: M7VOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1VOFS<br />
<br />
===Matrices===<br />
----<br />
{{Anchor|M7A}}<br />
====M7A - Mode 7 matrix A and Multiplication factor 1 ($211B write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix A (8.8 fixed point)<br />
++++-++++---++++-++++- 16-bit multiplication factor (signed)<br />
<br />
On write: M7A = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 16-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7B}}<br />
====M7B - Mode 7 matrix B and Multiplication factor 2 ($211C write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix B (8.8 fixed point)<br />
++++-++++- 8-bit multiplication factor (signed)<br />
<br />
On write: M7B = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 8-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7n}}<br />
====M7n - Mode 7 matrix C-D ($211D-211E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix n (8.8 fixed point)<br />
<br />
On write: M7n = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
===Center===<br />
----<br />
{{Anchor|M7X}}<br />
====M7X - Mode 7 center X ($211F write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center X (signed)<br />
<br />
On write: M7X = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
{{Anchor|M7Y}}<br />
====M7Y - Mode 7 center Y ($2120 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center Y (signed)<br />
<br />
On write: M7Y = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
==Windows==<br />
'''See: [[Windows]]'''<br />
<br />
===Window mask settings===<br />
----<br />
{{Anchor|W12SEL}}<br />
====W12SEL - Window Mask Settings for BG1 and BG2 ($2123 write)====<br />
7 bit 0<br />
---- ----<br />
DdCc BbAa<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG1<br />
|||| ||+-- Enable window 1 for BG1<br />
|||| |+--- Invert window 2 for BG1<br />
|||| +---- Enable window 2 for BG1<br />
|||+------ Invert window 1 for BG2<br />
||+------- Enable window 1 for BG2<br />
|+-------- Invert window 2 for BG2<br />
+--------- Enable window 2 for BG2<br />
<br />
{{Anchor|W34SEL}}<br />
====W34SEL - Window Mask Settings for BG3 and BG4 ($2124 write)====<br />
7 bit 0<br />
---- ----<br />
HhGg FfEe<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG3<br />
|||| ||+-- Enable window 1 for BG3<br />
|||| |+--- Invert window 2 for BG3<br />
|||| +---- Enable window 2 for BG3<br />
|||+------ Invert window 1 for BG4<br />
||+------- Enable window 1 for BG4<br />
|+-------- Invert window 2 for BG4<br />
+--------- Enable window 2 for BG4<br />
<br />
{{Anchor|WOBJSEL}}<br />
====WOBJSEL - Window Mask Settings for OBJ and Color Window ($2125 write)====<br />
7 bit 0<br />
---- ----<br />
LlKk JjIi<br />
|||| ||||<br />
|||| |||+- Invert window 1 for OBJ<br />
|||| ||+-- Enable window 1 for OBJ<br />
|||| |+--- Invert window 2 for OBJ<br />
|||| +---- Enable window 2 for OBJ<br />
|||+------ Invert window 1 for color<br />
||+------- Enable window 1 for color<br />
|+-------- Invert window 2 for color<br />
+--------- Enable window 2 for color<br />
<br />
The color window is used to black areas of the main or sub screen, see: [[#CGWSEL|CGWSEL]].<br />
<br />
===Window positions===<br />
----<br />
{{Anchor|WH0}}<br />
====WH0 - Window 1 left position ($2126 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 1 left edge position<br />
<br />
{{Anchor|WH1}}<br />
====WH1 - Window 1 right position ($2127 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 1 right edge position<br />
<br />
{{Anchor|WH2}}<br />
====WH2 - Window 2 left position ($2128 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
{{Anchor|WH3}}<br />
====WH3 - Window 2 right position ($2129 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
===Window mask logic===<br />
----<br />
{{Anchor|WBGLOG}}<br />
====WBGLOG - Window BG mask logic ($212A write)====<br />
7 bit 0<br />
---- ----<br />
4433 2211<br />
|||| ||||<br />
|||| ||++- BG1 window mask logic<br />
|||| ++--- BG2 window mask logic<br />
||++------ BG3 window mask logic<br />
++-------- BG4 window mask logic<br />
<br />
{{Anchor|WOBJLOG}}<br />
====WOBJLOG - Window OBJ and color math mask logic ($212B write)====<br />
7 bit 0<br />
---- ----<br />
.... CCOO<br />
||||<br />
||++- OBJ window mask logic<br />
++--- Color window mask logic<br />
<br />
<b>Mask logic types</b><br />
<u>Value|Logic</u><br />
0 | OR<br />
1 | AND<br />
2 | XOR<br />
3 | XNOR<br />
<br />
The color window is used to mask regions of the main and sub-screens, see: [[#CGWSEL|CGWSEL]].<br />
<br />
===Window enable===<br />
----<br />
{{Anchor|TMW}}<br />
====TMW - Main screen layer window enable ($212E write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to main screen BG1<br />
| ||+-- Apply enabled windows to main screen BG2<br />
| |+--- Apply enabled windows to main screen BG3<br />
| +---- Apply enabled windows to main screen BG4<br />
+------ Apply enabled windows to main screen OBJ<br />
<br />
{{Anchor|TSW}}<br />
====TSW - Subscreen layer window enable ($212F write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to subscreen BG1<br />
| ||+-- Apply enabled windows to subscreen BG2<br />
| |+--- Apply enabled windows to subscreen BG3<br />
| +---- Apply enabled windows to subscreen BG4<br />
+------ Apply enabled windows to subscreen OBJ<br />
<br />
==Color math==<br />
{{Anchor|CGWSEL}}<br />
===CGWSEL - Color addition select ($2130 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
MMSS ..AD<br />
|||| ||<br />
|||| |+- Direct color mode<br />
|||| +-- Addend (0 = fixed color, 1 = subscreen)<br />
||++------ Sub screen color window transparent region<br />
++-------- Main screen color window black region<br />
<br />
<B>Region types</B><br />
<u>Value|Region</u><br />
0 |Nowhere<br />
1 |Outside color window<br />
2 |Inside color window<br />
3 |Everywhere<br />
<br />
* The window region settings will replace the main-screen color with black, or sub-screen with transparent, on pixels according to the color windows ([[PPU registers#WOBJSEL|WOBJSEL]] high nibble). If the color windows are not enabled by WOBJSEL, everything is "outside" them. The main-screen setting is used to force a region of the main screen to black. The sub-screen setting is for masking [[color math]].<br />
* '''Addend''' selects either the fixed color ([[#COLDATA|COLDATA]]) or sub-screen for color math. Both can be masked by the window region.<br />
* '''Direct color mode''' is not directly related to color math, but for 8-bpp background modes it selects between palettes and [[direct color]].<br />
* Some older emulators have known inaccurate implementations of the <tt>MM</tt> bits:<br />
** Snes9x 1.43 ignores color math for the entire line if either bit is 1.<br />
** ZSNES ignores color math for any pixels where the main screen was replaced with black. This means that the final result for those pixels is always black.<br />
<br />
{{Anchor|CGADSUB}}<br />
<br />
===CGADSUB - Color math designation ($2131 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
MHBO 4321<br />
|||| ||||<br />
|||| |||+- BG1 color math enable<br />
|||| ||+-- BG2 color math enable<br />
|||| |+--- BG3 color math enable<br />
|||| +---- BG4 color math enable<br />
|||+------ OBJ color math enable (palettes 4-7 only)<br />
||+------- Backdrop color math enable<br />
|+-------- Half color math<br />
+--------- Operator type (0 = add, 1 = subtract)<br />
<br />
This designates which elements of the main screen will have color math applied to them. After layering, if the visible pixel belongs to a color-math enabled layer, the chosen operation will be applied with the subscreen (or fixed color).<br />
<br />
{{Anchor|COLDATA}}<br />
<br />
===COLDATA - Fixed color data ($2132 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
BGRC CCCC<br />
|||| ||||<br />
|||+-++++- Color value<br />
||+------- Write color value to blue channel<br />
|+-------- Write color value to green channel<br />
+--------- Write color value to red channel<br />
<br />
<tt>COLDATA</tt> requires one, two or three writes to set the fixed color to a target color value. For example:<br />
* Black - 1 write: <tt>%111_00000</tt> ''(bgr=0)''<br />
* White - 2 write: <tt>%111_11111</tt> ''(bgr=31)''<br />
* Dark Blue - 2 writes: <tt>%100_10010</tt> ''(b=18)'', <tt>%011_00000</tt> ''(gr=0)''<br />
* Light Green - 2 writes: <tt>%101_10010</tt> ''(br=20)'', <tt>%010_11111</tt> ''(g=31)''<br />
* Light Blue - 3 writes: <tt>%100_11110</tt> ''(b=30)'', <tt>%010_11011</tt> ''(g=27)'', <tt>%001_10110</tt> ''(r=22)''<br />
* Gold - 3 writes: <tt>%100_00000</tt> ''(b=0)'', <tt>%010_11011</tt> ''(g=27)'', <tt>%001_11111</tt> ''(r=31)''<br />
<br />
==Multiplication result==<br />
{{Anchor|MPY|MPYL|MPYM|MPYH}}<br />
===MPYL, MPYM, MPYH - Multiplication result ($2134, $2135, $2136 read)===<br />
MPYH MPYM MPYL<br />
$2136 $2135 $2134<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
HHHH HHHH MMMM MMMM LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit multiplication result (signed)<br />
<br />
This result may be read back after writing [[#M7A|M7A]] with a signed 16-bit value (write twice), and [[#M7B|M7B]] with a signed 8-bit value (write once).<br />
These two values will be multiplied to produce the signed 24-bit value read here.<br />
<br />
See: [[Multiplication]]<br />
<br />
==H/V counters==<br />
{{Anchor|SLHV}}<br />
===SLHV - Software latch for H/V counters ($2137 read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
xxxx xxxx<br />
|||| ||||<br />
++++-++++- CPU [[Open bus]]<br />
<br />
On read: counter_latch = 1<br />
<br />
===Counters===<br />
----<br />
{{Anchor|OPHCT}}<br />
====OPHCT - Output horizontal counter ($213C read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxH HHHH HHHH<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Horizontal counter value<br />
++++-+++-------------- PPU2 [[open bus]]<br />
<br />
On read: If ophct_byte == 0: value = OPHCT.low<br />
If ophct_byte == 1: value = OPHCT.high<br />
ophct_byte = ~ophct_byte<br />
<br />
{{Anchor|OPVCT}}<br />
====OPVCT - Output vertical counter ($213D read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxV VVVV VVVV<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Vertical counter value<br />
++++-+++-------------- PPU2 [[open bus]]<br />
<br />
On read: If opvct_byte == 0: value = OPVCT.low<br />
If opvct_byte == 1: value = OPVCT.high<br />
opvct_byte = ~opvct_byte<br />
<br />
When counter_latch transitions from 0 to 1, these registers are latched with the current counter values. counter_latch is set when SLHV is read or /EXTLATCH (PPU2 pin 29) is asserted, and is cleared when STAT78 is read. /EXTLATCH is connected to joypad IO D7 and can be controlled by the CPU via WRIO or by a joypad.<br />
<br />
counter_latch behavior has not been fully confirmed.<br />
<br />
==Status==<br />
{{Anchor|STAT77}}<br />
===STAT77 - PPU1 status flags and version ($213E read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
TRMx VVVV<br />
|||| ||||<br />
|||| ++++- PPU1 version<br />
|||+------ PPU1 [[open bus]]<br />
||+------- Master/slave mode (PPU1 pin 25)<br />
|+-------- Range over flag (sprite tile overflow)<br />
+--------- Time over flag (sprite overflow)<br />
<br />
{{Anchor|STAT78}}<br />
===STAT78 - PPU2 status flags and version ($213F read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
FLxM VVVV<br />
|||| ||||<br />
|||| ++++- PPU2 version<br />
|||+------ 0: 262 or 525i lines = 60Hz, 1: 312 or 625i lines = 50Hz (PPU2 pin 30)<br />
||+------- PPU2 [[open bus]]<br />
|+-------- Counter latch value<br />
+--------- Interlace field<br />
<br />
On read: counter_latch = 0<br />
ophct_byte = 0<br />
opvct_byte = 0<br />
<br />
If a condition that sets counter_latch is active when STAT78 is read, it is not known if counter_latch is cleared. Existing documentation suggests it is not cleared and the counters are not relatched.<br />
<br />
[[Category:Graphics]]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=1181ROM header2024-01-23T06:36:20Z<p>Fiskbit: /* Non Power-of-2 ROM Size */ but -> so</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. This is not needed by the SNES hardware, though the game software can access it as ROM data.<br />
<br />
However, emulators and flashcarts rely on this header to know how to emulate the game cartridge. Homebrew games should also provide a valid header. Example code: [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template]<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is. See: [[#Header Verification|Header Verification]] below.<br />
<br />
This internal ROM header is to be confused with the additional 512-byte headers used by copier devices. See: [[ROM file formats]]<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|-<br />
| $FFE0 || 32 || [[CPU vectors|Interrupt vectors]]<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is DSP ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other ([[Super Game Boy]]/[[Satellaview]])<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}<br />
<br />
== Checksum ==<br />
<br />
The checksum is a 16-bit sum of all of the bytes in the ROM, potentially with some portions repeated. It is always computed as if the ROM is a power of 2 in size, as given by the ROM header.<br />
<br />
However, some SNES games have a ROM data size that is not a power of 2, e.g. a 3MB game might use a 2MB ROM and a 1MB ROM together. These will use [[mirroring]] to fill remaining space to reach the next largest power of 2.<br />
<br />
=== Non Power-of-2 ROM Size ===<br />
<br />
A physical cartridge will mirror its ROM chips to fill the [[memory map]], but a [[ROM file formats|ROM file]] dump will usually try to omit duplication. To accommodate this, when the file's data doesn't already add to a power of 2, it will be treated as a combination of two regions, each a power of 2 in size. The larger of the two always comes first in the file, and the smaller one will be duplicated until the combined size reaches the next power of 2. The total size will match what is specified in the ROM header, and when preparing a dump we must ensure the result of this process matches the physical cart's mirrored memory map.<br />
<br />
If a ROM file's data size is neither a power of 2 nor the sum of two powers of 2, an emulator will have to first pad the smaller portion to reach the next power of 2. Emulators are inconsistent about what to use for padding (some may fill with 0s), so it is recommended to ensure your ROM file's remainder reaches a power of 2 boundary to avoid this ambiguity. <br />
<br />
The general process for preparing the combined ROM data:<br />
# Find the largest power of 2 less than or equal to the data size.<br />
# If data remains past this point:<br />
<ol type="A"><br />
:<li>Find the smallest power of 2 greater than or equal to this remainder.</li><br />
:<li>Pad the remainder with 0s to meet this power of 2.</li><br />
:<li>Now that the remainder is a power of 2, repeat this data until the remainder matches the size of the first part of the ROM (the power of 2 in step 1).</li><br />
</ol><br />
<br />
When a ROM file's data size is not already a power of 2, most frequently it will be two ROMs in a 2:1 size ratio, which will result in doubling the last third of the data. Cases that need padding are usually homebrew ROMs looking to conserve space by omitting unused memory regions.<br />
<br />
=== Computing the Checksum ===<br />
<br />
Once we have a ROM prepared with a power of 2 size equal to what the ROM header specified, we may compute its checksum.<br />
<br />
Because the ROM header will be part of the computed checksum, before computing the checksum we should first fill the header's checksum and complement values with <tt>$0000</tt> and <tt>$FFFF</tt>. Any value plus its complement will produce the same result, so this ensures the resulting checksum matches the ROM even after the computed checksum is replaced in the header.<br />
<br />
Once ready:<br />
<br />
# Start with a 16-bit <tt>checksum = 0</tt>.<br />
# Add every byte from the prepared data to the checksum. (Overflow is discarded.)<br />
# Store the checksum in the ROM header ($FFDC or equivalent).<br />
# Store <tt>checksum ^ $FFFF</tt> in the ROM header ($FFDE).<br />
<br />
== Header Verification ==<br />
<br />
The primary way to verify a candidate header is to evaluate the [[#Checksum|checksum]] it contains. Some flash-carts appear to use only the checksum to distinguish LoROM from HiROM.<br />
<br />
If no valid checksum can be found (e.g. ROM-hacks or homebrews often omit it), additional heuristics may be used to estimate validity:<ref>[https://github.com/bsnes-emu/bsnes/blob/f57657f27ddec337b1960c7ddaa1b23894bc00c3/bsnes/heuristics/super-famicom.cpp#L515 bsnes SuperFamicom::scoreHeader] - source code for estimating header likelihood</ref><ref>[https://github.com/snes9xgit/snes9x/blob/a2e0580992873ec3913fd1ef09f22f368fe44b3b/memmap.cpp#L1421 snes9x CMemory::LoadRomInt] - source code for estimating header likelihood</ref><br />
* ROM checksum matches.<br />
* Checksum and compliment sum to $FFFF.<br />
* Map mode matches header location.<br />
* Specified ROM size is not smaller than file size.<br />
* A reset vector < $8000 is invalid because it points outside of ROM.<br />
* The first instruction at a valid reset vector is likely to be: <tt>sei, clc, sec, stz, jmp, jml</tt><br />
* The first instruction at a valid reset vector is unlikely to be: <tt>brk, cop, stp, wdm, $FF (sbc long)</tt><br />
* ROM and RAM sizes are reasonable.<br />
* Game name field is ASCII characters only.<br />
<br />
== References ==<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=1180ROM header2024-01-23T06:34:48Z<p>Fiskbit: /* Checksum */ Minor edits for flow. Removes 'to save size' (there are arguably multiple reasons why ROMs don't contain data duplicates).</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. This is not needed by the SNES hardware, though the game software can access it as ROM data.<br />
<br />
However, emulators and flashcarts rely on this header to know how to emulate the game cartridge. Homebrew games should also provide a valid header. Example code: [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template]<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is. See: [[#Header Verification|Header Verification]] below.<br />
<br />
This internal ROM header is to be confused with the additional 512-byte headers used by copier devices. See: [[ROM file formats]]<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|-<br />
| $FFE0 || 32 || [[CPU vectors|Interrupt vectors]]<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is DSP ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other ([[Super Game Boy]]/[[Satellaview]])<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}<br />
<br />
== Checksum ==<br />
<br />
The checksum is a 16-bit sum of all of the bytes in the ROM, potentially with some portions repeated. It is always computed as if the ROM is a power of 2 in size, as given by the ROM header.<br />
<br />
However, some SNES games have a ROM data size that is not a power of 2, e.g. a 3MB game might use a 2MB ROM and a 1MB ROM together. These will use [[mirroring]] to fill remaining space to reach the next largest power of 2.<br />
<br />
=== Non Power-of-2 ROM Size ===<br />
<br />
A physical cartridge will mirror its ROM chips to fill the [[memory map]], but a [[ROM file formats|ROM file]] dump will usually try to omit duplication. To accommodate this, when the file's data doesn't already add to a power of 2, it will be treated as a combination of two regions, each a power of 2 in size. The larger of the two always comes first in the file, and the smaller one will be duplicated until the combined size reaches the next power of 2. The total size will match what is specified in the ROM header, and when preparing a dump we must ensure the result of this process matches the physical cart's mirrored memory map.<br />
<br />
If a ROM file's data size is neither a power of 2 nor the sum of two powers of 2, an emulator will have to first pad the smaller portion to reach the next power of 2. Emulators are inconsistent about what to use for padding (some may fill with 0s), but it is recommended to ensure your ROM file's remainder reaches a power of 2 boundary to avoid this ambiguity. <br />
<br />
The general process for preparing the combined ROM data:<br />
# Find the largest power of 2 less than or equal to the data size.<br />
# If data remains past this point:<br />
<ol type="A"><br />
:<li>Find the smallest power of 2 greater than or equal to this remainder.</li><br />
:<li>Pad the remainder with 0s to meet this power of 2.</li><br />
:<li>Now that the remainder is a power of 2, repeat this data until the remainder matches the size of the first part of the ROM (the power of 2 in step 1).</li><br />
</ol><br />
<br />
When a ROM file's data size is not already a power of 2, most frequently it will be two ROMs in a 2:1 size ratio, which will result in doubling the last third of the data. Cases that need padding are usually homebrew ROMs looking to conserve space by omitting unused memory regions.<br />
<br />
=== Computing the Checksum ===<br />
<br />
Once we have a ROM prepared with a power of 2 size equal to what the ROM header specified, we may compute its checksum.<br />
<br />
Because the ROM header will be part of the computed checksum, before computing the checksum we should first fill the header's checksum and complement values with <tt>$0000</tt> and <tt>$FFFF</tt>. Any value plus its complement will produce the same result, so this ensures the resulting checksum matches the ROM even after the computed checksum is replaced in the header.<br />
<br />
Once ready:<br />
<br />
# Start with a 16-bit <tt>checksum = 0</tt>.<br />
# Add every byte from the prepared data to the checksum. (Overflow is discarded.)<br />
# Store the checksum in the ROM header ($FFDC or equivalent).<br />
# Store <tt>checksum ^ $FFFF</tt> in the ROM header ($FFDE).<br />
<br />
== Header Verification ==<br />
<br />
The primary way to verify a candidate header is to evaluate the [[#Checksum|checksum]] it contains. Some flash-carts appear to use only the checksum to distinguish LoROM from HiROM.<br />
<br />
If no valid checksum can be found (e.g. ROM-hacks or homebrews often omit it), additional heuristics may be used to estimate validity:<ref>[https://github.com/bsnes-emu/bsnes/blob/f57657f27ddec337b1960c7ddaa1b23894bc00c3/bsnes/heuristics/super-famicom.cpp#L515 bsnes SuperFamicom::scoreHeader] - source code for estimating header likelihood</ref><ref>[https://github.com/snes9xgit/snes9x/blob/a2e0580992873ec3913fd1ef09f22f368fe44b3b/memmap.cpp#L1421 snes9x CMemory::LoadRomInt] - source code for estimating header likelihood</ref><br />
* ROM checksum matches.<br />
* Checksum and compliment sum to $FFFF.<br />
* Map mode matches header location.<br />
* Specified ROM size is not smaller than file size.<br />
* A reset vector < $8000 is invalid because it points outside of ROM.<br />
* The first instruction at a valid reset vector is likely to be: <tt>sei, clc, sec, stz, jmp, jml</tt><br />
* The first instruction at a valid reset vector is unlikely to be: <tt>brk, cop, stp, wdm, $FF (sbc long)</tt><br />
* ROM and RAM sizes are reasonable.<br />
* Game name field is ASCII characters only.<br />
<br />
== References ==<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=1178ROM header2024-01-23T05:03:50Z<p>Fiskbit: /* Checksum */ Mentions that for most non-power-of-2 ROMs, resizing is just duplicating the last third.</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. This is not needed by the SNES hardware, though the game software can access it as ROM data.<br />
<br />
However, emulators and flashcarts rely on this header to know how to emulate the game cartridge. Homebrew games should also provide a valid header. Example code: [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template]<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is. See: [[#Header Verification|Header Verification]] below.<br />
<br />
This internal ROM header is to be confused with the additional 512-byte headers used by copier devices. See: [[ROM file formats]]<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|-<br />
| $FFE0 || 32 || [[CPU vectors|Interrupt vectors]]<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is DSP ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other ([[Super Game Boy]]/[[Satellaview]])<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}<br />
<br />
== Checksum ==<br />
<br />
The checksum is a 16-bit sum of all of the bytes in the ROM, potentially with some portions repeated. It is always computed as if the ROM is a power of 2 in size, though some SNES games are not. For example, some games use multiple ROM chips together, and so their size is often the sum of two powers of 2 (e.g. a 3MB game might use a 2MB ROM and a 1MB ROM together).<br />
<br />
Before calculating the checksum, if the data is not already a power of 2 in size, we must repeat some of the data until it is a power of 2 in a way that matches how it will be mirrored in the [[memory map]]. For most ROMs that are not already a power of 2, this is normally done just by duplicating the last third of the ROM. In general, though, this resizing is done as follows:<br />
<br />
# Find the largest power of 2 less than or equal to the data size.<br />
# If data remains past this point:<br />
<ol type="a"><br />
:<li>Find the smallest power of 2 greater than or equal to this remainder.</li><br />
:<li>Pad the remainder with 0s to meet this power of 2.<tt>*</tt></li><br />
:<li>Now that the remainder is a power of 2, repeat this data until the remainder matches the size of the first part of the ROM (the power of 2 in step 1).</li><br />
</ol><br />
<br />
:<tt>*</tt> Note: This padding is outside the specification, and emulators are inconsistent about how to pad. It is recommended to ensure your ROM file's remainder reaches a power of 2 boundary.<br />
<br />
Because the ROM header will be part of the computed checksum, before computing the checksum we must first fill the header's checksum and complement values with <tt>$0000</tt> and <tt>$FFFF</tt>. Any value plus its complement will produce the same result, so this ensures the resulting checksum matches the ROM even after the computed checksum is placed in the header.<br />
<br />
Once ready:<br />
<br />
# Start with a 16-bit <tt>checksum = 0</tt>.<br />
# Add every byte from the prepared data to the checksum. (Overflow is discarded.)<br />
# Store the checksum in the ROM header ($FFDC or equivalent).<br />
# Store <tt>checksum ^ $FFFF</tt> in the ROM header ($FFDE).<br />
<br />
== Header Verification ==<br />
<br />
The primary way to verify a candidate header is to evaluate the [[#Checksum|checksum]] it contains. Some flash-carts appear to use only the checksum to distinguish LoROM from HiROM.<br />
<br />
If no valid checksum can be found (e.g. ROM-hacks or homebrews often omit it), additional heuristics may be used to estimate validity:<ref>[https://github.com/bsnes-emu/bsnes/blob/f57657f27ddec337b1960c7ddaa1b23894bc00c3/bsnes/heuristics/super-famicom.cpp#L515 bsnes SuperFamicom::scoreHeader] - source code for estimating header likelihood</ref><ref>[https://github.com/snes9xgit/snes9x/blob/a2e0580992873ec3913fd1ef09f22f368fe44b3b/memmap.cpp#L1421 snes9x CMemory::LoadRomInt] - source code for estimating header likelihood</ref><br />
* ROM checksum matches.<br />
* Checksum and compliment sum to $FFFF.<br />
* Map mode matches header location.<br />
* Specified ROM size is not smaller than file size.<br />
* A reset vector < $8000 is invalid because it points outside of ROM.<br />
* The first instruction at a valid reset vector is likely to be: <tt>sei, clc, sec, stz, jmp, jml</tt><br />
* The first instruction at a valid reset vector is unlikely to be: <tt>brk, cop, stp, wdm, $FF (sbc long)</tt><br />
* ROM and RAM sizes are reasonable.<br />
* Game name field is ASCII characters only.<br />
<br />
== References ==<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=1177ROM header2024-01-23T04:25:39Z<p>Fiskbit: /* Checksum */ Fixes checksum to use bytes instead of words. Clarifies comment about adding a value and its complement. Rewords things to maybe be a bit clearer and not bury the lede. Minor formatting changes.</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. This is not needed by the SNES hardware, though the game software can access it as ROM data.<br />
<br />
However, emulators and flashcarts rely on this header to know how to emulate the game cartridge. Homebrew games should also provide a valid header. Example code: [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template]<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is. See: [[#Header Verification|Header Verification]] below.<br />
<br />
This internal ROM header is to be confused with the additional 512-byte headers used by copier devices. See: [[ROM file formats]]<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|-<br />
| $FFE0 || 32 || [[CPU vectors|Interrupt vectors]]<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is DSP ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other ([[Super Game Boy]]/[[Satellaview]])<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}<br />
<br />
== Checksum ==<br />
<br />
The checksum is a 16-bit sum of all of the bytes in the ROM, potentially with some portions repeated. It is always computed as if the ROM is a power of 2 in size, though some SNES games are not. For example, some games use multiple ROM chips together, and so their size is often the sum of two powers of 2 (e.g. a 3MB game might use a 2MB ROM and a 1MB ROM together).<br />
<br />
Before calculating the checksum, if the data is not already a power of 2 in size, we must repeat some of the data until it is a power of 2 in a way that matches how it will be mirrored in the [[memory map]]. To do this:<br />
<br />
# Find the largest power of 2 less than or equal to the data size.<br />
# If data remains past this point:<br />
<ol type="a"><br />
:<li>Find the smallest power of 2 greater than or equal to this remainder.</li><br />
:<li>Pad the remainder with 0s to meet this power of 2.<tt>*</tt></li><br />
:<li>Now that the remainder is a power of 2, repeat this data until the remainder matches the size of the first part of the ROM (the power of 2 in step 1).</li><br />
</ol><br />
<br />
:<tt>*</tt> Note: This padding is outside the specification, and emulators are inconsistent about how to pad. It is recommended to ensure your ROM file's remainder reaches a power of 2 boundary.<br />
<br />
Because the ROM header will be part of the computed checksum, before computing the checksum we must first fill the header's checksum and complement values with <tt>$0000</tt> and <tt>$FFFF</tt>. Any value plus its complement will produce the same result, so this ensures the resulting checksum matches the ROM even after the computed checksum is placed in the header.<br />
<br />
Once ready:<br />
<br />
# Start with a 16-bit <tt>checksum = 0</tt>.<br />
# Add every byte from the prepared data to the checksum. (Overflow is discarded.)<br />
# Store the checksum in the ROM header ($FFDC or equivalent).<br />
# Store <tt>checksum ^ $FFFF</tt> in the ROM header ($FFDE).<br />
<br />
== Header Verification ==<br />
<br />
The primary way to verify a candidate header is to evaluate the [[#Checksum|checksum]] it contains. Some flash-carts appear to use only the checksum to distinguish LoROM from HiROM.<br />
<br />
If no valid checksum can be found (e.g. ROM-hacks or homebrews often omit it), additional heuristics may be used to estimate validity:<ref>[https://github.com/bsnes-emu/bsnes/blob/f57657f27ddec337b1960c7ddaa1b23894bc00c3/bsnes/heuristics/super-famicom.cpp#L515 bsnes SuperFamicom::scoreHeader] - source code for estimating header likelihood</ref><ref>[https://github.com/snes9xgit/snes9x/blob/a2e0580992873ec3913fd1ef09f22f368fe44b3b/memmap.cpp#L1421 snes9x CMemory::LoadRomInt] - source code for estimating header likelihood</ref><br />
* ROM checksum matches.<br />
* Checksum and compliment sum to $FFFF.<br />
* Map mode matches header location.<br />
* Specified ROM size is not smaller than file size.<br />
* A reset vector < $8000 is invalid because it points outside of ROM.<br />
* The first instruction at a valid reset vector is likely to be: <tt>sei, clc, sec, stz, jmp, jml</tt><br />
* The first instruction at a valid reset vector is unlikely to be: <tt>brk, cop, stp, wdm, $FF (sbc long)</tt><br />
* ROM and RAM sizes are reasonable.<br />
* Game name field is ASCII characters only.<br />
<br />
== References ==<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Controller_connector&diff=1176Controller connector2024-01-12T10:47:53Z<p>Fiskbit: Fixes JOY register typo.</p>
<hr />
<div>== Pinout ==<br />
Port 1<br />
_<br />
/ \<br />
| 7 | -- GND<br />
| 6 | <> joypad IO D6<br />
| 5 | <- joypad 1 D1<br />
|---|<br />
| 4 | <- joypad 1 D0<br />
| 3 | -> OUT0<br />
| 2 | -> joypad 1 /OE<br />
| 1 | -- +5V <br />
|___|<br />
<br />
Port 2<br />
___<br />
| |<br />
| 1 | -- +5V<br />
| 2 | -> joypad 2 /OE<br />
| 3 | -> OUT0<br />
| 4 | <- joypad 2 D0<br />
|---|<br />
| 5 | <- joypad 2 D1<br />
| 6 | <> joypad IO D7<br />
| 7 | -- GND<br />
\_/<br />
<br />
=== Signal descriptions ===<br />
* '''OUT0''': Output to the controller, used on standard controllers to latch the current button state. Written manually via [[MMIO_registers#JOYOUT|JOYOUT]] D0 or automatically during autoread.<br />
* '''joypad 1 /OE''', '''joypad 2 /OE''': Clocks the joypad. Asserted when reading [[MMIO_registers#JOYSER0|JOYSER0]] (joypad 1) or [[MMIO_registers#JOYSER1|JOYSER1]] (joypad 2) and during autoread.<br />
* '''joypad 1 D1..0''': Controller input read manually via [[MMIO_registers#JOYSER0|JOYSER0]] D1..0 or automatically via [[MMIO_registers#JOY1|JOY1]] (D0) and [[MMIO_registers#JOY3|JOY3]] (D1).<br />
* '''joypad 2 D1..0''': Controller input read manually via [[MMIO_registers#JOYSER1|JOYSER1]] D1..0 or automatically via [[MMIO_registers#JOY2|JOY2]] (D0) and [[MMIO_registers#JOY4|JOY4]] (D1).<br />
* '''joypad IO D7..6''': Bidirectional IO bits accessed through [[MMIO_registers#RDIO|RDIO]] and [[MMIO_registers#WRIO|WRIO]] D7..6. D7 also functions specially as a light pen input via [[PPU_registers#OPHCT|OPHCT]] and [[PPU_registers#OPVCT|OPVCT]].<br />
<br />
== Standard controller wiring ==<br />
{| class="wikitable"<br />
! Color !! Pin !! Name<br />
|-<br />
| White || 1 || +5V<br />
|-<br />
| Yellow || 2 || joypad /OE<br />
|-<br />
| Orange || 3 || OUT0<br />
|-<br />
| Red || 4 || joypad D0<br />
|-<br />
| Brown || 7 || GND<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Controller_connector&diff=1175Controller connector2024-01-12T09:39:23Z<p>Fiskbit: Reformats the pinout to match our typical layout (names and directions in the diagram). De-densifies the information. Adds more detailed signal descriptions.</p>
<hr />
<div>== Pinout ==<br />
Port 1<br />
_<br />
/ \<br />
| 7 | -- GND<br />
| 6 | <> joypad IO D6<br />
| 5 | <- joypad 1 D1<br />
|---|<br />
| 4 | <- joypad 1 D0<br />
| 3 | -> OUT0<br />
| 2 | -> joypad 1 /OE<br />
| 1 | -- +5V <br />
|___|<br />
<br />
Port 2<br />
___<br />
| |<br />
| 1 | -- +5V<br />
| 2 | -> joypad 2 /OE<br />
| 3 | -> OUT0<br />
| 4 | <- joypad 2 D0<br />
|---|<br />
| 5 | <- joypad 2 D1<br />
| 6 | <> joypad IO D7<br />
| 7 | -- GND<br />
\_/<br />
<br />
=== Signal descriptions ===<br />
* '''OUT0''': Output to the controller, used on standard controllers to latch the current button state. Written manually via [[MMIO_registers#JOYOUT|JOYOUT]] D0 or automatically during autoread.<br />
* '''joypad 1 /OE''', '''joypad 2 /OE''': Clocks the joypad. Asserted when reading [[MMIO_registers#JOYSER0|JOYSER0]] (joypad 1) or [[MMIO_registers#JOYSER1|JOYSER1]] (joypad 2) and during autoread.<br />
* '''joypad 1 D1..0''': Controller input read manually via [[MMIO_registers#JOYSER0|JOYSER0]] D1..0 or automatically via [[MMIO_registers#JOY1|JOY1]] (D0) and [[MMIO_registers#JOY2|JOY2]] (D1).<br />
* '''joypad 2 D1..0''': Controller input read manually via [[MMIO_registers#JOYSER1|JOYSER1]] D1..0 or automatically via [[MMIO_registers#JOY3|JOY3]] (D0) and [[MMIO_registers#JOY4|JOY4]] (D1).<br />
* '''joypad IO D7..6''': Bidirectional IO bits accessed through [[MMIO_registers#RDIO|RDIO]] and [[MMIO_registers#WRIO|WRIO]] D7..6. D7 also functions specially as a light pen input via [[PPU_registers#OPHCT|OPHCT]] and [[PPU_registers#OPVCT|OPVCT]].<br />
<br />
== Standard controller wiring ==<br />
{| class="wikitable"<br />
! Color !! Pin !! Name<br />
|-<br />
| White || 1 || +5V<br />
|-<br />
| Yellow || 2 || joypad /OE<br />
|-<br />
| Orange || 3 || OUT0<br />
|-<br />
| Red || 4 || joypad D0<br />
|-<br />
| Brown || 7 || GND<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Open_bus&diff=1174Open bus2024-01-11T21:05:07Z<p>Fiskbit: 5A22 register reads are not visible externally. (e.g. SD2SNES requires code hooks to get joypad state.)</p>
<hr />
<div>When no device responds on a data bus, the result is known as [[open bus]]. This can happen at memory regions where no ROM or other device is mapped, or when reading from a register for which only some bits are driven, leaving the other bits floating.<br />
<br />
In general, open bus will retain the last value present on it for some amount of time before it decays, and so reads will return this stale value. The timing and decay behavior are analog effects and can vary. Driving a bit effectively refreshes its decay timer, largely independent of the other bits.<br />
<br />
== CPU open bus ==<br />
For the CPU bus, the last value driven on each data line will be retained, and reading from open bus will simply repeat each line's value. CPU writes always drive all 8 bits, while CPU reads may drive all, some, or none of the bits. CPU open bus has been found to decay in approximately 2 frames.<ref>[https://forums.nesdev.org/viewtopic.php?p=287063#p287063 Forum post]: lidnariq's open bus test ROM and results</ref> In most cases, the open bus value is the last byte of the instruction data fetched before the read (often the high byte of the read address), or for an indirect instruction it may be the high byte of the fetched indirect address. Note that a cartridge can potentially influence the behavior of CPU open bus, such as maintaining its value indefinitely or forcing undriven bits to 0 or 1.<br />
<br />
The 5A22 S-CPU chip also has an internal data bus, and it is suspected there are actually separate read and write buses, each only updated by their respective access type. When reading registers internal to the 5A22 (readable registers in the range $4016-$437F), the value only exists on the internal read bus, ignoring and separate from any value on the external bus. In this case, any bits that are open bus would return the value from the last read, even if a write occurred in between because that write would not have updated the read bus. The read bus state would also be lost as soon as an external read occurs, overridden by the external bus even if the external bus is open.<br />
<br />
== PPU open bus ==<br />
The two PPU units each have an internal output bus with open bus behavior. When the CPU reads a readable PPU register, that PPU drives all 8 bits of its output bus onto the CPU bus, but the output bus may itself have open bus bits retaining for some time the last value driven on them. A PPU register with open bus bits means those bits are not driven onto the PPU's output bus, and so the CPU will read the stale or decayed open bus value persisting on those bits. Writes to PPU registers go to the PPU's internal input bus and do not affect the output bus.<br />
* PPU1 read addresses: $2134-$2136, $2138-$213A, $213E<br />
** Some of PPU1's write-only registers also return PPU1 open bus when read: $21x4-$21x6, $21x8-$21xA (x=0-2)<br />
* PPU2 read addresses: $213B-$213D, $213F<br />
<br />
== References ==<br />
:* [https://problemkaputt.de/fullsnes.htm#snesunpredictablethings Fullsnes: SNES Unpredictable Things]<br />
:* [https://www.romhacking.net/community/548/ Anomie's SNES Documents] - ob-wrap.txt<br />
:* [https://wiki.superfamicom.org/open-bus Open Bus] - superfamicom.org wiki (Anomie)</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Open_bus&diff=1173Open bus2024-01-11T16:17:57Z<p>Fiskbit: Makes this page more accurate and detailed. Adds decay time reference.</p>
<hr />
<div>When no device responds on a data bus, the result is known as [[open bus]]. This can happen at memory regions where no ROM or other device is mapped, or when reading from a register for which only some bits are driven, leaving the other bits floating.<br />
<br />
In general, open bus will retain the last value present on it for some amount of time before it decays, and so reads will return this stale value. The timing and decay behavior are analog effects and can vary. Driving a bit effectively refreshes its decay timer, largely independent of the other bits.<br />
<br />
== CPU open bus ==<br />
For the CPU bus, the last value driven on each data line will be retained, and reading from open bus will simply repeat each line's value. CPU writes always drive all 8 bits, while CPU reads may drive all, some, or none of the bits. CPU open bus has been found to decay in approximately 2 frames.<ref>[https://forums.nesdev.org/viewtopic.php?p=287063#p287063 Forum post]: lidnariq's open bus test ROM and results</ref> In most cases, the open bus value is the last byte of the instruction data fetched before the read (often the high byte of the read address), or for an indirect instruction it may be the high byte of the fetched indirect address. Note that a cartridge can potentially influence the behavior of CPU open bus, such as maintaining its value indefinitely or forcing undriven bits to 0 or 1.<br />
<br />
The 5A22 S-CPU chip also has an internal data bus, and it is suspected there are actually separate read and write buses, each only updated by their respective access type. When reading registers internal to the 5A22 (readable registers in the range $4016-$437F), it is suspected the value only exists on the internal read bus, ignoring and separate from any value on the external bus. In this case, any bits that are open bus would return the value from the last read, even if a write occurred in between because that write would not have updated the read bus. The read bus state would also be lost as soon as an external read occurs, overridden by the external bus even if the external bus is open.<br />
<br />
== PPU open bus ==<br />
The two PPU units each have an internal output bus with open bus behavior. When the CPU reads a readable PPU register, that PPU drives all 8 bits of its output bus onto the CPU bus, but the output bus may itself have open bus bits retaining for some time the last value driven on them. A PPU register with open bus bits means those bits are not driven onto the PPU's output bus, and so the CPU will read the stale or decayed open bus value persisting on those bits. Writes to PPU registers go to the PPU's internal input bus and do not affect the output bus.<br />
* PPU1 read addresses: $2134-$2136, $2138-$213A, $213E<br />
** Some of PPU1's write-only registers also return PPU1 open bus when read: $21x4-$21x6, $21x8-$21xA (x=0-2)<br />
* PPU2 read addresses: $213B-$213D, $213F<br />
<br />
== References ==<br />
:* [https://problemkaputt.de/fullsnes.htm#snesunpredictablethings Fullsnes: SNES Unpredictable Things]<br />
:* [https://www.romhacking.net/community/548/ Anomie's SNES Documents] - ob-wrap.txt<br />
:* [https://wiki.superfamicom.org/open-bus Open Bus] - superfamicom.org wiki (Anomie)</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=PPU_registers&diff=1172PPU registers2024-01-10T11:27:31Z<p>Fiskbit: Explicitly notes that SLHV open bus is CPU open bus. Makes 'if' formatting on register access consistently use colon instead of comma.</p>
<hr />
<div>The SNES PPU is accessed through [[MMIO register table|memory-mapped registers]] at $2100-213F.<br />
<br />
{| class="wikitable"<br />
|+ PPU register summary<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
{{:MMIO register table/PPU}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/PPU|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
* '''8x2''' - An internal 2-byte state accessed by two 8-bit read or writes (LSB first).<br />
<br />
==Display configuration==<br />
{{Anchor|INIDISP}}<br />
===INIDISP - Screen display ($2100 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
F... BBBB<br />
| ||||<br />
| ++++- Screen brightness (linear steps from 0 = none to $F = full)<br />
+--------- Force blanking<br />
<br />
{{Anchor|BGMODE}}<br />
===BGMODE - BG mode and Character size ($2105 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
4321 PMMM<br />
|||| ||||<br />
|||| |+++- BG mode (see below)<br />
|||| +---- Mode 1 BG3 priority (0 = normal, 1 = high)<br />
|||+------ BG1 character size (0 = 8x8, 1 = 16x16)<br />
||+------- BG2 character size (0 = 8x8, 1 = 16x16)<br />
|+-------- BG3 character size (0 = 8x8, 1 = 16x16)<br />
+--------- BG4 character size (0 = 8x8, 1 = 16x16)<br />
<br />
'''BG Modes'''<br />
Mode| BG bit depth |Offsets | Priorities (front -> back) | Notes <u><br />
|BG1 BG2 BG3 BG4|per tile| | <br />
0 | 2 2 2 2 | No | S3 1H 2H S2 1L 2L S1 3H 4H S0 3L 4L| </u><br />
1 | 4 4 2 | No | S3 1H 2H S2 1L 2L S1 3H S0 3L |BG3 priority = 0 <u><br />
| | |3H S3 1H 2H S2 1L 2L S1 S0 3L |BG3 priority = 1 <br />
2 | 4 4 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
3 | 8 4 | No | S3 1H S2 2H S1 1L S0 2L | <br />
4 | 8 2 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
5 | 4 2 | No | S3 1H S2 2H S1 1L S0 2L |Fixed 16 pixel char width. Forced high-res mode.<br />
6 | 4 | Yes | S3 1H S2 S1 1L S0 |Fixed 16 pixel char width. Forced high-res mode.<br />
7 | 8 | No | S3 S2 S1 1L S0 |Fixed 8x8 char size. </u><br />
7EXT| 8 7 | No | S3 S2 2H S1 1L S0 2L |Fixed 8x8 char size. BG2 bit 7 acts as priority.<br />
<br />
See: [[Backgrounds]].<br />
<br />
{{Anchor|MOSAIC}}<br />
<br />
===MOSAIC - Screen pixelation ($2106 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSS 4321<br />
|||| ||||<br />
|||| |||+- Enable BG1 mosaic<br />
|||| ||+-- Enable BG2 mosaic<br />
|||| |+--- Enable BG3 mosaic<br />
|||| +---- Enable BG4 mosaic<br />
++++------ Mosaic size in pixels (0 = 1x1, ..., 15 = 16x16)<br />
<br />
{{Anchor|BGnSC}}<br />
===BGnSC - BG1-4 tilemap address and size ($2107-$210A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAYX<br />
|||| ||||<br />
|||| |||+- Horizontal tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
|||| ||+-- Vertical tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
++++-++--- Tilemap VRAM address (word address = AAAAAA << 10)<br />
<br />
Tilemaps may be placed at any 2 KiB (1 KiW) page.<br />
<br />
===CHR word base address===<br />
----<br />
<br />
The tile base address for background CHR can start at any 8 KiB (4 KiW) page.<br />
<br />
Tilemap offsets that go past the end of VRAM are allowed to wrap around to the beginning.<br />
<br />
{{Anchor|BG12NBA}}<br />
====BG12NBA - BG1 and BG2 CHR word base address ($210B write)====<br />
7 bit 0<br />
---- ----<br />
BBBB AAAA<br />
|||| ||||<br />
|||| ++++- BG1 CHR word base address (word address = AAAA << 12)<br />
++++------ BG2 CHR word base address (word address = BBBB << 12)<br />
<br />
{{Anchor|BG34NBA}}<br />
====BG34NBA - BG3 and BG4 CHR word base address ($210C write)====<br />
7 bit 0<br />
---- ----<br />
DDDD CCCC<br />
|||| ||||<br />
|||| ++++- BG3 CHR word base address (word address = CCCC << 12)<br />
++++------ BG4 CHR word base address (word address = DDDD << 12)<br />
<br />
===Scroll===<br />
----<br />
<br />
Each of these scroll registers is normally updated by two single-byte writes to the same address. After two consecutive writes the scroll value is fully updated.<br />
<br />
The two-write mechanism internally keeps shared latch values, so these registers should not normally be written in mixed order. Complete both writes to one register before moving on to the next.<br />
<br />
The scroll offset is always relative to the top-left of the screen, even when updating mid-frame with HDMA.<br />
<br />
Because the first line of rendering is always a blank line, with vertical scroll of 0 the top line of the BG will be hidden. In the default [[#SETINI|224-lines mode]] an extra (224th) line of BG is also visible at the bottom to compensate.<br />
<br />
{{Anchor|BGnHOFS}}<br />
====BGnHOFS - BG1-4 horizontal scroll offset ($210D, $210F, $2111, $2113 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..XX XXXX XXXX<br />
|| |||| ||||<br />
++---++++-++++- BGn horizontal scroll<br />
<br />
On write: BGnHOFS = (value << 8) | (bgofs_latch & ~7) | (bghofs_latch & 7)<br />
bgofs_latch = value<br />
bghofs_latch = value<br />
<br />
Note: BG1HOFS uses the same address as M7HOFS<br />
<br />
{{Anchor|BGnVOFS}}<br />
====BGnVOFS - BG1-4 vertical scroll offset ($210E, $2110, $2112, $2114 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..YY YYYY YYYY<br />
|| |||| ||||<br />
++---++++-++++- BGn vertical scroll<br />
<br />
On write: BGnVOFS = (value << 8) | bgofs_latch<br />
bgofs_latch = value<br />
<br />
Note: BG1VOFS uses the same address as M7VOFS<br />
<br />
===Layer enable===<br />
----<br />
<br />
{{Anchor|TM}}<br />
====TM - Main screen layer enable ($212C write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on main screen<br />
| ||+-- Enable BG2 on main screen<br />
| |+--- Enable BG3 on main screen<br />
| +---- Enable BG4 on main screen<br />
+------ Enable OBJ on main screen<br />
<br />
{{Anchor|TS}}<br />
====TS - Subscreen layer enable ($212D write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on subscreen<br />
| ||+-- Enable BG2 on subscreen<br />
| |+--- Enable BG3 on subscreen<br />
| +---- Enable BG4 on subscreen<br />
+------ Enable OBJ on subscreen<br />
<br />
{{Anchor|SETINI}}<br />
===SETINI - Screen Mode/Video Select ($2133 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
EX.. HOiI<br />
|| ||||<br />
|| |||+- Screen interlacing<br />
|| ||+-- OBJ interlacing<br />
|| |+--- Overscan mode<br />
|| +---- High-res mode<br />
|+-------- EXTBG mode<br />
+--------- External sync<br />
<br />
* '''Screen interlacing''' causes every odd frame to lower its picture scanlines half a line between the even frames. When enabled, this produces a 480i picture composed of 2 frames (fields), instead of the default 240p progressive picture where each frame appears at the same vertical level.<br />
** [[#STAT78 - PPU2 status flags and version ($213F read)|'''STAT78''']] ($213F) can be used to check whether the current frame is an even or odd field.<br />
** When interlacing is enabled for BG mode 5 or 6, the BG layers are automatically interlaced to give a view of the background that has double the vertical resolution in 480i, effectively making every BG pixel half as tall.<br />
* '''OBJ interlacing''' interlaces the sprites to double their vertical resolution in 480i. Sprite pixels will appear half as tall.<br />
* '''Overscan mode''' enables the full 239 line picture when set, instead of only 224. On NTSC televisions this extra area is not normally visible, but on PAL it is very visible. Setting this causes NMI/vblank to begin 8 lines later, and end 8 lines earlier, dramatically reducing the vblank length in NTSC. Sprite and scroll positions are relative to the end of the blanking period, so enabling this automatically shifts everything up 8 lines. Using this feature makes the SNES drawing positions similar to the NES.<br />
* '''High-res mode''' doubles the horizontal output resolution from 256 to 512 pixels.<br />
** In most BG modes this causes the sub screen to render pixels on even columns (assuming zero-based column indices), and the main screen to render on odd columns. This is sometimes called "pseudo-hires". Some games use this for a transparency effect (''Kirby's Dreamland 3'', ''Jurassic Park''), relying on blurring from the composite video signal to blend the columns.<br />
** In BG modes 5 and 6, this high-res is forced, but the BG layers are automatically interleaved to double their horizontal resolution, making every BG pixel half as wide.<br />
* '''EXTBG''' controls a second-layer effect in BG [[Mode 7|mode 7]] only. In other modes, enabling EXTBG will display garbage.<br />
* '''External sync''' is used for super-imposing images from an external device. Normally 0.<br />
<br />
==VRAM==<br />
{{Anchor|VMAIN}}<br />
===VMAIN - Video Port Control ($2115 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
M... RRII<br />
| ||||<br />
| ||++- Address increment amount:<br />
| || 0: Increment by 1 word<br />
| || 1: Increment by 32 words<br />
| || 2: Increment by 128 words<br />
| || 3: Increment by 128 words<br />
| ++--- Address remapping: (VMADD -> Internal)<br />
| 0: None<br />
| 1: Remap rrrrrrrr YYYccccc -> rrrrrrrr cccccYYY (2bpp)<br />
| 2: Remap rrrrrrrY YYcccccP -> rrrrrrrc ccccPYYY (4bpp)<br />
| 3: Remap rrrrrrYY YcccccPP -> rrrrrrcc cccPPYYY (8bpp)<br />
+--------- Address increment mode:<br />
0: Increment after writing $2118 or reading $2139<br />
1: Increment after writing $2119 or reading $213A<br />
<br />
* '''Address remapping''' allows redirection of the write address to update 32-tile rows horizontally when using <code>II</code> = 0. Within a 32-tile group, sequential access iterates through the same 8-pixel row of each tile horizontally. After 32 spans, it will reach the second row of the first tile. Finally after a group of 32 tiles has been updated, it advances to the next group of 32 tiles..<br />
** This is suitable for a 32x32 tilemap in 8x8 tile mode. By filling each row of the tilemap with sequential values, each group of 32 tiles now corresponds to a contiguous horizontal span of pixels.<br />
** P = tile bitplane-word, c = group column, Y = tile pixel row, r = group row.<br />
** When setting the starting address, the starting tile of a 32-tile group will always be the at the same position as its remapped address.<br />
** With 4bpp or 8bpp modes, each increment advances through the 2 or 4 plane-words of a single tile before advancing to the next tile.<br />
** Simplified explanation:<br />
*** 1. Write all planes for an 8 pixel span before proceeding horizontally to the next.<br />
*** 2. After completing a row of 256 pixels (32 spans), proceed vertically to the next.<br />
<br />
===VRAM address===<br />
----<br />
{{Anchor|VMADD|VMADDL|VMADDH}}<br />
====VMADDL, VMADDH - VRAM word address ($2116, $2117 write)====<br />
VMADDH VMADDL<br />
$2117 $2116<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
hHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM word address<br />
<br />
On write: Update VMADD<br />
vram_latch = [VMADD]<br />
<br />
Because the SNES only has 64 KiB of VRAM, VRAM address bit 15 has no effect.<br />
<br />
The VRAM can only be read during vertical-blank or force-blank. If the PPU is in horizontal-blank or active-display then the VRAM will not be read and <tt>vram_latch</tt> will contain invalid data.<br />
<br />
===VRAM data===<br />
----<br />
{{Anchor|VMDATA|VMDATAL|VMDATAH}}<br />
====VMDATAL, VMDATAH - VRAM data write ($2118, $2119 write)====<br />
VMDATAH VMDATAL<br />
$2119 $2118<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word<br />
<br />
On $2118 write: If address increment mode == 0: increment VMADD<br />
On $2119 write: If address increment mode == 1: increment VMADD<br />
<br />
The VRAM can only be written to in vertical-blank or force-blank. Any VRAM writes during horizontal-blank or active-display will be ignored.<br />
<br />
<tt>[[#VMADD|VMADD]]</tt> will always increment, depending on the state of <tt>[[#VMAIN|VMAIN]]</tt>, even if the VRAM write is ignored.<br />
<br />
{{Anchor|VMDATAREAD|VMDATALREAD|VMDATAHREAD}}<br />
====VMDATALREAD, VMDATAHREAD - VRAM data read ($2139, $213A read)====<br />
VMDATAHREAD VMDATALREAD<br />
$213A $2139<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word from vram_latch<br />
<br />
On $2139 read: value = vram_latch.low<br />
If address increment mode == 0:<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
On $213A read: value = vram_latch.high<br />
If address increment mode == 1:<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
<br />
When reading multiple bytes/words with increment, we normally have to do 1 extra read at the start to account for the <tt>vram_latch</tt> behaviour.<br />
<br />
The <tt>vram_latch</tt> is loaded immediately after you set an address with <tt>[[#VMADD|VMADD]]</tt>, and the word value at that address will be available for the next reads from <tt>VMDATAxREAD</tt>.<br />
<br />
When incrementing due to <tt>VMDATAxREAD</tt>, the next word value is loaded into <tt>vram_latch</tt> ''before ''the increment. This means that the first 2 reads after setting <tt>VMADD</tt> will ''both'' return the same word stored at that address, before the increment takes effect and allows you to read the subsequent bytes/words.<br />
<br />
So:<br />
* When reading a single byte/word of data: simply set the address with <tt>VMADD</tt>, and then read the data via <tt>VMDATAxREAD</tt>.<br />
* When reading a block of contiguous data: after writing <tt>VMADD</tt> do one dummy read to <tt>VMDATAxREAD</tt> to pre-load the <tt>vram_latch</tt>. After this you can simply reach each byte/word sequentially with auto-increment.<br />
<br />
<br />
The VRAM can only be read during vertical-blank or force-blank. If the PPU is in horizontal-blank or active-display then the VRAM will not be read and <tt>vram_latch</tt> will contain invalid data.<br />
<br />
<tt>[[#VMADD|VMADD]]</tt> will always increment, depending on the state of <tt>[[#VMAIN|VMAIN]]</tt>, even if the VRAM is not read.<br />
<br />
==CGRAM==<br />
{{Anchor|CGADD}}<br />
===CGADD - CGRAM word address ($2121 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- CGRAM word address<br />
<br />
On write: cgram_byte = 0<br />
<br />
===CGRAM data===<br />
----<br />
{{Anchor|CGDATA}}<br />
====CGDATA - CGRAM data write ($2122 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.BBB BBGG GGGR RRRR<br />
||| |||| |||| ||||<br />
||| |||| |||+-++++- Red component <br />
||| ||++---+++------- Green component<br />
+++-++--------------- Blue component<br />
<br />
On write: If cgram_byte == 0: cgram_latch = value<br />
If cgram_byte == 1: CGDATA = (value << 8) | cgram_latch<br />
cgram_byte = ~cgram_byte<br />
<br />
Two single-byte writes to this register will update a single CGRAM word. The effect is applied only once the second byte is written.<br />
<br />
Each write will increment the internal byte address. After two writes it will automatically have incremented to the next word.<br />
<br />
{{Anchor|CGDATAREAD}}<br />
<br />
====CGDATAREAD - CGRAM data read ($213B read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xBBB BBGG GGGR RRRR<br />
|||| |||| |||| ||||<br />
|||| |||| |||+-++++- Red component <br />
|||| ||++---+++------- Green component<br />
|+++-++-------------- Blue component<br />
+-------------------- PPU2 [[open bus]]<br />
<br />
On read: If cgram_byte == 0: value = CGDATA.low<br />
If cgram_byte == 1: value = CGDATA.high<br />
cgram_byte = ~cgram_byte<br />
<br />
==OAM==<br />
{{Anchor|OBJSEL|OBSEL}}<br />
===OBJSEL - Object size and Character address ($2101 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSN NbBB<br />
|||| ||||<br />
|||| |+++- Name base address (word address = bBB << 13)<br />
|||+-+---- Name select (word offset = (NN+1) << 12)<br />
+++------- Object size:<br />
0: 8x8 and 16x16<br />
1: 8x8 and 32x32<br />
2: 8x8 and 64x64<br />
3: 16x16 and 32x32<br />
4: 16x16 and 64x64<br />
5: 32x32 and 64x64<br />
6: 16x32 and 32x64<br />
7: 16x32 and 32x32<br />
<br />
* '''Name base address''' selects a 16 KiB-aligned quarter of VRAM for the first 8 KiB of available sprite tiles. Bit 2 was reserved for a planned but never implemented expansion to 128 KiB VRAM, so is normally 0.<br />
* '''Name select''' controls a relative offset from the name base address in NN+1 8 KiB increments, selecting a second 8 KiB of available sprite tiles. With name select of 0, the second half follows the base 8 KiB contiguously.<br />
* '''Object size''' controls the sizes available for sprites. The two modes featuring rectangular sizes (6, 7) were not documented by the SNES development manual.<br />
<br />
Fullsnes refers to this register as '''OBSEL'''.<br />
<br />
===OAM address===<br />
----<br />
{{Anchor|OAMADD|OAMADDL|OAMADDH}}<br />
====OAMADDL, OAMADDH - OAM word address ($2102, $2103 write)====<br />
OAMADDH OAMADDL<br />
$2103 $2102<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
P... ...B AAAA AAAA<br />
| | |||| ||||<br />
| | ++++-++++- OAM word address<br />
| | ++++-+++0- OAM priority rotation index<br />
| +------------- OAM table select (0 = 256 word table, 1 = 16 word table)<br />
+--------------------- OAM priority rotation (1 = enable)<br />
<br />
On write: Update OAMADD<br />
internal_oamadd = (OAMADD & $1FF) << 1<br />
<br />
* '''Priority rotation''' causes the highest priority sprite to be at the last OAMADD set before the visible picture (bits 1-7 only). Otherwise OAM 0 is the highest priority sprite. This can be used for a simple sprite priority rotation.<br />
<br />
===OAM data===<br />
----<br />
{{Anchor|OAMDATA}}<br />
====OAMDATA - OAM data write ($2104 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On write: If (internal_oamadd & 1) == 0: oam_latch = value<br />
If internal_oamadd < $200 and (internal_oamadd & 1) == 1:<br />
[internal_oamadd-1] = oam_latch<br />
[internal_oamadd] = value<br />
If internal_oamadd >= $200: [internal_oamadd] = value<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
When the OAM byte address is less than 512:<br />
:Two single-byte writes to this register will update a single OAM word. The effect is applied only once the second byte is written.<br />
When the OAM byte address is 512 or above:<br />
:Each write immediately applies to the current byte.<br />
<br />
Each write will increment the internal byte address.<br />
<br />
{{Anchor|OAMDATAREAD}}<br />
====OAMDATAREAD - OAM data read ($2138 read)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On read: value = [internal_oamadd]<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
==Mode 7==<br />
{{Anchor|M7SEL}}<br />
===M7SEL - Mode 7 settings ($211A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
RF.. ..YX<br />
|| ||<br />
|| |+- Flip screen horizontally (backgrounds only)<br />
|| +-- Flip screen vertically (backgrounds only)<br />
|+-------- Non-tilemap fill (0 = transparent, 1 = character 0)<br />
+--------- Tilemap repeat (0 = tilemap repeats, 1 = Non-tilemap fill beyond tilemap boundaries)<br />
<br />
===Scroll===<br />
----<br />
{{Anchor|M7HOFS}}<br />
====M7HOFS - Mode 7 horizontal scroll offset ($210D write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 horizontal scroll (signed)<br />
<br />
On write: M7HOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1HOFS<br />
<br />
{{Anchor|M7VOFS}}<br />
====M7VOFS - Mode 7 vertical scroll offset ($210E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 vertical scroll (signed)<br />
<br />
On write: M7VOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1VOFS<br />
<br />
===Matrices===<br />
----<br />
{{Anchor|M7A}}<br />
====M7A - Mode 7 matrix A and Multiplication factor 1 ($211B write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix A (8.8 fixed point)<br />
++++-++++---++++-++++- 16-bit multiplication factor (signed)<br />
<br />
On write: M7A = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 16-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7B}}<br />
====M7B - Mode 7 matrix B and Multiplication factor 2 ($211C write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix B (8.8 fixed point)<br />
++++-++++- 8-bit multiplication factor (signed)<br />
<br />
On write: M7B = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 8-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7n}}<br />
====M7n - Mode 7 matrix C-D ($211D-211E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix n (8.8 fixed point)<br />
<br />
On write: M7n = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
===Center===<br />
----<br />
{{Anchor|M7X}}<br />
====M7X - Mode 7 center X ($211F write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center X (signed)<br />
<br />
On write: M7X = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
{{Anchor|M7Y}}<br />
====M7Y - Mode 7 center Y ($2120 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center Y (signed)<br />
<br />
On write: M7Y = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
==Windows==<br />
'''See: [[Windows]]'''<br />
<br />
===Window mask settings===<br />
----<br />
{{Anchor|W12SEL}}<br />
====W12SEL - Window Mask Settings for BG1 and BG2 ($2123 write)====<br />
7 bit 0<br />
---- ----<br />
DdCc BbAa<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG1<br />
|||| ||+-- Enable window 1 for BG1<br />
|||| |+--- Invert window 2 for BG1<br />
|||| +---- Enable window 2 for BG1<br />
|||+------ Invert window 1 for BG2<br />
||+------- Enable window 1 for BG2<br />
|+-------- Invert window 2 for BG2<br />
+--------- Enable window 2 for BG2<br />
<br />
{{Anchor|W34SEL}}<br />
====W34SEL - Window Mask Settings for BG3 and BG4 ($2124 write)====<br />
7 bit 0<br />
---- ----<br />
HhGg FfEe<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG3<br />
|||| ||+-- Enable window 1 for BG3<br />
|||| |+--- Invert window 2 for BG3<br />
|||| +---- Enable window 2 for BG3<br />
|||+------ Invert window 1 for BG4<br />
||+------- Enable window 1 for BG4<br />
|+-------- Invert window 2 for BG4<br />
+--------- Enable window 2 for BG4<br />
<br />
{{Anchor|WOBJSEL}}<br />
====WOBJSEL - Window Mask Settings for OBJ and Color Window ($2125 write)====<br />
7 bit 0<br />
---- ----<br />
LlKk JjIi<br />
|||| ||||<br />
|||| |||+- Invert window 1 for OBJ<br />
|||| ||+-- Enable window 1 for OBJ<br />
|||| |+--- Invert window 2 for OBJ<br />
|||| +---- Enable window 2 for OBJ<br />
|||+------ Invert window 1 for color<br />
||+------- Enable window 1 for color<br />
|+-------- Invert window 2 for color<br />
+--------- Enable window 2 for color<br />
<br />
The color window is used to black areas of the main or sub screen, see: [[#CGWSEL|CGWSEL]].<br />
<br />
===Window positions===<br />
----<br />
{{Anchor|WH0}}<br />
====WH0 - Window 1 left position ($2126 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 1 left edge position<br />
<br />
{{Anchor|WH1}}<br />
====WH1 - Window 1 right position ($2127 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 1 right edge position<br />
<br />
{{Anchor|WH2}}<br />
====WH2 - Window 2 left position ($2128 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
{{Anchor|WH3}}<br />
====WH3 - Window 2 right position ($2129 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
===Window mask logic===<br />
----<br />
{{Anchor|WBGLOG}}<br />
====WBGLOG - Window BG mask logic ($212A write)====<br />
7 bit 0<br />
---- ----<br />
4433 2211<br />
|||| ||||<br />
|||| ||++- BG1 window mask logic<br />
|||| ++--- BG2 window mask logic<br />
||++------ BG3 window mask logic<br />
++-------- BG4 window mask logic<br />
<br />
{{Anchor|WOBJLOG}}<br />
====WOBJLOG - Window OBJ and color math mask logic ($212B write)====<br />
7 bit 0<br />
---- ----<br />
.... CCOO<br />
||||<br />
||++- OBJ window mask logic<br />
++--- Color window mask logic<br />
<br />
<b>Mask logic types</b><br />
<u>Value|Logic</u><br />
0 | OR<br />
1 | AND<br />
2 | XOR<br />
3 | XNOR<br />
<br />
The color window is used to mask regions of the main and sub-screens, see: [[#CGWSEL|CGWSEL]].<br />
<br />
===Window enable===<br />
----<br />
{{Anchor|TMW}}<br />
====TMW - Main screen layer window enable ($212E write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to main screen BG1<br />
| ||+-- Apply enabled windows to main screen BG2<br />
| |+--- Apply enabled windows to main screen BG3<br />
| +---- Apply enabled windows to main screen BG4<br />
+------ Apply enabled windows to main screen OBJ<br />
<br />
{{Anchor|TSW}}<br />
====TSW - Subscreen layer window enable ($212F write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to subscreen BG1<br />
| ||+-- Apply enabled windows to subscreen BG2<br />
| |+--- Apply enabled windows to subscreen BG3<br />
| +---- Apply enabled windows to subscreen BG4<br />
+------ Apply enabled windows to subscreen OBJ<br />
<br />
==Color math==<br />
{{Anchor|CGWSEL}}<br />
===CGWSEL - Color addition select ($2130 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
MMSS ..AD<br />
|||| ||<br />
|||| |+- Direct color mode<br />
|||| +-- Addend (0 = fixed color, 1 = subscreen)<br />
||++------ Sub screen color window transparent region<br />
++-------- Main screen color window black region<br />
<br />
<B>Region types</B><br />
<u>Value|Region</u><br />
0 |Nowhere<br />
1 |Outside color window<br />
2 |Inside color window<br />
3 |Everywhere<br />
<br />
* The window region settings will replace the main-screen color with black, or sub-screen with transparent, on pixels according to the color windows ([[PPU registers#WOBJSEL|WOBJSEL]] high nibble). If the color windows are not enabled by WOBJSEL, everything is "outside" them. The main-screen setting is used to force a region of the main screen to black. The sub-screen setting is for masking [[color math]].<br />
* '''Addend''' selects either the fixed color ([[#COLDATA|COLDATA]]) or sub-screen for color math. Both can be masked by the window region.<br />
* '''Direct color mode''' is not directly related to color math, but for 8-bpp background modes it selects between palettes and [[direct color]].<br />
* Some older emulators have known inaccurate implementations of the <tt>MM</tt> bits:<br />
** Snes9x 1.43 ignores color math for the entire line if either bit is 1.<br />
** ZSNES ignores color math for any pixels where the main screen was replaced with black. This means that the final result for those pixels is always black.<br />
<br />
{{Anchor|CGADSUB}}<br />
<br />
===CGADSUB - Color math designation ($2131 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
MHBO 4321<br />
|||| ||||<br />
|||| |||+- BG1 color math enable<br />
|||| ||+-- BG2 color math enable<br />
|||| |+--- BG3 color math enable<br />
|||| +---- BG4 color math enable<br />
|||+------ OBJ color math enable (palettes 4-7 only)<br />
||+------- Backdrop color math enable<br />
|+-------- Half color math<br />
+--------- Operator type (0 = add, 1 = subtract)<br />
<br />
This designates which elements of the main screen will have color math applied to them. After layering, if the visible pixel belongs to a color-math enabled layer, the chosen operation will be applied with the subscreen (or fixed color).<br />
<br />
{{Anchor|COLDATA}}<br />
<br />
===COLDATA - Fixed color data ($2132 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
BGRC CCCC<br />
|||| ||||<br />
|||+-++++- Color value<br />
||+------- Write color value to blue channel<br />
|+-------- Write color value to green channel<br />
+--------- Write color value to red channel<br />
<br />
<tt>COLDATA</tt> requires one, two or three writes to set the fixed color to a target color value. For example:<br />
* Black - 1 write: <tt>%111_00000</tt> ''(bgr=0)''<br />
* White - 2 write: <tt>%111_11111</tt> ''(bgr=31)''<br />
* Dark Blue - 2 writes: <tt>%100_10010</tt> ''(b=18)'', <tt>%011_00000</tt> ''(gr=0)''<br />
* Light Green - 2 writes: <tt>%101_10010</tt> ''(br=20)'', <tt>%010_11111</tt> ''(g=31)''<br />
* Light Blue - 3 writes: <tt>%100_11110</tt> ''(b=30)'', <tt>%010_11011</tt> ''(g=27)'', <tt>%001_10110</tt> ''(r=22)''<br />
* Gold - 3 writes: <tt>%100_00000</tt> ''(b=0)'', <tt>%010_11011</tt> ''(g=27)'', <tt>%001_11111</tt> ''(r=31)''<br />
<br />
==Multiplication result==<br />
{{Anchor|MPY|MPYL|MPYM|MPYH}}<br />
===MPYL, MPYM, MPYH - Multiplication result ($2134, $2135, $2136 read)===<br />
MPYH MPYM MPYL<br />
$2136 $2135 $2134<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
HHHH HHHH MMMM MMMM LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit multiplication result (signed)<br />
<br />
This result may be read back after writing [[#M7A|M7A]] with a signed 16-bit value (write twice), and [[#M7B|M7B]] with a signed 8-bit value (write once).<br />
These two values will be multiplied to produce the signed 24-bit value read here.<br />
<br />
See: [[Multiplication]]<br />
<br />
==H/V counters==<br />
{{Anchor|SLHV}}<br />
===SLHV - Software latch for H/V counters ($2137 read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
xxxx xxxx<br />
|||| ||||<br />
++++-++++- CPU [[Open bus]]<br />
<br />
On read: counter_latch = 1<br />
<br />
===Counters===<br />
----<br />
{{Anchor|OPHCT}}<br />
====OPHCT - Output horizontal counter ($213C read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxH HHHH HHHH<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Horizontal counter value<br />
++++-+++-------------- PPU2 [[open bus]]<br />
<br />
On read: If ophct_byte == 0: value = OPHCT.low<br />
If ophct_byte == 1: value = OPHCT.high<br />
ophct_byte = ~ophct_byte<br />
<br />
{{Anchor|OPVCT}}<br />
====OPVCT - Output vertical counter ($213D read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxV VVVV VVVV<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Vertical counter value<br />
++++-+++-------------- PPU2 [[open bus]]<br />
<br />
On read: If opvct_byte == 0: value = OPVCT.low<br />
If opvct_byte == 1: value = OPVCT.high<br />
opvct_byte = ~opvct_byte<br />
<br />
When counter_latch transitions from 0 to 1, these registers are latched with the current counter values. counter_latch is set when SLHV is read or /EXTLATCH (PPU2 pin 29) is asserted, and is cleared when STAT78 is read. /EXTLATCH is connected to joypad IO D7 and can be controlled by the CPU via WRIO or by a joypad.<br />
<br />
counter_latch behavior has not been fully confirmed.<br />
<br />
==Status==<br />
{{Anchor|STAT77}}<br />
===STAT77 - PPU1 status flags and version ($213E read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
TRMx VVVV<br />
|||| ||||<br />
|||| ++++- PPU1 version<br />
|||+------ PPU1 [[open bus]]<br />
||+------- Master/slave mode (PPU1 pin 25)<br />
|+-------- Range over flag (sprite tile overflow)<br />
+--------- Time over flag (sprite overflow)<br />
<br />
{{Anchor|STAT78}}<br />
===STAT78 - PPU2 status flags and version ($213F read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
FLxM VVVV<br />
|||| ||||<br />
|||| ++++- PPU2 version<br />
|||+------ 0: 262 or 525i lines = 60Hz, 1: 312 or 625i lines = 50Hz (PPU2 pin 30)<br />
||+------- PPU2 [[open bus]]<br />
|+-------- Counter latch value<br />
+--------- Interlace field<br />
<br />
On read: counter_latch = 0<br />
ophct_byte = 0<br />
opvct_byte = 0<br />
<br />
If a condition that sets counter_latch is active when STAT78 is read, it is not known if counter_latch is cleared. Existing documentation suggests it is not cleared and the counters are not relatched.<br />
<br />
[[Category:Graphics]]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=PPU_pinout&diff=1165PPU pinout2023-10-19T02:39:38Z<p>Fiskbit: /* Signal descriptions */ EXTLATCH apparently connects to joypad 2 D1 on the Sharp SF-1 Super Famicom TV. Reported by l_oliveira.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==S-PPU1==<br />
===Pinout===<br />
^<br />
/ \<br />
/ \<br />
/ \<br />
TST1 -> / 1 100 \ <- SYSTEM CLK<br />
TST0 -> / 2 99 \ <- TST2<br />
/PRD -> / 3 (*) 98 \ <- /RESET<br />
/PWR -> / 4 97 \ <- /PIXEL CLK IN<br />
PA7 -> / 5 96 \ -- GND<br />
PA6 -> / 6 95 \ ?? FIELD<br />
PA5 -> / 7 94 \ -> /OVER<br />
PA4 -> / 8 93 \ -> /PIXEL CLK OUT<br />
PA3 -> / 9 92 \ ?? /HCLD<br />
PA2 -> / 10 91 \ ?? /VCLD<br />
PA1 -> / 11 90 \ -> COLOR0<br />
PA0 -> / 12 89 \ -> COLOR1<br />
+5V -- / 13 88 \ -> COLOR2<br />
CPU D7 <> / 14 87 \ -> PRIO0<br />
CPU D6 <> / 15 86 \ -> PRIO1<br />
CPU D5 <> / 16 85 \ -> CHR0<br />
CPU D4 <> / 17 84 \ -> CHR1<br />
CPU D3 <> / 18 83 \ -> CHR2<br />
CPU D2 <> / 19 82 \ -> CHR3<br />
CPU D1 <> / 20 81 \ -- +5V<br />
CPU D0 <> / 21 \<br />
GND -- / 22 /<br />
HVCMODE -> / 23 80 / -> /VRD<br />
PALMODE -> / 24 79 / -> /VBWR<br />
/MASTER -> / 25 78 / -> /VAWR<br />
/EXTSYNC -> / 26 Nintendo 5C77 77 / -- GND<br />
GND -- / 27 Package QFP-100, 0.65mm pitch 76 / -> VAA0<br />
VDB0 <> / 28 75 / -> VAA1<br />
VDB1 <> / 29 S-PPU1 74 / -> VAA2 <br />
VDB2 <> / 30 73 / -> VAA3<br />
/ 72 / -> VAA4<br />
\ 71 / -> VAA5<br />
VDB3 <> \ 31 70 / -> VAA6<br />
VDB4 <> \ 32 69 / -> VAA7 <br />
VDB5 <> \ 33 68 / -> VAA8 <br />
VDB6 <> \ 34 67 / -> VAA9<br />
VDB7 <> \ 35 66 / -> VAA10<br />
+5V -- \ 36 65 / -> VAA11<br />
VDA0 <> \ 37 64 / -> VAA12 Orientation:<br />
VDA1 <> \ 38 63 / -> VAA13 --------------------<br />
VDA2 <> \ 39 62 / -- +5V 80 51<br />
VDA3 <> \ 40 61 / -> VAB0 | |<br />
VDA4 <> \ 41 60 / -> VAB1 .-----------.<br />
VDA5 <> \ 42 59 / -> VAB2 81-| Nintendo O|-50<br />
VDA6 <> \ 43 58 / -> VAB3 | S-PPU1 |<br />
VDA7 <> \ 44 57 / -> VAB4 100-|@ 5C77 |-31<br />
GND -- \ 45 56 / -> VAB5 '-----------'<br />
VA15 <- \ 46 55 / -> VAB6 | |<br />
VA14 <- \ 47 O 54 / -> VAB7 01 30<br />
VAB13 <- \ 48 53 / -> VAB8 <br />
VAB12 <- \ 49 52 / -> VAB9 Legend:<br />
VAB11 <- \ 50 51 / -> VAB10 ----------------------------<br />
\ / --[5C77]-- Power, n/a<br />
\ / ->[5C77]<- 5C77 input<br />
\ / <-[5C77]-> 5C77 output<br />
V <>[5C77]<> Bidirectional<br />
??[5C77]?? Unknown<br />
<br />
==S-PPU2==<br />
===Pinout===<br />
_____<br />
/ \<br />
/BURST <- / 1 100 \ -> /CSYNC<br />
/PED <- / 2 99 \ -- GND<br />
COLORBURST CLK <- / 3 98 \ <- HVCMODE<br />
/TRANSPARENT <> / 4 97 \ -> B<br />
+5V -- / 5 96 \ -> G<br />
/PWR -> / 6 95 \ -> R<br />
/PRD -> / 7 94 \ -- +5VA<br />
CPU D7 <> / 8 93 \ <- DIGITAL VIDEO ENABLE<br />
CPU D6 <> / 9 92 \ <> TST14<br />
CPU D5 <> / 10 91 \ <> TST13<br />
CPU D4 <> / 11 90 \ <> TST12<br />
CPU D3 <> / 12 89 \ <> TST11<br />
CPU D2 <> / 13 88 \ <> TST10<br />
CPU D1 <> / 14 87 \ <> TST9<br />
CPU D0 <> / 15 86 \ <> TST8<br />
GND -- / 16 85 \ <> TST7<br />
PA7 -> / 17 84 \ <> TST6<br />
PA6 -> / 18 83 \ -- +5V <br />
PA5 -> / 19 82 \ <> TST5<br />
PA4 -> / 20 81 \ <> TST4<br />
PA3 -> / 21 \<br />
PA2 -> / 22 O /<br />
PA1 -> / 23 80 / <> TST3<br />
PA0 -> / 24 79 / <> TST2<br />
HBLANK <- / 25 78 / <> TST1<br />
VBLANK <- / 26 Nintendo 5C78 77 / <> TST0<br />
/PIXEL CLK OUT <- / 27 Package QFP-100, 0.65mm pitch 76 / <- EXT7<br />
/RESOUT1 <- / 28 75 / <- EXT6<br />
/EXTLATCH -> / 29 S-PPU2 74 / <- EXT5 <br />
PALMODE -> / 30 73 / <- EXT4<br />
/ O 72 / <- EXT3<br />
\ 71 / <- EXT2<br />
SYSTEM CLK -> \ 31 70 / <- EXT1<br />
+5V -- \ 32 69 / <- EXT0 <br />
/RESOUT0 <- \ 33 68 / -- GND <br />
/RESET -> \ 34 67 / <- VDA7 <br />
GND -- \ 35 66 / <- VDA6 <br />
FIELD -> \ 36 65 / <- VDA5 <br />
/OVER1 -> \ 37 64 / <- VDA4 Orientation:<br />
/PIXEL CLK IN -> \ 38 63 / <- VDA3 --------------------<br />
/HCLD -> \ 39 62 / <- VDA2 80 51<br />
/VCLD -> \ 40 61 / <- VDA1 | |<br />
COLOR0 -> \ 41 60 / <- VDA0 .-----------.<br />
COLOR1 -> \ 42 59 / -- +5V 81-|O Nintendo |-50<br />
COLOR2 -> \ 43 58 / <- VDB7 | S-PPU2 |<br />
PRIO0 -> \ 44 57 / <- VDB6 100-| 5C78 O|-31<br />
PRIO1 -> \ 45 56 / <- VDB5 \-----------'<br />
CHR0 -> \ 46 55 / <- VDB4 | |<br />
CHR1 -> \ 47 54 / <- VDB3 01 30<br />
CHR2 -> \ 48 53 / <- VDB2 <br />
CHR3 -> \ 49 52 / <- VDB1 Legend:<br />
/OVER2 -> \ 50 51 / <- VDB0 ----------------------------<br />
\ / --[5C78]-- Power, n/a<br />
\ / ->[5C78]<- 5C78 input<br />
\ / <-[5C78]-> 5C78 output<br />
V <>[5C78]<> Bidirectional<br />
??[5C78]?? Unknown<br />
<br />
===Signal descriptions===<br />
* '''COLORBURST CLK''': 3.58 MHz clock.<br />
* '''/PIXEL CLK IN, /PIXEL CLK OUT''': 5.37 MHz dot clock. In comes from S-PPU1. Out goes to expansion port pin 22.<br />
* '''SYSTEM CLK''': 21.47727 MHz clock.<br />
* '''/RESOUT0''': S-PPU1 reset.<br />
* '''/RESOUT1''': The main reset signal, connected to the CPU, APU, cartridge, and expansion port.<br />
* '''/RESET''': Reset from CIC.<br />
* '''/EXTLATCH''': Controls H/V counter latching. Normally connected to joypad IO D7, but connected instead on the Sharp SF-1 TV to joypad 2 D1. Can be used with a light pen.<br />
* '''/OVER1, /OVER2''': /OVER from S-PPU1.<br />
* '''/TRANSPARENT''': This is believed to be high whenever an opaque (sprite or tilemap) pixel is drawn.<br />
* '''EXT7..0''': Video input, connected to VDB7..0.<br />
* '''DIGITAL VIDEO ENABLE''': When high, TST4..0, TST9..5, and TST14..10 are digital R4..0, G4..0, and B4..0 output. For correct digital video output, this should be connected to /OVER.<ref>[https://shmups.system11.org/viewtopic.php?f=6&t=66597 Shmups forum thread]: Sharp analog RGB for the 3-Chip SNES using digital signals</ref> As sold, this is connected to ground.<br />
* '''TST14..12''': When DIGITAL VIDEO ENABLE is low, these control other kinds of data that can be outputted over the TST pins. Output correlated with VRAM accesses has been observed.<ref>[https://circuit-board.de/forum/index.php/Thread/25396-SNES-Chips-decapped-2PPU-1CHIP-APU-DSP/?postID=702636#post702636 circuit-board forum thread]: SNES-Chips decapped (2PPU, 1CHIP, APU, DSP)</ref> As sold, these are connected to ground.<br />
<br />
==References==</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=MediaWiki:Mainpage&diff=1156MediaWiki:Mainpage2023-08-23T09:15:17Z<p>Fiskbit: Point to the new main page name.</p>
<hr />
<div>SNESdev Wiki</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Main_Page&diff=1155Main Page2023-08-23T09:14:35Z<p>Fiskbit: Fiskbit moved page Main Page to SNESdev Wiki: Rename main page to match wiki name.</p>
<hr />
<div>#REDIRECT [[SNESdev Wiki]]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=SNESdev_Wiki&diff=1154SNESdev Wiki2023-08-23T09:14:35Z<p>Fiskbit: Fiskbit moved page Main Page to SNESdev Wiki: Rename main page to match wiki name.</p>
<hr />
<div>'''SNES Development Wiki'''<br />
<br />
== Reference ==<br />
<br />
=== General ===<br />
* [[Memory map]]<br />
* [[ROM header]]<br />
* [[CPU vectors]]<br />
* [[SNES Development Manual]]<br />
* [[65C816]] - SNES main CPU, part of the S-CPU<br />
* [[Tools]]<br />
* [[Timing]]<br />
* [[Errata]]<br />
* [[Glossary]]<br />
<br />
=== Registers ===<br />
* [[MMIO registers]]<br />
* [[PPU registers]]<br />
* [[DMA registers]]<br />
<br />
=== Pinouts ===<br />
* [[APU pinout]]<br />
* [[CPU pinout]]<br />
* [[PPU pinout]]<br />
* [[WRAM pinout]]<br />
* [[Cartridge connector]]<br />
* [[Controller connector]]<br />
<br />
=== Peripherals ===<br />
* [[Standard controller]]<br />
* [[Mouse]]<br />
* [[Multitap]]<br />
* [[Super Scope]]<br />
* [[NTT Data Keypad]]<br />
* [[Copier]]<br />
<br />
=== PPU ===<br />
* [[Backgrounds]]<br />
* [[Tilemaps]]<br />
* [[Tiles]]<br />
* [[Sprites]]<br />
* [[Palettes]]<br />
* [[Windows]]<br />
* [[Offset-per-tile]]<br />
* [[Color math]]<br />
<br />
=== Sound ===<br />
* [[S-SMP]] - SNES SHVC-SOUND chip includes the S-SMP / SPC-700 CPU and S-DSP<br />
* [[SPC-700 instruction set]]<br />
* [[DSP envelopes]]<br />
* [[BRR samples]]<br />
<br />
=== Expansions ===<br />
* [[DSP-1]] - also includes DSP-2, DSP-3, DSP-4<br />
* [[SA-1]]<br />
* [[Super FX]] (GSU)<br />
* [[MSU-1]]<br />
* [[Super Game Boy]]<br />
* [[CX4]] used in ''Mega Man X2''/''X3''<br />
<br />
=== Formats ===<br />
* [[ROM file formats]]<br />
* [[Save file formats]]<br />
* [[SPC file format]] - for music.<br />
<br />
== Examples and Guides ==<br />
<br />
=== General ===<br />
* [[Tutorials]]<br />
<br />
=== SNES hardware ===<br />
* [[Init code]]<br />
* [[VBlank interrupts]]<br />
* [[Booting the SPC700]]<br />
* [[Controller reading]]<br />
* [[Multiplication]]<br />
* [[Division]]<br />
* [[DMA examples]]<br />
* [[Blargg SPC upload]] - Playing an SPC rip on SNES hardware<br />
<br />
=== 65c816 guides ===<br />
* [[65c816 for 6502 developers]]<br />
* [[Using X as a pointer]]<br />
* [[MVN and MVP block copy]]<br />
* [[Register sizes in ca65]]<br />
* [[Signature byte]] - supplying a parameter byte for BRK/COP interrupts or WDM<br />
<br />
=== Emulation ===<br />
* [[Emulator tests]]<br />
* [[Tricky-to-emulate games]]<br />
* [[Uncommon graphics mode games]]<br />
<br />
=== Video ===<br />
* [[VBlank routine]]<br />
* [[SNES PPU for NES developers]]<br />
* [[Scrolling a large map]]<br />
* [[Drawing window shapes]]<br />
* [[HDMA examples]]<br />
* [[Reading and writing PPU memory]]<br />
* [[Mode 7 perspective effects]]<br />
* [[Starting HDMA mid-frame]]<br />
* [[Variable width fonts]]<br />
* [https://undisbeliever.net/snesdev/registers/inidisp.html#extended-vblank Extending vblank ]<br />
<br />
== Links ==<br />
* [https://forums.nesdev.org/viewforum.php?f=12 SNESdev Forum] - NESDev subforum<br />
* [https://problemkaputt.de/fullsnes.htm Fullsnes] - Nocash's SNES hardware document<br />
* [https://wiki.superfamicom.org/ Superfamicom.org SNES Development Wiki]<br />
* [https://en.wikibooks.org/wiki/Super_NES_Programming Super NES Programming Wikibooks]<br />
* [https://superfamicom.org/ Superfamicom.org SNES cartridge database]<br />
* [https://snescentral.com/ SNES Central] - game database and PCB images<br />
* [https://www.romhacking.net/community/548/ Anomie's SNES documents] at RHDN<br />
<br />
== MediaWiki ==<br />
<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]<br />
* [[:Category:Deletion requests|Deletion requests]]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Reading_and_writing_PPU_memory&diff=1139Reading and writing PPU memory2023-03-21T10:09:41Z<p>Fiskbit: Adds references category to separate out the references.</p>
<hr />
<div><br />
== CGRAM ==<br />
<br />
The PPU contains an internal 256&nbsp;x&nbsp;15bit memory called [[CGRAM]] that holds the palette data.<br />
<br />
<br />
The S-CPU can access the CGRAM using the <tt>[[PPU registers#CGADD|CGADD]]</tt>, <tt>[[PPU registers#CGDATA|CGDATA]]</tt> and <tt>[[PPU registers#CGDATAREAD|CGDATAREAD]]</tt> registers.<br />
<br />
* The S-CPU can only access the CGRAM during [[Timing#Vertical_Blank|Vertical Blank]], [[Timing#Horizontal_Blank|Horizontal Blank]] or [[PPU registers#INIDISP|Force Blank]].<br />
** If the <tt>CGDATA</tt> or <tt>CGDATAREAD</tt> registers are accessed during active-display the data will be read from or written to the wrong CGRAM address.<br />
* <tt>CGDATA</tt> is a write-twice register. You must always write to <tt>CGDATA</tt> an even number of times.<br />
** The color data is only written to the CGRAM on the second <tt>CGDATA</tt> write.<br />
* <tt>CGDATAREAD</tt> is a read-twice register. You should always read from <tt>CGDATAREAD</tt> an even number of times.<br />
* You should always set the CGRAM word address with <tt>CGADD</tt> before reading or writing to CGRAM.<br />
** This will also reset an internal odd/even counter.<br />
** Mixing CGRAM reads and writes is not recommended.<br />
* Each CGRAM color is 15 bits in size.<br />
** When writing to CGRAM, bit 15 is ignored<br />
** When reading CGRAM, bit 15 will be [[Open bus|PPU2 open bus]] and should be masked.<br />
<br />
<br />
<br />
To write to CGRAM, first set the CGRAM word address (ie, palette color index) with an 8-bit write to <tt>CGADD</tt>. Then preform two 8-bit writes to <tt>CGDATA</tt>. After the second write to <tt>CGDATA</tt> the color data will be written to CGRAM and the internal CGRAM word address will be incremented by one. Subsequent colors can be written to CGRAM with two more 8-bit writes to <tt>CGDATA</tt>.<br />
<br />
<pre><br />
.a8<br />
.i16<br />
// DB access registers<br />
// REQUIRES: h-blank, v-blank or force-blank<br />
<br />
// Set a single CGRAM color at `COLOR_INDEX` to `COLOR_VALUE`<br />
<br />
// Set CGRAM word address (color index)<br />
lda #COLOR_INDEX<br />
sta CGADD<br />
<br />
// Write low byte<br />
lda #.lobyte(COLOR_VALUE)<br />
sta CGDATA<br />
<br />
// Write high byte<br />
lda #.hibyte(COLOR_VALUE)<br />
sta CGDATA<br />
</pre><br />
<br />
<pre><br />
Variables:<br />
zpFarPtr - a 3 byte pointer in zero-page.<br />
<br />
<br />
// Write a block of colors to CGRAM.<br />
//<br />
// INPUT: A = starting color index<br />
// INPUT: X = number of colors to write (MUST BE > 0)<br />
// INPUT: zpFarPtr = palette data<br />
// REQUIRES: Vertical-Blank or Force-Blank.<br />
// (There is not enough Horizontal-Blank time to run this code)<br />
.a8<br />
.i16<br />
// DB access registers<br />
.proc WriteCgramBlock<br />
// Set CGRAM word address (color index)<br />
sta CGADD<br />
<br />
ldy #0<br />
Loop:<br />
// Write low byte<br />
lda [zpFarPtr],y<br />
sta CGDATA<br />
iny<br />
<br />
// Write high byte<br />
lda [zpFarPtr],y<br />
sta CGDATA<br />
iny<br />
<br />
dex<br />
bne Loop<br />
rts<br />
.endproc<br />
</pre><br />
<br />
<br />
Writing to CGRAM using DMA or HDMA is preformed using the ''One register, write twice'' transfer pattern (DMAP pattern 2). (See [[HDMA examples#HDMA to CGRAM]] for a HDMA example.)<br />
<pre><br />
Variables:<br />
cgramBuffer : uint16[256] = a buffer of 256 colors in RAM<br />
<br />
<br />
// Transfer a 256 color buffer (`cgramBuffer`) to CGRAM using DMA channel 0<br />
//<br />
// REQUIRES: Vertical-Blank or Force-Blank<br />
// DB access registers<br />
// Uses DMA channel 0<br />
subroutine TransferBufferToCgram:<br />
// reset CGRAM address<br />
CGADD = 0<br />
<br />
<br />
// DMA parameters: one write-twice register, to PPU<br />
DMAP0 = 2<br />
<br />
// B-Bus address<br />
BBAD0 = .lobyte(CGDATA)<br />
<br />
// A-Bus address<br />
A1T0 = .loword(cgramBuffer)<br />
A1B0 = .bankbyte(cgramBuffer)<br />
<br />
// Transfer size (SHOULD BE EVEN)<br />
DAS0 = .sizeof(cgramBuffer)<br />
<br />
// Start DMA transfer on channel 0<br />
MDMAEN = 1 << 0<br />
</pre><br />
<br />
<br />
<br />
Reading from CGRAM is preformed with the <tt>CGDATAREAD</tt> register in a similar manner as CGRAM writes. Bit 15 of the color data is open-bus and should be masked to 0.<br />
<pre><br />
VARIABLES: zpTmpWord - a temporary uint16 variable in zero-page.<br />
<br />
// INPUT: A = color index to read<br />
// OUTPUT: zpTmpWord = color value<br />
// REQUIRES: v-blank or force-blank<br />
.a8<br />
.i16<br />
// DB access registers<br />
.proc ReadCgramColor<br />
sta CGADD<br />
<br />
// Read low-byte<br />
lda CGDATAREAD<br />
sta zpTmpWord<br />
<br />
// Read high-byte<br />
lda CGDATAREAD<br />
// (The MSB is open-bus and should be masked)<br />
and #0x7f<br />
sta zpTmpWord + 1<br />
<br />
rts<br />
.endproc<br />
</pre><br />
<br />
<br />
<br />
== OAM: Object Attribute Memory ==<br />
<br />
The PPU contains two internal RAM blocks (a 512 byte low-table and a 32 byte hi-table) that form the [[Sprites#OAM|OAM]].<br />
<br />
<br />
The S-CPU can access the OAM using the <tt>[[PPU registers#OAMADD|OAMADD]]</tt>, <tt>[[PPU registers#OAMDATA|OAMDATA]]</tt> and <tt>[[PPU registers#OAMDATAREAD|OAMDATAREAD]]</tt> PPU registers.<br />
<br />
* The S-CPU can only access the OAM during [[Timing#Vertical_Blank|Vertical Blank]] or [[PPU_registers#INIDISP|Force Blank]].<br />
* Writing to <tt>OAMADD</tt> sets an internal 9 bit OAM word address.<br />
** The 8th bit of the internal OAM word address (bit 0 of <tt>OAMADDH</tt>) determines which OAM table accessed.<br />
** The internal OAM address is reset whenever <tt>OAMADDL</tt> or <tt>OAMADDH</tt> is written to.<br />
** You should always set both <tt>OAMADDL</tt> and <tt>OAMADDH</tt> (eg, with a 16 bit write to <tt>OAMADD</tt>) when setting the OAM word address.<br />
** You should always write to <tt>OAMADD</tt> before transferring data to the OAM.<br />
* <tt>OAMDATA</tt> is a write-twice register when writing to the OAM low-table.<br />
** When writing to the low-table, the data is only written to the OAM on the second <tt>OAMDATA</tt> write.<br />
** When writing to the hi-table, the data is written to the OAM on every <tt>OAMDATA</tt> write.<br />
** Despite this, you should always treat <tt>OAMDATA</tt> as a write-twice register.<br />
* The internal OAM addresses is reset to the last value written to <tt>OAMADD</tt> when VBlank starts and the screen is enabled (not in force-blank).<ref>higan source code, sfc/ppu/object.cpp <tt>PPU::Object::scanline()</tt>, by Near</ref><br />
* <tt>OAMADD</tt> can also enable ''OAM priority rotation''.<br />
** When using ''OAM priority rotation'', the first-sprite is updated and may be incremented on every <tt>OAMDATA</tt> write or <tt>OAMDATAREAD</tt> read.<ref>higan source code, sfc/ppu/io.cpp and sfc/ppu/object.cpp, by Near (search for <tt>setFirstSprite()</tt>)</ref><br />
** If you are using ''OAM priority rotation'', you will need to write to <tt>OAMADD</tt> any after a OAM transfer to reset the first-sprite.<br />
<br />
<br />
Reading and writing to OAM is the same as writing to CGRAM, except the OAM address register (<tt>OAMADD</tt>) is 16 bits wide.<br />
<br />
<br />
It is highly recommended that you create a [[VBlank routine#Buffers|544 byte OAM buffer]] in Work-RAM and only transfer data to the OAM via this buffer during the Vertical Blanking Period. (See [[VBlank routine#OAM_buffer_example]] for an example of a DMA transfer from an OAM buffer to OAM.)<br />
<br />
<br />
<br />
== VRAM: Video RAM ==<br />
<br />
The PPU is connected to two external 32K&nbsp;x&nbsp;8bit SRAM chips, called VRAM (Video RAM).<br />
<br />
The PPU accesses the VRAM in one of three modes, depending on context:<br />
* 16 bit VRAM: Both VRAM chips are combined into a single 32K&nbsp;x&nbsp;16bit (64KB) memory. Used for [[Tiles|tile]] data (2/4/8 bpp), [[Tilemaps|nametable]] data and [[Offset-per-tile|offset-per-tile]] data.<br />
* Two separate 16K&nbsp;x&nbsp;8bit VRAM chips<ref>VRAM address bits 14 and 15 (S-PPU-1 pins 47 & 46) are shared across both VRAM chips and are always zero in Mode 7.</ref>: Used by Mode 7. The low-VRAM chip holds the [[Tilemaps#Mode_7|Mode 7 Tilemap]], the high-VRAM chip holds the [[Tiles#Mode_7|Mode 7 tile data]].<br />
* Two separate 32K&nbsp;x&nbsp;8bit VRAM chips with a shared auto-incrementing address bus: Used by the <tt>VMAIN</tt>, <tt>VMADD</tt>, <tt>VMDATA</tt> and <tt>VMDATAREAD</tt> [[PPU registers#VRAM|PPU Registers]] to allow the S-CPU to access VRAM.<br />
<br />
<br />
<br />
The S-CPU can access VRAM using the <tt>[[PPU_registers#VMAIN|VMAIN]]</tt>, <tt>[[PPU registers#VMADD|VMADD]]</tt>, <tt>[[PPU registers#VMDATA|VMDATA]]</tt>, <tt>[[PPU registers#VMDATAREAD|VMDATAREAD]]</tt> registers.<br />
<br />
* The S-CPU can only access the VRAM during [[Timing#Vertical_Blank|Vertical Blank]] or [[PPU_registers#INIDISP|Force Blank]].<br />
** If the <tt>VMAIN</tt>, <tt>VMDATA</tt> or <tt>VMDATAREAD</tt> registers are accessed during horizontal-blank or active-display the VRAM will not be read from or written to.<br />
* <tt>VMDATA</tt> and <tt>VMDATAREAD</tt> are '''not''' word registers.<br />
** <tt>VMDATALREAD</tt> and <tt>VMDATAL</tt> will read from or write to the low-byte VRAM chip.<br />
** <tt>VMDATAHREAD</tt> and <tt>VMDATAH</tt> will read from or write to the high-byte VRAM chip.<br />
** You can perform a 16-bit read from <tt>VMDATAREAD</tt> or 16-bit write to <tt>VMDATA</tt> to read/write both VRAM chips at once.<br />
* How the internal VRAM word address is incremented is controlled by the <tt>[[PPU registers#VMAIN|VMAIN]]</tt> register.<br />
** You should always write to <tt>VMAIN</tt> before performing a VRAM transfer, unless you know the exact state of the <tt>VMAIN</tt> register (ie, immediately following a previous VRAM transfer in the VBlank routine).<br />
** The ''Address increment mode'' flag (bit 7) of <tt>VMAIN</tt> determines if the internal VRAM word address is incremented on low or high byte VRAM access.<br />
*** When ''Address increment mode'' is 0, the internal VRAM word address increments after writing to <tt>VMDATAL</tt> or reading from <tt>VMDATALREAD</tt>.<br />
*** When ''Address increment mode'' is 1, the internal VRAM word address increments after writing to <tt>VMDATAH</tt> or reading from <tt>VMDATAHREAD</tt>.<br />
*** To write a block of data to only the low-VRAM chip (ie, Mode 7 tilemap data): Set ''Address increment mode'' to 0, write the data to <tt>VMDATAL</tt>.<br />
*** To write a block of data to only the high-VRAM chip (ie, Mode 7 tile data): Set ''Address increment mode'' to 1, write the data to <tt>VMDATAH</tt>.<br />
*** To write word data to the VRAM: Set ''Address increment mode'' to 1, write to both <tt>VMDATAL</tt> and <tt>VMDATAH</tt> (in order).<br />
** The ''Address increment'' bits (bits 0-1) of <tt>VMAIN</tt> controls how much the internal VRAM word address will be incremented by:<br />
*** <tt>0b00</tt>: Increments the VRAM word address by 1.<br />
*** <tt>0b01</tt>: Increments the VRAM word address by 32. Useful for writing a 32-word tilemap column to VRAM.<br />
*** <tt>0b10</tt> or <tt>0b11</tt>: Increments the VRAM word address by 128. Useful for writing a 128-byte Mode 7 tilemap column to VRAM.<br />
** The ''Address remapping'' bits (bits 2-3) of <tt>VMAIN</tt> remap how the internal VRAM word address bits are connected the address bus of the two VRAM chips.<br />
*** For most transfers the ''Address remapping'' bits will be 0 (no-remapping).<br />
* Writing to <tt>VMADD</tt> will set the internal VRAM word address.<br />
** You should always write to both <tt>VMADDL</tt> and <tt>VMADDH</tt> (eg, with a 16 bit write to <tt>VMADD</tt>) when setting the VRAM word address.<br />
** Writing to <tt>VMADDL</tt> or <tt>VMADDH</tt> will cause the PPU to perform a VRAM read to the VRAM latch.<br />
*** If the PPU is in horizontal-blank or active-display, no VRAM read will occur and the latch will contain invalid data.<br />
* Reading from <tt>VMDATAREAD</tt> will immediately read the value of the VRAM latch, '''then''' perform a VRAM read and '''then''' increment the internal VRAM word address (depending on <tt>VMAIN</tt>).<br />
** This means you will need to perform a dummy read from <tt>VMDATAxREAD</tt> if you want to read multiple bytes/words from VRAM.<br />
** When reading a single byte or word of VRAM: Set the word address with <tt>VMADD</tt> and read the VRAM data via </tt>VMDATALREAD</tt> and/or <tt>VMDATAHREAD</tt>.<br />
** When reading multiple bytes/words of VRAM: Set the word address with <tt>VMADD</tt>, do a dummy read via <tt>VMDATAxREAD</tt>, repeatedly read the VRAM data from <tt>VMDATAxREAD</tt>.<br />
** The PPU will read from VRAM (to the VRAM latch) on every <tt>VMDATALREAD</tt> or <tt>VMDATAHREAD</tt> read.<ref>higan source code, sfc/ppu/io.cpp, by Near</ref><br />
** If the PPU is in horizontal-blank or active-display, no VRAM read will occur and the latch will contain invalid data.<br />
<br />
<br />
=== Common VMAIN values ===<br />
{| class="wikitable"<br />
|+ Common VMAIN values<br />
|-<br />
! !! VMAIN value !! Access !! Increment !! Used for<br />
|-<br />
| Word data || <tt>$80</tt> || 16 bit write to <tt>VMDATAL</tt>&nbsp;&amp;&nbsp;<tt>VMDATAH</tt> || 1 || [[Tiles|2/4/8 bpp tile]] data, [[Tilemaps|tilemap]] data and [[Offset-per-tile|offset-per-tile]] data<br />
|-<br />
| Low byte || <tt>$00</tt> || 8 bit write to <tt>VMDATAL</tt> || 1 || [[Tilemaps#Mode 7|Mode 7 tilemap]] data, low-byte of a [[Split tilemap|split tilemap]], [[1bpp tiles|1bpp tile]] data<br />
|-<br />
| High byte || <tt>$80</tt> || 8 bit write to <tt>VMDATAH</tt> || 1 || [[Tiles#Mode 7|Mode 7 tile]] data, high-byte of a [[Split tilemap|split tilemap]], [[1bpp tiles|1bpp tile]] data<br />
|-<br />
| Tilemap column || <tt>$81</tt> || 16 bit write to <tt>VMDATAL</tt>&nbsp;&amp;&nbsp;<tt>VMDATAH</tt> || 32 || One [[Scrolling a large map|tilemap column]]<br/><small>(Only 1 column can be transferred at a time, <tt>VMADD</tt> must be set before writing the next column.)<br/>(Tilemap columns are discontiguous on 64x64 tilemaps and require 2 transfers per column)</small><br />
|-<br />
| Mode 7 tilemap column || <tt>$02</tt> || 8 bit write to <tt>VMDATAL</tt> || 128 || One [[Tilemaps#Mode 7|Mode 7 tilemap]] column<br/><small>(Only 1 column can be transferred at a time, <tt>VMADD</tt> must be set before writing the next column)</small><br />
|}<br />
<br />
<br />
=== Writing word data to VRAM ===<br />
<br />
The most common value for <tt>VMAIN</tt> is <tt>0x80</tt>, which enables sequential word access to VRAM. This is useful for writing tile data (2/4/8 bpp), tilemap data and offset-per-tile data to VRAM.<br />
<br />
When the ''Address increment mode'' bit (bit 7) of <tt>VMAIN</tt> is set, word data can be written to both VRAM-chips with either:<br />
* An 8 bit write to <tt>VMDATAL</tt>, followed by a second 8 bit write to <tt>VMDATAH</tt><br />
* A 16 bit write to <tt>VMDATA</tt><br />
* A DMA to <tt>VMDATAL</tt> and <tt>VMDATAH</tt>, using the ''two registers'' DMA transfer pattern (DMAP pattern 1).<br />
<br />
<br />
<pre><br />
// Write `TileData` to VRAM word address `VRAM_BG1_TILES_WADDR`.<br />
//<br />
// REQUIRES: Force-Blank<br />
// (There might not be enough Vertical-Blank time if `TileData` is too large)<br />
.a8<br />
.i16<br />
// DB access registers<br />
<br />
// Set VMAIN to word access<br />
lda #0x80<br />
sta VMAIN<br />
<br />
// Set VRAM word address<br />
ldx #VRAM_BG1_TILES_WADDR<br />
stx VMADD<br />
<br />
<br />
// Use a 16 bit Accumulator<br />
rep #$30<br />
.a16<br />
<br />
ldx #0<br />
Loop:<br />
// Read one word of TileData and write it to VRAM<br />
lda f:TileData,x<br />
sta VMDATA<br />
<br />
inx<br />
inx<br />
cpx #TILE_DATA_SIZE<br />
bcc Loop<br />
<br />
// restore 8 bit Accumulator<br />
sep #$20<br />
.a8<br />
</pre><br />
<br />
<br />
<br />
Writing word data to VRAM using DMA is preformed using the ''two registers'' transfer pattern (DMAP pattern 1) to <tt>VMDATA</tt>.<br />
<pre><br />
// Transfer the word data at `data` to VRAM word address `vram_waddr` using DMA.<br />
//<br />
// REQUIRES: Vertical-Blank or Force-Blank<br />
// DB access registers<br />
// Uses DMA channel 0<br />
subroutine WriteTileDataToVram(vram_waddr, data, data_size):<br />
// Set VMAIN to word access<br />
VMAIN = 0x80<br />
<br />
// Set VRAM word address<br />
VMADD = vram_waddr<br />
<br />
// DMA parameters: two registers, to PPU<br />
DMAP0 = 1<br />
<br />
// B-Bus address<br />
BBAD0 = .lobyte(VMDATA)<br />
<br />
// A-Bus address<br />
A1T0 = .loword(data)<br />
A1B0 = .bankbyte(data)<br />
<br />
// Transfer size<br />
DAS0 = data_size<br />
<br />
// Start DMA transfer on channel 0<br />
MDMAEN = 1 << 0<br />
</pre><br />
<br />
<br />
<br />
=== Reading a single byte/word of VRAM ===<br />
<br />
Reading a single byte/word from VRAM can be done in a similar manner as reading from CGRAM. You should always set the <tt>VMAIN</tt> register before writing to <tt>VMADD</tt> to ensure VRAM is accessed in the intended manner.<br />
<br />
<pre><br />
// Read ONE word of VRAM data from VRAM word address `X`<br />
//<br />
// INPUT: X - VRAM word address<br />
// OUTPUT: Y - data at VRAM word address `X`<br />
//<br />
// REQUIRES: Vertical-Blank or Force-Blank.<br />
//<br />
// DB access registers<br />
.a8<br />
.i16<br />
.proc ReadOneVramWord<br />
// Set VMAIN to word access<br />
lda #0x80<br />
sta VMAIN<br />
<br />
// Set VRAM word address<br />
stx VMADD<br />
<br />
// Read VRAM<br />
ldy VMDATAREAD<br />
<br />
rts<br />
.endproc<br />
</pre><br />
<br />
<br />
<br />
=== Reading a block of VRAM ===<br />
<br />
Due to the way the [[PPU_registers#VMDATAREAD|PPU updates the vram_latch]] when accessing the <tt>VMADD</tt> and <tt>VMDATAxREAD</tt> registers, a dummy read to <tt>VMDATAxREAD</tt> is required when reading a block of contiguous VRAM data.<br />
<br />
Failure to issue a dummy read will result in an off-by-one error, with the first two bytes/words containing duplicate data from the same VRAM address.<br />
<br />
<pre><br />
// REQUIRES: Vertical-Blank or Force-Blank.<br />
// DB access registers<br />
.a8<br />
.i16<br />
// Set VMAIN to word access<br />
lda #0x80<br />
sta VMAIN<br />
<br />
// Set VRAM word address<br />
ldx #$6000<br />
stx VMADD // populates vram_latch with data at VRAM word address $6000<br />
<br />
ldy VMDATAREAD // Y = data at VRAM word address $6000<br />
ldy VMDATAREAD // Y = data at VRAM word address $6000 <-- off by one error<br />
ldy VMDATAREAD // Y = data at VRAM word address $6001<br />
ldy VMDATAREAD // Y = data at VRAM word address $6002<br />
ldy VMDATAREAD // Y = data at VRAM word address $6003<br />
</pre><br />
<br />
<br />
<br />
A block of VRAM can be read into RAM using DMA (by setting the ''direction'' bit of [[DMA_registers#DMAPn|DMAPn]] to transfer data from the B-Bus (PPU) to the A-Bus).<br />
<br />
<pre><br />
Variables:<br />
zpFarPtr - a 3 byte pointer in zero-page.<br />
<br />
// Transfer VRAM word data to WRAM using DMA.<br />
//<br />
// INPUT: X = VRAM word address<br />
// Y = data size<br />
// zpFarPtr = address to write VRAM word data to (MUST be a work-RAM or cart-RAM address)<br />
//<br />
// REQUIRES: Vertical-Blank or Force-Blank<br />
// Uses DMA channel 0<br />
//<br />
// DB access registers<br />
.a8<br />
.i16<br />
.proc ReadVramWordData<br />
// Set VMAIN to word access<br />
lda #0x80<br />
sta VMAIN<br />
<br />
// Set VRAM word address (required)<br />
stx VMADD<br />
<br />
// Dummy read (required)<br />
ldx VMDATAREAD<br />
<br />
<br />
// Setup DMA channel 0<br />
// DMA from word register VMDATAREAD to `zpFarPtr`<br />
<br />
// DMA parameters: two registers, PPU to CPU<br />
lda #$81<br />
sta DMAP0<br />
<br />
// B-Bus address<br />
lda #.lobyte(VMDATAREAD)<br />
sta BBAD0<br />
<br />
// A-Bus address<br />
ldx zpFarPtr<br />
stx A1T0<br />
lda zpFarPtr + 2<br />
sta A1B0<br />
<br />
// Transfer size (in Y register)<br />
sty DAS0<br />
<br />
// Start DMA transfer on channel 0<br />
lda #1 << 0<br />
sta MDMAEN<br />
.endproc<br />
</pre><br />
<br />
== References ==</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=MediaWiki:Loginprompt&diff=1121MediaWiki:Loginprompt2023-02-24T16:32:42Z<p>Fiskbit: Adds log-in prompt explaining how to register an account.</p>
<hr />
<div>Looking to register an account? Because of spam, accounts are currently created manually by request. Please reach out to Fiskbit or lidnariq on the [http://forums.nesdev.org/ NESdev forums] or [https://discord.gg/JSG4kuF8EK NESdev Discord server] if you'd like to contribute to the wiki.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=CPU_pinout&diff=513CPU pinout2022-06-03T00:58:58Z<p>Fiskbit: Adds missing CPU prefix on A23.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==Pinout==<br />
^<br />
/ \<br />
/ \<br />
/ \<br />
+5V -- / 1 100 \ -> CPU A7<br />
CPU A8 <- / 2 99 \ -> CPU A6<br />
CPU A9 <- / 3 . 98 \ -> CPU A5<br />
CPU A10 <- / 4 97 \ -> CPU A4<br />
CPU A11 <- / 5 96 \ -> CPU A3<br />
CPU A12 <- / 6 95 \ -> CPU A2<br />
CPU A13 <- / 7 94 \ -> CPU A1<br />
CPU A14 <- / 8 93 \ -> CPU A0<br />
CPU A15 <- / 9 92 \ -> CPU /RD<br />
CPU A16 <- / 10 91 \ -> CPU /WR<br />
CPU A17 <- / 11 90 \ -- GND<br />
CPU A18 <- / 12 89 \ -> /VECTORPULL<br />
CPU A19 <- / 13 88 \ -> UNKNOWN CLK 88<br />
CPU A20 <- / 14 87 \ -> VDA<br />
CPU A21 <- / 15 86 \ -> VPA<br />
CPU A22 <- / 16 85 \ -- +5V<br />
CPU A23 <- / 17 84 \ -> XFLAG<br />
GND -- / 18 83 \ -> MFLAG<br />
joypad IO D0 <> / 19 82 \ -> /MEMLOCK<br />
joypad IO D1 <> / 20 81 \ <- RDY<br />
joypad IO D2 <> / 21 \<br />
joypad IO D3 <> / 22 O /<br />
joypad IO D4 <> / 23 80 / -> R/W<br />
joypad IO D5 <> / 24 79 / -- GND<br />
joypad IO D6 <> / 25 78 / -> /WRAMSEL<br />
joypad IO D7 <> / 26 Nintendo 5A22 77 / -> /ROMSEL<br />
joypad 2 D0 -> / 27 Package QFP-100, 0.65mm pitch 76 / <- /ABORT<br />
joypad 2 D1 -> / 28 75 / <- HALT<br />
joypad 2 D2 -> / 29 S-CPU 74 / <- HVCMODE <br />
joypad 2 D3 -> / 30 73 / <- TM<br />
/ O 72 / -> PHI2<br />
\ 71 / -> UNKNOWN CLK 71<br />
joypad 2 D4 -> \ 31 70 / -> /DMA<br />
joypad 1 D0 -> \ 32 69 / -> /PWR<br />
joypad 1 D1 -> \ 33 68 / -> /PRD<br />
+5V -- \ 34 67 / <> CPU D7<br />
joypad 1 /OE <- \ 35 66 / <> CPU D6<br />
joypad 2 /OE <- \ 36 65 / <> CPU D5<br />
OUT0 <- \ 37 64 / <> CPU D4 Orientation:<br />
OUT1 <- \ 38 63 / <> CPU D3 --------------------<br />
OUT2 <- \ 39 62 / <> CPU D2 80 51<br />
REFRESH <- \ 40 61 / <> CPU D1 | |<br />
TCKSEL0 -> \ 41 60 / <> CPU D0 .-----------.<br />
TCKSEL1 -> \ 42 59 / -- +5V 81-|O Nintendo |-50<br />
HBLANK -> \ 43 58 / -> PA7 | S-CPU |<br />
VBLANK -> \ 44 57 / -> PA6 100-|. 5A22 O|-31<br />
/NMI -> \ 45 56 / -> PA5 '-----------'<br />
/IRQ -> \ 46 55 / -> PA4 | |<br />
GND -- \ 47 54 / -> PA3 01 30<br />
SYSTEM CLK -> \ 48 53 / -> PA2 <br />
REFRESH /ENABLE -> \ 49 52 / -> PA1 Legend:<br />
/RESET -> \ 50 51 / -> PA0 ----------------------------<br />
\ / --[5A22]-- Power, n/a<br />
\ / ->[5A22]<- 5A22 input<br />
\ / <-[5A22]-> 5A22 output<br />
V <>[5A22]<> Bidirectional<br />
<br />
==Signal descriptions==<br />
* '''VPA, VDA''': Valid Data Address and Valid Program Address, useful for caching or single-step.<br />
** VPA = 0, VDA = 0: Address bus may be invalid.<br />
** VPA = 0, VDA = 1: Valid data address<br />
** VPA = 1, VDA = 0: Valid program address<br />
** VPA = 1, VDA = 1: Opcode fetch</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Backgrounds&diff=494Backgrounds2022-06-01T04:17:40Z<p>Fiskbit: Adds padding to priorities in BG mode table.</p>
<hr />
<div>The SNES backgrounds consist of one or more layers of [[tilemaps]].<br />
<br />
There are 8 available background modes, which can be changed at any time via [[PPU registers#BGMODE|BGMODE]], even mid-screen.<br />
<br />
Each mode has 1-4 layers, consisting of [[tiles]] that are 2bpp (4-color), 4bpp (16-color), or 8bpp (256-color).<br />
<br />
{{anchor|Priority table}}<br />
{| class="wikitable"<br />
! Mode<br />
! style="background:#ffdddd" | BG1<br />
! style="background:#ddddff" | BG2<br />
! style="background:#ddffdd" | BG3<br />
! style="background:#ffddff" | BG4<br />
! [[#High resolution|Hi-res]]<br />
! [[#Priority|Priority]] (front ↔ back)<br />
|-<br />
! [[#Mode 0|0]]<br />
| 2 || 2 || 2 || 2 ||<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span> <span style="background-color:#ddddff>2b</span> <span style="background-color:#ffffdd>S2</span> <span style="background-color:#ffdddd>1a</span> <span style="background-color:#ddddff>2a</span> <span style="background-color:#ffffdd>S1</span> <span style="background-color:#ddffdd>3b</span> <span style="background-color:#ffddff>4b</span> <span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddffdd>3a</span> <span style="background-color:#ffddff>4a</span></tt><br />
|-<br />
! [[#Mode 1|1]]<br />
| 4 || 4 || 2 || ||<br />
| <tt style="white-space: nowrap"><span style="background-color:#ddffdd>3c</span> <span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span> <span style="background-color:#ddddff>2b</span> <span style="background-color:#ffffdd>S2</span> <span style="background-color:#ffdddd>1a</span> <span style="background-color:#ddddff>2a</span> <span style="background-color:#ffffdd>S1</span> <span style="background-color:#ddffdd>3b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddffdd>3a</span></tt><br />
|-<br />
! [[#Mode 2|2]]<br />
| 4 || 4 ||[[Offset-per-tile|OPT]]|| ||<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span> <span style="background-color:#ddddff>2b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddddff>2a</span></tt><br />
|-<br />
! [[#Mode 3|3]]<br />
| 8 || 4 || || ||<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span> <span style="background-color:#ddddff>2b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddddff>2a</span></tt><br />
|-<br />
! [[#Mode 4|4]]<br />
| 8 || 2 ||OPT|| ||<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span> <span style="background-color:#ddddff>2b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddddff>2a</span></tt><br />
|-<br />
! [[#Mode 5|5]]<br />
| 4 || 2 || || || ✔<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span> <span style="background-color:#ddddff>2b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddddff>2a</span></tt><br />
|-<br />
! [[#Mode 6|6]]<br />
| 4 || ||OPT|| || ✔<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffdddd>1b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span></tt><br />
|-<br />
! [[#Mode 7|7]]<br />
| 8 ||(7)|| || ||<br />
| <tt style="white-space: nowrap">&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S2</span> <span style="background-color:#ddddff>2b</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffdddd>1a</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color:#ffffdd>S0</span> <span style="background-color:#ddddff>2a</span></tt><br />
|}<br />
<br />
== Properties ==<br />
<br />
Each of the 4 BG layers has several independent properties:<br />
* [[Tilemap]] VRAM address: [[PPU registers#BGnSC|BGnSC]]<br />
* [[Tiles|tileset]] (CHR) VRAM address: [[PPU registers#BG12NBA|BG12NBA]], [[PPU registers#BG34NBA|BG34NBA]]<br />
* Multiple tilemaps in 1x1, 2x1, 1x2, 2x2 arrangements: [[PPU registers#BGnSC|BGnSC]]<br />
* Tile size of 8x8 (or 16x8) or 16x16: [[PPU registers#BGMODE|BGMODE]]<br />
* Scroll position: [[PPU registers#BGnHOFS|BGnHOFS]], [[PPU registers#BGnVOFS|BGnVOFS]]<br />
* [[Windows]]<br />
* [[Mosaic]]: [[PPU registers#MOSAIC|MOSAIC]]<br />
<br />
Each of the 4 BG layers can be independently activated for the main and sub-screen.<br />
* If [[color math]] is enabled, the main screen can be blended with the subscreen.<br />
* If high resolution is enabled ([[PPU registers#SETINI|SETINI]]) the main-screen appears on even columns, and the sub-screen appears on odd columns.<br />
* If neither color math nor high resolution is enabled, only the main screen is seen.<br />
<br />
The 4 BG layers and sprites (OBJ) are composited to make the main and sub-screens, layered according to their [[priority]]<br />
<br />
== Priority ==<br />
<br />
The way background layers and [[sprites]] are composited on top of each other is different for each mode.<br />
<br />
Each of the background layers BG1-4 is further subdivided into a high and low priority layer, using [[tilemap]] attributes.<br />
Sprites (OBJ) are also subdivided into 4 layers of their own using their [[OAM]] attributes.<br />
<br />
In the [[#Priority table|mode table above]]:<br />
* <tt><span style="background-color:#ffffdd>S3</span> <span style="background-color:#ffffdd>S2</span> <span style="background-color:#ffffdd>S1</span> <span style="background-color:#ffffdd>S0</span></tt> are the [[sprite]] layers with priority 3, 2, 1 and 0.<br />
* <tt><span style="background-color:#ffdddd>1b</span> <span style="background-color:#ffdddd>1a</span></tt> is BG1 layers with high (b) and low (a) priority.<br />
* <tt><span style="background-color:#ddddff>2b</span> <span style="background-color:#ddddff>2a</span></tt> is BG2 layers with high (b) and low (a) priority.<br />
* <tt><span style="background-color:#ddffdd>3c</span> <span style="background-color:#ddffdd>3b</span> <span style="background-color:#ddffdd>3a</span></tt> BG3 in mode 1 has two different high priority positions, selected by [[PPU registers#BGMODE|BGMODE]]. Depending on the BGMODE setting, high can be either at the top (c) or middle (b), the low (a) priority is always in the same position.<br />
* <tt><span style="background-color:#ffddff>4b</span> <span style="background-color:#ffddff>4a</span></tt> is BG4 layers with high (b) and low (a) priority.<br />
<br />
In mode 7 BG1 only has one layer (a), but [[#EXTBG|EXTBG]] can enable BG2 split into two layers (b, a).<br />
<br />
== High resolution ==<br />
<br />
High resolution is automatically used in mode 5 and 6, but can be manually enabled for other modes via [[PPU registers#SETINI|SETINI]].<br />
<br />
This doubles the horizontal resolution of the SNES from 256 to 512 pixels.<br />
<br />
The main-screen appears on every even column, and the sub-screen appears on every odd column.<br />
[[Color math]] may still be available, but since the sub-screen is no longer a hidden layer it has limited use.<br />
<br />
In [[#Mode 5|modes 5 and 6]] high-resolution is forced, and the background layers are automatically de-interleaved into the main and sub-screens,<br />
rendering each tile at half its usual width, but with twice the density of pixels.<br />
<br />
In other modes you would have to compose the main/sub-screen with alternating columns.<br />
<br />
However, because 512px output tends to get blurred significantly on TVs through normal composite output,<br />
some games use the alternating columns as an alternative to [[color math]], providing something like a 50% "blend" of the main and sub screens.<br />
This usage is sometimes known as '''pseudo hi-res'''. (See: Jurassic Park, Kirby's Dream Land 3.)<br />
<br />
=== Interlacing ===<br />
<br />
Interlacing can also be enabled via [[PPU registers#SETINI|SETINI]].<br />
<br />
Interlacing shifts every second frame (field) down by half a line. The result is that over 2 frames you get twice the vertical resolution, but at half the framerate.<br />
[[PPU registers#STAT78|STAT78]] can be used to determine whether you're currently on an even or odd field.<br />
<br />
In [[#Mode 5|modes 5 and 6]], if interlacing is enabled the vertical tile density is automatically doubled across the two fields.<br />
<br />
In other modes, you would have to manually change the picture between even and odd fields to make use of the increased vertical resolution.<br />
<br />
The lowered framerate has the drawback of a visible "flickering". Perception of this flickering varies from person to person,<br />
but it is intensified by images with sharp vertical contrast. Normally interlaced video for broadcast is vertically filtered/blurred to reduce this effect,<br />
but this is harder to accomplish on the SNES with its palette limitations.<br />
<br />
Care should be taken not to switch interlacing on or off too frequently in the middle of a game.<br />
On most modern televisions and capture devices, doing so will often cause the signal to drop and re-synchronize for a few seconds (or more).<br />
You might wish to wait for user input to confirm before proceeding into action, after swiching into or out of interlaced mode.<br />
Older CRT televisions can generally switch to interlacing instantly, though it cannot be changed mid-frame.<br />
<br />
== Mode 0 ==<br />
The only mode which has 4 independent BG layers. Its drawback is that each layer is only 2bpp (4 color).<br />
<br />
Each of the 4 layers and choose one of 8 4-color palettes from a different subset of [[CGRAM]]:<br />
* BG0 at CGRAM 0<br />
* BG1 at CGRAM 16<br />
* BG2 at CGRAM 32<br />
* BG3 at CGRAM 48<br />
<br />
== Mode 1 ==<br />
The most commonly used mode, which has two main 4bpp (16 color) layers BG1 and BG2, and one auxiliar 2bpp (4 color) layer BG3.<br />
<br />
BG3 has an additional [[priority]] control in mode 1. Its priority bit in [[PPU registers#BGMODE|BGMODE]] allows it to be rendered either above or below BG1 and BG2.<br />
<br />
BG3 selects a palette from the first 16 entries of [[CGRAM]].<br />
<br />
In many games, BG1 and BG2 are used for a colourful main background with parallax, and BG3 to overlay a HUD or text box.<br />
<br />
BG3 can also be useful for things like a blended cloud or fog in the foreground, or a third parallax layer in the deep background. (Super Metroid has many good examples.)<br />
<br />
== Mode 2 ==<br />
Has two 4bpp layers like mode 1, but BG3 is used to encode [[offset-per-tile]] for BG1 and BG2, instead of being a visible layer.<br />
<br />
{{anchor|Mode 3|Mode 4}}<br />
== Mode 3 & 4 ==<br />
Mode 3 has an 8bpp BG1 layer, allowing use of all 256 colors of [[CGRAM]]. This can also be used as [[direct color]], bypassing CGRAM entirely.<br />
<br />
Mode 3 also has a 4bpp (16 color) auxiliary layer BG2.<br />
<br />
Mode 4 instead has a 2bpp (2 color) auxiliary layer BG2, and BG3 is used to encode [[offset-per-tile]]. (BG2 palettes are stored in the first 16 entries of [[CGRAM]].)<br />
<br />
{{anchor|Mode 5|Mode 6}}<br />
== Mode 5 & 6 ==<br />
Mode 5 and 6 force [[#High resolution|high resolution]] on, and automatically divide the background so that the tiles appear at double horizontal density.<br />
<br />
The 8x8 pixel tilemap tile size selectable via [[PPU registers#BGMODE|BGMODE]] is replaced with a 16x8 pixel tile mode instead. The 16x16 mode is still 16x16.<br />
<br />
If interlacing is enabled ([[PPU registers#SETINI|SETINI]]), the vertical tile density is automatically doubled as well, giving the option for vertical high-resolution as well.<br />
<br />
Horizontal scrolling in these modes only has a resolution of 2 hi-res pixels (i.e. 1/256 of the screen per increment), but when interlaced the vertical scrolling has fine control (1/480).<br />
<br />
Mode 5 has a 4bpp (16 color) BG1, and an auxiliary 2bpp (4 color) BG2.<br />
<br />
Mode 6 is like mode 5 but replaces BG2 with an invisible BG3 providing [[offset-per-tile]].<br />
<br />
== Mode 7 ==<br />
<br />
Mode 7 ignores most of the layer configuration options, and instead always occupies the entire first half of VRAM with a 128x128 tilemap, and 256 available 8x8 tiles.<br />
:See: [[Tilemaps#Mode 7|Mode 7 tilemaps]], [[Tiles#Mode 7|Mode 7 tiles]].<br />
<br />
This provides a single background layer that has a unique transformation property.<br />
<br />
The [[PPU registers#M7SEL|M7SEL]] register provides a few unusual properties just for mode 7:<br />
* The entire tilemap can be horizontally or vertically flipped.<br />
* Outside the boundary of the square, three options are provided:<br />
** Transparency<br />
** Fill with tile 0<br />
** Infinite horizontal and vertical wrapping (repetition) of the tilemap<br />
<br />
Mode 7 tiles can use [[direct color]], if desired.<br />
<br />
Sometimes mode 7 is used for large rotating boss characters. Even though it has only 1 background layer, sprites can be used to draw things around it that would normally be "background", and another BG mode might be switched to with HDMA to enable a solid floor background at the bottom, for example.<br />
<br />
=== EXTBG ===<br />
<br />
Normally mode 7 is a single layer on BG1 only, but [[PPU registers#SETINI|SETINI]] can be used to enable '''Mode 7 EXTBG''' which activates BG2 as a duplicate of BG1, but split into two layers.<br />
<br />
EXTBG BG2 treats the high bit of each tile's pixel color like a tilemap priority bit, allowing BG2 to be split into two layers which can appear above and below sprites. This sort of makes BG2 into two "7bpp" layers but they are not independently transformed or scrolled.<br />
<br />
=== Affine Transformation ===<br />
<br />
Instead of rendering normally, this tilemap (effectively a 1024x1024 pixel square) can be given an arbitrary 2D [https://en.wikipedia.org/wiki/Affine_transformation affine transformation], which means this is a square that can:<br />
* Translate or slide its position up, down, left or right.<br />
* Rotate at any angle.<br />
* Zoom in and out or be squashed, but the scaling must be uniform along any axis (i.e. it can stretch along a straight line, but it cannot "bend").<br />
* Shear or skew.<br />
<br />
It is conceptually similar to [https://en.wikipedia.org/wiki/Texture_mapping texture mapping] a single quad on a modern GPU. Conversely, it is also like selecting a [https://en.wikipedia.org/wiki/Parallelogram parallelogram] region from the tilemap, and stretching its four corners to the rectangle of the screen.<br />
<br />
The affine transformation can be changed every scanline via [[HDMA]], allowing versatile perspective and distortion effects.<br />
: See: [[Mode 7 perspective effects]]<br />
<br />
The usual scrolling registers for [[PPU registers#M7HOFS|M7HOFS]], [[PPU registers#M7VOFS|M7VOFS]], which pre-scroll the tilemap before transformation.<br />
<br />
The affine transformation is applied by [[PPU registers#M7A|M7A]], [[PPU registers#M7B|M7B]], [[PPU registers#M7C|M7C]], [[PPU registers#M7D|M7D]], with an additional pivot-point center offset via [[PPU registers#M7X|M7X]], [[PPU registers#M7Y|M7Y]].<br />
<br />
ABCD defines a [https://en.wikipedia.org/wiki/Transformation_matrix transformation matrix], which combined with the offset and pivot maps screen pixel coordinats (Sx,Sy) to texel coordinates (Tx,Ty):<br />
+-- --+ +-- --+ +-- --+ +- -+<br />
| M7A M7B | | Sx + M7HOFS - M7X | | M7X | | Tx |<br />
| | * | | + | | = | |<br />
| M7C M7D | | Sy + M7VOFS - M7Y | | M7Y | | Ty |<br />
+-- --+ +-- --+ +-- --+ +- -+<br />
<br />
==== Affine Matrix ====<br />
<br />
'''M7A''', '''M7B''', '''M7C''', '''M7D''' together define how to map the tilemap "texture" to the screen, as pixels are rasterized left to right, top to bottom. In this explanation a ''pixel'' is an output pixel on the screen, and a ''texel'' is the color fetched from the 1024x1024 background tilemap. Each of these is an 8.8 fixed point value.<br />
<br />
When you move one ''pixel'' to the right on the screen:<br />
* '''M7A''' is how many ''texels'' to move to the right on the background.<br />
* '''M7C''' is how many ''texels'' to move down.<br />
<br />
When you move one ''pixel'' down on the screen:<br />
* '''M7B''' is how many ''texels'' to move right.<br />
* '''M7D''' is how many ''texels'' to move down.<br />
<br />
In modern computer graphics terms: (M7A,M7C) and (M7B,M7D) are [https://en.wikipedia.org/wiki/Vector_(mathematics_and_physics) 2D vectors] defining Δ''u'' and Δ''v'' for [https://en.wikipedia.org/wiki/Texture_mapping texture mapping].<br />
<br />
This is why the [[Init code|recommended default values]] of (1,0) (0,1) makes mode 7 behave like a normal background. 1 pixel to the right = 1 texel to the right. 1 pixel down = 1 texel down.<br />
<br />
'''Scaling''' can be accomplished by changing the length of these vectors.<br />
* (2,0) (0,1) will move 2 texels right for every 1 pixel, shrinking by 1/2 in the horizontal.<br />
* (1,0) (0,-0.1) will move 0.1 texels up for every 1 pixel, stretching by 10 in the vertical and flipping upside down.<br />
<br />
'''Rotation''' can be accomplished by rotating these vectors.<br />
* (''cos'' Ï´, ''sin'' Ï´) (''-sin'' Ï´, ''cos'' Ï´) form a standard [https://en.wikipedia.org/wiki/Rotation_matrix rotation matrix] that will rotate the map by Ï´ degrees.<br />
:: On screen: it will rotate the map counter-clockwise relative to the pivot-point.<br />
:: On map: it will rotate the view parallelogram clockwise.<br />
* (0.866, 0.500) (-0.500, 0.866) will rotate by 30 degrees (CCW on screen, CW on map).<br />
<br />
'''[https://en.wikipedia.org/wiki/Shear_mapping Shearing]''' creates a slanted mapping by adding to one coordinate unevenly.<br />
* (1,0) (0.2,1) causes the background to gradually slide to the left as it proceeds down the screen.<br />
<br />
Scaling, rotation, and shearing together can be combined into a single [https://en.wikipedia.org/wiki/Transformation_matrix transformation matrix] in A/B/C/D. This can be computed by [https://en.wikipedia.org/wiki/Matrix_multiplication matrix multiplication].<br />
<br />
==== Center Adjustment ====<br />
<br />
'''M7X''' and '''M7Y''' define a ''texel'' coordinate that becomes the center (a.k.a. pivot-point) of the scaling/rotation applied by the affine transformation matrix ABCD. This allows you to rotate around some other point besides the top left of the map.<br />
<br />
'''M7HOFS''' and '''M7VOFS''' define a starting point for rasterization. After the transformation ABCD/XY is applied, this is a ''pixel'' coordinate that shifts the top-left of the screen. E.g. increasing M7HOFS by 1 will have the effect of moving whatever is in view 1 pixel to the left. With the default matrix this is exactly the same as just scrolling the map in other modes.<br />
<br />
==== Summary ====<br />
<br />
On screen:<br />
* Start with a view of the top left 256x224 pixels of the tilemap.<br />
* From that view, pick a pivot-point on the map (M7X,M7Y) and rotate and scale around that point (ABCD).<br />
* Now (M7HOFS,M7VOFS) will move (right,down) in screen-space, scrolling over the transformed map.<br />
<br />
On the map:<br />
* (A,C) and (B,D) are vectors that define the angle and size of a parallelogram that will be the screen's view.<br />
* The top-left corner of the parallelogram is a more complicated computation involving all 8 register values.<br />
* Increasing M7HOFS or M7VOFS by 1 will move the top-left corner along the direction of the parallelogram sides equivalent to 1 screen pixel.<br />
<br />
== See Also ==<br />
* [[Tilemaps]]<br />
* [[Tiles]]<br />
* [[Uncommon graphics mode games]]<br />
<br />
== External Links ==<br />
* [https://novasquirrel.github.io/Mode7Preview/ Novasquirrel: Mode 7 Preview] - Javascript preview of mode 7 register effect<br />
* [https://dev.telinc1.com/mode7/index.html Telinc1: Mode 7 Simulator] - Javascript preview of mode 7 register effect<br />
<br />
== References ==<br />
* [[SNES Development Manual]] Book I 2-5-1 Rotation/Enlargement/Reduction<br />
* [https://www.youtube.com/watch?v=5SBEAZIfDAg Retro Game Mechanics Explained: SNES Background Modes 0-6] - video<br />
* [https://www.youtube.com/watch?v=3FVN_Ze7bzw Retro Game Mechanics Explained: SNES Background Mode 7] - video<br />
* [https://www.youtube.com/watch?v=AnEuk8Vj3w0 Retro Game Mechanics Explained: SNES Background Modes Higher Resolutions] - video</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Sprites&diff=493Sprites2022-06-01T04:13:27Z<p>Fiskbit: Notes which direction the priorities go in.</p>
<hr />
<div>Sprites allow 16-color graphics tiles to be rendered at freely placed locations on the screen, independent of the tile grids that [[backgrounds]] are constrained to.<br />
<br />
Terms:<br />
* '''OAM''' - the "object attribute memory", the memory storage area for sprite data.<br />
* '''OBJ''' - the rendering layer for sprites.<br />
<br />
== OBJ ==<br />
<br />
Each sprite places a rectangular group of tiles at a given location on the screen.<br />
<br />
All sprites use 4bpp 16-color [[tiles]]. Each sprite selects one of 8 palettes from the last half of [[CGRAM]].<br />
<br />
The [[PPU registers#OBJSEL|OBJSEL]] register selects the VRAM location for sprite tiles, and also two rectangular sizes that sprites can use during rendering.<br />
<br />
Sprites can be squares of 8x8, 16x16, 32x32, 64x64, and rectangular 16x32 or 32x64 pixels in various allowed combinations. There is no 8x16 sprite size like the NES has.<br />
They are made of groups of tiles, adjacent horizontally (+1) and vertically (+16) on a 16 tile wide grid in VRAM.<br />
<br />
Sprite can be placed above or below various background layers using their priority attribute.<br />
<br />
'''[[Color math]]:'''<br />
* The OBJ layer cannot be subdivided between the main and sub screens, so sprites cannot use color math against each other.<br />
* On the main screen, color math only applies to sprites using palettes 4-7, allowing palettes 0-3 to act as opaque sprites while the others are blending.<br />
* On the sub screen, color math applies to all sprites, which can be used as an alternative if all palettes need to participate.<br />
<br />
== OAM ==<br />
<br />
OAM is a 544 byte internal memory, defining the properties of 128 sprites to be rendered.<br />
<br />
Internally the memory is divided into words. A single word is not updated until both of its bytes are written. OAM is written through [[PPU registers#OAMDATA|OAMDATA]].<br />
<br />
The first 512 bytes (256 words) of OAM are 4-byte groups which define most of the properties of a sprite: (i = sprite index 0-127)<br />
<br />
byte 7 bit 0<br />
----- ---------<br />
i*4+0: XXXX XXXX - Low 8 bits of X position<br />
i*4+1: YYYY YYYY - Y position<br />
i*4+2: TTTT TTTT - Low 8 bits of tile<br />
i*4+3: VHPP CCCt<br />
|||| |||+-- High bit of tile a.k.a. "name select"<br />
|||| +++--- Palette selection<br />
||++------- Priority (3..0 = highest..lowest)<br />
++--------- Flip vertical (V) and horizontal (H)<br />
<br />
The final 32 bytes (16 words) contain some additional properties, with 4 sprites packed into each byte:<br />
<br />
byte 7 bit 0<br />
----- ---------<br />
i/4: DdCc BbAa<br />
|||| |||+-- Sprite i high bit of x<br />
|||| ||+--- Sprite i size selection<br />
|||| ++---- Sprite i+1 x/size<br />
||++------- Sprite i+2 x/size<br />
++--------- Sprite i+3 x/size<br />
<br />
* X position gives the top left corner of the sprite. The high bit acts as a -256, allowing a sprite to be placed partially off the left side of the screen.<br />
* Y position is only 8 bits, but will wrap across the top of the screen. With normal [[PPU registers#SETINI|overscan blanking]], sprites up to size 32x32 can be completely hidden at Y=224.<br />
* Tile selection is 9 bits, but the high bit is also selecting from the second sprite page, which does not have to be contiguous with the first (See: [[PPU registers#OBSEL|OBSEL]]).<br />
* Priority allows placement in front or behind various [[Backgrounds|background]] layers.<br />
* Flip can be applied horizontally or vertically to a sprite.<br />
* The size selection allows one of 2 sprite sizes to be used for each sprite, chosen via [[PPU registers#OBSEL|OBSEL]].<br />
<br />
There is no visibility flag for sprites, so all 128 are always active, but they can be placed offscreen. Y=224 is sufficiently offscreen for most cases, but 64x64 sprites might be a problem. The high bit of X can be used as well to move the sprite offscreen, but make sure the low byte of X is not $00, as a hardware bug causes sprites at X=$100 to count against the per-scanline limit (see: [[Errata]]).<br />
<br />
For simple needs, it can be useful to set the last 32 bytes to a suitable default instead of dealing with the inconvenience of recalculating and repacking the information each time.<br />
<br />
Like the NES, sprites appear 1 line lower than their Y value, however because the first line of rendering is always hidden on SNES, a sprite with Y=0 will appear to begin on the first visible line. However, a background with Y scroll of 0 will appear to have its top pixel cut off by the hidden line. Thus either sprite Y should be adjusted 1 line higher, or background scroll 1 line lower so that the two will correspond correctly.<br />
<br />
== See Also ==<br />
<br />
* [[OAM layout]] - diagram of OAM memory tables</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Errata&diff=436Errata2022-05-31T11:56:58Z<p>Fiskbit: More clarity on vblank NMI behavior.</p>
<hr />
<div>This page describes quirks in the SNES hardware that programmers need to be aware of. They could be mistakes in the hardware's implementation, or just unintuitive behavior.<br />
<br />
== Video ==<br />
* [[Offset-per-tile]] never affects the first (leftmost) tile.<br />
* When color math is set to affect sprites, it will only affect sprites using the last four palettes.<br />
* If the vblank NMI in [[MMIO registers#NMITIMEN|NMITIMEN]] transitions from disabled to enabled while the vblank flag in [[MMIO registers#RDNMI|RDNMI]] is set, an NMI will trigger immediately. This can cause an NMI to occur somewhere other than the start of vblank, or cause more than one NMI to occur in a single vblank.<br />
* When there are too many sprite tiles on a scanline, the SNES will drop the <em>highest priority sprites</em> instead of the lowest priority ones.<br />
* The SNES programming manual describes a situation where the Time Over flag is erroneously set when the first hardware sprite is 16x16, 32x32, or 64x64, has a horizontal position of 0-255, and other hardware sprites have negative horizontal positions.<br />
* The SNES programming manual says that a hardware sprite should not have its horizontal position set to $100.<br />
** This seems to be because that causes a sprite to contribute to the scanline limits when it should not <ref>[https://wiki.superfamicom.org/sprites Super Famicom Development Wiki]: Sprites</ref><br />
* [[PPU registers#INIDISP|INIDISP]] (register $2100) problems<br />
** Changing the brightness is not instant. On a 3-chip SNES, it may only take a few pixels to change the brightness, but on a 1-chip SNES it may be a gradual fade that takes 72 pixels or more.<br />
*** This can be a problem for games that extend vblank by disabling rendering and enabling it several scanlines into the frame. For this use-case, it's recommended to disable rendering by writing <code>$8F</code> (or $80 ORed with whatever the desired brightness is) to INIDISP instead of <code>$80</code>, so that the brightness is not changed as rendering is enabled.<br />
** INIDISP early read bug: When INIDISP is written to, the PPU doesn't wait for the value to be put on the bus before attempting to read it. This means that the SNES will end up rendering about one pixel where INIDISP has been set to whatever was on the data bus before the correct value. For instructions that don't use indirect addressing, this will likely be the last byte of the instruction.<br />
*** Workaround: Use long addressing to write to INIDISP during rendering, and take advantage of how PPU registers are available in many different banks. <code>STA $8F2100</code> will put $8f on the bus before the written value, and <code>STA $0F2100</code> will put $0f on the bus before the written value, and so on.<br />
* 16x32 sprites do not work correctly with [[PPU_registers#SETINI_-_Screen_Mode/Video_Select_($2133_write)|OBJ interlacing]]<ref>[https://forums.nesdev.org/viewtopic.php?t=21389 Forum thread]: 16x32 sprites in interlaced mode?</ref><br />
** When OBJ interlacing is on, 16x32 sprites are treated as if they are 16x16 - the bottom 16x16 is ignored, and the top 16x16 is squished into 16x8. 32x64 sprites behave as expected.<br />
* 16x32 and 32x64 sprites do not handle being vertically flipped correctly.<br />
<br />
== Audio ==<br />
* The gauss interpolation table has some mistakes in it <ref>[https://problemkaputt.de/fullsnes.htm#snesapudspbrrpitch Fullsnes]: SNES APU DSP BRR Pitch</ref><br />
* The SNES programming manual warns that writing to the first two SPC700 communication registers ($2140 and $2141) with a 16-bit write can also write to $2143 <ref>[[SNES Development Manual]] Book 1, section 3-9-6: Sound Programming Cautions</ref><ref>[https://wiki.superfamicom.org/spc700-reference#communication-ports-74 Super Famicom Development Wiki]: SPC700 Reference</ref><br />
** This may be difficult to trigger or perhaps not actually exist<ref>[https://forums.nesdev.org/viewtopic.php?p=279472#p279472 Forum post]: APU crosstalk 16-bit bug</ref><br />
* Writing to SPC700 communication registers ($2140, $2141, $2142, $2143) at the same time the other processor reads it can result in incorrect data being read.<br />
** A SPC700 program may want to read twice and only proceed when two subsequent reads have the same value.<br />
<br />
== DMA ==<br />
* Some A-Bus addresses are invalid: <ref>higan source code, [https://github.com/higan-emu/higan/blob/master/higan/sfc/cpu/dma.cpp sfc/cpu/dma.cpp], by Near</ref><br />
** The A-Bus address cannot access a B-Bus address ($21xx)<br />
** The A-Bus address cannot access the MMIO or DMA registers ($4000-$41ff, $4200-$421f, $4300-$437f)<br />
** The A-Bus address cannot be a Work-RAM address if the B-Bus address is WMDATA ($2180). This means DMA cannot be used to copy from one section of the SNES's RAM to another.<br />
* On version 1 of the 5A22 chip ("S-CPU"), the chip can crash if [[DMA]] finishes right before [[HDMA]] happens. This is generally only a problem for games that want to use DMA to clear RAM, as that's the main reason to use DMA during rendering.<br />
* On version 2 of the 5A22 chip ("S-CPU-A"), a recent HDMA transfer to/from INIDISP (Meaning that BBADn is set to zero $00) can make a DMA transfer fail. Nothing will happen and the DMA size registers (DASnL, DASnH) will be unchanged, instead of zero like they normally are after a DMA has been completed.<br />
** Workaround: Set BBADn to $ff instead, and set the transfer pattern to 1. This will cause HDMA to write to $21ff (nothing) and then $2100 (INIDISP). Both bytes should be set to the same value to prevent the INIDISP early read bug.<br />
** S-CPU (the first version), S-CPU-B and the 1-CHIP SNES are not affected by this bug.<br />
<br />
== References ==<br />
:* https://undisbeliever.net/snesdev/registers/inidisp.html#glitches-and-hardware-bugs<br />
:* [[SNES Development Manual]] Book 1, section 2-25-1: Documented Problems<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Errata&diff=435Errata2022-05-31T11:55:57Z<p>Fiskbit: /* Video */ Further explains the behavior where enabling NMIs during vblank triggers an immediate NMI.</p>
<hr />
<div>This page describes quirks in the SNES hardware that programmers need to be aware of. They could be mistakes in the hardware's implementation, or just unintuitive behavior.<br />
<br />
== Video ==<br />
* [[Offset-per-tile]] never affects the first (leftmost) tile.<br />
* When color math is set to affect sprites, it will only affect sprites using the last four palettes.<br />
* If the vblank NMI in [[MMIO registers#NMITIMEN|NMITIMEN]] was disabled and is enabled while the vblank flag in [[MMIO registers#RDNMI|RDNMI]] is set, an NMI will trigger immediately. This can cause an NMI to occur somewhere other than the start of vblank, or cause more than one NMI to occur in a single vblank.<br />
* When there are too many sprite tiles on a scanline, the SNES will drop the <em>highest priority sprites</em> instead of the lowest priority ones.<br />
* The SNES programming manual describes a situation where the Time Over flag is erroneously set when the first hardware sprite is 16x16, 32x32, or 64x64, has a horizontal position of 0-255, and other hardware sprites have negative horizontal positions.<br />
* The SNES programming manual says that a hardware sprite should not have its horizontal position set to $100.<br />
** This seems to be because that causes a sprite to contribute to the scanline limits when it should not <ref>[https://wiki.superfamicom.org/sprites Super Famicom Development Wiki]: Sprites</ref><br />
* [[PPU registers#INIDISP|INIDISP]] (register $2100) problems<br />
** Changing the brightness is not instant. On a 3-chip SNES, it may only take a few pixels to change the brightness, but on a 1-chip SNES it may be a gradual fade that takes 72 pixels or more.<br />
*** This can be a problem for games that extend vblank by disabling rendering and enabling it several scanlines into the frame. For this use-case, it's recommended to disable rendering by writing <code>$8F</code> (or $80 ORed with whatever the desired brightness is) to INIDISP instead of <code>$80</code>, so that the brightness is not changed as rendering is enabled.<br />
** INIDISP early read bug: When INIDISP is written to, the PPU doesn't wait for the value to be put on the bus before attempting to read it. This means that the SNES will end up rendering about one pixel where INIDISP has been set to whatever was on the data bus before the correct value. For instructions that don't use indirect addressing, this will likely be the last byte of the instruction.<br />
*** Workaround: Use long addressing to write to INIDISP during rendering, and take advantage of how PPU registers are available in many different banks. <code>STA $8F2100</code> will put $8f on the bus before the written value, and <code>STA $0F2100</code> will put $0f on the bus before the written value, and so on.<br />
* 16x32 sprites do not work correctly with [[PPU_registers#SETINI_-_Screen_Mode/Video_Select_($2133_write)|OBJ interlacing]]<ref>[https://forums.nesdev.org/viewtopic.php?t=21389 Forum thread]: 16x32 sprites in interlaced mode?</ref><br />
** When OBJ interlacing is on, 16x32 sprites are treated as if they are 16x16 - the bottom 16x16 is ignored, and the top 16x16 is squished into 16x8. 32x64 sprites behave as expected.<br />
* 16x32 and 32x64 sprites do not handle being vertically flipped correctly.<br />
<br />
== Audio ==<br />
* The gauss interpolation table has some mistakes in it <ref>[https://problemkaputt.de/fullsnes.htm#snesapudspbrrpitch Fullsnes]: SNES APU DSP BRR Pitch</ref><br />
* The SNES programming manual warns that writing to the first two SPC700 communication registers ($2140 and $2141) with a 16-bit write can also write to $2143 <ref>[[SNES Development Manual]] Book 1, section 3-9-6: Sound Programming Cautions</ref><ref>[https://wiki.superfamicom.org/spc700-reference#communication-ports-74 Super Famicom Development Wiki]: SPC700 Reference</ref><br />
** This may be difficult to trigger or perhaps not actually exist<ref>[https://forums.nesdev.org/viewtopic.php?p=279472#p279472 Forum post]: APU crosstalk 16-bit bug</ref><br />
* Writing to SPC700 communication registers ($2140, $2141, $2142, $2143) at the same time the other processor reads it can result in incorrect data being read.<br />
** A SPC700 program may want to read twice and only proceed when two subsequent reads have the same value.<br />
<br />
== DMA ==<br />
* Some A-Bus addresses are invalid: <ref>higan source code, [https://github.com/higan-emu/higan/blob/master/higan/sfc/cpu/dma.cpp sfc/cpu/dma.cpp], by Near</ref><br />
** The A-Bus address cannot access a B-Bus address ($21xx)<br />
** The A-Bus address cannot access the MMIO or DMA registers ($4000-$41ff, $4200-$421f, $4300-$437f)<br />
** The A-Bus address cannot be a Work-RAM address if the B-Bus address is WMDATA ($2180). This means DMA cannot be used to copy from one section of the SNES's RAM to another.<br />
* On version 1 of the 5A22 chip ("S-CPU"), the chip can crash if [[DMA]] finishes right before [[HDMA]] happens. This is generally only a problem for games that want to use DMA to clear RAM, as that's the main reason to use DMA during rendering.<br />
* On version 2 of the 5A22 chip ("S-CPU-A"), a recent HDMA transfer to/from INIDISP (Meaning that BBADn is set to zero $00) can make a DMA transfer fail. Nothing will happen and the DMA size registers (DASnL, DASnH) will be unchanged, instead of zero like they normally are after a DMA has been completed.<br />
** Workaround: Set BBADn to $ff instead, and set the transfer pattern to 1. This will cause HDMA to write to $21ff (nothing) and then $2100 (INIDISP). Both bytes should be set to the same value to prevent the INIDISP early read bug.<br />
** S-CPU (the first version), S-CPU-B and the 1-CHIP SNES are not affected by this bug.<br />
<br />
== References ==<br />
:* https://undisbeliever.net/snesdev/registers/inidisp.html#glitches-and-hardware-bugs<br />
:* [[SNES Development Manual]] Book 1, section 2-25-1: Documented Problems<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Errata&diff=434Errata2022-05-31T11:32:58Z<p>Fiskbit: /* References */ Uses : to indent non-automated references to match automated ones.</p>
<hr />
<div>This page describes quirks in the SNES hardware that programmers need to be aware of. They could be mistakes in the hardware's implementation, or just unintuitive behavior.<br />
<br />
== Video ==<br />
* [[Offset-per-tile]] never affects the first (leftmost) tile.<br />
* When color math is set to affect sprites, it will only affect sprites using the last four palettes.<br />
* If NMI is enabled during vblank, NMI will trigger immediately.<br />
* When there are too many sprite tiles on a scanline, the SNES will drop the <em>highest priority sprites</em> instead of the lowest priority ones.<br />
* The SNES programming manual describes a situation where the Time Over flag is erroneously set when the first hardware sprite is 16x16, 32x32, or 64x64, has a horizontal position of 0-255, and other hardware sprites have negative horizontal positions.<br />
* The SNES programming manual says that a hardware sprite should not have its horizontal position set to $100.<br />
** This seems to be because that causes a sprite to contribute to the scanline limits when it should not <ref>[https://wiki.superfamicom.org/sprites Super Famicom Development Wiki]: Sprites</ref><br />
* [[PPU registers#INIDISP|INIDISP]] (register $2100) problems<br />
** Changing the brightness is not instant. On a 3-chip SNES, it may only take a few pixels to change the brightness, but on a 1-chip SNES it may be a gradual fade that takes 72 pixels or more.<br />
*** This can be a problem for games that extend vblank by disabling rendering and enabling it several scanlines into the frame. For this use-case, it's recommended to disable rendering by writing <code>$8F</code> (or $80 ORed with whatever the desired brightness is) to INIDISP instead of <code>$80</code>, so that the brightness is not changed as rendering is enabled.<br />
** INIDISP early read bug: When INIDISP is written to, the PPU doesn't wait for the value to be put on the bus before attempting to read it. This means that the SNES will end up rendering about one pixel where INIDISP has been set to whatever was on the data bus before the correct value. For instructions that don't use indirect addressing, this will likely be the last byte of the instruction.<br />
*** Workaround: Use long addressing to write to INIDISP during rendering, and take advantage of how PPU registers are available in many different banks. <code>STA $8F2100</code> will put $8f on the bus before the written value, and <code>STA $0F2100</code> will put $0f on the bus before the written value, and so on.<br />
* 16x32 sprites do not work correctly with [[PPU_registers#SETINI_-_Screen_Mode/Video_Select_($2133_write)|OBJ interlacing]]<ref>[https://forums.nesdev.org/viewtopic.php?t=21389 Forum thread]: 16x32 sprites in interlaced mode?</ref><br />
** When OBJ interlacing is on, 16x32 sprites are treated as if they are 16x16 - the bottom 16x16 is ignored, and the top 16x16 is squished into 16x8. 32x64 sprites behave as expected.<br />
* 16x32 and 32x64 sprites do not handle being vertically flipped correctly.<br />
<br />
== Audio ==<br />
* The gauss interpolation table has some mistakes in it <ref>[https://problemkaputt.de/fullsnes.htm#snesapudspbrrpitch Fullsnes]: SNES APU DSP BRR Pitch</ref><br />
* The SNES programming manual warns that writing to the first two SPC700 communication registers ($2140 and $2141) with a 16-bit write can also write to $2143 <ref>[[SNES Development Manual]] Book 1, section 3-9-6: Sound Programming Cautions</ref><ref>[https://wiki.superfamicom.org/spc700-reference#communication-ports-74 Super Famicom Development Wiki]: SPC700 Reference</ref><br />
** This may be difficult to trigger or perhaps not actually exist<ref>[https://forums.nesdev.org/viewtopic.php?p=279472#p279472 Forum post]: APU crosstalk 16-bit bug</ref><br />
* Writing to SPC700 communication registers ($2140, $2141, $2142, $2143) at the same time the other processor reads it can result in incorrect data being read.<br />
** A SPC700 program may want to read twice and only proceed when two subsequent reads have the same value.<br />
<br />
== DMA ==<br />
* Some A-Bus addresses are invalid: <ref>higan source code, [https://github.com/higan-emu/higan/blob/master/higan/sfc/cpu/dma.cpp sfc/cpu/dma.cpp], by Near</ref><br />
** The A-Bus address cannot access a B-Bus address ($21xx)<br />
** The A-Bus address cannot access the MMIO or DMA registers ($4000-$41ff, $4200-$421f, $4300-$437f)<br />
** The A-Bus address cannot be a Work-RAM address if the B-Bus address is WMDATA ($2180). This means DMA cannot be used to copy from one section of the SNES's RAM to another.<br />
* On version 1 of the 5A22 chip ("S-CPU"), the chip can crash if [[DMA]] finishes right before [[HDMA]] happens. This is generally only a problem for games that want to use DMA to clear RAM, as that's the main reason to use DMA during rendering.<br />
* On version 2 of the 5A22 chip ("S-CPU-A"), a recent HDMA transfer to/from INIDISP (Meaning that BBADn is set to zero $00) can make a DMA transfer fail. Nothing will happen and the DMA size registers (DASnL, DASnH) will be unchanged, instead of zero like they normally are after a DMA has been completed.<br />
** Workaround: Set BBADn to $ff instead, and set the transfer pattern to 1. This will cause HDMA to write to $21ff (nothing) and then $2100 (INIDISP). Both bytes should be set to the same value to prevent the INIDISP early read bug.<br />
** S-CPU (the first version), S-CPU-B and the 1-CHIP SNES are not affected by this bug.<br />
<br />
== References ==<br />
:* https://undisbeliever.net/snesdev/registers/inidisp.html#glitches-and-hardware-bugs<br />
:* [[SNES Development Manual]] Book 1, section 2-25-1: Documented Problems<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=OAM_layout&diff=421OAM layout2022-05-30T06:11:49Z<p>Fiskbit: Fixes sprite indices 0-15.</p>
<hr />
<div>[[OAM]] is a total of 544 bytes in size, consisting of a 512 byte low table and a 32 byte high table.<br />
It holds the properties of up all 128 sprites.<br />
<br />
Each of these cells represents one byte. The high table packs information on 4 sprites into each byte.<br />
<br />
<pre><br />
--+------------------+------------------+------------------+------------------------------------------------+<br />
L | Sprite #0 | Sprite #0 | Sprite #0 | Sprite #0 |<br />
O | x pos (low bits) | y pos | tile (low bits) | flip v/h, priority, palette, high bit of tile) |<br />
W +------------------+------------------+------------------+------------------------------------------------+<br />
| Sprite #1 | Sprite #1 | Sprite #1 | Sprite #1 |<br />
T | x pos (low bits) | y pos | tile (low bits) | flip v/h, priority, palette, high bit of tile) |<br />
A +------------------+------------------+------------------+------------------------------------------------+<br />
B | Sprite #2 | Sprite #2 | Sprite #2 | Sprite #2 |<br />
L | x pos (low bits) | y pos | tile (low bits) | flip v/h, priority, palette, high bit of tile) |<br />
E +------------------+------------------+------------------+------------------------------------------------+<br />
| | | | |<br />
| .... | ... | ... | ... |<br />
| | | | |<br />
+------------------+------------------+------------------+------------------------------------------------+<br />
| Sprite #127 | Sprite #127 | Sprite #127 | Sprite #127 |<br />
| x pos (low bits) | y pos | tile (low bits) | flip v/h, priority, palette, high bit of tile) |<br />
--+------------------+------------------+------------------+------------------------------------------------+<br />
H | Sprites #0-3 | Sprites #4-7 | Sprites #8-11 | Sprites #12-15 |<br />
I | | | | |<br />
G | | | | |<br />
H | size select bits | size select bits | size select bits | size select bits |<br />
| | | | | | | | | | | | | | | | | | | | |<br />
| v v v v | v v v v | v v v v | v v v v |<br />
T | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ |<br />
A | |s |s |s |s | | |s |s |s |s | | |s |s |s |s | | |s |s |s |s | |<br />
B | |#4|#3|#2|#1| | |#4|#3|#2|#1| | |#4|#3|#2|#1| | |#4|#3|#2|#1| |<br />
L | | x| x| x| x| | | x| x| x| x| | | x| x| x| x| | | x| x| x| x| |<br />
E | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ |<br />
| ^ ^ ^ ^ | ^ ^ ^ ^ | ^ ^ ^ ^ | ^ ^ ^ ^ |<br />
| | | | | | | | | | | | | | | | | | | | |<br />
| hi x bits | hi x bits | hi x bits | hi x bits |<br />
+------------------+------------------+------------------+------------------------------------------------+<br />
| | | | |<br />
| .... | ... | ... | ... |<br />
| | | | |<br />
+------------------+------------------+------------------+------------------------------------------------+<br />
| Sprites #112-115 | Sprites #116-119 | Sprites #120-123 | Sprites #124-127 |<br />
| | | | | <br />
| | | | |<br />
| size select bits | size select bits | size select bits | size select bits |<br />
| | | | | | | | | | | | | | | | | | | | |<br />
| v v v v | v v v v | v v v v | v v v v |<br />
| +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ |<br />
| |s |s |s |s | | |s |s |s |s | | |s |s |s |s | | |s |s |s |s | |<br />
| |#4|#3|#2|#1| | |#4|#3|#2|#1| | |#4|#3|#2|#1| | |#4|#3|#2|#1| |<br />
| | x| x| x| x| | | x| x| x| x| | | x| x| x| x| | | x| x| x| x| |<br />
| +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ | +--+--+--+--+ |<br />
| ^ ^ ^ ^ | ^ ^ ^ ^ | ^ ^ ^ ^ | ^ ^ ^ ^ |<br />
| | | | | | | | | | | | | | | | | | | | |<br />
| hi x bits | hi x bits | hi x bits | hi x bits |<br />
+------------------+------------------+------------------+------------------------------------------------+<br />
</pre></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=MediaWiki:Sidebar&diff=370MediaWiki:Sidebar2022-05-29T00:34:02Z<p>Fiskbit: Changes the SNES Discord link to make it more clear it's not run by NESdev staff. (Sidebar size limits prevent longer, more descriptive links here without ugly wrapping.)</p>
<hr />
<div>* navigation<br />
** mainpage|Wiki main page<br />
** http://forums.nesdev.org/ | NESdev Forums<br />
** https://discord.gg/JSG4kuF8EK | NESdev Discord<br />
** https://discord.gg/yXNEV6p | SNES Discord<br />
** recentchanges-url|recentchanges<br />
* TOOLBOX<br />
* LANGUAGES</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Mouse&diff=368Mouse2022-05-28T08:29:31Z<p>Fiskbit: /* References */ Uses : to match indentation of automated references.</p>
<hr />
<div>The '''Super NES Mouse''' (SNS-016) is a peripheral for the Super NES that was originally bundled with ''Mario Paint''.<br />
The '''Hyper Click Retro Style Mouse''' by Hyperkin is an optical mouse mostly compatible with software for the Super NES Mouse, with some behavior quirks.<br />
<br />
Unlike the standard controller which returns 16 bits of data, the Super NES mouse returns 32 bits. All 32 bits can be read manually via $4016 as on the NES, but an alternative is to let the SNES do its automatic controller reading to get the first 16 bits, and then read the last 16 bits manually.<br />
<br />
== Report ==<br />
<br />
The report is divided functionally into four bytes. The most significant bit is delivered first:<br />
<pre><br />
76543210 First byte<br />
++++++++- Always zero: 00000000<br />
<br />
76543210 Second byte<br />
||||++++- Signature: 0001<br />
||++----- Current sensitivity (0: low; 1: medium; 2: high)<br />
|+------- Left button (1: pressed)<br />
+-------- Right button (1: pressed)<br />
<br />
76543210 Third byte<br />
|+++++++- Vertical displacement since last read<br />
+-------- Direction (1: up; 0: down)<br />
<br />
76543210 Fourth byte<br />
|+++++++- Horizontal displacement since last read<br />
+-------- Direction (1: left; 0: right)<br />
</pre><br />
<br />
After the fourth byte, subsequent bits will read as all 1, though the Hyperkin clone mouse instead reads a single 1 then all 0s. <ref>[//forums.nesdev.org/viewtopic.php?p=231607#p231607 forum post]: Hyperkin SNES mouse investigation</ref><br />
<br />
The Hyper Click mouse will not give a stable report if it is read too fast. Between each read and the next, there should be at least 180 master cycles. Between the 2nd and 3rd byte (16th and 17th bit) of the report should be at least 336 master cycles. Reading faster than this will result in corrupted values.<ref>[//forums.nesdev.org/viewtopic.php?p=236484#p236484 forum post]: Hyperkin mouse reads have a speed limit</ref>.<br />
<br />
=== Automatic read ===<br />
<br />
Official documents recommend reading the first 2 bytes with the [[MMIO registers#NMITIMEN|automatic read]] system, and then reading the remaining 16 bits through the [[MMIO registers#JOYSER1|serial interface]]. Alternatively it can be read entirely through the serial interface.<br />
<br />
Through the automatic read, the second byte of the mouse report will be the low byte of 16-bit automatic report.<br />
<br />
<pre><br />
.a8<br />
; wait for automatic read to finish<br />
:<br />
lda a:$4212<br />
and #1<br />
bne :-<br />
;<br />
; Note: after the end of auto-read, a delay should be placed here to accomodate<br />
; the hyperkin mouse. This might be a good time to check the signature byte,<br />
; or do other mouse-related logic.<br />
;<br />
; now read the remaining serial report<br />
ldy #16<br />
:<br />
lda a:$4017 ; mouse in second controller port<br />
lsr<br />
rol a:mouseread_x<br />
rol a:mouseread_y<br />
nop ; extra delay to match mouse.x65 timing (might need extra in fastrom for hyperkin?)<br />
dey<br />
bne :-<br />
rts<br />
</pre><br />
<br />
== Motion ==<br />
<br />
Motion of the mouse is given as a displacement since the last mouse read, delivered in the third and fourth bytes of the report.<br />
<br />
The displacements are in [[wikipedia:Signed number representations#Sign-and-magnitude method|sign-and-magnitude]], not [[wikipedia:Signed number representations#Two's complement|two's complement]].<br />
For example, $05 represents five mickeys (movement units) in one direction and $85 represents five mickeys in the other.<br />
To convert these to two's complement, use [[Synthetic instructions#Negate A|negation]]:<br />
<pre><br />
; Convert to two's complement<br />
lda third_byte<br />
bpl :+<br />
eor #$7F<br />
sec<br />
adc #$00<br />
:<br />
sta y_velocity<br />
<br />
lda fourth_byte<br />
bpl :+<br />
eor #$7F<br />
sec<br />
adc #$00<br />
:<br />
sta x_velocity<br />
</pre><br />
<br />
When the magnitude of motion is 0, the reported sign will repeat the last used sign value for that coordinate.<br />
<br />
== Sensitivity ==<br />
<br />
The mouse can be set to low, medium, or high sensitivity.<br />
<br />
On the original SNES mouse this can be changed by sending a clock while the latch ($4016.d0) is turned on. The latch is controlled by writing to $4016, and the clock is sent by reading the serial port (either $4016 or $4017).<br />
<br />
<pre><br />
.a8<br />
lda #1<br />
sta $4016<br />
lda $4016 ; mouse in first port<br />
stz $4016<br />
</pre><br />
<br />
Note that this cannot be done while an automatic read is active. Either test $4212 before proceeding, or otherwise guarantee that an automatic read is not occurring at the same time.<br />
<br />
Some revisions of the mouse's microcontroller power up in an unknown state and may return useless values before the sensitivity is changed for the first time.<ref name="fullsnes">Martin Korth. "[//problemkaputt.de/fullsnes.htm#snescontrollersmousetwobuttonmouse Fullsnes: SNES Controllers Mouse Two Button Mouse]".</ref><br />
<br />
The Hyper Click mouse will not cycle its sensitivity this way. Instead it has a manual button on the underside that must be pressed by the user to cycle sensitivity. It will always report 0 for sensitivity, regardless of its manual setting. For this reason, it is not advised to use the software sensitivity cycling to automatically detect the presence of a mouse.<ref>[//forums.nesdev.org/viewtopic.php?p=231600#p231600 forum post]: Hyperkin SNES Mouse cannot software-cycle sensitivity</ref><br />
<br />
On the original SNES mouse, sensitivity setting 0 responds linearly to motion, at a rate of 50 counts per inch<ref>[http://problemkaputt.de/fullsnes.htm#snescontrollersmousetwobuttonmouse FullSNES] - Nocash SNES Mouse documentation</ref>. Values range from 0 to 63, but values higher than 25 are increasingly difficult to produce. <ref>[//forums.nesdev.org/viewtopic.php?p=232667#p232667 forum post]: SNES Mouse sensitivity measurements</ref><br />
<br />
Sensitivity settings 1 and 2 appear to remap the equivalent setting 0 values 0-7 to a table, and clamping at the highest value. (Rarely, however, other values may be seen in settings 1 and 2.)<br />
{| class="wikitable"<br />
|-<br />
! Sensitivity<br />
! colspan = "11" | Value<br />
|-<br />
! 0<br />
| 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 || ...<br />
|-<br />
! 1<br />
| 0 || 1 || 2 || 3 || 8 || 10 || 12 || 21 || 21 || 21 || ...<br />
|-<br />
! 2<br />
| 0 || 1 || 4 || 9 || 12 || 20 || 24 || 28 || 28 || 28 || ...<br />
|}<br />
<br />
The Hyper Click's two manually selected sensitivities both scale linearly with motion speed. Low sensitivity produces 0-31, and high sensitivity produces 0-63. The magnitude of the result is not dependent on the rate of polling, so it appears to report the current speed rather than the distance travelled since the last poll. The maximum value (31/63) at either sensitivity appears to correspond roughly to a speed of 8 inches per second. (This mouse should be used on a surface with a visible texture.)<ref>[//forums.nesdev.org/viewtopic.php?p=232668#p232668 forum post]: Hyperkin Mouse sensitivity measurements</ref><br />
<br />
== Other notes ==<br />
<br />
Some documents about interfacing with the mouse recommend reading the first 16 bits at one speed, delaying a while, and reading the other 16 bits at another speed, following logic analyzer traces from a Super NES console.<br />
However, this is simply a result of Mario Paint using the automatic controller reading feature, and the authentic mouse will give a correct report when read at any reasonable speed.<br />
For example, a program could read 8 bits, wait a couple thousand cycles, and then read the other 24.<br />
The Hyper Click needs a delay after the first 16 bits, though not nearly as much as these documents recommend.<br />
<br />
== References ==<br />
:* [[SNES Development Manual]]: Book II 4-7-8 mouse.x65, Mouse Driver Routine<br />
:* [https://rainwarrior.ca/projects/nes/shadowrun_mouse.zip Shadowrun Mouse Patch] - source code example for using mouse.<br />
<references /><br />
<br />
[[Category:Controllers]]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=WRAM_pinout&diff=358WRAM pinout2022-05-26T07:41:12Z<p>Fiskbit: Names for connected signals, more signal descriptions, and corrections.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==Pinout==<br />
____________________<br />
| |<br />
+5V -- | 01 . 64 | -- GND<br />
CPU D4 <> | 02 63 | <> CPU D3<br />
CPU D5 <> | 03 62 | <> CPU D2<br />
CPU D6 <> | 04 61 | <> CPU D1<br />
CPU D7 <> | 05 60 | <> CPU D0<br />
SYSTEM CLK -> | 06 59 | <- CPU /WR<br />
REFRESH -> | 07 58 | <- /PWR<br />
/RESET -> | 08 57 | <- CPU /RD<br />
NC -- | 09 56 | <- /PRD<br />
CS1 (+5V) -> | 10 55 | ?? G (NC)<br />
CS2 (+5V) -> | 11 54 | <- PA1<br />
CS3 (+5V) -> | 12 53 | <- PA0<br />
/CS1 (GND) -> | 13 52 | <- PS3 (+5V)<br />
/CS2 (GND) -> | 14 51 | <- PS2 (+5V)<br />
/CS3 (/WRAMSEL) -> | 15 Nintendo 50 | <- PS1 (PA7) Orientation:<br />
+5V -- | 16 S-WRAM 49 | -- +5V --------------------<br />
GND -- | 17 48 | -- GND 64 33<br />
NC -- | 18 47 | <- /PS5 (PA6) | |<br />
NC -- | 19 46 | <- /PS4 (PA5) .-----------.<br />
NC -- | 20 45 | <- /PS3 (PA4) | Nintendo |<br />
NC -- | 21 44 | <- /PS2 (PA3) | S-WRAM O|<br />
NC -- | 22 43 | <- /PS1 (PA2) |. |<br />
CPU A0 -> | 23 42 | <- ENABLE (A22) '-----------'<br />
CPU A9 -> | 24 41 | <- CPU A8 | |<br />
CPU A1 -> | 25 40 | <- CPU A16 01 32<br />
CPU A10 -> | 26 39 | <- CPU A7<br />
CPU A2 -> | 27 38 | <- CPU A15 Legend:<br />
CPU A11 -> | 28 37 | <- CPU A6 ----------------------------<br />
CPU A3 -> | 29 36 | <- CPU A14 --[S-WRAM]-- Power, n/a<br />
CPU A12 -> | 30 O 35 | <- CPU A5 ->[S-WRAM]<- S-WRAM input<br />
CPU A4 -> | 31 34 | <- CPU A13 <-[S-WRAM]-> S-WRAM output<br />
+5V -- | 32 33 | -- GND <>[S-WRAM]<> Bidirectional<br />
|____________________| ??[S-WRAM]?? Unknown<br />
<br />
==Signal descriptions==<br />
* '''ENABLE''': This enables WRAM A16 (and possibly also A15..13) and is connected to CPU A22, allowing CPU A16 to address different RAM in banks $7E and $7F, but not elsewhere (resulting in the same 8 KiB in the other banks).<br />
* '''/CS3''': CPU select, connected to the CPU's /WRAMSEL output to map S-WRAM to CPU bus addresses $00-3F,80-BF:0000-1FFF and $7E-7F:0000-FFFF.<br />
* '''PS1''', '''/PS5..1''': Peripheral select, connected to PA7..2 to map S-WRAM to peripheral bus addresses $80-83.<br />
* '''PA1..0''': These select the S-WRAM register being accessed on the peripheral bus (WRAM data or address).<br />
* '''G''': Suspected to be an output indicating whether S-WRAM is selected on either bus. This has been seen outputting low while the console is held in reset and spiking high while running.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Tilemaps&diff=355Tilemaps2022-05-26T04:42:17Z<p>Fiskbit: /* Mode 7 */ Fix formatting.</p>
<hr />
<div>With the exception of [[#Mode 7|mode 7]], a tilemap is a 2-kilobyte (1-kiloword) block of data in VRAM that defines a 32x32 tile region that can be used by a [[Backgrounds|background]].<br />
<br />
Tilemaps are manually allocated within VRAM. With registers [[PPU registers#BGnSC|BGnSC]], background layers can select a single tilemap, and can also combine contiguous pairs of tilemaps horizontally, vertically, or four tilemaps into a 2x2 background. In all combined versions, each individual tilemap is still only 32x32 tiles.<br />
<br />
The tiles of a tilemap can also count "double" with [[PPU registers#BGMODE|BGMODE]], selecting a 2x2 group of adjacent 8x8 pixel [[tiles]] for a 16x16 pixel region in the background, instead of just a single 8x8 tile. This doubles the tilemap's size in each dimension without increasing memory usage. ([[Mode 5|mode 5 and 6]] also have a special 16x8 tile mode, that selects tiles in horizontal pairs.)<br />
<br />
== Format ==<br />
<br />
VMDATAH VMDATAL<br />
$4119 $4118<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
VHPC CCTT TTTT TTTT<br />
|||| |||| |||| ||||<br />
|||| ||++---++++-++++- Tile index<br />
|||+-++--------------- Palette selection<br />
||+------------------- Priority<br />
++-------------------- Flip vertical (V) or horizontal (H)<br />
<br />
Each tilemap entry is a 16-bit word in VRAM. Each row is 32 tiles, left to right, and the rows are top to bottom.<br />
<br />
* Tile index - 10 bits selecting one of 1024 tiles from VRAM relative to the base address given at: [[PPU registers#BG12NBA|BG12NBA]] or [[PPU registers#BG34NBA|BG34NBA]].<br />
* Palette selection - 0-7 selects one of 8 palettes from [[CGRAM]], depending on the [[Background modes|background mode]].<br />
* Priority - tilemaps are separated into background (0) and foreground (1) layers which can allow sprites to appear between these layers. See [[backgrounds]].<br />
* Flip - each tile can be flipped horizontally or vertically.<br />
<br />
When using 16x16 tile modes, the tile index specifies the top left 8x8 tile from VRAM, and it will automatically include the adjacent tiles horizontally (+1) and vertically (+16, +17).<br />
<br />
== Mode 7 ==<br />
<br />
Mode 7 has a special format for its tilemaps.<br />
<br />
A mode 7 tilemap is 128x128 tiles. It always begins at 0 in VRAM, covering the first half of the VRAM memory space.<br />
<br />
The tilemap is stored only in the low byte of each word of VRAM, and the tile data is stored in the high bytes. See [[Tiles#Mode 7|Tiles, Mode 7]].<br />
<br />
Each byte of the tilemap is simply an 8-bit index to one of the 256 tiles. There are no attributes. The order is contiguous rows of 128 tiles, top to bottom.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Tiles&diff=354Tiles2022-05-26T04:35:28Z<p>Fiskbit: /* 8bpp Direct Color */ Changes bit diagram so it doesn't suggest involvement of unrelated bits.</p>
<hr />
<div>Graphics tile data is stored in VRAM, to be used by [[backgrounds]] and [[sprites]].<br />
<br />
Each tile is an 8x8 pixel unit, and there are several formats available.<br />
<br />
Within memory, tiles are arranged in rows of 16 columns. [[Tools#Debugging|Debugging emulators]] can usually display VRAM with this layout.<br />
<br />
Backgrounds and sprites can sometimes work with 16x16 or larger groups of tiles. These are made up of 8x8 pixel tiles, using tiles that are adjacent horizontally (+1) or vertically (+16) in the 16-column layout.<br />
<br />
== 2bpp ==<br />
Used in all layers of BG mode 0, and for one layer in modes 1, 4 and 5.<br />
<br />
Each word of VRAM defines two planes of data for an 8 pixel row.<br />
<br />
The low byte contains the bit-0 plane, and the high byte contains the bit-1 plane. These combine to form a 2bpp result.<br />
<br />
plane 1 ($2119) plane 0 ($2118)<br />
7 ... bit ... 0 7 ... bit ... 0<br />
--------------- ---------------<br />
0 1 0 1 0 1 0 1 0 0 0 1 1 1 0 1<br />
<br />
2bpp result<br />
0 .. pixel .. 7<br />
---------------<br />
0 2 0 3 1 3 0 3<br />
<br />
8 data words (16 bytes) define one 2bpp tile, top to bottom.<br />
<br />
Each pixel has a value of 0-3 to index a palette in [[CGRAM]].<br />
<br />
== 4bpp ==<br />
The most common format. Used for all sprites, and in BG modes 1, 2, 3, 5, and 6.<br />
<br />
This format is essentially two 2bpp tiles. Bit planes 0 and 1 are in the first 16 bytes, and a second 16 bytes contains bit planes 2 and 3.<br />
<br />
With 4 planes, each pixel can have a value from 0-15 to index a color palette.<br />
<br />
== 8bpp ==<br />
Used in BG modes 3 and 4.<br />
<br />
This is like two 4bpp tiles, or four 2bpp tiles. 64 bytes per tile:<br />
* Planes 0, 1 (16 bytes)<br />
* Planes 2, 3<br />
* Planes 4, 5<br />
* Planes 6, 7<br />
<br />
With 8 planes, the palette index value ranges from 0-255.<br />
<br />
== 8bpp Direct Color ==<br />
An alternate mode for BG modes 3 and 4.<br />
<br />
This is the same data format as 8bpp, but instead of using the result as a palette index, the bits correspond directly to RGB values.<br />
<br />
8bpp pixel<br />
7 bit 0<br />
---------<br />
BBGG GRRR<br />
|||| ||||<br />
++++-++++-- Direct RGB to 5-bit color: RRRr0 GGGg0 BBb00<br />
| ||<br />
...b gr..<br />
---------<br />
7 bit 0<br />
attribute<br />
<br />
This allows you to specify colors directly, bypassing the palette, but with the limitation that you can only control 8 of the 15 bits that define a color.<br />
<br />
3 additional bits come from the [[tilemap]]. Its palette selection attribute provides 3 more bits instead of selecting a palette.<br />
However, those 3 extra bits have to apply to the whole 8x8 tile.<br />
<br />
== Mode 7 ==<br />
Mode 7 stores all of its tile data only in the high byte of VRAM words.<br />
<br />
If we ignore the low byte of each word and think of them as contiguous bytes, each byte corresponds to 1 pixel, giving an 8-bit palette index. This format is "chunky" rather than "planar".<br />
<br />
Pixel bytes are in order: left to right, top to bottom.<br />
<br />
== Mode 7 Direct Color ==<br />
An alternative color mode for mode 7. It's the same chunky 1 byte per pixel data organization, but using the direct color format (see [[#8bpp Direct Color|above]]) instead.<br />
<br />
pixel<br />
7 bit 0<br />
---------<br />
BBGG GRRR<br />
|||| ||||<br />
++++-++++-- Direct RGB to 5-bit color: RRR00 GGG00 BB000<br />
<br />
Because mode 7 tilemaps do not have attributes, we have 0 instead of the "extra" colors of the other direct color modes.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Timing&diff=347Timing2022-05-26T03:52:02Z<p>Fiskbit: JOYSERn link formatting.</p>
<hr />
<div><br />
The SNES master clock:<br />
* NTSC 21.447 MHz<br />
* PAL 21.281 MHz<br />
<br />
A 65816 CPU "cycle" can take 6, 8 or 12 master cycles on the SNES, depending on the memory region accessed, and the [[MMIO registers#MEMSEL|MEMSEL]] fast-ROM setting.<br />
<br />
This gives some commonly quoted SNES CPU speeds, though none of them tell a complete story:<br />
* 3.58 MHz fast-ROM (6-cycle)<br />
* 2.68 MHz slow-ROM (8-cycle)<br />
* 1.79 MHz other (12-cycle)<br />
<br />
The speed of access depends on the memory region:<ref>[https://problemkaputt.de/fullsnes.htm#cpuclockcycles Fullsnes]: CPU Clock Cycles</ref><br />
* 6-cycles for fast-ROM access, enabled via MEMSEL and accessed at an address of $800000 of higher.<br />
* 8-cycles for slow-ROM access.<br />
* 8-cycles for internal WRAM.<br />
* 6-cycles for most [[MMIO registers]].<br />
* 12-cycles for [[MMIO registers#JOYSER0|JOYSER0 and JOYSER1]].<br />
* 6-cycles for "internal" cycles not accessing memory (e.g. 2nd cycle of NOP)<br />
<br />
== References ==<br />
<References/></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=SNESdev_Wiki&diff=335SNESdev Wiki2022-05-26T01:27:22Z<p>Fiskbit: /* General */ Adds CPU vectors link.</p>
<hr />
<div>'''SNES Development Wiki'''<br />
<br />
== Reference ==<br />
<br />
=== General ===<br />
* [[Memory map]]<br />
* [[ROM header]]<br />
* [[CPU vectors]]<br />
* [[SNES Development Manual]]<br />
* [[Tools]]<br />
* [[Errata]]<br />
<br />
=== Registers ===<br />
* [[MMIO registers]]<br />
* [[PPU registers]]<br />
* [[DMA registers]]<br />
<br />
=== Pinouts ===<br />
* [[APU pinout]]<br />
* [[CPU pinout]]<br />
* [[PPU pinout]]<br />
* [[WRAM pinout]]<br />
* [[Cartridge connector]]<br />
<br />
=== Peripherals ===<br />
* [[Standard controller]]<br />
* [[Mouse]]<br />
<br />
=== PPU ===<br />
* [[Background modes]]<br />
* [[Tilemaps]]<br />
* [[Tiles]]<br />
* [[Sprites]]<br />
* [[Offset-per-tile]]<br />
* [[Color math]]<br />
<br />
== Examples and Guides ==<br />
<br />
=== SNES hardware ===<br />
* [[Init code]]<br />
* [[Booting the SPC700]]<br />
* [[Controller reading]]<br />
* [[Multiplication]]<br />
* [[Division]]<br />
<br />
=== 65c816 guides ===<br />
* [[65c816 for 6502 developers]]<br />
* [[Using X as a pointer]]<br />
* [[MVN and MVP block copy]]<br />
<br />
=== Emulation ===<br />
* [[Tricky-to-emulate games]]<br />
* [[Uncommon graphics mode games]]<br />
<br />
=== Video ===<br />
* [[SNES PPU for NES developers]]<br />
* [[Scrolling a large map]]<br />
* [[Shaped windows]]<br />
* [[Mode 7 perspective effects]]<br />
* [[Starting HDMA mid-frame]]<br />
* [[Variable width fonts]]<br />
* [[Extending vblank]]<br />
<br />
== Links ==<br />
* [https://forums.nesdev.org/viewforum.php?f=12 SNESdev Forum] - NESDev subforum<br />
* [https://problemkaputt.de/fullsnes.htm Fullsnes] - Nocash's SNES hardware document<br />
* [https://wiki.superfamicom.org/ Superfamicom.org SNES Development Wiki]<br />
* [https://superfamicom.org/ Superfamicom.org SNES cartridge database]<br />
<br />
== MediaWiki ==<br />
<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=334ROM header2022-05-26T01:24:03Z<p>Fiskbit: Adds interrupts back to the table as a single link to CPU vectors page.</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. Emulators and flashcarts rely on this header to know how to emulate the game. Therefore, homebrew games also need to provide a header. [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template] contains an example of how to set one up.<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is.<br />
<br />
Things that increase an emulator's confidence include the checksum and checksum compliment adding up to $FFFF, the memory map value being correct for the header location, and the ROM and RAM size being reasonable values. If the first instruction in the reset routine is <code>SEI</code> then that increases confidence even further. At least one flash cart actually checks to see if the checksum is correct, so it's recommended to set a correct checksum.<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|-<br />
| $FFE0 || 32 || [[CPU vectors|Interrupt vectors]]<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is [[DSP]] ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other (Super Game Boy/Satellaview)<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=CPU_vectors&diff=333CPU vectors2022-05-26T01:22:30Z<p>Fiskbit: Puts empty slots and ABORT into the tables.</p>
<hr />
<div>When an interrupt occurs, the address of the interrupt handler is read from the vector table in bank $00. The vector used is determined by the type of interrupt and the current CPU mode. Vectors are all 16-bit and the target bank is forced to $00.<br />
<br />
==65C816 mode vectors==<br />
{| class="wikitable"<br />
! Vector !! Address !! Examples<br />
|-<br />
! COP<br />
! $FFE4-FFE5<br />
| COP instruction<br />
|-<br />
! BRK<br />
! $FFE6-FFE7<br />
| BRK instruction<br />
|-<br />
! (ABORT)<br />
! $FFE8-FFE9<br />
| (Unused on 5A22 S-CPU)<br />
|-<br />
! NMI<br />
! $FFEA-FFEB<br />
| [[MMIO registers#NMITIMEN|NMITIMEN]] vblank interrupt, or 5A22 /NMI input<br />
|-<br />
! (none)<br />
! $FFEC-FFED<br />
|<br />
|-<br />
! IRQ<br />
! $FFEE-FFEF<br />
| [[MMIO registers#NMITIMEN|NMITIMEN]] H/V timer interrupt, or external interrupt (5A22 /IRQ input)<br />
|}<br />
<br />
==6502 mode vectors==<br />
{| class="wikitable"<br />
! Vector !! Address !! Examples<br />
|-<br />
! COP<br />
! $FFF4-FFF5<br />
| COP instruction<br />
|-<br />
! (none)<br />
! $FFF6-FFF7<br />
|<br />
|-<br />
! (ABORT)<br />
! $FFF8-FFF9<br />
| (Unused on 5A22 S-CPU)<br />
|-<br />
|-<br />
! NMI<br />
! $FFFA-FFFB<br />
| 5A22 /NMI input<br />
|-<br />
! RESET<br />
! $FFFC-FFFD<br />
| 5A22 /RESET (CPU always resets into 6502 mode)<br />
|-<br />
! IRQ/BRK<br />
! $FFFE-FFFF<br />
| BRK instruction, or external interrupt (5A22 /IRQ input)<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=332ROM header2022-05-26T01:03:23Z<p>Fiskbit: Adds link to CPU vectors page.</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. Emulators and flashcarts rely on this header to know how to emulate the game. Therefore, homebrew games also need to provide a header. [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template] contains an example of how to set one up.<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the [[CPU vectors|interrupt vectors]], with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is.<br />
<br />
Things that increase an emulator's confidence include the checksum and checksum compliment adding up to $FFFF, the memory map value being correct for the header location, and the ROM and RAM size being reasonable values. If the first instruction in the reset routine is <code>SEI</code> then that increases confidence even further. At least one flash cart actually checks to see if the checksum is correct, so it's recommended to set a correct checksum.<br />
<br />
See also: [[Memory map]]<br />
<br />
== Cartridge header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is [[DSP]] ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other (Super Game Boy/Satellaview)<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=ROM_header&diff=331ROM header2022-05-26T01:01:38Z<p>Fiskbit: Moves vectors to 'CPU vectors' page and adds $ prefix to addresses.</p>
<hr />
<div>Nintendo required SNES developers to include a header in the game's data that describes what hardware the game cartridge contains. Emulators and flashcarts rely on this header to know how to emulate the game. Therefore, homebrew games also need to provide a header. [https://github.com/pinobatch/lorom-template/blob/master/src/snesheader.s lorom-template] contains an example of how to set one up.<br />
<br />
The header is located at the <em>CPU</em> address range $00FFC0-$00FFDF, right before the interrupt vectors, with an optional second header at $00FFB0-$00FFBF. This means that the location of the header within the actual ROM file will change based on the cartridge's memory map mode - with [[LoROM]] games placing it at $007Fxx, [[HiROM]] games placing it at $00FFxx, and [[ExHiROM]] games placing it at $40FFxx. Therefore, if it's correctly filled out, an emulator will have a higher chance of being able to figure out where the header is.<br />
<br />
Things that increase an emulator's confidence include the checksum and checksum compliment adding up to $FFFF, the memory map value being correct for the header location, and the ROM and RAM size being reasonable values. If the first instruction in the reset routine is <code>SEI</code> then that increases confidence even further. At least one flash cart actually checks to see if the checksum is correct, so it's recommended to set a correct checksum.<br />
<br />
See also: [[Memory map]]<br />
<br />
== Header ==<br />
<br />
{| class="wikitable"<br />
|+ Header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| $FFC0 || 21 || Cartridge title (21 bytes uppercase ASCII. Unused bytes should be spaces.)<br />
|-<br />
| $FFD5 || 1 || ROM speed and memory map mode (LoROM/HiROM/ExHiROM)<br />
|-<br />
| $FFD6 || 1 || Chipset (Indicates if a cartridge contains extra RAM, a battery, and/or a coprocessor)<br />
|-<br />
| $FFD7 || 1 || ROM size: 1<<N kilobytes, rounded up (so 8=256KB, 12=4096KB and so on)<br />
|-<br />
| $FFD8 || 1 || RAM size: 1<<N kilobytes (so 1=2KB, 5=32KB, and so on)<br />
|-<br />
| $FFD9 || 1 || Country (Implies NTSC/PAL)<br />
|-<br />
| $FFDA || 1 || Developer ID<br />
|-<br />
| $FFDB || 1 || ROM version (0 = first)<br />
|-<br />
| $FFDC || 2 || Checksum<br />
|-<br />
| $FFDE || 2 || Checksum compliment (Checksum ^ $FFFF)<br />
|}<br />
<br />
=== $FFD5 ===<br />
Address $00FFD5 indicates the ROM speed and map mode.<br />
<br />
<pre><br />
001smmmm<br />
|++++- Map mode<br />
+----- Speed: 0=Slow, 1=Fast<br />
</pre><br />
<br />
Available modes include:<br />
* 0: [[LoROM]]<br />
* 1: [[HiROM]]<br />
* 5: [[ExHiROM]]<br />
<br />
=== $FFD6 ===<br />
Address $00FFD6 indicates what extra hardware is in the cartridge, if any.<br />
<br />
Possible values include:<br />
* $00 - ROM only<br />
* $01 - ROM + RAM<br />
* $02 - ROM + RAM + battery<br />
* $x3 - ROM + coprocessor<br />
* $x4 - ROM + coprocessor + RAM<br />
* $x5 - ROM + coprocessor + RAM + battery<br />
* $x6 - ROM + coprocessor + battery<br />
* $0x - Coprocessor is [[DSP]] ([[DSP-1]], 2, 3 or 4)<br />
* $1x - Coprocessor is [[GSU]] (SuperFX)<br />
* $2x - Coprocessor is [[OBC1]]<br />
* $3x - Coprocessor is [[SA-1]]<br />
* $4x - Coprocessor is [[S-DD1]]<br />
* $5x - Coprocessor is [[S-RTC]]<br />
* $Ex - Coprocessor is Other (Super Game Boy/Satellaview)<br />
* $Fx - Coprocessor is Custom (specified with $FFBF)<br />
<br />
When coprocessor is Custom, $FFBF selects from:<br />
* $00 - [[SPC7110]]<br />
* $01 - [[ST010]]/[[ST011]]<br />
* $02 - [[ST018]]<br />
* $03 - [[CX4]]<br />
<br />
== Expanded cartridge header ==<br />
The expanded header's presence is indicate by putting $33 in $00FFDA, which is the developer ID.<br />
Some early games may indicate just $00FFBF by setting $00FFD4 to zero.<br />
<br />
{| class="wikitable"<br />
|+ Expanded header contents<br />
|-<br />
! First address !! Length !! Contents<br />
|-<br />
| FFB0 || 2 || ASCII maker code<br />
|-<br />
| FFB2 || 4 || ASCII game code<br />
|-<br />
| FFB6 || 6 || Reserved, should be zero<br />
|-<br />
| FFBC || 1 || Expansion flash size: 1 << N kilobytes<br />
|-<br />
| FFBD || 1 || Expansion RAM size: 1 << N kilobytes - for GSU?<br />
|-<br />
| FFBE || 1 || Special version (usually zero)<br />
|-<br />
| FFBF || 1 || Chipset subtype, used if chipset is $F0-$FF<br />
|}</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=CPU_vectors&diff=330CPU vectors2022-05-26T00:56:55Z<p>Fiskbit: Adds CPU vector page.</p>
<hr />
<div>When an interrupt occurs, the address of the interrupt handler is read from the vector table in bank $00. The vector used is determined by the type of interrupt and the current CPU mode. Vectors are all 16-bit and the target bank is forced to $00.<br />
<br />
==65C816 mode vectors==<br />
{| class="wikitable"<br />
! Vector !! Address !! Examples<br />
|-<br />
! COP<br />
! $FFE4-FFE5<br />
| COP instruction<br />
|-<br />
! BRK<br />
! $FFE6-FFE7<br />
| BRK instruction<br />
|-<br />
! NMI<br />
! $FFEA-FFEB<br />
| [[MMIO registers#NMITIMEN|NMITIMEN]] vblank interrupt, or 5A22 /NMI input<br />
|-<br />
! IRQ<br />
! $FFEE-FFEF<br />
| [[MMIO registers#NMITIMEN|NMITIMEN]] H/V timer interrupt, or external interrupt (5A22 /IRQ input)<br />
|}<br />
<br />
==6502 mode vectors==<br />
{| class="wikitable"<br />
! Vector !! Address !! Examples<br />
|-<br />
! COP<br />
! $FFF4-FFF5<br />
| COP instruction<br />
|-<br />
! NMI<br />
! $FFFA-FFFB<br />
| 5A22 /NMI input<br />
|-<br />
! RESET<br />
! $FFFC-FFFD<br />
| 5A22 /RESET (CPU always resets into 6502 mode)<br />
|-<br />
! IRQ/BRK<br />
! $FFFE-FFFF<br />
| BRK instruction, or external interrupt (5A22 /IRQ input)<br />
|}<br />
<br />
==Unused vectors==<br />
The 65C816 also features an ABORT vector at $FFE8-FFE9 and $FFF8-FFF9, but this is not used nor exposed by the 5A22 S-CPU.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Standard_controller&diff=319Standard controller2022-05-25T23:37:28Z<p>Fiskbit: Notes the read behavior past first 16 reads.</p>
<hr />
<div>The standard controller reports 16 bits of data.<br />
<br />
See: [[Controller reading]]<br />
<br />
If using the [[MMIO registers#NMITIMEN|automatic read]], the reports will be available at [[MMIO registers#JOY1|JOY1]], etc.<br />
<br />
JOY1H JOY1L<br />
$4219 $4218<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
BYsS UDLR AXlr 0000<br />
|||| |||| |||| ||||<br />
|||| |||| |||| ++++- Signature<br />
|||| |||| ||++------ L/R shoulder buttons<br />
|||| |||| ++-------- A/X buttons<br />
|||| ++++------------- D-pad<br />
||++------------------ Select (s) and Start (S)<br />
++-------------------- B/Y buttons<br />
<br />
If manually reading through [[MMIO registers#JOYSER0|JOYSER0]], these bits are delivered starting with the most significant bit (B button).<br />
<br />
The signature is guaranteed to be 0000 for a standard SNES controller. Finding another value here indicates that a different peripheral is plugged in.<br />
<br />
Additional reads past the first 16 return 1's on official controllers.<br />
<br />
The first 8 bits of this report are identical to the NES controller, with SNES Y and B substituted for NES B and A. This offers potential compatibility with an NES controller through an adapter, though the game would have to ignore the extra buttons and signature which would normally report as all 1s from an NES controller.<br />
<br />
The first two controllers report through JOYSER0 ($4016) D0 and JOYSER1 ($4017) D0, and a 4-player multitap will also report two more controllers through D1, similar to simple Famicom 4-player adapters. All 4 reports can be delivered by the automatic read system to JOY1-4.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=PPU_registers&diff=318PPU registers2022-05-25T23:35:36Z<p>Fiskbit: /* OAMADDL, OAMADDH - OAM word address ($2102, $2103 write) */ Fixes register address typo.</p>
<hr />
<div>The SNES PPU is accessed through memory-mapped registers at $2100-213F.<br />
<br />
{| class="wikitable"<br />
|+ PPU register summary<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
{{:MMIO register table/PPU}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/PPU|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
* '''8x2''' - An internal 2-byte state accessed by two 8-bit read or writes (LSB first).<br />
<br />
==Display configuration==<br />
{{Anchor|INIDISP}}<br />
===INIDISP - Screen display ($2100 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
F... BBBB<br />
| ||||<br />
| ++++- Screen brightness (linear steps from 0 = none to $F = full)<br />
+--------- Force blanking<br />
<br />
{{Anchor|BGMODE}}<br />
===BGMODE - BG mode and Character size ($2105 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
4321 PMMM<br />
|||| ||||<br />
|||| |+++- BG mode (see below)<br />
|||| +---- Mode 1 BG3 priority (0 = normal, 1 = high)<br />
|||+------ BG1 character size (0 = 8x8, 1 = 16x16)<br />
||+------- BG2 character size (0 = 8x8, 1 = 16x16)<br />
|+-------- BG3 character size (0 = 8x8, 1 = 16x16)<br />
+--------- BG4 character size (0 = 8x8, 1 = 16x16)<br />
<br />
'''BG Modes'''<br />
Mode| BG bit depth |Offsets | Priorities (front -> back) | Notes <u><br />
|BG1 BG2 BG3 BG4|per tile| | <br />
0 | 2 2 2 2 | No | S3 1H 2H S2 1L 2L S1 3H 4H S0 3L 4L| </u><br />
1 | 4 4 2 | No | S3 1H 2H S2 1L 2L S1 3H S0 3L |BG3 priority = 0 <u><br />
| | |3H S3 1H 2H S2 1L 2L S1 S0 3L |BG3 priority = 1 <br />
2 | 4 4 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
3 | 8 4 | No | S3 1H S2 2H S1 1L S0 2L | <br />
4 | 8 2 | Yes | S3 1H S2 2H S1 1L S0 2L | <br />
5 | 4 2 | No | S3 1H S2 2H S1 1L S0 2L |Fixed 16 pixel char width. Forced high-res mode.<br />
6 | 4 | Yes | S3 1H S2 S1 1L S0 |Fixed 16 pixel char width. Forced high-res mode.<br />
7 | 8 | No | S3 S2 S1 1L S0 |Fixed 8x8 char size. </u><br />
7EXT| 8 7 | No | S3 S2 2H S1 1L S0 2L |Fixed 8x8 char size. BG2 bit 7 acts as priority.<br />
<br />
{{Anchor|MOSAIC}}<br />
===MOSAIC - Screen pixelation ($2106 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSS 4321<br />
|||| ||||<br />
|||| |||+- Enable BG1 mosaic<br />
|||| ||+-- Enable BG2 mosaic<br />
|||| |+--- Enable BG3 mosaic<br />
|||| +---- Enable BG4 mosaic<br />
++++------ Mosaic size in pixels (0 = 1x1, ..., 15 = 16x16)<br />
<br />
{{Anchor|BGnSC}}<br />
===BGnSC - BG1-4 tilemap address and size ($2107-$210A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAYX<br />
|||| ||||<br />
|||| |||+- Horizontal tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
|||| ||+-- Vertical tilemap count (0 = 1 tilemap, 1 = 2 tilemaps)<br />
++++-++--- Tilemap VRAM address (address = AAAAAA << 10)<br />
<br />
Tilemaps may be placed at any 2 KiB page.<br />
<br />
===CHR word base address===<br />
----<br />
<br />
The tile base address for background CHR can start at any 4 KiB page.<br />
<br />
Tilemap offsets that go past the end of VRAM are allowed to wrap around to the beginning.<br />
<br />
{{Anchor|BG12NBA}}<br />
====BG12NBA - BG1 and BG2 CHR word base address ($210B write)====<br />
7 bit 0<br />
---- ----<br />
BBBB AAAA<br />
|||| ||||<br />
|||| ++++- BG1 CHR word base address (address = AAAA << 12)<br />
++++------ BG2 CHR word base address (address = BBBB << 12)<br />
<br />
{{Anchor|BG34NBA}}<br />
====BG34NBA - BG3 and BG4 CHR word base address ($210C write)====<br />
7 bit 0<br />
---- ----<br />
DDDD CCCC<br />
|||| ||||<br />
|||| ++++- BG3 CHR word base address (address = CCCC << 12)<br />
++++------ BG4 CHR word base address (address = DDDD << 12)<br />
<br />
===Scroll===<br />
----<br />
<br />
Each of these scroll registers is normally updated by two single-byte writes to the same address. After two consecutive writes the scroll value is fully updated.<br />
<br />
The two-write mechanism internally keeps shared latch values, so these registers should not normally be written in mixed order. Complete both writes to one register before moving on to the next.<br />
<br />
The scroll offset is always relative to the top-left of the screen, even when updating mid-frame with HDMA.<br />
<br />
{{Anchor|BGnHOFS}}<br />
====BGnHOFS - BG1-4 horizontal scroll offset ($210D, $210F, $2111, $2113 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..XX XXXX XXXX<br />
|| |||| ||||<br />
++---++++-++++- BGn horizontal scroll<br />
<br />
On write: BGnHOFS = (value << 8) | (bgofs_latch & ~7) | (bghofs_latch & 7)<br />
bgofs_latch = value<br />
bghofs_latch = value<br />
<br />
Note: BG1HOFS uses the same address as M7HOFS<br />
<br />
{{Anchor|BGnVOFS}}<br />
====BGnVOFS - BG1-4 vertical scroll offset ($210E, $2110, $2112, $2114 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.... ..YY YYYY YYYY<br />
|| |||| ||||<br />
++---++++-++++- BGn vertical scroll<br />
<br />
On write: BGnVOFS = (value << 8) | bgofs_latch<br />
bgofs_latch = value<br />
<br />
Note: BG1VOFS uses the same address as M7VOFS<br />
<br />
===Layer enable===<br />
----<br />
<br />
{{Anchor|TM}}<br />
====TM - Main screen layer enable ($212C write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on main screen<br />
| ||+-- Enable BG2 on main screen<br />
| |+--- Enable BG3 on main screen<br />
| +---- Enable BG4 on main screen<br />
+------ Enable OBJ on main screen<br />
<br />
{{Anchor|TS}}<br />
====TS - Subscreen layer enable ($212D write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Enable BG1 on subscreen<br />
| ||+-- Enable BG2 on subscreen<br />
| |+--- Enable BG3 on subscreen<br />
| +---- Enable BG4 on subscreen<br />
+------ Enable OBJ on subscreen<br />
<br />
{{Anchor|SETINI}}<br />
===SETINI - Screen Mode/Video Select ($2133 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
EX.. HOiI<br />
|| ||||<br />
|| |||+- Screen interlacing<br />
|| ||+-- OBJ interlacing<br />
|| |+--- Overscan mode<br />
|| +---- High-res mode<br />
|+-------- EXTBG mode<br />
+--------- External sync<br />
<br />
* '''Screen interlacing''' causes every odd frame to lower its picture scanlines half a line between the even frames. When enabled, this produces a 480i picture composed of 2 frames (fields), instead of the default 240p progressive picture where each frame appears at the same vertical level.<br />
** [[#STAT78 - PPU2 status flags and version ($213F read)|'''STAT78''']] ($213F) can be used to check whether the current frame is an even or odd field.<br />
** When interlacing is enabled for BG mode 5 or 6, the BG layers are automatically interlaced to give a view of the background that has double the vertical resolution in 480i, effectively making every BG pixel half as tall.<br />
* '''OBJ interlacing''' interlaces the sprites to double their vertical resolution in 480i. Sprite pixels will appear half as tall.<br />
* '''Overscan mode''' enables the full 240 line picture when set, instead of only 224. On NTSC televisions this extra area is not normally visible, but on PAL it is very visible. Setting this causes NMI/vblank to begin 8 lines later, and end 8 lines earlier, dramatically reducing the vblank length in NTSC. Sprite and scroll positions are relative to the end of the blanking period, so enabling this automatically shifts everything up 8 lines. Using this feature makes the SNES drawing positions similar to the NES.<br />
* '''High-res mode''' doubles the horizontal output resolution from 256 to 512 pixels.<br />
** In most BG modes this causes the main screen to render pixels on even columns, and the sub screen to render on odd columns. This is sometimes called "pseudo-hires". Some games use this for a transparency effect (''Kirby's Dreamland 3'', ''Jurassic Park''), relying on blurring from the composite video signal to blend the columns.<br />
** In BG modes 5 and 6, this high-res is forced, but the BG layers are automatically interleaved to double their horizontal resolution, making every BG pixel half as wide.<br />
* '''EXTBG''' controls a second-layer effect in BG [[Mode 7|mode 7]] only.<br />
* '''External sync''' is used for super-imposing images from an external device. Normally 0.<br />
<br />
==VRAM==<br />
{{Anchor|VMAIN}}<br />
===VMAIN - Video Port Control ($2115 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
M... RRII<br />
| ||||<br />
| ||++- Address increment amount:<br />
| || 0: Increment by 1 word<br />
| || 1: Increment by 32 words<br />
| || 2: Increment by 128 words<br />
| || 3: Increment by 128 words<br />
| ++--- Address remapping:<br />
| 0: None<br />
| 1: Remap rrrrrrrr YYYccccc -> rrrrrrrr cccccYYY (2bpp)<br />
| 2: Remap rrrrrrrY YYcccccP -> rrrrrrrc ccccPYYY (4bpp)<br />
| 3: Remap rrrrrrYY YcccccPP -> rrrrrrcc cccPPYYY (8bpp)<br />
+--------- Address increment mode:<br />
0: Increment after writing $2118 or reading $2139<br />
1: Increment after writing $2119 or reading $213A<br />
<br />
* '''Address remapping''' allows redirection of the write address to update 32-tile rows horizontally when using II = 0. Within a 32-tile group, sequential access iterates through the same 8-pixel row of each tile horizontally. After 32 spans, it returns to the second row of the first tile. Finally after a group of 32 tiles has been updated, it advances to the next group of 32 tiles..<br />
** This is suitable for a 32x32 tilemap in 8x8 tile mode. By filling each row of the tilemap with sequential values, each group of 32 tiles now corresponds to a contiguous horizontal span of pixels.<br />
** P = tile bitplane-word, c = group column, Y = tile pixel row, r = group row.<br />
** When setting the starting address, the starting tile of a 32-tile group will always be the at the same position as its remapped address.<br />
** With 4bpp or 8bpp modes, each increment advances through the 2 or 4 plane-words of a single tile before advancing to the next tile.<br />
** Simplified explanation:<br />
*** 1. Write all planes for an 8 pixel span before proceeding horizontally to the next.<br />
*** 2. After completing a row of 256 pixels (32 spans), proceed vertically to the next.<br />
<br />
===VRAM address===<br />
----<br />
{{Anchor|VMADD|VMADDL|VMADDH}}<br />
====VMADDL, VMADDH - VRAM word address ($2116, $2117 write)====<br />
VMADDH VMADDL<br />
$2117 $2116<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
hHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM word address<br />
<br />
On write: Update VMADD<br />
vram_latch = [VMADD]<br />
<br />
Because the SNES only has 64 KiB of VRAM, VRAM address bit 15 has no effect.<br />
<br />
===VRAM data===<br />
----<br />
{{Anchor|VMDATA|VMDATAL|VMDATAH}}<br />
====VMDATAL, VMDATAH - VRAM data write ($2118, $2119 write)====<br />
VMDATAH VMDATAL<br />
$2119 $2118<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word<br />
<br />
On $2118 write: If address increment mode == 0, increment VMADD<br />
On $2119 write: If address increment mode == 1, increment VMADD<br />
<br />
{{Anchor|VMDATAREAD|VMDATALREAD|VMDATAHREAD}}<br />
====VMDATALREAD, VMDATAHREAD - VRAM data read ($2139, $213A read)====<br />
VMDATAHREAD VMDATALREAD<br />
$213A $2139<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- VRAM data word from vram_latch<br />
<br />
On $2139 read: value = vram_latch.low<br />
If address increment mode == 0:<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
On $213A read: value = vram_latch.high<br />
If address increment mode == 1,<br />
vram_latch = [VMADD]<br />
Increment VMADD<br />
<br />
==CGRAM==<br />
{{Anchor|CGADD}}<br />
===CGADD - CGRAM word address ($2121 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- CGRAM word address<br />
<br />
On write: cgram_byte = 0<br />
<br />
===CGRAM data===<br />
----<br />
{{Anchor|CGDATA}}<br />
====CGDATA - CGRAM data write ($2122 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
.BBB BBGG GGGR RRRR<br />
||| |||| |||| ||||<br />
||| |||| |||+-++++- Red component <br />
||| ||++---+++------- Green component<br />
+++-++-------------- Blue component<br />
<br />
On write: If cgram_byte == 0, cgram_latch = value<br />
If cgram_byte == 1, CGDATA = (value << 8) | cgram_latch<br />
cgram_byte = ~cgram_byte<br />
<br />
Two single-byte writes to this register will update a single CGRAM word. The effect is applied only once the second byte is written.<br />
<br />
Each write will increment the internal byte address. After two writes it will automatically have incremented to the next word.<br />
<br />
{{Anchor|CGDATAREAD}}<br />
====CGDATAREAD - CGRAM data read ($213B read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xBBB BBGG GGGR RRRR<br />
|||| |||| |||| ||||<br />
|||| |||| |||+-++++- Red component <br />
|||| ||++---+++------- Green component<br />
|+++-++-------------- Blue component<br />
+-------------------- PPU2 open bus<br />
<br />
On read: If cgram_byte == 0, value = CGDATA.low<br />
If cgram_byte == 1, value = CGDATA.high<br />
cgram_byte = ~cgram_byte<br />
<br />
==OAM==<br />
{{Anchor|OBSEL}}<br />
===OBSEL - Object size and Character address ($2101 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
SSSN NbBB<br />
|||| ||||<br />
|||| |+++- Name base address (word address = bBB << 13)<br />
|||+-+---- Name select (word offset = (NN+1) << 12)<br />
+++------- Object size:<br />
0: 8x8 and 16x16<br />
1: 8x8 and 32x32<br />
2: 8x8 and 64x64<br />
3: 16x16 and 32x32<br />
4: 16x16 and 64x64<br />
5: 32x32 and 64x64<br />
6: 16x32 and 32x64<br />
7: 16x32 and 32x32<br />
<br />
* '''Name base address''' selects a 16 KiB-aligned quarter of VRAM for the first 8 KiB of available sprite tiles. Bit 2 was reserved for a planned but never implemented expansion to 128 KiB VRAM, so is normally 0.<br />
* '''Name select''' controls a relative offset from the name base address in NN+1 8 KiB increments, selecting a second 8 KiB of available sprite tiles. With name select of 0, the second half follows the base 8 KiB contiguously.<br />
* '''Object size''' controls the sizes available for sprites. The two modes featuring rectangular sizes (6, 7) were not documented by the SNES development manual.<br />
<br />
===OAM address===<br />
----<br />
{{Anchor|OAMADD|OAMADDL|OAMADDH}}<br />
====OAMADDL, OAMADDH - OAM word address ($2102, $2103 write)====<br />
OAMADDH OAMADDL<br />
$2103 $2102<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
P... ...B AAAA AAAA<br />
| | |||| ||||<br />
| | ++++-++++- OAM word address<br />
| +------------- OAM table select (0 = 256 word table, 1 = 16 word table)<br />
+--------------------- OAM priority rotation (1 = enable)<br />
<br />
On write: Update OAMADD<br />
internal_oamadd = (OAMADD & $1FF) << 1<br />
<br />
===OAM data===<br />
----<br />
{{Anchor|OAMDATA}}<br />
====OAMDATA - OAM data write ($2104 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On write: If (internal_oamadd & 1) == 0, oam_latch = value<br />
If internal_oamadd < $200 and (internal_oamadd & 1) == 1:<br />
[internal_oamadd-1] = oam_latch<br />
[internal_oamadd] = value<br />
If internal_oamadd >= $200, [internal_oamadd] = value<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
When the OAM byte address is less than 512:<br />
:Two single-byte writes to this register will update a single OAM word. The effect is applied only once the second byte is written.<br />
When the OAM byte address is 512 or above:<br />
:Each write immediately applies to the current byte.<br />
<br />
Each write will increment the internal byte address.<br />
<br />
{{Anchor|OAMDATAREAD}}<br />
====OAMDATAREAD - OAM data read ($2138 read)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- OAM data<br />
<br />
On read: value = [internal_oamadd]<br />
internal_oamadd = internal_oamadd + 1<br />
<br />
==Mode 7==<br />
{{Anchor|M7SEL}}<br />
===M7SEL - Mode 7 settings ($211A write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
RF.. ..YX<br />
|| ||<br />
|| |+- Flip screen horizontally (backgrounds only)<br />
|| +-- Flip screen vertically (backgrounds only)<br />
|+-------- Non-tilemap fill (0 = transparent, 1 = character 0)<br />
+--------- Tilemap repeat (0 = tilemap repeats, 1 = Non-tilemap fill beyond tilemap boundaries)<br />
<br />
===Scroll===<br />
----<br />
{{Anchor|M7HOFS}}<br />
====M7HOFS - Mode 7 horizontal scroll offset ($210D write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 horizontal scroll (signed)<br />
<br />
On write: M7HOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1HOFS<br />
<br />
{{Anchor|M7VOFS}}<br />
====M7VOFS - Mode 7 vertical scroll offset ($210E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 vertical scroll (signed)<br />
<br />
On write: M7VOFS = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
Note: This register uses the same address as BG1VOFS<br />
<br />
===Matrices===<br />
----<br />
{{Anchor|M7A}}<br />
====M7A - Mode 7 matrix A and Multiplication factor 1 ($211B write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix A (8.8 fixed point)<br />
++++-++++---++++-++++- 16-bit multiplication factor (signed)<br />
<br />
On write: M7A = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 16-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7B}}<br />
====M7B - Mode 7 matrix B and Multiplication factor 2 ($211C write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix B (8.8 fixed point)<br />
++++-++++- 8-bit multiplication factor (signed)<br />
<br />
On write: M7B = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
The last 8-bit value (signed) written here is also used to provide a 24-bit multiplication result at [[#MPY|MPY]].<br />
<br />
{{Anchor|M7n}}<br />
====M7n - Mode 7 matrix C-D ($211D-211E write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
DDDD DDDD dddd dddd<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Mode 7 matrix n (8.8 fixed point)<br />
<br />
On write: M7n = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
===Center===<br />
----<br />
{{Anchor|M7X}}<br />
====M7X - Mode 7 center X ($211F write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...X XXXX XXXX XXXX<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center X (signed)<br />
<br />
On write: M7X = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
{{Anchor|M7Y}}<br />
====M7Y - Mode 7 center Y ($2120 write twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
...Y YYYY YYYY YYYY<br />
| |||| |||| ||||<br />
+-++++---++++-++++- Mode 7 center Y (signed)<br />
<br />
On write: M7Y = (value << 8) | mode7_latch<br />
mode7_latch = value<br />
<br />
==Windows==<br />
===Window mask settings===<br />
----<br />
{{Anchor|W12SEL}}<br />
====W12SEL - Window Mask Settings for BG1 and BG2 ($2123 write)====<br />
7 bit 0<br />
---- ----<br />
DdCc BbAa<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG1<br />
|||| ||+-- Enable window 1 for BG1<br />
|||| |+--- Invert window 2 for BG1<br />
|||| +---- Enable window 2 for BG1<br />
|||+------ Invert window 1 for BG2<br />
||+------- Enable window 1 for BG2<br />
|+-------- Invert window 2 for BG2<br />
+--------- Enable window 2 for BG2<br />
<br />
{{Anchor|W34SEL}}<br />
====W34SEL - Window Mask Settings for BG3 and BG4 ($2124 write)====<br />
7 bit 0<br />
---- ----<br />
HhGg FfEe<br />
|||| ||||<br />
|||| |||+- Invert window 1 for BG3<br />
|||| ||+-- Enable window 1 for BG3<br />
|||| |+--- Invert window 2 for BG3<br />
|||| +---- Enable window 2 for BG3<br />
|||+------ Invert window 1 for BG4<br />
||+------- Enable window 1 for BG4<br />
|+-------- Invert window 2 for BG4<br />
+--------- Enable window 2 for BG4<br />
<br />
{{Anchor|WOBJSEL}}<br />
====WOBJSEL - Window Mask Settings for OBJ and Color Window ($2125 write)====<br />
7 bit 0<br />
---- ----<br />
LlKk JjIi<br />
|||| ||||<br />
|||| |||+- Invert window 1 for OBJ<br />
|||| ||+-- Enable window 1 for OBJ<br />
|||| |+--- Invert window 2 for OBJ<br />
|||| +---- Enable window 2 for OBJ<br />
|||+------ Invert window 1 for color math<br />
||+------- Enable window 1 for color math<br />
|+-------- Invert window 2 for color math<br />
+--------- Enable window 2 for color math<br />
<br />
===Window positions===<br />
----<br />
{{Anchor|WH0}}<br />
====WH0 - Window 1 left position ($2126 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 1 left edge position<br />
<br />
{{Anchor|WH1}}<br />
====WH1 - Window 1 right position ($2127 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 1 right edge position<br />
<br />
{{Anchor|WH2}}<br />
====WH2 - Window 2 left position ($2128 write)====<br />
7 bit 0<br />
---- ----<br />
LLLL LLLL<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
{{Anchor|WH3}}<br />
====WH3 - Window 2 right position ($2129 write)====<br />
7 bit 0<br />
---- ----<br />
RRRR RRRR<br />
|||| ||||<br />
++++-++++- Window 2 left edge position<br />
<br />
===Window mask logic===<br />
----<br />
{{Anchor|WBGLOG}}<br />
====WBGLOG - Window BG mask logic ($212A write)====<br />
7 bit 0<br />
---- ----<br />
4433 2211<br />
|||| ||||<br />
|||| ||++- BG1 window mask logic<br />
|||| ++--- BG2 window mask logic<br />
||++------ BG3 window mask logic<br />
++-------- BG4 window mask logic<br />
<br />
{{Anchor|WOBJLOG}}<br />
====WOBJLOG - Window OBJ and color math mask logic ($212B write)====<br />
7 bit 0<br />
---- ----<br />
.... CCOO<br />
||||<br />
||++- OBJ window mask logic<br />
++--- Color window mask logic<br />
<br />
<b>Mask logic types</b><br />
<u>Value|Logic</u><br />
0 | OR<br />
1 | AND<br />
2 | XOR<br />
3 | XNOR<br />
<br />
===Window enable===<br />
----<br />
{{Anchor|TMW}}<br />
====TMW - Main screen layer window enable ($212E write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to main screen BG1<br />
| ||+-- Apply enabled windows to main screen BG2<br />
| |+--- Apply enabled windows to main screen BG3<br />
| +---- Apply enabled windows to main screen BG4<br />
+------ Apply enabled windows to main screen OBJ<br />
<br />
{{Anchor|TSW}}<br />
====TSW - Subscreen layer window enable ($212F write)====<br />
7 bit 0<br />
---- ----<br />
...O 4321<br />
| ||||<br />
| |||+- Apply enabled windows to subscreen BG1<br />
| ||+-- Apply enabled windows to subscreen BG2<br />
| |+--- Apply enabled windows to subscreen BG3<br />
| +---- Apply enabled windows to subscreen BG4<br />
+------ Apply enabled windows to subscreen OBJ<br />
<br />
==Color math==<br />
{{Anchor|CGWSEL}}<br />
===CGWSEL - Color addition select ($2130 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
BBMM ..AD<br />
|||| ||<br />
|||| |+- Direct color mode<br />
|||| +-- Addend (0 = fixed color, 1 = subscreen)<br />
||++------ Color math disable region<br />
++-------- Clip colors to black before math region<br />
<br />
<B>Region types</B><br />
<u>Value|Region </u><br />
0 |Nowhere<br />
1 |Outside color window<br />
2 |Inside color window<br />
3 |Everywhere<br />
<br />
{{Anchor|CGADSUB}}<br />
===CGADSUB - Color math designation ($2131 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
MHBO 4321<br />
|||| ||||<br />
|||| |||+- BG1 color math enable<br />
|||| ||+-- BG2 color math enable<br />
|||| |+--- BG3 color math enable<br />
|||| +---- BG4 color math enable<br />
|||+------ OBJ color math enable<br />
||+------- Backdrop color math enable<br />
|+-------- Half color math<br />
+--------- Operator type (0 = add, 1 = subtract)<br />
<br />
{{Anchor|COLDATA}}<br />
===COLDATA - Fixed color data ($2132 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
BGRC CCCC<br />
|||| ||||<br />
|||+-++++- Color value<br />
||+------- Write color value to blue channel<br />
|+-------- Write color value to green channel<br />
+--------- Write color value to red channel<br />
<br />
==Multiplication result==<br />
{{Anchor|MPY|MPYL|MPYM|MPYH}}<br />
===MPYL, MPYM, MPYH - Multiplication result ($2134, $2135, $2136 read)===<br />
MPYH MPYM MPYL<br />
$2136 $2135 $2134<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
HHHH HHHH MMMM MMMM LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit multiplication result (signed)<br />
<br />
This result may be read back after writing [[#M7A|M7A]] with a signed 16-bit value (write twice), and [[#M7B|M7B]] with a signed 8-bit value (write once).<br />
These two values will be multiplied to produce the signed 24-bit value read here.<br />
<br />
See: [[Multiplication]]<br />
<br />
==H/V counters==<br />
{{Anchor|SLHV}}<br />
===SLHV - Software latch for H/V counters ($2137 read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
xxxx xxxx<br />
|||| ||||<br />
++++-++++- Open bus<br />
<br />
On read: counter_latch = 1<br />
<br />
===Counters===<br />
----<br />
{{Anchor|OPHCT}}<br />
====OPHCT - Output horizontal counter ($213C read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxH HHHH HHHH<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Horizontal counter value<br />
++++-+++-------------- PPU2 open bus<br />
<br />
On read: If ophct_byte == 0, value = OPHCT.low<br />
If ophct_byte == 1, value = OPHCT.high<br />
ophct_byte = ~ophct_byte<br />
<br />
{{Anchor|OPVCT}}<br />
====OPVCT - Output vertical counter ($213D read twice)====<br />
15 bit 8 7 bit 0<br />
---- ---- ---- ----<br />
xxxx xxxV VVVV VVVV<br />
|||| |||| |||| ||||<br />
|||| |||+---++++-++++- Vertical counter value<br />
++++-+++-------------- PPU2 open bus<br />
<br />
On read: If opvct_byte == 0, value = OPVCT.low<br />
If opvct_byte == 1, value = OPVCT.high<br />
opvct_byte = ~opvct_byte<br />
<br />
When counter_latch transitions from 0 to 1, these registers are latched with the current counter values. counter_latch is set when SLHV is read or /EXTLATCH (PPU2 pin 29) is asserted, and is cleared when STAT78 is read. /EXTLATCH is connected to joypad IO D7 and can be controlled by the CPU via WRIO or by a joypad.<br />
<br />
counter_latch behavior has not been fully confirmed.<br />
<br />
==Status==<br />
{{Anchor|STAT77}}<br />
===STAT77 - PPU1 status flags and version ($213E read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
TRMx VVVV<br />
|||| ||||<br />
|||| ++++- PPU1 version<br />
|||+------ PPU1 open bus<br />
||+------- Master/slave mode (PPU1 pin 25)<br />
|+-------- Range over flag (sprite tile overflow)<br />
+--------- Time over flag (sprite overflow)<br />
<br />
{{Anchor|STAT78}}<br />
===STAT78 - PPU2 status flags and version ($213F read)===<br />
----<br />
7 bit 0<br />
---- ----<br />
FLxM VVVV<br />
|||| ||||<br />
|||| ++++- PPU2 version<br />
|||+------ NTSC/PAL mode (0 = NTSC, 1 = PAL) (PPU2 pin 30)<br />
||+------- PPU2 open bus<br />
|+-------- Counter latch value<br />
+--------- Interlace field<br />
<br />
On read: counter_latch = 0<br />
ophct_byte = 0<br />
opvct_byte = 0<br />
<br />
If a condition that sets counter_latch is active when STAT78 is read, it is not known if counter_latch is cleared. Existing documentation suggests it is not cleared and the counters are not relatched.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=SNESdev_Wiki&diff=316SNESdev Wiki2022-05-25T23:16:23Z<p>Fiskbit: Remove MediaWiki links that aren't relevant to standard contributors.</p>
<hr />
<div>'''SNES Development Wiki'''<br />
<br />
== Reference ==<br />
<br />
=== General ===<br />
* [[Memory map]]<br />
* [[ROM header]]<br />
* [[SNES Development Manual]]<br />
* [[Tools]]<br />
* [[Errata]]<br />
<br />
=== Registers ===<br />
* [[MMIO registers]]<br />
* [[PPU registers]]<br />
* [[DMA registers]]<br />
<br />
=== Pinouts ===<br />
* [[APU pinout]]<br />
* [[CPU pinout]]<br />
* [[PPU pinout]]<br />
* [[WRAM pinout]]<br />
* [[Cartridge connector]]<br />
<br />
=== Peripherals ===<br />
* [[Standard controller]]<br />
* [[Mouse]]<br />
<br />
=== PPU ===<br />
* [[Offset-per-tile]]<br />
<br />
== Examples and Guides ==<br />
<br />
=== SNES hardware ===<br />
* [[Init code]]<br />
* [[Booting the SPC700]]<br />
* [[Controller reading]]<br />
* [[Multiplication]]<br />
* [[Division]]<br />
<br />
=== 65c816 guides ===<br />
* [[65c816 for 6502 developers]]<br />
* [[Using X as a pointer]]<br />
* [[MVN and MVP block copy]]<br />
<br />
=== Emulation ===<br />
* [[Tricky-to-emulate games]]<br />
* [[Uncommon graphics mode games]]<br />
<br />
=== Video ===<br />
* [[SNES PPU for NES developers]]<br />
* [[Scrolling a large map]]<br />
* [[Shaped windows]]<br />
* [[Mode 7 perspective effects]]<br />
* [[Starting HDMA mid-frame]]<br />
* [[Variable width fonts]]<br />
* [[Extending vblank]]<br />
<br />
== Links ==<br />
* [https://forums.nesdev.org/viewforum.php?f=12 SNESdev Forum] - NESDev subforum<br />
* [https://problemkaputt.de/fullsnes.htm Fullsnes] - Nocash's SNES hardware document<br />
* [https://wiki.superfamicom.org/ Superfamicom.org SNES Development Wiki]<br />
* [https://superfamicom.org/ Superfamicom.org SNES cartridge database]<br />
<br />
== MediaWiki ==<br />
<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=SNESdev_Wiki&diff=315SNESdev Wiki2022-05-25T23:14:43Z<p>Fiskbit: /* Pinouts */ Adds WRAM pinout link.</p>
<hr />
<div>'''SNES Development Wiki'''<br />
<br />
== Reference ==<br />
<br />
=== General ===<br />
* [[Memory map]]<br />
* [[ROM header]]<br />
* [[SNES Development Manual]]<br />
* [[Tools]]<br />
* [[Errata]]<br />
<br />
=== Registers ===<br />
* [[MMIO registers]]<br />
* [[PPU registers]]<br />
* [[DMA registers]]<br />
<br />
=== Pinouts ===<br />
* [[APU pinout]]<br />
* [[CPU pinout]]<br />
* [[PPU pinout]]<br />
* [[WRAM pinout]]<br />
* [[Cartridge connector]]<br />
<br />
=== Peripherals ===<br />
* [[Standard controller]]<br />
* [[Mouse]]<br />
<br />
=== PPU ===<br />
* [[Offset-per-tile]]<br />
<br />
== Examples and Guides ==<br />
<br />
=== SNES hardware ===<br />
* [[Init code]]<br />
* [[Booting the SPC700]]<br />
* [[Controller reading]]<br />
* [[Multiplication]]<br />
* [[Division]]<br />
<br />
=== 65c816 guides ===<br />
* [[65c816 for 6502 developers]]<br />
* [[Using X as a pointer]]<br />
* [[MVN and MVP block copy]]<br />
<br />
=== Emulation ===<br />
* [[Tricky-to-emulate games]]<br />
* [[Uncommon graphics mode games]]<br />
<br />
=== Video ===<br />
* [[SNES PPU for NES developers]]<br />
* [[Scrolling a large map]]<br />
* [[Shaped windows]]<br />
* [[Mode 7 perspective effects]]<br />
* [[Starting HDMA mid-frame]]<br />
* [[Variable width fonts]]<br />
* [[Extending vblank]]<br />
<br />
== Links ==<br />
* [https://forums.nesdev.org/viewforum.php?f=12 SNESdev Forum] - NESDev subforum<br />
* [https://problemkaputt.de/fullsnes.htm Fullsnes] - Nocash's SNES hardware document<br />
* [https://wiki.superfamicom.org/ Superfamicom.org SNES Development Wiki]<br />
* [https://superfamicom.org/ Superfamicom.org SNES cartridge database]<br />
<br />
== MediaWiki ==<br />
<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]<br />
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki release mailing list]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]<br />
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=CPU_pinout&diff=314CPU pinout2022-05-25T23:14:03Z<p>Fiskbit: /* Pinout */ Minor formatting fix.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==Pinout==<br />
^<br />
/ \<br />
/ \<br />
/ \<br />
+5V -- / 1 100 \ -> CPU A7<br />
CPU A8 <- / 2 99 \ -> CPU A6<br />
CPU A9 <- / 3 . 98 \ -> CPU A5<br />
CPU A10 <- / 4 97 \ -> CPU A4<br />
CPU A11 <- / 5 96 \ -> CPU A3<br />
CPU A12 <- / 6 95 \ -> CPU A2<br />
CPU A13 <- / 7 94 \ -> CPU A1<br />
CPU A14 <- / 8 93 \ -> CPU A0<br />
CPU A15 <- / 9 92 \ -> CPU /RD<br />
CPU A16 <- / 10 91 \ -> CPU /WR<br />
CPU A17 <- / 11 90 \ -- GND<br />
CPU A18 <- / 12 89 \ -> /VECTORPULL<br />
CPU A19 <- / 13 88 \ -> UNKNOWN CLK 88<br />
CPU A20 <- / 14 87 \ -> VDA<br />
CPU A21 <- / 15 86 \ -> VPA<br />
CPU A22 <- / 16 85 \ -- +5V<br />
A23 <- / 17 84 \ -> XFLAG<br />
GND -- / 18 83 \ -> MFLAG<br />
joypad IO D0 <> / 19 82 \ -> /MEMLOCK<br />
joypad IO D1 <> / 20 81 \ <- RDY<br />
joypad IO D2 <> / 21 \<br />
joypad IO D3 <> / 22 O /<br />
joypad IO D4 <> / 23 80 / -> R/W<br />
joypad IO D5 <> / 24 79 / -- GND<br />
joypad IO D6 <> / 25 78 / -> /WRAMSEL<br />
joypad IO D7 <> / 26 Nintendo 5A22 77 / -> /ROMSEL<br />
joypad 2 D0 -> / 27 Package QFP-100, 0.65mm pitch 76 / <- /ABORT<br />
joypad 2 D1 -> / 28 75 / <- HALT<br />
joypad 2 D2 -> / 29 S-CPU 74 / <- HVCMODE <br />
joypad 2 D3 -> / 30 73 / <- TM<br />
/ O 72 / -> PHI2<br />
\ 71 / -> UNKNOWN CLK 71<br />
joypad 2 D4 -> \ 31 70 / -> /DMA<br />
joypad 1 D0 -> \ 32 69 / -> /PWR<br />
joypad 1 D1 -> \ 33 68 / -> /PRD<br />
+5V -- \ 34 67 / <> CPU D7<br />
joypad 1 /OE <- \ 35 66 / <> CPU D6<br />
joypad 2 /OE <- \ 36 65 / <> CPU D5<br />
OUT0 <- \ 37 64 / <> CPU D4 Orientation:<br />
OUT1 <- \ 38 63 / <> CPU D3 --------------------<br />
OUT2 <- \ 39 62 / <> CPU D2 80 51<br />
REFRESH <- \ 40 61 / <> CPU D1 | |<br />
TCKSEL0 -> \ 41 60 / <> CPU D0 .-----------.<br />
TCKSEL1 -> \ 42 59 / -- +5V 81-|O Nintendo |-50<br />
HBLANK -> \ 43 58 / -> PA7 | S-CPU |<br />
VBLANK -> \ 44 57 / -> PA6 100-|. 5A22 O|-31<br />
/NMI -> \ 45 56 / -> PA5 '-----------'<br />
/IRQ -> \ 46 55 / -> PA4 | |<br />
GND -- \ 47 54 / -> PA3 01 30<br />
SYSTEM CLK -> \ 48 53 / -> PA2 <br />
REFRESH /ENABLE -> \ 49 52 / -> PA1 Legend:<br />
/RESET -> \ 50 51 / -> PA0 ----------------------------<br />
\ / --[5A22]-- Power, n/a<br />
\ / ->[5A22]<- 5A22 input<br />
\ / <-[5A22]-> 5A22 output<br />
V <>[5A22]<> Bidirectional<br />
<br />
==Signal descriptions==<br />
* '''VPA, VDA''': Valid Data Address and Valid Program Address, useful for caching or single-step.<br />
** VPA = 0, VDA = 0: Address bus may be invalid.<br />
** VPA = 0, VDA = 1: Valid data address<br />
** VPA = 1, VDA = 0: Valid program address<br />
** VPA = 1, VDA = 1: Opcode fetch</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=PPU_pinout&diff=313PPU pinout2022-05-25T23:13:40Z<p>Fiskbit: /* Pinout */ Minor formatting fix.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==S-PPU1==<br />
===Pinout===<br />
^<br />
/ \<br />
/ \<br />
/ \<br />
TST1 -> / 1 100 \ <- SYSTEM CLK<br />
TST0 -> / 2 99 \ <- TST2<br />
/PRD -> / 3 (*) 98 \ <- /RESET<br />
/PWR -> / 4 97 \ <- /PIXEL CLK IN<br />
PA7 -> / 5 96 \ -- GND<br />
PA6 -> / 6 95 \ ?? FIELD<br />
PA5 -> / 7 94 \ -> /OVER<br />
PA4 -> / 8 93 \ -> /PIXEL CLK OUT<br />
PA3 -> / 9 92 \ ?? /HCLD<br />
PA2 -> / 10 91 \ ?? /VCLD<br />
PA1 -> / 11 90 \ -> COLOR0<br />
PA0 -> / 12 89 \ -> COLOR1<br />
+5V -- / 13 88 \ -> COLOR2<br />
CPU D7 <> / 14 87 \ -> PRIO0<br />
CPU D6 <> / 15 86 \ -> PRIO1<br />
CPU D5 <> / 16 85 \ -> CHR0<br />
CPU D4 <> / 17 84 \ -> CHR1<br />
CPU D3 <> / 18 83 \ -> CHR2<br />
CPU D2 <> / 19 82 \ -> CHR3<br />
CPU D1 <> / 20 81 \ -- +5V<br />
CPU D0 <> / 21 \<br />
GND -- / 22 /<br />
HVCMODE -> / 23 80 / -> /VRD<br />
PALMODE -> / 24 79 / -> /VBWR<br />
/MASTER -> / 25 78 / -> /VAWR<br />
/EXTSYNC -> / 26 Nintendo 5C77 77 / -- GND<br />
GND -- / 27 Package QFP-100, 0.65mm pitch 76 / -> VAA0<br />
VDB0 <> / 28 75 / -> VAA1<br />
VDB1 <> / 29 S-PPU1 74 / -> VAA2 <br />
VDB2 <> / 30 73 / -> VAA3<br />
/ 72 / -> VAA4<br />
\ 71 / -> VAA5<br />
VDB3 <> \ 31 70 / -> VAA6<br />
VDB4 <> \ 32 69 / -> VAA7 <br />
VDB5 <> \ 33 68 / -> VAA8 <br />
VDB6 <> \ 34 67 / -> VAA9<br />
VDB7 <> \ 35 66 / -> VAA10<br />
+5V -- \ 36 65 / -> VAA11<br />
VDA0 <> \ 37 64 / -> VAA12 Orientation:<br />
VDA1 <> \ 38 63 / -> VAA13 --------------------<br />
VDA2 <> \ 39 62 / -- +5V 80 51<br />
VDA3 <> \ 40 61 / -> VAB0 | |<br />
VDA4 <> \ 41 60 / -> VAB1 .-----------.<br />
VDA5 <> \ 42 59 / -> VAB2 81-| Nintendo O|-50<br />
VDA6 <> \ 43 58 / -> VAB3 | S-PPU1 |<br />
VDA7 <> \ 44 57 / -> VAB4 100-|@ 5C77 |-31<br />
GND -- \ 45 56 / -> VAB5 '-----------'<br />
VA15 <- \ 46 55 / -> VAB6 | |<br />
VA14 <- \ 47 O 54 / -> VAB7 01 30<br />
VAB13 <- \ 48 53 / -> VAB8 <br />
VAB12 <- \ 49 52 / -> VAB9 Legend:<br />
VAB11 <- \ 50 51 / -> VAB10 ----------------------------<br />
\ / --[5C77]-- Power, n/a<br />
\ / ->[5C77]<- 5C77 input<br />
\ / <-[5C77]-> 5C77 output<br />
V <>[5C77]<> Bidirectional<br />
??[5C77]?? Unknown<br />
<br />
==S-PPU2==<br />
===Pinout===<br />
_____<br />
/ \<br />
/BURST <- / 1 100 \ -> /CSYNC<br />
/PED ?? / 2 99 \ -- GND<br />
COLORBURST CLK <- / 3 98 \ <- HVCMODE<br />
/TRANSPARENT <- / 4 97 \ -> B<br />
+5V -- / 5 96 \ -> G<br />
/PWR -> / 6 95 \ -> R<br />
/PRD -> / 7 94 \ -- +5VA<br />
CPU D7 <> / 8 93 \ <- DIGITAL VIDEO ENABLE<br />
CPU D6 <> / 9 92 \ <- TST14<br />
CPU D5 <> / 10 91 \ <- TST13<br />
CPU D4 <> / 11 90 \ <- TST12<br />
CPU D3 <> / 12 89 \ ?? TST11<br />
CPU D2 <> / 13 88 \ ?? TST10<br />
CPU D1 <> / 14 87 \ ?? TST9<br />
CPU D0 <> / 15 86 \ ?? TST8<br />
GND -- / 16 85 \ ?? TST7<br />
PA7 -> / 17 84 \ ?? TST6<br />
PA6 -> / 18 83 \ -- +5V <br />
PA5 -> / 19 82 \ ?? TST5<br />
PA4 -> / 20 81 \ ?? TST4<br />
PA3 -> / 21 \<br />
PA2 -> / 22 O /<br />
PA1 -> / 23 80 / ?? TST3<br />
PA0 -> / 24 79 / ?? TST2<br />
HBLANK <- / 25 78 / ?? TST1<br />
VBLANK <- / 26 Nintendo 5C78 77 / ?? TST0<br />
/PIXEL CLK OUT <- / 27 Package QFP-100, 0.65mm pitch 76 / <- EXT7<br />
/RESOUT1 <- / 28 75 / <- EXT6<br />
/EXTLATCH -> / 29 S-PPU2 74 / <- EXT5 <br />
PALMODE -> / 30 73 / <- EXT4<br />
/ O 72 / <- EXT3<br />
\ 71 / <- EXT2<br />
SYSTEM CLK -> \ 31 70 / <- EXT1<br />
+5V -- \ 32 69 / <- EXT0 <br />
/RESOUT0 <- \ 33 68 / -- GND <br />
/RESET -> \ 34 67 / <> VDA7 <br />
GND -- \ 35 66 / <> VDA6 <br />
FIELD ?? \ 36 65 / <> VDA5 <br />
/OVER1 -> \ 37 64 / <> VDA4 Orientation:<br />
/PIXEL CLK IN -> \ 38 63 / <> VDA3 --------------------<br />
/HCLD ?? \ 39 62 / <> VDA2 80 51<br />
/VCLD ?? \ 40 61 / <> VDA1 | |<br />
COLOR0 -> \ 41 60 / <> VDA0 .-----------.<br />
COLOR1 -> \ 42 59 / -- +5V 81-|O Nintendo |-50<br />
COLOR2 -> \ 43 58 / <> VDB7 | S-PPU2 |<br />
PRIO0 -> \ 44 57 / <> VDB6 100-| 5C78 O|-31<br />
PRIO1 -> \ 45 56 / <> VDB5 \-----------'<br />
CHR0 -> \ 46 55 / <> VDB4 | |<br />
CHR1 -> \ 47 54 / <> VDB3 01 30<br />
CHR2 -> \ 48 53 / <> VDB2 <br />
CHR3 -> \ 49 52 / <> VDB1 Legend:<br />
/OVER2 -> \ 50 51 / <> VDB0 ----------------------------<br />
\ / --[5C78]-- Power, n/a<br />
\ / ->[5C78]<- 5C78 input<br />
\ / <-[5C78]-> 5C78 output<br />
V <>[5C78]<> Bidirectional<br />
??[5C78]?? Unknown<br />
<br />
===Signal descrptions===<br />
* '''COLORBURST CLK''': 3.58 MHz clock.<br />
* '''/PIXEL CLK IN, /PIXEL CLK OUT''': 5.37 MHz clock. In comes from S-PPU1. Out goes to expansion port pin 22.<br />
* '''SYSTEM CLK''': 21.47727 MHz clock.<br />
* '''/RESOUT0''': S-PPU1 reset.<br />
* '''/RESOUT1''': The main reset signal, connected to the CPU, APU, cartridge, and expansion port.<br />
* '''/RESET''': Reset from CIC.<br />
* '''/EXTLATCH''': Controls H/V counter latching. Connected to joypad IO D7. Can be used with a light pen.<br />
* '''/OVER1, /OVER2''': /OVER from S-PPU1.<br />
* '''/TRANSPARENT''': This is believed to be high whenever an opaque (sprite or tilemap) pixel is drawn.<br />
* '''EXT7..0''': Video input, connected to VDB7..0.<br />
* '''DIGITAL VIDEO ENABLE''': When high, TST4..0, TST9..5, and TST14..10 are digital R4..0, G4..0, and B4..0 output. For correct digital video output, this should be connected to /OVER.<ref>[https://shmups.system11.org/viewtopic.php?f=6&t=66597 Shmups forum thread]: Sharp analog RGB for the 3-Chip SNES using digital signals</ref> As sold, this is connected to ground.<br />
* '''TST14..12''': When DIGITAL VIDEO ENABLE is low, these control other kinds of data that can be outputted over the TST pins. Output correlated with VRAM accesses has been observed.<ref>[https://circuit-board.de/forum/index.php/Thread/25396-SNES-Chips-decapped-2PPU-1CHIP-APU-DSP/?postID=702636#post702636 circuit-board forum thread]: SNES-Chips decapped (2PPU, 1CHIP, APU, DSP)</ref> As sold, these are connected to ground.<br />
<br />
==References==</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=WRAM_pinout&diff=312WRAM pinout2022-05-25T23:13:16Z<p>Fiskbit: Adds S-WRAM pinout.</p>
<hr />
<div>[[Category:Pinouts]]<br />
==Pinout==<br />
____________________<br />
| |<br />
+5V -- | 01 . 64 | -- GND<br />
CPU D4 <> | 02 63 | <> CPU D3<br />
CPU D5 <> | 03 62 | <> CPU D2<br />
CPU D6 <> | 04 61 | <> CPU D1<br />
CPU D7 <> | 05 60 | <> CPU D0<br />
SYSTEM CLK -> | 06 59 | <- CPU /WR<br />
REFRESH -> | 07 58 | <- /PWR<br />
/RESET -> | 08 57 | <- CPU /RD<br />
NC -- | 09 56 | <- /PRD<br />
CS1 -> | 10 55 | ?? G<br />
CS2 -> | 11 54 | <- PA1<br />
CS3 -> | 12 53 | <- PA0<br />
/CS1 (GND) -> | 13 52 | <- PS3 (PA7)<br />
/CS2 (GND) -> | 14 51 | <- PS2 (+5V)<br />
/CS3 -> | 15 Nintendo 50 | <- PS1 (+5V) Orientation:<br />
+5V -- | 16 S-WRAM 49 | -- +5V --------------------<br />
GND -- | 17 48 | -- GND 64 33<br />
NC -- | 18 47 | <- /PS5 | |<br />
NC -- | 19 46 | <- /PS4 .-----------.<br />
NC -- | 20 45 | <- /PS3 | Nintendo |<br />
NC -- | 21 44 | <- /PS2 | S-WRAM O|<br />
NC -- | 22 43 | <- /PS1 |. |<br />
CPU A0 -> | 23 42 | <- ENABLE '-----------'<br />
CPU A9 -> | 24 41 | <- CPU A8 | |<br />
CPU A1 -> | 25 40 | <- CPU A16 01 32<br />
CPU A10 -> | 26 39 | <- CPU A7<br />
CPU A2 -> | 27 38 | <- CPU A15 Legend:<br />
CPU A11 -> | 28 37 | <- CPU A6 ----------------------------<br />
CPU A3 -> | 29 36 | <- CPU A14 --[S-WRAM]-- Power, n/a<br />
CPU A12 -> | 30 O 35 | <- CPU A5 ->[S-WRAM]<- S-WRAM input<br />
CPU A4 -> | 31 34 | <- CPU A13 <-[S-WRAM]-> S-WRAM output<br />
+5V -- | 32 33 | -- GND <>[S-WRAM]<> Bidirectional<br />
|____________________| ??[S-WRAM]?? Unknown<br />
<br />
==Signal descriptions==<br />
* '''PS3''', '''/PS5..1''': These are peripheral bus enables and are connected to PA7..2 to map S-WRAM to $80-83.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=MMIO_registers&diff=304MMIO registers2022-05-25T09:30:05Z<p>Fiskbit: /* NMITIMEN - Interrupts and Joypad reading ($4200 write) */ Adds NMI details. Mentions the slight delay before auto-read starts. Improves auto-read description.</p>
<hr />
<div>This page covers 5A22, APU, and WRAM memory-mapped registers.<br />
* For PPU-related MMIO registers '''$2100-$213F''', see [[PPU registers]].<br />
* For DMA-related MMIO registers '''$4300-$437F''', see [[DMA registers]].<br />
* For a complete summary table of all MMIO registers, see [[MMIO register table]].<br />
<br />
{| class="wikitable"<br />
|+ 5A22, APU, and WRAM register summary<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
{{:MMIO register table/MMIO}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/MMIO|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
==Interrupts==<br />
{{Anchor|NMITIMEN}}<br />
===NMITIMEN - Interrupts and Joypad reading ($4200 write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
N.VH ...J<br />
| || |<br />
| || +- Joypad auto-read enable<br />
| ++------ H/V timer IRQ:<br />
| 00 = Disable timer<br />
| 01 = IRQ when H counter == HTIME<br />
| 10 = IRQ when V counter == VTIME and H counter == 0<br />
| 11 = IRQ when V counter == VTIME and H counter == HTIME<br />
+--------- Vblank NMI enable<br />
<br />
On power-on: NMITIMEN = $00<br />
On reset: NMITIMEN = $00<br />
<br />
* If the vblank NMI is enabled, then an NMI will occur at the start of vblank. More precisely, an NMI occurs whenever the bitwise AND of vblank NMI enable and RDNMI's vblank flag becomes 1. As a result, it is possible to get multiple NMIs for a single vblank if vblank NMI is disabled and enabled again while the vblank flag is still 1. This can be prevented by reading RDNMI, which clears the vblank flag.<br />
* TODO: Details on the exact conditions for an IRQ.<br />
* Auto-read runs shortly after the beginning of vblank, reading the current state of the two controller ports into the [[#JOY1|JOY1-4]] registers without halting the CPU. It is equivalent to writing 1 and then 0 to [[#JOYOUT|JOYOUT]] (used for latching standard controllers) and then reading [[#JOYSER0|JOYSER0]] and [[#JOYSER1|JOYSER1]] 16 times each. The controllers are then left in this state so that additional reads can be done manually from JOYSER0 and JOYSER1 to get any additional data, though this is unnecessary for standard controllers. Auto-read takes approximately 3 scanlines, during which the JOYOUT, JOYSER0, and JOYSER1 registers should not be manually accessed.<br />
<br />
===Screen timer values===<br />
----<br />
{{Anchor|HTIME}}<br />
====HTIMEL, HTIMEH - H timer target ($4207, $4208 write)====<br />
HTIMEH HTIMEL<br />
$4208 $4207<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
.... ...H LLLL LLLL<br />
| |||| ||||<br />
+---++++-++++- H counter target for timer IRQ<br />
<br />
On power-on: HTIME = $1FF<br />
<br />
Note that setting a value larger than the maximum H counter value of 339 will prevent the timer's H condition from being met.<br />
<br />
{{Anchor|VTIME}}<br />
====VTIMEL, VTIMEH - V timer target ($4209, $420A write)====<br />
VTIMEH VTIMEL<br />
$420A $4209<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
.... ...H LLLL LLLL<br />
| |||| ||||<br />
+---++++-++++- V counter target for timer IRQ<br />
<br />
On power-on: VTIME = $1FF<br />
<br />
Note that setting a value larger than the maximum V counter value will prevent the timer's V condition from being met. The maximum depends on the region (261 for NTSC, 311 for PAL) and interlacing (1 additional scanline every other frame).<br />
<br />
===Status===<br />
----<br />
{{Anchor|RDNMI}}<br />
====RDNMI - Vblank flag and CPU version ($4210 read)====<br />
7 bit 0<br />
---- ----<br />
Nxxx VVVV<br />
|||| ||||<br />
|||| ++++- CPU version<br />
|+++------ (Open bus)<br />
+--------- Vblank flag<br />
<br />
On power-on: RDNMI = RDNMI & $7F<br />
On reset: RDNMI = RDNMI & $7F<br />
On read: RDNMI = RDNMI & $7F<br />
<br />
The vblank flag is set at the start of vblank and cleared at the end of vblank or on read.<br />
<br />
{{Anchor|TIMEUP}}<br />
====TIMEUP - Timer flag ($4211 read)====<br />
7 bit 0<br />
---- ----<br />
Txxx xxxx<br />
|||| ||||<br />
|+++-++++- (Open bus)<br />
+--------- Timer flag<br />
<br />
On power-on: TIMEUP = TIMEUP & $7F<br />
On reset: TIMEUP = TIMEUP & $7F<br />
On read: TIMEUP = TIMEUP & $7F<br />
<br />
The timer flag is set when the timer condition specified in NMITIMEN becomes true and is cleared on read.<br />
<br />
{{Anchor|HVBJOY}}<br />
====HVBJOY - Screen and Joypad status ($4212 read)====<br />
7 bit 0<br />
---- ----<br />
VHxx xxxJ<br />
|||| ||||<br />
|||| |||+- Joypad auto-read in-progress flag<br />
||++-+++-- (Open bus)<br />
|+-------- Hblank flag<br />
+--------- Vblank flag<br />
<br />
* J - Set during joypad auto-read.<br />
* H - Set during horizontal blank period.<br />
* V - Set during vertical blank period.<br />
<br />
==APU==<br />
{{Anchor|APUIOn}}<br />
===APUIOn - Data-to-APU register n ($214n write) (n = 0..3)===<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- Data to APU<br />
<br />
===APUIOn - Data-from-APU register n ($214n read) (n = 0..3)===<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- Data from APU<br />
<br />
When the SPC700 reads from $F4+n in its address space, it receives the last value written to APUIOn. When APUIOn is read, the value received is the last one written by the SPC700 to $F4+n. If APUIOn is read while its corresponding $F4+n register is being written, the value read will be the bitwise OR of the old and new values.<br />
<br />
These registers are mirrored across $2140-217F.<br />
<br />
==WRAM==<br />
{{Anchor|WMDATA}}<br />
===WMDATA - S-WRAM data access ($2180 read/write)===<br />
----<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- S-WRAM data<br />
<br />
On write: [WMADD] = value<br />
WMADD = WMADD + 1<br />
<br />
This register's presence on the peripheral bus allows DMA between S-WRAM and another, different source.<br />
<br />
Because DMA simultaneously accesses the source and destination, S-WRAM cannot succesfully be both because it cannot simultaneously read from and write to itself. DMA from S-WRAM to this register has no effect, and DMA from this register to S-WRAM writes open bus. In both cases, the address is not incremented.<br />
<br />
{{Anchor|WMADD}}<br />
<br />
===WMADDL, WMADDM, WMADDH - S-WRAM address ($2181, $2182, $2183 write)===<br />
----<br />
WMADDH WMADDM WMADDL<br />
$2183 $2182 $2181<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
.... ...H MMMM MMMM LLLL LLLL<br />
| |||| |||| |||| ||||<br />
+---++++-++++---++++-++++- S-WRAM address for WMDATA<br />
<br />
DMA from S-WRAM to these registers has no effect.<br />
<br />
==ROM==<br />
{{Anchor|MEMSEL}}<br />
===MEMSEL - ROM access speed ($420D write)===<br />
7 bit 0<br />
---- ----<br />
.... ...F<br />
|<br />
+- FastROM enable<br />
<br />
On power-on: MEMSEL = $00<br />
<br />
If enabled, ROM access to banks $80-FF takes only 6 system clock cycles instead of 8.<br />
<br />
==Joypads==<br />
===Joypad NES-style interface===<br />
----<br />
{{Anchor|JOYOUT}}<br />
====JOYOUT - Joypad output ($4016 write)====<br />
7 bit 0<br />
---- ----<br />
.... .210<br />
|||<br />
||+- OUT0<br />
++-- OUT2-1 (not connected)<br />
<br />
OUT0 is used by standard controllers to latch the current button state. OUT2-1 are not connected in standard consoles, but may be used in the [[Super Famicom Box]] hotel system.<br />
<br />
{{Anchor|JOYSER0}}<br />
====JOYSER0 - Joypad serial data port 1 ($4016 read)====<br />
7 bit 0<br />
---- ----<br />
xxxx xxDD<br />
|||| ||||<br />
|||| ||++- Joypad port 1 data 2-1<br />
++++-++--- (Open bus)<br />
<br />
On read: Joypad port 1 is clocked (via joypad 1 /OE)<br />
<br />
{{Anchor|JOYSER1}}<br />
====JOYSER1 - Joypad serial data port 2 ($4017 read)====<br />
7 bit 0<br />
---- ----<br />
xxx1 11DD<br />
|||| ||||<br />
|||| ||++- Joypad port 2 data 2-1<br />
|||+-++--- Joypad 2 D4-2 (always 1)<br />
+++------- (Open bus)<br />
<br />
On read: Joypad port 2 is clocked (via joypad 2 /OE)<br />
<br />
The CPU has 5 joypad 2 inputs. Joypad port 2's data 2 and 1 pins connect to D1-0, while D4-2 are tied to ground (and thus read as 1).<br />
<br />
===Joypad I/O interface===<br />
----<br />
{{Anchor|WRIO}}<br />
====WRIO - Write I/O ($4201 write)====<br />
7 bit 0<br />
---- ----<br />
21DD DDDD<br />
|||| ||||<br />
||++-++++- CPU I/O D5-0 (not connected)<br />
|+-------- Joypad port 1 I/O<br />
+--------- Joypad port 2 I/O, and<br />
PPU /EXTLATCH light pen input<br />
<br />
On power-on: WRIO = $FF<br />
<br />
{{Anchor|RDIO}}<br />
====RDIO - Read I/O ($4213 read)====<br />
7 bit 0<br />
---- ----<br />
21DD DDDD<br />
|||| ||||<br />
||++-++++- CPU I/O D5-0 (not connected)<br />
|+-------- Joypad port 1 I/O<br />
+--------- Joypad port 2 I/O<br />
<br />
The I/O pins allow bidirectional communication between the CPU and joypads on a single wire per bit. Either side is able to set the bits to 0, so to read the value being sent by the other side, the reader must set its own corresponding bits to 1 before reading.<br />
<br />
The not-connected D5-0 bits can be used as general-purpose storage on standard consoles, but are used in the [[Super Famicom Box]] hotel system to communicate with its HD64180 CPU.<br />
<br />
Joypad port 2's I/O bit is also connected to the PPU's /EXTLATCH input, allowing the PPU's H and V counters to be latched when this bit is set to 0 by the CPU or joypad. This is intended to support a light pen or gun device. This should normally be set to 1 by the CPU to allow the counters to be latched. (See [[PPU_registers#OPHCT|PPU registers]])<br />
<br />
===Auto-read results===<br />
----<br />
{{Anchor|JOY1}}<br />
====JOY1L, JOY1H - Joypad port 1 data 1 ($4218, $4219 read)====<br />
JOY1H JOY1L<br />
$4219 $4218<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Joypad port 1 data 1 (first read on left to last read on right)<br />
<br />
{{Anchor|JOY2}}<br />
====JOY2L, JOY2H - Joypad port 2 data 1 ($421A, $421B read)====<br />
JOY2H JOY2L<br />
$421B $421A<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Joypad port 2 data 1 (first read on left to last read on right)<br />
<br />
{{Anchor|JOY3}}<br />
====JOY3L, JOY3H - Joypad port 1 data 2 ($421C, $421D read)====<br />
JOY3H JOY3L<br />
$421D $421C<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Joypad port 1 data 2 (first read on left to last read on right)<br />
<br />
{{Anchor|JOY4}}<br />
====JOY4L, JOY4H - Joypad port 2 data 2 ($421E, $421F read)====<br />
JOY4H JOY4L<br />
$421F $421E<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Joypad port 2 data 2 (first read on left to last read on right)<br />
<br />
==Math==<br />
===Multiplication===<br />
----<br />
{{Anchor|WRMPYA}}<br />
====WRMPYA - Multiplication factor A ($4202 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- Multiplication factor (8-bit unsigned)<br />
<br />
On power-on: WRMPYA = $FF<br />
<br />
{{Anchor|WRMPYB}}<br />
====WRMPYB - Multiplication factor B ($4203 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- Multiplication factor (8-bit unsigned)<br />
<br />
On write: Begins multiplication process<br />
<br />
The multiplication process takes up to 8 cycles and the result is written to RDMPY as it goes. See 5A22 [[Multiplication]] for more information.<br />
<br />
===Division===<br />
----<br />
{{Anchor|WRDIV}}<br />
====WRDIVL, WRDIVH - Dividend ($4204, $4205 write)====<br />
WRDIVH WRDIVL<br />
$4205 $4204<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Dividend (16-bit unsigned)<br />
<br />
On power-on: WRDIV = $FFFF<br />
<br />
{{Anchor|WRDIVB}}<br />
====WRDIVB - Divisor ($4206 write)====<br />
7 bit 0<br />
---- ----<br />
DDDD DDDD<br />
|||| ||||<br />
++++-++++- Divisor (8-bit unsigned)<br />
<br />
On write: Begins division process<br />
<br />
The division process takes up to 16 CPU cycles and the result is written to RDDIV (quotient) and RDMPY (remainder) as it goes. Dividing by 0 results in a quotient of $FFFF and a remainder equal to the dividend (WRDIV). See 5A22 [[Division]] for more information.<br />
<br />
===Result===<br />
----<br />
{{Anchor|RDDIV}}<br />
====RDDIVL, RDDIVH - Quotient ($4214, $4215 read)====<br />
RDDIVH RDDIVL<br />
$4215 $4214<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Quotient (16-bit unsigned)<br />
<br />
{{Anchor|RDMPY}}<br />
====RDMPYL, RDMPYH - Product or Remainder ($4216, $4217 read)====<br />
RDMPYH RDMPYL<br />
$4217 $4216<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Product (16-bit unsigned), or<br />
Remainder (16-bit unsigned)</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=DMA_registers&diff=303DMA registers2022-05-25T08:12:41Z<p>Fiskbit: Clarifies the DMA RAM copy limitation.</p>
<hr />
<div>The SNES's DMA (Direct Memory Access) unit allows a game to copy graphics, palettes, [[OAM]] and more at a much higher speed than the CPU can accomplish alone. This allows a game to make better use of the limited amount of time it has in vblank to change graphical memory.<br />
<br />
The SNES has two address buses: the CPU bus (also known as the A bus, which contains cartridge ROM, cartridge RAM, and the SNES's RAM) and the peripheral bus (also known as the B bus, which contains anything in the $2100-$21FF range, including [[PPU registers]] and APU registers). These buses use the same data bus, and DMA works by having a read on one address bus act as a write on the other, so copies are always from one bus to the other.<br />
<br />
Although it can be specified as both the source and destination, DMA cannot copy from one area of the console's 128 KiB S-WRAM to another. For that, the <code>[[MVN]]</code> and <code>[[MVP]]</code> instructions are probably the best available choice. However, DMA can copy between S-WRAM and some other kind of RAM, such as cartridge RAM.<br />
<br />
The SNES also features [[HDMA]] which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.<br />
<br />
These registers are always accessed at 3.58 MHz! That means that any channels that are not currently in use can have their registers repurposed for a small amount of fast RAM.<br />
<br />
{| class="wikitable"<br />
|+ DMA register summary (n = 0..7)<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
! [[DMA registers#MDMAEN|MDMAEN]]<br />
! $420B<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| DMA enable.<br />
|-<br />
! [[DMA registers#HDMAEN|HDMAEN]]<br />
! $420C<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| HDMA enable.<br />
|-<br />
{{:MMIO register table/DMA}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/DMA|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
== DMA channels ==<br />
<br />
The SNES contains 8 separate DMA "channels" - each one contains a set of parameters to configure a DMA transfers. They are configured with registers in the $4300-$437f range, where the first 16 addresses correspond to the first register, the second 16 addresses correspond to the next, and so on.<br />
<br />
{{Anchor|MDMAEN}}<br />
=== MDMAEN - Start DMA transfer ($420B write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 select<br />
|||| ||+-- Channel 1 select<br />
|||| |+--- Channel 2 select<br />
|||| +---- Channel 3 select<br />
|||+------ Channel 4 select<br />
||+------- Channel 5 select<br />
|+-------- Channel 6 select<br />
+--------- Channel 7 select<br />
<br />
On power-on: MDMAEN = $00<br />
On reset: MDMAEN = $00<br />
</pre><br />
<br />
Upon writing to this register, a DMA transfer is started for each bit that was set, starting with the lowest selected channel number up toward the highest. The CPU is stopped until all transfers have completed. If an HDMA transfer happens while the DMA transfer is going, the DMA transfer will be temporarily paused to allow the HDMA transfer to happen.<br />
<br />
{{Anchor|HDMAEN}}<br />
=== HDMAEN - Enable HDMA transfers ($420C write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 HDMA enable<br />
|||| ||+-- Channel 1 HDMA enable<br />
|||| |+--- Channel 2 HDMA enable<br />
|||| +---- Channel 3 HDMA enable<br />
|||+------ Channel 4 HDMA enable<br />
||+------- Channel 5 HDMA enable<br />
|+-------- Channel 6 HDMA enable<br />
+--------- Channel 7 HDMA enable<br />
<br />
On power-on: HDMAEN = $00<br />
On reset: HDMAEN = $00<br />
</pre><br />
<br />
This register enables HDMA for the selected channels.<br />
<br />
==DMA channel registers==<br />
{{Anchor|DMAPn}}<br />
=== DMAPn - DMA/HDMA parameters ($43n0 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
DIxA APPP<br />
|||| ||||<br />
|||| |+++- Transfer pattern (see below)<br />
|||+-+---- Address adjust mode (DMA only):<br />
||| 0: Increment A bus address after copy<br />
||| 1/3: Fixed<br />
||| 2: Decrement A bus address after copy<br />
||+------- (Unused)<br />
|+-------- Indirect (HDMA only)<br />
+--------- Direction: 0=Copy from A to B, 1=Copy from B to A<br />
<br />
On power-on: DMAPn = $FF<br />
<br />
The '''transfer pattern''' (P) controls the address pattern for different register types, and for HDMA also the number of bytes delivered per table entry.<br />
<br />
For example: pattern 1 allows a DMA copy to VRAM, which requires writing to two alternating addresses.<br />
<br />
{| class="wikitable"<br />
|+ DMA transfer patterns<br />
|-<br />
! Pattern !! HDMA bytes !! B Bus address !! Usage example<br />
|-<br />
! 0<br />
| 1 || <tt style="white-space: nowrap">+0</tt> || WRAM, Mode 7 graphics/tilemap<br />
|-<br />
! 1<br />
| 2 || <tt style="white-space: nowrap">+0 +1</tt> || VRAM<br />
|-<br />
! 2<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || OAM, CGRAM<br />
|-<br />
! 3<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || Scroll positions, Mode 7 parameters<br />
|-<br />
! 4<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +2 +3</tt> || Window <br />
|-<br />
! 5<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +0 +1</tt> || (Undocumented)<br />
|-<br />
! 6<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || (Same as 2, undocumented)<br />
|-<br />
! 7<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || (Same as 3, undocumented)<br />
|}<br />
<br />
A fixed '''address adjust mode''' (A) can be used to fill the DMA target with a single repeated byte of data.<br />
<br />
{{Anchor|BBADn}}<br />
<br />
=== BBADn - B-bus address ($43n1 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- Selects a hardware register to read or write from, in the $2100-$21ff range<br />
<br />
On power-on: BBADn = $FF<br />
<br />
This can be used with various [[PPU registers]], and also [[MMIO registers#WMDATA|WMDATA]].<br />
<br />
{{Anchor|UNUSEDn}}<br />
<br />
=== UNUSEDn - Unused byte ($43nB and $43nF read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
NNNN NNNN<br />
|||| ||||<br />
++++-++++- One unused byte available through two different addresses<br />
<br />
On power-on: UNUSEDn = $FF<br />
<br />
Seems to have no effect on DMA or HDMA, and this register cannot be used as a source for a DMA fill.<br />
<br />
==Configuration registers (DMA) ==<br />
<br />
{{Anchor|A1Tn|A1TnL|A1TnH|A1Bn}}<br />
=== A1TnL, A1TnH, A1Bn - DMA Current Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
----<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- Address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
The low 16-bits of this address change as the DMA happens, but the bank byte is fixed. DMA can not cross banks.<br />
<br />
[[#HDMA-A1Tn|HDMA uses these registers]] for its table address instead.<br />
<br />
{{Anchor|DASn|DASnL|DASnH}}<br />
=== DASnL, DASnH - DMA Byte-Counter ($43n5, $43n6 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASnH DASnL<br />
$43n6 $43n5<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- 16-bit number that indicates how many bytes to transfer<br />
<br />
On power-on: DASn = $FFFF<br />
<br />
A byte count of zero means 65536 bytes.<br />
<br />
This byte count is not affected by the DMA pattern.<br />
The SNES will stop before a pattern is completed if it runs out of bytes.<br />
<br />
Once the DMA finishes, these registers will be zero.<br />
<br />
[[#HDMA-DASn|HDMA uses these registers]] for its indirect address instead.<br />
<br />
== Configuration registers (HDMA) ==<br />
<br />
{{Anchor|HDMA-A1Tn}}<br />
=== A1TnL, A1TnH, A1Bn - HDMA Table Start Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit little-endian address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
These registers control where the channel's HDMA table is. During initiation of HDMA this address gets copied into [[#A2AnL|A2AnL]]/[[#A2AnH|A2AnH]] ($43n8/$43n9).<br />
<br />
{{Anchor|HDMA-DASn|DASBn}}<br />
<br />
=== DASBn - Indirect HDMA Bank ($43n7 read/write) (n = 0..7) ===<br />
<br />
7 bit 0<br />
---- ----<br />
BBBB BBBB<br />
|||| ||||<br />
++++-++++- High byte (bank) of HDMA indirect address<br />
<br />
On power-on: DASBn = $FF<br />
<br />
This must be set manually by the program to control the indirect HDMA bank. (DASnL/DASnH are automatically updated from the table.)<br />
<br />
== Other HDMA registers ==<br />
These keep track of each channel's state as HDMA is happening.<br />
<br />
{{Anchor|HDMA-DASn}}<br />
=== DASnL, DASnH, DASBn - Indirect HDMA Address ($43n5, $43n6, $43n7 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASBn DASnH DASnL<br />
$43n7 $43n6 $43n5<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---------<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- The current indirect DMA address.<br />
<br />
On power-on: DASn = $FFFFFF<br />
<br />
With indirect HDMA, if the repeat bit is set in the table entry, then the SNES will continue to read increasing addresses starting from the one given in the table, using these registers to keep track of where it currently is.<br />
<br />
The low 16 bits are automatically copied from the table, but the bank byte [[#DASBn|DASBn]] must be manually set by the program. The bank byte is fixed, and HDMA will not cross banks.<br />
<br />
{{Anchor|A2An|A2AnL|A2AnH}}<br />
<br />
=== A2AnL, A2AnH - HDMA Table Current Address ($43n8, $43n9 read/write) (n = 0..7) ===<br />
----<br />
<br />
A2AnH A2AnL<br />
$43n9 $43n8<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Low 16 bits of the current address within the HDMA table<br />
<br />
On power-on: A2An = $FFFF<br />
<br />
Bank byte is taken from $43n4, as it does not change.<br />
<br />
{{Anchor|NLTRn}}<br />
=== NLTRn - HDMA Line-Counter ($43nA read/write) (n = 0..7) ===<br />
----<br />
7 bit 0<br />
---- ----<br />
RLLL LLLL<br />
|||| ||||<br />
|+++-++++- Number of scanlines left<br />
+--------- Repeat flag<br />
<br />
On power-on: NLTRn = $FF<br />
<br />
Automatically loaded from the table. Scanline count is decremented every scanline until it hits zero.<br />
<br />
== HDMA table format ==<br />
<br />
HDMA tables specify what values to write to the selected B bus register, as well as which scanlines to write the values on. The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).<br />
<br />
=== Direct HDMA table entries ===<br />
----<br />
<br />
1 byte - Line count, and repeat mode<br />
N bytes - Data<br />
<br />
The number of bytes in each Data section is determined by the pattern chosen for the channel in register $43n0.<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written, then the SNES waits for the specified number of scanlines before continuing onto the next table entry.<br />
* If repeat mode is on, then the total size of the data section is the number of scanlines multiplied by the number of bytes in the pattern. One pattern's worth of bytes are written for however many scanlines indicated in the table.<br />
<br />
=== Indirect HDMA table entries ===<br />
----<br />
1 byte - Line count, and repeat mode<br />
2 bytes - Pointer to access the data through<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written from an address starting from the pointer given.<br />
* If repeat mode is on, the SNES continues to progress through the bytes that the pointer points to, for however many scanlines are indicated in the table.<br />
<br />
=== Line count, repeat mode byte ===<br />
----<br />
Possible values for the "Line count, and repeat mode" byte are as follows:<br />
* $00: Stop processing HDMA on that channel for the rest of the frame.<br />
* $01-$80: Write once, then wait for X-1 scanlines<br />
* $81-$FF: Write every scanline for X-$80 scanlines, repeat mode</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=DMA_registers&diff=300DMA registers2022-05-25T07:28:05Z<p>Fiskbit: Adds CPU and peripheral bus names to the intro. Elaborates a bit on what DMA is doing to explain why transfers must be cross-bus.</p>
<hr />
<div>The SNES's DMA (Direct Memory Access) unit allows a game to copy graphics, palettes, [[OAM]] and more at a much higher speed than the CPU can accomplish alone. This allows a game to make better use of the limited amount of time it has in vblank to change graphical memory.<br />
<br />
The SNES has two address buses: the CPU bus (also known as the A bus, which contains cartridge ROM, cartridge RAM, and the SNES's RAM) and the peripheral bus (also known as the B bus, which contains anything in the $2100-$21FF range, including [[PPU registers]] and APU registers). These buses use the same data bus, and DMA works by having a read on one address bus act as a write on the other, so copies are always from one bus to the other.<br />
<br />
DMA cannot copy from one area of the SNES's RAM to another, even if RAM is specified as both the source and destination. For that, the <code>[[MVN]]</code> and <code>[[MVP]]</code> instructions are probably the best available choice.<br />
<br />
The SNES also features [[HDMA]] which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.<br />
<br />
These registers are always accessed at 3.58 MHz! That means that any channels that are not currently in use can have their registers repurposed for a small amount of fast RAM.<br />
<br />
{| class="wikitable"<br />
|+ DMA register summary (n = 0..7)<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
! [[DMA registers#MDMAEN|MDMAEN]]<br />
! $420B<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| DMA enable.<br />
|-<br />
! [[DMA registers#HDMAEN|HDMAEN]]<br />
! $420C<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| HDMA enable.<br />
|-<br />
{{:MMIO register table/DMA}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/DMA|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
== DMA channels ==<br />
<br />
The SNES contains 8 separate DMA "channels" - each one contains a set of parameters to configure a DMA transfers. They are configured with registers in the $4300-$437f range, where the first 16 addresses correspond to the first register, the second 16 addresses correspond to the next, and so on.<br />
<br />
{{Anchor|MDMAEN}}<br />
=== MDMAEN - Start DMA transfer ($420B write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 select<br />
|||| ||+-- Channel 1 select<br />
|||| |+--- Channel 2 select<br />
|||| +---- Channel 3 select<br />
|||+------ Channel 4 select<br />
||+------- Channel 5 select<br />
|+-------- Channel 6 select<br />
+--------- Channel 7 select<br />
<br />
On power-on: MDMAEN = $00<br />
On reset: MDMAEN = $00<br />
</pre><br />
<br />
Upon writing to this register, a DMA transfer is started for each bit that was set, starting with the lowest selected channel number up toward the highest. The CPU is stopped until all transfers have completed. If an HDMA transfer happens while the DMA transfer is going, the DMA transfer will be temporarily paused to allow the HDMA transfer to happen.<br />
<br />
{{Anchor|HDMAEN}}<br />
=== HDMAEN - Enable HDMA transfers ($420C write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 HDMA enable<br />
|||| ||+-- Channel 1 HDMA enable<br />
|||| |+--- Channel 2 HDMA enable<br />
|||| +---- Channel 3 HDMA enable<br />
|||+------ Channel 4 HDMA enable<br />
||+------- Channel 5 HDMA enable<br />
|+-------- Channel 6 HDMA enable<br />
+--------- Channel 7 HDMA enable<br />
<br />
On power-on: HDMAEN = $00<br />
On reset: HDMAEN = $00<br />
</pre><br />
<br />
This register enables HDMA for the selected channels.<br />
<br />
==DMA channel registers==<br />
{{Anchor|DMAPn}}<br />
=== DMAPn - DMA/HDMA parameters ($43n0 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
DIxA APPP<br />
|||| ||||<br />
|||| |+++- Transfer pattern (see below)<br />
|||+-+---- Address adjust mode (DMA only):<br />
||| 0: Increment A bus address after copy<br />
||| 1/3: Fixed<br />
||| 2: Decrement A bus address after copy<br />
||+------- (Unused)<br />
|+-------- Indirect (HDMA only)<br />
+--------- Direction: 0=Copy from A to B, 1=Copy from B to A<br />
<br />
On power-on: DMAPn = $FF<br />
<br />
The '''transfer pattern''' (P) controls the address pattern for different register types, and for HDMA also the number of bytes delivered per table entry.<br />
<br />
For example: pattern 1 allows a DMA copy to VRAM, which requires writing to two alternating addresses.<br />
<br />
{| class="wikitable"<br />
|+ DMA transfer patterns<br />
|-<br />
! Pattern !! HDMA bytes !! B Bus address !! Usage example<br />
|-<br />
! 0<br />
| 1 || <tt style="white-space: nowrap">+0</tt> || WRAM, Mode 7 graphics/tilemap<br />
|-<br />
! 1<br />
| 2 || <tt style="white-space: nowrap">+0 +1</tt> || VRAM<br />
|-<br />
! 2<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || OAM, CGRAM<br />
|-<br />
! 3<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || Scroll positions, Mode 7 parameters<br />
|-<br />
! 4<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +2 +3</tt> || Window <br />
|-<br />
! 5<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +0 +1</tt> || (Undocumented)<br />
|-<br />
! 6<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || (Same as 2, undocumented)<br />
|-<br />
! 7<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || (Same as 3, undocumented)<br />
|}<br />
<br />
A fixed '''address adjust mode''' (A) can be used to fill the DMA target with a single repeated byte of data.<br />
<br />
{{Anchor|BBADn}}<br />
<br />
=== BBADn - B-bus address ($43n1 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- Selects a hardware register to read or write from, in the $2100-$21ff range<br />
<br />
On power-on: BBADn = $FF<br />
<br />
This can be used with various [[PPU registers]], and also [[MMIO registers#WMDATA|WMDATA]].<br />
<br />
{{Anchor|UNUSEDn}}<br />
<br />
=== UNUSEDn - Unused byte ($43nB and $43nF read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
NNNN NNNN<br />
|||| ||||<br />
++++-++++- One unused byte available through two different addresses<br />
<br />
On power-on: UNUSEDn = $FF<br />
<br />
Seems to have no effect on DMA or HDMA, and this register cannot be used as a source for a DMA fill.<br />
<br />
==Configuration registers (DMA) ==<br />
<br />
{{Anchor|A1Tn|A1TnL|A1TnH|A1Bn}}<br />
=== A1TnL, A1TnH, A1Bn - DMA Current Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
----<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- Address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
The low 16-bits of this address change as the DMA happens, but the bank byte is fixed. DMA can not cross banks.<br />
<br />
[[#HDMA-A1Tn|HDMA uses these registers]] for its table address instead.<br />
<br />
{{Anchor|DASn|DASnL|DASnH}}<br />
=== DASnL, DASnH - DMA Byte-Counter ($43n5, $43n6 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASnH DASnL<br />
$43n6 $43n5<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- 16-bit number that indicates how many bytes to transfer<br />
<br />
On power-on: DASn = $FFFF<br />
<br />
A byte count of zero means 65536 bytes.<br />
<br />
This byte count is not affected by the DMA pattern.<br />
The SNES will stop before a pattern is completed if it runs out of bytes.<br />
<br />
Once the DMA finishes, these registers will be zero.<br />
<br />
[[#HDMA-DASn|HDMA uses these registers]] for its indirect address instead.<br />
<br />
== Configuration registers (HDMA) ==<br />
<br />
{{Anchor|HDMA-A1Tn}}<br />
=== A1TnL, A1TnH, A1Bn - HDMA Table Start Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit little-endian address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
These registers control where the channel's HDMA table is. During initiation of HDMA this address gets copied into [[#A2AnL|A2AnL]]/[[#A2AnH|A2AnH]] ($43n8/$43n9).<br />
<br />
{{Anchor|HDMA-DASn|DASBn}}<br />
<br />
=== DASBn - Indirect HDMA Bank ($43n7 read/write) (n = 0..7) ===<br />
<br />
7 bit 0<br />
---- ----<br />
BBBB BBBB<br />
|||| ||||<br />
++++-++++- High byte (bank) of HDMA indirect address<br />
<br />
On power-on: DASBn = $FF<br />
<br />
This must be set manually by the program to control the indirect HDMA bank. (DASnL/DASnH are automatically updated from the table.)<br />
<br />
== Other HDMA registers ==<br />
These keep track of each channel's state as HDMA is happening.<br />
<br />
{{Anchor|HDMA-DASn}}<br />
=== DASnL, DASnH, DASBn - Indirect HDMA Address ($43n5, $43n6, $43n7 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASBn DASnH DASnL<br />
$43n7 $43n6 $43n5<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---------<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- The current indirect DMA address.<br />
<br />
On power-on: DASn = $FFFFFF<br />
<br />
With indirect HDMA, if the repeat bit is set in the table entry, then the SNES will continue to read increasing addresses starting from the one given in the table, using these registers to keep track of where it currently is.<br />
<br />
The low 16 bits are automatically copied from the table, but the bank byte [[#DASBn|DASBn]] must be manually set by the program. The bank byte is fixed, and HDMA will not cross banks.<br />
<br />
{{Anchor|A2An|A2AnL|A2AnH}}<br />
<br />
=== A2AnL, A2AnH - HDMA Table Current Address ($43n8, $43n9 read/write) (n = 0..7) ===<br />
----<br />
<br />
A2AnH A2AnL<br />
$43n9 $43n8<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Low 16 bits of the current address within the HDMA table<br />
<br />
On power-on: A2An = $FFFF<br />
<br />
Bank byte is taken from $43n4, as it does not change.<br />
<br />
{{Anchor|NLTRn}}<br />
=== NLTRn - HDMA Line-Counter ($43nA read/write) (n = 0..7) ===<br />
----<br />
7 bit 0<br />
---- ----<br />
RLLL LLLL<br />
|||| ||||<br />
|+++-++++- Number of scanlines left<br />
+--------- Repeat flag<br />
<br />
On power-on: NLTRn = $FF<br />
<br />
Automatically loaded from the table. Scanline count is decremented every scanline until it hits zero.<br />
<br />
== HDMA table format ==<br />
<br />
HDMA tables specify what values to write to the selected B bus register, as well as which scanlines to write the values on. The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).<br />
<br />
=== Direct HDMA table entries ===<br />
----<br />
<br />
1 byte - Line count, and repeat mode<br />
N bytes - Data<br />
<br />
The number of bytes in each Data section is determined by the pattern chosen for the channel in register $43n0.<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written, then the SNES waits for the specified number of scanlines before continuing onto the next table entry.<br />
* If repeat mode is on, then the total size of the data section is the number of scanlines multiplied by the number of bytes in the pattern. One pattern's worth of bytes are written for however many scanlines indicated in the table.<br />
<br />
=== Indirect HDMA table entries ===<br />
----<br />
1 byte - Line count, and repeat mode<br />
2 bytes - Pointer to access the data through<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written from an address starting from the pointer given.<br />
* If repeat mode is on, the SNES continues to progress through the bytes that the pointer points to, for however many scanlines are indicated in the table.<br />
<br />
=== Line count, repeat mode byte ===<br />
----<br />
Possible values for the "Line count, and repeat mode" byte are as follows:<br />
* $00: Stop processing HDMA on that channel for the rest of the frame.<br />
* $01-$80: Write once, then wait for X-1 scanlines<br />
* $81-$FF: Write every scanline for X-$80 scanlines, repeat mode</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=Memory_map&diff=299Memory map2022-05-25T07:06:48Z<p>Fiskbit: Uses consistent, unambiguous units (KiB and MiB).</p>
<hr />
<div>== Overall ==<br />
[[File:Snes overall map.png]]<br />
<br />
The SNES natively provides access to RAM and I/O at specific locations. The cartridge is free to provide whatever it wants in the remaining space, and there are specific address ranges that are conventionally used to add access to additional hardware such as extra RAM or a coprocessor. Different address ranges are accessed at different speeds, and the speed of the ROM at banks $80-$FF may be changed with register $420D.<br />
<br />
The first 8 KiB of RAM is mirrored into many banks for convenient access, and banks $7E-$7F provide continuous access to the entire 128 KiB in one continuous address range.<br />
<br />
A [[ROM header]] is always present in the memory map at $00FFC0, though LoROM and HiROM will place these at a different location within the ROM itself.<br />
<br />
== LoROM ==<br />
[[File:Snes lorom map.png]]<br />
<br />
The LoROM mapping mode uses 32 KiB banks. The first 15 address pins are connected normally, but the 16th address pin on the SNES cartridge port is not connected to anything.<br />
<br />
The benefit of LoROM is that it is simpler to understand, and is closer to what NES developers are used to. LoROM can go up to 4 MiB, but past the 2 MiB mark the RAM and I/O are no longer mirrored into the banks. For a LoROM game over 2 MiB, it's recommended to put code toward the beginning of the ROM, and data toward the end.<br />
<br />
The [[ROM header]] resides at the end of the first 32 KiB bank at $007FC0 in the ROM, mapped to $00FFC0 in memory.<br />
<br />
Connections:<br />
<pre><br />
A0-A14 --> A0-A14<br />
A15 (Not connected)<br />
A16 --> A15<br />
A17 --> A16<br />
A18 --> A17<br />
A19 --> A18<br />
A20 --> A19<br />
A21 --> A20<br />
A22 --> A21<br />
A23 (Not connected)<br />
</pre><br />
<br />
== HiROM ==<br />
[[File:Snes hirom map.png]]<br />
<br />
The HiROM mapping mode uses 64 KiB banks. It is created by connecting the SNES's address pins to the ROM's address pins 1-to-1, without skipping any pins. This allows access to a linear view of the entire ROM at $C0-$FF. This simplifies programming for data that can cross bank boundaries. Additionally it helps fit more data into the ROM, because data that cannot cross bank boundaries has fewer bank boundaries to avoid.<br />
<br />
HiROM can be viewed as a superset of LoROM because it still provides access to the last 32 KiB of every bank in the LoROM area. Therefore a HiROM game can decide to put code in those areas and be programmed as if it were a LoROM game, while still having the benefits of 64 KiB data banks.<br />
<br />
The [[ROM header]] resides at the end of the first 64 KiB bank at $00FFC0 in the ROM, mapped to $00FFC0 in memory.<br />
<br />
Connections:<br />
<pre><br />
A0-A21 --> A0-A21<br />
A22 (Not connected)<br />
A23 (Not connected)<br />
</pre><br />
<br />
== ExHiROM ==<br />
<br />
ExHiROM is a map meant for exceeding the 4 MiB limit HiROM normally has. Banks $80-$FF point to the first 4 MiB of the ROM file as normal, but banks $00-$7D can point up to an additional 4 MiB (minus 64 KiB due to the RAM banks).<br />
<br />
Connections:<br />
<pre><br />
A0-A21 --> A0-21<br />
A22 (Not connected)<br />
A23 --> A22 (inverted)<br />
</pre><br />
<br />
The [[ROM header]] resides at the end of the first 32 KiB bank past 4MB at $40FFC0 in the ROM, mapped to $00FFC0 in memory.</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=DMA_registers&diff=298DMA registers2022-05-25T06:09:49Z<p>Fiskbit: /* NLTRn - HDMA Line-Counter ($43nA read/write) (n = 0..7) */ Adds missing n suffix to NLTR.</p>
<hr />
<div>The SNES's DMA (Direct Memory Access) unit allows a game to copy graphics, palettes, [[OAM]] and more at a much higher speed than the CPU can accomplish alone. This allows a game to make better use of the limited amount of time it has in vblank to change graphical memory.<br />
<br />
The SNES has two address buses - consisting of the A bus (which contains cartridge ROM, cartridge RAM, and the SNES's RAM) and the B bus (anything in the $2100-$21ff range, including [[PPU registers]] and APU registers). DMA always involves copying something from one of these two buses to something on the other bus.<br />
<br />
DMA cannot copy from one area of the SNES's RAM to another, even if RAM is specified as both the source and destination. For that, the <code>[[MVN]]</code> and <code>[[MVP]]</code> instructions are probably the best available choice.<br />
<br />
The SNES also features [[HDMA]] which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.<br />
<br />
These registers are always accessed at 3.58 MHz! That means that any channels that are not currently in use can have their registers repurposed for a small amount of fast RAM.<br />
<br />
{| class="wikitable"<br />
|+ DMA register summary (n = 0..7)<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
! [[DMA registers#MDMAEN|MDMAEN]]<br />
! $420B<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| DMA enable.<br />
|-<br />
! [[DMA registers#HDMAEN|HDMAEN]]<br />
! $420C<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| HDMA enable.<br />
|-<br />
{{:MMIO register table/DMA}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/DMA|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
== DMA channels ==<br />
<br />
The SNES contains 8 separate DMA "channels" - each one contains a set of parameters to configure a DMA transfers. They are configured with registers in the $4300-$437f range, where the first 16 addresses correspond to the first register, the second 16 addresses correspond to the next, and so on.<br />
<br />
<br />
{{Anchor|MDMAEN}}<br />
=== MDMAEN - Start DMA transfer ($420B write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 select<br />
|||| ||+-- Channel 1 select<br />
|||| |+--- Channel 2 select<br />
|||| +---- Channel 3 select<br />
|||+------ Channel 4 select<br />
||+------- Channel 5 select<br />
|+-------- Channel 6 select<br />
+--------- Channel 7 select<br />
<br />
On power-on: MDMAEN = $00<br />
On reset: MDMAEN = $00<br />
</pre><br />
<br />
Upon writing to this register, a DMA transfer is started for each bit that was set, starting with the lowest selected channel number up toward the highest. The CPU is stopped until all transfers have completed. If an HDMA transfer happens while the DMA transfer is going, the DMA transfer will be temporarily paused to allow the HDMA transfer to happen.<br />
<br />
{{Anchor|HDMAEN}}<br />
=== HDMAEN - Enable HDMA transfers ($420C write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 HDMA enable<br />
|||| ||+-- Channel 1 HDMA enable<br />
|||| |+--- Channel 2 HDMA enable<br />
|||| +---- Channel 3 HDMA enable<br />
|||+------ Channel 4 HDMA enable<br />
||+------- Channel 5 HDMA enable<br />
|+-------- Channel 6 HDMA enable<br />
+--------- Channel 7 HDMA enable<br />
<br />
On power-on: HDMAEN = $00<br />
On reset: HDMAEN = $00<br />
</pre><br />
<br />
This register enables HDMA for the selected channels.<br />
<br />
==DMA channel registers==<br />
{{Anchor|DMAPn}}<br />
=== DMAPn - DMA/HDMA parameters ($43n0 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
DIxA APPP<br />
|||| ||||<br />
|||| |+++- Transfer pattern (see below)<br />
|||+-+---- Address adjust mode (DMA only):<br />
||| 0: Increment A bus address after copy<br />
||| 1/3: Fixed<br />
||| 2: Decrement A bus address after copy<br />
||+------- (Unused)<br />
|+-------- Indirect (HDMA only)<br />
+--------- Direction: 0=Copy from A to B, 1=Copy from B to A<br />
<br />
On power-on: DMAPn = $FF<br />
<br />
The '''transfer pattern''' (P) controls the address pattern for different register types, and for HDMA also the number of bytes delivered per table entry.<br />
<br />
For example: pattern 1 allows a DMA copy to VRAM, which requires writing to two alternating addresses.<br />
<br />
{| class="wikitable"<br />
|+ DMA transfer patterns<br />
|-<br />
! Pattern !! HDMA bytes !! B Bus address !! Usage example<br />
|-<br />
! 0<br />
| 1 || <tt style="white-space: nowrap">+0</tt> || WRAM, Mode 7 graphics/tilemap<br />
|-<br />
! 1<br />
| 2 || <tt style="white-space: nowrap">+0 +1</tt> || VRAM<br />
|-<br />
! 2<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || OAM, CGRAM<br />
|-<br />
! 3<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || Scroll positions, Mode 7 parameters<br />
|-<br />
! 4<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +2 +3</tt> || Window <br />
|-<br />
! 5<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +0 +1</tt> || (Undocumented)<br />
|-<br />
! 6<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || (Same as 2, undocumented)<br />
|-<br />
! 7<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || (Same as 3, undocumented)<br />
|}<br />
<br />
A fixed '''address adjust mode''' (A) can be used to fill the DMA target with a single repeated byte of data.<br />
<br />
{{Anchor|BBADn}}<br />
<br />
=== BBADn - B-bus address ($43n1 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- Selects a hardware register to read or write from, in the $2100-$21ff range<br />
<br />
On power-on: BBADn = $FF<br />
<br />
This can be used with various [[PPU registers]], and also [[MMIO registers#WMDATA|WMDATA]].<br />
<br />
{{Anchor|UNUSEDn}}<br />
<br />
=== UNUSEDn - Unused byte ($43nB and $43nF read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
NNNN NNNN<br />
|||| ||||<br />
++++-++++- One unused byte available through two different addresses<br />
<br />
On power-on: UNUSEDn = $FF<br />
<br />
Seems to have no effect on DMA or HDMA, and this register cannot be used as a source for a DMA fill.<br />
<br />
==Configuration registers (DMA) ==<br />
<br />
{{Anchor|A1Tn|A1TnL|A1TnH|A1Bn}}<br />
=== A1TnL, A1TnH, A1Bn - DMA Current Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
----<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- Address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
The low 16-bits of this address change as the DMA happens, but the bank byte is fixed. DMA can not cross banks.<br />
<br />
[[#HDMA-A1Tn|HDMA uses these registers]] for its table address instead.<br />
<br />
{{Anchor|DASn|DASnL|DASnH}}<br />
=== DASnL, DASnH - DMA Byte-Counter ($43n5, $43n6 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASnH DASnL<br />
$43n6 $43n5<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- 16-bit number that indicates how many bytes to transfer<br />
<br />
On power-on: DASn = $FFFF<br />
<br />
A byte count of zero means 65536 bytes.<br />
<br />
This byte count is not affected by the DMA pattern.<br />
The SNES will stop before a pattern is completed if it runs out of bytes.<br />
<br />
Once the DMA finishes, these registers will be zero.<br />
<br />
[[#HDMA-DASn|HDMA uses these registers]] for its indirect address instead.<br />
<br />
== Configuration registers (HDMA) ==<br />
<br />
{{Anchor|HDMA-A1Tn}}<br />
=== A1TnL, A1TnH, A1Bn - HDMA Table Start Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit little-endian address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
These registers control where the channel's HDMA table is. During initiation of HDMA this address gets copied into [[#A2AnL|A2AnL]]/[[#A2AnH|A2AnH]] ($43n8/$43n9).<br />
<br />
{{Anchor|HDMA-DASn|DASBn}}<br />
<br />
=== DASBn - Indirect HDMA Bank ($43n7 read/write) (n = 0..7) ===<br />
<br />
7 bit 0<br />
---- ----<br />
BBBB BBBB<br />
|||| ||||<br />
++++-++++- High byte (bank) of HDMA indirect address<br />
<br />
On power-on: DASBn = $FF<br />
<br />
This must be set manually by the program to control the indirect HDMA bank. (DASnL/DASnH are automatically updated from the table.)<br />
<br />
== Other HDMA registers ==<br />
These keep track of each channel's state as HDMA is happening.<br />
<br />
{{Anchor|HDMA-DASn}}<br />
=== DASnL, DASnH, DASBn - Indirect HDMA Address ($43n5, $43n6, $43n7 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASBn DASnH DASnL<br />
$43n7 $43n6 $43n5<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---------<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- The current indirect DMA address.<br />
<br />
On power-on: DASn = $FFFFFF<br />
<br />
With indirect HDMA, if the repeat bit is set in the table entry, then the SNES will continue to read increasing addresses starting from the one given in the table, using these registers to keep track of where it currently is.<br />
<br />
The low 16 bits are automatically copied from the table, but the bank byte [[#DASBn|DASBn]] must be manually set by the program. The bank byte is fixed, and HDMA will not cross banks.<br />
<br />
{{Anchor|A2An|A2AnL|A2AnH}}<br />
<br />
=== A2AnL, A2AnH - HDMA Table Current Address ($43n8, $43n9 read/write) (n = 0..7) ===<br />
----<br />
<br />
A2AnH A2AnL<br />
$43n9 $43n8<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Low 16 bits of the current address within the HDMA table<br />
<br />
On power-on: A2An = $FFFF<br />
<br />
Bank byte is taken from $43n4, as it does not change.<br />
<br />
{{Anchor|NLTRn}}<br />
=== NLTRn - HDMA Line-Counter ($43nA read/write) (n = 0..7) ===<br />
----<br />
7 bit 0<br />
---- ----<br />
RLLL LLLL<br />
|||| ||||<br />
|+++-++++- Number of scanlines left<br />
+--------- Repeat flag<br />
<br />
On power-on: NLTRn = $FF<br />
<br />
Automatically loaded from the table. Scanline count is decremented every scanline until it hits zero.<br />
<br />
== HDMA table format ==<br />
<br />
HDMA tables specify what values to write to the selected B bus register, as well as which scanlines to write the values on. The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).<br />
<br />
=== Direct HDMA table entries ===<br />
----<br />
<br />
1 byte - Line count, and repeat mode<br />
N bytes - Data<br />
<br />
The number of bytes in each Data section is determined by the pattern chosen for the channel in register $43n0.<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written, then the SNES waits for the specified number of scanlines before continuing onto the next table entry.<br />
* If repeat mode is on, then the total size of the data section is the number of scanlines multiplied by the number of bytes in the pattern. One pattern's worth of bytes are written for however many scanlines indicated in the table.<br />
<br />
=== Indirect HDMA table entries ===<br />
----<br />
1 byte - Line count, and repeat mode<br />
2 bytes - Pointer to access the data through<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written from an address starting from the pointer given.<br />
* If repeat mode is on, the SNES continues to progress through the bytes that the pointer points to, for however many scanlines are indicated in the table.<br />
<br />
=== Line count, repeat mode byte ===<br />
----<br />
Possible values for the "Line count, and repeat mode" byte are as follows:<br />
* $00: Stop processing HDMA on that channel for the rest of the frame.<br />
* $01-$80: Write once, then wait for X-1 scanlines<br />
* $81-$FF: Write every scanline for X-$80 scanlines, repeat mode</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=MMIO_register_table/DMA&diff=297MMIO register table/DMA2022-05-25T06:08:52Z<p>Fiskbit: Fixes register name: NTRL -> NLTR.</p>
<hr />
<div><noinclude><br />
See: [[MMIO register table]]<br />
{| class="wikitable"<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
</noinclude><br />
<br />
|-<br />
! [[DMA registers#DMAPn|DMAPn]]<br />
! $43n0<br />
| style="text-align: right" | <tt style="white-space: nowrap">DI.A APPP</tt><br />
| RW8<br />
| Direction (D), indirect HDMA (I), address increment (A), transfer pattern (P).<br />
|-<br />
! [[DMA registers#BBADn|BBADn]]<br />
! $43n1<br />
| style="text-align: right" | <tt style="white-space: nowrap">AAAA AAAA</tt><br />
| RW8<br />
| B-bus address.<br />
|-<br />
! [[DMA registers#A1TnL|A1TnL]]<br/>[[DMA registers#A1TnH|A1TnH]]<br/>[[DMA registers#A1Bn|A1Bn]]<br />
! $43n2<br/>$43n3<br/>$43n4<br />
| style="text-align: right" | <tt style="white-space: nowrap">LLLL LLLL<br/>HHHH HHHH<br/>BBBB BBBB</tt><br />
| RW24<br />
| DMA source address / HDMA table start address.<br />
|-<br />
! [[DMA registers#DASnL|DASnL]]<br/>[[DMA registers#DASnH|DASnH]]<br/>[[DMA registers#DASBn|DASBn]]<br />
! $43n5<br/>$43n6<br/>$43n7<br />
| style="text-align: right" | <tt style="white-space: nowrap">LLLL LLLL<br/>HHHH HHHH<br/>BBBB BBBB</tt><br />
| RW24<br />
| DMA byte count (H:L) / HDMA indirect table address (B:H:L).<br />
|-<br />
! [[DMA registers#A2AnL|A2AnL]]<br/>[[DMA registers#A2AnH|A2AnH]]<br />
! $43n8<br/>$43n9<br />
| style="text-align: right" | <tt style="white-space: nowrap">LLLL LLLL<br/>HHHH HHHH</tt><br />
| RW16<br />
| HDMA table current address within bank (H:L).<br />
|-<br />
! [[DMA registers#NLTRn|NLTRn]]<br />
! $43nA<br />
| style="text-align: right" | <tt style="white-space: nowrap">RLLL LLLL</tt><br />
| RW8<br />
| HDMA reload flag (R) and scanline counter (L).<br />
|-<br />
! [[DMA registers#UNUSEDn|UNUSEDn]]<br />
! $43nB<br/>$43nF<br />
| style="text-align: right" | <tt style="white-space: nowrap">DDDD DDDD</tt><br />
| RW8<br />
| Unused shared data byte (D).<br />
|-<br />
<br />
<noinclude><br />
|}<br />
</noinclude></div>Fiskbithttps://snes.nesdev.org/w/index.php?title=DMA_registers&diff=296DMA registers2022-05-25T06:08:13Z<p>Fiskbit: Fixes register name: NTRL -> NLTR.</p>
<hr />
<div>The SNES's DMA (Direct Memory Access) unit allows a game to copy graphics, palettes, [[OAM]] and more at a much higher speed than the CPU can accomplish alone. This allows a game to make better use of the limited amount of time it has in vblank to change graphical memory.<br />
<br />
The SNES has two address buses - consisting of the A bus (which contains cartridge ROM, cartridge RAM, and the SNES's RAM) and the B bus (anything in the $2100-$21ff range, including [[PPU registers]] and APU registers). DMA always involves copying something from one of these two buses to something on the other bus.<br />
<br />
DMA cannot copy from one area of the SNES's RAM to another, even if RAM is specified as both the source and destination. For that, the <code>[[MVN]]</code> and <code>[[MVP]]</code> instructions are probably the best available choice.<br />
<br />
The SNES also features [[HDMA]] which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.<br />
<br />
These registers are always accessed at 3.58 MHz! That means that any channels that are not currently in use can have their registers repurposed for a small amount of fast RAM.<br />
<br />
{| class="wikitable"<br />
|+ DMA register summary (n = 0..7)<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
! [[DMA registers#MDMAEN|MDMAEN]]<br />
! $420B<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| DMA enable.<br />
|-<br />
! [[DMA registers#HDMAEN|HDMAEN]]<br />
! $420C<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| HDMA enable.<br />
|-<br />
{{:MMIO register table/DMA}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/DMA|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
== DMA channels ==<br />
<br />
The SNES contains 8 separate DMA "channels" - each one contains a set of parameters to configure a DMA transfers. They are configured with registers in the $4300-$437f range, where the first 16 addresses correspond to the first register, the second 16 addresses correspond to the next, and so on.<br />
<br />
<br />
{{Anchor|MDMAEN}}<br />
=== MDMAEN - Start DMA transfer ($420B write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 select<br />
|||| ||+-- Channel 1 select<br />
|||| |+--- Channel 2 select<br />
|||| +---- Channel 3 select<br />
|||+------ Channel 4 select<br />
||+------- Channel 5 select<br />
|+-------- Channel 6 select<br />
+--------- Channel 7 select<br />
<br />
On power-on: MDMAEN = $00<br />
On reset: MDMAEN = $00<br />
</pre><br />
<br />
Upon writing to this register, a DMA transfer is started for each bit that was set, starting with the lowest selected channel number up toward the highest. The CPU is stopped until all transfers have completed. If an HDMA transfer happens while the DMA transfer is going, the DMA transfer will be temporarily paused to allow the HDMA transfer to happen.<br />
<br />
{{Anchor|HDMAEN}}<br />
=== HDMAEN - Enable HDMA transfers ($420C write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 HDMA enable<br />
|||| ||+-- Channel 1 HDMA enable<br />
|||| |+--- Channel 2 HDMA enable<br />
|||| +---- Channel 3 HDMA enable<br />
|||+------ Channel 4 HDMA enable<br />
||+------- Channel 5 HDMA enable<br />
|+-------- Channel 6 HDMA enable<br />
+--------- Channel 7 HDMA enable<br />
<br />
On power-on: HDMAEN = $00<br />
On reset: HDMAEN = $00<br />
</pre><br />
<br />
This register enables HDMA for the selected channels.<br />
<br />
==DMA channel registers==<br />
{{Anchor|DMAPn}}<br />
=== DMAPn - DMA/HDMA parameters ($43n0 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
DIxA APPP<br />
|||| ||||<br />
|||| |+++- Transfer pattern (see below)<br />
|||+-+---- Address adjust mode (DMA only):<br />
||| 0: Increment A bus address after copy<br />
||| 1/3: Fixed<br />
||| 2: Decrement A bus address after copy<br />
||+------- (Unused)<br />
|+-------- Indirect (HDMA only)<br />
+--------- Direction: 0=Copy from A to B, 1=Copy from B to A<br />
<br />
On power-on: DMAPn = $FF<br />
<br />
The '''transfer pattern''' (P) controls the address pattern for different register types, and for HDMA also the number of bytes delivered per table entry.<br />
<br />
For example: pattern 1 allows a DMA copy to VRAM, which requires writing to two alternating addresses.<br />
<br />
{| class="wikitable"<br />
|+ DMA transfer patterns<br />
|-<br />
! Pattern !! HDMA bytes !! B Bus address !! Usage example<br />
|-<br />
! 0<br />
| 1 || <tt style="white-space: nowrap">+0</tt> || WRAM, Mode 7 graphics/tilemap<br />
|-<br />
! 1<br />
| 2 || <tt style="white-space: nowrap">+0 +1</tt> || VRAM<br />
|-<br />
! 2<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || OAM, CGRAM<br />
|-<br />
! 3<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || Scroll positions, Mode 7 parameters<br />
|-<br />
! 4<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +2 +3</tt> || Window <br />
|-<br />
! 5<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +0 +1</tt> || (Undocumented)<br />
|-<br />
! 6<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || (Same as 2, undocumented)<br />
|-<br />
! 7<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || (Same as 3, undocumented)<br />
|}<br />
<br />
A fixed '''address adjust mode''' (A) can be used to fill the DMA target with a single repeated byte of data.<br />
<br />
{{Anchor|BBADn}}<br />
<br />
=== BBADn - B-bus address ($43n1 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- Selects a hardware register to read or write from, in the $2100-$21ff range<br />
<br />
On power-on: BBADn = $FF<br />
<br />
This can be used with various [[PPU registers]], and also [[MMIO registers#WMDATA|WMDATA]].<br />
<br />
{{Anchor|UNUSEDn}}<br />
<br />
=== UNUSEDn - Unused byte ($43nB and $43nF read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
NNNN NNNN<br />
|||| ||||<br />
++++-++++- One unused byte available through two different addresses<br />
<br />
On power-on: UNUSEDn = $FF<br />
<br />
Seems to have no effect on DMA or HDMA, and this register cannot be used as a source for a DMA fill.<br />
<br />
==Configuration registers (DMA) ==<br />
<br />
{{Anchor|A1Tn|A1TnL|A1TnH|A1Bn}}<br />
=== A1TnL, A1TnH, A1Bn - DMA Current Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
----<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- Address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
The low 16-bits of this address change as the DMA happens, but the bank byte is fixed. DMA can not cross banks.<br />
<br />
[[#HDMA-A1Tn|HDMA uses these registers]] for its table address instead.<br />
<br />
{{Anchor|DASn|DASnL|DASnH}}<br />
=== DASnL, DASnH - DMA Byte-Counter ($43n5, $43n6 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASnH DASnL<br />
$43n6 $43n5<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- 16-bit number that indicates how many bytes to transfer<br />
<br />
On power-on: DASn = $FFFF<br />
<br />
A byte count of zero means 65536 bytes.<br />
<br />
This byte count is not affected by the DMA pattern.<br />
The SNES will stop before a pattern is completed if it runs out of bytes.<br />
<br />
Once the DMA finishes, these registers will be zero.<br />
<br />
[[#HDMA-DASn|HDMA uses these registers]] for its indirect address instead.<br />
<br />
== Configuration registers (HDMA) ==<br />
<br />
{{Anchor|HDMA-A1Tn}}<br />
=== A1TnL, A1TnH, A1Bn - HDMA Table Start Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit little-endian address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
These registers control where the channel's HDMA table is. During initiation of HDMA this address gets copied into [[#A2AnL|A2AnL]]/[[#A2AnH|A2AnH]] ($43n8/$43n9).<br />
<br />
{{Anchor|HDMA-DASn|DASBn}}<br />
<br />
=== DASBn - Indirect HDMA Bank ($43n7 read/write) (n = 0..7) ===<br />
<br />
7 bit 0<br />
---- ----<br />
BBBB BBBB<br />
|||| ||||<br />
++++-++++- High byte (bank) of HDMA indirect address<br />
<br />
On power-on: DASBn = $FF<br />
<br />
This must be set manually by the program to control the indirect HDMA bank. (DASnL/DASnH are automatically updated from the table.)<br />
<br />
== Other HDMA registers ==<br />
These keep track of each channel's state as HDMA is happening.<br />
<br />
{{Anchor|HDMA-DASn}}<br />
=== DASnL, DASnH, DASBn - Indirect HDMA Address ($43n5, $43n6, $43n7 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASBn DASnH DASnL<br />
$43n7 $43n6 $43n5<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---------<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- The current indirect DMA address.<br />
<br />
On power-on: DASn = $FFFFFF<br />
<br />
With indirect HDMA, if the repeat bit is set in the table entry, then the SNES will continue to read increasing addresses starting from the one given in the table, using these registers to keep track of where it currently is.<br />
<br />
The low 16 bits are automatically copied from the table, but the bank byte [[#DASBn|DASBn]] must be manually set by the program. The bank byte is fixed, and HDMA will not cross banks.<br />
<br />
{{Anchor|A2An|A2AnL|A2AnH}}<br />
<br />
=== A2AnL, A2AnH - HDMA Table Current Address ($43n8, $43n9 read/write) (n = 0..7) ===<br />
----<br />
<br />
A2AnH A2AnL<br />
$43n9 $43n8<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Low 16 bits of the current address within the HDMA table<br />
<br />
On power-on: A2An = $FFFF<br />
<br />
Bank byte is taken from $43n4, as it does not change.<br />
<br />
{{Anchor|NLTRn}}<br />
=== NLTRn - HDMA Line-Counter ($43nA read/write) (n = 0..7) ===<br />
----<br />
7 bit 0<br />
---- ----<br />
RLLL LLLL<br />
|||| ||||<br />
|+++-++++- Number of scanlines left<br />
+--------- Repeat flag<br />
<br />
On power-on: NLTR = $FF<br />
<br />
Automatically loaded from the table. Scanline count is decremented every scanline until it hits zero.<br />
<br />
== HDMA table format ==<br />
<br />
HDMA tables specify what values to write to the selected B bus register, as well as which scanlines to write the values on. The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).<br />
<br />
=== Direct HDMA table entries ===<br />
----<br />
<br />
1 byte - Line count, and repeat mode<br />
N bytes - Data<br />
<br />
The number of bytes in each Data section is determined by the pattern chosen for the channel in register $43n0.<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written, then the SNES waits for the specified number of scanlines before continuing onto the next table entry.<br />
* If repeat mode is on, then the total size of the data section is the number of scanlines multiplied by the number of bytes in the pattern. One pattern's worth of bytes are written for however many scanlines indicated in the table.<br />
<br />
=== Indirect HDMA table entries ===<br />
----<br />
1 byte - Line count, and repeat mode<br />
2 bytes - Pointer to access the data through<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written from an address starting from the pointer given.<br />
* If repeat mode is on, the SNES continues to progress through the bytes that the pointer points to, for however many scanlines are indicated in the table.<br />
<br />
=== Line count, repeat mode byte ===<br />
----<br />
Possible values for the "Line count, and repeat mode" byte are as follows:<br />
* $00: Stop processing HDMA on that channel for the rest of the frame.<br />
* $01-$80: Write once, then wait for X-1 scanlines<br />
* $81-$FF: Write every scanline for X-$80 scanlines, repeat mode</div>Fiskbithttps://snes.nesdev.org/w/index.php?title=DMA_registers&diff=295DMA registers2022-05-25T06:07:07Z<p>Fiskbit: Adds power-on and reset info for each register.</p>
<hr />
<div>The SNES's DMA (Direct Memory Access) unit allows a game to copy graphics, palettes, [[OAM]] and more at a much higher speed than the CPU can accomplish alone. This allows a game to make better use of the limited amount of time it has in vblank to change graphical memory.<br />
<br />
The SNES has two address buses - consisting of the A bus (which contains cartridge ROM, cartridge RAM, and the SNES's RAM) and the B bus (anything in the $2100-$21ff range, including [[PPU registers]] and APU registers). DMA always involves copying something from one of these two buses to something on the other bus.<br />
<br />
DMA cannot copy from one area of the SNES's RAM to another, even if RAM is specified as both the source and destination. For that, the <code>[[MVN]]</code> and <code>[[MVP]]</code> instructions are probably the best available choice.<br />
<br />
The SNES also features [[HDMA]] which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.<br />
<br />
These registers are always accessed at 3.58 MHz! That means that any channels that are not currently in use can have their registers repurposed for a small amount of fast RAM.<br />
<br />
{| class="wikitable"<br />
|+ DMA register summary (n = 0..7)<br />
|-<br />
! Name<br />
! Address<br />
! Bits<br />
! Type<br />
! Notes<br />
|-<br />
! [[DMA registers#MDMAEN|MDMAEN]]<br />
! $420B<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| DMA enable.<br />
|-<br />
! [[DMA registers#HDMAEN|HDMAEN]]<br />
! $420C<br />
| style="text-align: right" | <tt style="white-space: nowrap">7654 3210</tt><br />
| W8<br />
| HDMA enable.<br />
|-<br />
{{:MMIO register table/DMA}}<br />
|-<br />
! colspan=5 | <small>[[MMIO register table/DMA|table source]]</small><br />
|}<br />
<br />
Register types:<br />
* '''R''' - Readable<br />
* '''W''' - Writeable<br />
* '''8''' - 8-bit access only<br />
* '''16''' - 8-bit access to either address, or 16-bit access to the lower address.<br />
* '''24''' - 8-bit or 16-bit access to 3 registers.<br />
<br />
== DMA channels ==<br />
<br />
The SNES contains 8 separate DMA "channels" - each one contains a set of parameters to configure a DMA transfers. They are configured with registers in the $4300-$437f range, where the first 16 addresses correspond to the first register, the second 16 addresses correspond to the next, and so on.<br />
<br />
<br />
{{Anchor|MDMAEN}}<br />
=== MDMAEN - Start DMA transfer ($420B write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 select<br />
|||| ||+-- Channel 1 select<br />
|||| |+--- Channel 2 select<br />
|||| +---- Channel 3 select<br />
|||+------ Channel 4 select<br />
||+------- Channel 5 select<br />
|+-------- Channel 6 select<br />
+--------- Channel 7 select<br />
<br />
On power-on: MDMAEN = $00<br />
On reset: MDMAEN = $00<br />
</pre><br />
<br />
Upon writing to this register, a DMA transfer is started for each bit that was set, starting with the lowest selected channel number up toward the highest. The CPU is stopped until all transfers have completed. If an HDMA transfer happens while the DMA transfer is going, the DMA transfer will be temporarily paused to allow the HDMA transfer to happen.<br />
<br />
{{Anchor|HDMAEN}}<br />
=== HDMAEN - Enable HDMA transfers ($420C write) ===<br />
----<br />
<pre><br />
7 bit 0<br />
---- ----<br />
7654 3210<br />
|||| ||||<br />
|||| |||+- Channel 0 HDMA enable<br />
|||| ||+-- Channel 1 HDMA enable<br />
|||| |+--- Channel 2 HDMA enable<br />
|||| +---- Channel 3 HDMA enable<br />
|||+------ Channel 4 HDMA enable<br />
||+------- Channel 5 HDMA enable<br />
|+-------- Channel 6 HDMA enable<br />
+--------- Channel 7 HDMA enable<br />
<br />
On power-on: HDMAEN = $00<br />
On reset: HDMAEN = $00<br />
</pre><br />
<br />
This register enables HDMA for the selected channels.<br />
<br />
==DMA channel registers==<br />
{{Anchor|DMAPn}}<br />
=== DMAPn - DMA/HDMA parameters ($43n0 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
DIxA APPP<br />
|||| ||||<br />
|||| |+++- Transfer pattern (see below)<br />
|||+-+---- Address adjust mode (DMA only):<br />
||| 0: Increment A bus address after copy<br />
||| 1/3: Fixed<br />
||| 2: Decrement A bus address after copy<br />
||+------- (Unused)<br />
|+-------- Indirect (HDMA only)<br />
+--------- Direction: 0=Copy from A to B, 1=Copy from B to A<br />
<br />
On power-on: DMAPn = $FF<br />
<br />
The '''transfer pattern''' (P) controls the address pattern for different register types, and for HDMA also the number of bytes delivered per table entry.<br />
<br />
For example: pattern 1 allows a DMA copy to VRAM, which requires writing to two alternating addresses.<br />
<br />
{| class="wikitable"<br />
|+ DMA transfer patterns<br />
|-<br />
! Pattern !! HDMA bytes !! B Bus address !! Usage example<br />
|-<br />
! 0<br />
| 1 || <tt style="white-space: nowrap">+0</tt> || WRAM, Mode 7 graphics/tilemap<br />
|-<br />
! 1<br />
| 2 || <tt style="white-space: nowrap">+0 +1</tt> || VRAM<br />
|-<br />
! 2<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || OAM, CGRAM<br />
|-<br />
! 3<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || Scroll positions, Mode 7 parameters<br />
|-<br />
! 4<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +2 +3</tt> || Window <br />
|-<br />
! 5<br />
| 4 || <tt style="white-space: nowrap">+0 +1 +0 +1</tt> || (Undocumented)<br />
|-<br />
! 6<br />
| 2 || <tt style="white-space: nowrap">+0 +0</tt> || (Same as 2, undocumented)<br />
|-<br />
! 7<br />
| 4 || <tt style="white-space: nowrap">+0 +0 +1 +1</tt> || (Same as 3, undocumented)<br />
|}<br />
<br />
A fixed '''address adjust mode''' (A) can be used to fill the DMA target with a single repeated byte of data.<br />
<br />
{{Anchor|BBADn}}<br />
<br />
=== BBADn - B-bus address ($43n1 read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
AAAA AAAA<br />
|||| ||||<br />
++++-++++- Selects a hardware register to read or write from, in the $2100-$21ff range<br />
<br />
On power-on: BBADn = $FF<br />
<br />
This can be used with various [[PPU registers]], and also [[MMIO registers#WMDATA|WMDATA]].<br />
<br />
{{Anchor|UNUSEDn}}<br />
<br />
=== UNUSEDn - Unused byte ($43nB and $43nF read/write) (n = 0..7) ===<br />
----<br />
<br />
7 bit 0<br />
---- ----<br />
NNNN NNNN<br />
|||| ||||<br />
++++-++++- One unused byte available through two different addresses<br />
<br />
On power-on: UNUSEDn = $FF<br />
<br />
Seems to have no effect on DMA or HDMA, and this register cannot be used as a source for a DMA fill.<br />
<br />
==Configuration registers (DMA) ==<br />
<br />
{{Anchor|A1Tn|A1TnL|A1TnH|A1Bn}}<br />
=== A1TnL, A1TnH, A1Bn - DMA Current Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
----<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- Address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
The low 16-bits of this address change as the DMA happens, but the bank byte is fixed. DMA can not cross banks.<br />
<br />
[[#HDMA-A1Tn|HDMA uses these registers]] for its table address instead.<br />
<br />
{{Anchor|DASn|DASnL|DASnH}}<br />
=== DASnL, DASnH - DMA Byte-Counter ($43n5, $43n6 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASnH DASnL<br />
$43n6 $43n5<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- 16-bit number that indicates how many bytes to transfer<br />
<br />
On power-on: DASn = $FFFF<br />
<br />
A byte count of zero means 65536 bytes.<br />
<br />
This byte count is not affected by the DMA pattern.<br />
The SNES will stop before a pattern is completed if it runs out of bytes.<br />
<br />
Once the DMA finishes, these registers will be zero.<br />
<br />
[[#HDMA-DASn|HDMA uses these registers]] for its indirect address instead.<br />
<br />
== Configuration registers (HDMA) ==<br />
<br />
{{Anchor|HDMA-A1Tn}}<br />
=== A1TnL, A1TnH, A1Bn - HDMA Table Start Address ($43n2, $43n3, $43n4 read/write) (n = 0..7) ===<br />
<br />
A1Bn A1TnH A1TnL<br />
$43n4 $43n3 $43n2<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---- ----<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- 24-bit little-endian address on the A bus<br />
<br />
On power-on: A1Tn = $FFFFFF<br />
<br />
These registers control where the channel's HDMA table is. During initiation of HDMA this address gets copied into [[#A2AnL|A2AnL]]/[[#A2AnH|A2AnH]] ($43n8/$43n9).<br />
<br />
{{Anchor|HDMA-DASn|DASBn}}<br />
<br />
=== DASBn - Indirect HDMA Bank ($43n7 read/write) (n = 0..7) ===<br />
<br />
7 bit 0<br />
---- ----<br />
BBBB BBBB<br />
|||| ||||<br />
++++-++++- High byte (bank) of HDMA indirect address<br />
<br />
On power-on: DASBn = $FF<br />
<br />
This must be set manually by the program to control the indirect HDMA bank. (DASnL/DASnH are automatically updated from the table.)<br />
<br />
== Other HDMA registers ==<br />
These keep track of each channel's state as HDMA is happening.<br />
<br />
{{Anchor|HDMA-DASn}}<br />
=== DASnL, DASnH, DASBn - Indirect HDMA Address ($43n5, $43n6, $43n7 read/write) (n = 0..7) ===<br />
----<br />
<br />
DASBn DASnH DASnL<br />
$43n7 $43n6 $43n5<br />
7 bit 0 7 bit 0 7 bit 0<br />
---- ---- ---- ---- ---------<br />
BBBB BBBB HHHH HHHH LLLL LLLL<br />
|||| |||| |||| |||| |||| ||||<br />
++++-++++---++++-++++---++++-++++- The current indirect DMA address.<br />
<br />
On power-on: DASn = $FFFFFF<br />
<br />
With indirect HDMA, if the repeat bit is set in the table entry, then the SNES will continue to read increasing addresses starting from the one given in the table, using these registers to keep track of where it currently is.<br />
<br />
The low 16 bits are automatically copied from the table, but the bank byte [[#DASBn|DASBn]] must be manually set by the program. The bank byte is fixed, and HDMA will not cross banks.<br />
<br />
{{Anchor|A2An|A2AnL|A2AnH}}<br />
<br />
=== A2AnL, A2AnH - HDMA Table Current Address ($43n8, $43n9 read/write) (n = 0..7) ===<br />
----<br />
<br />
A2AnH A2AnL<br />
$43n9 $43n8<br />
7 bit 0 7 bit 0<br />
---- ---- ---- ----<br />
HHHH HHHH LLLL LLLL<br />
|||| |||| |||| ||||<br />
++++-++++---++++-++++- Low 16 bits of the current address within the HDMA table<br />
<br />
On power-on: A2An = $FFFF<br />
<br />
Bank byte is taken from $43n4, as it does not change.<br />
<br />
{{Anchor|NTRLn}}<br />
=== NTRLn - HDMA Line-Counter ($43nA read/write) (n = 0..7) ===<br />
----<br />
7 bit 0<br />
---- ----<br />
RLLL LLLL<br />
|||| ||||<br />
|+++-++++- Number of scanlines left<br />
+--------- Repeat flag<br />
<br />
On power-on: NLTR = $FF<br />
<br />
Automatically loaded from the table. Scanline count is decremented every scanline until it hits zero.<br />
<br />
== HDMA table format ==<br />
<br />
HDMA tables specify what values to write to the selected B bus register, as well as which scanlines to write the values on. The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).<br />
<br />
=== Direct HDMA table entries ===<br />
----<br />
<br />
1 byte - Line count, and repeat mode<br />
N bytes - Data<br />
<br />
The number of bytes in each Data section is determined by the pattern chosen for the channel in register $43n0.<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written, then the SNES waits for the specified number of scanlines before continuing onto the next table entry.<br />
* If repeat mode is on, then the total size of the data section is the number of scanlines multiplied by the number of bytes in the pattern. One pattern's worth of bytes are written for however many scanlines indicated in the table.<br />
<br />
=== Indirect HDMA table entries ===<br />
----<br />
1 byte - Line count, and repeat mode<br />
2 bytes - Pointer to access the data through<br />
<br />
* If repeat mode is off, one pattern's worth of bytes are written from an address starting from the pointer given.<br />
* If repeat mode is on, the SNES continues to progress through the bytes that the pointer points to, for however many scanlines are indicated in the table.<br />
<br />
=== Line count, repeat mode byte ===<br />
----<br />
Possible values for the "Line count, and repeat mode" byte are as follows:<br />
* $00: Stop processing HDMA on that channel for the rest of the frame.<br />
* $01-$80: Write once, then wait for X-1 scanlines<br />
* $81-$FF: Write every scanline for X-$80 scanlines, repeat mode</div>Fiskbit