diff options
Diffstat (limited to 'apu.c')
| -rw-r--r-- | apu.c | 164 |
1 files changed, 164 insertions, 0 deletions
@@ -0,0 +1,164 @@ + + +static inline void ppu_tick(struct nes_state *state); +static inline uint8_t memory_read(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; + } + } 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->mode == 0) { + // if(apu->frame_cycle == 7457 || apu->frame_cycle == 14913 || apu->frame_cycle == 22371) { + // // Quarter frame + // } + // if(apu->frame_cycle == 14913 || apu->frame_cycle == 29829) { + // // Half frame + // } + // if(apu->frame_cycle == 29829 && !apu->irq_inhibit) { + // apu->irq_pending = 1; + // } + // if(apu->frame_cycle >= 29830) { + // apu->frame_cycle = 0; + // } + // } else { + // if(apu->frame_cycle == 7457 || apu->frame_cycle == 14913 || apu->frame_cycle == 22371 || apu->frame_cycle == 37281) { + // // Quarter frame + // } + // if(apu->frame_cycle == 14913 || apu->frame_cycle == 29829) { + // // Half frame + // } + // 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; + } + apu->dmc_dma_enabled = 0; + } + } + + for(uint32_t i = 0; i < 4; i++) { + state->cpu.cycles++; + ppu_tick(state); + } + } + } +} |
