1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Merge pull request #627 from TomHarte/ExtraROMDetails

Extends system ROM details; provides for manual import on the Mac
This commit is contained in:
Thomas Harte 2019-07-23 16:24:55 -04:00 committed by GitHub
commit 90f6ca4635
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 743 additions and 132 deletions

View File

@ -781,27 +781,37 @@ template <bool has_fdc> class ConcreteMachine:
ay_.ay().set_port_handler(&key_state_);
// construct the list of necessary ROMs
std::vector<std::string> required_roms = {"amsdos.rom"};
const std::string machine_name = "AmstradCPC";
std::vector<ROMMachine::ROM> required_roms = {
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) {
default:
model_number = "6128";
has_128k_ = true;
crcs[0] = 0x0219bb74;
crcs[1] = 0xca6af63d;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC464:
model_number = "464";
has_128k_ = false;
crcs[0] = 0x815752df;
crcs[1] = 0x7d9a3bac;
break;
case Analyser::Static::AmstradCPC::Target::Model::CPC664:
model_number = "664";
has_128k_ = false;
crcs[0] = 0x3f5a6dc4;
crcs[1] = 0x32fee492;
break;
}
required_roms.push_back("os" + model_number + ".rom");
required_roms.push_back("basic" + model_number + ".rom");
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
const auto roms = rom_fetcher("AmstradCPC", required_roms);
const auto roms = rom_fetcher(required_roms);
for(std::size_t index = 0; index < roms.size(); ++index) {
auto &data = roms[index];

View File

@ -347,30 +347,39 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
// Pick the required ROMs.
using Target = Analyser::Static::AppleII::Target;
std::vector<std::string> rom_names;
const std::string machine_name = "AppleII";
std::vector<ROMMachine::ROM> rom_descriptions;
size_t rom_size = 12*1024;
switch(target.model) {
default:
rom_names.push_back("apple2-character.rom");
rom_names.push_back("apple2o.rom");
rom_descriptions.emplace_back(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6);
rom_descriptions.emplace_back(machine_name, "the original Apple II ROM", "apple2o.rom", 12*1024, 0xba210588);
break;
case Target::Model::IIplus:
rom_names.push_back("apple2-character.rom");
rom_names.push_back("apple2.rom");
rom_descriptions.emplace_back(machine_name, "the basic Apple II character ROM", "apple2-character.rom", 2*1024, 0x64f415c6);
rom_descriptions.emplace_back(machine_name, "the Apple II+ ROM", "apple2.rom", 12*1024, 0xf66f9c26);
break;
case Target::Model::IIe:
rom_size += 3840;
rom_names.push_back("apple2eu-character.rom");
rom_names.push_back("apple2eu.rom");
rom_descriptions.emplace_back(machine_name, "the Apple IIe character ROM", "apple2eu-character.rom", 4*1024, 0x816a86f1);
rom_descriptions.emplace_back(machine_name, "the Apple IIe ROM", "apple2eu.rom", 32*1024, 0xe12be18d);
break;
case Target::Model::EnhancedIIe:
rom_size += 3840;
rom_names.push_back("apple2e-character.rom");
rom_names.push_back("apple2e.rom");
rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014d);
rom_descriptions.emplace_back(machine_name, "the Enhanced Apple IIe ROM", "apple2e.rom", 32*1024, 0x65989942);
break;
}
const auto roms = rom_fetcher("AppleII", rom_names);
const auto roms = rom_fetcher(rom_descriptions);
// Try to install a Disk II card now, before checking the ROM list,
// to make sure that Disk II dependencies have been communicated.
if(target.disk_controller != Target::DiskController::None) {
// Apple recommended slot 6 for the (first) Disk II.
install_card(6, new Apple::II::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector));
}
// Now, check and move the ROMs.
if(!roms[0] || !roms[1]) {
throw ROMMachine::Error::MissingROMs;
}
@ -382,11 +391,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
video_.set_character_rom(*roms[0]);
if(target.disk_controller != Target::DiskController::None) {
// Apple recommended slot 6 for the (first) Disk II.
install_card(6, new Apple::II::DiskIICard(rom_fetcher, target.disk_controller == Target::DiskController::SixteenSector));
}
// 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.
set_main_paging();

View File

@ -11,12 +11,22 @@
using namespace Apple::II;
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) {
const auto roms = rom_fetcher(
"DiskII",
{
is_16_sector ? "boot-16.rom" : "boot-13.rom",
is_16_sector ? "state-machine-16.rom" : "state-machine-13.rom"
std::vector<std::unique_ptr<std::vector<uint8_t>>> roms;
if(is_16_sector) {
roms = rom_fetcher({
{"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 {
roms = rom_fetcher({
{"DiskII", "the Disk II 13-sector boot ROM", "boot-13.rom", 256, 0xd34eb2ff},
{"DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620 }
});
}
if(!roms[0] || !roms[1]) {
throw ROMMachine::Error::MissingROMs;
}
boot_ = std::move(*roms[0]);
diskii_.set_state_machine(*roms[1]);
set_select_constraints(None);

View File

@ -70,33 +70,35 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
// Select a ROM name and determine the proper ROM and RAM sizes
// based on the machine model.
using Model = Analyser::Static::Macintosh::Target::Model;
std::string rom_name;
const std::string machine_name = "Macintosh";
uint32_t ram_size, rom_size;
std::vector<ROMMachine::ROM> rom_descriptions;
switch(model) {
default:
case Model::Mac128k:
ram_size = 128*1024;
rom_size = 64*1024;
rom_name = "mac128k.rom";
rom_descriptions.emplace_back(machine_name, "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28);
break;
case Model::Mac512k:
ram_size = 512*1024;
rom_size = 64*1024;
rom_name = "mac512k.rom";
rom_descriptions.emplace_back(machine_name, "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d);
break;
case Model::Mac512ke:
case Model::MacPlus:
case Model::MacPlus: {
ram_size = 512*1024;
rom_size = 128*1024;
rom_name = "macplus.rom";
break;
const std::initializer_list<uint32_t> crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e };
rom_descriptions.emplace_back(machine_name, "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s);
} break;
}
ram_mask_ = (ram_size >> 1) - 1;
rom_mask_ = (rom_size >> 1) - 1;
video_.set_ram_mask(ram_mask_);
// Grab a copy of the ROM and convert it into big-endian data.
const auto roms = rom_fetcher("Macintosh", { rom_name });
const auto roms = rom_fetcher(rom_descriptions);
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;
}

View File

@ -131,8 +131,7 @@ class ConcreteMachine:
joysticks_.emplace_back(new Joystick);
const auto roms = rom_fetcher(
"ColecoVision",
{ "coleco.rom" });
{ {"ColecoVision", "the ColecoVision BIOS", "coleco.rom", 8*1024, 0x3aa93ef3} });
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;

View File

@ -39,13 +39,20 @@ MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &
// attach the only drive there is
set_drive(drive_);
std::string rom_name;
std::string device_name;
uint32_t crc = 0;
switch(personality) {
case Personality::C1540: rom_name = "1540.bin"; break;
case Personality::C1541: rom_name = "1541.bin"; break;
case Personality::C1540:
device_name = "1540";
crc = 0x718d42b1;
break;
case Personality::C1541:
device_name = "1541";
crc = 0xfb760019;
break;
}
auto roms = rom_fetcher("Commodore1540", {rom_name});
auto roms = rom_fetcher({ {"Commodore1540", "the " + device_name + " ROM", device_name + ".bin", 16*1024, crc} });
if(!roms[0]) {
throw ROMMachine::Error::MissingROMs;
}

View File

@ -323,31 +323,34 @@ class ConcreteMachine:
// install a joystick
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
std::vector<std::string> rom_names = { "basic.bin" };
const std::string machine_name = "Vic20";
std::vector<ROMMachine::ROM> rom_names = {
{machine_name, "the VIC-20 BASIC ROM", "basic.bin", 8*1024, 0xdb4c43c1}
};
switch(target.region) {
default:
rom_names.push_back("characters-english.bin");
rom_names.push_back("kernel-pal.bin");
rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6);
rom_names.emplace_back(machine_name, "the English-language PAL VIC-20 kernel ROM", "kernel-pal.bin", 8*1024, 0x4be07cb4);
break;
case Analyser::Static::Commodore::Target::Region::American:
rom_names.push_back("characters-english.bin");
rom_names.push_back("kernel-ntsc.bin");
rom_names.emplace_back(machine_name, "the English-language VIC-20 character ROM", "characters-english.bin", 4*1024, 0x83e032a6);
rom_names.emplace_back(machine_name, "the English-language NTSC VIC-20 kernel ROM", "kernel-ntsc.bin", 8*1024, 0xe5e7c174);
break;
case Analyser::Static::Commodore::Target::Region::Danish:
rom_names.push_back("characters-danish.bin");
rom_names.push_back("kernel-danish.bin");
rom_names.emplace_back(machine_name, "the Danish VIC-20 character ROM", "characters-danish.bin", 4*1024, 0x7fc11454);
rom_names.emplace_back(machine_name, "the Danish VIC-20 kernel ROM", "kernel-danish.bin", 8*1024, 0x02adaf16);
break;
case Analyser::Static::Commodore::Target::Region::Japanese:
rom_names.push_back("characters-japanese.bin");
rom_names.push_back("kernel-japanese.bin");
rom_names.emplace_back(machine_name, "the Japanese VIC-20 character ROM", "characters-japanese.bin", 4*1024, 0xfcfd8a4b);
rom_names.emplace_back(machine_name, "the Japanese VIC-20 kernel ROM", "kernel-japanese.bin", 8*1024, 0x336900d7);
break;
case Analyser::Static::Commodore::Target::Region::Swedish:
rom_names.push_back("characters-swedish.bin");
rom_names.push_back("kernel-japanese.bin");
rom_names.emplace_back(machine_name, "the Swedish VIC-20 character ROM", "characters-swedish.bin", 4*1024, 0xd808551d);
rom_names.emplace_back(machine_name, "the Swedish VIC-20 kernel ROM", "kernel-swedish.bin", 8*1024, 0xb2a60662);
break;
}
const auto roms = rom_fetcher("Vic20", rom_names);
const auto roms = rom_fetcher(rom_names);
for(const auto &rom: roms) {
if(!rom) {

View File

@ -64,16 +64,20 @@ class ConcreteMachine:
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
speaker_.set_high_frequency_cutoff(7000);
std::vector<std::string> rom_names = {"basic.rom", "os.rom"};
const std::string machine_name = "Electron";
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}
};
if(target.has_adfs) {
rom_names.push_back("ADFS-E00_1.rom");
rom_names.push_back("ADFS-E00_2.rom");
required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993);
required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e);
}
const size_t dfs_rom_position = rom_names.size();
const size_t dfs_rom_position = required_roms.size();
if(target.has_dfs) {
rom_names.push_back("DFS-1770-2.20.rom");
required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5);
}
const auto roms = rom_fetcher("Electron", rom_names);
const auto roms = rom_fetcher(required_roms);
for(const auto &rom: roms) {
if(!rom) {

View File

@ -173,16 +173,20 @@ class ConcreteMachine:
mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f});
// Install the proper TV standard and select an ideal BIOS name.
std::vector<std::string> rom_names = {"msx.rom"};
const std::string machine_name = "MSX";
std::vector<ROMMachine::ROM> required_roms = {
{machine_name, "any MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3}
};
bool is_ntsc = true;
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 keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */
// TODO: CRCs below are incomplete, at best.
switch(target.region) {
case Target::Region::Japan:
rom_names.push_back("msx-japanese.rom");
required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390);
vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC);
is_ntsc = true;
@ -190,7 +194,7 @@ class ConcreteMachine:
date_format = 0;
break;
case Target::Region::USA:
rom_names.push_back("msx-american.rom");
required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0);
vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC);
is_ntsc = true;
@ -198,7 +202,7 @@ class ConcreteMachine:
date_format = 1;
break;
case Target::Region::Europe:
rom_names.push_back("msx-european.rom");
required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0);
vdp_.set_tv_standard(TI::TMS::TVStandard::PAL);
is_ntsc = false;
@ -211,10 +215,10 @@ class ConcreteMachine:
// but failing that fall back on patching the main one.
size_t disk_index = 0;
if(target.has_disk_drive) {
disk_index = rom_names.size();
rom_names.push_back("disk.rom");
disk_index = required_roms.size();
required_roms.emplace_back(machine_name, "the MSX-DOS ROM", "disk.rom", 16*1024, 0x721f61df);
}
const auto roms = rom_fetcher("MSX", rom_names);
const auto roms = rom_fetcher(required_roms);
if((!roms[0] && !roms[1]) || (target.has_disk_drive && !roms[2])) {
throw ROMMachine::Error::MissingROMs;

View File

@ -132,7 +132,12 @@ class ConcreteMachine:
// Load the BIOS if relevant.
if(has_bios()) {
const auto roms = rom_fetcher("MasterSystem", {"bios.sms"});
// TODO: there's probably a million other versions of the Master System BIOS; try to build a
// CRC32 catalogue of those. So far:
//
// 0072ed54 = US/European BIOS 1.3
// 48d44a13 = Japanese BIOS 2.1
const auto roms = rom_fetcher({ {"MasterSystem", "the Master System BIOS", "bios.sms", 8*1024, { 0x0072ed54, 0x48d44a13 } } });
if(!roms[0]) {
// No BIOS found; attempt to boot as though it has already disabled itself.
memory_control_ |= 0x08;

View File

@ -228,19 +228,34 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
diskii_.set_clocking_hint_observer(this);
}
std::vector<std::string> rom_names = {"colour.rom"};
const std::string machine_name = "Oric";
std::vector<ROMMachine::ROM> rom_names = { {machine_name, "the Oric colour ROM", "colour.rom", 128, 0xd50fca65} };
switch(target.rom) {
case Analyser::Static::Oric::Target::ROM::BASIC10: rom_names.push_back("basic10.rom"); break;
case Analyser::Static::Oric::Target::ROM::BASIC11: rom_names.push_back("basic11.rom"); break;
case Analyser::Static::Oric::Target::ROM::Pravetz: rom_names.push_back("pravetz.rom"); break;
case Analyser::Static::Oric::Target::ROM::BASIC10:
rom_names.emplace_back(machine_name, "Oric BASIC 1.0", "basic10.rom", 16*1024, 0xf18710b4);
break;
case Analyser::Static::Oric::Target::ROM::BASIC11:
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;
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: rom_names.push_back("microdisc.rom"); break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: rom_names.push_back("8dos.rom"); break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
rom_names.emplace_back(machine_name, "the ORIC Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9c);
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06);
// 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;
}
const auto roms = rom_fetcher("Oric", rom_names);
const auto roms = rom_fetcher(rom_names);
for(std::size_t index = 0; index < roms.size(); ++index) {
if(!roms[index]) {
@ -261,11 +276,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
pravetz_rom_ = std::move(*roms[2]);
pravetz_rom_.resize(512);
auto state_machine_rom = rom_fetcher("DiskII", {"state-machine-16.rom"});
if(!state_machine_rom[0]) {
throw ROMMachine::Error::MissingROMs;
}
diskii_.set_state_machine(*state_machine_rom[0]);
diskii_.set_state_machine(*roms[diskii_state_machine_index]);
} break;
}

View File

@ -16,15 +16,41 @@
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
a route for fetching any system ROMs they might need.
The caller will supply the idiomatic name of the machine plus a vector of the names of ROM files that it expects
to be present. The recevier should return a vector of unique_ptrs that either contain the contents of the
ROM from @c names that corresponds by index, or else are the nullptr
The caller will supply a vector of the names of ROM files that it would like to inspect. The recevier should
return a vector of unique_ptrs that either contain the contents of the ROM from @c names that corresponds by
index, or else are @c nullptr.
*/
typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> ROMFetcher;
typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::vector<ROM> &roms)> ROMFetcher;
enum class Error {
MissingROMs

View File

@ -76,7 +76,11 @@ template<bool is_zx81> class ConcreteMachine:
clear_all_keys();
const bool use_zx81_rom = target.is_ZX81 || target.ZX80_uses_ZX81_ROM;
const auto roms = rom_fetcher("ZX8081", { use_zx81_rom ? "zx81.rom" : "zx80.rom" });
const auto roms =
use_zx81_rom ?
rom_fetcher({ {"ZX8081", "the ZX81 BASIC ROM", "zx81.rom", 8 * 1024, 0x4b1dd6eb} }) :
rom_fetcher({ {"ZX8081", "the ZX80 BASIC ROM", "zx80.rom", 4 * 1024, 0x4c7fc597} });
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
rom_ = std::move(*roms[0]);

View File

@ -145,7 +145,6 @@
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B1EDB431E39A0AC009D6819 /* chip.png */; };
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2A332B1DB86821002876E3 /* OricOptions.xib */; };
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */; };
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */; };
@ -633,7 +632,6 @@
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; };
4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; };
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; };
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCA6CC61D9DD9F000C2D7B2 /* CommodoreROM.cpp */; };
@ -666,6 +664,11 @@
4BD67DCC209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; };
4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
4BDA00DA22E60EE300AC3CD0 /* ROMRequester.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BDA00D922E60EE300AC3CD0 /* ROMRequester.xib */; };
4BDA00DD22E622C200AC3CD0 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; };
4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */; };
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */; };
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
@ -1473,6 +1476,11 @@
4BD67DCE209BF27B00AB2146 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
4BD67DCF209BF27B00AB2146 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
4BD9137D1F311BC5009BCF85 /* i8255.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8255.hpp; path = 8255/i8255.hpp; sourceTree = "<group>"; };
4BDA00D922E60EE300AC3CD0 /* ROMRequester.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ROMRequester.xib; sourceTree = "<group>"; };
4BDA00DE22E644AF00AC3CD0 /* CSROMReceiverView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSROMReceiverView.h; sourceTree = "<group>"; };
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSROMReceiverView.m; sourceTree = "<group>"; };
4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+CRC32.m"; sourceTree = "<group>"; };
4BDA00E322E663B900AC3CD0 /* NSData+CRC32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+CRC32.h"; sourceTree = "<group>"; };
4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = "<group>"; };
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = "<group>"; };
@ -1736,13 +1744,15 @@
4BBC34241D2208B100FFC9DF /* CSFastLoading.h */,
4B2A53951D117D36003C6002 /* CSMachine.h */,
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */,
4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */,
4B2A53971D117D36003C6002 /* KeyCodes.h */,
4B8FE2251DA1DE2D0090D3CE /* NSBundle+DataResource.h */,
4BDA00E322E663B900AC3CD0 /* NSData+CRC32.h */,
4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */,
4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */,
4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */,
4B8FE2261DA1DE2D0090D3CE /* NSBundle+DataResource.m */,
4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */,
4B2A53961D117D36003C6002 /* CSMachine.mm */,
4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */,
4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */,
4B643F3B1D77AD6D00D431D6 /* StaticAnalyser */,
4B2A53981D117D36003C6002 /* Wrappers */,
@ -2944,6 +2954,7 @@
4BE5F85A1C3E1C2500C43F01 /* Resources */,
4BD5F1961D1352A000631CD1 /* Updater */,
4B55CE5A1C3B7D6F0093A61B /* Views */,
4BDA00DB22E60EE900AC3CD0 /* ROMRequester */,
);
path = "Clock Signal";
sourceTree = "<group>";
@ -3297,6 +3308,16 @@
name = 8255;
sourceTree = "<group>";
};
4BDA00DB22E60EE900AC3CD0 /* ROMRequester */ = {
isa = PBXGroup;
children = (
4BDA00D922E60EE300AC3CD0 /* ROMRequester.xib */,
4BDA00DE22E644AF00AC3CD0 /* CSROMReceiverView.h */,
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */,
);
path = ROMRequester;
sourceTree = "<group>";
};
4BE5F85A1C3E1C2500C43F01 /* Resources */ = {
isa = PBXGroup;
children = (
@ -3544,6 +3565,7 @@
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */,
4B79E4451E3AF38600141F11 /* floppy35.png in Resources */,
4B55DD8420DF06680043F2E5 /* MachinePicker.xib in Resources */,
4BDA00DA22E60EE300AC3CD0 /* ROMRequester.xib in Resources */,
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */,
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */,
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */,
@ -3555,7 +3577,6 @@
4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */,
4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */,
4B79E4461E3AF38600141F11 /* floppy525.png in Resources */,
4BC9DF451D044FCA00F44158 /* ROMImages in Resources */,
4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */,
4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */,
);
@ -3610,6 +3631,7 @@
4BB299B91B587D8400A49093 /* rorz in Resources */,
4BB299F61B587D8400A49093 /* tsxn in Resources */,
4BB298F11B587D8400A49093 /* start in Resources */,
4BDA00DD22E622C200AC3CD0 /* ROMImages in Resources */,
4BB299061B587D8400A49093 /* asla in Resources */,
4BB299901B587D8400A49093 /* lsrn in Resources */,
4BB298FE1B587D8400A49093 /* anday in Resources */,
@ -4067,6 +4089,7 @@
4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */,
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */,
4BB4BFB022A42F290069048D /* MacintoshIMG.cpp in Sources */,
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */,
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
@ -4099,6 +4122,7 @@
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
4B45189F1F75FD1C00926311 /* AcornADF.cpp in Sources */,
4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */,
@ -4120,6 +4144,7 @@
4B4518851F75E91A00926311 /* DiskController.cpp in Sources */,
4B8334841F5DA0360097E338 /* Z80Storage.cpp in Sources */,
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */,
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */,
4BD191F42191180E0042E144 /* ScanTarget.cpp in Sources */,
4BCD634922D6756400F567F1 /* MacintoshDoubleDensityDrive.cpp in Sources */,
4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */,
@ -4193,7 +4218,6 @@
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */,
4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */,
4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */,
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */,

View File

@ -1,8 +1,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="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -14,7 +14,7 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@ -25,7 +25,7 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<openGLView hidden="YES" useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSOpenGLView">
<openGLView hidden="YES" wantsLayer="YES" useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSOpenGLView">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
</openGLView>
</subviews>
@ -36,6 +36,11 @@
<constraint firstItem="DEG-fq-cjd" firstAttribute="width" secondItem="gIp-Ho-8D9" secondAttribute="width" id="mYS-bH-DST"/>
</constraints>
</view>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="backgroundColor">
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-x8E"/>
<outlet property="initialFirstResponder" destination="DEG-fq-cjd" id="9RI-Kx-QeN"/>

View File

@ -10,10 +10,13 @@
#import "CSStaticAnalyser.h"
#import "CSOpenGLView.h"
#import "CSAudioQueue.h"
#import "CSOpenGLView.h"
#import "CSROMReceiverView.h"
#import "CSBestEffortUpdater.h"
#import "CSJoystickManager.h"
#import "NSData+CRC32.h"
#include "KeyCodes.h"

View File

@ -16,7 +16,8 @@ class MachineDocument:
CSOpenGLViewDelegate,
CSOpenGLViewResponderDelegate,
CSBestEffortUpdaterDelegate,
CSAudioQueueDelegate
CSAudioQueueDelegate,
CSROMReciverViewDelegate
{
fileprivate let actionLock = NSLock()
fileprivate let drawLock = NSLock()
@ -152,12 +153,27 @@ class MachineDocument:
}
// MARK: configuring
fileprivate var missingROMs: [CSMissingROM] = []
fileprivate var selectedMachine: CSStaticAnalyser?
func configureAs(_ analysis: CSStaticAnalyser) {
if let machine = CSMachine(analyser: analysis) {
let missingROMs = NSMutableArray()
if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) {
self.selectedMachine = nil
self.machine = machine
self.optionsPanelNibName = analysis.optionsPanelNibName
setupMachineOutput()
setupActivityDisplay()
} else {
// Store the selected machine and list of missing ROMs, and
// show the missing ROMs dialogue.
self.missingROMs = []
for untypedMissingROM in missingROMs {
self.missingROMs.append(untypedMissingROM as! CSMissingROM)
}
self.selectedMachine = analysis
requestRoms()
}
}
@ -303,19 +319,173 @@ class MachineDocument:
}
}
// MARK: New machine creation
// MARK: New machine creation.
@IBOutlet var machinePicker: MachinePicker?
@IBOutlet var machinePickerPanel: NSWindow?
@IBAction func createMachine(_ sender: NSButton?) {
self.configureAs(machinePicker!.selectedMachine())
machinePicker = nil
let selectedMachine = machinePicker!.selectedMachine()
self.windowControllers[0].window?.endSheet(self.machinePickerPanel!)
machinePicker = nil
self.configureAs(selectedMachine)
}
@IBAction func cancelCreateMachine(_ sender: NSButton?) {
close()
}
// MARK: User ROM provision.
@IBOutlet var romRequesterPanel: NSWindow?
@IBOutlet var romRequesterText: NSTextField?
@IBOutlet var romReceiverErrorField: NSTextField?
@IBOutlet var romReceiverView: CSROMReceiverView?
private var romRequestBaseText = ""
func requestRoms() {
// Load the ROM requester dialogue.
Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil)
self.romReceiverView!.delegate = self
self.romRequestBaseText = romRequesterText!.stringValue
romReceiverErrorField?.alphaValue = 0.0
// Populate the current absentee list.
populateMissingRomList()
// Show the thing.
self.windowControllers[0].window?.beginSheet(self.romRequesterPanel!, completionHandler: nil)
}
@IBAction func cancelRequestROMs(_ sender: NSButton?) {
close()
}
func populateMissingRomList() {
// Fill in the missing details; first build a list of all the individual
// 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) {
// 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 no ROMs are still missing, start the machine.
do {
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.selectedMachine!)
} else {
populateMissingRomList()
}
} else {
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
// NSAnimationContext hence the various stateful diplications below. isShowingError
// should be essentially a duplicate of the current alphaValue, and animationCount
// is to resolve my inability to figure out how to cancel scheduled animations.
private var errorText = ""
private var isShowingError = false
private var animationCount = 0
private func showRomReceiverError(error: String) {
// Set or append the new error.
if self.errorText.count > 0 {
self.errorText = self.errorText + "\n" + error
} else {
self.errorText = error
}
// Apply the new complete text.
romReceiverErrorField!.stringValue = self.errorText
if !isShowingError {
// Schedule the box's appearance.
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = 0.1
romReceiverErrorField?.animator().alphaValue = 1.0
NSAnimationContext.endGrouping()
isShowingError = true
}
// Schedule the box to disappear.
self.animationCount = self.animationCount + 1
let capturedAnimationCount = animationCount
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(2)) {
if self.animationCount == capturedAnimationCount {
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = 1.0
self.romReceiverErrorField?.animator().alphaValue = 0.0
NSAnimationContext.endGrouping()
self.isShowingError = false
self.errorText = ""
}
}
}
// MARK: Joystick-via-the-keyboard selection
@IBAction func useKeyboardAsKeyboard(_ sender: NSMenuItem?) {
machine.inputMode = .keyboard

View File

@ -509,6 +509,10 @@
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy35</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-apple-diskimage</string>
</array>
<key>CFBundleTypeName</key>
<string>DiskCopy 4.2 Disk Image</string>
<key>CFBundleTypeOSTypes</key>

View File

@ -33,6 +33,14 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
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.
#import "CSAtari2600.h"
#import "CSZX8081.h"
@ -45,8 +53,10 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
Initialises an instance of CSMachine.
@param result The CSStaticAnalyser result that describes the machine needed.
@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.
*/
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray<CSMissingROM *> *)missingROMs NS_DESIGNATED_INITIALIZER;
- (void)runForInterval:(NSTimeInterval)interval;

View File

@ -74,6 +74,68 @@ struct ActivityObserver: public Activity::Observer {
__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 {
NSString *_machineName;
NSString *_fileName;
NSString *_descriptiveName;
NSUInteger _size;
NSArray<NSNumber *> *_crc32s;
}
- (NSString *)machineName {
return _machineName;
}
- (void)setMachineName:(NSString *)machineName {
_machineName = [machineName copy];
}
- (NSString *)fileName {
return _fileName;
}
- (void)setFileName:(NSString *)fileName {
_fileName = [fileName copy];
}
- (NSString *)descriptiveName {
return _descriptiveName;
}
- (void)setDescriptiveName:(NSString *)descriptiveName {
_descriptiveName = [descriptiveName copy];
}
- (NSUInteger)size {
return _size;
}
- (void)setSize:(NSUInteger)size {
_size = size;
}
- (NSArray<NSNumber *> *)crc32s {
return _crc32s;
}
- (void)setCrc32s:(NSArray<NSNumber *> *)crc32s {
_crc32s = [crc32s copy];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@/%@, %@ bytes, CRCs: %@", _fileName, _descriptiveName, @(_size), _crc32s];
}
@end
@implementation CSMachine {
SpeakerDelegate _speakerDelegate;
ActivityObserver _activityObserver;
@ -90,14 +152,37 @@ struct ActivityObserver: public Activity::Observer {
std::unique_ptr<Outputs::Display::OpenGL::ScanTarget> _scanTarget;
}
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result {
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableArray<CSMissingROM *> *)missingROMs {
self = [super init];
if(self) {
_analyser = result;
Machine::Error error;
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error));
if(!_machine) return nil;
std::vector<ROMMachine::ROM> missing_roms;
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error));
if(!_machine) {
for(const auto &missing_rom : missing_roms) {
CSMissingROM *rom = [[CSMissingROM alloc] init];
// Copy/convert the primitive fields.
rom.machineName = [NSString stringWithUTF8String:missing_rom.machine_name.c_str()];
rom.fileName = [NSString stringWithUTF8String:missing_rom.file_name.c_str()];
rom.descriptiveName = missing_rom.descriptive_name.empty() ? nil : [NSString stringWithUTF8String:missing_rom.descriptive_name.c_str()];
rom.size = missing_rom.size;
// Convert the CRC list.
NSMutableArray<NSNumber *> *crc32s = [[NSMutableArray alloc] init];
for(const auto &crc : missing_rom.crc32s) {
[crc32s addObject:@(crc)];
}
rom.crc32s = [crc32s copy];
// Add to the missing list.
[missingROMs addObject:rom];
}
return nil;
}
_inputMode =
(_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive())

View File

@ -8,4 +8,4 @@
#include "ROMMachine.hpp"
ROMMachine::ROMFetcher CSROMFetcher();
ROMMachine::ROMFetcher CSROMFetcher(std::vector<ROMMachine::ROM> *missing_roms = nullptr);

View File

@ -14,15 +14,39 @@
#include <string>
ROMMachine::ROMFetcher CSROMFetcher() {
return [] (const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
NSString *subDirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:machine.c_str()]];
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
for(const auto &name: names) {
NSData *fileData = [[NSBundle mainBundle] dataForResource:[NSString stringWithUTF8String:name.c_str()] withExtension:nil subdirectory:subDirectory];
ROMMachine::ROMFetcher CSROMFetcher(std::vector<ROMMachine::ROM> *missing_roms) {
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];
if(!fileData)
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
for(const auto &rom: roms) {
NSData *fileData;
NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:rom.machine_name.c_str()]];
// Check for this file first within the application support directories.
for(NSURL *supportURL in supportURLs) {
NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory]
URLByAppendingPathComponent:[NSString stringWithUTF8String:rom.file_name.c_str()]];
fileData = [NSData dataWithContentsOfURL:fullURL];
if(fileData) break;
}
// Failing that, check inside the application bundle.
if(!fileData) {
fileData = [[NSBundle mainBundle]
dataForResource:[NSString stringWithUTF8String:rom.file_name.c_str()]
withExtension:nil
subdirectory:subdirectory];
}
// Store an appropriate result, accumulating a list of the missing if requested.
if(!fileData) {
results.emplace_back(nullptr);
if(missing_roms) {
missing_roms->push_back(rom);
}
}
else {
std::unique_ptr<std::vector<std::uint8_t>> data(new std::vector<std::uint8_t>);
*data = fileData.stdVector8;

View File

@ -0,0 +1,17 @@
//
// NSData+CRC32.m
// Clock Signal
//
// Created by Thomas Harte on 22/07/2019.
// Copyright 2019 Thomas Harte. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <stdint.h>
@interface NSData (CRC32)
@property(nonnull, nonatomic, readonly) NSNumber *crc32;
@end

View File

@ -0,0 +1,19 @@
//
// NSData+CRC32.m
// Clock Signal
//
// Created by Thomas Harte on 22/07/2019.
// Copyright 2019 Thomas Harte. All rights reserved.
//
#import "NSData+CRC32.h"
#include <zlib.h>
@implementation NSData (StdVector)
- (NSNumber *)crc32 {
return @(crc32(crc32(0, Z_NULL, 0), self.bytes, (uInt)self.length));
}
@end

View File

@ -64,7 +64,7 @@ Gw
<tabViewItems>
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
<view key="view" id="dHz-Yv-GNq">
<rect key="frame" x="10" y="33" width="554" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
@ -168,7 +168,7 @@ Gw
</tabViewItem>
<tabViewItem label="Electron" identifier="electron" id="muc-z9-Vqc">
<view key="view" id="SRc-2D-95G">
<rect key="frame" x="10" y="33" width="654" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JqM-IK-FMP">
@ -199,13 +199,29 @@ Gw
</tabViewItem>
<tabViewItem label="Macintosh" identifier="mac" id="lmR-z3-xSm">
<view key="view" id="7Yf-vi-Q0W">
<rect key="frame" x="10" y="33" width="654" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="F6e-UC-25u">
<rect key="frame" x="15" y="74" width="574" height="17"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="At present Clock Signal emulates only the Macintosh 512ke." id="IGV-Yp-6Af">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="F6e-UC-25u" secondAttribute="trailing" constant="17" id="42z-hS-aPq"/>
<constraint firstItem="F6e-UC-25u" firstAttribute="leading" secondItem="7Yf-vi-Q0W" secondAttribute="leading" constant="17" id="bIg-C3-xdz"/>
<constraint firstItem="F6e-UC-25u" firstAttribute="top" secondItem="7Yf-vi-Q0W" secondAttribute="top" constant="3" id="cxs-OP-oH5"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="F6e-UC-25u" secondAttribute="bottom" constant="17" id="vpF-ER-pmD"/>
</constraints>
</view>
</tabViewItem>
<tabViewItem label="MSX" identifier="msx" id="6SR-DY-zdI">
<view key="view" id="mWD-An-tR7">
<rect key="frame" x="10" y="33" width="654" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8xT-Pr-8SE">
@ -253,7 +269,7 @@ Gw
</tabViewItem>
<tabViewItem label="Oric" identifier="oric" id="NSx-DC-p4M">
<view key="view" id="sOR-e0-8iZ">
<rect key="frame" x="10" y="33" width="654" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
@ -318,7 +334,7 @@ Gw
</tabViewItem>
<tabViewItem label="Vic-20" identifier="vic20" id="cyO-PU-hSU">
<view key="view" id="fLI-XB-QCr">
<rect key="frame" x="10" y="33" width="554" height="94"/>
<rect key="frame" x="10" y="33" width="604" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ueK-gq-gaF">
@ -497,7 +513,7 @@ Gw
<constraint firstItem="VUb-QG-x7c" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="zT3-Ea-QQJ"/>
</constraints>
</view>
<point key="canvasLocation" x="34" y="89"/>
<point key="canvasLocation" x="34" y="88.5"/>
</window>
<customObject id="192-Eb-Rpg" customClass="MachinePicker" customModule="Clock_Signal" customModuleProvider="target">
<connections>

View File

@ -0,0 +1,27 @@
//
// CSROMReceiverView.h
// Clock Signal
//
// Created by Thomas Harte on 22/07/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class CSROMReceiverView;
@protocol CSROMReciverViewDelegate
/*!
Announces receipt of a file by drag and drop to the delegate.
@param view The view making the request.
@param URL The file URL of the received file.
*/
- (void)romReceiverView:(nonnull CSROMReceiverView *)view didReceiveFileAtURL:(nonnull NSURL *)URL;
@end
@interface CSROMReceiverView : NSView
@property(nonatomic, weak, nullable) id <CSROMReciverViewDelegate> delegate;
@end

View File

@ -0,0 +1,40 @@
//
// CSROMReceiverView.m
// Clock Signal
//
// Created by Thomas Harte on 22/07/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import "CSROMReceiverView.h"
@interface CSROMReceiverView () <NSDraggingDestination>
@end
@implementation CSROMReceiverView
- (void)awakeFromNib {
[super awakeFromNib];
// Accept file URLs by drag and drop.
[self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]];
}
#pragma mark - NSDraggingDestination
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
// Just forward the URLs.
for(NSPasteboardItem *item in [[sender draggingPasteboard] pasteboardItems]) {
NSURL *URL = [NSURL URLWithString:[item stringForType:(__bridge NSString *)kUTTypeFileURL]];
[self.delegate romReceiverView:self didReceiveFileAtURL:URL];
}
return YES;
}
- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender {
// The emulator will take a copy of any deposited ROM files.
return NSDragOperationCopy;
}
@end

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
<connections>
<outlet property="romReceiverErrorField" destination="Bia-0m-GxK" id="wnH-Kz-uXD"/>
<outlet property="romReceiverView" destination="EiT-Mj-1SZ" id="Y2M-Qd-i7y"/>
<outlet property="romRequesterPanel" destination="QvC-M9-y7g" id="saI-YH-9NP"/>
<outlet property="romRequesterText" destination="5qG-I3-Qav" id="J9M-T8-Xyy"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="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="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" customClass="CSROMReceiverView">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5qG-I3-Qav">
<rect key="frame" x="18" y="148" width="444" height="102"/>
<textFieldCell key="cell" enabled="NO" allowsUndo="NO" id="itJ-2T-0ia">
<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 view:
</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nvl-dm-8bK">
<rect key="frame" x="384" y="13" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oS1-Pk-LsO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="cancelRequestROMs:" target="-2" id="58q-yi-dlv"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bia-0m-GxK">
<rect key="frame" x="20" y="61" width="442" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="center" title="Multiline Label" drawsBackground="YES" id="8jl-xs-LjP">
<font key="font" metaFont="message"/>
<color key="textColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="Bia-0m-GxK" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" id="9Lj-3p-7Vr"/>
<constraint firstAttribute="trailing" secondItem="5qG-I3-Qav" secondAttribute="trailing" constant="20" id="AwT-WO-Nyf"/>
<constraint firstAttribute="bottom" secondItem="nvl-dm-8bK" secondAttribute="bottom" constant="20" id="BU0-5T-YRH"/>
<constraint firstAttribute="bottom" secondItem="Bia-0m-GxK" secondAttribute="bottom" constant="61" id="ENn-7a-NGa"/>
<constraint firstAttribute="trailing" secondItem="Bia-0m-GxK" secondAttribute="trailing" constant="18" id="UUy-bf-87w"/>
<constraint firstItem="5qG-I3-Qav" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" id="bRM-z1-cRf"/>
<constraint firstAttribute="trailing" secondItem="nvl-dm-8bK" secondAttribute="trailing" constant="20" id="eQI-Nj-ane"/>
<constraint firstItem="nvl-dm-8bK" firstAttribute="top" relation="greaterThanOrEqual" secondItem="5qG-I3-Qav" secondAttribute="bottom" constant="17" id="plB-x4-He5"/>
<constraint firstItem="5qG-I3-Qav" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="qeC-Rh-hmr"/>
</constraints>
</view>
</window>
</objects>
</document>

View File

@ -9,6 +9,7 @@
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
@ -339,21 +340,21 @@ int main(int argc, char *argv[]) {
}
std::cout << std::endl;
}
return 0;
return EXIT_SUCCESS;
}
// Perform a sanity check on arguments.
if(arguments.file_name.empty()) {
std::cerr << "Usage: " << final_path_component(argv[0]) << usage_suffix << std::endl;
std::cerr << "Use --help to learn more about available options." << std::endl;
return -1;
return EXIT_FAILURE;
}
// Determine the machine for the supplied file.
Analyser::Static::TargetList targets = Analyser::Static::GetTargets(arguments.file_name);
if(targets.empty()) {
std::cerr << "Cannot open " << arguments.file_name << "; no target machine found" << std::endl;
return -1;
return EXIT_FAILURE;
}
Concurrency::BestEffortUpdater updater;
@ -365,12 +366,10 @@ int main(int argc, char *argv[]) {
// /usr/local/share/CLK/[system];
// /usr/share/CLK/[system]; or
// [user-supplied path]/[system]
std::vector<std::string> rom_names;
std::string machine_name;
ROMMachine::ROMFetcher rom_fetcher = [&rom_names, &machine_name, &arguments]
(const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
rom_names.insert(rom_names.end(), names.begin(), names.end());
machine_name = machine;
std::vector<ROMMachine::ROM> requested_roms;
ROMMachine::ROMFetcher rom_fetcher = [&requested_roms, &arguments]
(const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
requested_roms.insert(requested_roms.end(), roms.begin(), roms.end());
std::vector<std::string> paths = {
"/usr/local/share/CLK/",
@ -386,10 +385,10 @@ int main(int argc, char *argv[]) {
}
std::vector<std::unique_ptr<std::vector<uint8_t>>> results;
for(const auto &name: names) {
for(const auto &rom: roms) {
FILE *file = nullptr;
for(const auto &path: paths) {
std::string local_path = path + machine + "/" + name;
std::string local_path = path + rom.machine_name + "/" + rom.file_name;
file = std::fopen(local_path.c_str(), "rb");
if(file) break;
}
@ -425,13 +424,17 @@ int main(int argc, char *argv[]) {
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 << "One or more of the following was needed but not found:" << std::endl;
for(const auto &name: rom_names) {
std::cerr << machine_name << '/' << name << std::endl;
for(const auto &rom: requested_roms) {
std::cerr << rom.machine_name << '/' << rom.file_name;
if(!rom.descriptive_name.empty()) {
std::cerr << " (" << rom.descriptive_name << ")";
}
std::cerr << std::endl;
}
break;
}
return -1;
return EXIT_FAILURE;
}
best_effort_updater_delegate.machine = machine.get();
@ -441,7 +444,7 @@ int main(int argc, char *argv[]) {
// Attempt to set up video and audio.
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return -1;
return EXIT_FAILURE;
}
// Ask for no depth buffer, a core profile and vsync-aligned rendering.
@ -463,7 +466,7 @@ int main(int argc, char *argv[]) {
if(!window || !gl_context) {
std::cerr << "Could not create " << (window ? "OpenGL context" : "window");
std::cerr << "; reported error: \"" << SDL_GetError() << "\"" << std::endl;
return -1;
return EXIT_FAILURE;
}
SDL_GL_MakeCurrent(window, gl_context);
@ -816,5 +819,5 @@ int main(int argc, char *argv[]) {
SDL_DestroyWindow( window );
SDL_Quit();
return 0;
return EXIT_SUCCESS;
}