#pragma once #include "stdafx.h" #include "Snapshotable.h" #include "APU.h" #include "BaseExpansionAudio.h" #include "SSGAudio.h" #include "Console.h" #include "CPU.h" #include #include "ym3438.h" using EPSMSSGAudio = SSGAudio; class EPSMAudio : public EPSMSSGAudio { private: ym3438_t _chip; int16_t _lastOutputs[2]; int16_t _currentOutputs[2]; uint8_t writeValue; uint16_t writeAddr; uint16_t irqATimer; uint16_t irqBTimer; uint16_t irqACurrentTimer; uint16_t irqBCurrentTimer; uint8_t irqATimerEnable; uint8_t irqBTimerEnable; uint8_t irqAHighValue; uint8_t irqALowValue; uint8_t irqBValue; uint8_t irqEnable; uint16_t currentRegister; double _clock; double _clockIRQ; double _cycleCountIRQ = 0; static constexpr uint8_t cycleCount = 24; struct InputEntry { uint8_t addr = 0; uint8_t data = 0; uint8_t cycle = 0; uint8_t wrote = 0; }; static constexpr uint8_t INPUT_BUFFER_SIZE = cycleCount; using InputBuffer = std::array; InputBuffer _inputBuffer; void UpdateOutputLevel() { for (size_t x = 0; x < 2; x++) { _console->GetApu()->AddExpansionAudioDelta(x == 0 ? AudioChannel::EPSM_L : AudioChannel::EPSM_R, _currentOutputs[x] - _lastOutputs[x]); _lastOutputs[x] = _currentOutputs[x]; } } uint8_t GetCurrentCycle() const { return static_cast(std::floor(_clock)) % cycleCount; } void WriteToChip(uint8_t a, uint8_t d) { const auto cycle = GetCurrentCycle(); if (_inputBuffer[cycle].wrote) { std::cout << "EPSM CHIP DOUBLE WRITE" << std::endl; } _inputBuffer[cycle] = { a, d, cycle, true }; } void WriteToChipIRQ(uint16_t addr, uint8_t value) { switch (addr) { case 0x0: case 0x2: currentRegister = value; break; case 0x1: if (irqEnable) { if (currentRegister == 0x24) { //Timer A High 8 bits irqAHighValue = value; } if (currentRegister == 0x25) { //Timer A Low 2 bits irqALowValue = (value & 0x3); } if (currentRegister == 0x26) { //Timer B 8 bits irqBValue = value; } if (currentRegister == 0x27) { //Load+Enable IRQ (0xA = TimerB, 0x5 = TimerA) if ((value & 0x5)) { irqATimer = (uint16_t(irqAHighValue) << 2) | irqALowValue; irqACurrentTimer = 72 * (1024 - irqATimer) * 2; irqATimerEnable = 1; } if ((value & 0xA)) { irqBTimer = 1152 * (256 - irqBValue) * 2; irqBCurrentTimer = irqBTimer; irqBTimerEnable = 1; } if ((value & 0x10)) { //Enable/Reset IRQ _console->GetCpu()->ClearIrqSource(IRQSource::EPSM); irqATimerEnable = 0; } if ((value & 0x20)) { //Enable/Reset IRQ _console->GetCpu()->ClearIrqSource(IRQSource::EPSM); irqBTimerEnable = 0; } } } if ((currentRegister == 0x29)) { //std::cout << std::hex << "value: " << value << std::endl; if ((value & 0x3 && (value & 0x80))) { //if ((value & 0x3 )) { //enable IRQ's //std::cout << "enable IRQ's" << std::endl; irqEnable = 1; } if (!(value & 0x83)) { //enable IRQ's //std::cout << "enable IRQ's" << std::endl; irqEnable = 0; _console->GetCpu()->ClearIrqSource(IRQSource::EPSM); irqATimerEnable = 0; irqBTimerEnable = 0; } } break; case 0x3: /*if (currentRegister == 0x10) { std::cout << "0x10" << std::endl; }*/ break; } } uint32_t getClockFrequency() { return _console->GetSettings()->GetEPSMClockFrequency() / 6; } protected: void StreamState(bool saving) override { EPSMSSGAudio::StreamState(saving); ArrayInfo lastOutputs{ _lastOutputs, 2 }; ArrayInfo currentOutputs{ _currentOutputs, 2 }; ArrayInfo inputBuffer{ &_inputBuffer }; ValueInfo chip{ &_chip }; ValueInfo clock { &_clock }; Stream(lastOutputs, currentOutputs, inputBuffer, chip, clock); } void ClockAudio() override { EPSMSSGAudio::ClockAudio(); _clock += getClockFrequency() / (double)_console->GetCpu()->GetClockRate(_console->GetModel()); _clockIRQ += _console->GetSettings()->GetEPSMClockFrequency() / (double)_console->GetCpu()->GetClockRate(_console->GetModel()); while (_clockIRQ >= _cycleCountIRQ) { _cycleCountIRQ++; if (irqATimerEnable) { irqACurrentTimer--; if (!irqACurrentTimer) { irqACurrentTimer++; _console->GetCpu()->SetIrqSource(IRQSource::EPSM); } } if (irqBTimerEnable) { irqBCurrentTimer--; if (!irqBCurrentTimer) { irqBCurrentTimer++; _console->GetCpu()->SetIrqSource(IRQSource::EPSM); } } } while (_clock >= cycleCount) { for (uint8_t x = 0; x < 2; x++) { _currentOutputs[x] = 0; } for (uint8_t cycle = 0; cycle < cycleCount; cycle++) { _clock--; int16_t samples[4]; OPN2_Clock(&_chip, samples); for (uint8_t x = 0; x < 2; x++) { _currentOutputs[x] += samples[x]; _currentOutputs[x] += samples[x+2]/8; } auto& input = _inputBuffer[cycle]; if(input.wrote) { input.wrote = false; OPN2_Write(&_chip, input.addr, input.data); } } for (uint8_t x = 0; x < 2; x++) { _currentOutputs[x] /= 5; } UpdateOutputLevel(); } } virtual uint32_t GetSSGClockFrequency() override { return EPSMSSGAudio::GetSSGClockFrequency() * (_console->GetSettings()->GetEPSMClockFrequency() / 3579545 ); } public: EPSMAudio(shared_ptr console) : EPSMSSGAudio(console) { memset(_lastOutputs, 0, sizeof(_lastOutputs)); memset(_currentOutputs, 0, sizeof(_currentOutputs)); _inputBuffer = {}; _clock = 0; _clockIRQ = 0; irqEnable = 0; irqATimerEnable = 0; irqBTimerEnable = 0; OPN2_Reset(&_chip); OPN2_SetChipType(0); } void WriteRegister(uint16_t addr, uint8_t value, uint8_t custom = 0, uint8_t epsmA0 = 0, uint8_t epsmA1 = 0) { if (!custom) { switch (addr) { case 0x4016: if ((value & 0x0F) == 0x02) {writeAddr = 0x0;} //A0 = 0, A1 = 0 if ((value & 0x0F) == 0x0A) {writeAddr = 0x1;} //A0 = 1, A1 = 0 if ((value & 0x0F) == 0x06) {writeAddr = 0x2;} //A0 = 0, A1 = 1 if ((value & 0x0F) == 0x0E) {writeAddr = 0x3;} //A0 = 1, A1 = 1 if (value & 0x0E) {writeValue = value;} if ((value & 0x0F) == 0x00) { writeValue = (writeValue & 0xF0) | (value >> 4); const uint8_t a0 = !!(writeAddr & 0x1); const uint8_t a1 = !!(writeAddr & 0x2); if (a0 == 0x0) { writeAddr = 0xC000; } if (a0 == 0x1) { writeAddr = 0xE000; } if (a1 == 0x0) { EPSMSSGAudio::WriteRegister(writeAddr, value); } WriteToChip(a0 | (a1 << 1), writeValue); WriteToChipIRQ(a0 | (a1 << 1), value); } break; case 0x401c: //0xC000 A0 = 0, A1 = 0 case 0x401d: //0xE000 A0 = 1, A1 = 0 case 0x401e: //0xC002 A0 = 0, A1 = 1 case 0x401f: //0xE002 A0 = 1, A1 = 1 const uint8_t a0 = !!(addr & 0x1); //const uint8_t a0 = (addr & 0xF000) == 0xE000; const uint8_t a1 = !!(addr & 0x2); //const uint8_t a1 = !!(addr & 0xF); if (a0 == 0x0) { addr = 0xC000; } if (a0 == 0x1) { addr = 0xE000; } if (a1 == 0x0) { EPSMSSGAudio::WriteRegister(addr, value); } WriteToChip(a0 | (a1 << 1), value); WriteToChipIRQ(a0 | (a1 << 1), value); break; } } else{ if (epsmA0 == 0x0) { addr = 0xC000; } if (epsmA0 == 0x1) { addr = 0xE000; } if (epsmA1 == 0x0) { EPSMSSGAudio::WriteRegister(addr, value); } WriteToChip(epsmA0 | (epsmA1 << 1), value); WriteToChipIRQ(epsmA0 | (epsmA1 << 1), value); } } };