diff --git a/src/apu.c b/src/apu.c index 7ef935a..6ac9a7f 100644 --- a/src/apu.c +++ b/src/apu.c @@ -1,25 +1,35 @@ #include "memory.h" +//#define NESE_DEBUG "APU" +#include "log.h" + void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq) { - // TODO + apu->frame_delay = nes_apu_quarter_frame_ticks; + // TODO: Set LFSR? } -int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { +static int nes_apu_run(nes_APU* apu, nes_Memory* core_mem, + int ticks) { +// int ret = 0; nes_APU_Memory* mem = &core_mem->apu; for (int i = 0; i < core_mem->apu.n_events; ++i) { int reg = core_mem->apu.events[i].reg; int val = core_mem->apu.events[i].val; + LOGD("$40%02X < %02X", reg, val); + switch (reg) { // TODO: 0x00 - 0x13 // TODO: 0x15 case 0x17: // Frame Counter - apu->frame_reg = val & ( apu_Frame_Mode | - apu_Frame_Inhibit); + (void)val; +#if 0 + val &= (apu_Frame_Mode | apu_Frame_Inhibit); + apu->frame_reg = val; apu->frame = 0; - apu->frame_delay = 0; + apu->frame_delay = nes_apu_quarter_frame_ticks; if (val & apu_Frame_Mode) { // TODO @@ -32,9 +42,8 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { if (val & apu_Frame_Inhibit) { mem->status &= ~apu_Status_Frame_Int; } - +#endif break; - } } @@ -42,10 +51,11 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { // Frame advance - apu->frame_delay += nes_apu_hsync_ticks; + apu->frame_delay -= ticks; - if (apu->frame_delay >= nes_apu_quarter_frame_ticks) { - apu->frame_delay -= nes_apu_quarter_frame_ticks; + if (apu->frame_delay <= 0) { + LOGD("Quarter Frame (%d overshoot)", -apu->frame_delay); + apu->frame_delay += nes_apu_quarter_frame_ticks; int end = 0; int quarter_frame = 1; @@ -80,8 +90,10 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { } if (end) { + LOGD("Frame sequence end (frame_reg %02X)", apu->frame_reg); if (0 == apu->frame_reg) { mem->status |= apu_Status_Frame_Int; +// ret = 1; } apu->frame = 0; } else { @@ -92,4 +104,17 @@ int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { // TODO: DMC Interrupt return (mem->status & apu_Status_Frame_Int); +// return ret; +} + +int nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) { + int ticks = nes_apu_hsync_ticks - apu->ticks_handled; + apu->ticks_handled = 0; + return nes_apu_run(apu, core_mem, ticks); +} + +int nes_apu_finish_qframe(nes_APU* apu, nes_Memory* core_mem) { + LOGD("Finishing Quarter Frame (%d remain)", apu->frame_delay); + apu->ticks_handled = apu->frame_delay; + return nes_apu_run(apu, core_mem, apu->frame_delay); } diff --git a/src/apu.h b/src/apu.h index 82d886e..2994a2d 100644 --- a/src/apu.h +++ b/src/apu.h @@ -4,8 +4,9 @@ #include -#define nes_apu_hsync_ticks (4U) -#define nes_apu_quarter_frame_ticks (262U) +#define nes_ppu_to_apu_tick_ratio (4U) +#define nes_apu_hsync_ticks (341U * 4U) +#define nes_apu_quarter_frame_ticks (341U * 262U) typedef struct { @@ -90,6 +91,7 @@ typedef struct { nes_apu_Frame frame_reg; int frame; int frame_delay; + int ticks_handled; uint8_t reg[20]; @@ -100,6 +102,7 @@ struct nes_Memory; void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq); int nes_apu_hsync(nes_APU*, struct nes_Memory*); +int nes_apu_finish_qframe(nes_APU*, struct nes_Memory*); #endif // NESE_APU_H_ diff --git a/src/f6502.c b/src/f6502.c index 90800a4..2f0dfcc 100644 --- a/src/f6502.c +++ b/src/f6502.c @@ -9,12 +9,12 @@ #include #endif -#ifdef F6502_TEST +#include "nes.h" + #include #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type,member));}) -#endif //#define NESE_DEBUG "Core" #include "log.h" @@ -98,8 +98,12 @@ static inline uint8_t f6502_read(nes_Memory* mem, case 0x4000: switch (addr & 0x1FU) { case 0x15: /* APU Status */ - { uint8_t ret = mem->apu.status; + { f6502_Core* core = container_of(mem, f6502_Core, memory); + + uint8_t ret = mem->apu.status; mem->apu.status &= ~apu_Status_Frame_Int; + f6502_set_IRQ(core, 0); + return ret; } @@ -264,12 +268,22 @@ static inline int f6502_write(nes_Memory* mem, case 0x4000: switch (addr & 0x1FU) { case 0x17: // APU Frame - // TODO: Clear interrupt here? - /* - if (val & apu_Frame_apu_Frame_Inhibit) { - f6502_Int_IRQ(core, 0); + { f6502_Core* core = container_of(mem, f6502_Core, memory); + nes* sys = container_of(core, nes, core); + nes_APU* apu = &sys->apu; + +// printf("APU: $%04X < %02X\n", addr, val); + + apu->frame_reg = val; + apu->frame = 0; + apu->frame_delay = nes_apu_quarter_frame_ticks; + + // Clear this ASAP + if (val & apu_Frame_Inhibit) { + mem->apu.status &= ~apu_Status_Frame_Int; + f6502_set_IRQ(core, 0); } - */ + } case 0x00: case 0x01: case 0x02: @@ -437,8 +451,10 @@ static inline bool f6502_irq_ready(const f6502_Core* core) { static inline int f6502_check_interrupts(f6502_Core* core) { if (f6502_nmi_ready(core)) { core->interrupts |= f6502_Int_NMI_Serviced; + LOGI("%lu: NMI", core->clocks); return f6502_interrupt(core, f6502_Vector_NMI); } else if (f6502_irq_ready(core)) { + LOGI("%lu: IRQ", core->clocks); return f6502_interrupt(core, f6502_Vector_IRQ); } return 0; @@ -1704,6 +1720,8 @@ int f6502_step(f6502_Core* core, int clocks) { } */ clocks_elapsed += f6502_check_interrupts(core); - return ( f6502_do_step(core, clocks - clocks_elapsed) + - clocks_elapsed); + clocks_elapsed += f6502_do_step(core, + clocks - clocks_elapsed); + core->clocks += clocks_elapsed; + return clocks_elapsed; } diff --git a/src/f6502.h b/src/f6502.h index d5791f8..803e1c7 100644 --- a/src/f6502.h +++ b/src/f6502.h @@ -41,6 +41,7 @@ typedef enum { } f6502_Interrupt; struct f6502_Core { + uint64_t clocks; f6502_Registers registers; f6502_Interrupt interrupts; nes_Memory memory; diff --git a/src/nes.c b/src/nes.c index 3a018c0..ee5cae9 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" @@ -88,7 +88,6 @@ static int nes_hsync(nes* sys, void* plat) { // Emulate the happy part of the backdrop override quirk int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ? (mem->addr & 0x1FU) : 0; - LOGD("Background: %d", pal_idx); // Don't use the rendering palette (masked transparency) status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx] & 0x3FU); @@ -117,7 +116,6 @@ int nes_loop(nes* sys, void* plat) { bool vbl_clear = false; while (0 == status) { - // TODO: Move to inline PPU function? int target_dot = nes_ppu_scanline_dots; if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline && @@ -148,6 +146,26 @@ int nes_loop(nes* sys, void* plat) { } int dots_to_run = target_dot - dot; + + // Will APU quarter frame hit this line? + // TODO: Only do this when the IRQ is pending? + if (sys->apu.frame_delay <= nes_apu_hsync_ticks) { + int qframe_dot = ( + ( sys->apu.frame_delay + + (nes_ppu_to_apu_tick_ratio - 1)) / + nes_ppu_to_apu_tick_ratio + ); + if (dot >= qframe_dot) { + int irq = nes_apu_finish_qframe( + &sys->apu, &sys->core.memory + ); + // TODO: How might this interfere with MMC3? + f6502_set_IRQ(&sys->core, irq); + } else { + dots_to_run = qframe_dot - dot; + } + } + if (dots_to_run > 0) { int cpu_cycles = (dots_to_run + 2) / 3; dot += 3 * f6502_step(&sys->core, cpu_cycles);