diff --git a/Analyser/Static/Commodore/StaticAnalyser.cpp b/Analyser/Static/Commodore/StaticAnalyser.cpp index b7e63f160..8ae02d491 100644 --- a/Analyser/Static/Commodore/StaticAnalyser.cpp +++ b/Analyser/Static/Commodore/StaticAnalyser.cpp @@ -16,6 +16,7 @@ #include "../../../Outputs/Log.hpp" #include +#include #include using namespace Analyser::Static::Commodore; @@ -78,7 +79,7 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media } if(!files.empty()) { - target->memory_model = Target::MemoryModel::Unexpanded; + auto memory_model = Target::MemoryModel::Unexpanded; std::ostringstream string_stream; string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ","; if(files.front().is_basic()) { @@ -94,16 +95,18 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media default: LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address); case 0x1001: - target->memory_model = Target::MemoryModel::Unexpanded; + memory_model = Target::MemoryModel::Unexpanded; break; case 0x1201: - target->memory_model = Target::MemoryModel::ThirtyTwoKB; + memory_model = Target::MemoryModel::ThirtyTwoKB; break; case 0x0401: - target->memory_model = Target::MemoryModel::EightKB; + memory_model = Target::MemoryModel::EightKB; break; } + target->set_memory_model(memory_model); + // General approach: increase memory size conservatively such that the largest file found will fit. // for(File &file : files) { // 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()) { - // Inspect filename for a region hint. + // Inspect filename for configuration hints. std::string lowercase_name = file_name; 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) { 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. target->has_c1540 = !target->media.disks.empty(); diff --git a/Analyser/Static/Commodore/Target.hpp b/Analyser/Static/Commodore/Target.hpp index a9737a428..89ad4538e 100644 --- a/Analyser/Static/Commodore/Target.hpp +++ b/Analyser/Static/Commodore/Target.hpp @@ -31,7 +31,26 @@ struct Target: public ::Analyser::Static::Target { 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; bool has_c1540 = false; std::string loading_command; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 3a34d30f8..20e6bd3ba 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -320,7 +320,7 @@ class ConcreteMachine: tape_->set_delegate(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_)); 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_write_memory_map_, &ram_[baseaddr], baseaddr, length); - // Add 6502-visible RAM as requested - switch(target.memory_model) { - case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded: - // The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000. - set_ram(0x0000, 0x0400); - set_ram(0x1000, 0x1000); - break; - case Analyser::Static::Commodore::Target::MemoryModel::EightKB: - // 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; - } + // Add 6502-visible RAM as requested. + set_ram(0x0000, 0x0400); + set_ram(0x1000, 0x1000); // Built-in RAM. + if(target.enabled_ram.bank0) set_ram(0x0400, 0x0c00); // Bank 0: 0x0400 -> 0x1000. + if(target.enabled_ram.bank1) set_ram(0x2000, 0x2000); // Bank 1: 0x2000 -> 0x4000. + if(target.enabled_ram.bank2) set_ram(0x4000, 0x2000); // Bank 2: 0x4000 -> 0x6000. + if(target.enabled_ram.bank3) set_ram(0x6000, 0x2000); // Bank 3: 0x6000 -> 0x8000. + if(target.enabled_ram.bank5) set_ram(0xa000, 0x2000); // Bank 5: 0xa000 -> 0xc000. #undef set_ram @@ -453,6 +445,8 @@ class ConcreteMachine: write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast(character_rom_.size())); write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast(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); if(!target.loading_command.empty()) { type_string(target.loading_command); @@ -711,7 +705,7 @@ class ConcreteMachine: std::vector rom_; uint16_t rom_address_, rom_length_; - uint8_t ram_[0x8000]; + uint8_t ram_[0x10000]; uint8_t colour_ram_[0x0400]; uint8_t *processor_read_memory_map_[64]; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 2caaea298..bce5aabfa 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -121,11 +121,13 @@ case CSMachineVic20RegionEuropean: target->region = Target::Region::European; break; case CSMachineVic20RegionJapanese: target->region = Target::Region::Japanese; break; } + auto memory_model = Target::MemoryModel::Unexpanded; switch(memorySize) { - default: target->memory_model = Target::MemoryModel::Unexpanded; break; - case 8: target->memory_model = Target::MemoryModel::EightKB; break; - case 32: target->memory_model = Target::MemoryModel::ThirtyTwoKB; break; + default: break; + case 8: memory_model = Target::MemoryModel::EightKB; break; + case 32: memory_model = Target::MemoryModel::ThirtyTwoKB; break; } + target->set_memory_model(memory_model); target->has_c1540 = !!hasC1540; _targets.push_back(std::move(target)); }