Just like the NES, the SNES has a special area of memory called the OAM, that stores the attributes of sprites. For the NES using sprites is pretty straight forward:

  1. During the main game routine, write sprites to an OAM buffer.

  2. At the end of the main game routine, move the unused sprites in the OAM buffer offscreen.

  3. During vblank period, DMA the OAM buffer, into OAM.

Each sprite entry for the NES is exactly 4 bytes in size, so writing a sprite to the OAM buffer is just a matter of writing to a few registers and incrementing an offset by 4 bytes.

The SNES is a little more difficult in this area. The SNES's OAM is 544 bytes total. The first 512 bytes are arranged similar to the NES, but there is an addition 32 bytes, where sprites get 2 additional bits, with 4 of them per byte. Getting the first 512 bytes is a piece of cake, but the extra 32 bytes is a bit of a mess to work with.

More information can be found here

To make the OAM easier to work with, we can "extend" the OAM buffer by an extra 512 bytes, where we can place the full 9-bit x-coordinates and size selection bits, like so:


byte 0: xxxxxxxx
byte 1: -------x
byte 2: -------s
byte 3: --------

Now we need a routine that builds the 32-bytes at the end of the OAM, using the information from the 512-byte table located after the OAM buffer. This routine also moves unused sprites offscreen, by setting Y = 240. It has to be called once at the end of every frame in order for your sprites to work properly.


process_oam:
php
rep #$10
sep #$20

ldx !sprite_number
lda #$f0
clear_oam_loop:
cpx #$0200
beq finished_clearing_oam_loop
sta !oam+1,x
inx #4
bra clear_oam_loop
finished_clearing_oam_loop:

ldy #$0000
sty !sprite_number

build_hioam_loop:
lda !oam+544+14,y		;; sprite 3, size bit
asl
ora !oam+544+13,y		;; sprite 3, 9th x bit
asl
ora !oam+544+10,y		;; sprite 2, size bit
asl
ora !oam+544+9,y		;; sprite 2, 9th x bit
asl
ora !oam+544+6,y		;; sprite 1, size bit
asl
ora !oam+544+5,y		;; sprite 1, 9th x bit
asl
ora !oam+544+2,y		;; sprite 0, size bit
asl
ora !oam+544+1,y		;; sprite 0, 9th x bit
sta !oam,x
rep #$20
tya
clc
adc #$0010
tay
sep #$20
inx
cpx #$0220
bne build_hioam_loop

plp
rts

Now that you have inserted the above routine to run once at the end of every frame, we need a routine we can call whenever we need to display a sprite onscreen.


display_sprite:
ldx !sprite_number
cpx #$0200
beq dont_display_sprite		;;sprite number can't exceed 128
lda !x
cmp #$0100
bcc inbounds_x
cmp #$fff0
bcc dont_display_sprite
inbounds_x:			;;display sprite if x is between -16 and 256
sta !oam,x
and #$01ff
sta !oam+544,x
lda !y
cmp #$00f0
bcc inbounds_y
cmp #$fff0
bcc dont_display_sprite
inbounds_y:			;;display sprite if y is between -16 and 240
sta !oam+1,x
lda !attributes
sta !oam+2,x
lda !size
sta !oam+544+2,x
inx #4
stx !sprite_number
dont_display_sprite:
rts

Now lets test the above routine, by writing a 16x16 sprite to the middle of the screen, at coordinates (120,104) using the registers labeled "x", "y", "attributes", and "size". The register labeled "attributes" is the equivalent of bytes 2 and 3 of normal OAM. Were just going to leave it as #0 for now.


rep #$30
lda #120
sta !x
lda #104
sta !y
lda #0
sta !attributes
lda #1
sta !size
jsr display_sprite