static void ppu_tick(struct nes_state *state); static 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; } // Reading $4015 clears the frame IRQ flag apu->irq_pending = 0; // Only clear CPU IRQ if DMC isn't requesting it if(!(apu->dmc_bytes_remaining > 0 && apu->dmc_irq_enable)) { state->cpu.irq_pending = 0; } return result; } // $4010–$4013, $4015, $4017 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; case 0x4017: { // Frame counter control apu->mode = (val >> 7) & 1; apu->irq_inhibit = (val >> 6) & 1; // If IRQ inhibit flag is set, clear the frame IRQ if(apu->irq_inhibit) { apu->irq_pending = 0; // Only clear CPU IRQ if DMC isn't requesting it if(!(apu->dmc_bytes_remaining > 0 && apu->dmc_irq_enable)) { state->cpu.irq_pending = 0; } } // Reset frame counter (with delay, but we'll approximate immediately for now) apu->frame_cycle = 0; // If 5-step mode, immediately clock half-frame and quarter-frame // (For timing purposes without audio, we can leave this empty) } 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); // } // } // } }