mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 03:32:01 +00:00
Simplifies initialisation procedure for all machines.
With the side effect of allowing every machine to try to load only the ROMs that it needs.
This commit is contained in:
parent
3e2d271566
commit
3862fdb44c
@ -17,9 +17,6 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique
|
||||
}
|
||||
}
|
||||
|
||||
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target *target) {
|
||||
}
|
||||
|
||||
bool MultiConfigurationTarget::insert_media(const Analyser::Static::Media &media) {
|
||||
bool inserted = false;
|
||||
for(const auto &target : targets_) {
|
||||
|
@ -29,7 +29,6 @@ struct MultiConfigurationTarget: public ConfigurationTarget::Machine {
|
||||
MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard ConfigurationTarget::Machine interface; see there for documentation.
|
||||
void configure_as_target(const Analyser::Static::Target *target) override;
|
||||
bool insert_media(const Analyser::Static::Media &media) override;
|
||||
|
||||
private:
|
||||
|
@ -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};
|
||||
|
@ -44,13 +44,6 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
);
|
||||
}
|
||||
|
||||
enum ROMType: int {
|
||||
OS464 = 0, BASIC464,
|
||||
OS664, BASIC664,
|
||||
OS6128, BASIC6128,
|
||||
AMSDOS
|
||||
};
|
||||
|
||||
/*!
|
||||
Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output
|
||||
is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period
|
||||
@ -761,7 +754,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
/*!
|
||||
The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80.
|
||||
*/
|
||||
class ConcreteMachine:
|
||||
template <bool has_fdc> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
@ -773,7 +766,7 @@ class ConcreteMachine:
|
||||
public Machine,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
ConcreteMachine(const Analyser::Static::AmstradCPC::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
z80_(*this),
|
||||
crtc_bus_handler_(ram_, interrupt_timer_),
|
||||
crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_),
|
||||
@ -792,7 +785,59 @@ class ConcreteMachine:
|
||||
fdc_.set_clocking_hint_observer(this);
|
||||
tape_player_.set_clocking_hint_observer(this);
|
||||
|
||||
// install the keyboard state class as the AY port handler
|
||||
ay_.ay().set_port_handler(&key_state_);
|
||||
|
||||
// construct the list of necessary ROMs
|
||||
std::vector<std::string> required_roms = {"amsdos.rom"};
|
||||
std::string model_number;
|
||||
switch(target.model) {
|
||||
default:
|
||||
model_number = "6128";
|
||||
has_128k_ = true;
|
||||
break;
|
||||
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
|
||||
model_number = "464";
|
||||
has_128k_ = false;
|
||||
break;
|
||||
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
|
||||
model_number = "664";
|
||||
has_128k_ = false;
|
||||
break;
|
||||
}
|
||||
required_roms.push_back("os" + model_number + ".rom");
|
||||
required_roms.push_back("basic" + model_number + ".rom");
|
||||
|
||||
// fetch and verify the ROMs
|
||||
const auto roms = rom_fetcher("AmstradCPC", required_roms);
|
||||
|
||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
||||
auto &data = roms[index];
|
||||
if(!data) throw ROMMachine::Error::MissingROMs;
|
||||
roms_[static_cast<int>(index)] = std::move(*data);
|
||||
roms_[static_cast<int>(index)].resize(16384);
|
||||
}
|
||||
|
||||
// Establish default memory map
|
||||
upper_rom_is_paged_ = true;
|
||||
upper_rom_ = ROMType::BASIC;
|
||||
|
||||
write_pointers_[0] = &ram_[0];
|
||||
write_pointers_[1] = &ram_[16384];
|
||||
write_pointers_[2] = &ram_[32768];
|
||||
write_pointers_[3] = &ram_[49152];
|
||||
|
||||
read_pointers_[0] = roms_[ROMType::OS].data();
|
||||
read_pointers_[1] = write_pointers_[1];
|
||||
read_pointers_[2] = write_pointers_[2];
|
||||
read_pointers_[3] = roms_[upper_rom_].data();
|
||||
|
||||
// Type whatever is required.
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
/// The entry point for performing a partial Z80 machine cycle.
|
||||
@ -847,8 +892,8 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Check for an upper ROM selection
|
||||
if(has_fdc_ && !(address&0x2000)) {
|
||||
upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : rom_model_ + 1;
|
||||
if(has_fdc && !(address&0x2000)) {
|
||||
upper_rom_ = (*cycle.value == 7) ? ROMType::AMSDOS : ROMType::BASIC;
|
||||
if(upper_rom_is_paged_) read_pointers_[3] = roms_[upper_rom_].data();
|
||||
}
|
||||
|
||||
@ -867,13 +912,13 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Check for an FDC access
|
||||
if(has_fdc_ && (address & 0x580) == 0x100) {
|
||||
if(has_fdc && (address & 0x580) == 0x100) {
|
||||
flush_fdc();
|
||||
fdc_.set_register(address & 1, *cycle.value);
|
||||
}
|
||||
|
||||
// Check for a disk motor access
|
||||
if(has_fdc_ && !(address & 0x580)) {
|
||||
if(has_fdc && !(address & 0x580)) {
|
||||
flush_fdc();
|
||||
fdc_.set_motor_on(!!(*cycle.value));
|
||||
}
|
||||
@ -888,7 +933,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Check for an FDC access
|
||||
if(has_fdc_ && (address & 0x580) == 0x100) {
|
||||
if(has_fdc && (address & 0x580) == 0x100) {
|
||||
flush_fdc();
|
||||
*cycle.value &= fdc_.get_register(address & 1);
|
||||
}
|
||||
@ -961,50 +1006,6 @@ class ConcreteMachine:
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
/// The ConfigurationTarget entry point; should configure this meachine as described by @c target.
|
||||
void configure_as_target(const Analyser::Static::Target *target) override final {
|
||||
auto *const cpc_target = dynamic_cast<const Analyser::Static::AmstradCPC::Target *>(target);
|
||||
|
||||
switch(cpc_target->model) {
|
||||
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
|
||||
rom_model_ = ROMType::OS464;
|
||||
has_128k_ = false;
|
||||
has_fdc_ = false;
|
||||
break;
|
||||
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
|
||||
rom_model_ = ROMType::OS664;
|
||||
has_128k_ = false;
|
||||
has_fdc_ = true;
|
||||
break;
|
||||
case Analyser::Static::AmstradCPC::Target::Model::CPC6128:
|
||||
rom_model_ = ROMType::OS6128;
|
||||
has_128k_ = true;
|
||||
has_fdc_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Establish default memory map
|
||||
upper_rom_is_paged_ = true;
|
||||
upper_rom_ = rom_model_ + 1;
|
||||
|
||||
write_pointers_[0] = &ram_[0];
|
||||
write_pointers_[1] = &ram_[16384];
|
||||
write_pointers_[2] = &ram_[32768];
|
||||
write_pointers_[3] = &ram_[49152];
|
||||
|
||||
read_pointers_[0] = roms_[rom_model_].data();
|
||||
read_pointers_[1] = write_pointers_[1];
|
||||
read_pointers_[2] = write_pointers_[2];
|
||||
read_pointers_[3] = roms_[upper_rom_].data();
|
||||
|
||||
// Type whatever is required.
|
||||
if(!cpc_target->loading_command.empty()) {
|
||||
type_string(cpc_target->loading_command);
|
||||
}
|
||||
|
||||
insert_media(target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
// If there are any tapes supplied, use the first of them.
|
||||
if(!media.tapes.empty()) {
|
||||
@ -1019,28 +1020,7 @@ class ConcreteMachine:
|
||||
if(c == 4) break;
|
||||
}
|
||||
|
||||
return !media.tapes.empty() || (!media.disks.empty() && has_fdc_);
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
auto roms = roms_with_names(
|
||||
"AmstradCPC",
|
||||
{
|
||||
"os464.rom", "basic464.rom",
|
||||
"os664.rom", "basic664.rom",
|
||||
"os6128.rom", "basic6128.rom",
|
||||
"amsdos.rom"
|
||||
});
|
||||
|
||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
||||
auto &data = roms[index];
|
||||
if(!data) return false;
|
||||
roms_[static_cast<int>(index)] = std::move(*data);
|
||||
roms_[static_cast<int>(index)].resize(16384);
|
||||
}
|
||||
|
||||
return true;
|
||||
return !media.tapes.empty() || (!media.disks.empty() && has_fdc);
|
||||
}
|
||||
|
||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final {
|
||||
@ -1078,7 +1058,7 @@ class ConcreteMachine:
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
if(has_fdc_) fdc_.set_activity_observer(observer);
|
||||
if(has_fdc) fdc_.set_activity_observer(observer);
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
@ -1117,7 +1097,7 @@ class ConcreteMachine:
|
||||
case 1: crtc_bus_handler_.set_colour(value & 0x1f); break;
|
||||
case 2:
|
||||
// Perform ROM paging.
|
||||
read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[rom_model_].data();
|
||||
read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[ROMType::OS].data();
|
||||
|
||||
upper_rom_is_paged_ = !(value & 8);
|
||||
read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3];
|
||||
@ -1169,7 +1149,7 @@ class ConcreteMachine:
|
||||
HalfCycles time_since_fdc_update_;
|
||||
void flush_fdc() {
|
||||
// Clock the FDC, if connected, using a lazy scale by two
|
||||
if(has_fdc_ && !fdc_is_sleeping_) {
|
||||
if(has_fdc && !fdc_is_sleeping_) {
|
||||
fdc_.run_for(Cycles(time_since_fdc_update_.as_int()));
|
||||
}
|
||||
time_since_fdc_update_ = HalfCycles(0);
|
||||
@ -1184,13 +1164,16 @@ class ConcreteMachine:
|
||||
|
||||
uint8_t ram_[128 * 1024];
|
||||
|
||||
std::vector<uint8_t> roms_[7];
|
||||
int rom_model_;
|
||||
bool has_fdc_, fdc_is_sleeping_;
|
||||
bool fdc_is_sleeping_;
|
||||
bool tape_player_is_sleeping_;
|
||||
bool has_128k_;
|
||||
|
||||
enum ROMType: int {
|
||||
AMSDOS = 0, OS = 1, BASIC = 2
|
||||
};
|
||||
std::vector<uint8_t> roms_[3];
|
||||
bool upper_rom_is_paged_;
|
||||
int upper_rom_;
|
||||
ROMType upper_rom_;
|
||||
|
||||
uint8_t *ram_pages_[4];
|
||||
uint8_t *read_pointers_[4];
|
||||
@ -1205,8 +1188,13 @@ class ConcreteMachine:
|
||||
using namespace AmstradCPC;
|
||||
|
||||
// See header; constructs and returns an instance of the Amstrad CPC.
|
||||
Machine *Machine::AmstradCPC() {
|
||||
return new AmstradCPC::ConcreteMachine;
|
||||
Machine *Machine::AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::AmstradCPC::Target;
|
||||
const Target *const cpc_target = dynamic_cast<const Target *>(target);
|
||||
switch(cpc_target->model) {
|
||||
default: return new AmstradCPC::ConcreteMachine<true>(*cpc_target, rom_fetcher);
|
||||
case Target::Model::CPC464: return new AmstradCPC::ConcreteMachine<false>(*cpc_target, rom_fetcher);
|
||||
}
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
uint8_t ram_[65536], aux_ram_[65536];
|
||||
std::vector<uint8_t> apple2_rom_, apple2plus_rom_, rom_;
|
||||
std::vector<uint8_t> rom_;
|
||||
std::vector<uint8_t> character_rom_;
|
||||
uint8_t keyboard_input_ = 0x00;
|
||||
|
||||
@ -98,8 +98,6 @@ class ConcreteMachine:
|
||||
Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
|
||||
Cycles cycles_since_audio_update_;
|
||||
|
||||
ROMMachine::ROMFetcher rom_fetcher_;
|
||||
|
||||
// MARK: - Cards
|
||||
std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
|
||||
Cycles cycles_since_card_update_;
|
||||
@ -226,7 +224,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
public:
|
||||
ConcreteMachine():
|
||||
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||
m6502_(*this),
|
||||
video_bus_handler_(ram_),
|
||||
audio_toggle_(audio_queue_),
|
||||
@ -254,6 +252,41 @@ class ConcreteMachine:
|
||||
// Add a couple of joysticks.
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
|
||||
// Pick the required ROMs.
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
std::vector<std::string> rom_names = {"apple2-character.rom"};
|
||||
switch(target.model) {
|
||||
default:
|
||||
rom_names.push_back("apple2o.rom");
|
||||
break;
|
||||
case Target::Model::IIplus:
|
||||
rom_names.push_back("apple2.rom");
|
||||
break;
|
||||
}
|
||||
const auto roms = rom_fetcher("AppleII", rom_names);
|
||||
|
||||
if(!roms[0] || !roms[1]) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
|
||||
character_rom_ = std::move(*roms[0]);
|
||||
rom_ = std::move(*roms[1]);
|
||||
if(rom_.size() > 12*1024) {
|
||||
rom_.erase(rom_.begin(), rom_.begin() + static_cast<off_t>(rom_.size()) - 12*1024);
|
||||
}
|
||||
|
||||
if(target.disk_controller != Target::DiskController::None) {
|
||||
// Apple recommended slot 6 for the (first) Disk II.
|
||||
install_card(6, new AppleII::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector));
|
||||
}
|
||||
|
||||
// Set up the default memory blocks.
|
||||
memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_;
|
||||
memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200];
|
||||
set_language_card_paging();
|
||||
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -596,27 +629,6 @@ class ConcreteMachine:
|
||||
audio_queue_.perform();
|
||||
}
|
||||
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
auto roms = roms_with_names(
|
||||
"AppleII",
|
||||
{
|
||||
"apple2o.rom",
|
||||
"apple2.rom",
|
||||
"apple2-character.rom"
|
||||
});
|
||||
|
||||
if(!roms[0] || !roms[1] || !roms[2]) return false;
|
||||
|
||||
apple2_rom_ = std::move(*roms[0]);
|
||||
apple2plus_rom_ = std::move(*roms[1]);
|
||||
|
||||
character_rom_ = std::move(*roms[2]);
|
||||
|
||||
rom_fetcher_ = roms_with_names;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) override {
|
||||
m6502_.run_for(cycles);
|
||||
}
|
||||
@ -651,28 +663,6 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: ConfigurationTarget
|
||||
void configure_as_target(const Analyser::Static::Target *target) override {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
auto *const apple_target = dynamic_cast<const Target *>(target);
|
||||
|
||||
if(apple_target->disk_controller != Target::DiskController::None) {
|
||||
// Apple recommended slot 6 for the (first) Disk II.
|
||||
install_card(6, new AppleII::DiskIICard(rom_fetcher_, apple_target->disk_controller == Target::DiskController::SixteenSector));
|
||||
}
|
||||
|
||||
rom_ = (apple_target->model == Target::Model::II) ? apple2_rom_ : apple2plus_rom_;
|
||||
if(rom_.size() > 12*1024) {
|
||||
rom_.erase(rom_.begin(), rom_.begin() + static_cast<off_t>(rom_.size()) - 12*1024);
|
||||
}
|
||||
|
||||
// Set up the default memory blocks.
|
||||
memory_blocks_[0].read_pointer = memory_blocks_[0].write_pointer = ram_;
|
||||
memory_blocks_[1].read_pointer = memory_blocks_[1].write_pointer = &ram_[0x200];
|
||||
set_language_card_paging();
|
||||
|
||||
insert_media(apple_target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(!media.disks.empty()) {
|
||||
auto diskii = diskii_card();
|
||||
@ -722,8 +712,10 @@ class ConcreteMachine:
|
||||
|
||||
using namespace AppleII;
|
||||
|
||||
Machine *Machine::AppleII() {
|
||||
return new ConcreteMachine;
|
||||
Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
const Target *const appleii_target = dynamic_cast<const Target *>(target);
|
||||
return new ConcreteMachine(*appleii_target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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",
|
||||
|
@ -76,24 +76,16 @@ class Joystick: public Inputs::ConcreteJoystick {
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public JoystickMachine::Machine,
|
||||
public Outputs::CRT::Delegate {
|
||||
public:
|
||||
ConcreteMachine() {
|
||||
ConcreteMachine(const Analyser::Static::Atari::Target &target) {
|
||||
set_clock_rate(NTSC_clock_rate);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
close_output();
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override {
|
||||
auto *const atari_target = dynamic_cast<const Analyser::Static::Atari::Target *>(target);
|
||||
const std::vector<uint8_t> &rom = target->media.cartridges.front()->get_segments().front().data;
|
||||
const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
|
||||
|
||||
using PagingModel = Analyser::Static::Atari::Target::PagingModel;
|
||||
switch(atari_target->paging_model) {
|
||||
switch(target.paging_model) {
|
||||
case PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
|
||||
case PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break;
|
||||
case PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break;
|
||||
@ -105,21 +97,21 @@ class ConcreteMachine:
|
||||
case PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break;
|
||||
|
||||
case PagingModel::Atari8k:
|
||||
if(atari_target->uses_superchip) {
|
||||
if(target.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8kSuperChip>(rom));
|
||||
} else {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8k>(rom));
|
||||
}
|
||||
break;
|
||||
case PagingModel::Atari16k:
|
||||
if(atari_target->uses_superchip) {
|
||||
if(target.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16kSuperChip>(rom));
|
||||
} else {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16k>(rom));
|
||||
}
|
||||
break;
|
||||
case PagingModel::Atari32k:
|
||||
if(atari_target->uses_superchip) {
|
||||
if(target.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32kSuperChip>(rom));
|
||||
} else {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32k>(rom));
|
||||
@ -131,8 +123,8 @@ class ConcreteMachine:
|
||||
joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
return false;
|
||||
~ConcreteMachine() {
|
||||
close_output();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
||||
@ -254,8 +246,10 @@ class ConcreteMachine:
|
||||
|
||||
using namespace Atari2600;
|
||||
|
||||
Machine *Machine::Atari2600() {
|
||||
return new Atari2600::ConcreteMachine;
|
||||
Machine *Machine::Atari2600(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::Atari::Target;
|
||||
const Target *const atari_target = dynamic_cast<const Target *>(target);
|
||||
return new Atari2600::ConcreteMachine(*atari_target);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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
|
||||
|
@ -112,7 +112,7 @@ class ConcreteMachine:
|
||||
public JoystickMachine::Machine {
|
||||
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
z80_(*this),
|
||||
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
|
||||
ay_(audio_queue_),
|
||||
@ -122,6 +122,19 @@ class ConcreteMachine:
|
||||
set_clock_rate(3579545);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
|
||||
const auto roms = rom_fetcher(
|
||||
"ColecoVision",
|
||||
{ "coleco.rom" });
|
||||
|
||||
if(!roms[0]) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
|
||||
bios_ = *roms[0];
|
||||
bios_.resize(8192);
|
||||
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -153,11 +166,6 @@ class ConcreteMachine:
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override {
|
||||
// Insert the media.
|
||||
insert_media(target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(!media.cartridges.empty()) {
|
||||
const auto &segment = media.cartridges.front()->get_segments().front();
|
||||
@ -181,20 +189,6 @@ class ConcreteMachine:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
auto roms = roms_with_names(
|
||||
"ColecoVision",
|
||||
{ "coleco.rom" });
|
||||
|
||||
if(!roms[0]) return false;
|
||||
|
||||
bios_ = *roms[0];
|
||||
bios_.resize(8192);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: Z80::BusHandler
|
||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
// The SN76489 will use its ready line to trigger the Z80's wait for three
|
||||
@ -408,8 +402,8 @@ class ConcreteMachine:
|
||||
|
||||
using namespace Coleco::Vision;
|
||||
|
||||
Machine *Machine::ColecoVision() {
|
||||
return new ConcreteMachine;
|
||||
Machine *Machine::ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
return new ConcreteMachine(*target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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);
|
||||
|
@ -62,18 +62,6 @@ enum JoystickInput {
|
||||
Fire = 0x20
|
||||
};
|
||||
|
||||
enum ROM {
|
||||
CharactersDanish = 0,
|
||||
CharactersEnglish,
|
||||
CharactersJapanese,
|
||||
CharactersSwedish,
|
||||
KernelDanish,
|
||||
KernelJapanese,
|
||||
KernelNTSC,
|
||||
KernelPAL,
|
||||
KernelSwedish
|
||||
};
|
||||
|
||||
/*!
|
||||
Models the user-port VIA, which is the Vic's connection point for controlling its tape recorder;
|
||||
sensing the presence or absence of a tape and controlling the tape motor; and reading the current
|
||||
@ -304,7 +292,7 @@ class ConcreteMachine:
|
||||
public ClockingHint::Observer,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
m6502_(*this),
|
||||
user_port_via_port_handler_(new UserPortVIA),
|
||||
keyboard_via_port_handler_(new KeyboardVIA),
|
||||
@ -332,127 +320,68 @@ class ConcreteMachine:
|
||||
|
||||
// install a joystick
|
||||
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
rom_fetcher_ = roms_with_names;
|
||||
|
||||
auto roms = roms_with_names(
|
||||
"Vic20",
|
||||
{
|
||||
"characters-danish.bin",
|
||||
"characters-english.bin",
|
||||
"characters-japanese.bin",
|
||||
"characters-swedish.bin",
|
||||
"kernel-danish.bin",
|
||||
"kernel-japanese.bin",
|
||||
"kernel-ntsc.bin",
|
||||
"kernel-pal.bin",
|
||||
"kernel-swedish.bin",
|
||||
"basic.bin"
|
||||
});
|
||||
|
||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
||||
auto &data = roms[index];
|
||||
if(!data) return false;
|
||||
if(index < 9) roms_[index] = std::move(*data); else basic_rom_ = std::move(*data);
|
||||
std::vector<std::string> rom_names = { "basic.bin" };
|
||||
switch(target.region) {
|
||||
default:
|
||||
rom_names.push_back("characters-english.bin");
|
||||
rom_names.push_back("kernel-pal.bin");
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::American:
|
||||
rom_names.push_back("characters-english.bin");
|
||||
rom_names.push_back("kernel-ntsc.bin");
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Danish:
|
||||
rom_names.push_back("characters-danish.bin");
|
||||
rom_names.push_back("kernel-danish.bin");
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Japanese:
|
||||
rom_names.push_back("characters-japanese.bin");
|
||||
rom_names.push_back("kernel-japanese.bin");
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Swedish:
|
||||
rom_names.push_back("characters-swedish.bin");
|
||||
rom_names.push_back("kernel-japanese.bin");
|
||||
break;
|
||||
}
|
||||
|
||||
const auto roms = rom_fetcher("Vic20", rom_names);
|
||||
|
||||
for(const auto &rom: roms) {
|
||||
if(!rom) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
}
|
||||
|
||||
basic_rom_ = std::move(*roms[0]);
|
||||
character_rom_ = std::move(*roms[1]);
|
||||
kernel_rom_ = std::move(*roms[2]);
|
||||
|
||||
// Characters ROMs should be 4kb.
|
||||
for(std::size_t index = 0; index < 4; ++index) roms_[index].resize(4096);
|
||||
character_rom_.resize(4096);
|
||||
// Kernel ROMs and the BASIC ROM should be 8kb.
|
||||
for(std::size_t index = 4; index < roms.size(); ++index) roms_[index].resize(8192);
|
||||
kernel_rom_.resize(8192);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override final {
|
||||
commodore_target_ = *dynamic_cast<const Analyser::Static::Commodore::Target *>(target);
|
||||
|
||||
if(!commodore_target_.loading_command.empty()) {
|
||||
type_string(commodore_target_.loading_command);
|
||||
}
|
||||
|
||||
if(commodore_target_.has_c1540) {
|
||||
if(target.has_c1540) {
|
||||
// construct the 1540
|
||||
c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Machine::C1540));
|
||||
c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Personality::C1540, rom_fetcher));
|
||||
|
||||
// attach it to the serial bus
|
||||
c1540_->set_serial_bus(serial_bus_);
|
||||
|
||||
// give it a means to obtain its ROM
|
||||
c1540_->set_rom_fetcher(rom_fetcher_);
|
||||
|
||||
// give it a little warm up
|
||||
c1540_->run_for(Cycles(2000000));
|
||||
}
|
||||
|
||||
insert_media(target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_->set_tape(media.tapes.front());
|
||||
}
|
||||
|
||||
if(!media.disks.empty() && c1540_) {
|
||||
c1540_->set_disk(media.disks.front());
|
||||
}
|
||||
|
||||
if(!media.cartridges.empty()) {
|
||||
rom_address_ = 0xa000;
|
||||
std::vector<uint8_t> rom_image = media.cartridges.front()->get_segments().front().data;
|
||||
rom_length_ = static_cast<uint16_t>(rom_image.size());
|
||||
|
||||
rom_ = rom_image;
|
||||
rom_.resize(0x2000);
|
||||
}
|
||||
|
||||
set_use_fast_tape();
|
||||
|
||||
return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty();
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool is_pressed) override final {
|
||||
if(key != KeyRestore)
|
||||
keyboard_via_port_handler_->set_key_state(key, is_pressed);
|
||||
else
|
||||
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
|
||||
}
|
||||
|
||||
void clear_all_keys() override final {
|
||||
keyboard_via_port_handler_->clear_all_keys();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
||||
return joysticks_;
|
||||
}
|
||||
|
||||
void set_ntsc_6560() {
|
||||
set_clock_rate(1022727);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560::OutputMode::NTSC);
|
||||
mos6560_->set_clock_rate(1022727);
|
||||
}
|
||||
}
|
||||
|
||||
void set_pal_6560() {
|
||||
set_clock_rate(1108404);
|
||||
if(mos6560_) {
|
||||
mos6560_->set_output_mode(MOS::MOS6560::OutputMode::PAL);
|
||||
mos6560_->set_clock_rate(1108404);
|
||||
}
|
||||
}
|
||||
|
||||
void set_memory_map(Analyser::Static::Commodore::Target::MemoryModel memory_model, Analyser::Static::Commodore::Target::Region region) {
|
||||
// Determine PAL/NTSC
|
||||
if(region == Analyser::Static::Commodore::Target::Region::American || region == Analyser::Static::Commodore::Target::Region::Japanese) {
|
||||
if(target.region == Analyser::Static::Commodore::Target::Region::American || target.region == Analyser::Static::Commodore::Target::Region::Japanese) {
|
||||
// NTSC
|
||||
set_ntsc_6560();
|
||||
set_clock_rate(1022727);
|
||||
output_mode_ = MOS::MOS6560::OutputMode::NTSC;
|
||||
} else {
|
||||
// PAL
|
||||
set_pal_6560();
|
||||
set_clock_rate(1108404);
|
||||
output_mode_ = MOS::MOS6560::OutputMode::PAL;
|
||||
}
|
||||
|
||||
// Initialise the memory maps as all pointing to nothing
|
||||
@ -465,7 +394,7 @@ class ConcreteMachine:
|
||||
write_to_map(processor_write_memory_map_, &ram_[baseaddr], baseaddr, length);
|
||||
|
||||
// Add 6502-visible RAM as requested
|
||||
switch(memory_model) {
|
||||
switch(target.memory_model) {
|
||||
case Analyser::Static::Commodore::Target::MemoryModel::Unexpanded:
|
||||
// The default Vic-20 memory map has 1kb at address 0 and another 4kb at address 0x1000.
|
||||
set_ram(0x0000, 0x0400);
|
||||
@ -512,39 +441,53 @@ class ConcreteMachine:
|
||||
write_to_map(processor_read_memory_map_, basic_rom_.data(), 0xc000, static_cast<uint16_t>(basic_rom_.size()));
|
||||
|
||||
// install the system ROM
|
||||
ROM character_rom;
|
||||
ROM kernel_rom;
|
||||
switch(region) {
|
||||
default:
|
||||
character_rom = CharactersEnglish;
|
||||
kernel_rom = KernelPAL;
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::American:
|
||||
character_rom = CharactersEnglish;
|
||||
kernel_rom = KernelNTSC;
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Danish:
|
||||
character_rom = CharactersDanish;
|
||||
kernel_rom = KernelDanish;
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Japanese:
|
||||
character_rom = CharactersJapanese;
|
||||
kernel_rom = KernelJapanese;
|
||||
break;
|
||||
case Analyser::Static::Commodore::Target::Region::Swedish:
|
||||
character_rom = CharactersSwedish;
|
||||
kernel_rom = KernelSwedish;
|
||||
break;
|
||||
write_to_map(processor_read_memory_map_, character_rom_.data(), 0x8000, static_cast<uint16_t>(character_rom_.size()));
|
||||
write_to_map(mos6560_bus_handler_.video_memory_map, character_rom_.data(), 0x0000, static_cast<uint16_t>(character_rom_.size()));
|
||||
write_to_map(processor_read_memory_map_, kernel_rom_.data(), 0xe000, static_cast<uint16_t>(kernel_rom_.size()));
|
||||
|
||||
insert_media(target.media);
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_->set_tape(media.tapes.front());
|
||||
}
|
||||
|
||||
write_to_map(processor_read_memory_map_, roms_[character_rom].data(), 0x8000, static_cast<uint16_t>(roms_[character_rom].size()));
|
||||
write_to_map(mos6560_bus_handler_.video_memory_map, roms_[character_rom].data(), 0x0000, static_cast<uint16_t>(roms_[character_rom].size()));
|
||||
write_to_map(processor_read_memory_map_, roms_[kernel_rom].data(), 0xe000, static_cast<uint16_t>(roms_[kernel_rom].size()));
|
||||
if(!media.disks.empty() && c1540_) {
|
||||
c1540_->set_disk(media.disks.front());
|
||||
}
|
||||
|
||||
// install the inserted ROM if there is one
|
||||
if(!rom_.empty()) {
|
||||
if(!media.cartridges.empty()) {
|
||||
rom_address_ = 0xa000;
|
||||
std::vector<uint8_t> rom_image = media.cartridges.front()->get_segments().front().data;
|
||||
rom_length_ = static_cast<uint16_t>(rom_image.size());
|
||||
|
||||
rom_ = rom_image;
|
||||
rom_.resize(0x2000);
|
||||
write_to_map(processor_read_memory_map_, rom_.data(), rom_address_, rom_length_);
|
||||
}
|
||||
|
||||
set_use_fast_tape();
|
||||
|
||||
return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty();
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool is_pressed) override final {
|
||||
if(key != KeyRestore)
|
||||
keyboard_via_port_handler_->set_key_state(key, is_pressed);
|
||||
else
|
||||
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
|
||||
}
|
||||
|
||||
void clear_all_keys() override final {
|
||||
keyboard_via_port_handler_->clear_all_keys();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
||||
return joysticks_;
|
||||
}
|
||||
|
||||
// to satisfy CPU::MOS6502::Processor
|
||||
@ -680,8 +623,8 @@ class ConcreteMachine:
|
||||
void setup_output(float aspect_ratio) override final {
|
||||
mos6560_.reset(new MOS::MOS6560::MOS6560<Vic6560BusHandler>(mos6560_bus_handler_));
|
||||
mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
|
||||
// Make a guess: PAL. Without setting a clock rate the 6560 isn't fully set up so contractually something must be set.
|
||||
set_memory_map(commodore_target_.memory_model, commodore_target_.region);
|
||||
mos6560_->set_output_mode(output_mode_);
|
||||
mos6560_->set_clock_rate(get_clock_rate());
|
||||
}
|
||||
|
||||
void close_output() override final {
|
||||
@ -760,12 +703,8 @@ class ConcreteMachine:
|
||||
void update_video() {
|
||||
mos6560_->run_for(cycles_since_mos6560_update_.flush());
|
||||
}
|
||||
Analyser::Static::Commodore::Target commodore_target_;
|
||||
|
||||
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
|
||||
|
||||
std::vector<uint8_t> roms_[9];
|
||||
|
||||
std::vector<uint8_t> character_rom_;
|
||||
std::vector<uint8_t> basic_rom_;
|
||||
std::vector<uint8_t> kernel_rom_;
|
||||
@ -775,8 +714,6 @@ class ConcreteMachine:
|
||||
uint8_t ram_[0x8000];
|
||||
uint8_t colour_ram_[0x0400];
|
||||
|
||||
std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> rom_fetcher_;
|
||||
|
||||
uint8_t *processor_read_memory_map_[64];
|
||||
uint8_t *processor_write_memory_map_[64];
|
||||
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length) {
|
||||
@ -794,6 +731,7 @@ class ConcreteMachine:
|
||||
|
||||
Cycles cycles_since_mos6560_update_;
|
||||
Vic6560BusHandler mos6560_bus_handler_;
|
||||
MOS::MOS6560::OutputMode output_mode_;
|
||||
std::unique_ptr<MOS::MOS6560::MOS6560<Vic6560BusHandler>> mos6560_;
|
||||
std::shared_ptr<UserPortVIA> user_port_via_port_handler_;
|
||||
std::shared_ptr<KeyboardVIA> keyboard_via_port_handler_;
|
||||
@ -822,8 +760,10 @@ class ConcreteMachine:
|
||||
|
||||
using namespace Commodore::Vic20;
|
||||
|
||||
Machine *Machine::Vic20() {
|
||||
return new Vic20::ConcreteMachine;
|
||||
Machine *Machine::Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::Commodore::Target;
|
||||
const Target *const commodore_target = dynamic_cast<const Target *>(target);
|
||||
return new Vic20::ConcreteMachine(*commodore_target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -22,9 +22,6 @@ namespace ConfigurationTarget {
|
||||
*/
|
||||
class Machine {
|
||||
public:
|
||||
/// Instructs the machine to configure itself as described by @c target and insert the included media.
|
||||
virtual void configure_as_target(const Analyser::Static::Target *target) = 0;
|
||||
|
||||
/*!
|
||||
Requests that the machine insert @c media as a modification to current state
|
||||
|
||||
|
@ -49,10 +49,10 @@ class ConcreteMachine:
|
||||
public Utility::TypeRecipient,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
m6502_(*this),
|
||||
sound_generator_(audio_queue_),
|
||||
speaker_(sound_generator_) {
|
||||
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
m6502_(*this),
|
||||
sound_generator_(audio_queue_),
|
||||
speaker_(sound_generator_) {
|
||||
memset(key_states_, 0, sizeof(key_states_));
|
||||
for(int c = 0; c < 16; c++)
|
||||
memset(roms_[c], 0xff, 16384);
|
||||
@ -61,60 +61,53 @@ class ConcreteMachine:
|
||||
set_clock_rate(2000000);
|
||||
|
||||
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
|
||||
|
||||
std::vector<std::string> rom_names = {"basic.rom", "os.rom"};
|
||||
if(target.has_adfs) {
|
||||
rom_names.push_back("ADFS-E00_1.rom");
|
||||
rom_names.push_back("ADFS-E00_2.rom");
|
||||
}
|
||||
const size_t dfs_rom_position = rom_names.size();
|
||||
if(target.has_dfs) {
|
||||
rom_names.push_back("DFS-1770-2.20.rom");
|
||||
}
|
||||
const auto roms = rom_fetcher("Electron", rom_names);
|
||||
|
||||
for(const auto &rom: roms) {
|
||||
if(!rom) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
}
|
||||
set_rom(ROM::BASIC, *roms[0], false);
|
||||
set_rom(ROM::OS, *roms[1], false);
|
||||
|
||||
if(target.has_dfs || target.has_adfs) {
|
||||
plus3_.reset(new Plus3);
|
||||
|
||||
if(target.has_dfs) {
|
||||
set_rom(ROM::Slot0, *roms[dfs_rom_position], true);
|
||||
}
|
||||
if(target.has_adfs) {
|
||||
set_rom(ROM::Slot4, *roms[2], true);
|
||||
set_rom(ROM::Slot5, *roms[3], true);
|
||||
}
|
||||
}
|
||||
|
||||
insert_media(target.media);
|
||||
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
if(target.should_shift_restart) {
|
||||
shift_restart_counter_ = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
audio_queue_.flush();
|
||||
}
|
||||
|
||||
void set_rom(ROMSlot slot, const std::vector<uint8_t> &data, bool is_writeable) override final {
|
||||
uint8_t *target = nullptr;
|
||||
switch(slot) {
|
||||
case ROMSlotDFS: dfs_ = data; return;
|
||||
case ROMSlotADFS1: adfs1_ = data; return;
|
||||
case ROMSlotADFS2: adfs2_ = data; return;
|
||||
|
||||
case ROMSlotOS: target = os_; break;
|
||||
default:
|
||||
target = roms_[slot];
|
||||
rom_write_masks_[slot] = is_writeable;
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy in, with mirroring.
|
||||
std::size_t rom_ptr = 0;
|
||||
while(rom_ptr < 16384) {
|
||||
std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size());
|
||||
std::memcpy(&target[rom_ptr], data.data(), size_to_copy);
|
||||
rom_ptr += size_to_copy;
|
||||
}
|
||||
rom_inserted_[slot] = true;
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
auto roms = roms_with_names(
|
||||
"Electron",
|
||||
{
|
||||
"DFS-1770-2.20.rom",
|
||||
"ADFS-E00_1.rom", "ADFS-E00_2.rom",
|
||||
"basic.rom", "os.rom"
|
||||
});
|
||||
ROMSlot slots[] = {
|
||||
ROMSlotDFS,
|
||||
ROMSlotADFS1, ROMSlotADFS2,
|
||||
ROMSlotBASIC, ROMSlotOS
|
||||
};
|
||||
|
||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
||||
auto &data = roms[index];
|
||||
if(!data) return false;
|
||||
set_rom(slots[index], *data, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool isPressed) override final {
|
||||
if(key == KeyBreak) {
|
||||
m6502_.set_reset_line(isPressed);
|
||||
@ -131,32 +124,6 @@ class ConcreteMachine:
|
||||
if(is_holding_shift_) set_key_state(KeyShift, true);
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override final {
|
||||
auto *const acorn_target = dynamic_cast<const Analyser::Static::Acorn::Target *>(target);
|
||||
|
||||
if(!acorn_target->loading_command.empty()) {
|
||||
type_string(acorn_target->loading_command);
|
||||
}
|
||||
|
||||
if(acorn_target->should_shift_restart) {
|
||||
shift_restart_counter_ = 1000000;
|
||||
}
|
||||
|
||||
if(acorn_target->has_dfs || acorn_target->has_adfs) {
|
||||
plus3_.reset(new Plus3);
|
||||
|
||||
if(acorn_target->has_dfs) {
|
||||
set_rom(ROMSlot0, dfs_, true);
|
||||
}
|
||||
if(acorn_target->has_adfs) {
|
||||
set_rom(ROMSlot4, adfs1_, true);
|
||||
set_rom(ROMSlot5, adfs2_, true);
|
||||
}
|
||||
}
|
||||
|
||||
insert_media(target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_.set_tape(media.tapes.front());
|
||||
@ -167,11 +134,11 @@ class ConcreteMachine:
|
||||
plus3_->set_disk(media.disks.front(), 0);
|
||||
}
|
||||
|
||||
ROMSlot slot = ROMSlot12;
|
||||
ROM slot = ROM::Slot12;
|
||||
for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : media.cartridges) {
|
||||
const ROMSlot first_slot_tried = slot;
|
||||
while(rom_inserted_[slot]) {
|
||||
slot = static_cast<ROMSlot>((static_cast<int>(slot) + 1)&15);
|
||||
const ROM first_slot_tried = slot;
|
||||
while(rom_inserted_[static_cast<int>(slot)]) {
|
||||
slot = static_cast<ROM>((static_cast<int>(slot) + 1) & 15);
|
||||
if(slot == first_slot_tried) return false;
|
||||
}
|
||||
set_rom(slot, cartridge->get_segments().front().data, false);
|
||||
@ -258,7 +225,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// latch the paged ROM in case external hardware is being emulated
|
||||
active_rom_ = (Electron::ROMSlot)(*value & 0xf);
|
||||
active_rom_ = *value & 0xf;
|
||||
|
||||
// apply the ULA's test
|
||||
if(*value & 0x08) {
|
||||
@ -363,7 +330,7 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
if(basic_is_active_) {
|
||||
*value &= roms_[ROMSlotBASIC][address & 16383];
|
||||
*value &= roms_[static_cast<int>(ROM::BASIC)][address & 16383];
|
||||
}
|
||||
} else if(rom_write_masks_[active_rom_]) {
|
||||
roms_[active_rom_][address & 16383] = *value;
|
||||
@ -494,6 +461,48 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ROM {
|
||||
Slot0 = 0,
|
||||
Slot1, Slot2, Slot3,
|
||||
Slot4, Slot5, Slot6, Slot7,
|
||||
|
||||
Keyboard = 8, Slot9,
|
||||
BASIC = 10, Slot11,
|
||||
|
||||
Slot12, Slot13, Slot14, Slot15,
|
||||
|
||||
OS, DFS,
|
||||
ADFS1, ADFS2
|
||||
};
|
||||
|
||||
/*!
|
||||
Sets the contents of @c slot to @c data. If @c is_writeable is @c true then writing to the slot
|
||||
is enabled: it acts as if it were sideways RAM. Otherwise the slot is modelled as containing ROM.
|
||||
*/
|
||||
void set_rom(ROM slot, const std::vector<uint8_t> &data, bool is_writeable) {
|
||||
uint8_t *target = nullptr;
|
||||
switch(slot) {
|
||||
case ROM::DFS: dfs_ = data; return;
|
||||
case ROM::ADFS1: adfs1_ = data; return;
|
||||
case ROM::ADFS2: adfs2_ = data; return;
|
||||
|
||||
case ROM::OS: target = os_; break;
|
||||
default:
|
||||
target = roms_[static_cast<int>(slot)];
|
||||
rom_write_masks_[static_cast<int>(slot)] = is_writeable;
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy in, with mirroring.
|
||||
std::size_t rom_ptr = 0;
|
||||
while(rom_ptr < 16384) {
|
||||
std::size_t size_to_copy = std::min(16384 - rom_ptr, data.size());
|
||||
std::memcpy(&target[rom_ptr], data.data(), size_to_copy);
|
||||
rom_ptr += size_to_copy;
|
||||
}
|
||||
rom_inserted_[static_cast<int>(slot)] = true;
|
||||
}
|
||||
|
||||
// MARK: - Work deferral updates.
|
||||
inline void update_display() {
|
||||
if(cycles_since_display_update_ > 0) {
|
||||
@ -540,7 +549,7 @@ class ConcreteMachine:
|
||||
std::vector<uint8_t> dfs_, adfs1_, adfs2_;
|
||||
|
||||
// Paging
|
||||
ROMSlot active_rom_ = ROMSlot::ROMSlot0;
|
||||
int active_rom_ = static_cast<int>(ROM::Slot0);
|
||||
bool keyboard_is_active_ = false;
|
||||
bool basic_is_active_ = false;
|
||||
|
||||
@ -590,8 +599,10 @@ class ConcreteMachine:
|
||||
|
||||
using namespace Electron;
|
||||
|
||||
Machine *Machine::Electron() {
|
||||
return new Electron::ConcreteMachine;
|
||||
Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::Acorn::Target;
|
||||
const Target *const acorn_target = dynamic_cast<const Target *>(target);
|
||||
return new Electron::ConcreteMachine(*acorn_target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class ConcreteMachine:
|
||||
public ClockingHint::Observer,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine():
|
||||
ConcreteMachine(const Analyser::Static::MSX::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||
z80_(*this),
|
||||
i8255_(i8255_port_handler_),
|
||||
ay_(audio_queue_),
|
||||
@ -112,6 +112,51 @@ class ConcreteMachine:
|
||||
|
||||
// Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC.
|
||||
mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f});
|
||||
|
||||
// Fetch the necessary ROMs.
|
||||
std::vector<std::string> rom_names = {"msx.rom"};
|
||||
if(target.has_disk_drive) {
|
||||
rom_names.push_back("disk.rom");
|
||||
}
|
||||
const auto roms = rom_fetcher("MSX", rom_names);
|
||||
|
||||
if(!roms[0] || (target.has_disk_drive && !roms[1])) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
|
||||
memory_slots_[0].source = std::move(*roms[0]);
|
||||
memory_slots_[0].source.resize(32768);
|
||||
|
||||
for(size_t c = 0; c < 8; ++c) {
|
||||
for(size_t slot = 0; slot < 3; ++slot) {
|
||||
memory_slots_[slot].read_pointers[c] = unpopulated_;
|
||||
memory_slots_[slot].write_pointers[c] = scratch_;
|
||||
}
|
||||
|
||||
memory_slots_[3].read_pointers[c] =
|
||||
memory_slots_[3].write_pointers[c] = &ram_[c * 8192];
|
||||
}
|
||||
|
||||
map(0, 0, 0, 32768);
|
||||
page_memory(0);
|
||||
|
||||
// Add a disk cartridge if any disks were supplied.
|
||||
if(target.has_disk_drive) {
|
||||
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
|
||||
memory_slots_[2].source = std::move(*roms[1]);
|
||||
memory_slots_[2].source.resize(16384);
|
||||
|
||||
map(2, 0, 0x4000, 0x2000);
|
||||
unmap(2, 0x6000, 0x2000);
|
||||
}
|
||||
|
||||
// Insert the media.
|
||||
insert_media(target.media);
|
||||
|
||||
// Type whatever has been requested.
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -152,25 +197,6 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override {
|
||||
auto *const msx_target = dynamic_cast<const Analyser::Static::MSX::Target *>(target);
|
||||
|
||||
// Add a disk cartridge if any disks were supplied.
|
||||
if(msx_target->has_disk_drive) {
|
||||
map(2, 0, 0x4000, 0x2000);
|
||||
unmap(2, 0x6000, 0x2000);
|
||||
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
|
||||
}
|
||||
|
||||
// Insert the media.
|
||||
insert_media(target->media);
|
||||
|
||||
// Type whatever has been requested.
|
||||
if(!msx_target->loading_command.empty()) {
|
||||
type_string(msx_target->loading_command);
|
||||
}
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(!media.cartridges.empty()) {
|
||||
const auto &segment = media.cartridges.front()->get_segments().front();
|
||||
@ -467,39 +493,6 @@ class ConcreteMachine:
|
||||
audio_queue_.perform();
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
auto roms = roms_with_names(
|
||||
"MSX",
|
||||
{
|
||||
"msx.rom",
|
||||
"disk.rom"
|
||||
});
|
||||
|
||||
if(!roms[0] || !roms[1]) return false;
|
||||
|
||||
memory_slots_[0].source = std::move(*roms[0]);
|
||||
memory_slots_[0].source.resize(32768);
|
||||
|
||||
memory_slots_[2].source = std::move(*roms[1]);
|
||||
memory_slots_[2].source.resize(16384);
|
||||
|
||||
for(size_t c = 0; c < 8; ++c) {
|
||||
for(size_t slot = 0; slot < 3; ++slot) {
|
||||
memory_slots_[slot].read_pointers[c] = unpopulated_;
|
||||
memory_slots_[slot].write_pointers[c] = scratch_;
|
||||
}
|
||||
|
||||
memory_slots_[3].read_pointers[c] =
|
||||
memory_slots_[3].write_pointers[c] = &ram_[c * 8192];
|
||||
}
|
||||
|
||||
map(0, 0, 0, 32768);
|
||||
page_memory(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_keyboard_line(int line) {
|
||||
selected_key_line_ = line;
|
||||
}
|
||||
@ -683,8 +676,10 @@ class ConcreteMachine:
|
||||
|
||||
using namespace MSX;
|
||||
|
||||
Machine *Machine::MSX() {
|
||||
return new ConcreteMachine;
|
||||
Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::MSX::Target;
|
||||
const Target *const msx_target = dynamic_cast<const Target *>(target);
|
||||
return new ConcreteMachine(*msx_target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -66,7 +66,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
z80_(*this),
|
||||
tape_player_(ZX8081ClockRate),
|
||||
ay_(audio_queue_),
|
||||
@ -74,6 +74,55 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
set_clock_rate(ZX8081ClockRate);
|
||||
speaker_.set_input_rate(static_cast<float>(ZX8081ClockRate) / 2.0f);
|
||||
clear_all_keys();
|
||||
|
||||
const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM;
|
||||
const auto roms = rom_fetcher("ZX8081", { use_zx81_rom ? "zx81.rom" : "zx80.rom" });
|
||||
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
|
||||
|
||||
rom_ = std::move(*roms[0]);
|
||||
rom_.resize(use_zx81_rom ? 8192 : 4096);
|
||||
|
||||
if(is_zx81) {
|
||||
tape_trap_address_ = 0x37c;
|
||||
tape_return_address_ = 0x380;
|
||||
vsync_start_ = HalfCycles(32);
|
||||
vsync_end_ = HalfCycles(64);
|
||||
automatic_tape_motor_start_address_ = 0x0340;
|
||||
automatic_tape_motor_end_address_ = 0x03c3;
|
||||
} else {
|
||||
tape_trap_address_ = 0x220;
|
||||
tape_return_address_ = 0x248;
|
||||
vsync_start_ = HalfCycles(26);
|
||||
vsync_end_ = HalfCycles(66);
|
||||
automatic_tape_motor_start_address_ = 0x0206;
|
||||
automatic_tape_motor_end_address_ = 0x024d;
|
||||
}
|
||||
rom_mask_ = static_cast<uint16_t>(rom_.size() - 1);
|
||||
|
||||
switch(target.memory_model) {
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded:
|
||||
ram_.resize(1024);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 1023;
|
||||
break;
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB:
|
||||
ram_.resize(16384);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 16383;
|
||||
break;
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB:
|
||||
ram_.resize(65536);
|
||||
ram_base_ = 8192;
|
||||
ram_mask_ = 65535;
|
||||
break;
|
||||
}
|
||||
Memory::Fuzz(ram_);
|
||||
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -105,7 +154,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
video_->run_for(cycle.length);
|
||||
}
|
||||
|
||||
if(is_zx81_) horizontal_counter_ %= HalfCycles(Cycles(207));
|
||||
if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207));
|
||||
if(!tape_advance_delay_) {
|
||||
tape_player_.run_for(cycle.length);
|
||||
} else {
|
||||
@ -129,7 +178,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
set_vsync(false);
|
||||
}
|
||||
if(!(address & 2)) nmi_is_enabled_ = false;
|
||||
if(!(address & 1)) nmi_is_enabled_ = is_zx81_;
|
||||
if(!(address & 1)) nmi_is_enabled_ = is_zx81;
|
||||
|
||||
// The below emulates the ZonX AY expansion device.
|
||||
if(is_zx81) {
|
||||
@ -281,54 +330,6 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target *target) override final {
|
||||
auto *const zx8081_target = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target);
|
||||
is_zx81_ = zx8081_target->is_ZX81;
|
||||
if(is_zx81_) {
|
||||
rom_ = zx81_rom_;
|
||||
tape_trap_address_ = 0x37c;
|
||||
tape_return_address_ = 0x380;
|
||||
vsync_start_ = HalfCycles(32);
|
||||
vsync_end_ = HalfCycles(64);
|
||||
automatic_tape_motor_start_address_ = 0x0340;
|
||||
automatic_tape_motor_end_address_ = 0x03c3;
|
||||
} else {
|
||||
rom_ = zx8081_target->ZX80_uses_ZX81_ROM ? zx81_rom_ : zx80_rom_;
|
||||
tape_trap_address_ = 0x220;
|
||||
tape_return_address_ = 0x248;
|
||||
vsync_start_ = HalfCycles(26);
|
||||
vsync_end_ = HalfCycles(66);
|
||||
automatic_tape_motor_start_address_ = 0x0206;
|
||||
automatic_tape_motor_end_address_ = 0x024d;
|
||||
}
|
||||
rom_mask_ = static_cast<uint16_t>(rom_.size() - 1);
|
||||
|
||||
switch(zx8081_target->memory_model) {
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::Unexpanded:
|
||||
ram_.resize(1024);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 1023;
|
||||
break;
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::SixteenKB:
|
||||
ram_.resize(16384);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 16383;
|
||||
break;
|
||||
case Analyser::Static::ZX8081::Target::MemoryModel::SixtyFourKB:
|
||||
ram_.resize(65536);
|
||||
ram_base_ = 8192;
|
||||
ram_mask_ = 65535;
|
||||
break;
|
||||
}
|
||||
Memory::Fuzz(ram_);
|
||||
|
||||
if(!zx8081_target->loading_command.empty()) {
|
||||
type_string(zx8081_target->loading_command);
|
||||
}
|
||||
|
||||
insert_media(target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
@ -339,30 +340,10 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
}
|
||||
|
||||
void type_string(const std::string &string) override final {
|
||||
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper(is_zx81_));
|
||||
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper(is_zx81));
|
||||
Utility::TypeRecipient::add_typer(string, std::move(mapper));
|
||||
}
|
||||
|
||||
// Obtains the system ROMs.
|
||||
bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override {
|
||||
const auto roms = roms_with_names(
|
||||
"ZX8081",
|
||||
{
|
||||
"zx80.rom", "zx81.rom",
|
||||
});
|
||||
|
||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
||||
if(!roms[index]) return false;
|
||||
}
|
||||
|
||||
zx80_rom_ = std::move(*roms[0]);
|
||||
zx81_rom_ = std::move(*roms[1]);
|
||||
zx80_rom_.resize(4096);
|
||||
zx81_rom_.resize(8192);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: - Keyboard
|
||||
void set_key_state(uint16_t key, bool is_pressed) override final {
|
||||
if(is_pressed)
|
||||
@ -436,7 +417,6 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
|
||||
|
||||
std::unique_ptr<Video> video_;
|
||||
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
||||
|
||||
uint16_t tape_trap_address_, tape_return_address_;
|
||||
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||
@ -456,7 +436,6 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_;
|
||||
Storage::Tape::ZX8081::Parser parser_;
|
||||
|
||||
bool is_zx81_;
|
||||
bool nmi_is_enabled_ = false;
|
||||
|
||||
HalfCycles vsync_start_, vsync_end_;
|
||||
@ -522,14 +501,12 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
using namespace ZX8081;
|
||||
|
||||
// See header; constructs and returns an instance of the ZX80 or 81.
|
||||
Machine *Machine::ZX8081(const Analyser::Static::Target *target_hint) {
|
||||
const Analyser::Static::ZX8081::Target *const hint = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target_hint);
|
||||
Machine *Machine::ZX8081(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
const Analyser::Static::ZX8081::Target *const zx_target = dynamic_cast<const Analyser::Static::ZX8081::Target *>(target);
|
||||
|
||||
// Instantiate the correct type of machine.
|
||||
if(hint->is_ZX81)
|
||||
return new ZX8081::ConcreteMachine<true>();
|
||||
else
|
||||
return new ZX8081::ConcreteMachine<false>();
|
||||
if(zx_target->is_ZX81) return new ZX8081::ConcreteMachine<true>(*zx_target, rom_fetcher);
|
||||
else return new ZX8081::ConcreteMachine<false>(*zx_target, rom_fetcher);
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -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) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user