When the sprite is evaluated -- the attributes are fetched first to see if they're to be flipped vertically.
The 'normal' CHR is determined by the subtraction of the current scanline and the sprite Y coord (also used to determine whether or not the sprite is in-range). Ex:
Code:
u16 is_in_range = current_scanline - sprite_Y;
if(is_in_range < 8)
{
// sprite is in range
// fetch CHR according to is_in_range
// ie: if is_in_range == 5, CHR comes from $xxx5 and $xxxD
}
To flip vertically -- simply XOR is_in_range with $7 (8x8 sprites) or $F (8x16 sprites) after the sprite is found to be in range.