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

dspSingle.cpp (6018B)


      1 #include "dspSingle.h"
      2 
      3 #include "dsp56kEmu/dsp.h"
      4 
      5 #if DSP56300_DEBUGGER
      6 #include "dsp56kDebugger/debugger.h"
      7 #endif
      8 
      9 namespace virusLib
     10 {
     11 	constexpr dsp56k::TWord g_externalMemStart	= 0x020000;
     12 
     13 	DspSingle::DspSingle(uint32_t _memorySize, bool _use56367Peripherals/* = false*/, const char* _name/* = nullptr*/, bool _use56303Peripherals/* = false*/)
     14 		: m_name(_name ? _name : std::string())
     15 		, m_periphX362(_use56367Peripherals ? &m_periphY367 : nullptr)
     16 		, m_hdi08(_use56303Peripherals ? m_periphX303.getHI08() : m_periphX362.getHDI08())
     17 		, m_audio(_use56303Peripherals ? static_cast<dsp56k::Audio&>(m_periphX303.getEssi0()) : static_cast<dsp56k::Audio&>(m_periphX362.getEsai()))
     18 		, m_esxiClock(_use56303Peripherals ? static_cast<dsp56k::EsxiClock&>(m_periphX303.getEssiClock()) : static_cast<dsp56k::EsxiClock&>(m_periphX362.getEsaiClock()))
     19 	{
     20 		const size_t requiredMemSize = 
     21 			dsp56k::alignedSize<dsp56k::DSP>() + 
     22 			dsp56k::alignedSize<dsp56k::Memory>() + 
     23 			dsp56k::Memory::calcMemSize(_memorySize, g_externalMemStart) * sizeof(uint32_t);
     24 
     25 		m_buffer.resize(dsp56k::alignedSize(requiredMemSize));
     26 
     27 		auto* buf = m_buffer.data();
     28 		buf = dsp56k::alignedAddress(buf);
     29 
     30 		auto* bufDSPClass = buf;
     31 		auto* bufMemClass = bufDSPClass + dsp56k::alignedSize<dsp56k::DSP>();
     32 		auto* bufMemSpace = bufMemClass + dsp56k::alignedSize<dsp56k::Memory>();
     33 
     34 		m_memory = new (bufMemClass)dsp56k::Memory(m_memoryValidator, _memorySize, _memorySize, g_externalMemStart, reinterpret_cast<dsp56k::TWord*>(bufMemSpace));
     35 
     36 		dsp56k::IPeripherals* periphX = &m_periphX362;
     37 		dsp56k::IPeripherals* periphY = &m_periphNop;
     38 
     39 		if(_use56303Peripherals)
     40 		{
     41 			periphX = &m_periphX303;
     42 			m_periphX303.getEssiClock().setExternalClockFrequency(4'000'000);	// 4 Mhz
     43 			m_periphX303.getEssiClock().setSamplerate(12000000/256);
     44 			drainESSI1();
     45 		}
     46 
     47 		if (_use56367Peripherals)
     48 			periphY = &m_periphY367;
     49 
     50 		m_dsp = new (buf)dsp56k::DSP(*m_memory, periphX, periphY);
     51 
     52 		m_jit = &m_dsp->getJit();
     53 	}
     54 
     55 	DspSingle::~DspSingle()
     56 	{
     57 		m_dspThread.reset();
     58 
     59 		if(m_dsp)
     60 		{
     61 			m_dsp->~DSP();
     62 			m_memory->~Memory();
     63 
     64 			m_dsp = nullptr;
     65 			m_memory = nullptr;
     66 		}
     67 	}
     68 
     69 	void DspSingle::startDSPThread(const bool _createDebugger)
     70 	{
     71 #if DSP56300_DEBUGGER
     72 		const auto debugger = _createDebugger ? std::make_shared<dsp56kDebugger::Debugger>(*m_dsp) : std::shared_ptr<dsp56kDebugger::Debugger>();
     73 #else
     74 		const auto debugger = std::shared_ptr<dsp56k::DebuggerInterface>();
     75 #endif
     76 
     77 		m_dspThread.reset(new dsp56k::DSPThread(*m_dsp, m_name.empty() ? nullptr : m_name.c_str(), debugger));
     78 
     79 #ifdef ZYNTHIAN
     80 		m_dspThread->setLogToDebug(false);
     81 		m_dspThread->setLogToStdout(false);
     82 #endif
     83 	}
     84 
     85 	template<typename T> void processAudio(DspSingle& _dsp, const synthLib::TAudioInputsT<T>& _inputs, const synthLib::TAudioOutputsT<T>& _outputs, const size_t _samples, uint32_t _latency, std::vector<T>& _dummyIn, std::vector<T>& _dummyOut)
     86 	{
     87 		DspSingle::ensureSize(_dummyIn, _samples<<1);
     88 		DspSingle::ensureSize(_dummyOut, _samples<<1);
     89 
     90 		const T* dIn = _dummyIn.data();
     91 		T* dOut = _dummyOut.data();
     92 
     93 		const T* inputs[] = {_inputs[0] ? _inputs[0] : dIn, _inputs[1] ? _inputs[1] : dIn, dIn, dIn, dIn, dIn, dIn, dIn};
     94 		T* outputs[] = 
     95 			{ _outputs[0] ? _outputs[0] : dOut
     96 			, _outputs[1] ? _outputs[1] : dOut
     97 			, _outputs[2] ? _outputs[2] : dOut
     98 			, _outputs[3] ? _outputs[3] : dOut
     99 			, _outputs[4] ? _outputs[4] : dOut
    100 			, _outputs[5] ? _outputs[5] : dOut
    101 			, dOut, dOut, dOut, dOut, dOut, dOut};
    102 
    103 		_dsp.getAudio().processAudioInterleaved(inputs, outputs, static_cast<uint32_t>(_samples), _latency);
    104 	}
    105 	void DspSingle::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, const size_t _samples, const uint32_t _latency)
    106 	{
    107 		virusLib::processAudio(*this, _inputs, _outputs, _samples, _latency, m_dummyBufferInF, m_dummyBufferOutF);
    108 	}
    109 
    110 	void DspSingle::processAudio(const synthLib::TAudioInputsInt& _inputs, const synthLib::TAudioOutputsInt& _outputs, const size_t _samples, const uint32_t _latency)
    111 	{
    112 		virusLib::processAudio(*this, _inputs, _outputs, _samples, _latency, m_dummyBufferInI, m_dummyBufferOutI);
    113 	}
    114 
    115 	void DspSingle::disableESSI1()
    116 	{
    117 		// Model A uses ESSI1 to send some initialization data to something.
    118 		// Disable it once it has done doing that because otherwise it keeps running
    119 		// and causes a performance hit that we can prevent by disabling it
    120 		auto& essi = m_periphX303.getEssi1();
    121 		auto crb = essi.readCRB();
    122 		crb &= ~((1<<dsp56k::Essi::CRB_RE) | dsp56k::Essi::CRB_TE);
    123 		essi.writeCRB(crb);
    124 	}
    125 
    126 	void DspSingle::drainESSI1()
    127 	{
    128 		auto& essi = m_periphX303.getEssi1();
    129 		auto& ins = essi.getAudioInputs();
    130 		auto& outs = essi.getAudioOutputs();
    131 
    132 		while(!ins.full())
    133 			ins.push_back({});
    134 
    135 		while(!outs.empty())
    136 			outs.pop_front();
    137 	}
    138 
    139 	std::thread DspSingle::boot(const ROMFile::BootRom& _bootRom, const std::vector<dsp56k::TWord>& _commandStream)
    140 	{
    141 		// Copy BootROM to DSP memory
    142 		for (uint32_t i=0; i<_bootRom.data.size(); i++)
    143 		{
    144 			const auto p = _bootRom.offset + i;
    145 			getMemory().set(dsp56k::MemArea_P, p, _bootRom.data[i]);
    146 			getJIT().notifyProgramMemWrite(p);
    147 		}
    148 
    149 //		dsp.memory().saveAssembly((m_file + "_BootROM.asm").c_str(), bootRom.offset, bootRom.size, false, false, &periph);
    150 
    151 		// Write command stream to HDI08 RX
    152 		m_commandStream = _commandStream;
    153 		m_commandStreamReadIndex = 0;
    154 
    155 		while(!getHDI08().dataRXFull() && m_commandStreamReadIndex < m_commandStream.size())
    156 			getHDI08().writeRX(&m_commandStream[m_commandStreamReadIndex++], 1);
    157 
    158 		getHDI08().setReadRxCallback([this]
    159 		{
    160 			if(m_commandStreamReadIndex >= m_commandStream.size())
    161 			{
    162 				getHDI08().setReadRxCallback(nullptr);
    163 				return;
    164 			}
    165 
    166 			getHDI08().writeRX(&m_commandStream[m_commandStreamReadIndex++], 1);
    167 		});
    168 
    169 		std::thread waitForCommandStreamWrite([this]()
    170 		{
    171 			while(m_commandStreamReadIndex < m_commandStream.size())
    172 				std::this_thread::yield();
    173 		});
    174 
    175 		// Initialize the DSP
    176 		getDSP().setPC(_bootRom.offset);
    177 		return waitForCommandStreamWrite;
    178 	}
    179 }