mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #105 from TomHarte/ParkerBros
Introduces detection and emulation of the Parker Bros paging scheme
This commit is contained in:
commit
3f36eeb071
@ -66,30 +66,42 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
cycles_since_6532_update_ += (cycles_run_for / 3);
|
cycles_since_6532_update_ += (cycles_run_for / 3);
|
||||||
|
|
||||||
if(operation != CPU6502::BusOperation::Ready) {
|
if(operation != CPU6502::BusOperation::Ready) {
|
||||||
|
uint16_t masked_address = address & 0x1fff;
|
||||||
|
|
||||||
// check for a paging access
|
// check for potential paging
|
||||||
if(rom_size_ > 4096 && ((address & 0x1f00) == 0x1f00)) {
|
switch(paging_model_) {
|
||||||
uint8_t *base_ptr = rom_pages_[0];
|
default:
|
||||||
uint8_t first_paging_register = (uint8_t)(0xf8 - (rom_size_ >> 14)*2);
|
// check for an Atari paging access
|
||||||
|
if(rom_size_ > 4096 && ((address & 0x1f00) == 0x1f00)) {
|
||||||
|
uint8_t *base_ptr = rom_pages_[0];
|
||||||
|
uint8_t first_paging_register = (uint8_t)(0xf8 - (rom_size_ >> 14)*2);
|
||||||
|
|
||||||
const uint8_t paging_register = address&0xff;
|
const uint8_t paging_register = address&0xff;
|
||||||
if(paging_register >= first_paging_register) {
|
if(paging_register >= first_paging_register) {
|
||||||
const uint16_t selected_page = paging_register - first_paging_register;
|
const uint16_t selected_page = paging_register - first_paging_register;
|
||||||
if(selected_page * 4096 < rom_size_) {
|
if(selected_page * 4096 < rom_size_) {
|
||||||
base_ptr = &rom_[selected_page * 4096];
|
base_ptr = &rom_[selected_page * 4096];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(base_ptr != rom_pages_[0]) {
|
||||||
|
rom_pages_[0] = base_ptr;
|
||||||
|
rom_pages_[1] = base_ptr + 1024;
|
||||||
|
rom_pages_[2] = base_ptr + 2048;
|
||||||
|
rom_pages_[3] = base_ptr + 3072;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case StaticAnalyser::Atari2600PagingModel::ParkerBros:
|
||||||
if(base_ptr != rom_pages_[0]) {
|
if(masked_address >= 0x1fe0 && masked_address < 0x1ff8) {
|
||||||
rom_pages_[0] = base_ptr;
|
int slot = (masked_address >> 3) & 3;
|
||||||
rom_pages_[1] = base_ptr + 1024;
|
int target = masked_address & 7;
|
||||||
rom_pages_[2] = base_ptr + 2048;
|
rom_pages_[slot] = &rom_[target * 1024];
|
||||||
rom_pages_[3] = base_ptr + 3072;
|
}
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for a ROM read
|
// check for a ROM read
|
||||||
uint16_t masked_address = address & 0x1fff;
|
|
||||||
if(address&0x1000)
|
if(address&0x1000)
|
||||||
{
|
{
|
||||||
// check for a RAM access
|
// check for a RAM access
|
||||||
@ -278,13 +290,13 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
|
|||||||
|
|
||||||
// On a real paged cartridge, any page may initially be visible. Various homebrew authors appear to have
|
// On a real paged cartridge, any page may initially be visible. Various homebrew authors appear to have
|
||||||
// decided the last page will always be initially visible. So do that.
|
// decided the last page will always be initially visible. So do that.
|
||||||
size_t romMask = rom_size_ - 1;
|
size_t rom_mask = rom_size_ - 1;
|
||||||
uint8_t *rom_base = rom_;
|
uint8_t *rom_base = rom_;
|
||||||
if(rom_size_ > 4096) rom_base = &rom_[rom_size_ - 4096];
|
if(rom_size_ > 4096) rom_base = &rom_[rom_size_ - 4096];
|
||||||
rom_pages_[0] = rom_base;
|
rom_pages_[0] = rom_base;
|
||||||
rom_pages_[1] = &rom_base[1024 & romMask];
|
rom_pages_[1] = &rom_base[1024 & rom_mask];
|
||||||
rom_pages_[2] = &rom_base[2048 & romMask];
|
rom_pages_[2] = &rom_base[2048 & rom_mask];
|
||||||
rom_pages_[3] = &rom_base[3072 & romMask];
|
rom_pages_[3] = &rom_base[3072 & rom_mask];
|
||||||
|
|
||||||
switch(target.atari.paging_model)
|
switch(target.atari.paging_model)
|
||||||
{
|
{
|
||||||
@ -305,6 +317,8 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
|
|||||||
ram_read_start_ = 0x1000;
|
ram_read_start_ = 0x1000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paging_model_ = target.atari.paging_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Audio and Video
|
#pragma mark - Audio and Video
|
||||||
|
@ -59,6 +59,7 @@ class Machine:
|
|||||||
// ROM information
|
// ROM information
|
||||||
uint8_t *rom_, *rom_pages_[4];
|
uint8_t *rom_, *rom_pages_[4];
|
||||||
size_t rom_size_;
|
size_t rom_size_;
|
||||||
|
StaticAnalyser::Atari2600PagingModel paging_model_;
|
||||||
|
|
||||||
// cartridge RAM expansion store
|
// cartridge RAM expansion store
|
||||||
std::vector<uint8_t> ram_;
|
std::vector<uint8_t> ram_;
|
||||||
|
@ -90,6 +90,28 @@ static void DeterminePagingFor2kCartridge(StaticAnalyser::Target &target, const
|
|||||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CommaVid;
|
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CommaVid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DeterminePagingFor8kCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const std::vector<StaticAnalyser::MOS6502::Disassembly> &disassemblies)
|
||||||
|
{
|
||||||
|
std::set<uint16_t> internal_accesses;
|
||||||
|
for(const StaticAnalyser::MOS6502::Disassembly &disassembly : disassemblies)
|
||||||
|
{
|
||||||
|
internal_accesses.insert(disassembly.internal_stores.begin(), disassembly.internal_stores.end());
|
||||||
|
internal_accesses.insert(disassembly.internal_modifies.begin(), disassembly.internal_modifies.end());
|
||||||
|
internal_accesses.insert(disassembly.internal_loads.begin(), disassembly.internal_loads.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool looks_like_atari = false;
|
||||||
|
bool looks_like_parker_bros = false;
|
||||||
|
for(uint16_t address : internal_accesses)
|
||||||
|
{
|
||||||
|
looks_like_atari |= ((address & 0x1fff) >= 0x1ff8) && ((address & 0x1fff) < 0x1ffa);
|
||||||
|
looks_like_parker_bros |= ((address & 0x1fff) >= 0x1fe0) && ((address & 0x1fff) < 0x1fe8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(looks_like_parker_bros) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::ParkerBros;
|
||||||
|
if(looks_like_atari) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Atari8k;
|
||||||
|
}
|
||||||
|
|
||||||
static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment)
|
static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment)
|
||||||
{
|
{
|
||||||
if(segment.data.size() == 2048)
|
if(segment.data.size() == 2048)
|
||||||
@ -100,8 +122,8 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
|
|||||||
|
|
||||||
uint16_t entry_address, break_address;
|
uint16_t entry_address, break_address;
|
||||||
|
|
||||||
entry_address = (uint16_t)(segment.data[0xffc] | (segment.data[0xffd] << 8));
|
entry_address = (uint16_t)(segment.data[segment.data.size() - 4] | (segment.data[segment.data.size() - 3] << 8));
|
||||||
break_address = (uint16_t)(segment.data[0xffe] | (segment.data[0xfff] << 8));
|
break_address = (uint16_t)(segment.data[segment.data.size() - 2] | (segment.data[segment.data.size() - 1] << 8));
|
||||||
|
|
||||||
std::function<size_t(uint16_t address)> address_mapper = [](uint16_t address) {
|
std::function<size_t(uint16_t address)> address_mapper = [](uint16_t address) {
|
||||||
if(!(address & 0x1000)) return (size_t)-1;
|
if(!(address & 0x1000)) return (size_t)-1;
|
||||||
@ -112,25 +134,43 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
|
|||||||
std::set<uint16_t> internal_stores;
|
std::set<uint16_t> internal_stores;
|
||||||
for(std::vector<uint8_t>::difference_type base = 0; base < segment.data.size(); base += 4096)
|
for(std::vector<uint8_t>::difference_type base = 0; base < segment.data.size(); base += 4096)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> sub_data(segment.data.begin() + base, segment.data.begin() + base + 2048);
|
std::vector<uint8_t> sub_data(segment.data.begin() + base, segment.data.begin() + base + 4096);
|
||||||
disassemblies.push_back(StaticAnalyser::MOS6502::Disassemble(sub_data, address_mapper, {entry_address, break_address}));
|
disassemblies.push_back(StaticAnalyser::MOS6502::Disassemble(sub_data, address_mapper, {entry_address, break_address}));
|
||||||
internal_stores.insert(disassemblies.back().internal_stores.begin(), disassemblies.back().internal_stores.end());
|
internal_stores.insert(disassemblies.back().internal_stores.begin(), disassemblies.back().internal_stores.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for any sort of on-cartridge RAM; that might imply a Super Chip or else immediately tip the
|
if(segment.data.size() == 8192)
|
||||||
// hat that this is a CBS RAM+ cartridge
|
|
||||||
if(internal_stores.size() > 4)
|
|
||||||
{
|
{
|
||||||
bool writes_above_128 = false;
|
DeterminePagingFor8kCartridge(target, segment, disassemblies);
|
||||||
for(uint16_t address : internal_stores)
|
|
||||||
{
|
|
||||||
writes_above_128 |= ((address & 0x1fff) > 0x10ff) && ((address & 0x1fff) < 0x1200);
|
|
||||||
}
|
|
||||||
if(writes_above_128)
|
|
||||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CBSRamPlus;
|
|
||||||
else
|
|
||||||
target.atari.uses_superchip = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for any sort of on-cartridge RAM; that might imply a Super Chip or else immediately tip the
|
||||||
|
// hat that this is a CBS RAM+ cartridge. Atari ROM images always have the same value stored over RAM
|
||||||
|
// regions.
|
||||||
|
bool has_superchip = true;
|
||||||
|
bool is_ram_plus = true;
|
||||||
|
for(size_t address = 0; address < 256; address++)
|
||||||
|
{
|
||||||
|
if(segment.data[address] != segment.data[0])
|
||||||
|
{
|
||||||
|
if(address < 128) has_superchip = false;
|
||||||
|
is_ram_plus = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.atari.uses_superchip = has_superchip;
|
||||||
|
if(is_ram_plus) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CBSRamPlus;
|
||||||
|
// if(internal_stores.size() > 4)
|
||||||
|
// {
|
||||||
|
// bool writes_above_128 = false;
|
||||||
|
// for(uint16_t address : internal_stores)
|
||||||
|
// {
|
||||||
|
// writes_above_128 |= ((address & 0x1fff) > 0x10ff) && ((address & 0x1fff) < 0x1200);
|
||||||
|
// }
|
||||||
|
// if(writes_above_128)
|
||||||
|
// target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CBSRamPlus;
|
||||||
|
// else
|
||||||
|
// target.atari.uses_superchip = true;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaticAnalyser::Atari::AddTargets(
|
void StaticAnalyser::Atari::AddTargets(
|
||||||
|
Loading…
Reference in New Issue
Block a user