SNESには128個の独立したスプライトがある。 OAM (Object Attribute Memory) にデータを書き込むことで 表示する画像や座標の設定をする。
OAM全体は544バイトで構成され、 512バイトの下位テーブルと、32バイトの上位テーブルに分けられる。 双方共に128項目を入れることができる。 OAM は 0x2102 レジスタでアクセスするアドレスを決め、 0x2103 のビット 0 でテーブル選択を行い、 0x2104 を通じて書き込むか、もしくは 0x2138 から読み込まれる。 上位テーブルは32バイト分しかなく、0x2102 の下位 4 ビットのみが テーブルのインデックスとして使われる。
内部のOAMアドレスは、スキャンラインの描画中に無効になる。 無効化は決定的に起こるが、どのようにして決定されるのかは分からない。 OAMアドレスに対して何か操作がある瞬間か、 スプライトが現在のスキャンラインに現れる瞬間が 無効化の決定に関係しているのだろうと考えられている。 内部OAMアドレスはブランク強制期間中でない場合、 V-Blank 開始時に 0x2102/0x2103 からリロードされる。 0x2100 のビット 7 が 1 から 0 に変更された時にも リロード処理は発生する。
1バイトの読み込み/書き込み時にアドレスはインクリメントされる。 (内部アドレスは10ビット分あり、ビット9がテーブル選択、 ビット0~8がテーブルのインデックスを表す。) 読み込み動作は、そのまま今のバイトを読み込む。 下位テーブルに対する書き込みは、ワードサイズのバッファに行き、 上位バイトも追加で書き込まれた時に、OAMの適切なワードに書き込まれる。 読み込み、書き込みが交互に起こった場合、ワード値の 上位バイトが書き込みの代わりに読み込まれることになり、 書き込み内容はOAMに反映されなくなる。 このように交互に動作させると、書き込みは上位バイトにのみ行われ、 その上、下位バイトにはゴミデータが書き込まれてしまう。
順を追って説明すると、
この動作の時、 OAM : 01 00 00 02 00 03 のようなデータが書き込まれると予想することができるが、実際には
OAM : 00 00 01 02 01 03 のように書き込まれる。
上位テーブルへの書き込みの方は期待通りに動作する。
下位テーブルの1項分目のフォーマットは次のような4バイトで構成される。
OBJ*4+0: xxxxxxxx OBJ*4+1: yyyyyyyy OBJ*4+2: cccccccc OBJ*4+3: vhoopppN
上位テーブルの1項目分のフォーマットは次のような2ビットで構成される。
bit 0/2/4/6 of byte OBJ/4: X bit 1/3/5/7 of byte OBJ/4: s
記号の詳細
スプライトのサイズは 0x2101 のビット 5 ~ 7 と OAM の サイズフラグで調節する。 0x2101 では全てのスプライトのサイズを調節する。 OAM サイズフラグが 0 の時は小さい方のサイズが、 1 の時は大きいほうのサイズが選択される。
スプライト用に、16色のパレットが8セット用意されている。 それらは CGRAM のインデックス 128 から始まる。 OAM で指定される 'ppp' は、 (128 + ppp * 16) ~ (128 + ppp * 16 + 15) の範囲を指す。 長方形でないスプライトの場合、パレットの最初のエントリは透明色を表す。
スプライトのパレット 4 ~ 7 は色計算と共有される。
スプライト用に 2 つの 16x16 サイズのキャラクタテーブルが VRAM に用意されている。 これらは BG タイルマップと同じように動作する。 タイル 0x00 は、タイル 0x0F の右側のタイルで、タイル 0xF0 の下側にある。 タイル 0x10 は、タイル 0x00 の下側のタイルで、タイル 0x1F の右側、 タイル 0xFF は、タイル 0xF0 の左側のタイルで、タイル 0x0F の上側にある。 どのキャラクタテーブルを使用するかは OAM の N ビットで設定される。 16x16 のサイズを使って、タイル 0xFF を指定した場合、キャラクタは 0xFF, 0xF0, 0x0F, 0x00 で構成されることになる。 最初に、テーブルは 0x2101 のネームベースビットで指定され、 次に 0x2101 のネームビットでオフセットが指定される。 VRAM 上のスプライトの最初のタイルのワードアドレスは次の式で表される。
((Base<<13) + (cccccccc<<4) + (N ? ((Name+1)<<12) : 0)) & 0x7fff
キャラクタデータの詳細は BG 参照。
スプライトには優先順位についての考え方が2種類ある。 1つは OAM の優先順位ビットで、BG に対してスプライトの 表示順位を調整する。詳しくは BG 参照。
もう1つは、スプライト同士の間の表示順位で、 これはスプライトのインデックスと優先順位ローテーションによって調整される。
優先順位ローテーションは 0x2103 のビット 7 でセットする。 このビットが 0 の時、スプライト 0 が最初のスプライトになる。 1 の場合、現在の内部 OAM ワードアドレスから (OAM アドレス無効化処理は影響しない) 、 スプライトに優先順位が設定される (OAMAddr&0xFE)>>1 。 0x2102/0x2103 に 0x104 が 4 バイトで書かれた時、 次のフレームでスプライト 3 が優先順位を得る。 しかし、OAM アドレスリセット によって、内部OAMアドレスは 0x104 にリセットされるので、さらに次のフレームでは スプライト 2 が優先順位を得る。
有名な1つの奇妙な動作 : 0x2102/0x2103 を 0xA に設定した時、 4n+2*(A&1)+1 バイトが書き込まれる。 (例えば、so the next byte written would go to the last byte in the 4-byte sprite record) sprite ((OAMAddr>>1)+Y)&0x7F has priority (where Y is the current line as addressed by sprites). Thus, if you put all 128 8x8 sprites at Y=63, write $8000 to $2102/3, then read 3 bytes from $2138, you will see sprites 63-70 having priority on successive scanlines.
"最初のスプライト"は、OAM の優先順位ビットの設定に関わらず 他のスプライトよりも前に(一番前に)表示される。 "最初のスプライト" + 1のスプライトは "最初のスプライト" + 2の前に、 "最初のスプライト" + 2のスプライトは "最初のスプライト" + 3の前に表示され、 これは "最初のスプライト" + 127 まで続く。 (127から0にラッピングされる。) 一番前のスプライトのみがBGに対する優先順位を設定できることに注意して欲しい。 "最初のスプライト" + 3 と "最初のスプライト" + 4 は交換可能だが、 "最初のスプライト" + 3 が優先順位 0 、"最初のスプライト" + 4 が優先順位 3 に設定されている時、両方のスプライトがBGの裏に隠されてしまう。 "最初のスプライト" + 4 が BG の前面に来るのが普通の考え方で、 直感に反することのように見えるが、たくさんのゲームがこの挙動に従って動いている。
スプライトはスキャンライン毎に描画される。
詳細は PPU/画面のレンダリング を参照