diff --git a/src/f6502.c b/src/f6502.c index dfd1440..dc2f087 100644 --- a/src/f6502.c +++ b/src/f6502.c @@ -186,11 +186,13 @@ static inline int f6502_write(nes_Memory* mem, case 0x2000: switch (addr & 0x7U) { case ppu_reg_ctrl: + LOGI("PPU: CTRL %02X", val); if ( (mem->ppu.status & ppu_Status_VBlank) && !(mem->ppu.ctrl & ppu_Control_VBlank) && (val & ppu_Control_VBlank)) { f6502_Core* core = container_of(mem, f6502_Core, memory); /* Trigger NMI if VBlank was set */ + f6502_set_NMI(core, 0); f6502_set_NMI(core, 1); ret = TRIGGER_INT; } @@ -232,10 +234,7 @@ static inline int f6502_write(nes_Memory* mem, if (mem->ppu.latch) { mem->ppu.t &= 0xFF00U; mem->ppu.t |= val; - mem->ppu.addr = (mem->ppu.t & 0x3FFFU); - // Keep ctrl & t nametable in sync - mem->ppu.ctrl &= ~ppu_Control_Nametable_Mask; - mem->ppu.ctrl |= ((val >> 10) & ppu_Control_Nametable_Mask); + mem->ppu.addr = mem->ppu.t; LOGI("PPU: ADDR %04X", mem->ppu.addr); } else { mem->ppu.t &= 0x00FFU; @@ -1715,14 +1714,10 @@ step_done: int f6502_step(f6502_Core* core, int clocks) { int clocks_elapsed = 0; - // TODO: Why are we processing seven clocks prior to NMI? - // This passes the interrupt test with 6 steps -/* + // NMI occurs after next instruction if (f6502_nmi_ready(core)) { - clocks_elapsed += f6502_do_step(core, 7); - clocks -= clocks_elapsed; + clocks_elapsed += f6502_do_step(core, 1); } -*/ clocks_elapsed += f6502_check_interrupts(core); clocks = f6502_do_step(core, clocks - clocks_elapsed); clocks_elapsed += CLOCKS(clocks); diff --git a/src/nes.c b/src/nes.c index ae4337f..19d8a22 100644 --- a/src/nes.c +++ b/src/nes.c @@ -3,7 +3,7 @@ #include "nes.h" #include "port.h" -//#define NESE_DEBUG "NES" +#define NESE_DEBUG "NES" #include "log.h" @@ -57,6 +57,7 @@ static int nes_hsync(nes* sys, void* plat) { } } else { +/* nes_ppu_render_line(&sys->ppu, &sys->core.memory.ppu); nese_line_ready( @@ -67,6 +68,7 @@ static int nes_hsync(nes* sys, void* plat) { nes_ppu_visible_line)), sys->ppu.scanline - nes_ppu_visible_line ); +*/ } } @@ -105,10 +107,12 @@ static int nes_hsync(nes* sys, void* plat) { return status; } -#define vblank_set_dot (1U) -#define mapper_hsync_dot (300U) -#define render_dot (256U) -#define vblank_clear_dot (341U) +#define mapper_hsync_dot (324U) +#define vblank_clear_dot (260U) +#define render_dot (257U) +#define vblank_set_dot (0U) + +#define scanline_dots (nes_ppu_scanline_dots) int nes_loop(nes* sys, void* plat) { int status = 0; @@ -116,9 +120,17 @@ int nes_loop(nes* sys, void* plat) { bool mapper_hsync = false; bool vbl_clear = false; bool vbl_set = false; + bool render = false; while (0 == status) { - int target_dot = nes_ppu_scanline_dots; + int target_dot = scanline_dots; + if ( sys->core.memory.mapper.hsync && + sys->ppu.scanline >= nes_ppu_prerender_line && + sys->ppu.scanline < nes_ppu_postrender_line && + dot < mapper_hsync_dot) { + target_dot = mapper_hsync_dot; + mapper_hsync = true; + } if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline && (sys->core.memory.ppu.status & ppu_Status_VBlank)) { @@ -126,12 +138,11 @@ int nes_loop(nes* sys, void* plat) { vbl_clear = true; } - if ( sys->core.memory.mapper.hsync && - sys->ppu.scanline > nes_ppu_prerender_line && + if ( sys->ppu.scanline >= nes_ppu_visible_line && sys->ppu.scanline < nes_ppu_postrender_line && - dot < mapper_hsync_dot) { - target_dot = mapper_hsync_dot; - mapper_hsync = true; + dot < render_dot ) { + target_dot = render_dot; + render = true; } if ( vbl_set || @@ -189,6 +200,20 @@ int nes_loop(nes* sys, void* plat) { vbl_set = false; } + if (render && dot >= render_dot) { + nes_ppu_render_line(&sys->ppu, + &sys->core.memory.ppu); + nese_line_ready( + plat, + sys->ppu.screen_data + + ( nes_ppu_render_w * + ( sys->ppu.scanline - + nes_ppu_visible_line)), + sys->ppu.scanline - nes_ppu_visible_line + ); + render = false; + } + if (vbl_clear && dot >= vblank_clear_dot) { sys->core.memory.ppu.status &= ~ppu_Status_VBlank; vbl_clear = false; @@ -201,8 +226,8 @@ int nes_loop(nes* sys, void* plat) { } // It's possible we returned due to a pending interrupt. - if (dot >= nes_ppu_scanline_dots) { - dot -= nes_ppu_scanline_dots; + if (dot >= scanline_dots) { + dot -= scanline_dots; status = nes_hsync(sys, plat); if ( nes_ppu_vblank_line == sys->ppu.scanline && dot >= vblank_set_dot ) { diff --git a/src/ppu.c b/src/ppu.c index e0902f5..76ff89a 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -115,12 +115,19 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { } else { int back_bank = nes_PPU_Nametable_Bank_Index + - ((mem->addr >> 10) & 3); + ((mem->addr >> 10) & 3); - int y_coarse = (mem->addr >> 5) & 0x1F; + int y_coarse = (mem->addr >> 5) & 0x1FU; int y_fine = mem->addr >> 12; int x_coarse = mem->addr & 0x1FU; + LOGD("Scanline %d N %d X %d Y %d", + ppu->scanline, + back_bank - nes_PPU_Nametable_Bank_Index, + (x_coarse * 8) + mem->x, + (y_coarse * 8) + y_fine + ); + const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2; const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse; @@ -347,16 +354,14 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { // Increment internal registers if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) { - uint16_t mask = 0b10000011111; + const uint16_t mask = 0b10000011111; mem->addr = (mem->addr & ~mask) | (mem->t & mask); int y_scroll = (mem->addr >> 12) | ((mem->addr >> 2) & 0xF8U); if (nes_ppu_render_h - 1 == y_scroll) { y_scroll = 0; - mem->addr ^= 0x800; - } else if (0xFFU == y_scroll) { - y_scroll = 0; + mem->addr ^= 0x0800U; } else { ++y_scroll; }