1
0
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:
Thomas Harte 2023-01-12 23:01:11 -05:00
parent 0d8c014099
commit 2e7e5ea12b
11 changed files with 283 additions and 230 deletions

View File

@ -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_;
};
}

View File

@ -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_;
};
}

View File

@ -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_;
};
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View 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.
}

View 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, FCFF.
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 */

View File

@ -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 */

View File

@ -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 */,