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:
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);
+ };
+}