mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 07:30:21 +00:00
Merge branch 'master' into Enterprise
This commit is contained in:
commit
8e0a6df03b
@ -750,7 +750,7 @@ HalfCycles TMS9918::get_next_sequence_point() {
|
|||||||
if(next_line_interrupt_row == -1) {
|
if(next_line_interrupt_row == -1) {
|
||||||
return generate_interrupts_ ?
|
return generate_interrupts_ ?
|
||||||
half_cycles_before_internal_cycles(time_until_frame_interrupt) :
|
half_cycles_before_internal_cycles(time_until_frame_interrupt) :
|
||||||
HalfCycles(-1);
|
HalfCycles::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out the number of internal cycles until the next line interrupt, which is the amount
|
// Figure out the number of internal cycles until the next line interrupt, which is the amount
|
||||||
|
@ -787,45 +787,43 @@ template <bool has_fdc> class ConcreteMachine:
|
|||||||
ay_.ay().set_port_handler(&key_state_);
|
ay_.ay().set_port_handler(&key_state_);
|
||||||
|
|
||||||
// construct the list of necessary ROMs
|
// construct the list of necessary ROMs
|
||||||
const std::string machine_name = "AmstradCPC";
|
bool has_amsdos = false;
|
||||||
std::vector<ROMMachine::ROM> required_roms = {
|
ROM::Name firmware, basic;
|
||||||
ROMMachine::ROM(machine_name, "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecd)
|
|
||||||
};
|
|
||||||
std::string model_number;
|
|
||||||
uint32_t crcs[2];
|
|
||||||
switch(target.model) {
|
switch(target.model) {
|
||||||
default:
|
|
||||||
model_number = "6128";
|
|
||||||
has_128k_ = true;
|
|
||||||
crcs[0] = 0x0219bb74;
|
|
||||||
crcs[1] = 0xca6af63d;
|
|
||||||
break;
|
|
||||||
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
|
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
|
||||||
model_number = "464";
|
firmware = ROM::Name::CPC464Firmware;
|
||||||
has_128k_ = false;
|
basic = ROM::Name::CPC464BASIC;
|
||||||
crcs[0] = 0x815752df;
|
|
||||||
crcs[1] = 0x7d9a3bac;
|
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
|
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
|
||||||
model_number = "664";
|
firmware = ROM::Name::CPC664Firmware;
|
||||||
has_128k_ = false;
|
basic = ROM::Name::CPC664BASIC;
|
||||||
crcs[0] = 0x3f5a6dc4;
|
has_amsdos = true;
|
||||||
crcs[1] = 0x32fee492;
|
break;
|
||||||
|
default:
|
||||||
|
firmware = ROM::Name::CPC6128Firmware;
|
||||||
|
basic = ROM::Name::CPC6128BASIC;
|
||||||
|
has_amsdos = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
required_roms.emplace_back(machine_name, "the CPC " + model_number + " firmware", "os" + model_number + ".rom", 16*1024, crcs[0]);
|
|
||||||
required_roms.emplace_back(machine_name, "the CPC " + model_number + " BASIC ROM", "basic" + model_number + ".rom", 16*1024, crcs[1]);
|
|
||||||
|
|
||||||
// fetch and verify the ROMs
|
ROM::Request request = ROM::Request(firmware) && ROM::Request(basic);
|
||||||
const auto roms = rom_fetcher(required_roms);
|
if(has_amsdos) {
|
||||||
|
request = request && ROM::Request(ROM::Name::AMSDOS);
|
||||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
|
||||||
auto &data = roms[index];
|
|
||||||
if(!data) throw ROMMachine::Error::MissingROMs;
|
|
||||||
roms_[index] = std::move(*data);
|
|
||||||
roms_[index].resize(16384);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch and verify the ROMs.
|
||||||
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
|
throw ROMMachine::Error::MissingROMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(has_amsdos) {
|
||||||
|
roms_[ROMType::AMSDOS] = roms.find(ROM::Name::AMSDOS)->second;
|
||||||
|
}
|
||||||
|
roms_[ROMType::OS] = roms.find(firmware)->second;
|
||||||
|
roms_[ROMType::BASIC] = roms.find(basic)->second;
|
||||||
|
|
||||||
// Establish default memory map
|
// Establish default memory map
|
||||||
upper_rom_is_paged_ = true;
|
upper_rom_is_paged_ = true;
|
||||||
upper_rom_ = ROMType::BASIC;
|
upper_rom_ = ROMType::BASIC;
|
||||||
|
@ -377,49 +377,55 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
|
|
||||||
// Pick the required ROMs.
|
// Pick the required ROMs.
|
||||||
using Target = Analyser::Static::AppleII::Target;
|
using Target = Analyser::Static::AppleII::Target;
|
||||||
const std::string machine_name = "AppleII";
|
ROM::Name character, system;
|
||||||
std::vector<ROMMachine::ROM> rom_descriptions;
|
|
||||||
size_t rom_size = 12*1024;
|
|
||||||
switch(target.model) {
|
switch(target.model) {
|
||||||
default:
|
default:
|
||||||
rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II));
|
character = ROM::Name::AppleIICharacter;
|
||||||
rom_descriptions.emplace_back(machine_name, "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588);
|
system = ROM::Name::AppleIIOriginal;
|
||||||
break;
|
break;
|
||||||
case Target::Model::IIplus:
|
case Target::Model::IIplus:
|
||||||
rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::II));
|
character = ROM::Name::AppleIICharacter;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26);
|
system = ROM::Name::AppleIIPlus;
|
||||||
break;
|
break;
|
||||||
case Target::Model::IIe:
|
case Target::Model::IIe:
|
||||||
rom_size += 3840;
|
character = ROM::Name::AppleIIeCharacter;
|
||||||
rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::IIe));
|
system = ROM::Name::AppleIIe;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18d);
|
|
||||||
break;
|
break;
|
||||||
case Target::Model::EnhancedIIe:
|
case Target::Model::EnhancedIIe:
|
||||||
rom_size += 3840;
|
character = ROM::Name::AppleIIEnhancedECharacter;
|
||||||
rom_descriptions.push_back(video_.rom_description(Video::VideoBase::CharacterROM::EnhancedIIe));
|
system = ROM::Name::AppleIIEnhancedE;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const auto roms = rom_fetcher(rom_descriptions);
|
|
||||||
|
|
||||||
// Try to install a Disk II card now, before checking the ROM list,
|
ROM::Request request = ROM::Request(character) && ROM::Request(system);
|
||||||
// to make sure that Disk II dependencies have been communicated.
|
|
||||||
if(target.disk_controller != Target::DiskController::None) {
|
// Add the necessary Disk II requests if appropriate.
|
||||||
|
const bool has_disk_controller = target.disk_controller != Target::DiskController::None;
|
||||||
|
const bool is_sixteen_sector = target.disk_controller == Target::DiskController::SixteenSector;
|
||||||
|
if(has_disk_controller) {
|
||||||
// Apple recommended slot 6 for the (first) Disk II.
|
// Apple recommended slot 6 for the (first) Disk II.
|
||||||
install_card(6, new Apple::II::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector));
|
request = request && DiskIICard::rom_request(is_sixteen_sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, check and move the ROMs.
|
// Request, validate and install ROMs.
|
||||||
if(!roms[0] || !roms[1]) {
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_ = std::move(*roms[1]);
|
if(has_disk_controller) {
|
||||||
if(rom_.size() > rom_size) {
|
install_card(6, new Apple::II::DiskIICard(roms, is_sixteen_sector));
|
||||||
rom_.erase(rom_.begin(), rom_.end() - off_t(rom_size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
video_.set_character_rom(*roms[0]);
|
rom_ = std::move(roms.find(system)->second);
|
||||||
|
// The IIe and Enhanced IIe ROMs often distributed are oversized; trim if necessary.
|
||||||
|
if(system == ROM::Name::AppleIIe || system == ROM::Name::AppleIIEnhancedE) {
|
||||||
|
if(rom_.size() > 16128) {
|
||||||
|
rom_.erase(rom_.begin(), rom_.end() - off_t(16128));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
video_.set_character_rom(roms.find(character)->second);
|
||||||
|
|
||||||
// Set up the default memory blocks. On a II or II+ these values will never change.
|
// Set up the default memory blocks. On a II or II+ these values will never change.
|
||||||
// On a IIe they'll be affected by selection of auxiliary RAM.
|
// On a IIe they'll be affected by selection of auxiliary RAM.
|
||||||
@ -781,12 +787,14 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||||
options->output = get_video_signal_configurable();
|
options->output = get_video_signal_configurable();
|
||||||
|
options->use_square_pixels = video_.get_use_square_pixels();
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
|
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
|
||||||
const auto options = dynamic_cast<Options *>(str.get());
|
const auto options = dynamic_cast<Options *>(str.get());
|
||||||
set_video_signal_configurable(options->output);
|
set_video_signal_configurable(options->output);
|
||||||
|
video_.set_use_square_pixels(options->use_square_pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: MediaTarget
|
// MARK: MediaTarget
|
||||||
|
@ -30,8 +30,11 @@ class Machine {
|
|||||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||||
friend Configurable::DisplayOption<Options>;
|
friend Configurable::DisplayOption<Options>;
|
||||||
public:
|
public:
|
||||||
|
bool use_square_pixels = false;
|
||||||
|
|
||||||
Options(Configurable::OptionsType) : Configurable::DisplayOption<Options>(Configurable::Display::CompositeColour) {
|
Options(Configurable::OptionsType) : Configurable::DisplayOption<Options>(Configurable::Display::CompositeColour) {
|
||||||
if(needs_declare()) {
|
if(needs_declare()) {
|
||||||
|
DeclareField(use_square_pixels);
|
||||||
declare_display_option();
|
declare_display_option();
|
||||||
limit_enum(&output, Configurable::Display::CompositeMonochrome, Configurable::Display::CompositeColour, -1);
|
limit_enum(&output, Configurable::Display::CompositeMonochrome, Configurable::Display::CompositeColour, -1);
|
||||||
}
|
}
|
||||||
|
@ -10,27 +10,34 @@
|
|||||||
|
|
||||||
using namespace Apple::II;
|
using namespace Apple::II;
|
||||||
|
|
||||||
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) {
|
ROM::Request DiskIICard::rom_request(bool is_16_sector) {
|
||||||
std::vector<std::unique_ptr<std::vector<uint8_t>>> roms;
|
|
||||||
if(is_16_sector) {
|
if(is_16_sector) {
|
||||||
roms = rom_fetcher({
|
return ROM::Request(ROM::Name::DiskIIBoot16Sector) && ROM::Request(ROM::Name::DiskIIStateMachine16Sector);
|
||||||
{"DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6},
|
|
||||||
{"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } }
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
roms = rom_fetcher({
|
|
||||||
{"DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ff},
|
|
||||||
{"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 } }
|
|
||||||
// {"DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620 }
|
|
||||||
/* TODO: once the DiskII knows how to decode common images of the 13-sector state machine, use that instead of the 16-sector. */
|
/* TODO: once the DiskII knows how to decode common images of the 13-sector state machine, use that instead of the 16-sector. */
|
||||||
});
|
return ROM::Request(ROM::Name::DiskIIBoot13Sector) && ROM::Request(ROM::Name::DiskIIStateMachine16Sector);
|
||||||
}
|
}
|
||||||
if(!roms[0] || !roms[1]) {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DiskIICard::DiskIICard(ROM::Map &map, bool is_16_sector) : diskii_(2045454) {
|
||||||
|
std::vector<std::unique_ptr<std::vector<uint8_t>>> roms;
|
||||||
|
ROM::Map::iterator state_machine, boot;
|
||||||
|
if(is_16_sector) {
|
||||||
|
state_machine = map.find(ROM::Name::DiskIIStateMachine16Sector);
|
||||||
|
boot = map.find(ROM::Name::DiskIIBoot16Sector);
|
||||||
|
} else {
|
||||||
|
// TODO: see above re: 13-sector state machine.
|
||||||
|
state_machine = map.find(ROM::Name::DiskIIStateMachine16Sector);
|
||||||
|
boot = map.find(ROM::Name::DiskIIBoot13Sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state_machine == map.end() || boot == map.end()) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
boot_ = std::move(*roms[0]);
|
boot_ = std::move(boot->second);
|
||||||
diskii_.set_state_machine(*roms[1]);
|
diskii_.set_state_machine(state_machine->second);
|
||||||
set_select_constraints(None);
|
set_select_constraints(None);
|
||||||
diskii_.set_clocking_hint_observer(this);
|
diskii_.set_clocking_hint_observer(this);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ namespace II {
|
|||||||
|
|
||||||
class DiskIICard: public Card, public ClockingHint::Observer {
|
class DiskIICard: public Card, public ClockingHint::Observer {
|
||||||
public:
|
public:
|
||||||
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
static ROM::Request rom_request(bool is_16_sector);
|
||||||
|
DiskIICard(ROM::Map &, bool is_16_sector);
|
||||||
|
|
||||||
void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final;
|
void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final;
|
||||||
void run_for(Cycles cycles, int stretches) final;
|
void run_for(Cycles cycles, int stretches) final;
|
||||||
|
@ -15,9 +15,8 @@ VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
|
|||||||
crt_(910, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance1),
|
crt_(910, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance1),
|
||||||
is_iie_(is_iie) {
|
is_iie_(is_iie) {
|
||||||
|
|
||||||
// Show only the centre 75% of the TV frame.
|
|
||||||
crt_.set_display_type(Outputs::Display::DisplayType::CompositeColour);
|
crt_.set_display_type(Outputs::Display::DisplayType::CompositeColour);
|
||||||
crt_.set_visible_area(Outputs::Display::Rect(0.118f, 0.122f, 0.77f, 0.77f));
|
set_use_square_pixels(use_square_pixels_);
|
||||||
|
|
||||||
// TODO: there seems to be some sort of bug whereby switching modes can cause
|
// TODO: there seems to be some sort of bug whereby switching modes can cause
|
||||||
// a signal discontinuity that knocks phase out of whack. So it isn't safe to
|
// a signal discontinuity that knocks phase out of whack. So it isn't safe to
|
||||||
@ -26,6 +25,41 @@ VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
|
|||||||
// crt_.set_immediate_default_phase(0.5f);
|
// crt_.set_immediate_default_phase(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoBase::set_use_square_pixels(bool use_square_pixels) {
|
||||||
|
use_square_pixels_ = use_square_pixels;
|
||||||
|
|
||||||
|
// HYPER-UGLY HACK. See correlated hack in the Macintosh.
|
||||||
|
#ifdef __APPLE__
|
||||||
|
crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.122f, 0.75f, 0.77f));
|
||||||
|
#else
|
||||||
|
if(use_square_pixels) {
|
||||||
|
crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.112f, 0.75f, 0.73f));
|
||||||
|
} else {
|
||||||
|
crt_.set_visible_area(Outputs::Display::Rect(0.128f, 0.12f, 0.75f, 0.77f));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(use_square_pixels) {
|
||||||
|
// From what I can make out, many contemporary Apple II monitors were
|
||||||
|
// calibrated slightly to stretch the Apple II's display slightly wider
|
||||||
|
// than it should be per the NTSC standards, for approximately square
|
||||||
|
// pixels. This reproduces that.
|
||||||
|
|
||||||
|
// 243 lines and 52µs are visible.
|
||||||
|
// i.e. to be square, 1 pixel should be: (1/243 * 52) * (3/4) = 156/972 = 39/243 µs
|
||||||
|
// On an Apple II each pixel is actually 1/7µs.
|
||||||
|
// Therefore the adjusted aspect ratio should be (4/3) * (39/243)/(1/7) = (4/3) * 273/243 = 1092/729 = 343/243 ~= 1.412
|
||||||
|
crt_.set_aspect_ratio(343.0f / 243.0f);
|
||||||
|
} else {
|
||||||
|
// Standard NTSC aspect ratio.
|
||||||
|
crt_.set_aspect_ratio(4.0f / 3.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool VideoBase::get_use_square_pixels() {
|
||||||
|
return use_square_pixels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||||
crt_.set_scan_target(scan_target);
|
crt_.set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,14 @@ class VideoBase: public VideoSwitches<Cycles> {
|
|||||||
/// Gets the type of output.
|
/// Gets the type of output.
|
||||||
Outputs::Display::DisplayType get_display_type() const;
|
Outputs::Display::DisplayType get_display_type() const;
|
||||||
|
|
||||||
|
/// Sets whether the current CRT should be recalibrated away from normative NTSC
|
||||||
|
/// to produce square pixels in 40-column text mode.
|
||||||
|
void set_use_square_pixels(bool);
|
||||||
|
bool get_use_square_pixels();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Outputs::CRT::CRT crt_;
|
Outputs::CRT::CRT crt_;
|
||||||
|
bool use_square_pixels_ = false;
|
||||||
|
|
||||||
// State affecting output video stream generation.
|
// State affecting output video stream generation.
|
||||||
uint8_t *pixel_pointer_ = nullptr;
|
uint8_t *pixel_pointer_ = nullptr;
|
||||||
|
@ -228,36 +228,6 @@ template <typename TimeUnit> class VideoSwitches {
|
|||||||
return external_.annunciator_3;
|
return external_.annunciator_3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CharacterROM {
|
|
||||||
/// The ROM that shipped with both the Apple II and the II+.
|
|
||||||
II,
|
|
||||||
/// The ROM that shipped with the original IIe.
|
|
||||||
IIe,
|
|
||||||
/// The ROM that shipped with the Enhanced IIe.
|
|
||||||
EnhancedIIe,
|
|
||||||
/// The ROM that shipped with the IIgs.
|
|
||||||
IIgs
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @returns A file-level description of @c rom.
|
|
||||||
static ROMMachine::ROM rom_description(CharacterROM rom) {
|
|
||||||
const std::string machine_name = "AppleII";
|
|
||||||
switch(rom) {
|
|
||||||
case CharacterROM::II:
|
|
||||||
return ROMMachine::ROM(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6);
|
|
||||||
|
|
||||||
case CharacterROM::IIe:
|
|
||||||
return ROMMachine::ROM(machine_name, "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1);
|
|
||||||
|
|
||||||
default: // To appease GCC.
|
|
||||||
case CharacterROM::EnhancedIIe:
|
|
||||||
return ROMMachine::ROM(machine_name, "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014d);
|
|
||||||
|
|
||||||
case CharacterROM::IIgs:
|
|
||||||
return ROMMachine::ROM(machine_name, "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the character ROM for this video output.
|
/// Set the character ROM for this video output.
|
||||||
void set_character_rom(const std::vector<uint8_t> &rom) {
|
void set_character_rom(const std::vector<uint8_t> &rom) {
|
||||||
character_rom_ = rom;
|
character_rom_ = rom;
|
||||||
|
@ -189,31 +189,23 @@ class ConcreteMachine:
|
|||||||
speaker_.set_input_rate(float(CLOCK_RATE) / float(audio_divider));
|
speaker_.set_input_rate(float(CLOCK_RATE) / float(audio_divider));
|
||||||
|
|
||||||
using Target = Analyser::Static::AppleIIgs::Target;
|
using Target = Analyser::Static::AppleIIgs::Target;
|
||||||
std::vector<ROMMachine::ROM> rom_descriptions;
|
ROM::Name system;
|
||||||
const std::string machine_name = "AppleIIgs";
|
|
||||||
switch(target.model) {
|
switch(target.model) {
|
||||||
case Target::Model::ROM00:
|
case Target::Model::ROM00: system = ROM::Name::AppleIIgsROM00; break;
|
||||||
/* TODO */
|
case Target::Model::ROM01: system = ROM::Name::AppleIIgsROM01; break;
|
||||||
case Target::Model::ROM01:
|
default: system = ROM::Name::AppleIIgsROM03; break;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Target::Model::ROM03:
|
|
||||||
rom_descriptions.emplace_back(machine_name, "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
rom_descriptions.push_back(video_->rom_description(Video::Video::CharacterROM::EnhancedIIe));
|
constexpr ROM::Name characters = ROM::Name::AppleIIEnhancedECharacter;
|
||||||
|
constexpr ROM::Name microcontroller = ROM::Name::AppleIIgsMicrocontrollerROM03;
|
||||||
|
|
||||||
// TODO: pick a different ADB ROM for earlier machine revisions?
|
ROM::Request request = ROM::Request(system) && ROM::Request(characters) && ROM::Request(microcontroller);
|
||||||
rom_descriptions.emplace_back(machine_name, "the Apple IIgs ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0);
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
const auto roms = rom_fetcher(rom_descriptions);
|
|
||||||
if(!roms[0] || !roms[1] || !roms[2]) {
|
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
rom_ = *roms[0];
|
rom_ = roms.find(system)->second;
|
||||||
video_->set_character_rom(*roms[1]);
|
video_->set_character_rom(roms.find(characters)->second);
|
||||||
adb_glu_->set_microcontroller_rom(*roms[2]);
|
adb_glu_->set_microcontroller_rom(roms.find(microcontroller)->second);
|
||||||
|
|
||||||
// Run only the currently-interesting self test.
|
// Run only the currently-interesting self test.
|
||||||
// rom_[0x36402] = 2;
|
// rom_[0x36402] = 2;
|
||||||
|
@ -96,25 +96,24 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
using Model = Analyser::Static::Macintosh::Target::Model;
|
using Model = Analyser::Static::Macintosh::Target::Model;
|
||||||
const std::string machine_name = "Macintosh";
|
const std::string machine_name = "Macintosh";
|
||||||
uint32_t ram_size, rom_size;
|
uint32_t ram_size, rom_size;
|
||||||
std::vector<ROMMachine::ROM> rom_descriptions;
|
ROM::Name rom_name;
|
||||||
switch(model) {
|
switch(model) {
|
||||||
default:
|
default:
|
||||||
case Model::Mac128k:
|
case Model::Mac128k:
|
||||||
ram_size = 128*1024;
|
ram_size = 128*1024;
|
||||||
rom_size = 64*1024;
|
rom_size = 64*1024;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28);
|
rom_name = ROM::Name::Macintosh128k;
|
||||||
break;
|
break;
|
||||||
case Model::Mac512k:
|
case Model::Mac512k:
|
||||||
ram_size = 512*1024;
|
ram_size = 512*1024;
|
||||||
rom_size = 64*1024;
|
rom_size = 64*1024;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d);
|
rom_name = ROM::Name::Macintosh512k;
|
||||||
break;
|
break;
|
||||||
case Model::Mac512ke:
|
case Model::Mac512ke:
|
||||||
case Model::MacPlus: {
|
case Model::MacPlus: {
|
||||||
ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024;
|
ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024;
|
||||||
rom_size = 128*1024;
|
rom_size = 128*1024;
|
||||||
const std::initializer_list<uint32_t> crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e };
|
rom_name = ROM::Name::MacintoshPlus;
|
||||||
rom_descriptions.emplace_back(machine_name, "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s);
|
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
ram_mask_ = ram_size - 1;
|
ram_mask_ = ram_size - 1;
|
||||||
@ -123,12 +122,12 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
video_.set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_mask_ >> 1);
|
video_.set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_mask_ >> 1);
|
||||||
|
|
||||||
// Grab a copy of the ROM and convert it into big-endian data.
|
// Grab a copy of the ROM and convert it into big-endian data.
|
||||||
const auto roms = rom_fetcher(rom_descriptions);
|
ROM::Request request(rom_name);
|
||||||
if(!roms[0]) {
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
roms[0]->resize(rom_size);
|
Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
|
||||||
Memory::PackBigEndian16(*roms[0], rom_);
|
|
||||||
|
|
||||||
// Randomise memory contents.
|
// Randomise memory contents.
|
||||||
Memory::Fuzz(ram_);
|
Memory::Fuzz(ram_);
|
||||||
|
@ -74,15 +74,13 @@ class ConcreteMachine:
|
|||||||
video_->set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size());
|
video_->set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size());
|
||||||
Memory::Fuzz(ram_);
|
Memory::Fuzz(ram_);
|
||||||
|
|
||||||
std::vector<ROMMachine::ROM> rom_descriptions = {
|
constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100;
|
||||||
{"AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64}
|
ROM::Request request(rom_name);
|
||||||
// {"AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43}
|
auto roms = rom_fetcher(request);
|
||||||
};
|
if(!request.validate(roms)) {
|
||||||
const auto roms = rom_fetcher(rom_descriptions);
|
|
||||||
if(!roms[0]) {
|
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
Memory::PackBigEndian16(*roms[0], rom_);
|
Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
|
||||||
|
|
||||||
// Set up basic memory map.
|
// Set up basic memory map.
|
||||||
memory_map_[0] = BusDevice::MostlyRAM;
|
memory_map_[0] = BusDevice::MostlyRAM;
|
||||||
|
@ -127,15 +127,13 @@ class ConcreteMachine:
|
|||||||
joysticks_.emplace_back(new Joystick);
|
joysticks_.emplace_back(new Joystick);
|
||||||
joysticks_.emplace_back(new Joystick);
|
joysticks_.emplace_back(new Joystick);
|
||||||
|
|
||||||
const auto roms = rom_fetcher(
|
constexpr ROM::Name rom_name = ROM::Name::ColecoVisionBIOS;
|
||||||
{ {"ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3} });
|
const ROM::Request request(rom_name);
|
||||||
|
auto roms = rom_fetcher(request);
|
||||||
if(!roms[0]) {
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
|
bios_ = roms.find(rom_name)->second;
|
||||||
bios_ = *roms[0];
|
|
||||||
bios_.resize(8192);
|
|
||||||
|
|
||||||
if(!target.media.cartridges.empty()) {
|
if(!target.media.cartridges.empty()) {
|
||||||
const auto &segment = target.media.cartridges.front()->get_segments().front();
|
const auto &segment = target.media.cartridges.front()->get_segments().front();
|
||||||
|
@ -41,7 +41,8 @@ namespace C1540 {
|
|||||||
*/
|
*/
|
||||||
class Machine final: public MachineBase {
|
class Machine final: public MachineBase {
|
||||||
public:
|
public:
|
||||||
Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher);
|
static ROM::Request rom_request(Personality personality);
|
||||||
|
Machine(Personality personality, const ROM::Map &roms);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the serial bus to which this drive should attach itself.
|
Sets the serial bus to which this drive should attach itself.
|
||||||
|
@ -16,7 +16,15 @@
|
|||||||
|
|
||||||
using namespace Commodore::C1540;
|
using namespace Commodore::C1540;
|
||||||
|
|
||||||
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ROM::Request Machine::rom_request(Personality personality) {
|
||||||
|
switch(personality) {
|
||||||
|
default:
|
||||||
|
case Personality::C1540: return ROM::Request(ROM::Name::Commodore1540);
|
||||||
|
case Personality::C1541: return ROM::Request(ROM::Name::Commodore1541);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineBase::MachineBase(Personality personality, const ROM::Map &roms) :
|
||||||
Storage::Disk::Controller(1000000),
|
Storage::Disk::Controller(1000000),
|
||||||
m6502_(*this),
|
m6502_(*this),
|
||||||
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
|
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
|
||||||
@ -39,28 +47,22 @@ MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &
|
|||||||
emplace_drive(1000000, 300, 2);
|
emplace_drive(1000000, 300, 2);
|
||||||
set_drive(1);
|
set_drive(1);
|
||||||
|
|
||||||
std::string device_name;
|
ROM::Name rom_name;
|
||||||
uint32_t crc = 0;
|
|
||||||
switch(personality) {
|
switch(personality) {
|
||||||
case Personality::C1540:
|
default:
|
||||||
device_name = "1540";
|
case Personality::C1540: rom_name = ROM::Name::Commodore1540; break;
|
||||||
crc = 0x718d42b1;
|
case Personality::C1541: rom_name = ROM::Name::Commodore1541; break;
|
||||||
break;
|
|
||||||
case Personality::C1541:
|
|
||||||
device_name = "1541";
|
|
||||||
crc = 0xfb760019;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto roms = rom_fetcher({ {"Commodore1540", "the " + device_name + " ROM", device_name + ".bin", 16*1024, crc} });
|
auto rom = roms.find(rom_name);
|
||||||
if(!roms[0]) {
|
if(rom == roms.end()) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
std::memcpy(rom_, roms[0]->data(), std::min(sizeof(rom_), roms[0]->size()));
|
std::memcpy(rom_, roms.find(rom_name)->second.data(), std::min(sizeof(rom_), roms.find(rom_name)->second.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::Machine(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
|
Machine::Machine(Personality personality, const ROM::Map &roms) :
|
||||||
MachineBase(personality, rom_fetcher) {}
|
MachineBase(personality, roms) {}
|
||||||
|
|
||||||
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) {
|
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) {
|
||||||
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
|
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
|
||||||
|
@ -127,7 +127,7 @@ class MachineBase:
|
|||||||
public Storage::Disk::Controller {
|
public Storage::Disk::Controller {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher);
|
MachineBase(Personality personality, const ROM::Map &roms);
|
||||||
|
|
||||||
// to satisfy CPU::MOS6502::Processor
|
// to satisfy CPU::MOS6502::Processor
|
||||||
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
|
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
|
||||||
|
@ -316,53 +316,48 @@ class ConcreteMachine:
|
|||||||
// Install a joystick.
|
// Install a joystick.
|
||||||
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
|
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
|
||||||
|
|
||||||
const std::string machine_name = "Vic20";
|
ROM::Request request(ROM::Name::Vic20BASIC);
|
||||||
std::vector<ROMMachine::ROM> rom_names = {
|
ROM::Name kernel, character;
|
||||||
{machine_name, "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1}
|
|
||||||
};
|
|
||||||
switch(target.region) {
|
switch(target.region) {
|
||||||
default:
|
default:
|
||||||
rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6);
|
character = ROM::Name::Vic20EnglishCharacters;
|
||||||
rom_names.emplace_back(machine_name, "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4);
|
kernel = ROM::Name::Vic20EnglishPALKernel;
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::Commodore::Target::Region::American:
|
case Analyser::Static::Commodore::Target::Region::American:
|
||||||
rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6);
|
character = ROM::Name::Vic20EnglishCharacters;
|
||||||
rom_names.emplace_back(machine_name, "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174);
|
kernel = ROM::Name::Vic20EnglishNTSCKernel;
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::Commodore::Target::Region::Danish:
|
case Analyser::Static::Commodore::Target::Region::Danish:
|
||||||
rom_names.emplace_back(machine_name, "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454);
|
character = ROM::Name::Vic20DanishCharacters;
|
||||||
rom_names.emplace_back(machine_name, "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16);
|
kernel = ROM::Name::Vic20DanishKernel;
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::Commodore::Target::Region::Japanese:
|
case Analyser::Static::Commodore::Target::Region::Japanese:
|
||||||
rom_names.emplace_back(machine_name, "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4b);
|
character = ROM::Name::Vic20JapaneseCharacters;
|
||||||
rom_names.emplace_back(machine_name, "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7);
|
kernel = ROM::Name::Vic20JapaneseKernel;
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::Commodore::Target::Region::Swedish:
|
case Analyser::Static::Commodore::Target::Region::Swedish:
|
||||||
rom_names.emplace_back(machine_name, "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551d);
|
character = ROM::Name::Vic20SwedishCharacters;
|
||||||
rom_names.emplace_back(machine_name, "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662);
|
kernel = ROM::Name::Vic20SwedishKernel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto roms = rom_fetcher(rom_names);
|
if(target.has_c1540) {
|
||||||
|
request = request && Commodore::C1540::Machine::rom_request(Commodore::C1540::Personality::C1540);
|
||||||
|
}
|
||||||
|
request = request && ROM::Request(character) && ROM::Request(kernel);
|
||||||
|
|
||||||
for(const auto &rom: roms) {
|
auto roms = rom_fetcher(request);
|
||||||
if(!rom) {
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
basic_rom_ = std::move(*roms[0]);
|
basic_rom_ = std::move(roms.find(ROM::Name::Vic20BASIC)->second);
|
||||||
character_rom_ = std::move(*roms[1]);
|
character_rom_ = std::move(roms.find(character)->second);
|
||||||
kernel_rom_ = std::move(*roms[2]);
|
kernel_rom_ = std::move(roms.find(kernel)->second);
|
||||||
|
|
||||||
// Characters ROMs should be 4kb.
|
|
||||||
character_rom_.resize(4096);
|
|
||||||
// Kernel ROMs and the BASIC ROM should be 8kb.
|
|
||||||
kernel_rom_.resize(8192);
|
|
||||||
|
|
||||||
if(target.has_c1540) {
|
if(target.has_c1540) {
|
||||||
// construct the 1540
|
// construct the 1540
|
||||||
c1540_ = std::make_unique<::Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, rom_fetcher);
|
c1540_ = std::make_unique<::Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, roms);
|
||||||
|
|
||||||
// attach it to the serial bus
|
// attach it to the serial bus
|
||||||
c1540_->set_serial_bus(serial_bus_);
|
c1540_->set_serial_bus(serial_bus_);
|
||||||
|
@ -69,37 +69,25 @@ template <bool has_scsi_bus> class ConcreteMachine:
|
|||||||
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
|
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
|
||||||
speaker_.set_high_frequency_cutoff(6000);
|
speaker_.set_high_frequency_cutoff(6000);
|
||||||
|
|
||||||
const std::string machine_name = "Electron";
|
::ROM::Request request = ::ROM::Request(::ROM::Name::AcornBASICII) && ::ROM::Request(::ROM::Name::AcornElectronMOS100);
|
||||||
std::vector<ROMMachine::ROM> required_roms = {
|
|
||||||
{machine_name, "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781},
|
|
||||||
{machine_name, "the Electron MOS ROM", "os.rom", 16*1024, 0xbf63fb1f}
|
|
||||||
};
|
|
||||||
const size_t pres_adfs_rom_position = required_roms.size();
|
|
||||||
if(target.has_pres_adfs) {
|
if(target.has_pres_adfs) {
|
||||||
required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993);
|
request = request && ::ROM::Request(::ROM::Name::PRESADFSSlot1) && ::ROM::Request(::ROM::Name::PRESADFSSlot2);
|
||||||
required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e);
|
|
||||||
}
|
}
|
||||||
const size_t acorn_adfs_rom_position = required_roms.size();
|
|
||||||
if(target.has_acorn_adfs) {
|
if(target.has_acorn_adfs) {
|
||||||
required_roms.emplace_back(machine_name, "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6);
|
request = request && ::ROM::Request(::ROM::Name::AcornADFS);
|
||||||
}
|
}
|
||||||
const size_t dfs_rom_position = required_roms.size();
|
|
||||||
if(target.has_dfs) {
|
if(target.has_dfs) {
|
||||||
required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5);
|
request = request && ::ROM::Request(::ROM::Name::Acorn1770DFS);
|
||||||
}
|
}
|
||||||
const size_t ap6_rom_position = required_roms.size();
|
|
||||||
if(target.has_ap6_rom) {
|
if(target.has_ap6_rom) {
|
||||||
required_roms.emplace_back(machine_name, "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfc);
|
request = request && ::ROM::Request(::ROM::Name::PRESAdvancedPlus6);
|
||||||
}
|
}
|
||||||
const auto roms = rom_fetcher(required_roms);
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
for(const auto &rom: roms) {
|
|
||||||
if(!rom) {
|
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
}
|
set_rom(ROM::BASIC, roms.find(::ROM::Name::AcornBASICII)->second, false);
|
||||||
set_rom(ROM::BASIC, *roms[0], false);
|
set_rom(ROM::OS, roms.find(::ROM::Name::AcornElectronMOS100)->second, false);
|
||||||
set_rom(ROM::OS, *roms[1], false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ROM slot mapping applied:
|
ROM slot mapping applied:
|
||||||
@ -115,19 +103,18 @@ template <bool has_scsi_bus> class ConcreteMachine:
|
|||||||
plus3_ = std::make_unique<Plus3>();
|
plus3_ = std::make_unique<Plus3>();
|
||||||
|
|
||||||
if(target.has_dfs) {
|
if(target.has_dfs) {
|
||||||
set_rom(ROM::Slot0, *roms[dfs_rom_position], true);
|
set_rom(ROM::Slot0, roms.find(::ROM::Name::Acorn1770DFS)->second, true);
|
||||||
}
|
}
|
||||||
if(target.has_pres_adfs) {
|
if(target.has_pres_adfs) {
|
||||||
set_rom(ROM::Slot4, *roms[pres_adfs_rom_position], true);
|
set_rom(ROM::Slot4, roms.find(::ROM::Name::PRESADFSSlot1)->second, true);
|
||||||
set_rom(ROM::Slot5, *roms[pres_adfs_rom_position+1], true);
|
set_rom(ROM::Slot5, roms.find(::ROM::Name::PRESADFSSlot2)->second, true);
|
||||||
}
|
}
|
||||||
if(target.has_acorn_adfs) {
|
if(target.has_acorn_adfs) {
|
||||||
set_rom(ROM::Slot6, *roms[acorn_adfs_rom_position], true);
|
set_rom(ROM::Slot6, roms.find(::ROM::Name::AcornADFS)->second, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(target.has_ap6_rom) {
|
if(target.has_ap6_rom) {
|
||||||
set_rom(ROM::Slot15, *roms[ap6_rom_position], true);
|
set_rom(ROM::Slot15, roms.find(::ROM::Name::PRESAdvancedPlus6)->second, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(target.has_sideways_ram) {
|
if(target.has_sideways_ram) {
|
||||||
|
@ -169,19 +169,22 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Install the proper TV standard and select an ideal BIOS name.
|
// Install the proper TV standard and select an ideal BIOS name.
|
||||||
const std::string machine_name = "MSX";
|
const std::string machine_name = "MSX";
|
||||||
std::vector<ROMMachine::ROM> required_roms = {
|
ROM::Request bios_request = ROM::Request(ROM::Name::MSXGenericBIOS);
|
||||||
{machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3}
|
// std::vector<ROMMachine::ROM> required_roms = {
|
||||||
};
|
// {machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u}
|
||||||
|
// };
|
||||||
|
|
||||||
bool is_ntsc = true;
|
bool is_ntsc = true;
|
||||||
uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */
|
uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */
|
||||||
uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */
|
uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */
|
||||||
uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */
|
uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */
|
||||||
|
ROM::Name regional_bios_name;
|
||||||
|
|
||||||
// TODO: CRCs below are incomplete, at best.
|
// TODO: CRCs below are incomplete, at best.
|
||||||
switch(target.region) {
|
switch(target.region) {
|
||||||
|
default:
|
||||||
case Target::Region::Japan:
|
case Target::Region::Japan:
|
||||||
required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390);
|
regional_bios_name = ROM::Name::MSXJapaneseBIOS;
|
||||||
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
||||||
|
|
||||||
is_ntsc = true;
|
is_ntsc = true;
|
||||||
@ -189,7 +192,7 @@ class ConcreteMachine:
|
|||||||
date_format = 0;
|
date_format = 0;
|
||||||
break;
|
break;
|
||||||
case Target::Region::USA:
|
case Target::Region::USA:
|
||||||
required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0);
|
regional_bios_name = ROM::Name::MSXAmericanBIOS;
|
||||||
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
||||||
|
|
||||||
is_ntsc = true;
|
is_ntsc = true;
|
||||||
@ -197,7 +200,7 @@ class ConcreteMachine:
|
|||||||
date_format = 1;
|
date_format = 1;
|
||||||
break;
|
break;
|
||||||
case Target::Region::Europe:
|
case Target::Region::Europe:
|
||||||
required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0);
|
regional_bios_name = ROM::Name::MSXEuropeanBIOS;
|
||||||
vdp_->set_tv_standard(TI::TMS::TVStandard::PAL);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::PAL);
|
||||||
|
|
||||||
is_ntsc = false;
|
is_ntsc = false;
|
||||||
@ -205,27 +208,30 @@ class ConcreteMachine:
|
|||||||
date_format = 2;
|
date_format = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
bios_request = bios_request || ROM::Request(regional_bios_name);
|
||||||
|
|
||||||
// Fetch the necessary ROMs; try the region-specific ROM first,
|
// Fetch the necessary ROMs; try the region-specific ROM first,
|
||||||
// but failing that fall back on patching the main one.
|
// but failing that fall back on patching the main one.
|
||||||
size_t disk_index = 0;
|
ROM::Request request;
|
||||||
if(target.has_disk_drive) {
|
if(target.has_disk_drive) {
|
||||||
disk_index = required_roms.size();
|
request = ROM::Request(ROM::Name::MSXDOS) && bios_request;
|
||||||
required_roms.emplace_back(machine_name, "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61df);
|
} else {
|
||||||
|
request = bios_request;
|
||||||
}
|
}
|
||||||
const auto roms = rom_fetcher(required_roms);
|
|
||||||
|
|
||||||
if((!roms[0] && !roms[1]) || (target.has_disk_drive && !roms[2])) {
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out which BIOS to use, either a specific one or the generic
|
// Figure out which BIOS to use, either a specific one or the generic
|
||||||
// one appropriately patched.
|
// one appropriately patched.
|
||||||
if(roms[1]) {
|
const auto regional_bios = roms.find(regional_bios_name);
|
||||||
memory_slots_[0].source = std::move(*roms[1]);
|
if(regional_bios != roms.end()) {
|
||||||
|
memory_slots_[0].source = std::move(regional_bios->second);
|
||||||
memory_slots_[0].source.resize(32768);
|
memory_slots_[0].source.resize(32768);
|
||||||
} else {
|
} else {
|
||||||
memory_slots_[0].source = std::move(*roms[0]);
|
memory_slots_[0].source = std::move(roms.find(ROM::Name::MSXGenericBIOS)->second);
|
||||||
memory_slots_[0].source.resize(32768);
|
memory_slots_[0].source.resize(32768);
|
||||||
|
|
||||||
memory_slots_[0].source[0x2b] = uint8_t(
|
memory_slots_[0].source[0x2b] = uint8_t(
|
||||||
@ -252,7 +258,7 @@ class ConcreteMachine:
|
|||||||
// Add a disk cartridge if any disks were supplied.
|
// Add a disk cartridge if any disks were supplied.
|
||||||
if(target.has_disk_drive) {
|
if(target.has_disk_drive) {
|
||||||
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
|
memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source));
|
||||||
memory_slots_[2].source = std::move(*roms[disk_index]);
|
memory_slots_[2].source = std::move(roms.find(ROM::Name::MSXDOS)->second);
|
||||||
memory_slots_[2].source.resize(16384);
|
memory_slots_[2].source.resize(16384);
|
||||||
|
|
||||||
map(2, 0, 0x4000, 0x2000);
|
map(2, 0, 0x4000, 0x2000);
|
||||||
|
@ -141,23 +141,20 @@ class ConcreteMachine:
|
|||||||
// 0072ed54 = US/European BIOS 1.3
|
// 0072ed54 = US/European BIOS 1.3
|
||||||
// 48d44a13 = Japanese BIOS 2.1
|
// 48d44a13 = Japanese BIOS 2.1
|
||||||
const bool is_japanese = target.region == Target::Region::Japan;
|
const bool is_japanese = target.region == Target::Region::Japan;
|
||||||
const auto roms = rom_fetcher(
|
const ROM::Name bios_name = is_japanese ? ROM::Name::MasterSystemJapaneseBIOS : ROM::Name::MasterSystemWesternBIOS;
|
||||||
{ {"MasterSystem",
|
ROM::Request request(bios_name, true);
|
||||||
is_japanese ? "the Japanese Master System BIOS" : "the European/US Master System BIOS",
|
auto roms = rom_fetcher(request);
|
||||||
is_japanese ? "japanese-bios.sms" : "bios.sms",
|
request.validate(roms);
|
||||||
8*1024,
|
|
||||||
{ is_japanese ? 0x48d44a13u : 0x0072ed54u }
|
const auto rom = roms.find(bios_name);
|
||||||
} }
|
if(rom == roms.end()) {
|
||||||
);
|
|
||||||
if(!roms[0]) {
|
|
||||||
// No BIOS found; attempt to boot as though it has already disabled itself.
|
// No BIOS found; attempt to boot as though it has already disabled itself.
|
||||||
has_bios_ = false;
|
has_bios_ = false;
|
||||||
memory_control_ |= 0x08;
|
memory_control_ |= 0x08;
|
||||||
std::cerr << "No BIOS found; attempting to start cartridge directly" << std::endl;
|
std::cerr << "No BIOS found; attempting to start cartridge directly" << std::endl;
|
||||||
} else {
|
} else {
|
||||||
has_bios_ = true;
|
has_bios_ = true;
|
||||||
roms[0]->resize(8*1024);
|
memcpy(&bios_, rom->second.data(), std::min(sizeof(bios_), rom->second.size()));
|
||||||
memcpy(&bios_, roms[0]->data(), roms[0]->size());
|
|
||||||
}
|
}
|
||||||
page_cartridge();
|
page_cartridge();
|
||||||
|
|
||||||
|
@ -304,73 +304,64 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS
|
|||||||
ram_[c] |= 0x40;
|
ram_[c] |= 0x40;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string machine_name = "Oric";
|
::ROM::Request request = ::ROM::Request(::ROM::Name::OricColourROM, true);
|
||||||
std::vector<ROMMachine::ROM> rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65} };
|
::ROM::Name basic;
|
||||||
switch(target.rom) {
|
switch(target.rom) {
|
||||||
case Analyser::Static::Oric::Target::ROM::BASIC10:
|
case Analyser::Static::Oric::Target::ROM::BASIC10: basic = ::ROM::Name::OricBASIC10; break;
|
||||||
rom_names.emplace_back(machine_name, "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4);
|
default:
|
||||||
break;
|
case Analyser::Static::Oric::Target::ROM::BASIC11: basic = ::ROM::Name::OricBASIC11; break;
|
||||||
case Analyser::Static::Oric::Target::ROM::BASIC11:
|
case Analyser::Static::Oric::Target::ROM::Pravetz: basic = ::ROM::Name::OricPravetzBASIC; break;
|
||||||
rom_names.emplace_back(machine_name, "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92bef);
|
|
||||||
break;
|
|
||||||
case Analyser::Static::Oric::Target::ROM::Pravetz:
|
|
||||||
rom_names.emplace_back(machine_name, "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
size_t diskii_state_machine_index = 0;
|
request = request && ::ROM::Request(basic);
|
||||||
|
|
||||||
switch(disk_interface) {
|
switch(disk_interface) {
|
||||||
default: break;
|
default: break;
|
||||||
case DiskInterface::BD500:
|
case DiskInterface::BD500:
|
||||||
rom_names.emplace_back(machine_name, "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34);
|
request = request && ::ROM::Request(::ROM::Name::OricByteDrive500);
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Jasmin:
|
case DiskInterface::Jasmin:
|
||||||
rom_names.emplace_back(machine_name, "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89);
|
request = request && ::ROM::Request(::ROM::Name::OricJasmin);
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Microdisc:
|
case DiskInterface::Microdisc:
|
||||||
rom_names.emplace_back(machine_name, "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9c);
|
request = request && ::ROM::Request(::ROM::Name::OricMicrodisc);
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Pravetz:
|
case DiskInterface::Pravetz:
|
||||||
rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06);
|
request = request && ::ROM::Request(::ROM::Name::Oric8DOSBoot) && ::ROM::Request(::ROM::Name::DiskIIStateMachine16Sector);
|
||||||
// These ROM details are coupled with those in the DiskIICard.
|
|
||||||
diskii_state_machine_index = rom_names.size();
|
|
||||||
rom_names.push_back({"DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, { 0x9796a238, 0xb72a2c70 }});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto roms = rom_fetcher(rom_names);
|
auto roms = rom_fetcher(request);
|
||||||
|
if(!request.validate(roms)) {
|
||||||
for(std::size_t index = 0; index < roms.size(); ++index) {
|
|
||||||
if(!roms[index]) {
|
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
video_->set_colour_rom(*roms[0]);
|
// The colour ROM is optional; an alternative composite encoding can be used if
|
||||||
rom_ = std::move(*roms[1]);
|
// it is absent.
|
||||||
|
const auto colour_rom = roms.find(::ROM::Name::OricColourROM);
|
||||||
|
if(colour_rom != roms.end()) {
|
||||||
|
video_->set_colour_rom(colour_rom->second);
|
||||||
|
}
|
||||||
|
rom_ = std::move(roms.find(basic)->second);
|
||||||
|
|
||||||
switch(disk_interface) {
|
switch(disk_interface) {
|
||||||
default: break;
|
default: break;
|
||||||
case DiskInterface::BD500:
|
case DiskInterface::BD500:
|
||||||
disk_rom_ = std::move(*roms[2]);
|
disk_rom_ = std::move(roms.find(::ROM::Name::OricByteDrive500)->second);
|
||||||
disk_rom_.resize(8192);
|
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Jasmin:
|
case DiskInterface::Jasmin:
|
||||||
disk_rom_ = std::move(*roms[2]);
|
disk_rom_ = std::move(roms.find(::ROM::Name::OricJasmin)->second);
|
||||||
disk_rom_.resize(2048);
|
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Microdisc:
|
case DiskInterface::Microdisc:
|
||||||
disk_rom_ = std::move(*roms[2]);
|
disk_rom_ = std::move(roms.find(::ROM::Name::OricMicrodisc)->second);
|
||||||
disk_rom_.resize(8192);
|
|
||||||
break;
|
break;
|
||||||
case DiskInterface::Pravetz: {
|
case DiskInterface::Pravetz: {
|
||||||
pravetz_rom_ = std::move(*roms[2]);
|
pravetz_rom_ = std::move(roms.find(::ROM::Name::Oric8DOSBoot)->second);
|
||||||
pravetz_rom_.resize(512);
|
pravetz_rom_.resize(512);
|
||||||
|
|
||||||
diskii_->set_state_machine(*roms[diskii_state_machine_index]);
|
diskii_->set_state_machine(roms.find(::ROM::Name::DiskIIStateMachine16Sector)->second);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_.resize(16384);
|
|
||||||
paged_rom_ = rom_.data();
|
paged_rom_ = rom_.data();
|
||||||
|
|
||||||
switch(target.disk_interface) {
|
switch(target.disk_interface) {
|
||||||
|
@ -54,7 +54,7 @@ void VideoOutput::set_display_type(Outputs::Display::DisplayType display_type) {
|
|||||||
|
|
||||||
#ifdef SUPPLY_COMPOSITE
|
#ifdef SUPPLY_COMPOSITE
|
||||||
const auto data_type =
|
const auto data_type =
|
||||||
(display_type == Outputs::Display::DisplayType::RGB) ?
|
(!has_colour_rom_ || display_type == Outputs::Display::DisplayType::RGB) ?
|
||||||
Outputs::Display::InputDataType::Red1Green1Blue1 :
|
Outputs::Display::InputDataType::Red1Green1Blue1 :
|
||||||
Outputs::Display::InputDataType::PhaseLinkedLuminance8;
|
Outputs::Display::InputDataType::PhaseLinkedLuminance8;
|
||||||
#else
|
#else
|
||||||
@ -80,6 +80,7 @@ Outputs::Display::ScanStatus VideoOutput::get_scaled_scan_status() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutput::set_colour_rom(const std::vector<uint8_t> &rom) {
|
void VideoOutput::set_colour_rom(const std::vector<uint8_t> &rom) {
|
||||||
|
has_colour_rom_ = true;
|
||||||
for(std::size_t c = 0; c < 8; c++) {
|
for(std::size_t c = 0; c < 8; c++) {
|
||||||
colour_forms_[c] = 0;
|
colour_forms_[c] = 0;
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ class VideoOutput {
|
|||||||
Outputs::CRT::CRT crt_;
|
Outputs::CRT::CRT crt_;
|
||||||
Outputs::CRT::CRTFrequencyMismatchWarner<VideoOutput> frequency_mismatch_warner_;
|
Outputs::CRT::CRTFrequencyMismatchWarner<VideoOutput> frequency_mismatch_warner_;
|
||||||
bool crt_is_60Hz_ = false;
|
bool crt_is_60Hz_ = false;
|
||||||
|
bool has_colour_rom_ = false;
|
||||||
|
|
||||||
void update_crt_frequency();
|
void update_crt_frequency();
|
||||||
|
|
||||||
|
@ -10,38 +10,16 @@
|
|||||||
#define ROMMachine_hpp
|
#define ROMMachine_hpp
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Utility/ROMCatalogue.hpp"
|
||||||
|
|
||||||
namespace ROMMachine {
|
namespace ROMMachine {
|
||||||
|
|
||||||
/*!
|
|
||||||
Describes a ROM image; this term is used in this emulator strictly in the sense of firmware —
|
|
||||||
system software that is an inherent part of a machine.
|
|
||||||
*/
|
|
||||||
struct ROM {
|
|
||||||
/// The machine with which this ROM is associated, in a form that is safe for using as
|
|
||||||
/// part of a file name.
|
|
||||||
std::string machine_name;
|
|
||||||
/// A descriptive name for this ROM, suitable for use in a bullet-point list, a bracket
|
|
||||||
/// clause, etc, e.g. "the Electron MOS 1.0".
|
|
||||||
std::string descriptive_name;
|
|
||||||
/// An idiomatic file name for this ROM, e.g. "os10.rom".
|
|
||||||
std::string file_name;
|
|
||||||
/// The expected size of this ROM in bytes, e.g. 32768.
|
|
||||||
size_t size = 0;
|
|
||||||
/// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform
|
|
||||||
/// to test user-provided ROMs of unknown provenance. **Not** intended to be used
|
|
||||||
/// to exclude ROMs where the user's intent is otherwise clear.
|
|
||||||
std::vector<uint32_t> crc32s;
|
|
||||||
|
|
||||||
ROM(std::string machine_name, std::string descriptive_name, std::string file_name, size_t size, uint32_t crc32) :
|
|
||||||
machine_name(machine_name), descriptive_name(descriptive_name), file_name(file_name), size(size), crc32s({crc32}) {}
|
|
||||||
ROM(std::string machine_name, std::string descriptive_name, std::string file_name, size_t size, std::initializer_list<uint32_t> crc32s) :
|
|
||||||
machine_name(machine_name), descriptive_name(descriptive_name), file_name(file_name), size(size), crc32s(crc32s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Defines the signature for a function that must be supplied by the host environment in order to give machines
|
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.
|
a route for fetching any system ROMs they might need.
|
||||||
@ -50,7 +28,7 @@ struct ROM {
|
|||||||
return a vector of unique_ptrs that either contain the contents of the ROM from @c names that corresponds by
|
return a vector of unique_ptrs that either contain the contents of the ROM from @c names that corresponds by
|
||||||
index, or else are @c nullptr.
|
index, or else are @c nullptr.
|
||||||
*/
|
*/
|
||||||
typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::vector<ROM> &roms)> ROMFetcher;
|
typedef std::function<ROM::Map(const ROM::Request &request)> ROMFetcher;
|
||||||
|
|
||||||
enum class Error {
|
enum class Error {
|
||||||
MissingROMs
|
MissingROMs
|
||||||
|
@ -74,15 +74,13 @@ template<bool is_zx81> class ConcreteMachine:
|
|||||||
speaker_.set_input_rate(float(ZX8081ClockRate) / 2.0f);
|
speaker_.set_input_rate(float(ZX8081ClockRate) / 2.0f);
|
||||||
|
|
||||||
const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM;
|
const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM;
|
||||||
const auto roms =
|
const ROM::Name rom_name = use_zx81_rom ? ROM::Name::ZX81 : ROM::Name::ZX80;
|
||||||
use_zx81_rom ?
|
const ROM::Request request(rom_name);
|
||||||
rom_fetcher({ {"ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6eb} }) :
|
auto roms = rom_fetcher(request);
|
||||||
rom_fetcher({ {"ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597} });
|
if(!request.validate(roms)) {
|
||||||
|
throw ROMMachine::Error::MissingROMs;
|
||||||
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
|
}
|
||||||
|
rom_ = std::move(roms.find(rom_name)->second);
|
||||||
rom_ = std::move(*roms[0]);
|
|
||||||
rom_.resize(use_zx81_rom ? 8192 : 4096);
|
|
||||||
|
|
||||||
rom_mask_ = uint16_t(rom_.size() - 1);
|
rom_mask_ = uint16_t(rom_.size() - 1);
|
||||||
|
|
||||||
|
@ -146,33 +146,24 @@ template<Model model> class ConcreteMachine:
|
|||||||
set_clock_rate(clock_rate());
|
set_clock_rate(clock_rate());
|
||||||
speaker_.set_input_rate(float(clock_rate()) / 2.0f);
|
speaker_.set_input_rate(float(clock_rate()) / 2.0f);
|
||||||
|
|
||||||
// With only the +2a and +3 currently supported, the +3 ROM is always
|
ROM::Name rom_name;
|
||||||
// the one required.
|
|
||||||
std::vector<ROMMachine::ROM> rom_names;
|
|
||||||
const std::string machine = "ZXSpectrum";
|
|
||||||
switch(model) {
|
switch(model) {
|
||||||
case Model::SixteenK:
|
case Model::SixteenK:
|
||||||
case Model::FortyEightK:
|
case Model::FortyEightK: rom_name = ROM::Name::Spectrum48k; break;
|
||||||
rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531f);
|
case Model::OneTwoEightK: rom_name = ROM::Name::Spectrum128k; break;
|
||||||
break;
|
case Model::Plus2: rom_name = ROM::Name::SpecrumPlus2; break;
|
||||||
|
|
||||||
case Model::OneTwoEightK:
|
|
||||||
rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Model::Plus2:
|
|
||||||
rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dc);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Model::Plus2a:
|
case Model::Plus2a:
|
||||||
case Model::Plus3: {
|
case Model::Plus3: rom_name = ROM::Name::SpectrumPlus3; break;
|
||||||
const std::initializer_list<uint32_t> crc32s = { 0x96e3c17a, 0xbe0d9ec4 };
|
// TODO: possibly accept the +3 ROM in multiple parts?
|
||||||
rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s);
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
const auto roms = rom_fetcher(rom_names);
|
const auto request = ROM::Request(rom_name);
|
||||||
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
|
auto roms = rom_fetcher(request);
|
||||||
memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size()));
|
if(!request.validate(roms)) {
|
||||||
|
throw ROMMachine::Error::MissingROMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &rom = roms.find(rom_name)->second;
|
||||||
|
memcpy(rom_.data(), rom.data(), std::min(rom_.size(), rom.size()));
|
||||||
|
|
||||||
// Register for sleeping notifications.
|
// Register for sleeping notifications.
|
||||||
tape_player_.set_clocking_hint_observer(this);
|
tape_player_.set_clocking_hint_observer(this);
|
||||||
|
482
Machines/Utility/ROMCatalogue.cpp
Normal file
482
Machines/Utility/ROMCatalogue.cpp
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
//
|
||||||
|
// ROMCatalogue.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 01/06/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ROMCatalogue.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <codecvt>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace ROM;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr Name MaxName = Name::SpectrumPlus3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Request::Request(Name name, bool optional) {
|
||||||
|
node.name = name;
|
||||||
|
node.is_optional = optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
Request Request::append(Node::Type type, const Request &rhs) {
|
||||||
|
// Start with the easiest case: this is already an appropriate
|
||||||
|
// request, and so is the new thing.
|
||||||
|
if(node.type == type && rhs.node.type == type) {
|
||||||
|
Request new_request = *this;
|
||||||
|
new_request.node.children.insert(new_request.node.children.end(), rhs.node.children.begin(), rhs.node.children.end());
|
||||||
|
new_request.node.sort();
|
||||||
|
return new_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possibly: left is appropriate request and rhs is just one more thing?
|
||||||
|
if(node.type == type && rhs.node.type == Node::Type::One) {
|
||||||
|
Request new_request = *this;
|
||||||
|
new_request.node.children.push_back(rhs.node);
|
||||||
|
new_request.node.sort();
|
||||||
|
return new_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or: right is appropriate request and this is just one more thing?
|
||||||
|
if(rhs.node.type == type && node.type == Node::Type::One) {
|
||||||
|
Request new_request = rhs;
|
||||||
|
new_request.node.children.push_back(node);
|
||||||
|
new_request.node.sort();
|
||||||
|
return new_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise create a new parent node.
|
||||||
|
Request parent;
|
||||||
|
parent.node.type = type;
|
||||||
|
parent.node.children.push_back(this->node);
|
||||||
|
parent.node.children.push_back(rhs.node);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Request Request::operator &&(const Request &rhs) {
|
||||||
|
return append(Node::Type::All, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Request Request::operator ||(const Request &rhs) {
|
||||||
|
return append(Node::Type::Any, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::validate(Map &map) const {
|
||||||
|
return node.validate(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ROM::Description> Request::all_descriptions() const {
|
||||||
|
std::vector<Description> result;
|
||||||
|
node.add_descriptions(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::Node::add_descriptions(std::vector<Description> &result) const {
|
||||||
|
if(type == Type::One) {
|
||||||
|
result.push_back(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto &child: children) {
|
||||||
|
child.add_descriptions(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request Request::subtract(const ROM::Map &map) const {
|
||||||
|
Request copy(*this);
|
||||||
|
if(copy.node.subtract(map)) {
|
||||||
|
copy.node.name = Name::None;
|
||||||
|
copy.node.type = Node::Type::One;
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::Node::subtract(const ROM::Map &map) {
|
||||||
|
switch(type) {
|
||||||
|
case Type::One:
|
||||||
|
return map.find(name) != map.end();
|
||||||
|
|
||||||
|
default: {
|
||||||
|
bool has_all = true;
|
||||||
|
bool has_any = false;
|
||||||
|
|
||||||
|
auto iterator = children.begin();
|
||||||
|
while(iterator != children.end()) {
|
||||||
|
const bool did_subtract = iterator->subtract(map);
|
||||||
|
has_all &= did_subtract;
|
||||||
|
has_any |= did_subtract;
|
||||||
|
if(did_subtract) {
|
||||||
|
iterator = children.erase(iterator);
|
||||||
|
} else {
|
||||||
|
++iterator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (type == Type::All && has_all) || (type == Type::Any && has_any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::empty() {
|
||||||
|
return node.type == Node::Type::One && node.name == Name::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::Node::validate(Map &map) const {
|
||||||
|
// Leaf nodes are easy: check that the named ROM is present,
|
||||||
|
// unless it's optional, in which case it is always valid.
|
||||||
|
//
|
||||||
|
// If it is present, make sure it's the proper size.
|
||||||
|
if(type == Type::One) {
|
||||||
|
auto rom = map.find(name);
|
||||||
|
if(rom == map.end()) {
|
||||||
|
return is_optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Description description(name);
|
||||||
|
rom->second.resize(description.size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a collection node then. Check for both any or all
|
||||||
|
// simultaneously, since all nodes will need to be visited
|
||||||
|
// regardless of any/all in order to ensure proper sizing.
|
||||||
|
bool has_all = true;
|
||||||
|
bool has_any = false;
|
||||||
|
|
||||||
|
for(const auto &child: children) {
|
||||||
|
const bool is_valid = child.validate(map);
|
||||||
|
has_all &= is_valid;
|
||||||
|
has_any |= is_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (type == Type::Any && has_any) || (type == Type::All && has_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::visit(
|
||||||
|
const std::function<void(ListType, size_t)> &enter_list,
|
||||||
|
const std::function<void(void)> &exit_list,
|
||||||
|
const std::function<void(ROM::Request::ListType, const ROM::Description &, bool, size_t)> &add_item
|
||||||
|
) const {
|
||||||
|
node.visit(enter_list, exit_list, add_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::visit(
|
||||||
|
const std::function<void(LineItem, ListType, int, const ROM::Description *, bool, size_t)> &add_item
|
||||||
|
) const {
|
||||||
|
int indentation_level = 0;
|
||||||
|
node.visit(
|
||||||
|
[&indentation_level, &add_item] (ROM::Request::ListType type, size_t size) {
|
||||||
|
add_item(LineItem::NewList, type, indentation_level, nullptr, false, size);
|
||||||
|
++indentation_level;
|
||||||
|
},
|
||||||
|
[&indentation_level] {
|
||||||
|
--indentation_level;
|
||||||
|
},
|
||||||
|
[&indentation_level, &add_item] (ROM::Request::ListType type, const ROM::Description &rom, bool is_optional, size_t remaining) {
|
||||||
|
add_item(LineItem::Description, type, indentation_level, &rom, is_optional, remaining);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::Node::visit(
|
||||||
|
const std::function<void(ListType, size_t)> &enter_list,
|
||||||
|
const std::function<void(void)> &exit_list,
|
||||||
|
const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
|
||||||
|
) const {
|
||||||
|
switch(type) {
|
||||||
|
case Type::One:
|
||||||
|
enter_list(ListType::Single, 1);
|
||||||
|
add_item(ROM::Request::ListType::Any, Description(name), is_optional, 0);
|
||||||
|
exit_list();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::Any:
|
||||||
|
case Type::All: {
|
||||||
|
const ListType list_type = type == Type::Any ? ListType::Any : ListType::All;
|
||||||
|
enter_list(list_type, children.size());
|
||||||
|
for(size_t index = 0; index < children.size(); index++) {
|
||||||
|
auto &child = children[index];
|
||||||
|
|
||||||
|
if(child.type == Type::One) {
|
||||||
|
add_item(list_type, Description(child.name), child.is_optional, children.size() - 1 - index);
|
||||||
|
} else {
|
||||||
|
child.visit(enter_list, exit_list, add_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit_list();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Description> ROM::all_descriptions() {
|
||||||
|
std::vector<Description> result;
|
||||||
|
for(int name = 1; name <= MaxName; name++) {
|
||||||
|
result.push_back(Description(ROM::Name(name)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Description> Description::from_crc(uint32_t crc32) {
|
||||||
|
for(int name = 1; name <= MaxName; name++) {
|
||||||
|
const Description candidate = Description(ROM::Name(name));
|
||||||
|
|
||||||
|
const auto found_crc = std::find(candidate.crc32s.begin(), candidate.crc32s.end(), crc32);
|
||||||
|
if(found_crc != candidate.crc32s.end()) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Description::description(int flags) const {
|
||||||
|
std::stringstream output;
|
||||||
|
|
||||||
|
// If there are no CRCs, don't output them.
|
||||||
|
if(crc32s.empty()) flags &= ~ DescriptionFlag::CRC;
|
||||||
|
|
||||||
|
// Print the file name(s) and the descriptive name.
|
||||||
|
if(flags & DescriptionFlag::Filename) {
|
||||||
|
flags &= ~DescriptionFlag::Filename;
|
||||||
|
|
||||||
|
output << machine_name << '/';
|
||||||
|
if(file_names.size() == 1) {
|
||||||
|
output << file_names[0];
|
||||||
|
} else {
|
||||||
|
output << "{";
|
||||||
|
bool is_first = true;
|
||||||
|
for(const auto &file_name: file_names) {
|
||||||
|
if(!is_first) output << " or ";
|
||||||
|
output << file_name;
|
||||||
|
is_first = false;
|
||||||
|
}
|
||||||
|
output << "}";
|
||||||
|
}
|
||||||
|
output << " (" << descriptive_name;
|
||||||
|
if(!flags) {
|
||||||
|
output << ")";
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
output << "; ";
|
||||||
|
} else {
|
||||||
|
output << descriptive_name;
|
||||||
|
if(!flags) {
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
output << " (";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the size.
|
||||||
|
if(flags & DescriptionFlag::Size) {
|
||||||
|
flags &= ~DescriptionFlag::Size;
|
||||||
|
output << size << " bytes";
|
||||||
|
|
||||||
|
if(!flags) {
|
||||||
|
output << ")";
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
output << "; ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the CRC(s).
|
||||||
|
if(flags & DescriptionFlag::CRC) {
|
||||||
|
flags &= ~DescriptionFlag::CRC;
|
||||||
|
|
||||||
|
output << ((crc32s.size() > 1) ? "usual crc32s: " : "usual crc32: ");
|
||||||
|
bool is_first = true;
|
||||||
|
for(const auto crc32: crc32s) {
|
||||||
|
if(!is_first) output << ", ";
|
||||||
|
is_first = false;
|
||||||
|
output << std::hex << std::setfill('0') << std::setw(8) << crc32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flags) {
|
||||||
|
output << ")";
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring Request::description(int description_flags, wchar_t bullet_point) {
|
||||||
|
std::wstringstream output;
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter;
|
||||||
|
|
||||||
|
visit(
|
||||||
|
[&output, description_flags, bullet_point, &wstring_converter] (ROM::Request::LineItem item, ROM::Request::ListType type, int indentation_level, const ROM::Description *description, bool is_optional, size_t remaining) {
|
||||||
|
if(indentation_level) {
|
||||||
|
output << std::endl;
|
||||||
|
for(int c = 0; c < indentation_level; c++) output << '\t';
|
||||||
|
output << bullet_point << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(item) {
|
||||||
|
case ROM::Request::LineItem::NewList:
|
||||||
|
if(remaining > 1) {
|
||||||
|
if(!indentation_level) output << " ";
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
case ROM::Request::ListType::All: output << "all of:"; break;
|
||||||
|
case ROM::Request::ListType::Any:
|
||||||
|
if(remaining == 2) {
|
||||||
|
output << "either of:";
|
||||||
|
} else {
|
||||||
|
output << "any of:";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output << ":";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ROM::Request::LineItem::Description:
|
||||||
|
if(is_optional) output << "optionally, ";
|
||||||
|
|
||||||
|
output << wstring_converter.from_bytes(description->description(description_flags));
|
||||||
|
|
||||||
|
if(remaining) {
|
||||||
|
output << ";";
|
||||||
|
if(remaining == 1) {
|
||||||
|
output << ((type == ROM::Request::ListType::All) ? " and" : " or");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output << ".";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Description::Description(Name name) {
|
||||||
|
switch(name) {
|
||||||
|
default: assert(false); break;
|
||||||
|
|
||||||
|
case Name::AMSDOS: *this = Description(name, "AmstradCPC", "the Amstrad Disk Operating System", "amsdos.rom", 16*1024, 0x1fe22ecdu); break;
|
||||||
|
case Name::CPC464Firmware: *this = Description(name, "AmstradCPC", "the CPC 464 firmware", "os464.rom", 16*1024, 0x815752dfu); break;
|
||||||
|
case Name::CPC464BASIC: *this = Description(name, "AmstradCPC", "the CPC 464 BASIC ROM", "basic464.rom", 16*1024, 0x7d9a3bacu); break;
|
||||||
|
case Name::CPC664Firmware: *this = Description(name, "AmstradCPC", "the CPC 664 firmware", "os664.rom", 16*1024, 0x3f5a6dc4u); break;
|
||||||
|
case Name::CPC664BASIC: *this = Description(name, "AmstradCPC", "the CPC 664 BASIC ROM", "basic664.rom", 16*1024, 0x32fee492u); break;
|
||||||
|
case Name::CPC6128Firmware: *this = Description(name, "AmstradCPC", "the CPC 6128 firmware", "os6128.rom", 16*1024, 0x0219bb74u); break;
|
||||||
|
case Name::CPC6128BASIC: *this = Description(name, "AmstradCPC", "the CPC 6128 BASIC ROM", "basic6128.rom", 16*1024, 0xca6af63du); break;
|
||||||
|
|
||||||
|
case Name::AppleIIEnhancedE: *this = Description(name, "AppleII", "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942u); break;
|
||||||
|
case Name::AppleIIe: *this = Description(name, "AppleII", "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18du); break;
|
||||||
|
case Name::AppleIIPlus: *this = Description(name, "AppleII", "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26u); break;
|
||||||
|
case Name::AppleIIOriginal: *this = Description(name, "AppleII", "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588u); break;
|
||||||
|
case Name::AppleIICharacter: *this = Description(name, "AppleII", "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6u); break;
|
||||||
|
case Name::AppleIIeCharacter: *this = Description(name, "AppleII", "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1u); break;
|
||||||
|
case Name::AppleIIEnhancedECharacter:
|
||||||
|
*this = Description(name, "AppleII", "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014du);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Name::AppleIIgsROM00: /* TODO */
|
||||||
|
case Name::AppleIIgsROM01: *this = Description(name, "AppleIIgs", "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0u); break;
|
||||||
|
case Name::AppleIIgsROM03: *this = Description(name, "AppleIIgs", "the Apple IIgs ROM03", "apple2gs.rom2", 256*1024, 0xde7ddf29u); break;
|
||||||
|
case Name::AppleIIgsCharacter: *this = Description(name, "AppleIIgs", "the Apple IIgs character ROM", "apple2gs.chr", 4*1024, 0x91e53cd8u); break;
|
||||||
|
case AppleIIgsMicrocontrollerROM03:
|
||||||
|
*this = Description(name, "AppleIIgs", "the Apple IIgs ROM03 ADB microcontroller ROM", "341s0632-2", 4*1024, 0xe1c11fb0u);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Name::DiskIIBoot16Sector:
|
||||||
|
*this = Description(name, "DiskII", "the Disk II 16-sector boot ROM", "boot-16.rom", 256, 0xce7144f6u);
|
||||||
|
break;
|
||||||
|
case Name::DiskIIStateMachine16Sector:
|
||||||
|
*this = Description(name, "DiskII", "the Disk II 16-sector state machine ROM", "state-machine-16.rom", 256, std::initializer_list<uint32_t>{ 0x9796a238, 0xb72a2c70 } );
|
||||||
|
break;
|
||||||
|
case Name::DiskIIBoot13Sector:
|
||||||
|
*this = Description(name, "DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ffu);
|
||||||
|
break;
|
||||||
|
case Name::DiskIIStateMachine13Sector:
|
||||||
|
*this = Description(name, "DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620u);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break;
|
||||||
|
case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break;
|
||||||
|
case Name::MacintoshPlus: {
|
||||||
|
const std::initializer_list<uint32_t> crcs = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e };
|
||||||
|
*this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crcs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break;
|
||||||
|
case Name::AtariSTTOS104: *this = Description(name, "AtariST", "the UK TOS 1.04 ROM", "tos104.img", 192*1024, 0xa50d1d43u); break;
|
||||||
|
case Name::AtariSTEmuTOS192: *this = Description(name, "AtariST", "the UK EmuTOS 1.92 ROM", "etos192uk.img", 192*1024, 0xfc3b9e61u); break;
|
||||||
|
|
||||||
|
case Name::ColecoVisionBIOS:
|
||||||
|
*this = Description(name, "ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3u);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Name::ZX80: *this = Description(name, "ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597u); break;
|
||||||
|
case Name::ZX81: *this = Description(name, "ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6ebu); break;
|
||||||
|
|
||||||
|
case Name::Spectrum48k: *this = Description(name, "ZXSpectrum", "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531fu); break;
|
||||||
|
case Name::Spectrum128k: *this = Description(name, "ZXSpectrum", "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); break;
|
||||||
|
case Name::SpecrumPlus2: *this = Description(name, "ZXSpectrum", "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); break;
|
||||||
|
case Name::SpectrumPlus3: {
|
||||||
|
const std::initializer_list<uint32_t> crcs = { 0x96e3c17a, 0xbe0d9ec4 };
|
||||||
|
*this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crcs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Name::AcornBASICII: *this = Description(name, "Electron", "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781u); break;
|
||||||
|
case Name::PRESADFSSlot1: *this = Description(name, "Electron", "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993u); break;
|
||||||
|
case Name::PRESADFSSlot2: *this = Description(name, "Electron", "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0eu); break;
|
||||||
|
case Name::AcornADFS: *this = Description(name, "Electron", "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6u); break;
|
||||||
|
case Name::Acorn1770DFS: *this = Description(name, "Electron", "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5u); break;
|
||||||
|
case Name::PRESAdvancedPlus6:
|
||||||
|
*this = Description(name, "Electron", "the 8kb Advanced Plus 6 ROM", "AP6v133.rom", 8*1024, 0xe0013cfcu);
|
||||||
|
break;
|
||||||
|
case Name::AcornElectronMOS100:
|
||||||
|
*this = Description(name, "Electron", "the Electron MOS ROM v1.00", "os.rom", 16*1024, 0xbf63fb1fu);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Name::MasterSystemJapaneseBIOS: *this = Description(name, "MasterSystem", "the Japanese Master System BIOS", "japanese-bios.sms", 8*1024, 0x48d44a13u); break;
|
||||||
|
case Name::MasterSystemWesternBIOS: *this = Description(name, "MasterSystem", "the European/US Master System BIOS", "bios.sms", 8*1024, 0x0072ed54u); break;
|
||||||
|
|
||||||
|
case Name::Commodore1540: *this = Description(name, "Commodore1540", "the 1540 ROM", "1540.bin", 16*1024, 0x718d42b1u); break;
|
||||||
|
case Name::Commodore1541: *this = Description(name, "Commodore1540", "the 1541 ROM", "1541.bin", 16*1024, 0xfb760019); break;
|
||||||
|
|
||||||
|
case Name::Vic20BASIC: *this = Description(name, "Vic20", "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1u); break;
|
||||||
|
case Name::Vic20EnglishCharacters: *this = Description(name, "Vic20", "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6u); break;
|
||||||
|
case Name::Vic20EnglishPALKernel: *this = Description(name, "Vic20", "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4u); break;
|
||||||
|
case Name::Vic20EnglishNTSCKernel: *this = Description(name, "Vic20", "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174u); break;
|
||||||
|
case Name::Vic20DanishCharacters: *this = Description(name, "Vic20", "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454u); break;
|
||||||
|
case Name::Vic20DanishKernel: *this = Description(name, "Vic20", "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16u); break;
|
||||||
|
case Name::Vic20JapaneseCharacters: *this = Description(name, "Vic20", "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4bu); break;
|
||||||
|
case Name::Vic20JapaneseKernel: *this = Description(name, "Vic20", "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7u); break;
|
||||||
|
case Name::Vic20SwedishCharacters: *this = Description(name, "Vic20", "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551du); break;
|
||||||
|
case Name::Vic20SwedishKernel: *this = Description(name, "Vic20", "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662u); break;
|
||||||
|
|
||||||
|
case Name::OricColourROM: *this = Description(name, "Oric", "the Oric colour ROM", "colour.rom", 128, 0xd50fca65u); break;
|
||||||
|
case Name::OricBASIC10: *this = Description(name, "Oric", "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4u); break;
|
||||||
|
case Name::OricBASIC11: *this = Description(name, "Oric", "Oric BASIC 1.1", "basic11.rom", 16*1024, 0xc3a92befu); break;
|
||||||
|
case Name::OricPravetzBASIC: *this = Description(name, "Oric", "Pravetz BASIC", "pravetz.rom", 16*1024, 0x58079502u); break;
|
||||||
|
case Name::OricByteDrive500: *this = Description(name, "Oric", "the Oric Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34u); break;
|
||||||
|
case Name::OricJasmin: *this = Description(name, "Oric", "the Oric Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89u); break;
|
||||||
|
case Name::OricMicrodisc: *this = Description(name, "Oric", "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9cu); break;
|
||||||
|
case Name::Oric8DOSBoot: *this = Description(name, "Oric", "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06u); break;
|
||||||
|
|
||||||
|
case Name::MSXGenericBIOS: *this = Description(name, "MSX", "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u); break;
|
||||||
|
case Name::MSXJapaneseBIOS: *this = Description(name, "MSX", "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390u); break;
|
||||||
|
case Name::MSXAmericanBIOS: *this = Description(name, "MSX", "an American MSX BIOS", "msx-american.rom", 32*1024, 0u); break;
|
||||||
|
case Name::MSXEuropeanBIOS: *this = Description(name, "MSX", "a European MSX BIOS", "msx-european.rom", 32*1024, 0u); break;
|
||||||
|
case Name::MSXDOS: *this = Description(name, "MSX", "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61dfu); break;
|
||||||
|
|
||||||
|
case Name::SinclairQLJS:
|
||||||
|
*this = Description(name, "SinclairQL", "the Sinclair QL 'JS' ROM", "js.rom", 48*1024, 0x0f95aab5u);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
269
Machines/Utility/ROMCatalogue.hpp
Normal file
269
Machines/Utility/ROMCatalogue.hpp
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
//
|
||||||
|
// ROMCatalogue.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 01/06/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ROMCatalogue_hpp
|
||||||
|
#define ROMCatalogue_hpp
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ROM {
|
||||||
|
|
||||||
|
enum Name {
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Acorn.
|
||||||
|
AcornBASICII,
|
||||||
|
AcornElectronMOS100,
|
||||||
|
PRESADFSSlot1,
|
||||||
|
PRESADFSSlot2,
|
||||||
|
AcornADFS,
|
||||||
|
PRESAdvancedPlus6,
|
||||||
|
Acorn1770DFS,
|
||||||
|
|
||||||
|
// Amstrad CPC.
|
||||||
|
AMSDOS,
|
||||||
|
CPC464Firmware, CPC464BASIC,
|
||||||
|
CPC664Firmware, CPC664BASIC,
|
||||||
|
CPC6128Firmware, CPC6128BASIC,
|
||||||
|
|
||||||
|
// Apple II.
|
||||||
|
AppleIIOriginal,
|
||||||
|
AppleIIPlus,
|
||||||
|
AppleIICharacter,
|
||||||
|
AppleIIe,
|
||||||
|
AppleIIeCharacter,
|
||||||
|
AppleIIEnhancedE,
|
||||||
|
AppleIIEnhancedECharacter,
|
||||||
|
|
||||||
|
// Apple IIgs.
|
||||||
|
AppleIIgsROM00,
|
||||||
|
AppleIIgsROM01,
|
||||||
|
AppleIIgsROM03,
|
||||||
|
AppleIIgsMicrocontrollerROM03,
|
||||||
|
AppleIIgsCharacter,
|
||||||
|
|
||||||
|
// Atari ST.
|
||||||
|
AtariSTTOS100,
|
||||||
|
AtariSTTOS104,
|
||||||
|
AtariSTEmuTOS192,
|
||||||
|
|
||||||
|
// ColecoVision.
|
||||||
|
ColecoVisionBIOS,
|
||||||
|
|
||||||
|
// Commodore 1540/1541.
|
||||||
|
Commodore1540,
|
||||||
|
Commodore1541,
|
||||||
|
|
||||||
|
// Disk II.
|
||||||
|
DiskIIStateMachine16Sector,
|
||||||
|
DiskIIBoot16Sector,
|
||||||
|
DiskIIStateMachine13Sector,
|
||||||
|
DiskIIBoot13Sector,
|
||||||
|
|
||||||
|
// Macintosh.
|
||||||
|
Macintosh128k,
|
||||||
|
Macintosh512k,
|
||||||
|
MacintoshPlus,
|
||||||
|
|
||||||
|
// Master System.
|
||||||
|
MasterSystemJapaneseBIOS,
|
||||||
|
MasterSystemWesternBIOS,
|
||||||
|
|
||||||
|
// MSX.
|
||||||
|
MSXGenericBIOS,
|
||||||
|
MSXJapaneseBIOS,
|
||||||
|
MSXAmericanBIOS,
|
||||||
|
MSXEuropeanBIOS,
|
||||||
|
MSXDOS,
|
||||||
|
|
||||||
|
// Oric.
|
||||||
|
OricColourROM,
|
||||||
|
OricBASIC10,
|
||||||
|
OricBASIC11,
|
||||||
|
OricPravetzBASIC,
|
||||||
|
OricByteDrive500,
|
||||||
|
OricJasmin,
|
||||||
|
OricMicrodisc,
|
||||||
|
Oric8DOSBoot,
|
||||||
|
|
||||||
|
// Sinclair QL.
|
||||||
|
SinclairQLJS,
|
||||||
|
|
||||||
|
// Vic-20.
|
||||||
|
Vic20BASIC,
|
||||||
|
Vic20EnglishCharacters,
|
||||||
|
Vic20EnglishPALKernel,
|
||||||
|
Vic20EnglishNTSCKernel,
|
||||||
|
Vic20DanishCharacters,
|
||||||
|
Vic20DanishKernel,
|
||||||
|
Vic20JapaneseCharacters,
|
||||||
|
Vic20JapaneseKernel,
|
||||||
|
Vic20SwedishCharacters,
|
||||||
|
Vic20SwedishKernel,
|
||||||
|
|
||||||
|
// ZX80/81.
|
||||||
|
ZX80,
|
||||||
|
ZX81,
|
||||||
|
|
||||||
|
// ZX Spectrum.
|
||||||
|
Spectrum48k,
|
||||||
|
Spectrum128k,
|
||||||
|
SpecrumPlus2,
|
||||||
|
SpectrumPlus3,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using Map = std::map<ROM::Name, std::vector<uint8_t>>;
|
||||||
|
|
||||||
|
struct Description {
|
||||||
|
/// The ROM's enum name.
|
||||||
|
Name name = Name::None;
|
||||||
|
/// The machine with which this ROM is associated, in a form that is safe for using as
|
||||||
|
/// part of a file name.
|
||||||
|
std::string machine_name;
|
||||||
|
/// A descriptive name for this ROM, suitable for use in a bullet-point list, a bracket
|
||||||
|
/// clause, etc, e.g. "the Electron MOS 1.0".
|
||||||
|
std::string descriptive_name;
|
||||||
|
/// All idiomatic file name for this ROM, e.g. "os10.rom".
|
||||||
|
std::vector<std::string> file_names;
|
||||||
|
/// The expected size of this ROM in bytes, e.g. 32768.
|
||||||
|
size_t size = 0;
|
||||||
|
/// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform
|
||||||
|
/// to test user-provided ROMs of unknown provenance. **Not** intended to be used
|
||||||
|
/// to exclude ROMs where the user's intent is otherwise clear.
|
||||||
|
std::set<uint32_t> crc32s;
|
||||||
|
|
||||||
|
/// Constructs the @c Description that correlates to @c name.
|
||||||
|
Description(Name name);
|
||||||
|
|
||||||
|
/// Constructs the @c Description that correlates to @c crc32, if any.
|
||||||
|
static std::optional<Description> from_crc(uint32_t crc32);
|
||||||
|
|
||||||
|
enum DescriptionFlag {
|
||||||
|
Size = 1 << 0,
|
||||||
|
CRC = 1 << 1,
|
||||||
|
Filename = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provides a single-line of text describing this ROM, including the usual base text
|
||||||
|
/// plus all the fields provided as @c flags .
|
||||||
|
std::string description(int flags) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename FileNameT, typename CRC32T> Description(
|
||||||
|
Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0)
|
||||||
|
) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
|
||||||
|
// Slightly lazy: deal with the case where the constructor wasn't provided with any
|
||||||
|
// CRCs by spotting that the set has exactly one member, which has value 0. The alternative
|
||||||
|
// would be to provide a partial specialisation that never put anything into the set.
|
||||||
|
if(this->crc32s.size() == 1 && !*this->crc32s.begin()) {
|
||||||
|
this->crc32s.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns a vector of all possible instances of ROM::Description — i.e. descriptions of every ROM
|
||||||
|
/// currently known to the ROM catalogue.
|
||||||
|
std::vector<Description> all_descriptions();
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
Request(Name name, bool optional = false);
|
||||||
|
Request() {}
|
||||||
|
|
||||||
|
/// Forms the request that would be satisfied by @c this plus the right-hand side.
|
||||||
|
Request operator &&(const Request &);
|
||||||
|
|
||||||
|
/// Forms the request that would be satisfied by either @c this or the right-hand side.
|
||||||
|
Request operator ||(const Request &);
|
||||||
|
|
||||||
|
/// Inspects the ROMMap to ensure that it satisfies this @c Request.
|
||||||
|
/// @c returns @c true if the request is satisfied; @c false otherwise.
|
||||||
|
///
|
||||||
|
/// All ROMs in the map will be resized to their idiomatic sizes.
|
||||||
|
bool validate(Map &) const;
|
||||||
|
|
||||||
|
/// Returns a flattened array of all @c ROM::Descriptions that relate to anything
|
||||||
|
/// anywhere in this ROM request.
|
||||||
|
std::vector<Description> all_descriptions() const;
|
||||||
|
|
||||||
|
/// @returns @c true if this request is empty, i.e. would be satisfied with no ROMs; @c false otherwise.
|
||||||
|
bool empty();
|
||||||
|
|
||||||
|
/// @returns what remains of this ROM request given that everything in @c map has been found.
|
||||||
|
Request subtract(const ROM::Map &map) const;
|
||||||
|
|
||||||
|
enum class ListType {
|
||||||
|
Any, All, Single
|
||||||
|
};
|
||||||
|
void visit(
|
||||||
|
const std::function<void(ListType, size_t size)> &enter_list,
|
||||||
|
const std::function<void(void)> &exit_list,
|
||||||
|
const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
|
||||||
|
) const;
|
||||||
|
|
||||||
|
enum class LineItem {
|
||||||
|
NewList, Description
|
||||||
|
};
|
||||||
|
void visit(
|
||||||
|
const std::function<void(LineItem, ListType, int level, const ROM::Description *, bool is_optional, size_t remaining)> &add_item
|
||||||
|
) const;
|
||||||
|
|
||||||
|
/// @returns a full bullet-pointed list of the requirements of this request, including
|
||||||
|
/// appropriate conjuntives. This text is intended to be glued to the end of an opening
|
||||||
|
/// portion of a sentence, e.g. "Please supply" + request.description(0, L'*').
|
||||||
|
std::wstring description(int description_flags, wchar_t bullet_point);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Node {
|
||||||
|
enum class Type {
|
||||||
|
Any, All, One
|
||||||
|
};
|
||||||
|
Type type = Type::One;
|
||||||
|
Name name = Name::None;
|
||||||
|
/// @c true if this ROM is optional for machine startup. Generally indicates something
|
||||||
|
/// that would make emulation more accurate, but not sufficiently so to make it
|
||||||
|
/// a necessity.
|
||||||
|
bool is_optional = false;
|
||||||
|
std::vector<Node> children;
|
||||||
|
|
||||||
|
void add_descriptions(std::vector<Description> &) const;
|
||||||
|
bool validate(Map &) const;
|
||||||
|
void visit(
|
||||||
|
const std::function<void(ListType, size_t)> &enter_list,
|
||||||
|
const std::function<void(void)> &exit_list,
|
||||||
|
const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
|
||||||
|
) const;
|
||||||
|
bool subtract(const ROM::Map &map);
|
||||||
|
void sort() {
|
||||||
|
// Don't do a full sort, but move anything optional to the back.
|
||||||
|
// This makes them print more nicely; it's a human-facing tweak only.
|
||||||
|
ssize_t index = ssize_t(children.size() - 1);
|
||||||
|
bool has_seen_non_optional = false;
|
||||||
|
while(index >= 0) {
|
||||||
|
has_seen_non_optional |= !children[size_t(index)].is_optional;
|
||||||
|
if(children[size_t(index)].is_optional && has_seen_non_optional) {
|
||||||
|
std::rotate(children.begin() + index, children.begin() + index + 1, children.end());
|
||||||
|
}
|
||||||
|
--index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Node node;
|
||||||
|
|
||||||
|
Request append(Node::Type type, const Request &rhs);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ROMCatalogue_hpp */
|
@ -12,6 +12,11 @@
|
|||||||
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||||
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||||
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; };
|
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; };
|
||||||
|
4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; };
|
||||||
|
4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; };
|
||||||
|
4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
||||||
|
4B051C95266EF50200CA44E8 /* AppleIIOptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */; };
|
||||||
|
4B051C97266EF5F600CA44E8 /* CSAppleII.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C96266EF5F600CA44E8 /* CSAppleII.mm */; };
|
||||||
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||||
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||||
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
|
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
|
||||||
@ -566,7 +571,6 @@
|
|||||||
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
|
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
|
||||||
4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; };
|
4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; };
|
||||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
||||||
4BA3189422E7A4CA00D18CFA /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
|
||||||
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
|
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
|
||||||
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; };
|
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; };
|
||||||
4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; };
|
4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; };
|
||||||
@ -1019,6 +1023,11 @@
|
|||||||
4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
|
4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
|
||||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
|
4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
|
||||||
4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||||
|
4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ROMCatalogue.cpp; sourceTree = "<group>"; };
|
||||||
|
4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMCatalogue.hpp; sourceTree = "<group>"; };
|
||||||
|
4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleIIOptionsPanel.swift; sourceTree = "<group>"; };
|
||||||
|
4B051C96266EF5F600CA44E8 /* CSAppleII.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSAppleII.mm; sourceTree = "<group>"; };
|
||||||
|
4B051C98266EF60500CA44E8 /* CSAppleII.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSAppleII.h; sourceTree = "<group>"; };
|
||||||
4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = "<group>"; };
|
4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = "<group>"; };
|
||||||
4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; };
|
4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
|
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
|
||||||
@ -2401,8 +2410,10 @@
|
|||||||
4B2A53981D117D36003C6002 /* Wrappers */ = {
|
4B2A53981D117D36003C6002 /* Wrappers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4B051C98266EF60500CA44E8 /* CSAppleII.h */,
|
||||||
4B2A53991D117D36003C6002 /* CSAtari2600.h */,
|
4B2A53991D117D36003C6002 /* CSAtari2600.h */,
|
||||||
4B14978D1EE4B4D200CE2596 /* CSZX8081.h */,
|
4B14978D1EE4B4D200CE2596 /* CSZX8081.h */,
|
||||||
|
4B051C96266EF5F600CA44E8 /* CSAppleII.mm */,
|
||||||
4B2A539A1D117D36003C6002 /* CSAtari2600.mm */,
|
4B2A539A1D117D36003C6002 /* CSAtari2600.mm */,
|
||||||
4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */,
|
4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */,
|
||||||
);
|
);
|
||||||
@ -2415,11 +2426,13 @@
|
|||||||
4B055ABE1FAE98000060FFFF /* MachineForTarget.cpp */,
|
4B055ABE1FAE98000060FFFF /* MachineForTarget.cpp */,
|
||||||
4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */,
|
4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */,
|
||||||
4BCE005B227D30CC000CA200 /* MemoryPacker.cpp */,
|
4BCE005B227D30CC000CA200 /* MemoryPacker.cpp */,
|
||||||
|
4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */,
|
||||||
4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */,
|
4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */,
|
||||||
4B2B3A471F9B8FA70062DABF /* Typer.cpp */,
|
4B2B3A471F9B8FA70062DABF /* Typer.cpp */,
|
||||||
4B055ABF1FAE98000060FFFF /* MachineForTarget.hpp */,
|
4B055ABF1FAE98000060FFFF /* MachineForTarget.hpp */,
|
||||||
4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */,
|
4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */,
|
||||||
4BCE005C227D30CC000CA200 /* MemoryPacker.hpp */,
|
4BCE005C227D30CC000CA200 /* MemoryPacker.hpp */,
|
||||||
|
4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */,
|
||||||
4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */,
|
4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */,
|
||||||
4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */,
|
4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */,
|
||||||
4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */,
|
4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */,
|
||||||
@ -2771,6 +2784,7 @@
|
|||||||
4B55CE551C3B7D360093A61B /* Documents */ = {
|
4B55CE551C3B7D360093A61B /* Documents */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */,
|
||||||
4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */,
|
4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */,
|
||||||
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */,
|
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */,
|
||||||
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
||||||
@ -4762,7 +4776,7 @@
|
|||||||
4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */,
|
4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */,
|
||||||
4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */,
|
4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */,
|
||||||
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */,
|
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */,
|
||||||
4BA3189422E7A4CA00D18CFA /* ROMImages in Resources */,
|
4B051C93266D9D6900CA44E8 /* ROMImages in Resources */,
|
||||||
4B79E4461E3AF38600141F11 /* floppy525.png in Resources */,
|
4B79E4461E3AF38600141F11 /* floppy525.png in Resources */,
|
||||||
4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */,
|
4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */,
|
||||||
4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */,
|
4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */,
|
||||||
@ -5321,6 +5335,7 @@
|
|||||||
4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */,
|
4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */,
|
||||||
4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */,
|
4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */,
|
||||||
4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */,
|
4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */,
|
||||||
|
4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */,
|
||||||
4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */,
|
4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */,
|
||||||
4B302185208A550100773308 /* DiskII.cpp in Sources */,
|
4B302185208A550100773308 /* DiskII.cpp in Sources */,
|
||||||
4B0F1BB32602645900B85C66 /* StaticAnalyser.cpp in Sources */,
|
4B0F1BB32602645900B85C66 /* StaticAnalyser.cpp in Sources */,
|
||||||
@ -5395,11 +5410,13 @@
|
|||||||
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
|
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
|
||||||
4B0ACC2C23775819008902D0 /* IntelligentKeyboard.cpp in Sources */,
|
4B0ACC2C23775819008902D0 /* IntelligentKeyboard.cpp in Sources */,
|
||||||
4B92E26A234AE35100CD6D1B /* MFP68901.cpp in Sources */,
|
4B92E26A234AE35100CD6D1B /* MFP68901.cpp in Sources */,
|
||||||
|
4B051C97266EF5F600CA44E8 /* CSAppleII.mm in Sources */,
|
||||||
4B0ACC2A23775819008902D0 /* Video.cpp in Sources */,
|
4B0ACC2A23775819008902D0 /* Video.cpp in Sources */,
|
||||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */,
|
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */,
|
||||||
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
||||||
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,
|
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,
|
||||||
4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */,
|
4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */,
|
||||||
|
4B051C95266EF50200CA44E8 /* AppleIIOptionsPanel.swift in Sources */,
|
||||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,
|
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,
|
||||||
4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */,
|
4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */,
|
||||||
4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */,
|
4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */,
|
||||||
@ -5537,6 +5554,7 @@
|
|||||||
4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */,
|
4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */,
|
||||||
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */,
|
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */,
|
||||||
4B0F1BDA2602FF9800B85C66 /* Video.cpp in Sources */,
|
4B0F1BDA2602FF9800B85C66 /* Video.cpp in Sources */,
|
||||||
|
4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */,
|
||||||
4B54C0C81F8D91E50050900F /* Keyboard.cpp in Sources */,
|
4B54C0C81F8D91E50050900F /* Keyboard.cpp in Sources */,
|
||||||
4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */,
|
4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */,
|
||||||
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
|
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
|
||||||
|
@ -56,6 +56,18 @@
|
|||||||
argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK"
|
argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = ""/Users/thomasharte/Desktop/Soft/Apple II/WOZs/Prince of Persia side A.woz""
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--rompath=~/ROMs"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--has-disk-drive"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Macintosh/MusicWorks 0.42.image""
|
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Macintosh/MusicWorks 0.42.image""
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
@ -70,11 +82,11 @@
|
|||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--volume=0.001"
|
argument = "--volume=0.001"
|
||||||
isEnabled = "NO">
|
isEnabled = "YES">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--new=amstradcpc"
|
argument = "--new=amstradcpc"
|
||||||
isEnabled = "YES">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/ColecoVision/Galaxian (1983)(Atari).col""
|
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/ColecoVision/Galaxian (1983)(Atari).col""
|
||||||
@ -116,6 +128,14 @@
|
|||||||
argument = "--model=cpc6128"
|
argument = "--model=cpc6128"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--new=appleii"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--use-square-pixels"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
</CommandLineArguments>
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -13,17 +13,27 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ZW7-Bw-4RP" customClass="MachinePanel" customModule="Clock_Signal" customModuleProvider="target">
|
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ZW7-Bw-4RP" customClass="AppleIIOptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="80" y="150" width="200" height="61"/>
|
<rect key="contentRect" x="80" y="150" width="200" height="159"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||||
<view key="contentView" id="tpZ-0B-QQu">
|
<view key="contentView" id="tpZ-0B-QQu">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="200" height="61"/>
|
<rect key="frame" x="0.0" y="0.0" width="200" height="84"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kDb-7g-cVx">
|
||||||
|
<rect key="frame" x="18" y="47" width="162" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Use Square Pixels" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="h9q-Wb-em8">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="optionDidChange:" target="ZW7-Bw-4RP" id="pNS-aK-0no"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ex3-VM-58z">
|
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ex3-VM-58z">
|
||||||
<rect key="frame" x="18" y="17" width="165" height="25"/>
|
<rect key="frame" x="17" y="16" width="167" height="25"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Colour" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="gOu-dv-tre" id="u3N-Je-c2L">
|
<popUpButtonCell key="cell" type="push" title="Colour" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="gOu-dv-tre" id="u3N-Je-c2L">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="menu"/>
|
<font key="font" metaFont="menu"/>
|
||||||
@ -40,16 +50,20 @@
|
|||||||
</popUpButton>
|
</popUpButton>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="bottom" secondItem="ex3-VM-58z" secondAttribute="bottom" constant="20" id="4ZS-q5-TJL"/>
|
<constraint firstAttribute="bottom" secondItem="ex3-VM-58z" secondAttribute="bottom" constant="20" symbolic="YES" id="4ZS-q5-TJL"/>
|
||||||
<constraint firstItem="ex3-VM-58z" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="8Pj-Ns-TrJ"/>
|
<constraint firstItem="ex3-VM-58z" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="8Pj-Ns-TrJ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kDb-7g-cVx" secondAttribute="trailing" constant="20" symbolic="YES" id="KHa-of-eY7"/>
|
||||||
|
<constraint firstItem="kDb-7g-cVx" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" symbolic="YES" id="OcZ-Xa-394"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="ex3-VM-58z" secondAttribute="trailing" constant="20" id="QWA-lY-ugz"/>
|
<constraint firstAttribute="trailing" secondItem="ex3-VM-58z" secondAttribute="trailing" constant="20" id="QWA-lY-ugz"/>
|
||||||
<constraint firstItem="ex3-VM-58z" firstAttribute="top" secondItem="tpZ-0B-QQu" secondAttribute="top" constant="20" id="szw-WO-3tS"/>
|
<constraint firstItem="ex3-VM-58z" firstAttribute="top" secondItem="kDb-7g-cVx" secondAttribute="bottom" constant="8" id="jDW-N8-c4V"/>
|
||||||
|
<constraint firstItem="kDb-7g-cVx" firstAttribute="top" secondItem="tpZ-0B-QQu" secondAttribute="top" constant="20" symbolic="YES" id="wdj-PF-zxO"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="displayTypeButton" destination="ex3-VM-58z" id="lmZ-aN-lcj"/>
|
<outlet property="displayTypeButton" destination="ex3-VM-58z" id="lmZ-aN-lcj"/>
|
||||||
|
<outlet property="squarePixelButton" destination="kDb-7g-cVx" id="zbx-o8-7yC"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-161" y="38.5"/>
|
<point key="canvasLocation" x="-161" y="104.5"/>
|
||||||
</window>
|
</window>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -13,17 +13,17 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="gsl-7V-TTU" customClass="Atari2600OptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
|
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="gsl-7V-TTU" customClass="Atari2600OptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="80" y="150" width="200" height="121"/>
|
<rect key="contentRect" x="80" y="150" width="200" height="121"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||||
<view key="contentView" id="aQh-Pm-DEo">
|
<view key="contentView" id="aQh-Pm-DEo">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="200" height="121"/>
|
<rect key="frame" x="0.0" y="0.0" width="200" height="121"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rQO-uD-fwn">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rQO-uD-fwn">
|
||||||
<rect key="frame" x="14" y="73" width="86" height="32"/>
|
<rect key="frame" x="13" y="74" width="88" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="l3H-0m-aK0">
|
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="l3H-0m-aK0">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="3qw-C1-NYW">
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="3qw-C1-NYW">
|
||||||
<rect key="frame" x="18" y="58" width="164" height="18"/>
|
<rect key="frame" x="18" y="58" width="162" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Black and White" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UP7-mf-IKo">
|
<buttonCell key="cell" type="check" title="Black and White" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UP7-mf-IKo">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="Xbc-cw-Sc2">
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="Xbc-cw-Sc2">
|
||||||
<rect key="frame" x="18" y="38" width="164" height="18"/>
|
<rect key="frame" x="18" y="36" width="162" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Left Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="wlJ-8s-PEh">
|
<buttonCell key="cell" type="check" title="Left Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="wlJ-8s-PEh">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="kPV-Tm-TTc">
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="kPV-Tm-TTc">
|
||||||
<rect key="frame" x="18" y="18" width="164" height="18"/>
|
<rect key="frame" x="18" y="14" width="162" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Right Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="F05-cA-66S">
|
<buttonCell key="cell" type="check" title="Right Player Difficulty" bezelStyle="regularSquare" imagePosition="left" inset="2" id="F05-cA-66S">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nt7-8K-xY9">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nt7-8K-xY9">
|
||||||
<rect key="frame" x="100" y="73" width="86" height="32"/>
|
<rect key="frame" x="99" y="74" width="88" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="Select" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8Na-Z1-EXS">
|
<buttonCell key="cell" type="push" title="Select" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8Na-Z1-EXS">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
|
@ -137,22 +137,22 @@ class MachineDocument:
|
|||||||
volumeSlider.floatValue = userDefaultsVolume()
|
volumeSlider.floatValue = userDefaultsVolume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var missingROMs: [CSMissingROM] = []
|
private var missingROMs: String = ""
|
||||||
func configureAs(_ analysis: CSStaticAnalyser) {
|
func configureAs(_ analysis: CSStaticAnalyser) {
|
||||||
self.machineDescription = analysis
|
self.machineDescription = analysis
|
||||||
|
|
||||||
actionLock.lock()
|
actionLock.lock()
|
||||||
drawLock.lock()
|
drawLock.lock()
|
||||||
|
|
||||||
let missingROMs = NSMutableArray()
|
let missingROMs = NSMutableString()
|
||||||
if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) {
|
if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) {
|
||||||
|
setRomRequesterIsVisible(false)
|
||||||
|
|
||||||
self.machine = machine
|
self.machine = machine
|
||||||
machine.setVolume(userDefaultsVolume())
|
machine.setVolume(userDefaultsVolume())
|
||||||
setupMachineOutput()
|
setupMachineOutput()
|
||||||
} else {
|
} else {
|
||||||
// Store the selected machine and list of missing ROMs, and
|
self.missingROMs = missingROMs as String
|
||||||
// show the missing ROMs dialogue.
|
|
||||||
self.missingROMs = missingROMs.map({$0 as! CSMissingROM})
|
|
||||||
requestRoms()
|
requestRoms()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,23 +413,42 @@ class MachineDocument:
|
|||||||
@IBOutlet var romReceiverErrorField: NSTextField?
|
@IBOutlet var romReceiverErrorField: NSTextField?
|
||||||
@IBOutlet var romReceiverView: CSROMReceiverView?
|
@IBOutlet var romReceiverView: CSROMReceiverView?
|
||||||
private var romRequestBaseText = ""
|
private var romRequestBaseText = ""
|
||||||
|
|
||||||
|
private func setRomRequesterIsVisible(_ visible : Bool) {
|
||||||
|
if !visible && self.romRequesterPanel == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.romRequesterPanel!.isVisible == visible {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if visible {
|
||||||
|
self.windowControllers[0].window?.beginSheet(self.romRequesterPanel!, completionHandler: nil)
|
||||||
|
} else {
|
||||||
|
self.windowControllers[0].window?.endSheet(self.romRequesterPanel!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func requestRoms() {
|
func requestRoms() {
|
||||||
// Don't act yet if there's no window controller yet.
|
// Don't act yet if there's no window controller yet.
|
||||||
if self.windowControllers.count == 0 {
|
if self.windowControllers.count == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the ROM requester dialogue.
|
// Load the ROM requester dialogue if it's not already loaded.
|
||||||
|
if self.romRequesterPanel == nil {
|
||||||
Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil)
|
Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil)
|
||||||
self.romReceiverView!.delegate = self
|
self.romReceiverView!.delegate = self
|
||||||
self.romRequestBaseText = romRequesterText!.stringValue
|
self.romRequestBaseText = romRequesterText!.stringValue
|
||||||
romReceiverErrorField?.alphaValue = 0.0
|
romReceiverErrorField?.alphaValue = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
// Populate the current absentee list.
|
// Populate the current absentee list.
|
||||||
populateMissingRomList()
|
populateMissingRomList()
|
||||||
|
|
||||||
// Show the thing.
|
// Show the thing.
|
||||||
self.windowControllers[0].window?.beginSheet(self.romRequesterPanel!, completionHandler: nil)
|
setRomRequesterIsVisible(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func cancelRequestROMs(_ sender: NSButton?) {
|
@IBAction func cancelRequestROMs(_ sender: NSButton?) {
|
||||||
@ -437,90 +456,18 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func populateMissingRomList() {
|
func populateMissingRomList() {
|
||||||
// Fill in the missing details; first build a list of all the individual
|
romRequesterText!.stringValue = self.romRequestBaseText + self.missingROMs
|
||||||
// line items.
|
|
||||||
var requestLines: [String] = []
|
|
||||||
for missingROM in self.missingROMs {
|
|
||||||
if let descriptiveName = missingROM.descriptiveName {
|
|
||||||
requestLines.append("• " + descriptiveName)
|
|
||||||
} else {
|
|
||||||
requestLines.append("• " + missingROM.fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suffix everything up to the penultimate line with a semicolon;
|
|
||||||
// the penultimate line with a semicolon and a conjunctive; the final
|
|
||||||
// line with a full stop.
|
|
||||||
for x in 0 ..< requestLines.count {
|
|
||||||
if x < requestLines.count - 2 {
|
|
||||||
requestLines[x].append(";")
|
|
||||||
} else if x < requestLines.count - 1 {
|
|
||||||
requestLines[x].append("; and")
|
|
||||||
} else {
|
|
||||||
requestLines[x].append(".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
romRequesterText!.stringValue = self.romRequestBaseText + requestLines.joined(separator: "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func romReceiverView(_ view: CSROMReceiverView, didReceiveFileAt URL: URL) {
|
func romReceiverView(_ view: CSROMReceiverView, didReceiveFileAt URL: URL) {
|
||||||
// Test whether the file identified matches any of the currently missing ROMs.
|
// Test whether the file identified matches any of the currently missing ROMs.
|
||||||
// If so then remove that ROM from the missing list and update the request screen.
|
// If so then remove that ROM from the missing list and update the request screen.
|
||||||
// If no ROMs are still missing, start the machine.
|
// If no ROMs are still missing, start the machine.
|
||||||
do {
|
if CSMachine.attemptInstallROM(URL) {
|
||||||
let fileData = try Data(contentsOf: URL)
|
|
||||||
var didInstallRom = false
|
|
||||||
|
|
||||||
// Try to match by size first, CRC second. Accept that some ROMs may have
|
|
||||||
// some additional appended data. Arbitrarily allow them to be up to 10kb
|
|
||||||
// too large.
|
|
||||||
var index = 0
|
|
||||||
for missingROM in self.missingROMs {
|
|
||||||
if fileData.count >= missingROM.size && fileData.count < missingROM.size + 10*1024 {
|
|
||||||
// Trim to size.
|
|
||||||
let trimmedData = fileData[0 ..< missingROM.size]
|
|
||||||
|
|
||||||
// Get CRC.
|
|
||||||
if missingROM.crc32s.contains( (trimmedData as NSData).crc32 ) {
|
|
||||||
// This ROM matches; copy it into the application library,
|
|
||||||
// strike it from the missing ROM list and decide how to
|
|
||||||
// proceed.
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
let targetPath = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
||||||
.appendingPathComponent("ROMImages")
|
|
||||||
.appendingPathComponent(missingROM.machineName)
|
|
||||||
let targetFile = targetPath
|
|
||||||
.appendingPathComponent(missingROM.fileName)
|
|
||||||
|
|
||||||
do {
|
|
||||||
try fileManager.createDirectory(atPath: targetPath.path, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
try trimmedData.write(to: targetFile)
|
|
||||||
} catch let error {
|
|
||||||
showRomReceiverError(error: "Couldn't write to application support directory: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.missingROMs.remove(at: index)
|
|
||||||
didInstallRom = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index = index + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if didInstallRom {
|
|
||||||
if self.missingROMs.count == 0 {
|
|
||||||
self.windowControllers[0].window?.endSheet(self.romRequesterPanel!)
|
|
||||||
configureAs(self.machineDescription!)
|
configureAs(self.machineDescription!)
|
||||||
} else {
|
|
||||||
populateMissingRomList()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
showRomReceiverError(error: "Didn't recognise contents of \(URL.lastPathComponent)")
|
showRomReceiverError(error: "Didn't recognise contents of \(URL.lastPathComponent)")
|
||||||
}
|
}
|
||||||
} catch let error {
|
|
||||||
showRomReceiverError(error: "Couldn't read file at \(URL.absoluteString): \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yucky ugliness follows; my experience as an iOS developer intersects poorly with
|
// Yucky ugliness follows; my experience as an iOS developer intersects poorly with
|
||||||
|
@ -33,20 +33,15 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
CSMachineKeyboardInputModeJoystick,
|
CSMachineKeyboardInputModeJoystick,
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface CSMissingROM: NSObject
|
|
||||||
@property (nonatomic, readonly, nonnull) NSString *machineName;
|
|
||||||
@property (nonatomic, readonly, nonnull) NSString *fileName;
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *descriptiveName;
|
|
||||||
@property (nonatomic, readonly) NSUInteger size;
|
|
||||||
@property (nonatomic, readonly, nonnull) NSArray<NSNumber *> *crc32s;
|
|
||||||
@end
|
|
||||||
|
|
||||||
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
||||||
#import "CSAtari2600.h"
|
#import "CSAtari2600.h"
|
||||||
#import "CSZX8081.h"
|
#import "CSZX8081.h"
|
||||||
|
#import "CSAppleII.h"
|
||||||
|
|
||||||
@interface CSMachine : NSObject
|
@interface CSMachine : NSObject
|
||||||
|
|
||||||
|
+ (BOOL)attemptInstallROM:(nonnull NSURL *)url;
|
||||||
|
|
||||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -56,7 +51,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
@param missingROMs An array that is filled with a list of ROMs that the machine requested but which
|
@param missingROMs An array that is filled with a list of ROMs that the machine requested but which
|
||||||
were not found; populated only if this `init` has failed.
|
were not found; populated only if this `init` has failed.
|
||||||
*/
|
*/
|
||||||
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray<CSMissingROM *> *)missingROMs NS_DESIGNATED_INITIALIZER;
|
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableString *)missingROMs NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
- (float)idealSamplingRateFromRange:(NSRange)range;
|
- (float)idealSamplingRateFromRange:(NSRange)range;
|
||||||
@property (readonly, getter=isStereo) BOOL stereo;
|
@property (readonly, getter=isStereo) BOOL stereo;
|
||||||
@ -109,5 +104,6 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
// Special-case accessors; undefined behaviour if accessed for a machine not of the corresponding type.
|
// Special-case accessors; undefined behaviour if accessed for a machine not of the corresponding type.
|
||||||
@property (nonatomic, readonly, nullable) CSAtari2600 *atari2600;
|
@property (nonatomic, readonly, nullable) CSAtari2600 *atari2600;
|
||||||
@property (nonatomic, readonly, nullable) CSZX8081 *zx8081;
|
@property (nonatomic, readonly, nullable) CSZX8081 *zx8081;
|
||||||
|
@property (nonatomic, readonly, nullable) CSAppleII *appleII;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
@interface CSMachine() <CSScanTargetViewDisplayLinkDelegate>
|
@interface CSMachine() <CSScanTargetViewDisplayLinkDelegate>
|
||||||
- (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
- (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||||
@ -74,28 +76,6 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
__unsafe_unretained CSMachine *machine;
|
__unsafe_unretained CSMachine *machine;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface CSMissingROM (/*Mutability*/)
|
|
||||||
@property (nonatomic, nonnull, copy) NSString *machineName;
|
|
||||||
@property (nonatomic, nonnull, copy) NSString *fileName;
|
|
||||||
@property (nonatomic, nullable, copy) NSString *descriptiveName;
|
|
||||||
@property (nonatomic, readwrite) NSUInteger size;
|
|
||||||
@property (nonatomic, copy) NSArray<NSNumber *> *crc32s;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CSMissingROM
|
|
||||||
|
|
||||||
@synthesize machineName=_machineName;
|
|
||||||
@synthesize fileName=_fileName;
|
|
||||||
@synthesize descriptiveName=_descriptiveName;
|
|
||||||
@synthesize size=_size;
|
|
||||||
@synthesize crc32s=_crc32s;
|
|
||||||
|
|
||||||
- (NSString *)description {
|
|
||||||
return [NSString stringWithFormat:@"%@/%@, %lu bytes, CRCs: %@", _fileName, _descriptiveName, (unsigned long)_size, _crc32s];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CSMachine {
|
@implementation CSMachine {
|
||||||
SpeakerDelegate _speakerDelegate;
|
SpeakerDelegate _speakerDelegate;
|
||||||
ActivityObserver _activityObserver;
|
ActivityObserver _activityObserver;
|
||||||
@ -126,35 +106,18 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
NSMutableArray<dispatch_block_t> *_inputEvents;
|
NSMutableArray<dispatch_block_t> *_inputEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableArray<CSMissingROM *> *)missingROMs {
|
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableString *)missingROMs {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if(self) {
|
if(self) {
|
||||||
_analyser = result;
|
_analyser = result;
|
||||||
|
|
||||||
Machine::Error error;
|
Machine::Error error;
|
||||||
std::vector<ROMMachine::ROM> missing_roms;
|
ROM::Request missing_roms;
|
||||||
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error));
|
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error));
|
||||||
if(!_machine) {
|
if(!_machine) {
|
||||||
for(const auto &missing_rom : missing_roms) {
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter;
|
||||||
CSMissingROM *rom = [[CSMissingROM alloc] init];
|
const std::wstring description = missing_roms.description(0, L'•');
|
||||||
|
[missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]];
|
||||||
// Copy/convert the primitive fields.
|
|
||||||
rom.machineName = @(missing_rom.machine_name.c_str());
|
|
||||||
rom.fileName = @(missing_rom.file_name.c_str());
|
|
||||||
rom.descriptiveName = missing_rom.descriptive_name.empty() ? nil : @(missing_rom.descriptive_name.c_str());
|
|
||||||
rom.size = missing_rom.size;
|
|
||||||
|
|
||||||
// Convert the CRC list.
|
|
||||||
NSMutableArray<NSNumber *> *crc32s = [[NSMutableArray alloc] initWithCapacity:missing_rom.crc32s.size()];
|
|
||||||
for(const auto &crc : missing_rom.crc32s) {
|
|
||||||
[crc32s addObject:@(crc)];
|
|
||||||
}
|
|
||||||
rom.crc32s = crc32s;
|
|
||||||
|
|
||||||
// Add to the missing list.
|
|
||||||
[missingROMs addObject:rom];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +577,10 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
return [[CSZX8081 alloc] initWithZX8081:_machine->raw_pointer() owner:self];
|
return [[CSZX8081 alloc] initWithZX8081:_machine->raw_pointer() owner:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (CSAppleII *)appleII {
|
||||||
|
return [[CSAppleII alloc] initWithAppleII:_machine->raw_pointer() owner:self];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Input device queries
|
#pragma mark - Input device queries
|
||||||
|
|
||||||
- (BOOL)hasJoystick {
|
- (BOOL)hasJoystick {
|
||||||
@ -783,4 +750,8 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
_timer = nil;
|
_timer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (BOOL)attemptInstallROM:(NSURL *)url {
|
||||||
|
return CSInstallROM(url);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,4 +8,5 @@
|
|||||||
|
|
||||||
#include "ROMMachine.hpp"
|
#include "ROMMachine.hpp"
|
||||||
|
|
||||||
ROMMachine::ROMFetcher CSROMFetcher(std::vector<ROMMachine::ROM> *missing_roms = nullptr);
|
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing = nullptr);
|
||||||
|
BOOL CSInstallROM(NSURL *);
|
||||||
|
@ -11,47 +11,96 @@
|
|||||||
|
|
||||||
#import "NSBundle+DataResource.h"
|
#import "NSBundle+DataResource.h"
|
||||||
#import "NSData+StdVector.h"
|
#import "NSData+StdVector.h"
|
||||||
|
#import "NSData+CRC32.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
ROMMachine::ROMFetcher CSROMFetcher(std::vector<ROMMachine::ROM> *missing_roms) {
|
namespace {
|
||||||
return [missing_roms] (const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
|
|
||||||
NSArray<NSURL *> *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
|
NSString *directoryFor(const ROM::Description &description) {
|
||||||
for(const auto &rom: roms) {
|
return [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray<NSURL *> *urlsFor(const ROM::Description &description, const std::string &file_name) {
|
||||||
|
NSMutableArray<NSURL *> *const urls = [[NSMutableArray alloc] init];
|
||||||
|
NSArray<NSURL *> *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
|
||||||
|
NSString *const subdirectory = directoryFor(description);
|
||||||
|
|
||||||
|
for(NSURL *supportURL in supportURLs) {
|
||||||
|
[urls addObject:[[supportURL URLByAppendingPathComponent:subdirectory]
|
||||||
|
URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CSInstallROM(NSURL *url) {
|
||||||
|
NSData *const data = [NSData dataWithContentsOfURL:url];
|
||||||
|
if(!data) return NO;
|
||||||
|
|
||||||
|
// Try for a direct CRC match.
|
||||||
|
std::optional<ROM::Description> target_description;
|
||||||
|
target_description = ROM::Description::from_crc(uint32_t(data.crc32.integerValue));
|
||||||
|
|
||||||
|
// See whether there's an acceptable trimming that creates a CRC match.
|
||||||
|
if(!target_description) {
|
||||||
|
const std::vector<ROM::Description> descriptions = ROM::all_descriptions();
|
||||||
|
for(const auto &description: descriptions) {
|
||||||
|
if(description.size > data.length) continue;
|
||||||
|
|
||||||
|
NSData *const trimmedData = [data subdataWithRange:NSMakeRange(0, description.size)];
|
||||||
|
if(description.crc32s.find(uint32_t(trimmedData.crc32.unsignedIntValue)) != description.crc32s.end()) {
|
||||||
|
target_description = description;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no destination was found, stop.
|
||||||
|
if(!target_description) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data to its destination and report success.
|
||||||
|
NSURL *const targetURL = [urlsFor(*target_description, target_description->file_names[0]) firstObject];
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtPath:targetURL.URLByDeletingLastPathComponent.path withIntermediateDirectories:YES attributes:nil error:nil];
|
||||||
|
[data writeToURL:targetURL atomically:NO];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) {
|
||||||
|
return [missing] (const ROM::Request &roms) -> ROM::Map {
|
||||||
|
ROM::Map results;
|
||||||
|
for(const auto &description: roms.all_descriptions()) {
|
||||||
|
for(const auto &file_name: description.file_names) {
|
||||||
NSData *fileData;
|
NSData *fileData;
|
||||||
NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:rom.machine_name.c_str()]];
|
|
||||||
|
|
||||||
// Check for this file first within the application support directories.
|
// Check for this file first within the application support directories.
|
||||||
for(NSURL *supportURL in supportURLs) {
|
for(NSURL *fileURL in urlsFor(description, file_name)) {
|
||||||
NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory]
|
fileData = [NSData dataWithContentsOfURL:fileURL];
|
||||||
URLByAppendingPathComponent:[NSString stringWithUTF8String:rom.file_name.c_str()]];
|
|
||||||
fileData = [NSData dataWithContentsOfURL:fullURL];
|
|
||||||
if(fileData) break;
|
if(fileData) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failing that, check inside the application bundle.
|
// Failing that, check inside the application bundle.
|
||||||
if(!fileData) {
|
if(!fileData) {
|
||||||
fileData = [[NSBundle mainBundle]
|
fileData = [[NSBundle mainBundle]
|
||||||
dataForResource:[NSString stringWithUTF8String:rom.file_name.c_str()]
|
dataForResource:[NSString stringWithUTF8String:file_name.c_str()]
|
||||||
withExtension:nil
|
withExtension:nil
|
||||||
subdirectory:subdirectory];
|
subdirectory:directoryFor(description)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store an appropriate result, accumulating a list of the missing if requested.
|
// Store an appropriate result.
|
||||||
if(!fileData) {
|
if(fileData) {
|
||||||
results.emplace_back(nullptr);
|
results[description.name] = fileData.stdVector8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(missing_roms) {
|
if(missing) {
|
||||||
missing_roms->push_back(rom);
|
*missing = roms.subtract(results);
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto data = std::make_unique<std::vector<std::uint8_t>>();
|
|
||||||
*data = fileData.stdVector8;
|
|
||||||
results.emplace_back(std::move(data));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
18
OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.h
Normal file
18
OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// CSAppleII.h
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 07/06/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@class CSAppleII;
|
||||||
|
#import "CSMachine.h"
|
||||||
|
|
||||||
|
@interface CSAppleII : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithAppleII:(void *)appleII owner:(CSMachine *)machine;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL useSquarePixels;
|
||||||
|
|
||||||
|
@end
|
57
OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.mm
Normal file
57
OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAppleII.mm
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// CSAppleII.m
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 07/06/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "CSAppleII.h"
|
||||||
|
|
||||||
|
#include "AppleII.hpp"
|
||||||
|
|
||||||
|
@implementation CSAppleII {
|
||||||
|
Apple::II::Machine *_appleII;
|
||||||
|
__weak CSMachine *_machine;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithAppleII:(void *)appleII owner:(CSMachine *)machine {
|
||||||
|
self = [super init];
|
||||||
|
if(self) {
|
||||||
|
_appleII = (Apple::II::Machine *)appleII;
|
||||||
|
_machine = machine;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Options
|
||||||
|
|
||||||
|
- (void)setUseSquarePixels:(BOOL)useSquarePixels {
|
||||||
|
Configurable::Device *const configurable = dynamic_cast<Configurable::Device *>(_appleII);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(configurable);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@synchronized(_machine) {
|
||||||
|
auto options = configurable->get_options();
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(dynamic_cast<Apple::II::Machine::Options *>(options.get()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto appleii_configurable = static_cast<Apple::II::Machine::Options *>(options.get());
|
||||||
|
appleii_configurable->use_square_pixels = useSquarePixels;
|
||||||
|
configurable->set_options(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)useSquarePixels {
|
||||||
|
Configurable::Device *const configurable = dynamic_cast<Configurable::Device *>(_appleII);
|
||||||
|
|
||||||
|
@synchronized(_machine) {
|
||||||
|
auto options = configurable->get_options();
|
||||||
|
auto appleii_configurable = dynamic_cast<Apple::II::Machine::Options *>(options.get());
|
||||||
|
return appleii_configurable->use_square_pixels ? YES : NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -20,18 +20,16 @@
|
|||||||
<windowStyleMask key="styleMask" titled="YES"/>
|
<windowStyleMask key="styleMask" titled="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" customClass="CSROMReceiverView">
|
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" customClass="CSROMReceiverView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5qG-I3-Qav">
|
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5qG-I3-Qav">
|
||||||
<rect key="frame" x="18" y="154" width="444" height="96"/>
|
<rect key="frame" x="18" y="186" width="444" height="64"/>
|
||||||
<textFieldCell key="cell" enabled="NO" allowsUndo="NO" id="itJ-2T-0ia">
|
<textFieldCell key="cell" enabled="NO" allowsUndo="NO" id="itJ-2T-0ia">
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
<string key="title">Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.
Please drag and drop the following over this text:
|
<string key="title">Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.
Please drag and drop over this text</string>
|
||||||
|
|
||||||
</string>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
|
@ -35,7 +35,8 @@ class VanillaSerialPort: public Commodore::Serial::Port {
|
|||||||
_serialPort = std::make_shared<VanillaSerialPort>();
|
_serialPort = std::make_shared<VanillaSerialPort>();
|
||||||
|
|
||||||
auto rom_fetcher = CSROMFetcher();
|
auto rom_fetcher = CSROMFetcher();
|
||||||
_c1540 = std::make_unique<Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, rom_fetcher);
|
auto roms = rom_fetcher(Commodore::C1540::Machine::rom_request(Commodore::C1540::Personality::C1540));
|
||||||
|
_c1540 = std::make_unique<Commodore::C1540::Machine>(Commodore::C1540::Personality::C1540, roms);
|
||||||
_c1540->set_serial_bus(_serialBus);
|
_c1540->set_serial_bus(_serialBus);
|
||||||
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
|
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
|
||||||
}
|
}
|
||||||
|
@ -103,21 +103,20 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
std::unique_ptr<EmuTOS> _machine;
|
std::unique_ptr<EmuTOS> _machine;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length {
|
- (void)testImage:(ROM::Name)name trace:(NSString *)trace length:(int)length {
|
||||||
const std::vector<ROMMachine::ROM> rom_names = {{"AtariST", "", image.UTF8String, 0, 0 }};
|
const auto roms = CSROMFetcher()(ROM::Request(name));
|
||||||
const auto roms = CSROMFetcher()(rom_names);
|
|
||||||
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
|
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
|
||||||
_machine = std::make_unique<EmuTOS>(*roms[0], traceLocation.fileSystemRepresentation);
|
_machine = std::make_unique<EmuTOS>(roms.find(name)->second, traceLocation.fileSystemRepresentation);
|
||||||
_machine->run_for(HalfCycles(length));
|
_machine->run_for(HalfCycles(length));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testEmuTOSStartup {
|
- (void)testEmuTOSStartup {
|
||||||
[self testImage:@"etos192uk.img" trace:@"etos192uk" length:313490];
|
[self testImage:ROM::Name::AtariSTEmuTOS192 trace:@"etos192uk" length:313490];
|
||||||
// TODO: assert that machine is now STOPped.
|
// TODO: assert that machine is now STOPped.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testTOSStartup {
|
- (void)testTOSStartup {
|
||||||
[self testImage:@"tos100.img" trace:@"tos100" length:54011091];
|
[self testImage:ROM::Name::AtariSTTOS100 trace:@"tos100" length:54011091];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -101,10 +101,11 @@ class QL: public ComparativeBusHandler {
|
|||||||
Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace.
|
Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace.
|
||||||
*/
|
*/
|
||||||
- (void)testStartup {
|
- (void)testStartup {
|
||||||
const std::vector<ROMMachine::ROM> rom_names = {{"SinclairQL", "", "js.rom", 0, 0 }};
|
constexpr ROM::Name rom_name = ROM::Name::SinclairQLJS;
|
||||||
const auto roms = CSROMFetcher()(rom_names);
|
ROM::Request request(rom_name);
|
||||||
|
const auto roms = CSROMFetcher()(request);
|
||||||
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"];
|
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"];
|
||||||
_machine = std::make_unique<QL>(*roms[0], traceLocation.UTF8String);
|
_machine = std::make_unique<QL>(roms.find(rom_name)->second, traceLocation.UTF8String);
|
||||||
|
|
||||||
// This is how many cycles it takes to exhaust the supplied trace file.
|
// This is how many cycles it takes to exhaust the supplied trace file.
|
||||||
_machine->run_for(HalfCycles(23923180));
|
_machine->run_for(HalfCycles(23923180));
|
||||||
|
@ -24,8 +24,10 @@ struct ContentionAnalysis {
|
|||||||
HalfCycles pattern[8];
|
HalfCycles pattern[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <Sinclair::ZXSpectrum::VideoTiming video_timing> ContentionAnalysis analyse() {
|
using Timing = Sinclair::ZXSpectrum::Video::Timing;
|
||||||
Sinclair::ZXSpectrum::Video<video_timing> video;
|
|
||||||
|
template <Timing video_timing> ContentionAnalysis analyse() {
|
||||||
|
Sinclair::ZXSpectrum::Video::Video<video_timing> video;
|
||||||
ContentionAnalysis analysis;
|
ContentionAnalysis analysis;
|
||||||
|
|
||||||
// Advance to the start of the first interrupt.
|
// Advance to the start of the first interrupt.
|
||||||
@ -82,7 +84,7 @@ template <Sinclair::ZXSpectrum::VideoTiming video_timing> ContentionAnalysis ana
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)test48k {
|
- (void)test48k {
|
||||||
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::FortyEightK>();
|
const auto analysis = analyse<Timing::FortyEightK>();
|
||||||
|
|
||||||
// Check time from interrupt.
|
// Check time from interrupt.
|
||||||
XCTAssertEqual(analysis.time_after_interrupt, 14335*2);
|
XCTAssertEqual(analysis.time_after_interrupt, 14335*2);
|
||||||
@ -105,7 +107,7 @@ template <Sinclair::ZXSpectrum::VideoTiming video_timing> ContentionAnalysis ana
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)test128k {
|
- (void)test128k {
|
||||||
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::OneTwoEightK>();
|
const auto analysis = analyse<Timing::OneTwoEightK>();
|
||||||
|
|
||||||
// Check time from interrupt.
|
// Check time from interrupt.
|
||||||
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
|
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
|
||||||
@ -128,7 +130,7 @@ template <Sinclair::ZXSpectrum::VideoTiming video_timing> ContentionAnalysis ana
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPlus3 {
|
- (void)testPlus3 {
|
||||||
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::Plus3>();
|
const auto analysis = analyse<Timing::Plus3>();
|
||||||
|
|
||||||
// Check time from interrupt.
|
// Check time from interrupt.
|
||||||
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
|
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
|
||||||
|
@ -251,16 +251,16 @@ void MainWindow::tile(const QMainWindow *previous) {
|
|||||||
|
|
||||||
void MainWindow::launchMachine() {
|
void MainWindow::launchMachine() {
|
||||||
const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
missingRoms.clear();
|
|
||||||
|
|
||||||
ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this]
|
ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this]
|
||||||
(const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
|
(const ROM::Request &roms) -> ROM::Map {
|
||||||
std::vector<std::unique_ptr<std::vector<uint8_t>>> results;
|
ROM::Map results;
|
||||||
|
|
||||||
for(const auto &rom: roms) {
|
for(const auto &description: roms.all_descriptions()) {
|
||||||
|
for(const auto &file_name: description.file_names) {
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
for(const auto &path: appDataLocations) {
|
for(const auto &path: appDataLocations) {
|
||||||
const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name;
|
const std::string source = path.toStdString() + "/ROMImages/" + description.machine_name + "/" + file_name;
|
||||||
const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString();
|
const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString();
|
||||||
|
|
||||||
file = fopen(nativeSource.c_str(), "rb");
|
file = fopen(nativeSource.c_str(), "rb");
|
||||||
@ -270,14 +270,14 @@ void MainWindow::launchMachine() {
|
|||||||
if(file) {
|
if(file) {
|
||||||
auto data = fileContentsAndClose(file);
|
auto data = fileContentsAndClose(file);
|
||||||
if(data) {
|
if(data) {
|
||||||
results.push_back(std::move(data));
|
results[description.name] = *data;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results.push_back(nullptr);
|
|
||||||
missingRoms.push_back(rom);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
missingRoms = roms.subtract(results);
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
Machine::Error error;
|
Machine::Error error;
|
||||||
@ -291,22 +291,7 @@ void MainWindow::launchMachine() {
|
|||||||
|
|
||||||
// Populate request text.
|
// Populate request text.
|
||||||
QString requestText = romRequestBaseText;
|
QString requestText = romRequestBaseText;
|
||||||
size_t index = 0;
|
requestText += QString::fromWCharArray(missingRoms.description(0, L'•').c_str());
|
||||||
for(const auto &rom: missingRoms) {
|
|
||||||
requestText += "• ";
|
|
||||||
requestText += rom.descriptive_name.c_str();
|
|
||||||
|
|
||||||
++index;
|
|
||||||
if(index == missingRoms.size()) {
|
|
||||||
requestText += ".\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(index == missingRoms.size() - 1) {
|
|
||||||
requestText += "; and\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
requestText += ";\n";
|
|
||||||
}
|
|
||||||
ui->missingROMsBox->setPlainText(requestText);
|
ui->missingROMsBox->setPlainText(requestText);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@ -418,7 +403,7 @@ void MainWindow::launchMachine() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Analyser::Machine::AppleII:
|
case Analyser::Machine::AppleII:
|
||||||
addDisplayMenu(settingsPrefix, "Colour", "Monochrome", "", "");
|
addAppleIIMenu();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Analyser::Machine::Atari2600:
|
case Analyser::Machine::Atari2600:
|
||||||
@ -686,6 +671,41 @@ void MainWindow::toggleAtari2600Switch(Atari2600Switch toggleSwitch) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::addAppleIIMenu() {
|
||||||
|
// Add the standard display settings.
|
||||||
|
addDisplayMenu("appleII", "Colour", "Monochrome", "", "");
|
||||||
|
|
||||||
|
// Add an additional tick box, for square pixels.
|
||||||
|
QAction *const squarePixelsAction = new QAction(tr("Square Pixels"));
|
||||||
|
squarePixelsAction->setCheckable(true);
|
||||||
|
connect(squarePixelsAction, &QAction::triggered, this, [=] {
|
||||||
|
std::lock_guard lock_guard(machineMutex);
|
||||||
|
|
||||||
|
// Apply the new setting to the machine.
|
||||||
|
setAppleIISquarePixels(squarePixelsAction->isChecked());
|
||||||
|
|
||||||
|
// Also store it.
|
||||||
|
Settings settings;
|
||||||
|
settings.setValue("appleII.squarePixels", squarePixelsAction->isChecked());
|
||||||
|
});
|
||||||
|
displayMenu->addAction(squarePixelsAction);
|
||||||
|
|
||||||
|
// Establish initial selection.
|
||||||
|
Settings settings;
|
||||||
|
const bool useSquarePixels = settings.value("appleII.squarePixels").toBool();
|
||||||
|
squarePixelsAction->setChecked(useSquarePixels);
|
||||||
|
setAppleIISquarePixels(useSquarePixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setAppleIISquarePixels(bool squarePixels) {
|
||||||
|
Configurable::Device *const configurable = machine->configurable_device();
|
||||||
|
auto options = configurable->get_options();
|
||||||
|
auto appleii_options = static_cast<Apple::II::Machine::Options *>(options.get());
|
||||||
|
|
||||||
|
appleii_options->use_square_pixels = squarePixels;
|
||||||
|
configurable->set_options(options);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector<int16_t> &buffer) {
|
void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector<int16_t> &buffer) {
|
||||||
audioBuffer.write(buffer);
|
audioBuffer.write(buffer);
|
||||||
}
|
}
|
||||||
@ -736,28 +756,23 @@ void MainWindow::dropEvent(QDropEvent* event) {
|
|||||||
CRC::CRC32 generator;
|
CRC::CRC32 generator;
|
||||||
const uint32_t crc = generator.compute_crc(*contents);
|
const uint32_t crc = generator.compute_crc(*contents);
|
||||||
|
|
||||||
bool wasUsed = false;
|
std::optional<ROM::Description> target_rom = ROM::Description::from_crc(crc);
|
||||||
for(const auto &rom: missingRoms) {
|
if(target_rom) {
|
||||||
if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
|
|
||||||
foundROM = true;
|
|
||||||
|
|
||||||
// Ensure the destination folder exists.
|
// Ensure the destination folder exists.
|
||||||
const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name;
|
const std::string path = appDataLocation + "/ROMImages/" + target_rom->machine_name;
|
||||||
const QDir dir(QString::fromStdString(path));
|
const QDir dir(QString::fromStdString(path));
|
||||||
if (!dir.exists())
|
if (!dir.exists())
|
||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
|
|
||||||
// Write into place.
|
// Write into place.
|
||||||
const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString();
|
const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + target_rom->file_names[0])).toStdString();
|
||||||
FILE *const target = fopen(destination.c_str(), "wb");
|
FILE *const target = fopen(destination.c_str(), "wb");
|
||||||
fwrite(contents->data(), 1, contents->size(), target);
|
fwrite(contents->data(), 1, contents->size(), target);
|
||||||
fclose(target);
|
fclose(target);
|
||||||
|
|
||||||
wasUsed = true;
|
// Note that at least one meaningful ROM was supplied.
|
||||||
}
|
foundROM = true;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if(!wasUsed) {
|
|
||||||
if(!unusedRoms.isEmpty()) unusedRoms += ", ";
|
if(!unusedRoms.isEmpty()) unusedRoms += ", ";
|
||||||
unusedRoms += url.fileName();
|
unusedRoms += url.fileName();
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,9 @@
|
|||||||
#include "../../Activity/Observer.hpp"
|
#include "../../Activity/Observer.hpp"
|
||||||
|
|
||||||
// There are machine-specific controls for the following:
|
// There are machine-specific controls for the following:
|
||||||
#include "../../Machines/Sinclair/ZX8081/ZX8081.hpp"
|
#include "../../Machines/Apple/AppleII/AppleII.hpp"
|
||||||
#include "../../Machines/Atari/2600/Atari2600.hpp"
|
#include "../../Machines/Atari/2600/Atari2600.hpp"
|
||||||
|
#include "../../Machines/Sinclair/ZX8081/ZX8081.hpp"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui { class MainWindow; }
|
namespace Ui { class MainWindow; }
|
||||||
@ -60,7 +61,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
void launchMachine();
|
void launchMachine();
|
||||||
|
|
||||||
QString romRequestBaseText;
|
QString romRequestBaseText;
|
||||||
std::vector<ROMMachine::ROM> missingRoms;
|
ROM::Request missingRoms;
|
||||||
|
|
||||||
// File drag and drop is supported.
|
// File drag and drop is supported.
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
@ -137,6 +138,9 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
void addAtari2600Menu();
|
void addAtari2600Menu();
|
||||||
void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
|
void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
|
||||||
|
|
||||||
|
void addAppleIIMenu();
|
||||||
|
void setAppleIISquarePixels(bool);
|
||||||
|
|
||||||
void setWindowTitle();
|
void setWindowTitle();
|
||||||
bool mouseIsCaptured = false;
|
bool mouseIsCaptured = false;
|
||||||
|
|
||||||
|
@ -860,9 +860,7 @@
|
|||||||
<property name="plainText">
|
<property name="plainText">
|
||||||
<string>Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.
|
<string>Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once.
|
||||||
|
|
||||||
Please drag and drop the following over this window:
|
Please drag and drop over this window</string>
|
||||||
|
|
||||||
</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -694,11 +694,10 @@ int main(int argc, char *argv[]) {
|
|||||||
// /usr/local/share/CLK/[system];
|
// /usr/local/share/CLK/[system];
|
||||||
// /usr/share/CLK/[system]; or
|
// /usr/share/CLK/[system]; or
|
||||||
// [user-supplied path]/[system]
|
// [user-supplied path]/[system]
|
||||||
std::vector<ROMMachine::ROM> requested_roms;
|
ROM::Request missing_roms;
|
||||||
ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments]
|
std::vector<std::string> checked_paths;
|
||||||
(const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
|
ROMMachine::ROMFetcher rom_fetcher = [&missing_roms, &arguments, &checked_paths]
|
||||||
requested_roms.insert(requested_roms.end(), roms.begin(), roms.end());
|
(const ROM::Request &roms) -> ROM::Map {
|
||||||
|
|
||||||
std::vector<std::string> paths = {
|
std::vector<std::string> paths = {
|
||||||
"/usr/local/share/CLK/",
|
"/usr/local/share/CLK/",
|
||||||
"/usr/share/CLK/"
|
"/usr/share/CLK/"
|
||||||
@ -706,41 +705,56 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
const auto rompath = arguments.selections.find("rompath");
|
const auto rompath = arguments.selections.find("rompath");
|
||||||
if(rompath != arguments.selections.end()) {
|
if(rompath != arguments.selections.end()) {
|
||||||
if(rompath->second.back() != '/') {
|
std::string path = rompath->second;
|
||||||
paths.push_back(rompath->second + "/");
|
|
||||||
} else {
|
// Ensure the path ends in a slash.
|
||||||
paths.push_back(rompath->second);
|
if(path.back() != '/') {
|
||||||
}
|
path += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<std::vector<uint8_t>>> results;
|
// If ~ is present, expand it to %HOME%.
|
||||||
for(const auto &rom: roms) {
|
const size_t tilde_position = path.find("~");
|
||||||
|
if(tilde_position != std::string::npos) {
|
||||||
|
path.replace(tilde_position, 1, getenv("HOME"));
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.push_back(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ROM::Map results;
|
||||||
|
for(const auto &description: roms.all_descriptions()) {
|
||||||
|
for(const auto &file_name: description.file_names) {
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
|
std::vector<std::string> rom_checked_paths;
|
||||||
for(const auto &path: paths) {
|
for(const auto &path: paths) {
|
||||||
std::string local_path = path + rom.machine_name + "/" + rom.file_name;
|
std::string local_path = path + description.machine_name + "/" + file_name;
|
||||||
file = std::fopen(local_path.c_str(), "rb");
|
file = std::fopen(local_path.c_str(), "rb");
|
||||||
|
rom_checked_paths.push_back(local_path);
|
||||||
if(file) break;
|
if(file) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!file) {
|
if(!file) {
|
||||||
results.emplace_back(nullptr);
|
std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = std::make_unique<std::vector<uint8_t>>();
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
std::fseek(file, 0, SEEK_END);
|
std::fseek(file, 0, SEEK_END);
|
||||||
data->resize(std::ftell(file));
|
data.resize(std::ftell(file));
|
||||||
std::fseek(file, 0, SEEK_SET);
|
std::fseek(file, 0, SEEK_SET);
|
||||||
std::size_t read = fread(data->data(), 1, data->size(), file);
|
std::size_t read = fread(data.data(), 1, data.size(), file);
|
||||||
std::fclose(file);
|
std::fclose(file);
|
||||||
|
|
||||||
if(read == data->size())
|
if(read == data.size()) {
|
||||||
results.emplace_back(std::move(data));
|
results[description.name] = std::move(data);
|
||||||
else
|
} else {
|
||||||
results.emplace_back(nullptr);
|
std::copy(rom_checked_paths.begin(), rom_checked_paths.end(), std::back_inserter(checked_paths));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
missing_roms = roms.subtract(results);
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -758,24 +772,22 @@ int main(int argc, char *argv[]) {
|
|||||||
if(!machine) {
|
if(!machine) {
|
||||||
switch(error) {
|
switch(error) {
|
||||||
default: break;
|
default: break;
|
||||||
case ::Machine::Error::MissingROM:
|
case ::Machine::Error::MissingROM: {
|
||||||
std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath." << std::endl;
|
std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/, or provide a --rompath, e.g. --rompath=~/ROMs." << std::endl;
|
||||||
std::cerr << "One or more of the following was needed but not found:" << std::endl;
|
std::cerr << "Needed — but didn't find —";
|
||||||
for(const auto &rom: requested_roms) {
|
|
||||||
std::cerr << rom.machine_name << '/' << rom.file_name << " (";
|
using DescriptionFlag = ROM::Description::DescriptionFlag;
|
||||||
if(!rom.descriptive_name.empty()) {
|
std::wcerr << missing_roms.description(DescriptionFlag::Filename | DescriptionFlag::CRC, L'*');
|
||||||
std::cerr << rom.descriptive_name << "; ";
|
|
||||||
}
|
std::cerr << std::endl << std::endl << "Searched unsuccessfully: ";
|
||||||
std::cerr << "usual crc32s: ";
|
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
for(const auto crc32: rom.crc32s) {
|
for(const auto &path: checked_paths) {
|
||||||
if(!is_first) std::cerr << ", ";
|
if(!is_first) std::cerr << "; ";
|
||||||
|
std::cerr << path;
|
||||||
is_first = false;
|
is_first = false;
|
||||||
std::cerr << std::hex << std::setfill('0') << std::setw(8) << crc32;
|
|
||||||
}
|
}
|
||||||
std::cerr << ")" << std::endl;
|
std::cerr << std::endl;
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user