From 7888e1d5408ed5f88cff788c37b942076f6b9c63 Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Wed, 2 Apr 2025 06:30:39 +0200 Subject: skinny_version working horizontally --- ppu.c | 653 +++++++++++++++++++++++++++++++----------------------------------- 1 file changed, 303 insertions(+), 350 deletions(-) (limited to 'ppu.c') diff --git a/ppu.c b/ppu.c index fbe3670..1c34381 100644 --- a/ppu.c +++ b/ppu.c @@ -1,115 +1,81 @@ static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); -static void ppu_reset(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - - __builtin_memset(ppu, 0, sizeof(struct ppu_state)); - ppu->scanline = 261; -} +static void ppu_sprite_shift(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; -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; + if(!(ppu->reg_mask & 0x10)) { + return; } -} -static uint32_t palette_mirror(uint32_t addr) { - addr &= 0x1f; - if((addr & 0x13) == 0x10) { - addr &= ~0x10; + for(uint32_t i = 0; i < ppu->sprite_count; i++) { + if(ppu->sprite_positions[i] > 0) { + ppu->sprite_positions[i]--; + } else { + ppu->sprite_shift_lo[i] <<= 1; + ppu->sprite_shift_hi[i] <<= 1; + } } - return addr; } -static uint8_t ppu_read_mem(struct nes_state *state, uint32_t addr) { + +static void ppu_reset(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - addr &= 0x3fff; + memset(ppu, 0, sizeof(struct ppu_state)); - if(addr < 0x2000) { - return state->chrrom[addr]; - } else if(addr < 0x3f00) { - uint32_t nt_index = addr - 0x2000; - uint32_t mirrored = nt_mirror(state->ines.mirroring, nt_index); - return ppu->vram[mirrored]; - } else { - uint32_t mirrored = palette_mirror(addr); - return ppu->palette[mirrored]; - } } -static void ppu_write_mem(struct nes_state *state, uint32_t addr, uint8_t value) { - struct ppu_state *ppu = &state->ppu; - - addr &= 0x3fff; +static uint32_t ppu_resolve_ciram(struct nes_state *state, uint32_t addr) { + // Apply mirroring logic to address in $2000–$2FFF range + addr &= 0x0fff; - if(addr < 0x2000) { - return; // CHR ROM is read-only - } else if(addr < 0x3f00) { - uint32_t mirrored = nt_mirror(state->ines.mirroring, addr - 0x2000); - ppu->vram[mirrored] = value; - } else { - ppu->palette[palette_mirror(addr)] = value; + switch(state->ines.mirroring) { + case MIRROR_VERTICAL: + return (addr & 0x0800) | (addr & 0x03ff); // $2000/$2800 → $0000, $2400/$2C00 → $0400 + case MIRROR_HORIZONTAL: + return ((addr & 0x0400) >> 1) | (addr & 0x03ff); // $2000/$2400 → $0000, $2800/$2C00 → $0400 + default: + return addr & 0x07ff; // For now, 4-screen = direct } } +static uint8_t ppu_ciram_read(struct nes_state *state, uint32_t addr) { + return state->ppu.ciram[ppu_resolve_ciram(state, addr)]; +} + +static void ppu_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + state->ppu.ciram[ppu_resolve_ciram(state, addr)] = 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); + ppu->reg_ctrl = value; + ppu->temp_addr = (ppu->temp_addr & 0xf3ff) | ((value & 0x03) << 10); } 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"); + state->ppu.reg_mask = value; } 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); + ppu->temp_addr = (ppu->temp_addr & ~0x001f) | (value >> 3); ppu->write_latch = 1; } else { - uint32_t coarse_y = (value >> 3) & 0x1f; - 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->temp_addr = (ppu->temp_addr & ~0x73e0) | ((value & 0x07) << 12) | ((value & 0xf8) << 2); ppu->write_latch = 0; } } @@ -117,392 +83,379 @@ 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->temp_addr = (ppu->temp_addr & 0x00ff) | ((value & 0x3f) << 8); ppu->write_latch = 1; } else { - ppu->vram_addr |= value; + ppu->temp_addr = (ppu->temp_addr & 0xff00) | value; + ppu->vram_addr = ppu->temp_addr; ppu->write_latch = 0; } } 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); + uint32_t addr = ppu->vram_addr & 0x3fff; - 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); + if(addr < 0x2000) { + // CHR-RAM, skip + } else if(addr < 0x3f00) { + uint32_t mirrored_addr = addr & 0x0fff; + ppu->ciram[mirrored_addr & 0x7ff] = value; + } else if(addr < 0x4000) { + uint32_t pal_addr = addr & 0x1f; + if((pal_addr & 0x13) == 0x10) { + pal_addr &= ~0x10; + } + ppu->palette[pal_addr] = value; + } - ppu->vram_addr += (ppu->control & 0x04) ? 32 : 1; - ppu->vram_addr &= 0x3fff; + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; } static uint8_t ppu_read_2002(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; + uint8_t result = ppu->reg_status; - 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->reg_status &= ~0x80; 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); + uint32_t addr = ppu->vram_addr & 0x3fff; + uint8_t result = 0; - 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->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->render_fine_y++; - if(ppu->render_fine_y == 8) { - ppu->render_fine_y = 0; - ppu->render_coarse_y++; - - if(ppu->render_coarse_y == 30) { - ppu->render_coarse_y = 0; - ppu->render_nt_y ^= 0x800; + // Read from CHR-RAM (CHR-ROM in PPU) + if(addr < 0x2000) { + result = ppu->vram_read_buffer; + ppu->vram_read_buffer = state->chrrom[addr]; + } else if(addr < 0x3f00) { + // Read from CIRAM (internal VRAM) + uint32_t mirrored_addr = addr & 0x0fff; + if(state->ines.mirroring == 2) { + mirrored_addr |= (addr & 0x0800) ? 0x400 : 0x000; // Handle 4-screen mirroring + } + result = ppu->ciram[mirrored_addr & 0x7ff]; + } else if(addr < 0x4000) { + // Read from palette + uint32_t pal_addr = addr & 0x1f; + if((pal_addr & 0x13) == 0x10) { + pal_addr &= ~0x10; // Skip over the unused area of the palette } + result = ppu->palette[pal_addr]; } -} - -static uint8_t ppu_fetch_name_table(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - 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); + // Update VRAM address based on control register + ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; + return result; } -static uint8_t ppu_fetch_attribute(struct nes_state *state) { +static void ppu_evaluate_sprites(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; + uint8_t sprite_height = (ppu->reg_ctrl & 0x20) ? 16 : 8; + uint8_t n = 0; - 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); -} + for(uint8_t i = 0; i < 64; i++) { + uint8_t y = ppu->oam[i * 4 + 0]; + int row = (int)ppu->scanline - y; + + if(row >= 0 && row < sprite_height) { + if(n < 8) { + uint8_t *src = ppu->oam + i * 4; + uint8_t *dst = ppu->secondary_oam + n * 4; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + ppu->sprite_indexes[n] = i; + if(i == 0) { + ppu->sprite_zero_hit_possible = 1; + } + n++; + } else { + ppu->reg_status |= 0x20; + break; + } + } + } -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->render_fine_y; - return ppu_read_mem(state, addr); + ppu->sprite_count = n; } -static uint8_t ppu_fetch_pattern_hi(struct nes_state *state, uint8_t tile) { +static void ppu_fetch_sprite_patterns(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - uint32_t base = (ppu->control & 0x10) ? 0x1000 : 0x0000; - uint32_t addr = base + tile * 16 + ppu->render_fine_y + 8; - return ppu_read_mem(state, addr); -} + for(uint8_t i = 0; i < ppu->sprite_count; i++) { + uint8_t *s = ppu->secondary_oam + i * 4; + uint8_t y = s[0], tile = s[1], attr = s[2], x = s[3]; + uint8_t row = ppu->scanline - y; + uint8_t height = (ppu->reg_ctrl & 0x20) ? 16 : 8; -static void ppu_load_background_shifters(struct nes_state *state, uint8_t pattern_lo, uint8_t pattern_hi) { - struct ppu_state *ppu = &state->ppu; + if(attr & 0x80) { + row = height - 1 - row; + } - ppu->bg_shift_lo = (ppu->bg_shift_lo & 0xff00) | pattern_lo; - ppu->bg_shift_hi = (ppu->bg_shift_hi & 0xff00) | pattern_hi; + uint32_t addr; + if(height == 16) { + uint32_t bank = (tile & 1) ? 0x1000 : 0x0000; + tile &= 0xfe; + if(row >= 8) { + tile++; + row -= 8; + } + addr = bank + tile * 16 + row; + } else { + uint32_t bank = (ppu->reg_ctrl & 0x08) ? 0x1000 : 0x0000; + addr = bank + tile * 16 + row; + } - uint8_t pal = ppu->bg_attribute_latch & 3; - ppu->attr_shift_lo = (ppu->attr_shift_lo & 0xff00) | ((pal & 1) ? 0xff : 0x00); - ppu->attr_shift_hi = (ppu->attr_shift_hi & 0xff00) | ((pal & 2) ? 0xff : 0x00); -} + uint8_t lsb = state->chrrom[addr]; + uint8_t msb = state->chrrom[addr + 8]; -static void ppu_shift_background(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; + if(attr & 0x40) { + lsb = ((lsb * 0x0202020202ULL & 0x010884422010ULL) % 1023) & 0xff; + msb = ((msb * 0x0202020202ULL & 0x010884422010ULL) % 1023) & 0xff; + } - ppu->bg_shift_lo <<= 1; - ppu->bg_shift_hi <<= 1; - ppu->attr_shift_lo <<= 1; - ppu->attr_shift_hi <<= 1; + ppu->sprite_shift_lo[i] = lsb; + ppu->sprite_shift_hi[i] = msb; + ppu->sprite_positions[i] = x; + ppu->sprite_priorities[i] = (attr >> 5) & 1; + } } static void ppu_render_pixel(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; + uint8_t bg_pixel = 0; + uint8_t bg_palette = 0; + uint8_t sp_pixel = 0; + uint8_t sp_palette = 0; + uint8_t sp_prio = 0; + uint8_t sp_zero = 0; + uint8_t final_color = 0; - if(!(ppu->mask & 0x08)) { - return; - } + struct ppu_state *ppu = &state->ppu; uint32_t x = ppu->dot - 1; uint32_t y = ppu->scanline; - uint8_t shift = 15 - ppu->fine_x; - - uint8_t pattern = ((ppu->bg_shift_hi >> shift) & 1) << 1; - pattern |= (ppu->bg_shift_lo >> shift) & 1; - - uint8_t attrib = ((ppu->attr_shift_hi >> shift) & 1) << 1; - attrib |= (ppu->attr_shift_lo >> shift) & 1; - uint8_t index = (attrib << 2) | pattern; - - uint8_t color_index; - if(index == 0) { - color_index = ppu->palette[0]; - } else { - color_index = ppu->palette[index & 0x1f]; + if(x >= 256 || y >= 240) { + return; } - ppu->pixels[y * 256 + x] = color_index; -} + uint32_t bit = 0x8000 >> ppu->fine_x; -static void ppu_evaluate_sprites(struct nes_state *state, uint32_t target_scanline) { - struct ppu_state *ppu = &state->ppu; + if(ppu->reg_mask & 0x08) { + uint8_t p0 = (ppu->bg_shift_pattern_low & bit) ? 1 : 0; + uint8_t p1 = (ppu->bg_shift_pattern_high & bit) ? 1 : 0; + bg_pixel = (p1 << 1) | p0; - ppu->sprite_count = 0; + uint8_t a0 = (ppu->bg_shift_attrib_low & bit) ? 1 : 0; + uint8_t a1 = (ppu->bg_shift_attrib_high & bit) ? 1 : 0; + bg_palette = (a1 << 1) | a0; + } - 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; + if(ppu->reg_mask & 0x10) { + for(uint8_t i = 0; i < ppu->sprite_count; i++) { + if(ppu->sprite_positions[i] == 0) { + uint8_t p0 = (ppu->sprite_shift_lo[i] & 0x80) ? 1 : 0; + uint8_t p1 = (ppu->sprite_shift_hi[i] & 0x80) ? 1 : 0; + sp_pixel = (p1 << 1) | p0; + + if(sp_pixel) { + sp_palette = ppu->secondary_oam[i * 4 + 2] & 3; + sp_prio = ppu->sprite_priorities[i]; + sp_zero = (ppu->sprite_indexes[i] == 0); + 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; - - if(height == 16) { - uint32_t bank = (tile & 1) ? 0x1000 : 0x0000; - tile &= 0xfe; - addr = bank | ((tile << 4) + (y_offset & 0x07)); - } else { - 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; - if(height == 16) { - uint32_t bank = (tile & 1) ? 0x1000 : 0x0000; - tile &= 0xfe; - addr = bank | ((tile << 4) + (y_offset & 0x07) + 8); + if(bg_pixel == 0 && sp_pixel == 0) { + final_color = ppu->palette[0]; + } else if(bg_pixel == 0 && sp_pixel != 0) { + final_color = ppu->palette[0x10 | (sp_palette << 2) | sp_pixel]; + } else if(bg_pixel != 0 && sp_pixel == 0) { + final_color = ppu->palette[(bg_palette << 2) | bg_pixel]; } else { - uint32_t base = (state->ppu.control & 0x08) ? 0x1000 : 0x0000; - addr = base | ((tile << 4) + (y_offset & 0x07) + 8); - } - return ppu_read_mem(state, addr); -} - -static void ppu_render_sprites(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - - if(!(ppu->mask & 0x10)) { - return; - } - - uint32_t x = ppu->dot - 1; - uint32_t y = ppu->scanline; - uint8_t bg_pixel = ppu->pixels[y * 256 + x] & 0x3f; - - for(uint32_t i = 0; i < ppu->sprite_count; i++) { - uint8_t *spr = &ppu->secondary_oam[i * 4]; - - uint8_t sy = spr[0]; - uint8_t tile = spr[1]; - 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 offset_y = y - sy; - - if(attr & 0x80) { - offset_y = height - 1 - offset_y; + if(sp_zero && ppu->sprite_zero_hit_possible && x < 255) { + ppu->reg_status |= 0x40; } - - uint8_t shift = 7 - (x - sx); - if(attr & 0x40) { - shift = x - sx; - } - - 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; - uint8_t spr_pixel = (hi_bit << 1) | lo_bit; - - if(spr_pixel == 0) { - continue; - } - - uint8_t palette = (attr & 3) << 2; - uint8_t color_index = ppu_read_mem(state, 0x3f10 + palette + spr_pixel); - - if(i == 0 && bg_pixel && spr_pixel) { - ppu->sprite_zero_hit = 0x40; - } - - if(!(attr & 0x20) || bg_pixel == 0) { - ppu->pixels[y * 256 + x] = color_index; + if(sp_prio == 0) { + final_color = ppu->palette[0x10 | (sp_palette << 2) | sp_pixel]; + } else { + final_color = ppu->palette[(bg_palette << 2) | bg_pixel]; } } + assert(y*256+x <= 256*240); + ppu->pixels[y * 256 + x] = final_color; } -static void ppu_copy_horizontal_scroll(struct nes_state *state) { - struct ppu_state *ppu = &state->ppu; - 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; - 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; - uint32_t scanline = ppu->scanline; uint32_t dot = ppu->dot; + uint32_t scanline = ppu->scanline; - if(scanline < 240 || scanline == 261) { - if((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336)) { - ppu_shift_background(state); + uint8_t rendering = (ppu->reg_mask & 0x18) != 0; - switch((dot - 1) % 8) { - case 0: - ppu->next_tile = ppu_fetch_name_table(state); - break; - case 2: - ppu->next_attr = ppu_fetch_attribute(state); + if(rendering && scanline < 240 && dot >= 1 && dot <= 256) { + ppu_render_pixel(state); + } + + if(rendering && ((dot >= 2 && dot <= 257) || (dot >= 322 && dot <= 337))) { + ppu_sprite_shift(state); + ppu->bg_shift_pattern_low <<= 1; + ppu->bg_shift_pattern_high <<= 1; + ppu->bg_shift_attrib_low <<= 1; + ppu->bg_shift_attrib_high <<= 1; + } + + if(scanline < 240 || scanline == 261) { + if(rendering && ((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336))) { + switch(dot % 8) { + case 1: { + uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); + ppu->bg_next_tile_id = ppu->ciram[nt_addr & 0x07ff]; break; - case 4: - ppu->next_lo = ppu_fetch_pattern_lo(state, ppu->next_tile); + } + case 3: { + uint32_t attr_addr = 0x23c0 | (ppu->vram_addr & 0x0c00) | ((ppu->vram_addr >> 4) & 0x38) | ((ppu->vram_addr >> 2) & 0x07); + uint8_t attr = ppu->ciram[attr_addr & 0x07ff]; + uint8_t shift = ((ppu->vram_addr >> 4) & 4) | (ppu->vram_addr & 2); + ppu->bg_next_tile_attrib = (attr >> shift) & 3; break; - case 6: - ppu->next_hi = ppu_fetch_pattern_hi(state, ppu->next_tile); + } + case 5: { + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_lsb = (base + tile * 16 + fine_y) & 0x1fff; + ppu->bg_next_tile_lsb = state->chrrom[addr_lsb]; break; + } case 7: { - uint8_t attr = ppu->next_attr; - 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); + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_msb = (base + tile * 16 + fine_y + 8) & 0x1fff; + ppu->bg_next_tile_msb = state->chrrom[addr_msb]; break; } + case 0: { + ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; + ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; + + uint8_t a = ppu->bg_next_tile_attrib; + ppu->bg_shift_attrib_low = (ppu->bg_shift_attrib_low & 0xff00) | ((a & 1) ? 0xff : 0x00); + ppu->bg_shift_attrib_high = (ppu->bg_shift_attrib_high & 0xff00) | ((a & 2) ? 0xff : 0x00); + + if((ppu->vram_addr & 0x001f) == 31) { + ppu->vram_addr &= ~0x001f; + ppu->vram_addr ^= 0x0400; + } else { + ppu->vram_addr++; } - if(dot % 8 == 0) { - ppu_inc_x(state); + + break; + } } } - if(scanline < 240 && dot >= 1 && dot <= 256) { - ppu_render_pixel(state); - ppu_render_sprites(state); - } + if(rendering) { + if(dot == 256) { + if((ppu->vram_addr & 0x7000) != 0x7000) { + ppu->vram_addr += 0x1000; + } else { + ppu->vram_addr &= ~0x7000; + uint32_t y = (ppu->vram_addr & 0x03e0) >> 5; + if(y == 29) { + y = 0; + ppu->vram_addr ^= 0x0800; + } else if(y == 31) { + y = 0; + } else { + y++; + } + ppu->vram_addr = (ppu->vram_addr & ~0x03e0) | (y << 5); + } + } - if(dot == 256) { - ppu_inc_y(state); - } + if(dot == 257) { + ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); + } - if(dot == 257) { - ppu_copy_horizontal_scroll(state); - if(scanline < 240) { - ppu_evaluate_sprites(state, scanline + 1); + if(scanline == 261 && dot >= 280 && dot <= 304) { + ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); } - } - if(scanline == 261 && dot >= 280) { // && dot <= 304) { - ppu_copy_vertical_scroll(state); + if(dot == 257 && scanline < 240) { + ppu_evaluate_sprites(state); + } + + if(dot == 340 && scanline < 240) { + ppu_fetch_sprite_patterns(state); + } } } if(scanline == 241 && dot == 1) { - ppu->vblank = 0x80; - state->nmi_pending = 1; - ppu->frame_ready = 1; + ppu->reg_status |= 0x80; + if(ppu->reg_ctrl & 0x80) { + state->nmi_pending = 1; + } } if(scanline == 261 && dot == 1) { - ppu->vblank = 0; - ppu->sprite_overflow = 0; - ppu->sprite_zero_hit = 0; + ppu->reg_status &= ~0x80; + ppu->reg_status &= ~0x40; + ppu->sprite_zero_hit_possible = 0; } - ppu->dot++; - if(ppu->dot == 341) { - ppu->dot = 0; - ppu->scanline++; - if(ppu->scanline == 262) { - ppu->scanline = 0; + dot++; + if(dot > 340) { + dot = 0; + scanline++; + if(scanline > 261) { + scanline = 0; + ppu->frame_ready = 1; } } + + ppu->dot = dot; + ppu->scanline = scanline; } static void ppu_dma_4014(struct nes_state *state, uint8_t page) { - uint32_t base = (uint32_t)page << 8; - uint32_t i; + uint32_t base = page << 8; - uint32_t idle = (state->cycle & 1) ? 2 : 1; - while(idle--) { - ppu_tick(state); ppu_tick(state); ppu_tick(state); + // Add 1 or 2 idle cycles depending on current CPU cycle + uint8_t idle_cycles = (state->cycle & 1) ? 1 : 2; + for(uint8_t i = 0; i < idle_cycles; i++) { state->cycle++; + ppu_tick(state); ppu_tick(state); ppu_tick(state); } - for(i = 0; i < 256; i++) { - ppu_tick(state); ppu_tick(state); ppu_tick(state); + for(uint32_t i = 0; i < 256; i++) { + uint32_t addr = base + i; + + // First CPU cycle (read, ticks only) state->cycle++; + ppu_tick(state); ppu_tick(state); ppu_tick(state); - uint8_t val = memory_read_dma(state, base + i); + // Perform read + uint8_t value = memory_read_dma(state, addr); - ppu_tick(state); ppu_tick(state); ppu_tick(state); - state->cycle++; + // Second CPU cycle (write) + ppu_write_2004(state, value); - ppu_write_2004(state, val); + state->cycle++; + ppu_tick(state); ppu_tick(state); ppu_tick(state); } } \ No newline at end of file -- cgit v1.2.3