Catch writes to FFFC.
This commit is contained in:
1 parent
b2d8bd940b
commit
12aede0514
1 file changed
+49
-28
@@ -28,6 +28,7 @@
|
||||
#include "Analyser/Static/Sega/Target.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
@@ -93,7 +94,6 @@ template <Analyser::Static::Sega::Target::Model model> class ConcreteMachine:
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::Sega::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
region_(target.region),
|
||||
paging_scheme_(target.paging_scheme),
|
||||
z80_(*this),
|
||||
sn76489_(
|
||||
(target.model == Target::Model::SG1000) ? TI::SN76489::Personality::SN76489 : TI::SN76489::Personality::SMS,
|
||||
@@ -102,7 +102,8 @@ public:
|
||||
opll_(audio_queue_, audio_divider),
|
||||
mixer_(sn76489_, opll_),
|
||||
speaker_(mixer_),
|
||||
keyboard_({Inputs::Keyboard::Key::Enter, Inputs::Keyboard::Key::Escape}, {}) {
|
||||
keyboard_({Inputs::Keyboard::Key::Enter, Inputs::Keyboard::Key::Escape}, {}),
|
||||
paging_(target.paging_scheme) {
|
||||
// Pick the clock rate based on the region.
|
||||
const double clock_rate = target.region == Target::Region::Europe ? 3546893.0 : 3579540.0;
|
||||
speaker_.set_input_rate(float(clock_rate / audio_divider));
|
||||
@@ -126,13 +127,6 @@ public:
|
||||
cartridge_.resize(TargetSize, 0xff);
|
||||
}
|
||||
|
||||
if(paging_scheme_ == Target::PagingScheme::Codemasters) {
|
||||
// The Codemasters cartridges start with pages 0, 1 and 0 again initially visible.
|
||||
paging_registers_[0] = 0;
|
||||
paging_registers_[1] = 1;
|
||||
paging_registers_[2] = 0;
|
||||
}
|
||||
|
||||
// Load the BIOS if available.
|
||||
//
|
||||
// TODO: there's probably a million other versions of the Master System BIOS; try to build a
|
||||
@@ -154,17 +148,21 @@ public:
|
||||
std::cerr << "No BIOS found; attempting to start cartridge directly" << std::endl;
|
||||
} else {
|
||||
has_bios_ = true;
|
||||
std::copy_n(rom->second.begin(), std::min(sizeof(bios_), rom->second.size()), bios_);
|
||||
std::copy_n(
|
||||
rom->second.begin(),
|
||||
std::min(bios_.size(), rom->second.size()),
|
||||
bios_.begin()
|
||||
);
|
||||
}
|
||||
page_cartridge();
|
||||
|
||||
// Map RAM.
|
||||
if constexpr (is_master_system(model)) {
|
||||
map(read_pointers_, ram_, 8*1024, 0xc000, 0x10000);
|
||||
map(write_pointers_, ram_, 8*1024, 0xc000, 0x10000);
|
||||
map(read_pointers_, ram_.data(), 8*1024, 0xc000, 0x10000);
|
||||
map(write_pointers_, ram_.data(), 8*1024, 0xc000, 0x10000);
|
||||
} else {
|
||||
map(read_pointers_, ram_, 1024, 0xc000, 0x10000);
|
||||
map(write_pointers_, ram_, 1024, 0xc000, 0x10000);
|
||||
map(read_pointers_, ram_.data(), 1024, 0xc000, 0x10000);
|
||||
map(write_pointers_, ram_.data(), 1024, 0xc000, 0x10000);
|
||||
}
|
||||
|
||||
// Apply a relatively low low-pass filter. More guidance needed here.
|
||||
@@ -242,25 +240,28 @@ public:
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
if(paging_scheme_ == Target::PagingScheme::Sega) {
|
||||
if(paging_.scheme == Target::PagingScheme::Sega) {
|
||||
if(address >= 0xfffd && cartridge_.size() > 48*1024) {
|
||||
if(paging_registers_[address - 0xfffd] != *cycle.value) {
|
||||
paging_registers_[address - 0xfffd] = *cycle.value;
|
||||
if(paging_.page[address - 0xfffd] != *cycle.value) {
|
||||
paging_.page[address - 0xfffd] = *cycle.value;
|
||||
page_cartridge();
|
||||
}
|
||||
} else if(address == 0xfffc) {
|
||||
paging_.control = *cycle.value;
|
||||
page_cartridge();
|
||||
}
|
||||
} else {
|
||||
// i.e. this is the Codemasters paging scheme.
|
||||
if(!(address&0x3fff) && address < 0xc000) {
|
||||
if(paging_registers_[address >> 14] != *cycle.value) {
|
||||
paging_registers_[address >> 14] = *cycle.value;
|
||||
if(paging_.page[address >> 14] != *cycle.value) {
|
||||
paging_.page[address >> 14] = *cycle.value;
|
||||
page_cartridge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(write_pointers_[address >> 10]) write_pointers_[address >> 10][address & 1023] = *cycle.value;
|
||||
// else Logger::info().append("Ignored write to ROM");
|
||||
// else Logger::info().append("Ignored write to ROM at %04x", address);
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Input:
|
||||
@@ -492,7 +493,6 @@ private:
|
||||
|
||||
using Target = Analyser::Static::Sega::Target;
|
||||
const Target::Region region_;
|
||||
const Target::PagingScheme paging_scheme_;
|
||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||
JustInTimeActor<TI::TMS::TMS9918<tms_personality()>> vdp_;
|
||||
|
||||
@@ -510,8 +510,9 @@ private:
|
||||
HalfCycles time_since_sn76489_update_;
|
||||
HalfCycles time_until_debounce_;
|
||||
|
||||
uint8_t ram_[8*1024];
|
||||
uint8_t bios_[8*1024];
|
||||
std::array<uint8_t, 8*1024> ram_;
|
||||
std::array<uint8_t, 8*1024> sram_;
|
||||
std::array<uint8_t, 8*1024> bios_;
|
||||
std::vector<uint8_t> cartridge_;
|
||||
|
||||
uint8_t io_port_control_ = 0x0f;
|
||||
@@ -522,21 +523,41 @@ private:
|
||||
// The memory map has a 1kb granularity; this is determined by the SG1000's 1kb of RAM.
|
||||
const uint8_t *read_pointers_[64];
|
||||
uint8_t *write_pointers_[64];
|
||||
template <typename T> void map(T **target, uint8_t *source, size_t size, size_t start_address, size_t end_address = 0) {
|
||||
template <typename T>
|
||||
void map(T **target, uint8_t *source, size_t size, size_t start_address, size_t end_address = 0) {
|
||||
if(!end_address) end_address = start_address + size;
|
||||
for(auto address = start_address; address < end_address; address += 1024) {
|
||||
target[address >> 10] = source ? &source[(address - start_address) & (size - 1)] : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t paging_registers_[3] = {0, 1, 2};
|
||||
struct Paging {
|
||||
Paging(const Target::PagingScheme scheme) : scheme(scheme) {
|
||||
switch(scheme) {
|
||||
case Target::PagingScheme::Codemasters:
|
||||
// The Codemasters cartridges start with pages 0, 1 and 0 again initially visible.
|
||||
page[2] = 0;
|
||||
break;
|
||||
|
||||
case Target::PagingScheme::Sega:
|
||||
page[2] = 2;
|
||||
break;
|
||||
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t control = 0;
|
||||
uint8_t page[3] = {0, 1, 2};
|
||||
const Target::PagingScheme scheme;
|
||||
} paging_;
|
||||
uint8_t memory_control_ = 0;
|
||||
void page_cartridge() {
|
||||
// Either install the cartridge or don't; Japanese machines can't see
|
||||
// anything but the cartridge.
|
||||
if(!(memory_control_ & 0x40) || region_ == Target::Region::Japan) {
|
||||
for(size_t c = 0; c < 3; ++c) {
|
||||
const size_t start_addr = (paging_registers_[c] * 0x4000) % cartridge_.size();
|
||||
const size_t start_addr = (paging_.page[c] * 0x4000) % cartridge_.size();
|
||||
map(
|
||||
read_pointers_,
|
||||
cartridge_.data() + start_addr,
|
||||
@@ -545,7 +566,7 @@ private:
|
||||
}
|
||||
|
||||
// The first 1kb doesn't page though, if this is the Sega paging scheme.
|
||||
if(paging_scheme_ == Target::PagingScheme::Sega) {
|
||||
if(paging_.scheme == Target::PagingScheme::Sega) {
|
||||
map(read_pointers_, cartridge_.data(), 0x400, 0x0000);
|
||||
}
|
||||
} else {
|
||||
@@ -554,7 +575,7 @@ private:
|
||||
|
||||
// Throw the BIOS on top if this machine has one and it isn't disabled.
|
||||
if(has_bios_ && !(memory_control_ & 0x08)) {
|
||||
map(read_pointers_, bios_, 8*1024, 0);
|
||||
map(read_pointers_, bios_.data(), 8*1024, 0);
|
||||
}
|
||||
}
|
||||
bool has_bios_ = true;
|
||||
|
||||
Reference in new issue
Block a user