n2xdsp.cpp (5781B)
1 #include "n2xdsp.h" 2 3 #include "n2xhardware.h" 4 5 #include "dsp56kDebugger/debugger.h" 6 7 #include "dsp56kEmu/dspthread.h" 8 9 #include "mc68k/hdi08.h" 10 11 namespace n2x 12 { 13 static constexpr dsp56k::TWord g_xyMemSize = 0x010000; 14 static constexpr dsp56k::TWord g_externalMemAddr = 0x008000; 15 static constexpr dsp56k::TWord g_pMemSize = 0x004000; 16 17 namespace 18 { 19 dsp56k::DefaultMemoryValidator g_memValidator; 20 } 21 DSP::DSP(Hardware& _hw, mc68k::Hdi08& _hdiUc, const uint32_t _index) 22 : m_hardware(_hw) 23 , m_hdiUC(_hdiUc) 24 , m_index(_index) 25 , m_name(_index ? "DSP B" : "DSP A") 26 , m_memory(g_memValidator, g_pMemSize, g_xyMemSize, g_externalMemAddr) 27 , m_dsp(m_memory, &m_periphX, &m_periphNop) 28 , m_haltDSP(m_dsp) 29 , m_boot(m_dsp) 30 { 31 if(!_hw.isValid()) 32 return; 33 34 { 35 auto& clock = m_periphX.getEsaiClock(); 36 auto& esai = m_periphX.getEsai(); 37 38 clock.setExternalClockFrequency(3'333'333); // schematic claims 1 MHz but we measured 10/3 Mhz 39 40 constexpr auto samplerate = g_samplerate; 41 constexpr auto clockMultiplier = 2; 42 43 clock.setSamplerate(samplerate * clockMultiplier); 44 45 clock.setClockSource(dsp56k::EsaiClock::ClockSource::Cycles); 46 47 if(m_index == 0) 48 { 49 // DSP A = chip U2 = left on the schematic 50 // Sends its audio to DSP B at twice the sample rate, it sends four words per frame 51 clock.setEsaiDivider(&esai, 0); 52 } 53 else 54 { 55 // DSP B = chip U3 = right on the schematic 56 // receives audio from DSP A at twice the sample rate 57 // sends its audio to the DACs at regular sample rate 58 clock.setEsaiDivider(&esai, 1, 0); 59 // clock.setEsaiCounter(&esai, -1, 0); 60 } 61 } 62 63 auto config = m_dsp.getJit().getConfig(); 64 65 config.aguSupportBitreverse = true; 66 config.linkJitBlocks = true; 67 config.dynamicPeripheralAddressing = false; 68 #ifdef _DEBUG 69 config.debugDynamicPeripheralAddressing = true; 70 #endif 71 config.maxInstructionsPerBlock = 0; 72 config.support16BitSCMode = true; 73 config.dynamicFastInterrupts = true; 74 75 m_dsp.getJit().setConfig(config); 76 77 // fill P memory with something that reminds us if we jump to garbage 78 for(dsp56k::TWord i=0; i<m_memory.sizeP(); ++i) 79 { 80 m_memory.set(dsp56k::MemArea_P, i, 0x000200); // debug instruction 81 m_dsp.getJit().notifyProgramMemWrite(i); 82 } 83 84 hdi08().setRXRateLimit(0); 85 86 m_periphX.getEsai().writeEmptyAudioIn(2); 87 88 m_hdiUC.setRxEmptyCallback([&](const bool _needMoreData) 89 { 90 onUCRxEmpty(_needMoreData); 91 }); 92 93 m_hdiUC.setWriteTxCallback([this](const uint32_t _word) 94 { 95 if(m_boot.hdiWriteTX(_word)) 96 onDspBootFinished(); 97 }); 98 m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq) 99 { 100 hdiSendIrqToDSP(_irq); 101 }); 102 m_hdiUC.setReadIsrCallback([&](const uint8_t _isr) 103 { 104 return hdiUcReadIsr(_isr); 105 }); 106 m_hdiUC.setInitHdi08Callback([&] 107 { 108 // clear init flag again immediately, code is waiting for it to happen 109 m_hdiUC.icr(m_hdiUC.icr() & 0x7f); 110 m_hdiUC.isr(m_hdiUC.isr() | mc68k::Hdi08::IsrBits::Txde | mc68k::Hdi08::IsrBits::Trdy); 111 }); 112 113 m_irqInterruptDone = dsp().registerInterruptFunc([this] 114 { 115 m_triggerInterruptDone.notify(); 116 }); 117 } 118 119 void DSP::terminate() 120 { 121 for(uint32_t i=0; i<32768; ++i) 122 m_triggerInterruptDone.notify(); 123 124 m_thread->terminate(); 125 } 126 127 void DSP::join() const 128 { 129 m_thread->join(); 130 } 131 132 void DSP::onDspBootFinished() 133 { 134 m_hdiUC.setWriteTxCallback([&](const uint32_t _word) 135 { 136 hdiTransferUCtoDSP(_word); 137 }); 138 139 #if DSP56300_DEBUGGER 140 if(!m_index) 141 m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); 142 else 143 #endif 144 m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); 145 146 m_thread->setLogToStdout(false); 147 } 148 149 void DSP::onUCRxEmpty(const bool _needMoreData) 150 { 151 if(_needMoreData) 152 { 153 hwLib::ScopedResumeDSP rA(m_hardware.getDSPA().getHaltDSP()); 154 hwLib::ScopedResumeDSP rB(m_hardware.getDSPB().getHaltDSP()); 155 156 while(dsp().hasPendingInterrupts()) 157 std::this_thread::yield(); 158 } 159 hdiTransferDSPtoUC(); 160 } 161 162 void DSP::hdiTransferUCtoDSP(const uint32_t _word) 163 { 164 // LOG('[' << m_name << "] toDSP writeRX=" << HEX(_word) << ", ucPC=" << HEX(m_hardware.getUC().getPrevPC())); 165 hdi08().writeRX(&_word, 1); 166 } 167 168 void DSP::hdiSendIrqToDSP(const uint8_t _irq) 169 { 170 if(m_hardware.requestingHaltDSPs() && getHaltDSP().isHalting()) 171 { 172 // this is a very hacky way to execute a DSP interrupt even though the DSP is halted. This case happens if the DSPs run too fast 173 // and are halted by the sync code, but the UC wants to inject an interrupt, which needs to be executed immediately. 174 // In this case, we execute the interrupt without altering the DSP state 175 176 const auto numOps = dsp().getInstructionCounter(); 177 const auto numCycles = dsp().getCycles(); 178 179 const auto pc = dsp().getPC(); 180 dsp().getJit().exec(_irq); 181 dsp().setPC(pc); 182 183 const_cast<uint64_t&>(dsp().getInstructionCounter()) = numOps; 184 const_cast<uint64_t&>(dsp().getCycles()) = numCycles; 185 } 186 else 187 { 188 dsp().injectExternalInterrupt(_irq); 189 dsp().injectExternalInterrupt(m_irqInterruptDone); 190 191 hwLib::ScopedResumeDSP rA(m_hardware.getDSPA().getHaltDSP()); 192 hwLib::ScopedResumeDSP rB(m_hardware.getDSPB().getHaltDSP()); 193 m_triggerInterruptDone.wait(); 194 } 195 196 hdiTransferDSPtoUC(); 197 } 198 199 uint8_t DSP::hdiUcReadIsr(uint8_t _isr) 200 { 201 hdiTransferDSPtoUC(); 202 203 // transfer DSP host flags HF2&3 to uc 204 const auto hf23 = hdi08().readControlRegister() & 0x18; 205 _isr &= ~0x18; 206 _isr |= hf23; 207 // always ready to receive more data 208 _isr |= mc68k::Hdi08::IsrBits::Trdy; 209 return _isr; 210 } 211 212 bool DSP::hdiTransferDSPtoUC() 213 { 214 if (m_hdiUC.canReceiveData() && hdi08().hasTX()) 215 { 216 const auto v = hdi08().readTX(); 217 // LOG('[' << m_name << "] HDI dsp2UC=" << HEX(v)); 218 m_hdiUC.writeRx(v); 219 return true; 220 } 221 return false; 222 } 223 }