summaryrefslogtreecommitdiff
path: root/apu.c
diff options
context:
space:
mode:
authorPeter Fors <peter.fors@mindkiller.com>2025-06-04 15:26:11 +0200
committerPeter Fors <peter.fors@mindkiller.com>2025-06-04 15:26:11 +0200
commit6dd73982c514445c4d2a4787c37666d0812a3dad (patch)
tree78f4e43890a9f7aeb6460066bcfe117e1f96cdd8 /apu.c
parentf02853edb8a624fbe85ea322eaba9e643756d44e (diff)
added scaffolding for apu
Diffstat (limited to 'apu.c')
-rw-r--r--apu.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/apu.c b/apu.c
new file mode 100644
index 0000000..0c16123
--- /dev/null
+++ b/apu.c
@@ -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);
+ }
+ }
+ }
+}