summaryrefslogtreecommitdiff
path: root/mknes_apu.c
diff options
context:
space:
mode:
Diffstat (limited to 'mknes_apu.c')
-rw-r--r--mknes_apu.c142
1 files changed, 142 insertions, 0 deletions
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);
+ }
+ }
+ }
+}