gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

xtDSP.cpp (4740B)


      1 #include "xtDSP.h"
      2 
      3 #include "xtHardware.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 #include "dsp56kEmu/types.h"
     13 
     14 namespace xt
     15 {
     16 	static dsp56k::DefaultMemoryValidator g_memoryValidator;
     17 
     18 	DSP::DSP(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()
     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.getEssiClock().setExternalClockFrequency(10'240'000);	// 10,24 MHz
     31 		m_periphX.getEssiClock().setSamplerate(40000);
     32 		m_periphX.getEssiClock().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 
     43 		// allow dynamic peripheral addressing for code following clr b M_AAR3,r2
     44 		enableDynamicPeripheralAddressing(config, m_dsp, 0x62f41b, dsp56k::M_AAR3, 16);
     45 
     46 		m_dsp.getJit().setConfig(config);
     47 
     48 		// fill P memory with something that reminds us if we jump to garbage
     49 		for(dsp56k::TWord i=0; i<m_memory.sizeP(); ++i)
     50 		{
     51 			m_memory.set(dsp56k::MemArea_P, i, 0x000200);	// debug instruction
     52 			m_dsp.getJit().notifyProgramMemWrite(i);
     53 		}
     54 
     55 //		getPeriph().disableTimers(true);
     56 
     57 		m_periphX.getEssi0().writeEmptyAudioIn(8);
     58 
     59 		hdi08().setRXRateLimit(0);
     60 		hdi08().setTransmitDataAlwaysEmpty(false);
     61 
     62 		m_hdiUC.setRxEmptyCallback([&](const bool needMoreData)
     63 		{
     64 			onUCRxEmpty(needMoreData);
     65 		});
     66 		m_hdiUC.setWriteTxCallback([&](const uint32_t _word)
     67 		{
     68 			if(m_boot.hdiWriteTX(_word))
     69 				onDspBooted();
     70 		});
     71 		m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq)
     72 		{
     73 			hdiSendIrqToDSP(_irq);
     74 		});
     75 		m_hdiUC.setReadIsrCallback([&](const uint8_t _isr)
     76 		{
     77 			return hdiUcReadIsr(_isr);
     78 		});
     79 	}
     80 
     81 	void DSP::exec()
     82 	{
     83 		m_thread->join();
     84 		m_thread.reset();
     85 
     86 		m_hdiUC.setRxEmptyCallback({});
     87 		m_dsp.exec();
     88 	}
     89 
     90 	void DSP::dumpPMem(const std::string& _filename)
     91 	{
     92 		m_memory.saveAssembly((_filename + ".asm").c_str(), 0, g_pMemSize, true, false, &m_periphX);
     93 	}
     94 
     95 	void DSP::dumpXYMem(const std::string& _filename) const
     96 	{
     97 		m_memory.save((_filename + "_X.txt").c_str(), dsp56k::MemArea_X);
     98 		m_memory.save((_filename + "_Y.txt").c_str(), dsp56k::MemArea_Y);
     99 	}
    100 
    101 	void DSP::transferHostFlagsUc2Dsdp()
    102 	{
    103 		const uint32_t hf01 = m_hdiUC.icr() & 0x18;
    104 
    105 		if (hf01 != m_hdiHF01)
    106 		{
    107 //			LOG('[' << m_name << "] HDI HF01=" << HEXN((hf01>>3),1));
    108 			waitDspRxEmpty();
    109 			m_hdiHF01 = hf01;
    110 			hdi08().setPendingHostFlags01(hf01);
    111 		}
    112 	}
    113 
    114 	void DSP::onDspBooted()
    115 	{
    116 		m_hdiUC.setWriteTxCallback([&](const uint32_t _word)
    117 		{
    118 			hdiTransferUCtoDSP(_word);
    119 		});
    120 
    121 #if DSP56300_DEBUGGER
    122 		m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp)));
    123 #else
    124 		m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str()));
    125 #endif
    126 
    127 		m_thread->setLogToStdout(false);
    128 	}
    129 
    130 	void DSP::onUCRxEmpty(bool _needMoreData)
    131 	{
    132 		hdi08().injectTXInterrupt();
    133 
    134 		if (_needMoreData)
    135 		{
    136 			m_hardware.ucYieldLoop([&]()
    137 			{
    138 				return dsp().hasPendingInterrupts() || (hdi08().txInterruptEnabled() && !hdi08().hasTX());
    139 			});
    140 		}
    141 
    142 		hdiTransferDSPtoUC();
    143 	}
    144 
    145 	bool DSP::hdiTransferDSPtoUC()
    146 	{
    147 		if (m_hdiUC.canReceiveData() && hdi08().hasTX())
    148 		{
    149 			const auto v = hdi08().readTX();
    150 //			LOG('[' << m_name << "] HDI dsp2uc=" << HEX(v));
    151 			m_hdiUC.writeRx(v);
    152 			return true;
    153 		}
    154 		return false;
    155 	}
    156 
    157 	void DSP::hdiTransferUCtoDSP(dsp56k::TWord _word)
    158 	{
    159 		m_haveSentTXtoDSP = true;
    160 //		LOG('[' << m_name << "] toDSP writeRX=" << HEX(_word));
    161 
    162 		// this can happen during "Reorganising Memory", DSP must resume to process incoming data
    163 		if (hdi08().dataRXFull())
    164 			m_hardware.resumeDSP();
    165 		hdi08().writeRX(&_word, 1);
    166 	}
    167 
    168 	void DSP::hdiSendIrqToDSP(uint8_t _irq)
    169 	{
    170 		waitDspRxEmpty();
    171 
    172 //		LOG('[' << m_name << "] Inject interrupt" << HEXN(_irq,2));
    173 
    174 		dsp().injectExternalInterrupt(_irq);
    175 
    176 		m_hardware.ucYieldLoop([&]()
    177 		{
    178 			return dsp().hasPendingInterrupts();
    179 		});
    180 
    181 		hdiTransferDSPtoUC();
    182 	}
    183 
    184 	uint8_t DSP::hdiUcReadIsr(uint8_t _isr)
    185 	{
    186 		// transfer DSP host flags HF2&3 to uc
    187 		const auto hf23 = hdi08().readControlRegister() & 0x18;
    188 		_isr &= ~0x18;
    189 		_isr |= hf23;
    190 		return _isr;
    191 	}
    192 
    193 	void DSP::waitDspRxEmpty()
    194 	{
    195 		m_hardware.ucYieldLoop([&]()
    196 		{
    197 			return (hdi08().hasRXData() && hdi08().rxInterruptEnabled()) || dsp().hasPendingInterrupts();
    198 		});
    199 //		LOG("writeRX wait over");
    200 	}
    201 
    202 }