mqdsp.cpp (5403B)
1 #include "mqdsp.h" 2 3 #include "mqhardware.h" 4 5 #if DSP56300_DEBUGGER 6 #include "dsp56kDebugger/debugger.h" 7 #endif 8 9 #include "mc68k/hdi08.h" 10 11 #include "dsp56kEmu/aar.h" 12 13 namespace mqLib 14 { 15 static dsp56k::DefaultMemoryValidator g_memoryValidator; 16 static constexpr dsp56k::TWord g_magicEsaiPacket = 0x654300; 17 18 MqDsp::MqDsp(Hardware& _hardware, mc68k::Hdi08& _hdiUC, const uint32_t _index) 19 : m_hardware(_hardware), m_hdiUC(_hdiUC) 20 , m_index(_index) 21 , m_name({static_cast<char>('A' + _index)}) 22 , m_periphX(nullptr) 23 , m_memory(g_memoryValidator, g_pMemSize, g_xyMemSize, g_bridgedAddr, m_memoryBuffer) 24 , m_dsp(m_memory, &m_periphX, &m_periphNop) 25 , m_boot(m_dsp) 26 { 27 if(!_hardware.isValid()) 28 return; 29 30 m_periphX.getEsaiClock().setExternalClockFrequency(44100 * 768); // measured as being roughly 33,9MHz, this should be exact 31 m_periphX.getEsaiClock().setSamplerate(44100); // verified 32 m_periphX.getEsaiClock().setClockSource(dsp56k::EsaiClock::ClockSource::Cycles); 33 34 auto config = m_dsp.getJit().getConfig(); 35 36 config.aguSupportBitreverse = true; 37 config.linkJitBlocks = true; 38 config.dynamicPeripheralAddressing = false; 39 #ifdef _DEBUG 40 config.debugDynamicPeripheralAddressing = true; 41 #endif 42 config.maxInstructionsPerBlock = 0; // TODO: needs to be 1 if DSP factory tests are run, to be investigated 43 44 // allow dynamic peripheral addressing for code following clr b M_AAR3,r2 45 enableDynamicPeripheralAddressing(config, m_dsp, 0x62f41b, dsp56k::M_AAR3, 16); 46 47 m_dsp.getJit().setConfig(config); 48 49 // fill P memory with something that reminds us if we jump to garbage 50 for(dsp56k::TWord i=0; i<m_memory.sizeP(); ++i) 51 { 52 m_memory.set(dsp56k::MemArea_P, i, 0x000200); // debug instruction 53 m_dsp.getJit().notifyProgramMemWrite(i); 54 } 55 56 getPeriph().disableTimers(true); // only used to test DSP load, we report 0 all the time for now 57 58 m_periphX.getEsai().writeEmptyAudioIn(8); 59 60 hdi08().setRXRateLimit(0); 61 hdi08().setTransmitDataAlwaysEmpty(false); 62 63 m_hdiUC.setRxEmptyCallback([&](const bool needMoreData) 64 { 65 onUCRxEmpty(needMoreData); 66 }); 67 m_hdiUC.setWriteTxCallback([&](const uint32_t _word) 68 { 69 if(m_boot.hdiWriteTX(_word)) 70 onDspBootFinished(); 71 }); 72 m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq) 73 { 74 hdiSendIrqToDSP(_irq); 75 }); 76 m_hdiUC.setReadIsrCallback([&](const uint8_t _isr) 77 { 78 return hdiUcReadIsr(_isr); 79 }); 80 } 81 82 void MqDsp::exec() 83 { 84 m_thread->join(); 85 m_thread.reset(); 86 87 m_hdiUC.setRxEmptyCallback({}); 88 m_dsp.exec(); 89 } 90 91 void MqDsp::dumpPMem(const std::string& _filename) 92 { 93 m_memory.saveAssembly((_filename + ".asm").c_str(), 0, g_pMemSize, true, false, &m_periphX); 94 } 95 96 void MqDsp::dumpXYMem(const std::string& _filename) const 97 { 98 m_memory.save((_filename + "_X.txt").c_str(), dsp56k::MemArea_X); 99 m_memory.save((_filename + "_Y.txt").c_str(), dsp56k::MemArea_Y); 100 } 101 102 void MqDsp::transferHostFlagsUc2Dsdp() 103 { 104 const uint32_t hf01 = m_hdiUC.icr() & 0x18; 105 106 if (hf01 != m_hdiHF01) 107 { 108 // LOG('[' << m_name << "] HDI HF01=" << HEXN((hf01>>3),1)); 109 waitDspRxEmpty(); 110 m_hdiHF01 = hf01; 111 hdi08().setPendingHostFlags01(hf01); 112 } 113 } 114 115 void MqDsp::onDspBootFinished() 116 { 117 m_hdiUC.setWriteTxCallback([&](const uint32_t _word) 118 { 119 hdiTransferUCtoDSP(_word); 120 }); 121 122 #if DSP56300_DEBUGGER 123 m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); 124 #else 125 m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); 126 #endif 127 128 m_thread->setLogToStdout(false); 129 } 130 131 void MqDsp::onUCRxEmpty(bool _needMoreData) 132 { 133 hdi08().injectTXInterrupt(); 134 135 if (_needMoreData) 136 { 137 m_hardware.ucYieldLoop([&]() 138 { 139 return dsp().hasPendingInterrupts() || (hdi08().txInterruptEnabled() && !hdi08().hasTX()); 140 }); 141 } 142 143 hdiTransferDSPtoUC(); 144 } 145 146 bool MqDsp::hdiTransferDSPtoUC() 147 { 148 if (m_hdiUC.canReceiveData() && hdi08().hasTX()) 149 { 150 const auto v = hdi08().readTX(); 151 // LOG('[' << m_name << "] HDI dsp2uc=" << HEX(v)); 152 if (v == g_magicEsaiPacket) 153 m_receivedMagicEsaiPacket = true; 154 m_hdiUC.writeRx(v); 155 return true; 156 } 157 return false; 158 } 159 160 void MqDsp::hdiTransferUCtoDSP(dsp56k::TWord _word) 161 { 162 m_haveSentTXtoDSP = true; 163 // LOG('[' << m_name << "] toDSP writeRX=" << HEX(_word)); 164 hdi08().writeRX(&_word, 1); 165 } 166 167 void MqDsp::hdiSendIrqToDSP(uint8_t _irq) 168 { 169 waitDspRxEmpty(); 170 171 if(_irq == 0x92) 172 { 173 // LOG('[' << m_name << "] DSP timeout, waiting..."); 174 // this one is sent by the uc if the DSP taking too long to reset HF2 back to one. Instead of aborting here, we wait a bit longer for the DSP to finish on its own 175 m_hardware.ucYieldLoop([&] 176 { 177 return !bittest(hdi08().readControlRegister(), dsp56k::HDI08::HCR_HF2); 178 }); 179 // LOG('[' << m_name << "] DSP timeout wait done"); 180 } 181 // else 182 // LOG('[' << m_name << "] Inject interrupt" << HEXN(_irq,2)); 183 184 dsp().injectExternalInterrupt(_irq); 185 186 m_hardware.ucYieldLoop([&]() 187 { 188 return dsp().hasPendingInterrupts(); 189 }); 190 191 hdiTransferDSPtoUC(); 192 } 193 194 uint8_t MqDsp::hdiUcReadIsr(uint8_t _isr) 195 { 196 // transfer DSP host flags HF2&3 to uc 197 const auto hf23 = hdi08().readControlRegister() & 0x18; 198 _isr &= ~0x18; 199 _isr |= hf23; 200 return _isr; 201 } 202 203 void MqDsp::waitDspRxEmpty() 204 { 205 m_hardware.ucYieldLoop([&]() 206 { 207 return (hdi08().hasRXData() && hdi08().rxInterruptEnabled()) || dsp().hasPendingInterrupts(); 208 }); 209 // LOG("writeRX wait over"); 210 } 211 212 }