mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Merge pull request #493 from TomHarte/SpecificROMs
Clarifies startup procedure for machines
This commit is contained in:
commit
774d8668bf
@ -1,29 +0,0 @@
|
||||
//
|
||||
// MultiConfigurationTarget.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiConfigurationTarget.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
|
||||
if(configuration_target) targets_.push_back(configuration_target);
|
||||
}
|
||||
}
|
||||
|
||||
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_) {
|
||||
inserted |= target->insert_media(media);
|
||||
}
|
||||
return inserted;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
//
|
||||
// MultiConfigurationTarget.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiConfigurationTarget_hpp
|
||||
#define MultiConfigurationTarget_hpp
|
||||
|
||||
#include "../../../../Machines/ConfigurationTarget.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the configuration target interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
struct MultiConfigurationTarget: public ConfigurationTarget::Machine {
|
||||
public:
|
||||
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:
|
||||
std::vector<ConfigurationTarget::Machine *> targets_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiConfigurationTarget_hpp */
|
@ -0,0 +1,26 @@
|
||||
//
|
||||
// MultiMediaTarget.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiMediaTarget.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiMediaTarget::MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
MediaTarget::Machine *media_target = machine->media_target();
|
||||
if(media_target) targets_.push_back(media_target);
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiMediaTarget::insert_media(const Analyser::Static::Media &media) {
|
||||
bool inserted = false;
|
||||
for(const auto &target : targets_) {
|
||||
inserted |= target->insert_media(media);
|
||||
}
|
||||
return inserted;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
//
|
||||
// MultiMediaTarget.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiMediaTarget_hpp
|
||||
#define MultiMediaTarget_hpp
|
||||
|
||||
#include "../../../../Machines/MediaTarget.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the media target interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
struct MultiMediaTarget: public MediaTarget::Machine {
|
||||
public:
|
||||
MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard MediaTarget::Machine interface; see there for documentation.
|
||||
bool insert_media(const Analyser::Static::Media &media) override;
|
||||
|
||||
private:
|
||||
std::vector<MediaTarget::Machine *> targets_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiMediaTarget_hpp */
|
@ -15,10 +15,10 @@ using namespace Analyser::Dynamic;
|
||||
MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) :
|
||||
machines_(std::move(machines)),
|
||||
configurable_(machines_),
|
||||
configuration_target_(machines_),
|
||||
crt_machine_(machines_, machines_mutex_),
|
||||
joystick_machine_(machines),
|
||||
keyboard_machine_(machines_) {
|
||||
keyboard_machine_(machines_),
|
||||
media_target_(machines_) {
|
||||
crt_machine_.set_delegate(this);
|
||||
}
|
||||
|
||||
@ -26,11 +26,11 @@ Activity::Source *MultiMachine::activity_source() {
|
||||
return nullptr; // TODO
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *MultiMachine::configuration_target() {
|
||||
MediaTarget::Machine *MultiMachine::media_target() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->configuration_target();
|
||||
return machines_.front()->media_target();
|
||||
} else {
|
||||
return &configuration_target_;
|
||||
return &media_target_;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include "../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include "Implementation/MultiConfigurable.hpp"
|
||||
#include "Implementation/MultiConfigurationTarget.hpp"
|
||||
#include "Implementation/MultiCRTMachine.hpp"
|
||||
#include "Implementation/MultiJoystickMachine.hpp"
|
||||
#include "Implementation/MultiKeyboardMachine.hpp"
|
||||
#include "Implementation/MultiMediaTarget.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -51,11 +51,11 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::De
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
|
||||
|
||||
Activity::Source *activity_source() override;
|
||||
ConfigurationTarget::Machine *configuration_target() override;
|
||||
Configurable::Device *configurable_device() override;
|
||||
CRTMachine::Machine *crt_machine() override;
|
||||
JoystickMachine::Machine *joystick_machine() override;
|
||||
KeyboardMachine::Machine *keyboard_machine() override;
|
||||
Configurable::Device *configurable_device() override;
|
||||
MediaTarget::Machine *media_target() override;
|
||||
void *raw_pointer() override;
|
||||
|
||||
private:
|
||||
@ -65,10 +65,10 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::De
|
||||
std::mutex machines_mutex_;
|
||||
|
||||
MultiConfigurable configurable_;
|
||||
MultiConfigurationTarget configuration_target_;
|
||||
MultiCRTMachine crt_machine_;
|
||||
MultiJoystickMachine joystick_machine_;
|
||||
MultiKeyboardMachine keyboard_machine_;
|
||||
MultiMediaTarget media_target_;
|
||||
|
||||
void pick_first();
|
||||
bool has_picked_ = false;
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "../Utility/Typer.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -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,9 +754,9 @@ 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 MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
@ -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() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "AppleII.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -45,7 +45,7 @@ namespace {
|
||||
|
||||
class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
@ -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);
|
||||
}
|
||||
@ -650,29 +662,7 @@ class ConcreteMachine:
|
||||
string_serialiser_.reset(new Utility::StringSerialiser(string, true));
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// MARK: MediaTarget
|
||||
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() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
|
||||
@ -76,24 +75,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 +96,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 +122,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 +245,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() {}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "../../Components/AY38910/AY38910.hpp" // For the Super Game Module.
|
||||
#include "../../Components/SN76489/SN76489.hpp"
|
||||
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
|
||||
@ -108,11 +107,10 @@ class ConcreteMachine:
|
||||
public Machine,
|
||||
public CPU::Z80::BusHandler,
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
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 +120,36 @@ 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);
|
||||
|
||||
if(!target.media.cartridges.empty()) {
|
||||
const auto &segment = target.media.cartridges.front()->get_segments().front();
|
||||
cartridge_ = segment.data;
|
||||
if(cartridge_.size() >= 32768)
|
||||
cartridge_address_limit_ = 0xffff;
|
||||
else
|
||||
cartridge_address_limit_ = static_cast<uint16_t>(0x8000 + cartridge_.size() - 1);
|
||||
|
||||
if(cartridge_.size() > 32768) {
|
||||
cartridge_pages_[0] = &cartridge_[cartridge_.size() - 16384];
|
||||
cartridge_pages_[1] = cartridge_.data();
|
||||
is_megacart_ = true;
|
||||
} else {
|
||||
cartridge_pages_[0] = cartridge_.data();
|
||||
cartridge_pages_[1] = cartridge_.data() + 16384;
|
||||
is_megacart_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -153,48 +181,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();
|
||||
cartridge_ = segment.data;
|
||||
if(cartridge_.size() >= 32768)
|
||||
cartridge_address_limit_ = 0xffff;
|
||||
else
|
||||
cartridge_address_limit_ = static_cast<uint16_t>(0x8000 + cartridge_.size() - 1);
|
||||
|
||||
if(cartridge_.size() > 32768) {
|
||||
cartridge_pages_[0] = &cartridge_[cartridge_.size() - 16384];
|
||||
cartridge_pages_[1] = cartridge_.data();
|
||||
is_megacart_ = true;
|
||||
} else {
|
||||
cartridge_pages_[0] = cartridge_.data();
|
||||
cartridge_pages_[1] = cartridge_.data() + 16384;
|
||||
is_megacart_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
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 +394,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() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "Keyboard.hpp"
|
||||
|
||||
#include "../../../Activity/Source.hpp"
|
||||
#include "../../ConfigurationTarget.hpp"
|
||||
#include "../../MediaTarget.hpp"
|
||||
#include "../../CRTMachine.hpp"
|
||||
#include "../../KeyboardMachine.hpp"
|
||||
#include "../../JoystickMachine.hpp"
|
||||
@ -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
|
||||
@ -292,7 +280,7 @@ class Joystick: public Inputs::ConcreteJoystick {
|
||||
|
||||
class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public JoystickMachine::Machine,
|
||||
public Configurable::Device,
|
||||
@ -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() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
//
|
||||
// ConfigurationTarget.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 08/09/2016.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfigurationTarget_hpp
|
||||
#define ConfigurationTarget_hpp
|
||||
|
||||
#include "../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ConfigurationTarget {
|
||||
|
||||
/*!
|
||||
A ConfigurationTarget::Machine is anything that can accept a Analyser::Static::Target
|
||||
and configure itself appropriately, or accept a list of media subsequently to insert.
|
||||
*/
|
||||
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
|
||||
|
||||
@returns @c true if any media was inserted; @c false otherwise.
|
||||
*/
|
||||
virtual bool insert_media(const Analyser::Static::Media &media) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ConfigurationTarget_h */
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
#include "../Activity/Source.hpp"
|
||||
#include "ConfigurationTarget.hpp"
|
||||
#include "MediaTarget.hpp"
|
||||
#include "CRTMachine.hpp"
|
||||
#include "JoystickMachine.hpp"
|
||||
#include "KeyboardMachine.hpp"
|
||||
@ -27,11 +27,11 @@ struct DynamicMachine {
|
||||
virtual ~DynamicMachine() {}
|
||||
|
||||
virtual Activity::Source *activity_source() = 0;
|
||||
virtual ConfigurationTarget::Machine *configuration_target() = 0;
|
||||
virtual Configurable::Device *configurable_device() = 0;
|
||||
virtual CRTMachine::Machine *crt_machine() = 0;
|
||||
virtual JoystickMachine::Machine *joystick_machine() = 0;
|
||||
virtual KeyboardMachine::Machine *keyboard_machine() = 0;
|
||||
virtual Configurable::Device *configurable_device() = 0;
|
||||
virtual MediaTarget::Machine *media_target() = 0;
|
||||
|
||||
/*!
|
||||
Provides a raw pointer to the underlying machine if and only if this dynamic machine really is
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "Electron.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
|
||||
@ -41,7 +41,7 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
@ -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,50 @@ 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;
|
||||
}
|
||||
|
||||
if(static_cast<int>(slot) < 16)
|
||||
rom_inserted_[static_cast<int>(slot)] = true;
|
||||
}
|
||||
|
||||
// MARK: - Work deferral updates.
|
||||
inline void update_display() {
|
||||
if(cycles_since_display_update_ > 0) {
|
||||
@ -540,7 +551,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 +601,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() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
|
||||
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
|
||||
@ -84,14 +84,14 @@ class ConcreteMachine:
|
||||
public Machine,
|
||||
public CPU::Z80::BusHandler,
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public MemoryMap,
|
||||
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() {}
|
||||
|
@ -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 */
|
||||
|
34
Machines/MediaTarget.hpp
Normal file
34
Machines/MediaTarget.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// MediaTarget.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 08/09/2016.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MediaTarget_hpp
|
||||
#define MediaTarget_hpp
|
||||
|
||||
#include "../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MediaTarget {
|
||||
|
||||
/*!
|
||||
A MediaTarget::Machine is anything that can accept new media while running.
|
||||
*/
|
||||
class Machine {
|
||||
public:
|
||||
/*!
|
||||
Requests that the machine insert @c media as a modification to current state
|
||||
|
||||
@returns @c true if any media was inserted; @c false otherwise.
|
||||
*/
|
||||
virtual bool insert_media(const Analyser::Static::Media &media) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* MediaTarget_hpp */
|
@ -13,7 +13,7 @@
|
||||
#include "Video.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
|
||||
@ -193,7 +193,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
@ -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(µdisc_);
|
||||
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(µdisc_);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -21,6 +21,7 @@ namespace Machine {
|
||||
|
||||
enum class Error {
|
||||
None,
|
||||
UnknownError,
|
||||
UnknownMachine,
|
||||
MissingROM,
|
||||
NoTargets
|
||||
|
@ -29,8 +29,8 @@ template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine
|
||||
return get<Activity::Source>();
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *configuration_target() override {
|
||||
return get<ConfigurationTarget::Machine>();
|
||||
MediaTarget::Machine *media_target() override {
|
||||
return get<MediaTarget::Machine>();
|
||||
}
|
||||
|
||||
CRTMachine::Machine *crt_machine() override {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "ZX8081.hpp"
|
||||
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
|
||||
@ -59,14 +59,14 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
|
||||
template<bool is_zx81> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public Utility::TypeRecipient,
|
||||
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() {}
|
||||
|
@ -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;
|
||||
|
@ -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) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -595,8 +595,8 @@
|
||||
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */; };
|
||||
4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; };
|
||||
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */; };
|
||||
4BBB70A4202011C2002FE009 /* MultiConfigurationTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiConfigurationTarget.cpp */; };
|
||||
4BBB70A5202011C2002FE009 /* MultiConfigurationTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiConfigurationTarget.cpp */; };
|
||||
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; };
|
||||
4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; };
|
||||
4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; };
|
||||
4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; };
|
||||
4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; };
|
||||
@ -1027,7 +1027,7 @@
|
||||
4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+StdVector.h"; sourceTree = "<group>"; };
|
||||
4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+StdVector.mm"; sourceTree = "<group>"; };
|
||||
4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = "<group>"; };
|
||||
4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MediaTarget.hpp; sourceTree = "<group>"; };
|
||||
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = "<group>"; };
|
||||
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = "<group>"; };
|
||||
4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = "<group>"; };
|
||||
@ -1324,8 +1324,8 @@
|
||||
4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = "<group>"; };
|
||||
4BBB14301CD2CECE00BDB55C /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = "<group>"; };
|
||||
4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = "<group>"; };
|
||||
4BBB70A2202011C2002FE009 /* MultiConfigurationTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiConfigurationTarget.hpp; sourceTree = "<group>"; };
|
||||
4BBB70A3202011C2002FE009 /* MultiConfigurationTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiConfigurationTarget.cpp; sourceTree = "<group>"; };
|
||||
4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = "<group>"; };
|
||||
4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = "<group>"; };
|
||||
4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiCRTMachine.cpp; sourceTree = "<group>"; };
|
||||
4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiCRTMachine.hpp; sourceTree = "<group>"; };
|
||||
4BBC34241D2208B100FFC9DF /* CSFastLoading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFastLoading.h; sourceTree = "<group>"; };
|
||||
@ -2820,7 +2820,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */,
|
||||
4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */,
|
||||
4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */,
|
||||
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */,
|
||||
4BBB709C2020109C002FE009 /* DynamicMachine.hpp */,
|
||||
4B7041271F92C26900735E45 /* JoystickMachine.hpp */,
|
||||
@ -2858,16 +2858,16 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */,
|
||||
4BBB70A3202011C2002FE009 /* MultiConfigurationTarget.cpp */,
|
||||
4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */,
|
||||
4B1B88C6202E469300B67DFF /* MultiJoystickMachine.cpp */,
|
||||
4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */,
|
||||
4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */,
|
||||
4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */,
|
||||
4B1B88BF202E3DB200B67DFF /* MultiConfigurable.hpp */,
|
||||
4BBB70A2202011C2002FE009 /* MultiConfigurationTarget.hpp */,
|
||||
4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */,
|
||||
4B1B88C7202E469300B67DFF /* MultiJoystickMachine.hpp */,
|
||||
4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */,
|
||||
4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */,
|
||||
4B9BE3FF203A0C0600FFAE60 /* MultiSpeaker.hpp */,
|
||||
);
|
||||
path = Implementation;
|
||||
@ -3691,7 +3691,7 @@
|
||||
4BAD13441FF709C700FD114A /* MSX.cpp in Sources */,
|
||||
4B055AC41FAE9AE80060FFFF /* Keyboard.cpp in Sources */,
|
||||
4B055A941FAE85B50060FFFF /* CommodoreROM.cpp in Sources */,
|
||||
4BBB70A5202011C2002FE009 /* MultiConfigurationTarget.cpp in Sources */,
|
||||
4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */,
|
||||
4B1B88BD202E3D3D00B67DFF /* MultiMachine.cpp in Sources */,
|
||||
4B055A971FAE85BB0060FFFF /* ZX8081.cpp in Sources */,
|
||||
4B055AAD1FAE85FD0060FFFF /* PCMTrack.cpp in Sources */,
|
||||
@ -3856,7 +3856,7 @@
|
||||
4B4518841F75E91A00926311 /* UnformattedTrack.cpp in Sources */,
|
||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
|
||||
4B894528201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4BBB70A4202011C2002FE009 /* MultiConfigurationTarget.cpp in Sources */,
|
||||
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */,
|
||||
4B89453A201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */,
|
||||
4B54C0C21F8D91CD0050900F /* Keyboard.cpp in Sources */,
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include "CSROMFetcher.hpp"
|
||||
|
||||
#include "ConfigurationTarget.hpp"
|
||||
#include "MediaTarget.hpp"
|
||||
#include "JoystickMachine.hpp"
|
||||
#include "KeyboardMachine.hpp"
|
||||
#include "KeyCodes.h"
|
||||
@ -199,8 +199,8 @@ struct ActivityObserver: public Activity::Observer {
|
||||
|
||||
- (void)applyMedia:(const Analyser::Static::Media &)media {
|
||||
@synchronized(self) {
|
||||
ConfigurationTarget::Machine *const configurationTarget = _machine->configuration_target();
|
||||
if(configurationTarget) configurationTarget->insert_media(media);
|
||||
MediaTarget::Machine *const mediaTarget = _machine->media_target();
|
||||
if(mediaTarget) mediaTarget->insert_media(media);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../Machines/Utility/MachineForTarget.hpp"
|
||||
|
||||
#include "../../Machines/ConfigurationTarget.hpp"
|
||||
#include "../../Machines/MediaTarget.hpp"
|
||||
#include "../../Machines/CRTMachine.hpp"
|
||||
|
||||
#include "../../Concurrency/BestEffortUpdater.hpp"
|
||||
@ -487,7 +487,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
case SDL_DROPFILE: {
|
||||
Analyser::Static::Media media = Analyser::Static::GetMedia(event.drop.file);
|
||||
machine->configuration_target()->insert_media(media);
|
||||
machine->media_target()->insert_media(media);
|
||||
} break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
|
@ -38,7 +38,7 @@ std::shared_ptr<Track> Storage::Disk::track_for_sectors(uint8_t *const source, i
|
||||
source_pointer += byte_size;
|
||||
}
|
||||
|
||||
if(sectors.size()) {
|
||||
if(!sectors.empty()) {
|
||||
return is_double_density ? Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors) : Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace {
|
||||
const int PLLClockRate = 1920000;
|
||||
}
|
||||
|
||||
Parser::Parser() {
|
||||
Parser::Parser(): crc_(0x1021) {
|
||||
shifter_.set_delegate(this);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ class Parser: public Storage::Tape::Parser<SymbolType>, public Shifter::Delegate
|
||||
|
||||
private:
|
||||
bool did_update_shifter(int new_value, int length);
|
||||
CRC::CCITT crc_;
|
||||
CRC::Generator<uint16_t, 0x0000, 0x0000, false, false> crc_;
|
||||
Shifter shifter_;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user