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