Here is the almost unreadable code for it:
no_animation_slot:Basically what it does, is compares the previous metasprite with the current one, or checks if the animation update flag is set, to see if there is a change in animation frame. When there is, it looks at the size of the metasprite to keep track of DMA usage. If it spills over 4kB in one frame, it doesn't update it until the next frame. If there is enough DMA time, it clears the previous animation frame from the tables (see below).
jsr clear_vram_slot
stz {frame_id}
rts
animation:
lda {animation_update}
bne +
lda {metasprite_request}
cmp {metasprite}
bne +
rts
+;
lda #$0000
ldy {metasprite_request}
beq no_animation_slot
lda $000c,y
sta {vram_size}
clc
adc {total_dma_legnth}
cmp #$0080
beq yes_metasprite_pattern
bcc yes_metasprite_pattern
lda {first_object_to_dma}
bne +
tdc
sta {first_object_to_dma}
+;
no_metasprite_pattern:
rts
yes_metasprite_pattern:
jsr clear_vram_slot
ldy {metasprite_request}
lda $000a,y
asl #5
sta {vram_width}
dynamic_animation:
lda {rotation_disable}
bne +
stz {180_degrees_flip}
lda {angle}
cmp #$8000
bcc +
lda #$c000
sta {180_degrees_flip} //this is so rotation of 180 degrees doesn't happen until
+; //the frame gets updated
stz {animation_update}
lda {metasprite_request}
sta {metasprite}
tay
lda $000e,y
clc
adc {animation_index}
inc #2
sta {frame_id}
tax
lda {animation_copies},x
beq +
inc
sta {animation_copies},x
rts
+;
inc
sta {animation_copies},x
lda $0006,y
clc
adc {graphics_address}
sta {temp3}
lda $0008,y
sta {temp2}
lda $0010,y
bpl +
tya
clc
adc {animation_index}
tay
lda $0010,y
tay
+;
-;
lda $0010,y
bne +
lda #$ffff
sta {animation_chr},x
rts
+;
lda $0018,y
sta {temp}
jsr find_vram_slot
sta {animation_chr},x
cmp #$ffff
bne +
tya
clc
adc #$000a
tay
bra -
+;
tax
-;
lda $0010,y
sta {sprite_size},x
lda $0012,y
sta {sprite_x},x
lda $0014,y
sta {sprite_y},x
lda $0016,y
sta {sprite_attributes},x
tya
clc
adc #$000a
tay
lda $0018,y
sta {temp}
lda $0010,y
beq +
jsr find_vram_slot
sta {sprite_name},x
cmp #$ffff
beq -
tax
bra -
+;
lda #$ffff
sta {sprite_name},x
lda {vram_size}
clc
adc {total_dma_legnth}
sta {total_dma_legnth}
rts
find_vram_slot:
phx
lda $0010,y
cmp #$0002
beq large_slot
jmp small_slot
large_slot:
ldx {large_slot_stack_index}
dex
dex
bpl +
lda #$ffff
plx
rts
+;
lda {large_slot_stack},x
stx {large_slot_stack_index}
tax
phy
ldy {dma_updates}
lda {temp}
clc
adc {temp3}
sta {dma_address},y
adc {vram_width}
sta {dma_address}+2,y
adc {vram_width}
sta {dma_address}+4,y
adc {vram_width}
sta {dma_address}+6,y
sep #$20
lda {temp2}
sta {dma_bank},y
sta {dma_bank}+2,y
sta {dma_bank}+4,y
sta {dma_bank}+6,y
lda #$80
sta {dma_legnth},y
sta {dma_legnth}+2,y
sta {dma_legnth}+4,y
sta {dma_legnth}+6,y
rep #$20
txa
and #$01ff
asl #4
sta {dma_destination},y
adc #$0100
sta {dma_destination}+2,y
adc #$0100
sta {dma_destination}+4,y
adc #$0100
sta {dma_destination}+6,y
tya
adc #$0008
tay
lda #$0000
sta {dma_legnth},y
sty {dma_updates}
ply
txa
plx
rts
small_slot:
ldx {small_slot_stack_index}
dex
dex
bpl +
lda #$ffff
plx
rts
+;
lda {small_slot_stack},x
stx {small_slot_stack_index}
tax
phy
ldy {dma_updates}
lda {temp}
clc
adc {temp3}
sta {dma_address},y
adc {vram_width}
sta {dma_address}+2,y
sep #$20
lda {temp2}
sta {dma_bank},y
sta {dma_bank}+2,y
lda #$40
sta {dma_legnth},y
sta {dma_legnth}+2,y
rep #$20
txa
and #$01ff
asl #4
sta {dma_destination},y
adc #$0100
sta {dma_destination}+2,y
iny #4
lda #$0000
sta {dma_legnth},y
sty {dma_updates}
ply
txa
plx
rts
no_slot_to_clear:
rts
clear_vram_slot:
ldy {metasprite}
beq no_slot_to_clear
ldx {frame_id}
beq no_slot_to_clear
lda {animation_copies},x
beq +
dec
sta {animation_copies},x
bne no_slot_to_clear
+;
lda {animation_chr},x
tax
-;
cpx #$ffff
beq no_slot_to_clear
lda {sprite_size},x
beq no_slot_to_clear
tay
phx
txa
cpy #$0002
beq +
ldx {small_slot_stack_index}
sta {small_slot_stack},x
inx
inx
stx {small_slot_stack_index}
bra ++
+;
ldx {large_slot_stack_index}
sta {large_slot_stack},x
inx
inx
stx {large_slot_stack_index}
+;
plx
lda #$0000
sta {sprite_size},x
lda {sprite_name},x
tax
jmp -
Then it looks at the metasprite's frame number, uses that as an index to a super long list holding both the CHR bits for the first sprite, and the amount of copies that frame has. If there were no copies of that frame already being used, it creates a linked list table for it. First it finds an unused CHR name for the first sprite (0-127 are for 16x16 sprites, 128-511 are for 32x32 sprites), then it uses that number as the entry point for the vram linked sprite table, where every CHR number has a cooresponding X-displacement, Y-displacement, attributes word, and the CHR number for the next sprite in the frame/metasprite.
Clearing the animation frame is basically getting all the sprites off the linked list, and putting them back on the list of empty vram slots. Displaying an animation frame, involves going through the linked list, adding the X and Y coordinates, and putting it on the OAM.
If you're reading this, I've probably lost you, and that is the problem with programming. I share my codes with people so they don't have to reinvent the wheel, but when I do, it confuses the shit out of them.