1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +00:00

Merge pull request #706 from TomHarte/Vic20Flags

Permits Vic-20 memory to be specified in banks;
This commit is contained in:
Thomas Harte 2019-12-26 23:04:56 -05:00 committed by GitHub
commit ec9357e080
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 27 deletions

View File

@ -16,6 +16,7 @@
#include "../../../Outputs/Log.hpp" #include "../../../Outputs/Log.hpp"
#include <algorithm> #include <algorithm>
#include <cstring>
#include <sstream> #include <sstream>
using namespace Analyser::Static::Commodore; using namespace Analyser::Static::Commodore;
@ -78,7 +79,7 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
} }
if(!files.empty()) { if(!files.empty()) {
target->memory_model = Target::MemoryModel::Unexpanded; auto memory_model = Target::MemoryModel::Unexpanded;
std::ostringstream string_stream; std::ostringstream string_stream;
string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ","; string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ",";
if(files.front().is_basic()) { if(files.front().is_basic()) {
@ -94,16 +95,18 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
default: default:
LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address); LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address);
case 0x1001: case 0x1001:
target->memory_model = Target::MemoryModel::Unexpanded; memory_model = Target::MemoryModel::Unexpanded;
break; break;
case 0x1201: case 0x1201:
target->memory_model = Target::MemoryModel::ThirtyTwoKB; memory_model = Target::MemoryModel::ThirtyTwoKB;
break; break;
case 0x0401: case 0x0401:
target->memory_model = Target::MemoryModel::EightKB; memory_model = Target::MemoryModel::EightKB;
break; break;
} }
target->set_memory_model(memory_model);
// General approach: increase memory size conservatively such that the largest file found will fit. // General approach: increase memory size conservatively such that the largest file found will fit.
// for(File &file : files) { // for(File &file : files) {
// std::size_t file_size = file.data.size(); // std::size_t file_size = file.data.size();
@ -145,13 +148,52 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
} }
if(!target->media.empty()) { if(!target->media.empty()) {
// Inspect filename for a region hint. // Inspect filename for configuration hints.
std::string lowercase_name = file_name; std::string lowercase_name = file_name;
std::transform(lowercase_name.begin(), lowercase_name.end(), lowercase_name.begin(), ::tolower); std::transform(lowercase_name.begin(), lowercase_name.end(), lowercase_name.begin(), ::tolower);
// Hint 1: 'ntsc' anywhere in the name implies America.
if(lowercase_name.find("ntsc") != std::string::npos) { if(lowercase_name.find("ntsc") != std::string::npos) {
target->region = Analyser::Static::Commodore::Target::Region::American; target->region = Analyser::Static::Commodore::Target::Region::American;
} }
// Potential additional hints: check for TheC64 tags.
auto final_underscore = lowercase_name.find_last_of('_');
if(final_underscore != std::string::npos) {
auto iterator = lowercase_name.begin() + ssize_t(final_underscore) + 1;
while(iterator != lowercase_name.end()) {
// Grab the next tag.
char next_tag[3] = {0, 0, 0};
next_tag[0] = *iterator++;
if(iterator == lowercase_name.end()) break;
next_tag[1] = *iterator++;
// Exit early if attempting to read another tag has run over the file extension.
if(next_tag[0] == '.' || next_tag[1] == '.') break;
// Check whether it's anything.
target->enabled_ram.bank0 |= !strcmp(next_tag, "b0");
target->enabled_ram.bank1 |= !strcmp(next_tag, "b1");
target->enabled_ram.bank2 |= !strcmp(next_tag, "b2");
target->enabled_ram.bank3 |= !strcmp(next_tag, "b3");
target->enabled_ram.bank5 |= !strcmp(next_tag, "b5");
if(!strcmp(next_tag, "tn")) { // i.e. NTSC.
target->region = Analyser::Static::Commodore::Target::Region::American;
}
if(!strcmp(next_tag, "tp")) { // i.e. PAL.
target->region = Analyser::Static::Commodore::Target::Region::European;
}
// Unhandled:
//
// M6: this is a C64 file.
// MV: this is a Vic-20 file.
// J1/J2: this C64 file should have the primary joystick in slot 1/2.
// RO: this disk image should be treated as read-only.
}
}
// Attach a 1540 if there are any disks here. // Attach a 1540 if there are any disks here.
target->has_c1540 = !target->media.disks.empty(); target->has_c1540 = !target->media.disks.empty();

View File

@ -31,7 +31,26 @@ struct Target: public ::Analyser::Static::Target {
Swedish Swedish
}; };
MemoryModel memory_model = MemoryModel::Unexpanded; /// Maps from a named memory model to a bank enabled/disabled set.
void set_memory_model(MemoryModel memory_model) {
// This is correct for unexpanded and 32kb memory models.
enabled_ram.bank0 = enabled_ram.bank1 =
enabled_ram.bank2 = enabled_ram.bank3 =
enabled_ram.bank5 = memory_model == MemoryModel::ThirtyTwoKB;
// Bank 0 will need to be enabled if this is an 8kb machine.
enabled_ram.bank0 |= memory_model == MemoryModel::EightKB;
}
struct {
bool bank0 = false;
bool bank1 = false;
bool bank2 = false;
bool bank3 = false;
bool bank5 = false;
// Sic. There is no bank 4; this is because the area that logically would be
// bank 4 is occupied by the character ROM, colour RAM, hardware registers, etc.
} enabled_ram;
Region region = Region::European; Region region = Region::European;
bool has_c1540 = false; bool has_c1540 = false;
std::string loading_command; std::string loading_command;

View File

@ -320,7 +320,7 @@ class ConcreteMachine:
tape_->set_delegate(this); tape_->set_delegate(this);
tape_->set_clocking_hint_observer(this); tape_->set_clocking_hint_observer(this);
// install a joystick // Install a joystick.
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_)); joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
const std::string machine_name = "Vic20"; const std::string machine_name = "Vic20";
@ -401,22 +401,14 @@ class ConcreteMachine:
write_to_map(processor_read_memory_map_, &ram_[baseaddr], baseaddr, length); \ write_to_map(processor_read_memory_map_, &ram_[baseaddr], baseaddr, length); \
write_to_map(processor_write_memory_map_, &ram_[baseaddr], baseaddr, length); write_to_map(processor_write_memory_map_, &ram_[baseaddr], baseaddr, length);
// Add 6502-visible RAM as requested // Add 6502-visible RAM as requested.
switch(target.memory_model) { set_ram(0x0000, 0x0400);
case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded: set_ram(0x1000, 0x1000); // Built-in RAM.
// The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000. if(target.enabled_ram.bank0) set_ram(0x0400, 0x0c00); // Bank 0: 0x0400 -> 0x1000.
set_ram(0x0000, 0x0400); if(target.enabled_ram.bank1) set_ram(0x2000, 0x2000); // Bank 1: 0x2000 -> 0x4000.
set_ram(0x1000, 0x1000); if(target.enabled_ram.bank2) set_ram(0x4000, 0x2000); // Bank 2: 0x4000 -> 0x6000.
break; if(target.enabled_ram.bank3) set_ram(0x6000, 0x2000); // Bank 3: 0x6000 -> 0x8000.
case Analyser::Static::Commodore::Target::MemoryModel::EightKB: if(target.enabled_ram.bank5) set_ram(0xa000, 0x2000); // Bank 5: 0xa000 -> 0xc000.
// An 8kb Vic-20 fills in the gap between the two blocks of RAM on an unexpanded machine.
set_ram(0x0000, 0x2000);
break;
case Analyser::Static::Commodore::Target::MemoryModel::ThirtyTwoKB:
// A 32kb Vic-20 fills the entire lower 32kb with RAM.
set_ram(0x0000, 0x8000);
break;
}
#undef set_ram #undef set_ram
@ -453,6 +445,8 @@ class ConcreteMachine:
write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast<uint16_t>(character_rom_.size())); write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast<uint16_t>(character_rom_.size()));
write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast<uint16_t>(kernel_rom_.size())); write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast<uint16_t>(kernel_rom_.size()));
// The insert_media occurs last, so if there's a conflict between cartridges and RAM,
// the cartridge wins.
insert_media(target.media); insert_media(target.media);
if(!target.loading_command.empty()) { if(!target.loading_command.empty()) {
type_string(target.loading_command); type_string(target.loading_command);
@ -711,7 +705,7 @@ class ConcreteMachine:
std::vector<uint8_t> rom_; std::vector<uint8_t> rom_;
uint16_t rom_address_, rom_length_; uint16_t rom_address_, rom_length_;
uint8_t ram_[0x8000]; uint8_t ram_[0x10000];
uint8_t colour_ram_[0x0400]; uint8_t colour_ram_[0x0400];
uint8_t *processor_read_memory_map_[64]; uint8_t *processor_read_memory_map_[64];

View File

@ -121,11 +121,13 @@
case CSMachineVic20RegionEuropean: target->region = Target::Region::European; break; case CSMachineVic20RegionEuropean: target->region = Target::Region::European; break;
case CSMachineVic20RegionJapanese: target->region = Target::Region::Japanese; break; case CSMachineVic20RegionJapanese: target->region = Target::Region::Japanese; break;
} }
auto memory_model = Target::MemoryModel::Unexpanded;
switch(memorySize) { switch(memorySize) {
default: target->memory_model = Target::MemoryModel::Unexpanded; break; default: break;
case 8: target->memory_model = Target::MemoryModel::EightKB; break; case 8: memory_model = Target::MemoryModel::EightKB; break;
case 32: target->memory_model = Target::MemoryModel::ThirtyTwoKB; break; case 32: memory_model = Target::MemoryModel::ThirtyTwoKB; break;
} }
target->set_memory_model(memory_model);
target->has_c1540 = !!hasC1540; target->has_c1540 = !!hasC1540;
_targets.push_back(std::move(target)); _targets.push_back(std::move(target));
} }