1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-29 16:55:59 +00:00

Simplifies initialisation procedure for all machines.

With the side effect of allowing every machine to try to load only the ROMs that it needs.
This commit is contained in:
Thomas Harte 2018-07-10 20:00:46 -04:00
parent 3e2d271566
commit 3862fdb44c
32 changed files with 610 additions and 717 deletions

View File

@ -17,9 +17,6 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique
} }
} }
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target *target) {
}
bool MultiConfigurationTarget::insert_media(const Analyser::Static::Media &media) { bool MultiConfigurationTarget::insert_media(const Analyser::Static::Media &media) {
bool inserted = false; bool inserted = false;
for(const auto &target : targets_) { for(const auto &target : targets_) {

View File

@ -29,7 +29,6 @@ struct MultiConfigurationTarget: public ConfigurationTarget::Machine {
MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines); MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
// Below is the standard ConfigurationTarget::Machine interface; see there for documentation. // Below is the standard ConfigurationTarget::Machine interface; see there for documentation.
void configure_as_target(const Analyser::Static::Target *target) override;
bool insert_media(const Analyser::Static::Media &media) override; bool insert_media(const Analyser::Static::Media &media) override;
private: private:

View File

@ -69,13 +69,13 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
target->media.cartridges = AcornCartridgesFrom(media.cartridges); target->media.cartridges = AcornCartridgesFrom(media.cartridges);
// if there are any tapes, attempt to get data from the first // if there are any tapes, attempt to get data from the first
if(media.tapes.size() > 0) { if(!media.tapes.empty()) {
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front(); std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
std::vector<File> files = GetFiles(tape); std::vector<File> files = GetFiles(tape);
tape->reset(); tape->reset();
// continue if there are any files // continue if there are any files
if(files.size()) { if(!files.empty()) {
bool is_basic = true; bool is_basic = true;
// protected files are always for *RUNning only // protected files are always for *RUNning only
@ -103,7 +103,7 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
} }
} }
if(media.disks.size() > 0) { if(!media.disks.empty()) {
std::shared_ptr<Storage::Disk::Disk> disk = media.disks.front(); std::shared_ptr<Storage::Disk::Disk> disk = media.disks.front();
std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue; std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue;
dfs_catalogue = GetDFSCatalogue(disk); dfs_catalogue = GetDFSCatalogue(disk);

View File

@ -111,7 +111,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
for(auto &tape : media.tapes) { for(auto &tape : media.tapes) {
std::vector<File> tape_files = GetFiles(tape); std::vector<File> tape_files = GetFiles(tape);
tape->reset(); tape->reset();
if(tape_files.size()) { if(!tape_files.empty()) {
for(const auto &file : tape_files) { for(const auto &file : tape_files) {
if(file.data_type == File::MachineCode) { if(file.data_type == File::MachineCode) {
std::vector<uint16_t> entry_points = {file.starting_address}; std::vector<uint16_t> entry_points = {file.starting_address};

View File

@ -44,13 +44,6 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
); );
} }
enum ROMType: int {
OS464 = 0, BASIC464,
OS664, BASIC664,
OS6128, BASIC6128,
AMSDOS
};
/*! /*!
Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output
is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period
@ -761,7 +754,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
/*! /*!
The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80. The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80.
*/ */
class ConcreteMachine: template <bool has_fdc> class ConcreteMachine:
public CRTMachine::Machine, public CRTMachine::Machine,
public ConfigurationTarget::Machine, public ConfigurationTarget::Machine,
public KeyboardMachine::Machine, public KeyboardMachine::Machine,
@ -773,7 +766,7 @@ class ConcreteMachine:
public Machine, public Machine,
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine() : ConcreteMachine(const Analyser::Static::AmstradCPC::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this), z80_(*this),
crtc_bus_handler_(ram_, interrupt_timer_), crtc_bus_handler_(ram_, interrupt_timer_),
crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_), crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_),
@ -792,7 +785,59 @@ class ConcreteMachine:
fdc_.set_clocking_hint_observer(this); fdc_.set_clocking_hint_observer(this);
tape_player_.set_clocking_hint_observer(this); tape_player_.set_clocking_hint_observer(this);
// install the keyboard state class as the AY port handler
ay_.ay().set_port_handler(&key_state_); ay_.ay().set_port_handler(&key_state_);
// construct the list of necessary ROMs
std::vector<std::string> required_roms = {"amsdos.rom"};
std::string model_number;
switch(target.model) {
default:
model_number = "6128";
has_128k_ = true;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
model_number = "464";
has_128k_ = false;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
model_number = "664";
has_128k_ = false;
break;
}
required_roms.push_back("os" + model_number + ".rom");
required_roms.push_back("basic" + model_number + ".rom");
// fetch and verify the ROMs
const auto roms = rom_fetcher("AmstradCPC", required_roms);
for(std::size_t index = 0; index < roms.size(); ++index) {
auto &data = roms[index];
if(!data) throw ROMMachine::Error::MissingROMs;
roms_[static_cast<int>(index)] = std::move(*data);
roms_[static_cast<int>(index)].resize(16384);
}
// Establish default memory map
upper_rom_is_paged_ = true;
upper_rom_ = ROMType::BASIC;
write_pointers_[0] = &ram_[0];
write_pointers_[1] = &ram_[16384];
write_pointers_[2] = &ram_[32768];
write_pointers_[3] = &ram_[49152];
read_pointers_[0] = roms_[ROMType::OS].data();
read_pointers_[1] = write_pointers_[1];
read_pointers_[2] = write_pointers_[2];
read_pointers_[3] = roms_[upper_rom_].data();
// Type whatever is required.
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
insert_media(target.media);
} }
/// The entry point for performing a partial Z80 machine cycle. /// The entry point for performing a partial Z80 machine cycle.
@ -847,8 +892,8 @@ class ConcreteMachine:
} }
// Check for an upper ROM selection // Check for an upper ROM selection
if(has_fdc_ && !(address&0x2000)) { if(has_fdc && !(address&0x2000)) {
upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : rom_model_ + 1; upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : ROMType::BASIC;
if(upper_rom_is_paged_) read_pointers_[3] = roms_[upper_rom_].data(); if(upper_rom_is_paged_) read_pointers_[3] = roms_[upper_rom_].data();
} }
@ -867,13 +912,13 @@ class ConcreteMachine:
} }
// Check for an FDC access // Check for an FDC access
if(has_fdc_ && (address & 0x580) == 0x100) { if(has_fdc && (address & 0x580) == 0x100) {
flush_fdc(); flush_fdc();
fdc_.set_register(address & 1, *cycle.value); fdc_.set_register(address & 1, *cycle.value);
} }
// Check for a disk motor access // Check for a disk motor access
if(has_fdc_ && !(address & 0x580)) { if(has_fdc && !(address & 0x580)) {
flush_fdc(); flush_fdc();
fdc_.set_motor_on(!!(*cycle.value)); fdc_.set_motor_on(!!(*cycle.value));
} }
@ -888,7 +933,7 @@ class ConcreteMachine:
} }
// Check for an FDC access // Check for an FDC access
if(has_fdc_ && (address & 0x580) == 0x100) { if(has_fdc && (address & 0x580) == 0x100) {
flush_fdc(); flush_fdc();
*cycle.value &= fdc_.get_register(address & 1); *cycle.value &= fdc_.get_register(address & 1);
} }
@ -961,50 +1006,6 @@ class ConcreteMachine:
z80_.run_for(cycles); z80_.run_for(cycles);
} }
/// The ConfigurationTarget entry point; should configure this meachine as described by @c target.
void configure_as_target(const Analyser::Static::Target *target) override final {
auto *const cpc_target = dynamic_cast<const Analyser::Static::AmstradCPC::Target *>(target);
switch(cpc_target->model) {
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
rom_model_ = ROMType::OS464;
has_128k_ = false;
has_fdc_ = false;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
rom_model_ = ROMType::OS664;
has_128k_ = false;
has_fdc_ = true;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC6128:
rom_model_ = ROMType::OS6128;
has_128k_ = true;
has_fdc_ = true;
break;
}
// Establish default memory map
upper_rom_is_paged_ = true;
upper_rom_ = rom_model_ + 1;
write_pointers_[0] = &ram_[0];
write_pointers_[1] = &ram_[16384];
write_pointers_[2] = &ram_[32768];
write_pointers_[3] = &ram_[49152];
read_pointers_[0] = roms_[rom_model_].data();
read_pointers_[1] = write_pointers_[1];
read_pointers_[2] = write_pointers_[2];
read_pointers_[3] = roms_[upper_rom_].data();
// Type whatever is required.
if(!cpc_target->loading_command.empty()) {
type_string(cpc_target->loading_command);
}
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override final { bool insert_media(const Analyser::Static::Media &media) override final {
// If there are any tapes supplied, use the first of them. // If there are any tapes supplied, use the first of them.
if(!media.tapes.empty()) { if(!media.tapes.empty()) {
@ -1019,28 +1020,7 @@ class ConcreteMachine:
if(c == 4) break; if(c == 4) break;
} }
return !media.tapes.empty() || (!media.disks.empty() && has_fdc_); return !media.tapes.empty() || (!media.disks.empty() && has_fdc);
}
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
auto roms = roms_with_names(
"AmstradCPC",
{
"os464.rom", "basic464.rom",
"os664.rom", "basic664.rom",
"os6128.rom", "basic6128.rom",
"amsdos.rom"
});
for(std::size_t index = 0; index < roms.size(); ++index) {
auto &data = roms[index];
if(!data) return false;
roms_[static_cast<int>(index)] = std::move(*data);
roms_[static_cast<int>(index)].resize(16384);
}
return true;
} }
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final { void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final {
@ -1078,7 +1058,7 @@ class ConcreteMachine:
// MARK: - Activity Source // MARK: - Activity Source
void set_activity_observer(Activity::Observer *observer) override { void set_activity_observer(Activity::Observer *observer) override {
if(has_fdc_) fdc_.set_activity_observer(observer); if(has_fdc) fdc_.set_activity_observer(observer);
} }
// MARK: - Configuration options. // MARK: - Configuration options.
@ -1117,7 +1097,7 @@ class ConcreteMachine:
case 1: crtc_bus_handler_.set_colour(value & 0x1f); break; case 1: crtc_bus_handler_.set_colour(value & 0x1f); break;
case 2: case 2:
// Perform ROM paging. // Perform ROM paging.
read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[rom_model_].data(); read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[ROMType::OS].data();
upper_rom_is_paged_ = !(value & 8); upper_rom_is_paged_ = !(value & 8);
read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3]; read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3];
@ -1169,7 +1149,7 @@ class ConcreteMachine:
HalfCycles time_since_fdc_update_; HalfCycles time_since_fdc_update_;
void flush_fdc() { void flush_fdc() {
// Clock the FDC, if connected, using a lazy scale by two // Clock the FDC, if connected, using a lazy scale by two
if(has_fdc_ && !fdc_is_sleeping_) { if(has_fdc && !fdc_is_sleeping_) {
fdc_.run_for(Cycles(time_since_fdc_update_.as_int())); fdc_.run_for(Cycles(time_since_fdc_update_.as_int()));
} }
time_since_fdc_update_ = HalfCycles(0); time_since_fdc_update_ = HalfCycles(0);
@ -1184,13 +1164,16 @@ class ConcreteMachine:
uint8_t ram_[128 * 1024]; uint8_t ram_[128 * 1024];
std::vector<uint8_t> roms_[7]; bool fdc_is_sleeping_;
int rom_model_;
bool has_fdc_, fdc_is_sleeping_;
bool tape_player_is_sleeping_; bool tape_player_is_sleeping_;
bool has_128k_; bool has_128k_;
enum ROMType: int {
AMSDOS = 0, OS = 1, BASIC = 2
};
std::vector<uint8_t> roms_[3];
bool upper_rom_is_paged_; bool upper_rom_is_paged_;
int upper_rom_; ROMType upper_rom_;
uint8_t *ram_pages_[4]; uint8_t *ram_pages_[4];
uint8_t *read_pointers_[4]; uint8_t *read_pointers_[4];
@ -1205,8 +1188,13 @@ class ConcreteMachine:
using namespace AmstradCPC; using namespace AmstradCPC;
// See header; constructs and returns an instance of the Amstrad CPC. // See header; constructs and returns an instance of the Amstrad CPC.
Machine *Machine::AmstradCPC() { Machine *Machine::AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new AmstradCPC::ConcreteMachine; using Target = Analyser::Static::AmstradCPC::Target;
const Target *const cpc_target = dynamic_cast<const Target *>(target);
switch(cpc_target->model) {
default: return new AmstradCPC::ConcreteMachine<true>(*cpc_target, rom_fetcher);
case Target::Model::CPC464: return new AmstradCPC::ConcreteMachine<false>(*cpc_target, rom_fetcher);
}
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -10,8 +10,9 @@
#define AmstradCPC_hpp #define AmstradCPC_hpp
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <cstdint>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -28,7 +29,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns an Amstrad CPC. /// Creates and returns an Amstrad CPC.
static Machine *AmstradCPC(); static Machine *AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
} }

View File

@ -89,7 +89,7 @@ class ConcreteMachine:
} }
uint8_t ram_[65536], aux_ram_[65536]; uint8_t ram_[65536], aux_ram_[65536];
std::vector<uint8_t> apple2_rom_, apple2plus_rom_, rom_; std::vector<uint8_t> rom_;
std::vector<uint8_t> character_rom_; std::vector<uint8_t> character_rom_;
uint8_t keyboard_input_ = 0x00; uint8_t keyboard_input_ = 0x00;
@ -98,8 +98,6 @@ class ConcreteMachine:
Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_; Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
Cycles cycles_since_audio_update_; Cycles cycles_since_audio_update_;
ROMMachine::ROMFetcher rom_fetcher_;
// MARK: - Cards // MARK: - Cards
std::array<std::unique_ptr<AppleII::Card>, 7> cards_; std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
Cycles cycles_since_card_update_; Cycles cycles_since_card_update_;
@ -226,7 +224,7 @@ class ConcreteMachine:
} }
public: public:
ConcreteMachine(): ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
m6502_(*this), m6502_(*this),
video_bus_handler_(ram_), video_bus_handler_(ram_),
audio_toggle_(audio_queue_), audio_toggle_(audio_queue_),
@ -254,6 +252,41 @@ class ConcreteMachine:
// Add a couple of joysticks. // Add a couple of joysticks.
joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick);
joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick);
// Pick the required ROMs.
using Target = Analyser::Static::AppleII::Target;
std::vector<std::string> rom_names = {"apple2-character.rom"};
switch(target.model) {
default:
rom_names.push_back("apple2o.rom");
break;
case Target::Model::IIplus:
rom_names.push_back("apple2.rom");
break;
}
const auto roms = rom_fetcher("AppleII", rom_names);
if(!roms[0] || !roms[1]) {
throw ROMMachine::Error::MissingROMs;
}
character_rom_ = std::move(*roms[0]);
rom_ = std::move(*roms[1]);
if(rom_.size() > 12*1024) {
rom_.erase(rom_.begin(), rom_.begin() + static_cast<off_t>(rom_.size()) - 12*1024);
}
if(target.disk_controller != Target::DiskController::None) {
// Apple recommended slot 6 for the (first) Disk II.
install_card(6, new AppleII::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector));
}
// Set up the default memory blocks.
memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_;
memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200];
set_language_card_paging();
insert_media(target.media);
} }
~ConcreteMachine() { ~ConcreteMachine() {
@ -596,27 +629,6 @@ class ConcreteMachine:
audio_queue_.perform(); audio_queue_.perform();
} }
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
auto roms = roms_with_names(
"AppleII",
{
"apple2o.rom",
"apple2.rom",
"apple2-character.rom"
});
if(!roms[0] || !roms[1] || !roms[2]) return false;
apple2_rom_ = std::move(*roms[0]);
apple2plus_rom_ = std::move(*roms[1]);
character_rom_ = std::move(*roms[2]);
rom_fetcher_ = roms_with_names;
return true;
}
void run_for(const Cycles cycles) override { void run_for(const Cycles cycles) override {
m6502_.run_for(cycles); m6502_.run_for(cycles);
} }
@ -651,28 +663,6 @@ class ConcreteMachine:
} }
// MARK: ConfigurationTarget // MARK: ConfigurationTarget
void configure_as_target(const Analyser::Static::Target *target) override {
using Target = Analyser::Static::AppleII::Target;
auto *const apple_target = dynamic_cast<const Target *>(target);
if(apple_target->disk_controller != Target::DiskController::None) {
// Apple recommended slot 6 for the (first) Disk II.
install_card(6, new AppleII::DiskIICard(rom_fetcher_, apple_target->disk_controller == Target::DiskController::SixteenSector));
}
rom_ = (apple_target->model == Target::Model::II) ? apple2_rom_ : apple2plus_rom_;
if(rom_.size() > 12*1024) {
rom_.erase(rom_.begin(), rom_.begin() + static_cast<off_t>(rom_.size()) - 12*1024);
}
// Set up the default memory blocks.
memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_;
memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200];
set_language_card_paging();
insert_media(apple_target->media);
}
bool insert_media(const Analyser::Static::Media &media) override { bool insert_media(const Analyser::Static::Media &media) override {
if(!media.disks.empty()) { if(!media.disks.empty()) {
auto diskii = diskii_card(); auto diskii = diskii_card();
@ -722,8 +712,10 @@ class ConcreteMachine:
using namespace AppleII; using namespace AppleII;
Machine *Machine::AppleII() { Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new ConcreteMachine; using Target = Analyser::Static::AppleII::Target;
const Target *const appleii_target = dynamic_cast<const Target *>(target);
return new ConcreteMachine(*appleii_target, rom_fetcher);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -10,6 +10,8 @@
#define AppleII_hpp #define AppleII_hpp
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -24,7 +26,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns an AppleII. /// Creates and returns an AppleII.
static Machine *AppleII(); static Machine *AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
}; };

View File

@ -11,7 +11,7 @@
using namespace AppleII; using namespace AppleII;
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) { DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) {
auto roms = rom_fetcher( const auto roms = rom_fetcher(
"DiskII", "DiskII",
{ {
is_16_sector ? "boot-16.rom" : "boot-13.rom", is_16_sector ? "boot-16.rom" : "boot-13.rom",

View File

@ -76,24 +76,16 @@ class Joystick: public Inputs::ConcreteJoystick {
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CRTMachine::Machine, public CRTMachine::Machine,
public ConfigurationTarget::Machine,
public JoystickMachine::Machine, public JoystickMachine::Machine,
public Outputs::CRT::Delegate { public Outputs::CRT::Delegate {
public: public:
ConcreteMachine() { ConcreteMachine(const Analyser::Static::Atari::Target &target) {
set_clock_rate(NTSC_clock_rate); set_clock_rate(NTSC_clock_rate);
}
~ConcreteMachine() { const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
close_output();
}
void configure_as_target(const Analyser::Static::Target *target) override {
auto *const atari_target = dynamic_cast<const Analyser::Static::Atari::Target *>(target);
const std::vector<uint8_t> &rom = target->media.cartridges.front()->get_segments().front().data;
using PagingModel = Analyser::Static::Atari::Target::PagingModel; using PagingModel = Analyser::Static::Atari::Target::PagingModel;
switch(atari_target->paging_model) { switch(target.paging_model) {
case PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break; case PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
case PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break; case PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break;
case PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break; case PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break;
@ -105,21 +97,21 @@ class ConcreteMachine:
case PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break; case PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break;
case PagingModel::Atari8k: case PagingModel::Atari8k:
if(atari_target->uses_superchip) { if(target.uses_superchip) {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8kSuperChip>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8kSuperChip>(rom));
} else { } else {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8k>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8k>(rom));
} }
break; break;
case PagingModel::Atari16k: case PagingModel::Atari16k:
if(atari_target->uses_superchip) { if(target.uses_superchip) {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16kSuperChip>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16kSuperChip>(rom));
} else { } else {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16k>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16k>(rom));
} }
break; break;
case PagingModel::Atari32k: case PagingModel::Atari32k:
if(atari_target->uses_superchip) { if(target.uses_superchip) {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32kSuperChip>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32kSuperChip>(rom));
} else { } else {
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32k>(rom)); bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32k>(rom));
@ -131,8 +123,8 @@ class ConcreteMachine:
joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1)); joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
} }
bool insert_media(const Analyser::Static::Media &media) override { ~ConcreteMachine() {
return false; close_output();
} }
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override { std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
@ -254,8 +246,10 @@ class ConcreteMachine:
using namespace Atari2600; using namespace Atari2600;
Machine *Machine::Atari2600() { Machine *Machine::Atari2600(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new Atari2600::ConcreteMachine; using Target = Analyser::Static::Atari::Target;
const Target *const atari_target = dynamic_cast<const Target *>(target);
return new Atari2600::ConcreteMachine(*atari_target);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -9,6 +9,10 @@
#ifndef Atari2600_cpp #ifndef Atari2600_cpp
#define Atari2600_cpp #define Atari2600_cpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include "Atari2600Inputs.h" #include "Atari2600Inputs.h"
namespace Atari2600 { namespace Atari2600 {
@ -21,7 +25,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns an Atari 2600 on the heap. /// Creates and returns an Atari 2600 on the heap.
static Machine *Atari2600(); static Machine *Atari2600(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
/// Sets the switch @c input to @c state. /// Sets the switch @c input to @c state.
virtual void set_switch_is_enabled(Atari2600Switch input, bool state) = 0; virtual void set_switch_is_enabled(Atari2600Switch input, bool state) = 0;

View File

@ -26,7 +26,7 @@ namespace CRTMachine {
that optionally provide a speaker, and that nominate a clock rate and can announce to a delegate that optionally provide a speaker, and that nominate a clock rate and can announce to a delegate
should that clock rate change. should that clock rate change.
*/ */
class Machine: public ROMMachine::Machine { class Machine {
public: public:
/*! /*!
Causes the machine to set up its CRT and, if it has one, speaker. The caller guarantees Causes the machine to set up its CRT and, if it has one, speaker. The caller guarantees

View File

@ -112,7 +112,7 @@ class ConcreteMachine:
public JoystickMachine::Machine { public JoystickMachine::Machine {
public: public:
ConcreteMachine() : ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this), z80_(*this),
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider), sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
ay_(audio_queue_), ay_(audio_queue_),
@ -122,6 +122,19 @@ class ConcreteMachine:
set_clock_rate(3579545); set_clock_rate(3579545);
joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick);
joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick);
const auto roms = rom_fetcher(
"ColecoVision",
{ "coleco.rom" });
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;
}
bios_ = *roms[0];
bios_.resize(8192);
insert_media(target.media);
} }
~ConcreteMachine() { ~ConcreteMachine() {
@ -153,11 +166,6 @@ class ConcreteMachine:
z80_.run_for(cycles); z80_.run_for(cycles);
} }
void configure_as_target(const Analyser::Static::Target *target) override {
// Insert the media.
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override { bool insert_media(const Analyser::Static::Media &media) override {
if(!media.cartridges.empty()) { if(!media.cartridges.empty()) {
const auto &segment = media.cartridges.front()->get_segments().front(); const auto &segment = media.cartridges.front()->get_segments().front();
@ -181,20 +189,6 @@ class ConcreteMachine:
return true; return true;
} }
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
auto roms = roms_with_names(
"ColecoVision",
{ "coleco.rom" });
if(!roms[0]) return false;
bios_ = *roms[0];
bios_.resize(8192);
return true;
}
// MARK: Z80::BusHandler // MARK: Z80::BusHandler
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
// The SN76489 will use its ready line to trigger the Z80's wait for three // The SN76489 will use its ready line to trigger the Z80's wait for three
@ -408,8 +402,8 @@ class ConcreteMachine:
using namespace Coleco::Vision; using namespace Coleco::Vision;
Machine *Machine::ColecoVision() { Machine *Machine::ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new ConcreteMachine; return new ConcreteMachine(*target, rom_fetcher);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -9,13 +9,16 @@
#ifndef ColecoVision_hpp #ifndef ColecoVision_hpp
#define ColecoVision_hpp #define ColecoVision_hpp
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace Coleco { namespace Coleco {
namespace Vision { namespace Vision {
class Machine { class Machine {
public: public:
virtual ~Machine(); virtual ~Machine();
static Machine *ColecoVision(); static Machine *ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
} }

View File

@ -9,6 +9,25 @@
#ifndef Commodore1540_hpp #ifndef Commodore1540_hpp
#define Commodore1540_hpp #define Commodore1540_hpp
namespace Commodore {
namespace C1540 {
/// Defines the type of drive this 1540 hardware is configured as.
enum class Personality {
C1540,
C1541
};
/*
Implementation note: this is defined up here so that it precedes
C1540Base.hpp below. The alternative option was to factor it out,
but the whole point of the C1540.hpp/C1540Base.hpp split is supposed
to be to create a single file of public interface.
*/
}
}
#include "../SerialBus.hpp" #include "../SerialBus.hpp"
#include "../../ROMMachine.hpp" #include "../../ROMMachine.hpp"
#include "../../../Storage/Disk/Disk.hpp" #include "../../../Storage/Disk/Disk.hpp"
@ -20,18 +39,9 @@ namespace C1540 {
/*! /*!
Provides an emulation of the C1540. Provides an emulation of the C1540.
*/ */
class Machine: public MachineBase, public ROMMachine::Machine { class Machine: public MachineBase {
public: public:
enum Personality { Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher);
C1540,
C1541
};
Machine(Personality p);
/*!
Sets the source for this drive's ROM image.
*/
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names);
/*! /*!
Sets the serial bus to which this drive should attach itself. Sets the serial bus to which this drive should attach itself.
@ -43,9 +53,6 @@ class Machine: public MachineBase, public ROMMachine::Machine {
/// Inserts @c disk into the drive. /// Inserts @c disk into the drive.
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk); void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
private:
Personality personality_;
}; };
} }

View File

@ -16,7 +16,7 @@
using namespace Commodore::C1540; using namespace Commodore::C1540;
MachineBase::MachineBase() : MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
Storage::Disk::Controller(1000000), Storage::Disk::Controller(1000000),
m6502_(*this), m6502_(*this),
drive_(new Storage::Disk::Drive(1000000, 300, 2)), drive_(new Storage::Disk::Drive(1000000, 300, 2)),
@ -38,9 +38,22 @@ MachineBase::MachineBase() :
// attach the only drive there is // attach the only drive there is
set_drive(drive_); set_drive(drive_);
std::string rom_name;
switch(personality) {
case Personality::C1540: rom_name = "1540.bin"; break;
case Personality::C1541: rom_name = "1541.bin"; break;
}
auto roms = rom_fetcher("Commodore1540", {rom_name});
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;
}
std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size()));
} }
Machine::Machine(Commodore::C1540::Machine::Personality personality) : personality_(personality) {} Machine::Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
MachineBase(personality, rom_fetcher) {}
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) { void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) {
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus); Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
@ -82,19 +95,6 @@ Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation,
return Cycles(1); return Cycles(1);
} }
bool Machine::set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) {
std::string rom_name;
switch(personality_) {
case Personality::C1540: rom_name = "1540.bin"; break;
case Personality::C1541: rom_name = "1541.bin"; break;
}
auto roms = roms_with_names("Commodore1540", {rom_name});
if(!roms[0]) return false;
std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size()));
return true;
}
void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) { void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) {
drive_->set_disk(disk); drive_->set_disk(disk);
} }

View File

@ -19,6 +19,8 @@
#include "../../../../Storage/Disk/Controller/DiskController.hpp" #include "../../../../Storage/Disk/Controller/DiskController.hpp"
#include "../C1540.hpp"
namespace Commodore { namespace Commodore {
namespace C1540 { namespace C1540 {
@ -125,7 +127,7 @@ class MachineBase:
public Storage::Disk::Controller { public Storage::Disk::Controller {
public: public:
MachineBase(); MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher);
// to satisfy CPU::MOS6502::Processor // to satisfy CPU::MOS6502::Processor
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value); Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);

View File

@ -62,18 +62,6 @@ enum JoystickInput {
Fire = 0x20 Fire = 0x20
}; };
enum ROM {
CharactersDanish = 0,
CharactersEnglish,
CharactersJapanese,
CharactersSwedish,
KernelDanish,
KernelJapanese,
KernelNTSC,
KernelPAL,
KernelSwedish
};
/*! /*!
Models the user-port VIA, which is the Vic's connection point for controlling its tape recorder; Models the user-port VIA, which is the Vic's connection point for controlling its tape recorder;
sensing the presence or absence of a tape and controlling the tape motor; and reading the current sensing the presence or absence of a tape and controlling the tape motor; and reading the current
@ -304,7 +292,7 @@ class ConcreteMachine:
public ClockingHint::Observer, public ClockingHint::Observer,
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine() : ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(*this),
user_port_via_port_handler_(new UserPortVIA), user_port_via_port_handler_(new UserPortVIA),
keyboard_via_port_handler_(new KeyboardVIA), keyboard_via_port_handler_(new KeyboardVIA),
@ -332,127 +320,68 @@ class ConcreteMachine:
// 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_));
}
// Obtains the system ROMs. std::vector<std::string> rom_names = { "basic.bin" };
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { switch(target.region) {
rom_fetcher_ = roms_with_names; default:
rom_names.push_back("characters-english.bin");
auto roms = roms_with_names( rom_names.push_back("kernel-pal.bin");
"Vic20", break;
{ case Analyser::Static::Commodore::Target::Region::American:
"characters-danish.bin", rom_names.push_back("characters-english.bin");
"characters-english.bin", rom_names.push_back("kernel-ntsc.bin");
"characters-japanese.bin", break;
"characters-swedish.bin", case Analyser::Static::Commodore::Target::Region::Danish:
"kernel-danish.bin", rom_names.push_back("characters-danish.bin");
"kernel-japanese.bin", rom_names.push_back("kernel-danish.bin");
"kernel-ntsc.bin", break;
"kernel-pal.bin", case Analyser::Static::Commodore::Target::Region::Japanese:
"kernel-swedish.bin", rom_names.push_back("characters-japanese.bin");
"basic.bin" rom_names.push_back("kernel-japanese.bin");
}); break;
case Analyser::Static::Commodore::Target::Region::Swedish:
for(std::size_t index = 0; index < roms.size(); ++index) { rom_names.push_back("characters-swedish.bin");
auto &data = roms[index]; rom_names.push_back("kernel-japanese.bin");
if(!data) return false; break;
if(index < 9) roms_[index] = std::move(*data); else basic_rom_ = std::move(*data);
} }
const auto roms = rom_fetcher("Vic20", rom_names);
for(const auto &rom: roms) {
if(!rom) {
throw ROMMachine::Error::MissingROMs;
}
}
basic_rom_ = std::move(*roms[0]);
character_rom_ = std::move(*roms[1]);
kernel_rom_ = std::move(*roms[2]);
// Characters ROMs should be 4kb. // Characters ROMs should be 4kb.
for(std::size_t index = 0; index < 4; ++index) roms_[index].resize(4096); character_rom_.resize(4096);
// Kernel ROMs and the BASIC ROM should be 8kb. // Kernel ROMs and the BASIC ROM should be 8kb.
for(std::size_t index = 4; index < roms.size(); ++index) roms_[index].resize(8192); kernel_rom_.resize(8192);
return true; if(target.has_c1540) {
}
void configure_as_target(const Analyser::Static::Target *target) override final {
commodore_target_ = *dynamic_cast<const Analyser::Static::Commodore::Target *>(target);
if(!commodore_target_.loading_command.empty()) {
type_string(commodore_target_.loading_command);
}
if(commodore_target_.has_c1540) {
// construct the 1540 // construct the 1540
c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Machine::C1540)); c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Personality::C1540, rom_fetcher));
// attach it to the serial bus // attach it to the serial bus
c1540_->set_serial_bus(serial_bus_); c1540_->set_serial_bus(serial_bus_);
// give it a means to obtain its ROM
c1540_->set_rom_fetcher(rom_fetcher_);
// give it a little warm up // give it a little warm up
c1540_->run_for(Cycles(2000000)); c1540_->run_for(Cycles(2000000));
} }
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override final {
if(!media.tapes.empty()) {
tape_->set_tape(media.tapes.front());
}
if(!media.disks.empty() && c1540_) {
c1540_->set_disk(media.disks.front());
}
if(!media.cartridges.empty()) {
rom_address_ = 0xa000;
std::vector<uint8_t> rom_image = media.cartridges.front()->get_segments().front().data;
rom_length_ = static_cast<uint16_t>(rom_image.size());
rom_ = rom_image;
rom_.resize(0x2000);
}
set_use_fast_tape();
return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty();
}
void set_key_state(uint16_t key, bool is_pressed) override final {
if(key != KeyRestore)
keyboard_via_port_handler_->set_key_state(key, is_pressed);
else
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
}
void clear_all_keys() override final {
keyboard_via_port_handler_->clear_all_keys();
}
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
return joysticks_;
}
void set_ntsc_6560() {
set_clock_rate(1022727);
if(mos6560_) {
mos6560_->set_output_mode(MOS::MOS6560::OutputMode::NTSC);
mos6560_->set_clock_rate(1022727);
}
}
void set_pal_6560() {
set_clock_rate(1108404);
if(mos6560_) {
mos6560_->set_output_mode(MOS::MOS6560::OutputMode::PAL);
mos6560_->set_clock_rate(1108404);
}
}
void set_memory_map(Analyser::Static::Commodore::Target::MemoryModel memory_model, Analyser::Static::Commodore::Target::Region region) {
// Determine PAL/NTSC // Determine PAL/NTSC
if(region == Analyser::Static::Commodore::Target::Region::American || region == Analyser::Static::Commodore::Target::Region::Japanese) { if(target.region == Analyser::Static::Commodore::Target::Region::American || target.region == Analyser::Static::Commodore::Target::Region::Japanese) {
// NTSC // NTSC
set_ntsc_6560(); set_clock_rate(1022727);
output_mode_ = MOS::MOS6560::OutputMode::NTSC;
} else { } else {
// PAL // PAL
set_pal_6560(); set_clock_rate(1108404);
output_mode_ = MOS::MOS6560::OutputMode::PAL;
} }
// Initialise the memory maps as all pointing to nothing // Initialise the memory maps as all pointing to nothing
@ -465,7 +394,7 @@ class ConcreteMachine:
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(memory_model) { switch(target.memory_model) {
case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded: case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded:
// The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000. // The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000.
set_ram(0x0000, 0x0400); set_ram(0x0000, 0x0400);
@ -512,39 +441,53 @@ class ConcreteMachine:
write_to_map(processor_read_memory_map_, basic_rom_.data(), 0xc000, static_cast<uint16_t>(basic_rom_.size())); write_to_map(processor_read_memory_map_, basic_rom_.data(), 0xc000, static_cast<uint16_t>(basic_rom_.size()));
// install the system ROM // install the system ROM
ROM character_rom; write_to_map(processor_read_memory_map_, character_rom_.data(), 0x8000, static_cast<uint16_t>(character_rom_.size()));
ROM kernel_rom; write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast<uint16_t>(character_rom_.size()));
switch(region) { write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast<uint16_t>(kernel_rom_.size()));
default:
character_rom = CharactersEnglish; insert_media(target.media);
kernel_rom = KernelPAL; if(!target.loading_command.empty()) {
break; type_string(target.loading_command);
case Analyser::Static::Commodore::Target::Region::American: }
character_rom = CharactersEnglish; }
kernel_rom = KernelNTSC;
break; bool insert_media(const Analyser::Static::Media &media) override final {
case Analyser::Static::Commodore::Target::Region::Danish: if(!media.tapes.empty()) {
character_rom = CharactersDanish; tape_->set_tape(media.tapes.front());
kernel_rom = KernelDanish;
break;
case Analyser::Static::Commodore::Target::Region::Japanese:
character_rom = CharactersJapanese;
kernel_rom = KernelJapanese;
break;
case Analyser::Static::Commodore::Target::Region::Swedish:
character_rom = CharactersSwedish;
kernel_rom = KernelSwedish;
break;
} }
write_to_map(processor_read_memory_map_, roms_[character_rom].data(), 0x8000, static_cast<uint16_t>(roms_[character_rom].size())); if(!media.disks.empty() && c1540_) {
write_to_map(mos6560_bus_handler_.video_memory_map, roms_[character_rom].data(), 0x0000, static_cast<uint16_t>(roms_[character_rom].size())); c1540_->set_disk(media.disks.front());
write_to_map(processor_read_memory_map_, roms_[kernel_rom].data(), 0xe000, static_cast<uint16_t>(roms_[kernel_rom].size())); }
// install the inserted ROM if there is one if(!media.cartridges.empty()) {
if(!rom_.empty()) { rom_address_ = 0xa000;
std::vector<uint8_t> rom_image = media.cartridges.front()->get_segments().front().data;
rom_length_ = static_cast<uint16_t>(rom_image.size());
rom_ = rom_image;
rom_.resize(0x2000);
write_to_map(processor_read_memory_map_, rom_.data(), rom_address_, rom_length_); write_to_map(processor_read_memory_map_, rom_.data(), rom_address_, rom_length_);
} }
set_use_fast_tape();
return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty();
}
void set_key_state(uint16_t key, bool is_pressed) override final {
if(key != KeyRestore)
keyboard_via_port_handler_->set_key_state(key, is_pressed);
else
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
}
void clear_all_keys() override final {
keyboard_via_port_handler_->clear_all_keys();
}
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
return joysticks_;
} }
// to satisfy CPU::MOS6502::Processor // to satisfy CPU::MOS6502::Processor
@ -680,8 +623,8 @@ class ConcreteMachine:
void setup_output(float aspect_ratio) override final { void setup_output(float aspect_ratio) override final {
mos6560_.reset(new MOS::MOS6560::MOS6560<Vic6560BusHandler>(mos6560_bus_handler_)); mos6560_.reset(new MOS::MOS6560::MOS6560<Vic6560BusHandler>(mos6560_bus_handler_));
mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20. mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
// Make a guess: PAL. Without setting a clock rate the 6560 isn't fully set up so contractually something must be set. mos6560_->set_output_mode(output_mode_);
set_memory_map(commodore_target_.memory_model, commodore_target_.region); mos6560_->set_clock_rate(get_clock_rate());
} }
void close_output() override final { void close_output() override final {
@ -760,12 +703,8 @@ class ConcreteMachine:
void update_video() { void update_video() {
mos6560_->run_for(cycles_since_mos6560_update_.flush()); mos6560_->run_for(cycles_since_mos6560_update_.flush());
} }
Analyser::Static::Commodore::Target commodore_target_;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_; CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
std::vector<uint8_t> roms_[9];
std::vector<uint8_t> character_rom_; std::vector<uint8_t> character_rom_;
std::vector<uint8_t> basic_rom_; std::vector<uint8_t> basic_rom_;
std::vector<uint8_t> kernel_rom_; std::vector<uint8_t> kernel_rom_;
@ -775,8 +714,6 @@ class ConcreteMachine:
uint8_t ram_[0x8000]; uint8_t ram_[0x8000];
uint8_t colour_ram_[0x0400]; uint8_t colour_ram_[0x0400];
std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> rom_fetcher_;
uint8_t *processor_read_memory_map_[64]; uint8_t *processor_read_memory_map_[64];
uint8_t *processor_write_memory_map_[64]; uint8_t *processor_write_memory_map_[64];
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) { void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) {
@ -794,6 +731,7 @@ class ConcreteMachine:
Cycles cycles_since_mos6560_update_; Cycles cycles_since_mos6560_update_;
Vic6560BusHandler mos6560_bus_handler_; Vic6560BusHandler mos6560_bus_handler_;
MOS::MOS6560::OutputMode output_mode_;
std::unique_ptr<MOS::MOS6560::MOS6560<Vic6560BusHandler>> mos6560_; std::unique_ptr<MOS::MOS6560::MOS6560<Vic6560BusHandler>> mos6560_;
std::shared_ptr<UserPortVIA> user_port_via_port_handler_; std::shared_ptr<UserPortVIA> user_port_via_port_handler_;
std::shared_ptr<KeyboardVIA> keyboard_via_port_handler_; std::shared_ptr<KeyboardVIA> keyboard_via_port_handler_;
@ -822,8 +760,10 @@ class ConcreteMachine:
using namespace Commodore::Vic20; using namespace Commodore::Vic20;
Machine *Machine::Vic20() { Machine *Machine::Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new Vic20::ConcreteMachine; using Target = Analyser::Static::Commodore::Target;
const Target *const commodore_target = dynamic_cast<const Target *>(target);
return new Vic20::ConcreteMachine(*commodore_target, rom_fetcher);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -10,6 +10,11 @@
#define Vic20_hpp #define Vic20_hpp
#include "../../../Configurable/Configurable.hpp" #include "../../../Configurable/Configurable.hpp"
#include "../../../Analyser/Static/StaticAnalyser.hpp"
#include "../../ROMMachine.hpp"
#include <memory>
#include <vector>
namespace Commodore { namespace Commodore {
namespace Vic20 { namespace Vic20 {
@ -22,7 +27,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns a Vic-20. /// Creates and returns a Vic-20.
static Machine *Vic20(); static Machine *Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
} }

View File

@ -22,9 +22,6 @@ namespace ConfigurationTarget {
*/ */
class Machine { class Machine {
public: public:
/// Instructs the machine to configure itself as described by @c target and insert the included media.
virtual void configure_as_target(const Analyser::Static::Target *target) = 0;
/*! /*!
Requests that the machine insert @c media as a modification to current state Requests that the machine insert @c media as a modification to current state

View File

@ -49,10 +49,10 @@ class ConcreteMachine:
public Utility::TypeRecipient, public Utility::TypeRecipient,
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine() : ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(*this),
sound_generator_(audio_queue_), sound_generator_(audio_queue_),
speaker_(sound_generator_) { speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_)); memset(key_states_, 0, sizeof(key_states_));
for(int c = 0; c < 16; c++) for(int c = 0; c < 16; c++)
memset(roms_[c], 0xff, 16384); memset(roms_[c], 0xff, 16384);
@ -61,60 +61,53 @@ class ConcreteMachine:
set_clock_rate(2000000); set_clock_rate(2000000);
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider); speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
std::vector<std::string> rom_names = {"basic.rom", "os.rom"};
if(target.has_adfs) {
rom_names.push_back("ADFS-E00_1.rom");
rom_names.push_back("ADFS-E00_2.rom");
}
const size_t dfs_rom_position = rom_names.size();
if(target.has_dfs) {
rom_names.push_back("DFS-1770-2.20.rom");
}
const auto roms = rom_fetcher("Electron", rom_names);
for(const auto &rom: roms) {
if(!rom) {
throw ROMMachine::Error::MissingROMs;
}
}
set_rom(ROM::BASIC, *roms[0], false);
set_rom(ROM::OS, *roms[1], false);
if(target.has_dfs || target.has_adfs) {
plus3_.reset(new Plus3);
if(target.has_dfs) {
set_rom(ROM::Slot0, *roms[dfs_rom_position], true);
}
if(target.has_adfs) {
set_rom(ROM::Slot4, *roms[2], true);
set_rom(ROM::Slot5, *roms[3], true);
}
}
insert_media(target.media);
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
if(target.should_shift_restart) {
shift_restart_counter_ = 1000000;
}
} }
~ConcreteMachine() { ~ConcreteMachine() {
audio_queue_.flush(); audio_queue_.flush();
} }
void set_rom(ROMSlot slot, const std::vector<uint8_t> &data, bool is_writeable) override final {
uint8_t *target = nullptr;
switch(slot) {
case ROMSlotDFS: dfs_ = data; return;
case ROMSlotADFS1: adfs1_ = data; return;
case ROMSlotADFS2: adfs2_ = data; return;
case ROMSlotOS: target = os_; break;
default:
target = roms_[slot];
rom_write_masks_[slot] = is_writeable;
break;
}
// Copy in, with mirroring.
std::size_t rom_ptr = 0;
while(rom_ptr < 16384) {
std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size());
std::memcpy(&target[rom_ptr], data.data(), size_to_copy);
rom_ptr += size_to_copy;
}
rom_inserted_[slot] = true;
}
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
auto roms = roms_with_names(
"Electron",
{
"DFS-1770-2.20.rom",
"ADFS-E00_1.rom", "ADFS-E00_2.rom",
"basic.rom", "os.rom"
});
ROMSlot slots[] = {
ROMSlotDFS,
ROMSlotADFS1, ROMSlotADFS2,
ROMSlotBASIC, ROMSlotOS
};
for(std::size_t index = 0; index < roms.size(); ++index) {
auto &data = roms[index];
if(!data) return false;
set_rom(slots[index], *data, false);
}
return true;
}
void set_key_state(uint16_t key, bool isPressed) override final { void set_key_state(uint16_t key, bool isPressed) override final {
if(key == KeyBreak) { if(key == KeyBreak) {
m6502_.set_reset_line(isPressed); m6502_.set_reset_line(isPressed);
@ -131,32 +124,6 @@ class ConcreteMachine:
if(is_holding_shift_) set_key_state(KeyShift, true); if(is_holding_shift_) set_key_state(KeyShift, true);
} }
void configure_as_target(const Analyser::Static::Target *target) override final {
auto *const acorn_target = dynamic_cast<const Analyser::Static::Acorn::Target *>(target);
if(!acorn_target->loading_command.empty()) {
type_string(acorn_target->loading_command);
}
if(acorn_target->should_shift_restart) {
shift_restart_counter_ = 1000000;
}
if(acorn_target->has_dfs || acorn_target->has_adfs) {
plus3_.reset(new Plus3);
if(acorn_target->has_dfs) {
set_rom(ROMSlot0, dfs_, true);
}
if(acorn_target->has_adfs) {
set_rom(ROMSlot4, adfs1_, true);
set_rom(ROMSlot5, adfs2_, true);
}
}
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override final { bool insert_media(const Analyser::Static::Media &media) override final {
if(!media.tapes.empty()) { if(!media.tapes.empty()) {
tape_.set_tape(media.tapes.front()); tape_.set_tape(media.tapes.front());
@ -167,11 +134,11 @@ class ConcreteMachine:
plus3_->set_disk(media.disks.front(), 0); plus3_->set_disk(media.disks.front(), 0);
} }
ROMSlot slot = ROMSlot12; ROM slot = ROM::Slot12;
for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : media.cartridges) { for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : media.cartridges) {
const ROMSlot first_slot_tried = slot; const ROM first_slot_tried = slot;
while(rom_inserted_[slot]) { while(rom_inserted_[static_cast<int>(slot)]) {
slot = static_cast<ROMSlot>((static_cast<int>(slot) + 1)&15); slot = static_cast<ROM>((static_cast<int>(slot) + 1) & 15);
if(slot == first_slot_tried) return false; if(slot == first_slot_tried) return false;
} }
set_rom(slot, cartridge->get_segments().front().data, false); set_rom(slot, cartridge->get_segments().front().data, false);
@ -258,7 +225,7 @@ class ConcreteMachine:
} }
// latch the paged ROM in case external hardware is being emulated // latch the paged ROM in case external hardware is being emulated
active_rom_ = (Electron::ROMSlot)(*value & 0xf); active_rom_ = *value & 0xf;
// apply the ULA's test // apply the ULA's test
if(*value & 0x08) { if(*value & 0x08) {
@ -363,7 +330,7 @@ class ConcreteMachine:
} }
} }
if(basic_is_active_) { if(basic_is_active_) {
*value &= roms_[ROMSlotBASIC][address & 16383]; *value &= roms_[static_cast<int>(ROM::BASIC)][address & 16383];
} }
} else if(rom_write_masks_[active_rom_]) { } else if(rom_write_masks_[active_rom_]) {
roms_[active_rom_][address & 16383] = *value; roms_[active_rom_][address & 16383] = *value;
@ -494,6 +461,48 @@ class ConcreteMachine:
} }
private: private:
enum class ROM {
Slot0 = 0,
Slot1, Slot2, Slot3,
Slot4, Slot5, Slot6, Slot7,
Keyboard = 8, Slot9,
BASIC = 10, Slot11,
Slot12, Slot13, Slot14, Slot15,
OS, DFS,
ADFS1, ADFS2
};
/*!
Sets the contents of @c slot to @c data. If @c is_writeable is @c true then writing to the slot
is enabled: it acts as if it were sideways RAM. Otherwise the slot is modelled as containing ROM.
*/
void set_rom(ROM slot, const std::vector<uint8_t> &data, bool is_writeable) {
uint8_t *target = nullptr;
switch(slot) {
case ROM::DFS: dfs_ = data; return;
case ROM::ADFS1: adfs1_ = data; return;
case ROM::ADFS2: adfs2_ = data; return;
case ROM::OS: target = os_; break;
default:
target = roms_[static_cast<int>(slot)];
rom_write_masks_[static_cast<int>(slot)] = is_writeable;
break;
}
// Copy in, with mirroring.
std::size_t rom_ptr = 0;
while(rom_ptr < 16384) {
std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size());
std::memcpy(&target[rom_ptr], data.data(), size_to_copy);
rom_ptr += size_to_copy;
}
rom_inserted_[static_cast<int>(slot)] = true;
}
// MARK: - Work deferral updates. // MARK: - Work deferral updates.
inline void update_display() { inline void update_display() {
if(cycles_since_display_update_ > 0) { if(cycles_since_display_update_ > 0) {
@ -540,7 +549,7 @@ class ConcreteMachine:
std::vector<uint8_t> dfs_, adfs1_, adfs2_; std::vector<uint8_t> dfs_, adfs1_, adfs2_;
// Paging // Paging
ROMSlot active_rom_ = ROMSlot::ROMSlot0; int active_rom_ = static_cast<int>(ROM::Slot0);
bool keyboard_is_active_ = false; bool keyboard_is_active_ = false;
bool basic_is_active_ = false; bool basic_is_active_ = false;
@ -590,8 +599,10 @@ class ConcreteMachine:
using namespace Electron; using namespace Electron;
Machine *Machine::Electron() { Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new Electron::ConcreteMachine; using Target = Analyser::Static::Acorn::Target;
const Target *const acorn_target = dynamic_cast<const Target *>(target);
return new Electron::ConcreteMachine(*acorn_target, rom_fetcher);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -10,6 +10,8 @@
#define Electron_hpp #define Electron_hpp
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@ -17,20 +19,6 @@
namespace Electron { namespace Electron {
enum ROMSlot: uint8_t {
ROMSlot0 = 0,
ROMSlot1, ROMSlot2, ROMSlot3,
ROMSlot4, ROMSlot5, ROMSlot6, ROMSlot7,
ROMSlotKeyboard = 8, ROMSlot9,
ROMSlotBASIC = 10, ROMSlot11,
ROMSlot12, ROMSlot13, ROMSlot14, ROMSlot15,
ROMSlotOS, ROMSlotDFS,
ROMSlotADFS1, ROMSlotADFS2
};
/// @returns The options available for an Electron. /// @returns The options available for an Electron.
std::vector<std::unique_ptr<Configurable::Option>> get_options(); std::vector<std::unique_ptr<Configurable::Option>> get_options();
@ -45,13 +33,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns an Electron. /// Creates and returns an Electron.
static Machine *Electron(); static Machine *Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
/*!
Sets the contents of @c slot to @c data. If @c is_writeable is @c true then writing to the slot
is enabled: it acts as if it were sideways RAM. Otherwise the slot is modelled as containing ROM.
*/
virtual void set_rom(ROMSlot slot, const std::vector<uint8_t> &data, bool is_writeable) = 0;
}; };
} }

View File

@ -91,7 +91,7 @@ class ConcreteMachine:
public ClockingHint::Observer, public ClockingHint::Observer,
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine(): ConcreteMachine(const Analyser::Static::MSX::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
z80_(*this), z80_(*this),
i8255_(i8255_port_handler_), i8255_(i8255_port_handler_),
ay_(audio_queue_), ay_(audio_queue_),
@ -112,6 +112,51 @@ class ConcreteMachine:
// Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC. // Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC.
mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f}); mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f});
// Fetch the necessary ROMs.
std::vector<std::string> rom_names = {"msx.rom"};
if(target.has_disk_drive) {
rom_names.push_back("disk.rom");
}
const auto roms = rom_fetcher("MSX", rom_names);
if(!roms[0] || (target.has_disk_drive && !roms[1])) {
throw ROMMachine::Error::MissingROMs;
}
memory_slots_[0].source = std::move(*roms[0]);
memory_slots_[0].source.resize(32768);
for(size_t c = 0; c < 8; ++c) {
for(size_t slot = 0; slot < 3; ++slot) {
memory_slots_[slot].read_pointers[c] = unpopulated_;
memory_slots_[slot].write_pointers[c] = scratch_;
}
memory_slots_[3].read_pointers[c] =
memory_slots_[3].write_pointers[c] = &ram_[c * 8192];
}
map(0, 0, 0, 32768);
page_memory(0);
// Add a disk cartridge if any disks were supplied.
if(target.has_disk_drive) {
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
memory_slots_[2].source = std::move(*roms[1]);
memory_slots_[2].source.resize(16384);
map(2, 0, 0x4000, 0x2000);
unmap(2, 0x6000, 0x2000);
}
// Insert the media.
insert_media(target.media);
// Type whatever has been requested.
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
} }
~ConcreteMachine() { ~ConcreteMachine() {
@ -152,25 +197,6 @@ class ConcreteMachine:
} }
} }
void configure_as_target(const Analyser::Static::Target *target) override {
auto *const msx_target = dynamic_cast<const Analyser::Static::MSX::Target *>(target);
// Add a disk cartridge if any disks were supplied.
if(msx_target->has_disk_drive) {
map(2, 0, 0x4000, 0x2000);
unmap(2, 0x6000, 0x2000);
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
}
// Insert the media.
insert_media(target->media);
// Type whatever has been requested.
if(!msx_target->loading_command.empty()) {
type_string(msx_target->loading_command);
}
}
bool insert_media(const Analyser::Static::Media &media) override { bool insert_media(const Analyser::Static::Media &media) override {
if(!media.cartridges.empty()) { if(!media.cartridges.empty()) {
const auto &segment = media.cartridges.front()->get_segments().front(); const auto &segment = media.cartridges.front()->get_segments().front();
@ -467,39 +493,6 @@ class ConcreteMachine:
audio_queue_.perform(); audio_queue_.perform();
} }
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
auto roms = roms_with_names(
"MSX",
{
"msx.rom",
"disk.rom"
});
if(!roms[0] || !roms[1]) return false;
memory_slots_[0].source = std::move(*roms[0]);
memory_slots_[0].source.resize(32768);
memory_slots_[2].source = std::move(*roms[1]);
memory_slots_[2].source.resize(16384);
for(size_t c = 0; c < 8; ++c) {
for(size_t slot = 0; slot < 3; ++slot) {
memory_slots_[slot].read_pointers[c] = unpopulated_;
memory_slots_[slot].write_pointers[c] = scratch_;
}
memory_slots_[3].read_pointers[c] =
memory_slots_[3].write_pointers[c] = &ram_[c * 8192];
}
map(0, 0, 0, 32768);
page_memory(0);
return true;
}
void set_keyboard_line(int line) { void set_keyboard_line(int line) {
selected_key_line_ = line; selected_key_line_ = line;
} }
@ -683,8 +676,10 @@ class ConcreteMachine:
using namespace MSX; using namespace MSX;
Machine *Machine::MSX() { Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new ConcreteMachine; using Target = Analyser::Static::MSX::Target;
const Target *const msx_target = dynamic_cast<const Target *>(target);
return new ConcreteMachine(*msx_target, rom_fetcher);
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -10,17 +10,22 @@
#define MSX_hpp #define MSX_hpp
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <memory>
#include <vector>
namespace MSX { namespace MSX {
std::vector<std::unique_ptr<Configurable::Option>> get_options();
class Machine { class Machine {
public: public:
virtual ~Machine(); virtual ~Machine();
static Machine *MSX(); static Machine *MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
std::vector<std::unique_ptr<Configurable::Option>> get_options();
} }
#endif /* MSX_hpp */ #endif /* MSX_hpp */

View File

@ -206,9 +206,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
public Machine { public Machine {
public: public:
ConcreteMachine(const Analyser::Static::Oric::Target *target) : ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(*this),
rom_type_(target ? target->rom : Analyser::Static::Oric::Target::ROM::BASIC10),
ay8910_(audio_queue_), ay8910_(audio_queue_),
speaker_(ay8910_), speaker_(ay8910_),
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_), via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
@ -222,16 +221,9 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) { if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) {
diskii_.set_clocking_hint_observer(this); diskii_.set_clocking_hint_observer(this);
} }
}
~ConcreteMachine() {
audio_queue_.flush();
}
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
std::vector<std::string> rom_names = {"colour.rom"}; std::vector<std::string> rom_names = {"colour.rom"};
switch(rom_type_) { switch(target.rom) {
case Analyser::Static::Oric::Target::ROM::BASIC10: rom_names.push_back("basic10.rom"); break; case Analyser::Static::Oric::Target::ROM::BASIC10: rom_names.push_back("basic10.rom"); break;
case Analyser::Static::Oric::Target::ROM::BASIC11: rom_names.push_back("basic11.rom"); break; case Analyser::Static::Oric::Target::ROM::BASIC11: rom_names.push_back("basic11.rom"); break;
case Analyser::Static::Oric::Target::ROM::Pravetz: rom_names.push_back("pravetz.rom"); break; case Analyser::Static::Oric::Target::ROM::Pravetz: rom_names.push_back("pravetz.rom"); break;
@ -239,13 +231,15 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
switch(disk_interface) { switch(disk_interface) {
default: break; default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: rom_names.push_back("microdisc.rom"); break; case Analyser::Static::Oric::Target::DiskInterface::Microdisc: rom_names.push_back("microdisc.rom"); break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: rom_names.push_back("8dos.rom"); break; case Analyser::Static::Oric::Target::DiskInterface::Pravetz: rom_names.push_back("8dos.rom"); break;
} }
auto roms = roms_with_names("Oric", rom_names); const auto roms = rom_fetcher("Oric", rom_names);
for(std::size_t index = 0; index < roms.size(); ++index) { for(std::size_t index = 0; index < roms.size(); ++index) {
if(!roms[index]) return false; if(!roms[index]) {
throw ROMMachine::Error::MissingROMs;
}
} }
colour_rom_ = std::move(*roms[0]); colour_rom_ = std::move(*roms[0]);
@ -261,8 +255,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
pravetz_rom_ = std::move(*roms[2]); pravetz_rom_ = std::move(*roms[2]);
pravetz_rom_.resize(512); pravetz_rom_.resize(512);
auto state_machine_rom = roms_with_names("DiskII", {"state-machine-16.rom"}); auto state_machine_rom = rom_fetcher("DiskII", {"state-machine-16.rom"});
if(!state_machine_rom[0]) return false; if(!state_machine_rom[0]) {
throw ROMMachine::Error::MissingROMs;
}
diskii_.set_state_machine(*state_machine_rom[0]); diskii_.set_state_machine(*state_machine_rom[0]);
} break; } break;
} }
@ -271,9 +267,35 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
rom_.resize(16384); rom_.resize(16384);
paged_rom_ = rom_.data(); paged_rom_ = rom_.data();
if(video_output_) video_output_->set_colour_rom(colour_rom_); switch(target.disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
microdisc_did_change_paging_flags(&microdisc_);
microdisc_.set_delegate(this);
break;
}
return true; if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
switch(target.rom) {
case Analyser::Static::Oric::Target::ROM::BASIC10:
tape_get_byte_address_ = 0xe630;
tape_speed_address_ = 0x67;
break;
case Analyser::Static::Oric::Target::ROM::BASIC11:
case Analyser::Static::Oric::Target::ROM::Pravetz:
tape_get_byte_address_ = 0xe6c9;
tape_speed_address_ = 0x024d;
break;
}
insert_media(target.media);
}
~ConcreteMachine() {
audio_queue_.flush();
} }
void set_key_state(uint16_t key, bool is_pressed) override final { void set_key_state(uint16_t key, bool is_pressed) override final {
@ -292,41 +314,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
use_fast_tape_hack_ = activate; use_fast_tape_hack_ = activate;
} }
// to satisfy ConfigurationTarget::Machine
void configure_as_target(const Analyser::Static::Target *target) override final {
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target);
switch(oric_target->disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
microdisc_did_change_paging_flags(&microdisc_);
microdisc_.set_delegate(this);
break;
}
if(!oric_target->loading_command.empty()) {
type_string(oric_target->loading_command);
}
switch(rom_type_) {
case Analyser::Static::Oric::Target::ROM::BASIC10:
tape_get_byte_address_ = 0xe630;
tape_speed_address_ = 0x67;
break;
case Analyser::Static::Oric::Target::ROM::BASIC11:
case Analyser::Static::Oric::Target::ROM::Pravetz:
tape_get_byte_address_ = 0xe6c9;
tape_speed_address_ = 0x024d;
break;
}
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override final { bool insert_media(const Analyser::Static::Media &media) override final {
bool inserted = false; bool inserted = false;
if(media.tapes.size()) { if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front()); tape_player_.set_tape(media.tapes.front());
inserted = true; inserted = true;
} }
@ -587,7 +578,6 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_; CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
// RAM and ROM // RAM and ROM
Analyser::Static::Oric::Target::ROM rom_type_;
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_; std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;
uint8_t ram_[65536]; uint8_t ram_[65536];
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
@ -650,13 +640,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
using namespace Oric; using namespace Oric;
Machine *Machine::Oric(const Analyser::Static::Target *target_hint) { Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) {
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint); auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint);
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface; using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
switch(oric_target->disk_interface) { switch(oric_target->disk_interface) {
default: return new ConcreteMachine<DiskInterface::None>(oric_target); default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(oric_target); case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(oric_target); case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
} }
} }

View File

@ -11,6 +11,7 @@
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp" #include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace Oric { namespace Oric {
@ -25,7 +26,7 @@ class Machine {
virtual ~Machine(); virtual ~Machine();
/// Creates and returns an Oric. /// Creates and returns an Oric.
static Machine *Oric(const Analyser::Static::Target *target_hint); static Machine *Oric(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
}; };
} }

View File

@ -16,13 +16,18 @@
namespace ROMMachine { namespace ROMMachine {
/*!
Defines the signature for a function that must be supplied by the host environment in order to give machines
a route for fetching any system ROMs they might need.
The caller will supply the idiomatic name of the machine plus a vector of the names of ROM files that it expects
to be present. The recevier should return a vector of unique_ptrs that either contain the contents of the
ROM from @c names that corresponds by index, or else are the nullptr
*/
typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> ROMFetcher; typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> ROMFetcher;
struct Machine { enum class Error {
/*! MissingROMs
Provides the machine with a way to obtain such ROMs as it needs.
*/
virtual bool set_rom_fetcher(const ROMFetcher &rom_with_name) { return true; }
}; };
} }

View File

@ -25,36 +25,36 @@ namespace {
::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) { ::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) {
error = Machine::Error::None; error = Machine::Error::None;
::Machine::DynamicMachine *machine = nullptr;
switch(target->machine) {
case Analyser::Machine::AmstradCPC: machine = new Machine::TypedDynamicMachine<AmstradCPC::Machine>(AmstradCPC::Machine::AmstradCPC()); break;
case Analyser::Machine::AppleII: machine = new Machine::TypedDynamicMachine<AppleII::Machine>(AppleII::Machine::AppleII()); break;
case Analyser::Machine::Atari2600: machine = new Machine::TypedDynamicMachine<Atari2600::Machine>(Atari2600::Machine::Atari2600()); break;
case Analyser::Machine::ColecoVision: machine = new Machine::TypedDynamicMachine<Coleco::Vision::Machine>(Coleco::Vision::Machine::ColecoVision()); break;
case Analyser::Machine::Electron: machine = new Machine::TypedDynamicMachine<Electron::Machine>(Electron::Machine::Electron()); break;
case Analyser::Machine::MSX: machine = new Machine::TypedDynamicMachine<MSX::Machine>(MSX::Machine::MSX()); break;
case Analyser::Machine::Oric: machine = new Machine::TypedDynamicMachine<Oric::Machine>(Oric::Machine::Oric(target)); break;
case Analyser::Machine::Vic20: machine = new Machine::TypedDynamicMachine<Commodore::Vic20::Machine>(Commodore::Vic20::Machine::Vic20()); break;
case Analyser::Machine::ZX8081: machine = new Machine::TypedDynamicMachine<ZX8081::Machine>(ZX8081::Machine::ZX8081(target)); break;
default: Machine::DynamicMachine *machine = nullptr;
error = Machine::Error::UnknownMachine; try {
return nullptr; #define BindD(name, m) case Analyser::Machine::m: machine = new Machine::TypedDynamicMachine<name::Machine>(name::Machine::m(target, rom_fetcher)); break;
} #define Bind(m) BindD(m, m)
switch(target->machine) {
Bind(AmstradCPC)
Bind(AppleII)
Bind(Atari2600)
BindD(Coleco::Vision, ColecoVision)
Bind(Electron)
Bind(MSX)
Bind(Oric)
BindD(Commodore::Vic20, Vic20)
Bind(ZX8081)
// TODO: this shouldn't depend on CRT machine's inclusion of ROM machine. default:
CRTMachine::Machine *crt_machine = machine->crt_machine(); error = Machine::Error::UnknownMachine;
if(crt_machine) {
if(!machine->crt_machine()->set_rom_fetcher(rom_fetcher)) {
delete machine;
error = Machine::Error::MissingROM;
return nullptr; return nullptr;
} }
} #undef Bind
} catch(ROMMachine::Error construction_error) {
ConfigurationTarget::Machine *configuration_target = machine->configuration_target(); switch(construction_error) {
if(configuration_target) { case ROMMachine::Error::MissingROMs:
machine->configuration_target()->configure_as_target(target); error = Machine::Error::MissingROM;
break;
default:
error = Machine::Error::UnknownError;
break;
}
} }
return machine; return machine;

View File

@ -21,6 +21,7 @@ namespace Machine {
enum class Error { enum class Error {
None, None,
UnknownError,
UnknownMachine, UnknownMachine,
MissingROM, MissingROM,
NoTargets NoTargets

View File

@ -66,7 +66,7 @@ template<bool is_zx81> class ConcreteMachine:
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,
public Machine { public Machine {
public: public:
ConcreteMachine() : ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this), z80_(*this),
tape_player_(ZX8081ClockRate), tape_player_(ZX8081ClockRate),
ay_(audio_queue_), ay_(audio_queue_),
@ -74,6 +74,55 @@ template<bool is_zx81> class ConcreteMachine:
set_clock_rate(ZX8081ClockRate); set_clock_rate(ZX8081ClockRate);
speaker_.set_input_rate(static_cast<float>(ZX8081ClockRate) / 2.0f); speaker_.set_input_rate(static_cast<float>(ZX8081ClockRate) / 2.0f);
clear_all_keys(); clear_all_keys();
const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM;
const auto roms = rom_fetcher("ZX8081", { use_zx81_rom ? "zx81.rom" : "zx80.rom" });
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
rom_ = std::move(*roms[0]);
rom_.resize(use_zx81_rom ? 8192 : 4096);
if(is_zx81) {
tape_trap_address_ = 0x37c;
tape_return_address_ = 0x380;
vsync_start_ = HalfCycles(32);
vsync_end_ = HalfCycles(64);
automatic_tape_motor_start_address_ = 0x0340;
automatic_tape_motor_end_address_ = 0x03c3;
} else {
tape_trap_address_ = 0x220;
tape_return_address_ = 0x248;
vsync_start_ = HalfCycles(26);
vsync_end_ = HalfCycles(66);
automatic_tape_motor_start_address_ = 0x0206;
automatic_tape_motor_end_address_ = 0x024d;
}
rom_mask_ = static_cast<uint16_t>(rom_.size() - 1);
switch(target.memory_model) {
case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded:
ram_.resize(1024);
ram_base_ = 16384;
ram_mask_ = 1023;
break;
case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB:
ram_.resize(16384);
ram_base_ = 16384;
ram_mask_ = 16383;
break;
case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB:
ram_.resize(65536);
ram_base_ = 8192;
ram_mask_ = 65535;
break;
}
Memory::Fuzz(ram_);
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
insert_media(target.media);
} }
~ConcreteMachine() { ~ConcreteMachine() {
@ -105,7 +154,7 @@ template<bool is_zx81> class ConcreteMachine:
video_->run_for(cycle.length); video_->run_for(cycle.length);
} }
if(is_zx81_) horizontal_counter_ %= HalfCycles(Cycles(207)); if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207));
if(!tape_advance_delay_) { if(!tape_advance_delay_) {
tape_player_.run_for(cycle.length); tape_player_.run_for(cycle.length);
} else { } else {
@ -129,7 +178,7 @@ template<bool is_zx81> class ConcreteMachine:
set_vsync(false); set_vsync(false);
} }
if(!(address & 2)) nmi_is_enabled_ = false; if(!(address & 2)) nmi_is_enabled_ = false;
if(!(address & 1)) nmi_is_enabled_ = is_zx81_; if(!(address & 1)) nmi_is_enabled_ = is_zx81;
// The below emulates the ZonX AY expansion device. // The below emulates the ZonX AY expansion device.
if(is_zx81) { if(is_zx81) {
@ -281,54 +330,6 @@ template<bool is_zx81> class ConcreteMachine:
z80_.run_for(cycles); z80_.run_for(cycles);
} }
void configure_as_target(const Analyser::Static::Target *target) override final {
auto *const zx8081_target = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target);
is_zx81_ = zx8081_target->is_ZX81;
if(is_zx81_) {
rom_ = zx81_rom_;
tape_trap_address_ = 0x37c;
tape_return_address_ = 0x380;
vsync_start_ = HalfCycles(32);
vsync_end_ = HalfCycles(64);
automatic_tape_motor_start_address_ = 0x0340;
automatic_tape_motor_end_address_ = 0x03c3;
} else {
rom_ = zx8081_target->ZX80_uses_ZX81_ROM ? zx81_rom_ : zx80_rom_;
tape_trap_address_ = 0x220;
tape_return_address_ = 0x248;
vsync_start_ = HalfCycles(26);
vsync_end_ = HalfCycles(66);
automatic_tape_motor_start_address_ = 0x0206;
automatic_tape_motor_end_address_ = 0x024d;
}
rom_mask_ = static_cast<uint16_t>(rom_.size() - 1);
switch(zx8081_target->memory_model) {
case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded:
ram_.resize(1024);
ram_base_ = 16384;
ram_mask_ = 1023;
break;
case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB:
ram_.resize(16384);
ram_base_ = 16384;
ram_mask_ = 16383;
break;
case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB:
ram_.resize(65536);
ram_base_ = 8192;
ram_mask_ = 65535;
break;
}
Memory::Fuzz(ram_);
if(!zx8081_target->loading_command.empty()) {
type_string(zx8081_target->loading_command);
}
insert_media(target->media);
}
bool insert_media(const Analyser::Static::Media &media) override final { bool insert_media(const Analyser::Static::Media &media) override final {
if(!media.tapes.empty()) { if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front()); tape_player_.set_tape(media.tapes.front());
@ -339,30 +340,10 @@ template<bool is_zx81> class ConcreteMachine:
} }
void type_string(const std::string &string) override final { void type_string(const std::string &string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper(is_zx81_)); std::unique_ptr<CharacterMapper> mapper(new CharacterMapper(is_zx81));
Utility::TypeRecipient::add_typer(string, std::move(mapper)); Utility::TypeRecipient::add_typer(string, std::move(mapper));
} }
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
const auto roms = roms_with_names(
"ZX8081",
{
"zx80.rom", "zx81.rom",
});
for(std::size_t index = 0; index < roms.size(); ++index) {
if(!roms[index]) return false;
}
zx80_rom_ = std::move(*roms[0]);
zx81_rom_ = std::move(*roms[1]);
zx80_rom_.resize(4096);
zx81_rom_.resize(8192);
return true;
}
// MARK: - Keyboard // MARK: - Keyboard
void set_key_state(uint16_t key, bool is_pressed) override final { void set_key_state(uint16_t key, bool is_pressed) override final {
if(is_pressed) if(is_pressed)
@ -436,7 +417,6 @@ template<bool is_zx81> class ConcreteMachine:
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_; CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
std::unique_ptr<Video> video_; std::unique_ptr<Video> video_;
std::vector<uint8_t> zx81_rom_, zx80_rom_;
uint16_t tape_trap_address_, tape_return_address_; uint16_t tape_trap_address_, tape_return_address_;
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_; uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
@ -456,7 +436,6 @@ template<bool is_zx81> class ConcreteMachine:
HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_; HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_;
Storage::Tape::ZX8081::Parser parser_; Storage::Tape::ZX8081::Parser parser_;
bool is_zx81_;
bool nmi_is_enabled_ = false; bool nmi_is_enabled_ = false;
HalfCycles vsync_start_, vsync_end_; HalfCycles vsync_start_, vsync_end_;
@ -522,14 +501,12 @@ template<bool is_zx81> class ConcreteMachine:
using namespace ZX8081; using namespace ZX8081;
// See header; constructs and returns an instance of the ZX80 or 81. // See header; constructs and returns an instance of the ZX80 or 81.
Machine *Machine::ZX8081(const Analyser::Static::Target *target_hint) { Machine *Machine::ZX8081(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
const Analyser::Static::ZX8081::Target *const hint = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target_hint); const Analyser::Static::ZX8081::Target *const zx_target = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target);
// Instantiate the correct type of machine. // Instantiate the correct type of machine.
if(hint->is_ZX81) if(zx_target->is_ZX81) return new ZX8081::ConcreteMachine<true>(*zx_target, rom_fetcher);
return new ZX8081::ConcreteMachine<true>(); else return new ZX8081::ConcreteMachine<false>(*zx_target, rom_fetcher);
else
return new ZX8081::ConcreteMachine<false>();
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@ -11,6 +11,7 @@
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp" #include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace ZX8081 { namespace ZX8081 {
@ -21,7 +22,7 @@ class Machine {
public: public:
virtual ~Machine(); virtual ~Machine();
static Machine *ZX8081(const Analyser::Static::Target *target_hint); static Machine *ZX8081(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
virtual void set_tape_is_playing(bool is_playing) = 0; virtual void set_tape_is_playing(bool is_playing) = 0;
virtual bool get_tape_is_playing() = 0; virtual bool get_tape_is_playing() = 0;

View File

@ -95,14 +95,14 @@ template <typename T, T reset_value, T xor_output, bool reflect_input, bool refl
those used by the FM and MFM disk encodings. those used by the FM and MFM disk encodings.
*/ */
struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> { struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> {
CCITT() : Generator(0x1021) {} CCITT(): Generator(0x1021) {}
}; };
/*! /*!
Provides a generator of "standard 32-bit" CRCs. Provides a generator of "standard 32-bit" CRCs.
*/ */
struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> { struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> {
CRC32() : Generator(0x04c11db7) {} CRC32(): Generator(0x04c11db7) {}
}; };
} }