1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-10-25 09:27:01 +00:00

Compare commits

...

77 Commits

Author SHA1 Message Date
Thomas Harte
11228dc265 Merge pull request #937 from TomHarte/XKeySyms
Eliminate magic constants in Qt/X11 keyboard code.
2021-05-05 22:21:31 -04:00
Thomas Harte
ef50967793 Limit X11 linkage to Linux. 2021-05-05 22:17:24 -04:00
Thomas Harte
5f6c08b7e0 Avoid partial struct instantiation. 2021-05-05 22:00:50 -04:00
Thomas Harte
6cb23ec5be Tidy up and comment. 2021-05-05 21:58:54 -04:00
Thomas Harte
1bae70bcf8 Correct capitalisation. 2021-05-05 21:49:01 -04:00
Thomas Harte
9820591ba4 Corrects enum references. 2021-05-05 21:46:34 -04:00
Thomas Harte
77071b3c69 Adds KeySym -> key lookup. 2021-05-05 21:41:59 -04:00
Thomas Harte
335e839b31 Wrangles a single working call to XKeysymToKeycode. 2021-05-05 21:35:08 -04:00
Thomas Harte
6fe947b8b9 Fix class name, add constructor. 2021-05-05 19:17:23 -04:00
Thomas Harte
22b29e77a7 Add keyboard.cpp/h to the Qt project. 2021-05-05 19:06:25 -04:00
Thomas Harte
4858cfce6b Starts to factor out the keyboard mapper.
The more easily to clarify as to #includes, etc, and to allow for a relevant constructor.
2021-05-05 18:56:10 -04:00
Thomas Harte
8da3e91f5e Merge branch 'master' into XKeySyms 2021-05-03 22:23:55 -04:00
Thomas Harte
012235bfeb Merge pull request #936 from TomHarte/Style
Correct minor style errors.
2021-05-03 22:23:27 -04:00
Thomas Harte
052e284c33 Add overt fallthrough. 2021-05-03 22:17:43 -04:00
Thomas Harte
32e3dd71b1 Be overt in empty std::string construction. 2021-05-03 22:17:32 -04:00
Thomas Harte
95f4272919 Make sure size_t is visible. 2021-05-03 22:17:25 -04:00
Thomas Harte
00679b6135 t may be unused, per the if constexpr. 2021-05-03 22:17:19 -04:00
Thomas Harte
2c18bb4508 Make it overt that this can't return without a value. 2021-05-03 22:17:12 -04:00
Thomas Harte
0cf1c9040a Add missing fallthrough declaration. 2021-05-03 22:17:06 -04:00
Thomas Harte
9196341482 Retrenches: it seems nativeVirtualKey does what I want.
Hooray!
2021-05-03 21:45:53 -04:00
Thomas Harte
685140a4c2 Correct Qt -> QT. 2021-05-03 21:18:14 -04:00
Thomas Harte
1465b0ee4d Shunt X11 code to bottom of file, to avoid #include interference. 2021-05-03 21:15:20 -04:00
Thomas Harte
0bf6b765d3 Further namespace/name corrections. 2021-05-03 21:11:47 -04:00
Thomas Harte
4774676e2a Correct keypad symbols, push X11 into a namespace. 2021-05-03 21:09:01 -04:00
Thomas Harte
9c29655da2 Add x11extras as per use of <QX11Info>. 2021-05-03 20:43:22 -04:00
Thomas Harte
c8ab18f2b6 Add overt fallthrough. 2021-05-03 20:38:50 -04:00
Thomas Harte
8ebce466db Be overt in empty std::string construction. 2021-05-03 20:35:23 -04:00
Thomas Harte
1b39b17125 Make sure size_t is visible. 2021-05-03 20:33:25 -04:00
Thomas Harte
5a46853075 t may be unused, per the if constexpr. 2021-05-03 20:32:16 -04:00
Thomas Harte
48ad4d4c4c Make it overt that this can't return without a value. 2021-05-03 20:31:39 -04:00
Thomas Harte
056a036712 Add missing fallthrough declaration. 2021-05-03 20:31:13 -04:00
Thomas Harte
70eaa79108 Makes an attempt to use X11 KeySyms.
Rather than hard-coding a mapping.
2021-05-03 18:51:58 -04:00
Thomas Harte
c906dc3c0a Merge pull request #935 from TomHarte/OricJoystick
Adds Altai-style joystick support for the Oric.
2021-04-29 20:15:42 -04:00
Thomas Harte
d1dcb41b6f Adds Altai-style joystick support. 2021-04-29 18:29:29 -04:00
Thomas Harte
96ac86a757 Merge pull request #934 from TomHarte/OricTapes
Relaxes Oric .tap signature check.
2021-04-29 18:14:36 -04:00
Thomas Harte
4919786825 Relaxes Oric .tap signature check. 2021-04-29 18:00:02 -04:00
Thomas Harte
24b4185714 Merge pull request #933 from TomHarte/SpectrumJoystick
Adds ZX Spectrum joystick support.
2021-04-28 21:08:36 -04:00
Thomas Harte
ad10d0037a Inverts the Game Controller Framework value of the y axis. 2021-04-28 20:31:35 -04:00
Thomas Harte
b6554c8255 Adds joystick support. 2021-04-28 20:19:01 -04:00
Thomas Harte
01dc83d0d6 Merge pull request #932 from MaddTheSane/xcodemaintenance
Xcode maintenance.
2021-04-27 19:53:51 -04:00
C.W. Betts
2fd08789ab Xcode maintenance. 2021-04-27 12:50:26 -06:00
Thomas Harte
bc9e529995 Merge pull request #931 from TomHarte/FieldName
This field is counted in half-cycles.
2021-04-26 21:33:38 -04:00
Thomas Harte
708c24cc57 This field is counted in half-cycles. 2021-04-26 21:20:32 -04:00
Thomas Harte
7fb3048257 Update AllDisk and AllTape. 2021-04-26 21:04:25 -04:00
Thomas Harte
9319f0525a Merge pull request #930 from TomHarte/SZX
Adds SZX support.
2021-04-26 20:57:06 -04:00
Thomas Harte
b7a62e0121 Adds SZX support.
Tweaking exposed Spectrum state object as relevant.
2021-04-26 20:47:28 -04:00
Thomas Harte
bd5dd9b9a3 Merge pull request #929 from TomHarte/SpectrumSnapshots
Adds loading of state snapshots for the ZX Spectrum
2021-04-26 17:44:02 -04:00
Thomas Harte
3348167c46 Ensures AY registers are conveyed. 2021-04-26 17:39:11 -04:00
Thomas Harte
700c505974 Ensures the ZX Spectrum properly reports its display type. 2021-04-25 21:16:22 -04:00
Thomas Harte
d403036d86 Reduce bounce at Spectrum startup. 2021-04-25 20:56:57 -04:00
Thomas Harte
5e08d7db39 Carries through paging state; avoids file rereads. 2021-04-25 20:46:49 -04:00
Thomas Harte
c34cb310a8 Switches to more straightforward handler for .z80-style compression. 2021-04-25 18:07:36 -04:00
Thomas Harte
8d86aa69bc Adds an assert to check handling of compressed data. 2021-04-25 18:02:31 -04:00
Thomas Harte
cc41ccc5f1 Adds RAM deserialisation. 2021-04-25 17:55:52 -04:00
Thomas Harte
e6252fe0ed Sneaks up towards loading RAM. 2021-04-25 17:34:43 -04:00
Thomas Harte
03577de675 Adds an empty vessel for .z80 support. 2021-04-25 16:54:34 -04:00
Thomas Harte
205518ba75 Switch to more efficient copy. 2021-04-25 16:51:07 -04:00
Thomas Harte
2510064218 Completes state object.
Subject to not yet dealing with last_fetches_ and last_contended_access_ correctly. Thought required.
2021-04-25 14:20:40 -04:00
Thomas Harte
0ef2806970 Adds just enough to ensure that border state gets through. 2021-04-25 14:16:35 -04:00
Thomas Harte
d80f03e369 Corrects longstanding deviation from naming convention. 2021-04-25 14:11:36 -04:00
Thomas Harte
fd271d920b Adds capture and forwarding of border colour. 2021-04-25 14:00:12 -04:00
Thomas Harte
2bbf8bc9fa Ensures 16/48kb snapshots are properly copied into place. 2021-04-25 13:27:11 -04:00
Thomas Harte
9b65d56ed0 Resolves potential flaw in POPping here. 2021-04-25 13:26:53 -04:00
Thomas Harte
a5098a60ec Attempts to get in-SNA software to start. 2021-04-25 13:18:26 -04:00
Thomas Harte
0ebd900e40 Baby steps: apply Z80 state.
As far as it currently is. Since SNA is leaving the PC at the default of 0x0000, this currently has no visible effect.
2021-04-25 13:03:24 -04:00
Thomas Harte
7aeb17ac92 Corrects HeaderDoc/etc directive. 2021-04-25 13:01:23 -04:00
Thomas Harte
cc78bfb229 Forwards most of the Z80 state. 2021-04-25 13:00:43 -04:00
Thomas Harte
485c2a866c Without yet a struct for Spectrum states, at least checks general wiring. 2021-04-24 23:38:00 -04:00
Thomas Harte
5b419ca5bf Add State folder to Scons and Qt projects. 2021-04-24 23:25:08 -04:00
Thomas Harte
14ae579fca Add further note to future self. 2021-04-24 23:19:41 -04:00
Thomas Harte
1c2ea0d7fe unique_ptr makes more sense here. 2021-04-24 23:19:30 -04:00
Thomas Harte
e7a9ae18a1 Introduce further default state. 2021-04-24 23:18:00 -04:00
Thomas Harte
d61f478a39 Basic sketch for state snapshots: an extra field on Target.
I think it doesn't make sense for states to own a target as that complicates the concept of Media. Plus they're distinct because it makes sense to have only one per Target. Let's see how this pans out.
2021-04-24 23:17:47 -04:00
Thomas Harte
9cc747b3e2 Resolves potential source of errors: specifying incorrect table size.
(Having made exactly this mistake with the ZX Spectrum)
2021-04-24 12:10:28 -04:00
Thomas Harte
2f223f7db2 Spectrum emulation is no longer +2a/+3 specific. 2021-04-23 22:55:54 -04:00
Thomas Harte
17f11a3be3 Merge pull request #928 from TomHarte/ContentionTests
Add timing tests, fix +3 discrepancy.
2021-04-23 22:54:34 -04:00
Thomas Harte
37dcf61130 Add timing tests, fix +3 discrepancy. 2021-04-23 22:29:57 -04:00
43 changed files with 1750 additions and 443 deletions

View File

@@ -57,6 +57,11 @@
#include "../../Storage/MassStorage/Formats/DAT.hpp"
#include "../../Storage/MassStorage/Formats/HFV.hpp"
// State Snapshots
#include "../../Storage/State/SNA.hpp"
#include "../../Storage/State/SZX.hpp"
#include "../../Storage/State/Z80.hpp"
// Tapes
#include "../../Storage/Tape/Formats/CAS.hpp"
#include "../../Storage/Tape/Formats/CommodoreTAP.hpp"
@@ -73,15 +78,23 @@
using namespace Analyser::Static;
static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) {
Media result;
namespace {
std::string get_extension(const std::string &name) {
// Get the extension, if any; it will be assumed that extensions are reliable, so an extension is a broad-phase
// test as to file format.
std::string::size_type final_dot = file_name.find_last_of(".");
if(final_dot == std::string::npos) return result;
std::string extension = file_name.substr(final_dot + 1);
std::string::size_type final_dot = name.find_last_of(".");
if(final_dot == std::string::npos) return name;
std::string extension = name.substr(final_dot + 1);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
return extension;
}
}
static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) {
Media result;
const std::string extension = get_extension(file_name);
#define InsertInstance(list, instance, platforms) \
list.emplace_back(instance);\
@@ -199,14 +212,35 @@ Media Analyser::Static::GetMedia(const std::string &file_name) {
TargetList Analyser::Static::GetTargets(const std::string &file_name) {
TargetList targets;
const std::string extension = get_extension(file_name);
// Check whether the file directly identifies a target; if so then just return that.
#define Format(ext, class) \
if(extension == ext) { \
try { \
auto target = Storage::State::class::load(file_name); \
if(target) { \
targets.push_back(std::move(target)); \
return targets; \
} \
} catch(...) {} \
}
Format("sna", SNA);
Format("szx", SZX);
Format("z80", Z80);
#undef TryInsert
// Otherwise:
//
// Collect all disks, tapes ROMs, etc as can be extrapolated from this file, forming the
// union of all platforms this file might be a target for.
TargetPlatform::IntType potential_platforms = 0;
Media media = GetMediaAndPlatforms(file_name, potential_platforms);
// Hand off to platform-specific determination of whether these things are actually compatible and,
// if so, how to load them.
// Hand off to platform-specific determination of whether these
// things are actually compatible and, if so, how to load them.
#define Append(x) if(potential_platforms & TargetPlatform::x) {\
auto new_targets = x::GetTargets(media, file_name, potential_platforms);\
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\

View File

@@ -15,6 +15,7 @@
#include "../../Storage/Disk/Disk.hpp"
#include "../../Storage/MassStorage/MassStorageDevice.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../../Reflection/Struct.hpp"
#include <memory>
#include <string>
@@ -23,8 +24,10 @@
namespace Analyser {
namespace Static {
struct State;
/*!
A list of disks, tapes and cartridges.
A list of disks, tapes and cartridges, and possibly a state snapshot.
*/
struct Media {
std::vector<std::shared_ptr<Storage::Disk::Disk>> disks;
@@ -48,13 +51,16 @@ struct Media {
};
/*!
A list of disks, tapes and cartridges plus information about the machine to which to attach them and its configuration,
and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness.
Describes a machine and possibly its state; conventionally subclassed to add other machine-specific configuration fields and any
necessary instructions on how to launch any software provided, plus a measure of confidence in this target's correctness.
*/
struct Target {
Target(Machine machine) : machine(machine) {}
virtual ~Target() {}
// This field is entirely optional.
std::unique_ptr<Reflection::Struct> state;
Machine machine;
Media media;
float confidence = 0.0f;

View File

@@ -12,6 +12,8 @@
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
#include "../../Reflection/Struct.hpp"
namespace GI {
namespace AY38910 {
@@ -162,6 +164,8 @@ template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource
uint8_t a_left_ = 255, a_right_ = 255;
uint8_t b_left_ = 255, b_right_ = 255;
uint8_t c_left_ = 255, c_right_ = 255;
friend struct State;
};
/*!
@@ -192,6 +196,29 @@ struct Utility {
};
struct State: public Reflection::StructImpl<State> {
uint8_t registers[16]{};
uint8_t selected_register = 0;
// TODO: all audio-production thread state.
State() {
if(needs_declare()) {
DeclareField(registers);
DeclareField(selected_register);
}
}
template <typename AY> void apply(AY &target) {
// Establish emulator-thread state
for(uint8_t c = 0; c < 16; c++) {
target.select_register(c);
target.set_register_value(registers[c]);
}
target.select_register(selected_register);
}
};
}
}

View File

@@ -151,7 +151,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const {
#undef SHIFT
#undef X
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
return table_lookup_sequence_for_character(key_sequences, character);
}
bool CharacterMapper::needs_pause_after_key(uint16_t key) const {

View File

@@ -16,6 +16,7 @@ void Keyboard::perform_command(const Command &command) {
switch(command.type) {
case Command::Type::Reset:
modifiers_ = 0xffff;
[[fallthrough]];
case Command::Type::Flush: {
std::lock_guard lock_guard(keys_mutex_);
pending_events_.clear();

View File

@@ -151,5 +151,5 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const {
#undef SHIFT
#undef X
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
return table_lookup_sequence_for_character(key_sequences, character);
}

View File

@@ -145,7 +145,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const {
#undef SHIFT
#undef X
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
return table_lookup_sequence_for_character(key_sequences, character);
}
bool CharacterMapper::needs_pause_after_key(uint16_t key) const {

View File

@@ -20,6 +20,7 @@
#include "MediaTarget.hpp"
#include "MouseMachine.hpp"
#include "ScanProducer.hpp"
#include "StateProducer.hpp"
#include "TimedMachine.hpp"
#endif /* MachineTypes_h */

View File

@@ -127,5 +127,5 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const {
#undef SHIFT
#undef X
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
return table_lookup_sequence_for_character(key_sequences, character);
}

View File

@@ -40,6 +40,45 @@
#include <memory>
#include <vector>
namespace {
/*!
Provides an Altai-style joystick.
*/
class Joystick: public Inputs::ConcreteJoystick {
public:
Joystick() :
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
Input(Input::Right),
Input(Input::Fire)
}) {}
void did_set_input(const Input &digital_input, bool is_active) final {
#define APPLY(b) if(is_active) state_ &= ~b; else state_ |= b;
switch(digital_input.type) {
default: return;
case Input::Right: APPLY(0x02); break;
case Input::Left: APPLY(0x01); break;
case Input::Down: APPLY(0x08); break;
case Input::Up: APPLY(0x10); break;
case Input::Fire: APPLY(0x20); break;
}
#undef APPLY
}
uint8_t get_state() {
return state_;
}
private:
uint8_t state_ = 0xff;
};
}
namespace Oric {
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
@@ -140,7 +179,12 @@ class TapePlayer: public Storage::Tape::BinaryTapePlayer {
class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
public:
VIAPortHandler(Concurrency::DeferringAsyncTaskQueue &audio_queue, AY &ay8910, Speaker &speaker, TapePlayer &tape_player, Keyboard &keyboard) :
audio_queue_(audio_queue), ay8910_(ay8910), speaker_(speaker), tape_player_(tape_player), keyboard_(keyboard) {}
audio_queue_(audio_queue), ay8910_(ay8910), speaker_(speaker), tape_player_(tape_player), keyboard_(keyboard)
{
// Attach a couple of joysticks.
joysticks_.emplace_back(new Joystick);
joysticks_.emplace_back(new Joystick);
}
/*!
Reponds to the 6522's control line output change signal; on an Oric A2 is connected to
@@ -165,6 +209,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
} else {
update_ay();
ay8910_.set_data_input(value);
porta_output_ = value;
}
}
@@ -176,7 +221,10 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
uint8_t column = ay8910_.get_port_output(false) ^ 0xff;
return keyboard_.query_column(column) ? 0x08 : 0x00;
} else {
return ay8910_.get_data_output();
uint8_t result = ay8910_.get_data_output();
if(porta_output_ & 0x40) result &= static_cast<Joystick *>(joysticks_[0].get())->get_state();
if(porta_output_ & 0x80) result &= static_cast<Joystick *>(joysticks_[1].get())->get_state();
return result;
}
}
@@ -193,12 +241,17 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
audio_queue_.perform();
}
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() {
return joysticks_;
}
private:
void update_ay() {
speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush<Cycles>());
}
bool ay_bdir_ = false;
bool ay_bc1_ = false;
uint8_t porta_output_ = 0xff;
HalfCycles cycles_since_ay_update_;
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
@@ -206,12 +259,15 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
Speaker &speaker_;
TapePlayer &tape_player_;
Keyboard &keyboard_;
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
};
template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS6502Esque::Type processor_type> class ConcreteMachine:
public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer,
public MachineTypes::AudioProducer,
public MachineTypes::JoystickMachine,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public Configurable::Device,
@@ -731,8 +787,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS
}
}
// MARK - typing
// MARK: - typing
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
// MARK: - Joysticks
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
return via_port_handler_.get_joysticks();
}
};
}

View File

@@ -275,13 +275,14 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const {
switch(machine_) {
case Machine::ZX80:
return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
return table_lookup_sequence_for_character(zx80_key_sequences, character);
case Machine::ZX81:
return table_lookup_sequence_for_character(zx81_key_sequences, sizeof(zx81_key_sequences), character);
return table_lookup_sequence_for_character(zx81_key_sequences, character);
default:
case Machine::ZXSpectrum:
return table_lookup_sequence_for_character(spectrum_key_sequences, sizeof(zx81_key_sequences), character);
return table_lookup_sequence_for_character(spectrum_key_sequences, character);
}
}

View File

@@ -0,0 +1,53 @@
//
// State.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef State_hpp
#define State_hpp
#include "../../../Reflection/Struct.hpp"
#include "../../../Processors/Z80/State/State.hpp"
#include "Video.hpp"
#include "../../../Components/AY38910/AY38910.hpp"
namespace Sinclair {
namespace ZXSpectrum {
struct State: public Reflection::StructImpl<State> {
CPU::Z80::State z80;
Video::State video;
// In 16kb or 48kb mode, RAM will be 16kb or 48kb and represent
// memory in standard linear order. In 128kb mode, RAM will be
// 128kb with the first 16kb representing bank 0, the next bank 1, etc.
std::vector<uint8_t> ram;
// Meaningful for 128kb machines only.
uint8_t last_7ffd = 0;
GI::AY38910::State ay;
// Meaningful for the +2a and +3 only.
uint8_t last_1ffd = 0;
State() {
if(needs_declare()) {
DeclareField(z80);
DeclareField(video);
DeclareField(ram);
DeclareField(last_7ffd);
DeclareField(last_1ffd);
DeclareField(ay);
}
}
};
}
}
#endif /* State_h */

View File

@@ -12,12 +12,15 @@
#include "../../../Outputs/CRT/CRT.hpp"
#include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../Reflection/Struct.hpp"
#include <algorithm>
namespace Sinclair {
namespace ZXSpectrum {
namespace Video {
enum class VideoTiming {
enum class Timing {
FortyEightK,
OneTwoEightK,
Plus3,
@@ -47,11 +50,11 @@ enum class VideoTiming {
*/
template <VideoTiming timing> class Video {
template <Timing timing> class Video {
private:
struct Timings {
// Number of cycles per line. Will be 224 or 228.
int cycles_per_line;
int half_cycles_per_line;
// Number of lines comprising a whole frame. Will be 311 or 312.
int lines_per_frame;
@@ -68,7 +71,7 @@ template <VideoTiming timing> class Video {
int delays[8];
constexpr Timings(int cycles_per_line, int lines_per_frame, int contention_leadin, int contention_duration, int interrupt_offset, const int *delays) noexcept :
cycles_per_line(cycles_per_line * 2),
half_cycles_per_line(cycles_per_line * 2),
lines_per_frame(lines_per_frame),
contention_leadin(contention_leadin * 2),
contention_duration(contention_duration * 2),
@@ -78,41 +81,41 @@ template <VideoTiming timing> class Video {
};
static constexpr Timings get_timings() {
if constexpr (timing == VideoTiming::Plus3) {
if constexpr (timing == Timing::Plus3) {
constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2};
return Timings(228, 311, 6, 129, 14365, delays);
return Timings(228, 311, 6, 129, 14361, delays);
}
if constexpr (timing == VideoTiming::OneTwoEightK) {
if constexpr (timing == Timing::OneTwoEightK) {
constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0};
return Timings(228, 311, 4, 128, 14361, delays);
}
if constexpr (timing == VideoTiming::FortyEightK) {
if constexpr (timing == Timing::FortyEightK) {
constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0};
return Timings(224, 312, 4, 128, 14335, delays);
}
}
// TODO: how long is the interrupt line held for?
static constexpr int interrupt_duration = 48;
// Interrupt should be held for 32 cycles.
static constexpr int interrupt_duration = 64;
public:
void run_for(HalfCycles duration) {
constexpr auto timings = get_timings();
constexpr int sync_line = (timings.interrupt_time / timings.cycles_per_line) + 1;
constexpr int sync_line = (timings.interrupt_time / timings.half_cycles_per_line) + 1;
constexpr int sync_position = (timing == VideoTiming::FortyEightK) ? 164 * 2 : 166 * 2;
constexpr int sync_position = (timing == Timing::FortyEightK) ? 164 * 2 : 166 * 2;
constexpr int sync_length = 17 * 2;
constexpr int burst_position = sync_position + 40;
constexpr int burst_length = 17;
int cycles_remaining = duration.as<int>();
while(cycles_remaining) {
int line = time_into_frame_ / timings.cycles_per_line;
int offset = time_into_frame_ % timings.cycles_per_line;
const int cycles_this_line = std::min(cycles_remaining, timings.cycles_per_line - offset);
int line = time_into_frame_ / timings.half_cycles_per_line;
int offset = time_into_frame_ % timings.half_cycles_per_line;
const int cycles_this_line = std::min(cycles_remaining, timings.half_cycles_per_line - offset);
const int end_offset = offset + cycles_this_line;
if(!offset) {
@@ -220,7 +223,7 @@ template <VideoTiming timing> class Video {
if(offset >= burst_position && offset < burst_position+burst_length && end_offset > offset) {
const int burst_duration = std::min(burst_position + burst_length, end_offset) - offset;
if constexpr (timing >= VideoTiming::OneTwoEightK) {
if constexpr (timing >= Timing::OneTwoEightK) {
crt_.output_colour_burst(burst_duration, 116, is_alternate_line_);
// The colour burst phase above is an empirical guess. I need to research further.
} else {
@@ -236,7 +239,7 @@ template <VideoTiming timing> class Video {
}
cycles_remaining -= cycles_this_line;
time_into_frame_ = (time_into_frame_ + cycles_this_line) % (timings.cycles_per_line * timings.lines_per_frame);
time_into_frame_ = (time_into_frame_ + cycles_this_line) % (timings.half_cycles_per_line * timings.lines_per_frame);
}
}
@@ -248,7 +251,7 @@ template <VideoTiming timing> class Video {
}
static constexpr int half_cycles_per_line() {
if constexpr (timing == VideoTiming::FortyEightK) {
if constexpr (timing == Timing::FortyEightK) {
// TODO: determine real figure here, if one exists.
// The source I'm looking at now suggests that the theoretical
// ideal of 224*2 ignores the real-life effects of separate
@@ -259,6 +262,39 @@ template <VideoTiming timing> class Video {
}
}
static constexpr HalfCycles frame_duration() {
const auto timings = get_timings();
return HalfCycles(timings.half_cycles_per_line * timings.lines_per_frame);
}
HalfCycles time_since_interrupt() {
const auto timings = get_timings();
if(time_into_frame_ >= timings.interrupt_time) {
return HalfCycles(time_into_frame_ - timings.interrupt_time);
} else {
return HalfCycles(time_into_frame_) + frame_duration() - HalfCycles(timings.interrupt_time);
}
}
void set_time_since_interrupt(const HalfCycles time) {
// Advance using run_for to ensure that all proper CRT interactions occurred.
const auto timings = get_timings();
const auto target = (time + timings.interrupt_time) % frame_duration();
const auto now = HalfCycles(time_into_frame_);
// Maybe this is easy?
if(target == now) return;
// Is the time within this frame?
if(time > now) {
run_for(target - time);
return;
}
// Then it's necessary to finish this frame and run into the next.
run_for(frame_duration() - now + time);
}
public:
Video() :
crt_(half_cycles_per_line(), 2, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red2Green2Blue2)
@@ -267,6 +303,11 @@ template <VideoTiming timing> class Video {
crt_.set_display_type(Outputs::Display::DisplayType::RGB);
crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f));
// Get the CRT roughly into phase.
//
// TODO: this is coupled to an assumption about the initial CRT. Fix.
const auto timings = get_timings();
crt_.output_blank(timings.lines_per_frame*timings.half_cycles_per_line - timings.interrupt_time);
}
void set_video_source(const uint8_t *source) {
@@ -290,7 +331,7 @@ template <VideoTiming timing> class Video {
}
// If not, it'll be in the next batch.
return timings.interrupt_time + timings.cycles_per_line * timings.lines_per_frame - time_into_frame_;
return timings.interrupt_time + timings.half_cycles_per_line * timings.lines_per_frame - time_into_frame_;
}
/*!
@@ -307,15 +348,15 @@ template <VideoTiming timing> class Video {
*/
HalfCycles access_delay(HalfCycles offset) const {
constexpr auto timings = get_timings();
const int delay_time = (time_into_frame_ + offset.as<int>() + timings.contention_leadin) % (timings.cycles_per_line * timings.lines_per_frame);
const int delay_time = (time_into_frame_ + offset.as<int>() + timings.contention_leadin) % (timings.half_cycles_per_line * timings.lines_per_frame);
assert(!(delay_time&1));
// Check for a time within the no-contention window.
if(delay_time >= (191*timings.cycles_per_line + timings.contention_duration)) {
if(delay_time >= (191*timings.half_cycles_per_line + timings.contention_duration)) {
return 0;
}
const int time_into_line = delay_time % timings.cycles_per_line;
const int time_into_line = delay_time % timings.half_cycles_per_line;
if(time_into_line >= timings.contention_duration) {
return 0;
}
@@ -328,21 +369,21 @@ template <VideoTiming timing> class Video {
*/
uint8_t get_floating_value() const {
constexpr auto timings = get_timings();
const uint8_t out_of_bounds = (timing == VideoTiming::Plus3) ? last_contended_access_ : 0xff;
const uint8_t out_of_bounds = (timing == Timing::Plus3) ? last_contended_access_ : 0xff;
const int line = time_into_frame_ / timings.cycles_per_line;
const int line = time_into_frame_ / timings.half_cycles_per_line;
if(line >= 192) {
return out_of_bounds;
}
const int time_into_line = time_into_frame_ % timings.cycles_per_line;
const int time_into_line = time_into_frame_ % timings.half_cycles_per_line;
if(time_into_line >= 256 || (time_into_line&8)) {
return out_of_bounds;
}
// The +2a and +3 always return the low bit as set.
const uint8_t value = last_fetches_[(time_into_line >> 1) & 3];
if constexpr (timing == VideoTiming::Plus3) {
if constexpr (timing == Timing::Plus3) {
return value | 1;
}
return value;
@@ -354,7 +395,7 @@ template <VideoTiming timing> class Video {
bus is accessed when the gate array isn't currently reading.
*/
void set_last_contended_area_access([[maybe_unused]] uint8_t value) {
if constexpr (timing == VideoTiming::Plus3) {
if constexpr (timing == Timing::Plus3) {
last_contended_access_ = value | 1;
}
}
@@ -363,6 +404,7 @@ template <VideoTiming timing> class Video {
Sets the current border colour.
*/
void set_border_colour(uint8_t colour) {
border_byte_ = colour;
border_colour_ = palette[colour];
}
@@ -381,11 +423,17 @@ template <VideoTiming timing> class Video {
crt_.set_display_type(type);
}
/*! Gets the display type. */
Outputs::Display::DisplayType get_display_type() const {
return crt_.get_display_type();
}
private:
int time_into_frame_ = 0;
Outputs::CRT::CRT crt_;
const uint8_t *memory_ = nullptr;
uint8_t border_colour_ = 0;
uint8_t border_byte_ = 0;
uint8_t *pixel_target_ = nullptr;
int attribute_address_ = 0;
@@ -398,6 +446,8 @@ template <VideoTiming timing> class Video {
uint8_t last_fetches_[4] = {0xff, 0xff, 0xff, 0xff};
uint8_t last_contended_access_ = 0xff;
friend struct State;
#define RGB(r, g, b) (r << 4) | (g << 2) | b
static constexpr uint8_t palette[] = {
RGB(0, 0, 0), RGB(0, 0, 2), RGB(2, 0, 0), RGB(2, 0, 2),
@@ -408,6 +458,41 @@ template <VideoTiming timing> class Video {
#undef RGB
};
struct State: public Reflection::StructImpl<State> {
uint8_t border_colour = 0;
int half_cycles_since_interrupt = 0;
bool flash = 0;
int flash_counter = 0;
bool is_alternate_line = false;
State() {
if(needs_declare()) {
DeclareField(border_colour);
DeclareField(half_cycles_since_interrupt);
DeclareField(flash);
DeclareField(flash_counter);
DeclareField(is_alternate_line);
}
}
template <typename Video> State(const Video &source) : State() {
border_colour = source.border_byte_;
flash = source.flash_mask_;
flash_counter = source.flash_counter_;
is_alternate_line = source. is_alternate_line_;
half_cycles_since_interrupt = source.time_since_interrupt().template as<int>();
}
template <typename Video> void apply(Video &target) {
target.set_border_colour(border_colour);
target.flash_mask_ = flash ? 0xff : 0x00;
target.flash_counter_ = flash_counter;
target.is_alternate_line_ = is_alternate_line;
target.set_time_since_interrupt(HalfCycles(half_cycles_since_interrupt));
}
};
}
}
}

View File

@@ -8,9 +8,9 @@
#include "ZXSpectrum.hpp"
#include "State.hpp"
#include "Video.hpp"
#define LOG_PREFIX "[Spectrum] "
#include "../Keyboard/Keyboard.hpp"
#include "../../../Activity/Source.hpp"
#include "../../MachineTypes.hpp"
@@ -24,7 +24,9 @@
// just grab the CPC's version of an FDC.
#include "../../AmstradCPC/FDC.hpp"
#define LOG_PREFIX "[Spectrum] "
#include "../../../Outputs/Log.hpp"
#include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
@@ -39,12 +41,76 @@
#include "../../../ClockReceiver/JustInTime.hpp"
#include "../../../Processors/Z80/State/State.hpp"
#include "../Keyboard/Keyboard.hpp"
#include <array>
namespace {
/*!
Provides a simultaneous Kempston and Interface 2-style joystick.
*/
class Joystick: public Inputs::ConcreteJoystick {
public:
Joystick() :
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
Input(Input::Right),
Input(Input::Fire)
}) {}
void did_set_input(const Input &digital_input, bool is_active) final {
#define APPLY_KEMPSTON(b) if(is_active) kempston_ |= b; else kempston_ &= ~b;
#define APPLY_SINCLAIR(b) if(is_active) sinclair_ &= ~b; else sinclair_ |= b;
switch(digital_input.type) {
default: return;
case Input::Right:
APPLY_KEMPSTON(0x01);
APPLY_SINCLAIR(0x0208);
break;
case Input::Left:
APPLY_KEMPSTON(0x02);
APPLY_SINCLAIR(0x0110);
break;
case Input::Down:
APPLY_KEMPSTON(0x04);
APPLY_SINCLAIR(0x0404);
break;
case Input::Up:
APPLY_KEMPSTON(0x08);
APPLY_SINCLAIR(0x0802);
break;
case Input::Fire:
APPLY_KEMPSTON(0x10);
APPLY_SINCLAIR(0x1001);
break;
}
#undef APPLY_KEMPSTON
#undef APPLY_SINCLAIR
}
/// @returns The value that a Kempston joystick interface would report if this joystick
/// were plugged into it.
uint8_t get_kempston() {
return kempston_;
}
/// @returns The value that a Sinclair interface would report if this joystick
/// were plugged into it via @c port (which should be either 0 or 1, for ports 1 or 2).
uint8_t get_sinclair(int port) {
return uint8_t(sinclair_ >> (port * 8));
}
private:
uint8_t kempston_ = 0x00;
uint16_t sinclair_ = 0xffff;
};
}
namespace Sinclair {
namespace ZXSpectrum {
@@ -58,6 +124,7 @@ template<Model model> class ConcreteMachine:
public CPU::Z80::BusHandler,
public Machine,
public MachineTypes::AudioProducer,
public MachineTypes::JoystickMachine,
public MachineTypes::MappedKeyboardMachine,
public MachineTypes::MediaTarget,
public MachineTypes::ScanProducer,
@@ -110,6 +177,10 @@ template<Model model> class ConcreteMachine:
// Register for sleeping notifications.
tape_player_.set_clocking_hint_observer(this);
// Attach a couple of joysticks.
joysticks_.emplace_back(new Joystick);
joysticks_.emplace_back(new Joystick);
// Set up initial memory map.
update_memory_map();
set_video_address();
@@ -124,6 +195,29 @@ template<Model model> class ConcreteMachine:
duration_to_press_enter_ = Cycles(5 * clock_rate());
keyboard_.set_key_state(ZX::Keyboard::KeyEnter, true);
}
// Install state if supplied.
if(target.state) {
const auto state = static_cast<State *>(target.state.get());
state->z80.apply(z80_);
state->video.apply(*video_.last_valid());
state->ay.apply(ay_);
// If this is a 48k or 16k machine, remap source data from its original
// linear form to whatever the banks end up being; otherwise copy as is.
if(model <= Model::FortyEightK) {
const size_t num_banks = std::min(size_t(48*1024), state->ram.size()) >> 14;
for(size_t c = 0; c < num_banks; c++) {
memcpy(&write_pointers_[c + 1][(c+1) * 0x4000], &state->ram[c * 0x4000], 0x4000);
}
} else {
memcpy(ram_.data(), state->ram.data(), std::min(ram_.size(), state->ram.size()));
port1ffd_ = state->last_1ffd;
port7ffd_ = state->last_7ffd;
update_memory_map();
}
}
}
~ConcreteMachine() {
@@ -202,6 +296,10 @@ template<Model model> class ConcreteMachine:
video_->set_display_type(display_type);
}
Outputs::Display::DisplayType get_display_type() const override {
return video_->get_display_type();
}
// MARK: - BusHandler.
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
@@ -322,6 +420,7 @@ template<Model model> class ConcreteMachine:
break;
}
}
[[fallthrough]];
case PartialMachineCycle::Read:
if constexpr (model == Model::SixteenK) {
@@ -384,10 +483,6 @@ template<Model model> class ConcreteMachine:
// Set the proper video base pointer.
set_video_address();
// Potentially lock paging, _after_ the current
// port values have taken effect.
disable_paging_ |= *cycle.value & 0x20;
}
// Test for +2a/+3 paging (i.e. port 1ffd).
@@ -435,6 +530,11 @@ template<Model model> class ConcreteMachine:
bool did_match = false;
*cycle.value = 0xff;
if(!(address&32)) {
did_match = true;
*cycle.value &= static_cast<Joystick *>(joysticks_[0].get())->get_kempston();
}
if(!(address&1)) {
did_match = true;
@@ -447,6 +547,10 @@ template<Model model> class ConcreteMachine:
*cycle.value &= keyboard_.read(address);
*cycle.value &= tape_player_.get_input() ? 0xbf : 0xff;
// Add Joystick input on top.
if(!(address&0x1000)) *cycle.value &= static_cast<Joystick *>(joysticks_[0].get())->get_sinclair(0);
if(!(address&0x0800)) *cycle.value &= static_cast<Joystick *>(joysticks_[1].get())->get_sinclair(1);
// If this read is within 200 cycles of the previous,
// count it as an adjacent hit; if 20 of those have
// occurred then start the tape motor.
@@ -625,6 +729,7 @@ template<Model model> class ConcreteMachine:
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly); // OptionsType is arbitrary, but not optional.
options->automatic_tape_motor_control = use_automatic_tape_motor_control_;
options->quickload = allow_fast_tape_hack_;
options->output = get_video_signal_configurable();
return options;
}
@@ -713,6 +818,10 @@ template<Model model> class ConcreteMachine:
set_memory(2, 2);
set_memory(3, port7ffd_ & 7);
}
// Potentially lock paging, _after_ the current
// port values have taken effect.
disable_paging_ = port7ffd_ & 0x20;
}
void set_memory(int bank, uint8_t source) {
@@ -758,10 +867,10 @@ template<Model model> class ConcreteMachine:
// MARK: - Video.
using VideoType =
std::conditional_t<
model <= Model::FortyEightK, Video<VideoTiming::FortyEightK>,
model <= Model::FortyEightK, Video::Video<Video::Timing::FortyEightK>,
std::conditional_t<
model <= Model::Plus2, Video<VideoTiming::OneTwoEightK>,
Video<VideoTiming::Plus3>
model <= Model::Plus2, Video::Video<Video::Timing::OneTwoEightK>,
Video::Video<Video::Timing::Plus3>
>
>;
JustInTimeActor<VideoType> video_;
@@ -872,6 +981,12 @@ template<Model model> class ConcreteMachine:
// MARK: - Automatic startup.
Cycles duration_to_press_enter_;
// MARK: - Joysticks
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
return joysticks_;
}
};

View File

@@ -0,0 +1,26 @@
//
// StateProducer.hpp
// Clock Signal
//
// Created by Thomas Harte on 24/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef State_h
#define State_h
#include <memory>
#include "../Analyser/Static/StaticAnalyser.hpp"
namespace MachineTypes {
struct StateProducer {
// TODO.
// virtual bool get_state(Analyser::Static::State *, [[maybe_unused]] bool advance_to_simple = false) {
// return false;
// }
};
};
#endif /* State_h */

View File

@@ -131,13 +131,3 @@ bool Typer::type_next_character() {
return true;
}
// MARK: - Character mapper
const uint16_t *CharacterMapper::table_lookup_sequence_for_character(const KeySequence *sequences, std::size_t length, char character) const {
std::size_t ucharacter = size_t((unsigned char)character);
if(ucharacter >= (length / sizeof(KeySequence))) return nullptr;
if(sequences[ucharacter][0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) return nullptr;
return sequences[ucharacter];
}

View File

@@ -44,11 +44,15 @@ class CharacterMapper {
typedef uint16_t KeySequence[16];
/*!
Provided in the base class as a convenience: given the lookup table of key sequences @c sequences,
with @c length entries, returns the sequence for character @c character if it exists; otherwise
returns @c nullptr.
Provided in the base class as a convenience: given the C array of key sequences @c sequences,
returns the sequence for character @c character if it exists; otherwise returns @c nullptr.
*/
const uint16_t *table_lookup_sequence_for_character(const KeySequence *sequences, std::size_t length, char character) const;
template <typename Collection> const uint16_t *table_lookup_sequence_for_character(const Collection &sequences, char character) const {
std::size_t ucharacter = size_t((unsigned char)character);
if(ucharacter >= sizeof(sequences) / sizeof(KeySequence)) return nullptr;
if(sequences[ucharacter][0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) return nullptr;
return sequences[ucharacter];
}
};
/*!

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
@@ -173,6 +173,8 @@
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */; };
4B2B946526377C0200E7097C /* SZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B946326377C0200E7097C /* SZX.cpp */; };
4B2B946626377C0200E7097C /* SZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B946326377C0200E7097C /* SZX.cpp */; };
4B2BF19123DCC6A200C3AD60 /* BD500.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03523CEB86000B98D9E /* BD500.cpp */; };
4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; };
4B2BF19623E10F0100C3AD60 /* CSHighPrecisionTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */; };
@@ -478,6 +480,11 @@
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894516201967B4007DE474 /* StaticAnalyser.cpp */; };
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; };
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; };
4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */; };
4B8DD3862634D37E00B3C866 /* SNA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD3842634D37E00B3C866 /* SNA.cpp */; };
4B8DD3872634D37E00B3C866 /* SNA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD3842634D37E00B3C866 /* SNA.cpp */; };
4B8DD39726360DDF00B3C866 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD39526360DDF00B3C866 /* Z80.cpp */; };
4B8DD39826360DDF00B3C866 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD39526360DDF00B3C866 /* Z80.cpp */; };
4B8DF4D825465B7500F3433C /* IIgsMemoryMapTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */; };
4B8DF4F9254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; };
4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; };
@@ -1010,7 +1017,7 @@
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>"; };
4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ScanTarget.cpp; path = ../../Outputs/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; };
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
4B055A7C1FAE84A50060FFFF /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
@@ -1058,8 +1065,8 @@
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
4B0E04E81FC9E5DA00F43484 /* CAS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAS.cpp; sourceTree = "<group>"; };
4B0E04E91FC9E5DA00F43484 /* CAS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CAS.hpp; sourceTree = "<group>"; };
4B0E04F81FC9FA3000F43484 /* 9918.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 9918.hpp; path = 9918/9918.hpp; sourceTree = "<group>"; };
4B0E04F91FC9FA3100F43484 /* 9918.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 9918.cpp; path = 9918/9918.cpp; sourceTree = "<group>"; };
4B0E04F81FC9FA3000F43484 /* 9918.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 9918.hpp; sourceTree = "<group>"; };
4B0E04F91FC9FA3100F43484 /* 9918.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 9918.cpp; sourceTree = "<group>"; };
4B0E61051FF34737002A9DBD /* MSX.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MSX.cpp; path = Parsers/MSX.cpp; sourceTree = "<group>"; };
4B0E61061FF34737002A9DBD /* MSX.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MSX.hpp; path = Parsers/MSX.hpp; sourceTree = "<group>"; };
4B0F1BB02602645900B85C66 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
@@ -1076,13 +1083,13 @@
4B0F1C1B2604EA1000B85C66 /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B0F1C212605996900B85C66 /* ZXSpectrumTAP.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ZXSpectrumTAP.cpp; sourceTree = "<group>"; };
4B0F1C222605996900B85C66 /* ZXSpectrumTAP.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ZXSpectrumTAP.hpp; sourceTree = "<group>"; };
4B0F1C3D26095AC600B85C66 /* FDC.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FDC.hpp; path = AmstradCPC/FDC.hpp; sourceTree = "<group>"; };
4B0F1C3D26095AC600B85C66 /* FDC.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FDC.hpp; sourceTree = "<group>"; };
4B0F94FC208C1A1600FE41D9 /* NIB.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NIB.cpp; sourceTree = "<group>"; };
4B0F94FD208C1A1600FE41D9 /* NIB.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NIB.hpp; sourceTree = "<group>"; };
4B0F9500208C42A300FE41D9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Target.hpp; path = AppleII/Target.hpp; sourceTree = "<group>"; };
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMSegmentEventSourceTests.mm; sourceTree = "<group>"; };
4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = MSX/Keyboard.cpp; sourceTree = "<group>"; };
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = MSX/Keyboard.hpp; sourceTree = "<group>"; };
4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = "<group>"; };
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
@@ -1091,14 +1098,14 @@
4B14978D1EE4B4D200CE2596 /* CSZX8081.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSZX8081.h; sourceTree = "<group>"; };
4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSZX8081.mm; sourceTree = "<group>"; };
4B1497971EE4B97F00CE2596 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/ZX8081Options.xib"; sourceTree = SOURCE_ROOT; };
4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BitReverse.cpp; path = Data/BitReverse.cpp; sourceTree = "<group>"; };
4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = BitReverse.hpp; path = Data/BitReverse.hpp; sourceTree = "<group>"; };
4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BitReverse.cpp; sourceTree = "<group>"; };
4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BitReverse.hpp; sourceTree = "<group>"; };
4B15A9FA208249BB005E6C8D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = AppleII/StaticAnalyser.cpp; sourceTree = "<group>"; };
4B15A9FB208249BB005E6C8D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = AppleII/StaticAnalyser.hpp; sourceTree = "<group>"; };
4B1667F61FFF1E2400A16032 /* Konami.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Konami.hpp; path = MSX/Cartridges/Konami.hpp; sourceTree = "<group>"; };
4B1667F91FFF215E00A16032 /* ASCII16kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII16kb.hpp; path = MSX/Cartridges/ASCII16kb.hpp; sourceTree = "<group>"; };
4B1667FA1FFF215E00A16032 /* ASCII8kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII8kb.hpp; path = MSX/Cartridges/ASCII8kb.hpp; sourceTree = "<group>"; };
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = KonamiWithSCC.hpp; path = MSX/Cartridges/KonamiWithSCC.hpp; sourceTree = "<group>"; };
4B1667F61FFF1E2400A16032 /* Konami.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Konami.hpp; sourceTree = "<group>"; };
4B1667F91FFF215E00A16032 /* ASCII16kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ASCII16kb.hpp; sourceTree = "<group>"; };
4B1667FA1FFF215E00A16032 /* ASCII8kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ASCII8kb.hpp; sourceTree = "<group>"; };
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KonamiWithSCC.hpp; sourceTree = "<group>"; };
4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StringSerialiser.cpp; sourceTree = "<group>"; };
4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StringSerialiser.hpp; sourceTree = "<group>"; };
4B1B58F4246CC4E8009C171E /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = "<group>"; };
@@ -1137,16 +1144,18 @@
4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryFuzzer.cpp; sourceTree = "<group>"; };
4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MemoryFuzzer.hpp; sourceTree = "<group>"; };
4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
4B2B946326377C0200E7097C /* SZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SZX.cpp; sourceTree = "<group>"; };
4B2B946426377C0200E7097C /* SZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SZX.hpp; sourceTree = "<group>"; };
4B2BF19423E10F0000C3AD60 /* CSHighPrecisionTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSHighPrecisionTimer.h; sourceTree = "<group>"; };
4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSHighPrecisionTimer.m; sourceTree = "<group>"; };
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapePRG.cpp; sourceTree = "<group>"; };
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapePRG.hpp; sourceTree = "<group>"; };
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Oric/Video.cpp; sourceTree = "<group>"; };
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Oric/Video.hpp; sourceTree = "<group>"; };
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
4B2C45411E3C3896002A2389 /* cartridge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cartridge.png; sourceTree = "<group>"; };
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Electron.hpp; sourceTree = "<group>"; };
4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ReactiveDevice.cpp; sourceTree = "<group>"; };
4B2E86B625D7490E0024F1E9 /* ReactiveDevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ReactiveDevice.hpp; sourceTree = "<group>"; };
4B2E86BC25D74F160024F1E9 /* Mouse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Mouse.cpp; sourceTree = "<group>"; };
@@ -1159,8 +1168,8 @@
4B302183208A550100773308 /* DiskII.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskII.cpp; sourceTree = "<group>"; };
4B30512B1D989E2200B4FED8 /* Drive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drive.cpp; sourceTree = "<group>"; };
4B30512C1D989E2200B4FED8 /* Drive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Drive.hpp; sourceTree = "<group>"; };
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Plus3.cpp; path = Electron/Plus3.cpp; sourceTree = "<group>"; };
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Plus3.hpp; path = Electron/Plus3.hpp; sourceTree = "<group>"; };
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Plus3.cpp; sourceTree = "<group>"; };
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Plus3.hpp; sourceTree = "<group>"; };
4B31B88F1FBFBCD800C140D5 /* Configurable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Configurable.hpp; sourceTree = "<group>"; };
4B322DF31F5A26BF004EB04C /* 6502Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Implementation.hpp; sourceTree = "<group>"; };
4B322DF41F5A2714004EB04C /* 6502Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Storage.hpp; sourceTree = "<group>"; };
@@ -1171,10 +1180,10 @@
4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80Implementation.hpp; sourceTree = "<group>"; };
4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryDump.cpp; sourceTree = "<group>"; };
4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = "<group>"; };
4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AmstradCPC.cpp; path = AmstradCPC/AmstradCPC.cpp; sourceTree = "<group>"; };
4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AmstradCPC.hpp; path = AmstradCPC/AmstradCPC.hpp; sourceTree = "<group>"; };
4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncTaskQueue.cpp; path = ../../Concurrency/AsyncTaskQueue.cpp; sourceTree = "<group>"; };
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AsyncTaskQueue.hpp; path = ../../Concurrency/AsyncTaskQueue.hpp; sourceTree = "<group>"; };
4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AmstradCPC.cpp; sourceTree = "<group>"; };
4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AmstradCPC.hpp; sourceTree = "<group>"; };
4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AsyncTaskQueue.cpp; sourceTree = "<group>"; };
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AsyncTaskQueue.hpp; sourceTree = "<group>"; };
4B3AF7D02413470E00873C0B /* Enum.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Enum.hpp; sourceTree = "<group>"; };
4B3AF7D12413472200873C0B /* Struct.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Struct.hpp; sourceTree = "<group>"; };
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = "<group>"; };
@@ -1233,8 +1242,8 @@
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AY38910.cpp; path = AY38910/AY38910.cpp; sourceTree = "<group>"; };
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AY38910.hpp; path = AY38910/AY38910.hpp; sourceTree = "<group>"; };
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = "<group>"; };
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AY38910.hpp; sourceTree = "<group>"; };
4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = "<group>"; };
4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KonamiSCC.hpp; sourceTree = "<group>"; };
4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = "<group>"; };
@@ -1255,14 +1264,14 @@
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardMachine.cpp; sourceTree = "<group>"; };
4B54C0BD1F8D8F450050900F /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = Oric/Keyboard.cpp; sourceTree = "<group>"; };
4B54C0BE1F8D8F450050900F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = Oric/Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C01F8D91CD0050900F /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = AmstradCPC/Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C11F8D91CD0050900F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = AmstradCPC/Keyboard.cpp; sourceTree = "<group>"; };
4B54C0BD1F8D8F450050900F /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B54C0BE1F8D8F450050900F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C01F8D91CD0050900F /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C11F8D91CD0050900F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B54C0C31F8D91D90050900F /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C41F8D91D90050900F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B54C0C61F8D91E50050900F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = Electron/Keyboard.cpp; sourceTree = "<group>"; };
4B54C0C71F8D91E50050900F /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = Electron/Keyboard.hpp; sourceTree = "<group>"; };
4B54C0C61F8D91E50050900F /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B54C0C71F8D91E50050900F /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = "<group>"; };
4B55DD8020DF06680043F2E5 /* MachinePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachinePicker.swift; sourceTree = "<group>"; };
4B55DD8220DF06680043F2E5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MachinePicker.xib; sourceTree = "<group>"; };
@@ -1276,10 +1285,10 @@
4B5D5C9625F56FC7001B4623 /* Spectrum.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Spectrum.hpp; path = Parsers/Spectrum.hpp; sourceTree = "<group>"; };
4B5FADB81DE3151600AEC565 /* FileHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileHolder.cpp; sourceTree = "<group>"; };
4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = "<group>"; };
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Microdisc.cpp; path = Oric/Microdisc.cpp; sourceTree = "<group>"; };
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Microdisc.hpp; path = Oric/Microdisc.hpp; sourceTree = "<group>"; };
4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = "<group>"; };
4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = "<group>"; };
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Microdisc.cpp; sourceTree = "<group>"; };
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Microdisc.hpp; sourceTree = "<group>"; };
4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DisplayMetrics.cpp; sourceTree = "<group>"; };
4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DisplayMetrics.hpp; sourceTree = "<group>"; };
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
@@ -1315,7 +1324,7 @@
4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = WOZ.hpp; sourceTree = "<group>"; };
4B7041271F92C26900735E45 /* JoystickMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JoystickMachine.hpp; sourceTree = "<group>"; };
4B70412A1F92C2A700735E45 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = "<group>"; };
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ROMSlotHandler.hpp; path = MSX/ROMSlotHandler.hpp; sourceTree = "<group>"; };
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMSlotHandler.hpp; sourceTree = "<group>"; };
4B7136841F78724F008B8ED9 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
4B7136851F78724F008B8ED9 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
4B7136871F78725F008B8ED9 /* Shifter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Shifter.cpp; sourceTree = "<group>"; };
@@ -1330,13 +1339,13 @@
4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = "<group>"; };
4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MacintoshVolume.cpp; path = Encodings/MacintoshVolume.cpp; sourceTree = "<group>"; };
4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MacintoshVolume.hpp; path = Encodings/MacintoshVolume.hpp; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Z80.hpp; sourceTree = "<group>"; };
4B770A961FE9EE770026DC70 /* CompoundSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundSource.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Electron/Video.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TypedDynamicMachine.hpp; sourceTree = "<group>"; };
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MSX.cpp; path = MSX/MSX.cpp; sourceTree = "<group>"; };
4B79A5001FC913C900EEDAD5 /* MSX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MSX.hpp; path = MSX/MSX.hpp; sourceTree = "<group>"; };
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSX.cpp; sourceTree = "<group>"; };
4B79A5001FC913C900EEDAD5 /* MSX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MSX.hpp; sourceTree = "<group>"; };
4B79E4411E3AF38600141F11 /* cassette.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cassette.png; sourceTree = "<group>"; };
4B79E4421E3AF38600141F11 /* floppy35.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = floppy35.png; sourceTree = "<group>"; };
4B79E4431E3AF38600141F11 /* floppy525.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = floppy525.png; sourceTree = "<group>"; };
@@ -1344,13 +1353,13 @@
4B7A90E42041097C008514A2 /* ColecoVision.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColecoVision.cpp; sourceTree = "<group>"; };
4B7A90EB20410A85008514A2 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4B7A90EC20410A85008514A2 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Jasmin.cpp; path = Oric/Jasmin.cpp; sourceTree = "<group>"; };
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Jasmin.hpp; path = Oric/Jasmin.hpp; sourceTree = "<group>"; };
4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Jasmin.cpp; sourceTree = "<group>"; };
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Jasmin.hpp; sourceTree = "<group>"; };
4B7BA03223C58B1E00B98D9E /* STX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = STX.hpp; sourceTree = "<group>"; };
4B7BA03323C58B1E00B98D9E /* STX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = STX.cpp; sourceTree = "<group>"; };
4B7BA03523CEB86000B98D9E /* BD500.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BD500.cpp; path = Oric/BD500.cpp; sourceTree = "<group>"; };
4B7BA03623CEB86000B98D9E /* BD500.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BD500.hpp; path = Oric/BD500.hpp; sourceTree = "<group>"; };
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskController.hpp; path = Oric/DiskController.hpp; sourceTree = "<group>"; };
4B7BA03523CEB86000B98D9E /* BD500.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BD500.cpp; sourceTree = "<group>"; };
4B7BA03623CEB86000B98D9E /* BD500.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BD500.hpp; sourceTree = "<group>"; };
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = "<group>"; };
4B7BA03E23D55E7900B98D9E /* CRC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRC.hpp; sourceTree = "<group>"; };
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LFSR.hpp; sourceTree = "<group>"; };
4B7F188C2154825D00388727 /* MasterSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MasterSystem.cpp; sourceTree = "<group>"; };
@@ -1379,8 +1388,8 @@
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Acorn.hpp; path = Parsers/Acorn.hpp; sourceTree = "<group>"; };
4B8805F21DCFD22A003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Parsers/Commodore.cpp; sourceTree = "<group>"; };
4B8805F31DCFD22A003085B1 /* Commodore.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Commodore.hpp; path = Parsers/Commodore.hpp; sourceTree = "<group>"; };
4B8805F51DCFF6C9003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Data/Commodore.cpp; sourceTree = "<group>"; };
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Commodore.hpp; path = Data/Commodore.hpp; sourceTree = "<group>"; };
4B8805F51DCFF6C9003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Commodore.cpp; sourceTree = "<group>"; };
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Commodore.hpp; sourceTree = "<group>"; };
4B8805F91DCFF807003085B1 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Parsers/Oric.cpp; sourceTree = "<group>"; };
4B8805FA1DCFF807003085B1 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Parsers/Oric.hpp; sourceTree = "<group>"; };
4B89449220194A47007DE474 /* CSStaticAnalyser+TargetVector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "CSStaticAnalyser+TargetVector.h"; path = "StaticAnalyser/CSStaticAnalyser+TargetVector.h"; sourceTree = "<group>"; };
@@ -1427,6 +1436,13 @@
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = "<group>"; };
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SpectrumVideoContentionTests.mm; sourceTree = "<group>"; };
4B8DD375263481BB00B3C866 /* StateProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StateProducer.hpp; sourceTree = "<group>"; };
4B8DD3842634D37E00B3C866 /* SNA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SNA.cpp; sourceTree = "<group>"; };
4B8DD3852634D37E00B3C866 /* SNA.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SNA.hpp; sourceTree = "<group>"; };
4B8DD3912635A72F00B3C866 /* State.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = "<group>"; };
4B8DD39526360DDF00B3C866 /* Z80.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Z80.cpp; sourceTree = "<group>"; };
4B8DD39626360DDF00B3C866 /* Z80.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80.hpp; sourceTree = "<group>"; };
4B8DF4D62546561300F3433C /* MemoryMap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemoryMap.hpp; sourceTree = "<group>"; };
4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = IIgsMemoryMapTests.mm; sourceTree = "<group>"; };
4B8DF4ED254B840B00F3433C /* AppleClock.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AppleClock.hpp; sourceTree = "<group>"; };
@@ -1520,8 +1536,8 @@
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000RollShiftTests.mm; sourceTree = "<group>"; };
4B9F11C82272375400701480 /* qltrace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = qltrace.txt.gz; sourceTree = "<group>"; };
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = OPCLOGR2.BIN; path = "68000 Coverage/OPCLOGR2.BIN"; sourceTree = "<group>"; };
4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Data/ZX8081.cpp; sourceTree = "<group>"; };
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Data/ZX8081.hpp; sourceTree = "<group>"; };
4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX8081.cpp; sourceTree = "<group>"; };
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZX8081.hpp; sourceTree = "<group>"; };
4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+StdVector.h"; sourceTree = "<group>"; };
4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+StdVector.mm"; sourceTree = "<group>"; };
@@ -1838,8 +1854,8 @@
4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = "<group>"; };
4BBB70A6202014E2002FE009 /* MultiProducer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiProducer.cpp; sourceTree = "<group>"; };
4BBB70A7202014E2002FE009 /* MultiProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiProducer.hpp; sourceTree = "<group>"; };
4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = "<group>"; };
4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = "<group>"; };
4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = i8272.cpp; sourceTree = "<group>"; };
4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = i8272.hpp; sourceTree = "<group>"; };
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = "<group>"; };
4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Flywheel.hpp; sourceTree = "<group>"; };
4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = "<group>"; };
@@ -1903,14 +1919,14 @@
4BCE005F227D39AB000CA200 /* Video.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bus.cpp; sourceTree = "<group>"; };
4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = "<group>"; };
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Oric/Oric.cpp; sourceTree = "<group>"; };
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = "<group>"; };
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Oric.cpp; sourceTree = "<group>"; };
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Oric.hpp; sourceTree = "<group>"; };
4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; };
4BD0FBC2233706A200148981 /* CSApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSApplication.m; sourceTree = "<group>"; };
4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = "<group>"; };
4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = "<group>"; };
4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = "<group>"; };
4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = "<group>"; };
4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 9918Base.hpp; sourceTree = "<group>"; };
4BD388872239E198002D14B5 /* 68000Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000Tests.mm; sourceTree = "<group>"; };
4BD424DD2193B5340097291A /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = "<group>"; };
4BD424DE2193B5340097291A /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = "<group>"; };
@@ -1918,17 +1934,17 @@
4BD424E22193B5820097291A /* Rectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rectangle.cpp; sourceTree = "<group>"; };
4BD424E32193B5830097291A /* Rectangle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Rectangle.hpp; sourceTree = "<group>"; };
4BD424E42193B5830097291A /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = "<group>"; };
4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = "<group>"; };
4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 1770.cpp; sourceTree = "<group>"; };
4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 1770.hpp; sourceTree = "<group>"; };
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = "<group>"; };
4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetGLSLFragments.cpp; sourceTree = "<group>"; };
4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = "<group>"; };
4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Log.hpp; sourceTree = "<group>"; };
4BD61663206B2AC700236112 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/QuickLoadOptions.xib"; sourceTree = SOURCE_ROOT; };
4BD67DC9209BE4D600AB2146 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
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>"; };
4BD9137D1F311BC5009BCF85 /* i8255.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = i8255.hpp; sourceTree = "<group>"; };
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatrikRakTests.swift; 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>"; };
@@ -1956,22 +1972,22 @@
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = "<group>"; };
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = "<group>"; };
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTC6845.hpp; sourceTree = "<group>"; };
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Disassembler.hpp; sourceTree = "<group>"; };
4BE8EB5525C0EA490040BC40 /* Sizes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sizes.hpp; sourceTree = "<group>"; };
4BE8EB6425C750B50040BC40 /* DAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DAT.cpp; sourceTree = "<group>"; };
4BE8EB6525C750B50040BC40 /* DAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DAT.hpp; sourceTree = "<group>"; };
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = "<group>"; };
4BE9A6B01EDE293000CBCB47 /* zexdoc.com */ = {isa = PBXFileReference; lastKnownFileType = file; name = zexdoc.com; path = Zexall/zexdoc.com; sourceTree = "<group>"; };
4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = "<group>"; };
4BEA525F1DF333D8007E74F2 /* Tape.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = Electron/Tape.hpp; sourceTree = "<group>"; };
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Interrupts.hpp; path = Electron/Interrupts.hpp; sourceTree = "<group>"; };
4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SoundGenerator.cpp; path = Electron/SoundGenerator.cpp; sourceTree = "<group>"; };
4BEA52621DF339D7007E74F2 /* SoundGenerator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = SoundGenerator.hpp; path = Electron/SoundGenerator.hpp; sourceTree = "<group>"; };
4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = "<group>"; };
4BEA525F1DF333D8007E74F2 /* Tape.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = "<group>"; };
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Interrupts.hpp; sourceTree = "<group>"; };
4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SoundGenerator.cpp; sourceTree = "<group>"; };
4BEA52621DF339D7007E74F2 /* SoundGenerator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SoundGenerator.hpp; sourceTree = "<group>"; };
4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSXDSK.cpp; sourceTree = "<group>"; };
4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = "<group>"; };
4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DiskROM.cpp; path = MSX/DiskROM.cpp; sourceTree = "<group>"; };
4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskROM.hpp; path = MSX/DiskROM.hpp; sourceTree = "<group>"; };
4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskROM.cpp; sourceTree = "<group>"; };
4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskROM.hpp; sourceTree = "<group>"; };
4BEDA3B425B25563000C2DBD /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = "<group>"; };
4BEDA3B525B25563000C2DBD /* Decoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = "<group>"; };
4BEDA3B625B25563000C2DBD /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@@ -2005,7 +2021,7 @@
4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; };
4BF437F0209D112F008CBD6B /* Sector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sector.hpp; sourceTree = "<group>"; };
4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = "<group>"; };
4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ScanTarget.hpp; path = ../../Outputs/ScanTarget.hpp; sourceTree = "<group>"; };
4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = "<group>"; };
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = "<group>"; };
4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816.hpp; sourceTree = "<group>"; };
4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Storage.hpp; sourceTree = "<group>"; };
@@ -2157,8 +2173,7 @@
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */,
4BBF99071C8FBA6F0075DAFB /* Internals */,
);
name = CRT;
path = ../../Outputs/CRT;
path = CRT;
sourceTree = "<group>";
};
4B0E04F71FC9F2C800F43484 /* 9918 */ = {
@@ -2168,7 +2183,7 @@
4B0E04F81FC9FA3000F43484 /* 9918.hpp */,
4BD388431FE34E060042B588 /* Implementation */,
);
name = 9918;
path = 9918;
sourceTree = "<group>";
};
4B0F1BAF2602645900B85C66 /* ZXSpectrum */ = {
@@ -2208,6 +2223,7 @@
4B0F1BFA260300D900B85C66 /* ZXSpectrum.cpp */,
4B0F1BFB260300D900B85C66 /* ZXSpectrum.hpp */,
4B0F1C092603BA5F00B85C66 /* Video.hpp */,
4B8DD3912635A72F00B3C866 /* State.hpp */,
);
path = ZXSpectrum;
sourceTree = "<group>";
@@ -2276,7 +2292,7 @@
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */,
4B1667F61FFF1E2400A16032 /* Konami.hpp */,
);
name = Cartridges;
path = Cartridges;
sourceTree = "<group>";
};
4B1B58F3246CC4E8009C171E /* State */ = {
@@ -2285,8 +2301,7 @@
4B1B58F4246CC4E8009C171E /* State.cpp */,
4B1B58F5246CC4E8009C171E /* State.hpp */,
);
name = State;
path = Z80/State;
path = State;
sourceTree = "<group>";
};
4B1B58FC246E19FD009C171E /* State */ = {
@@ -2433,7 +2448,7 @@
4BEA525F1DF333D8007E74F2 /* Tape.hpp */,
4B7913CB1DFCD80E00175A82 /* Video.hpp */,
);
name = Electron;
path = Electron;
sourceTree = "<group>";
};
4B302181208A550100773308 /* DiskII */ = {
@@ -2467,8 +2482,7 @@
4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */,
4B322DFE1F5A2981004EB04C /* Z80AllRAM.hpp */,
);
name = AllRAM;
path = Z80/AllRAM;
path = AllRAM;
sourceTree = "<group>";
};
4B322DFF1F5A2981004EB04C /* Implementation */ = {
@@ -2480,8 +2494,7 @@
4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */,
4B322E021F5A29D5004EB04C /* Z80Storage.hpp */,
);
name = Implementation;
path = Z80/Implementation;
path = Implementation;
sourceTree = "<group>";
};
4B366DFD1B5C165F0026627B /* Outputs */ = {
@@ -2498,6 +2511,7 @@
4BD060A41FE49D3C006E14BE /* Speaker */,
);
name = Outputs;
path = ../../Outputs;
sourceTree = "<group>";
};
4B38F3491F2EC12000D9235D /* AmstradCPC */ = {
@@ -2509,7 +2523,7 @@
4B54C0C01F8D91CD0050900F /* Keyboard.hpp */,
4B0F1C3D26095AC600B85C66 /* FDC.hpp */,
);
name = AmstradCPC;
path = AmstradCPC;
sourceTree = "<group>";
};
4B3940E81DA83C8700427841 /* Concurrency */ = {
@@ -2519,6 +2533,7 @@
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */,
);
name = Concurrency;
path = ../../Concurrency;
sourceTree = "<group>";
};
4B3AF7CF2413470E00873C0B /* Reflection */ = {
@@ -2669,7 +2684,7 @@
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */,
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */,
);
name = AY38910;
path = AY38910;
sourceTree = "<group>";
};
4B4B1A39200198C900A0F866 /* KonamiSCC */ = {
@@ -2840,6 +2855,7 @@
4B8805F81DCFF6CD003085B1 /* Data */,
4BAB62AA1D3272D200DF5BA0 /* Disk */,
4B6AAEA1230E3E1D0078E864 /* MassStorage */,
4B8DD3832634D37E00B3C866 /* State */,
4B69FB3A1C4D908A00B5F0AA /* Tape */,
);
name = Storage;
@@ -2969,7 +2985,7 @@
4B322DFF1F5A2981004EB04C /* Implementation */,
4B1B58F3246CC4E8009C171E /* State */,
);
name = Z80;
path = Z80;
sourceTree = "<group>";
};
4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = {
@@ -2984,7 +3000,7 @@
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */,
4B1667F81FFF1E2900A16032 /* Cartridges */,
);
name = MSX;
path = MSX;
sourceTree = "<group>";
};
4B7A90E22041097C008514A2 /* ColecoVision */ = {
@@ -3105,7 +3121,7 @@
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */,
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */,
);
name = Data;
path = Data;
sourceTree = "<group>";
};
4B8944E2201967B4007DE474 /* Analyser */ = {
@@ -3246,6 +3262,19 @@
path = AmstradCPC;
sourceTree = "<group>";
};
4B8DD3832634D37E00B3C866 /* State */ = {
isa = PBXGroup;
children = (
4B2B946326377C0200E7097C /* SZX.cpp */,
4B2B946426377C0200E7097C /* SZX.hpp */,
4B8DD3842634D37E00B3C866 /* SNA.cpp */,
4B8DD3852634D37E00B3C866 /* SNA.hpp */,
4B8DD39526360DDF00B3C866 /* Z80.cpp */,
4B8DD39626360DDF00B3C866 /* Z80.hpp */,
);
path = State;
sourceTree = "<group>";
};
4B8DF4EC254B840B00F3433C /* AppleClock */ = {
isa = PBXGroup;
children = (
@@ -3939,8 +3968,12 @@
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
isa = PBXGroup;
children = (
4B85322922778E4200F26553 /* Comparative68000.hpp */,
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
4B1E85801D176468001EF87D /* 6532Tests.swift */,
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
4B8DF5132550D62900F3433C /* 65816kromTests.swift */,
4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */,
4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */,
4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */,
@@ -3949,46 +3982,43 @@
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
4BD388872239E198002D14B5 /* 68000Tests.mm */,
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
4B3BA0C41D318B44005DD7A7 /* Bridges */,
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
4B85322922778E4200F26553 /* Comparative68000.hpp */,
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */,
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */,
4BB73EB81B587A5100552FC2 /* Info.plist */,
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */,
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */,
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
4B1414631B588A1100E04248 /* Test Binaries */,
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
4BB73EB81B587A5100552FC2 /* Info.plist */,
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
4B1E85801D176468001EF87D /* 6532Tests.swift */,
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
4B8DF5132550D62900F3433C /* 65816kromTests.swift */,
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */,
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */,
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */,
4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */,
4B3BA0C41D318B44005DD7A7 /* Bridges */,
4B1414631B588A1100E04248 /* Test Binaries */,
);
path = "Clock SignalTests";
sourceTree = "<group>";
@@ -4015,6 +4045,7 @@
4B92294222B04A3D00A1458F /* MouseMachine.hpp */,
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */,
4B046DC31CFE651500E9E45E /* ScanProducer.hpp */,
4B8DD375263481BB00B3C866 /* StateProducer.hpp */,
4BC57CD32434282000FBC404 /* TimedMachine.hpp */,
4B38F3491F2EC12000D9235D /* AmstradCPC */,
4BCE0048227CE8CA000CA200 /* Apple */,
@@ -4054,8 +4085,7 @@
4BB8616C24E22DC500A00E03 /* BufferingScanTarget.hpp */,
4BB8616D24E22DC500A00E03 /* BufferingScanTarget.cpp */,
);
name = ScanTargets;
path = ../../Outputs/ScanTargets;
path = ScanTargets;
sourceTree = "<group>";
};
4BBB70A1202011C2002FE009 /* Implementation */ = {
@@ -4083,7 +4113,7 @@
4BBC951C1F368D83008F4C34 /* i8272.cpp */,
4BBC951D1F368D83008F4C34 /* i8272.hpp */,
);
name = 8272;
path = 8272;
sourceTree = "<group>";
};
4BBF49B41ED2881600AB3669 /* FUSE */ = {
@@ -4300,7 +4330,7 @@
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */,
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */,
);
name = Oric;
path = Oric;
sourceTree = "<group>";
};
4BD060A41FE49D3C006E14BE /* Speaker */ = {
@@ -4309,8 +4339,7 @@
4BD060A51FE49D3C006E14BE /* Speaker.hpp */,
4B8EF6051FE5AF830076CCDD /* Implementation */,
);
name = Speaker;
path = ../../Outputs/Speaker;
path = Speaker;
sourceTree = "<group>";
};
4BD191D5219113B80042E144 /* OpenGL */ = {
@@ -4323,8 +4352,7 @@
4B961408222760E0001A7BF2 /* Screenshot.hpp */,
4BD424DC2193B5340097291A /* Primitives */,
);
name = OpenGL;
path = ../../Outputs/OpenGL;
path = OpenGL;
sourceTree = "<group>";
};
4BD388431FE34E060042B588 /* Implementation */ = {
@@ -4332,7 +4360,7 @@
children = (
4BD388411FE34E010042B588 /* 9918Base.hpp */,
);
name = Implementation;
path = Implementation;
sourceTree = "<group>";
};
4BD424DC2193B5340097291A /* Primitives */ = {
@@ -4354,7 +4382,7 @@
4BD468F51D8DF41D0084958B /* 1770.cpp */,
4BD468F61D8DF41D0084958B /* 1770.hpp */,
);
name = 1770;
path = 1770;
sourceTree = "<group>";
};
4BD67DC8209BE4D600AB2146 /* DiskII */ = {
@@ -4384,7 +4412,7 @@
children = (
4BD9137D1F311BC5009BCF85 /* i8255.hpp */,
);
name = 8255;
path = 8255;
sourceTree = "<group>";
};
4BDA00DB22E60EE900AC3CD0 /* ROMRequester */ = {
@@ -4446,7 +4474,7 @@
children = (
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */,
);
name = 6845;
path = 6845;
sourceTree = "<group>";
};
4BE9A6B21EDE294200CBCB47 /* Zexall */ = {
@@ -4685,7 +4713,7 @@
};
};
buildConfigurationList = 4BB73E991B587A5100552FC2 /* Build configuration list for PBXProject "Clock Signal" */;
compatibilityVersion = "Xcode 3.2";
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -5210,9 +5238,11 @@
4B1B58F7246CC4E8009C171E /* State.cpp in Sources */,
4B0ACC03237756F6008902D0 /* Line.cpp in Sources */,
4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */,
4B2B946626377C0200E7097C /* SZX.cpp in Sources */,
4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */,
4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */,
4B894533201967B4007DE474 /* 6502.cpp in Sources */,
4B8DD3872634D37E00B3C866 /* SNA.cpp in Sources */,
4B055AA91FAE85EF0060FFFF /* CommodoreGCR.cpp in Sources */,
4B055ADB1FAE9B460060FFFF /* 6560.cpp in Sources */,
4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */,
@@ -5246,6 +5276,7 @@
4B055AC81FAE9AFB0060FFFF /* C1540.cpp in Sources */,
4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */,
4B055A911FAE85B50060FFFF /* Cartridge.cpp in Sources */,
4B8DD39826360DDF00B3C866 /* Z80.cpp in Sources */,
4B894525201967B4007DE474 /* Tape.cpp in Sources */,
4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */,
4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */,
@@ -5342,6 +5373,7 @@
4BE211FF253FC80900435408 /* StaticAnalyser.cpp in Sources */,
4B0F1BE22602FF9C00B85C66 /* ZX8081.cpp in Sources */,
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
4B8DD39726360DDF00B3C866 /* Z80.cpp in Sources */,
4BEDA40C25B2844B000C2DBD /* Decoder.cpp in Sources */,
4B89453C201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */,
@@ -5483,6 +5515,7 @@
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
4B8DD3862634D37E00B3C866 /* SNA.cpp in Sources */,
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */,
4B894524201967B4007DE474 /* Tape.cpp in Sources */,
4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */,
@@ -5500,6 +5533,7 @@
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
4BB307BB235001C300457D33 /* 6850.cpp in Sources */,
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
4B2B946526377C0200E7097C /* SZX.cpp in Sources */,
4BC131762346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
@@ -5643,6 +5677,7 @@
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */,
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */,
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
4B778F1223A5EC720000D260 /* CRT.cpp in Sources */,
4B778EF423A5DB3A0000D260 /* C1540.cpp in Sources */,
@@ -5961,7 +5996,8 @@
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
@@ -6000,7 +6036,10 @@
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_LABEL = YES;
INFOPLIST_FILE = "Clock Signal/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MTL_TREAT_WARNINGS_AS_ERRORS = YES;
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
@@ -6049,7 +6088,10 @@
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_LABEL = YES;
INFOPLIST_FILE = "Clock Signal/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MTL_TREAT_WARNINGS_AS_ERRORS = YES;
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
@@ -6074,7 +6116,11 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = NO;
INFOPLIST_FILE = "Clock SignalTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -6099,7 +6145,11 @@
ENABLE_HARDENED_RUNTIME = NO;
GCC_OPTIMIZATION_LEVEL = 2;
INFOPLIST_FILE = "Clock SignalTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";

View File

@@ -592,6 +592,66 @@
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>sna</string>
</array>
<key>CFBundleTypeName</key>
<string>ZX Spectrum SNA snapshot</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>z80</string>
</array>
<key>CFBundleTypeName</key>
<string>ZX Spectrum Z80 snapshot</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>szx</string>
</array>
<key>CFBundleTypeName</key>
<string>ZX Spectrum SZX snapshot</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>

View File

@@ -368,7 +368,7 @@ API_AVAILABLE(macos(11.0))
return self;
}
-(void)update {
- (void)update {
// Update buttons.
for(CSGCJoystickButton *button in _buttons) {
// This assumes that the values provided by GCDeviceButtonInput are
@@ -379,7 +379,11 @@ API_AVAILABLE(macos(11.0))
float val = axis.axis.value;
val += 1;
val /= 2;
axis.position = val;
if(axis.type == CSJoystickAxisTypeY) {
axis.position = 1 - val;
} else {
axis.position = val;
}
}
for(CSGCJoystickHat *hat in _hats) {
// This assumes that the values provided by GCDeviceDirectionPad are

View File

@@ -0,0 +1,156 @@
//
// SpectrumVideoContentionTests.cpp
// Clock SignalTests
//
// Created by Thomas Harte on 23/4/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../Machines/Sinclair/ZXSpectrum/Video.hpp"
@interface SpectrumVideoContentionTests : XCTestCase
@end
@implementation SpectrumVideoContentionTests
struct ContentionAnalysis {
int time_after_interrupt = 0;
int contended_lines = 0;
int contention_length = 0;
int line_length = 0;
int total_lines = 0;
HalfCycles pattern[8];
};
template <Sinclair::ZXSpectrum::VideoTiming video_timing> ContentionAnalysis analyse() {
Sinclair::ZXSpectrum::Video<video_timing> video;
ContentionAnalysis analysis;
// Advance to the start of the first interrupt.
while(video.get_interrupt_line()) {
video.run_for(HalfCycles(1));
}
while(!video.get_interrupt_line()) {
video.run_for(HalfCycles(1));
}
// Count half cycles until first non-zero contended time.
while(video.access_delay(HalfCycles(0)) == HalfCycles(0)) {
video.run_for(HalfCycles(2));
analysis.time_after_interrupt += 2;
}
// Grab the contention pattern.
for(int c = 0; c < 8; c++) {
analysis.pattern[c] = video.access_delay(HalfCycles(0));
video.run_for(HalfCycles(2));
}
// Figure out how long contention goes on for.
int c = 0;
analysis.contention_length = 16; // For the 16 just skipped.
do {
analysis.contention_length += 2;
video.run_for(HalfCycles(2));
c++;
} while(analysis.pattern[c&7] == video.access_delay(HalfCycles(0)));
// Look for next start of contention to determine line length.
analysis.line_length = analysis.contention_length;
while(video.access_delay(HalfCycles(0)) == HalfCycles(0)) {
video.run_for(HalfCycles(2));
analysis.line_length += 2;
}
// Count contended lines.
analysis.contended_lines = 1;
while(video.access_delay(HalfCycles(0)) == analysis.pattern[0]) {
video.run_for(HalfCycles(analysis.line_length));
++analysis.contended_lines;
}
// Count total lines.
analysis.total_lines = analysis.contended_lines;
while(video.access_delay(HalfCycles(0)) != analysis.pattern[0]) {
video.run_for(HalfCycles(analysis.line_length));
++analysis.total_lines;
}
return analysis;
}
- (void)test48k {
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::FortyEightK>();
// Check time from interrupt.
XCTAssertEqual(analysis.time_after_interrupt, 14335*2);
// Check contention pattern.
XCTAssertEqual(analysis.pattern[0], HalfCycles(6 * 2));
XCTAssertEqual(analysis.pattern[1], HalfCycles(5 * 2));
XCTAssertEqual(analysis.pattern[2], HalfCycles(4 * 2));
XCTAssertEqual(analysis.pattern[3], HalfCycles(3 * 2));
XCTAssertEqual(analysis.pattern[4], HalfCycles(2 * 2));
XCTAssertEqual(analysis.pattern[5], HalfCycles(1 * 2));
XCTAssertEqual(analysis.pattern[6], HalfCycles(0 * 2));
XCTAssertEqual(analysis.pattern[7], HalfCycles(0 * 2));
// Check line length and count.
XCTAssertEqual(analysis.contention_length, 128*2);
XCTAssertEqual(analysis.line_length, 224*2);
XCTAssertEqual(analysis.contended_lines, 192);
XCTAssertEqual(analysis.total_lines, 312);
}
- (void)test128k {
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::OneTwoEightK>();
// Check time from interrupt.
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
// Check contention pattern.
XCTAssertEqual(analysis.pattern[0], HalfCycles(6 * 2));
XCTAssertEqual(analysis.pattern[1], HalfCycles(5 * 2));
XCTAssertEqual(analysis.pattern[2], HalfCycles(4 * 2));
XCTAssertEqual(analysis.pattern[3], HalfCycles(3 * 2));
XCTAssertEqual(analysis.pattern[4], HalfCycles(2 * 2));
XCTAssertEqual(analysis.pattern[5], HalfCycles(1 * 2));
XCTAssertEqual(analysis.pattern[6], HalfCycles(0 * 2));
XCTAssertEqual(analysis.pattern[7], HalfCycles(0 * 2));
// Check line length and count.
XCTAssertEqual(analysis.contention_length, 128*2);
XCTAssertEqual(analysis.line_length, 228*2);
XCTAssertEqual(analysis.contended_lines, 192);
XCTAssertEqual(analysis.total_lines, 311);
}
- (void)testPlus3 {
const auto analysis = analyse<Sinclair::ZXSpectrum::VideoTiming::Plus3>();
// Check time from interrupt.
XCTAssertEqual(analysis.time_after_interrupt, 14361*2);
// Check contention pattern.
XCTAssertEqual(analysis.pattern[0], HalfCycles(1 * 2));
XCTAssertEqual(analysis.pattern[1], HalfCycles(0 * 2));
XCTAssertEqual(analysis.pattern[2], HalfCycles(7 * 2));
XCTAssertEqual(analysis.pattern[3], HalfCycles(6 * 2));
XCTAssertEqual(analysis.pattern[4], HalfCycles(5 * 2));
XCTAssertEqual(analysis.pattern[5], HalfCycles(4 * 2));
XCTAssertEqual(analysis.pattern[6], HalfCycles(3 * 2));
XCTAssertEqual(analysis.pattern[7], HalfCycles(2 * 2));
// Check line length and count.
XCTAssertEqual(analysis.contention_length, 130*2); // By the manner used for detection above,
// the first obviously missing contention spot
// will be after 130 cycles, not the textbook 129,
// because cycle 130 is a delay of 0 either way.
XCTAssertEqual(analysis.line_length, 228*2);
XCTAssertEqual(analysis.contended_lines, 192);
XCTAssertEqual(analysis.total_lines, 311);
}
@end

View File

@@ -12,7 +12,13 @@ CONFIG += object_parallel_to_source
INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib
LIBS += -lz
# Add flags (i) to identify that this is a Qt build; and
# If targetting X11, link against that.
linux {
QT += x11extras
LIBS += -lX11
}
# Add flags (i) to identify that this is a Qt build; and
# (ii) to disable asserts in release builds.
DEFINES += TARGET_QT
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG
@@ -129,14 +135,16 @@ SOURCES += \
$$SRC/Storage/MassStorage/Encodings/*.cpp \
$$SRC/Storage/MassStorage/Formats/*.cpp \
$$SRC/Storage/MassStorage/SCSI/*.cpp \
$$SRC/Storage/State/*.cpp \
$$SRC/Storage/Tape/*.cpp \
$$SRC/Storage/Tape/Formats/*.cpp \
$$SRC/Storage/Tape/Parsers/*.cpp \
\
main.cpp \
mainwindow.cpp \
scantargetwidget.cpp \
timer.cpp
main.cpp \
mainwindow.cpp \
scantargetwidget.cpp \
timer.cpp \
keyboard.cpp
HEADERS += \
$$SRC/Activity/*.hpp \
@@ -269,16 +277,18 @@ HEADERS += \
$$SRC/Storage/MassStorage/Encodings/*.hpp \
$$SRC/Storage/MassStorage/Formats/*.hpp \
$$SRC/Storage/MassStorage/SCSI/*.hpp \
$$SRC/Storage/State/*.hpp \
$$SRC/Storage/Tape/*.hpp \
$$SRC/Storage/Tape/Formats/*.hpp \
$$SRC/Storage/Tape/Parsers/*.hpp \
\
audiobuffer.h \
functionthread.h \
mainwindow.h \
scantargetwidget.h \
settings.h \
timer.h
audiobuffer.h \
functionthread.h \
mainwindow.h \
scantargetwidget.h \
settings.h \
keyboard.h \
timer.h
FORMS += \
mainwindow.ui

202
OSBindings/Qt/keyboard.cpp Normal file
View File

@@ -0,0 +1,202 @@
#include "keyboard.h"
#include <QDebug>
#include <QGuiApplication>
// Qt is the worst.
//
// Assume your keyboard has a key labelled both . and >, as on US and UK keyboards. Call it the dot key.
// Perform the following:
// 1. press dot key;
// 2. press shift key;
// 3. release dot key;
// 4. release shift key.
//
// Per empirical testing, and key repeat aside, on both macOS and Ubuntu 19.04 that sequence will result in
// _three_ keypress events, but only _two_ key release events. You'll get presses for Qt::Key_Period, Qt::Key_Greater
// and Qt::Key_Shift. You'll get releases only for Qt::Key_Greater and Qt::Key_Shift.
//
// How can you detect at runtime that Key_Greater and Key_Period are the same physical key?
//
// You can't. On Ubuntu they have the same values for QKeyEvent::nativeScanCode(), which are unique to the key,
// but they have different ::nativeVirtualKey()s.
//
// On macOS they have the same ::nativeScanCode() only because on macOS [almost] all keys have the same
// ::nativeScanCode(). So that's not usable. They have the same ::nativeVirtualKey()s, but since that isn't true
// on Ubuntu, that's also not usable.
//
// So how can you track the physical keys on a keyboard via Qt?
//
// You can't. Qt is the worst. SDL doesn't have this problem, including in X11, but I don't want the non-Qt dependency.
#ifdef Q_OS_LINUX
#define HAS_X11
#endif
#ifdef HAS_X11
#include <QX11Info>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#endif
KeyboardMapper::KeyboardMapper() {
#ifdef HAS_X11
struct DesiredMapping {
KeySym source = 0;
Inputs::Keyboard::Key destination;
};
using Key = Inputs::Keyboard::Key;
constexpr DesiredMapping mappings[] = {
{XK_Escape, Key::Escape},
{XK_F1, Key::F1}, {XK_F2, Key::F2}, {XK_F3, Key::F3}, {XK_F4, Key::F4}, {XK_F5, Key::F5},
{XK_F6, Key::F6}, {XK_F7, Key::F7}, {XK_F8, Key::F8}, {XK_F9, Key::F9}, {XK_F10, Key::F10},
{XK_F11, Key::F11}, {XK_F12, Key::F12},
{XK_Sys_Req, Key::PrintScreen},
{XK_Scroll_Lock, Key::ScrollLock},
{XK_Pause, Key::Pause},
{XK_grave, Key::BackTick},
{XK_1, Key::k1}, {XK_2, Key::k2}, {XK_3, Key::k3}, {XK_4, Key::k4}, {XK_5, Key::k5},
{XK_6, Key::k6}, {XK_7, Key::k7}, {XK_8, Key::k8}, {XK_9, Key::k9}, {XK_0, Key::k0},
{XK_minus, Key::Hyphen},
{XK_equal, Key::Equals},
{XK_BackSpace, Key::Backspace},
{XK_Tab, Key::Tab},
{XK_Q, Key::Q}, {XK_W, Key::W}, {XK_E, Key::E}, {XK_R, Key::R}, {XK_T, Key::T},
{XK_Y, Key::Y}, {XK_U, Key::U}, {XK_I, Key::I}, {XK_O, Key::O}, {XK_P, Key::P},
{XK_bracketleft, Key::OpenSquareBracket},
{XK_bracketright, Key::CloseSquareBracket},
{XK_backslash, Key::Backslash},
{XK_Caps_Lock, Key::CapsLock},
{XK_A, Key::A}, {XK_S, Key::S}, {XK_D, Key::D}, {XK_F, Key::F}, {XK_G, Key::G},
{XK_H, Key::H}, {XK_J, Key::J}, {XK_K, Key::K}, {XK_L, Key::L},
{XK_semicolon, Key::Semicolon},
{XK_apostrophe, Key::Quote},
{XK_Return, Key::Enter},
{XK_Shift_L, Key::LeftShift},
{XK_Z, Key::Z}, {XK_X, Key::X}, {XK_C, Key::C}, {XK_V, Key::V},
{XK_B, Key::B}, {XK_N, Key::N}, {XK_M, Key::M},
{XK_comma, Key::Comma},
{XK_period, Key::FullStop},
{XK_slash, Key::ForwardSlash},
{XK_Shift_R, Key::RightShift},
{XK_Control_L, Key::LeftControl},
{XK_Control_R, Key::RightControl},
{XK_Alt_L, Key::LeftOption},
{XK_Alt_R, Key::RightOption},
{XK_Meta_L, Key::LeftMeta},
{XK_Meta_R, Key::RightMeta},
{XK_space, Key::Space},
{XK_Left, Key::Left}, {XK_Right, Key::Right}, {XK_Up, Key::Up}, {XK_Down, Key::Down},
{XK_Insert, Key::Insert},
{XK_Delete, Key::Delete},
{XK_Home, Key::Home},
{XK_End, Key::End},
{XK_Num_Lock, Key::NumLock},
{XK_KP_Divide, Key::KeypadSlash},
{XK_KP_Multiply, Key::KeypadAsterisk},
{XK_KP_Delete, Key::KeypadDelete},
{XK_KP_7, Key::Keypad7}, {XK_KP_8, Key::Keypad8}, {XK_KP_9, Key::Keypad9}, {XK_KP_Add, Key::KeypadPlus},
{XK_KP_4, Key::Keypad4}, {XK_KP_5, Key::Keypad5}, {XK_KP_6, Key::Keypad6}, {XK_KP_Subtract, Key::KeypadMinus},
{XK_KP_1, Key::Keypad1}, {XK_KP_2, Key::Keypad2}, {XK_KP_3, Key::Keypad3}, {XK_KP_Enter, Key::KeypadEnter},
{XK_KP_0, Key::Keypad0},
{XK_KP_Decimal, Key::KeypadDecimalPoint},
{XK_KP_Equal, Key::KeypadEquals},
{XK_Help, Key::Help},
{}
};
// Extra level of nonsense here:
//
// (1) assume a PC-esque keyboard, with a close-to-US/UK layout;
// (2) from there, use any of the X11 KeySyms I'd expect to be achievable from each physical key to
// look up the X11 KeyCode;
// (3) henceforth, map from X11 KeyCode to the Inputs::Keyboard::Key.
const DesiredMapping *mapping = mappings;
while(mapping->source != 0) {
const auto code = XKeysymToKeycode(QX11Info::display(), mapping->source);
keyByKeySym[code] = mapping->destination;
++mapping;
}
#endif
}
std::optional<Inputs::Keyboard::Key> KeyboardMapper::keyForEvent(QKeyEvent *event) {
#ifdef HAS_X11
if(QGuiApplication::platformName() == QLatin1String("xcb")) {
const auto key = keyByKeySym.find(event->nativeScanCode());
if(key == keyByKeySym.end()) return std::nullopt;
return key->second;
}
#endif
// Fall back on a limited, faulty adaptation.
#define BIND2(qtKey, clkKey) case Qt::qtKey: return Inputs::Keyboard::Key::clkKey;
#define BIND(key) BIND2(Key_##key, key)
switch(event->key()) {
default: return {};
BIND(Escape);
BIND(F1); BIND(F2); BIND(F3); BIND(F4); BIND(F5); BIND(F6);
BIND(F7); BIND(F8); BIND(F9); BIND(F10); BIND(F11); BIND(F12);
BIND2(Key_Print, PrintScreen);
BIND(ScrollLock); BIND(Pause);
BIND2(Key_AsciiTilde, BackTick);
BIND2(Key_1, k1); BIND2(Key_2, k2); BIND2(Key_3, k3); BIND2(Key_4, k4); BIND2(Key_5, k5);
BIND2(Key_6, k6); BIND2(Key_7, k7); BIND2(Key_8, k8); BIND2(Key_9, k9); BIND2(Key_0, k0);
BIND2(Key_Minus, Hyphen);
BIND2(Key_Plus, Equals);
BIND(Backspace);
BIND(Tab); BIND(Q); BIND(W); BIND(E); BIND(R); BIND(T); BIND(Y);
BIND(U); BIND(I); BIND(O); BIND(P);
BIND2(Key_BraceLeft, OpenSquareBracket);
BIND2(Key_BraceRight, CloseSquareBracket);
BIND(Backslash);
BIND(CapsLock); BIND(A); BIND(S); BIND(D); BIND(F); BIND(G);
BIND(H); BIND(J); BIND(K); BIND(L);
BIND(Semicolon);
BIND2(Key_Apostrophe, Quote);
BIND2(Key_QuoteDbl, Quote);
// TODO: something to hash?
BIND2(Key_Return, Enter);
BIND2(Key_Shift, LeftShift);
BIND(Z); BIND(X); BIND(C); BIND(V);
BIND(B); BIND(N); BIND(M);
BIND(Comma);
BIND2(Key_Period, FullStop);
BIND2(Key_Slash, ForwardSlash);
// Omitted: right shift.
BIND2(Key_Control, LeftControl);
BIND2(Key_Alt, LeftOption);
BIND2(Key_Meta, LeftMeta);
BIND(Space);
BIND2(Key_AltGr, RightOption);
BIND(Left); BIND(Right); BIND(Up); BIND(Down);
BIND(Insert); BIND(Home); BIND(PageUp); BIND(Delete); BIND(End); BIND(PageDown);
BIND(NumLock);
}
#undef BIND
#undef BIND2
}

18
OSBindings/Qt/keyboard.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include <QKeyEvent>
#include <map>
#include <optional>
#include "../../Inputs/Keyboard.hpp"
class KeyboardMapper {
public:
KeyboardMapper();
std::optional<Inputs::Keyboard::Key> keyForEvent(QKeyEvent *);
private:
std::map<quint32, Inputs::Keyboard::Key> keyByKeySym;
};
#endif // MAINWINDOW_H

View File

@@ -851,176 +851,10 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) {
processEvent(event);
}
// Qt is the worst.
//
// Assume your keyboard has a key labelled both . and >, as they do on US and UK keyboards. Call it the dot key.
// Perform the following:
// 1. press dot key;
// 2. press shift key;
// 3. release dot key;
// 4. release shift key.
//
// Per empirical testing, and key repeat aside, on both macOS and Ubuntu 19.04 that sequence will result in
// _three_ keypress events, but only _two_ key release events. You'll get presses for Qt::Key_Period, Qt::Key_Greater
// and Qt::Key_Shift. You'll get releases only for Qt::Key_Greater and Qt::Key_Shift.
//
// How can you detect at runtime that Key_Greater and Key_Period are the same physical key?
//
// You can't. On Ubuntu they have the same values for QKeyEvent::nativeScanCode(), which are unique to the key,
// but they have different ::nativeVirtualKey()s.
//
// On macOS they have the same ::nativeScanCode() only because on macOS [almost] all keys have the same
// ::nativeScanCode(). So that's not usable. They have the same ::nativeVirtualKey()s, but since that isn't true
// on Ubuntu, that's also not usable.
//
// So how can you track the physical keys on a keyboard via Qt?
//
// You can't. Qt is the worst. SDL doesn't have this problem, including in X11, but I'm not sure I want the extra
// dependency. I may need to reassess.
std::optional<Inputs::Keyboard::Key> MainWindow::keyForEvent(QKeyEvent *event) {
// Workaround for X11: assume PC-esque mapping from ::nativeScanCode to symbols.
//
// Yucky, ugly, harcoded yuck. TODO: work out how `xmodmap -pke` seems to derive these codes at runtime.
if(QGuiApplication::platformName() == QLatin1String("xcb")) {
#define BIND(code, key) case code: return Inputs::Keyboard::Key::key;
switch(event->nativeScanCode()) {
default: qDebug() << "Unmapped" << event->nativeScanCode(); return {};
BIND(1, Escape);
BIND(67, F1); BIND(68, F2); BIND(69, F3); BIND(70, F4); BIND(71, F5);
BIND(72, F6); BIND(73, F7); BIND(74, F8); BIND(75, F9); BIND(76, F10);
BIND(95, F11); BIND(96, F12);
BIND(107, PrintScreen);
BIND(78, ScrollLock);
BIND(127, Pause);
BIND(49, BackTick);
BIND(10, k1); BIND(11, k2); BIND(12, k3); BIND(13, k4); BIND(14, k5);
BIND(15, k6); BIND(16, k7); BIND(17, k8); BIND(18, k9); BIND(19, k0);
BIND(20, Hyphen);
BIND(21, Equals);
BIND(22, Backspace);
BIND(23, Tab);
BIND(24, Q); BIND(25, W); BIND(26, E); BIND(27, R); BIND(28, T);
BIND(29, Y); BIND(30, U); BIND(31, I); BIND(32, O); BIND(33, P);
BIND(34, OpenSquareBracket);
BIND(35, CloseSquareBracket);
BIND(51, Backslash);
BIND(66, CapsLock);
BIND(38, A); BIND(39, S); BIND(40, D); BIND(41, F); BIND(42, G);
BIND(43, H); BIND(44, J); BIND(45, K); BIND(46, L);
BIND(47, Semicolon);
BIND(48, Quote);
BIND(36, Enter);
BIND(50, LeftShift);
BIND(52, Z); BIND(53, X); BIND(54, C); BIND(55, V);
BIND(56, B); BIND(57, N); BIND(58, M);
BIND(59, Comma);
BIND(60, FullStop);
BIND(61, ForwardSlash);
BIND(62, RightShift);
BIND(105, LeftControl);
BIND(204, LeftOption);
BIND(205, LeftMeta);
BIND(65, Space);
BIND(108, RightOption);
BIND(113, Left); BIND(114, Right); BIND(111, Up); BIND(116, Down);
BIND(118, Insert);
BIND(119, Delete);
BIND(110, Home);
BIND(115, End);
BIND(77, NumLock);
BIND(106, KeypadSlash);
BIND(63, KeypadAsterisk);
BIND(91, KeypadDelete);
BIND(79, Keypad7); BIND(80, Keypad8); BIND(81, Keypad9); BIND(86, KeypadPlus);
BIND(83, Keypad4); BIND(84, Keypad5); BIND(85, Keypad6); BIND(82, KeypadMinus);
BIND(87, Keypad1); BIND(88, Keypad2); BIND(89, Keypad3); BIND(104, KeypadEnter);
BIND(90, Keypad0);
BIND(129, KeypadDecimalPoint);
BIND(125, KeypadEquals);
BIND(146, Help);
}
#undef BIND
}
// Fall back on a limited, faulty adaptation.
#define BIND2(qtKey, clkKey) case Qt::qtKey: return Inputs::Keyboard::Key::clkKey;
#define BIND(key) BIND2(Key_##key, key)
switch(event->key()) {
default: return {};
BIND(Escape);
BIND(F1); BIND(F2); BIND(F3); BIND(F4); BIND(F5); BIND(F6);
BIND(F7); BIND(F8); BIND(F9); BIND(F10); BIND(F11); BIND(F12);
BIND2(Key_Print, PrintScreen);
BIND(ScrollLock); BIND(Pause);
BIND2(Key_AsciiTilde, BackTick);
BIND2(Key_1, k1); BIND2(Key_2, k2); BIND2(Key_3, k3); BIND2(Key_4, k4); BIND2(Key_5, k5);
BIND2(Key_6, k6); BIND2(Key_7, k7); BIND2(Key_8, k8); BIND2(Key_9, k9); BIND2(Key_0, k0);
BIND2(Key_Minus, Hyphen);
BIND2(Key_Plus, Equals);
BIND(Backspace);
BIND(Tab); BIND(Q); BIND(W); BIND(E); BIND(R); BIND(T); BIND(Y);
BIND(U); BIND(I); BIND(O); BIND(P);
BIND2(Key_BraceLeft, OpenSquareBracket);
BIND2(Key_BraceRight, CloseSquareBracket);
BIND(Backslash);
BIND(CapsLock); BIND(A); BIND(S); BIND(D); BIND(F); BIND(G);
BIND(H); BIND(J); BIND(K); BIND(L);
BIND(Semicolon);
BIND2(Key_Apostrophe, Quote);
BIND2(Key_QuoteDbl, Quote);
// TODO: something to hash?
BIND2(Key_Return, Enter);
BIND2(Key_Shift, LeftShift);
BIND(Z); BIND(X); BIND(C); BIND(V);
BIND(B); BIND(N); BIND(M);
BIND(Comma);
BIND2(Key_Period, FullStop);
BIND2(Key_Slash, ForwardSlash);
// Omitted: right shift.
BIND2(Key_Control, LeftControl);
BIND2(Key_Alt, LeftOption);
BIND2(Key_Meta, LeftMeta);
BIND(Space);
BIND2(Key_AltGr, RightOption);
BIND(Left); BIND(Right); BIND(Up); BIND(Down);
BIND(Insert); BIND(Home); BIND(PageUp); BIND(Delete); BIND(End); BIND(PageDown);
BIND(NumLock);
}
#undef BIND
#undef BIND2
}
bool MainWindow::processEvent(QKeyEvent *event) {
if(!machine) return true;
const auto key = keyForEvent(event);
const auto key = keyMapper.keyForEvent(event);
if(!key) return true;
const bool isPressed = event->type() == QEvent::KeyPress;

View File

@@ -12,6 +12,7 @@
#include "timer.h"
#include "ui_mainwindow.h"
#include "functionthread.h"
#include "keyboard.h"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../../Machines/Utility/MachineForTarget.hpp"
@@ -144,7 +145,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
QMenu *inputMenu = nullptr;
std::optional<Inputs::Keyboard::Key> keyForEvent(QKeyEvent *);
KeyboardMapper keyMapper;
void register_led(const std::string &) override;
void set_led_status(const std::string &, bool) override;

View File

@@ -125,6 +125,7 @@ SOURCES += glob.glob('../../Storage/MassStorage/*.cpp')
SOURCES += glob.glob('../../Storage/MassStorage/Encodings/*.cpp')
SOURCES += glob.glob('../../Storage/MassStorage/Formats/*.cpp')
SOURCES += glob.glob('../../Storage/MassStorage/SCSI/*.cpp')
SOURCES += glob.glob('../../Storage/State/*.cpp')
SOURCES += glob.glob('../../Storage/Tape/*.cpp')
SOURCES += glob.glob('../../Storage/Tape/Formats/*.cpp')
SOURCES += glob.glob('../../Storage/Tape/Parsers/*.cpp')

View File

@@ -33,18 +33,18 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const {
case Register::L: return hl_.halves.low;
case Register::HL: return hl_.full;
case Register::ADash: return afDash_.halves.high;
case Register::FlagsDash: return afDash_.halves.low;
case Register::AFDash: return afDash_.full;
case Register::BDash: return bcDash_.halves.high;
case Register::CDash: return bcDash_.halves.low;
case Register::BCDash: return bcDash_.full;
case Register::DDash: return deDash_.halves.high;
case Register::EDash: return deDash_.halves.low;
case Register::DEDash: return deDash_.full;
case Register::HDash: return hlDash_.halves.high;
case Register::LDash: return hlDash_.halves.low;
case Register::HLDash: return hlDash_.full;
case Register::ADash: return af_dash_.halves.high;
case Register::FlagsDash: return af_dash_.halves.low;
case Register::AFDash: return af_dash_.full;
case Register::BDash: return bc_dash_.halves.high;
case Register::CDash: return bc_dash_.halves.low;
case Register::BCDash: return bc_dash_.full;
case Register::DDash: return de_dash_.halves.high;
case Register::EDash: return de_dash_.halves.low;
case Register::DEDash: return de_dash_.full;
case Register::HDash: return hl_dash_.halves.high;
case Register::LDash: return hl_dash_.halves.low;
case Register::HLDash: return hl_dash_.full;
case Register::IXh: return ix_.halves.high;
case Register::IXl: return ix_.halves.low;
@@ -86,18 +86,18 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
case Register::L: hl_.halves.low = uint8_t(value); break;
case Register::HL: hl_.full = value; break;
case Register::ADash: afDash_.halves.high = uint8_t(value); break;
case Register::FlagsDash: afDash_.halves.low = uint8_t(value); break;
case Register::AFDash: afDash_.full = value; break;
case Register::BDash: bcDash_.halves.high = uint8_t(value); break;
case Register::CDash: bcDash_.halves.low = uint8_t(value); break;
case Register::BCDash: bcDash_.full = value; break;
case Register::DDash: deDash_.halves.high = uint8_t(value); break;
case Register::EDash: deDash_.halves.low = uint8_t(value); break;
case Register::DEDash: deDash_.full = value; break;
case Register::HDash: hlDash_.halves.high = uint8_t(value); break;
case Register::LDash: hlDash_.halves.low = uint8_t(value); break;
case Register::HLDash: hlDash_.full = value; break;
case Register::ADash: af_dash_.halves.high = uint8_t(value); break;
case Register::FlagsDash: af_dash_.halves.low = uint8_t(value); break;
case Register::AFDash: af_dash_.full = value; break;
case Register::BDash: bc_dash_.halves.high = uint8_t(value); break;
case Register::CDash: bc_dash_.halves.low = uint8_t(value); break;
case Register::BCDash: bc_dash_.full = value; break;
case Register::DDash: de_dash_.halves.high = uint8_t(value); break;
case Register::EDash: de_dash_.halves.low = uint8_t(value); break;
case Register::DEDash: de_dash_.full = value; break;
case Register::HDash: hl_dash_.halves.high = uint8_t(value); break;
case Register::LDash: hl_dash_.halves.low = uint8_t(value); break;
case Register::HLDash: hl_dash_.full = value; break;
case Register::IXh: ix_.halves.high = uint8_t(value); break;
case Register::IXl: ix_.halves.low = uint8_t(value); break;

View File

@@ -461,17 +461,17 @@ template < class T,
case MicroOp::ExAFAFDash: {
const uint8_t a = a_;
const uint8_t f = get_flags();
set_flags(afDash_.halves.low);
a_ = afDash_.halves.high;
afDash_.halves.high = a;
afDash_.halves.low = f;
set_flags(af_dash_.halves.low);
a_ = af_dash_.halves.high;
af_dash_.halves.high = a;
af_dash_.halves.low = f;
} break;
case MicroOp::EXX: {
uint16_t temp;
swap(de_, deDash_);
swap(bc_, bcDash_);
swap(hl_, hlDash_);
swap(de_, de_dash_);
swap(bc_, bc_dash_);
swap(hl_, hl_dash_);
} break;
#undef swap

View File

@@ -129,7 +129,7 @@ class ProcessorStorage {
uint8_t a_;
RegisterPair16 bc_, de_, hl_;
RegisterPair16 afDash_, bcDash_, deDash_, hlDash_;
RegisterPair16 af_dash_, bc_dash_, de_dash_, hl_dash_;
RegisterPair16 ix_, iy_, pc_, sp_;
RegisterPair16 ir_, refresh_addr_;
bool iff1_ = false, iff2_ = false;

View File

@@ -19,10 +19,10 @@ State::State(const ProcessorBase &src): State() {
registers.bc = src.bc_.full;
registers.de = src.de_.full;
registers.hl = src.hl_.full;
registers.afDash = src.afDash_.full;
registers.bcDash = src.bcDash_.full;
registers.deDash = src.deDash_.full;
registers.hlDash = src.hlDash_.full;
registers.af_dash = src.af_dash_.full;
registers.bc_dash = src.bc_dash_.full;
registers.de_dash = src.de_dash_.full;
registers.hl_dash = src.hl_dash_.full;
registers.ix = src.ix_.full;
registers.iy = src.iy_.full;
registers.ir = src.ir_.full;
@@ -108,10 +108,10 @@ void State::apply(ProcessorBase &target) {
target.bc_.full = registers.bc;
target.de_.full = registers.de;
target.hl_.full = registers.hl;
target.afDash_.full = registers.afDash;
target.bcDash_.full = registers.bcDash;
target.deDash_.full = registers.deDash;
target.hlDash_.full = registers.hlDash;
target.af_dash_.full = registers.af_dash;
target.bc_dash_.full = registers.bc_dash;
target.de_dash_.full = registers.de_dash;
target.hl_dash_.full = registers.hl_dash;
target.ix_.full = registers.ix;
target.iy_.full = registers.iy;
target.ir_.full = registers.ir;
@@ -179,10 +179,10 @@ State::Registers::Registers() {
DeclareField(bc);
DeclareField(de);
DeclareField(hl);
DeclareField(afDash);
DeclareField(bcDash);
DeclareField(deDash);
DeclareField(hlDash);
DeclareField(af_dash); // TODO: is there any disadvantage to declaring these for reflective
DeclareField(bc_dash); // purposes as AF', BC', etc?
DeclareField(de_dash);
DeclareField(hl_dash);
DeclareField(ix);
DeclareField(iy);
DeclareField(ir);

View File

@@ -31,7 +31,7 @@ struct State: public Reflection::StructImpl<State> {
uint8_t a;
uint8_t flags;
uint16_t bc, de, hl;
uint16_t afDash, bcDash, deDash, hlDash;
uint16_t af_dash, bc_dash, de_dash, hl_dash;
uint16_t ix, iy, ir;
uint16_t program_counter, stack_pointer;
uint16_t memptr;
@@ -60,25 +60,25 @@ struct State: public Reflection::StructImpl<State> {
obviously doesn't.
*/
struct ExecutionState: public Reflection::StructImpl<ExecutionState> {
bool is_halted;
bool is_halted = false;
uint8_t requests;
uint8_t last_requests;
uint8_t temp8;
uint8_t operation;
uint16_t temp16;
unsigned int flag_adjustment_history;
uint16_t pc_increment;
uint16_t refresh_address;
uint8_t requests = 0;
uint8_t last_requests = 0;
uint8_t temp8 = 0;
uint8_t operation = 0;
uint16_t temp16 = 0;
unsigned int flag_adjustment_history = 0;
uint16_t pc_increment = 1;
uint16_t refresh_address = 0;
ReflectableEnum(Phase,
UntakenConditionalCall, Reset, IRQMode0, IRQMode1, IRQMode2,
NMI, FetchDecode, Operation
);
Phase phase;
int half_cycles_into_step;
int steps_into_phase;
Phase phase = Phase::FetchDecode;
int half_cycles_into_step = 0;
int steps_into_phase = 0;
uint16_t instruction_page = 0;
ExecutionState();

View File

@@ -22,7 +22,7 @@ It currently contains emulations of the:
* Oric 1/Atmos;
* Sega Master System;
* Sinclair ZX80/81; and
* Sinclair ZX Spectrum +2a/+3.
* Sinclair ZX Spectrum.
## Single-step Loading

View File

@@ -12,6 +12,7 @@
#include <cassert>
#include <cstdarg>
#include <cstring>
#include <cstddef>
#include <string>
#include <typeindex>
#include <typeinfo>
@@ -318,12 +319,12 @@ template <typename Owner> class StructImpl: public Struct {
if(iterator != contents_.end()) {
return iterator->first;
} else {
return "";
return std::string();
}
}
private:
template <typename Type> bool declare_reflectable(Type *t, const std::string &name) {
template <typename Type> bool declare_reflectable([[maybe_unused]] Type *t, const std::string &name) {
if constexpr (std::is_base_of<Reflection::Struct, Type>::value) {
Reflection::Struct *const str = static_cast<Reflection::Struct *>(t);
declare_emplace(str, name);

View File

@@ -43,7 +43,7 @@ class FileHolder final {
Rewrite opens the file for rewriting; none of the original content is preserved; whatever
the caller outputs will replace the existing file.
@raises ErrorCantOpen if the file cannot be opened.
@throws ErrorCantOpen if the file cannot be opened.
*/
FileHolder(const std::string &file_name, FileMode ideal_mode = FileMode::ReadWrite);

79
Storage/State/SNA.cpp Normal file
View File

@@ -0,0 +1,79 @@
//
// SNA.cpp
// Clock Signal
//
// Created by Thomas Harte on 24/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "SNA.hpp"
#include "../FileHolder.hpp"
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
#include "../../Machines/Sinclair/ZXSpectrum/State.hpp"
using namespace Storage::State;
std::unique_ptr<Analyser::Static::Target> SNA::load(const std::string &file_name) {
// Make sure the file is accessible and appropriately sized.
FileHolder file(file_name);
if(file.stats().st_size != 48*1024 + 0x1b) {
return nullptr;
}
// SNAs are always for 48kb machines.
using Target = Analyser::Static::ZXSpectrum::Target;
auto result = std::make_unique<Target>();
result->model = Target::Model::FortyEightK;
// Prepare to populate ZX Spectrum state.
auto *const state = new Sinclair::ZXSpectrum::State();
result->state = std::unique_ptr<Reflection::Struct>(state);
// Comments below: [offset] [contents]
// 00 I
const uint8_t i = file.get8();
// 01 HL'; 03 DE'; 05 BC'; 07 AF'
state->z80.registers.hl_dash = file.get16le();
state->z80.registers.de_dash = file.get16le();
state->z80.registers.bc_dash = file.get16le();
state->z80.registers.af_dash = file.get16le();
// 09 HL; 0B DE; 0D BC; 0F IY; 11 IX
state->z80.registers.hl = file.get16le();
state->z80.registers.de = file.get16le();
state->z80.registers.bc = file.get16le();
state->z80.registers.iy = file.get16le();
state->z80.registers.ix = file.get16le();
// 13 IFF2 (in bit 2)
const uint8_t iff = file.get8();
state->z80.registers.iff1 = state->z80.registers.iff2 = iff & 4;
// 14 R
const uint8_t r = file.get8();
state->z80.registers.ir = uint16_t((i << 8) | r);
// 15 AF; 17 SP; 19 interrupt mode
state->z80.registers.flags = file.get8();
state->z80.registers.a = file.get8();
state->z80.registers.stack_pointer = file.get16le();
state->z80.registers.interrupt_mode = file.get8();
// 1A border colour
state->video.border_colour = file.get8();
// 1B 48kb RAM contents
state->ram = file.read(48*1024);
// To establish program counter, point it to a RET that
// I know is in the 16/48kb ROM. This avoids having to
// try to do a pop here, given that the true program counter
// might currently be in the ROM.
state->z80.registers.program_counter = 0x1d83;
return result;
}

24
Storage/State/SNA.hpp Normal file
View File

@@ -0,0 +1,24 @@
//
// SNA.hpp
// Clock Signal
//
// Created by Thomas Harte on 24/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef Storage_State_SNA_hpp
#define Storage_State_SNA_hpp
#include "../../Analyser/Static/StaticAnalyser.hpp"
namespace Storage {
namespace State {
struct SNA {
static std::unique_ptr<Analyser::Static::Target> load(const std::string &file_name);
};
}
}
#endif /* Storage_State_SNA_hpp */

196
Storage/State/SZX.cpp Normal file
View File

@@ -0,0 +1,196 @@
//
// SZX.cpp
// Clock Signal
//
// Created by Thomas Harte on 26/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "SZX.hpp"
#include "../FileHolder.hpp"
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
#include "../../Machines/Sinclair/ZXSpectrum/State.hpp"
#define LOG_PREFIX "[SZX] "
#include "../../Outputs/Log.hpp"
#include <zlib.h>
using namespace Storage::State;
std::unique_ptr<Analyser::Static::Target> SZX::load(const std::string &file_name) {
FileHolder file(file_name);
// Construct a target with a Spectrum state.
using Target = Analyser::Static::ZXSpectrum::Target;
auto result = std::make_unique<Target>();
auto *const state = new Sinclair::ZXSpectrum::State();
result->state = std::unique_ptr<Reflection::Struct>(state);
// Check signature and major version number.
if(!file.check_signature("ZXST")) {
return nullptr;
}
const uint8_t major_version = file.get8();
[[maybe_unused]] const uint8_t minor_version = file.get8();
if(major_version > 1) {
return nullptr;
}
// Check for a supported machine type.
const uint8_t machine_type = file.get8();
switch(machine_type) {
default: return nullptr;
case 0: result->model = Target::Model::SixteenK; break;
case 1: result->model = Target::Model::FortyEightK; break;
case 2: result->model = Target::Model::OneTwoEightK; break;
case 3: result->model = Target::Model::Plus2; break;
case 4: result->model = Target::Model::Plus2a; break;
case 5: result->model = Target::Model::Plus3; break;
}
// Consequential upon selected machine...
switch(result->model) {
case Target::Model::SixteenK: state->ram.resize(16 * 1024); break;
case Target::Model::FortyEightK: state->ram.resize(48 * 1024); break;
default:
state->ram.resize(128 * 1024);
break;
}
const uint8_t file_flags = file.get8();
[[maybe_unused]] const bool uses_late_timings = file_flags & 1;
// Now parse all included blocks.
while(true) {
const uint32_t blockID = file.get32le();
const uint32_t size = file.get32le();
if(file.eof()) break;
const auto location = file.tell();
#define BLOCK(str) str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24)
switch(blockID) {
default:
LOG("Unhandled block " << char(blockID) << char(blockID >> 8) << char(blockID >> 16) << char(blockID >> 24));
break;
// ZXSTZ80REGS
case BLOCK("Z80R"): {
state->z80.registers.flags = file.get8();
state->z80.registers.a = file.get8();
state->z80.registers.bc = file.get16le();
state->z80.registers.de = file.get16le();
state->z80.registers.hl = file.get16le();
state->z80.registers.af_dash = file.get16le();
state->z80.registers.bc_dash = file.get16le();
state->z80.registers.de_dash = file.get16le();
state->z80.registers.hl_dash = file.get16le();
state->z80.registers.ix = file.get16le();
state->z80.registers.iy = file.get16le();
state->z80.registers.stack_pointer = file.get16le();
state->z80.registers.program_counter = file.get16le();
const uint8_t i = file.get8();
const uint8_t r = file.get8();
state->z80.registers.ir = uint16_t((i << 8) | r);
state->z80.registers.iff1 = file.get8();
state->z80.registers.iff2 = file.get8();
state->z80.registers.interrupt_mode = file.get8();
state->video.half_cycles_since_interrupt = int(file.get32le()) * 2;
// SZX includes a count of remaining cycles that interrupt should be asserted for
// because it supports hardware that might cause an interrupt other than the display.
// This emulator doesn't, so this field can be ignored.
[[maybe_unused]] uint8_t remaining_interrupt_cycles = file.get8();
const uint8_t flags = file.get8();
state->z80.execution_state.is_halted = flags & 2;
// TODO: bit 0 indicates that the last instruction was an EI, or an invalid
// DD or FD. I assume I'm supposed to use that to conclude an interrupt
// verdict but I'm unclear what the effect of an invalid DD or FD is so
// have not yet implemented this.
state->z80.registers.memptr = file.get16le();
} break;
// ZXSTAYBLOCK
case BLOCK("AY\0\0"): {
// This applies to 48kb machines with AY boxes only. This emulator
// doesn't currently support those.
[[maybe_unused]] const uint8_t interface_type = file.get8();
state->ay.selected_register = file.get8();
file.read(state->ay.registers, 16);
} break;
// ZXSTRAMPAGE
case BLOCK("RAMP"): {
const uint16_t flags = file.get16le();
const uint8_t page = file.get8();
std::vector<uint8_t> contents;
if(flags & 1) {
// ZLib compression is applied.
contents.resize(16 * 1024);
const std::vector<uint8_t> source = file.read(size - 3);
uLongf output_length;
uncompress(contents.data(), &output_length, source.data(), source.size());
assert(output_length == contents.size());
} else {
// Data is raw.
contents = file.read(16 * 1024);
}
switch(result->model) {
case Target::Model::SixteenK:
case Target::Model::FortyEightK: {
size_t address = 0;
switch(page) {
default: break;
case 5: address = 0x4000; break;
case 2: address = 0x8000; break;
case 0: address = 0xc000; break;
}
if(address > 0 && (address - 0x4000) <= state->ram.size()) {
memcpy(&state->ram[address - 0x4000], contents.data(), 0x4000);
}
} break;
default:
if(page < 8) {
memcpy(&state->ram[page * 0x4000], contents.data(), 0x4000);
}
break;
}
} break;
// ZXSTSPECREGS
case BLOCK("SPCR"): {
state->video.border_colour = file.get8();
state->last_7ffd = file.get8();
state->last_1ffd = file.get8();
// TODO: use last write to FE, at least.
} break;
}
#undef BLOCK
// Advance to the next block.
file.seek(location + size, SEEK_SET);
}
return result;
}

24
Storage/State/SZX.hpp Normal file
View File

@@ -0,0 +1,24 @@
//
// SZX.hpp
// Clock Signal
//
// Created by Thomas Harte on 26/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef Storage_State_SZX_hpp
#define Storage_State_SZX_hpp
#include "../../Analyser/Static/StaticAnalyser.hpp"
namespace Storage {
namespace State {
struct SZX {
static std::unique_ptr<Analyser::Static::Target> load(const std::string &file_name);
};
}
}
#endif /* Storage_State_SZX_hpp */

209
Storage/State/Z80.cpp Normal file
View File

@@ -0,0 +1,209 @@
//
// Z80.cpp
// Clock Signal
//
// Created by Thomas Harte on 25/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "Z80.hpp"
#include "../FileHolder.hpp"
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
#include "../../Machines/Sinclair/ZXSpectrum/State.hpp"
using namespace Storage::State;
namespace {
std::vector<uint8_t> read_memory(Storage::FileHolder &file, size_t size, bool is_compressed) {
if(!is_compressed) {
return file.read(size);
}
std::vector<uint8_t> result(size);
size_t cursor = 0;
while(cursor != size) {
const uint8_t next = file.get8();
// If the next byte definitely doesn't, or can't,
// start an ED ED sequence then just take it.
if(next != 0xed || cursor == size - 1) {
result[cursor] = next;
++cursor;
continue;
}
// Grab the next byte. If it's not ED then write
// both and continue.
const uint8_t after = file.get8();
if(after != 0xed) {
result[cursor] = next;
result[cursor+1] = after;
cursor += 2;
continue;
}
// An ED ED has begun, so grab the RLE sequence.
const uint8_t count = file.get8();
const uint8_t value = file.get8();
memset(&result[cursor], value, count);
cursor += count;
}
return result;
}
}
std::unique_ptr<Analyser::Static::Target> Z80::load(const std::string &file_name) {
FileHolder file(file_name);
// Construct a target with a Spectrum state.
using Target = Analyser::Static::ZXSpectrum::Target;
auto result = std::make_unique<Target>();
auto *const state = new Sinclair::ZXSpectrum::State();
result->state = std::unique_ptr<Reflection::Struct>(state);
// Read version 1 header.
state->z80.registers.a = file.get8();
state->z80.registers.flags = file.get8();
state->z80.registers.bc = file.get16le();
state->z80.registers.hl = file.get16le();
state->z80.registers.program_counter = file.get16le();
state->z80.registers.stack_pointer = file.get16le();
state->z80.registers.ir = file.get16be(); // Stored I then R.
// Bit 7 of R is stored separately; likely this relates to an
// optimisation in the Z80 emulator that for some reason was
// exported into its file format.
const uint8_t raw_misc = file.get8();
const uint8_t misc = (raw_misc == 0xff) ? 1 : raw_misc;
state->z80.registers.ir = uint16_t((state->z80.registers.ir & ~0x80) | ((misc&1) << 7));
state->z80.registers.de = file.get16le();
state->z80.registers.bc_dash = file.get16le();
state->z80.registers.de_dash = file.get16le();
state->z80.registers.hl_dash = file.get16le();
state->z80.registers.af_dash = file.get16be(); // Stored A' then F'.
state->z80.registers.iy = file.get16le();
state->z80.registers.ix = file.get16le();
state->z80.registers.iff1 = bool(file.get8());
state->z80.registers.iff2 = bool(file.get8());
// Ignored from the next byte:
//
// bit 2 = 1 => issue 2 emulation
// bit 3 = 1 => double interrupt frequency (?)
// bit 45 => video synchronisation (to do with emulation hackery?)
// bit 67 => joystick type
state->z80.registers.interrupt_mode = file.get8() & 3;
// If the program counter is non-0 then this is a version 1 snapshot,
// which means it's definitely a 48k image.
if(state->z80.registers.program_counter) {
result->model = Target::Model::FortyEightK;
state->ram = read_memory(file, 48*1024, misc & 0x20);
return result;
}
// This was a version 1 or 2 snapshot, so keep going...
const uint16_t bonus_header_size = file.get16le();
if(bonus_header_size != 23 && bonus_header_size != 54 && bonus_header_size != 55) {
return nullptr;
}
state->z80.registers.program_counter = file.get16le();
const uint8_t model = file.get8();
switch(model) {
default: return nullptr;
case 0: result->model = Target::Model::FortyEightK; break;
case 3: result->model = Target::Model::OneTwoEightK; break;
case 7:
case 8: result->model = Target::Model::Plus3; break;
case 12: result->model = Target::Model::Plus2; break;
case 13: result->model = Target::Model::Plus2a; break;
}
state->last_7ffd = file.get8();
file.seek(1, SEEK_CUR);
if(file.get8() & 0x80) {
// The 'hardware modify' bit, which inexplicably does this:
switch(result->model) {
default: break;
case Target::Model::FortyEightK: result->model = Target::Model::SixteenK; break;
case Target::Model::OneTwoEightK: result->model = Target::Model::Plus2; break;
case Target::Model::Plus3: result->model = Target::Model::Plus2a; break;
}
}
state->ay.selected_register = file.get8();
file.read(state->ay.registers, 16);
if(bonus_header_size != 23) {
// More Z80, the emulator, lack of encapsulation to deal with here.
const uint16_t low_t_state = file.get16le();
const uint16_t high_t_state = file.get8();
switch(result->model) {
case Target::Model::SixteenK:
case Target::Model::FortyEightK:
state->video.half_cycles_since_interrupt = ((17471 - low_t_state) + (high_t_state * 17472)) * 2;
break;
default:
state->video.half_cycles_since_interrupt = ((17726 - low_t_state) + (high_t_state * 17727)) * 2;
break;
}
// Skip: Spectator flag, MGT, Multiface and other ROM flags.
file.seek(5, SEEK_CUR);
// Skip: highly Z80-the-emulator-specific stuff about user-defined joystick.
file.seek(20, SEEK_CUR);
// Skip: Disciple/Plus D stuff.
file.seek(3, SEEK_CUR);
if(bonus_header_size == 55) {
state->last_1ffd = file.get8();
}
}
// Grab RAM.
switch(result->model) {
case Target::Model::SixteenK: state->ram.resize(16 * 1024); break;
case Target::Model::FortyEightK: state->ram.resize(48 * 1024); break;
default: state->ram.resize(128 * 1024); break;
}
while(true) {
const uint16_t block_size = file.get16le();
const uint8_t page = file.get8();
const auto location = file.tell();
if(file.eof()) break;
const auto data = read_memory(file, 16384, block_size != 0xffff);
if(result->model == Target::Model::SixteenK || result->model == Target::Model::FortyEightK) {
switch(page) {
default: break;
case 4: memcpy(&state->ram[0x4000], data.data(), 16384); break;
case 5: memcpy(&state->ram[0x8000], data.data(), 16384); break;
case 8: memcpy(&state->ram[0x0000], data.data(), 16384); break;
}
} else {
if(page >= 3 && page <= 10) {
memcpy(&state->ram[(page - 3) * 0x4000], data.data(), 16384);
}
}
assert(location + block_size == file.tell());
file.seek(location + block_size, SEEK_SET);
}
return result;
}

24
Storage/State/Z80.hpp Normal file
View File

@@ -0,0 +1,24 @@
//
// Z80.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef Storage_State_Z80_hpp
#define Storage_State_Z80_hpp
#include "../../Analyser/Static/StaticAnalyser.hpp"
namespace Storage {
namespace State {
struct Z80 {
static std::unique_ptr<Analyser::Static::Target> load(const std::string &file_name);
};
}
}
#endif /* Storage_State_Z80_hpp */

View File

@@ -15,9 +15,19 @@ using namespace Storage::Tape;
OricTAP::OricTAP(const std::string &file_name) :
file_(file_name)
{
// check the file signature
if(!file_.check_signature("\x16\x16\x16\x24", 4))
throw ErrorNotOricTAP;
// Check for a sequence of at least three 0x16s followed by a 0x24.
while(true) {
const uint8_t next = file_.get8();
if(next != 0x16 && next != 0x24) {
throw ErrorNotOricTAP;
}
if(next == 0x24) {
if(file_.tell() < 4) {
throw ErrorNotOricTAP;
}
break;
}
}
// then rewind and start again
virtual_reset();

View File

@@ -40,8 +40,8 @@ enum Type: IntType {
Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB,
ZX8081 = ZX80 | ZX81,
AllCartridge = Atari2600 | AcornElectron | Coleco | MSX,
AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX, // TODO: | AtariST
AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX80 | ZX81 | MSX | ZXSpectrum,
AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII,
AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX8081 | MSX | ZXSpectrum,
};
class TypeDistinguisher {