#include "app/emu/audio/apu.h" #include #include #include #include #include #include "app/emu/audio/dsp.h" #include "app/emu/audio/spc700.h" #include "app/emu/cpu/clock.h" #include "app/emu/memory/memory.h" namespace yaze { namespace app { namespace emu { namespace audio { static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0); static const double apuCyclesPerMasterPal = (32040 * 32) / (1364 * 312 * 50.0); static const uint8_t bootRom[0x40] = { 0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78, 0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5, 0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba, 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff}; void Apu::Init() { // Set the clock frequency clock_.SetFrequency(kApuClockSpeed); } void Apu::Reset() { spc700_.Reset(true); dsp_.Reset(); ram.clear(); rom_readable_ = true; dsp_adr_ = 0; cycles_ = 0; memset(in_ports_, 0, sizeof(in_ports_)); memset(out_ports_, 0, sizeof(out_ports_)); for (int i = 0; i < 3; i++) { timer_[i].cycles = 0; timer_[i].divider = 0; timer_[i].target = 0; timer_[i].counter = 0; timer_[i].enabled = false; } } void Apu::Update() { auto cycles_to_run = clock_.GetCycleCount(); for (auto i = 0; i < cycles_to_run; ++i) { // Update the SPC700 uint8_t opcode = spc700_.read(spc700_.PC); spc700_.ExecuteInstructions(opcode); spc700_.PC++; } } void Apu::RunCycles(uint64_t cycles) { uint64_t sync_to = (uint64_t)cycles * (memory_.pal_timing() ? apuCyclesPerMasterPal : apuCyclesPerMaster); while (cycles_ < sync_to) { spc700_.RunOpcode(); } } void Apu::Cycle() { if ((cycles_ & 0x1f) == 0) { // every 32 cycles dsp_.Cycle(); } // handle timers for (int i = 0; i < 3; i++) { if (timer_[i].cycles == 0) { timer_[i].cycles = i == 2 ? 16 : 128; if (timer_[i].enabled) { timer_[i].divider++; if (timer_[i].divider == timer_[i].target) { timer_[i].divider = 0; timer_[i].counter++; timer_[i].counter &= 0xf; } } } timer_[i].cycles--; } cycles_++; } uint8_t Apu::Read(uint16_t adr) { switch (adr) { case 0xf0: case 0xf1: case 0xfa: case 0xfb: case 0xfc: { return 0; } case 0xf2: { return dsp_adr_; } case 0xf3: { return dsp_.Read(dsp_adr_ & 0x7f); } case 0xf4: case 0xf5: case 0xf6: case 0xf7: case 0xf8: case 0xf9: { return in_ports_[adr - 0xf4]; } case 0xfd: case 0xfe: case 0xff: { uint8_t ret = timer_[adr - 0xfd].counter; timer_[adr - 0xfd].counter = 0; return ret; } } if (rom_readable_ && adr >= 0xffc0) { return bootRom[adr - 0xffc0]; } return ram[adr]; } void Apu::Write(uint16_t adr, uint8_t val) { switch (adr) { case 0xf0: { break; // test register } case 0xf1: { for (int i = 0; i < 3; i++) { if (!timer_[i].enabled && (val & (1 << i))) { timer_[i].divider = 0; timer_[i].counter = 0; } timer_[i].enabled = val & (1 << i); } if (val & 0x10) { in_ports_[0] = 0; in_ports_[1] = 0; } if (val & 0x20) { in_ports_[2] = 0; in_ports_[3] = 0; } rom_readable_ = val & 0x80; break; } case 0xf2: { dsp_adr_ = val; break; } case 0xf3: { if (dsp_adr_ < 0x80) dsp_.Write(dsp_adr_, val); break; } case 0xf4: case 0xf5: case 0xf6: case 0xf7: { out_ports_[adr - 0xf4] = val; break; } case 0xf8: case 0xf9: { in_ports_[adr - 0xf4] = val; break; } case 0xfa: case 0xfb: case 0xfc: { timer_[adr - 0xfa].target = val; break; } } ram[adr] = val; } uint8_t Apu::SpcRead(uint16_t adr) { Cycle(); return Read(adr); } void Apu::SpcWrite(uint16_t adr, uint8_t val) { Cycle(); Write(adr, val); } void Apu::SpcIdle(bool waiting) { Cycle(); } } // namespace audio } // namespace emu } // namespace app } // namespace yaze