| @@ -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; | 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; | int n_sprites = 0; | ||||
| const oam_sprite* sprites = (oam_sprite*)mem->oam; | const oam_sprite* sprites = (oam_sprite*)mem->oam; | ||||
| const oam_sprite* sprite = sprites + | 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); | int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA); | ||||
| const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)]; | 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; | 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]; | 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) { | switch (over_x) { | ||||
| default: | 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: | 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: | 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: | 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: | 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: | 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: | 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: | 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; | ++n_sprites; | ||||
| @@ -352,6 +316,20 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||||
| } else { | } else { | ||||
| mem->status &= ~ppu_Status_Overflow; | 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 | // Increment internal registers | ||||