From 030724a9aea346e4a9843d5842fb28c6d6c4cf1a Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Thu, 9 Oct 2025 22:07:52 +0200 Subject: Rearrangement and refactoring and optimizations and more accuracy --- mknes_apu.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 mknes_apu.c (limited to 'mknes_apu.c') diff --git a/mknes_apu.c b/mknes_apu.c new file mode 100644 index 0000000..98552ed --- /dev/null +++ b/mknes_apu.c @@ -0,0 +1,142 @@ + + +static inline void ppu_tick(struct nes_state *state); +static uint8_t memory_read(struct nes_state *state, uint32_t offset); +static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); + +// DMC frequency table (NTSC) +static const uint16_t dmc_rate_table[16] = { + 428, 380, 340, 320, 286, 254, 226, 214, + 190, 160, 142, 128, 106, 85, 72, 54 +}; + +// $4015 write +static void apu_write4015(struct nes_state *state, uint8_t val) { + struct apu_state *apu = &state->apu; + + if(val & 0x10) { + if(!apu->dmc_dma_enabled || apu->dmc_bytes_remaining == 0) { + apu->dmc_current_addr = 0xC000 + ((uint16_t)apu->dmc_sample_addr << 6); + apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4); + } + apu->dmc_dma_enabled = 1; + } else { + apu->dmc_dma_enabled = 0; + } +} + +// $4015 read +static uint8_t apu_read4015(struct nes_state *state) { + struct apu_state *apu = &state->apu; + uint8_t result = 0; + + if(apu->dmc_bytes_remaining > 0) { + result |= 0x10; + } + if(apu->irq_pending) { + result |= 0x40; + apu->irq_pending = 0; + } + return result; +} + +// $4010–$4013, $4015 write +static void apu_write(struct nes_state *state, uint16_t addr, uint8_t val) { + struct apu_state *apu = &state->apu; + + switch(addr) { + case 0x4010: { + apu->dmc_irq_enable = (val >> 7) & 1; + apu->dmc_loop_flag = (val >> 6) & 1; + apu->dmc_freq_index = val & 0x0F; + } break; + case 0x4011: { + // DAC write ignored + } break; + case 0x4012: { + apu->dmc_sample_addr = val; + } break; + case 0x4013: { + apu->dmc_sample_len = val; + } break; + case 0x4015: { + apu_write4015(state, val); + } break; + } +} + +// APU tick +static inline void apu_tick(struct nes_state *state) { + struct apu_state *apu = &state->apu; + + apu->frame_cycle++; + + if(apu->mode == 0) { + if(apu->frame_cycle == 7457) { + // Quarter frame + } else if(apu->frame_cycle == 14913) { + // Quarter frame + // Half frame + } else if(apu->frame_cycle == 22371) { + // Quarter frame + } else if(apu->frame_cycle == 29829) { + // Half frame + if(!apu->irq_inhibit) { + apu->irq_pending = 1; + state->cpu.irq_pending = 1; + } + } else if(apu->frame_cycle >= 29830) { + apu->frame_cycle = 0; + } + } else { + if(apu->frame_cycle == 7457) { + // Quarter frame + } else if(apu->frame_cycle == 14913) { + // Quarter frame + // Half frame + } else if(apu->frame_cycle == 22371) { + // Quarter frame + } else if(apu->frame_cycle == 29829) { + // Half frame + } else if(apu->frame_cycle == 37281) { + // Quarter frame + } else if(apu->frame_cycle >= 37282) { + apu->frame_cycle = 0; + } + } + + if(apu->dmc_dma_enabled && apu->dmc_bytes_remaining > 0) { + apu->dmc_sample_timer++; + if(apu->dmc_sample_timer >= dmc_rate_table[apu->dmc_freq_index]) { + apu->dmc_sample_timer = 0; + + uint8_t val = memory_read(state, apu->dmc_current_addr); + (void)val; + + apu->dmc_current_addr++; + if(apu->dmc_current_addr == 0x0000) { + apu->dmc_current_addr = 0x8000; + } + + apu->dmc_bytes_remaining--; + + if(apu->dmc_bytes_remaining == 0) { + if(apu->dmc_loop_flag) { + apu->dmc_current_addr = 0xc000 + ((uint16_t)apu->dmc_sample_addr << 6); + apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4); + } else { + if(apu->dmc_irq_enable) { + apu->irq_pending = 1; + state->cpu.irq_pending = 1; + } + apu->dmc_dma_enabled = 0; + } + } + + for(uint32_t i = 0; i < 4; i++) { + state->cpu.cycles++; + ppu_tick(state); + } + } + } +} -- cgit v1.2.3