If you can tolerate my shitty newbie code, here's the animation scheme I used for a project I got started on.
I drew a bunch of my animations separately, first being kind of greedy with space used up. Then I realized I can shift over some parts of animations to better pack into tiles. Finally, I made note of tile re-use, and removed even more tiles. I ended up with this CHR layout, which clearly isn't complete, but has a lot of the animation in almost half the space it originally had:

For the animation system, this code snippet describes individual animation frame layouts, the "metasprites". Each line more or less maps to a single sprite's attributes, but the positions are relative to a position specified during drawing. This is for the player's first standing frame:
Code:
pl_mapping_stand1:
.byte <-32, $00, %00000000, <-8
.byte <-32, $01, %00000000, 0
.byte <-24, $10, %00000000, <-8
.byte <-24, $11, %00000000, 0
.byte <-16, $20, %00000000, <-8
.byte <-16, $21, %00000000, 0
.byte <-8, $30, %00000000, <-8
.byte <-8, $31, %00000000, 0
.byte MAP_END
MAP_END is a "magic number" which I use to indicate the end of a mapping, as there is no predefined size for the mapping. I think I use $80 for it.
Following that, the different mappings are sequenced as animations:
Code:
pl_anim_stand:
; Animation total frame count
.byte 2
; Animation repeat frame #
.byte 0
; Pointer to first mapping
.addr pl_mapping_stand1
; Number of frames to display this mapping, and pad byte
.byte 32, 0
; Pointer to second mapping
.addr pl_mapping_stand2
; Number of frames to display this mapping, and pad byte
.byte 28, 0
And finally, a lookup table for animations, so I may refer to them by number:
Code:
pl_anim_num_map:
.addr pl_anim_dummy
.addr pl_anim_stand
.addr pl_anim_run
.addr pl_anim_jump
.addr pl_anim_fall
metasprite.asm takes a pointer argument to a metasprite mapping (the first table I defined) and will draw it at the specified X and Y coordinates. I won't paste the whole thing here since that will make the post rather large.
If you follow
player_render.asm you can see how animations are sequenced, and how calls to
metasprite.asm are made based on it (several levels of indirection used for the animations).
I hope maybe this is a helpful reference, though there will certainly be room for improvement (as there always is).