mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-16 11:30:22 +00:00
Fleshes out most of a cleaner memory slot layout.
This commit is contained in:
parent
0d8c014099
commit
2e7e5ea12b
@ -9,15 +9,14 @@
|
||||
#ifndef ASCII16kb_hpp
|
||||
#define ASCII16kb_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
#include "../MemorySlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class ASCII16kbROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
ASCII16kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
ASCII16kbROMSlotHandler(MSX::MemorySlot &slot) : slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) final {
|
||||
switch(address >> 11) {
|
||||
@ -28,13 +27,13 @@ class ASCII16kbROMSlotHandler: public ROMSlotHandler {
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x4000, 0x4000, 0x4000);
|
||||
slot_.map(0, 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();
|
||||
}
|
||||
map_.map(slot_, value * 0x4000, 0x8000, 0x4000);
|
||||
slot_.map(0, value * 0x4000, 0x8000, 0x4000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -44,8 +43,7 @@ class ASCII16kbROMSlotHandler: public ROMSlotHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
MSX::MemorySlot &slot_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,15 +9,14 @@
|
||||
#ifndef ASCII8kb_hpp
|
||||
#define ASCII8kb_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
#include "../MemorySlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class ASCII8kbROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
ASCII8kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
ASCII8kbROMSlotHandler(MSX::MemorySlot &slot) : slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) final {
|
||||
switch(address >> 11) {
|
||||
@ -28,25 +27,25 @@ class ASCII8kbROMSlotHandler: public ROMSlotHandler {
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000 || address == 0x60ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x4000, 0x2000);
|
||||
slot_.map(0, 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();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
slot_.map(0, 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();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
slot_.map(0, 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();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -56,8 +55,7 @@ class ASCII8kbROMSlotHandler: public ROMSlotHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
MSX::MemorySlot &slot_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,15 +9,14 @@
|
||||
#ifndef Konami_hpp
|
||||
#define Konami_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
#include "../MemorySlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class KonamiROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
KonamiROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
KonamiROMSlotHandler(MSX::MemorySlot &slot) : slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) final {
|
||||
switch(address >> 13) {
|
||||
@ -28,19 +27,19 @@ class KonamiROMSlotHandler: public ROMSlotHandler {
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0x6000, 0x2000);
|
||||
break;
|
||||
case 4:
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x8000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0x8000, 0x2000);
|
||||
break;
|
||||
case 5:
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0xa000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -49,8 +48,7 @@ class KonamiROMSlotHandler: public ROMSlotHandler {
|
||||
return "K";
|
||||
}
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
MSX::MemorySlot &slot_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#ifndef KonamiWithSCC_hpp
|
||||
#define KonamiWithSCC_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
#include "../MemorySlotHandler.hpp"
|
||||
#include "../../../Components/KonamiSCC/KonamiSCC.hpp"
|
||||
|
||||
namespace MSX {
|
||||
@ -17,8 +17,8 @@ namespace Cartridge {
|
||||
|
||||
class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot, Konami::SCC &scc) :
|
||||
map_(map), slot_(slot), scc_(scc) {}
|
||||
KonamiWithSCCROMSlotHandler(MSX::MemorySlot &slot, Konami::SCC &scc) :
|
||||
slot_(slot), scc_(scc) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) final {
|
||||
switch(address >> 11) {
|
||||
@ -29,13 +29,13 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x5000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x4000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0x4000, 0x2000);
|
||||
break;
|
||||
case 0x0e:
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x7000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0x6000, 0x2000);
|
||||
break;
|
||||
case 0x12:
|
||||
if(pc_is_outside_bios) {
|
||||
@ -43,10 +43,10 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
}
|
||||
if((value&0x3f) == 0x3f) {
|
||||
scc_is_visible_ = true;
|
||||
map_.unmap(slot_, 0x8000, 0x2000);
|
||||
slot_.unmap(0, 0x8000, 0x2000);
|
||||
} else {
|
||||
scc_is_visible_ = false;
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0x8000, 0x2000);
|
||||
}
|
||||
break;
|
||||
case 0x13:
|
||||
@ -61,7 +61,7 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0xb000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
slot_.map(0, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -80,8 +80,7 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
MSX::MemorySlot &slot_;
|
||||
Konami::SCC &scc_;
|
||||
bool scc_is_visible_ = false;
|
||||
};
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
using namespace MSX;
|
||||
|
||||
DiskROM::DiskROM(const std::vector<uint8_t> &rom) :
|
||||
DiskROM::DiskROM(const MSX::MemorySlot &slot) :
|
||||
WD1770(P1793),
|
||||
rom_(rom) {
|
||||
rom_(slot.source()) {
|
||||
emplace_drives(2, 8000000, 300, 2);
|
||||
set_is_double_density(true);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#ifndef DiskROM_hpp
|
||||
#define DiskROM_hpp
|
||||
|
||||
#include "ROMSlotHandler.hpp"
|
||||
#include "MemorySlotHandler.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
@ -23,7 +23,7 @@ namespace MSX {
|
||||
|
||||
class DiskROM: public ROMSlotHandler, public WD::WD1770 {
|
||||
public:
|
||||
DiskROM(const std::vector<uint8_t> &rom);
|
||||
DiskROM(const MSX::MemorySlot &slot);
|
||||
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) final;
|
||||
uint8_t read(uint16_t address) final;
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include "DiskROM.hpp"
|
||||
#include "Keyboard.hpp"
|
||||
#include "ROMSlotHandler.hpp"
|
||||
#include "MemorySlotHandler.hpp"
|
||||
|
||||
#include "../../Analyser/Static/MSX/Cartridge.hpp"
|
||||
#include "Cartridges/ASCII8kb.hpp"
|
||||
@ -141,7 +141,6 @@ class ConcreteMachine:
|
||||
public MachineTypes::MappedKeyboardMachine,
|
||||
public MachineTypes::JoystickMachine,
|
||||
public Configurable::Device,
|
||||
public MemoryMap,
|
||||
public ClockingHint::Observer,
|
||||
public Activity::Source {
|
||||
public:
|
||||
@ -237,42 +236,37 @@ class ConcreteMachine:
|
||||
// one appropriately patched.
|
||||
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);
|
||||
regional_bios->second.resize(32768);
|
||||
memory_slots_[0].set_source(regional_bios->second);
|
||||
} else {
|
||||
memory_slots_[0].source = std::move(roms.find(bios_name)->second);
|
||||
memory_slots_[0].source.resize(32768);
|
||||
std::vector<uint8_t> &bios = roms.find(bios_name)->second;
|
||||
|
||||
bios.resize(32768);
|
||||
|
||||
// Modify the generic ROM to reflect the selected region, date format, etc.
|
||||
memory_slots_[0].source[0x2b] = uint8_t(
|
||||
bios[0x2b] = uint8_t(
|
||||
(is_ntsc ? 0x00 : 0x80) |
|
||||
(date_format << 4) |
|
||||
character_generator
|
||||
);
|
||||
memory_slots_[0].source[0x2c] = keyboard;
|
||||
bios[0x2c] = keyboard;
|
||||
|
||||
memory_slots_[0].set_source(bios);
|
||||
}
|
||||
|
||||
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_primary(0);
|
||||
memory_slots_[0].map(0, 0, 0, 32768);
|
||||
memory_slots_[3].map(0, 0, 0, 65536);
|
||||
|
||||
// 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.find(ROM::Name::MSXDOS)->second);
|
||||
memory_slots_[2].source.resize(16384);
|
||||
memory_slots_[2].handler = std::make_unique<DiskROM>(memory_slots_[2]);
|
||||
|
||||
map(2, 0, 0x4000, 0x2000);
|
||||
unmap(2, 0x6000, 0x2000);
|
||||
std::vector<uint8_t> &dos = roms.find(ROM::Name::MSXDOS)->second;
|
||||
dos.resize(16384);
|
||||
memory_slots_[2].set_source(dos);
|
||||
|
||||
memory_slots_[2].map(0, 0, 0x4000, 0x2000);
|
||||
memory_slots_[2].unmap(0, 0x6000, 0x2000);
|
||||
}
|
||||
|
||||
// Insert the media.
|
||||
@ -282,6 +276,9 @@ class ConcreteMachine:
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
// Establish default paging.
|
||||
page_primary(0);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -330,24 +327,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();
|
||||
memory_slots_[1].source = segment.data;
|
||||
map(1, 0, uint16_t(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address));
|
||||
auto &slot = memory_slots_[1];
|
||||
|
||||
slot.set_source(segment.data);
|
||||
slot.map(0, 0, uint16_t(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address));
|
||||
|
||||
auto msx_cartridge = dynamic_cast<Analyser::Static::MSX::Cartridge *>(media.cartridges.front().get());
|
||||
if(msx_cartridge) {
|
||||
switch(msx_cartridge->type) {
|
||||
default: break;
|
||||
case Analyser::Static::MSX::Cartridge::Konami:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiROMSlotHandler(*this, 1));
|
||||
slot.handler = std::make_unique<Cartridge::KonamiROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::KonamiWithSCC:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_));
|
||||
slot.handler = std::make_unique<Cartridge::KonamiWithSCCROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot), scc_);
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::ASCII8kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
||||
slot.handler = std::make_unique<Cartridge::ASCII8kbROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::ASCII16kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII16kbROMSlotHandler(*this, 1));
|
||||
slot.handler = std::make_unique<Cartridge::ASCII16kbROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -388,36 +387,6 @@ class ConcreteMachine:
|
||||
return c >= 32 && c < 127;
|
||||
}
|
||||
|
||||
// MARK: MSX::MemoryMap
|
||||
void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) final {
|
||||
assert(!(destination_address & 8191));
|
||||
assert(!(length & 8191));
|
||||
assert(size_t(destination_address) + length <= 65536);
|
||||
|
||||
for(std::size_t c = 0; c < (length >> 13); ++c) {
|
||||
if(memory_slots_[slot].wrapping_strategy == ROMSlotHandler::WrappingStrategy::Repeat) {
|
||||
source_address %= memory_slots_[slot].source.size();
|
||||
}
|
||||
memory_slots_[slot].read_pointers[(destination_address >> 13) + c] =
|
||||
(source_address < memory_slots_[slot].source.size()) ? &memory_slots_[slot].source[source_address] : unpopulated_;
|
||||
source_address += 8192;
|
||||
}
|
||||
|
||||
update_paging();
|
||||
}
|
||||
|
||||
void unmap(int slot, uint16_t destination_address, std::size_t length) final {
|
||||
assert(!(destination_address & 8191));
|
||||
assert(!(length & 8191));
|
||||
assert(size_t(destination_address) + length <= 65536);
|
||||
|
||||
for(std::size_t c = 0; c < (length >> 13); ++c) {
|
||||
memory_slots_[slot].read_pointers[(destination_address >> 13) + c] = nullptr;
|
||||
}
|
||||
|
||||
update_paging();
|
||||
}
|
||||
|
||||
// MARK: Memory paging.
|
||||
void page_primary(uint8_t value) {
|
||||
primary_slots_ = value;
|
||||
@ -427,17 +396,18 @@ class ConcreteMachine:
|
||||
void update_paging() {
|
||||
uint8_t primary = primary_slots_;
|
||||
|
||||
// Update final slot; this direct pointer will be used for
|
||||
// secondary slot communication.
|
||||
final_slot_ = &memory_slots_[primary >> 6];
|
||||
|
||||
// TODO: factor in secondary slot selection below.
|
||||
for(std::size_t c = 0; c < 8; c += 2) {
|
||||
const MemorySlot &slot = memory_slots_[primary & 3];
|
||||
primary >>= 2;
|
||||
|
||||
read_pointers_[c] = slot.read_pointers[c];
|
||||
write_pointers_[c] = slot.write_pointers[c];
|
||||
read_pointers_[c+1] = slot.read_pointers[c+1];
|
||||
write_pointers_[c+1] = slot.write_pointers[c+1];
|
||||
read_pointers_[c] = slot.read_pointer(c);
|
||||
write_pointers_[c] = slot.write_pointer(c);
|
||||
read_pointers_[c+1] = slot.read_pointer(c+1);
|
||||
write_pointers_[c+1] = slot.write_pointer(c+1);
|
||||
}
|
||||
set_use_fast_tape();
|
||||
}
|
||||
@ -527,7 +497,7 @@ class ConcreteMachine:
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
if(address == 0xffff && final_slot_->supports_secondary_paging) {
|
||||
*cycle.value = final_slot_->secondary_paging ^ 0xff;
|
||||
*cycle.value = final_slot_->secondary_paging() ^ 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -542,7 +512,7 @@ class ConcreteMachine:
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Write: {
|
||||
if(address == 0xffff && final_slot_->supports_secondary_paging) {
|
||||
final_slot_->secondary_paging = *cycle.value;
|
||||
final_slot_->set_secondary_paging(*cycle.value);
|
||||
update_paging();
|
||||
break;
|
||||
}
|
||||
@ -551,7 +521,10 @@ class ConcreteMachine:
|
||||
if(memory_slots_[slot_hit].handler) {
|
||||
update_audio();
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.template flush<HalfCycles>());
|
||||
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
|
||||
memory_slots_[slot_hit].handler->write(
|
||||
address,
|
||||
*cycle.value,
|
||||
read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointer(pc_address_ >> 13));
|
||||
} else {
|
||||
write_pointers_[address >> 13][address & 8191] = *cycle.value;
|
||||
}
|
||||
@ -798,7 +771,7 @@ class ConcreteMachine:
|
||||
allow_fast_tape_ &&
|
||||
tape_player_.has_tape() &&
|
||||
!(primary_slots_ & 3) &&
|
||||
!(memory_slots_[0].secondary_paging & 3);
|
||||
!(memory_slots_[0].secondary_paging() & 3);
|
||||
}
|
||||
|
||||
i8255PortHandler i8255_port_handler_;
|
||||
@ -810,47 +783,14 @@ class ConcreteMachine:
|
||||
|
||||
// Divides the current 64kb address space into 8kb chunks.
|
||||
// 8kb resolution is used by some cartride titles.
|
||||
uint8_t *read_pointers_[8];
|
||||
const uint8_t *read_pointers_[8];
|
||||
uint8_t *write_pointers_[8];
|
||||
|
||||
/// Optionally attaches non-default logic to any of the four things selectable
|
||||
/// via the primary slot register.
|
||||
///
|
||||
/// Tracks secondary and game-specific paging within each slot.
|
||||
///
|
||||
/// Standard MSX assignments:
|
||||
/// 0 = ROM;
|
||||
/// 3 = RAM.
|
||||
///
|
||||
/// Additional assignments customarily used by this emulator:
|
||||
/// 1 = any inserted cartridge;
|
||||
/// 2 = the disk ROM, if present.
|
||||
struct MemorySlot {
|
||||
// Each slot may be visible for any number of quarters of the memory map;
|
||||
// these are the read and write pointers that should appear in the main
|
||||
// memory map if this slot is visible there.
|
||||
uint8_t *read_pointers[8];
|
||||
uint8_t *write_pointers[8];
|
||||
|
||||
/// Sets the handler that will be provided with @c run_for and @c read or @c write calls
|
||||
/// for memory accesses **anywhere it has left memory unmapped**. Anywhere that can be
|
||||
/// described just in terms of standard memory access read and write destinations should be,
|
||||
/// ideally to avoid virtual dispatch.
|
||||
void set_handler(ROMSlotHandler *slot_handler) {
|
||||
handler.reset(slot_handler);
|
||||
wrapping_strategy = handler->wrapping_strategy();
|
||||
}
|
||||
|
||||
struct MemorySlot: public MSX::MemorySlot {
|
||||
HalfCycles cycles_since_update;
|
||||
std::unique_ptr<ROMSlotHandler> handler;
|
||||
|
||||
ROMSlotHandler::WrappingStrategy wrapping_strategy = ROMSlotHandler::WrappingStrategy::Repeat;
|
||||
|
||||
bool supports_secondary_paging = false;
|
||||
uint8_t secondary_paging = 0x00;
|
||||
|
||||
/// Per-slot storage, as a convenience.
|
||||
std::vector<uint8_t> source;
|
||||
};
|
||||
MemorySlot memory_slots_[4];
|
||||
MemorySlot *final_slot_ = nullptr;
|
||||
|
79
Machines/MSX/MemorySlotHandler.cpp
Normal file
79
Machines/MSX/MemorySlotHandler.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// MemorySlotHandler.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 12/01/2023.
|
||||
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MemorySlotHandler.hpp"
|
||||
|
||||
using namespace MSX;
|
||||
|
||||
MemorySlot::MemorySlot() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemorySlot::set_secondary_paging(uint8_t value) {
|
||||
secondary_paging_ = value;
|
||||
}
|
||||
|
||||
uint8_t MemorySlot::secondary_paging() const {
|
||||
return secondary_paging_;
|
||||
}
|
||||
|
||||
const uint8_t *MemorySlot::read_pointer(int segment) const {
|
||||
const int subslot = (secondary_paging_ >> (segment & ~1)) & 3;
|
||||
return read_pointers_[subslot][segment];
|
||||
}
|
||||
|
||||
uint8_t *MemorySlot::write_pointer(int segment) const {
|
||||
const int subslot = (secondary_paging_ >> (segment & ~1)) & 3;
|
||||
return write_pointers_[subslot][segment];
|
||||
}
|
||||
|
||||
void MemorySlot::apply_mapping(uint8_t port, uint8_t value) {
|
||||
// TODO.
|
||||
(void)port;
|
||||
(void)value;
|
||||
}
|
||||
|
||||
void MemorySlot::set_source(const std::vector<uint8_t> &source) {
|
||||
source_ = source;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> &MemorySlot::source() const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
void MemorySlot::map(int subslot, 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);
|
||||
|
||||
for(std::size_t c = 0; c < (length >> 13); ++c) {
|
||||
source_address %= source_.size();
|
||||
read_pointers_[subslot][(destination_address >> 13) + c] = &source_[source_address];
|
||||
source_address += 8192;
|
||||
}
|
||||
|
||||
// TODO: allow write_pointers_ to be set.
|
||||
// TODO: need to indicate that mapping changed.
|
||||
}
|
||||
|
||||
void MemorySlot::unmap(int subslot, 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;
|
||||
}
|
||||
|
||||
// TODO: need to indicate that mapping changed.
|
||||
}
|
120
Machines/MSX/MemorySlotHandler.hpp
Normal file
120
Machines/MSX/MemorySlotHandler.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// MemorySlotHandler.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MemorySlotHandler_hpp
|
||||
#define MemorySlotHandler_hpp
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Analyser/Dynamic/ConfidenceCounter.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
Design assumptions:
|
||||
|
||||
- to-ROM writes and paging events are 'rare', so virtual call costs aren't worrisome;
|
||||
- ROM type variety is sufficiently slender that most of it can be built into the MSX.
|
||||
|
||||
Part of the motivation is also that the MSX has four logical slots, the ROM, RAM plus two
|
||||
things plugged in. So even if the base class were templated to remove the virtual call,
|
||||
there'd just be a switch on what to call.
|
||||
*/
|
||||
namespace MSX {
|
||||
|
||||
class MemorySlot {
|
||||
public:
|
||||
MemorySlot();
|
||||
|
||||
/// 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;
|
||||
|
||||
/// @returns A pointer to the area of memory currently underneath @c address.
|
||||
uint8_t *write_pointer(int segment) const;
|
||||
|
||||
/// Sets the value most-recently written to one of the standard
|
||||
/// memory mapping ports, FC–FF.
|
||||
void apply_mapping(uint8_t port, uint8_t value);
|
||||
|
||||
/// Copies an underlying source buffer.
|
||||
void set_source(const std::vector<uint8_t> &source);
|
||||
|
||||
/// Provides a reference to the internal source storage.
|
||||
const std::vector<uint8_t> &source() const;
|
||||
|
||||
/// Maps the content from @c source_address in the buffer previously
|
||||
/// supplied to @c set_source to the region indicated by
|
||||
/// @c destination_address and @c length within @c subslot.
|
||||
void map(
|
||||
int subslot,
|
||||
std::size_t source_address,
|
||||
uint16_t destination_address,
|
||||
std::size_t length);
|
||||
|
||||
/// Marks the region indicated by @c destination_address and @c length
|
||||
/// as unmapped. In practical terms that means that a @c ROMSlotHandler
|
||||
/// 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<uint8_t> source_;
|
||||
uint8_t *read_pointers_[4][8];
|
||||
uint8_t *write_pointers_[4][8];
|
||||
uint8_t secondary_paging_ = 0;
|
||||
|
||||
using MemoryChunk = std::array<uint8_t, 8192>;
|
||||
inline static MemoryChunk unmapped{0xff};
|
||||
inline static MemoryChunk scratch;
|
||||
};
|
||||
|
||||
class ROMSlotHandler {
|
||||
public:
|
||||
virtual ~ROMSlotHandler() {}
|
||||
|
||||
/*! Advances time by @c half_cycles. */
|
||||
virtual void run_for([[maybe_unused]] HalfCycles half_cycles) {}
|
||||
|
||||
/*! Announces an attempt to write @c value to @c address. */
|
||||
virtual void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) = 0;
|
||||
|
||||
/*! Seeks the result of a read at @c address; this is used only if the area is unmapped. */
|
||||
virtual uint8_t read([[maybe_unused]] uint16_t address) { return 0xff; }
|
||||
|
||||
/*! @returns The probability that this handler is correct for the data it owns. */
|
||||
float get_confidence() {
|
||||
return confidence_counter_.get_confidence();
|
||||
}
|
||||
|
||||
virtual std::string debug_type() {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected:
|
||||
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* MemorySlotHandler_hpp */
|
@ -1,85 +0,0 @@
|
||||
//
|
||||
// ROMSlotHandler.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ROMSlotHandler_hpp
|
||||
#define ROMSlotHandler_hpp
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Analyser/Dynamic/ConfidenceCounter.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
Design assumptions:
|
||||
|
||||
- to-ROM writes and paging events are 'rare', so virtual call costs aren't worrisome;
|
||||
- ROM type variety is sufficiently slender that most of it can be built into the MSX.
|
||||
|
||||
Part of the motivation is also that the MSX has four logical slots, the ROM, RAM plus two
|
||||
things plugged in. So even if the base class were templated to remove the virtual call,
|
||||
there'd just be a switch on what to call.
|
||||
*/
|
||||
namespace MSX {
|
||||
|
||||
class MemoryMap {
|
||||
public:
|
||||
/*!
|
||||
Maps source data from the ROM's source to the given address range.
|
||||
*/
|
||||
virtual void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) = 0;
|
||||
|
||||
/*!
|
||||
Unmaps source data from the given address range; the slot handler's read function will be used
|
||||
to respond to queries in that range.
|
||||
*/
|
||||
virtual void unmap(int slot, uint16_t destination_address, std::size_t length) = 0;
|
||||
};
|
||||
|
||||
class ROMSlotHandler {
|
||||
public:
|
||||
virtual ~ROMSlotHandler() {}
|
||||
|
||||
/*! Advances time by @c half_cycles. */
|
||||
virtual void run_for([[maybe_unused]] HalfCycles half_cycles) {}
|
||||
|
||||
/*! Announces an attempt to write @c value to @c address. */
|
||||
virtual void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) = 0;
|
||||
|
||||
/*! Seeks the result of a read at @c address; this is used only if the area is unmapped. */
|
||||
virtual uint8_t read([[maybe_unused]] uint16_t address) { return 0xff; }
|
||||
|
||||
enum class WrappingStrategy {
|
||||
/// All accesses are modulo the size of the ROM.
|
||||
Repeat,
|
||||
/// Out-of-bounds accesses read a vacant bus.
|
||||
Empty
|
||||
};
|
||||
|
||||
/*! @returns The wrapping strategy to apply to mapping requests from this ROM slot. */
|
||||
virtual WrappingStrategy wrapping_strategy() const {
|
||||
return WrappingStrategy::Repeat;
|
||||
}
|
||||
|
||||
/*! @returns The probability that this handler is correct for the data it owns. */
|
||||
float get_confidence() {
|
||||
return confidence_counter_.get_confidence();
|
||||
}
|
||||
|
||||
virtual std::string debug_type() {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected:
|
||||
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ROMSlotHandler_hpp */
|
@ -1062,6 +1062,8 @@
|
||||
4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BEEE6BB20DC72EA003723BF /* CompositeOptions.xib */; };
|
||||
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; };
|
||||
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; };
|
||||
4BF0BC68297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF0BC67297108D100CCA2B5 /* MemorySlotHandler.cpp */; };
|
||||
4BF0BC69297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF0BC67297108D100CCA2B5 /* MemorySlotHandler.cpp */; };
|
||||
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||
4BF701A026FFD32300996424 /* AmigaBlitterTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */; };
|
||||
@ -1457,7 +1459,7 @@
|
||||
4B6FD0352923061300EC4760 /* HDV.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HDV.hpp; sourceTree = "<group>"; };
|
||||
4B7041271F92C26900735E45 /* JoystickMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JoystickMachine.hpp; sourceTree = "<group>"; };
|
||||
4B70412A1F92C2A700735E45 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = "<group>"; };
|
||||
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMSlotHandler.hpp; sourceTree = "<group>"; };
|
||||
4B70EF6A1FFDCDF400A3494E /* MemorySlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemorySlotHandler.hpp; sourceTree = "<group>"; };
|
||||
4B7136841F78724F008B8ED9 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
|
||||
4B7136851F78724F008B8ED9 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
|
||||
4B7136871F78725F008B8ED9 /* Shifter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Shifter.cpp; sourceTree = "<group>"; };
|
||||
@ -2208,6 +2210,7 @@
|
||||
4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = "<group>"; };
|
||||
4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = "<group>"; };
|
||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = "<group>"; };
|
||||
4BF0BC67297108D100CCA2B5 /* MemorySlotHandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MemorySlotHandler.cpp; sourceTree = "<group>"; };
|
||||
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LanguageCardSwitches.hpp; sourceTree = "<group>"; };
|
||||
4BF40A5A254263140033EA39 /* AuxiliaryMemorySwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AuxiliaryMemorySwitches.hpp; sourceTree = "<group>"; };
|
||||
4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; };
|
||||
@ -3261,11 +3264,12 @@
|
||||
children = (
|
||||
4BEBFB4F2002DB30000708CC /* DiskROM.cpp */,
|
||||
4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */,
|
||||
4BF0BC67297108D100CCA2B5 /* MemorySlotHandler.cpp */,
|
||||
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */,
|
||||
4BEBFB502002DB30000708CC /* DiskROM.hpp */,
|
||||
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */,
|
||||
4B70EF6A1FFDCDF400A3494E /* MemorySlotHandler.hpp */,
|
||||
4B79A5001FC913C900EEDAD5 /* MSX.hpp */,
|
||||
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */,
|
||||
4B1667F81FFF1E2900A16032 /* Cartridges */,
|
||||
);
|
||||
path = MSX;
|
||||
@ -5544,6 +5548,7 @@
|
||||
4B0E04F11FC9EA9500F43484 /* MSX.cpp in Sources */,
|
||||
4B055AD51FAE9B0B0060FFFF /* Video.cpp in Sources */,
|
||||
4B9EC0EB26B384080060A31F /* Keyboard.cpp in Sources */,
|
||||
4BF0BC69297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */,
|
||||
4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */,
|
||||
4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */,
|
||||
@ -5802,6 +5807,7 @@
|
||||
4B4518A11F75FD1C00926311 /* D64.cpp in Sources */,
|
||||
4B1558C01F844ECD006E9A97 /* BitReverse.cpp in Sources */,
|
||||
4BCE0052227CE8CA000CA200 /* DiskIICard.cpp in Sources */,
|
||||
4BF0BC68297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */,
|
||||
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
|
||||
4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */,
|
||||
4BB4BFB922A4372F0069048D /* StaticAnalyser.cpp in Sources */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user