diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-04-01 00:06:02 +0200 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-04-01 00:06:02 +0200 |
| commit | 59b092914fcf3c33a783a2813ebe268586609ff9 (patch) | |
| tree | a18b4236e82525e01d2e3bce0cdc464abfeb9cf9 /ppu.c | |
| parent | a386ef64f6376b3ef8434a6cdf456495287fcbca (diff) | |
before add scroll_latch
Diffstat (limited to 'ppu.c')
| -rw-r--r-- | ppu.c | 241 |
1 files changed, 128 insertions, 113 deletions
@@ -9,6 +9,35 @@ static void ppu_reset(struct nes_state *state) { } +static uint32_t nt_mirror(uint8_t mode, uint32_t addr) { + addr &= 0x0fff; + uint32_t table = addr / 0x400; + uint32_t offset = addr & 0x03ff; + + switch(mode) { + case 0: // Horizontal (A A B B) + if(table == 0 || table == 1) return offset; + return offset + 0x400; + case 1: // Vertical (A B A B) + return (table & 1) * 0x400 + offset; + case 2: // 1-screen lower + return offset; + case 3: // 1-screen upper + return offset + 0x400; + case 4: // 4-screen (unsupported in base PPU logic) + default: + return addr; + } +} + +static uint32_t palette_mirror(uint32_t addr) { + addr &= 0x1f; + if((addr & 0x13) == 0x10) { + addr &= ~0x10; + } + return addr; +} + static uint8_t ppu_read_mem(struct nes_state *state, uint32_t addr) { struct ppu_state *ppu = &state->ppu; addr &= 0x3fff; @@ -17,19 +46,11 @@ static uint8_t ppu_read_mem(struct nes_state *state, uint32_t addr) { return state->chrrom[addr]; } else if(addr < 0x3f00) { uint32_t nt_index = addr - 0x2000; - uint32_t page = nt_index / 0x400; - uint32_t offset = nt_index & 0x03ff; - if(page == 0 || page == 2) { - return ppu->vram[offset]; - } else { - return ppu->vram[offset + 0x400]; - } + uint32_t mirrored = nt_mirror(state->ines.mirroring, nt_index); + return ppu->vram[mirrored]; } else { - addr &= 0x1f; - if((addr & 0x13) == 0x10) { - addr &= ~0x10; - } - return ppu->palette[addr]; + uint32_t mirrored = palette_mirror(addr); + return ppu->palette[mirrored]; } } @@ -37,34 +58,18 @@ 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 >= 0x3f00 && addr < 0x3f20) { - printf("PPU write: %04x = %02x\n", addr, value); -} if(addr < 0x2000) { - // Ignore write to CHR ROM - return; + return; // CHR ROM is read-only } else if(addr < 0x3f00) { - uint32_t nt_index = addr - 0x2000; - uint32_t page = nt_index / 0x400; - uint32_t offset = nt_index & 0x03ff; - if(page == 0 || page == 2) { - ppu->vram[offset] = value; - } else { - ppu->vram[offset + 0x400] = value; - } + uint32_t mirrored = nt_mirror(state->ines.mirroring, addr - 0x2000); + ppu->vram[mirrored] = value; } else { - addr &= 0x1f; - if((addr & 0x13) == 0x10) { - addr &= ~0x10; - } - ppu->palette[addr] = value; - if(addr == 0) { - printf("Write to universal BG color: %02x\n", value); - } + ppu->palette[palette_mirror(addr)] = value; } } + static void ppu_write_2000(struct nes_state *state, uint8_t value) { struct ppu_state *ppu = &state->ppu; @@ -93,6 +98,8 @@ static void ppu_write_2004(struct nes_state *state, uint8_t 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); @@ -108,24 +115,17 @@ 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"); + + 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; } else { ppu->vram_addr |= value; ppu->write_latch = 0; - - uint32_t addr = ppu->vram_addr; - - ppu->coarse_x = (addr >> 0) & 0x1f; - ppu->coarse_y = (addr >> 5) & 0x1f; - ppu->nt_x_offset = (addr & 0x400) ? 0x400 : 0; - ppu->nt_y_offset = (addr & 0x800) ? 0x800 : 0; - ppu->fine_y = (addr >> 12) & 0x07; } } @@ -135,7 +135,10 @@ static void ppu_write_2007(struct nes_state *state, uint8_t 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; } static uint8_t ppu_read_2002(struct nes_state *state) { @@ -156,76 +159,69 @@ static uint8_t ppu_read_2004(struct nes_state *state) { static uint8_t ppu_read_2007(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - printf("ppu_read_2007\n"); + // 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; } static void ppu_inc_x(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - ppu->coarse_x++; - if(ppu->coarse_x == 32) { - ppu->coarse_x = 0; - ppu->nt_x_offset ^= 0x400; + 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; - ppu->fine_y++; - if(ppu->fine_y == 8) { - ppu->fine_y = 0; - ppu->coarse_y++; + ppu->render_fine_y++; + if(ppu->render_fine_y == 8) { + ppu->render_fine_y = 0; + ppu->render_coarse_y++; - if(ppu->coarse_y == 30) { - ppu->coarse_y = 0; - ppu->nt_y_offset ^= 0x800; + 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->nt_x_offset | ppu->nt_y_offset | (ppu->coarse_y << 5) | ppu->coarse_x; - - uint8_t tile = ppu_read_mem(state, addr); - // printf("NT[%04x] = %02x (X:%u Y:%u)\n", addr, tile, ppu->coarse_x, ppu->coarse_y); - // if(ppu->nt_x_offset != 0 || ppu->nt_y_offset != 0) - // printf("NT[%04x] -> tile %02x (X:%d Y:%d ntx:%x nty:%x)\n", addr, tile, ppu->coarse_x, ppu->coarse_y, ppu->nt_x_offset, ppu->nt_y_offset); - return tile; + 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) { - uint32_t x = state->ppu.coarse_x; - uint32_t y = state->ppu.coarse_y; - uint32_t nt = (state->ppu.nt_y_offset ? 0x800 : 0) | (state->ppu.nt_x_offset ? 0x400 : 0); + struct ppu_state *ppu = &state->ppu; + + 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); - uint8_t val = ppu_read_mem(state, addr); - // printf("ATTR[%04x] = %02x (X:%u Y:%u)\n", addr, val, x, y); - return val; -} + 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 << 4) + ppu->fine_y; + 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 << 4) + ppu->fine_y + 8; - + uint32_t addr = base + tile * 16 + ppu->render_fine_y + 8; return ppu_read_mem(state, addr); } @@ -267,50 +263,67 @@ static void ppu_render_pixel(struct nes_state *state) { attrib |= (ppu->attr_shift_lo >> shift) & 1; uint8_t index = (attrib << 2) | pattern; - uint8_t color_index = (index == 0) ? ppu->palette[0] : ppu->palette[index]; + + uint8_t color_index; + if(index == 0) { + color_index = ppu->palette[0]; + } else { + color_index = ppu->palette[index & 0x1f]; + } ppu->pixels[y * 256 + x] = color_index; } -static void ppu_evaluate_sprites(struct nes_state *state) { +static void ppu_evaluate_sprites(struct nes_state *state, uint32_t target_scanline) { struct ppu_state *ppu = &state->ppu; - uint8_t count = 0; - uint8_t sprite_height = (ppu->control & 0x20) ? 16 : 8; + ppu->sprite_count = 0; for(uint32_t i = 0; i < 64; i++) { - uint8_t y = ppu->oam[i * 4]; - if(ppu->scanline >= y && ppu->scanline < y + sprite_height) { - if(count < 8) { - uint8_t j = count * 4; - uint32_t base = i * 4; - ppu->secondary_oam[j + 0] = ppu->oam[base + 0]; - ppu->secondary_oam[j + 1] = ppu->oam[base + 1]; - ppu->secondary_oam[j + 2] = ppu->oam[base + 2]; - ppu->secondary_oam[j + 3] = ppu->oam[base + 3]; - count++; + 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; } } } -// if(count) printf("sprite_count: %d\n", count); - ppu->sprite_count = count; } -static uint8_t ppu_fetch_sprite_tile(struct nes_state *state, uint8_t tile, uint8_t y_offset, uint8_t attr, uint8_t height) { - uint32_t base; +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; if(height == 16) { uint32_t bank = (tile & 1) ? 0x1000 : 0x0000; tile &= 0xfe; - base = bank | ((tile & 0xfe) << 4); + addr = bank | ((tile << 4) + (y_offset & 0x07)); } else { - base = ((state->ppu.control & 0x08) ? 0x1000 : 0x0000) | (tile << 4); + uint32_t base = (state->ppu.control & 0x08) ? 0x1000 : 0x0000; + addr = base | ((tile << 4) + (y_offset & 0x07)); } + return ppu_read_mem(state, addr); +} + +static uint8_t ppu_fetch_sprite_pattern_hi(struct nes_state *state, uint8_t tile, uint8_t y_offset, uint8_t attr, uint8_t height) { + uint32_t addr; - uint32_t addr = base + y_offset; + if(height == 16) { + uint32_t bank = (tile & 1) ? 0x1000 : 0x0000; + tile &= 0xfe; + addr = bank | ((tile << 4) + (y_offset & 0x07) + 8); + } else { + uint32_t base = (state->ppu.control & 0x08) ? 0x1000 : 0x0000; + addr = base | ((tile << 4) + (y_offset & 0x07) + 8); + } return ppu_read_mem(state, addr); } @@ -333,11 +346,15 @@ static void ppu_render_sprites(struct nes_state *state) { uint8_t attr = spr[2]; uint8_t sx = spr[3]; + uint8_t height = (ppu->control & 0x20) ? 16 : 8; + + if(y < sy || y >= sy + height) { + continue; + } if(x < sx || x >= sx + 8) { continue; } - uint8_t height = (ppu->control & 0x20) ? 16 : 8; uint8_t offset_y = y - sy; if(attr & 0x80) { @@ -349,8 +366,8 @@ static void ppu_render_sprites(struct nes_state *state) { shift = x - sx; } - uint8_t pattern_lo = ppu_fetch_sprite_tile(state, tile, offset_y, attr, height); - uint8_t pattern_hi = ppu_fetch_sprite_tile(state, tile, offset_y + 8, attr, height); + uint8_t pattern_lo = ppu_fetch_sprite_pattern_lo(state, tile, offset_y, attr, height); + uint8_t pattern_hi = ppu_fetch_sprite_pattern_hi(state, tile, offset_y, attr, height); uint8_t lo_bit = (pattern_lo >> shift) & 1; uint8_t hi_bit = (pattern_hi >> shift) & 1; @@ -375,21 +392,17 @@ static void ppu_render_sprites(struct nes_state *state) { static void ppu_copy_horizontal_scroll(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - uint32_t addr = ppu->vram_addr; - - ppu->coarse_x = (addr >> 0) & 0x1f; - ppu->nt_x_offset = (addr & 0x400) ? 0x400 : 0; + 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; - uint32_t addr = ppu->vram_addr; - - ppu->coarse_y = (addr >> 5) & 0x1f; - ppu->nt_y_offset = (addr & 0x800) ? 0x800 : 0; - ppu->fine_y = (addr >> 12) & 0x07; + ppu->render_coarse_y = (addr >> 5) & 0x1f; + ppu->render_fine_y = (addr >> 12) & 0x07; + ppu->render_nt_y = (addr & 0x800) ? 0x800 : 0; } __attribute__((flatten)) @@ -399,7 +412,7 @@ static void ppu_tick(struct nes_state *state) { uint32_t scanline = ppu->scanline; uint32_t dot = ppu->dot; - if(scanline < 240) { + if(scanline < 240 || scanline == 261) { if((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336)) { ppu_shift_background(state); @@ -418,7 +431,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; @@ -429,7 +442,7 @@ static void ppu_tick(struct nes_state *state) { } } - if(dot >= 1 && dot <= 256) { + if(scanline < 240 && dot >= 1 && dot <= 256) { ppu_render_pixel(state); ppu_render_sprites(state); } @@ -439,11 +452,13 @@ static void ppu_tick(struct nes_state *state) { } if(dot == 257) { - ppu_evaluate_sprites(state); ppu_copy_horizontal_scroll(state); + if(scanline < 240) { + ppu_evaluate_sprites(state, scanline + 1); + } } - if(scanline == 261 && dot >= 280 && dot <= 304) { + if(scanline == 261 && dot >= 280) { // && dot <= 304) { ppu_copy_vertical_scroll(state); } } |
