| @@ -232,6 +232,9 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||
| int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2; | |||
| uint8_t foreground[nes_ppu_render_w]; | |||
| memset(foreground, 0xFFU, nes_ppu_render_w); | |||
| int n_sprites = 0; | |||
| const oam_sprite* sprites = (oam_sprite*)mem->oam; | |||
| const oam_sprite* sprite = sprites + | |||
| @@ -265,7 +268,10 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||
| int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA); | |||
| const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)]; | |||
| uint8_t* dst = scanline_ptr + sprite->x; | |||
| const uint8_t* back = | |||
| (sprite->attr & oam_Attr_Background) ? | |||
| (scanline_ptr + sprite->x) : NULL; | |||
| uint8_t* dst = foreground + sprite->x; | |||
| const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w; | |||
| @@ -275,73 +281,31 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||
| pat1 = sprite_rev[tmp]; | |||
| } | |||
| // TODO: Handle column 0 sprite mask | |||
| #define do_sprite(idx, pi, shift) \ | |||
| if (back && back[idx] != 0xFFU) { \ | |||
| dst[idx] = back[idx]; \ | |||
| } else { \ | |||
| int pal_idx = (pat##pi >> shift) & 3; \ | |||
| if (pal_idx) dst[idx] = pal[pal_idx]; \ | |||
| } | |||
| switch (over_x) { | |||
| default: | |||
| { int pal_idx = (pat0 >> 0) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[7] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[7] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(7, 0, 0); | |||
| case 1: | |||
| { int pal_idx = (pat1 >> 0) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[6] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[6] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(6, 1, 0); | |||
| case 2: | |||
| { int pal_idx = (pat0 >> 2) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[5] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[5] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(5, 0, 2); | |||
| case 3: | |||
| { int pal_idx = (pat1 >> 2) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[4] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[4] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(4, 1, 2); | |||
| case 4: | |||
| { int pal_idx = (pat0 >> 4) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[3] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[3] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(3, 0, 4); | |||
| case 5: | |||
| { int pal_idx = (pat1 >> 4) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[2] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[2] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(2, 1, 4); | |||
| case 6: | |||
| { int pal_idx = (pat0 >> 6) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[1] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[1] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(1, 0, 6); | |||
| case 7: | |||
| { int pal_idx = (pat1 >> 6) & 3; | |||
| if ( ( !(sprite->attr & oam_Attr_Background) || | |||
| dst[0] == 0xFFU) | |||
| && pal_idx) { | |||
| dst[0] = pal[pal_idx]; | |||
| } | |||
| } | |||
| do_sprite(0, 1, 6); | |||
| } | |||
| ++n_sprites; | |||
| @@ -352,6 +316,20 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||
| } else { | |||
| mem->status &= ~ppu_Status_Overflow; | |||
| } | |||
| // Mask column 0? | |||
| if (!(mem->mask & ppu_Mask_Left_Sprite)) { | |||
| memset(foreground, 0xFFU, 8); | |||
| } | |||
| // Overlay the sprites | |||
| const uint8_t* fg = foreground; | |||
| uint8_t* dst = scanline_ptr; | |||
| for (int i = 0; i < nes_ppu_render_w; ++i) { | |||
| if (*fg != 0xFFU) *dst = *fg; | |||
| ++fg; | |||
| ++dst; | |||
| } | |||
| } | |||
| // Increment internal registers | |||