| @@ -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 | |||
| ); | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -4,6 +4,9 @@ | |||
| #include <stdint.h> | |||
| #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_ | |||