From 2e4c4c3e9175297e7f6db0745667ab5372785d15 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 24 Sep 2018 21:34:42 -0400 Subject: [PATCH] Makes some attempt to implement paging. This causes several of the 32kb games to be recognised by the BIOS and permitted to start, so it really really may be time to stop deferring work on the VDP. --- Machines/MasterSystem/MasterSystem.cpp | 59 ++++++++++++++++++++------ 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 5c4360f21..dae6df17d 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -22,6 +22,8 @@ #include "../../Analyser/Static/Sega/Target.hpp" +#include + namespace { const int sn76489_divider = 2; } @@ -91,16 +93,17 @@ class ConcreteMachine: joysticks_.emplace_back(new Joystick); // Clear the memory map. - for(auto &pointer: read_pointers_) { - pointer = nullptr; - } - for(auto &pointer: write_pointers_) { - pointer = nullptr; - } + map(read_pointers_, nullptr, 0x10000, 0); + map(write_pointers_, nullptr, 0x10000, 0); // Take a copy of the cartridge and place it into memory. cartridge_ = target.media.cartridges[0]->get_segments()[0].data; - map(read_pointers_, cartridge_.data(), static_cast(cartridge_.size()), 0x0000, 0xc000); + if(cartridge_.size() < 48*1024) { + std::size_t new_space = 48*1024 - cartridge_.size(); + cartridge_.resize(48*1024); + memset(&cartridge_[48*1024 - new_space], 0xff, new_space); + } + page_cartridge(); // Establish the BIOS (if relevant) and RAM. if(target.model == Analyser::Static::Sega::Target::Model::MasterSystem) { @@ -159,7 +162,11 @@ class ConcreteMachine: break; case CPU::Z80::PartialMachineCycle::Write: - if(write_pointers_[address >> 10]) write_pointers_[address >> 10][address & 1023] = *cycle.value; + if(address >= 0xfffd && cartridge_.size() > 48*1024) { + paging_registers_[address - 0xfffd] = *cycle.value; + page_cartridge(); + } + else if(write_pointers_[address >> 10]) write_pointers_[address >> 10][address & 1023] = *cycle.value; break; case CPU::Z80::PartialMachineCycle::Input: @@ -201,7 +208,21 @@ class ConcreteMachine: case CPU::Z80::PartialMachineCycle::Output: switch(address & 0xc1) { case 0x00: - printf("TODO: [output] memory control\n"); + if(model_ == Analyser::Static::Sega::Target::Model::MasterSystem) { + // TODO: Obey the RAM enable. + + // Either install the cartridge or don't. + if(!((*cycle.value) & 0x40)) { + page_cartridge(); + } else { + map(read_pointers_, nullptr, 0xc000, 0x0000); + } + + // Throw the BIOS on top if it isn't disabled. + if(!((*cycle.value) & 0x08)) { + map(read_pointers_, bios_, 8*1024, 0); + } + } break; case 0x01: printf("TODO: [output] I/O port control\n"); @@ -286,10 +307,22 @@ class ConcreteMachine: // 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 void map(T **target, uint8_t *source, int size, int start_address, int end_address = -1) { - if(end_address == -1) end_address = start_address + size; - for(int address = start_address; address < end_address; address += 1024) { - target[address >> 10] = &source[(address - start_address) & (size - 1)]; + template 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}; + void page_cartridge() { + for(size_t c = 0; c < 3; ++c) { + const size_t start_addr = (paging_registers_[c] * 0x4000) % cartridge_.size(); + map( + read_pointers_, + cartridge_.data() + start_addr, + std::min(static_cast(0x4000), cartridge_.size() - start_addr), + c * 0x4000); } } };