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

commit 2bd2d12fccb3e7774837eef3a106ad5489af84b0
parent fad1023dcb322f47016badf7d7981cccad173a78
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Mon,  5 Aug 2024 19:22:49 +0200

replace DSP boot code with C++

Diffstat:
Msource/hardwareLib/CMakeLists.txt | 2+-
Asource/hardwareLib/dspBootCode.cpp | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msource/hardwareLib/dspBootCode.h | 93++++++++++++++++++++++++-------------------------------------------------------
Msource/mqLib/mqdsp.cpp | 49+++++++++++++++++++------------------------------
Msource/mqLib/mqdsp.h | 6+++++-
Msource/nord/n2x/n2xLib/n2xdsp.cpp | 54++++++++++++++++++++++++------------------------------
Msource/nord/n2x/n2xLib/n2xdsp.h | 5+++++
Msource/xtLib/xtDSP.cpp | 53+++++++++++++++++++++--------------------------------
Msource/xtLib/xtDSP.h | 5++++-
9 files changed, 173 insertions(+), 160 deletions(-)

diff --git a/source/hardwareLib/CMakeLists.txt b/source/hardwareLib/CMakeLists.txt @@ -5,7 +5,7 @@ add_library(hardwareLib STATIC) set(SOURCES am29f.cpp am29f.h - dspBootCode.h + dspBootCode.cpp dspBootCode.h haltDSP.cpp haltDSP.h i2c.cpp i2c.h i2cFlash.cpp i2cFlash.h diff --git a/source/hardwareLib/dspBootCode.cpp b/source/hardwareLib/dspBootCode.cpp @@ -0,0 +1,66 @@ +#include "dspBootCode.h" + +#include "dsp56kEmu/dsp.h" + +namespace hwLib +{ + DspBoot::DspBoot(dsp56k::DSP& _dsp) : m_dsp(_dsp) + { + // H(D)I08 interface needs to be enabled after boot, set it up now already + auto enableHdi08 = [](dsp56k::HDI08& _hdi08) + { + _hdi08.writePortControlRegister(1<<dsp56k::HDI08::HPCR_HEN); + }; + + if(auto* periph56362 = dynamic_cast<dsp56k::Peripherals56362*>(m_dsp.getPeriph(0))) + enableHdi08(periph56362->getHDI08()); + else if(auto* periph56303 = dynamic_cast<dsp56k::Peripherals56303*>(m_dsp.getPeriph(0))) + enableHdi08(periph56303->getHI08()); + } + + bool DspBoot::hdiWriteTX(const dsp56k::TWord& _val) + { + // DSP Boot Code emulation. The following bytes are sent to the DSP via HDI08: + // + // * Length = number of words to be received + // * Address = target address where words will be written to + // * 'Length' number of words + // + // After completion, DSP jumps to 'Address' to execute the code it just received + + switch (m_state) + { + case State::Length: + m_remaining = _val; + m_state = State::Address; + return false; + case State::Address: + m_address = _val; + + LOG("DSP Boot: " << m_remaining << " words, initial PC " << HEX(m_address)); + + // r0 is used as counter, r1 holds the initial PC + m_dsp.regs().r[0].var = static_cast<int32_t>(m_address + m_remaining); + m_dsp.regs().r[1].var = static_cast<int32_t>(m_address); + + m_dsp.regs().sr.var &= ~0xff; // ccr is cleared before jump to r1 + m_dsp.setPC(m_address); // jmp (r1) + + m_state = State::Data; + return false; + case State::Data: + m_dsp.memory().set(dsp56k::MemArea_P, m_address++, _val); + if(0 == --m_remaining) + { + LOG("DSP Boot: finished"); + m_state = State::Finished; + return true; + } + return false; + case State::Finished: + return true; + } + assert(false && "invalid state"); + return false; + } +} diff --git a/source/hardwareLib/dspBootCode.h b/source/hardwareLib/dspBootCode.h @@ -1,74 +1,37 @@ #pragma once -#include <cstdint> +#include "dsp56kEmu/types.h" + +namespace dsp56k +{ + class DSP; +} namespace hwLib { - static constexpr uint32_t g_dspBootCode56362[] = + class DspBoot { - 0x350013, 0x0afa23, 0xff0035, 0x0afa22, - 0xff000e, 0x0afa01, 0xff0022, 0x0afa20, - 0xff005e, 0x61f400, 0xff1000, 0x050c8f, - 0x0afa00, 0xff0021, 0x31a900, 0x0afa01, - 0xff0012, 0x0ad161, 0x04d191, 0x019191, - 0xff0013, 0x044894, 0x019191, 0xff0016, - 0x045094, 0x221100, 0x06c800, 0xff001f, - 0x019191, 0xff001c, 0x009814, 0x000000, - 0x050c5a, 0x050c5d, 0x62f400, 0xd00000, - 0x08f4b8, 0xd00409, 0x060680, 0xff0029, - 0x07da8a, 0x0c1c10, 0x219000, 0x219100, - 0x06c800, 0xff0033, 0x060380, 0xff0031, - 0x07da8a, 0x0c1c10, 0x07588c, 0x000000, - 0x050c46, 0x0afa02, 0xff005c, 0x0afa01, - 0xff003e, 0x0afa00, 0xff0046, 0x08f484, - 0x000038, 0x050c0b, 0x0afa20, 0xff0043, - 0x08f484, 0x005018, 0x050c06, 0x08f484, - 0x000218, 0x050c03, 0x08f484, 0x001c1e, - 0x0a8426, 0x0a8380, 0xff0049, 0x084806, - 0x0a8380, 0xff004c, 0x085006, 0x221100, - 0x06c800, 0xff0059, 0x0a83a0, 0xff0058, - 0x0a8383, 0xff0052, 0x00008c, 0x050c03, - 0x085846, 0x000000, 0x0000b9, 0x0ae180, - 0x0afa01, 0xff005f, 0x050c00, 0x66f41b, - 0xff0090, 0x0503a6, 0x04cfdd, 0x013f03, - 0x013e23, 0x045517, 0x060980, 0xff008b, - 0x07de85, 0x07de84, 0x07de86, 0x300013, - 0x70f400, 0x001600, 0x06d820, 0x4258a2, - 0x320013, 0x72f400, 0x000c00, 0x06da20, - 0x075a86, 0x300013, 0x06d800, 0xff007d, - 0x54e000, 0x200063, 0x200018, 0x5cd800, - 0x200043, 0x200018, 0x320013, 0x06da00, - 0xff0083, 0x07da8c, 0x200053, 0x200018, - 0x022d07, 0x08d73c, 0x0d104a, 0x000005, - 0x013d03, 0x00008c, 0x050c02, 0x017d03, - 0x000200, 0x000086 - }; + public: + enum class State + { + Length, + Address, + Data, + Finished + }; - static constexpr uint32_t g_dspBootCode56303[] = - { - 0x240a13, 0x0afa02, 0xff0028, 0x0afa01, - 0xff0009, 0x0afa00, 0xff0011, 0x0af080, - 0xff0014, 0x0afa20, 0xff000e, 0x08f484, - 0x005018, 0x050c09, 0x08f484, 0x000218, - 0x050c06, 0x08f484, 0x001c1e, 0x050c03, - 0x08f484, 0x000038, 0x0a8426, 0x0a8380, - 0xff0017, 0x084806, 0x0a8380, 0xff001a, - 0x085006, 0x221100, 0x06c800, 0xff0026, - 0x0a83a0, 0xff0026, 0x0a8383, 0xff0020, - 0x00008c, 0x050c02, 0x085846, 0x050c52, - 0x0afa01, 0xff0048, 0x07f41c, 0x000302, - 0x07f41b, 0x00c000, 0x07f41f, 0x000007, - 0x060680, 0xff0038, 0x019382, 0xff0032, - 0x044a98, 0x019381, 0xff0035, 0x04ca95, - 0x0c1c10, 0x219000, 0x219100, 0x06c800, - 0xff0046, 0x060380, 0xff0045, 0x019382, - 0xff003f, 0x044a98, 0x019381, 0xff0042, - 0x04ca95, 0x0c1c10, 0x07588c, 0x050c12, - 0x62f400, 0xd00000, 0x08f4b8, 0xd00409, - 0x060680, 0xff004f, 0x07da8a, 0x0c1c10, - 0x219000, 0x219100, 0x06c800, 0xff0058, - 0x060380, 0xff0057, 0x07da8a, 0x0c1c10, - 0x060380, 0xff0057, 0x07da8a, 0x0c1c10, - 0x07588c, 0x0000b9, 0x0ae180 + explicit DspBoot(dsp56k::DSP& _dsp); + + bool hdiWriteTX(const dsp56k::TWord& _val); + + bool finished() const { return m_state == State::Finished; } + + private: + dsp56k::DSP& m_dsp; + + State m_state = State::Length; + + dsp56k::TWord m_remaining = 0; + dsp56k::TWord m_address = 0; }; } diff --git a/source/mqLib/mqdsp.cpp b/source/mqLib/mqdsp.cpp @@ -8,8 +8,6 @@ #include "mc68k/hdi08.h" -#include "hardwareLib/dspBootCode.h" - #include "dsp56kEmu/aar.h" namespace mqLib @@ -24,6 +22,7 @@ namespace mqLib , m_periphX(nullptr) , m_memory(g_memoryValidator, g_pMemSize, g_xyMemSize, g_bridgedAddr, m_memoryBuffer) , m_dsp(m_memory, &m_periphX, &m_periphNop) + , m_boot(m_dsp) { if(!_hardware.isValid()) return; @@ -54,25 +53,6 @@ namespace mqLib m_dsp.getJit().notifyProgramMemWrite(i); } - // rewrite bootloader to work at address g_bootCodeBase instead of $ff0000 - for(uint32_t i=0; i<std::size(hwLib::g_dspBootCode56362); ++i) - { - uint32_t code = hwLib::g_dspBootCode56362[i]; - if((hwLib::g_dspBootCode56362[i] & 0xffff00) == 0xff0000) - { - code = g_bootCodeBase | (hwLib::g_dspBootCode56362[i] & 0xff); - } - - m_memory.set(dsp56k::MemArea_P, i + g_bootCodeBase, code); - m_dsp.getJit().notifyProgramMemWrite(i + g_bootCodeBase); - } - -// m_memory.saveAssembly("dspBootDisasm.asm", g_bootCodeBase, static_cast<uint32_t>(std::size(g_dspBootCode)), true, true, &m_periphX, nullptr); - - // set OMR pins so that bootcode wants program data via HDI08 RX - m_dsp.setPC(g_bootCodeBase); - m_dsp.regs().omr.var |= OMR_MA | OMR_MB | OMR_MC | OMR_MD; - getPeriph().disableTimers(true); // only used to test DSP load, we report 0 all the time for now m_periphX.getEsai().writeEmptyAudioIn(8); @@ -86,7 +66,8 @@ namespace mqLib }); m_hdiUC.setWriteTxCallback([&](const uint32_t _word) { - hdiTransferUCtoDSP(_word); + if(m_boot.hdiWriteTX(_word)) + onDspBootFinished(); }); m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq) { @@ -96,14 +77,6 @@ namespace mqLib { return hdiUcReadIsr(_isr); }); - -#if DSP56300_DEBUGGER - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); -#else - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); -#endif - - m_thread->setLogToStdout(false); } void MqDsp::exec() @@ -139,6 +112,22 @@ namespace mqLib } } + void MqDsp::onDspBootFinished() + { + m_hdiUC.setWriteTxCallback([&](const uint32_t _word) + { + hdiTransferUCtoDSP(_word); + }); + +#if DSP56300_DEBUGGER + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); +#else + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); +#endif + + m_thread->setLogToStdout(false); + } + void MqDsp::onUCRxEmpty(bool _needMoreData) { hdi08().injectTXInterrupt(); diff --git a/source/mqLib/mqdsp.h b/source/mqLib/mqdsp.h @@ -4,6 +4,8 @@ #include "dsp56kEmu/dspthread.h" #include "dsp56kEmu/peripherals.h" +#include "hardwareLib/dspBootCode.h" + #include "wLib/wDsp.h" namespace mc68k @@ -21,7 +23,6 @@ namespace mqLib static constexpr dsp56k::TWord g_bridgedAddr = 0x080000; // start of external SRAM, mapped to X and Y static constexpr dsp56k::TWord g_xyMemSize = 0x800000; // due to weird AAR mapping we just allocate enough so that everything fits into it static constexpr dsp56k::TWord g_pMemSize = 0x2000; // only $0000 < $1400 for DSP, rest for us - static constexpr dsp56k::TWord g_bootCodeBase = 0x1500; MqDsp(Hardware& _hardware, mc68k::Hdi08& _hdiUC, uint32_t _index); void exec(); @@ -52,6 +53,7 @@ namespace mqLib bool haveSentTXToDSP() const { return m_haveSentTXtoDSP; } bool receivedMagicEsaiPacket() const { return m_receivedMagicEsaiPacket; } + void onDspBootFinished(); private: void onUCRxEmpty(bool _needMoreData); @@ -76,5 +78,7 @@ namespace mqLib std::unique_ptr<dsp56k::DSPThread> m_thread; bool m_receivedMagicEsaiPacket = false; + + hwLib::DspBoot m_boot; }; } diff --git a/source/nord/n2x/n2xLib/n2xdsp.cpp b/source/nord/n2x/n2xLib/n2xdsp.cpp @@ -3,8 +3,9 @@ #include "n2xhardware.h" #include "dsp56kDebugger/debugger.h" + #include "dsp56kEmu/dspthread.h" -#include "hardwareLib/dspBootCode.h" + #include "mc68k/hdi08.h" namespace n2x @@ -12,7 +13,6 @@ namespace n2x static constexpr dsp56k::TWord g_xyMemSize = 0x010000; static constexpr dsp56k::TWord g_externalMemAddr = 0x008000; static constexpr dsp56k::TWord g_pMemSize = 0x004000; - static constexpr dsp56k::TWord g_bootCodeBase = 0x003f00; namespace { @@ -26,6 +26,7 @@ namespace n2x , m_memory(g_memValidator, g_pMemSize, g_xyMemSize, g_externalMemAddr) , m_dsp(m_memory, &m_periphX, &m_periphNop) , m_haltDSP(m_dsp) + , m_boot(m_dsp) { if(!_hw.isValid()) return; @@ -80,23 +81,6 @@ namespace n2x m_dsp.getJit().notifyProgramMemWrite(i); } - // rewrite bootloader to work at address g_bootCodeBase instead of $ff0000 - for(uint32_t i=0; i<std::size(hwLib::g_dspBootCode56362); ++i) - { - uint32_t code = hwLib::g_dspBootCode56362[i]; - if((hwLib::g_dspBootCode56362[i] & 0xffff00) == 0xff0000) - { - code = g_bootCodeBase | (hwLib::g_dspBootCode56362[i] & 0xff); - } - - m_memory.set(dsp56k::MemArea_P, i + g_bootCodeBase, code); - m_dsp.getJit().notifyProgramMemWrite(i + g_bootCodeBase); - } - - // set OMR pins so that bootcode wants program data via HDI08 RX - m_dsp.setPC(g_bootCodeBase); - m_dsp.regs().omr.var |= OMR_MA | OMR_MB | OMR_MC | OMR_MD; - hdi08().setRXRateLimit(0); m_periphX.getEsai().writeEmptyAudioIn(2); @@ -105,9 +89,11 @@ namespace n2x { onUCRxEmpty(_needMoreData); }); - m_hdiUC.setWriteTxCallback([&](const uint32_t _word) + + m_hdiUC.setWriteTxCallback([this](const uint32_t _word) { - hdiTransferUCtoDSP(_word); + if(m_boot.hdiWriteTX(_word)) + onDspBootFinished(); }); m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq) { @@ -124,15 +110,6 @@ namespace n2x m_hdiUC.isr(m_hdiUC.isr() | mc68k::Hdi08::IsrBits::Txde | mc68k::Hdi08::IsrBits::Trdy); }); -#if DSP56300_DEBUGGER - if(!m_index) - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); - else -#endif - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); - - m_thread->setLogToStdout(false); - m_irqInterruptDone = dsp().registerInterruptFunc([this] { m_triggerInterruptDone.notify(); @@ -152,6 +129,23 @@ namespace n2x m_thread->join(); } + void DSP::onDspBootFinished() + { + m_hdiUC.setWriteTxCallback([&](const uint32_t _word) + { + hdiTransferUCtoDSP(_word); + }); + +#if DSP56300_DEBUGGER + if(!m_index) + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); + else +#endif + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); + + m_thread->setLogToStdout(false); + } + void DSP::onUCRxEmpty(const bool _needMoreData) { if(_needMoreData) diff --git a/source/nord/n2x/n2xLib/n2xdsp.h b/source/nord/n2x/n2xLib/n2xdsp.h @@ -7,6 +7,8 @@ #include "dsp56kEmu/dspthread.h" #include "baseLib/semaphore.h" + +#include "hardwareLib/dspBootCode.h" #include "hardwareLib/haltDSP.h" namespace mc68k @@ -43,6 +45,7 @@ namespace n2x void terminate(); void join() const; + void onDspBootFinished(); private: void onUCRxEmpty(bool _needMoreData); @@ -68,5 +71,7 @@ namespace n2x uint32_t m_irqInterruptDone = 0; hwLib::HaltDSP m_haltDSP; + + hwLib::DspBoot m_boot; }; } diff --git a/source/xtLib/xtDSP.cpp b/source/xtLib/xtDSP.cpp @@ -1,6 +1,5 @@ #include "xtDSP.h" -#include "hardwareLib/dspBootCode.h" #include "xtHardware.h" #if DSP56300_DEBUGGER @@ -8,6 +7,7 @@ #endif #include "mc68k/hdi08.h" + #include "dsp56kEmu/aar.h" #include "dsp56kEmu/types.h" @@ -22,6 +22,7 @@ namespace xt , m_periphX() , m_memory(g_memoryValidator, g_pMemSize, g_xyMemSize, g_bridgedAddr, m_memoryBuffer) , m_dsp(m_memory, &m_periphX, &m_periphNop) + , m_boot(m_dsp) { if(!_hardware.isValid()) return; @@ -51,28 +52,7 @@ namespace xt m_dsp.getJit().notifyProgramMemWrite(i); } - const auto& bootCode = hwLib::g_dspBootCode56303; - - // rewrite bootloader to work at address g_bootCodeBase instead of $ff0000 - for(uint32_t i=0; i<std::size(bootCode); ++i) - { - uint32_t code = bootCode[i]; - if((code & 0xffff00) == 0xff0000) - { - code = g_bootCodeBase | (bootCode[i] & 0xff); - } - - m_memory.set(dsp56k::MemArea_P, i + g_bootCodeBase, code); - m_dsp.getJit().notifyProgramMemWrite(i + g_bootCodeBase); - } - -// m_memory.saveAssembly("dspBootDisasm.asm", g_bootCodeBase, static_cast<uint32_t>(std::size(bootCode)), true, true, &m_periphX, nullptr); - - // set OMR pins so that bootcode wants program data via HDI08 RX - m_dsp.setPC(g_bootCodeBase); - m_dsp.regs().omr.var |= OMR_MA | OMR_MB | OMR_MC | OMR_MD; - -// getPeriph().disableTimers(true); // only used to test DSP load, we report 0 all the time for now +// getPeriph().disableTimers(true); m_periphX.getEssi0().writeEmptyAudioIn(8); @@ -85,7 +65,8 @@ namespace xt }); m_hdiUC.setWriteTxCallback([&](const uint32_t _word) { - hdiTransferUCtoDSP(_word); + if(m_boot.hdiWriteTX(_word)) + onDspBooted(); }); m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq) { @@ -95,14 +76,6 @@ namespace xt { return hdiUcReadIsr(_isr); }); - -#if DSP56300_DEBUGGER - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); -#else - m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); -#endif - - m_thread->setLogToStdout(false); } void DSP::exec() @@ -138,6 +111,22 @@ namespace xt } } + void DSP::onDspBooted() + { + m_hdiUC.setWriteTxCallback([&](const uint32_t _word) + { + hdiTransferUCtoDSP(_word); + }); + +#if DSP56300_DEBUGGER + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp))); +#else + m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str())); +#endif + + m_thread->setLogToStdout(false); + } + void DSP::onUCRxEmpty(bool _needMoreData) { hdi08().injectTXInterrupt(); diff --git a/source/xtLib/xtDSP.h b/source/xtLib/xtDSP.h @@ -4,6 +4,8 @@ #include "dsp56kEmu/dspthread.h" #include "dsp56kEmu/peripherals.h" +#include "hardwareLib/dspBootCode.h" + #include "wLib/wDsp.h" namespace mc68k @@ -21,7 +23,6 @@ namespace xt static constexpr dsp56k::TWord g_bridgedAddr = 0x020000; // start of external SRAM, mapped to X and Y static constexpr dsp56k::TWord g_xyMemSize = 0x800000; // due to weird AAR mapping we just allocate enough so that everything fits into it static constexpr dsp56k::TWord g_pMemSize = 0x020000; // DSP code does not use all of it, gives space for our boot code - static constexpr dsp56k::TWord g_bootCodeBase = 0x010000; DSP(Hardware& _hardware, mc68k::Hdi08& _hdiUC, uint32_t _index); void exec(); @@ -50,6 +51,7 @@ namespace xt dsp56k::DSPThread& thread() { return *m_thread; } bool haveSentTXToDSP() const { return m_haveSentTXtoDSP; } + void onDspBooted(); private: void onUCRxEmpty(bool _needMoreData); @@ -72,5 +74,6 @@ namespace xt uint32_t m_hdiHF01 = 0; // uc => DSP std::unique_ptr<dsp56k::DSPThread> m_thread; + hwLib::DspBoot m_boot; }; }