SNES Development
Finding free VRAM spots for 32x32 and 16x16 sprites

This code will allow you to automatically find an empty space in VRAM so you can fit a 16x16 or 32x32 sprite pattern as needed.

If you need to find an open 32x32 VRAM sprite slot, jump to the “find_32x32_slot” routine, and it will return the CHR number in the accumulator, for use in the OAM. Same with finding an open 16x16 slot. To find the VRAM address to DMA the sprite, multiply the CHR number by 16 by doing ASL 4 times. This routine uses both index registers, so you may want to save them before the routine if needed. It also leaves the index registers in 8-bit mode.

I am going to include the code for clearing a VRAM sprite slot from the table eventually, because animated sprites might either get killed or move offscreen, so it needs to remove the sprite from the VRAM to make room for other sprites.

find_32x32_slot:
sep #$30
ldx #$00
lda {vram_slot_table},x      //check first 32x32 slot
beq +                              //if zero, found open slot
-;
inx
lda {vram_slot_table},x
bne -
+;

cpx #$20                      //slot values of #32 are invalid
bne +
rep #$20
lda #$0200                      //end routine with CHR number = $0200, if slot is invalid
rts

+;
lda #$0f
sta {vram_slot_table},x     //when slot is valid, mark it with #$0f.  Each of the 4 bits corresponds to a specific 16x16 cell inside the 32x32 cell
lda large_slot_vram_location,x       //find the CHR number
rep #$20
and #$00ff
asl
rts                                            //routine ends with CHR number of slot found, in accumulator

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

find_16x16_slot:
rep #$20                                  //uses 16-bit accumulator to read two slot entries at once
sep #$10
ldx #$00
lda #$0f0f                                //everything is compared to #$0f0f, in order to find a pair of 32x32 slots that aren't full
cmp {vram_slot_table},x
bne +
-;
inx #2
cmp {vram_slot_table},x
beq -
+;
lda {vram_slot_table},x             //go back into 8-bit mode to determine which 32x32 slot is the one that's not full
sep #$20
cmp #$0f
bne +
inx
xba                                        //if this 32x32 slot is full, the other one must not be.  Switch sides of the accumulator.
+;

cpx #$20                                //again, slot #32 is invalid
bne +
rep #$20
lda #$0200
rts

+;
tay                                          //find the first blank 16x16 slot, of the 32x32 slot
ora vram_slot_table_bit_set,y
sta {vram_slot_table},x              //mark bit as used
lda large_slot_vram_location,x     //get the CHR number of the 32x32 slot, the 16x16 slot is a part of
ora small_slot_vram_location,y    //get the CHR number of the 16x16 slot, within the 32x32 slot
rep #$20
and #$00ff
asl
rts                                            //routine ends with CHR number of slot found, in accumulator

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

large_slot_vram_location:
db $00,$02,$04,$06            // n = 32x32 slot number
db $20,$22,$24,$26            // CHR number = LUT(n)*2
db $40,$42,$44,$46            // vram address = LUT(n)*32
db $60,$62,$64,$66
db $80,$82,$84,$86
db $a0,$a2,$a4,$a6
db $c0,$c2,$c4,$c6
db $e0,$e2,$e4,$e6

//find first empty bit per byte.  Bytes represent 32x32 slots, with the low 4 bits representing the 16x16 slots that make up the 32x32 slot.
//0000 => 000x => 0001
//0001 => 00x1 => 0010
//0010 => 001x => 0001
//0011 => 0x11 => 0100
//0100 => 010x => 0001
//etc

vram_slot_table_bit_set:
db $01,$02,$01,$04,$01,$02,$01,$08,$01,$02,$01,$04,$01,$02,$01


//corresponds to table above
//0000 => 000x => 0001, top left of 32x32 slot
//0001 => 00x1 => 0010, top right
//0011 => 0x11 => 0100, bottom left
//0111 => x111 => 1000, bottom right

small_slot_vram_location:     
db $00,$01,$00,$10,$00,$01,$00,$11,$00,$01,$00,$10,$00,$01,$00