From 3862fdb44c012583cb143864cca226aedfee4159 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 Jul 2018 20:00:46 -0400 Subject: [PATCH 1/9] Simplifies initialisation procedure for all machines. With the side effect of allowing every machine to try to load only the ROMs that it needs. --- .../MultiConfigurationTarget.cpp | 3 - .../MultiConfigurationTarget.hpp | 1 - Analyser/Static/Acorn/StaticAnalyser.cpp | 6 +- Analyser/Static/Oric/StaticAnalyser.cpp | 2 +- Machines/AmstradCPC/AmstradCPC.cpp | 166 ++++++------ Machines/AmstradCPC/AmstradCPC.hpp | 5 +- Machines/AppleII/AppleII.cpp | 90 +++---- Machines/AppleII/AppleII.hpp | 4 +- Machines/AppleII/DiskIICard.cpp | 2 +- Machines/Atari2600/Atari2600.cpp | 30 +-- Machines/Atari2600/Atari2600.hpp | 6 +- Machines/CRTMachine.hpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 38 ++- Machines/ColecoVision/ColecoVision.hpp | 5 +- Machines/Commodore/1540/C1540.hpp | 35 ++- .../Commodore/1540/Implementation/C1540.cpp | 30 +-- .../1540/Implementation/C1540Base.hpp | 4 +- Machines/Commodore/Vic-20/Vic20.cpp | 248 +++++++----------- Machines/Commodore/Vic-20/Vic20.hpp | 7 +- Machines/ConfigurationTarget.hpp | 3 - Machines/Electron/Electron.cpp | 185 +++++++------ Machines/Electron/Electron.hpp | 24 +- Machines/MSX/MSX.cpp | 105 ++++---- Machines/MSX/MSX.hpp | 11 +- Machines/Oric/Oric.cpp | 100 ++++--- Machines/Oric/Oric.hpp | 3 +- Machines/ROMMachine.hpp | 15 +- Machines/Utility/MachineForTarget.cpp | 52 ++-- Machines/Utility/MachineForTarget.hpp | 1 + Machines/ZX8081/ZX8081.cpp | 137 ++++------ Machines/ZX8081/ZX8081.hpp | 3 +- NumberTheory/CRC.hpp | 4 +- 32 files changed, 610 insertions(+), 717 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurationTarget.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurationTarget.cpp index 53804d939..32691fdc7 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurationTarget.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurationTarget.cpp @@ -17,9 +17,6 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector> &machines); // Below is the standard ConfigurationTarget::Machine interface; see there for documentation. - void configure_as_target(const Analyser::Static::Target *target) override; bool insert_media(const Analyser::Static::Media &media) override; private: diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index ea5b8f315..9d74a094a 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -69,13 +69,13 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me target->media.cartridges = AcornCartridgesFrom(media.cartridges); // if there are any tapes, attempt to get data from the first - if(media.tapes.size() > 0) { + if(!media.tapes.empty()) { std::shared_ptr tape = media.tapes.front(); std::vector files = GetFiles(tape); tape->reset(); // continue if there are any files - if(files.size()) { + if(!files.empty()) { bool is_basic = true; // protected files are always for *RUNning only @@ -103,7 +103,7 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me } } - if(media.disks.size() > 0) { + if(!media.disks.empty()) { std::shared_ptr disk = media.disks.front(); std::unique_ptr dfs_catalogue, adfs_catalogue; dfs_catalogue = GetDFSCatalogue(disk); diff --git a/Analyser/Static/Oric/StaticAnalyser.cpp b/Analyser/Static/Oric/StaticAnalyser.cpp index 9c40c6635..2ec0457c0 100644 --- a/Analyser/Static/Oric/StaticAnalyser.cpp +++ b/Analyser/Static/Oric/StaticAnalyser.cpp @@ -111,7 +111,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med for(auto &tape : media.tapes) { std::vector tape_files = GetFiles(tape); tape->reset(); - if(tape_files.size()) { + if(!tape_files.empty()) { for(const auto &file : tape_files) { if(file.data_type == File::MachineCode) { std::vector entry_points = {file.starting_address}; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 95ce484d0..752855cb3 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -44,13 +44,6 @@ std::vector> get_options() { ); } -enum ROMType: int { - OS464 = 0, BASIC464, - OS664, BASIC664, - OS6128, BASIC6128, - AMSDOS -}; - /*! Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period @@ -761,7 +754,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler { /*! The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80. */ -class ConcreteMachine: +template class ConcreteMachine: public CRTMachine::Machine, public ConfigurationTarget::Machine, public KeyboardMachine::Machine, @@ -773,7 +766,7 @@ class ConcreteMachine: public Machine, public Activity::Source { public: - ConcreteMachine() : + ConcreteMachine(const Analyser::Static::AmstradCPC::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : z80_(*this), crtc_bus_handler_(ram_, interrupt_timer_), crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_), @@ -792,7 +785,59 @@ class ConcreteMachine: fdc_.set_clocking_hint_observer(this); tape_player_.set_clocking_hint_observer(this); + // install the keyboard state class as the AY port handler ay_.ay().set_port_handler(&key_state_); + + // construct the list of necessary ROMs + std::vector required_roms = {"amsdos.rom"}; + std::string model_number; + switch(target.model) { + default: + model_number = "6128"; + has_128k_ = true; + break; + case Analyser::Static::AmstradCPC::Target::Model::CPC464: + model_number = "464"; + has_128k_ = false; + break; + case Analyser::Static::AmstradCPC::Target::Model::CPC664: + model_number = "664"; + has_128k_ = false; + break; + } + required_roms.push_back("os" + model_number + ".rom"); + required_roms.push_back("basic" + model_number + ".rom"); + + // fetch and verify the ROMs + const auto roms = rom_fetcher("AmstradCPC", required_roms); + + for(std::size_t index = 0; index < roms.size(); ++index) { + auto &data = roms[index]; + if(!data) throw ROMMachine::Error::MissingROMs; + roms_[static_cast(index)] = std::move(*data); + roms_[static_cast(index)].resize(16384); + } + + // Establish default memory map + upper_rom_is_paged_ = true; + upper_rom_ = ROMType::BASIC; + + write_pointers_[0] = &ram_[0]; + write_pointers_[1] = &ram_[16384]; + write_pointers_[2] = &ram_[32768]; + write_pointers_[3] = &ram_[49152]; + + read_pointers_[0] = roms_[ROMType::OS].data(); + read_pointers_[1] = write_pointers_[1]; + read_pointers_[2] = write_pointers_[2]; + read_pointers_[3] = roms_[upper_rom_].data(); + + // Type whatever is required. + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } + + insert_media(target.media); } /// The entry point for performing a partial Z80 machine cycle. @@ -847,8 +892,8 @@ class ConcreteMachine: } // Check for an upper ROM selection - if(has_fdc_ && !(address&0x2000)) { - upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : rom_model_ + 1; + if(has_fdc && !(address&0x2000)) { + upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : ROMType::BASIC; if(upper_rom_is_paged_) read_pointers_[3] = roms_[upper_rom_].data(); } @@ -867,13 +912,13 @@ class ConcreteMachine: } // Check for an FDC access - if(has_fdc_ && (address & 0x580) == 0x100) { + if(has_fdc && (address & 0x580) == 0x100) { flush_fdc(); fdc_.set_register(address & 1, *cycle.value); } // Check for a disk motor access - if(has_fdc_ && !(address & 0x580)) { + if(has_fdc && !(address & 0x580)) { flush_fdc(); fdc_.set_motor_on(!!(*cycle.value)); } @@ -888,7 +933,7 @@ class ConcreteMachine: } // Check for an FDC access - if(has_fdc_ && (address & 0x580) == 0x100) { + if(has_fdc && (address & 0x580) == 0x100) { flush_fdc(); *cycle.value &= fdc_.get_register(address & 1); } @@ -961,50 +1006,6 @@ class ConcreteMachine: z80_.run_for(cycles); } - /// The ConfigurationTarget entry point; should configure this meachine as described by @c target. - void configure_as_target(const Analyser::Static::Target *target) override final { - auto *const cpc_target = dynamic_cast(target); - - switch(cpc_target->model) { - case Analyser::Static::AmstradCPC::Target::Model::CPC464: - rom_model_ = ROMType::OS464; - has_128k_ = false; - has_fdc_ = false; - break; - case Analyser::Static::AmstradCPC::Target::Model::CPC664: - rom_model_ = ROMType::OS664; - has_128k_ = false; - has_fdc_ = true; - break; - case Analyser::Static::AmstradCPC::Target::Model::CPC6128: - rom_model_ = ROMType::OS6128; - has_128k_ = true; - has_fdc_ = true; - break; - } - - // Establish default memory map - upper_rom_is_paged_ = true; - upper_rom_ = rom_model_ + 1; - - write_pointers_[0] = &ram_[0]; - write_pointers_[1] = &ram_[16384]; - write_pointers_[2] = &ram_[32768]; - write_pointers_[3] = &ram_[49152]; - - read_pointers_[0] = roms_[rom_model_].data(); - read_pointers_[1] = write_pointers_[1]; - read_pointers_[2] = write_pointers_[2]; - read_pointers_[3] = roms_[upper_rom_].data(); - - // Type whatever is required. - if(!cpc_target->loading_command.empty()) { - type_string(cpc_target->loading_command); - } - - insert_media(target->media); - } - bool insert_media(const Analyser::Static::Media &media) override final { // If there are any tapes supplied, use the first of them. if(!media.tapes.empty()) { @@ -1019,28 +1020,7 @@ class ConcreteMachine: if(c == 4) break; } - return !media.tapes.empty() || (!media.disks.empty() && has_fdc_); - } - - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - auto roms = roms_with_names( - "AmstradCPC", - { - "os464.rom", "basic464.rom", - "os664.rom", "basic664.rom", - "os6128.rom", "basic6128.rom", - "amsdos.rom" - }); - - for(std::size_t index = 0; index < roms.size(); ++index) { - auto &data = roms[index]; - if(!data) return false; - roms_[static_cast(index)] = std::move(*data); - roms_[static_cast(index)].resize(16384); - } - - return true; + return !media.tapes.empty() || (!media.disks.empty() && has_fdc); } void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final { @@ -1078,7 +1058,7 @@ class ConcreteMachine: // MARK: - Activity Source void set_activity_observer(Activity::Observer *observer) override { - if(has_fdc_) fdc_.set_activity_observer(observer); + if(has_fdc) fdc_.set_activity_observer(observer); } // MARK: - Configuration options. @@ -1117,7 +1097,7 @@ class ConcreteMachine: case 1: crtc_bus_handler_.set_colour(value & 0x1f); break; case 2: // Perform ROM paging. - read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[rom_model_].data(); + read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[ROMType::OS].data(); upper_rom_is_paged_ = !(value & 8); read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3]; @@ -1169,7 +1149,7 @@ class ConcreteMachine: HalfCycles time_since_fdc_update_; void flush_fdc() { // Clock the FDC, if connected, using a lazy scale by two - if(has_fdc_ && !fdc_is_sleeping_) { + if(has_fdc && !fdc_is_sleeping_) { fdc_.run_for(Cycles(time_since_fdc_update_.as_int())); } time_since_fdc_update_ = HalfCycles(0); @@ -1184,13 +1164,16 @@ class ConcreteMachine: uint8_t ram_[128 * 1024]; - std::vector roms_[7]; - int rom_model_; - bool has_fdc_, fdc_is_sleeping_; + bool fdc_is_sleeping_; bool tape_player_is_sleeping_; bool has_128k_; + + enum ROMType: int { + AMSDOS = 0, OS = 1, BASIC = 2 + }; + std::vector roms_[3]; bool upper_rom_is_paged_; - int upper_rom_; + ROMType upper_rom_; uint8_t *ram_pages_[4]; uint8_t *read_pointers_[4]; @@ -1205,8 +1188,13 @@ class ConcreteMachine: using namespace AmstradCPC; // See header; constructs and returns an instance of the Amstrad CPC. -Machine *Machine::AmstradCPC() { - return new AmstradCPC::ConcreteMachine; +Machine *Machine::AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::AmstradCPC::Target; + const Target *const cpc_target = dynamic_cast(target); + switch(cpc_target->model) { + default: return new AmstradCPC::ConcreteMachine(*cpc_target, rom_fetcher); + case Target::Model::CPC464: return new AmstradCPC::ConcreteMachine(*cpc_target, rom_fetcher); + } } Machine::~Machine() {} diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index 55dce79fa..e523a69c0 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -10,8 +10,9 @@ #define AmstradCPC_hpp #include "../../Configurable/Configurable.hpp" +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" -#include #include #include @@ -28,7 +29,7 @@ class Machine { virtual ~Machine(); /// Creates and returns an Amstrad CPC. - static Machine *AmstradCPC(); + static Machine *AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; } diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 533349964..192dddebd 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -89,7 +89,7 @@ class ConcreteMachine: } uint8_t ram_[65536], aux_ram_[65536]; - std::vector apple2_rom_, apple2plus_rom_, rom_; + std::vector rom_; std::vector character_rom_; uint8_t keyboard_input_ = 0x00; @@ -98,8 +98,6 @@ class ConcreteMachine: Outputs::Speaker::LowpassSpeaker speaker_; Cycles cycles_since_audio_update_; - ROMMachine::ROMFetcher rom_fetcher_; - // MARK: - Cards std::array, 7> cards_; Cycles cycles_since_card_update_; @@ -226,7 +224,7 @@ class ConcreteMachine: } public: - ConcreteMachine(): + ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): m6502_(*this), video_bus_handler_(ram_), audio_toggle_(audio_queue_), @@ -254,6 +252,41 @@ class ConcreteMachine: // Add a couple of joysticks. joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick); + + // Pick the required ROMs. + using Target = Analyser::Static::AppleII::Target; + std::vector rom_names = {"apple2-character.rom"}; + switch(target.model) { + default: + rom_names.push_back("apple2o.rom"); + break; + case Target::Model::IIplus: + rom_names.push_back("apple2.rom"); + break; + } + const auto roms = rom_fetcher("AppleII", rom_names); + + if(!roms[0] || !roms[1]) { + throw ROMMachine::Error::MissingROMs; + } + + character_rom_ = std::move(*roms[0]); + rom_ = std::move(*roms[1]); + if(rom_.size() > 12*1024) { + rom_.erase(rom_.begin(), rom_.begin() + static_cast(rom_.size()) - 12*1024); + } + + if(target.disk_controller != Target::DiskController::None) { + // Apple recommended slot 6 for the (first) Disk II. + install_card(6, new AppleII::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector)); + } + + // Set up the default memory blocks. + memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_; + memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200]; + set_language_card_paging(); + + insert_media(target.media); } ~ConcreteMachine() { @@ -596,27 +629,6 @@ class ConcreteMachine: audio_queue_.perform(); } - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - auto roms = roms_with_names( - "AppleII", - { - "apple2o.rom", - "apple2.rom", - "apple2-character.rom" - }); - - if(!roms[0] || !roms[1] || !roms[2]) return false; - - apple2_rom_ = std::move(*roms[0]); - apple2plus_rom_ = std::move(*roms[1]); - - character_rom_ = std::move(*roms[2]); - - rom_fetcher_ = roms_with_names; - - return true; - } - void run_for(const Cycles cycles) override { m6502_.run_for(cycles); } @@ -651,28 +663,6 @@ class ConcreteMachine: } // MARK: ConfigurationTarget - void configure_as_target(const Analyser::Static::Target *target) override { - using Target = Analyser::Static::AppleII::Target; - auto *const apple_target = dynamic_cast(target); - - if(apple_target->disk_controller != Target::DiskController::None) { - // Apple recommended slot 6 for the (first) Disk II. - install_card(6, new AppleII::DiskIICard(rom_fetcher_, apple_target->disk_controller == Target::DiskController::SixteenSector)); - } - - rom_ = (apple_target->model == Target::Model::II) ? apple2_rom_ : apple2plus_rom_; - if(rom_.size() > 12*1024) { - rom_.erase(rom_.begin(), rom_.begin() + static_cast(rom_.size()) - 12*1024); - } - - // Set up the default memory blocks. - memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_; - memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200]; - set_language_card_paging(); - - insert_media(apple_target->media); - } - bool insert_media(const Analyser::Static::Media &media) override { if(!media.disks.empty()) { auto diskii = diskii_card(); @@ -722,8 +712,10 @@ class ConcreteMachine: using namespace AppleII; -Machine *Machine::AppleII() { - return new ConcreteMachine; +Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::AppleII::Target; + const Target *const appleii_target = dynamic_cast(target); + return new ConcreteMachine(*appleii_target, rom_fetcher); } Machine::~Machine() {} diff --git a/Machines/AppleII/AppleII.hpp b/Machines/AppleII/AppleII.hpp index 76f42efc0..c680b3163 100644 --- a/Machines/AppleII/AppleII.hpp +++ b/Machines/AppleII/AppleII.hpp @@ -10,6 +10,8 @@ #define AppleII_hpp #include "../../Configurable/Configurable.hpp" +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" #include #include @@ -24,7 +26,7 @@ class Machine { virtual ~Machine(); /// Creates and returns an AppleII. - static Machine *AppleII(); + static Machine *AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; }; diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index 8edc1ea6d..a73d16844 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -11,7 +11,7 @@ using namespace AppleII; DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) { - auto roms = rom_fetcher( + const auto roms = rom_fetcher( "DiskII", { is_16_sector ? "boot-16.rom" : "boot-13.rom", diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 10c170f82..c28410d03 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -76,24 +76,16 @@ class Joystick: public Inputs::ConcreteJoystick { class ConcreteMachine: public Machine, public CRTMachine::Machine, - public ConfigurationTarget::Machine, public JoystickMachine::Machine, public Outputs::CRT::Delegate { public: - ConcreteMachine() { + ConcreteMachine(const Analyser::Static::Atari::Target &target) { set_clock_rate(NTSC_clock_rate); - } - ~ConcreteMachine() { - close_output(); - } - - void configure_as_target(const Analyser::Static::Target *target) override { - auto *const atari_target = dynamic_cast(target); - const std::vector &rom = target->media.cartridges.front()->get_segments().front().data; + const std::vector &rom = target.media.cartridges.front()->get_segments().front().data; using PagingModel = Analyser::Static::Atari::Target::PagingModel; - switch(atari_target->paging_model) { + switch(target.paging_model) { case PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge(rom)); break; case PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge(rom)); break; case PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge(rom)); break; @@ -105,21 +97,21 @@ class ConcreteMachine: case PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge(rom)); break; case PagingModel::Atari8k: - if(atari_target->uses_superchip) { + if(target.uses_superchip) { bus_.reset(new Cartridge::Cartridge(rom)); } else { bus_.reset(new Cartridge::Cartridge(rom)); } break; case PagingModel::Atari16k: - if(atari_target->uses_superchip) { + if(target.uses_superchip) { bus_.reset(new Cartridge::Cartridge(rom)); } else { bus_.reset(new Cartridge::Cartridge(rom)); } break; case PagingModel::Atari32k: - if(atari_target->uses_superchip) { + if(target.uses_superchip) { bus_.reset(new Cartridge::Cartridge(rom)); } else { bus_.reset(new Cartridge::Cartridge(rom)); @@ -131,8 +123,8 @@ class ConcreteMachine: joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1)); } - bool insert_media(const Analyser::Static::Media &media) override { - return false; + ~ConcreteMachine() { + close_output(); } std::vector> &get_joysticks() override { @@ -254,8 +246,10 @@ class ConcreteMachine: using namespace Atari2600; -Machine *Machine::Atari2600() { - return new Atari2600::ConcreteMachine; +Machine *Machine::Atari2600(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::Atari::Target; + const Target *const atari_target = dynamic_cast(target); + return new Atari2600::ConcreteMachine(*atari_target); } Machine::~Machine() {} diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 5b195bd4f..0df9f3aac 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -9,6 +9,10 @@ #ifndef Atari2600_cpp #define Atari2600_cpp +#include "../../Configurable/Configurable.hpp" +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" + #include "Atari2600Inputs.h" namespace Atari2600 { @@ -21,7 +25,7 @@ class Machine { virtual ~Machine(); /// Creates and returns an Atari 2600 on the heap. - static Machine *Atari2600(); + static Machine *Atari2600(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); /// Sets the switch @c input to @c state. virtual void set_switch_is_enabled(Atari2600Switch input, bool state) = 0; diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index b82deb045..9c7346782 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -26,7 +26,7 @@ namespace CRTMachine { that optionally provide a speaker, and that nominate a clock rate and can announce to a delegate should that clock rate change. */ -class Machine: public ROMMachine::Machine { +class Machine { public: /*! Causes the machine to set up its CRT and, if it has one, speaker. The caller guarantees diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 2933f7f5b..4fcfd3157 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -112,7 +112,7 @@ class ConcreteMachine: public JoystickMachine::Machine { public: - ConcreteMachine() : + ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : z80_(*this), sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider), ay_(audio_queue_), @@ -122,6 +122,19 @@ class ConcreteMachine: set_clock_rate(3579545); joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick); + + const auto roms = rom_fetcher( + "ColecoVision", + { "coleco.rom" }); + + if(!roms[0]) { + throw ROMMachine::Error::MissingROMs; + } + + bios_ = *roms[0]; + bios_.resize(8192); + + insert_media(target.media); } ~ConcreteMachine() { @@ -153,11 +166,6 @@ class ConcreteMachine: z80_.run_for(cycles); } - void configure_as_target(const Analyser::Static::Target *target) override { - // Insert the media. - insert_media(target->media); - } - bool insert_media(const Analyser::Static::Media &media) override { if(!media.cartridges.empty()) { const auto &segment = media.cartridges.front()->get_segments().front(); @@ -181,20 +189,6 @@ class ConcreteMachine: return true; } - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - auto roms = roms_with_names( - "ColecoVision", - { "coleco.rom" }); - - if(!roms[0]) return false; - - bios_ = *roms[0]; - bios_.resize(8192); - - return true; - } - // MARK: Z80::BusHandler forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { // The SN76489 will use its ready line to trigger the Z80's wait for three @@ -408,8 +402,8 @@ class ConcreteMachine: using namespace Coleco::Vision; -Machine *Machine::ColecoVision() { - return new ConcreteMachine; +Machine *Machine::ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + return new ConcreteMachine(*target, rom_fetcher); } Machine::~Machine() {} diff --git a/Machines/ColecoVision/ColecoVision.hpp b/Machines/ColecoVision/ColecoVision.hpp index 258f22e82..54f989476 100644 --- a/Machines/ColecoVision/ColecoVision.hpp +++ b/Machines/ColecoVision/ColecoVision.hpp @@ -9,13 +9,16 @@ #ifndef ColecoVision_hpp #define ColecoVision_hpp +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" + namespace Coleco { namespace Vision { class Machine { public: virtual ~Machine(); - static Machine *ColecoVision(); + static Machine *ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; } diff --git a/Machines/Commodore/1540/C1540.hpp b/Machines/Commodore/1540/C1540.hpp index 22b94ea50..a3d17b17d 100644 --- a/Machines/Commodore/1540/C1540.hpp +++ b/Machines/Commodore/1540/C1540.hpp @@ -9,6 +9,25 @@ #ifndef Commodore1540_hpp #define Commodore1540_hpp +namespace Commodore { +namespace C1540 { + +/// Defines the type of drive this 1540 hardware is configured as. +enum class Personality { + C1540, + C1541 +}; + +/* + Implementation note: this is defined up here so that it precedes + C1540Base.hpp below. The alternative option was to factor it out, + but the whole point of the C1540.hpp/C1540Base.hpp split is supposed + to be to create a single file of public interface. +*/ + +} +} + #include "../SerialBus.hpp" #include "../../ROMMachine.hpp" #include "../../../Storage/Disk/Disk.hpp" @@ -20,18 +39,9 @@ namespace C1540 { /*! Provides an emulation of the C1540. */ -class Machine: public MachineBase, public ROMMachine::Machine { +class Machine: public MachineBase { public: - enum Personality { - C1540, - C1541 - }; - Machine(Personality p); - - /*! - Sets the source for this drive's ROM image. - */ - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names); + Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher); /*! Sets the serial bus to which this drive should attach itself. @@ -43,9 +53,6 @@ class Machine: public MachineBase, public ROMMachine::Machine { /// Inserts @c disk into the drive. void set_disk(std::shared_ptr disk); - - private: - Personality personality_; }; } diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index 0db3a2ae2..be61e8cb8 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -16,7 +16,7 @@ using namespace Commodore::C1540; -MachineBase::MachineBase() : +MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : Storage::Disk::Controller(1000000), m6502_(*this), drive_(new Storage::Disk::Drive(1000000, 300, 2)), @@ -38,9 +38,22 @@ MachineBase::MachineBase() : // attach the only drive there is set_drive(drive_); + + std::string rom_name; + switch(personality) { + case Personality::C1540: rom_name = "1540.bin"; break; + case Personality::C1541: rom_name = "1541.bin"; break; + } + + auto roms = rom_fetcher("Commodore1540", {rom_name}); + if(!roms[0]) { + throw ROMMachine::Error::MissingROMs; + } + std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size())); } -Machine::Machine(Commodore::C1540::Machine::Personality personality) : personality_(personality) {} +Machine::Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : + MachineBase(personality, rom_fetcher) {} void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) { Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus); @@ -82,19 +95,6 @@ Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation, return Cycles(1); } -bool Machine::set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) { - std::string rom_name; - switch(personality_) { - case Personality::C1540: rom_name = "1540.bin"; break; - case Personality::C1541: rom_name = "1541.bin"; break; - } - - auto roms = roms_with_names("Commodore1540", {rom_name}); - if(!roms[0]) return false; - std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size())); - return true; -} - void Machine::set_disk(std::shared_ptr disk) { drive_->set_disk(disk); } diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp index 309d64f82..25e938371 100644 --- a/Machines/Commodore/1540/Implementation/C1540Base.hpp +++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp @@ -19,6 +19,8 @@ #include "../../../../Storage/Disk/Controller/DiskController.hpp" +#include "../C1540.hpp" + namespace Commodore { namespace C1540 { @@ -125,7 +127,7 @@ class MachineBase: public Storage::Disk::Controller { public: - MachineBase(); + MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher); // 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 31a8f5ca2..088cd49f6 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -62,18 +62,6 @@ enum JoystickInput { Fire = 0x20 }; -enum ROM { - CharactersDanish = 0, - CharactersEnglish, - CharactersJapanese, - CharactersSwedish, - KernelDanish, - KernelJapanese, - KernelNTSC, - KernelPAL, - KernelSwedish -}; - /*! Models the user-port VIA, which is the Vic's connection point for controlling its tape recorder; sensing the presence or absence of a tape and controlling the tape motor; and reading the current @@ -304,7 +292,7 @@ class ConcreteMachine: public ClockingHint::Observer, public Activity::Source { public: - ConcreteMachine() : + ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), user_port_via_port_handler_(new UserPortVIA), keyboard_via_port_handler_(new KeyboardVIA), @@ -332,127 +320,68 @@ class ConcreteMachine: // install a joystick joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_)); - } - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - rom_fetcher_ = roms_with_names; - - auto roms = roms_with_names( - "Vic20", - { - "characters-danish.bin", - "characters-english.bin", - "characters-japanese.bin", - "characters-swedish.bin", - "kernel-danish.bin", - "kernel-japanese.bin", - "kernel-ntsc.bin", - "kernel-pal.bin", - "kernel-swedish.bin", - "basic.bin" - }); - - for(std::size_t index = 0; index < roms.size(); ++index) { - auto &data = roms[index]; - if(!data) return false; - if(index < 9) roms_[index] = std::move(*data); else basic_rom_ = std::move(*data); + std::vector rom_names = { "basic.bin" }; + switch(target.region) { + default: + rom_names.push_back("characters-english.bin"); + rom_names.push_back("kernel-pal.bin"); + break; + case Analyser::Static::Commodore::Target::Region::American: + rom_names.push_back("characters-english.bin"); + rom_names.push_back("kernel-ntsc.bin"); + break; + case Analyser::Static::Commodore::Target::Region::Danish: + rom_names.push_back("characters-danish.bin"); + rom_names.push_back("kernel-danish.bin"); + break; + case Analyser::Static::Commodore::Target::Region::Japanese: + rom_names.push_back("characters-japanese.bin"); + rom_names.push_back("kernel-japanese.bin"); + break; + case Analyser::Static::Commodore::Target::Region::Swedish: + rom_names.push_back("characters-swedish.bin"); + rom_names.push_back("kernel-japanese.bin"); + break; } + const auto roms = rom_fetcher("Vic20", rom_names); + + for(const auto &rom: roms) { + if(!rom) { + 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. - for(std::size_t index = 0; index < 4; ++index) roms_[index].resize(4096); + character_rom_.resize(4096); // Kernel ROMs and the BASIC ROM should be 8kb. - for(std::size_t index = 4; index < roms.size(); ++index) roms_[index].resize(8192); + kernel_rom_.resize(8192); - return true; - } - - void configure_as_target(const Analyser::Static::Target *target) override final { - commodore_target_ = *dynamic_cast(target); - - if(!commodore_target_.loading_command.empty()) { - type_string(commodore_target_.loading_command); - } - - if(commodore_target_.has_c1540) { + if(target.has_c1540) { // construct the 1540 - c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Machine::C1540)); + c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Personality::C1540, rom_fetcher)); // attach it to the serial bus c1540_->set_serial_bus(serial_bus_); - // give it a means to obtain its ROM - c1540_->set_rom_fetcher(rom_fetcher_); - // give it a little warm up c1540_->run_for(Cycles(2000000)); } - insert_media(target->media); - } - - bool insert_media(const Analyser::Static::Media &media) override final { - if(!media.tapes.empty()) { - tape_->set_tape(media.tapes.front()); - } - - if(!media.disks.empty() && c1540_) { - c1540_->set_disk(media.disks.front()); - } - - if(!media.cartridges.empty()) { - rom_address_ = 0xa000; - std::vector rom_image = media.cartridges.front()->get_segments().front().data; - rom_length_ = static_cast(rom_image.size()); - - rom_ = rom_image; - rom_.resize(0x2000); - } - - set_use_fast_tape(); - - return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty(); - } - - void set_key_state(uint16_t key, bool is_pressed) override final { - if(key != KeyRestore) - keyboard_via_port_handler_->set_key_state(key, is_pressed); - else - user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed); - } - - void clear_all_keys() override final { - keyboard_via_port_handler_->clear_all_keys(); - } - - std::vector> &get_joysticks() override { - return joysticks_; - } - - void set_ntsc_6560() { - set_clock_rate(1022727); - if(mos6560_) { - mos6560_->set_output_mode(MOS::MOS6560::OutputMode::NTSC); - mos6560_->set_clock_rate(1022727); - } - } - - void set_pal_6560() { - set_clock_rate(1108404); - if(mos6560_) { - mos6560_->set_output_mode(MOS::MOS6560::OutputMode::PAL); - mos6560_->set_clock_rate(1108404); - } - } - - void set_memory_map(Analyser::Static::Commodore::Target::MemoryModel memory_model, Analyser::Static::Commodore::Target::Region region) { // Determine PAL/NTSC - if(region == Analyser::Static::Commodore::Target::Region::American || region == Analyser::Static::Commodore::Target::Region::Japanese) { + if(target.region == Analyser::Static::Commodore::Target::Region::American || target.region == Analyser::Static::Commodore::Target::Region::Japanese) { // NTSC - set_ntsc_6560(); + set_clock_rate(1022727); + output_mode_ = MOS::MOS6560::OutputMode::NTSC; } else { // PAL - set_pal_6560(); + set_clock_rate(1108404); + output_mode_ = MOS::MOS6560::OutputMode::PAL; } // Initialise the memory maps as all pointing to nothing @@ -465,7 +394,7 @@ class ConcreteMachine: write_to_map(processor_write_memory_map_, &ram_[baseaddr], baseaddr, length); // Add 6502-visible RAM as requested - switch(memory_model) { + switch(target.memory_model) { case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded: // The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000. set_ram(0x0000, 0x0400); @@ -512,39 +441,53 @@ class ConcreteMachine: write_to_map(processor_read_memory_map_, basic_rom_.data(), 0xc000, static_cast(basic_rom_.size())); // install the system ROM - ROM character_rom; - ROM kernel_rom; - switch(region) { - default: - character_rom = CharactersEnglish; - kernel_rom = KernelPAL; - break; - case Analyser::Static::Commodore::Target::Region::American: - character_rom = CharactersEnglish; - kernel_rom = KernelNTSC; - break; - case Analyser::Static::Commodore::Target::Region::Danish: - character_rom = CharactersDanish; - kernel_rom = KernelDanish; - break; - case Analyser::Static::Commodore::Target::Region::Japanese: - character_rom = CharactersJapanese; - kernel_rom = KernelJapanese; - break; - case Analyser::Static::Commodore::Target::Region::Swedish: - character_rom = CharactersSwedish; - kernel_rom = KernelSwedish; - break; + write_to_map(processor_read_memory_map_, character_rom_.data(), 0x8000, static_cast(character_rom_.size())); + write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast(character_rom_.size())); + write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast(kernel_rom_.size())); + + insert_media(target.media); + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } + } + + bool insert_media(const Analyser::Static::Media &media) override final { + if(!media.tapes.empty()) { + tape_->set_tape(media.tapes.front()); } - write_to_map(processor_read_memory_map_, roms_[character_rom].data(), 0x8000, static_cast(roms_[character_rom].size())); - write_to_map(mos6560_bus_handler_.video_memory_map, roms_[character_rom].data(), 0x0000, static_cast(roms_[character_rom].size())); - write_to_map(processor_read_memory_map_, roms_[kernel_rom].data(), 0xe000, static_cast(roms_[kernel_rom].size())); + if(!media.disks.empty() && c1540_) { + c1540_->set_disk(media.disks.front()); + } - // install the inserted ROM if there is one - if(!rom_.empty()) { + if(!media.cartridges.empty()) { + rom_address_ = 0xa000; + std::vector rom_image = media.cartridges.front()->get_segments().front().data; + rom_length_ = static_cast(rom_image.size()); + + rom_ = rom_image; + rom_.resize(0x2000); write_to_map(processor_read_memory_map_, rom_.data(), rom_address_, rom_length_); } + + set_use_fast_tape(); + + return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty(); + } + + void set_key_state(uint16_t key, bool is_pressed) override final { + if(key != KeyRestore) + keyboard_via_port_handler_->set_key_state(key, is_pressed); + else + user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed); + } + + void clear_all_keys() override final { + keyboard_via_port_handler_->clear_all_keys(); + } + + std::vector> &get_joysticks() override { + return joysticks_; } // to satisfy CPU::MOS6502::Processor @@ -680,8 +623,8 @@ class ConcreteMachine: void setup_output(float aspect_ratio) override final { mos6560_.reset(new MOS::MOS6560::MOS6560(mos6560_bus_handler_)); mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20. - // Make a guess: PAL. Without setting a clock rate the 6560 isn't fully set up so contractually something must be set. - set_memory_map(commodore_target_.memory_model, commodore_target_.region); + mos6560_->set_output_mode(output_mode_); + mos6560_->set_clock_rate(get_clock_rate()); } void close_output() override final { @@ -760,12 +703,8 @@ class ConcreteMachine: void update_video() { mos6560_->run_for(cycles_since_mos6560_update_.flush()); } - Analyser::Static::Commodore::Target commodore_target_; - CPU::MOS6502::Processor m6502_; - std::vector roms_[9]; - std::vector character_rom_; std::vector basic_rom_; std::vector kernel_rom_; @@ -775,8 +714,6 @@ class ConcreteMachine: uint8_t ram_[0x8000]; uint8_t colour_ram_[0x0400]; - std::function>>(const std::string &machine, const std::vector &names)> rom_fetcher_; - uint8_t *processor_read_memory_map_[64]; uint8_t *processor_write_memory_map_[64]; void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) { @@ -794,6 +731,7 @@ class ConcreteMachine: Cycles cycles_since_mos6560_update_; Vic6560BusHandler mos6560_bus_handler_; + MOS::MOS6560::OutputMode output_mode_; std::unique_ptr> mos6560_; std::shared_ptr user_port_via_port_handler_; std::shared_ptr keyboard_via_port_handler_; @@ -822,8 +760,10 @@ class ConcreteMachine: using namespace Commodore::Vic20; -Machine *Machine::Vic20() { - return new Vic20::ConcreteMachine; +Machine *Machine::Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::Commodore::Target; + const Target *const commodore_target = dynamic_cast(target); + return new Vic20::ConcreteMachine(*commodore_target, rom_fetcher); } Machine::~Machine() {} diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index c3616ab6c..ea5e7d0b7 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -10,6 +10,11 @@ #define Vic20_hpp #include "../../../Configurable/Configurable.hpp" +#include "../../../Analyser/Static/StaticAnalyser.hpp" +#include "../../ROMMachine.hpp" + +#include +#include namespace Commodore { namespace Vic20 { @@ -22,7 +27,7 @@ class Machine { virtual ~Machine(); /// Creates and returns a Vic-20. - static Machine *Vic20(); + static Machine *Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; } diff --git a/Machines/ConfigurationTarget.hpp b/Machines/ConfigurationTarget.hpp index a86f4f456..a1fdf57c5 100644 --- a/Machines/ConfigurationTarget.hpp +++ b/Machines/ConfigurationTarget.hpp @@ -22,9 +22,6 @@ namespace ConfigurationTarget { */ class Machine { public: - /// Instructs the machine to configure itself as described by @c target and insert the included media. - virtual void configure_as_target(const Analyser::Static::Target *target) = 0; - /*! Requests that the machine insert @c media as a modification to current state diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 22565379f..defa8a718 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -49,10 +49,10 @@ class ConcreteMachine: public Utility::TypeRecipient, public Activity::Source { public: - ConcreteMachine() : - m6502_(*this), - sound_generator_(audio_queue_), - speaker_(sound_generator_) { + ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : + m6502_(*this), + sound_generator_(audio_queue_), + speaker_(sound_generator_) { memset(key_states_, 0, sizeof(key_states_)); for(int c = 0; c < 16; c++) memset(roms_[c], 0xff, 16384); @@ -61,60 +61,53 @@ class ConcreteMachine: set_clock_rate(2000000); speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider); + + std::vector rom_names = {"basic.rom", "os.rom"}; + if(target.has_adfs) { + rom_names.push_back("ADFS-E00_1.rom"); + rom_names.push_back("ADFS-E00_2.rom"); + } + const size_t dfs_rom_position = rom_names.size(); + if(target.has_dfs) { + rom_names.push_back("DFS-1770-2.20.rom"); + } + const auto roms = rom_fetcher("Electron", rom_names); + + for(const auto &rom: roms) { + if(!rom) { + throw ROMMachine::Error::MissingROMs; + } + } + set_rom(ROM::BASIC, *roms[0], false); + set_rom(ROM::OS, *roms[1], false); + + if(target.has_dfs || target.has_adfs) { + plus3_.reset(new Plus3); + + if(target.has_dfs) { + set_rom(ROM::Slot0, *roms[dfs_rom_position], true); + } + if(target.has_adfs) { + set_rom(ROM::Slot4, *roms[2], true); + set_rom(ROM::Slot5, *roms[3], true); + } + } + + insert_media(target.media); + + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } + + if(target.should_shift_restart) { + shift_restart_counter_ = 1000000; + } } ~ConcreteMachine() { audio_queue_.flush(); } - void set_rom(ROMSlot slot, const std::vector &data, bool is_writeable) override final { - uint8_t *target = nullptr; - switch(slot) { - case ROMSlotDFS: dfs_ = data; return; - case ROMSlotADFS1: adfs1_ = data; return; - case ROMSlotADFS2: adfs2_ = data; return; - - case ROMSlotOS: target = os_; break; - default: - target = roms_[slot]; - rom_write_masks_[slot] = is_writeable; - break; - } - - // Copy in, with mirroring. - std::size_t rom_ptr = 0; - while(rom_ptr < 16384) { - std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size()); - std::memcpy(&target[rom_ptr], data.data(), size_to_copy); - rom_ptr += size_to_copy; - } - rom_inserted_[slot] = true; - } - - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - auto roms = roms_with_names( - "Electron", - { - "DFS-1770-2.20.rom", - "ADFS-E00_1.rom", "ADFS-E00_2.rom", - "basic.rom", "os.rom" - }); - ROMSlot slots[] = { - ROMSlotDFS, - ROMSlotADFS1, ROMSlotADFS2, - ROMSlotBASIC, ROMSlotOS - }; - - for(std::size_t index = 0; index < roms.size(); ++index) { - auto &data = roms[index]; - if(!data) return false; - set_rom(slots[index], *data, false); - } - - return true; - } - void set_key_state(uint16_t key, bool isPressed) override final { if(key == KeyBreak) { m6502_.set_reset_line(isPressed); @@ -131,32 +124,6 @@ class ConcreteMachine: if(is_holding_shift_) set_key_state(KeyShift, true); } - void configure_as_target(const Analyser::Static::Target *target) override final { - auto *const acorn_target = dynamic_cast(target); - - if(!acorn_target->loading_command.empty()) { - type_string(acorn_target->loading_command); - } - - if(acorn_target->should_shift_restart) { - shift_restart_counter_ = 1000000; - } - - if(acorn_target->has_dfs || acorn_target->has_adfs) { - plus3_.reset(new Plus3); - - if(acorn_target->has_dfs) { - set_rom(ROMSlot0, dfs_, true); - } - if(acorn_target->has_adfs) { - set_rom(ROMSlot4, adfs1_, true); - set_rom(ROMSlot5, adfs2_, true); - } - } - - insert_media(target->media); - } - bool insert_media(const Analyser::Static::Media &media) override final { if(!media.tapes.empty()) { tape_.set_tape(media.tapes.front()); @@ -167,11 +134,11 @@ class ConcreteMachine: plus3_->set_disk(media.disks.front(), 0); } - ROMSlot slot = ROMSlot12; + ROM slot = ROM::Slot12; for(std::shared_ptr cartridge : media.cartridges) { - const ROMSlot first_slot_tried = slot; - while(rom_inserted_[slot]) { - slot = static_cast((static_cast(slot) + 1)&15); + const ROM first_slot_tried = slot; + while(rom_inserted_[static_cast(slot)]) { + slot = static_cast((static_cast(slot) + 1) & 15); if(slot == first_slot_tried) return false; } set_rom(slot, cartridge->get_segments().front().data, false); @@ -258,7 +225,7 @@ class ConcreteMachine: } // latch the paged ROM in case external hardware is being emulated - active_rom_ = (Electron::ROMSlot)(*value & 0xf); + active_rom_ = *value & 0xf; // apply the ULA's test if(*value & 0x08) { @@ -363,7 +330,7 @@ class ConcreteMachine: } } if(basic_is_active_) { - *value &= roms_[ROMSlotBASIC][address & 16383]; + *value &= roms_[static_cast(ROM::BASIC)][address & 16383]; } } else if(rom_write_masks_[active_rom_]) { roms_[active_rom_][address & 16383] = *value; @@ -494,6 +461,48 @@ class ConcreteMachine: } private: + enum class ROM { + Slot0 = 0, + Slot1, Slot2, Slot3, + Slot4, Slot5, Slot6, Slot7, + + Keyboard = 8, Slot9, + BASIC = 10, Slot11, + + Slot12, Slot13, Slot14, Slot15, + + OS, DFS, + ADFS1, ADFS2 + }; + + /*! + Sets the contents of @c slot to @c data. If @c is_writeable is @c true then writing to the slot + is enabled: it acts as if it were sideways RAM. Otherwise the slot is modelled as containing ROM. + */ + void set_rom(ROM slot, const std::vector &data, bool is_writeable) { + uint8_t *target = nullptr; + switch(slot) { + case ROM::DFS: dfs_ = data; return; + case ROM::ADFS1: adfs1_ = data; return; + case ROM::ADFS2: adfs2_ = data; return; + + case ROM::OS: target = os_; break; + default: + target = roms_[static_cast(slot)]; + rom_write_masks_[static_cast(slot)] = is_writeable; + break; + } + + // Copy in, with mirroring. + std::size_t rom_ptr = 0; + while(rom_ptr < 16384) { + std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size()); + std::memcpy(&target[rom_ptr], data.data(), size_to_copy); + rom_ptr += size_to_copy; + } + rom_inserted_[static_cast(slot)] = true; + } + // MARK: - Work deferral updates. inline void update_display() { if(cycles_since_display_update_ > 0) { @@ -540,7 +549,7 @@ class ConcreteMachine: std::vector dfs_, adfs1_, adfs2_; // Paging - ROMSlot active_rom_ = ROMSlot::ROMSlot0; + int active_rom_ = static_cast(ROM::Slot0); bool keyboard_is_active_ = false; bool basic_is_active_ = false; @@ -590,8 +599,10 @@ class ConcreteMachine: using namespace Electron; -Machine *Machine::Electron() { - return new Electron::ConcreteMachine; +Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::Acorn::Target; + const Target *const acorn_target = dynamic_cast(target); + return new Electron::ConcreteMachine(*acorn_target, rom_fetcher); } Machine::~Machine() {} diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index ea88fa17c..90cf12993 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -10,6 +10,8 @@ #define Electron_hpp #include "../../Configurable/Configurable.hpp" +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" #include #include @@ -17,20 +19,6 @@ namespace Electron { -enum ROMSlot: uint8_t { - ROMSlot0 = 0, - ROMSlot1, ROMSlot2, ROMSlot3, - ROMSlot4, ROMSlot5, ROMSlot6, ROMSlot7, - - ROMSlotKeyboard = 8, ROMSlot9, - ROMSlotBASIC = 10, ROMSlot11, - - ROMSlot12, ROMSlot13, ROMSlot14, ROMSlot15, - - ROMSlotOS, ROMSlotDFS, - ROMSlotADFS1, ROMSlotADFS2 -}; - /// @returns The options available for an Electron. std::vector> get_options(); @@ -45,13 +33,7 @@ class Machine { virtual ~Machine(); /// Creates and returns an Electron. - static Machine *Electron(); - - /*! - Sets the contents of @c slot to @c data. If @c is_writeable is @c true then writing to the slot - is enabled: it acts as if it were sideways RAM. Otherwise the slot is modelled as containing ROM. - */ - virtual void set_rom(ROMSlot slot, const std::vector &data, bool is_writeable) = 0; + static Machine *Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 6b36bd1b8..fec603a7d 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -91,7 +91,7 @@ class ConcreteMachine: public ClockingHint::Observer, public Activity::Source { public: - ConcreteMachine(): + ConcreteMachine(const Analyser::Static::MSX::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): z80_(*this), i8255_(i8255_port_handler_), ay_(audio_queue_), @@ -112,6 +112,51 @@ class ConcreteMachine: // Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC. mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f}); + + // Fetch the necessary ROMs. + std::vector rom_names = {"msx.rom"}; + if(target.has_disk_drive) { + rom_names.push_back("disk.rom"); + } + const auto roms = rom_fetcher("MSX", rom_names); + + if(!roms[0] || (target.has_disk_drive && !roms[1])) { + throw ROMMachine::Error::MissingROMs; + } + + memory_slots_[0].source = std::move(*roms[0]); + memory_slots_[0].source.resize(32768); + + for(size_t c = 0; c < 8; ++c) { + for(size_t slot = 0; slot < 3; ++slot) { + memory_slots_[slot].read_pointers[c] = unpopulated_; + memory_slots_[slot].write_pointers[c] = scratch_; + } + + memory_slots_[3].read_pointers[c] = + memory_slots_[3].write_pointers[c] = &ram_[c * 8192]; + } + + map(0, 0, 0, 32768); + page_memory(0); + + // 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[1]); + memory_slots_[2].source.resize(16384); + + map(2, 0, 0x4000, 0x2000); + unmap(2, 0x6000, 0x2000); + } + + // Insert the media. + insert_media(target.media); + + // Type whatever has been requested. + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } } ~ConcreteMachine() { @@ -152,25 +197,6 @@ class ConcreteMachine: } } - void configure_as_target(const Analyser::Static::Target *target) override { - auto *const msx_target = dynamic_cast(target); - - // Add a disk cartridge if any disks were supplied. - if(msx_target->has_disk_drive) { - map(2, 0, 0x4000, 0x2000); - unmap(2, 0x6000, 0x2000); - memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source)); - } - - // Insert the media. - insert_media(target->media); - - // Type whatever has been requested. - if(!msx_target->loading_command.empty()) { - type_string(msx_target->loading_command); - } - } - bool insert_media(const Analyser::Static::Media &media) override { if(!media.cartridges.empty()) { const auto &segment = media.cartridges.front()->get_segments().front(); @@ -467,39 +493,6 @@ class ConcreteMachine: audio_queue_.perform(); } - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - auto roms = roms_with_names( - "MSX", - { - "msx.rom", - "disk.rom" - }); - - if(!roms[0] || !roms[1]) return false; - - memory_slots_[0].source = std::move(*roms[0]); - memory_slots_[0].source.resize(32768); - - memory_slots_[2].source = std::move(*roms[1]); - memory_slots_[2].source.resize(16384); - - for(size_t c = 0; c < 8; ++c) { - for(size_t slot = 0; slot < 3; ++slot) { - memory_slots_[slot].read_pointers[c] = unpopulated_; - memory_slots_[slot].write_pointers[c] = scratch_; - } - - memory_slots_[3].read_pointers[c] = - memory_slots_[3].write_pointers[c] = &ram_[c * 8192]; - } - - map(0, 0, 0, 32768); - page_memory(0); - - return true; - } - void set_keyboard_line(int line) { selected_key_line_ = line; } @@ -683,8 +676,10 @@ class ConcreteMachine: using namespace MSX; -Machine *Machine::MSX() { - return new ConcreteMachine; +Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + using Target = Analyser::Static::MSX::Target; + const Target *const msx_target = dynamic_cast(target); + return new ConcreteMachine(*msx_target, rom_fetcher); } Machine::~Machine() {} diff --git a/Machines/MSX/MSX.hpp b/Machines/MSX/MSX.hpp index 90e1445fb..1088786e2 100644 --- a/Machines/MSX/MSX.hpp +++ b/Machines/MSX/MSX.hpp @@ -10,17 +10,22 @@ #define MSX_hpp #include "../../Configurable/Configurable.hpp" +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" + +#include +#include namespace MSX { +std::vector> get_options(); + class Machine { public: virtual ~Machine(); - static Machine *MSX(); + static Machine *MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; -std::vector> get_options(); - } #endif /* MSX_hpp */ diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index ecf93e322..bddc76f69 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -206,9 +206,8 @@ template class Co public Machine { public: - ConcreteMachine(const Analyser::Static::Oric::Target *target) : + ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), - rom_type_(target ? target->rom : Analyser::Static::Oric::Target::ROM::BASIC10), ay8910_(audio_queue_), speaker_(ay8910_), via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_), @@ -222,16 +221,9 @@ template class Co if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) { diskii_.set_clocking_hint_observer(this); } - } - ~ConcreteMachine() { - audio_queue_.flush(); - } - - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { std::vector rom_names = {"colour.rom"}; - switch(rom_type_) { + switch(target.rom) { case Analyser::Static::Oric::Target::ROM::BASIC10: rom_names.push_back("basic10.rom"); break; case Analyser::Static::Oric::Target::ROM::BASIC11: rom_names.push_back("basic11.rom"); break; case Analyser::Static::Oric::Target::ROM::Pravetz: rom_names.push_back("pravetz.rom"); break; @@ -239,15 +231,17 @@ template class Co switch(disk_interface) { default: break; case Analyser::Static::Oric::Target::DiskInterface::Microdisc: rom_names.push_back("microdisc.rom"); break; - case Analyser::Static::Oric::Target::DiskInterface::Pravetz: rom_names.push_back("8dos.rom"); break; + case Analyser::Static::Oric::Target::DiskInterface::Pravetz: rom_names.push_back("8dos.rom"); break; } - auto roms = roms_with_names("Oric", rom_names); + const auto roms = rom_fetcher("Oric", rom_names); for(std::size_t index = 0; index < roms.size(); ++index) { - if(!roms[index]) return false; + if(!roms[index]) { + throw ROMMachine::Error::MissingROMs; + } } - + colour_rom_ = std::move(*roms[0]); rom_ = std::move(*roms[1]); @@ -261,8 +255,10 @@ template class Co pravetz_rom_ = std::move(*roms[2]); pravetz_rom_.resize(512); - auto state_machine_rom = roms_with_names("DiskII", {"state-machine-16.rom"}); - if(!state_machine_rom[0]) return false; + auto state_machine_rom = rom_fetcher("DiskII", {"state-machine-16.rom"}); + if(!state_machine_rom[0]) { + throw ROMMachine::Error::MissingROMs; + } diskii_.set_state_machine(*state_machine_rom[0]); } break; } @@ -271,9 +267,35 @@ template class Co rom_.resize(16384); paged_rom_ = rom_.data(); - if(video_output_) video_output_->set_colour_rom(colour_rom_); + switch(target.disk_interface) { + default: break; + case Analyser::Static::Oric::Target::DiskInterface::Microdisc: + microdisc_did_change_paging_flags(µdisc_); + microdisc_.set_delegate(this); + break; + } - return true; + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } + + switch(target.rom) { + case Analyser::Static::Oric::Target::ROM::BASIC10: + tape_get_byte_address_ = 0xe630; + tape_speed_address_ = 0x67; + break; + case Analyser::Static::Oric::Target::ROM::BASIC11: + case Analyser::Static::Oric::Target::ROM::Pravetz: + tape_get_byte_address_ = 0xe6c9; + tape_speed_address_ = 0x024d; + break; + } + + insert_media(target.media); + } + + ~ConcreteMachine() { + audio_queue_.flush(); } void set_key_state(uint16_t key, bool is_pressed) override final { @@ -292,41 +314,10 @@ template class Co use_fast_tape_hack_ = activate; } - // to satisfy ConfigurationTarget::Machine - void configure_as_target(const Analyser::Static::Target *target) override final { - auto *const oric_target = dynamic_cast(target); - - switch(oric_target->disk_interface) { - default: break; - case Analyser::Static::Oric::Target::DiskInterface::Microdisc: - microdisc_did_change_paging_flags(µdisc_); - microdisc_.set_delegate(this); - break; - } - - if(!oric_target->loading_command.empty()) { - type_string(oric_target->loading_command); - } - - switch(rom_type_) { - case Analyser::Static::Oric::Target::ROM::BASIC10: - tape_get_byte_address_ = 0xe630; - tape_speed_address_ = 0x67; - break; - case Analyser::Static::Oric::Target::ROM::BASIC11: - case Analyser::Static::Oric::Target::ROM::Pravetz: - tape_get_byte_address_ = 0xe6c9; - tape_speed_address_ = 0x024d; - break; - } - - insert_media(target->media); - } - bool insert_media(const Analyser::Static::Media &media) override final { bool inserted = false; - if(media.tapes.size()) { + if(!media.tapes.empty()) { tape_player_.set_tape(media.tapes.front()); inserted = true; } @@ -587,7 +578,6 @@ template class Co CPU::MOS6502::Processor m6502_; // RAM and ROM - Analyser::Static::Oric::Target::ROM rom_type_; std::vector rom_, microdisc_rom_, colour_rom_; uint8_t ram_[65536]; Cycles cycles_since_video_update_; @@ -650,13 +640,13 @@ template class Co using namespace Oric; -Machine *Machine::Oric(const Analyser::Static::Target *target_hint) { +Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) { auto *const oric_target = dynamic_cast(target_hint); using DiskInterface = Analyser::Static::Oric::Target::DiskInterface; switch(oric_target->disk_interface) { - default: return new ConcreteMachine(oric_target); - case DiskInterface::Microdisc: return new ConcreteMachine(oric_target); - case DiskInterface::Pravetz: return new ConcreteMachine(oric_target); + default: return new ConcreteMachine(*oric_target, rom_fetcher); + case DiskInterface::Microdisc: return new ConcreteMachine(*oric_target, rom_fetcher); + case DiskInterface::Pravetz: return new ConcreteMachine(*oric_target, rom_fetcher); } } diff --git a/Machines/Oric/Oric.hpp b/Machines/Oric/Oric.hpp index 3f827b8a4..8e3977bdf 100644 --- a/Machines/Oric/Oric.hpp +++ b/Machines/Oric/Oric.hpp @@ -11,6 +11,7 @@ #include "../../Configurable/Configurable.hpp" #include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" namespace Oric { @@ -25,7 +26,7 @@ class Machine { virtual ~Machine(); /// Creates and returns an Oric. - static Machine *Oric(const Analyser::Static::Target *target_hint); + static Machine *Oric(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); }; } diff --git a/Machines/ROMMachine.hpp b/Machines/ROMMachine.hpp index a397f6095..a42942563 100644 --- a/Machines/ROMMachine.hpp +++ b/Machines/ROMMachine.hpp @@ -16,13 +16,18 @@ namespace ROMMachine { +/*! + 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. + + The caller will supply the idiomatic name of the machine plus a vector of the names of ROM files that it expects + to be present. The recevier should return a vector of unique_ptrs that either contain the contents of the + ROM from @c names that corresponds by index, or else are the nullptr +*/ typedef std::function>>(const std::string &machine, const std::vector &names)> ROMFetcher; -struct Machine { - /*! - Provides the machine with a way to obtain such ROMs as it needs. - */ - virtual bool set_rom_fetcher(const ROMFetcher &rom_with_name) { return true; } +enum class Error { + MissingROMs }; } diff --git a/Machines/Utility/MachineForTarget.cpp b/Machines/Utility/MachineForTarget.cpp index ea67873aa..b6c2105a2 100644 --- a/Machines/Utility/MachineForTarget.cpp +++ b/Machines/Utility/MachineForTarget.cpp @@ -25,36 +25,36 @@ namespace { ::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) { error = Machine::Error::None; - ::Machine::DynamicMachine *machine = nullptr; - switch(target->machine) { - case Analyser::Machine::AmstradCPC: machine = new Machine::TypedDynamicMachine(AmstradCPC::Machine::AmstradCPC()); break; - case Analyser::Machine::AppleII: machine = new Machine::TypedDynamicMachine(AppleII::Machine::AppleII()); break; - case Analyser::Machine::Atari2600: machine = new Machine::TypedDynamicMachine(Atari2600::Machine::Atari2600()); break; - case Analyser::Machine::ColecoVision: machine = new Machine::TypedDynamicMachine(Coleco::Vision::Machine::ColecoVision()); break; - case Analyser::Machine::Electron: machine = new Machine::TypedDynamicMachine(Electron::Machine::Electron()); break; - case Analyser::Machine::MSX: machine = new Machine::TypedDynamicMachine(MSX::Machine::MSX()); break; - case Analyser::Machine::Oric: machine = new Machine::TypedDynamicMachine(Oric::Machine::Oric(target)); break; - case Analyser::Machine::Vic20: machine = new Machine::TypedDynamicMachine(Commodore::Vic20::Machine::Vic20()); break; - case Analyser::Machine::ZX8081: machine = new Machine::TypedDynamicMachine(ZX8081::Machine::ZX8081(target)); break; - default: - error = Machine::Error::UnknownMachine; - return nullptr; - } + Machine::DynamicMachine *machine = nullptr; + try { +#define BindD(name, m) case Analyser::Machine::m: machine = new Machine::TypedDynamicMachine(name::Machine::m(target, rom_fetcher)); break; +#define Bind(m) BindD(m, m) + switch(target->machine) { + Bind(AmstradCPC) + Bind(AppleII) + Bind(Atari2600) + BindD(Coleco::Vision, ColecoVision) + Bind(Electron) + Bind(MSX) + Bind(Oric) + BindD(Commodore::Vic20, Vic20) + Bind(ZX8081) - // TODO: this shouldn't depend on CRT machine's inclusion of ROM machine. - CRTMachine::Machine *crt_machine = machine->crt_machine(); - if(crt_machine) { - if(!machine->crt_machine()->set_rom_fetcher(rom_fetcher)) { - delete machine; - error = Machine::Error::MissingROM; + default: + error = Machine::Error::UnknownMachine; return nullptr; } - } - - ConfigurationTarget::Machine *configuration_target = machine->configuration_target(); - if(configuration_target) { - machine->configuration_target()->configure_as_target(target); +#undef Bind + } catch(ROMMachine::Error construction_error) { + switch(construction_error) { + case ROMMachine::Error::MissingROMs: + error = Machine::Error::MissingROM; + break; + default: + error = Machine::Error::UnknownError; + break; + } } return machine; diff --git a/Machines/Utility/MachineForTarget.hpp b/Machines/Utility/MachineForTarget.hpp index ee862e066..0b156c7c7 100644 --- a/Machines/Utility/MachineForTarget.hpp +++ b/Machines/Utility/MachineForTarget.hpp @@ -21,6 +21,7 @@ namespace Machine { enum class Error { None, + UnknownError, UnknownMachine, MissingROM, NoTargets diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ed52a1e98..0a1c67cf4 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -66,7 +66,7 @@ template class ConcreteMachine: public CPU::Z80::BusHandler, public Machine { public: - ConcreteMachine() : + ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : z80_(*this), tape_player_(ZX8081ClockRate), ay_(audio_queue_), @@ -74,6 +74,55 @@ template class ConcreteMachine: set_clock_rate(ZX8081ClockRate); speaker_.set_input_rate(static_cast(ZX8081ClockRate) / 2.0f); clear_all_keys(); + + const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM; + const auto roms = rom_fetcher("ZX8081", { use_zx81_rom ? "zx81.rom" : "zx80.rom" }); + if(!roms[0]) throw ROMMachine::Error::MissingROMs; + + rom_ = std::move(*roms[0]); + rom_.resize(use_zx81_rom ? 8192 : 4096); + + if(is_zx81) { + tape_trap_address_ = 0x37c; + tape_return_address_ = 0x380; + vsync_start_ = HalfCycles(32); + vsync_end_ = HalfCycles(64); + automatic_tape_motor_start_address_ = 0x0340; + automatic_tape_motor_end_address_ = 0x03c3; + } else { + tape_trap_address_ = 0x220; + tape_return_address_ = 0x248; + vsync_start_ = HalfCycles(26); + vsync_end_ = HalfCycles(66); + automatic_tape_motor_start_address_ = 0x0206; + automatic_tape_motor_end_address_ = 0x024d; + } + rom_mask_ = static_cast(rom_.size() - 1); + + switch(target.memory_model) { + case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded: + ram_.resize(1024); + ram_base_ = 16384; + ram_mask_ = 1023; + break; + case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB: + ram_.resize(16384); + ram_base_ = 16384; + ram_mask_ = 16383; + break; + case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB: + ram_.resize(65536); + ram_base_ = 8192; + ram_mask_ = 65535; + break; + } + Memory::Fuzz(ram_); + + if(!target.loading_command.empty()) { + type_string(target.loading_command); + } + + insert_media(target.media); } ~ConcreteMachine() { @@ -105,7 +154,7 @@ template class ConcreteMachine: video_->run_for(cycle.length); } - if(is_zx81_) horizontal_counter_ %= HalfCycles(Cycles(207)); + if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207)); if(!tape_advance_delay_) { tape_player_.run_for(cycle.length); } else { @@ -129,7 +178,7 @@ template class ConcreteMachine: set_vsync(false); } if(!(address & 2)) nmi_is_enabled_ = false; - if(!(address & 1)) nmi_is_enabled_ = is_zx81_; + if(!(address & 1)) nmi_is_enabled_ = is_zx81; // The below emulates the ZonX AY expansion device. if(is_zx81) { @@ -281,54 +330,6 @@ template class ConcreteMachine: z80_.run_for(cycles); } - void configure_as_target(const Analyser::Static::Target *target) override final { - auto *const zx8081_target = dynamic_cast(target); - is_zx81_ = zx8081_target->is_ZX81; - if(is_zx81_) { - rom_ = zx81_rom_; - tape_trap_address_ = 0x37c; - tape_return_address_ = 0x380; - vsync_start_ = HalfCycles(32); - vsync_end_ = HalfCycles(64); - automatic_tape_motor_start_address_ = 0x0340; - automatic_tape_motor_end_address_ = 0x03c3; - } else { - rom_ = zx8081_target->ZX80_uses_ZX81_ROM ? zx81_rom_ : zx80_rom_; - tape_trap_address_ = 0x220; - tape_return_address_ = 0x248; - vsync_start_ = HalfCycles(26); - vsync_end_ = HalfCycles(66); - automatic_tape_motor_start_address_ = 0x0206; - automatic_tape_motor_end_address_ = 0x024d; - } - rom_mask_ = static_cast(rom_.size() - 1); - - switch(zx8081_target->memory_model) { - case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded: - ram_.resize(1024); - ram_base_ = 16384; - ram_mask_ = 1023; - break; - case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB: - ram_.resize(16384); - ram_base_ = 16384; - ram_mask_ = 16383; - break; - case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB: - ram_.resize(65536); - ram_base_ = 8192; - ram_mask_ = 65535; - break; - } - Memory::Fuzz(ram_); - - if(!zx8081_target->loading_command.empty()) { - type_string(zx8081_target->loading_command); - } - - insert_media(target->media); - } - bool insert_media(const Analyser::Static::Media &media) override final { if(!media.tapes.empty()) { tape_player_.set_tape(media.tapes.front()); @@ -339,30 +340,10 @@ template class ConcreteMachine: } void type_string(const std::string &string) override final { - std::unique_ptr mapper(new CharacterMapper(is_zx81_)); + std::unique_ptr mapper(new CharacterMapper(is_zx81)); Utility::TypeRecipient::add_typer(string, std::move(mapper)); } - // Obtains the system ROMs. - bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { - const auto roms = roms_with_names( - "ZX8081", - { - "zx80.rom", "zx81.rom", - }); - - for(std::size_t index = 0; index < roms.size(); ++index) { - if(!roms[index]) return false; - } - - zx80_rom_ = std::move(*roms[0]); - zx81_rom_ = std::move(*roms[1]); - zx80_rom_.resize(4096); - zx81_rom_.resize(8192); - - return true; - } - // MARK: - Keyboard void set_key_state(uint16_t key, bool is_pressed) override final { if(is_pressed) @@ -436,7 +417,6 @@ template class ConcreteMachine: CPU::Z80::Processor z80_; std::unique_ptr