| @@ -186,11 +186,13 @@ static inline int f6502_write(nes_Memory* mem, | |||||
| case 0x2000: | case 0x2000: | ||||
| switch (addr & 0x7U) { | switch (addr & 0x7U) { | ||||
| case ppu_reg_ctrl: | case ppu_reg_ctrl: | ||||
| LOGI("PPU: CTRL %02X", val); | |||||
| if ( (mem->ppu.status & ppu_Status_VBlank) && | if ( (mem->ppu.status & ppu_Status_VBlank) && | ||||
| !(mem->ppu.ctrl & ppu_Control_VBlank) && | !(mem->ppu.ctrl & ppu_Control_VBlank) && | ||||
| (val & ppu_Control_VBlank)) { | (val & ppu_Control_VBlank)) { | ||||
| f6502_Core* core = container_of(mem, f6502_Core, memory); | f6502_Core* core = container_of(mem, f6502_Core, memory); | ||||
| /* Trigger NMI if VBlank was set */ | /* Trigger NMI if VBlank was set */ | ||||
| f6502_set_NMI(core, 0); | |||||
| f6502_set_NMI(core, 1); | f6502_set_NMI(core, 1); | ||||
| ret = TRIGGER_INT; | ret = TRIGGER_INT; | ||||
| } | } | ||||
| @@ -232,10 +234,7 @@ static inline int f6502_write(nes_Memory* mem, | |||||
| if (mem->ppu.latch) { | if (mem->ppu.latch) { | ||||
| mem->ppu.t &= 0xFF00U; | mem->ppu.t &= 0xFF00U; | ||||
| mem->ppu.t |= val; | 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); | LOGI("PPU: ADDR %04X", mem->ppu.addr); | ||||
| } else { | } else { | ||||
| mem->ppu.t &= 0x00FFU; | mem->ppu.t &= 0x00FFU; | ||||
| @@ -1715,14 +1714,10 @@ step_done: | |||||
| int f6502_step(f6502_Core* core, int clocks) { | int f6502_step(f6502_Core* core, int clocks) { | ||||
| int clocks_elapsed = 0; | 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)) { | 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_elapsed += f6502_check_interrupts(core); | ||||
| clocks = f6502_do_step(core, clocks - clocks_elapsed); | clocks = f6502_do_step(core, clocks - clocks_elapsed); | ||||
| clocks_elapsed += CLOCKS(clocks); | clocks_elapsed += CLOCKS(clocks); | ||||
| @@ -3,7 +3,7 @@ | |||||
| #include "nes.h" | #include "nes.h" | ||||
| #include "port.h" | #include "port.h" | ||||
| //#define NESE_DEBUG "NES" | |||||
| #define NESE_DEBUG "NES" | |||||
| #include "log.h" | #include "log.h" | ||||
| @@ -57,6 +57,7 @@ static int nes_hsync(nes* sys, void* plat) { | |||||
| } | } | ||||
| } else { | } else { | ||||
| /* | |||||
| nes_ppu_render_line(&sys->ppu, | nes_ppu_render_line(&sys->ppu, | ||||
| &sys->core.memory.ppu); | &sys->core.memory.ppu); | ||||
| nese_line_ready( | nese_line_ready( | ||||
| @@ -67,6 +68,7 @@ static int nes_hsync(nes* sys, void* plat) { | |||||
| nes_ppu_visible_line)), | nes_ppu_visible_line)), | ||||
| sys->ppu.scanline - 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; | 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 nes_loop(nes* sys, void* plat) { | ||||
| int status = 0; | int status = 0; | ||||
| @@ -116,9 +120,17 @@ int nes_loop(nes* sys, void* plat) { | |||||
| bool mapper_hsync = false; | bool mapper_hsync = false; | ||||
| bool vbl_clear = false; | bool vbl_clear = false; | ||||
| bool vbl_set = false; | bool vbl_set = false; | ||||
| bool render = false; | |||||
| while (0 == status) { | 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 && | if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline && | ||||
| (sys->core.memory.ppu.status & ppu_Status_VBlank)) { | (sys->core.memory.ppu.status & ppu_Status_VBlank)) { | ||||
| @@ -126,12 +138,11 @@ int nes_loop(nes* sys, void* plat) { | |||||
| vbl_clear = true; | 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 && | 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 || | if ( vbl_set || | ||||
| @@ -189,6 +200,20 @@ int nes_loop(nes* sys, void* plat) { | |||||
| vbl_set = false; | 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) { | if (vbl_clear && dot >= vblank_clear_dot) { | ||||
| sys->core.memory.ppu.status &= ~ppu_Status_VBlank; | sys->core.memory.ppu.status &= ~ppu_Status_VBlank; | ||||
| vbl_clear = false; | vbl_clear = false; | ||||
| @@ -201,8 +226,8 @@ int nes_loop(nes* sys, void* plat) { | |||||
| } | } | ||||
| // It's possible we returned due to a pending interrupt. | // 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); | status = nes_hsync(sys, plat); | ||||
| if ( nes_ppu_vblank_line == sys->ppu.scanline && | if ( nes_ppu_vblank_line == sys->ppu.scanline && | ||||
| dot >= vblank_set_dot ) { | dot >= vblank_set_dot ) { | ||||
| @@ -115,12 +115,19 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { | |||||
| } else { | } else { | ||||
| int back_bank = nes_PPU_Nametable_Bank_Index + | 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 y_fine = mem->addr >> 12; | ||||
| int x_coarse = mem->addr & 0x1FU; | 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 int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2; | ||||
| const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse; | 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 | // Increment internal registers | ||||
| if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) { | 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); | mem->addr = (mem->addr & ~mask) | (mem->t & mask); | ||||
| int y_scroll = (mem->addr >> 12) | | int y_scroll = (mem->addr >> 12) | | ||||
| ((mem->addr >> 2) & 0xF8U); | ((mem->addr >> 2) & 0xF8U); | ||||
| if (nes_ppu_render_h - 1 == y_scroll) { | if (nes_ppu_render_h - 1 == y_scroll) { | ||||
| y_scroll = 0; | y_scroll = 0; | ||||
| mem->addr ^= 0x800; | |||||
| } else if (0xFFU == y_scroll) { | |||||
| y_scroll = 0; | |||||
| mem->addr ^= 0x0800U; | |||||
| } else { | } else { | ||||
| ++y_scroll; | ++y_scroll; | ||||
| } | } | ||||