mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-25 09:27:01 +00:00
Compare commits
170 Commits
2021-04-06
...
2021-05-05
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11228dc265 | ||
|
|
ef50967793 | ||
|
|
5f6c08b7e0 | ||
|
|
6cb23ec5be | ||
|
|
1bae70bcf8 | ||
|
|
9820591ba4 | ||
|
|
77071b3c69 | ||
|
|
335e839b31 | ||
|
|
6fe947b8b9 | ||
|
|
22b29e77a7 | ||
|
|
4858cfce6b | ||
|
|
8da3e91f5e | ||
|
|
012235bfeb | ||
|
|
052e284c33 | ||
|
|
32e3dd71b1 | ||
|
|
95f4272919 | ||
|
|
00679b6135 | ||
|
|
2c18bb4508 | ||
|
|
0cf1c9040a | ||
|
|
9196341482 | ||
|
|
685140a4c2 | ||
|
|
1465b0ee4d | ||
|
|
0bf6b765d3 | ||
|
|
4774676e2a | ||
|
|
9c29655da2 | ||
|
|
c8ab18f2b6 | ||
|
|
8ebce466db | ||
|
|
1b39b17125 | ||
|
|
5a46853075 | ||
|
|
48ad4d4c4c | ||
|
|
056a036712 | ||
|
|
70eaa79108 | ||
|
|
c906dc3c0a | ||
|
|
d1dcb41b6f | ||
|
|
96ac86a757 | ||
|
|
4919786825 | ||
|
|
24b4185714 | ||
|
|
ad10d0037a | ||
|
|
b6554c8255 | ||
|
|
01dc83d0d6 | ||
|
|
2fd08789ab | ||
|
|
bc9e529995 | ||
|
|
708c24cc57 | ||
|
|
7fb3048257 | ||
|
|
9319f0525a | ||
|
|
b7a62e0121 | ||
|
|
bd5dd9b9a3 | ||
|
|
3348167c46 | ||
|
|
700c505974 | ||
|
|
d403036d86 | ||
|
|
5e08d7db39 | ||
|
|
c34cb310a8 | ||
|
|
8d86aa69bc | ||
|
|
cc41ccc5f1 | ||
|
|
e6252fe0ed | ||
|
|
03577de675 | ||
|
|
205518ba75 | ||
|
|
2510064218 | ||
|
|
0ef2806970 | ||
|
|
d80f03e369 | ||
|
|
fd271d920b | ||
|
|
2bbf8bc9fa | ||
|
|
9b65d56ed0 | ||
|
|
a5098a60ec | ||
|
|
0ebd900e40 | ||
|
|
7aeb17ac92 | ||
|
|
cc78bfb229 | ||
|
|
485c2a866c | ||
|
|
5b419ca5bf | ||
|
|
14ae579fca | ||
|
|
1c2ea0d7fe | ||
|
|
e7a9ae18a1 | ||
|
|
d61f478a39 | ||
|
|
9cc747b3e2 | ||
|
|
2f223f7db2 | ||
|
|
17f11a3be3 | ||
|
|
37dcf61130 | ||
|
|
856ebfacca | ||
|
|
9731fdd33b | ||
|
|
5ea605ccf7 | ||
|
|
d0c789ff9a | ||
|
|
9baa861742 | ||
|
|
30a1a53c97 | ||
|
|
bdb1b7e77c | ||
|
|
9293bcbc88 | ||
|
|
c481f475e7 | ||
|
|
ef01471e17 | ||
|
|
73c8157197 | ||
|
|
af1dc2d3b2 | ||
|
|
8f6b3feee1 | ||
|
|
a20f5528b7 | ||
|
|
f48876d80e | ||
|
|
db52f13c32 | ||
|
|
2590769d3f | ||
|
|
5667dcac36 | ||
|
|
bec71ead39 | ||
|
|
e4d9022d37 | ||
|
|
572be48f38 | ||
|
|
6f4ccebfa1 | ||
|
|
77fcf52d27 | ||
|
|
79c2bc1fd7 | ||
|
|
76370d9418 | ||
|
|
7bac18bd65 | ||
|
|
704737144a | ||
|
|
2a9c73a1d3 | ||
|
|
e87e851401 | ||
|
|
80d4846a27 | ||
|
|
9fd53c9c91 | ||
|
|
53eae873d8 | ||
|
|
93422f4b1c | ||
|
|
06cedb2e50 | ||
|
|
7fdb1d848b | ||
|
|
246fd9442f | ||
|
|
eb99a64b29 | ||
|
|
d7954a4cb1 | ||
|
|
ef636da866 | ||
|
|
fa18b06dbf | ||
|
|
349b9ce502 | ||
|
|
b2cf121410 | ||
|
|
71cf63bd35 | ||
|
|
d1bb3aada4 | ||
|
|
b4214c6e08 | ||
|
|
f5c7746493 | ||
|
|
f10ec80153 | ||
|
|
0af405aa46 | ||
|
|
cf481effa6 | ||
|
|
a1511f9600 | ||
|
|
325e2b3941 | ||
|
|
7017324d60 | ||
|
|
deb5d69ac7 | ||
|
|
68a04f4e6a | ||
|
|
0d61902b10 | ||
|
|
3eec210b30 | ||
|
|
5998f3b35b | ||
|
|
869567fdd9 | ||
|
|
2e70b5eb9f | ||
|
|
8a3bfb8672 | ||
|
|
06f1e64177 | ||
|
|
b42780173a | ||
|
|
36c8821c4c | ||
|
|
947de2d54a | ||
|
|
9347fe5f44 | ||
|
|
e82367def3 | ||
|
|
9cde7c12ba | ||
|
|
015556cc91 | ||
|
|
47c5a243aa | ||
|
|
070e359d82 | ||
|
|
b397059d5e | ||
|
|
400f54e508 | ||
|
|
e0736435f8 | ||
|
|
b09c5538c6 | ||
|
|
ce3d2913bf | ||
|
|
87202a2a27 | ||
|
|
818a4dff25 | ||
|
|
eacffa49f5 | ||
|
|
9e506c3206 | ||
|
|
29cf80339a | ||
|
|
50f53f7d97 | ||
|
|
73fbd89c85 | ||
|
|
f74fa06f2d | ||
|
|
ee989ab762 | ||
|
|
818655a9b6 | ||
|
|
57a7e0834f | ||
|
|
cd787486d2 | ||
|
|
67fd6787a6 | ||
|
|
627b96f73c | ||
|
|
25b8c4c062 | ||
|
|
1be88a5308 | ||
|
|
294280a94e | ||
|
|
32aebfebe0 |
@@ -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));\
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,11 +19,15 @@ namespace ZXSpectrum {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(Model,
|
||||
SixteenK,
|
||||
FortyEightK,
|
||||
OneTwoEightK,
|
||||
Plus2,
|
||||
Plus2a,
|
||||
Plus3,
|
||||
);
|
||||
|
||||
Model model = Model::Plus2a;
|
||||
Model model = Model::Plus2;
|
||||
bool should_hold_enter = false;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::ZXSpectrum) {
|
||||
|
||||
@@ -129,8 +129,8 @@ ClockingHint::Preference ACIA::preferred_clocking() const {
|
||||
// because it's unclear when the interrupt might come.
|
||||
if(bits_incoming_ && receive_interrupt_enabled_) return ClockingHint::Preference::RealTime;
|
||||
|
||||
// No clocking required then.
|
||||
return ClockingHint::Preference::None;
|
||||
// Real-time clocking not required then.
|
||||
return ClockingHint::Preference::JustInTime;
|
||||
}
|
||||
|
||||
bool ACIA::get_interrupt_line() const {
|
||||
|
||||
@@ -208,7 +208,7 @@ void MFP68901::run_for(HalfCycles time) {
|
||||
}
|
||||
|
||||
HalfCycles MFP68901::get_next_sequence_point() {
|
||||
return HalfCycles(-1);
|
||||
return HalfCycles::max();
|
||||
}
|
||||
|
||||
// MARK: - Timers
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -48,7 +48,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
public MachineTypes::MappedKeyboardMachine,
|
||||
public MachineTypes::JoystickMachine,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public Inputs::Keyboard,
|
||||
public Configurable::Device,
|
||||
public Activity::Source,
|
||||
public Apple::II::Card::Delegate {
|
||||
@@ -66,7 +65,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
uint8_t *ram_, *aux_ram_;
|
||||
};
|
||||
|
||||
CPU::MOS6502::Processor<(model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::PSynertek65C02 : CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
using Processor = CPU::MOS6502::Processor<
|
||||
(model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::PSynertek65C02 : CPU::MOS6502::Personality::P6502,
|
||||
ConcreteMachine,
|
||||
false>;
|
||||
Processor m6502_;
|
||||
VideoBusHandler video_bus_handler_;
|
||||
Apple::II::Video::Video<VideoBusHandler, is_iie()> video_;
|
||||
int cycles_into_current_line_ = 0;
|
||||
@@ -91,16 +94,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
|
||||
uint8_t ram_[65536], aux_ram_[65536];
|
||||
std::vector<uint8_t> rom_;
|
||||
uint8_t keyboard_input_ = 0x00;
|
||||
bool key_is_down_ = false;
|
||||
|
||||
uint8_t get_keyboard_input() {
|
||||
if(string_serialiser_) {
|
||||
return string_serialiser_->head() | 0x80;
|
||||
} else {
|
||||
return keyboard_input_;
|
||||
}
|
||||
}
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
Audio::Toggle audio_toggle_;
|
||||
@@ -258,16 +251,99 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
state.region_20_40.write ? &aux_ram_[0x2000] : &ram_[0x2000]);
|
||||
}
|
||||
|
||||
// MARK - typing
|
||||
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
|
||||
// MARK: - Keyboard and typing.
|
||||
|
||||
// MARK - Joysticks.
|
||||
struct Keyboard: public Inputs::Keyboard {
|
||||
Keyboard(Processor *m6502) : m6502_(m6502) {}
|
||||
|
||||
void reset_all_keys() final {
|
||||
open_apple_is_pressed = closed_apple_is_pressed = key_is_down = false;
|
||||
}
|
||||
|
||||
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
||||
// If no ASCII value is supplied, look for a few special cases.
|
||||
switch(key) {
|
||||
case Key::Left: value = 0x08; break;
|
||||
case Key::Right: value = 0x15; break;
|
||||
case Key::Down: value = 0x0a; break;
|
||||
case Key::Up: value = 0x0b; break;
|
||||
case Key::Backspace: value = 0x7f; break;
|
||||
case Key::Enter: value = 0x0d; break;
|
||||
case Key::Tab: value = '\t'; break;
|
||||
case Key::Escape: value = 0x1b; break;
|
||||
|
||||
case Key::LeftOption:
|
||||
case Key::RightMeta:
|
||||
open_apple_is_pressed = is_pressed;
|
||||
return true;
|
||||
|
||||
case Key::RightOption:
|
||||
case Key::LeftMeta:
|
||||
closed_apple_is_pressed = is_pressed;
|
||||
return true;
|
||||
|
||||
case Key::F1: case Key::F2: case Key::F3: case Key::F4:
|
||||
case Key::F5: case Key::F6: case Key::F7: case Key::F8:
|
||||
case Key::F9: case Key::F10: case Key::F11: case Key::F12:
|
||||
case Key::PrintScreen:
|
||||
case Key::ScrollLock:
|
||||
case Key::Pause:
|
||||
case Key::Insert:
|
||||
case Key::Home:
|
||||
case Key::PageUp:
|
||||
case Key::PageDown:
|
||||
case Key::End:
|
||||
// Accept a bunch non-symbolic other keys, as
|
||||
// reset, in the hope that the user can find
|
||||
// at least one usable key.
|
||||
m6502_->set_reset_line(is_pressed);
|
||||
return true;
|
||||
|
||||
default:
|
||||
if(!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||
if(!is_iie()) value = char(toupper(value));
|
||||
break;
|
||||
}
|
||||
|
||||
if(is_pressed) {
|
||||
keyboard_input = uint8_t(value | 0x80);
|
||||
key_is_down = true;
|
||||
} else {
|
||||
if((keyboard_input & 0x7f) == value) {
|
||||
key_is_down = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t get_keyboard_input() {
|
||||
if(string_serialiser) {
|
||||
return string_serialiser->head() | 0x80;
|
||||
} else {
|
||||
return keyboard_input;
|
||||
}
|
||||
}
|
||||
|
||||
// The IIe has three keys that are wired directly to the same input as the joystick buttons.
|
||||
bool open_apple_is_pressed = false;
|
||||
bool closed_apple_is_pressed = false;
|
||||
uint8_t keyboard_input = 0x00;
|
||||
bool key_is_down = false;
|
||||
std::unique_ptr<Utility::StringSerialiser> string_serialiser;
|
||||
|
||||
private:
|
||||
Processor *const m6502_;
|
||||
};
|
||||
Keyboard keyboard_;
|
||||
|
||||
// MARK: - Joysticks.
|
||||
JoystickPair joysticks_;
|
||||
|
||||
// The IIe has three keys that are wired directly to the same input as the joystick buttons.
|
||||
bool open_apple_is_pressed_ = false;
|
||||
bool closed_apple_is_pressed_ = false;
|
||||
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||
m6502_(*this),
|
||||
@@ -276,7 +352,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
audio_toggle_(audio_queue_),
|
||||
speaker_(audio_toggle_),
|
||||
language_card_(*this),
|
||||
auxiliary_switches_(*this) {
|
||||
auxiliary_switches_(*this),
|
||||
keyboard_(&m6502_) {
|
||||
// The system's master clock rate.
|
||||
constexpr float master_clock = 14318180.0;
|
||||
|
||||
@@ -435,18 +512,18 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
default: break;
|
||||
|
||||
case 0xc000:
|
||||
*value = get_keyboard_input();
|
||||
*value = keyboard_.get_keyboard_input();
|
||||
break;
|
||||
case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005: case 0xc006: case 0xc007:
|
||||
case 0xc008: case 0xc009: case 0xc00a: case 0xc00b: case 0xc00c: case 0xc00d: case 0xc00e: case 0xc00f:
|
||||
*value = (*value & 0x80) | (get_keyboard_input() & 0x7f);
|
||||
*value = (*value & 0x80) | (keyboard_.get_keyboard_input() & 0x7f);
|
||||
break;
|
||||
|
||||
case 0xc061: // Switch input 0.
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
joysticks_.button(0) ||
|
||||
(is_iie() && open_apple_is_pressed_)
|
||||
(is_iie() && keyboard_.open_apple_is_pressed)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@@ -454,7 +531,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
joysticks_.button(1) ||
|
||||
(is_iie() && closed_apple_is_pressed_)
|
||||
(is_iie() && keyboard_.closed_apple_is_pressed)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@@ -476,7 +553,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
} break;
|
||||
|
||||
// The IIe-only state reads follow...
|
||||
#define IIeSwitchRead(s) *value = get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
|
||||
#define IIeSwitchRead(s) *value = keyboard_.get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
|
||||
case 0xc011: IIeSwitchRead(language_card_.state().bank2); break;
|
||||
case 0xc012: IIeSwitchRead(language_card_.state().read); break;
|
||||
case 0xc013: IIeSwitchRead(auxiliary_switches_.switches().read_auxiliary_memory); break;
|
||||
@@ -559,15 +636,15 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
break;
|
||||
|
||||
case 0xc010:
|
||||
keyboard_input_ &= 0x7f;
|
||||
if(string_serialiser_) {
|
||||
if(!string_serialiser_->advance())
|
||||
string_serialiser_.reset();
|
||||
keyboard_.keyboard_input &= 0x7f;
|
||||
if(keyboard_.string_serialiser) {
|
||||
if(!keyboard_.string_serialiser->advance())
|
||||
keyboard_.string_serialiser.reset();
|
||||
}
|
||||
|
||||
// On the IIe, reading C010 returns additional key info.
|
||||
if(is_iie() && isReadOperation(operation)) {
|
||||
*value = (key_is_down_ ? 0x80 : 0x00) | (keyboard_input_ & 0x7f);
|
||||
*value = (keyboard_.key_is_down ? 0x80 : 0x00) | (keyboard_.keyboard_input & 0x7f);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -683,81 +760,16 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
m6502_.run_for(cycles);
|
||||
}
|
||||
|
||||
void reset_all_keys() final {
|
||||
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
||||
}
|
||||
|
||||
bool prefers_logical_input() final {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
||||
// If no ASCII value is supplied, look for a few special cases.
|
||||
switch(key) {
|
||||
case Key::Left: value = 0x08; break;
|
||||
case Key::Right: value = 0x15; break;
|
||||
case Key::Down: value = 0x0a; break;
|
||||
case Key::Up: value = 0x0b; break;
|
||||
case Key::Backspace: value = 0x7f; break;
|
||||
case Key::Enter: value = 0x0d; break;
|
||||
case Key::Tab: value = '\t'; break;
|
||||
case Key::Escape: value = 0x1b; break;
|
||||
|
||||
case Key::LeftOption:
|
||||
case Key::RightMeta:
|
||||
open_apple_is_pressed_ = is_pressed;
|
||||
return true;
|
||||
|
||||
case Key::RightOption:
|
||||
case Key::LeftMeta:
|
||||
closed_apple_is_pressed_ = is_pressed;
|
||||
return true;
|
||||
|
||||
case Key::F1: case Key::F2: case Key::F3: case Key::F4:
|
||||
case Key::F5: case Key::F6: case Key::F7: case Key::F8:
|
||||
case Key::F9: case Key::F10: case Key::F11: case Key::F12:
|
||||
case Key::PrintScreen:
|
||||
case Key::ScrollLock:
|
||||
case Key::Pause:
|
||||
case Key::Insert:
|
||||
case Key::Home:
|
||||
case Key::PageUp:
|
||||
case Key::PageDown:
|
||||
case Key::End:
|
||||
// Accept a bunch non-symbolic other keys, as
|
||||
// reset, in the hope that the user can find
|
||||
// at least one usable key.
|
||||
m6502_.set_reset_line(is_pressed);
|
||||
return true;
|
||||
|
||||
default:
|
||||
if(!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||
if(!is_iie()) value = char(toupper(value));
|
||||
break;
|
||||
}
|
||||
|
||||
if(is_pressed) {
|
||||
keyboard_input_ = uint8_t(value | 0x80);
|
||||
key_is_down_ = true;
|
||||
} else {
|
||||
if((keyboard_input_ & 0x7f) == value) {
|
||||
key_is_down_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Inputs::Keyboard &get_keyboard() final {
|
||||
return *this;
|
||||
return keyboard_;
|
||||
}
|
||||
|
||||
void type_string(const std::string &string) final {
|
||||
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
||||
keyboard_.string_serialiser = std::make_unique<Utility::StringSerialiser>(string, true);
|
||||
}
|
||||
|
||||
bool can_type(char c) const final {
|
||||
|
||||
@@ -252,7 +252,7 @@ void DMAController::set_component_prefers_clocking(ClockingHint::Source *, Clock
|
||||
}
|
||||
|
||||
ClockingHint::Preference DMAController::preferred_clocking() const {
|
||||
return (fdc_.preferred_clocking() == ClockingHint::Preference::None) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime;
|
||||
return (fdc_.preferred_clocking() == ClockingHint::Preference::None) ? ClockingHint::Preference::JustInTime : ClockingHint::Preference::RealTime;
|
||||
}
|
||||
|
||||
void DMAController::set_activity_observer(Activity::Observer *observer) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "MediaTarget.hpp"
|
||||
#include "MouseMachine.hpp"
|
||||
#include "ScanProducer.hpp"
|
||||
#include "StateProducer.hpp"
|
||||
#include "TimedMachine.hpp"
|
||||
|
||||
#endif /* MachineTypes_h */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class Machine {
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
bool automatic_tape_motor_control;
|
||||
bool automatic_tape_motor_control = true;
|
||||
|
||||
Options(Configurable::OptionsType type):
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly),
|
||||
|
||||
53
Machines/Sinclair/ZXSpectrum/State.hpp
Normal file
53
Machines/Sinclair/ZXSpectrum/State.hpp
Normal 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 */
|
||||
@@ -12,13 +12,18 @@
|
||||
#include "../../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Sinclair {
|
||||
namespace ZXSpectrum {
|
||||
namespace Video {
|
||||
|
||||
enum class VideoTiming {
|
||||
Plus3
|
||||
enum class Timing {
|
||||
FortyEightK,
|
||||
OneTwoEightK,
|
||||
Plus3,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -45,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;
|
||||
|
||||
@@ -61,75 +66,56 @@ template <VideoTiming timing> class Video {
|
||||
// Number of cycles after first pixel fetch at which interrupt is first signalled.
|
||||
int interrupt_time;
|
||||
|
||||
// Contention to apply, in half-cycles, as a function of number of half cycles since
|
||||
// Contention to apply, in whole cycles, as a function of number of whole cycles since
|
||||
// contention began.
|
||||
int delays[16];
|
||||
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 :
|
||||
half_cycles_per_line(cycles_per_line * 2),
|
||||
lines_per_frame(lines_per_frame),
|
||||
contention_leadin(contention_leadin * 2),
|
||||
contention_duration(contention_duration * 2),
|
||||
interrupt_time((cycles_per_line * lines_per_frame - interrupt_offset - contention_leadin) * 2),
|
||||
delays{ delays[0] * 2, delays[1] * 2, delays[2] * 2, delays[3] * 2, delays[4] * 2, delays[5] * 2, delays[6] * 2, delays[7] * 2}
|
||||
{}
|
||||
};
|
||||
|
||||
static constexpr Timings get_timings() {
|
||||
// Amstrad gate array timings, classic statement:
|
||||
//
|
||||
// Contention begins 14361 cycles "after interrupt" and follows the pattern [1, 0, 7, 6 5 4, 3, 2].
|
||||
// The first four bytes of video are fetched at 14365–14368 cycles, in the order [pixels, attribute, pixels, attribute].
|
||||
//
|
||||
// For my purposes:
|
||||
//
|
||||
// Video fetching always begins at 0. Since there are 311*228 = 70908 cycles per frame, and the interrupt
|
||||
// should "occur" (I assume: begin) 14365 before that, it should actually begin at 70908 - 14365 = 56543.
|
||||
//
|
||||
// Contention begins four cycles before the first video fetch, so it begins at 70904. I don't currently
|
||||
// know whether the four cycles is true across all models, so it's given here as convention_leadin.
|
||||
//
|
||||
// ... except that empirically that all seems to be two cycles off. So maybe I misunderstand what the
|
||||
// contention patterns are supposed to indicate relative to MREQ? It's frustrating that all documentation
|
||||
// I can find is vaguely in terms of contention patterns, and what they mean isn't well-defined in terms
|
||||
// of regular Z80 signalling.
|
||||
constexpr Timings result = {
|
||||
.cycles_per_line = 228 * 2,
|
||||
.lines_per_frame = 311,
|
||||
if constexpr (timing == Timing::Plus3) {
|
||||
constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2};
|
||||
return Timings(228, 311, 6, 129, 14361, delays);
|
||||
}
|
||||
|
||||
// i.e. video fetching begins five cycles after the start of the
|
||||
// contended memory pattern below; that should put a clear two
|
||||
// cycles between a Z80 access and the first video fetch.
|
||||
.contention_leadin = 5 * 2,
|
||||
.contention_duration = 129 * 2,
|
||||
if constexpr (timing == Timing::OneTwoEightK) {
|
||||
constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0};
|
||||
return Timings(228, 311, 4, 128, 14361, delays);
|
||||
}
|
||||
|
||||
// i.e. interrupt is first signalled 14368 cycles before the first video fetch.
|
||||
.interrupt_time = (228*311 - 14368) * 2,
|
||||
|
||||
.delays = {
|
||||
2, 1,
|
||||
0, 0,
|
||||
14, 13,
|
||||
12, 11,
|
||||
10, 9,
|
||||
8, 7,
|
||||
6, 5,
|
||||
4, 3,
|
||||
}
|
||||
};
|
||||
return result;
|
||||
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 = 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) {
|
||||
@@ -236,9 +222,14 @@ 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;
|
||||
crt_.output_colour_burst(burst_duration, 116, is_alternate_line_);
|
||||
|
||||
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 {
|
||||
crt_.output_default_colour_burst(burst_duration);
|
||||
}
|
||||
offset += burst_duration;
|
||||
// The colour burst phase above is an empirical guess. I need to research further.
|
||||
}
|
||||
|
||||
if(offset >= burst_position+burst_length && end_offset > offset) {
|
||||
@@ -248,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,14 +250,64 @@ template <VideoTiming timing> class Video {
|
||||
crt_.output_level(duration);
|
||||
}
|
||||
|
||||
static constexpr int half_cycles_per_line() {
|
||||
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
|
||||
// crystals, so I've nudged this experimentally.
|
||||
return 224*2 - 1;
|
||||
} else {
|
||||
return 227*2;
|
||||
}
|
||||
}
|
||||
|
||||
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_(227 * 2, 2, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
crt_(half_cycles_per_line(), 2, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
{
|
||||
// Show only the centre 80% of the TV frame.
|
||||
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_;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -305,21 +346,22 @@ template <VideoTiming timing> class Video {
|
||||
@returns How many cycles the [ULA/gate array] would delay the CPU for if it were to recognise that contention
|
||||
needs to be applied in @c offset half-cycles from now.
|
||||
*/
|
||||
int access_delay(HalfCycles offset) const {
|
||||
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;
|
||||
}
|
||||
|
||||
return timings.delays[time_into_line & 15];
|
||||
return HalfCycles(timings.delays[(time_into_line >> 1) & 7]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -327,20 +369,24 @@ template <VideoTiming timing> class Video {
|
||||
*/
|
||||
uint8_t get_floating_value() const {
|
||||
constexpr auto timings = get_timings();
|
||||
const int line = time_into_frame_ / timings.cycles_per_line;
|
||||
if(line >= 192) return 0xff;
|
||||
const uint8_t out_of_bounds = (timing == Timing::Plus3) ? last_contended_access_ : 0xff;
|
||||
|
||||
const int time_into_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.half_cycles_per_line;
|
||||
if(time_into_line >= 256 || (time_into_line&8)) {
|
||||
return last_contended_access_;
|
||||
return out_of_bounds;
|
||||
}
|
||||
|
||||
// The +2a and +3 always return the low bit as set.
|
||||
if constexpr (timing == VideoTiming::Plus3) {
|
||||
return last_fetches_[(time_into_line >> 1) & 3] | 1;
|
||||
const uint8_t value = last_fetches_[(time_into_line >> 1) & 3];
|
||||
if constexpr (timing == Timing::Plus3) {
|
||||
return value | 1;
|
||||
}
|
||||
|
||||
return last_fetches_[(time_into_line >> 1) & 3];
|
||||
return value;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -349,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;
|
||||
}
|
||||
}
|
||||
@@ -358,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];
|
||||
}
|
||||
|
||||
@@ -376,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;
|
||||
@@ -393,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),
|
||||
@@ -403,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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -81,14 +148,39 @@ template<Model model> class ConcreteMachine:
|
||||
|
||||
// With only the +2a and +3 currently supported, the +3 ROM is always
|
||||
// the one required.
|
||||
const auto roms =
|
||||
rom_fetcher({ {"ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, 0x96e3c17a} });
|
||||
std::vector<ROMMachine::ROM> rom_names;
|
||||
const std::string machine = "ZXSpectrum";
|
||||
switch(model) {
|
||||
case Model::SixteenK:
|
||||
case Model::FortyEightK:
|
||||
rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531f);
|
||||
break;
|
||||
|
||||
case Model::OneTwoEightK:
|
||||
rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995);
|
||||
break;
|
||||
|
||||
case Model::Plus2:
|
||||
rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dc);
|
||||
break;
|
||||
|
||||
case Model::Plus2a:
|
||||
case Model::Plus3: {
|
||||
const std::initializer_list<uint32_t> crc32s = { 0x96e3c17a, 0xbe0d9ec4 };
|
||||
rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s);
|
||||
} break;
|
||||
}
|
||||
const auto roms = rom_fetcher(rom_names);
|
||||
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
|
||||
memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size()));
|
||||
|
||||
// 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();
|
||||
@@ -103,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() {
|
||||
@@ -110,7 +225,7 @@ template<Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
static constexpr unsigned int clock_rate() {
|
||||
// constexpr unsigned int ClockRate = 3'500'000;
|
||||
constexpr unsigned int OriginalClockRate = 3'500'000;
|
||||
constexpr unsigned int Plus3ClockRate = 3'546'875; // See notes below; this is a guess.
|
||||
|
||||
// Notes on timing for the +2a and +3:
|
||||
@@ -137,7 +252,7 @@ template<Model model> class ConcreteMachine:
|
||||
// the Spectrum is a PAL machine with a fixed colour phase relationship. For
|
||||
// this emulator's world, that's a first!
|
||||
|
||||
return Plus3ClockRate;
|
||||
return model < Model::OneTwoEightK ? OriginalClockRate : Plus3ClockRate;
|
||||
}
|
||||
|
||||
// MARK: - TimedMachine.
|
||||
@@ -181,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) {
|
||||
@@ -189,18 +308,93 @@ template<Model model> class ConcreteMachine:
|
||||
const uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||
|
||||
// Apply contention if necessary.
|
||||
if(
|
||||
is_contended_[address >> 14] &&
|
||||
cycle.operation >= PartialMachineCycle::ReadOpcodeStart &&
|
||||
cycle.operation <= PartialMachineCycle::WriteStart) {
|
||||
// Assumption here: the trigger for the ULA inserting a delay is the falling edge
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
// Model applied: the trigger for the ULA inserting a delay is the falling edge
|
||||
// of MREQ, which is always half a cycle into a read or write.
|
||||
//
|
||||
// TODO: somehow provide that information in the PartialMachineCycle?
|
||||
if(
|
||||
is_contended_[address >> 14] &&
|
||||
cycle.operation >= PartialMachineCycle::ReadOpcodeStart &&
|
||||
cycle.operation <= PartialMachineCycle::WriteStart) {
|
||||
|
||||
const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1));
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
const auto delay = video_.last_valid()->access_delay(video_.time_since_flush());
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
}
|
||||
} else {
|
||||
switch(cycle.operation) {
|
||||
case CPU::Z80::PartialMachineCycle::Input:
|
||||
case CPU::Z80::PartialMachineCycle::Output:
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
case CPU::Z80::PartialMachineCycle::Interrupt:
|
||||
// For these, carry on into the actual handler, below.
|
||||
break;
|
||||
|
||||
// For anything else that isn't listed below, just advance
|
||||
// time and conclude here.
|
||||
default:
|
||||
advance(cycle.length);
|
||||
return HalfCycles(0);
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::InputStart:
|
||||
case CPU::Z80::PartialMachineCycle::OutputStart: {
|
||||
// The port address is loaded prior to IOREQ being visible; a contention
|
||||
// always occurs if it is in the $4000–$8000 range regardless of current
|
||||
// memory mapping.
|
||||
HalfCycles delay;
|
||||
HalfCycles time = video_.time_since_flush();
|
||||
|
||||
if((address & 0xc000) == 0x4000) {
|
||||
for(int c = 0; c < ((address & 1) ? 4 : 2); c++) {
|
||||
const auto next_delay = video_.last_valid()->access_delay(time);
|
||||
delay += next_delay;
|
||||
time += next_delay + 2;
|
||||
}
|
||||
} else {
|
||||
if(!(address & 1)) {
|
||||
delay = video_.last_valid()->access_delay(time + HalfCycles(2));
|
||||
}
|
||||
}
|
||||
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
} break;
|
||||
|
||||
case PartialMachineCycle::ReadOpcodeStart:
|
||||
case PartialMachineCycle::ReadStart:
|
||||
case PartialMachineCycle::WriteStart: {
|
||||
// These all start by loading the address bus, then set MREQ
|
||||
// half a cycle later.
|
||||
if(is_contended_[address >> 14]) {
|
||||
const auto delay = video_.last_valid()->access_delay(video_.time_since_flush());
|
||||
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
}
|
||||
} break;
|
||||
|
||||
case PartialMachineCycle::Internal: {
|
||||
// Whatever's on the address bus will remain there, without IOREQ or
|
||||
// MREQ interceding, for this entire bus cycle. So apply contentions
|
||||
// all the way along.
|
||||
if(is_contended_[address >> 14]) {
|
||||
const auto half_cycles = cycle.length.as<int>();
|
||||
assert(!(half_cycles & 1));
|
||||
|
||||
HalfCycles time = video_.time_since_flush();
|
||||
HalfCycles delay;
|
||||
for(int c = 0; c < half_cycles; c += 2) {
|
||||
const auto next_delay = video_.last_valid()->access_delay(time);
|
||||
delay += next_delay;
|
||||
time += next_delay + 2;
|
||||
}
|
||||
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// For all other machine cycles, model the action as happening at the end of the machine cycle;
|
||||
@@ -214,7 +408,7 @@ template<Model model> class ConcreteMachine:
|
||||
// Fast loading: ROM version.
|
||||
//
|
||||
// The below patches over part of the 'LD-BYTES' routine from the 48kb ROM.
|
||||
if(use_fast_tape_hack_ && address == 0x056b && read_pointers_[0] == &rom_[0xc000]) {
|
||||
if(use_fast_tape_hack_ && address == 0x056b && read_pointers_[0] == &rom_[classic_rom_offset()]) {
|
||||
// Stop pressing enter, if neccessry.
|
||||
if(duration_to_press_enter_ > Cycles(0)) {
|
||||
duration_to_press_enter_ = Cycles(0);
|
||||
@@ -226,12 +420,24 @@ template<Model model> class ConcreteMachine:
|
||||
break;
|
||||
}
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
case PartialMachineCycle::Read:
|
||||
if constexpr (model == Model::SixteenK) {
|
||||
// Assumption: with nothing mapped above 0x8000 on the 16kb Spectrum,
|
||||
// read the floating bus.
|
||||
if(address >= 0x8000) {
|
||||
*cycle.value = video_->get_floating_value();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*cycle.value = read_pointers_[address >> 14][address];
|
||||
|
||||
if(is_contended_[address >> 14]) {
|
||||
video_->set_last_contended_area_access(*cycle.value);
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
if(is_contended_[address >> 14]) {
|
||||
video_->set_last_contended_area_access(*cycle.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -243,12 +449,17 @@ template<Model model> class ConcreteMachine:
|
||||
|
||||
write_pointers_[address >> 14][address] = *cycle.value;
|
||||
|
||||
// Fill the floating bus buffer if this write is within the contended area.
|
||||
if(is_contended_[address >> 14]) {
|
||||
video_->set_last_contended_area_access(*cycle.value);
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
// Fill the floating bus buffer if this write is within the contended area.
|
||||
if(is_contended_[address >> 14]) {
|
||||
video_->set_last_contended_area_access(*cycle.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Partial port decodings here and in ::Input are as documented
|
||||
// at https://worldofspectrum.org/faq/reference/ports.htm
|
||||
|
||||
case PartialMachineCycle::Output:
|
||||
// Test for port FE.
|
||||
if(!(address&1)) {
|
||||
@@ -263,55 +474,70 @@ template<Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Test for classic 128kb paging register (i.e. port 7ffd).
|
||||
if((address & 0xc002) == 0x4000) {
|
||||
if (
|
||||
(model >= Model::OneTwoEightK && model <= Model::Plus2 && (address & 0x8002) == 0x0000) ||
|
||||
(model >= Model::Plus2a && (address & 0xc002) == 0x4000)
|
||||
) {
|
||||
port7ffd_ = *cycle.value;
|
||||
update_memory_map();
|
||||
|
||||
// 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).
|
||||
if((address & 0xf002) == 0x1000) {
|
||||
port1ffd_ = *cycle.value;
|
||||
update_memory_map();
|
||||
update_video_base();
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
if((address & 0xf002) == 0x1000) {
|
||||
port1ffd_ = *cycle.value;
|
||||
update_memory_map();
|
||||
update_video_base();
|
||||
|
||||
if constexpr (model == Model::Plus3) {
|
||||
fdc_->set_motor_on(*cycle.value & 0x08);
|
||||
if constexpr (model == Model::Plus3) {
|
||||
fdc_->set_motor_on(*cycle.value & 0x08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((address & 0xc002) == 0xc000) {
|
||||
// Select AY register.
|
||||
update_audio();
|
||||
GI::AY38910::Utility::select_register(ay_, *cycle.value);
|
||||
}
|
||||
|
||||
if((address & 0xc002) == 0x8000) {
|
||||
// Write to AY register.
|
||||
update_audio();
|
||||
GI::AY38910::Utility::write_data(ay_, *cycle.value);
|
||||
// Route to the AY if one is fitted.
|
||||
if constexpr (model >= Model::OneTwoEightK) {
|
||||
switch(address & 0xc002) {
|
||||
case 0xc000:
|
||||
// Select AY register.
|
||||
update_audio();
|
||||
GI::AY38910::Utility::select_register(ay_, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0x8000:
|
||||
// Write to AY register.
|
||||
update_audio();
|
||||
GI::AY38910::Utility::write_data(ay_, *cycle.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for FDC accesses.
|
||||
if constexpr (model == Model::Plus3) {
|
||||
switch(address) {
|
||||
switch(address & 0xf002) {
|
||||
default: break;
|
||||
case 0x3ffd: case 0x2ffd:
|
||||
case 0x3000: case 0x2000:
|
||||
fdc_->write((address >> 12) & 1, *cycle.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Input:
|
||||
case PartialMachineCycle::Input: {
|
||||
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;
|
||||
|
||||
// Port FE:
|
||||
//
|
||||
// address b8+: mask of keyboard lines to select
|
||||
@@ -321,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.
|
||||
@@ -339,27 +569,46 @@ template<Model model> class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
if((address & 0xc002) == 0xc000) {
|
||||
// Read from AY register.
|
||||
update_audio();
|
||||
*cycle.value &= GI::AY38910::Utility::read(ay_);
|
||||
if constexpr (model >= Model::OneTwoEightK) {
|
||||
if((address & 0xc002) == 0xc000) {
|
||||
did_match = true;
|
||||
|
||||
// Read from AY register.
|
||||
update_audio();
|
||||
*cycle.value &= GI::AY38910::Utility::read(ay_);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a floating bus read; these are particularly arcane
|
||||
// on the +2a/+3. See footnote to https://spectrumforeveryone.com/technical/memory-contention-floating-bus/
|
||||
// and, much more rigorously, http://sky.relative-path.com/zx/floating_bus.html
|
||||
if(!disable_paging_ && (address & 0xf003) == 0x0001) {
|
||||
*cycle.value &= video_->get_floating_value();
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
// Check for a +2a/+3 floating bus read; these are particularly arcane.
|
||||
// See footnote to https://spectrumforeveryone.com/technical/memory-contention-floating-bus/
|
||||
// and, much more rigorously, http://sky.relative-path.com/zx/floating_bus.html
|
||||
if(!disable_paging_ && (address & 0xf003) == 0x0001) {
|
||||
*cycle.value &= video_->get_floating_value();
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (model == Model::Plus3) {
|
||||
switch(address) {
|
||||
switch(address & 0xf002) {
|
||||
default: break;
|
||||
case 0x3ffd: case 0x2ffd:
|
||||
case 0x3000: case 0x2000:
|
||||
*cycle.value &= fdc_->read((address >> 12) & 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (model <= Model::Plus2) {
|
||||
if(!did_match) {
|
||||
*cycle.value = video_->get_floating_value();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case PartialMachineCycle::Interrupt:
|
||||
// At least one piece of Spectrum software, Escape from M.O.N.J.A.S. explicitly
|
||||
// assumes that a 0xff value will be on the bus during an interrupt acknowledgment.
|
||||
// I wasn't otherwise aware that this value is reliable.
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -480,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;
|
||||
}
|
||||
|
||||
@@ -568,13 +818,21 @@ 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) {
|
||||
is_contended_[bank] = (source >= 4 && source < 8);
|
||||
if constexpr (model >= Model::Plus2a) {
|
||||
is_contended_[bank] = source >= 4 && source < 8;
|
||||
} else {
|
||||
is_contended_[bank] = source < 0x80 && source & 1;
|
||||
}
|
||||
pages_[bank] = source;
|
||||
|
||||
uint8_t *read = (source < 0x80) ? &ram_[source * 16384] : &rom_[(source & 0x7f) * 16384];
|
||||
uint8_t *const read = (source < 0x80) ? &ram_[source * 16384] : &rom_[(source & 0x7f) * 16384];
|
||||
const auto offset = bank*16384;
|
||||
|
||||
read_pointers_[bank] = read - offset;
|
||||
@@ -607,8 +865,15 @@ template<Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Video.
|
||||
static constexpr VideoTiming video_timing = VideoTiming::Plus3;
|
||||
JustInTimeActor<Video<video_timing>> video_;
|
||||
using VideoType =
|
||||
std::conditional_t<
|
||||
model <= Model::FortyEightK, Video::Video<Video::Timing::FortyEightK>,
|
||||
std::conditional_t<
|
||||
model <= Model::Plus2, Video::Video<Video::Timing::OneTwoEightK>,
|
||||
Video::Video<Video::Timing::Plus3>
|
||||
>
|
||||
>;
|
||||
JustInTimeActor<VideoType> video_;
|
||||
|
||||
// MARK: - Keyboard.
|
||||
Sinclair::ZX::Keyboard::Keyboard keyboard_;
|
||||
@@ -695,11 +960,33 @@ template<Model model> class ConcreteMachine:
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr int classic_rom_offset() {
|
||||
switch(model) {
|
||||
case Model::SixteenK:
|
||||
case Model::FortyEightK:
|
||||
return 0x0000;
|
||||
|
||||
case Model::OneTwoEightK:
|
||||
case Model::Plus2:
|
||||
return 0x4000;
|
||||
|
||||
case Model::Plus2a:
|
||||
case Model::Plus3:
|
||||
return 0xc000;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Disc.
|
||||
JustInTimeActor<Amstrad::FDC, Cycles> fdc_;
|
||||
|
||||
// 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_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -712,8 +999,12 @@ Machine *Machine::ZXSpectrum(const Analyser::Static::Target *target, const ROMMa
|
||||
const auto zx_target = dynamic_cast<const Analyser::Static::ZXSpectrum::Target *>(target);
|
||||
|
||||
switch(zx_target->model) {
|
||||
case Model::Plus2a: return new ConcreteMachine<Model::Plus2a>(*zx_target, rom_fetcher);
|
||||
case Model::Plus3: return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher);
|
||||
case Model::SixteenK: return new ConcreteMachine<Model::SixteenK>(*zx_target, rom_fetcher);
|
||||
case Model::FortyEightK: return new ConcreteMachine<Model::FortyEightK>(*zx_target, rom_fetcher);
|
||||
case Model::OneTwoEightK: return new ConcreteMachine<Model::OneTwoEightK>(*zx_target, rom_fetcher);
|
||||
case Model::Plus2: return new ConcreteMachine<Model::Plus2>(*zx_target, rom_fetcher);
|
||||
case Model::Plus2a: return new ConcreteMachine<Model::Plus2a>(*zx_target, rom_fetcher);
|
||||
case Model::Plus3: return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -31,7 +31,7 @@ class Machine {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
bool automatic_tape_motor_control;
|
||||
bool automatic_tape_motor_control = true;
|
||||
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour),
|
||||
|
||||
26
Machines/StateProducer.hpp
Normal file
26
Machines/StateProducer.hpp
Normal 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 */
|
||||
@@ -182,6 +182,7 @@ std::vector<std::string> Machine::AllMachines(Type type, bool long_names) {
|
||||
AddName(Oric);
|
||||
AddName(Vic20);
|
||||
AddName(ZX8081);
|
||||
AddName(ZXSpectrum);
|
||||
}
|
||||
|
||||
#undef AddName
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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 */; };
|
||||
@@ -917,6 +924,7 @@
|
||||
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; };
|
||||
4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */; };
|
||||
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
|
||||
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */; };
|
||||
4BDACBEC22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
||||
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
|
||||
@@ -1009,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>"; };
|
||||
@@ -1057,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>"; };
|
||||
@@ -1075,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>"; };
|
||||
@@ -1090,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>"; };
|
||||
@@ -1136,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>"; };
|
||||
@@ -1158,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>"; };
|
||||
@@ -1170,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>"; };
|
||||
@@ -1232,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>"; };
|
||||
@@ -1254,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>"; };
|
||||
@@ -1275,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>"; };
|
||||
@@ -1314,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>"; };
|
||||
@@ -1329,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>"; };
|
||||
@@ -1343,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>"; };
|
||||
@@ -1378,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>"; };
|
||||
@@ -1426,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>"; };
|
||||
@@ -1519,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>"; };
|
||||
@@ -1837,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>"; };
|
||||
@@ -1902,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>"; };
|
||||
@@ -1917,23 +1934,24 @@
|
||||
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>"; };
|
||||
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSROMReceiverView.m; sourceTree = "<group>"; };
|
||||
4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+CRC32.m"; sourceTree = "<group>"; };
|
||||
4BDA00E322E663B900AC3CD0 /* NSData+CRC32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+CRC32.h"; sourceTree = "<group>"; };
|
||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Z80ContentionTests.mm; sourceTree = "<group>"; };
|
||||
4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ncr5380.cpp; sourceTree = "<group>"; };
|
||||
4BDACBEB22FFA5D20045EF7E /* ncr5380.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ncr5380.hpp; sourceTree = "<group>"; };
|
||||
4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||
@@ -1954,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>"; };
|
||||
@@ -2003,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>"; };
|
||||
@@ -2155,8 +2173,7 @@
|
||||
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */,
|
||||
4BBF99071C8FBA6F0075DAFB /* Internals */,
|
||||
);
|
||||
name = CRT;
|
||||
path = ../../Outputs/CRT;
|
||||
path = CRT;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B0E04F71FC9F2C800F43484 /* 9918 */ = {
|
||||
@@ -2166,7 +2183,7 @@
|
||||
4B0E04F81FC9FA3000F43484 /* 9918.hpp */,
|
||||
4BD388431FE34E060042B588 /* Implementation */,
|
||||
);
|
||||
name = 9918;
|
||||
path = 9918;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B0F1BAF2602645900B85C66 /* ZXSpectrum */ = {
|
||||
@@ -2206,6 +2223,7 @@
|
||||
4B0F1BFA260300D900B85C66 /* ZXSpectrum.cpp */,
|
||||
4B0F1BFB260300D900B85C66 /* ZXSpectrum.hpp */,
|
||||
4B0F1C092603BA5F00B85C66 /* Video.hpp */,
|
||||
4B8DD3912635A72F00B3C866 /* State.hpp */,
|
||||
);
|
||||
path = ZXSpectrum;
|
||||
sourceTree = "<group>";
|
||||
@@ -2274,7 +2292,7 @@
|
||||
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */,
|
||||
4B1667F61FFF1E2400A16032 /* Konami.hpp */,
|
||||
);
|
||||
name = Cartridges;
|
||||
path = Cartridges;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1B58F3246CC4E8009C171E /* State */ = {
|
||||
@@ -2283,8 +2301,7 @@
|
||||
4B1B58F4246CC4E8009C171E /* State.cpp */,
|
||||
4B1B58F5246CC4E8009C171E /* State.hpp */,
|
||||
);
|
||||
name = State;
|
||||
path = Z80/State;
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1B58FC246E19FD009C171E /* State */ = {
|
||||
@@ -2431,7 +2448,7 @@
|
||||
4BEA525F1DF333D8007E74F2 /* Tape.hpp */,
|
||||
4B7913CB1DFCD80E00175A82 /* Video.hpp */,
|
||||
);
|
||||
name = Electron;
|
||||
path = Electron;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B302181208A550100773308 /* DiskII */ = {
|
||||
@@ -2465,8 +2482,7 @@
|
||||
4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */,
|
||||
4B322DFE1F5A2981004EB04C /* Z80AllRAM.hpp */,
|
||||
);
|
||||
name = AllRAM;
|
||||
path = Z80/AllRAM;
|
||||
path = AllRAM;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B322DFF1F5A2981004EB04C /* Implementation */ = {
|
||||
@@ -2478,8 +2494,7 @@
|
||||
4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */,
|
||||
4B322E021F5A29D5004EB04C /* Z80Storage.hpp */,
|
||||
);
|
||||
name = Implementation;
|
||||
path = Z80/Implementation;
|
||||
path = Implementation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B366DFD1B5C165F0026627B /* Outputs */ = {
|
||||
@@ -2496,6 +2511,7 @@
|
||||
4BD060A41FE49D3C006E14BE /* Speaker */,
|
||||
);
|
||||
name = Outputs;
|
||||
path = ../../Outputs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B38F3491F2EC12000D9235D /* AmstradCPC */ = {
|
||||
@@ -2507,7 +2523,7 @@
|
||||
4B54C0C01F8D91CD0050900F /* Keyboard.hpp */,
|
||||
4B0F1C3D26095AC600B85C66 /* FDC.hpp */,
|
||||
);
|
||||
name = AmstradCPC;
|
||||
path = AmstradCPC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B3940E81DA83C8700427841 /* Concurrency */ = {
|
||||
@@ -2517,6 +2533,7 @@
|
||||
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */,
|
||||
);
|
||||
name = Concurrency;
|
||||
path = ../../Concurrency;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B3AF7CF2413470E00873C0B /* Reflection */ = {
|
||||
@@ -2667,7 +2684,7 @@
|
||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */,
|
||||
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */,
|
||||
);
|
||||
name = AY38910;
|
||||
path = AY38910;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B4B1A39200198C900A0F866 /* KonamiSCC */ = {
|
||||
@@ -2838,6 +2855,7 @@
|
||||
4B8805F81DCFF6CD003085B1 /* Data */,
|
||||
4BAB62AA1D3272D200DF5BA0 /* Disk */,
|
||||
4B6AAEA1230E3E1D0078E864 /* MassStorage */,
|
||||
4B8DD3832634D37E00B3C866 /* State */,
|
||||
4B69FB3A1C4D908A00B5F0AA /* Tape */,
|
||||
);
|
||||
name = Storage;
|
||||
@@ -2967,7 +2985,7 @@
|
||||
4B322DFF1F5A2981004EB04C /* Implementation */,
|
||||
4B1B58F3246CC4E8009C171E /* State */,
|
||||
);
|
||||
name = Z80;
|
||||
path = Z80;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = {
|
||||
@@ -2982,7 +3000,7 @@
|
||||
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */,
|
||||
4B1667F81FFF1E2900A16032 /* Cartridges */,
|
||||
);
|
||||
name = MSX;
|
||||
path = MSX;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B7A90E22041097C008514A2 /* ColecoVision */ = {
|
||||
@@ -3103,7 +3121,7 @@
|
||||
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */,
|
||||
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */,
|
||||
);
|
||||
name = Data;
|
||||
path = Data;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B8944E2201967B4007DE474 /* Analyser */ = {
|
||||
@@ -3244,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 = (
|
||||
@@ -3937,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 */,
|
||||
@@ -3947,45 +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 */,
|
||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.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 */,
|
||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||
4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */,
|
||||
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */,
|
||||
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */,
|
||||
4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */,
|
||||
4B3BA0C41D318B44005DD7A7 /* Bridges */,
|
||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||
);
|
||||
path = "Clock SignalTests";
|
||||
sourceTree = "<group>";
|
||||
@@ -4012,6 +4045,7 @@
|
||||
4B92294222B04A3D00A1458F /* MouseMachine.hpp */,
|
||||
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */,
|
||||
4B046DC31CFE651500E9E45E /* ScanProducer.hpp */,
|
||||
4B8DD375263481BB00B3C866 /* StateProducer.hpp */,
|
||||
4BC57CD32434282000FBC404 /* TimedMachine.hpp */,
|
||||
4B38F3491F2EC12000D9235D /* AmstradCPC */,
|
||||
4BCE0048227CE8CA000CA200 /* Apple */,
|
||||
@@ -4051,8 +4085,7 @@
|
||||
4BB8616C24E22DC500A00E03 /* BufferingScanTarget.hpp */,
|
||||
4BB8616D24E22DC500A00E03 /* BufferingScanTarget.cpp */,
|
||||
);
|
||||
name = ScanTargets;
|
||||
path = ../../Outputs/ScanTargets;
|
||||
path = ScanTargets;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BBB70A1202011C2002FE009 /* Implementation */ = {
|
||||
@@ -4080,7 +4113,7 @@
|
||||
4BBC951C1F368D83008F4C34 /* i8272.cpp */,
|
||||
4BBC951D1F368D83008F4C34 /* i8272.hpp */,
|
||||
);
|
||||
name = 8272;
|
||||
path = 8272;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BBF49B41ED2881600AB3669 /* FUSE */ = {
|
||||
@@ -4297,7 +4330,7 @@
|
||||
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */,
|
||||
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */,
|
||||
);
|
||||
name = Oric;
|
||||
path = Oric;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD060A41FE49D3C006E14BE /* Speaker */ = {
|
||||
@@ -4306,8 +4339,7 @@
|
||||
4BD060A51FE49D3C006E14BE /* Speaker.hpp */,
|
||||
4B8EF6051FE5AF830076CCDD /* Implementation */,
|
||||
);
|
||||
name = Speaker;
|
||||
path = ../../Outputs/Speaker;
|
||||
path = Speaker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD191D5219113B80042E144 /* OpenGL */ = {
|
||||
@@ -4320,8 +4352,7 @@
|
||||
4B961408222760E0001A7BF2 /* Screenshot.hpp */,
|
||||
4BD424DC2193B5340097291A /* Primitives */,
|
||||
);
|
||||
name = OpenGL;
|
||||
path = ../../Outputs/OpenGL;
|
||||
path = OpenGL;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD388431FE34E060042B588 /* Implementation */ = {
|
||||
@@ -4329,7 +4360,7 @@
|
||||
children = (
|
||||
4BD388411FE34E010042B588 /* 9918Base.hpp */,
|
||||
);
|
||||
name = Implementation;
|
||||
path = Implementation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD424DC2193B5340097291A /* Primitives */ = {
|
||||
@@ -4351,7 +4382,7 @@
|
||||
4BD468F51D8DF41D0084958B /* 1770.cpp */,
|
||||
4BD468F61D8DF41D0084958B /* 1770.hpp */,
|
||||
);
|
||||
name = 1770;
|
||||
path = 1770;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD67DC8209BE4D600AB2146 /* DiskII */ = {
|
||||
@@ -4381,7 +4412,7 @@
|
||||
children = (
|
||||
4BD9137D1F311BC5009BCF85 /* i8255.hpp */,
|
||||
);
|
||||
name = 8255;
|
||||
path = 8255;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BDA00DB22E60EE900AC3CD0 /* ROMRequester */ = {
|
||||
@@ -4443,7 +4474,7 @@
|
||||
children = (
|
||||
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */,
|
||||
);
|
||||
name = 6845;
|
||||
path = 6845;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BE9A6B21EDE294200CBCB47 /* Zexall */ = {
|
||||
@@ -4653,7 +4684,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 1200;
|
||||
LastUpgradeCheck = 1240;
|
||||
ORGANIZATIONNAME = "Thomas Harte";
|
||||
TargetAttributes = {
|
||||
4B055A691FAE763F0060FFFF = {
|
||||
@@ -4682,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 = (
|
||||
@@ -5207,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 */,
|
||||
@@ -5243,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 */,
|
||||
@@ -5339,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 */,
|
||||
@@ -5480,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 */,
|
||||
@@ -5497,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 */,
|
||||
@@ -5640,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 */,
|
||||
@@ -5649,6 +5687,7 @@
|
||||
4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */,
|
||||
4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */,
|
||||
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
|
||||
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */,
|
||||
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
||||
4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */,
|
||||
4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||
@@ -5815,7 +5854,6 @@
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
@@ -5836,7 +5874,6 @@
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
@@ -5860,8 +5897,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
@@ -5904,13 +5940,14 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -5919,8 +5956,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
@@ -5943,7 +5979,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
@@ -5957,12 +5993,13 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
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;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -5970,14 +6007,16 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -5990,13 +6029,17 @@
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
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)",
|
||||
@@ -6007,7 +6050,6 @@
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -6015,14 +6057,16 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -6037,13 +6081,17 @@
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
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)",
|
||||
@@ -6053,7 +6101,6 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -6069,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)";
|
||||
@@ -6094,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";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1240"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1240"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1240"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -11,12 +11,24 @@ import Cocoa
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
private var failedMetalCheck = false
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
// Insert code here to initialize your application.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application.
|
||||
// Check for at least one Metal-capable GPU; this check
|
||||
// will become unnecessary if/when the minimum OS version
|
||||
// that this project supports reascends to 10.14.
|
||||
if (MTLCopyAllDevices().count == 0) {
|
||||
self.failedMetalCheck = true
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "This application requires a Metal-capable GPU"
|
||||
alert.addButton(withTitle: "Exit")
|
||||
alert.runModal()
|
||||
|
||||
let application = notification.object as! NSApplication
|
||||
application.terminate(self)
|
||||
}
|
||||
}
|
||||
|
||||
private var hasShownOpenDocument = false
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -63,6 +63,10 @@ typedef NS_ENUM(NSInteger, CSMachineOricDiskInterface) {
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineSpectrumModel) {
|
||||
CSMachineSpectrumModelSixteenK,
|
||||
CSMachineSpectrumModelFortyEightK,
|
||||
CSMachineSpectrumModelOneTwoEightK,
|
||||
CSMachineSpectrumModelPlus2,
|
||||
CSMachineSpectrumModelPlus2a,
|
||||
CSMachineSpectrumModelPlus3,
|
||||
};
|
||||
|
||||
@@ -194,8 +194,12 @@
|
||||
using Target = Analyser::Static::ZXSpectrum::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
switch(model) {
|
||||
case CSMachineSpectrumModelPlus2a: target->model = Target::Model::Plus2a; break;
|
||||
case CSMachineSpectrumModelPlus3: target->model = Target::Model::Plus3; break;
|
||||
case CSMachineSpectrumModelSixteenK: target->model = Target::Model::SixteenK; break;
|
||||
case CSMachineSpectrumModelFortyEightK: target->model = Target::Model::FortyEightK; break;
|
||||
case CSMachineSpectrumModelOneTwoEightK: target->model = Target::Model::OneTwoEightK; break;
|
||||
case CSMachineSpectrumModelPlus2: target->model = Target::Model::Plus2; break;
|
||||
case CSMachineSpectrumModelPlus2a: target->model = Target::Model::Plus2a; break;
|
||||
case CSMachineSpectrumModelPlus3: target->model = Target::Model::Plus3; break;
|
||||
}
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="590" height="316"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="316"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@@ -584,11 +584,11 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX81" identifier="zx81" id="Wnn-nQ-gZ6">
|
||||
<view key="view" id="bmd-gL-gzT">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="186"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5aO-UX-HnX">
|
||||
<rect key="frame" x="108" y="47" width="116" height="25"/>
|
||||
<rect key="frame" x="108" y="157" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="7QC-Ij-hES" id="d3W-Gl-3Mf">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -602,7 +602,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
|
||||
<rect key="frame" x="18" y="53" width="87" height="16"/>
|
||||
<rect key="frame" x="18" y="163" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -622,24 +622,28 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX Spectrum" identifier="spectrum" id="HQv-oF-k8b">
|
||||
<view key="view" id="bMx-F6-JUb">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="186"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFZ-d4-WFv">
|
||||
<rect key="frame" x="67" y="47" width="62" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="+2a" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="Fo7-NL-Kv5" id="tYs-sA-oek">
|
||||
<rect key="frame" x="67" y="157" width="76" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="16kb" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="Fo7-NL-Kv5" id="tYs-sA-oek">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="8lt-dk-zPr">
|
||||
<items>
|
||||
<menuItem title="+2a" state="on" tag="21" id="Fo7-NL-Kv5"/>
|
||||
<menuItem title="16kb" tag="16" id="Fo7-NL-Kv5"/>
|
||||
<menuItem title="48kb" tag="48" id="xks-Rv-Umd"/>
|
||||
<menuItem title="128kb" tag="128" id="w8h-lY-JLX"/>
|
||||
<menuItem title="+2" tag="2" id="Vvu-ua-pjg"/>
|
||||
<menuItem title="+2a" state="on" tag="21" id="bFk-nC-Txe"/>
|
||||
<menuItem title="+3" tag="3" id="jwx-fZ-vXp"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy">
|
||||
<rect key="frame" x="18" y="53" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="163" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
|
||||
@@ -298,6 +298,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
case "spectrum":
|
||||
var model: CSMachineSpectrumModel = .plus2a
|
||||
switch spectrumModelTypeButton.selectedItem!.tag {
|
||||
case 16: model = .sixteenK
|
||||
case 48: model = .fortyEightK
|
||||
case 128: model = .oneTwoEightK
|
||||
case 2: model = .plus2
|
||||
case 21: model = .plus2a
|
||||
case 3: model = .plus3
|
||||
default: break
|
||||
|
||||
156
OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm
Normal file
156
OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm
Normal 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
|
||||
1533
OSBindings/Mac/Clock SignalTests/Z80ContentionTests.mm
Normal file
1533
OSBindings/Mac/Clock SignalTests/Z80ContentionTests.mm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
202
OSBindings/Qt/keyboard.cpp
Normal 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
18
OSBindings/Qt/keyboard.h
Normal 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
|
||||
@@ -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;
|
||||
@@ -1258,9 +1092,13 @@ void MainWindow::start_spectrum() {
|
||||
using Target = Analyser::Static::ZXSpectrum::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
|
||||
switch(ui->oricModelComboBox->currentIndex()) {
|
||||
default: target->model = Target::Model::Plus2a; break;
|
||||
case 1: target->model = Target::Model::Plus3; break;
|
||||
switch(ui->spectrumModelComboBox->currentIndex()) {
|
||||
default: target->model = Target::Model::SixteenK; break;
|
||||
case 1: target->model = Target::Model::FortyEightK; break;
|
||||
case 2: target->model = Target::Model::OneTwoEightK; break;
|
||||
case 3: target->model = Target::Model::Plus2; break;
|
||||
case 4: target->model = Target::Model::Plus2a; break;
|
||||
case 5: target->model = Target::Model::Plus3; break;
|
||||
}
|
||||
|
||||
launchTarget(std::move(target));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -551,6 +551,26 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="spectrumModelComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>128kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2a</string>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -82,24 +82,25 @@ template < class T,
|
||||
}
|
||||
number_of_cycles_ -= operation->machine_cycle.length;
|
||||
last_request_status_ = request_status_;
|
||||
|
||||
// TODO: eliminate this conditional if all bus cycles have an address filled in.
|
||||
last_address_bus_ = operation->machine_cycle.address ? *operation->machine_cycle.address : 0xdead;
|
||||
|
||||
number_of_cycles_ -= bus_handler_.perform_machine_cycle(operation->machine_cycle);
|
||||
if(uses_bus_request && bus_request_line_) goto do_bus_acknowledge;
|
||||
break;
|
||||
case MicroOp::MoveToNextProgram:
|
||||
advance_operation();
|
||||
break;
|
||||
case MicroOp::DecodeOperation:
|
||||
case MicroOp::IncrementR:
|
||||
refresh_addr_ = ir_;
|
||||
ir_.halves.low = (ir_.halves.low & 0x80) | ((ir_.halves.low + current_instruction_page_->r_step) & 0x7f);
|
||||
ir_.halves.low = (ir_.halves.low & 0x80) | ((ir_.halves.low + 1) & 0x7f);
|
||||
break;
|
||||
case MicroOp::DecodeOperation:
|
||||
pc_.full += pc_increment_ & uint16_t(halt_mask_);
|
||||
scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_];
|
||||
flag_adjustment_history_ <<= 1;
|
||||
break;
|
||||
case MicroOp::DecodeOperationNoRChange:
|
||||
refresh_addr_ = ir_;
|
||||
pc_.full += pc_increment_ & uint16_t(halt_mask_);
|
||||
scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_];
|
||||
break;
|
||||
|
||||
case MicroOp::Increment8NoFlags: ++ *static_cast<uint8_t *>(operation->source); break;
|
||||
case MicroOp::Increment16: ++ *static_cast<uint16_t *>(operation->source); break;
|
||||
@@ -460,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
|
||||
@@ -927,7 +928,7 @@ template < class T,
|
||||
return wait_line_;
|
||||
}
|
||||
|
||||
#define isTerminal(n) (n == MicroOp::MoveToNextProgram || n == MicroOp::DecodeOperation || n == MicroOp::DecodeOperationNoRChange)
|
||||
#define isTerminal(n) (n == MicroOp::MoveToNextProgram || n == MicroOp::DecodeOperation)
|
||||
|
||||
template < class T,
|
||||
bool uses_bus_request,
|
||||
|
||||
@@ -20,14 +20,14 @@ ProcessorStorage::ProcessorStorage() {
|
||||
#define ReadOpcodeWait(f) PartialMachineCycle(PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f)
|
||||
#define ReadOpcodeEnd() PartialMachineCycle(PartialMachineCycle::ReadOpcode, HalfCycles(1), &pc_.full, &operation_, false)
|
||||
|
||||
#define Refresh(len) PartialMachineCycle(PartialMachineCycle::Refresh, HalfCycles(len), &refresh_addr_.full, nullptr, false)
|
||||
#define Refresh() PartialMachineCycle(PartialMachineCycle::Refresh, HalfCycles(4), &refresh_addr_.full, nullptr, false)
|
||||
|
||||
#define ReadStart(addr, val) PartialMachineCycle(PartialMachineCycle::ReadStart, HalfCycles(3), &addr.full, &val, false)
|
||||
#define ReadWait(l, addr, val, f) PartialMachineCycle(PartialMachineCycle::ReadWait, HalfCycles(l), &addr.full, &val, f)
|
||||
#define ReadWait(addr, val) PartialMachineCycle(PartialMachineCycle::ReadWait, HalfCycles(2), &addr.full, &val, true)
|
||||
#define ReadEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Read, HalfCycles(3), &addr.full, &val, false)
|
||||
|
||||
#define WriteStart(addr, val) PartialMachineCycle(PartialMachineCycle::WriteStart,HalfCycles(3), &addr.full, &val, false)
|
||||
#define WriteWait(l, addr, val, f) PartialMachineCycle(PartialMachineCycle::WriteWait, HalfCycles(l), &addr.full, &val, f)
|
||||
#define WriteWait(addr, val) PartialMachineCycle(PartialMachineCycle::WriteWait, HalfCycles(2), &addr.full, &val, true)
|
||||
#define WriteEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Write, HalfCycles(3), &addr.full, &val, false)
|
||||
|
||||
#define InputStart(addr, val) PartialMachineCycle(PartialMachineCycle::InputStart, HalfCycles(3), &addr.full, &val, false)
|
||||
@@ -38,41 +38,24 @@ ProcessorStorage::ProcessorStorage() {
|
||||
#define OutputWait(addr, val, f) PartialMachineCycle(PartialMachineCycle::OutputWait, HalfCycles(2), &addr.full, &val, f)
|
||||
#define OutputEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Output, HalfCycles(3), &addr.full, &val, false)
|
||||
|
||||
#define IntAckStart(length, val) PartialMachineCycle(PartialMachineCycle::InterruptStart, HalfCycles(length), nullptr, &val, false)
|
||||
#define IntWait(val) PartialMachineCycle(PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true)
|
||||
#define IntAckEnd(val) PartialMachineCycle(PartialMachineCycle::Interrupt, HalfCycles(3), nullptr, &val, false)
|
||||
#define IntAckStart(length, val) PartialMachineCycle(PartialMachineCycle::InterruptStart, HalfCycles(length), &pc_.full, &val, false)
|
||||
#define IntWait(val) PartialMachineCycle(PartialMachineCycle::InterruptWait, HalfCycles(2), &pc_.full, &val, true)
|
||||
#define IntAckEnd(val) PartialMachineCycle(PartialMachineCycle::Interrupt, HalfCycles(3), &pc_.full, &val, false)
|
||||
|
||||
|
||||
// A wrapper to express a bus operation as a micro-op
|
||||
#define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
|
||||
|
||||
// Compound bus operations, as micro-ops
|
||||
|
||||
// Read3 is a standard read cycle: 1.5 cycles, then check the wait line, then 1.5 cycles;
|
||||
// Read4 is a four-cycle read that has to do something to calculate the address: 1.5 cycles, then an extra wait cycle, then check the wait line, then 1.5 cycles;
|
||||
// Read4Pre is a four-cycle read that has to do something after reading: 1.5 cycles, then check the wait line, then an extra wait cycle, then 1.5 cycles;
|
||||
// Read5 is a five-cycle read: 1.5 cycles, two wait cycles, check the wait line, 1.5 cycles.
|
||||
#define Read3(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
|
||||
#define Read4(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(2, addr, val, false)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
|
||||
#define Read4Pre(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadWait(2, addr, val, false)), BusOp(ReadEnd(addr, val))
|
||||
#define Read5(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(4, addr, val, false)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
|
||||
|
||||
#define Write3(addr, val) BusOp(WriteStart(addr, val)), BusOp(WriteWait(2, addr, val, true)), BusOp(WriteEnd(addr, val))
|
||||
#define Write5(addr, val) BusOp(WriteStart(addr, val)), BusOp(WriteWait(4, addr, val, false)), BusOp(WriteWait(2, addr, val, true)), BusOp(WriteEnd(addr, val))
|
||||
|
||||
#define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val))
|
||||
#define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val))
|
||||
#define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, HalfCycles(len), nullptr, nullptr, false}}
|
||||
#define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, HalfCycles(len), &last_address_bus_, nullptr, false}}
|
||||
#define Read(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(addr, val)), BusOp(ReadEnd(addr, val))
|
||||
#define Write(addr, val) BusOp(WriteStart(addr, val)), BusOp(WriteWait(addr, val)), BusOp(WriteEnd(addr, val))
|
||||
#define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val))
|
||||
#define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val))
|
||||
|
||||
/// A sequence is a series of micro-ops that ends in a move-to-next-program operation.
|
||||
#define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} }
|
||||
|
||||
/// An instruction is the part of an instruction that follows instruction fetch; it should include two or more refresh cycles and then the work of the instruction.
|
||||
#define Instr(r, ...) Sequence(BusOp(Refresh(r)), __VA_ARGS__)
|
||||
|
||||
/// A standard instruction is one with the most normal timing: two cycles of refresh, then the work.
|
||||
#define StdInstr(...) Instr(4, __VA_ARGS__)
|
||||
|
||||
// Assumption made: those instructions that are rated with an opcode fetch greater than four cycles spend the extra time
|
||||
// providing a lengthened refresh cycle. I assume this because the CPU doesn't have foresight and presumably spends the
|
||||
// normal refresh time decoding. So if it gets to cycle four and realises it has two more cycles of work, I have assumed
|
||||
@@ -82,65 +65,60 @@ ProcessorStorage::ProcessorStorage() {
|
||||
#define Inc16(r) {(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full}
|
||||
#define Inc8NoFlags(r) {MicroOp::Increment8NoFlags, &r}
|
||||
|
||||
#define ReadInc(addr, val) Read3(addr, val), Inc16(addr)
|
||||
#define Read4Inc(addr, val) Read4(addr, val), Inc16(addr)
|
||||
#define Read5Inc(addr, val) Read5(addr, val), Inc16(addr)
|
||||
#define WriteInc(addr, val) Write3(addr, val), {MicroOp::Increment16, &addr.full}
|
||||
#define ReadInc(addr, val) Read(addr, val), Inc16(addr)
|
||||
#define WriteInc(addr, val) Write(addr, val), {MicroOp::Increment16, &addr.full}
|
||||
|
||||
#define Read16Inc(addr, val) ReadInc(addr, val.halves.low), ReadInc(addr, val.halves.high)
|
||||
#define Read16(addr, val) ReadInc(addr, val.halves.low), Read3(addr, val.halves.high)
|
||||
#define Read16(addr, val) ReadInc(addr, val.halves.low), Read(addr, val.halves.high)
|
||||
|
||||
#define Write16(addr, val) WriteInc(addr, val.halves.low), Write3(addr, val.halves.high)
|
||||
#define Write16(addr, val) WriteInc(addr, val.halves.low), Write(addr, val.halves.high)
|
||||
|
||||
#define INDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &index}
|
||||
#define FINDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), {MicroOp::CalculateIndexAddress, &index}
|
||||
#define INDEX_ADDR() (add_offsets ? memptr_ : index)
|
||||
|
||||
#define Push(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.low)
|
||||
#define Pop(x) Read3(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read3(sp_, x.halves.high), {MicroOp::Increment16, &sp_.full}
|
||||
|
||||
#define Push8(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write5(sp_, x.halves.low)
|
||||
#define Pop7(x) Read3(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read4(sp_, x.halves.high), {MicroOp::Increment16, &sp_.full}
|
||||
#define Push(x) {MicroOp::Decrement16, &sp_.full}, Write(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write(sp_, x.halves.low)
|
||||
#define Pop(x) Read(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read(sp_, x.halves.high), {MicroOp::Increment16, &sp_.full}
|
||||
|
||||
/* The following are actual instructions */
|
||||
#define NOP Sequence(BusOp(Refresh(4)))
|
||||
#define NOP { {MicroOp::MoveToNextProgram} }
|
||||
|
||||
#define JP(cc) StdInstr(Read16Inc(pc_, memptr_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define CALL(cc) StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RET(cc) Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define LD(a, b) StdInstr({MicroOp::Move8, &b, &a})
|
||||
#define JP(cc) Sequence(Read16Inc(pc_, memptr_), {MicroOp::cc}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define CALL(cc) Sequence(ReadInc(pc_, memptr_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, ReadInc(pc_, memptr_.halves.high), InternalOperation(2), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RET(cc) Sequence(InternalOperation(2), {MicroOp::cc}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define JR(cc) Sequence(ReadInc(pc_, temp8_), {MicroOp::cc}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RST() Sequence(InternalOperation(2), {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define LD(a, b) Sequence({MicroOp::Move8, &b, &a})
|
||||
|
||||
#define LD_GROUP(r, ri) \
|
||||
LD(r, bc_.halves.high), LD(r, bc_.halves.low), LD(r, de_.halves.high), LD(r, de_.halves.low), \
|
||||
LD(r, index.halves.high), LD(r, index.halves.low), \
|
||||
StdInstr(INDEX(), Read3(INDEX_ADDR(), temp8_), {MicroOp::Move8, &temp8_, &ri}), \
|
||||
Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), {MicroOp::Move8, &temp8_, &ri}), \
|
||||
LD(r, a_)
|
||||
|
||||
#define READ_OP_GROUP(op) \
|
||||
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
|
||||
StdInstr(INDEX(), Read3(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr({MicroOp::op, &a_})
|
||||
Sequence({MicroOp::op, &bc_.halves.high}), Sequence({MicroOp::op, &bc_.halves.low}), \
|
||||
Sequence({MicroOp::op, &de_.halves.high}), Sequence({MicroOp::op, &de_.halves.low}), \
|
||||
Sequence({MicroOp::op, &index.halves.high}), Sequence({MicroOp::op, &index.halves.low}), \
|
||||
Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
Sequence({MicroOp::op, &a_})
|
||||
|
||||
#define READ_OP_GROUP_D(op) \
|
||||
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
|
||||
StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr({MicroOp::op, &a_})
|
||||
Sequence({MicroOp::op, &bc_.halves.high}), Sequence({MicroOp::op, &bc_.halves.low}), \
|
||||
Sequence({MicroOp::op, &de_.halves.high}), Sequence({MicroOp::op, &de_.halves.low}), \
|
||||
Sequence({MicroOp::op, &index.halves.high}), Sequence({MicroOp::op, &index.halves.low}), \
|
||||
Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence({MicroOp::op, &a_})
|
||||
|
||||
#define RMW(x, op, ...) StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(INDEX_ADDR(), x))
|
||||
#define RMWI(x, op, ...) StdInstr(Read4(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(INDEX_ADDR(), x))
|
||||
#define RMW(x, op, ...) Sequence(INDEX(), Read(INDEX_ADDR(), x), InternalOperation(2), {MicroOp::op, &x}, Write(INDEX_ADDR(), x))
|
||||
#define RMWI(x, op, ...) Sequence(Read(INDEX_ADDR(), x), InternalOperation(2), {MicroOp::op, &x}, Write(INDEX_ADDR(), x))
|
||||
|
||||
#define MODIFY_OP_GROUP(op) \
|
||||
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
|
||||
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
|
||||
Sequence({MicroOp::op, &bc_.halves.high}), Sequence({MicroOp::op, &bc_.halves.low}), \
|
||||
Sequence({MicroOp::op, &de_.halves.high}), Sequence({MicroOp::op, &de_.halves.low}), \
|
||||
Sequence({MicroOp::op, &index.halves.high}), Sequence({MicroOp::op, &index.halves.low}), \
|
||||
RMW(temp8_, op), \
|
||||
StdInstr({MicroOp::op, &a_})
|
||||
Sequence({MicroOp::op, &a_})
|
||||
|
||||
#define IX_MODIFY_OP_GROUP(op) \
|
||||
RMWI(bc_.halves.high, op), \
|
||||
@@ -153,18 +131,18 @@ ProcessorStorage::ProcessorStorage() {
|
||||
RMWI(a_, op)
|
||||
|
||||
#define IX_READ_OP_GROUP(op) \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
|
||||
StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_})
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}), \
|
||||
Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_})
|
||||
|
||||
#define ADD16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::ADD16, &s.full, &d.full})
|
||||
#define ADC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::ADC16, &s.full, &d.full})
|
||||
#define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
|
||||
#define ADD16(d, s) Sequence(InternalOperation(8), InternalOperation(6), {MicroOp::ADD16, &s.full, &d.full})
|
||||
#define ADC16(d, s) Sequence(InternalOperation(8), InternalOperation(6), {MicroOp::ADC16, &s.full, &d.full})
|
||||
#define SBC16(d, s) Sequence(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
|
||||
|
||||
void ProcessorStorage::install_default_instruction_set() {
|
||||
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, memptr_.halves.high));
|
||||
@@ -175,11 +153,8 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
assemble_base_page(fd_page_, iy_, true, fdcb_page_);
|
||||
assemble_ed_page(ed_page_);
|
||||
|
||||
fdcb_page_.r_step = 0;
|
||||
fd_page_.is_indexed = true;
|
||||
fdcb_page_.is_indexed = true;
|
||||
|
||||
ddcb_page_.r_step = 0;
|
||||
dd_page_.is_indexed = true;
|
||||
ddcb_page_.is_indexed = true;
|
||||
|
||||
@@ -202,9 +177,10 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
BusOp(ReadOpcodeStart()),
|
||||
BusOp(ReadOpcodeWait(true)),
|
||||
BusOp(ReadOpcodeEnd()),
|
||||
BusOp(Refresh(6)),
|
||||
BusOp(Refresh()),
|
||||
InternalOperation(2),
|
||||
Push(pc_),
|
||||
{ MicroOp::JumpTo66, nullptr, nullptr},
|
||||
{ MicroOp::JumpTo66 },
|
||||
{ MicroOp::MoveToNextProgram }
|
||||
};
|
||||
MicroOp irq_mode0_program[] = {
|
||||
@@ -212,14 +188,14 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
BusOp(IntAckStart(5, operation_)),
|
||||
BusOp(IntWait(operation_)),
|
||||
BusOp(IntAckEnd(operation_)),
|
||||
{ MicroOp::DecodeOperationNoRChange }
|
||||
{ MicroOp::DecodeOperation }
|
||||
};
|
||||
MicroOp irq_mode1_program[] = {
|
||||
{ MicroOp::BeginIRQ },
|
||||
BusOp(IntAckStart(7, operation_)), // 7 half cycles (including +
|
||||
BusOp(IntWait(operation_)), // [potentially 2 half cycles] +
|
||||
BusOp(IntAckEnd(operation_)), // Implicitly 3 half cycles +
|
||||
BusOp(Refresh(4)), // 4 half cycles +
|
||||
BusOp(Refresh()), // 4 half cycles +
|
||||
Push(pc_), // 12 half cycles = 26 half cycles = 13 cycles
|
||||
{ MicroOp::Move16, &temp16_.full, &pc_.full },
|
||||
{ MicroOp::MoveToNextProgram }
|
||||
@@ -229,7 +205,7 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
BusOp(IntAckStart(7, temp16_.halves.low)),
|
||||
BusOp(IntWait(temp16_.halves.low)),
|
||||
BusOp(IntAckEnd(temp16_.halves.low)),
|
||||
BusOp(Refresh(4)),
|
||||
BusOp(Refresh()),
|
||||
Push(pc_),
|
||||
{ MicroOp::Move8, &ir_.halves.high, &temp16_.halves.high },
|
||||
Read16(temp16_, pc_),
|
||||
@@ -244,8 +220,8 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
}
|
||||
|
||||
void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
#define IN_C(r) StdInstr({MicroOp::Move16, &bc_.full, &memptr_.full}, Input(bc_, r), {MicroOp::SetInFlags, &r})
|
||||
#define OUT_C(r) StdInstr(Output(bc_, r), {MicroOp::SetOutFlags})
|
||||
#define IN_C(r) Sequence({MicroOp::Move16, &bc_.full, &memptr_.full}, Input(bc_, r), {MicroOp::SetInFlags, &r})
|
||||
#define OUT_C(r) Sequence(Output(bc_, r), {MicroOp::SetOutFlags})
|
||||
#define IN_OUT(r) IN_C(r), OUT_C(r)
|
||||
|
||||
#define NOP_ROW() NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP
|
||||
@@ -255,58 +231,58 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
NOP_ROW(), /* 0x20 */
|
||||
NOP_ROW(), /* 0x30 */
|
||||
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(bc_.halves.high),
|
||||
/* 0x42 SBC HL, BC */ SBC16(hl_, bc_), /* 0x43 LD (nn), BC */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, bc_)),
|
||||
/* 0x44 NEG */ StdInstr({MicroOp::NEG}), /* 0x45 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x46 IM 0 */ StdInstr({MicroOp::IM}), /* 0x47 LD I, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.high}),
|
||||
/* 0x42 SBC HL, BC */ SBC16(hl_, bc_), /* 0x43 LD (nn), BC */ Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, bc_)),
|
||||
/* 0x44 NEG */ Sequence({MicroOp::NEG}), /* 0x45 RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x46 IM 0 */ Sequence({MicroOp::IM}), /* 0x47 LD I, A */ Sequence(InternalOperation(2), {MicroOp::Move8, &a_, &ir_.halves.high}),
|
||||
/* 0x48 IN C, (C); 0x49 OUT (C), C */ IN_OUT(bc_.halves.low),
|
||||
/* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, bc_)),
|
||||
/* 0x4c NEG */ StdInstr({MicroOp::NEG}), /* 0x4d RETI */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x4e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x4f LD R, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.low}),
|
||||
/* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, bc_)),
|
||||
/* 0x4c NEG */ Sequence({MicroOp::NEG}), /* 0x4d RETI */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x4e IM 0/1 */ Sequence({MicroOp::IM}), /* 0x4f LD R, A */ Sequence(InternalOperation(2), {MicroOp::Move8, &a_, &ir_.halves.low}),
|
||||
/* 0x50 IN D, (C); 0x51 OUT (C), D */ IN_OUT(de_.halves.high),
|
||||
/* 0x52 SBC HL, DE */ SBC16(hl_, de_), /* 0x53 LD (nn), DE */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, de_)),
|
||||
/* 0x54 NEG */ StdInstr({MicroOp::NEG}), /* 0x55 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x56 IM 1 */ StdInstr({MicroOp::IM}), /* 0x57 LD A, I */ Instr(6, {MicroOp::Move8, &ir_.halves.high, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x52 SBC HL, DE */ SBC16(hl_, de_), /* 0x53 LD (nn), DE */ Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, de_)),
|
||||
/* 0x54 NEG */ Sequence({MicroOp::NEG}), /* 0x55 RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x56 IM 1 */ Sequence({MicroOp::IM}), /* 0x57 LD A, I */ Sequence(InternalOperation(2), {MicroOp::Move8, &ir_.halves.high, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x58 IN E, (C); 0x59 OUT (C), E */ IN_OUT(de_.halves.low),
|
||||
/* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, de_)),
|
||||
/* 0x5c NEG */ StdInstr({MicroOp::NEG}), /* 0x5d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x5e IM 2 */ StdInstr({MicroOp::IM}), /* 0x5f LD A, R */ Instr(6, {MicroOp::Move8, &ir_.halves.low, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, de_)),
|
||||
/* 0x5c NEG */ Sequence({MicroOp::NEG}), /* 0x5d RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x5e IM 2 */ Sequence({MicroOp::IM}), /* 0x5f LD A, R */ Sequence(InternalOperation(2), {MicroOp::Move8, &ir_.halves.low, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x60 IN H, (C); 0x61 OUT (C), H */ IN_OUT(hl_.halves.high),
|
||||
/* 0x62 SBC HL, HL */ SBC16(hl_, hl_), /* 0x63 LD (nn), HL */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, hl_)),
|
||||
/* 0x64 NEG */ StdInstr({MicroOp::NEG}), /* 0x65 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x66 IM 0 */ StdInstr({MicroOp::IM}), /* 0x67 RRD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RRD}, Write3(hl_, temp8_)),
|
||||
/* 0x62 SBC HL, HL */ SBC16(hl_, hl_), /* 0x63 LD (nn), HL */ Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, hl_)),
|
||||
/* 0x64 NEG */ Sequence({MicroOp::NEG}), /* 0x65 RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x66 IM 0 */ Sequence({MicroOp::IM}), /* 0x67 RRD */ Sequence(Read(hl_, temp8_), InternalOperation(8), {MicroOp::RRD}, Write(hl_, temp8_)),
|
||||
/* 0x68 IN L, (C); 0x69 OUT (C), L */ IN_OUT(hl_.halves.low),
|
||||
/* 0x6a ADC HL, HL */ ADC16(hl_, hl_), /* 0x6b LD HL, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, hl_)),
|
||||
/* 0x6c NEG */ StdInstr({MicroOp::NEG}), /* 0x6d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x6e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x6f RLD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RLD}, Write3(hl_, temp8_)),
|
||||
/* 0x70 IN (C) */ IN_C(temp8_), /* 0x71 OUT (C), 0 */ StdInstr({MicroOp::SetZero}, Output(bc_, temp8_), {MicroOp::SetOutFlags}),
|
||||
/* 0x72 SBC HL, SP */ SBC16(hl_, sp_), /* 0x73 LD (nn), SP */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, sp_)),
|
||||
/* 0x74 NEG */ StdInstr({MicroOp::NEG}), /* 0x75 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x76 IM 1 */ StdInstr({MicroOp::IM}), /* 0x77 XX */ NOP,
|
||||
/* 0x6a ADC HL, HL */ ADC16(hl_, hl_), /* 0x6b LD HL, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, hl_)),
|
||||
/* 0x6c NEG */ Sequence({MicroOp::NEG}), /* 0x6d RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x6e IM 0/1 */ Sequence({MicroOp::IM}), /* 0x6f RLD */ Sequence(Read(hl_, temp8_), InternalOperation(8), {MicroOp::RLD}, Write(hl_, temp8_)),
|
||||
/* 0x70 IN (C) */ IN_C(temp8_), /* 0x71 OUT (C), 0 */ Sequence({MicroOp::SetZero}, Output(bc_, temp8_), {MicroOp::SetOutFlags}),
|
||||
/* 0x72 SBC HL, SP */ SBC16(hl_, sp_), /* 0x73 LD (nn), SP */ Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, sp_)),
|
||||
/* 0x74 NEG */ Sequence({MicroOp::NEG}), /* 0x75 RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x76 IM 1 */ Sequence({MicroOp::IM}), /* 0x77 XX */ NOP,
|
||||
/* 0x78 IN A, (C); 0x79 OUT (C), A */ IN_OUT(a_),
|
||||
/* 0x7a ADC HL, SP */ ADC16(hl_, sp_), /* 0x7b LD SP, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, sp_)),
|
||||
/* 0x7c NEG */ StdInstr({MicroOp::NEG}), /* 0x7d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x7e IM 2 */ StdInstr({MicroOp::IM}), /* 0x7f XX */ NOP,
|
||||
/* 0x7a ADC HL, SP */ ADC16(hl_, sp_), /* 0x7b LD SP, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, sp_)),
|
||||
/* 0x7c NEG */ Sequence({MicroOp::NEG}), /* 0x7d RETN */ Sequence(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x7e IM 2 */ Sequence({MicroOp::IM}), /* 0x7f XX */ NOP,
|
||||
NOP_ROW(), /* 0x80 ... 0x8f */
|
||||
NOP_ROW(), /* 0x90 ... 0x9f */
|
||||
/* 0xa0 LDI */ StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDI}),
|
||||
/* 0xa1 CPI */ StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPI}),
|
||||
/* 0xa2 INI */ Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INI}),
|
||||
/* 0xa3 OTI */ Instr(6, Read3(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_)),
|
||||
/* 0xa0 LDI */ Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDI}),
|
||||
/* 0xa1 CPI */ Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPI}),
|
||||
/* 0xa2 INI */ Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INI}),
|
||||
/* 0xa3 OTI */ Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_)),
|
||||
NOP, NOP, NOP, NOP,
|
||||
/* 0xa8 LDD */ StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDD}),
|
||||
/* 0xa9 CPD */ StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPD}),
|
||||
/* 0xaa IND */ Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::IND}),
|
||||
/* 0xab OTD */ Instr(6, Read3(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_)),
|
||||
/* 0xa8 LDD */ Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDD}),
|
||||
/* 0xa9 CPD */ Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPD}),
|
||||
/* 0xaa IND */ Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::IND}),
|
||||
/* 0xab OTD */ Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_)),
|
||||
NOP, NOP, NOP, NOP,
|
||||
/* 0xb0 LDIR */ StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDIR}, InternalOperation(10)),
|
||||
/* 0xb1 CPIR */ StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPIR}, InternalOperation(10)),
|
||||
/* 0xb2 INIR */ Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INIR}, InternalOperation(10)),
|
||||
/* 0xb3 OTIR */ Instr(6, Read3(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)),
|
||||
/* 0xb0 LDIR */ Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDIR}, InternalOperation(10)),
|
||||
/* 0xb1 CPIR */ Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPIR}, InternalOperation(10)),
|
||||
/* 0xb2 INIR */ Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INIR}, InternalOperation(10)),
|
||||
/* 0xb3 OTIR */ Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)),
|
||||
NOP, NOP, NOP, NOP,
|
||||
/* 0xb8 LDDR */ StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDDR}, InternalOperation(10)),
|
||||
/* 0xb9 CPDR */ StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPDR}, InternalOperation(10)),
|
||||
/* 0xba INDR */ Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INDR}, InternalOperation(10)),
|
||||
/* 0xbb OTDR */ Instr(6, Read3(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)),
|
||||
/* 0xb8 LDDR */ Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDDR}, InternalOperation(10)),
|
||||
/* 0xb9 CPDR */ Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPDR}, InternalOperation(10)),
|
||||
/* 0xba INDR */ Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INDR}, InternalOperation(10)),
|
||||
/* 0xbb OTDR */ Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)),
|
||||
NOP, NOP, NOP, NOP,
|
||||
NOP_ROW(), /* 0xc0 */
|
||||
NOP_ROW(), /* 0xd0 */
|
||||
@@ -346,77 +322,77 @@ void ProcessorStorage::assemble_cb_page(InstructionPage &target, RegisterPair16
|
||||
|
||||
void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets, InstructionPage &cb_page) {
|
||||
#define INC_DEC_LD(r) \
|
||||
StdInstr({MicroOp::Increment8, &r}), \
|
||||
StdInstr({MicroOp::Decrement8, &r}), \
|
||||
StdInstr(ReadInc(pc_, r))
|
||||
Sequence({MicroOp::Increment8, &r}), \
|
||||
Sequence({MicroOp::Decrement8, &r}), \
|
||||
Sequence(ReadInc(pc_, r))
|
||||
|
||||
#define INC_INC_DEC_LD(rf, r) \
|
||||
Instr(8, {MicroOp::Increment16, &rf.full}), INC_DEC_LD(r)
|
||||
Sequence(InternalOperation(4), {MicroOp::Increment16, &rf.full}), INC_DEC_LD(r)
|
||||
|
||||
#define DEC_INC_DEC_LD(rf, r) \
|
||||
Instr(8, {MicroOp::Decrement16, &rf.full}), INC_DEC_LD(r)
|
||||
Sequence(InternalOperation(4), {MicroOp::Decrement16, &rf.full}), INC_DEC_LD(r)
|
||||
|
||||
InstructionTable base_program_table = {
|
||||
/* 0x00 NOP */ NOP, /* 0x01 LD BC, nn */ StdInstr(Read16Inc(pc_, bc_)),
|
||||
/* 0x02 LD (BC), A */ StdInstr({MicroOp::SetAddrAMemptr, &bc_.full}, Write3(bc_, a_)),
|
||||
/* 0x00 NOP */ NOP, /* 0x01 LD BC, nn */ Sequence(Read16Inc(pc_, bc_)),
|
||||
/* 0x02 LD (BC), A */ Sequence({MicroOp::SetAddrAMemptr, &bc_.full}, Write(bc_, a_)),
|
||||
|
||||
/* 0x03 INC BC; 0x04 INC B; 0x05 DEC B; 0x06 LD B, n */
|
||||
INC_INC_DEC_LD(bc_, bc_.halves.high),
|
||||
|
||||
/* 0x07 RLCA */ StdInstr({MicroOp::RLCA}),
|
||||
/* 0x08 EX AF, AF' */ StdInstr({MicroOp::ExAFAFDash}), /* 0x09 ADD HL, BC */ ADD16(index, bc_),
|
||||
/* 0x0a LD A, (BC) */ StdInstr({MicroOp::Move16, &bc_.full, &memptr_.full}, Read3(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0x07 RLCA */ Sequence({MicroOp::RLCA}),
|
||||
/* 0x08 EX AF, AF' */ Sequence({MicroOp::ExAFAFDash}), /* 0x09 ADD HL, BC */ ADD16(index, bc_),
|
||||
/* 0x0a LD A, (BC) */ Sequence({MicroOp::Move16, &bc_.full, &memptr_.full}, Read(memptr_, a_), Inc16(memptr_)),
|
||||
|
||||
/* 0x0b DEC BC; 0x0c INC C; 0x0d DEC C; 0x0e LD C, n */
|
||||
DEC_INC_DEC_LD(bc_, bc_.halves.low),
|
||||
|
||||
/* 0x0f RRCA */ StdInstr({MicroOp::RRCA}),
|
||||
/* 0x10 DJNZ */ Instr(6, ReadInc(pc_, temp8_), {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0x11 LD DE, nn */ StdInstr(Read16Inc(pc_, de_)),
|
||||
/* 0x12 LD (DE), A */ StdInstr({MicroOp::SetAddrAMemptr, &de_.full}, Write3(de_, a_)),
|
||||
/* 0x0f RRCA */ Sequence({MicroOp::RRCA}),
|
||||
/* 0x10 DJNZ */ Sequence(InternalOperation(2), ReadInc(pc_, temp8_), {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0x11 LD DE, nn */ Sequence(Read16Inc(pc_, de_)),
|
||||
/* 0x12 LD (DE), A */ Sequence({MicroOp::SetAddrAMemptr, &de_.full}, Write(de_, a_)),
|
||||
|
||||
/* 0x13 INC DE; 0x14 INC D; 0x15 DEC D; 0x16 LD D, n */
|
||||
INC_INC_DEC_LD(de_, de_.halves.high),
|
||||
|
||||
/* 0x17 RLA */ StdInstr({MicroOp::RLA}),
|
||||
/* 0x18 JR */ StdInstr(ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0x17 RLA */ Sequence({MicroOp::RLA}),
|
||||
/* 0x18 JR */ Sequence(ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0x19 ADD HL, DE */ ADD16(index, de_),
|
||||
/* 0x1a LD A, (DE) */ StdInstr({MicroOp::Move16, &de_.full, &memptr_.full}, Read3(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0x1a LD A, (DE) */ Sequence({MicroOp::Move16, &de_.full, &memptr_.full}, Read(memptr_, a_), Inc16(memptr_)),
|
||||
|
||||
/* 0x1b DEC DE; 0x1c INC E; 0x1d DEC E; 0x1e LD E, n */
|
||||
DEC_INC_DEC_LD(de_, de_.halves.low),
|
||||
|
||||
/* 0x1f RRA */ StdInstr({MicroOp::RRA}),
|
||||
/* 0x20 JR NZ */ JR(TestNZ), /* 0x21 LD HL, nn */ StdInstr(Read16Inc(pc_, index)),
|
||||
/* 0x22 LD (nn), HL */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, index)),
|
||||
/* 0x1f RRA */ Sequence({MicroOp::RRA}),
|
||||
/* 0x20 JR NZ */ JR(TestNZ), /* 0x21 LD HL, nn */ Sequence(Read16Inc(pc_, index)),
|
||||
/* 0x22 LD (nn), HL */ Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, index)),
|
||||
|
||||
/* 0x23 INC HL; 0x24 INC H; 0x25 DEC H; 0x26 LD H, n */
|
||||
INC_INC_DEC_LD(index, index.halves.high),
|
||||
|
||||
/* 0x27 DAA */ StdInstr({MicroOp::DAA}),
|
||||
/* 0x27 DAA */ Sequence({MicroOp::DAA}),
|
||||
/* 0x28 JR Z */ JR(TestZ), /* 0x29 ADD HL, HL */ ADD16(index, index),
|
||||
/* 0x2a LD HL, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, index)),
|
||||
/* 0x2a LD HL, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, index)),
|
||||
|
||||
/* 0x2b DEC HL; 0x2c INC L; 0x2d DEC L; 0x2e LD L, n */
|
||||
DEC_INC_DEC_LD(index, index.halves.low),
|
||||
|
||||
/* 0x2f CPL */ StdInstr({MicroOp::CPL}),
|
||||
/* 0x30 JR NC */ JR(TestNC), /* 0x31 LD SP, nn */ StdInstr(Read16Inc(pc_, sp_)),
|
||||
/* 0x32 LD (nn), A */ StdInstr(Read16Inc(pc_, temp16_), {MicroOp::SetAddrAMemptr, &temp16_.full}, Write3(temp16_, a_)),
|
||||
/* 0x33 INC SP */ Instr(8, {MicroOp::Increment16, &sp_.full}),
|
||||
/* 0x34 INC (HL) */ StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::Increment8, &temp8_}, Write3(INDEX_ADDR(), temp8_)),
|
||||
/* 0x35 DEC (HL) */ StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::Decrement8, &temp8_}, Write3(INDEX_ADDR(), temp8_)),
|
||||
/* 0x36 LD (HL), n */ StdInstr(ReadInc(pc_, temp8_), Write3(INDEX_ADDR(), temp8_)),
|
||||
/* 0x37 SCF */ StdInstr({MicroOp::SCF}),
|
||||
/* 0x2f CPL */ Sequence({MicroOp::CPL}),
|
||||
/* 0x30 JR NC */ JR(TestNC), /* 0x31 LD SP, nn */ Sequence(Read16Inc(pc_, sp_)),
|
||||
/* 0x32 LD (nn), A */ Sequence(Read16Inc(pc_, temp16_), {MicroOp::SetAddrAMemptr, &temp16_.full}, Write(temp16_, a_)),
|
||||
/* 0x33 INC SP */ Sequence(InternalOperation(4), {MicroOp::Increment16, &sp_.full}),
|
||||
/* 0x34 INC (HL) */ Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::Increment8, &temp8_}, Write(INDEX_ADDR(), temp8_)),
|
||||
/* 0x35 DEC (HL) */ Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::Decrement8, &temp8_}, Write(INDEX_ADDR(), temp8_)),
|
||||
/* 0x36 LD (HL), n */ Sequence(ReadInc(pc_, temp8_), Write(INDEX_ADDR(), temp8_)),
|
||||
/* 0x37 SCF */ Sequence({MicroOp::SCF}),
|
||||
/* 0x38 JR C */ JR(TestC),
|
||||
/* 0x39 ADD HL, SP */ ADD16(index, sp_),
|
||||
/* 0x3a LD A, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read3(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0x3b DEC SP */ Instr(8, {MicroOp::Decrement16, &sp_.full}),
|
||||
/* 0x3a LD A, (nn) */ Sequence(Read16Inc(pc_, memptr_), Read(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0x3b DEC SP */ Sequence(InternalOperation(4), {MicroOp::Decrement16, &sp_.full}),
|
||||
|
||||
/* 0x3c INC A; 0x3d DEC A; 0x3e LD A, n */
|
||||
INC_DEC_LD(a_),
|
||||
|
||||
/* 0x3f CCF */ StdInstr({MicroOp::CCF}),
|
||||
/* 0x3f CCF */ Sequence({MicroOp::CCF}),
|
||||
|
||||
/* 0x40 LD B, B; 0x41 LD B, C; 0x42 LD B, D; 0x43 LD B, E; 0x44 LD B, H; 0x45 LD B, L; 0x46 LD B, (HL); 0x47 LD B, A */
|
||||
LD_GROUP(bc_.halves.high, bc_.halves.high),
|
||||
@@ -436,14 +412,14 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
/* 0x68 LD L, B; 0x69 LD L, C; 0x6a LD L, D; 0x6b LD L, E; 0x6c LD L, H; 0x6d LD H, L; 0x6e LD L, (HL); 0x6f LD L, A */
|
||||
LD_GROUP(index.halves.low, hl_.halves.low),
|
||||
|
||||
/* 0x70 LD (HL), B */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.high)),
|
||||
/* 0x71 LD (HL), C */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.low)),
|
||||
/* 0x72 LD (HL), D */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.high)),
|
||||
/* 0x73 LD (HL), E */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.low)),
|
||||
/* 0x74 LD (HL), H */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.halves.high)), // neither of these stores parts of the index register;
|
||||
/* 0x75 LD (HL), L */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.halves.low)), // they always store exactly H and L.
|
||||
/* 0x76 HALT */ StdInstr({MicroOp::HALT}),
|
||||
/* 0x77 LD (HL), A */ StdInstr(INDEX(), Write3(INDEX_ADDR(), a_)),
|
||||
/* 0x70 LD (HL), B */ Sequence(INDEX(), Write(INDEX_ADDR(), bc_.halves.high)),
|
||||
/* 0x71 LD (HL), C */ Sequence(INDEX(), Write(INDEX_ADDR(), bc_.halves.low)),
|
||||
/* 0x72 LD (HL), D */ Sequence(INDEX(), Write(INDEX_ADDR(), de_.halves.high)),
|
||||
/* 0x73 LD (HL), E */ Sequence(INDEX(), Write(INDEX_ADDR(), de_.halves.low)),
|
||||
/* 0x74 LD (HL), H */ Sequence(INDEX(), Write(INDEX_ADDR(), hl_.halves.high)), // neither of these stores parts of the index register;
|
||||
/* 0x75 LD (HL), L */ Sequence(INDEX(), Write(INDEX_ADDR(), hl_.halves.low)), // they always store exactly H and L.
|
||||
/* 0x76 HALT */ Sequence({MicroOp::HALT}),
|
||||
/* 0x77 LD (HL), A */ Sequence(INDEX(), Write(INDEX_ADDR(), a_)),
|
||||
|
||||
/* 0x78 LD A, B; 0x79 LD A, C; 0x7a LD A, D; 0x7b LD A, E; 0x7c LD A, H; 0x7d LD A, L; 0x7e LD A, (HL); 0x7f LD A, A */
|
||||
LD_GROUP(a_, a_),
|
||||
@@ -472,45 +448,45 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
/* 0xb8 CP B; 0xb9 CP C; 0xba CP D; 0xbb CP E; 0xbc CP H; 0xbd CP L; 0xbe CP (HL); 0xbf CP A */
|
||||
READ_OP_GROUP(CP8),
|
||||
|
||||
/* 0xc0 RET NZ */ RET(TestNZ), /* 0xc1 POP BC */ StdInstr(Pop(bc_)),
|
||||
/* 0xc2 JP NZ */ JP(TestNZ), /* 0xc3 JP nn */ StdInstr(Read16(pc_, memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xc4 CALL NZ */ CALL(TestNZ), /* 0xc5 PUSH BC */ Instr(6, Push(bc_)),
|
||||
/* 0xc6 ADD A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}),
|
||||
/* 0xc0 RET NZ */ RET(TestNZ), /* 0xc1 POP BC */ Sequence(Pop(bc_)),
|
||||
/* 0xc2 JP NZ */ JP(TestNZ), /* 0xc3 JP nn */ Sequence(Read16(pc_, memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xc4 CALL NZ */ CALL(TestNZ), /* 0xc5 PUSH BC */ Sequence(InternalOperation(2), Push(bc_)),
|
||||
/* 0xc6 ADD A, n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}),
|
||||
/* 0xc7 RST 00h */ RST(),
|
||||
/* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xca JP Z */ JP(TestZ), /* 0xcb [CB page] */StdInstr(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}),
|
||||
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, memptr_.halves.low), Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xce ADC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}),
|
||||
/* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ Sequence(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xca JP Z */ JP(TestZ), /* 0xcb [CB page] */Sequence(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}),
|
||||
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ Sequence(ReadInc(pc_, memptr_.halves.low), ReadInc(pc_, memptr_.halves.high), InternalOperation(2), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xce ADC A, n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}),
|
||||
/* 0xcf RST 08h */ RST(),
|
||||
/* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ StdInstr(Pop(de_)),
|
||||
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Output(memptr_, a_), Inc8NoFlags(memptr_.halves.low)),
|
||||
/* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Instr(6, Push(de_)),
|
||||
/* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}),
|
||||
/* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ Sequence(Pop(de_)),
|
||||
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */Sequence(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Output(memptr_, a_), Inc8NoFlags(memptr_.halves.low)),
|
||||
/* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Sequence(InternalOperation(2), Push(de_)),
|
||||
/* 0xd6 SUB n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}),
|
||||
/* 0xd7 RST 10h */ RST(),
|
||||
/* 0xd8 RET C */ RET(TestC), /* 0xd9 EXX */ StdInstr({MicroOp::EXX}),
|
||||
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Input(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0xdc CALL C */ CALL(TestC), /* 0xdd [DD page] */StdInstr({MicroOp::SetInstructionPage, &dd_page_}),
|
||||
/* 0xde SBC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}),
|
||||
/* 0xd8 RET C */ RET(TestC), /* 0xd9 EXX */ Sequence({MicroOp::EXX}),
|
||||
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */Sequence(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Input(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0xdc CALL C */ CALL(TestC), /* 0xdd [DD page] */Sequence({MicroOp::SetInstructionPage, &dd_page_}),
|
||||
/* 0xde SBC A, n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}),
|
||||
/* 0xdf RST 18h */ RST(),
|
||||
/* 0xe0 RET PO */ RET(TestPO), /* 0xe1 POP HL */ StdInstr(Pop(index)),
|
||||
/* 0xe2 JP PO */ JP(TestPO), /* 0xe3 EX (SP), HL */StdInstr(Pop7(memptr_), Push8(index), {MicroOp::Move16, &memptr_.full, &index.full}),
|
||||
/* 0xe4 CALL PO */ CALL(TestPO), /* 0xe5 PUSH HL */ Instr(6, Push(index)),
|
||||
/* 0xe6 AND n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::And, &temp8_}),
|
||||
/* 0xe0 RET PO */ RET(TestPO), /* 0xe1 POP HL */ Sequence(Pop(index)),
|
||||
/* 0xe2 JP PO */ JP(TestPO), /* 0xe3 EX (SP), HL */Sequence(Pop(memptr_), InternalOperation(2), Push(index), InternalOperation(4), {MicroOp::Move16, &memptr_.full, &index.full}),
|
||||
/* 0xe4 CALL PO */ CALL(TestPO), /* 0xe5 PUSH HL */ Sequence(InternalOperation(2), Push(index)),
|
||||
/* 0xe6 AND n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::And, &temp8_}),
|
||||
/* 0xe7 RST 20h */ RST(),
|
||||
/* 0xe8 RET PE */ RET(TestPE), /* 0xe9 JP (HL) */ StdInstr({MicroOp::Move16, &index.full, &pc_.full}),
|
||||
/* 0xea JP PE */ JP(TestPE), /* 0xeb EX DE, HL */StdInstr({MicroOp::ExDEHL}),
|
||||
/* 0xec CALL PE */ CALL(TestPE), /* 0xed [ED page] */StdInstr({MicroOp::SetInstructionPage, &ed_page_}),
|
||||
/* 0xee XOR n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::Xor, &temp8_}),
|
||||
/* 0xe8 RET PE */ RET(TestPE), /* 0xe9 JP (HL) */ Sequence({MicroOp::Move16, &index.full, &pc_.full}),
|
||||
/* 0xea JP PE */ JP(TestPE), /* 0xeb EX DE, HL */Sequence({MicroOp::ExDEHL}),
|
||||
/* 0xec CALL PE */ CALL(TestPE), /* 0xed [ED page] */Sequence({MicroOp::SetInstructionPage, &ed_page_}),
|
||||
/* 0xee XOR n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::Xor, &temp8_}),
|
||||
/* 0xef RST 28h */ RST(),
|
||||
/* 0xf0 RET p */ RET(TestP), /* 0xf1 POP AF */ StdInstr(Pop(temp16_), {MicroOp::DisassembleAF}),
|
||||
/* 0xf2 JP P */ JP(TestP), /* 0xf3 DI */ StdInstr({MicroOp::DI}),
|
||||
/* 0xf4 CALL P */ CALL(TestP), /* 0xf5 PUSH AF */ Instr(6, {MicroOp::AssembleAF}, Push(temp16_)),
|
||||
/* 0xf6 OR n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::Or, &temp8_}),
|
||||
/* 0xf0 RET p */ RET(TestP), /* 0xf1 POP AF */ Sequence(Pop(temp16_), {MicroOp::DisassembleAF}),
|
||||
/* 0xf2 JP P */ JP(TestP), /* 0xf3 DI */ Sequence({MicroOp::DI}),
|
||||
/* 0xf4 CALL P */ CALL(TestP), /* 0xf5 PUSH AF */ Sequence(InternalOperation(2), {MicroOp::AssembleAF}, Push(temp16_)),
|
||||
/* 0xf6 OR n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::Or, &temp8_}),
|
||||
/* 0xf7 RST 30h */ RST(),
|
||||
/* 0xf8 RET M */ RET(TestM), /* 0xf9 LD SP, HL */Instr(8, {MicroOp::Move16, &index.full, &sp_.full}),
|
||||
/* 0xfa JP M */ JP(TestM), /* 0xfb EI */ StdInstr({MicroOp::EI}),
|
||||
/* 0xfc CALL M */ CALL(TestM), /* 0xfd [FD page] */StdInstr({MicroOp::SetInstructionPage, &fd_page_}),
|
||||
/* 0xfe CP n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::CP8, &temp8_}),
|
||||
/* 0xf8 RET M */ RET(TestM), /* 0xf9 LD SP, HL */Sequence(InternalOperation(4), {MicroOp::Move16, &index.full, &sp_.full}),
|
||||
/* 0xfa JP M */ JP(TestM), /* 0xfb EI */ Sequence({MicroOp::EI}),
|
||||
/* 0xfc CALL M */ CALL(TestM), /* 0xfd [FD page] */Sequence({MicroOp::SetInstructionPage, &fd_page_}),
|
||||
/* 0xfe CP n */ Sequence(ReadInc(pc_, temp8_), {MicroOp::CP8, &temp8_}),
|
||||
/* 0xff RST 38h */ RST(),
|
||||
};
|
||||
|
||||
@@ -518,7 +494,7 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
// The indexed version of 0x36 differs substantially from the non-indexed by building index calculation into
|
||||
// the cycle that fetches the final operand. So patch in a different microprogram if building an indexed table.
|
||||
InstructionTable copy_table = {
|
||||
StdInstr(FINDEX(), Read5Inc(pc_, temp8_), Write3(INDEX_ADDR(), temp8_))
|
||||
Sequence(FINDEX(), ReadInc(pc_, temp8_), InternalOperation(4), Write(INDEX_ADDR(), temp8_))
|
||||
};
|
||||
std::memcpy(&base_program_table[0x36], ©_table[0], sizeof(copy_table[0]));
|
||||
}
|
||||
@@ -528,19 +504,27 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
}
|
||||
|
||||
void ProcessorStorage::assemble_fetch_decode_execute(InstructionPage &target, int length) {
|
||||
/// The fetch-decode-execute sequence for a regular four-clock M1 cycle.
|
||||
const MicroOp normal_fetch_decode_execute[] = {
|
||||
BusOp(ReadOpcodeStart()),
|
||||
BusOp(ReadOpcodeWait(true)),
|
||||
BusOp(ReadOpcodeEnd()),
|
||||
{ MicroOp::IncrementR },
|
||||
BusOp(Refresh()),
|
||||
{ MicroOp::DecodeOperation }
|
||||
};
|
||||
|
||||
/// The concluding fetch-decode-execute of a [dd/fd]cb nn oo sequence, i.e. an (IX+n) or (IY+n) operation.
|
||||
/// Per the observed 48kb/128kb Spectrum timings, this appears not to include a refresh cycle. So I've also
|
||||
/// taken a punt on it not incrementing R.
|
||||
const MicroOp short_fetch_decode_execute[] = {
|
||||
BusOp(ReadOpcodeStart()),
|
||||
BusOp(ReadOpcodeWait(false)),
|
||||
BusOp(ReadOpcodeWait(true)),
|
||||
BusOp(ReadOpcodeEnd()),
|
||||
{ MicroOp::DecodeOperation }
|
||||
BusOp(ReadStart(pc_, operation_)),
|
||||
BusOp(ReadWait(pc_, operation_)),
|
||||
BusOp(ReadEnd(pc_, operation_)),
|
||||
InternalOperation(4),
|
||||
{ MicroOp::DecodeOperation },
|
||||
};
|
||||
|
||||
copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute);
|
||||
target.fetch_decode_execute_data = target.fetch_decode_execute.data();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ class ProcessorStorage {
|
||||
struct MicroOp {
|
||||
enum Type {
|
||||
BusOperation,
|
||||
IncrementR,
|
||||
DecodeOperation,
|
||||
DecodeOperationNoRChange,
|
||||
MoveToNextProgram,
|
||||
|
||||
Increment8NoFlags,
|
||||
@@ -121,7 +121,6 @@ class ProcessorStorage {
|
||||
std::vector<MicroOp> all_operations;
|
||||
std::vector<MicroOp> fetch_decode_execute;
|
||||
MicroOp *fetch_decode_execute_data = nullptr;
|
||||
uint8_t r_step = 1;
|
||||
bool is_indexed = false;
|
||||
};
|
||||
|
||||
@@ -130,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;
|
||||
@@ -149,6 +148,8 @@ class ProcessorStorage {
|
||||
// that knowledge of what the last opcode did is necessary to get bits 5 & 3
|
||||
// correct for SCF and CCF.
|
||||
|
||||
uint16_t last_address_bus_ = 0; // The value most recently put out on the address bus.
|
||||
|
||||
HalfCycles number_of_cycles_;
|
||||
|
||||
enum Interrupt: uint8_t {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -68,29 +68,50 @@ enum Flag: uint8_t {
|
||||
*/
|
||||
struct PartialMachineCycle {
|
||||
enum Operation {
|
||||
/// The final half cycle of the opcode fetch part of an M1 cycle.
|
||||
ReadOpcode = 0,
|
||||
/// The 1.5 cycles of a read cycle.
|
||||
Read,
|
||||
/// The 1.5 cycles of a write cycle.
|
||||
Write,
|
||||
/// The 1.5 cycles of an input cycle.
|
||||
Input,
|
||||
/// The 1.5 cycles of an output cycle.
|
||||
Output,
|
||||
/// The 1.5 cycles of an interrupt acknowledgment.
|
||||
Interrupt,
|
||||
|
||||
/// The two-cycle refresh part of an M1 cycle.
|
||||
Refresh,
|
||||
/// A period with no changes in bus signalling.
|
||||
Internal,
|
||||
/// A bus acknowledgement cycle.
|
||||
BusAcknowledge,
|
||||
|
||||
/// A wait state within an M1 cycle.
|
||||
ReadOpcodeWait,
|
||||
/// A wait state within a read cycle.
|
||||
ReadWait,
|
||||
/// A wait state within a write cycle.
|
||||
WriteWait,
|
||||
/// A wait state within an input cycle.
|
||||
InputWait,
|
||||
/// A wait state within an output cycle.
|
||||
OutputWait,
|
||||
/// A wait state within an interrupt acknowledge cycle.
|
||||
InterruptWait,
|
||||
|
||||
/// The first 1.5 cycles of an M1 bus cycle, up to the sampling of WAIT.
|
||||
ReadOpcodeStart,
|
||||
/// The first 1.5 cycles of a read cycle, up to the sampling of WAIT.
|
||||
ReadStart,
|
||||
/// The first 1.5 cycles of a write cycle, up to the sampling of WAIT.
|
||||
WriteStart,
|
||||
/// The first 1.5 samples of an input bus cycle, up to the sampling of WAIT.
|
||||
InputStart,
|
||||
/// The first 1.5 samples of an output bus cycle, up to the sampling of WAIT.
|
||||
OutputStart,
|
||||
/// The first portion of an interrupt acknowledgement — 2.5 or 3.5 cycles, depending on interrupt mode.
|
||||
InterruptStart,
|
||||
};
|
||||
/// The operation being carried out by the Z80. See the various getters below for better classification.
|
||||
@@ -125,6 +146,230 @@ struct PartialMachineCycle {
|
||||
return operation >= Operation::ReadOpcodeWait && operation <= Operation::InterruptWait;
|
||||
}
|
||||
|
||||
enum Line {
|
||||
CLK = 1 << 0,
|
||||
|
||||
MREQ = 1 << 1,
|
||||
IOREQ = 1 << 2,
|
||||
|
||||
RD = 1 << 3,
|
||||
WR = 1 << 4,
|
||||
RFSH = 1 << 5,
|
||||
|
||||
M1 = 1 << 6,
|
||||
|
||||
BUSACK = 1 << 7,
|
||||
};
|
||||
|
||||
/// @returns A C-style array of the bus state at the beginning of each half cycle in this
|
||||
/// partial machine cycle. Each element is a combination of bit masks from the Line enum;
|
||||
/// bit set means line active, bit clear means line inactive. For the CLK line set means high.
|
||||
///
|
||||
/// @discussion This discrete sampling is prone to aliasing errors. Beware.
|
||||
const uint8_t *bus_state() const {
|
||||
switch(operation) {
|
||||
|
||||
//
|
||||
// M1 cycle
|
||||
//
|
||||
|
||||
case Operation::ReadOpcodeStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK | Line::M1,
|
||||
Line::M1 | Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::ReadOpcode:
|
||||
case Operation::ReadOpcodeWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::M1 | Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Refresh: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK | Line::RFSH | Line::MREQ,
|
||||
Line::RFSH,
|
||||
Line::CLK | Line::RFSH | Line::MREQ,
|
||||
Line::RFSH | Line::MREQ,
|
||||
Line::CLK | Line::RFSH,
|
||||
Line::RFSH,
|
||||
Line::CLK | Line::RFSH,
|
||||
Line::RFSH,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// Read cycle.
|
||||
//
|
||||
|
||||
case Operation::ReadStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK,
|
||||
Line::RD | Line::MREQ,
|
||||
Line::CLK | Line::RD | Line::MREQ,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::ReadWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Read: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
0,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// Write cycle.
|
||||
//
|
||||
|
||||
case Operation::WriteStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK,
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::WriteWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Write: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::MREQ | Line::WR,
|
||||
Line::CLK | Line::MREQ | Line::WR,
|
||||
0,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// Input cycle.
|
||||
//
|
||||
|
||||
case Operation::InputStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK,
|
||||
0,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::InputWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::IOREQ | Line::RD,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Input: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::IOREQ | Line::RD,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
0,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// Output cycle.
|
||||
//
|
||||
|
||||
case Operation::OutputStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK,
|
||||
0,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::OutputWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::IOREQ | Line::WR,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Output: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::IOREQ | Line::WR,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
0,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Interrupt acknowledge.
|
||||
//
|
||||
|
||||
//
|
||||
// Bus acknowldge.
|
||||
//
|
||||
|
||||
case Operation::BusAcknowledge: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK | Line::BUSACK,
|
||||
Line::BUSACK,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
//
|
||||
// Internal.
|
||||
//
|
||||
|
||||
case Operation::Internal: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PartialMachineCycle(const PartialMachineCycle &rhs) noexcept;
|
||||
PartialMachineCycle(Operation operation, HalfCycles length, uint16_t *address, uint8_t *value, bool was_requested) noexcept;
|
||||
PartialMachineCycle() noexcept;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
BIN
ROMImages/ZXSpectrum/128.rom
Normal file
BIN
ROMImages/ZXSpectrum/128.rom
Normal file
Binary file not shown.
BIN
ROMImages/ZXSpectrum/48.rom
Normal file
BIN
ROMImages/ZXSpectrum/48.rom
Normal file
Binary file not shown.
BIN
ROMImages/ZXSpectrum/plus2.rom
Normal file
BIN
ROMImages/ZXSpectrum/plus2.rom
Normal file
Binary file not shown.
@@ -9,3 +9,6 @@ material but retain that copyright"."
|
||||
With that in mind, Amstrad have kindly given their permission for the redistribution of their copyrighted material but retain that copyright. Material expected here, copyright Amstrad:
|
||||
|
||||
plus3.rom — the +2a/+3 ROM file, 64kb in size.
|
||||
plus2.rom – the +2 ROM file, 32kb in size.
|
||||
128.rom – the 128kb ROM file, 32kb in size.
|
||||
48.rom – the 16/48kb ROM file, 16kb in size.
|
||||
@@ -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);
|
||||
|
||||
@@ -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
79
Storage/State/SNA.cpp
Normal 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
24
Storage/State/SNA.hpp
Normal 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
196
Storage/State/SZX.cpp
Normal 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
24
Storage/State/SZX.hpp
Normal 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
209
Storage/State/Z80.cpp
Normal 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 4–5 => video synchronisation (to do with emulation hackery?)
|
||||
// bit 6–7 => 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
24
Storage/State/Z80.hpp
Normal 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 */
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user