From 6418936630fa16f7b80d0013dc92fe42bbd55561 Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Wed, 12 Mar 2025 00:05:22 -0700 Subject: [PATCH] Add sprite 0 hit logic --- src/cart.c | 2 +- src/nes.c | 19 +++++++++++++++---- src/ppu.c | 34 +++++++++++++++++++++++++++++++++- src/ppu.h | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/cart.c b/src/cart.c index 3c062ea..e994a0c 100644 --- a/src/cart.c +++ b/src/cart.c @@ -58,7 +58,7 @@ int nes_cart_load_mem(void* data, int size, nes* sys) { mem->ppu.bank[page] = mem->ppu.pal_bank; } - nes_PPU_set_mirroring( + nes_ppu_set_mirroring( &mem->ppu, hdr->flags_6 & ines_Flag_Vert_Mirror ); diff --git a/src/nes.c b/src/nes.c index 492024a..c511048 100644 --- a/src/nes.c +++ b/src/nes.c @@ -51,13 +51,14 @@ static int nes_hsync(nes* sys) { sys->ppu.scanline++; if (nes_ppu_frame_end_line == sys->ppu.scanline) { - sys->ppu.scanline = 0; + sys->ppu.scanline = nes_ppu_prerender_line; } switch (sys->ppu.scanline) { case nes_ppu_prerender_line: - // TODO: Reset PPU Status - // TODO: Get Sprite 0 Hit + sys->core.memory.ppu.status &= ~( ppu_Status_VBlank | + ppu_Status_Hit); + nes_ppu_find_hit_line(&sys->ppu, &sys->core.memory.ppu); break; case nes_ppu_postrender_line: @@ -77,8 +78,18 @@ int nes_loop(nes* sys) { int dot = 0; while (0 == status) { - // TODO: Partial scanline if sprite 0 hit int dots_remaining = nes_ppu_scanline_dots - dot; + if ( sys->ppu.hit_line == sys->ppu.scanline && + (sys->core.memory.ppu.mask & (ppu_Mask_Sprite | ppu_Mask_Back)) && + !(sys->core.memory.ppu.status & ppu_Status_Hit)) { + int hit_dot = ((oam_sprite*)sys->core.memory.ppu.oam)->x; + if (dot >= hit_dot) { +// printf("Line %d sprite 0 hit @ %d\n", sys->ppu.hit_line, hit_dot); + sys->core.memory.ppu.status |= ppu_Status_Hit; + } else { + dots_remaining = hit_dot - dot; + } + } int cpu_cycles = (dots_remaining + 2) / 3; dot += 3 * f6502_step(&sys->core, cpu_cycles); diff --git a/src/ppu.c b/src/ppu.c index 0ac7247..5de224b 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -10,7 +10,7 @@ static const uint8_t mirror_schemes[][4] = { }; -void nes_PPU_set_mirroring(nes_PPU_Memory* mem, +void nes_ppu_set_mirroring(nes_PPU_Memory* mem, nes_Nametable_Mirroring mirror) { for (int i = 0; i < 4; ++i) { mem->bank[nes_PPU_Nametable_Bank_Index + i] = @@ -18,3 +18,35 @@ void nes_PPU_set_mirroring(nes_PPU_Memory* mem, } } +void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) { + int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank); + oam_sprite sprite = *(oam_sprite*)mem->oam; + int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ? + 16 : 8; + int stride = 1; + int y_off = 0; + if (sprite.attr & oam_Attr_Flip_Y) { + stride = -1; + y_off = sprite_height - 1; + sprite_bank = (sprite.index & 1); + sprite.index &= 0xFEU; + } + + sprite_bank = (sprite_bank << 2) | (sprite.index >> 6); + + const int data_off = ((sprite.index & 0x3FU) << 4) + + ((y_off & 8) << 1) + (y_off & 7); + uint8_t* data = mem->bank[sprite_bank] + data_off; + + ppu->hit_line = -1; + + if (sprite.y + 1 <= nes_ppu_render_h && sprite.y > 0) { + for (int line = 0; line < sprite_height; ++line) { + if (data[0] | data[8]) { + ppu->hit_line = sprite.y + 1 + line + nes_ppu_visible_line; + break; + } + data += stride; + } + } +} diff --git a/src/ppu.h b/src/ppu.h index 0abc57f..88270f6 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -4,6 +4,9 @@ #include +#define nes_ppu_render_w (256U) +#define nes_ppu_render_h (240U) + #define nes_ppu_scanline_dots (341U) #define nes_ppu_prerender_line (0U) #define nes_ppu_visible_line (1U) @@ -11,6 +14,20 @@ #define nes_ppu_vblank_line (242U) #define nes_ppu_frame_end_line (262U) +typedef struct { + uint8_t y; + uint8_t index; + uint8_t attr; + uint8_t x; +} oam_sprite; + +typedef enum { + oam_Attr_Pal_Mask = 0b00000011, + oam_Attr_Background = 0b00100000, + oam_Attr_Flip_X = 0b01000000, + oam_Attr_Flip_Y = 0b10000000, +} oam_Attribute; + typedef enum { ppu_reg_ctrl = 0, @@ -35,6 +52,17 @@ typedef enum { ppu_Control_VBlank = 0b10000000, } nes_ppu_Control; +typedef enum { + ppu_Mask_Greyscale = 0b00000001, + ppu_Mask_Left_Back = 0b00000010, + ppu_Mask_Left_Sprite = 0b00000100, + ppu_Mask_Back = 0b00001000, + ppu_Mask_Sprite = 0b00010000, + ppu_Mask_More_Red = 0b00100000, + ppu_Mask_More_Green = 0b01000000, + ppu_Mask_More_Blue = 0b10000000, +} nes_ppu_Mask; + typedef enum { ppu_Status_Open_Bus_Mask = 0b00011111, ppu_Status_Overflow = 0b00100000, @@ -96,13 +124,15 @@ typedef enum { nes_Mirror_Four, } nes_Nametable_Mirroring; -void nes_PPU_set_mirroring(nes_PPU_Memory* mem, +void nes_ppu_set_mirroring(nes_PPU_Memory* mem, nes_Nametable_Mirroring mirror); - typedef struct { int scanline; + int hit_line; } nes_PPU; +void nes_ppu_find_hit_line(nes_PPU*, nes_PPU_Memory*); + #endif // NESE_PPU_H_