1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +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 inserted = false;
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);
// 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;
private:

View File

@ -69,13 +69,13 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
target->media.cartridges = AcornCartridgesFrom(media.cartridges);
// 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::vector<File> files = GetFiles(tape);
tape->reset();
// continue if there are any files
if(files.size()) {
if(!files.empty()) {
bool is_basic = true;
// 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::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue;
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) {
std::vector<File> tape_files = GetFiles(tape);
tape->reset();
if(tape_files.size()) {
if(!tape_files.empty()) {
for(const auto &file : tape_files) {
if(file.data_type == File::MachineCode) {
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
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.
*/
class ConcreteMachine:
template <bool has_fdc> class ConcreteMachine:
public CRTMachine::Machine,
public ConfigurationTarget::Machine,
public KeyboardMachine::Machine,
@ -773,7 +766,7 @@ class ConcreteMachine:
public Machine,
public Activity::Source {
public:
ConcreteMachine() :
ConcreteMachine(const Analyser::Static::AmstradCPC::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this),
crtc_bus_handler_(ram_, interrupt_timer_),
crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_),
@ -792,7 +785,59 @@ class ConcreteMachine:
fdc_.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_);
// 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.
@ -847,8 +892,8 @@ class ConcreteMachine:
}
// Check for an upper ROM selection
if(has_fdc_ && !(address&0x2000)) {
upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : rom_model_ + 1;
if(has_fdc && !(address&0x2000)) {
upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : ROMType::BASIC;
if(upper_rom_is_paged_) read_pointers_[3] = roms_[upper_rom_].data();
}
@ -867,13 +912,13 @@ class ConcreteMachine:
}
// Check for an FDC access
if(has_fdc_ && (address & 0x580) == 0x100) {
if(has_fdc && (address & 0x580) == 0x100) {
flush_fdc();
fdc_.set_register(address & 1, *cycle.value);
}
// Check for a disk motor access
if(has_fdc_ && !(address & 0x580)) {
if(has_fdc && !(address & 0x580)) {
flush_fdc();
fdc_.set_motor_on(!!(*cycle.value));
}
@ -888,7 +933,7 @@ class ConcreteMachine:
}
// Check for an FDC access
if(has_fdc_ && (address & 0x580) == 0x100) {
if(has_fdc && (address & 0x580) == 0x100) {
flush_fdc();
*cycle.value &= fdc_.get_register(address & 1);
}
@ -961,50 +1006,6 @@ class ConcreteMachine:
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 {
// If there are any tapes supplied, use the first of them.
if(!media.tapes.empty()) {
@ -1019,28 +1020,7 @@ class ConcreteMachine:
if(c == 4) break;
}
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;
return !media.tapes.empty() || (!media.disks.empty() && has_fdc);
}
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final {
@ -1078,7 +1058,7 @@ class ConcreteMachine:
// MARK: - Activity Source
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.
@ -1117,7 +1097,7 @@ class ConcreteMachine:
case 1: crtc_bus_handler_.set_colour(value & 0x1f); break;
case 2:
// 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);
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_;
void flush_fdc() {
// 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()));
}
time_since_fdc_update_ = HalfCycles(0);
@ -1184,13 +1164,16 @@ class ConcreteMachine:
uint8_t ram_[128 * 1024];
std::vector<uint8_t> roms_[7];
int rom_model_;
bool has_fdc_, fdc_is_sleeping_;
bool fdc_is_sleeping_;
bool tape_player_is_sleeping_;
bool has_128k_;
enum ROMType: int {
AMSDOS = 0, OS = 1, BASIC = 2
};
std::vector<uint8_t> roms_[3];
bool upper_rom_is_paged_;
int upper_rom_;
ROMType upper_rom_;
uint8_t *ram_pages_[4];
uint8_t *read_pointers_[4];
@ -1205,8 +1188,13 @@ class ConcreteMachine:
using namespace AmstradCPC;
// See header; constructs and returns an instance of the Amstrad CPC.
Machine *Machine::AmstradCPC() {
return new AmstradCPC::ConcreteMachine;
Machine *Machine::AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
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() {}

View File

@ -10,8 +10,9 @@
#define AmstradCPC_hpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <cstdint>
#include <memory>
#include <vector>
@ -28,7 +29,7 @@ class Machine {
virtual ~Machine();
/// 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];
std::vector<uint8_t> apple2_rom_, apple2plus_rom_, rom_;
std::vector<uint8_t> rom_;
std::vector<uint8_t> character_rom_;
uint8_t keyboard_input_ = 0x00;
@ -98,8 +98,6 @@ class ConcreteMachine:
Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
Cycles cycles_since_audio_update_;
ROMMachine::ROMFetcher rom_fetcher_;
// MARK: - Cards
std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
Cycles cycles_since_card_update_;
@ -226,7 +224,7 @@ class ConcreteMachine:
}
public:
ConcreteMachine():
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
m6502_(*this),
video_bus_handler_(ram_),
audio_toggle_(audio_queue_),
@ -254,6 +252,41 @@ class ConcreteMachine:
// Add a couple of joysticks.
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() {
@ -596,27 +629,6 @@ class ConcreteMachine:
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 {
m6502_.run_for(cycles);
}
@ -651,28 +663,6 @@ class ConcreteMachine:
}
// 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 {
if(!media.disks.empty()) {
auto diskii = diskii_card();
@ -722,8 +712,10 @@ class ConcreteMachine:
using namespace AppleII;
Machine *Machine::AppleII() {
return new ConcreteMachine;
Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
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() {}

View File

@ -10,6 +10,8 @@
#define AppleII_hpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <memory>
#include <vector>
@ -24,7 +26,7 @@ class Machine {
virtual ~Machine();
/// 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;
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) {
auto roms = rom_fetcher(
const auto roms = rom_fetcher(
"DiskII",
{
is_16_sector ? "boot-16.rom" : "boot-13.rom",

View File

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

View File

@ -9,6 +9,10 @@
#ifndef Atari2600_cpp
#define Atari2600_cpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include "Atari2600Inputs.h"
namespace Atari2600 {
@ -21,7 +25,7 @@ class Machine {
virtual ~Machine();
/// 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.
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
should that clock rate change.
*/
class Machine: public ROMMachine::Machine {
class Machine {
public:
/*!
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:
ConcreteMachine() :
ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this),
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
ay_(audio_queue_),
@ -122,6 +122,19 @@ class ConcreteMachine:
set_clock_rate(3579545);
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() {
@ -153,11 +166,6 @@ class ConcreteMachine:
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 {
if(!media.cartridges.empty()) {
const auto &segment = media.cartridges.front()->get_segments().front();
@ -181,20 +189,6 @@ class ConcreteMachine:
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
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
@ -408,8 +402,8 @@ class ConcreteMachine:
using namespace Coleco::Vision;
Machine *Machine::ColecoVision() {
return new ConcreteMachine;
Machine *Machine::ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
return new ConcreteMachine(*target, rom_fetcher);
}
Machine::~Machine() {}

View File

@ -9,13 +9,16 @@
#ifndef ColecoVision_hpp
#define ColecoVision_hpp
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace Coleco {
namespace Vision {
class Machine {
public:
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
#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 "../../ROMMachine.hpp"
#include "../../../Storage/Disk/Disk.hpp"
@ -20,18 +39,9 @@ namespace C1540 {
/*!
Provides an emulation of the C1540.
*/
class Machine: public MachineBase, public ROMMachine::Machine {
class Machine: public MachineBase {
public:
enum Personality {
C1540,
C1541
};
Machine(Personality p);
/*!
Sets the source for this drive's ROM image.
*/
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names);
Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher);
/*!
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.
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
private:
Personality personality_;
};
}

View File

@ -16,7 +16,7 @@
using namespace Commodore::C1540;
MachineBase::MachineBase() :
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
Storage::Disk::Controller(1000000),
m6502_(*this),
drive_(new Storage::Disk::Drive(1000000, 300, 2)),
@ -38,9 +38,22 @@ MachineBase::MachineBase() :
// attach the only drive there is
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) {
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
@ -82,19 +95,6 @@ Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation,
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) {
drive_->set_disk(disk);
}

View File

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

View File

@ -62,18 +62,6 @@ enum JoystickInput {
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;
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 Activity::Source {
public:
ConcreteMachine() :
ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this),
user_port_via_port_handler_(new UserPortVIA),
keyboard_via_port_handler_(new KeyboardVIA),
@ -332,127 +320,68 @@ class ConcreteMachine:
// install a joystick
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
}
// Obtains the system ROMs.
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
rom_fetcher_ = roms_with_names;
auto roms = roms_with_names(
"Vic20",
{
"characters-danish.bin",
"characters-english.bin",
"characters-japanese.bin",
"characters-swedish.bin",
"kernel-danish.bin",
"kernel-japanese.bin",
"kernel-ntsc.bin",
"kernel-pal.bin",
"kernel-swedish.bin",
"basic.bin"
});
for(std::size_t index = 0; index < roms.size(); ++index) {
auto &data = roms[index];
if(!data) return false;
if(index < 9) roms_[index] = std::move(*data); else basic_rom_ = std::move(*data);
std::vector<std::string> rom_names = { "basic.bin" };
switch(target.region) {
default:
rom_names.push_back("characters-english.bin");
rom_names.push_back("kernel-pal.bin");
break;
case Analyser::Static::Commodore::Target::Region::American:
rom_names.push_back("characters-english.bin");
rom_names.push_back("kernel-ntsc.bin");
break;
case Analyser::Static::Commodore::Target::Region::Danish:
rom_names.push_back("characters-danish.bin");
rom_names.push_back("kernel-danish.bin");
break;
case Analyser::Static::Commodore::Target::Region::Japanese:
rom_names.push_back("characters-japanese.bin");
rom_names.push_back("kernel-japanese.bin");
break;
case Analyser::Static::Commodore::Target::Region::Swedish:
rom_names.push_back("characters-swedish.bin");
rom_names.push_back("kernel-japanese.bin");
break;
}
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.
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.
for(std::size_t index = 4; index < roms.size(); ++index) roms_[index].resize(8192);
kernel_rom_.resize(8192);
return true;
}
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) {
if(target.has_c1540) {
// 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
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
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
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
set_ntsc_6560();
set_clock_rate(1022727);
output_mode_ = MOS::MOS6560::OutputMode::NTSC;
} else {
// PAL
set_pal_6560();
set_clock_rate(1108404);
output_mode_ = MOS::MOS6560::OutputMode::PAL;
}
// 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);
// Add 6502-visible RAM as requested
switch(memory_model) {
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);
@ -512,39 +441,53 @@ class ConcreteMachine:
write_to_map(processor_read_memory_map_, basic_rom_.data(), 0xc000, static_cast<uint16_t>(basic_rom_.size()));
// install the system ROM
ROM character_rom;
ROM kernel_rom;
switch(region) {
default:
character_rom = CharactersEnglish;
kernel_rom = KernelPAL;
break;
case Analyser::Static::Commodore::Target::Region::American:
character_rom = CharactersEnglish;
kernel_rom = KernelNTSC;
break;
case Analyser::Static::Commodore::Target::Region::Danish:
character_rom = CharactersDanish;
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_, character_rom_.data(), 0x8000, static_cast<uint16_t>(character_rom_.size()));
write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast<uint16_t>(character_rom_.size()));
write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast<uint16_t>(kernel_rom_.size()));
insert_media(target.media);
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
}
bool insert_media(const Analyser::Static::Media &media) override final {
if(!media.tapes.empty()) {
tape_->set_tape(media.tapes.front());
}
write_to_map(processor_read_memory_map_, roms_[character_rom].data(), 0x8000, static_cast<uint16_t>(roms_[character_rom].size()));
write_to_map(mos6560_bus_handler_.video_memory_map, roms_[character_rom].data(), 0x0000, static_cast<uint16_t>(roms_[character_rom].size()));
write_to_map(processor_read_memory_map_, roms_[kernel_rom].data(), 0xe000, static_cast<uint16_t>(roms_[kernel_rom].size()));
if(!media.disks.empty() && c1540_) {
c1540_->set_disk(media.disks.front());
}
// install the inserted ROM if there is one
if(!rom_.empty()) {
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);
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
@ -680,8 +623,8 @@ class ConcreteMachine:
void setup_output(float aspect_ratio) override final {
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.
// Make a guess: PAL. Without setting a clock rate the 6560 isn't fully set up so contractually something must be set.
set_memory_map(commodore_target_.memory_model, commodore_target_.region);
mos6560_->set_output_mode(output_mode_);
mos6560_->set_clock_rate(get_clock_rate());
}
void close_output() override final {
@ -760,12 +703,8 @@ class ConcreteMachine:
void update_video() {
mos6560_->run_for(cycles_since_mos6560_update_.flush());
}
Analyser::Static::Commodore::Target commodore_target_;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
std::vector<uint8_t> roms_[9];
std::vector<uint8_t> character_rom_;
std::vector<uint8_t> basic_rom_;
std::vector<uint8_t> kernel_rom_;
@ -775,8 +714,6 @@ class ConcreteMachine:
uint8_t ram_[0x8000];
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_write_memory_map_[64];
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_;
Vic6560BusHandler mos6560_bus_handler_;
MOS::MOS6560::OutputMode output_mode_;
std::unique_ptr<MOS::MOS6560::MOS6560<Vic6560BusHandler>> mos6560_;
std::shared_ptr<UserPortVIA> user_port_via_port_handler_;
std::shared_ptr<KeyboardVIA> keyboard_via_port_handler_;
@ -822,8 +760,10 @@ class ConcreteMachine:
using namespace Commodore::Vic20;
Machine *Machine::Vic20() {
return new Vic20::ConcreteMachine;
Machine *Machine::Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
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() {}

View File

@ -10,6 +10,11 @@
#define Vic20_hpp
#include "../../../Configurable/Configurable.hpp"
#include "../../../Analyser/Static/StaticAnalyser.hpp"
#include "../../ROMMachine.hpp"
#include <memory>
#include <vector>
namespace Commodore {
namespace Vic20 {
@ -22,7 +27,7 @@ class Machine {
virtual ~Machine();
/// 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 {
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

View File

@ -49,10 +49,10 @@ class ConcreteMachine:
public Utility::TypeRecipient,
public Activity::Source {
public:
ConcreteMachine() :
m6502_(*this),
sound_generator_(audio_queue_),
speaker_(sound_generator_) {
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this),
sound_generator_(audio_queue_),
speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_));
for(int c = 0; c < 16; c++)
memset(roms_[c], 0xff, 16384);
@ -61,60 +61,53 @@ class ConcreteMachine:
set_clock_rate(2000000);
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() {
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 {
if(key == KeyBreak) {
m6502_.set_reset_line(isPressed);
@ -131,32 +124,6 @@ class ConcreteMachine:
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 {
if(!media.tapes.empty()) {
tape_.set_tape(media.tapes.front());
@ -167,11 +134,11 @@ class ConcreteMachine:
plus3_->set_disk(media.disks.front(), 0);
}
ROMSlot slot = ROMSlot12;
ROM slot = ROM::Slot12;
for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : media.cartridges) {
const ROMSlot first_slot_tried = slot;
while(rom_inserted_[slot]) {
slot = static_cast<ROMSlot>((static_cast<int>(slot) + 1)&15);
const ROM first_slot_tried = slot;
while(rom_inserted_[static_cast<int>(slot)]) {
slot = static_cast<ROM>((static_cast<int>(slot) + 1) & 15);
if(slot == first_slot_tried) return 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
active_rom_ = (Electron::ROMSlot)(*value & 0xf);
active_rom_ = *value & 0xf;
// apply the ULA's test
if(*value & 0x08) {
@ -363,7 +330,7 @@ class ConcreteMachine:
}
}
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_]) {
roms_[active_rom_][address & 16383] = *value;
@ -494,6 +461,48 @@ class ConcreteMachine:
}
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.
inline void update_display() {
if(cycles_since_display_update_ > 0) {
@ -540,7 +549,7 @@ class ConcreteMachine:
std::vector<uint8_t> dfs_, adfs1_, adfs2_;
// Paging
ROMSlot active_rom_ = ROMSlot::ROMSlot0;
int active_rom_ = static_cast<int>(ROM::Slot0);
bool keyboard_is_active_ = false;
bool basic_is_active_ = false;
@ -590,8 +599,10 @@ class ConcreteMachine:
using namespace Electron;
Machine *Machine::Electron() {
return new Electron::ConcreteMachine;
Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
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() {}

View File

@ -10,6 +10,8 @@
#define Electron_hpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <cstdint>
#include <memory>
@ -17,20 +19,6 @@
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.
std::vector<std::unique_ptr<Configurable::Option>> get_options();
@ -45,13 +33,7 @@ class Machine {
virtual ~Machine();
/// Creates and returns an Electron.
static Machine *Electron();
/*!
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;
static Machine *Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
};
}

View File

@ -91,7 +91,7 @@ class ConcreteMachine:
public ClockingHint::Observer,
public Activity::Source {
public:
ConcreteMachine():
ConcreteMachine(const Analyser::Static::MSX::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
z80_(*this),
i8255_(i8255_port_handler_),
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.
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() {
@ -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 {
if(!media.cartridges.empty()) {
const auto &segment = media.cartridges.front()->get_segments().front();
@ -467,39 +493,6 @@ class ConcreteMachine:
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) {
selected_key_line_ = line;
}
@ -683,8 +676,10 @@ class ConcreteMachine:
using namespace MSX;
Machine *Machine::MSX() {
return new ConcreteMachine;
Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
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() {}

View File

@ -10,17 +10,22 @@
#define MSX_hpp
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
#include <memory>
#include <vector>
namespace MSX {
std::vector<std::unique_ptr<Configurable::Option>> get_options();
class Machine {
public:
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 */

View File

@ -206,9 +206,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
public Machine {
public:
ConcreteMachine(const Analyser::Static::Oric::Target *target) :
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this),
rom_type_(target ? target->rom : Analyser::Static::Oric::Target::ROM::BASIC10),
ay8910_(audio_queue_),
speaker_(ay8910_),
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) {
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"};
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::BASIC11: rom_names.push_back("basic11.rom"); break;
case Analyser::Static::Oric::Target::ROM::Pravetz: rom_names.push_back("pravetz.rom"); break;
@ -239,15 +231,17 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
switch(disk_interface) {
default: 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) {
if(!roms[index]) return false;
if(!roms[index]) {
throw ROMMachine::Error::MissingROMs;
}
}
colour_rom_ = std::move(*roms[0]);
rom_ = std::move(*roms[1]);
@ -261,8 +255,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
pravetz_rom_ = std::move(*roms[2]);
pravetz_rom_.resize(512);
auto state_machine_rom = roms_with_names("DiskII", {"state-machine-16.rom"});
if(!state_machine_rom[0]) return false;
auto state_machine_rom = rom_fetcher("DiskII", {"state-machine-16.rom"});
if(!state_machine_rom[0]) {
throw ROMMachine::Error::MissingROMs;
}
diskii_.set_state_machine(*state_machine_rom[0]);
} break;
}
@ -271,9 +267,35 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
rom_.resize(16384);
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 {
@ -292,41 +314,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
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 inserted = false;
if(media.tapes.size()) {
if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front());
inserted = true;
}
@ -587,7 +578,6 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
// RAM and ROM
Analyser::Static::Oric::Target::ROM rom_type_;
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;
uint8_t ram_[65536];
Cycles cycles_since_video_update_;
@ -650,13 +640,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
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);
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
switch(oric_target->disk_interface) {
default: return new ConcreteMachine<DiskInterface::None>(oric_target);
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(oric_target);
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(oric_target);
default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
}
}

View File

@ -11,6 +11,7 @@
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace Oric {
@ -25,7 +26,7 @@ class Machine {
virtual ~Machine();
/// 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 {
/*!
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;
struct Machine {
/*!
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; }
enum class Error {
MissingROMs
};
}

View File

@ -25,36 +25,36 @@ namespace {
::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) {
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:
error = Machine::Error::UnknownMachine;
return nullptr;
}
Machine::DynamicMachine *machine = nullptr;
try {
#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.
CRTMachine::Machine *crt_machine = machine->crt_machine();
if(crt_machine) {
if(!machine->crt_machine()->set_rom_fetcher(rom_fetcher)) {
delete machine;
error = Machine::Error::MissingROM;
default:
error = Machine::Error::UnknownMachine;
return nullptr;
}
}
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
if(configuration_target) {
machine->configuration_target()->configure_as_target(target);
#undef Bind
} catch(ROMMachine::Error construction_error) {
switch(construction_error) {
case ROMMachine::Error::MissingROMs:
error = Machine::Error::MissingROM;
break;
default:
error = Machine::Error::UnknownError;
break;
}
}
return machine;

View File

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

View File

@ -66,7 +66,7 @@ template<bool is_zx81> class ConcreteMachine:
public CPU::Z80::BusHandler,
public Machine {
public:
ConcreteMachine() :
ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
z80_(*this),
tape_player_(ZX8081ClockRate),
ay_(audio_queue_),
@ -74,6 +74,55 @@ template<bool is_zx81> class ConcreteMachine:
set_clock_rate(ZX8081ClockRate);
speaker_.set_input_rate(static_cast<float>(ZX8081ClockRate) / 2.0f);
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() {
@ -105,7 +154,7 @@ template<bool is_zx81> class ConcreteMachine:
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_) {
tape_player_.run_for(cycle.length);
} else {
@ -129,7 +178,7 @@ template<bool is_zx81> class ConcreteMachine:
set_vsync(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.
if(is_zx81) {
@ -281,54 +330,6 @@ template<bool is_zx81> class ConcreteMachine:
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 {
if(!media.tapes.empty()) {
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 {
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));
}
// 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
void set_key_state(uint16_t key, bool is_pressed) override final {
if(is_pressed)
@ -436,7 +417,6 @@ template<bool is_zx81> class ConcreteMachine:
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
std::unique_ptr<Video> video_;
std::vector<uint8_t> zx81_rom_, zx80_rom_;
uint16_t tape_trap_address_, tape_return_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_;
Storage::Tape::ZX8081::Parser parser_;
bool is_zx81_;
bool nmi_is_enabled_ = false;
HalfCycles vsync_start_, vsync_end_;
@ -522,14 +501,12 @@ template<bool is_zx81> class ConcreteMachine:
using namespace ZX8081;
// See header; constructs and returns an instance of the ZX80 or 81.
Machine *Machine::ZX8081(const Analyser::Static::Target *target_hint) {
const Analyser::Static::ZX8081::Target *const hint = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target_hint);
Machine *Machine::ZX8081(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
const Analyser::Static::ZX8081::Target *const zx_target = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target);
// Instantiate the correct type of machine.
if(hint->is_ZX81)
return new ZX8081::ConcreteMachine<true>();
else
return new ZX8081::ConcreteMachine<false>();
if(zx_target->is_ZX81) return new ZX8081::ConcreteMachine<true>(*zx_target, rom_fetcher);
else return new ZX8081::ConcreteMachine<false>(*zx_target, rom_fetcher);
}
Machine::~Machine() {}

View File

@ -11,6 +11,7 @@
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
namespace ZX8081 {
@ -21,7 +22,7 @@ class Machine {
public:
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 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.
*/
struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> {
CCITT() : Generator(0x1021) {}
CCITT(): Generator(0x1021) {}
};
/*!
Provides a generator of "standard 32-bit" CRCs.
*/
struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> {
CRC32() : Generator(0x04c11db7) {}
CRC32(): Generator(0x04c11db7) {}
};
}