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 bc1bc3e89b422bbc83b2e7683e568ea339df4866
parent 98f19e565d23ea724fd2ac30e5e7e2f7cc8d08f0
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Wed,  1 May 2024 15:22:45 +0200

add support for lower/higher rom dumps

Diffstat:
Msource/xtLib/CMakeLists.txt | 1+
Msource/xtLib/xt.cpp | 12++----------
Msource/xtLib/xtHardware.cpp | 6++++--
Msource/xtLib/xtHardware.h | 2+-
Msource/xtLib/xtRom.h | 9++++++++-
Asource/xtLib/xtRomLoader.cpp | 223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/xtLib/xtRomLoader.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 291 insertions(+), 14 deletions(-)

diff --git a/source/xtLib/CMakeLists.txt b/source/xtLib/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCES xtMidiTypes.h xtPic.cpp xtPic.h xtRom.cpp xtRom.h + xtRomLoader.cpp xtRomLoader.h xtState.cpp xtState.h xtSysexRemoteControl.cpp xtSysexRemoteControl.h xtUc.cpp xtUc.h diff --git a/source/xtLib/xt.cpp b/source/xtLib/xt.cpp @@ -7,21 +7,13 @@ #include "dsp56kEmu/threadtools.h" #include "xtHardware.h" - -#include "../mc68k/logging.h" +#include "xtRomLoader.h" namespace xt { Xt::Xt() { - // create hardware, will use in-memory ROM if no ROM provided - const auto romFile = synthLib::findROM(g_romSize, g_romSize); - if(romFile.empty()) - throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "Failed to find XT operating system file (.bin)"); - - MCLOG("Boot using ROM " << romFile); - - m_hw.reset(new Hardware(romFile)); + m_hw.reset(new Hardware()); if(!isValid()) return; diff --git a/source/xtLib/xtHardware.cpp b/source/xtLib/xtHardware.cpp @@ -5,11 +5,13 @@ #include <cstring> // memcpy +#include "xtRomLoader.h" + namespace xt { - Hardware::Hardware(const std::string& _romFilename) + Hardware::Hardware() : wLib::Hardware(40000) - , m_rom(_romFilename) + , m_rom(RomLoader::findROM()) , m_uc(m_rom) , m_dsps{DSP(*this, m_uc.getHdi08A().getHdi08(), 0)} , m_midi(m_uc.getQSM()) diff --git a/source/xtLib/xtHardware.h b/source/xtLib/xtHardware.h @@ -20,7 +20,7 @@ namespace xt static constexpr uint32_t g_dspCount = 1; public: - explicit Hardware(const std::string& _romFilename); + explicit Hardware(); virtual ~Hardware(); void process(); diff --git a/source/xtLib/xtRom.h b/source/xtLib/xtRom.h @@ -1,5 +1,7 @@ #pragma once +#include <utility> + #include "xtTypes.h" #include "../wLib/wRom.h" @@ -11,7 +13,7 @@ namespace xt public: static constexpr uint32_t Size = g_romSize; - Rom(const std::string& _filename) : ROM(_filename, Size) + Rom(std::vector<uint8_t> _data) : ROM({}, Size, std::move(_data)) { } @@ -19,5 +21,10 @@ namespace xt { return Size; } + + static Rom invalid() + { + return {{}}; + } }; } diff --git a/source/xtLib/xtRomLoader.cpp b/source/xtLib/xtRomLoader.cpp @@ -0,0 +1,223 @@ +#include "xtRomLoader.h" + +#include <cassert> +#include <map> +#include <algorithm> +#include <cstring> + +#include "../synthLib/os.h" +#include "../wLib/wRom.h" + +namespace xt +{ + constexpr uint32_t g_1Kb = 1024; + + constexpr uint32_t g_romSizeFull = 256 * g_1Kb; + + constexpr uint32_t g_romSizeHalf = 128 * g_1Kb; + + constexpr uint32_t g_midiSizeMin = 166 * g_1Kb; + constexpr uint32_t g_midiSizeMax = 171 * g_1Kb; + + Rom RomLoader::findROM() + { + std::vector<File> allFiles; + + { + // full roms are 256k in size + auto filesFull = findFiles(".bin", g_romSizeFull, g_romSizeFull); + + for (auto& file : filesFull) + { + if(detectFileType(file) && detectVersion(file)) + allFiles.push_back(std::move(file)); + } + } + + { + // half roms are 128k in size, we need exactly two files to combine them + auto filesHalf = findFiles(".bin", g_romSizeHalf, g_romSizeHalf); + + for(uint32_t i=0; i<static_cast<uint32_t>(filesHalf.size());) + { + auto& file = filesHalf[i]; + if(!detectFileType(file) || (file.type != FileType::HalfRomA && file.type != FileType::HalfRomB)) + filesHalf.erase(filesHalf.begin() + i); + else + ++i; + } + + if(filesHalf.size() == 2) + { + File& a = filesHalf.front(); + File& b = filesHalf.back(); + + if(a.type == FileType::HalfRomB && b.type == FileType::HalfRomA) + std::swap(a,b); + + if(a.type == FileType::HalfRomA && b.type == FileType::HalfRomB) + { + File result; + result.data.reserve(g_romSizeFull); + + for(size_t i=0; i<g_romSizeHalf; ++i) + { + result.data.push_back(a.data[i]); + result.data.push_back(b.data[i]); + } + + assert(result.data[0] == 0xc0 && result.data[1] == 0xde); + + result.name = a.name + '_' + b.name; + result.type = FileType::FullRom; + + if(detectVersion(result)) + allFiles.push_back(std::move(result)); + } + } + } + + { + // midi file OS update contain about half of the rom, wavetables are missing so an OS update cannot + // be used on its own, but it can be used to upgrade an older rom by replacing the upper half with + // the content of the midi OS update + auto filesMidi = findFiles(".mid", g_midiSizeMin, g_midiSizeMax); + + for (auto& file : filesMidi) + { + std::vector<uint8_t> data; + if(!wLib::ROM::loadFromMidiData(data, file.data)) + continue; + if(!removeBootloader(data)) + continue; + file.data = data; + if(detectFileType(file) && detectVersion(file)) + allFiles.emplace_back(std::move(file)); + } + } + + if(allFiles.empty()) + return Rom::invalid(); + + std::map<FileType, std::vector<File>> romsByType; + for (auto& file : allFiles) + { + romsByType[file.type].push_back(file); + } + + auto& fullRoms = romsByType[FileType::FullRom]; + + if(fullRoms.empty()) + return Rom::invalid(); + + File best; + + // use the highest version that we have + for (auto& fullRom : fullRoms) + { + if(fullRom.version > best.version) + best = std::move(fullRom); + } + + // apply OS update if we have any and the version of that upgrade is higher + auto& midiUpgrades = romsByType[FileType::MidiUpdate]; + + File bestMidi; + for (auto& midi : midiUpgrades) + { + if(midi.version > bestMidi.version && midi.version > best.version) + bestMidi = std::move(midi); + } + + if(bestMidi.version) + { + assert(bestMidi.data.size() <= best.data.size()); + ::memcpy(best.data.data(), bestMidi.data.data(), bestMidi.data.size()); + best.version = bestMidi.version; + best.name += "_upgraded_" + bestMidi.name; + } + + return {best.data}; + } + + std::vector<RomLoader::File> RomLoader::findFiles(const std::string& _extension, const size_t _sizeMin, const size_t _sizeMax) + { + const auto fileNames = synthLib::RomLoader::findFiles(_extension, _sizeMin, _sizeMax); + + std::vector<File> files; + + for (const auto& name : fileNames) + { + File f; + if(!synthLib::readFile(f.data, name)) + continue; + + f.name = synthLib::getFilenameWithoutPath(name); + files.emplace_back(std::move(f)); + } + return files; + } + + bool RomLoader::detectFileType(File& _file) + { + const auto& data = _file.data; + + switch (data.size()) + { + case g_romSizeFull: + // full rom starts with C0DE + if(data[0] != 0xc0 || data[1] != 0xde) + return false; + _file.type = FileType::FullRom; + return true; + case g_romSizeHalf: + // rom half starts with either C0 or DE + if(data[0] == 0xc0 && data[1] == 0x00) + _file.type = FileType::HalfRomA; + else if(data[0] == 0xde && data[1] == 0x00) + _file.type = FileType::HalfRomB; + else + return false; + return true; + default: + // OS update starts with C0DE too + if(data[0] != 0xc0 || data[1] != 0xde) + return false; + _file.type = FileType::MidiUpdate; + return true; + } + } + + bool RomLoader::removeBootloader(std::vector<uint8_t>& _data) + { + if(_data.size() < 2) + return false; + + const std::vector<uint8_t> pattern{0xc0, 0xde, 0x00, 0x00}; + + const auto it = std::search(_data.begin(), _data.end(), pattern.begin(), pattern.end()); + + if(it == _data.end()) + return false; + + if(it != _data.begin()) + _data.erase(_data.begin(), it); + + return true; + } + + bool RomLoader::detectVersion(File& _file) + { + _file.version = 0; + + const auto& data = _file.data; + if(data.size() < 0x33) + return false; + if(data[0x30] != '.') + return false; + + _file.version = (data[0x2f] - '0') * 100 + (data[0x31] - '0') * 10 + (data[0x32] - '0'); + + return _file.version > 200; + } +} diff --git a/source/xtLib/xtRomLoader.h b/source/xtLib/xtRomLoader.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../synthLib/romLoader.h" + +#include <cstdint> + +#include "xtRom.h" + +namespace xt +{ + class RomLoader : public synthLib::RomLoader + { + public: + enum class FileType + { + Unknown, + FullRom, + HalfRomA, + HalfRomB, + MidiUpdate, + }; + + struct File + { + FileType type = FileType::Unknown; + std::vector<uint8_t> data; + std::string name; + uint32_t version = 0; + + bool operator < (const File& _f) const + { + if(version < _f.version) + return true; + if(version > _f.version) + return false; + return name < _f.name; + } + bool operator == (const File& _f) const + { + return version == _f.version && name == _f.name; + } + }; + + static Rom findROM(); + + private: + static std::vector<File> findFiles(const std::string& _extension, size_t _sizeMin, size_t _sizeMax); + static bool detectFileType(File& _file); + static bool removeBootloader(std::vector<uint8_t>& _data); + static bool detectVersion(File& _file); + }; +}