From 183cb519e79c18065bbe3c09044df1831b3465e8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 15 Jan 2023 22:51:17 -0500 Subject: [PATCH] Give autonomy to secondary slots. --- Machines/MSX/Cartridges/ASCII16kb.hpp | 4 +- Machines/MSX/Cartridges/ASCII8kb.hpp | 8 +- Machines/MSX/Cartridges/Konami.hpp | 6 +- Machines/MSX/Cartridges/KonamiWithSCC.hpp | 10 +- Machines/MSX/MSX.cpp | 126 ++++++++++++---------- Machines/MSX/MemorySlotHandler.cpp | 49 +++++---- Machines/MSX/MemorySlotHandler.hpp | 44 +++++--- 7 files changed, 146 insertions(+), 101 deletions(-) diff --git a/Machines/MSX/Cartridges/ASCII16kb.hpp b/Machines/MSX/Cartridges/ASCII16kb.hpp index 5cbe2822a..170cd3ac6 100644 --- a/Machines/MSX/Cartridges/ASCII16kb.hpp +++ b/Machines/MSX/Cartridges/ASCII16kb.hpp @@ -27,13 +27,13 @@ class ASCII16kbROMSlotHandler: public MemorySlotHandler { if(pc_is_outside_bios) { if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x4000, 0x4000, 0x4000); + slot_.map(value * 0x4000, 0x4000, 0x4000); break; case 0xe: if(pc_is_outside_bios) { if(address == 0x7000 || address == 0x77ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x4000, 0x8000, 0x4000); + slot_.map(value * 0x4000, 0x8000, 0x4000); break; } } diff --git a/Machines/MSX/Cartridges/ASCII8kb.hpp b/Machines/MSX/Cartridges/ASCII8kb.hpp index e1aac0b17..16deddbe3 100644 --- a/Machines/MSX/Cartridges/ASCII8kb.hpp +++ b/Machines/MSX/Cartridges/ASCII8kb.hpp @@ -27,25 +27,25 @@ class ASCII8kbROMSlotHandler: public MemorySlotHandler { if(pc_is_outside_bios) { if(address == 0x6000 || address == 0x60ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x4000, 0x2000); + slot_.map(value * 0x2000, 0x4000, 0x2000); break; case 0xd: if(pc_is_outside_bios) { if(address == 0x6800 || address == 0x68ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x6000, 0x2000); + slot_.map(value * 0x2000, 0x6000, 0x2000); break; case 0xe: if(pc_is_outside_bios) { if(address == 0x7000 || address == 0x70ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x8000, 0x2000); + slot_.map(value * 0x2000, 0x8000, 0x2000); break; case 0xf: if(pc_is_outside_bios) { if(address == 0x7800 || address == 0x78ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0xa000, 0x2000); + slot_.map(value * 0x2000, 0xa000, 0x2000); break; } } diff --git a/Machines/MSX/Cartridges/Konami.hpp b/Machines/MSX/Cartridges/Konami.hpp index 7422ad1da..18837dc9e 100644 --- a/Machines/MSX/Cartridges/Konami.hpp +++ b/Machines/MSX/Cartridges/Konami.hpp @@ -27,19 +27,19 @@ class KonamiROMSlotHandler: public MemorySlotHandler { if(pc_is_outside_bios) { if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x6000, 0x2000); + slot_.map(value * 0x2000, 0x6000, 0x2000); break; case 4: if(pc_is_outside_bios) { if(address == 0x8000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x8000, 0x2000); + slot_.map(value * 0x2000, 0x8000, 0x2000); break; case 5: if(pc_is_outside_bios) { if(address == 0xa000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0xa000, 0x2000); + slot_.map(value * 0x2000, 0xa000, 0x2000); break; } } diff --git a/Machines/MSX/Cartridges/KonamiWithSCC.hpp b/Machines/MSX/Cartridges/KonamiWithSCC.hpp index 15da86b6f..00bb7141e 100644 --- a/Machines/MSX/Cartridges/KonamiWithSCC.hpp +++ b/Machines/MSX/Cartridges/KonamiWithSCC.hpp @@ -29,13 +29,13 @@ class KonamiWithSCCROMSlotHandler: public MemorySlotHandler { if(pc_is_outside_bios) { if(address == 0x5000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x4000, 0x2000); + slot_.map(value * 0x2000, 0x4000, 0x2000); break; case 0x0e: if(pc_is_outside_bios) { if(address == 0x7000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0x6000, 0x2000); + slot_.map(value * 0x2000, 0x6000, 0x2000); break; case 0x12: if(pc_is_outside_bios) { @@ -43,10 +43,10 @@ class KonamiWithSCCROMSlotHandler: public MemorySlotHandler { } if((value&0x3f) == 0x3f) { scc_is_visible_ = true; - slot_.unmap(0, 0x8000, 0x2000); + slot_.unmap(0x8000, 0x2000); } else { scc_is_visible_ = false; - slot_.map(0, value * 0x2000, 0x8000, 0x2000); + slot_.map(value * 0x2000, 0x8000, 0x2000); } break; case 0x13: @@ -61,7 +61,7 @@ class KonamiWithSCCROMSlotHandler: public MemorySlotHandler { if(pc_is_outside_bios) { if(address == 0xb000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal(); } - slot_.map(0, value * 0x2000, 0xa000, 0x2000); + slot_.map(value * 0x2000, 0xa000, 0x2000); break; } } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 8eafd8a7f..b0a98b64f 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -146,11 +146,6 @@ class ConcreteMachine: public Activity::Source, public MSX::MemorySlotChangeHandler { private: - static constexpr int RAMMemorySlot = 3; - static constexpr int RAMMemorySubSlot = 0; - - static constexpr int ExtensionROMSubSlot = 1; - // Provide 512kb of memory for an MSX 2; 64kb for an MSX 1. 'Slightly' arbitrary. static constexpr size_t RAMSize = model == Target::Model::MSX2 ? 512 * 1024 : 64 * 1024; @@ -253,7 +248,7 @@ class ConcreteMachine: const auto regional_bios = roms.find(regional_bios_name); if(regional_bios != roms.end()) { regional_bios->second.resize(32768); - memory_slots_[0].set_source(regional_bios->second); + bios_slot().set_source(regional_bios->second); has_bios = true; } } @@ -270,40 +265,33 @@ class ConcreteMachine: ); bios[0x2c] = keyboard; - memory_slots_[0].set_source(bios); + bios_slot().set_source(bios); } - memory_slots_[0].map(0, 0, 0, 32768); + bios_slot().map(0, 0, 32768); + + ram_slot().resize_source(RAMSize); + ram_slot().template map(0, 0, 65536); if constexpr (model == Target::Model::MSX2) { - // If there's an extension ROM, add it as a subslot in the same slot as RAM. - std::vector ram_plus; - - ram_plus.resize(RAMSize); + memory_slots_[3].supports_secondary_paging = true; const auto extension = roms.find(ROM::Name::MSX2Extension); - std::copy(extension->second.begin(), extension->second.end(), std::back_inserter(ram_plus)); - - memory_slots_[RAMMemorySlot].supports_secondary_paging = true; - memory_slots_[RAMMemorySlot].set_source(ram_plus); - - memory_slots_[RAMMemorySlot].template map(RAMMemorySubSlot, 0, 0, 65536); - memory_slots_[RAMMemorySlot].map(ExtensionROMSubSlot, RAMSize, 0, 32768); - } else { - memory_slots_[RAMMemorySlot].resize_source(65536); - memory_slots_[RAMMemorySlot].template map(RAMMemorySubSlot, 0, 0, 65536); + extension->second.resize(32768); + extension_rom_slot().set_source(extension->second); + extension_rom_slot().map(0, 0, 32768); } // Add a disk cartridge if any disks were supplied. if(target.has_disk_drive) { - memory_slots_[2].handler = std::make_unique(memory_slots_[2]); + disk_primary().handler = std::make_unique(disk_slot()); std::vector &dos = roms.find(ROM::Name::MSXDOS)->second; dos.resize(16384); - memory_slots_[2].set_source(dos); + disk_slot().set_source(dos); - memory_slots_[2].map(0, 0, 0x4000, 0x2000); - memory_slots_[2].unmap(0, 0x6000, 0x2000); + disk_slot().map(0, 0x4000, 0x2000); + disk_slot().unmap(0x6000, 0x2000); } // Insert the media. @@ -348,15 +336,15 @@ class ConcreteMachine: float get_confidence() final { if(performed_unmapped_access_ || pc_zero_accesses_ > 1) return 0.0f; - if(memory_slots_[1].handler) { - return memory_slots_[1].handler->get_confidence(); + if(cartridge_primary().handler) { + return cartridge_primary().handler->get_confidence(); } return 0.5f; } std::string debug_type() final { - if(memory_slots_[1].handler) { - return "MSX:" + memory_slots_[1].handler->debug_type(); + if(cartridge_primary().handler) { + return "MSX:" + cartridge_primary().handler->debug_type(); } return "MSX"; } @@ -364,26 +352,26 @@ class ConcreteMachine: bool insert_media(const Analyser::Static::Media &media) final { if(!media.cartridges.empty()) { const auto &segment = media.cartridges.front()->get_segments().front(); - auto &slot = memory_slots_[1]; + auto &slot = cartridge_slot(); slot.set_source(segment.data); - slot.map(0, 0, uint16_t(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address)); + slot.map(0, uint16_t(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address)); auto msx_cartridge = dynamic_cast(media.cartridges.front().get()); if(msx_cartridge) { switch(msx_cartridge->type) { default: break; case Analyser::Static::MSX::Cartridge::Konami: - slot.handler = std::make_unique(static_cast(slot)); + cartridge_primary().handler = std::make_unique(static_cast(slot)); break; case Analyser::Static::MSX::Cartridge::KonamiWithSCC: - slot.handler = std::make_unique(static_cast(slot), scc_); + cartridge_primary().handler = std::make_unique(static_cast(slot), scc_); break; case Analyser::Static::MSX::Cartridge::ASCII8kb: - slot.handler = std::make_unique(static_cast(slot)); + cartridge_primary().handler = std::make_unique(static_cast(slot)); break; case Analyser::Static::MSX::Cartridge::ASCII16kb: - slot.handler = std::make_unique(static_cast(slot)); + cartridge_primary().handler = std::make_unique(static_cast(slot)); break; } } @@ -394,11 +382,11 @@ class ConcreteMachine: } if(!media.disks.empty()) { - DiskROM *disk_rom = get_disk_rom(); - if(disk_rom) { + DiskROM *const handler = disk_handler(); + if(handler) { size_t drive = 0; for(auto &disk : media.disks) { - disk_rom->set_disk(disk, drive); + handler->set_disk(disk, drive); drive++; if(drive == 2) break; } @@ -442,7 +430,7 @@ class ConcreteMachine: final_slot_ = &memory_slots_[primary >> 6]; for(int c = 0; c < 8; c += 2) { - const MemorySlot &slot = memory_slots_[primary & 3]; + const PrimarySlot &slot = memory_slots_[primary & 3]; primary >>= 2; read_pointers_[c] = slot.read_pointer(c); @@ -752,9 +740,9 @@ class ConcreteMachine: // MARK: - Activity::Source void set_activity_observer(Activity::Observer *observer) final { - DiskROM *disk_rom = get_disk_rom(); - if(disk_rom) { - disk_rom->set_activity_observer(observer); + DiskROM *handler = disk_handler(); + if(handler) { + handler->set_activity_observer(observer); } i8255_port_handler_.set_activity_observer(observer); } @@ -765,12 +753,6 @@ class ConcreteMachine: } private: - uint8_t *ram() { - return memory_slots_[RAMMemorySlot].source().data(); - } - DiskROM *get_disk_rom() { - return dynamic_cast(memory_slots_[2].handler.get()); - } void update_audio() { speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2))); } @@ -873,14 +855,15 @@ class ConcreteMachine: /// Optionally attaches non-default logic to any of the four things selectable /// via the primary slot register. - struct MemorySlot: public MSX::MemorySlot { - using MSX::MemorySlot::MemorySlot; - + struct PrimarySlot: public MSX::PrimarySlot { + using MSX::PrimarySlot::PrimarySlot; HalfCycles cycles_since_update; + + /// Storage for a slot-specialised handler. std::unique_ptr handler; }; - MemorySlot memory_slots_[4]; - MemorySlot *final_slot_ = nullptr; + PrimarySlot memory_slots_[4]; + PrimarySlot *final_slot_ = nullptr; HalfCycles time_since_ay_update_; @@ -896,7 +879,40 @@ class ConcreteMachine: Ricoh::RP5C01::RP5C01 clock_; int next_clock_register_ = 0; -}; + + // + // Various helpers that dictate the slot arrangement used by this emulator. + // + MemorySlot &bios_slot() { + return memory_slots_[0].subslot(0); + } + MemorySlot &ram_slot() { + return memory_slots_[3].subslot(0); + } + MemorySlot &extension_rom_slot() { + return memory_slots_[3].subslot(1); + } + + MemorySlot &cartridge_slot() { + return cartridge_primary().subslot(0); + } + MemorySlot &disk_slot() { + return disk_primary().subslot(0); + } + + PrimarySlot &cartridge_primary() { + return memory_slots_[1]; + } + PrimarySlot &disk_primary() { + return memory_slots_[2]; + } + + uint8_t *ram() { + return ram_slot().source().data(); + } + DiskROM *disk_handler() { + return dynamic_cast(disk_primary().handler.get()); + }}; } diff --git a/Machines/MSX/MemorySlotHandler.cpp b/Machines/MSX/MemorySlotHandler.cpp index 6e5f503ae..7fb280603 100644 --- a/Machines/MSX/MemorySlotHandler.cpp +++ b/Machines/MSX/MemorySlotHandler.cpp @@ -12,31 +12,40 @@ using namespace MSX; +PrimarySlot::PrimarySlot(MemorySlotChangeHandler &handler) : + subslots_{handler, handler, handler, handler} {} + MemorySlot::MemorySlot(MemorySlotChangeHandler &handler) : handler_(handler) { - for(int subslot = 0; subslot < 4; subslot++) { - for(int region = 0; region < 8; region++) { - read_pointers_[subslot][region] = unmapped.data(); - write_pointers_[subslot][region] = scratch.data(); - } + for(int region = 0; region < 8; region++) { + read_pointers_[region] = unmapped.data(); + write_pointers_[region] = scratch.data(); } } -void MemorySlot::set_secondary_paging(uint8_t value) { +void PrimarySlot::set_secondary_paging(uint8_t value) { secondary_paging_ = value; } -uint8_t MemorySlot::secondary_paging() const { +uint8_t PrimarySlot::secondary_paging() const { return secondary_paging_; } -const uint8_t *MemorySlot::read_pointer(int segment) const { +const uint8_t *PrimarySlot::read_pointer(int segment) const { const int subslot = (secondary_paging_ >> (segment & ~1)) & 3; - return read_pointers_[subslot][segment]; + return subslots_[subslot].read_pointer(segment); +} + +uint8_t *PrimarySlot::write_pointer(int segment) const { + const int subslot = (secondary_paging_ >> (segment & ~1)) & 3; + return subslots_[subslot].write_pointer(segment); +} + +const uint8_t *MemorySlot::read_pointer(int segment) const { + return read_pointers_[segment]; } uint8_t *MemorySlot::write_pointer(int segment) const { - const int subslot = (secondary_paging_ >> (segment & ~1)) & 3; - return write_pointers_[subslot][segment]; + return write_pointers_[segment]; } void MemorySlot::set_source(const std::vector &source) { @@ -56,7 +65,7 @@ const std::vector &MemorySlot::source() const { } template -void MemorySlot::map(int subslot, std::size_t source_address, uint16_t destination_address, std::size_t length) { +void MemorySlot::map(std::size_t source_address, uint16_t destination_address, std::size_t length) { assert(!(destination_address & 8191)); assert(!(length & 8191)); assert(size_t(destination_address) + length <= 65536); @@ -65,9 +74,9 @@ void MemorySlot::map(int subslot, std::size_t source_address, uint16_t destinati source_address %= source_.size(); const int bank = int((destination_address >> 13) + c); - read_pointers_[subslot][bank] = &source_[source_address]; + read_pointers_[bank] = &source_[source_address]; if constexpr (type == AccessType::ReadWrite) { - write_pointers_[subslot][bank] = read_pointers_[subslot][bank]; + write_pointers_[bank] = read_pointers_[bank]; } source_address += 8192; @@ -76,17 +85,21 @@ void MemorySlot::map(int subslot, std::size_t source_address, uint16_t destinati handler_.did_page(); } -void MemorySlot::unmap(int subslot, uint16_t destination_address, std::size_t length) { +void MemorySlot::unmap(uint16_t destination_address, std::size_t length) { assert(!(destination_address & 8191)); assert(!(length & 8191)); assert(size_t(destination_address) + length <= 65536); for(std::size_t c = 0; c < (length >> 13); ++c) { - read_pointers_[subslot][(destination_address >> 13) + c] = nullptr; + read_pointers_[(destination_address >> 13) + c] = nullptr; } handler_.did_page(); } -template void MemorySlot::map(int subslot, std::size_t source_address, uint16_t destination_address, std::size_t length); -template void MemorySlot::map(int subslot, std::size_t source_address, uint16_t destination_address, std::size_t length); +MemorySlot &PrimarySlot::subslot(int slot) { + return subslots_[slot]; +} + +template void MemorySlot::map(std::size_t source_address, uint16_t destination_address, std::size_t length); +template void MemorySlot::map(std::size_t source_address, uint16_t destination_address, std::size_t length); diff --git a/Machines/MSX/MemorySlotHandler.hpp b/Machines/MSX/MemorySlotHandler.hpp index cac6ed032..cc046e78c 100644 --- a/Machines/MSX/MemorySlotHandler.hpp +++ b/Machines/MSX/MemorySlotHandler.hpp @@ -38,15 +38,6 @@ class MemorySlot { public: MemorySlot(MemorySlotChangeHandler &); - /// Attempts to write the argument as the secondary paging selection. - void set_secondary_paging(uint8_t); - - /// @returns The value most recently provided to @c set_secondary_paging. - uint8_t secondary_paging() const; - - /// Indicates whether this slot supports secondary paging. - bool supports_secondary_paging = false; - /// @returns A pointer to the area of memory currently underneath @c address that /// should be read const uint8_t *read_pointer(int segment) const; @@ -73,7 +64,6 @@ class MemorySlot { /// supplied to @c set_source to the region indicated by /// @c destination_address and @c length within @c subslot. template void map( - int subslot, std::size_t source_address, uint16_t destination_address, std::size_t length); @@ -83,15 +73,13 @@ class MemorySlot { /// will be used to field accesses to that area, allowing for areas that are not /// backed by memory to be modelled. void unmap( - int subslot, uint16_t destination_address, std::size_t length); private: std::vector source_; - uint8_t *read_pointers_[4][8]; - uint8_t *write_pointers_[4][8]; - uint8_t secondary_paging_ = 0; + uint8_t *read_pointers_[8]; + uint8_t *write_pointers_[8]; MemorySlotChangeHandler &handler_; @@ -100,6 +88,34 @@ class MemorySlot { inline static MemoryChunk scratch; }; +class PrimarySlot { + public: + PrimarySlot(MemorySlotChangeHandler &); + + /// @returns A pointer to the area of memory currently underneath @c address that + /// should be read + const uint8_t *read_pointer(int segment) const; + + /// @returns A pointer to the area of memory currently underneath @c address. + uint8_t *write_pointer(int segment) const; + + /// Attempts to write the argument as the secondary paging selection. + void set_secondary_paging(uint8_t); + + /// @returns The value most recently provided to @c set_secondary_paging. + uint8_t secondary_paging() const; + + /// Indicates whether this slot supports secondary paging. + bool supports_secondary_paging = false; + + /// Provides the subslot at the specified index. + MemorySlot &subslot(int); + + private: + MemorySlot subslots_[4]; + uint8_t secondary_paging_ = 0; +}; + class MemorySlotHandler { public: virtual ~MemorySlotHandler() {}