From b4a5fa33b0f1d3d931faecb0ffaa544b89813f6d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 30 May 2021 19:40:29 -0400 Subject: [PATCH 01/42] Improve SDL failed-ROM reporting. Specifically to include all paths tried, and not use the plural for 'crc32' when only one is present. --- .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- OSBindings/SDL/main.cpp | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 02b9d9828..6bf363373 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -106,7 +106,7 @@ + isEnabled = "NO"> requested_roms; - ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments] + std::vector checked_paths; + ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments, &checked_paths] (const std::vector &roms) -> std::vector>> { requested_roms.insert(requested_roms.end(), roms.begin(), roms.end()); @@ -716,13 +717,16 @@ int main(int argc, char *argv[]) { std::vector>> results; for(const auto &rom: roms) { FILE *file = nullptr; + std::vector rom_checked_paths; for(const auto &path: paths) { std::string local_path = path + rom.machine_name + "/" + rom.file_name; file = std::fopen(local_path.c_str(), "rb"); + rom_checked_paths.push_back(local_path); if(file) break; } if(!file) { + std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); results.emplace_back(nullptr); continue; } @@ -737,8 +741,10 @@ int main(int argc, char *argv[]) { if(read == data->size()) results.emplace_back(std::move(data)); - else + else { + std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); results.emplace_back(nullptr); + } } return results; @@ -766,7 +772,7 @@ int main(int argc, char *argv[]) { if(!rom.descriptive_name.empty()) { std::cerr << rom.descriptive_name << "; "; } - std::cerr << "usual crc32s: "; + std::cerr << ((rom.crc32s.size() > 1) ? "usual crc32s: " : "usual crc32: "); bool is_first = true; for(const auto crc32: rom.crc32s) { if(!is_first) std::cerr << ", "; @@ -775,6 +781,14 @@ int main(int argc, char *argv[]) { } std::cerr << ")" << std::endl; } + std::cerr << std::endl << "Tried specifically: "; + bool is_first = true; + for(const auto &path: checked_paths) { + if(!is_first) std::cerr << "; "; + std::cerr << path; + is_first = false; + } + std::cerr << std::endl; break; } From a30eeaab6a5fe8a1c1d2a37183861fdd745a434e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Jun 2021 21:55:59 -0400 Subject: [PATCH 02/42] Starts to introduce a new grammar for ROM requests. They can be optional, and chained together in AND or OR combinations. A central catalogue knows the definitions of all ROMs. --- Machines/AmstradCPC/AmstradCPC.cpp | 56 ++- Machines/Apple/AppleII/AppleII.cpp | 48 +-- Machines/Apple/AppleII/DiskIICard.cpp | 37 +- Machines/Apple/AppleII/DiskIICard.hpp | 3 +- Machines/Apple/AppleII/VideoSwitches.hpp | 30 -- Machines/Apple/AppleIIgs/AppleIIgs.cpp | 32 +- Machines/Apple/Macintosh/Macintosh.cpp | 17 +- Machines/Atari/ST/AtariST.cpp | 12 +- Machines/ColecoVision/ColecoVision.cpp | 12 +- Machines/Commodore/1540/C1540.hpp | 3 +- .../Commodore/1540/Implementation/C1540.cpp | 32 +- .../1540/Implementation/C1540Base.hpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 51 ++- Machines/Electron/Electron.cpp | 43 +- Machines/MSX/MSX.cpp | 35 +- Machines/MasterSystem/MasterSystem.cpp | 18 +- Machines/Oric/Oric.cpp | 62 ++- Machines/ROMMachine.hpp | 32 +- Machines/Sinclair/ZX8081/ZX8081.cpp | 16 +- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 37 +- Machines/Utility/ROMCatalogue.cpp | 379 ++++++++++++++++++ Machines/Utility/ROMCatalogue.hpp | 172 ++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 4 + 23 files changed, 795 insertions(+), 338 deletions(-) create mode 100644 Machines/Utility/ROMCatalogue.cpp create mode 100644 Machines/Utility/ROMCatalogue.hpp diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 32c0647a3..e530c6de5 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -787,45 +787,41 @@ template class ConcreteMachine: ay_.ay().set_port_handler(&key_state_); // construct the list of necessary ROMs - const std::string machine_name = "AmstradCPC"; - std::vector required_roms = { - ROMMachine::ROM(machine_name, "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecd) - }; - std::string model_number; - uint32_t crcs[2]; + bool has_amsdos = false; + ROM::Name firmware, basic; + switch(target.model) { - default: - model_number = "6128"; - has_128k_ = true; - crcs[0] = 0x0219bb74; - crcs[1] = 0xca6af63d; - break; case Analyser::Static::AmstradCPC::Target::Model::CPC464: - model_number = "464"; - has_128k_ = false; - crcs[0] = 0x815752df; - crcs[1] = 0x7d9a3bac; + firmware = ROM::Name::CPC464Firmware; + basic = ROM::Name::CPC464BASIC; break; case Analyser::Static::AmstradCPC::Target::Model::CPC664: - model_number = "664"; - has_128k_ = false; - crcs[0] = 0x3f5a6dc4; - crcs[1] = 0x32fee492; + firmware = ROM::Name::CPC664Firmware; + basic = ROM::Name::CPC664BASIC; + break; + default: + firmware = ROM::Name::CPC6128Firmware; + basic = ROM::Name::CPC6128BASIC; break; } - required_roms.emplace_back(machine_name, "the CPC " + model_number + " firmware", "os" + model_number + ".rom", 16*1024, crcs[0]); - required_roms.emplace_back(machine_name, "the CPC " + model_number + " BASIC ROM", "basic" + model_number + ".rom", 16*1024, crcs[1]); - // fetch and verify the ROMs - const auto roms = rom_fetcher(required_roms); - - for(std::size_t index = 0; index < roms.size(); ++index) { - auto &data = roms[index]; - if(!data) throw ROMMachine::Error::MissingROMs; - roms_[index] = std::move(*data); - roms_[index].resize(16384); + ROM::Request request = ROM::Request(firmware) && ROM::Request(basic); + if(has_amsdos) { + request = request && ROM::Request(ROM::Name::AMSDOS); } + // Fetch and verify the ROMs. + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; + } + + if(has_amsdos) { + roms_[ROMType::AMSDOS] = *roms.find(ROM::Name::AMSDOS); + } + roms_[ROMType::OS] = *roms.find(firmware); + roms_[ROMType::BASIC] = *roms.find(basic); + // Establish default memory map upper_rom_is_paged_ = true; upper_rom_ = ROMType::BASIC; diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 06044b807..081220beb 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -377,49 +377,49 @@ template class ConcreteMachine: // Pick the required ROMs. using Target = Analyser::Static::AppleII::Target; - const std::string machine_name = "AppleII"; - std::vector rom_descriptions; - size_t rom_size = 12*1024; + ROM::Name character, system; + switch(target.model) { default: - rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); - rom_descriptions.emplace_back(machine_name, "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588); + character = ROM::Name::AppleIICharacter; + system = ROM::Name::AppleIIOriginal; break; case Target::Model::IIplus: - rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); - rom_descriptions.emplace_back(machine_name, "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26); + character = ROM::Name::AppleIICharacter; + system = ROM::Name::AppleIIPlus; break; case Target::Model::IIe: - rom_size += 3840; - rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::IIe)); - rom_descriptions.emplace_back(machine_name, "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18d); + character = ROM::Name::AppleIIeCharacter; + system = ROM::Name::AppleIIe; break; case Target::Model::EnhancedIIe: - rom_size += 3840; - rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::EnhancedIIe)); - rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942); + character = ROM::Name::AppleIIEnhancedECharacter; + system = ROM::Name::AppleIIEnhancedE; break; } - const auto roms = rom_fetcher(rom_descriptions); - // Try to install a Disk II card now, before checking the ROM list, - // to make sure that Disk II dependencies have been communicated. - if(target.disk_controller != Target::DiskController::None) { + ROM::Request request = ROM::Request(character) && ROM::Request(system); + + // Add the necessary Disk II requests if appropriate. + const bool has_disk_controller = target.disk_controller != Target::DiskController::None; + const bool is_sixteen_sector = target.disk_controller == Target::DiskController::SixteenSector; + if(has_disk_controller) { // Apple recommended slot 6 for the (first) Disk II. - install_card(6, new Apple::II::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector)); + request = request && DiskIICard::rom_request(is_sixteen_sector); } - // Now, check and move the ROMs. - if(!roms[0] || !roms[1]) { + // Request, validate and install ROMs. + auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - rom_ = std::move(*roms[1]); - if(rom_.size() > rom_size) { - rom_.erase(rom_.begin(), rom_.end() - off_t(rom_size)); + if(has_disk_controller) { + install_card(6, new Apple::II::DiskIICard(roms, is_sixteen_sector)); } - video_.set_character_rom(*roms[0]); + rom_ = std::move(*roms.find(system)); + video_.set_character_rom(*roms.find(character)); // Set up the default memory blocks. On a II or II+ these values will never change. // On a IIe they'll be affected by selection of auxiliary RAM. diff --git a/Machines/Apple/AppleII/DiskIICard.cpp b/Machines/Apple/AppleII/DiskIICard.cpp index 7f0bd7a60..08addefcc 100644 --- a/Machines/Apple/AppleII/DiskIICard.cpp +++ b/Machines/Apple/AppleII/DiskIICard.cpp @@ -10,27 +10,34 @@ using namespace Apple::II; -DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) { - std::vector>> roms; +ROM::Request DiskIICard::rom_request(bool is_16_sector) { if(is_16_sector) { - roms = rom_fetcher({ - {"DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6}, - {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } - }); + return ROM::Request(ROM::Name::DiskIIBoot16Sector) && ROM::Request(ROM::Name::DiskIIStateMachine16Sector); } else { - roms = rom_fetcher({ - {"DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ff}, - {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } -// {"DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620 } - /* TODO: once the DiskII knows how to decode common images of the 13-sector state machine, use that instead of the 16-sector. */ - }); + /* TODO: once the DiskII knows how to decode common images of the 13-sector state machine, use that instead of the 16-sector. */ + return ROM::Request(ROM::Name::DiskIIBoot13Sector) && ROM::Request(ROM::Name::DiskIIStateMachine16Sector); } - if(!roms[0] || !roms[1]) { +} + + +DiskIICard::DiskIICard(ROM::Map &map, bool is_16_sector) : diskii_(2045454) { + std::vector>> roms; + ROM::Map::iterator state_machine, boot; + if(is_16_sector) { + state_machine = map.find(ROM::Name::DiskIIStateMachine16Sector); + boot = map.find(ROM::Name::DiskIIBoot16Sector); + } else { + // TODO: see above re: 13-sector state machine. + state_machine = map.find(ROM::Name::DiskIIStateMachine16Sector); + boot = map.find(ROM::Name::DiskIIBoot13Sector); + } + + if(state_machine == map.end() || boot == map.end()) { throw ROMMachine::Error::MissingROMs; } - boot_ = std::move(*roms[0]); - diskii_.set_state_machine(*roms[1]); + boot_ = std::move(boot->second); + diskii_.set_state_machine(state_machine->second); set_select_constraints(None); diskii_.set_clocking_hint_observer(this); } diff --git a/Machines/Apple/AppleII/DiskIICard.hpp b/Machines/Apple/AppleII/DiskIICard.hpp index 76815f0cf..979d31b07 100644 --- a/Machines/Apple/AppleII/DiskIICard.hpp +++ b/Machines/Apple/AppleII/DiskIICard.hpp @@ -25,7 +25,8 @@ namespace II { class DiskIICard: public Card, public ClockingHint::Observer { public: - DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector); + static ROM::Request rom_request(bool is_16_sector); + DiskIICard(ROM::Map &, bool is_16_sector); void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final; void run_for(Cycles cycles, int stretches) final; diff --git a/Machines/Apple/AppleII/VideoSwitches.hpp b/Machines/Apple/AppleII/VideoSwitches.hpp index faad5df88..7852516f4 100644 --- a/Machines/Apple/AppleII/VideoSwitches.hpp +++ b/Machines/Apple/AppleII/VideoSwitches.hpp @@ -228,36 +228,6 @@ template class VideoSwitches { return external_.annunciator_3; } - enum class CharacterROM { - /// The ROM that shipped with both the Apple II and the II+. - II, - /// The ROM that shipped with the original IIe. - IIe, - /// The ROM that shipped with the Enhanced IIe. - EnhancedIIe, - /// The ROM that shipped with the IIgs. - IIgs - }; - - /// @returns A file-level description of @c rom. - static ROMMachine::ROM rom_description(CharacterROM rom) { - const std::string machine_name = "AppleII"; - switch(rom) { - case CharacterROM::II: - return ROMMachine::ROM(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6); - - case CharacterROM::IIe: - return ROMMachine::ROM(machine_name, "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1); - - default: // To appease GCC. - case CharacterROM::EnhancedIIe: - return ROMMachine::ROM(machine_name, "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014d); - - case CharacterROM::IIgs: - return ROMMachine::ROM(machine_name, "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8); - } - } - /// Set the character ROM for this video output. void set_character_rom(const std::vector &rom) { character_rom_ = rom; diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index 218da7d9e..601db88a9 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -189,31 +189,23 @@ class ConcreteMachine: speaker_.set_input_rate(float(CLOCK_RATE) / float(audio_divider)); using Target = Analyser::Static::AppleIIgs::Target; - std::vector rom_descriptions; - const std::string machine_name = "AppleIIgs"; + ROM::Name system; switch(target.model) { - case Target::Model::ROM00: - /* TODO */ - case Target::Model::ROM01: - rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0); - break; - - case Target::Model::ROM03: - rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29); - break; + case Target::Model::ROM00: system = ROM::Name::AppleIIgsROM00; break; + case Target::Model::ROM01: system = ROM::Name::AppleIIgsROM01; break; + case Target::Model::ROM03: system = ROM::Name::AppleIIgsROM03; break; } - rom_descriptions.push_back(video_->rom_description(Video::Video::CharacterROM::EnhancedIIe)); + constexpr ROM::Name characters = ROM::Name::AppleIIEnhancedECharacter; + constexpr ROM::Name microcontroller = ROM::Name::AppleIIgsMicrocontrollerROM03; - // TODO: pick a different ADB ROM for earlier machine revisions? - rom_descriptions.emplace_back(machine_name, "the Apple IIgs ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0); - - const auto roms = rom_fetcher(rom_descriptions); - if(!roms[0] || !roms[1] || !roms[2]) { + ROM::Request request = ROM::Request(system) && ROM::Request(characters) && ROM::Request(microcontroller); + auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - rom_ = *roms[0]; - video_->set_character_rom(*roms[1]); - adb_glu_->set_microcontroller_rom(*roms[2]); + rom_ = roms.find(system)->second; + video_->set_character_rom(roms.find(characters)->second); + adb_glu_->set_microcontroller_rom(roms.find(microcontroller)->second); // Run only the currently-interesting self test. // rom_[0x36402] = 2; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 1d8ca3b35..c6e22f276 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -96,25 +96,24 @@ template class ConcreteMachin using Model = Analyser::Static::Macintosh::Target::Model; const std::string machine_name = "Macintosh"; uint32_t ram_size, rom_size; - std::vector rom_descriptions; + ROM::Name rom_name; switch(model) { default: case Model::Mac128k: ram_size = 128*1024; rom_size = 64*1024; - rom_descriptions.emplace_back(machine_name, "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28); + rom_name = ROM::Name::Macintosh128k; break; case Model::Mac512k: ram_size = 512*1024; rom_size = 64*1024; - rom_descriptions.emplace_back(machine_name, "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); + rom_name = ROM::Name::Macintosh512k; break; case Model::Mac512ke: case Model::MacPlus: { ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024; rom_size = 128*1024; - const std::initializer_list crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; - rom_descriptions.emplace_back(machine_name, "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s); + rom_name = ROM::Name::MacintoshPlus; } break; } ram_mask_ = ram_size - 1; @@ -123,12 +122,12 @@ template class ConcreteMachin video_.set_ram(reinterpret_cast(ram_.data()), ram_mask_ >> 1); // Grab a copy of the ROM and convert it into big-endian data. - const auto roms = rom_fetcher(rom_descriptions); - if(!roms[0]) { + ROM::Request request(rom_name); + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - roms[0]->resize(rom_size); - Memory::PackBigEndian16(*roms[0], rom_); + Memory::PackBigEndian16(roms.find(rom_name)->second, rom_); // Randomise memory contents. Memory::Fuzz(ram_); diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 1c3f496e0..ee3d31817 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -74,15 +74,13 @@ class ConcreteMachine: video_->set_ram(reinterpret_cast(ram_.data()), ram_.size()); Memory::Fuzz(ram_); - std::vector rom_descriptions = { - {"AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64} -// {"AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43} - }; - const auto roms = rom_fetcher(rom_descriptions); - if(!roms[0]) { + constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100; + ROM::Request request(rom_name); + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - Memory::PackBigEndian16(*roms[0], rom_); + Memory::PackBigEndian16(roms.find(rom_name)->second, rom_); // Set up basic memory map. memory_map_[0] = BusDevice::MostlyRAM; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 1523e3542..c798bc1a7 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -127,15 +127,13 @@ class ConcreteMachine: joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick); - const auto roms = rom_fetcher( - { {"ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3} }); - - if(!roms[0]) { + constexpr ROM::Name rom_name = ROM::Name::ColecoVisionBIOS; + const ROM::Request request(rom_name); + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } - - bios_ = *roms[0]; - bios_.resize(8192); + bios_ = roms.find(rom_name)->second; if(!target.media.cartridges.empty()) { const auto &segment = target.media.cartridges.front()->get_segments().front(); diff --git a/Machines/Commodore/1540/C1540.hpp b/Machines/Commodore/1540/C1540.hpp index 1b747b1ef..b04334423 100644 --- a/Machines/Commodore/1540/C1540.hpp +++ b/Machines/Commodore/1540/C1540.hpp @@ -41,7 +41,8 @@ namespace C1540 { */ class Machine final: public MachineBase { public: - Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher); + static ROM::Request rom_request(Personality personality); + Machine(Personality personality, const ROM::Map &roms); /*! Sets the serial bus to which this drive should attach itself. diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index 6a60aa59f..3252ddf31 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -16,7 +16,14 @@ using namespace Commodore::C1540; -MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : +ROM::Request MachineBase::rom_request(Personality personality) { + switch(personality) { + case Personality::C1540: return ROM::Request(ROM::Name::Commodore1540); + case Personality::C1541: return ROM::Request(ROM::Name::Commodore1541); + } +} + +MachineBase::MachineBase(Personality personality, const ROM::Map &roms) : Storage::Disk::Controller(1000000), m6502_(*this), serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)), @@ -39,28 +46,21 @@ MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher & emplace_drive(1000000, 300, 2); set_drive(1); - std::string device_name; - uint32_t crc = 0; + ROM::Name rom_name; switch(personality) { - case Personality::C1540: - device_name = "1540"; - crc = 0x718d42b1; - break; - case Personality::C1541: - device_name = "1541"; - crc = 0xfb760019; - break; + case Personality::C1540: rom_name = ROM::Name::Commodore1540; break; + case Personality::C1541: rom_name = ROM::Name::Commodore1541; break; } - auto roms = rom_fetcher({ {"Commodore1540", "the " + device_name + " ROM", device_name + ".bin", 16*1024, crc} }); - if(!roms[0]) { + auto rom = roms.find(rom_name); + if(rom == roms.end()) { throw ROMMachine::Error::MissingROMs; } - std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size())); + std::memcpy(rom_, roms.find(rom_name)->second.data(), std::min(sizeof(rom_), roms.find(rom_name)->second.size())); } -Machine::Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : - MachineBase(personality, rom_fetcher) {} +Machine::Machine(Personality personality, const ROM::Map &roms) : + MachineBase(personality, roms) {} void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) { Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus); diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp index 5190bed02..f0f50f8b3 100644 --- a/Machines/Commodore/1540/Implementation/C1540Base.hpp +++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp @@ -127,7 +127,7 @@ class MachineBase: public Storage::Disk::Controller { public: - MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher); + MachineBase(Personality personality, const ROM::Map &roms); // to satisfy CPU::MOS6502::Processor Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 70ec12dab..49432e44d 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -316,53 +316,48 @@ class ConcreteMachine: // Install a joystick. joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_)); - const std::string machine_name = "Vic20"; - std::vector rom_names = { - {machine_name, "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1} - }; + ROM::Request request(ROM::Name::Vic20BASIC); + ROM::Name kernel, character; switch(target.region) { default: - rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6); - rom_names.emplace_back(machine_name, "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4); + character = ROM::Name::Vic20EnglishCharacters; + kernel = ROM::Name::Vic20EnglishPALKernel; break; case Analyser::Static::Commodore::Target::Region::American: - rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6); - rom_names.emplace_back(machine_name, "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174); + character = ROM::Name::Vic20EnglishCharacters; + kernel = ROM::Name::Vic20EnglishNTSCKernel; break; case Analyser::Static::Commodore::Target::Region::Danish: - rom_names.emplace_back(machine_name, "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454); - rom_names.emplace_back(machine_name, "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16); + character = ROM::Name::Vic20DanishCharacters; + kernel = ROM::Name::Vic20DanishKernel; break; case Analyser::Static::Commodore::Target::Region::Japanese: - rom_names.emplace_back(machine_name, "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4b); - rom_names.emplace_back(machine_name, "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7); + character = ROM::Name::Vic20JapaneseCharacters; + kernel = ROM::Name::Vic20JapaneseKernel; break; case Analyser::Static::Commodore::Target::Region::Swedish: - rom_names.emplace_back(machine_name, "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551d); - rom_names.emplace_back(machine_name, "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662); + character = ROM::Name::Vic20SwedishCharacters; + kernel = ROM::Name::Vic20SwedishKernel; break; } - const auto roms = rom_fetcher(rom_names); + if(target.has_c1540) { + request = request && Commodore::C1540::Machine::rom_request(Commodore::C1540::Personality::C1540); + } + request = request && ROM::Request(character) && ROM::Request(kernel); - for(const auto &rom: roms) { - if(!rom) { - throw ROMMachine::Error::MissingROMs; - } + auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; } - basic_rom_ = std::move(*roms[0]); - character_rom_ = std::move(*roms[1]); - kernel_rom_ = std::move(*roms[2]); - - // Characters ROMs should be 4kb. - character_rom_.resize(4096); - // Kernel ROMs and the BASIC ROM should be 8kb. - kernel_rom_.resize(8192); + basic_rom_ = std::move(roms.find(ROM::Name::Vic20BASIC)->second); + character_rom_ = std::move(roms.find(character)->second); + kernel_rom_ = std::move(roms.find(kernel)->second); if(target.has_c1540) { // construct the 1540 - c1540_ = std::make_unique<::Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, rom_fetcher); + c1540_ = std::make_unique<::Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, roms); // attach it to the serial bus c1540_->set_serial_bus(serial_bus_); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index e76566a2d..8b9e09316 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -69,37 +69,25 @@ template class ConcreteMachine: speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider); speaker_.set_high_frequency_cutoff(6000); - const std::string machine_name = "Electron"; - std::vector required_roms = { - {machine_name, "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781}, - {machine_name, "the Electron MOS ROM", "os.rom", 16*1024, 0xbf63fb1f} - }; - const size_t pres_adfs_rom_position = required_roms.size(); + ::ROM::Request request = ::ROM::Request(::ROM::Name::AcornBASICII) && ::ROM::Request(::ROM::Name::AcornElectronMOS100); if(target.has_pres_adfs) { - required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993); - required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e); + request = request && ::ROM::Request(::ROM::Name::PRESADFSSlot1) && ::ROM::Request(::ROM::Name::PRESADFSSlot2); } - const size_t acorn_adfs_rom_position = required_roms.size(); if(target.has_acorn_adfs) { - required_roms.emplace_back(machine_name, "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6); + request = request && ::ROM::Request(::ROM::Name::AcornADFS); } - const size_t dfs_rom_position = required_roms.size(); if(target.has_dfs) { - required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5); + request = request && ::ROM::Request(::ROM::Name::Acorn1770DFS); } - const size_t ap6_rom_position = required_roms.size(); if(target.has_ap6_rom) { - required_roms.emplace_back(machine_name, "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfc); + request = request && ::ROM::Request(::ROM::Name::PRESAdvancedPlus6); } - const auto roms = rom_fetcher(required_roms); - - for(const auto &rom: roms) { - if(!rom) { - throw ROMMachine::Error::MissingROMs; - } + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; } - set_rom(ROM::BASIC, *roms[0], false); - set_rom(ROM::OS, *roms[1], false); + set_rom(ROM::BASIC, roms.find(::ROM::Name::AcornBASICII)->second, false); + set_rom(ROM::OS, roms.find(::ROM::Name::AcornElectronMOS100)->second, false); /* ROM slot mapping applied: @@ -115,19 +103,18 @@ template class ConcreteMachine: plus3_ = std::make_unique(); if(target.has_dfs) { - set_rom(ROM::Slot0, *roms[dfs_rom_position], true); + set_rom(ROM::Slot0, roms.find(::ROM::Name::Acorn1770DFS)->second, true); } if(target.has_pres_adfs) { - set_rom(ROM::Slot4, *roms[pres_adfs_rom_position], true); - set_rom(ROM::Slot5, *roms[pres_adfs_rom_position+1], true); + set_rom(ROM::Slot4, roms.find(::ROM::Name::PRESADFSSlot1)->second, true); + set_rom(ROM::Slot5, roms.find(::ROM::Name::PRESADFSSlot2)->second, true); } if(target.has_acorn_adfs) { - set_rom(ROM::Slot6, *roms[acorn_adfs_rom_position], true); + set_rom(ROM::Slot6, roms.find(::ROM::Name::AcornADFS)->second, true); } } - if(target.has_ap6_rom) { - set_rom(ROM::Slot15, *roms[ap6_rom_position], true); + set_rom(ROM::Slot15, roms.find(::ROM::Name::PRESAdvancedPlus6)->second, true); } if(target.has_sideways_ram) { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 15b2bf9ef..3ea353790 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -169,19 +169,21 @@ class ConcreteMachine: // Install the proper TV standard and select an ideal BIOS name. const std::string machine_name = "MSX"; - std::vector required_roms = { - {machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3} - }; + ROM::Request bios_request = ROM::Request(ROM::Name::MSXGenericBIOS); +// std::vector required_roms = { +// {machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u} +// }; bool is_ntsc = true; uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */ uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */ uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */ + ROM::Name regional_bios_name; // TODO: CRCs below are incomplete, at best. switch(target.region) { case Target::Region::Japan: - required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390); + regional_bios_name = ROM::Name::MSXJapaneseBIOS; vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); is_ntsc = true; @@ -189,7 +191,7 @@ class ConcreteMachine: date_format = 0; break; case Target::Region::USA: - required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0); + regional_bios_name = ROM::Name::MSXAmericanBIOS; vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); is_ntsc = true; @@ -197,7 +199,7 @@ class ConcreteMachine: date_format = 1; break; case Target::Region::Europe: - required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0); + regional_bios_name = ROM::Name::MSXEuropeanBIOS; vdp_->set_tv_standard(TI::TMS::TVStandard::PAL); is_ntsc = false; @@ -205,27 +207,30 @@ class ConcreteMachine: date_format = 2; break; } + bios_request = bios_request || ROM::Request(regional_bios_name); // Fetch the necessary ROMs; try the region-specific ROM first, // but failing that fall back on patching the main one. - size_t disk_index = 0; + ROM::Request request; if(target.has_disk_drive) { - disk_index = required_roms.size(); - required_roms.emplace_back(machine_name, "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61df); + request = ROM::Request(ROM::Name::MSXDOS) && bios_request; + } else { + request = bios_request; } - const auto roms = rom_fetcher(required_roms); - if((!roms[0] && !roms[1]) || (target.has_disk_drive && !roms[2])) { + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } // Figure out which BIOS to use, either a specific one or the generic // one appropriately patched. - if(roms[1]) { - memory_slots_[0].source = std::move(*roms[1]); + const auto regional_bios = roms.find(regional_bios_name); + if(regional_bios != roms.end()) { + memory_slots_[0].source = std::move(regional_bios->second); memory_slots_[0].source.resize(32768); } else { - memory_slots_[0].source = std::move(*roms[0]); + memory_slots_[0].source = std::move(roms.find(ROM::Name::MSXGenericBIOS)->second); memory_slots_[0].source.resize(32768); memory_slots_[0].source[0x2b] = uint8_t( @@ -252,7 +257,7 @@ class ConcreteMachine: // Add a disk cartridge if any disks were supplied. if(target.has_disk_drive) { memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source)); - memory_slots_[2].source = std::move(*roms[disk_index]); + memory_slots_[2].source = std::move(roms.find(ROM::Name::MSXDOS)->second); memory_slots_[2].source.resize(16384); map(2, 0, 0x4000, 0x2000); diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 562f48967..25d07e234 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -141,23 +141,19 @@ class ConcreteMachine: // 0072ed54 = US/European BIOS 1.3 // 48d44a13 = Japanese BIOS 2.1 const bool is_japanese = target.region == Target::Region::Japan; - const auto roms = rom_fetcher( - { {"MasterSystem", - is_japanese ? "the Japanese Master System BIOS" : "the European/US Master System BIOS", - is_japanese ? "japanese-bios.sms" : "bios.sms", - 8*1024, - { is_japanese ? 0x48d44a13u : 0x0072ed54u } - } } - ); - if(!roms[0]) { + const ROM::Name bios_name = is_japanese ? ROM::Name::MasterSystemJapaneseBIOS : ROM::Name::MasterSystemWesternBIOS; + ROM::Request request(bios_name, true); + const auto roms = rom_fetcher(request); + + const auto rom = roms.find(bios_name); + if(rom == roms.end()) { // No BIOS found; attempt to boot as though it has already disabled itself. has_bios_ = false; memory_control_ |= 0x08; std::cerr << "No BIOS found; attempting to start cartridge directly" << std::endl; } else { has_bios_ = true; - roms[0]->resize(8*1024); - memcpy(&bios_, roms[0]->data(), roms[0]->size()); + memcpy(&bios_, rom->second.data(), std::min(sizeof(bios_), rom->second.size())); } page_cartridge(); diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 016c23cc6..8af6b5e58 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -304,73 +304,63 @@ template rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65} }; + ::ROM::Request request = ::ROM::Request(::ROM::Name::OricColourROM, true); + ::ROM::Name basic; switch(target.rom) { - case Analyser::Static::Oric::Target::ROM::BASIC10: - rom_names.emplace_back(machine_name, "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4); - break; - case Analyser::Static::Oric::Target::ROM::BASIC11: - rom_names.emplace_back(machine_name, "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92bef); - break; - case Analyser::Static::Oric::Target::ROM::Pravetz: - rom_names.emplace_back(machine_name, "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502); - break; + case Analyser::Static::Oric::Target::ROM::BASIC10: basic = ::ROM::Name::OricBASIC10; break; + case Analyser::Static::Oric::Target::ROM::BASIC11: basic = ::ROM::Name::OricBASIC11; break; + case Analyser::Static::Oric::Target::ROM::Pravetz: basic = ::ROM::Name::OricPravetzBASIC; break; } - size_t diskii_state_machine_index = 0; + request = request && ::ROM::Request(basic); + switch(disk_interface) { default: break; case DiskInterface::BD500: - rom_names.emplace_back(machine_name, "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34); + request = request && ::ROM::Request(::ROM::Name::OricByteDrive500); break; case DiskInterface::Jasmin: - rom_names.emplace_back(machine_name, "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89); + request = request && ::ROM::Request(::ROM::Name::OricJasmin); break; case DiskInterface::Microdisc: - rom_names.emplace_back(machine_name, "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9c); + request = request && ::ROM::Request(::ROM::Name::OricMicrodisc); break; case DiskInterface::Pravetz: - rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06); - // These ROM details are coupled with those in the DiskIICard. - diskii_state_machine_index = rom_names.size(); - rom_names.push_back({"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 }}); + request = request && ::ROM::Request(::ROM::Name::Oric8DOSBoot) && ::ROM::Request(::ROM::Name::DiskIIStateMachine16Sector); break; } - const auto roms = rom_fetcher(rom_names); - - for(std::size_t index = 0; index < roms.size(); ++index) { - if(!roms[index]) { - throw ROMMachine::Error::MissingROMs; - } + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; } - video_->set_colour_rom(*roms[0]); - rom_ = std::move(*roms[1]); + // The colour ROM is optional; an alternative composite encoding can be used if + // it is absent. + const auto colour_rom = roms.find(::ROM::Name::OricColourROM); + if(colour_rom != roms.end()) { + video_->set_colour_rom(colour_rom->second); + } + rom_ = std::move(roms.find(basic)->second); switch(disk_interface) { default: break; case DiskInterface::BD500: - disk_rom_ = std::move(*roms[2]); - disk_rom_.resize(8192); + disk_rom_ = std::move(roms.find(::ROM::Name::OricByteDrive500)->second); break; case DiskInterface::Jasmin: - disk_rom_ = std::move(*roms[2]); - disk_rom_.resize(2048); + disk_rom_ = std::move(roms.find(::ROM::Name::OricJasmin)->second); break; case DiskInterface::Microdisc: - disk_rom_ = std::move(*roms[2]); - disk_rom_.resize(8192); + disk_rom_ = std::move(roms.find(::ROM::Name::OricMicrodisc)->second); break; case DiskInterface::Pravetz: { - pravetz_rom_ = std::move(*roms[2]); + pravetz_rom_ = std::move(roms.find(::ROM::Name::Oric8DOSBoot)->second); pravetz_rom_.resize(512); - diskii_->set_state_machine(*roms[diskii_state_machine_index]); + diskii_->set_state_machine(roms.find(::ROM::Name::DiskIIStateMachine16Sector)->second); } break; } - rom_.resize(16384); paged_rom_ = rom_.data(); switch(target.disk_interface) { diff --git a/Machines/ROMMachine.hpp b/Machines/ROMMachine.hpp index b1b9cb818..f28d89268 100644 --- a/Machines/ROMMachine.hpp +++ b/Machines/ROMMachine.hpp @@ -10,38 +10,16 @@ #define ROMMachine_hpp #include +#include #include +#include #include #include +#include "Utility/ROMCatalogue.hpp" + namespace ROMMachine { -/*! - Describes a ROM image; this term is used in this emulator strictly in the sense of firmware — - system software that is an inherent part of a machine. -*/ -struct ROM { - /// The machine with which this ROM is associated, in a form that is safe for using as - /// part of a file name. - std::string machine_name; - /// A descriptive name for this ROM, suitable for use in a bullet-point list, a bracket - /// clause, etc, e.g. "the Electron MOS 1.0". - std::string descriptive_name; - /// An idiomatic file name for this ROM, e.g. "os10.rom". - std::string file_name; - /// The expected size of this ROM in bytes, e.g. 32768. - size_t size = 0; - /// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform - /// to test user-provided ROMs of unknown provenance. **Not** intended to be used - /// to exclude ROMs where the user's intent is otherwise clear. - std::vector crc32s; - - ROM(std::string machine_name, std::string descriptive_name, std::string file_name, size_t size, uint32_t crc32) : - machine_name(machine_name), descriptive_name(descriptive_name), file_name(file_name), size(size), crc32s({crc32}) {} - ROM(std::string machine_name, std::string descriptive_name, std::string file_name, size_t size, std::initializer_list crc32s) : - machine_name(machine_name), descriptive_name(descriptive_name), file_name(file_name), size(size), crc32s(crc32s) {} -}; - /*! Defines the signature for a function that must be supplied by the host environment in order to give machines a route for fetching any system ROMs they might need. @@ -50,7 +28,7 @@ struct ROM { return a vector of unique_ptrs that either contain the contents of the ROM from @c names that corresponds by index, or else are @c nullptr. */ -typedef std::function>>(const std::vector &roms)> ROMFetcher; +typedef std::function ROMFetcher; enum class Error { MissingROMs diff --git a/Machines/Sinclair/ZX8081/ZX8081.cpp b/Machines/Sinclair/ZX8081/ZX8081.cpp index 74eae2a16..e61f5bc41 100644 --- a/Machines/Sinclair/ZX8081/ZX8081.cpp +++ b/Machines/Sinclair/ZX8081/ZX8081.cpp @@ -74,15 +74,13 @@ template class ConcreteMachine: speaker_.set_input_rate(float(ZX8081ClockRate) / 2.0f); const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM; - const auto roms = - use_zx81_rom ? - rom_fetcher({ {"ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6eb} }) : - rom_fetcher({ {"ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597} }); - - if(!roms[0]) throw ROMMachine::Error::MissingROMs; - - rom_ = std::move(*roms[0]); - rom_.resize(use_zx81_rom ? 8192 : 4096); + const ROM::Name rom_name = use_zx81_rom ? ROM::Name::ZX81 : ROM::Name::ZX80; + const ROM::Request request(rom_name); + auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; + } + rom_ = std::move(roms.find(rom_name)->second); rom_mask_ = uint16_t(rom_.size() - 1); diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index 88bf2a60b..d52ba453d 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -146,33 +146,24 @@ template class ConcreteMachine: set_clock_rate(clock_rate()); speaker_.set_input_rate(float(clock_rate()) / 2.0f); - // With only the +2a and +3 currently supported, the +3 ROM is always - // the one required. - std::vector rom_names; - const std::string machine = "ZXSpectrum"; + ROM::Name rom_name; switch(model) { case Model::SixteenK: - case Model::FortyEightK: - rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531f); - break; - - case Model::OneTwoEightK: - rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995); - break; - - case Model::Plus2: - rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dc); - break; - + case Model::FortyEightK: rom_name = ROM::Name::Spectrum48k; break; + case Model::OneTwoEightK: rom_name = ROM::Name::Spectrum128k; break; + case Model::Plus2: rom_name = ROM::Name::SpecrumPlus2; break; case Model::Plus2a: - case Model::Plus3: { - const std::initializer_list crc32s = { 0x96e3c17a, 0xbe0d9ec4 }; - rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s); - } break; + case Model::Plus3: rom_name = ROM::Name::SpectrumPlus3; break; + // TODO: possibly accept the +3 ROM in multiple parts? } - const auto roms = rom_fetcher(rom_names); - if(!roms[0]) throw ROMMachine::Error::MissingROMs; - memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size())); + const auto request = ROM::Request(rom_name); + const auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; + } + + const auto &rom = roms.find(rom_name)->second; + memcpy(rom_.data(), rom.data(), std::min(rom_.size(), rom.size())); // Register for sleeping notifications. tape_player_.set_clocking_hint_observer(this); diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp new file mode 100644 index 000000000..a4975220c --- /dev/null +++ b/Machines/Utility/ROMCatalogue.cpp @@ -0,0 +1,379 @@ +// +// ROMCatalogue.cpp +// Clock Signal +// +// Created by Thomas Harte on 01/06/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "ROMCatalogue.hpp" + +#include + +using namespace ROM; + +Request::Request(Name name, bool optional) : + node.name(name), node.is_optional(optional) {} + +Request::Request() {} + +Request Request::operator &&(const Request &rhs) { + return *this; +} + +Request Request::operator ||(const Request &rhs) { + return *this; +} + +bool Request::validate(const Map &) const { + /* TODO. */ + return true; +} + +std::vector Request::all_descriptions() const { + std::vector() result; + node.add_descriptions(result); + return result; +} + +void Request::Node::add_descriptions(std::vector &result) { + if(type == Type::One) { + result.push_back(name); + return; + } + + for(const auto &node: children) { + node.add_descriptions(result); + } +} + +Description::Description(Name name) { + switch(name) { + default: assert(false); break; + + case Name::AMSDOS: + *this = Request("AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); + break; + case Name::CPC464Firmware: + *this = Request("AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); + break; + case Name::CPC464BASIC: + *this = Request("AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); + break; + case Name::CPC664Firmware: + *this = Request("AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); + break; + case Name::CPC664BASIC: + *this = Request("AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); + break; + case Name::CPC6128Firmware: + *this = Request("AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); + break; + case Name::CPC6128BASIC: + *this = Request("AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); + break; + +//"AppleII" + AppleIIOriginal, + AppleIIPlus, + AppleIICharacter, + AppleIIe, + AppleIIeCharacter, + AppleIIEnhancedE, + AppleIIEnhancedECharacter, + } + +// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::EnhancedIIe)); +// rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942u); +// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::IIe)); +// rom_descriptions.emplace_back(machine_name, "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18du); +// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); +// rom_descriptions.emplace_back(machine_name, "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26u); +// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); +// rom_descriptions.emplace_back(machine_name, "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588u); + +// roms = rom_fetcher({ +// {"DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6}, +// {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } +// }); +// roms = rom_fetcher({ +// {"DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ff}, +// {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } +//// {"DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620 } +// }); + + +// enum class CharacterROM { +// /// The ROM that shipped with both the Apple II and the II+. +// II, +// /// The ROM that shipped with the original IIe. +// IIe, +// /// The ROM that shipped with the Enhanced IIe. +// EnhancedIIe, +// /// The ROM that shipped with the IIgs. +// IIgs +// }; +// +// /// @returns A file-level description of @c rom. +// static ROM::Name rom_name(CharacterROM rom) { +// const std::string machine_name = "AppleII"; +// switch(rom) { +// case CharacterROM::II: +// return ROMMachine::ROM(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6); +// +// case CharacterROM::IIe: +// return ROMMachine::ROM(machine_name, "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1); +// +// default: // To appease GCC. +// case CharacterROM::EnhancedIIe: +// return ROMMachine::ROM(machine_name, "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014d); +// +// case CharacterROM::IIgs: +// return ROMMachine::ROM(machine_name, "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8); +// } +// } +} + + +// const std::string machine_name = "AppleIIgs"; +// switch(target.model) { +// case Target::Model::ROM00: +// /* TODO */ +// case Target::Model::ROM01: +// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0u); +// break; +// +// case Target::Model::ROM03: +// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29u); +// break; +// } +// rom_descriptions.push_back(video_->rom_description(Video::Video::CharacterROM::EnhancedIIe)); +// +// // TODO: pick a different ADB ROM for earlier machine revisions? +// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0u); + + +// switch(model) { +// default: +// case Model::Mac128k: +// ram_size = 128*1024; +// rom_size = 64*1024; +// rom_descriptions.emplace_back(machine_name, "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28); +// break; +// case Model::Mac512k: +// ram_size = 512*1024; +// rom_size = 64*1024; +// rom_descriptions.emplace_back(machine_name, "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); +// break; +// case Model::Mac512ke: +// case Model::MacPlus: { +// ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024; +// rom_size = 128*1024; +// const std::initializer_list crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; +// rom_descriptions.emplace_back(machine_name, "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s); +// } break; +// } + + + +// std::vector rom_descriptions = { +// {"AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64} +//// {"AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43} +// }; + + +// rom_list.emplace_back(new ROMMachine::ROM("ColecoVision", "the ColecoVision BIOS", std::vector{ "coleco.rom" }, 8*1024, 0x3aa93ef3u)); + + +// if(use_zx81_rom) { +// rom_list.emplace_back(new ROMMachine::ROM("ZX8081", "the ZX81 BASIC ROM", std::vector{ "zx81.rom" }, 8 * 1024, 0x4b1dd6ebu)); +// } else { +// rom_list.emplace_back(new ROMMachine::ROM("ZX8081", "the ZX80 BASIC ROM", std::vector{ "zx80.rom" }, 4 * 1024, 0x4c7fc597u)); +// } + + + +// const std::string machine = "ZXSpectrum"; +// switch(model) { +// case Model::SixteenK: +// case Model::FortyEightK: +// rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531fu); +// break; +// +// case Model::OneTwoEightK: +// rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); +// break; +// +// case Model::Plus2: +// rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); +// break; +// +// case Model::Plus2a: +// case Model::Plus3: { +// const std::initializer_list crc32s = { 0x96e3c17a, 0xbe0d9ec4 }; +// rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s); +// } break; +// } + + + +// const std::string machine_name = "Electron"; +// std::vector required_roms = { +// {machine_name, "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781}, +// {machine_name, "the Electron MOS ROM", "os.rom", 16*1024, 0xbf63fb1f} +// }; +// const size_t pres_adfs_rom_position = required_roms.size(); +// if(target.has_pres_adfs) { +// required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993); +// required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e); +// } +// const size_t acorn_adfs_rom_position = required_roms.size(); +// if(target.has_acorn_adfs) { +// required_roms.emplace_back(machine_name, "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6); +// } +// const size_t dfs_rom_position = required_roms.size(); +// if(target.has_dfs) { +// required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5); +// } +// const size_t ap6_rom_position = required_roms.size(); +// if(target.has_ap6_rom) { +// required_roms.emplace_back(machine_name, "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfc); +// } + + +// new ROMMachine::ROM("MasterSystem", +// is_japanese ? "the Japanese Master System BIOS" : "the European/US Master System BIOS", +// is_japanese ? "japanese-bios.sms" : "bios.sms", +// 8*1024, +// is_japanese ? 0x48d44a13u : 0x0072ed54u, +// true +// ) + + +// switch(personality) { +// case Personality::C1540: +// device_name = "1540"; +// crc = 0x718d42b1; +// break; +// case Personality::C1541: +// device_name = "1541"; +// crc = 0xfb760019; +// break; +// } +// +// auto roms = rom_fetcher({ {"Commodore1540", "the " + device_name + " ROM", device_name + ".bin", 16*1024, crc} }); + + +// const std::string machine_name = "Vic20"; +// std::vector rom_names = { +// {machine_name, "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1u} +// }; +// switch(target.region) { +// default: +// rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); +// rom_names.emplace_back(machine_name, "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4u); +// break; +// case Analyser::Static::Commodore::Target::Region::American: +// rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); +// rom_names.emplace_back(machine_name, "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174u); +// break; +// case Analyser::Static::Commodore::Target::Region::Danish: +// rom_names.emplace_back(machine_name, "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454u); +// rom_names.emplace_back(machine_name, "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16u); +// break; +// case Analyser::Static::Commodore::Target::Region::Japanese: +// rom_names.emplace_back(machine_name, "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4bu); +// rom_names.emplace_back(machine_name, "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7u); +// break; +// case Analyser::Static::Commodore::Target::Region::Swedish: +// rom_names.emplace_back(machine_name, "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551du); +// rom_names.emplace_back(machine_name, "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662u); +// break; +// } + + +// const std::string machine_name = "Oric"; +// std::vector rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65u, true} }; +// switch(target.rom) { +// case Analyser::Static::Oric::Target::ROM::BASIC10: +// rom_names.emplace_back(machine_name, "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4u); +// break; +// case Analyser::Static::Oric::Target::ROM::BASIC11: +// rom_names.emplace_back(machine_name, "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92befu); +// break; +// case Analyser::Static::Oric::Target::ROM::Pravetz: +// rom_names.emplace_back(machine_name, "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502u); +// break; +// } +// size_t diskii_state_machine_index = 0; +// switch(disk_interface) { +// default: break; +// case DiskInterface::BD500: +// rom_names.emplace_back(machine_name, "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34u); +// break; +// case DiskInterface::Jasmin: +// rom_names.emplace_back(machine_name, "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89u); +// break; +// case DiskInterface::Microdisc: +// rom_names.emplace_back(machine_name, "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9cu); +// break; +// case DiskInterface::Pravetz: +// rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06u); +// // These ROM details are coupled with those in the DiskIICard. +// diskii_state_machine_index = rom_names.size(); +// rom_names.emplace_back("DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, std::initializer_list{ 0x9796a238u, 0xb72a2c70u }); +// break; +// } +// +// const auto collection = ROMMachine::ROMCollection(rom_names); + + +// const std::string machine_name = "MSX"; +// std::vector required_roms = { +// {machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u} +// }; +// +// bool is_ntsc = true; +// uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */ +// uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */ +// uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */ +// +// // TODO: CRCs below are incomplete, at best. +// switch(target.region) { +// case Target::Region::Japan: +// required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390u); +// vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); +// +// is_ntsc = true; +// character_generator = 0; +// date_format = 0; +// break; +// case Target::Region::USA: +// required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0u); +// vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); +// +// is_ntsc = true; +// character_generator = 1; +// date_format = 1; +// break; +// case Target::Region::Europe: +// required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0u); +// vdp_->set_tv_standard(TI::TMS::TVStandard::PAL); +// +// is_ntsc = false; +// character_generator = 1; +// date_format = 2; +// break; +// } +// +// ROMMachine::ROMVector rom_list; +// ROMMachine::ROMCollection *bios = new ROMMachine::ROMCollection(required_roms, ROMMachine::ROMCollection::Type::Any); +// rom_list.emplace_back(bios); +// +// // Fetch the necessary ROMs; try the region-specific ROM first, +// // but failing that fall back on patching the main one. +// size_t disk_index = 0; +// if(target.has_disk_drive) { +// disk_index = required_roms.size(); +// rom_list.emplace_back(new ROMMachine::ROM(machine_name, "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61dfu)); +// } diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp new file mode 100644 index 000000000..37e840974 --- /dev/null +++ b/Machines/Utility/ROMCatalogue.hpp @@ -0,0 +1,172 @@ +// +// ROMCatalogue.hpp +// Clock Signal +// +// Created by Thomas Harte on 01/06/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef ROMCatalogue_hpp +#define ROMCatalogue_hpp + +#include + +namespace ROM { + +enum Name { + Invalid, + + // Acorn. + AcornBASICII, + AcornElectronMOS100, + PRESADFSSlot1, + PRESADFSSlot2, + AcornADFS, + PRESAdvancedPlus6, + Acorn1770DFS, + + // Amstrad CPC. + AMSDOS, + CPC464Firmware, CPC464BASIC, + CPC664Firmware, CPC664BASIC, + CPC6128Firmware, CPC6128BASIC, + + // Apple II. + AppleIIOriginal, + AppleIIPlus, + AppleIICharacter, + AppleIIe, + AppleIIeCharacter, + AppleIIEnhancedE, + AppleIIEnhancedECharacter, + + // Apple IIgs. + AppleIIgsROM00, + AppleIIgsROM01, + AppleIIgsROM03, + AppleIIgsMicrocontrollerROM03, + + // Atari ST. + AtariSTTOS100, + AtariSTTOS104, + + // ColecoVision. + ColecoVisionBIOS, + + // Commodore 1540/1541. + Commodore1540, + Commodore1541, + + // Disk II. + DiskIIStateMachine16Sector, + DiskIIBoot16Sector, + DiskIIStateMachine13Sector, + DiskIIBoot13Sector, + + // Macintosh. + Macintosh128k, + Macintosh512k, + MacintoshPlus, + + // Master System. + MasterSystemJapaneseBIOS, + MasterSystemWesternBIOS, + + // MSX. + MSXGenericBIOS, + MSXJapaneseBIOS, + MSXAmericanBIOS, + MSXEuropeanBIOS, + MSXDOS, + + // Oric. + OricColourROM, + OricBASIC10, + OricBASIC11, + OricPravetzBASIC, + OricByteDrive500, + OricJasmin, + OricMicrodisc, + Oric8DOSBoot, + + // Vic-20. + Vic20BASIC, + Vic20EnglishCharacters, + Vic20EnglishPALKernel, + Vic20EnglishNTSCKernel, + Vic20DanishCharacters, + Vic20DanishKernel, + Vic20JapaneseCharacters, + Vic20JapaneseKernel, + Vic20SwedishCharacters, + Vic20SwedishKernel, + + // ZX80/81. + ZX80, + ZX81, + + // ZX Spectrum. + Spectrum48k, + Spectrum128k, + SpecrumPlus2, + SpectrumPlus3, +}; + +using Map = std::map>; + +struct Description { + /// The ROM's enum name. + Name name = Name::Invalid; + /// The machine with which this ROM is associated, in a form that is safe for using as + /// part of a file name. + std::string machine_name; + /// A descriptive name for this ROM, suitable for use in a bullet-point list, a bracket + /// clause, etc, e.g. "the Electron MOS 1.0". + std::string descriptive_name; + /// All idiomatic file name for this ROM, e.g. "os10.rom". + std::vector file_names; + /// The expected size of this ROM in bytes, e.g. 32768. + size_t size = 0; + /// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform + /// to test user-provided ROMs of unknown provenance. **Not** intended to be used + /// to exclude ROMs where the user's intent is otherwise clear. + std::vector crc32s; + + /// Constructs the @c Description that correlates to @c name. + Description(Name name); +}; + +struct Request { + Request(Name name, bool optional = false); + Request(); + + Request operator &&(const Request &); + Request operator ||(const Request &); + + /// Inspects the ROMMap to ensure that it satisfies this @c Request. + /// @c returns @c true if the request is satisfied; @c false otherwise. + bool validate(const Map &) const; + + std::vector all_descriptions() const; + + private: + struct Node { + enum class Type { + Any, All, One + }; + Type type = Type::One; + Name name = Name::Invalid; + /// @c true if this ROM is optional for machine startup. Generally indicates something + /// that would make emulation more accurate, but not sufficiently so to make it + /// a necessity. + bool is_optional = false; + std::vector children; + + void add_descriptions(std::vector &); + }; + Node node; +}; + +} + +#endif /* ROMCatalogue_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index bde5d0985..5c90eb50a 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1019,6 +1019,8 @@ 4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = ""; }; 4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; + 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ROMCatalogue.cpp; sourceTree = ""; }; + 4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMCatalogue.hpp; sourceTree = ""; }; 4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; 4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; }; 4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; }; @@ -2415,11 +2417,13 @@ 4B055ABE1FAE98000060FFFF /* MachineForTarget.cpp */, 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */, 4BCE005B227D30CC000CA200 /* MemoryPacker.cpp */, + 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */, 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */, 4B2B3A471F9B8FA70062DABF /* Typer.cpp */, 4B055ABF1FAE98000060FFFF /* MachineForTarget.hpp */, 4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */, 4BCE005C227D30CC000CA200 /* MemoryPacker.hpp */, + 4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */, 4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */, 4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */, 4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */, From 0aa8c3c40d51cfefc3521631f9f2827e853c1d70 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Jun 2021 22:22:56 -0400 Subject: [PATCH 03/42] For SDL at least, advances to failed linking. ... and with error reporting currently AWOL. --- Machines/AmstradCPC/AmstradCPC.cpp | 6 +- Machines/Apple/AppleII/AppleII.cpp | 4 +- .../Commodore/1540/Implementation/C1540.cpp | 2 +- OSBindings/SDL/main.cpp | 62 +++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index e530c6de5..2ea18fa22 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -817,10 +817,10 @@ template class ConcreteMachine: } if(has_amsdos) { - roms_[ROMType::AMSDOS] = *roms.find(ROM::Name::AMSDOS); + roms_[ROMType::AMSDOS] = roms.find(ROM::Name::AMSDOS)->second; } - roms_[ROMType::OS] = *roms.find(firmware); - roms_[ROMType::BASIC] = *roms.find(basic); + roms_[ROMType::OS] = roms.find(firmware)->second; + roms_[ROMType::BASIC] = roms.find(basic)->second; // Establish default memory map upper_rom_is_paged_ = true; diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 081220beb..5eb2c81bf 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -418,8 +418,8 @@ template class ConcreteMachine: install_card(6, new Apple::II::DiskIICard(roms, is_sixteen_sector)); } - rom_ = std::move(*roms.find(system)); - video_.set_character_rom(*roms.find(character)); + rom_ = std::move(roms.find(system)->second); + video_.set_character_rom(roms.find(character)->second); // Set up the default memory blocks. On a II or II+ these values will never change. // On a IIe they'll be affected by selection of auxiliary RAM. diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index 3252ddf31..72e1aad6e 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -16,7 +16,7 @@ using namespace Commodore::C1540; -ROM::Request MachineBase::rom_request(Personality personality) { +ROM::Request Machine::rom_request(Personality personality) { switch(personality) { case Personality::C1540: return ROM::Request(ROM::Name::Commodore1540); case Personality::C1541: return ROM::Request(ROM::Name::Commodore1541); diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 6c8be2163..735dfab6d 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -694,11 +694,11 @@ int main(int argc, char *argv[]) { // /usr/local/share/CLK/[system]; // /usr/share/CLK/[system]; or // [user-supplied path]/[system] - std::vector requested_roms; + ROM::Request requested_roms; std::vector checked_paths; ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments, &checked_paths] - (const std::vector &roms) -> std::vector>> { - requested_roms.insert(requested_roms.end(), roms.begin(), roms.end()); + (const ROM::Request &roms) -> ROM::Map { + requested_roms = roms; std::vector paths = { "/usr/local/share/CLK/", @@ -714,36 +714,36 @@ int main(int argc, char *argv[]) { } } - std::vector>> results; - for(const auto &rom: roms) { - FILE *file = nullptr; - std::vector rom_checked_paths; - for(const auto &path: paths) { - std::string local_path = path + rom.machine_name + "/" + rom.file_name; - file = std::fopen(local_path.c_str(), "rb"); - rom_checked_paths.push_back(local_path); - if(file) break; - } + ROM::Map results; + for(const auto &description: roms.all_descriptions()) { + for(const auto &file_name: description.file_names) { + FILE *file = nullptr; + std::vector rom_checked_paths; + for(const auto &path: paths) { + std::string local_path = path + description.machine_name + "/" + file_name; + file = std::fopen(local_path.c_str(), "rb"); + rom_checked_paths.push_back(local_path); + if(file) break; + } - if(!file) { - std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); - results.emplace_back(nullptr); - continue; - } + if(!file) { + std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); + continue; + } - auto data = std::make_unique>(); + std::vector data; - std::fseek(file, 0, SEEK_END); - data->resize(std::ftell(file)); - std::fseek(file, 0, SEEK_SET); - std::size_t read = fread(data->data(), 1, data->size(), file); - std::fclose(file); + std::fseek(file, 0, SEEK_END); + data.resize(std::ftell(file)); + std::fseek(file, 0, SEEK_SET); + std::size_t read = fread(data.data(), 1, data.size(), file); + std::fclose(file); - if(read == data->size()) - results.emplace_back(std::move(data)); - else { - std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); - results.emplace_back(nullptr); + if(read == data.size()) { + results[description.name] = std::move(data); + } else { + std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths)); + } } } @@ -765,7 +765,7 @@ int main(int argc, char *argv[]) { switch(error) { default: break; case ::Machine::Error::MissingROM: - std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath." << std::endl; +/* std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath." << std::endl; std::cerr << "One or more of the following was needed but not found:" << std::endl; for(const auto &rom: requested_roms) { std::cerr << rom.machine_name << '/' << rom.file_name << " ("; @@ -788,7 +788,7 @@ int main(int argc, char *argv[]) { std::cerr << path; is_first = false; } - std::cerr << std::endl; + std::cerr << std::endl;*/ break; } From f9954619d4a7e676480aa1b74a128eed5ff590d6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Jun 2021 22:28:30 -0400 Subject: [PATCH 04/42] Add missing header file. --- Machines/Utility/ROMCatalogue.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 37e840974..baf3f19d0 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -9,6 +9,7 @@ #ifndef ROMCatalogue_hpp #define ROMCatalogue_hpp +#include #include namespace ROM { From f05cdd5e34d9acf7c12d6cc4619c5473e9485110 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Jun 2021 22:39:18 -0400 Subject: [PATCH 05/42] With large swathes of implementation missing, compiles. --- Machines/Utility/ROMCatalogue.cpp | 56 ++++++++++++------- Machines/Utility/ROMCatalogue.hpp | 14 ++++- .../Clock Signal.xcodeproj/project.pbxproj | 4 ++ 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index a4975220c..1122b2900 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -8,20 +8,23 @@ #include "ROMCatalogue.hpp" +#include #include using namespace ROM; -Request::Request(Name name, bool optional) : - node.name(name), node.is_optional(optional) {} - -Request::Request() {} +Request::Request(Name name, bool optional) { + node.name = name; + node.is_optional = optional; +} Request Request::operator &&(const Request &rhs) { + (void)rhs; return *this; } Request Request::operator ||(const Request &rhs) { + (void)rhs; return *this; } @@ -31,12 +34,12 @@ bool Request::validate(const Map &) const { } std::vector Request::all_descriptions() const { - std::vector() result; + std::vector result; node.add_descriptions(result); return result; } -void Request::Node::add_descriptions(std::vector &result) { +void Request::Node::add_descriptions(std::vector &result) const { if(type == Type::One) { result.push_back(name); return; @@ -47,40 +50,53 @@ void Request::Node::add_descriptions(std::vector &result) { } } +std::optional Description::from_crc(uint32_t crc32) { + for(int name = 1; name <= SpectrumPlus3; name++) { + const Description candidate = Description(ROM::Name(name)); + + const auto found_crc = std::find(candidate.crc32s.begin(), candidate.crc32s.end(), crc32); + if(found_crc != candidate.crc32s.end()) { + return candidate; + } + } + + return std::nullopt; +} + Description::Description(Name name) { switch(name) { default: assert(false); break; case Name::AMSDOS: - *this = Request("AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); + *this = Description(name, "AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); break; case Name::CPC464Firmware: - *this = Request("AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); + *this = Description(name, "AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); break; case Name::CPC464BASIC: - *this = Request("AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); + *this = Description(name, "AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); break; case Name::CPC664Firmware: - *this = Request("AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); + *this = Description(name, "AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); break; case Name::CPC664BASIC: - *this = Request("AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); + *this = Description(name, "AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); break; case Name::CPC6128Firmware: - *this = Request("AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); + *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); break; case Name::CPC6128BASIC: - *this = Request("AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); + *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); break; //"AppleII" - AppleIIOriginal, - AppleIIPlus, - AppleIICharacter, - AppleIIe, - AppleIIeCharacter, - AppleIIEnhancedE, - AppleIIEnhancedECharacter, +// AppleIIOriginal, +// AppleIIPlus, +// AppleIICharacter, +// AppleIIe, +// AppleIIeCharacter, +// AppleIIEnhancedE, +// AppleIIEnhancedECharacter, } // rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::EnhancedIIe)); diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index baf3f19d0..9af771f3e 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -10,6 +10,8 @@ #define ROMCatalogue_hpp #include +#include +#include #include namespace ROM { @@ -135,11 +137,19 @@ struct Description { /// Constructs the @c Description that correlates to @c name. Description(Name name); + + /// Constructs the @c Description that correlates to @c crc32. + static std::optional from_crc(uint32_t crc32); + + private: + template Description( + Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s + ) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {} }; struct Request { Request(Name name, bool optional = false); - Request(); + Request() {} Request operator &&(const Request &); Request operator ||(const Request &); @@ -163,7 +173,7 @@ struct Request { bool is_optional = false; std::vector children; - void add_descriptions(std::vector &); + void add_descriptions(std::vector &) const; }; Node node; }; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 5c90eb50a..b5932eb04 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; }; + 4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; + 4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; }; @@ -5325,6 +5327,7 @@ 4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, 4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */, + 4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */, 4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */, 4B302185208A550100773308 /* DiskII.cpp in Sources */, 4B0F1BB32602645900B85C66 /* StaticAnalyser.cpp in Sources */, @@ -5541,6 +5544,7 @@ 4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */, 4B0F1BDA2602FF9800B85C66 /* Video.cpp in Sources */, + 4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */, 4B54C0C81F8D91E50050900F /* Keyboard.cpp in Sources */, 4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */, 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */, From d923fe72c0e30d81a5e73997a96bcf0c6e807d92 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Jun 2021 22:46:47 -0400 Subject: [PATCH 06/42] Resolves various ROM selection warnings. --- Machines/Apple/AppleIIgs/AppleIIgs.cpp | 2 +- Machines/Commodore/1540/Implementation/C1540.cpp | 2 ++ Machines/MSX/MSX.cpp | 1 + Machines/Oric/Oric.cpp | 5 +++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index 601db88a9..e778457ca 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -193,7 +193,7 @@ class ConcreteMachine: switch(target.model) { case Target::Model::ROM00: system = ROM::Name::AppleIIgsROM00; break; case Target::Model::ROM01: system = ROM::Name::AppleIIgsROM01; break; - case Target::Model::ROM03: system = ROM::Name::AppleIIgsROM03; break; + default: system = ROM::Name::AppleIIgsROM03; break; } constexpr ROM::Name characters = ROM::Name::AppleIIEnhancedECharacter; constexpr ROM::Name microcontroller = ROM::Name::AppleIIgsMicrocontrollerROM03; diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index 72e1aad6e..6ec47298d 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -18,6 +18,7 @@ using namespace Commodore::C1540; ROM::Request Machine::rom_request(Personality personality) { switch(personality) { + default: case Personality::C1540: return ROM::Request(ROM::Name::Commodore1540); case Personality::C1541: return ROM::Request(ROM::Name::Commodore1541); } @@ -48,6 +49,7 @@ MachineBase::MachineBase(Personality personality, const ROM::Map &roms) : ROM::Name rom_name; switch(personality) { + default: case Personality::C1540: rom_name = ROM::Name::Commodore1540; break; case Personality::C1541: rom_name = ROM::Name::Commodore1541; break; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 3ea353790..542d905c9 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -182,6 +182,7 @@ class ConcreteMachine: // TODO: CRCs below are incomplete, at best. switch(target.region) { + default: case Target::Region::Japan: regional_bios_name = ROM::Name::MSXJapaneseBIOS; vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 8af6b5e58..1011bf69e 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -307,8 +307,9 @@ template Date: Fri, 4 Jun 2021 18:54:50 -0400 Subject: [PATCH 07/42] Implements ROM::Request::validate. It now also validates ROM sizes, so can no longer take a const Map. --- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/Apple/Macintosh/Macintosh.cpp | 2 +- Machines/Atari/ST/AtariST.cpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 2 +- Machines/Electron/Electron.cpp | 2 +- Machines/MSX/MSX.cpp | 2 +- Machines/MasterSystem/MasterSystem.cpp | 3 +- Machines/Oric/Oric.cpp | 2 +- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 2 +- Machines/Utility/ROMCatalogue.cpp | 37 +++++++++++++++++-- Machines/Utility/ROMCatalogue.hpp | 5 ++- .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- 12 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 2ea18fa22..f7a4db3e5 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -811,7 +811,7 @@ template class ConcreteMachine: } // Fetch and verify the ROMs. - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index c6e22f276..4903055f3 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -123,7 +123,7 @@ template class ConcreteMachin // Grab a copy of the ROM and convert it into big-endian data. ROM::Request request(rom_name); - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index ee3d31817..e3c656f58 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -76,7 +76,7 @@ class ConcreteMachine: constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100; ROM::Request request(rom_name); - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index c798bc1a7..922229554 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -129,7 +129,7 @@ class ConcreteMachine: constexpr ROM::Name rom_name = ROM::Name::ColecoVisionBIOS; const ROM::Request request(rom_name); - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 8b9e09316..7590d8dc6 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -82,7 +82,7 @@ template class ConcreteMachine: if(target.has_ap6_rom) { request = request && ::ROM::Request(::ROM::Name::PRESAdvancedPlus6); } - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 542d905c9..69c05a350 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -219,7 +219,7 @@ class ConcreteMachine: request = bios_request; } - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 25d07e234..5b81fbfc8 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -143,7 +143,8 @@ class ConcreteMachine: const bool is_japanese = target.region == Target::Region::Japan; const ROM::Name bios_name = is_japanese ? ROM::Name::MasterSystemJapaneseBIOS : ROM::Name::MasterSystemWesternBIOS; ROM::Request request(bios_name, true); - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); + request.validate(roms); const auto rom = roms.find(bios_name); if(rom == roms.end()) { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 1011bf69e..49d16c7f5 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -330,7 +330,7 @@ template class ConcreteMachine: // TODO: possibly accept the +3 ROM in multiple parts? } const auto request = ROM::Request(rom_name); - const auto roms = rom_fetcher(request); + auto roms = rom_fetcher(request); if(!request.validate(roms)) { throw ROMMachine::Error::MissingROMs; } diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 1122b2900..e38a8d4f9 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -28,9 +28,8 @@ Request Request::operator ||(const Request &rhs) { return *this; } -bool Request::validate(const Map &) const { - /* TODO. */ - return true; +bool Request::validate(Map &map) const { + return node.validate(map); } std::vector Request::all_descriptions() const { @@ -50,6 +49,38 @@ void Request::Node::add_descriptions(std::vector &result) const { } } +bool Request::Node::validate(Map &map) const { + // Leaf nodes are easy: check that the named ROM is present, + // unless it's optional, in which case it is always valid. + // + // If it is present, make sure it's the proper size. + if(type == Type::One) { + auto rom = map.find(name); + if(rom == map.end()) { + return is_optional; + } + + const Description description(name); + rom->second.resize(description.size); + + return true; + } + + // This is a collection node then. Check for both any or all + // simultaneously, since all nodes will need to be visited + // regardless of any/all in order to ensure proper sizing. + bool has_all = true; + bool has_any = false; + + for(const auto &child: children) { + const bool is_valid = child.validate(map); + has_all &= is_valid; + has_any |= is_valid; + } + + return (type == Type::Any && has_any) || (type == Type::All && has_all); +} + std::optional Description::from_crc(uint32_t crc32) { for(int name = 1; name <= SpectrumPlus3; name++) { const Description candidate = Description(ROM::Name(name)); diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 9af771f3e..ec69124c7 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -156,7 +156,9 @@ struct Request { /// Inspects the ROMMap to ensure that it satisfies this @c Request. /// @c returns @c true if the request is satisfied; @c false otherwise. - bool validate(const Map &) const; + /// + /// All ROMs in the map will be resized to their idiomatic sizes. + bool validate(Map &) const; std::vector all_descriptions() const; @@ -174,6 +176,7 @@ struct Request { std::vector children; void add_descriptions(std::vector &) const; + bool validate(Map &) const; }; Node node; }; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 6bf363373..02b9d9828 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -106,7 +106,7 @@ + isEnabled = "YES"> Date: Fri, 4 Jun 2021 19:03:07 -0400 Subject: [PATCH 08/42] Attempts to implement tree construction. --- Machines/Utility/ROMCatalogue.cpp | 23 +++++++++++++++++++---- Machines/Utility/ROMCatalogue.hpp | 2 ++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index e38a8d4f9..8c8de8349 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -18,14 +18,29 @@ Request::Request(Name name, bool optional) { node.is_optional = optional; } +Request Request::append(Node::Type type, const Request &rhs) { + // Start with the easiest case: this is already an ::All request, and + // so is the new thing. + if(node.type == type && rhs.node.type == type) { + Request new_request = *this; + new_request.node.children.insert(new_request.node.children.end(), rhs.node.children.begin(), rhs.node.children.end()); + return new_request; + } + + // Otherwise create a new parent node. + Request parent; + parent.node.type = type; + parent.node.children.push_back(this->node); + parent.node.children.push_back(rhs.node); + return parent; +} + Request Request::operator &&(const Request &rhs) { - (void)rhs; - return *this; + return append(Node::Type::All, rhs); } Request Request::operator ||(const Request &rhs) { - (void)rhs; - return *this; + return append(Node::Type::Any, rhs); } bool Request::validate(Map &map) const { diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index ec69124c7..846095b19 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -179,6 +179,8 @@ struct Request { bool validate(Map &) const; }; Node node; + + Request append(Node::Type type, const Request &rhs); }; } From e36cc9e7778c66d299570f89a3b7c9d568e674d4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 19:19:55 -0400 Subject: [PATCH 09/42] Transcribes the Apple II ROM descriptions. --- Machines/Utility/ROMCatalogue.cpp | 132 +++++------------- Machines/Utility/ROMCatalogue.hpp | 1 + .../xcschemes/Clock Signal Kiosk.xcscheme | 6 +- 3 files changed, 42 insertions(+), 97 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 8c8de8349..a8bf346b8 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -113,108 +113,48 @@ Description::Description(Name name) { switch(name) { default: assert(false); break; - case Name::AMSDOS: - *this = Description(name, "AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); - break; - case Name::CPC464Firmware: - *this = Description(name, "AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); - break; - case Name::CPC464BASIC: - *this = Description(name, "AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); - break; - case Name::CPC664Firmware: - *this = Description(name, "AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); - break; - case Name::CPC664BASIC: - *this = Description(name, "AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); - break; - case Name::CPC6128Firmware: - *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); - break; - case Name::CPC6128BASIC: - *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); + case Name::AMSDOS: *this = Description(name, "AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); break; + case Name::CPC464Firmware: *this = Description(name, "AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); break; + case Name::CPC464BASIC: *this = Description(name, "AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); break; + case Name::CPC664Firmware: *this = Description(name, "AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); break; + case Name::CPC664BASIC: *this = Description(name, "AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); break; + case Name::CPC6128Firmware: *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); break; + case Name::CPC6128BASIC: *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); break; + + case Name::AppleIIEnhancedE: *this = Description(name, "AppleII", "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942u); break; + case Name::AppleIIe: *this = Description(name, "AppleII", "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18du); break; + case Name::AppleIIPlus: *this = Description(name, "AppleII", "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26u); break; + case Name::AppleIIOriginal: *this = Description(name, "AppleII", "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588u); break; + case Name::AppleIICharacter: *this = Description(name, "AppleII", "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6u); break; + case Name::AppleIIeCharacter: *this = Description(name, "AppleII", "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1u); break; + case Name::AppleIIEnhancedECharacter: + *this = Description(name, "AppleII", "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014du); break; -//"AppleII" -// AppleIIOriginal, -// AppleIIPlus, -// AppleIICharacter, -// AppleIIe, -// AppleIIeCharacter, -// AppleIIEnhancedE, -// AppleIIEnhancedECharacter, + case Name::AppleIIgsROM00: /* TODO */ + case Name::AppleIIgsROM01: *this = Description(name, "AppleIIgs", "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0u); break; + case Name::AppleIIgsROM03: *this = Description(name, "AppleIIgs", "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29u); break; + case Name::AppleIIgsCharacter: *this = Description(name, "AppleIIgs", "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8u); break; + case AppleIIgsMicrocontrollerROM03: + *this = Description(name, "AppleIIgs", "the Apple IIgs ROM03 ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0u); + break; + + case Name::DiskIIBoot16Sector: + *this = Description(name, "DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6u); + break; + case Name::DiskIIStateMachine16Sector: + *this = Description(name, "DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, std::initializer_list{ 0x9796a238, 0xb72a2c70 } ); + break; + case Name::DiskIIBoot13Sector: + *this = Description(name, "DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ffu); + break; + case Name::DiskIIStateMachine13Sector: + *this = Description(name, "DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620u); + break; } - -// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::EnhancedIIe)); -// rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942u); -// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::IIe)); -// rom_descriptions.emplace_back(machine_name, "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18du); -// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); -// rom_descriptions.emplace_back(machine_name, "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26u); -// rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II)); -// rom_descriptions.emplace_back(machine_name, "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588u); - -// roms = rom_fetcher({ -// {"DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6}, -// {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } -// }); -// roms = rom_fetcher({ -// {"DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ff}, -// {"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } } -//// {"DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620 } -// }); - - -// enum class CharacterROM { -// /// The ROM that shipped with both the Apple II and the II+. -// II, -// /// The ROM that shipped with the original IIe. -// IIe, -// /// The ROM that shipped with the Enhanced IIe. -// EnhancedIIe, -// /// The ROM that shipped with the IIgs. -// IIgs -// }; -// -// /// @returns A file-level description of @c rom. -// static ROM::Name rom_name(CharacterROM rom) { -// const std::string machine_name = "AppleII"; -// switch(rom) { -// case CharacterROM::II: -// return ROMMachine::ROM(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6); -// -// case CharacterROM::IIe: -// return ROMMachine::ROM(machine_name, "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1); -// -// default: // To appease GCC. -// case CharacterROM::EnhancedIIe: -// return ROMMachine::ROM(machine_name, "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014d); -// -// case CharacterROM::IIgs: -// return ROMMachine::ROM(machine_name, "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8); -// } -// } } -// const std::string machine_name = "AppleIIgs"; -// switch(target.model) { -// case Target::Model::ROM00: -// /* TODO */ -// case Target::Model::ROM01: -// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0u); -// break; -// -// case Target::Model::ROM03: -// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29u); -// break; -// } -// rom_descriptions.push_back(video_->rom_description(Video::Video::CharacterROM::EnhancedIIe)); -// -// // TODO: pick a different ADB ROM for earlier machine revisions? -// rom_descriptions.emplace_back(machine_name, "the Apple IIgs ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0u); - - // switch(model) { // default: // case Model::Mac128k: diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 846095b19..96b4eefd1 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -48,6 +48,7 @@ enum Name { AppleIIgsROM01, AppleIIgsROM03, AppleIIgsMicrocontrollerROM03, + AppleIIgsCharacter, // Atari ST. AtariSTTOS100, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 02b9d9828..7c5c8f004 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -74,7 +74,7 @@ + isEnabled = "NO"> + + Date: Fri, 4 Jun 2021 19:24:57 -0400 Subject: [PATCH 10/42] Transcribes the Macintosh, Atari ST, ColecoVision and ZX80/81 ROMs. --- Machines/Utility/ROMCatalogue.cpp | 57 +++++++++---------------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index a8bf346b8..646811f7e 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -151,50 +151,27 @@ Description::Description(Name name) { case Name::DiskIIStateMachine13Sector: *this = Description(name, "DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620u); break; + + case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break; + case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break; + case Name::MacintoshPlus: { + const std::initializer_list crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; + *this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s); + } break; + + case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break; + case Name::AtariSTTOS104: *this = Description(name, "AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43u); break; + + case Name::ColecoVisionBIOS: + *this = Description(name, "ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3u); + break; + + case Name::ZX80: *this = Description(name, "ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597u); break; + case Name::ZX81: *this = Description(name, "ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6ebu); break; } } -// switch(model) { -// default: -// case Model::Mac128k: -// ram_size = 128*1024; -// rom_size = 64*1024; -// rom_descriptions.emplace_back(machine_name, "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28); -// break; -// case Model::Mac512k: -// ram_size = 512*1024; -// rom_size = 64*1024; -// rom_descriptions.emplace_back(machine_name, "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); -// break; -// case Model::Mac512ke: -// case Model::MacPlus: { -// ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024; -// rom_size = 128*1024; -// const std::initializer_list crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; -// rom_descriptions.emplace_back(machine_name, "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s); -// } break; -// } - - - -// std::vector rom_descriptions = { -// {"AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64} -//// {"AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43} -// }; - - -// rom_list.emplace_back(new ROMMachine::ROM("ColecoVision", "the ColecoVision BIOS", std::vector{ "coleco.rom" }, 8*1024, 0x3aa93ef3u)); - - -// if(use_zx81_rom) { -// rom_list.emplace_back(new ROMMachine::ROM("ZX8081", "the ZX81 BASIC ROM", std::vector{ "zx81.rom" }, 8 * 1024, 0x4b1dd6ebu)); -// } else { -// rom_list.emplace_back(new ROMMachine::ROM("ZX8081", "the ZX80 BASIC ROM", std::vector{ "zx80.rom" }, 4 * 1024, 0x4c7fc597u)); -// } - - - // const std::string machine = "ZXSpectrum"; // switch(model) { // case Model::SixteenK: From 604a715a499a73bb278bf9409015b4fab5d6774e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 19:45:47 -0400 Subject: [PATCH 11/42] Transcribes the Spectrum, Electron, Master System and Vic-20 ROMs. --- Machines/Utility/ROMCatalogue.cpp | 137 ++++++++---------------------- 1 file changed, 37 insertions(+), 100 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 646811f7e..c210bbf6e 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -168,109 +168,46 @@ Description::Description(Name name) { case Name::ZX80: *this = Description(name, "ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597u); break; case Name::ZX81: *this = Description(name, "ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6ebu); break; + + case Name::Spectrum48k: *this = Description(name, "ZXSpectrum", "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531fu); break; + case Name::Spectrum128k: *this = Description(name, "ZXSpectrum", "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); break; + case Name::SpecrumPlus2: *this = Description(name, "ZXSpectrum", "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); break; + case Name::SpectrumPlus3: { + const std::initializer_list crc32s = { 0x96e3c17a, 0xbe0d9ec4 }; + *this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s); + } break; + + case Name::AcornBASICII: *this = Description(name, "Electron", "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781u); break; + case Name::PRESADFSSlot1: *this = Description(name, "Electron", "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993u); break; + case Name::PRESADFSSlot2: *this = Description(name, "Electron", "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0eu); break; + case Name::AcornADFS: *this = Description(name, "Electron", "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6u); break; + case Name::Acorn1770DFS: *this = Description(name, "Electron", "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfcu); break; + case Name::PRESAdvancedPlus6: + *this = Description(name, "Electron", "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5u); + break; + case Name::AcornElectronMOS100: + *this = Description(name, "Electron", "the Electron MOS ROM v1.00", "os.rom", 16*1024, 0xbf63fb1fu); + break; + + case Name::MasterSystemJapaneseBIOS: *this = Description(name, "MasterSystem", "the Japanese Master System BIOS", "japanese-bios.sms", 8*1024, 0x48d44a13u); break; + case Name::MasterSystemWesternBIOS: *this = Description(name, "MasterSystem", "the European/US Master System BIOS", "bios.sms", 8*1024, 0x0072ed54u); break; + + case Name::Commodore1540: *this = Description(name, "Commodore1540", "the 1540 ROM", "1540.bin", 16*1024, 0x718d42b1u); break; + case Name::Commodore1541: *this = Description(name, "Commodore1540", "the 1541 ROM", "1541.bin", 16*1024, 0xfb760019); break; + + case Name::Vic20BASIC: *this = Description(name, "Vic20", "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1u); break; + case Name::Vic20EnglishCharacters: *this = Description(name, "Vic20", "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); break; + case Name::Vic20EnglishPALKernel: *this = Description(name, "Vic20", "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4u); break; + case Name::Vic20EnglishNTSCKernel: *this = Description(name, "Vic20", "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174u); break; + case Name::Vic20DanishCharacters: *this = Description(name, "Vic20", "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454u); break; + case Name::Vic20DanishKernel: *this = Description(name, "Vic20", "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16u); break; + case Name::Vic20JapaneseCharacters: *this = Description(name, "Vic20", "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4bu); break; + case Name::Vic20JapaneseKernel: *this = Description(name, "Vic20", "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7u); break; + case Name::Vic20SwedishCharacters: *this = Description(name, "Vic20", "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551du); break; + case Name::Vic20SwedishKernel: *this = Description(name, "Vic20", "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662u); break; } } - -// const std::string machine = "ZXSpectrum"; -// switch(model) { -// case Model::SixteenK: -// case Model::FortyEightK: -// rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531fu); -// break; -// -// case Model::OneTwoEightK: -// rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); -// break; -// -// case Model::Plus2: -// rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); -// break; -// -// case Model::Plus2a: -// case Model::Plus3: { -// const std::initializer_list crc32s = { 0x96e3c17a, 0xbe0d9ec4 }; -// rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s); -// } break; -// } - - - -// const std::string machine_name = "Electron"; -// std::vector required_roms = { -// {machine_name, "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781}, -// {machine_name, "the Electron MOS ROM", "os.rom", 16*1024, 0xbf63fb1f} -// }; -// const size_t pres_adfs_rom_position = required_roms.size(); -// if(target.has_pres_adfs) { -// required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993); -// required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e); -// } -// const size_t acorn_adfs_rom_position = required_roms.size(); -// if(target.has_acorn_adfs) { -// required_roms.emplace_back(machine_name, "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6); -// } -// const size_t dfs_rom_position = required_roms.size(); -// if(target.has_dfs) { -// required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5); -// } -// const size_t ap6_rom_position = required_roms.size(); -// if(target.has_ap6_rom) { -// required_roms.emplace_back(machine_name, "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfc); -// } - - -// new ROMMachine::ROM("MasterSystem", -// is_japanese ? "the Japanese Master System BIOS" : "the European/US Master System BIOS", -// is_japanese ? "japanese-bios.sms" : "bios.sms", -// 8*1024, -// is_japanese ? 0x48d44a13u : 0x0072ed54u, -// true -// ) - - -// switch(personality) { -// case Personality::C1540: -// device_name = "1540"; -// crc = 0x718d42b1; -// break; -// case Personality::C1541: -// device_name = "1541"; -// crc = 0xfb760019; -// break; -// } -// -// auto roms = rom_fetcher({ {"Commodore1540", "the " + device_name + " ROM", device_name + ".bin", 16*1024, crc} }); - - -// const std::string machine_name = "Vic20"; -// std::vector rom_names = { -// {machine_name, "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1u} -// }; -// switch(target.region) { -// default: -// rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); -// rom_names.emplace_back(machine_name, "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4u); -// break; -// case Analyser::Static::Commodore::Target::Region::American: -// rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); -// rom_names.emplace_back(machine_name, "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174u); -// break; -// case Analyser::Static::Commodore::Target::Region::Danish: -// rom_names.emplace_back(machine_name, "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454u); -// rom_names.emplace_back(machine_name, "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16u); -// break; -// case Analyser::Static::Commodore::Target::Region::Japanese: -// rom_names.emplace_back(machine_name, "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4bu); -// rom_names.emplace_back(machine_name, "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7u); -// break; -// case Analyser::Static::Commodore::Target::Region::Swedish: -// rom_names.emplace_back(machine_name, "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551du); -// rom_names.emplace_back(machine_name, "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662u); -// break; -// } - - // const std::string machine_name = "Oric"; // std::vector rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65u, true} }; // switch(target.rom) { From 64931e476da50d5d6ad3a9abddec3cd5a9254825 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 19:50:49 -0400 Subject: [PATCH 12/42] Completes transcription of ROM details with the Oric and MSX. --- Machines/Utility/ROMCatalogue.cpp | 101 +++++------------------------- 1 file changed, 15 insertions(+), 86 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index c210bbf6e..dd42e06b0 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -205,91 +205,20 @@ Description::Description(Name name) { case Name::Vic20JapaneseKernel: *this = Description(name, "Vic20", "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7u); break; case Name::Vic20SwedishCharacters: *this = Description(name, "Vic20", "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551du); break; case Name::Vic20SwedishKernel: *this = Description(name, "Vic20", "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662u); break; + + case Name::OricColourROM: *this = Description(name, "Oric", "the Oric colour ROM", "colour.rom", 128, 0xd50fca65u); break; + case Name::OricBASIC10: *this = Description(name, "Oric", "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4u); break; + case Name::OricBASIC11: *this = Description(name, "Oric", "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92befu); break; + case Name::OricPravetzBASIC: *this = Description(name, "Oric", "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502u); break; + case Name::OricByteDrive500: *this = Description(name, "Oric", "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34u); break; + case Name::OricJasmin: *this = Description(name, "Oric", "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89u); break; + case Name::OricMicrodisc: *this = Description(name, "Oric", "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9cu); break; + case Name::Oric8DOSBoot: *this = Description(name, "Oric", "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06u); break; + + case Name::MSXGenericBIOS: *this = Description(name, "MSX", "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u); break; + case Name::MSXJapaneseBIOS: *this = Description(name, "MSX", "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390u); break; + case Name::MSXAmericanBIOS: *this = Description(name, "MSX", "an American MSX BIOS", "msx-american.rom", 32*1024, 0u); break; + case Name::MSXEuropeanBIOS: *this = Description(name, "MSX", "a European MSX BIOS", "msx-european.rom", 32*1024, 0u); break; + case Name::MSXDOS: *this = Description(name, "MSX", "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61dfu); break; } } - -// const std::string machine_name = "Oric"; -// std::vector rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65u, true} }; -// switch(target.rom) { -// case Analyser::Static::Oric::Target::ROM::BASIC10: -// rom_names.emplace_back(machine_name, "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4u); -// break; -// case Analyser::Static::Oric::Target::ROM::BASIC11: -// rom_names.emplace_back(machine_name, "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92befu); -// break; -// case Analyser::Static::Oric::Target::ROM::Pravetz: -// rom_names.emplace_back(machine_name, "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502u); -// break; -// } -// size_t diskii_state_machine_index = 0; -// switch(disk_interface) { -// default: break; -// case DiskInterface::BD500: -// rom_names.emplace_back(machine_name, "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34u); -// break; -// case DiskInterface::Jasmin: -// rom_names.emplace_back(machine_name, "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89u); -// break; -// case DiskInterface::Microdisc: -// rom_names.emplace_back(machine_name, "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9cu); -// break; -// case DiskInterface::Pravetz: -// rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06u); -// // These ROM details are coupled with those in the DiskIICard. -// diskii_state_machine_index = rom_names.size(); -// rom_names.emplace_back("DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, std::initializer_list{ 0x9796a238u, 0xb72a2c70u }); -// break; -// } -// -// const auto collection = ROMMachine::ROMCollection(rom_names); - - -// const std::string machine_name = "MSX"; -// std::vector required_roms = { -// {machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u} -// }; -// -// bool is_ntsc = true; -// uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */ -// uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */ -// uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */ -// -// // TODO: CRCs below are incomplete, at best. -// switch(target.region) { -// case Target::Region::Japan: -// required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390u); -// vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); -// -// is_ntsc = true; -// character_generator = 0; -// date_format = 0; -// break; -// case Target::Region::USA: -// required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0u); -// vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); -// -// is_ntsc = true; -// character_generator = 1; -// date_format = 1; -// break; -// case Target::Region::Europe: -// required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0u); -// vdp_->set_tv_standard(TI::TMS::TVStandard::PAL); -// -// is_ntsc = false; -// character_generator = 1; -// date_format = 2; -// break; -// } -// -// ROMMachine::ROMVector rom_list; -// ROMMachine::ROMCollection *bios = new ROMMachine::ROMCollection(required_roms, ROMMachine::ROMCollection::Type::Any); -// rom_list.emplace_back(bios); -// -// // Fetch the necessary ROMs; try the region-specific ROM first, -// // but failing that fall back on patching the main one. -// size_t disk_index = 0; -// if(target.has_disk_drive) { -// disk_index = required_roms.size(); -// rom_list.emplace_back(new ROMMachine::ROM(machine_name, "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61dfu)); -// } From ccd82591aa0526fd8323fe440f27176d38ede1bd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 21:53:56 -0400 Subject: [PATCH 13/42] Reinstates SDL error message; adds expansion of `~`. --- Machines/Utility/ROMCatalogue.cpp | 38 ++++++++++ Machines/Utility/ROMCatalogue.hpp | 14 ++++ .../xcschemes/Clock Signal Kiosk.xcscheme | 12 +++- OSBindings/SDL/main.cpp | 69 +++++++++++++++---- 4 files changed, 117 insertions(+), 16 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index dd42e06b0..e04c58042 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -96,6 +96,44 @@ bool Request::Node::validate(Map &map) const { return (type == Type::Any && has_any) || (type == Type::All && has_all); } +void Request::visit( + const std::function &enter_list, + const std::function &exit_list, + const std::function &add_item +) const { + node.visit(enter_list, exit_list, add_item); +} + +void Request::Node::visit( + const std::function &enter_list, + const std::function &exit_list, + const std::function &add_item +) const { + switch(type) { + case Type::One: + enter_list(ListType::Single); + add_item(ROM::Request::ListType::Any, Description(name), is_optional, 0); + exit_list(); + break; + + case Type::Any: + case Type::All: { + const ListType list_type = type == Type::Any ? ListType::Any : ListType::All; + enter_list(list_type); + for(size_t index = 0; index < children.size(); index++) { + auto &child = children[index]; + + if(child.type == Type::One) { + add_item(list_type, Description(child.name), child.is_optional, children.size() - 1 - index); + } else { + child.visit(enter_list, exit_list, add_item); + } + } + exit_list(); + } break; + } +} + std::optional Description::from_crc(uint32_t crc32) { for(int name = 1; name <= SpectrumPlus3; name++) { const Description candidate = Description(ROM::Name(name)); diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 96b4eefd1..01fc921d2 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -163,6 +163,15 @@ struct Request { std::vector all_descriptions() const; + enum class ListType { + Any, All, Single + }; + void visit( + const std::function &enter_list, + const std::function &exit_list, + const std::function &add_item + ) const; + private: struct Node { enum class Type { @@ -178,6 +187,11 @@ struct Request { void add_descriptions(std::vector &) const; bool validate(Map &) const; + void visit( + const std::function &enter_list, + const std::function &exit_list, + const std::function &add_item + ) const; }; Node node; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 7c5c8f004..e270b2f2b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -56,6 +56,14 @@ argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK" isEnabled = "NO"> + + + + @@ -106,7 +114,7 @@ + isEnabled = "NO"> diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 735dfab6d..aafe6be07 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -707,11 +707,20 @@ int main(int argc, char *argv[]) { const auto rompath = arguments.selections.find("rompath"); if(rompath != arguments.selections.end()) { - if(rompath->second.back() != '/') { - paths.push_back(rompath->second + "/"); - } else { - paths.push_back(rompath->second); + std::string path = rompath->second; + + // Ensure the path ends in a slash. + if(path.back() != '/') { + path += '/'; } + + // If ~ is present, expand it to %HOME%. + const size_t tilde_position = path.find("~"); + if(tilde_position != std::string::npos) { + path.replace(tilde_position, 1, getenv("HOME")); + } + + paths.push_back(path); } ROM::Map results; @@ -764,11 +773,33 @@ int main(int argc, char *argv[]) { if(!machine) { switch(error) { default: break; - case ::Machine::Error::MissingROM: -/* std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath." << std::endl; - std::cerr << "One or more of the following was needed but not found:" << std::endl; - for(const auto &rom: requested_roms) { - std::cerr << rom.machine_name << '/' << rom.file_name << " ("; + case ::Machine::Error::MissingROM: { + std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl; + std::cerr << "Needed "; + + int indentation_level = 0; + const auto indent = [&indentation_level] { + if(indentation_level) { + std::cerr << std::endl; + for(int c = 0; c < indentation_level; c++) std::cerr << '\t'; + std::cerr << "* "; + } + }; + + requested_roms.visit([&indentation_level, indent] (ROM::Request::ListType type) { + indent(); + switch(type) { + default: + case ROM::Request::ListType::All: std::cerr << "all of:"; break; + case ROM::Request::ListType::Any: std::cerr << "any of:"; break; + } + ++indentation_level; + }, [&indentation_level] { + --indentation_level; + }, [&indentation_level, indent] (ROM::Request::ListType type, const ROM::Description &rom, bool is_optional, size_t remaining) { + indent(); + if(is_optional) std::cerr << "optionally, "; + std::cerr << rom.machine_name << '/' << rom.file_names[0] << " ("; if(!rom.descriptive_name.empty()) { std::cerr << rom.descriptive_name << "; "; } @@ -779,17 +810,27 @@ int main(int argc, char *argv[]) { is_first = false; std::cerr << std::hex << std::setfill('0') << std::setw(8) << crc32; } - std::cerr << ")" << std::endl; - } - std::cerr << std::endl << "Tried specifically: "; + std::cerr << ")"; + + if(remaining) { + std::cerr << ";"; + if(remaining == 1) { + std::cerr << ((type == ROM::Request::ListType::All) ? " and" : " or"); + } + } else { + std::cerr << "."; + } + }); + + std::cerr << std::endl << std::endl << "Searched unsuccessfully: "; bool is_first = true; for(const auto &path: checked_paths) { if(!is_first) std::cerr << "; "; std::cerr << path; is_first = false; } - std::cerr << std::endl;*/ - break; + std::cerr << std::endl; + } break; } return EXIT_FAILURE; From fbee74e1fe9b6012ff9e860b68cf41bb6b7052ba Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:03:08 -0400 Subject: [PATCH 14/42] Avoids storing or printing a CRC if none is known. --- Machines/Utility/ROMCatalogue.hpp | 9 ++++-- .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- OSBindings/SDL/main.cpp | 29 ++++++++++++------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 01fc921d2..634ddc7a5 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -9,6 +9,7 @@ #ifndef ROMCatalogue_hpp #define ROMCatalogue_hpp +#include #include #include #include @@ -144,8 +145,12 @@ struct Description { private: template Description( - Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s - ) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {} + Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = CRC32T(0) + ) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} { + if(this->crc32s.size() == 1 && !this->crc32s[0]) { + this->crc32s.clear(); + } + } }; struct Request { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index e270b2f2b..2787530a5 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -58,7 +58,7 @@ + isEnabled = "NO"> 1) ? "usual crc32s: " : "usual crc32: "); + bool is_first = true; + for(const auto crc32: rom.crc32s) { + if(!is_first) std::cerr << ", "; + is_first = false; + std::cerr << std::hex << std::setfill('0') << std::setw(8) << crc32; + } + } + std::cerr << ")"; } - std::cerr << ((rom.crc32s.size() > 1) ? "usual crc32s: " : "usual crc32: "); - bool is_first = true; - for(const auto crc32: rom.crc32s) { - if(!is_first) std::cerr << ", "; - is_first = false; - std::cerr << std::hex << std::setfill('0') << std::setw(8) << crc32; - } - std::cerr << ")"; if(remaining) { std::cerr << ";"; From afd8dc0915b4da3c4363e5cb8455c0cf074fe988 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:24:31 -0400 Subject: [PATCH 15/42] Nudge just far enough to be able to launch again under macOS. --- .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 6 +- .../Mac/Clock Signal/Machine/CSROMFetcher.hpp | 2 +- .../Mac/Clock Signal/Machine/CSROMFetcher.mm | 57 +++++++++---------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 2787530a5..a9a61567e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -125,7 +125,7 @@ isEnabled = "NO"> diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 96bdc5d31..0d33f1ea1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -132,10 +132,10 @@ struct ActivityObserver: public Activity::Observer { _analyser = result; Machine::Error error; - std::vector missing_roms; + ROM::Request missing_roms; _machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error)); if(!_machine) { - for(const auto &missing_rom : missing_roms) { +/* for(const auto &missing_rom : missing_roms) { CSMissingROM *rom = [[CSMissingROM alloc] init]; // Copy/convert the primitive fields. @@ -153,7 +153,7 @@ struct ActivityObserver: public Activity::Observer { // Add to the missing list. [missingROMs addObject:rom]; - } + }*/ return nil; } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp index f1d039ade..bd00e601e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp @@ -8,4 +8,4 @@ #include "ROMMachine.hpp" -ROMMachine::ROMFetcher CSROMFetcher(std::vector *missing_roms = nullptr); +ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing = nullptr); diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm index b1f36c455..0dce4fdb2 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm @@ -14,46 +14,41 @@ #include -ROMMachine::ROMFetcher CSROMFetcher(std::vector *missing_roms) { - return [missing_roms] (const std::vector &roms) -> std::vector>> { +ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) { + return [missing] (const ROM::Request &roms) -> ROM::Map { NSArray *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; - std::vector>> results; - for(const auto &rom: roms) { - NSData *fileData; - NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:rom.machine_name.c_str()]]; + ROM::Map results; + for(const auto &description: roms.all_descriptions()) { + for(const auto &file_name: description.file_names) { + NSData *fileData; + NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]]; - // Check for this file first within the application support directories. - for(NSURL *supportURL in supportURLs) { - NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory] - URLByAppendingPathComponent:[NSString stringWithUTF8String:rom.file_name.c_str()]]; - fileData = [NSData dataWithContentsOfURL:fullURL]; - if(fileData) break; - } + // Check for this file first within the application support directories. + for(NSURL *supportURL in supportURLs) { + NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory] + URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]]; + fileData = [NSData dataWithContentsOfURL:fullURL]; + if(fileData) break; + } - // Failing that, check inside the application bundle. - if(!fileData) { - fileData = [[NSBundle mainBundle] - dataForResource:[NSString stringWithUTF8String:rom.file_name.c_str()] - withExtension:nil - subdirectory:subdirectory]; - } + // Failing that, check inside the application bundle. + if(!fileData) { + fileData = [[NSBundle mainBundle] + dataForResource:[NSString stringWithUTF8String:file_name.c_str()] + withExtension:nil + subdirectory:subdirectory]; + } - // Store an appropriate result, accumulating a list of the missing if requested. - if(!fileData) { - results.emplace_back(nullptr); - - if(missing_roms) { - missing_roms->push_back(rom); + // Store an appropriate result. + if(fileData) { + results[description.name] = fileData.stdVector8; } } - else { - auto data = std::make_unique>(); - *data = fileData.stdVector8; - results.emplace_back(std::move(data)); - } } + // TODO: sever all found ROMs from roms and store to missing, if provided. + return results; }; } From deff91e460a9cf97b457eca083e67aeb8fcc3ab0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:25:11 -0400 Subject: [PATCH 16/42] Correct Electron name mapping. --- Machines/Utility/ROMCatalogue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index e04c58042..aa53c984a 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -219,9 +219,9 @@ Description::Description(Name name) { case Name::PRESADFSSlot1: *this = Description(name, "Electron", "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993u); break; case Name::PRESADFSSlot2: *this = Description(name, "Electron", "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0eu); break; case Name::AcornADFS: *this = Description(name, "Electron", "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6u); break; - case Name::Acorn1770DFS: *this = Description(name, "Electron", "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfcu); break; + case Name::Acorn1770DFS: *this = Description(name, "Electron", "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5u); break; case Name::PRESAdvancedPlus6: - *this = Description(name, "Electron", "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5u); + *this = Description(name, "Electron", "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfcu); break; case Name::AcornElectronMOS100: *this = Description(name, "Electron", "the Electron MOS ROM v1.00", "os.rom", 16*1024, 0xbf63fb1fu); From 1d5144b912ba7bae23dd8a47e94c0b4283ca7ba6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:38:07 -0400 Subject: [PATCH 17/42] Correct no-interrupt signal. --- Components/9918/9918.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index b6f786b50..aef510c59 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -750,7 +750,7 @@ HalfCycles TMS9918::get_next_sequence_point() { if(next_line_interrupt_row == -1) { return generate_interrupts_ ? half_cycles_before_internal_cycles(time_until_frame_interrupt) : - HalfCycles(-1); + HalfCycles::max(); } // Figure out the number of internal cycles until the next line interrupt, which is the amount From 505d84f3361229286af210607b7d2bb14e63851d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:43:26 -0400 Subject: [PATCH 18/42] Corrects Amstrad 664 and 6128 ROM collection. --- Machines/AmstradCPC/AmstradCPC.cpp | 2 ++ Machines/Utility/ROMCatalogue.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index f7a4db3e5..3b1b43bae 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -798,10 +798,12 @@ template class ConcreteMachine: case Analyser::Static::AmstradCPC::Target::Model::CPC664: firmware = ROM::Name::CPC664Firmware; basic = ROM::Name::CPC664BASIC; + has_amsdos = true; break; default: firmware = ROM::Name::CPC6128Firmware; basic = ROM::Name::CPC6128BASIC; + has_amsdos = true; break; } diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index aa53c984a..93607a66d 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -156,8 +156,8 @@ Description::Description(Name name) { case Name::CPC464BASIC: *this = Description(name, "AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); break; case Name::CPC664Firmware: *this = Description(name, "AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); break; case Name::CPC664BASIC: *this = Description(name, "AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); break; - case Name::CPC6128Firmware: *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os664.rom", 16*1024, 0x0219bb74u); break; - case Name::CPC6128BASIC: *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic664.rom", 16*1024, 0xca6af63du); break; + case Name::CPC6128Firmware: *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os6128.rom", 16*1024, 0x0219bb74u); break; + case Name::CPC6128BASIC: *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic6128.rom", 16*1024, 0xca6af63du); break; case Name::AppleIIEnhancedE: *this = Description(name, "AppleII", "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942u); break; case Name::AppleIIe: *this = Description(name, "AppleII", "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18du); break; From b6b3d845a3dc81b9e30a8d4026153ac576a5b721 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 4 Jun 2021 22:48:08 -0400 Subject: [PATCH 19/42] Correct Apple IIe and Enhanced IIe startup. --- Machines/Apple/AppleII/AppleII.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 5eb2c81bf..6b6078824 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -419,6 +419,12 @@ template class ConcreteMachine: } rom_ = std::move(roms.find(system)->second); + // The IIe and Enhanced IIe ROMs often distributed are oversized; trim if necessary. + if(system == ROM::Name::AppleIIe || system == ROM::Name::AppleIIEnhancedE) { + if(rom_.size() > 16128) { + rom_.erase(rom_.begin(), rom_.end() - off_t(16128)); + } + } video_.set_character_rom(roms.find(character)->second); // Set up the default memory blocks. On a II or II+ these values will never change. From b0f551c307c27481f0e30ac54e50ddbcc85e6ed7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 5 Jun 2021 21:09:35 -0400 Subject: [PATCH 20/42] Ensures only _missing_ ROMs are reported. --- Machines/Utility/ROMCatalogue.cpp | 43 +++++++++++++++++++++++++++++-- Machines/Utility/ROMCatalogue.hpp | 10 ++++--- OSBindings/SDL/main.cpp | 11 ++++---- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 93607a66d..5a1181bfd 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -59,11 +59,50 @@ void Request::Node::add_descriptions(std::vector &result) const { return; } - for(const auto &node: children) { - node.add_descriptions(result); + for(const auto &child: children) { + child.add_descriptions(result); } } +Request Request::subtract(const ROM::Map &map) const { + Request copy(*this); + if(copy.node.subtract(map)) { + copy.node.name = Name::None; + copy.node.type = Node::Type::One; + } + return copy; +} + +bool Request::Node::subtract(const ROM::Map &map) { + switch(type) { + case Type::One: + return map.find(name) != map.end(); + + default: { + bool has_all = true; + bool has_any = false; + + auto iterator = children.begin(); + while(iterator != children.end()) { + const bool did_subtract = iterator->subtract(map); + has_all &= did_subtract; + has_any |= did_subtract; + if(did_subtract) { + iterator = children.erase(iterator); + } else { + ++iterator; + } + } + + return (type == Type::All && has_all) || (type == Type::Any && has_any); + } + } +} + +bool Request::empty() { + return node.type == Node::Type::One && node.name == Name::None; +} + bool Request::Node::validate(Map &map) const { // Leaf nodes are easy: check that the named ROM is present, // unless it's optional, in which case it is always valid. diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 634ddc7a5..0cb759507 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -18,7 +18,7 @@ namespace ROM { enum Name { - Invalid, + None, // Acorn. AcornBASICII, @@ -121,7 +121,7 @@ using Map = std::map>; struct Description { /// The ROM's enum name. - Name name = Name::Invalid; + Name name = Name::None; /// The machine with which this ROM is associated, in a form that is safe for using as /// part of a file name. std::string machine_name; @@ -177,13 +177,16 @@ struct Request { const std::function &add_item ) const; + Request subtract(const ROM::Map &map) const; + bool empty(); + private: struct Node { enum class Type { Any, All, One }; Type type = Type::One; - Name name = Name::Invalid; + Name name = Name::None; /// @c true if this ROM is optional for machine startup. Generally indicates something /// that would make emulation more accurate, but not sufficiently so to make it /// a necessity. @@ -197,6 +200,7 @@ struct Request { const std::function &exit_list, const std::function &add_item ) const; + bool subtract(const ROM::Map &map); }; Node node; diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index dfdf84377..5b73c5b5d 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -694,12 +694,10 @@ int main(int argc, char *argv[]) { // /usr/local/share/CLK/[system]; // /usr/share/CLK/[system]; or // [user-supplied path]/[system] - ROM::Request requested_roms; + ROM::Request missing_roms; std::vector checked_paths; - ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments, &checked_paths] + ROMMachine::ROMFetcher rom_fetcher = [&missing_roms, &arguments, &checked_paths] (const ROM::Request &roms) -> ROM::Map { - requested_roms = roms; - std::vector paths = { "/usr/local/share/CLK/", "/usr/share/CLK/" @@ -756,6 +754,7 @@ int main(int argc, char *argv[]) { } } + missing_roms = roms.subtract(results); return results; }; @@ -775,7 +774,7 @@ int main(int argc, char *argv[]) { default: break; case ::Machine::Error::MissingROM: { std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl; - std::cerr << "Needed "; + std::cerr << "Needed — but didn't find — "; int indentation_level = 0; const auto indent = [&indentation_level] { @@ -786,7 +785,7 @@ int main(int argc, char *argv[]) { } }; - requested_roms.visit([&indentation_level, indent] (ROM::Request::ListType type) { + missing_roms.visit([&indentation_level, indent] (ROM::Request::ListType type) { indent(); switch(type) { default: From 5acd97c860495115ddb28102f7b6b2c6fccb0ebb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 14:24:38 -0400 Subject: [PATCH 21/42] Puts enough in place for a GUI-led installation process. ... and provides a lot of the Objective-C wiring necessary to expose that to Swift. --- Machines/Utility/ROMCatalogue.cpp | 22 ++++-- Machines/Utility/ROMCatalogue.hpp | 12 +++- .../Documents/MachineDocument.swift | 35 ++------- .../Mac/Clock Signal/Machine/CSMachine.h | 12 +--- .../Mac/Clock Signal/Machine/CSMachine.mm | 28 ++------ .../Mac/Clock Signal/Machine/CSROMFetcher.hpp | 1 + .../Mac/Clock Signal/Machine/CSROMFetcher.mm | 71 ++++++++++++++++--- 7 files changed, 101 insertions(+), 80 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 5a1181bfd..45b6bb9ae 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -13,6 +13,10 @@ using namespace ROM; +namespace { +constexpr Name MaxName = Name::SpectrumPlus3; +} + Request::Request(Name name, bool optional) { node.name = name; node.is_optional = optional; @@ -173,8 +177,16 @@ void Request::Node::visit( } } +std::vector ROM::all_descriptions() { + std::vector result; + for(int name = 1; name <= MaxName; name++) { + result.push_back(Description(ROM::Name(name))); + } + return result; +} + std::optional Description::from_crc(uint32_t crc32) { - for(int name = 1; name <= SpectrumPlus3; name++) { + for(int name = 1; name <= MaxName; name++) { const Description candidate = Description(ROM::Name(name)); const auto found_crc = std::find(candidate.crc32s.begin(), candidate.crc32s.end(), crc32); @@ -232,8 +244,8 @@ Description::Description(Name name) { case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break; case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break; case Name::MacintoshPlus: { - const std::initializer_list crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; - *this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s); + const std::initializer_list crcs = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e }; + *this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crcs); } break; case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break; @@ -250,8 +262,8 @@ Description::Description(Name name) { case Name::Spectrum128k: *this = Description(name, "ZXSpectrum", "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); break; case Name::SpecrumPlus2: *this = Description(name, "ZXSpectrum", "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); break; case Name::SpectrumPlus3: { - const std::initializer_list crc32s = { 0x96e3c17a, 0xbe0d9ec4 }; - *this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s); + const std::initializer_list crcs = { 0x96e3c17a, 0xbe0d9ec4 }; + *this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crcs); } break; case Name::AcornBASICII: *this = Description(name, "Electron", "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781u); break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 0cb759507..86bc5d9df 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -135,7 +136,7 @@ struct Description { /// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform /// to test user-provided ROMs of unknown provenance. **Not** intended to be used /// to exclude ROMs where the user's intent is otherwise clear. - std::vector crc32s; + std::set crc32s; /// Constructs the @c Description that correlates to @c name. Description(Name name); @@ -145,14 +146,19 @@ struct Description { private: template Description( - Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = CRC32T(0) + Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0) ) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} { - if(this->crc32s.size() == 1 && !this->crc32s[0]) { + // Slightly lazy: deal with the case where the constructor wasn't provided with any + // CRCs by spotting that the set has exactly one member, which has value 0. The alternative + // would be to provide a partial specialisation that never put anything into the set. + if(this->crc32s.size() == 1 && !*this->crc32s.begin()) { this->crc32s.clear(); } } }; +std::vector all_descriptions(); + struct Request { Request(Name name, bool optional = false); Request() {} diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 726d6196b..71a02ced9 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -137,22 +137,18 @@ class MachineDocument: volumeSlider.floatValue = userDefaultsVolume() } - private var missingROMs: [CSMissingROM] = [] + private var missingROMs: String = "" func configureAs(_ analysis: CSStaticAnalyser) { self.machineDescription = analysis actionLock.lock() drawLock.lock() - let missingROMs = NSMutableArray() if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) { self.machine = machine machine.setVolume(userDefaultsVolume()) setupMachineOutput() } else { - // Store the selected machine and list of missing ROMs, and - // show the missing ROMs dialogue. - self.missingROMs = missingROMs.map({$0 as! CSMissingROM}) requestRoms() } @@ -437,30 +433,7 @@ class MachineDocument: } func populateMissingRomList() { - // Fill in the missing details; first build a list of all the individual - // line items. - var requestLines: [String] = [] - for missingROM in self.missingROMs { - if let descriptiveName = missingROM.descriptiveName { - requestLines.append("• " + descriptiveName) - } else { - requestLines.append("• " + missingROM.fileName) - } - } - - // Suffix everything up to the penultimate line with a semicolon; - // the penultimate line with a semicolon and a conjunctive; the final - // line with a full stop. - for x in 0 ..< requestLines.count { - if x < requestLines.count - 2 { - requestLines[x].append(";") - } else if x < requestLines.count - 1 { - requestLines[x].append("; and") - } else { - requestLines[x].append(".") - } - } - romRequesterText!.stringValue = self.romRequestBaseText + requestLines.joined(separator: "\n") + romRequesterText!.stringValue = self.romRequestBaseText + self.missingROMs } func romReceiverView(_ view: CSROMReceiverView, didReceiveFileAt URL: URL) { @@ -474,7 +447,7 @@ class MachineDocument: // Try to match by size first, CRC second. Accept that some ROMs may have // some additional appended data. Arbitrarily allow them to be up to 10kb // too large. - var index = 0 +/* var index = 0 for missingROM in self.missingROMs { if fileData.count >= missingROM.size && fileData.count < missingROM.size + 10*1024 { // Trim to size. @@ -506,7 +479,7 @@ class MachineDocument: } index = index + 1 - } + }*/ if didInstallRom { if self.missingROMs.count == 0 { diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 50ac394f6..9bc37dfad 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -33,20 +33,14 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { CSMachineKeyboardInputModeJoystick, }; -@interface CSMissingROM: NSObject -@property (nonatomic, readonly, nonnull) NSString *machineName; -@property (nonatomic, readonly, nonnull) NSString *fileName; -@property (nonatomic, readonly, nullable) NSString *descriptiveName; -@property (nonatomic, readonly) NSUInteger size; -@property (nonatomic, readonly, nonnull) NSArray *crc32s; -@end - // Deliberately low; to ensure CSMachine has been declared as an @class already. #import "CSAtari2600.h" #import "CSZX8081.h" @interface CSMachine : NSObject ++ (BOOL)attemptInstallROM:(NSURL *)url; + - (nonnull instancetype)init NS_UNAVAILABLE; /*! @@ -56,7 +50,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { @param missingROMs An array that is filled with a list of ROMs that the machine requested but which were not found; populated only if this `init` has failed. */ -- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray *)missingROMs NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSString *)missingROMs NS_DESIGNATED_INITIALIZER; - (float)idealSamplingRateFromRange:(NSRange)range; @property (readonly, getter=isStereo) BOOL stereo; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 0d33f1ea1..e81d63451 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -74,28 +74,6 @@ struct ActivityObserver: public Activity::Observer { __unsafe_unretained CSMachine *machine; }; -@interface CSMissingROM (/*Mutability*/) -@property (nonatomic, nonnull, copy) NSString *machineName; -@property (nonatomic, nonnull, copy) NSString *fileName; -@property (nonatomic, nullable, copy) NSString *descriptiveName; -@property (nonatomic, readwrite) NSUInteger size; -@property (nonatomic, copy) NSArray *crc32s; -@end - -@implementation CSMissingROM - -@synthesize machineName=_machineName; -@synthesize fileName=_fileName; -@synthesize descriptiveName=_descriptiveName; -@synthesize size=_size; -@synthesize crc32s=_crc32s; - -- (NSString *)description { - return [NSString stringWithFormat:@"%@/%@, %lu bytes, CRCs: %@", _fileName, _descriptiveName, (unsigned long)_size, _crc32s]; -} - -@end - @implementation CSMachine { SpeakerDelegate _speakerDelegate; ActivityObserver _activityObserver; @@ -126,7 +104,7 @@ struct ActivityObserver: public Activity::Observer { NSMutableArray *_inputEvents; } -- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableArray *)missingROMs { +- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSString *)missingROMs { self = [super init]; if(self) { _analyser = result; @@ -783,4 +761,8 @@ struct ActivityObserver: public Activity::Observer { _timer = nil; } ++ (BOOL)attemptInstallROM:(NSURL *)url { + return CSInstallROM(url); +} + @end diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp index bd00e601e..dede1a869 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp @@ -9,3 +9,4 @@ #include "ROMMachine.hpp" ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing = nullptr); +BOOL CSInstallROM(NSURL *); diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm index 0dce4fdb2..a48561a6b 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm @@ -11,24 +11,75 @@ #import "NSBundle+DataResource.h" #import "NSData+StdVector.h" +#import "NSData+CRC32.h" #include +namespace { + +NSString *directoryFor(const ROM::Description &description) { + return [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]]; +} + +NSArray *urlsFor(const ROM::Description &description, const std::string &file_name) { + NSMutableArray *const urls = [[NSMutableArray alloc] init]; + NSArray *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; + NSString *const subdirectory = directoryFor(description); + + for(NSURL *supportURL in supportURLs) { + [urls addObject:[[supportURL URLByAppendingPathComponent:subdirectory] + URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]]]; + } + + return urls; +} + +} + +BOOL CSInstallROM(NSURL *url) { + NSData *const data = [NSData dataWithContentsOfURL:url]; + if(!data) return NO; + + // Try for a direct CRC match. + std::optional target_description; + target_description = ROM::Description::from_crc(uint32_t(data.crc32.integerValue)); + + // See whether there's an acceptable trimming that creates a CRC match. + if(!target_description) { + const std::vector descriptions = ROM::all_descriptions(); + for(const auto &description: descriptions) { + if(description.size > data.length) continue; + + NSData *const trimmedData = [data subdataWithRange:NSMakeRange(0, description.size)]; + if(description.crc32s.find(uint32_t(trimmedData.crc32.unsignedIntValue)) != description.crc32s.end()) { + target_description = description; + break; + } + } + } + + // If no destination was found, stop. + if(!target_description) { + return NO; + } + + // Copy the data to its destination and report success. + NSURL *const targetURL = [urlsFor(*target_description, target_description->file_names[0]) firstObject]; + [data writeToURL:targetURL atomically:YES]; + + return YES; +} + ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) { return [missing] (const ROM::Request &roms) -> ROM::Map { - NSArray *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; - ROM::Map results; for(const auto &description: roms.all_descriptions()) { for(const auto &file_name: description.file_names) { NSData *fileData; - NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]]; // Check for this file first within the application support directories. - for(NSURL *supportURL in supportURLs) { - NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory] - URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]]; - fileData = [NSData dataWithContentsOfURL:fullURL]; + for(NSURL *fileURL in urlsFor(description, file_name)) { + fileData = [NSData dataWithContentsOfURL:fileURL]; if(fileData) break; } @@ -37,7 +88,7 @@ ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) { fileData = [[NSBundle mainBundle] dataForResource:[NSString stringWithUTF8String:file_name.c_str()] withExtension:nil - subdirectory:subdirectory]; + subdirectory:directoryFor(description)]; } // Store an appropriate result. @@ -47,7 +98,9 @@ ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) { } } - // TODO: sever all found ROMs from roms and store to missing, if provided. + if(missing) { + *missing = roms.subtract(results); + } return results; }; From 4494320238804bde0b4483b8284be1c313ec945f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 14:56:43 -0400 Subject: [PATCH 22/42] Corrects the macOS Swift side of things. --- .../Documents/MachineDocument.swift | 88 ++++++------------- .../Mac/Clock Signal/Machine/CSMachine.h | 4 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 3 +- .../Mac/Clock Signal/Machine/CSROMFetcher.mm | 3 +- 4 files changed, 35 insertions(+), 63 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 71a02ced9..983988958 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -144,11 +144,15 @@ class MachineDocument: actionLock.lock() drawLock.lock() + let missingROMs = NSMutableString() if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) { + setRomRequesterIsVisible(false) + self.machine = machine machine.setVolume(userDefaultsVolume()) setupMachineOutput() } else { + self.missingROMs = missingROMs as String requestRoms() } @@ -409,23 +413,38 @@ class MachineDocument: @IBOutlet var romReceiverErrorField: NSTextField? @IBOutlet var romReceiverView: CSROMReceiverView? private var romRequestBaseText = "" + + private func setRomRequesterIsVisible(_ visible : Bool) { + if self.romRequesterPanel!.isVisible == visible { + return + } + + if visible { + self.windowControllers[0].window?.beginSheet(self.romRequesterPanel!, completionHandler: nil) + } else { + self.windowControllers[0].window?.endSheet(self.romRequesterPanel!) + } + } + func requestRoms() { // Don't act yet if there's no window controller yet. if self.windowControllers.count == 0 { return } - // Load the ROM requester dialogue. - Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil) - self.romReceiverView!.delegate = self - self.romRequestBaseText = romRequesterText!.stringValue - romReceiverErrorField?.alphaValue = 0.0 + // Load the ROM requester dialogue if it's not already loaded. + if self.romRequesterPanel == nil { + Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil) + self.romReceiverView!.delegate = self + self.romRequestBaseText = romRequesterText!.stringValue + romReceiverErrorField?.alphaValue = 0.0 + } // Populate the current absentee list. populateMissingRomList() // Show the thing. - self.windowControllers[0].window?.beginSheet(self.romRequesterPanel!, completionHandler: nil) + setRomRequesterIsVisible(true) } @IBAction func cancelRequestROMs(_ sender: NSButton?) { @@ -440,59 +459,10 @@ class MachineDocument: // Test whether the file identified matches any of the currently missing ROMs. // If so then remove that ROM from the missing list and update the request screen. // If no ROMs are still missing, start the machine. - do { - let fileData = try Data(contentsOf: URL) - var didInstallRom = false - - // Try to match by size first, CRC second. Accept that some ROMs may have - // some additional appended data. Arbitrarily allow them to be up to 10kb - // too large. -/* var index = 0 - for missingROM in self.missingROMs { - if fileData.count >= missingROM.size && fileData.count < missingROM.size + 10*1024 { - // Trim to size. - let trimmedData = fileData[0 ..< missingROM.size] - - // Get CRC. - if missingROM.crc32s.contains( (trimmedData as NSData).crc32 ) { - // This ROM matches; copy it into the application library, - // strike it from the missing ROM list and decide how to - // proceed. - let fileManager = FileManager.default - let targetPath = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] - .appendingPathComponent("ROMImages") - .appendingPathComponent(missingROM.machineName) - let targetFile = targetPath - .appendingPathComponent(missingROM.fileName) - - do { - try fileManager.createDirectory(atPath: targetPath.path, withIntermediateDirectories: true, attributes: nil) - try trimmedData.write(to: targetFile) - } catch let error { - showRomReceiverError(error: "Couldn't write to application support directory: \(error)") - } - - self.missingROMs.remove(at: index) - didInstallRom = true - break - } - } - - index = index + 1 - }*/ - - if didInstallRom { - if self.missingROMs.count == 0 { - self.windowControllers[0].window?.endSheet(self.romRequesterPanel!) - configureAs(self.machineDescription!) - } else { - populateMissingRomList() - } - } else { - showRomReceiverError(error: "Didn't recognise contents of \(URL.lastPathComponent)") - } - } catch let error { - showRomReceiverError(error: "Couldn't read file at \(URL.absoluteString): \(error)") + if CSMachine.attemptInstallROM(URL) { + configureAs(self.machineDescription!) + } else { + showRomReceiverError(error: "Didn't recognise contents of \(URL.lastPathComponent)") } } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 9bc37dfad..7654899ed 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -39,7 +39,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { @interface CSMachine : NSObject -+ (BOOL)attemptInstallROM:(NSURL *)url; ++ (BOOL)attemptInstallROM:(nonnull NSURL *)url; - (nonnull instancetype)init NS_UNAVAILABLE; @@ -50,7 +50,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { @param missingROMs An array that is filled with a list of ROMs that the machine requested but which were not found; populated only if this `init` has failed. */ -- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSString *)missingROMs NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableString *)missingROMs NS_DESIGNATED_INITIALIZER; - (float)idealSamplingRateFromRange:(NSRange)range; @property (readonly, getter=isStereo) BOOL stereo; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index e81d63451..81b8f18ee 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -104,7 +104,7 @@ struct ActivityObserver: public Activity::Observer { NSMutableArray *_inputEvents; } -- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSString *)missingROMs { +- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableString *)missingROMs { self = [super init]; if(self) { _analyser = result; @@ -113,6 +113,7 @@ struct ActivityObserver: public Activity::Observer { ROM::Request missing_roms; _machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error)); if(!_machine) { + [missingROMs appendFormat:@"Who told you?"]; /* for(const auto &missing_rom : missing_roms) { CSMissingROM *rom = [[CSMissingROM alloc] init]; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm index a48561a6b..556596015 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm @@ -65,7 +65,8 @@ BOOL CSInstallROM(NSURL *url) { // Copy the data to its destination and report success. NSURL *const targetURL = [urlsFor(*target_description, target_description->file_names[0]) firstObject]; - [data writeToURL:targetURL atomically:YES]; + [[NSFileManager defaultManager] createDirectoryAtPath:targetURL.URLByDeletingLastPathComponent.path withIntermediateDirectories:YES attributes:nil error:nil]; + [data writeToURL:targetURL atomically:NO]; return YES; } From 76335e5cf29caa8290d70258550c091be926f325 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 18:15:00 -0400 Subject: [PATCH 23/42] Factors out and slightly generalises textual descriptions of ROM::Descriptions. --- Machines/Utility/ROMCatalogue.cpp | 69 +++++++++++++++++++ Machines/Utility/ROMCatalogue.hpp | 28 ++++++-- .../Mac/Clock Signal/Machine/CSMachine.mm | 1 + OSBindings/SDL/main.cpp | 20 +----- 4 files changed, 96 insertions(+), 22 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 45b6bb9ae..e2059544d 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include using namespace ROM; @@ -198,6 +200,73 @@ std::optional Description::from_crc(uint32_t crc32) { return std::nullopt; } +std::string Description::description(int flags) const { + std::stringstream output; + + // Print the file name(s) and the descriptive name. + if(flags & DescriptionFlag::Filename) { + flags &= ~DescriptionFlag::Filename; + + output << machine_name << '/'; + if(file_names.size() == 1) { + output << file_names[0]; + } else { + output << "{"; + bool is_first = true; + for(const auto &file_name: file_names) { + if(!is_first) output << " or "; + output << file_name; + is_first = false; + } + output << "}"; + } + output << " (" << descriptive_name; + if(!flags) { + output << ")"; + return output.str(); + } + output << "; "; + } else { + output << descriptive_name; + if(!flags) { + return output.str(); + } + output << " ("; + } + + // Print the size. + if(flags & DescriptionFlag::Size) { + flags &= ~DescriptionFlag::Size; + output << size << " bytes"; + + if(!flags) { + output << ")"; + return output.str(); + } + output << "; "; + } + + // Print the CRC(s). + if(flags & DescriptionFlag::CRC) { + flags &= ~DescriptionFlag::CRC; + + output << ((crc32s.size() > 1) ? "usual crc32s: " : "usual crc32: "); + bool is_first = true; + for(const auto crc32: crc32s) { + if(!is_first) output << ", "; + is_first = false; + output << std::hex << std::setfill('0') << std::setw(8) << crc32; + } + + if(!flags) { + output << ")"; + return output.str(); + } + } + + return output.str(); +} + Description::Description(Name name) { switch(name) { default: assert(false); break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 86bc5d9df..02ffc4987 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -141,9 +141,19 @@ struct Description { /// Constructs the @c Description that correlates to @c name. Description(Name name); - /// Constructs the @c Description that correlates to @c crc32. + /// Constructs the @c Description that correlates to @c crc32, if any. static std::optional from_crc(uint32_t crc32); + enum DescriptionFlag { + Size = 1 << 0, + CRC = 1 << 1, + Filename = 1 << 2, + }; + + /// Provides a single-line of text describing this ROM, including the usual base text + /// plus all the fields provided as @c flags . + std::string description(int flags) const; + private: template Description( Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0) @@ -157,13 +167,18 @@ struct Description { } }; +/// @returns a vector of all possible instances of ROM::Description — i.e. descriptions of every ROM +/// currently known to the ROM catalogue. std::vector all_descriptions(); struct Request { Request(Name name, bool optional = false); Request() {} + /// Forms the request that would be satisfied by @c this plus the right-hand side. Request operator &&(const Request &); + + /// Forms the request that would be satisfied by either @c this or the right-hand side. Request operator ||(const Request &); /// Inspects the ROMMap to ensure that it satisfies this @c Request. @@ -172,8 +187,16 @@ struct Request { /// All ROMs in the map will be resized to their idiomatic sizes. bool validate(Map &) const; + /// Returns a flattened array of all @c ROM::Descriptions that relate to anything + /// anywhere in this ROM request. std::vector all_descriptions() const; + /// @returns @c true if this request is empty, i.e. would be satisfied with no ROMs; @c false otherwise. + bool empty(); + + /// @returns what remains of this ROM request given that everything in @c map has been found. + Request subtract(const ROM::Map &map) const; + enum class ListType { Any, All, Single }; @@ -183,9 +206,6 @@ struct Request { const std::function &add_item ) const; - Request subtract(const ROM::Map &map) const; - bool empty(); - private: struct Node { enum class Type { diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 81b8f18ee..fc1573573 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -113,6 +113,7 @@ struct ActivityObserver: public Activity::Observer { ROM::Request missing_roms; _machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error)); if(!_machine) { + // TODO. [missingROMs appendFormat:@"Who told you?"]; /* for(const auto &missing_rom : missing_roms) { CSMissingROM *rom = [[CSMissingROM alloc] init]; diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 5b73c5b5d..49402eed7 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -798,25 +798,9 @@ int main(int argc, char *argv[]) { }, [&indentation_level, indent] (ROM::Request::ListType type, const ROM::Description &rom, bool is_optional, size_t remaining) { indent(); if(is_optional) std::cerr << "optionally, "; - std::cerr << rom.machine_name << '/' << rom.file_names[0]; - if(!rom.descriptive_name.empty() || !rom.crc32s.empty()) { - std::cerr << " ("; - if(!rom.descriptive_name.empty()) { - std::cerr << rom.descriptive_name; - if(!rom.crc32s.empty()) std::cerr << "; "; - } - if(!rom.crc32s.empty()) { - std::cerr << ((rom.crc32s.size() > 1) ? "usual crc32s: " : "usual crc32: "); - bool is_first = true; - for(const auto crc32: rom.crc32s) { - if(!is_first) std::cerr << ", "; - is_first = false; - std::cerr << std::hex << std::setfill('0') << std::setw(8) << crc32; - } - } - std::cerr << ")"; - } + using DescriptionFlag = ROM::Description::DescriptionFlag; + std::cerr << rom.description(DescriptionFlag::Filename | DescriptionFlag::CRC); if(remaining) { std::cerr << ";"; From 83beb3c0e689d7f986d3b9e7d9a1d9c4ba261ac7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 18:28:02 -0400 Subject: [PATCH 24/42] Introduces slightly-less manual `ROM::Request::visit`. --- Machines/Utility/ROMCatalogue.cpp | 21 ++++++ Machines/Utility/ROMCatalogue.hpp | 7 ++ .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- OSBindings/SDL/main.cpp | 66 +++++++++---------- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index e2059544d..2466491c8 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -149,6 +149,24 @@ void Request::visit( node.visit(enter_list, exit_list, add_item); } +void Request::visit( + const std::function &add_item +) const { + int indentation_level = 0; + node.visit( + [&indentation_level, &add_item] (ROM::Request::ListType type) { + add_item(LineItem::NewList, type, indentation_level, nullptr, false, -1); + ++indentation_level; + }, + [&indentation_level] { + --indentation_level; + }, + [&indentation_level, &add_item] (ROM::Request::ListType type, const ROM::Description &rom, bool is_optional, size_t remaining) { + add_item(LineItem::Description, type, indentation_level, &rom, is_optional, remaining); + } + ); +} + void Request::Node::visit( const std::function &enter_list, const std::function &exit_list, @@ -203,6 +221,9 @@ std::optional Description::from_crc(uint32_t crc32) { std::string Description::description(int flags) const { std::stringstream output; + // If there are no CRCs, don't output them. + if(crc32s.empty()) flags &= ~ DescriptionFlag::CRC; + // Print the file name(s) and the descriptive name. if(flags & DescriptionFlag::Filename) { flags &= ~DescriptionFlag::Filename; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 02ffc4987..2df582024 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -206,6 +206,13 @@ struct Request { const std::function &add_item ) const; + enum class LineItem { + NewList, Description + }; + void visit( + const std::function &add_item + ) const; + private: struct Node { enum class Type { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index a9a61567e..2787530a5 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -125,7 +125,7 @@ isEnabled = "NO"> diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 49402eed7..259699147 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -776,41 +776,41 @@ int main(int argc, char *argv[]) { std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl; std::cerr << "Needed — but didn't find — "; - int indentation_level = 0; - const auto indent = [&indentation_level] { - if(indentation_level) { - std::cerr << std::endl; - for(int c = 0; c < indentation_level; c++) std::cerr << '\t'; - std::cerr << "* "; - } - }; - - missing_roms.visit([&indentation_level, indent] (ROM::Request::ListType type) { - indent(); - switch(type) { - default: - case ROM::Request::ListType::All: std::cerr << "all of:"; break; - case ROM::Request::ListType::Any: std::cerr << "any of:"; break; - } - ++indentation_level; - }, [&indentation_level] { - --indentation_level; - }, [&indentation_level, indent] (ROM::Request::ListType type, const ROM::Description &rom, bool is_optional, size_t remaining) { - indent(); - if(is_optional) std::cerr << "optionally, "; - - using DescriptionFlag = ROM::Description::DescriptionFlag; - std::cerr << rom.description(DescriptionFlag::Filename | DescriptionFlag::CRC); - - if(remaining) { - std::cerr << ";"; - if(remaining == 1) { - std::cerr << ((type == ROM::Request::ListType::All) ? " and" : " or"); + missing_roms.visit( + [] (ROM::Request::LineItem item, ROM::Request::ListType type, int indentation_level, const ROM::Description *description, bool is_optional, size_t remaining) { + if(indentation_level) { + std::cerr << std::endl; + for(int c = 0; c < indentation_level; c++) std::cerr << '\t'; + std::cerr << "* "; + } + + switch(item) { + case ROM::Request::LineItem::NewList: + switch(type) { + default: + case ROM::Request::ListType::All: std::cerr << "all of:"; break; + case ROM::Request::ListType::Any: std::cerr << "any of:"; break; + } + break; + + case ROM::Request::LineItem::Description: + if(is_optional) std::cerr << "optionally, "; + + using DescriptionFlag = ROM::Description::DescriptionFlag; + std::cerr << description->description(DescriptionFlag::Filename | DescriptionFlag::CRC); + + if(remaining) { + std::cerr << ";"; + if(remaining == 1) { + std::cerr << ((type == ROM::Request::ListType::All) ? " and" : " or"); + } + } else { + std::cerr << "."; + } + break; } - } else { - std::cerr << "."; } - }); + ); std::cerr << std::endl << std::endl << "Searched unsuccessfully: "; bool is_first = true; From 95971f39f1a4fd71f0d3e10db29bfe4f8e4b4306 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 20:02:13 -0400 Subject: [PATCH 25/42] Reintroduces full messaging to macOS. --- Machines/Utility/ROMCatalogue.cpp | 45 +++++++++++++++++++ Machines/Utility/ROMCatalogue.hpp | 4 ++ .../xcschemes/Clock Signal Kiosk.xcscheme | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 27 +++-------- OSBindings/SDL/main.cpp | 37 +-------------- 5 files changed, 57 insertions(+), 58 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 2466491c8..0982c5735 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -10,7 +10,9 @@ #include #include +#include #include +#include #include using namespace ROM; @@ -288,6 +290,49 @@ std::string Description::description(int flags) const { return output.str(); } +std::wstring Request::description(int description_flags, wchar_t bullet_point) { + std::wstringstream output; + std::wstring_convert> wstring_converter; + + visit( + [&output, description_flags, bullet_point, &wstring_converter] (ROM::Request::LineItem item, ROM::Request::ListType type, int indentation_level, const ROM::Description *description, bool is_optional, size_t remaining) { + if(indentation_level) { + output << std::endl; + for(int c = 0; c < indentation_level; c++) output << '\t'; + output << bullet_point << ' '; + } + + switch(item) { + case ROM::Request::LineItem::NewList: + switch(type) { + default: + case ROM::Request::ListType::All: output << "all of:"; break; + case ROM::Request::ListType::Any: output << "any of:"; break; + } + break; + + case ROM::Request::LineItem::Description: + if(is_optional) output << "optionally, "; + + output << wstring_converter.from_bytes(description->description(description_flags)); + + if(remaining) { + output << ";"; + if(remaining == 1) { + output << ((type == ROM::Request::ListType::All) ? " and" : " or"); + } + } else { + output << "."; + } + break; + } + } + ); + + return output.str(); +} + + Description::Description(Name name) { switch(name) { default: assert(false); break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 2df582024..d995e397c 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -213,6 +213,10 @@ struct Request { const std::function &add_item ) const; + /// @returns a full bullet-pointed list of the requirements of this request, including + /// appropriate conjuntives. + std::wstring description(int description_flags, wchar_t bullet_point); + private: struct Node { enum class Type { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 2787530a5..131430afb 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -125,7 +125,7 @@ isEnabled = "NO"> diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index fc1573573..3b641ddae 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -31,6 +31,8 @@ #include #include +#include +#include @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; @@ -113,28 +115,9 @@ struct ActivityObserver: public Activity::Observer { ROM::Request missing_roms; _machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error)); if(!_machine) { - // TODO. - [missingROMs appendFormat:@"Who told you?"]; -/* for(const auto &missing_rom : missing_roms) { - CSMissingROM *rom = [[CSMissingROM alloc] init]; - - // Copy/convert the primitive fields. - rom.machineName = @(missing_rom.machine_name.c_str()); - rom.fileName = @(missing_rom.file_name.c_str()); - rom.descriptiveName = missing_rom.descriptive_name.empty() ? nil : @(missing_rom.descriptive_name.c_str()); - rom.size = missing_rom.size; - - // Convert the CRC list. - NSMutableArray *crc32s = [[NSMutableArray alloc] initWithCapacity:missing_rom.crc32s.size()]; - for(const auto &crc : missing_rom.crc32s) { - [crc32s addObject:@(crc)]; - } - rom.crc32s = crc32s; - - // Add to the missing list. - [missingROMs addObject:rom]; - }*/ - + std::wstring_convert> wstring_converter; + const std::wstring description = missing_roms.description(0, L'•'); + [missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]]; return nil; } diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 259699147..e78118884 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -776,41 +776,8 @@ int main(int argc, char *argv[]) { std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl; std::cerr << "Needed — but didn't find — "; - missing_roms.visit( - [] (ROM::Request::LineItem item, ROM::Request::ListType type, int indentation_level, const ROM::Description *description, bool is_optional, size_t remaining) { - if(indentation_level) { - std::cerr << std::endl; - for(int c = 0; c < indentation_level; c++) std::cerr << '\t'; - std::cerr << "* "; - } - - switch(item) { - case ROM::Request::LineItem::NewList: - switch(type) { - default: - case ROM::Request::ListType::All: std::cerr << "all of:"; break; - case ROM::Request::ListType::Any: std::cerr << "any of:"; break; - } - break; - - case ROM::Request::LineItem::Description: - if(is_optional) std::cerr << "optionally, "; - - using DescriptionFlag = ROM::Description::DescriptionFlag; - std::cerr << description->description(DescriptionFlag::Filename | DescriptionFlag::CRC); - - if(remaining) { - std::cerr << ";"; - if(remaining == 1) { - std::cerr << ((type == ROM::Request::ListType::All) ? " and" : " or"); - } - } else { - std::cerr << "."; - } - break; - } - } - ); + using DescriptionFlag = ROM::Description::DescriptionFlag; + std::wcerr << missing_roms.description(DescriptionFlag::Filename | DescriptionFlag::CRC, L'*'); std::cerr << std::endl << std::endl << "Searched unsuccessfully: "; bool is_first = true; From dd64aef91098a66c76829eb1bd1ca24506cea9c1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 20:25:26 -0400 Subject: [PATCH 26/42] Improves request construction and improves descriptions. --- Machines/Utility/ROMCatalogue.cpp | 49 ++++++++++++++----- Machines/Utility/ROMCatalogue.hpp | 7 +-- .../Clock Signal.xcodeproj/project.pbxproj | 4 +- .../Documents/MachineDocument.swift | 4 ++ .../ROMRequester/ROMRequester.xib | 12 ++--- OSBindings/SDL/main.cpp | 2 +- 6 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 0982c5735..734538247 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -27,14 +27,28 @@ Request::Request(Name name, bool optional) { } Request Request::append(Node::Type type, const Request &rhs) { - // Start with the easiest case: this is already an ::All request, and - // so is the new thing. + // Start with the easiest case: this is already an appropriate + // request, and so is the new thing. if(node.type == type && rhs.node.type == type) { Request new_request = *this; new_request.node.children.insert(new_request.node.children.end(), rhs.node.children.begin(), rhs.node.children.end()); return new_request; } + // Possibly: left is appropriate request and rhs is just one more thing? + if(node.type == type && rhs.node.type == Node::Type::One) { + Request new_request = *this; + new_request.node.children.push_back(rhs.node); + return new_request; + } + + // Or: right is appropriate request and this is just one more thing? + if(rhs.node.type == type && node.type == Node::Type::One) { + Request new_request = rhs; + new_request.node.children.push_back(node); + return new_request; + } + // Otherwise create a new parent node. Request parent; parent.node.type = type; @@ -144,7 +158,7 @@ bool Request::Node::validate(Map &map) const { } void Request::visit( - const std::function &enter_list, + const std::function &enter_list, const std::function &exit_list, const std::function &add_item ) const { @@ -156,8 +170,8 @@ void Request::visit( ) const { int indentation_level = 0; node.visit( - [&indentation_level, &add_item] (ROM::Request::ListType type) { - add_item(LineItem::NewList, type, indentation_level, nullptr, false, -1); + [&indentation_level, &add_item] (ROM::Request::ListType type, size_t size) { + add_item(LineItem::NewList, type, indentation_level, nullptr, false, size); ++indentation_level; }, [&indentation_level] { @@ -170,13 +184,13 @@ void Request::visit( } void Request::Node::visit( - const std::function &enter_list, + const std::function &enter_list, const std::function &exit_list, const std::function &add_item ) const { switch(type) { case Type::One: - enter_list(ListType::Single); + enter_list(ListType::Single, 1); add_item(ROM::Request::ListType::Any, Description(name), is_optional, 0); exit_list(); break; @@ -184,7 +198,7 @@ void Request::Node::visit( case Type::Any: case Type::All: { const ListType list_type = type == Type::Any ? ListType::Any : ListType::All; - enter_list(list_type); + enter_list(list_type, children.size()); for(size_t index = 0; index < children.size(); index++) { auto &child = children[index]; @@ -304,10 +318,21 @@ std::wstring Request::description(int description_flags, wchar_t bullet_point) { switch(item) { case ROM::Request::LineItem::NewList: - switch(type) { - default: - case ROM::Request::ListType::All: output << "all of:"; break; - case ROM::Request::ListType::Any: output << "any of:"; break; + if(remaining > 1) { + if(!indentation_level) output << " "; + switch(type) { + default: + case ROM::Request::ListType::All: output << "all of:"; break; + case ROM::Request::ListType::Any: + if(remaining == 2) { + output << "either of:"; + } else { + output << "any of:"; + } + break; + } + } else { + output << ":"; } break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index d995e397c..3dfb2542d 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -201,7 +201,7 @@ struct Request { Any, All, Single }; void visit( - const std::function &enter_list, + const std::function &enter_list, const std::function &exit_list, const std::function &add_item ) const; @@ -214,7 +214,8 @@ struct Request { ) const; /// @returns a full bullet-pointed list of the requirements of this request, including - /// appropriate conjuntives. + /// appropriate conjuntives. This text is intended to be glued to the end of an opening + /// portion of a sentence, e.g. "Please supply" + request.description(0, L'*'). std::wstring description(int description_flags, wchar_t bullet_point); private: @@ -233,7 +234,7 @@ struct Request { void add_descriptions(std::vector &) const; bool validate(Map &) const; void visit( - const std::function &enter_list, + const std::function &enter_list, const std::function &exit_list, const std::function &add_item ) const; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b5932eb04..bda241923 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; }; 4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; + 4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; }; @@ -568,7 +569,6 @@ 4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; 4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; }; 4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; }; - 4BA3189422E7A4CA00D18CFA /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; }; 4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; }; 4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; }; @@ -4768,7 +4768,7 @@ 4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */, 4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */, 4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */, - 4BA3189422E7A4CA00D18CFA /* ROMImages in Resources */, + 4B051C93266D9D6900CA44E8 /* ROMImages in Resources */, 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */, 4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */, 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */, diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 983988958..e2a0bc7c9 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -415,6 +415,10 @@ class MachineDocument: private var romRequestBaseText = "" private func setRomRequesterIsVisible(_ visible : Bool) { + if !visible && self.romRequesterPanel == nil { + return; + } + if self.romRequesterPanel!.isVisible == visible { return } diff --git a/OSBindings/Mac/Clock Signal/ROMRequester/ROMRequester.xib b/OSBindings/Mac/Clock Signal/ROMRequester/ROMRequester.xib index 82b71ef6e..44b71c68f 100644 --- a/OSBindings/Mac/Clock Signal/ROMRequester/ROMRequester.xib +++ b/OSBindings/Mac/Clock Signal/ROMRequester/ROMRequester.xib @@ -1,8 +1,8 @@ - + - + @@ -20,18 +20,16 @@ - + - + - Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.

Please drag and drop the following over this text: - - + Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.

Please drag and drop over this text diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index e78118884..6034207aa 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -774,7 +774,7 @@ int main(int argc, char *argv[]) { default: break; case ::Machine::Error::MissingROM: { std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl; - std::cerr << "Needed — but didn't find — "; + std::cerr << "Needed — but didn't find —"; using DescriptionFlag = ROM::Description::DescriptionFlag; std::wcerr << missing_roms.description(DescriptionFlag::Filename | DescriptionFlag::CRC, L'*'); From f27e331462e7a204bba1aae207173e144749bd46 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 20:34:55 -0400 Subject: [PATCH 27/42] Updates autotests to new RomFetcher world. --- Machines/Utility/ROMCatalogue.cpp | 10 ++++++++-- Machines/Utility/ROMCatalogue.hpp | 5 +++++ .../Mac/Clock SignalTests/Bridges/C1540Bridge.mm | 3 ++- OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm | 11 +++++------ OSBindings/Mac/Clock SignalTests/QLTests.mm | 7 ++++--- .../SpectrumVideoContentionTests.mm | 12 +++++++----- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 734538247..5b2d1a24a 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -408,8 +408,9 @@ Description::Description(Name name) { *this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crcs); } break; - case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break; - case Name::AtariSTTOS104: *this = Description(name, "AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43u); break; + case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break; + case Name::AtariSTTOS104: *this = Description(name, "AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43u); break; + case Name::AtariSTEmuTOS192: *this = Description(name, "AtariST", "the UK EmuTOS 1.92 ROM", "etos192uk.img", 192*1024, 0xfc3b9e61u); break; case Name::ColecoVisionBIOS: *this = Description(name, "ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3u); @@ -469,5 +470,10 @@ Description::Description(Name name) { case Name::MSXAmericanBIOS: *this = Description(name, "MSX", "an American MSX BIOS", "msx-american.rom", 32*1024, 0u); break; case Name::MSXEuropeanBIOS: *this = Description(name, "MSX", "a European MSX BIOS", "msx-european.rom", 32*1024, 0u); break; case Name::MSXDOS: *this = Description(name, "MSX", "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61dfu); break; + + case Name::SinclairQLJS: + *this = Description(name, "SinclairQL", "the Sinclair QL 'JS' ROM", "js.rom", 48*1024, 0x0f95aab5u); + break; + } } diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 3dfb2542d..c3d541915 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -55,6 +55,7 @@ enum Name { // Atari ST. AtariSTTOS100, AtariSTTOS104, + AtariSTEmuTOS192, // ColecoVision. ColecoVisionBIOS, @@ -95,6 +96,9 @@ enum Name { OricMicrodisc, Oric8DOSBoot, + // Sinclair QL. + SinclairQLJS, + // Vic-20. Vic20BASIC, Vic20EnglishCharacters, @@ -116,6 +120,7 @@ enum Name { Spectrum128k, SpecrumPlus2, SpectrumPlus3, + }; using Map = std::map>; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm b/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm index 9932ae35f..449ea3e8e 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm @@ -35,7 +35,8 @@ class VanillaSerialPort: public Commodore::Serial::Port { _serialPort = std::make_shared(); auto rom_fetcher = CSROMFetcher(); - _c1540 = std::make_unique(Commodore::C1540::Personality::C1540, rom_fetcher); + auto roms = rom_fetcher(Commodore::C1540::Machine::rom_request(Commodore::C1540::Personality::C1540)); + _c1540 = std::make_unique(Commodore::C1540::Personality::C1540, roms); _c1540->set_serial_bus(_serialBus); Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus); } diff --git a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm index 9c50a04a5..978d6387e 100644 --- a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm +++ b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm @@ -103,21 +103,20 @@ class EmuTOS: public ComparativeBusHandler { std::unique_ptr _machine; } -- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length { - const std::vector rom_names = {{"AtariST", "", image.UTF8String, 0, 0 }}; - const auto roms = CSROMFetcher()(rom_names); +- (void)testImage:(ROM::Name)name trace:(NSString *)trace length:(int)length { + const auto roms = CSROMFetcher()(ROM::Request(name)); NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"]; - _machine = std::make_unique(*roms[0], traceLocation.fileSystemRepresentation); + _machine = std::make_unique(roms.find(name)->second, traceLocation.fileSystemRepresentation); _machine->run_for(HalfCycles(length)); } - (void)testEmuTOSStartup { - [self testImage:@"etos192uk.img" trace:@"etos192uk" length:313490]; + [self testImage:ROM::Name::AtariSTEmuTOS192 trace:@"etos192uk" length:313490]; // TODO: assert that machine is now STOPped. } - (void)testTOSStartup { - [self testImage:@"tos100.img" trace:@"tos100" length:54011091]; + [self testImage:ROM::Name::AtariSTTOS100 trace:@"tos100" length:54011091]; } @end diff --git a/OSBindings/Mac/Clock SignalTests/QLTests.mm b/OSBindings/Mac/Clock SignalTests/QLTests.mm index 3df640dc2..79f32f10c 100644 --- a/OSBindings/Mac/Clock SignalTests/QLTests.mm +++ b/OSBindings/Mac/Clock SignalTests/QLTests.mm @@ -101,10 +101,11 @@ class QL: public ComparativeBusHandler { Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace. */ - (void)testStartup { - const std::vector rom_names = {{"SinclairQL", "", "js.rom", 0, 0 }}; - const auto roms = CSROMFetcher()(rom_names); + constexpr ROM::Name rom_name = ROM::Name::SinclairQLJS; + ROM::Request request(rom_name); + const auto roms = CSROMFetcher()(request); NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"]; - _machine = std::make_unique(*roms[0], traceLocation.UTF8String); + _machine = std::make_unique(roms.find(rom_name)->second, traceLocation.UTF8String); // This is how many cycles it takes to exhaust the supplied trace file. _machine->run_for(HalfCycles(23923180)); diff --git a/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm b/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm index 79c454a6c..c3c1c4cdc 100644 --- a/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm +++ b/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm @@ -24,8 +24,10 @@ struct ContentionAnalysis { HalfCycles pattern[8]; }; -template ContentionAnalysis analyse() { - Sinclair::ZXSpectrum::Video video; +using Timing = Sinclair::ZXSpectrum::Video::Timing; + +template ContentionAnalysis analyse() { + Sinclair::ZXSpectrum::Video::Video video; ContentionAnalysis analysis; // Advance to the start of the first interrupt. @@ -82,7 +84,7 @@ template ContentionAnalysis ana } - (void)test48k { - const auto analysis = analyse(); + const auto analysis = analyse(); // Check time from interrupt. XCTAssertEqual(analysis.time_after_interrupt, 14335*2); @@ -105,7 +107,7 @@ template ContentionAnalysis ana } - (void)test128k { - const auto analysis = analyse(); + const auto analysis = analyse(); // Check time from interrupt. XCTAssertEqual(analysis.time_after_interrupt, 14361*2); @@ -128,7 +130,7 @@ template ContentionAnalysis ana } - (void)testPlus3 { - const auto analysis = analyse(); + const auto analysis = analyse(); // Check time from interrupt. XCTAssertEqual(analysis.time_after_interrupt, 14361*2); From 4a2673d7574f790749e84e012530ddb039021aac Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 20:47:25 -0400 Subject: [PATCH 28/42] Make a prima facie attempt to adapt the Qt build. --- OSBindings/Qt/mainwindow.cpp | 90 ++++++++++++++---------------------- OSBindings/Qt/mainwindow.h | 2 +- 2 files changed, 36 insertions(+), 56 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 8b96cbcdb..df942ac03 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -251,33 +251,33 @@ void MainWindow::tile(const QMainWindow *previous) { void MainWindow::launchMachine() { const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - missingRoms.clear(); ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this] - (const std::vector &roms) -> std::vector>> { - std::vector>> results; + (const ROM::Request &roms) -> std::vector>> { + ROM::Map results; - for(const auto &rom: roms) { - FILE *file = nullptr; - for(const auto &path: appDataLocations) { - const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name; - const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString(); + for(const auto &description: roms.all_descriptions()) { + for(const auto &file_name: description.file_names) { + FILE *file = nullptr; + for(const auto &path: appDataLocations) { + const std::string source = path.toStdString() + "/ROMImages/" + description.machine_name + "/" + file_name; + const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString(); - file = fopen(nativeSource.c_str(), "rb"); - if(file) break; - } + file = fopen(nativeSource.c_str(), "rb"); + if(file) break; + } - if(file) { - auto data = fileContentsAndClose(file); - if(data) { - results.push_back(std::move(data)); - continue; + if(file) { + auto data = fileContentsAndClose(file); + if(data) { + results[description.name] = std::move(data); + continue; + } } } - - results.push_back(nullptr); - missingRoms.push_back(rom); } + + missingRoms = roms.subtract(results); return results; }; Machine::Error error; @@ -291,22 +291,7 @@ void MainWindow::launchMachine() { // Populate request text. QString requestText = romRequestBaseText; - size_t index = 0; - for(const auto &rom: missingRoms) { - requestText += "• "; - requestText += rom.descriptive_name.c_str(); - - ++index; - if(index == missingRoms.size()) { - requestText += ".\n"; - continue; - } - if(index == missingRoms.size() - 1) { - requestText += "; and\n"; - continue; - } - requestText += ";\n"; - } + requestText += missingRoms.description(0, L'•'); ui->missingROMsBox->setPlainText(requestText); } break; } @@ -736,28 +721,23 @@ void MainWindow::dropEvent(QDropEvent* event) { CRC::CRC32 generator; const uint32_t crc = generator.compute_crc(*contents); - bool wasUsed = false; - for(const auto &rom: missingRoms) { - if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) { - foundROM = true; + std::optional target = ROM::Description::from_crc(crc); + if(target) { + // Ensure the destination folder exists. + const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name; + const QDir dir(QString::fromStdString(path)); + if (!dir.exists()) + dir.mkpath("."); - // Ensure the destination folder exists. - const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name; - const QDir dir(QString::fromStdString(path)); - if (!dir.exists()) - dir.mkpath("."); + // Write into place. + const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString(); + FILE *const target = fopen(destination.c_str(), "wb"); + fwrite(contents->data(), 1, contents->size(), target); + fclose(target); - // Write into place. - const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString(); - FILE *const target = fopen(destination.c_str(), "wb"); - fwrite(contents->data(), 1, contents->size(), target); - fclose(target); - - wasUsed = true; - } - } - - if(!wasUsed) { + // Note that at least one meaningful ROM was supplied. + foundROM = true; + } else { if(!unusedRoms.isEmpty()) unusedRoms += ", "; unusedRoms += url.fileName(); } diff --git a/OSBindings/Qt/mainwindow.h b/OSBindings/Qt/mainwindow.h index ec25e1470..b83f92633 100644 --- a/OSBindings/Qt/mainwindow.h +++ b/OSBindings/Qt/mainwindow.h @@ -60,7 +60,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat void launchMachine(); QString romRequestBaseText; - std::vector missingRoms; + ROM::Request missingRoms; // File drag and drop is supported. void dragEnterEvent(QDragEnterEvent* event) override; From 43f686c22d192b7df5320dee8b68dd8dd84c40f3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 21:44:37 -0400 Subject: [PATCH 29/42] Correct return type and map insertion. --- OSBindings/Qt/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index df942ac03..47d439e2a 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -253,7 +253,7 @@ void MainWindow::launchMachine() { const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this] - (const ROM::Request &roms) -> std::vector>> { + (const ROM::Request &roms) -> ROM::Map { ROM::Map results; for(const auto &description: roms.all_descriptions()) { @@ -270,7 +270,7 @@ void MainWindow::launchMachine() { if(file) { auto data = fileContentsAndClose(file); if(data) { - results[description.name] = std::move(data); + results[description.name] = *data; continue; } } From 98ada2588a6648105f4c2f3f9ec7e4bc7ae353ec Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 21:51:51 -0400 Subject: [PATCH 30/42] Resolve name confusion. --- OSBindings/Qt/mainwindow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 47d439e2a..a87b73da4 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -721,16 +721,16 @@ void MainWindow::dropEvent(QDropEvent* event) { CRC::CRC32 generator; const uint32_t crc = generator.compute_crc(*contents); - std::optional target = ROM::Description::from_crc(crc); - if(target) { + std::optional target_rom = ROM::Description::from_crc(crc); + if(target_rom) { // Ensure the destination folder exists. - const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name; + const std::string path = appDataLocation + "/ROMImages/" + target_rom->machine_name; const QDir dir(QString::fromStdString(path)); if (!dir.exists()) dir.mkpath("."); // Write into place. - const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString(); + const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + target_rom->file_names[0])).toStdString(); FILE *const target = fopen(destination.c_str(), "wb"); fwrite(contents->data(), 1, contents->size(), target); fclose(target); From 9165a854848cc6d5c13c18b93f8c94c24caf5569 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 21:58:38 -0400 Subject: [PATCH 31/42] Correct `wstring` conversion. --- OSBindings/Qt/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index a87b73da4..88762db58 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -291,7 +291,7 @@ void MainWindow::launchMachine() { // Populate request text. QString requestText = romRequestBaseText; - requestText += missingRoms.description(0, L'•'); + requestText += QString::fromWCharArray(missingRoms.description(0, L'•').c_str()); ui->missingROMsBox->setPlainText(requestText); } break; } From 6c559d7556cee3a04ba6407748cbf5f3c7c0b508 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 22:02:11 -0400 Subject: [PATCH 32/42] Fix lead-in text. --- OSBindings/Qt/mainwindow.ui | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/OSBindings/Qt/mainwindow.ui b/OSBindings/Qt/mainwindow.ui index 4e576e4c3..931f8a73c 100644 --- a/OSBindings/Qt/mainwindow.ui +++ b/OSBindings/Qt/mainwindow.ui @@ -860,9 +860,7 @@ Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once. -Please drag and drop the following over this window: - - +Please drag and drop over this window From 54e3332673808502575e996b7c70b7dcad606036 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 22:36:26 -0400 Subject: [PATCH 33/42] Ensure optionals appear at the end of any ROM request list. --- Machines/Utility/ROMCatalogue.cpp | 3 +++ Machines/Utility/ROMCatalogue.hpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 5b2d1a24a..7a12069ff 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -32,6 +32,7 @@ Request Request::append(Node::Type type, const Request &rhs) { if(node.type == type && rhs.node.type == type) { Request new_request = *this; new_request.node.children.insert(new_request.node.children.end(), rhs.node.children.begin(), rhs.node.children.end()); + new_request.node.sort(); return new_request; } @@ -39,6 +40,7 @@ Request Request::append(Node::Type type, const Request &rhs) { if(node.type == type && rhs.node.type == Node::Type::One) { Request new_request = *this; new_request.node.children.push_back(rhs.node); + new_request.node.sort(); return new_request; } @@ -46,6 +48,7 @@ Request Request::append(Node::Type type, const Request &rhs) { if(rhs.node.type == type && node.type == Node::Type::One) { Request new_request = rhs; new_request.node.children.push_back(node); + new_request.node.sort(); return new_request; } diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index c3d541915..88ec7ca5b 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -9,6 +9,7 @@ #ifndef ROMCatalogue_hpp #define ROMCatalogue_hpp +#include #include #include #include @@ -244,6 +245,19 @@ struct Request { const std::function &add_item ) const; bool subtract(const ROM::Map &map); + void sort() { + // Don't do a full sort, but move anything optional to the back. + // This makes them print more nicely; it's a human-facing tweak only. + ssize_t index = ssize_t(children.size() - 1); + bool has_seen_non_optional = false; + while(index >= 0) { + has_seen_non_optional |= !children[size_t(index)].is_optional; + if(children[size_t(index)].is_optional && has_seen_non_optional) { + std::rotate(children.begin() + index, children.begin() + index + 1, children.end()); + } + --index; + } + } }; Node node; From 6e4832f99927a14fb0f76e20753f2a5607b134d6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 Jun 2021 22:43:53 -0400 Subject: [PATCH 34/42] Ensures Oric honours absence of the colour ROM. --- Machines/Oric/Video.cpp | 3 ++- Machines/Oric/Video.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 30fe2f805..b14aab6b1 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -54,7 +54,7 @@ void VideoOutput::set_display_type(Outputs::Display::DisplayType display_type) { #ifdef SUPPLY_COMPOSITE const auto data_type = - (display_type == Outputs::Display::DisplayType::RGB) ? + (!has_colour_rom_ || display_type == Outputs::Display::DisplayType::RGB) ? Outputs::Display::InputDataType::Red1Green1Blue1 : Outputs::Display::InputDataType::PhaseLinkedLuminance8; #else @@ -80,6 +80,7 @@ Outputs::Display::ScanStatus VideoOutput::get_scaled_scan_status() const { } void VideoOutput::set_colour_rom(const std::vector &rom) { + has_colour_rom_ = true; for(std::size_t c = 0; c < 8; c++) { colour_forms_[c] = 0; diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index 59181526d..bfd4df537 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -37,6 +37,7 @@ class VideoOutput { Outputs::CRT::CRT crt_; Outputs::CRT::CRTFrequencyMismatchWarner frequency_mismatch_warner_; bool crt_is_60Hz_ = false; + bool has_colour_rom_ = false; void update_crt_frequency(); From 96e7eb1bed378a2b84cafb26068b65291c536d4e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 7 Jun 2021 20:16:01 -0400 Subject: [PATCH 35/42] Adds a use-square-pixels option for the Apple II. --- Machines/Apple/AppleII/AppleII.cpp | 2 ++ Machines/Apple/AppleII/AppleII.hpp | 8 +++++++- Machines/Apple/AppleII/Video.cpp | 24 ++++++++++++++++++++++++ Machines/Apple/AppleII/Video.hpp | 6 ++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 6b6078824..80436626d 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -787,12 +787,14 @@ template class ConcreteMachine: std::unique_ptr get_options() final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); + options->use_square_pixels = video_.get_use_square_pixels(); return options; } void set_options(const std::unique_ptr &str) { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); + video_.set_use_square_pixels(options->use_square_pixels); } // MARK: MediaTarget diff --git a/Machines/Apple/AppleII/AppleII.hpp b/Machines/Apple/AppleII/AppleII.hpp index e373da4b8..94aa43289 100644 --- a/Machines/Apple/AppleII/AppleII.hpp +++ b/Machines/Apple/AppleII/AppleII.hpp @@ -30,10 +30,16 @@ class Machine { class Options: public Reflection::StructImpl, public Configurable::DisplayOption { friend Configurable::DisplayOption; public: - Options(Configurable::OptionsType) : Configurable::DisplayOption(Configurable::Display::CompositeColour) { + bool use_square_pixels = false; + + Options(Configurable::OptionsType) : Configurable::DisplayOption(Configurable::Display::CompositeColour) { if(needs_declare()) { declare_display_option(); limit_enum(&output, Configurable::Display::CompositeMonochrome, Configurable::Display::CompositeColour, -1); + + if(needs_declare()) { + DeclareField(use_square_pixels); + } } } }; diff --git a/Machines/Apple/AppleII/Video.cpp b/Machines/Apple/AppleII/Video.cpp index 7b0df2baa..6a07308b2 100644 --- a/Machines/Apple/AppleII/Video.cpp +++ b/Machines/Apple/AppleII/Video.cpp @@ -26,6 +26,30 @@ VideoBase::VideoBase(bool is_iie, std::function &&target) : // crt_.set_immediate_default_phase(0.5f); } +void VideoBase::set_use_square_pixels(bool use_square_pixels) { + use_square_pixels_ = use_square_pixels; + + if(use_square_pixels) { + // From what I can make out, many contemporary Apple II monitors were + // calibrated slightly to stretch the Apple II's display slightly wider + // than it should be per the NTSC standards, for approximately square + // pixels. This reproduces that. + + // 243 lines and 52µs are visible. + // i.e. to be square, 1 pixel should be: (1/243 * 52) * (3/4) = 156/972 = 39/243 µs + // On an Apple II each pixel is actually 1/7µs. + // Therefore the adjusted aspect ratio should be (4/3) * (39/243)/(1/7) = (4/3) * 273/243 = 1092/729 = 343/243 ~= 1.412 + crt_.set_aspect_ratio(343.0f / 243.0f); + } else { + // Standard NTSC aspect ratio. + crt_.set_aspect_ratio(4.0f / 3.0f); + } +} +bool VideoBase::get_use_square_pixels() { + return use_square_pixels_; +} + + void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } diff --git a/Machines/Apple/AppleII/Video.hpp b/Machines/Apple/AppleII/Video.hpp index afd332efc..ba414c83e 100644 --- a/Machines/Apple/AppleII/Video.hpp +++ b/Machines/Apple/AppleII/Video.hpp @@ -51,8 +51,14 @@ class VideoBase: public VideoSwitches { /// Gets the type of output. Outputs::Display::DisplayType get_display_type() const; + /// Sets whether the current CRT should be recalibrated away from normative NTSC + /// to produce square pixels in 40-column text mode. + void set_use_square_pixels(bool); + bool get_use_square_pixels(); + protected: Outputs::CRT::CRT crt_; + bool use_square_pixels_ = false; // State affecting output video stream generation. uint8_t *pixel_pointer_ = nullptr; From 778b9ef6838e6abb14b6422dc5d4494cb9805713 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 7 Jun 2021 20:41:02 -0400 Subject: [PATCH 36/42] Ensures set_square_pixels is exposed, works around OpenGL aspect ratio bug. --- Machines/Apple/AppleII/AppleII.hpp | 5 +---- Machines/Apple/AppleII/Video.cpp | 14 ++++++++++++-- .../xcschemes/Clock Signal Kiosk.xcscheme | 16 ++++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Machines/Apple/AppleII/AppleII.hpp b/Machines/Apple/AppleII/AppleII.hpp index 94aa43289..97d348578 100644 --- a/Machines/Apple/AppleII/AppleII.hpp +++ b/Machines/Apple/AppleII/AppleII.hpp @@ -34,12 +34,9 @@ class Machine { Options(Configurable::OptionsType) : Configurable::DisplayOption(Configurable::Display::CompositeColour) { if(needs_declare()) { + DeclareField(use_square_pixels); declare_display_option(); limit_enum(&output, Configurable::Display::CompositeMonochrome, Configurable::Display::CompositeColour, -1); - - if(needs_declare()) { - DeclareField(use_square_pixels); - } } } }; diff --git a/Machines/Apple/AppleII/Video.cpp b/Machines/Apple/AppleII/Video.cpp index 6a07308b2..ae358c0ab 100644 --- a/Machines/Apple/AppleII/Video.cpp +++ b/Machines/Apple/AppleII/Video.cpp @@ -15,9 +15,8 @@ VideoBase::VideoBase(bool is_iie, std::function &&target) : crt_(910, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance1), is_iie_(is_iie) { - // Show only the centre 75% of the TV frame. crt_.set_display_type(Outputs::Display::DisplayType::CompositeColour); - crt_.set_visible_area(Outputs::Display::Rect(0.118f, 0.122f, 0.77f, 0.77f)); + set_use_square_pixels(use_square_pixels_); // TODO: there seems to be some sort of bug whereby switching modes can cause // a signal discontinuity that knocks phase out of whack. So it isn't safe to @@ -29,6 +28,17 @@ VideoBase::VideoBase(bool is_iie, std::function &&target) : void VideoBase::set_use_square_pixels(bool use_square_pixels) { use_square_pixels_ = use_square_pixels; + // HYPER-UGLY HACK. See correlated hack in the Macintosh. +#ifdef __APPLE__ + crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.122f, 0.75f, 0.77f)); +#else + if(use_square_pixels) { + crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.112f, 0.75f, 0.73f)); + } else { + crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.12f, 0.75f, 0.77f)); + } +#endif + if(use_square_pixels) { // From what I can make out, many contemporary Apple II monitors were // calibrated slightly to stretch the Apple II's display slightly wider diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 131430afb..263537208 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -56,13 +56,17 @@ argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK" isEnabled = "NO"> + + + isEnabled = "NO"> + isEnabled = "YES"> + isEnabled = "YES"> + + From 462bbf2e40fefd8d2d8251837b7aa0f9b6bdde7d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 7 Jun 2021 21:21:45 -0400 Subject: [PATCH 37/42] Exposes square pixels option on macOS. --- .../Clock Signal.xcodeproj/project.pbxproj | 10 ++++ .../Base.lproj/AppleIIOptions.xib | 36 +++++++++---- .../Base.lproj/Atari2600Options.xib | 18 +++---- .../Mac/Clock Signal/Machine/CSMachine.h | 2 + .../Mac/Clock Signal/Machine/CSMachine.mm | 4 ++ .../Clock Signal/Machine/Wrappers/CSAppleII.h | 18 +++++++ .../Machine/Wrappers/CSAppleII.mm | 50 +++++++++++++++++++ 7 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.h create mode 100644 OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index bda241923..60a7ae8c2 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; }; 4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; + 4B051C95266EF50200CA44E8 /* AppleIIOptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */; }; + 4B051C97266EF5F600CA44E8 /* CSAppleII.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C96266EF5F600CA44E8 /* CSAppleII.mm */; }; 4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; }; @@ -1023,6 +1025,9 @@ 4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ROMCatalogue.cpp; sourceTree = ""; }; 4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMCatalogue.hpp; sourceTree = ""; }; + 4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleIIOptionsPanel.swift; sourceTree = ""; }; + 4B051C96266EF5F600CA44E8 /* CSAppleII.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSAppleII.mm; sourceTree = ""; }; + 4B051C98266EF60500CA44E8 /* CSAppleII.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSAppleII.h; sourceTree = ""; }; 4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; 4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; }; 4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; }; @@ -2405,8 +2410,10 @@ 4B2A53981D117D36003C6002 /* Wrappers */ = { isa = PBXGroup; children = ( + 4B051C98266EF60500CA44E8 /* CSAppleII.h */, 4B2A53991D117D36003C6002 /* CSAtari2600.h */, 4B14978D1EE4B4D200CE2596 /* CSZX8081.h */, + 4B051C96266EF5F600CA44E8 /* CSAppleII.mm */, 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */, 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */, ); @@ -2777,6 +2784,7 @@ 4B55CE551C3B7D360093A61B /* Documents */ = { isa = PBXGroup; children = ( + 4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */, 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */, 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */, 4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */, @@ -5402,11 +5410,13 @@ 4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */, 4B0ACC2C23775819008902D0 /* IntelligentKeyboard.cpp in Sources */, 4B92E26A234AE35100CD6D1B /* MFP68901.cpp in Sources */, + 4B051C97266EF5F600CA44E8 /* CSAppleII.mm in Sources */, 4B0ACC2A23775819008902D0 /* Video.cpp in Sources */, 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */, 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */, 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, + 4B051C95266EF50200CA44E8 /* AppleIIOptionsPanel.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib index 8c80a36f1..5b4ef82a7 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -13,17 +13,27 @@ - + - - - - + + + + + - + @@ -40,16 +50,20 @@ - + + + - + + + - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Options.xib index 2e6e00fb0..41c6e7572 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Options.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Options.xib @@ -1,8 +1,8 @@ - + - + @@ -13,17 +13,17 @@ - + - +