From 3dd141acdbb3eb7f83dc9a5875dd520c45ea39d1 Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Tue, 1 Apr 2025 23:43:17 +0200 Subject: soon --- ppu.c | 141 +++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 80 insertions(+), 61 deletions(-) (limited to 'ppu.c') diff --git a/ppu.c b/ppu.c index f2e3048..fbe3670 100644 --- a/ppu.c +++ b/ppu.c @@ -5,6 +5,7 @@ static void ppu_reset(struct nes_state *state) { __builtin_memset(ppu, 0, sizeof(struct ppu_state)); ppu->scanline = 261; + } static uint32_t nt_mirror(uint8_t mode, uint32_t addr) { @@ -54,11 +55,11 @@ static uint8_t ppu_read_mem(struct nes_state *state, uint32_t addr) { static void ppu_write_mem(struct nes_state *state, uint32_t addr, uint8_t value) { struct ppu_state *ppu = &state->ppu; + addr &= 0x3fff; if(addr < 0x2000) { - // CHR ROM is read-only - return; + return; // CHR ROM is read-only } else if(addr < 0x3f00) { uint32_t mirrored = nt_mirror(state->ines.mirroring, addr - 0x2000); ppu->vram[mirrored] = value; @@ -67,30 +68,37 @@ static void ppu_write_mem(struct nes_state *state, uint32_t addr, uint8_t value) } } + static void ppu_write_2000(struct nes_state *state, uint8_t value) { struct ppu_state *ppu = &state->ppu; ppu->control = value; ppu->nt_x_offset = (value & 0x01) ? 0x400 : 0; ppu->nt_y_offset = (value & 0x02) ? 0x800 : 0; + // printf("write 0x2000 %d\n", value); } static void ppu_write_2001(struct nes_state *state, uint8_t value) { state->ppu.mask = value; + // printf("PPU $2001 write: %02x (BG: %s, SPR: %s)\n", value, (value & 0x08) ? "on" : "off", (value & 0x10) ? "on" : "off"); } static void ppu_write_2003(struct nes_state *state, uint8_t value) { state->ppu.oam_addr = value; + // printf("write 0x2003 %d\n", value); } static void ppu_write_2004(struct nes_state *state, uint8_t value) { state->ppu.oam[state->ppu.oam_addr] = value; state->ppu.oam_addr++; + // printf("write 0x2004 %d\n", value); } static void ppu_write_2005(struct nes_state *state, uint8_t value) { struct ppu_state *ppu = &state->ppu; + // printf("2005 write at sc=%u dot=%u: %02x\n", ppu->scanline, ppu->dot, value); + if(ppu->write_latch == 0) { ppu->fine_x = value & 0x07; ppu->vram_addr = (ppu->vram_addr & ~0x001f) | ((value >> 3) & 0x1f); @@ -100,6 +108,7 @@ static void ppu_write_2005(struct nes_state *state, uint8_t value) { uint32_t fine_y = value & 0x07; uint32_t nt_y = (value & 0x80) ? 0x800 : 0; + // Bits 5–9: coarse Y, bit 11: nt_y, bits 12–14: fine Y ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (coarse_y << 5) | nt_y | (fine_y << 12); ppu->write_latch = 0; } @@ -108,6 +117,8 @@ static void ppu_write_2005(struct nes_state *state, uint8_t value) { static void ppu_write_2006(struct nes_state *state, uint8_t value) { struct ppu_state *ppu = &state->ppu; + // printf("2006 write at sc=%u dot=%u: %02x\n", ppu->scanline, ppu->dot, value); + if(ppu->write_latch == 0) { ppu->vram_addr = ((uint32_t)(value & 0x3f)) << 8; ppu->write_latch = 1; @@ -119,8 +130,12 @@ static void ppu_write_2006(struct nes_state *state, uint8_t value) { static void ppu_write_2007(struct nes_state *state, uint8_t value) { struct ppu_state *ppu = &state->ppu; + // printf("PPU $2007 write: addr=%04x val=%02x\n", ppu->vram_addr, value); ppu_write_mem(state, ppu->vram_addr, value); + +// printf("w%04x:%02x cx=%u cy=%u fx=%u ntx=%x nty=%x ", ppu->vram_addr, value, ppu->coarse_x, ppu->coarse_y, ppu->fine_x, ppu->nt_x_offset, ppu->nt_y_offset); + ppu->vram_addr += (ppu->control & 0x04) ? 32 : 1; ppu->vram_addr &= 0x3fff; } @@ -129,19 +144,24 @@ static uint8_t ppu_read_2002(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; uint8_t result = ppu->vblank | ppu->sprite_zero_hit | ppu->sprite_overflow; + // printf("PPU $2002 read at PC=%04x: result=%02x (vblank=%02x)\n", state->cpu.pc, result, ppu->vblank); + ppu->vblank = 0; ppu->write_latch = 0; return result; } static uint8_t ppu_read_2004(struct nes_state *state) { + printf("ppu_read_2004\n"); return state->ppu.oam[state->ppu.oam_addr]; } static uint8_t ppu_read_2007(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; + // printf("ppu_read_2007\n"); uint8_t value = ppu_read_mem(state, ppu->vram_addr); + ppu->vram_addr += (ppu->control & 0x04) ? 32 : 1; ppu->vram_addr &= 0x3fff; return value; @@ -149,62 +169,58 @@ static uint8_t ppu_read_2007(struct nes_state *state) { static void ppu_inc_x(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - if((ppu->vram_addr & 0x001f) == 31) { - ppu->vram_addr &= ~0x001f; - ppu->vram_addr ^= 0x0400; - } else { - ppu->vram_addr += 1; + + ppu->render_coarse_x++; + if(ppu->render_coarse_x == 32) { + ppu->render_coarse_x = 0; + ppu->render_nt_x ^= 0x400; } } static void ppu_inc_y(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - if((ppu->vram_addr & 0x7000) != 0x7000) { - ppu->vram_addr += 0x1000; - } else { - ppu->vram_addr &= ~0x7000; - uint32_t y = (ppu->vram_addr >> 5) & 0x1f; - - if(y == 29) { - y = 0; - ppu->vram_addr ^= 0x0800; - } else if(y == 31) { - y = 0; - } else { - y++; - } + ppu->render_fine_y++; + if(ppu->render_fine_y == 8) { + ppu->render_fine_y = 0; + ppu->render_coarse_y++; - ppu->vram_addr = (ppu->vram_addr & ~0x03e0) | (y << 5); + if(ppu->render_coarse_y == 30) { + ppu->render_coarse_y = 0; + ppu->render_nt_y ^= 0x800; + } } } static uint8_t ppu_fetch_name_table(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - uint32_t addr = 0x2000 | (ppu->vram_addr & 0x0fff); + uint32_t addr = 0x2000 | ppu->render_nt_x | ppu->render_nt_y | (ppu->render_coarse_y << 5) | ppu->render_coarse_x; return ppu_read_mem(state, addr); } static uint8_t ppu_fetch_attribute(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - uint32_t v = ppu->vram_addr; - uint32_t nt = 0x23c0 | (v & 0x0c00) | ((v >> 4) & 0x38) | ((v >> 2) & 0x07); - return ppu_read_mem(state, nt); + uint32_t x = ppu->render_coarse_x; + uint32_t y = ppu->render_coarse_y; + uint32_t nt = (ppu->render_nt_y ? 0x800 : 0) | (ppu->render_nt_x ? 0x400 : 0); + uint32_t addr = 0x23c0 | (nt & 0x0c00) | ((y >> 2) << 3) | (x >> 2); + + return ppu_read_mem(state, addr); } static uint8_t ppu_fetch_pattern_lo(struct nes_state *state, uint8_t tile) { struct ppu_state *ppu = &state->ppu; uint32_t base = (ppu->control & 0x10) ? 0x1000 : 0x0000; - uint32_t addr = base + tile * 16 + ((ppu->vram_addr >> 12) & 0x7); + uint32_t addr = base + tile * 16 + ppu->render_fine_y; return ppu_read_mem(state, addr); } static uint8_t ppu_fetch_pattern_hi(struct nes_state *state, uint8_t tile) { struct ppu_state *ppu = &state->ppu; uint32_t base = (ppu->control & 0x10) ? 0x1000 : 0x0000; - uint32_t addr = base + tile * 16 + ((ppu->vram_addr >> 12) & 0x7) + 8; + uint32_t addr = base + tile * 16 + ppu->render_fine_y + 8; return ppu_read_mem(state, addr); } @@ -257,6 +273,31 @@ static void ppu_render_pixel(struct nes_state *state) { ppu->pixels[y * 256 + x] = color_index; } +static void ppu_evaluate_sprites(struct nes_state *state, uint32_t target_scanline) { + struct ppu_state *ppu = &state->ppu; + + ppu->sprite_count = 0; + + for(uint32_t i = 0; i < 64; i++) { + uint8_t y = ppu->oam[i * 4 + 0]; + uint8_t height = (ppu->control & 0x20) ? 16 : 8; + + if(target_scanline >= y && target_scanline < y + height) { + if(ppu->sprite_count < 8) { + uint8_t *dest = &ppu->secondary_oam[ppu->sprite_count * 4]; + dest[0] = y; + dest[1] = ppu->oam[i * 4 + 1]; + dest[2] = ppu->oam[i * 4 + 2]; + dest[3] = ppu->oam[i * 4 + 3]; + ppu->sprite_count++; + } else { + ppu->sprite_overflow = 0x20; + break; + } + } + } +} + static uint8_t ppu_fetch_sprite_pattern_lo(struct nes_state *state, uint8_t tile, uint8_t y_offset, uint8_t attr, uint8_t height) { uint32_t addr; @@ -285,7 +326,6 @@ static uint8_t ppu_fetch_sprite_pattern_hi(struct nes_state *state, uint8_t tile return ppu_read_mem(state, addr); } - static void ppu_render_sprites(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; @@ -349,43 +389,22 @@ static void ppu_render_sprites(struct nes_state *state) { } } -static void ppu_evaluate_sprites(struct nes_state *state, uint32_t target_scanline) { - struct ppu_state *ppu = &state->ppu; - - ppu->sprite_count = 0; - - for(uint32_t i = 0; i < 64; i++) { - uint8_t y = ppu->oam[i * 4 + 0]; - uint8_t height = (ppu->control & 0x20) ? 16 : 8; - - if(target_scanline >= y && target_scanline < y + height) { - if(ppu->sprite_count < 8) { - uint8_t *dest = &ppu->secondary_oam[ppu->sprite_count * 4]; - dest[0] = y; - dest[1] = ppu->oam[i * 4 + 1]; - dest[2] = ppu->oam[i * 4 + 2]; - dest[3] = ppu->oam[i * 4 + 3]; - ppu->sprite_count++; - } else { - ppu->sprite_overflow = 0x20; - break; - } - } - } -} - - static void ppu_copy_horizontal_scroll(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->t_addr & 0x041f); + uint32_t addr = ppu->vram_addr; + ppu->render_coarse_x = (addr >> 0) & 0x1f; + ppu->render_nt_x = (addr & 0x400) ? 0x400 : 0; } static void ppu_copy_vertical_scroll(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->t_addr & 0x7be0); + uint32_t addr = ppu->vram_addr; + ppu->render_coarse_y = (addr >> 5) & 0x1f; + ppu->render_fine_y = (addr >> 12) & 0x07; + ppu->render_nt_y = (addr & 0x800) ? 0x800 : 0; } - +__attribute__((flatten)) static void ppu_tick(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; @@ -411,7 +430,7 @@ static void ppu_tick(struct nes_state *state) { break; case 7: { uint8_t attr = ppu->next_attr; - uint8_t shift = ((ppu->coarse_y >> 2) & 2) | ((ppu->coarse_x >> 1) & 1); + uint8_t shift = ((ppu->render_coarse_y >> 2) & 2) | ((ppu->render_coarse_x >> 1) & 1); ppu->bg_attribute_latch = (attr >> (shift * 2)) & 3; ppu_load_background_shifters(state, ppu->next_lo, ppu->next_hi); break; @@ -438,7 +457,7 @@ static void ppu_tick(struct nes_state *state) { } } - if(scanline == 261 && dot >= 280 && dot <= 304) { + if(scanline == 261 && dot >= 280) { // && dot <= 304) { ppu_copy_vertical_scroll(state); } } @@ -486,4 +505,4 @@ static void ppu_dma_4014(struct nes_state *state, uint8_t page) { ppu_write_2004(state, val); } -} +} \ No newline at end of file -- cgit v1.2.3