mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-31 05:16:08 +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/DAT.hpp" | ||||||
| #include "../../Storage/MassStorage/Formats/HFV.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 | // Tapes | ||||||
| #include "../../Storage/Tape/Formats/CAS.hpp" | #include "../../Storage/Tape/Formats/CAS.hpp" | ||||||
| #include "../../Storage/Tape/Formats/CommodoreTAP.hpp" | #include "../../Storage/Tape/Formats/CommodoreTAP.hpp" | ||||||
| @@ -73,15 +78,23 @@ | |||||||
|  |  | ||||||
| using namespace Analyser::Static; | using namespace Analyser::Static; | ||||||
|  |  | ||||||
| static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) { | namespace { | ||||||
| 	Media result; |  | ||||||
|  |  | ||||||
|  | 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 | 	// 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. | 	// test as to file format. | ||||||
| 	std::string::size_type final_dot = file_name.find_last_of("."); | 	std::string::size_type final_dot = name.find_last_of("."); | ||||||
| 	if(final_dot == std::string::npos) return result; | 	if(final_dot == std::string::npos) return name; | ||||||
| 	std::string extension = file_name.substr(final_dot + 1); | 	std::string extension = name.substr(final_dot + 1); | ||||||
| 	std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); | 	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) \ | #define InsertInstance(list, instance, platforms) \ | ||||||
| 	list.emplace_back(instance);\ | 	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 Analyser::Static::GetTargets(const std::string &file_name) { | ||||||
| 	TargetList targets; | 	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 | 	// 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. | 	// union of all platforms this file might be a target for. | ||||||
| 	TargetPlatform::IntType potential_platforms = 0; | 	TargetPlatform::IntType potential_platforms = 0; | ||||||
| 	Media media = GetMediaAndPlatforms(file_name, potential_platforms); | 	Media media = GetMediaAndPlatforms(file_name, potential_platforms); | ||||||
|  |  | ||||||
| 	// Hand off to platform-specific determination of whether these things are actually compatible and, | 	// Hand off to platform-specific determination of whether these | ||||||
| 	// if so, how to load them. | 	// things are actually compatible and, if so, how to load them. | ||||||
| #define Append(x) if(potential_platforms & TargetPlatform::x) {\ | #define Append(x) if(potential_platforms & TargetPlatform::x) {\ | ||||||
| 	auto new_targets = x::GetTargets(media, file_name, potential_platforms);\ | 	auto new_targets = x::GetTargets(media, file_name, potential_platforms);\ | ||||||
| 	std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\ | 	std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\ | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include "../../Storage/Disk/Disk.hpp" | #include "../../Storage/Disk/Disk.hpp" | ||||||
| #include "../../Storage/MassStorage/MassStorageDevice.hpp" | #include "../../Storage/MassStorage/MassStorageDevice.hpp" | ||||||
| #include "../../Storage/Tape/Tape.hpp" | #include "../../Storage/Tape/Tape.hpp" | ||||||
|  | #include "../../Reflection/Struct.hpp" | ||||||
|  |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| @@ -23,8 +24,10 @@ | |||||||
| namespace Analyser { | namespace Analyser { | ||||||
| namespace Static { | 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 { | struct Media { | ||||||
| 	std::vector<std::shared_ptr<Storage::Disk::Disk>> disks; | 	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, | 	Describes a machine and possibly its state; conventionally subclassed to add other machine-specific configuration fields and any | ||||||
| 	and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness. | 	necessary instructions on how to launch any software provided, plus a measure of confidence in this target's correctness. | ||||||
| */ | */ | ||||||
| struct Target { | struct Target { | ||||||
| 	Target(Machine machine) : machine(machine) {} | 	Target(Machine machine) : machine(machine) {} | ||||||
| 	virtual ~Target() {} | 	virtual ~Target() {} | ||||||
|  |  | ||||||
|  | 	// This field is entirely optional. | ||||||
|  | 	std::unique_ptr<Reflection::Struct> state; | ||||||
|  |  | ||||||
| 	Machine machine; | 	Machine machine; | ||||||
| 	Media media; | 	Media media; | ||||||
| 	float confidence = 0.0f; | 	float confidence = 0.0f; | ||||||
|   | |||||||
| @@ -19,11 +19,15 @@ namespace ZXSpectrum { | |||||||
|  |  | ||||||
| struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> { | struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> { | ||||||
| 	ReflectableEnum(Model, | 	ReflectableEnum(Model, | ||||||
|  | 		SixteenK, | ||||||
|  | 		FortyEightK, | ||||||
|  | 		OneTwoEightK, | ||||||
|  | 		Plus2, | ||||||
| 		Plus2a, | 		Plus2a, | ||||||
| 		Plus3, | 		Plus3, | ||||||
| 	); | 	); | ||||||
|  |  | ||||||
| 	Model model = Model::Plus2a; | 	Model model = Model::Plus2; | ||||||
| 	bool should_hold_enter = false; | 	bool should_hold_enter = false; | ||||||
|  |  | ||||||
| 	Target(): Analyser::Static::Target(Machine::ZXSpectrum) { | 	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. | 	// because it's unclear when the interrupt might come. | ||||||
| 	if(bits_incoming_ && receive_interrupt_enabled_) return ClockingHint::Preference::RealTime; | 	if(bits_incoming_ && receive_interrupt_enabled_) return ClockingHint::Preference::RealTime; | ||||||
|  |  | ||||||
| 	// No clocking required then. | 	// Real-time clocking not required then. | ||||||
| 	return ClockingHint::Preference::None; | 	return ClockingHint::Preference::JustInTime; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ACIA::get_interrupt_line() const { | bool ACIA::get_interrupt_line() const { | ||||||
|   | |||||||
| @@ -208,7 +208,7 @@ void MFP68901::run_for(HalfCycles time) { | |||||||
| } | } | ||||||
|  |  | ||||||
| HalfCycles MFP68901::get_next_sequence_point() { | HalfCycles MFP68901::get_next_sequence_point() { | ||||||
| 	return HalfCycles(-1); | 	return HalfCycles::max(); | ||||||
| } | } | ||||||
|  |  | ||||||
| // MARK: - Timers | // MARK: - Timers | ||||||
|   | |||||||
| @@ -12,6 +12,8 @@ | |||||||
| #include "../../Outputs/Speaker/Implementation/SampleSource.hpp" | #include "../../Outputs/Speaker/Implementation/SampleSource.hpp" | ||||||
| #include "../../Concurrency/AsyncTaskQueue.hpp" | #include "../../Concurrency/AsyncTaskQueue.hpp" | ||||||
|  |  | ||||||
|  | #include "../../Reflection/Struct.hpp" | ||||||
|  |  | ||||||
| namespace GI { | namespace GI { | ||||||
| namespace AY38910 { | 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 a_left_ = 255, a_right_ = 255; | ||||||
| 		uint8_t b_left_ = 255, b_right_ = 255; | 		uint8_t b_left_ = 255, b_right_ = 255; | ||||||
| 		uint8_t c_left_ = 255, c_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 SHIFT | ||||||
| #undef X | #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 { | bool CharacterMapper::needs_pause_after_key(uint16_t key) const { | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ void Keyboard::perform_command(const Command &command) { | |||||||
| 	switch(command.type) { | 	switch(command.type) { | ||||||
| 		case Command::Type::Reset: | 		case Command::Type::Reset: | ||||||
| 			modifiers_ = 0xffff; | 			modifiers_ = 0xffff; | ||||||
|  | 			[[fallthrough]]; | ||||||
| 		case Command::Type::Flush: { | 		case Command::Type::Flush: { | ||||||
| 			std::lock_guard lock_guard(keys_mutex_); | 			std::lock_guard lock_guard(keys_mutex_); | ||||||
| 			pending_events_.clear(); | 			pending_events_.clear(); | ||||||
|   | |||||||
| @@ -48,7 +48,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 	public MachineTypes::MappedKeyboardMachine, | 	public MachineTypes::MappedKeyboardMachine, | ||||||
| 	public MachineTypes::JoystickMachine, | 	public MachineTypes::JoystickMachine, | ||||||
| 	public CPU::MOS6502::BusHandler, | 	public CPU::MOS6502::BusHandler, | ||||||
| 	public Inputs::Keyboard, |  | ||||||
| 	public Configurable::Device, | 	public Configurable::Device, | ||||||
| 	public Activity::Source, | 	public Activity::Source, | ||||||
| 	public Apple::II::Card::Delegate { | 	public Apple::II::Card::Delegate { | ||||||
| @@ -66,7 +65,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 				uint8_t *ram_, *aux_ram_; | 				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_; | 		VideoBusHandler video_bus_handler_; | ||||||
| 		Apple::II::Video::Video<VideoBusHandler, is_iie()> video_; | 		Apple::II::Video::Video<VideoBusHandler, is_iie()> video_; | ||||||
| 		int cycles_into_current_line_ = 0; | 		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]; | 		uint8_t ram_[65536], aux_ram_[65536]; | ||||||
| 		std::vector<uint8_t> rom_; | 		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_; | 		Concurrency::DeferringAsyncTaskQueue audio_queue_; | ||||||
| 		Audio::Toggle audio_toggle_; | 		Audio::Toggle audio_toggle_; | ||||||
| @@ -258,15 +251,98 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 				state.region_20_40.write ? &aux_ram_[0x2000] : &ram_[0x2000]); | 				state.region_20_40.write ? &aux_ram_[0x2000] : &ram_[0x2000]); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// MARK - typing | 		// MARK: - Keyboard and typing. | ||||||
| 		std::unique_ptr<Utility::StringSerialiser> string_serialiser_; |  | ||||||
|  |  | ||||||
| 		// MARK - Joysticks. | 		struct Keyboard: public Inputs::Keyboard { | ||||||
| 		JoystickPair joysticks_; | 			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. | 			// The IIe has three keys that are wired directly to the same input as the joystick buttons. | ||||||
| 		bool open_apple_is_pressed_ = false; | 			bool open_apple_is_pressed = false; | ||||||
| 		bool closed_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_; | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): | 		ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): | ||||||
| @@ -276,7 +352,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 			audio_toggle_(audio_queue_), | 			audio_toggle_(audio_queue_), | ||||||
| 			speaker_(audio_toggle_), | 			speaker_(audio_toggle_), | ||||||
| 			language_card_(*this), | 			language_card_(*this), | ||||||
| 			auxiliary_switches_(*this) { | 			auxiliary_switches_(*this), | ||||||
|  | 			keyboard_(&m6502_) { | ||||||
| 			// The system's master clock rate. | 			// The system's master clock rate. | ||||||
| 			constexpr float master_clock = 14318180.0; | 			constexpr float master_clock = 14318180.0; | ||||||
|  |  | ||||||
| @@ -435,18 +512,18 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 								default: break; | 								default: break; | ||||||
|  |  | ||||||
| 								case 0xc000: | 								case 0xc000: | ||||||
| 									*value = get_keyboard_input(); | 									*value = keyboard_.get_keyboard_input(); | ||||||
| 								break; | 								break; | ||||||
| 								case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005: case 0xc006: case 0xc007: | 								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: | 								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; | 								break; | ||||||
|  |  | ||||||
| 								case 0xc061:	// Switch input 0. | 								case 0xc061:	// Switch input 0. | ||||||
| 									*value &= 0x7f; | 									*value &= 0x7f; | ||||||
| 									if( | 									if( | ||||||
| 										joysticks_.button(0) || | 										joysticks_.button(0) || | ||||||
| 										(is_iie() && open_apple_is_pressed_) | 										(is_iie() && keyboard_.open_apple_is_pressed) | ||||||
| 									) | 									) | ||||||
| 										*value |= 0x80; | 										*value |= 0x80; | ||||||
| 								break; | 								break; | ||||||
| @@ -454,7 +531,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 									*value &= 0x7f; | 									*value &= 0x7f; | ||||||
| 									if( | 									if( | ||||||
| 										joysticks_.button(1) || | 										joysticks_.button(1) || | ||||||
| 										(is_iie() && closed_apple_is_pressed_) | 										(is_iie() && keyboard_.closed_apple_is_pressed) | ||||||
| 									) | 									) | ||||||
| 										*value |= 0x80; | 										*value |= 0x80; | ||||||
| 								break; | 								break; | ||||||
| @@ -476,7 +553,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 								} break; | 								} break; | ||||||
|  |  | ||||||
| 								// The IIe-only state reads follow... | 								// 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 0xc011:	IIeSwitchRead(language_card_.state().bank2);								break; | ||||||
| 								case 0xc012:	IIeSwitchRead(language_card_.state().read);									break; | 								case 0xc012:	IIeSwitchRead(language_card_.state().read);									break; | ||||||
| 								case 0xc013:	IIeSwitchRead(auxiliary_switches_.switches().read_auxiliary_memory);		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; | 					break; | ||||||
|  |  | ||||||
| 					case 0xc010: | 					case 0xc010: | ||||||
| 						keyboard_input_ &= 0x7f; | 						keyboard_.keyboard_input &= 0x7f; | ||||||
| 						if(string_serialiser_) { | 						if(keyboard_.string_serialiser) { | ||||||
| 							if(!string_serialiser_->advance()) | 							if(!keyboard_.string_serialiser->advance()) | ||||||
| 								string_serialiser_.reset(); | 								keyboard_.string_serialiser.reset(); | ||||||
| 						} | 						} | ||||||
|  |  | ||||||
| 						// On the IIe, reading C010 returns additional key info. | 						// On the IIe, reading C010 returns additional key info. | ||||||
| 						if(is_iie() && isReadOperation(operation)) { | 						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; | 					break; | ||||||
|  |  | ||||||
| @@ -683,81 +760,16 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: | |||||||
| 			m6502_.run_for(cycles); | 			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 { | 		bool prefers_logical_input() final { | ||||||
| 			return true; | 			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 { | 		Inputs::Keyboard &get_keyboard() final { | ||||||
| 			return *this; | 			return keyboard_; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		void type_string(const std::string &string) final { | 		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 { | 		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 { | 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) { | 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 SHIFT | ||||||
| #undef X | #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 SHIFT | ||||||
| #undef X | #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 { | bool CharacterMapper::needs_pause_after_key(uint16_t key) const { | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ | |||||||
| #include "MediaTarget.hpp" | #include "MediaTarget.hpp" | ||||||
| #include "MouseMachine.hpp" | #include "MouseMachine.hpp" | ||||||
| #include "ScanProducer.hpp" | #include "ScanProducer.hpp" | ||||||
|  | #include "StateProducer.hpp" | ||||||
| #include "TimedMachine.hpp" | #include "TimedMachine.hpp" | ||||||
|  |  | ||||||
| #endif /* MachineTypes_h */ | #endif /* MachineTypes_h */ | ||||||
|   | |||||||
| @@ -127,5 +127,5 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { | |||||||
| #undef SHIFT | #undef SHIFT | ||||||
| #undef X | #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 <memory> | ||||||
| #include <vector> | #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 { | namespace Oric { | ||||||
|  |  | ||||||
| using DiskInterface = Analyser::Static::Oric::Target::DiskInterface; | using DiskInterface = Analyser::Static::Oric::Target::DiskInterface; | ||||||
| @@ -140,7 +179,12 @@ class TapePlayer: public Storage::Tape::BinaryTapePlayer { | |||||||
| class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { | class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { | ||||||
| 	public: | 	public: | ||||||
| 		VIAPortHandler(Concurrency::DeferringAsyncTaskQueue &audio_queue, AY &ay8910, Speaker &speaker, TapePlayer &tape_player, Keyboard &keyboard) : | 		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 | 			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 { | 			} else { | ||||||
| 				update_ay(); | 				update_ay(); | ||||||
| 				ay8910_.set_data_input(value); | 				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; | 				uint8_t column = ay8910_.get_port_output(false) ^ 0xff; | ||||||
| 				return keyboard_.query_column(column) ? 0x08 : 0x00; | 				return keyboard_.query_column(column) ? 0x08 : 0x00; | ||||||
| 			} else { | 			} 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(); | 			audio_queue_.perform(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() { | ||||||
|  | 			return joysticks_; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	private: | 	private: | ||||||
| 		void update_ay() { | 		void update_ay() { | ||||||
| 			speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush<Cycles>()); | 			speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush<Cycles>()); | ||||||
| 		} | 		} | ||||||
| 		bool ay_bdir_ = false; | 		bool ay_bdir_ = false; | ||||||
| 		bool ay_bc1_ = false; | 		bool ay_bc1_ = false; | ||||||
|  | 		uint8_t porta_output_ = 0xff; | ||||||
| 		HalfCycles cycles_since_ay_update_; | 		HalfCycles cycles_since_ay_update_; | ||||||
|  |  | ||||||
| 		Concurrency::DeferringAsyncTaskQueue &audio_queue_; | 		Concurrency::DeferringAsyncTaskQueue &audio_queue_; | ||||||
| @@ -206,12 +259,15 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { | |||||||
| 		Speaker &speaker_; | 		Speaker &speaker_; | ||||||
| 		TapePlayer &tape_player_; | 		TapePlayer &tape_player_; | ||||||
| 		Keyboard &keyboard_; | 		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: | template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS6502Esque::Type processor_type> class ConcreteMachine: | ||||||
| 	public MachineTypes::TimedMachine, | 	public MachineTypes::TimedMachine, | ||||||
| 	public MachineTypes::ScanProducer, | 	public MachineTypes::ScanProducer, | ||||||
| 	public MachineTypes::AudioProducer, | 	public MachineTypes::AudioProducer, | ||||||
|  | 	public MachineTypes::JoystickMachine, | ||||||
| 	public MachineTypes::MediaTarget, | 	public MachineTypes::MediaTarget, | ||||||
| 	public MachineTypes::MappedKeyboardMachine, | 	public MachineTypes::MappedKeyboardMachine, | ||||||
| 	public Configurable::Device, | 	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_; | 		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_) { | 	switch(machine_) { | ||||||
| 		case Machine::ZX80: | 		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: | 		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: | 		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> { | 		class Options: public Reflection::StructImpl<Options>, public Configurable::QuickloadOption<Options> { | ||||||
| 			friend Configurable::QuickloadOption<Options>; | 			friend Configurable::QuickloadOption<Options>; | ||||||
| 			public: | 			public: | ||||||
| 				bool automatic_tape_motor_control; | 				bool automatic_tape_motor_control = true; | ||||||
|  |  | ||||||
| 				Options(Configurable::OptionsType type): | 				Options(Configurable::OptionsType type): | ||||||
| 					Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly), | 					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 "../../../Outputs/CRT/CRT.hpp" | ||||||
| #include "../../../ClockReceiver/ClockReceiver.hpp" | #include "../../../ClockReceiver/ClockReceiver.hpp" | ||||||
|  |  | ||||||
|  | #include "../../../Reflection/Struct.hpp" | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
|  |  | ||||||
| namespace Sinclair { | namespace Sinclair { | ||||||
| namespace ZXSpectrum { | namespace ZXSpectrum { | ||||||
|  | namespace Video { | ||||||
|  |  | ||||||
| enum class VideoTiming { | enum class Timing { | ||||||
| 	Plus3 | 	FortyEightK, | ||||||
|  | 	OneTwoEightK, | ||||||
|  | 	Plus3, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -45,11 +50,11 @@ enum class VideoTiming { | |||||||
|  |  | ||||||
| */ | */ | ||||||
|  |  | ||||||
| template <VideoTiming timing> class Video { | template <Timing timing> class Video { | ||||||
| 	private: | 	private: | ||||||
| 		struct Timings { | 		struct Timings { | ||||||
| 			// Number of cycles per line. Will be 224 or 228. | 			// 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. | 			// Number of lines comprising a whole frame. Will be 311 or 312. | ||||||
| 			int lines_per_frame; | 			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. | 			// Number of cycles after first pixel fetch at which interrupt is first signalled. | ||||||
| 			int interrupt_time; | 			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. | 			// 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() { | 		static constexpr Timings get_timings() { | ||||||
| 			// Amstrad gate array timings, classic statement: | 			if constexpr (timing == Timing::Plus3) { | ||||||
| 			// | 				constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2}; | ||||||
| 			// Contention begins 14361 cycles "after interrupt" and follows the pattern [1, 0, 7, 6 5 4, 3, 2]. | 				return Timings(228, 311, 6, 129, 14361, delays); | ||||||
| 			// 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, |  | ||||||
|  |  | ||||||
| 				// 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, |  | ||||||
|  |  | ||||||
| 				// 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; |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 		// TODO: how long is the interrupt line held for? | 			if constexpr (timing == Timing::OneTwoEightK) { | ||||||
| 		static constexpr int interrupt_duration = 48; | 				constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0}; | ||||||
|  | 				return Timings(228, 311, 4, 128, 14361, delays); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if constexpr (timing == Timing::FortyEightK) { | ||||||
|  | 				constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0}; | ||||||
|  | 				return Timings(224, 312, 4, 128, 14335, delays); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Interrupt should be held for 32 cycles. | ||||||
|  | 		static constexpr int interrupt_duration = 64; | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		void run_for(HalfCycles duration) { | 		void run_for(HalfCycles duration) { | ||||||
| 			constexpr auto timings = get_timings(); | 			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 sync_length = 17 * 2; | ||||||
| 			constexpr int burst_position = sync_position + 40; | 			constexpr int burst_position = sync_position + 40; | ||||||
| 			constexpr int burst_length = 17; | 			constexpr int burst_length = 17; | ||||||
|  |  | ||||||
| 			int cycles_remaining = duration.as<int>(); | 			int cycles_remaining = duration.as<int>(); | ||||||
| 			while(cycles_remaining) { | 			while(cycles_remaining) { | ||||||
| 				int line = time_into_frame_ / timings.cycles_per_line; | 				int line = time_into_frame_ / timings.half_cycles_per_line; | ||||||
| 				int offset = time_into_frame_ % timings.cycles_per_line; | 				int offset = time_into_frame_ % timings.half_cycles_per_line; | ||||||
| 				const int cycles_this_line = std::min(cycles_remaining, timings.cycles_per_line - offset); | 				const int cycles_this_line = std::min(cycles_remaining, timings.half_cycles_per_line - offset); | ||||||
| 				const int end_offset = offset + cycles_this_line; | 				const int end_offset = offset + cycles_this_line; | ||||||
|  |  | ||||||
| 				if(!offset) { | 				if(!offset) { | ||||||
| @@ -236,9 +222,14 @@ template <VideoTiming timing> class Video { | |||||||
|  |  | ||||||
| 					if(offset >= burst_position && offset < burst_position+burst_length && end_offset > offset) { | 					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; | 						const int burst_duration = std::min(burst_position + burst_length, end_offset) - offset; | ||||||
|  |  | ||||||
|  | 						if constexpr (timing >= Timing::OneTwoEightK) { | ||||||
| 							crt_.output_colour_burst(burst_duration, 116, is_alternate_line_); | 							crt_.output_colour_burst(burst_duration, 116, is_alternate_line_); | ||||||
| 						offset += burst_duration; |  | ||||||
| 							// The colour burst phase above is an empirical guess. I need to research further. | 							// 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; | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					if(offset >= burst_position+burst_length && end_offset > offset) { | 					if(offset >= burst_position+burst_length && end_offset > offset) { | ||||||
| @@ -248,7 +239,7 @@ template <VideoTiming timing> class Video { | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				cycles_remaining -= cycles_this_line; | 				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); | 			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: | 	public: | ||||||
| 		Video() : | 		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. | 			// Show only the centre 80% of the TV frame. | ||||||
| 			crt_.set_display_type(Outputs::Display::DisplayType::RGB); | 			crt_.set_display_type(Outputs::Display::DisplayType::RGB); | ||||||
| 			crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f)); | 			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) { | 		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. | 			// 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 | 			@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. | 			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(); | 			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. | 			// 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; | 				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) { | 			if(time_into_line >= timings.contention_duration) { | ||||||
| 				return 0; | 				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 { | 		uint8_t get_floating_value() const { | ||||||
| 			constexpr auto timings = get_timings(); | 			constexpr auto timings = get_timings(); | ||||||
| 			const int line = time_into_frame_ / timings.cycles_per_line; | 			const uint8_t out_of_bounds = (timing == Timing::Plus3) ? last_contended_access_ : 0xff; | ||||||
| 			if(line >= 192) return 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)) { | 			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. | 			// The +2a and +3 always return the low bit as set. | ||||||
| 			if constexpr (timing == VideoTiming::Plus3) { | 			const uint8_t value = last_fetches_[(time_into_line >> 1) & 3]; | ||||||
| 				return last_fetches_[(time_into_line >> 1) & 3] | 1; | 			if constexpr (timing == Timing::Plus3) { | ||||||
|  | 				return value | 1; | ||||||
| 			} | 			} | ||||||
|  | 			return value; | ||||||
| 			return last_fetches_[(time_into_line >> 1) & 3]; |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/*! | 		/*! | ||||||
| @@ -349,7 +395,7 @@ template <VideoTiming timing> class Video { | |||||||
| 			bus is accessed when the gate array isn't currently reading. | 			bus is accessed when the gate array isn't currently reading. | ||||||
| 		*/ | 		*/ | ||||||
| 		void set_last_contended_area_access([[maybe_unused]] uint8_t value) { | 		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; | 				last_contended_access_ = value | 1; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -358,6 +404,7 @@ template <VideoTiming timing> class Video { | |||||||
| 			Sets the current border colour. | 			Sets the current border colour. | ||||||
| 		*/ | 		*/ | ||||||
| 		void set_border_colour(uint8_t colour) { | 		void set_border_colour(uint8_t colour) { | ||||||
|  | 			border_byte_ = colour; | ||||||
| 			border_colour_ = palette[colour]; | 			border_colour_ = palette[colour]; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -376,11 +423,17 @@ template <VideoTiming timing> class Video { | |||||||
| 			crt_.set_display_type(type); | 			crt_.set_display_type(type); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		/*! Gets the display type. */ | ||||||
|  | 		Outputs::Display::DisplayType get_display_type() const { | ||||||
|  | 			return crt_.get_display_type(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	private: | 	private: | ||||||
| 		int time_into_frame_ = 0; | 		int time_into_frame_ = 0; | ||||||
| 		Outputs::CRT::CRT crt_; | 		Outputs::CRT::CRT crt_; | ||||||
| 		const uint8_t *memory_ = nullptr; | 		const uint8_t *memory_ = nullptr; | ||||||
| 		uint8_t border_colour_ = 0; | 		uint8_t border_colour_ = 0; | ||||||
|  | 		uint8_t border_byte_ = 0; | ||||||
|  |  | ||||||
| 		uint8_t *pixel_target_ = nullptr; | 		uint8_t *pixel_target_ = nullptr; | ||||||
| 		int attribute_address_ = 0; | 		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_fetches_[4] = {0xff, 0xff, 0xff, 0xff}; | ||||||
| 		uint8_t last_contended_access_ = 0xff; | 		uint8_t last_contended_access_ = 0xff; | ||||||
|  |  | ||||||
|  | 		friend struct State; | ||||||
|  |  | ||||||
| #define RGB(r, g, b)	(r << 4) | (g << 2) | b | #define RGB(r, g, b)	(r << 4) | (g << 2) | b | ||||||
| 		static constexpr uint8_t palette[] = { | 		static constexpr uint8_t palette[] = { | ||||||
| 			RGB(0, 0, 0),	RGB(0, 0, 2),	RGB(2, 0, 0),	RGB(2, 0, 2), | 			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 | #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 "ZXSpectrum.hpp" | ||||||
|  |  | ||||||
|  | #include "State.hpp" | ||||||
| #include "Video.hpp" | #include "Video.hpp" | ||||||
|  | #include "../Keyboard/Keyboard.hpp" | ||||||
| #define LOG_PREFIX "[Spectrum] " |  | ||||||
|  |  | ||||||
| #include "../../../Activity/Source.hpp" | #include "../../../Activity/Source.hpp" | ||||||
| #include "../../MachineTypes.hpp" | #include "../../MachineTypes.hpp" | ||||||
| @@ -24,7 +24,9 @@ | |||||||
| // just grab the CPC's version of an FDC. | // just grab the CPC's version of an FDC. | ||||||
| #include "../../AmstradCPC/FDC.hpp" | #include "../../AmstradCPC/FDC.hpp" | ||||||
|  |  | ||||||
|  | #define LOG_PREFIX "[Spectrum] " | ||||||
| #include "../../../Outputs/Log.hpp" | #include "../../../Outputs/Log.hpp" | ||||||
|  |  | ||||||
| #include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp" | #include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp" | ||||||
| #include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" | #include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" | ||||||
| #include "../../../Outputs/Speaker/Implementation/SampleSource.hpp" | #include "../../../Outputs/Speaker/Implementation/SampleSource.hpp" | ||||||
| @@ -39,12 +41,76 @@ | |||||||
|  |  | ||||||
| #include "../../../ClockReceiver/JustInTime.hpp" | #include "../../../ClockReceiver/JustInTime.hpp" | ||||||
|  |  | ||||||
| #include "../../../Processors/Z80/State/State.hpp" |  | ||||||
|  |  | ||||||
| #include "../Keyboard/Keyboard.hpp" |  | ||||||
|  |  | ||||||
| #include <array> | #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 Sinclair { | ||||||
| namespace ZXSpectrum { | namespace ZXSpectrum { | ||||||
|  |  | ||||||
| @@ -58,6 +124,7 @@ template<Model model> class ConcreteMachine: | |||||||
| 	public CPU::Z80::BusHandler, | 	public CPU::Z80::BusHandler, | ||||||
| 	public Machine, | 	public Machine, | ||||||
| 	public MachineTypes::AudioProducer, | 	public MachineTypes::AudioProducer, | ||||||
|  | 	public MachineTypes::JoystickMachine, | ||||||
| 	public MachineTypes::MappedKeyboardMachine, | 	public MachineTypes::MappedKeyboardMachine, | ||||||
| 	public MachineTypes::MediaTarget, | 	public MachineTypes::MediaTarget, | ||||||
| 	public MachineTypes::ScanProducer, | 	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 | 			// With only the +2a and +3 currently supported, the +3 ROM is always | ||||||
| 			// the one required. | 			// the one required. | ||||||
| 			const auto roms = | 			std::vector<ROMMachine::ROM> rom_names; | ||||||
| 				rom_fetcher({ {"ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, 0x96e3c17a} }); | 			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; | 			if(!roms[0]) throw ROMMachine::Error::MissingROMs; | ||||||
| 			memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size())); | 			memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size())); | ||||||
|  |  | ||||||
| 			// Register for sleeping notifications. | 			// Register for sleeping notifications. | ||||||
| 			tape_player_.set_clocking_hint_observer(this); | 			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. | 			// Set up initial memory map. | ||||||
| 			update_memory_map(); | 			update_memory_map(); | ||||||
| 			set_video_address(); | 			set_video_address(); | ||||||
| @@ -103,6 +195,29 @@ template<Model model> class ConcreteMachine: | |||||||
| 				duration_to_press_enter_ = Cycles(5 * clock_rate()); | 				duration_to_press_enter_ = Cycles(5 * clock_rate()); | ||||||
| 				keyboard_.set_key_state(ZX::Keyboard::KeyEnter, true); | 				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() { | 		~ConcreteMachine() { | ||||||
| @@ -110,7 +225,7 @@ template<Model model> class ConcreteMachine: | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		static constexpr unsigned int clock_rate() { | 		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. | 			constexpr unsigned int Plus3ClockRate = 3'546'875;	// See notes below; this is a guess. | ||||||
|  |  | ||||||
| 			// Notes on timing for the +2a and +3: | 			// 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 | 			// the Spectrum is a PAL machine with a fixed colour phase relationship. For | ||||||
| 			// this emulator's world, that's a first! | 			// this emulator's world, that's a first! | ||||||
|  |  | ||||||
| 			return Plus3ClockRate; | 			return model < Model::OneTwoEightK ? OriginalClockRate : Plus3ClockRate; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// MARK: - TimedMachine. | 		// MARK: - TimedMachine. | ||||||
| @@ -181,6 +296,10 @@ template<Model model> class ConcreteMachine: | |||||||
| 			video_->set_display_type(display_type); | 			video_->set_display_type(display_type); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		Outputs::Display::DisplayType get_display_type() const override { | ||||||
|  | 			return video_->get_display_type(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// MARK: - BusHandler. | 		// MARK: - BusHandler. | ||||||
|  |  | ||||||
| 		forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { | 		forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { | ||||||
| @@ -189,19 +308,94 @@ template<Model model> class ConcreteMachine: | |||||||
| 			const uint16_t address = cycle.address ? *cycle.address : 0x0000; | 			const uint16_t address = cycle.address ? *cycle.address : 0x0000; | ||||||
|  |  | ||||||
| 			// Apply contention if necessary. | 			// Apply contention if necessary. | ||||||
|  | 			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. | ||||||
| 				if( | 				if( | ||||||
| 					is_contended_[address >> 14] && | 					is_contended_[address >> 14] && | ||||||
| 					cycle.operation >= PartialMachineCycle::ReadOpcodeStart && | 					cycle.operation >= PartialMachineCycle::ReadOpcodeStart && | ||||||
| 					cycle.operation <= PartialMachineCycle::WriteStart) { | 					cycle.operation <= PartialMachineCycle::WriteStart) { | ||||||
| 				// Assumption here: 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? |  | ||||||
|  |  | ||||||
| 				const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1)); | 					const auto delay = video_.last_valid()->access_delay(video_.time_since_flush()); | ||||||
| 					advance(cycle.length + delay); | 					advance(cycle.length + delay); | ||||||
| 					return 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; | 			// For all other machine cycles, model the action as happening at the end of the machine cycle; | ||||||
| 			// that means advancing time now. | 			// that means advancing time now. | ||||||
| @@ -214,7 +408,7 @@ template<Model model> class ConcreteMachine: | |||||||
| 					// Fast loading: ROM version. | 					// Fast loading: ROM version. | ||||||
| 					// | 					// | ||||||
| 					// The below patches over part of the 'LD-BYTES' routine from the 48kb ROM. | 					// 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. | 						// Stop pressing enter, if neccessry. | ||||||
| 						if(duration_to_press_enter_ > Cycles(0)) { | 						if(duration_to_press_enter_ > Cycles(0)) { | ||||||
| 							duration_to_press_enter_ = Cycles(0); | 							duration_to_press_enter_ = Cycles(0); | ||||||
| @@ -226,13 +420,25 @@ template<Model model> class ConcreteMachine: | |||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | 					[[fallthrough]]; | ||||||
|  |  | ||||||
| 				case PartialMachineCycle::Read: | 				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]; | 					*cycle.value = read_pointers_[address >> 14][address]; | ||||||
|  |  | ||||||
|  | 					if constexpr (model >= Model::Plus2a) { | ||||||
| 						if(is_contended_[address >> 14]) { | 						if(is_contended_[address >> 14]) { | ||||||
| 							video_->set_last_contended_area_access(*cycle.value); | 							video_->set_last_contended_area_access(*cycle.value); | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case PartialMachineCycle::Write: | 				case PartialMachineCycle::Write: | ||||||
| @@ -243,12 +449,17 @@ template<Model model> class ConcreteMachine: | |||||||
|  |  | ||||||
| 					write_pointers_[address >> 14][address] = *cycle.value; | 					write_pointers_[address >> 14][address] = *cycle.value; | ||||||
|  |  | ||||||
|  | 					if constexpr (model >= Model::Plus2a) { | ||||||
| 						// Fill the floating bus buffer if this write is within the contended area. | 						// Fill the floating bus buffer if this write is within the contended area. | ||||||
| 						if(is_contended_[address >> 14]) { | 						if(is_contended_[address >> 14]) { | ||||||
| 							video_->set_last_contended_area_access(*cycle.value); | 							video_->set_last_contended_area_access(*cycle.value); | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
|  | 				// Partial port decodings here and in ::Input are as documented | ||||||
|  | 				// at https://worldofspectrum.org/faq/reference/ports.htm | ||||||
|  |  | ||||||
| 				case PartialMachineCycle::Output: | 				case PartialMachineCycle::Output: | ||||||
| 					// Test for port FE. | 					// Test for port FE. | ||||||
| 					if(!(address&1)) { | 					if(!(address&1)) { | ||||||
| @@ -263,19 +474,19 @@ template<Model model> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					// Test for classic 128kb paging register (i.e. port 7ffd). | 					// 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; | 						port7ffd_ = *cycle.value; | ||||||
| 						update_memory_map(); | 						update_memory_map(); | ||||||
|  |  | ||||||
| 						// Set the proper video base pointer. | 						// Set the proper video base pointer. | ||||||
| 						set_video_address(); | 						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). | 					// Test for +2a/+3 paging (i.e. port 1ffd). | ||||||
|  | 					if constexpr (model >= Model::Plus2a) { | ||||||
| 						if((address & 0xf002) == 0x1000) { | 						if((address & 0xf002) == 0x1000) { | ||||||
| 							port1ffd_ = *cycle.value; | 							port1ffd_ = *cycle.value; | ||||||
| 							update_memory_map(); | 							update_memory_map(); | ||||||
| @@ -285,33 +496,48 @@ template<Model model> class ConcreteMachine: | |||||||
| 								fdc_->set_motor_on(*cycle.value & 0x08); | 								fdc_->set_motor_on(*cycle.value & 0x08); | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 					if((address & 0xc002) == 0xc000) { | 					// Route to the AY if one is fitted. | ||||||
|  | 					if constexpr (model >= Model::OneTwoEightK) { | ||||||
|  | 						switch(address & 0xc002) { | ||||||
|  | 							case 0xc000: | ||||||
| 								// Select AY register. | 								// Select AY register. | ||||||
| 								update_audio(); | 								update_audio(); | ||||||
| 								GI::AY38910::Utility::select_register(ay_, *cycle.value); | 								GI::AY38910::Utility::select_register(ay_, *cycle.value); | ||||||
| 					} | 							break; | ||||||
|  |  | ||||||
| 					if((address & 0xc002) == 0x8000) { | 							case 0x8000: | ||||||
| 								// Write to AY register. | 								// Write to AY register. | ||||||
| 								update_audio(); | 								update_audio(); | ||||||
| 								GI::AY38910::Utility::write_data(ay_, *cycle.value); | 								GI::AY38910::Utility::write_data(ay_, *cycle.value); | ||||||
|  | 							break; | ||||||
|  | 						} | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
|  | 					// Check for FDC accesses. | ||||||
| 					if constexpr (model == Model::Plus3) { | 					if constexpr (model == Model::Plus3) { | ||||||
| 						switch(address) { | 						switch(address & 0xf002) { | ||||||
| 							default: break; | 							default: break; | ||||||
| 							case 0x3ffd: case 0x2ffd: | 							case 0x3000: case 0x2000: | ||||||
| 								fdc_->write((address >> 12) & 1, *cycle.value); | 								fdc_->write((address >> 12) & 1, *cycle.value); | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case PartialMachineCycle::Input: | 				case PartialMachineCycle::Input: { | ||||||
|  | 					bool did_match = false; | ||||||
| 					*cycle.value = 0xff; | 					*cycle.value = 0xff; | ||||||
|  |  | ||||||
|  | 					if(!(address&32)) { | ||||||
|  | 						did_match = true; | ||||||
|  | 						*cycle.value &= static_cast<Joystick *>(joysticks_[0].get())->get_kempston(); | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 					if(!(address&1)) { | 					if(!(address&1)) { | ||||||
|  | 						did_match = true; | ||||||
|  |  | ||||||
| 						// Port FE: | 						// Port FE: | ||||||
| 						// | 						// | ||||||
| 						// address b8+: mask of keyboard lines to select | 						// address b8+: mask of keyboard lines to select | ||||||
| @@ -321,6 +547,10 @@ template<Model model> class ConcreteMachine: | |||||||
| 						*cycle.value &= keyboard_.read(address); | 						*cycle.value &= keyboard_.read(address); | ||||||
| 						*cycle.value &= tape_player_.get_input() ? 0xbf : 0xff; | 						*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, | 						// If this read is within 200 cycles of the previous, | ||||||
| 						// count it as an adjacent hit; if 20 of those have | 						// count it as an adjacent hit; if 20 of those have | ||||||
| 						// occurred then start the tape motor. | 						// occurred then start the tape motor. | ||||||
| @@ -339,27 +569,46 @@ template<Model model> class ConcreteMachine: | |||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
|  | 					if constexpr (model >= Model::OneTwoEightK) { | ||||||
| 						if((address & 0xc002) == 0xc000) { | 						if((address & 0xc002) == 0xc000) { | ||||||
|  | 							did_match = true; | ||||||
|  |  | ||||||
| 							// Read from AY register. | 							// Read from AY register. | ||||||
| 							update_audio(); | 							update_audio(); | ||||||
| 							*cycle.value &= GI::AY38910::Utility::read(ay_); | 							*cycle.value &= GI::AY38910::Utility::read(ay_); | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 					// Check for a floating bus read; these are particularly arcane | 					if constexpr (model >= Model::Plus2a) { | ||||||
| 					// on the +2a/+3. See footnote to https://spectrumforeveryone.com/technical/memory-contention-floating-bus/ | 						// 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 | 						// and, much more rigorously, http://sky.relative-path.com/zx/floating_bus.html | ||||||
| 						if(!disable_paging_ && (address & 0xf003) == 0x0001) { | 						if(!disable_paging_ && (address & 0xf003) == 0x0001) { | ||||||
| 							*cycle.value &= video_->get_floating_value(); | 							*cycle.value &= video_->get_floating_value(); | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 					if constexpr (model == Model::Plus3) { | 					if constexpr (model == Model::Plus3) { | ||||||
| 						switch(address) { | 						switch(address & 0xf002) { | ||||||
| 							default: break; | 							default: break; | ||||||
| 							case 0x3ffd: case 0x2ffd: | 							case 0x3000: case 0x2000: | ||||||
| 								*cycle.value &= fdc_->read((address >> 12) & 1); | 								*cycle.value &= fdc_->read((address >> 12) & 1); | ||||||
| 							break; | 							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; | 				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. | 			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->automatic_tape_motor_control = use_automatic_tape_motor_control_; | ||||||
| 			options->quickload = allow_fast_tape_hack_; | 			options->quickload = allow_fast_tape_hack_; | ||||||
|  | 			options->output = get_video_signal_configurable(); | ||||||
| 			return options; | 			return options; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -568,13 +818,21 @@ template<Model model> class ConcreteMachine: | |||||||
| 				set_memory(2, 2); | 				set_memory(2, 2); | ||||||
| 				set_memory(3, port7ffd_ & 7); | 				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) { | 		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; | 			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; | 			const auto offset = bank*16384; | ||||||
|  |  | ||||||
| 			read_pointers_[bank] = read - offset; | 			read_pointers_[bank] = read - offset; | ||||||
| @@ -607,8 +865,15 @@ template<Model model> class ConcreteMachine: | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// MARK: - Video. | 		// MARK: - Video. | ||||||
| 		static constexpr VideoTiming video_timing = VideoTiming::Plus3; | 		using VideoType = | ||||||
| 		JustInTimeActor<Video<video_timing>> video_; | 			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. | 		// MARK: - Keyboard. | ||||||
| 		Sinclair::ZX::Keyboard::Keyboard keyboard_; | 		Sinclair::ZX::Keyboard::Keyboard keyboard_; | ||||||
| @@ -695,11 +960,33 @@ template<Model model> class ConcreteMachine: | |||||||
| 			return true; | 			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. | 		// MARK: - Disc. | ||||||
| 		JustInTimeActor<Amstrad::FDC, Cycles> fdc_; | 		JustInTimeActor<Amstrad::FDC, Cycles> fdc_; | ||||||
|  |  | ||||||
| 		// MARK: - Automatic startup. | 		// MARK: - Automatic startup. | ||||||
| 		Cycles duration_to_press_enter_; | 		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,6 +999,10 @@ Machine *Machine::ZXSpectrum(const Analyser::Static::Target *target, const ROMMa | |||||||
| 	const auto zx_target = dynamic_cast<const Analyser::Static::ZXSpectrum::Target *>(target); | 	const auto zx_target = dynamic_cast<const Analyser::Static::ZXSpectrum::Target *>(target); | ||||||
|  |  | ||||||
| 	switch(zx_target->model) { | 	switch(zx_target->model) { | ||||||
|  | 		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::Plus2a:			return new ConcreteMachine<Model::Plus2a>(*zx_target, rom_fetcher); | ||||||
| 		case Model::Plus3:			return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher); | 		case Model::Plus3:			return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ class Machine { | |||||||
| 			friend Configurable::DisplayOption<Options>; | 			friend Configurable::DisplayOption<Options>; | ||||||
| 			friend Configurable::QuickloadOption<Options>; | 			friend Configurable::QuickloadOption<Options>; | ||||||
| 			public: | 			public: | ||||||
| 				bool automatic_tape_motor_control; | 				bool automatic_tape_motor_control = true; | ||||||
|  |  | ||||||
| 				Options(Configurable::OptionsType type) : | 				Options(Configurable::OptionsType type) : | ||||||
| 					Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), | 					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(Oric); | ||||||
| 		AddName(Vic20); | 		AddName(Vic20); | ||||||
| 		AddName(ZX8081); | 		AddName(ZX8081); | ||||||
|  | 		AddName(ZXSpectrum); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| #undef AddName | #undef AddName | ||||||
|   | |||||||
| @@ -131,13 +131,3 @@ bool Typer::type_next_character() { | |||||||
|  |  | ||||||
| 	return true; | 	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]; | 		typedef uint16_t KeySequence[16]; | ||||||
|  |  | ||||||
| 		/*! | 		/*! | ||||||
| 			Provided in the base class as a convenience: given the lookup table of key sequences @c sequences, | 			Provided in the base class as a convenience: given the C array of key sequences @c sequences, | ||||||
| 			with @c length entries, returns the sequence for character @c character if it exists; otherwise | 			returns the sequence for character @c character if it exists; otherwise returns @c nullptr. | ||||||
| 			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; | 	archiveVersion = 1; | ||||||
| 	classes = { | 	classes = { | ||||||
| 	}; | 	}; | ||||||
| 	objectVersion = 46; | 	objectVersion = 50; | ||||||
| 	objects = { | 	objects = { | ||||||
|  |  | ||||||
| /* Begin PBXBuildFile section */ | /* Begin PBXBuildFile section */ | ||||||
| @@ -173,6 +173,8 @@ | |||||||
| 		4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; }; | 		4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; }; | ||||||
| 		4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; }; | 		4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; }; | ||||||
| 		4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.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 */; }; | 		4B2BF19123DCC6A200C3AD60 /* BD500.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03523CEB86000B98D9E /* BD500.cpp */; }; | ||||||
| 		4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; }; | 		4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; }; | ||||||
| 		4B2BF19623E10F0100C3AD60 /* CSHighPrecisionTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */; }; | 		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 */; }; | 		4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894516201967B4007DE474 /* StaticAnalyser.cpp */; }; | ||||||
| 		4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; }; | 		4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; }; | ||||||
| 		4B89453F201967B4007DE474 /* 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 */; }; | 		4B8DF4D825465B7500F3433C /* IIgsMemoryMapTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */; }; | ||||||
| 		4B8DF4F9254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; }; | 		4B8DF4F9254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; }; | ||||||
| 		4B8DF4FA254E36AE00F3433C /* 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 */; }; | 		4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; }; | ||||||
| 		4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00E222E663B900AC3CD0 /* NSData+CRC32.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 */; }; | 		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 */; }; | 		4BDACBEC22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; }; | ||||||
| 		4BDACBED22FFA5D20045EF7E /* 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 */; }; | 		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>"; }; | 		4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; | ||||||
| 		4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; }; | 		4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; }; | ||||||
| 		4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; }; | 		4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; }; | ||||||
| 		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; }; | 		4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||||
| 		4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; }; | 		4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; }; | ||||||
| 		4B055A7C1FAE84A50060FFFF /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = 9918.cpp; path = 9918/9918.cpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; }; | ||||||
| 		4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = MSX/Keyboard.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; }; | 		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>"; }; | 		4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BitReverse.cpp; sourceTree = "<group>"; }; | ||||||
| 		4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = BitReverse.hpp; path = Data/BitReverse.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = ASCII16kb.hpp; path = MSX/Cartridges/ASCII16kb.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; name = ASCII8kb.hpp; path = MSX/Cartridges/ASCII8kb.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; name = KonamiWithSCC.hpp; path = MSX/Cartridges/KonamiWithSCC.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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Video.hpp; path = Oric/Video.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Electron.hpp; path = Electron/Electron.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Plus3.hpp; path = Electron/Plus3.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = AmstradCPC.hpp; path = AmstradCPC/AmstradCPC.hpp; 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; name = AsyncTaskQueue.cpp; path = ../../Concurrency/AsyncTaskQueue.cpp; 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; name = AsyncTaskQueue.hpp; path = ../../Concurrency/AsyncTaskQueue.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; }; | 		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>"; }; | 		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; name = AY38910.hpp; path = AY38910/AY38910.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		4B54C0BD1F8D8F450050900F /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; }; | ||||||
| 		4B54C0BE1F8D8F450050900F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = Oric/Keyboard.hpp; 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; name = Keyboard.hpp; path = AmstradCPC/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; name = Keyboard.cpp; path = AmstradCPC/Keyboard.cpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Keyboard.hpp; path = Electron/Keyboard.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Microdisc.hpp; path = Oric/Microdisc.hpp; 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; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; 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; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Video.hpp; path = Electron/Video.hpp; 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>"; }; | 		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>"; }; | 		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; name = MSX.hpp; path = MSX/MSX.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Jasmin.cpp; sourceTree = "<group>"; }; | ||||||
| 		4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Jasmin.hpp; path = Oric/Jasmin.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = BD500.hpp; path = Oric/BD500.hpp; 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; name = DiskController.hpp; path = Oric/DiskController.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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Commodore.hpp; path = Data/Commodore.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = ZX8081.hpp; path = Data/ZX8081.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = i8272.hpp; path = 8272/i8272.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Oric.hpp; path = Oric/Oric.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = 1770.hpp; path = 1770/1770.hpp; 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>"; }; | 		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>"; }; | 		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; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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; name = Tape.hpp; path = Electron/Tape.hpp; 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; name = Interrupts.hpp; path = Electron/Interrupts.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; name = SoundGenerator.cpp; path = Electron/SoundGenerator.cpp; 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; name = SoundGenerator.hpp; path = Electron/SoundGenerator.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskROM.cpp; sourceTree = "<group>"; }; | ||||||
| 		4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskROM.hpp; path = MSX/DiskROM.hpp; 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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		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>"; }; | 		4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Storage.hpp; sourceTree = "<group>"; }; | ||||||
| @@ -2155,8 +2173,7 @@ | |||||||
| 				4B0CCC431C62D0B3001CAC5F /* CRT.hpp */, | 				4B0CCC431C62D0B3001CAC5F /* CRT.hpp */, | ||||||
| 				4BBF99071C8FBA6F0075DAFB /* Internals */, | 				4BBF99071C8FBA6F0075DAFB /* Internals */, | ||||||
| 			); | 			); | ||||||
| 			name = CRT; | 			path = CRT; | ||||||
| 			path = ../../Outputs/CRT; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B0E04F71FC9F2C800F43484 /* 9918 */ = { | 		4B0E04F71FC9F2C800F43484 /* 9918 */ = { | ||||||
| @@ -2166,7 +2183,7 @@ | |||||||
| 				4B0E04F81FC9FA3000F43484 /* 9918.hpp */, | 				4B0E04F81FC9FA3000F43484 /* 9918.hpp */, | ||||||
| 				4BD388431FE34E060042B588 /* Implementation */, | 				4BD388431FE34E060042B588 /* Implementation */, | ||||||
| 			); | 			); | ||||||
| 			name = 9918; | 			path = 9918; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B0F1BAF2602645900B85C66 /* ZXSpectrum */ = { | 		4B0F1BAF2602645900B85C66 /* ZXSpectrum */ = { | ||||||
| @@ -2206,6 +2223,7 @@ | |||||||
| 				4B0F1BFA260300D900B85C66 /* ZXSpectrum.cpp */, | 				4B0F1BFA260300D900B85C66 /* ZXSpectrum.cpp */, | ||||||
| 				4B0F1BFB260300D900B85C66 /* ZXSpectrum.hpp */, | 				4B0F1BFB260300D900B85C66 /* ZXSpectrum.hpp */, | ||||||
| 				4B0F1C092603BA5F00B85C66 /* Video.hpp */, | 				4B0F1C092603BA5F00B85C66 /* Video.hpp */, | ||||||
|  | 				4B8DD3912635A72F00B3C866 /* State.hpp */, | ||||||
| 			); | 			); | ||||||
| 			path = ZXSpectrum; | 			path = ZXSpectrum; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| @@ -2274,7 +2292,7 @@ | |||||||
| 				4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */, | 				4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */, | ||||||
| 				4B1667F61FFF1E2400A16032 /* Konami.hpp */, | 				4B1667F61FFF1E2400A16032 /* Konami.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Cartridges; | 			path = Cartridges; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B1B58F3246CC4E8009C171E /* State */ = { | 		4B1B58F3246CC4E8009C171E /* State */ = { | ||||||
| @@ -2283,8 +2301,7 @@ | |||||||
| 				4B1B58F4246CC4E8009C171E /* State.cpp */, | 				4B1B58F4246CC4E8009C171E /* State.cpp */, | ||||||
| 				4B1B58F5246CC4E8009C171E /* State.hpp */, | 				4B1B58F5246CC4E8009C171E /* State.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = State; | 			path = State; | ||||||
| 			path = Z80/State; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B1B58FC246E19FD009C171E /* State */ = { | 		4B1B58FC246E19FD009C171E /* State */ = { | ||||||
| @@ -2431,7 +2448,7 @@ | |||||||
| 				4BEA525F1DF333D8007E74F2 /* Tape.hpp */, | 				4BEA525F1DF333D8007E74F2 /* Tape.hpp */, | ||||||
| 				4B7913CB1DFCD80E00175A82 /* Video.hpp */, | 				4B7913CB1DFCD80E00175A82 /* Video.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Electron; | 			path = Electron; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B302181208A550100773308 /* DiskII */ = { | 		4B302181208A550100773308 /* DiskII */ = { | ||||||
| @@ -2465,8 +2482,7 @@ | |||||||
| 				4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */, | 				4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */, | ||||||
| 				4B322DFE1F5A2981004EB04C /* Z80AllRAM.hpp */, | 				4B322DFE1F5A2981004EB04C /* Z80AllRAM.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = AllRAM; | 			path = AllRAM; | ||||||
| 			path = Z80/AllRAM; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B322DFF1F5A2981004EB04C /* Implementation */ = { | 		4B322DFF1F5A2981004EB04C /* Implementation */ = { | ||||||
| @@ -2478,8 +2494,7 @@ | |||||||
| 				4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */, | 				4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */, | ||||||
| 				4B322E021F5A29D5004EB04C /* Z80Storage.hpp */, | 				4B322E021F5A29D5004EB04C /* Z80Storage.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Implementation; | 			path = Implementation; | ||||||
| 			path = Z80/Implementation; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B366DFD1B5C165F0026627B /* Outputs */ = { | 		4B366DFD1B5C165F0026627B /* Outputs */ = { | ||||||
| @@ -2496,6 +2511,7 @@ | |||||||
| 				4BD060A41FE49D3C006E14BE /* Speaker */, | 				4BD060A41FE49D3C006E14BE /* Speaker */, | ||||||
| 			); | 			); | ||||||
| 			name = Outputs; | 			name = Outputs; | ||||||
|  | 			path = ../../Outputs; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B38F3491F2EC12000D9235D /* AmstradCPC */ = { | 		4B38F3491F2EC12000D9235D /* AmstradCPC */ = { | ||||||
| @@ -2507,7 +2523,7 @@ | |||||||
| 				4B54C0C01F8D91CD0050900F /* Keyboard.hpp */, | 				4B54C0C01F8D91CD0050900F /* Keyboard.hpp */, | ||||||
| 				4B0F1C3D26095AC600B85C66 /* FDC.hpp */, | 				4B0F1C3D26095AC600B85C66 /* FDC.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = AmstradCPC; | 			path = AmstradCPC; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B3940E81DA83C8700427841 /* Concurrency */ = { | 		4B3940E81DA83C8700427841 /* Concurrency */ = { | ||||||
| @@ -2517,6 +2533,7 @@ | |||||||
| 				4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */, | 				4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Concurrency; | 			name = Concurrency; | ||||||
|  | 			path = ../../Concurrency; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B3AF7CF2413470E00873C0B /* Reflection */ = { | 		4B3AF7CF2413470E00873C0B /* Reflection */ = { | ||||||
| @@ -2667,7 +2684,7 @@ | |||||||
| 				4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */, | 				4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */, | ||||||
| 				4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */, | 				4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = AY38910; | 			path = AY38910; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B4B1A39200198C900A0F866 /* KonamiSCC */ = { | 		4B4B1A39200198C900A0F866 /* KonamiSCC */ = { | ||||||
| @@ -2838,6 +2855,7 @@ | |||||||
| 				4B8805F81DCFF6CD003085B1 /* Data */, | 				4B8805F81DCFF6CD003085B1 /* Data */, | ||||||
| 				4BAB62AA1D3272D200DF5BA0 /* Disk */, | 				4BAB62AA1D3272D200DF5BA0 /* Disk */, | ||||||
| 				4B6AAEA1230E3E1D0078E864 /* MassStorage */, | 				4B6AAEA1230E3E1D0078E864 /* MassStorage */, | ||||||
|  | 				4B8DD3832634D37E00B3C866 /* State */, | ||||||
| 				4B69FB3A1C4D908A00B5F0AA /* Tape */, | 				4B69FB3A1C4D908A00B5F0AA /* Tape */, | ||||||
| 			); | 			); | ||||||
| 			name = Storage; | 			name = Storage; | ||||||
| @@ -2967,7 +2985,7 @@ | |||||||
| 				4B322DFF1F5A2981004EB04C /* Implementation */, | 				4B322DFF1F5A2981004EB04C /* Implementation */, | ||||||
| 				4B1B58F3246CC4E8009C171E /* State */, | 				4B1B58F3246CC4E8009C171E /* State */, | ||||||
| 			); | 			); | ||||||
| 			name = Z80; | 			path = Z80; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = { | 		4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = { | ||||||
| @@ -2982,7 +3000,7 @@ | |||||||
| 				4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */, | 				4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */, | ||||||
| 				4B1667F81FFF1E2900A16032 /* Cartridges */, | 				4B1667F81FFF1E2900A16032 /* Cartridges */, | ||||||
| 			); | 			); | ||||||
| 			name = MSX; | 			path = MSX; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B7A90E22041097C008514A2 /* ColecoVision */ = { | 		4B7A90E22041097C008514A2 /* ColecoVision */ = { | ||||||
| @@ -3103,7 +3121,7 @@ | |||||||
| 				4B8805F61DCFF6C9003085B1 /* Commodore.hpp */, | 				4B8805F61DCFF6C9003085B1 /* Commodore.hpp */, | ||||||
| 				4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */, | 				4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Data; | 			path = Data; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4B8944E2201967B4007DE474 /* Analyser */ = { | 		4B8944E2201967B4007DE474 /* Analyser */ = { | ||||||
| @@ -3244,6 +3262,19 @@ | |||||||
| 			path = AmstradCPC; | 			path = AmstradCPC; | ||||||
| 			sourceTree = "<group>"; | 			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 */ = { | 		4B8DF4EC254B840B00F3433C /* AppleClock */ = { | ||||||
| 			isa = PBXGroup; | 			isa = PBXGroup; | ||||||
| 			children = ( | 			children = ( | ||||||
| @@ -3937,8 +3968,12 @@ | |||||||
| 		4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { | 		4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { | ||||||
| 			isa = PBXGroup; | 			isa = PBXGroup; | ||||||
| 			children = ( | 			children = ( | ||||||
| 				4B85322922778E4200F26553 /* Comparative68000.hpp */, | 				4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, | ||||||
| 				4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, | 				4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, | ||||||
|  | 				4BC751B11D157E61006C31D9 /* 6522Tests.swift */, | ||||||
|  | 				4B1E85801D176468001EF87D /* 6532Tests.swift */, | ||||||
|  | 				4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */, | ||||||
|  | 				4B8DF5132550D62900F3433C /* 65816kromTests.swift */, | ||||||
| 				4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */, | 				4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */, | ||||||
| 				4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */, | 				4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */, | ||||||
| 				4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */, | 				4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */, | ||||||
| @@ -3947,45 +3982,43 @@ | |||||||
| 				4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */, | 				4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */, | ||||||
| 				4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */, | 				4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */, | ||||||
| 				4BD388872239E198002D14B5 /* 68000Tests.mm */, | 				4BD388872239E198002D14B5 /* 68000Tests.mm */, | ||||||
|  | 				4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */, | ||||||
| 				4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */, | 				4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */, | ||||||
| 				4BE34437238389E10058E78F /* AtariSTVideoTests.mm */, | 				4BE34437238389E10058E78F /* AtariSTVideoTests.mm */, | ||||||
|  | 				4B049CDC1DA3C82F00322067 /* BCDTest.swift */, | ||||||
|  | 				4B3BA0C41D318B44005DD7A7 /* Bridges */, | ||||||
|  | 				4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */, | ||||||
|  | 				4B85322922778E4200F26553 /* Comparative68000.hpp */, | ||||||
| 				4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, | 				4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, | ||||||
|  | 				4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */, | ||||||
| 				4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */, | 				4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */, | ||||||
|  | 				4BBF49AE1ED2880200AB3669 /* FUSETests.swift */, | ||||||
| 				4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */, | 				4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */, | ||||||
|  | 				4BB73EB81B587A5100552FC2 /* Info.plist */, | ||||||
|  | 				4B4F477B253530B7004245B8 /* Jeek816Tests.swift */, | ||||||
|  | 				4B1414611B58888700E04248 /* KlausDormannTests.swift */, | ||||||
| 				4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */, | 				4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */, | ||||||
| 				4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */, | 				4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */, | ||||||
| 				4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, | 				4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, | ||||||
| 				4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, | 				4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, | ||||||
| 				4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, | 				4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, | ||||||
|  | 				4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */, | ||||||
| 				4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, | 				4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, | ||||||
| 				4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, | 				4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, | ||||||
| 				4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */, | 				4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */, | ||||||
| 				4BE76CF822641ED300ACD6FA /* QLTests.mm */, | 				4BE76CF822641ED300ACD6FA /* QLTests.mm */, | ||||||
|  | 				4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */, | ||||||
|  | 				4B1414631B588A1100E04248 /* Test Binaries */, | ||||||
|  | 				4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, | ||||||
| 				4B2AF8681E513FC20027EE29 /* TIATests.mm */, | 				4B2AF8681E513FC20027EE29 /* TIATests.mm */, | ||||||
| 				4B1D08051E0F7A1100763741 /* TimeTests.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 */, | 				4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, | ||||||
|  | 				4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */, | ||||||
|  | 				4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */, | ||||||
| 				4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */, | 				4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */, | ||||||
| 				4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */, | 				4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */, | ||||||
| 				4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */, | 				4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */, | ||||||
| 				4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */, | 				4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */, | ||||||
| 				4B3BA0C41D318B44005DD7A7 /* Bridges */, |  | ||||||
| 				4B1414631B588A1100E04248 /* Test Binaries */, |  | ||||||
| 			); | 			); | ||||||
| 			path = "Clock SignalTests"; | 			path = "Clock SignalTests"; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| @@ -4012,6 +4045,7 @@ | |||||||
| 				4B92294222B04A3D00A1458F /* MouseMachine.hpp */, | 				4B92294222B04A3D00A1458F /* MouseMachine.hpp */, | ||||||
| 				4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */, | 				4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */, | ||||||
| 				4B046DC31CFE651500E9E45E /* ScanProducer.hpp */, | 				4B046DC31CFE651500E9E45E /* ScanProducer.hpp */, | ||||||
|  | 				4B8DD375263481BB00B3C866 /* StateProducer.hpp */, | ||||||
| 				4BC57CD32434282000FBC404 /* TimedMachine.hpp */, | 				4BC57CD32434282000FBC404 /* TimedMachine.hpp */, | ||||||
| 				4B38F3491F2EC12000D9235D /* AmstradCPC */, | 				4B38F3491F2EC12000D9235D /* AmstradCPC */, | ||||||
| 				4BCE0048227CE8CA000CA200 /* Apple */, | 				4BCE0048227CE8CA000CA200 /* Apple */, | ||||||
| @@ -4051,8 +4085,7 @@ | |||||||
| 				4BB8616C24E22DC500A00E03 /* BufferingScanTarget.hpp */, | 				4BB8616C24E22DC500A00E03 /* BufferingScanTarget.hpp */, | ||||||
| 				4BB8616D24E22DC500A00E03 /* BufferingScanTarget.cpp */, | 				4BB8616D24E22DC500A00E03 /* BufferingScanTarget.cpp */, | ||||||
| 			); | 			); | ||||||
| 			name = ScanTargets; | 			path = ScanTargets; | ||||||
| 			path = ../../Outputs/ScanTargets; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BBB70A1202011C2002FE009 /* Implementation */ = { | 		4BBB70A1202011C2002FE009 /* Implementation */ = { | ||||||
| @@ -4080,7 +4113,7 @@ | |||||||
| 				4BBC951C1F368D83008F4C34 /* i8272.cpp */, | 				4BBC951C1F368D83008F4C34 /* i8272.cpp */, | ||||||
| 				4BBC951D1F368D83008F4C34 /* i8272.hpp */, | 				4BBC951D1F368D83008F4C34 /* i8272.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = 8272; | 			path = 8272; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BBF49B41ED2881600AB3669 /* FUSE */ = { | 		4BBF49B41ED2881600AB3669 /* FUSE */ = { | ||||||
| @@ -4297,7 +4330,7 @@ | |||||||
| 				4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */, | 				4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */, | ||||||
| 				4B7BA03823CEB8D200B98D9E /* DiskController.hpp */, | 				4B7BA03823CEB8D200B98D9E /* DiskController.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Oric; | 			path = Oric; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BD060A41FE49D3C006E14BE /* Speaker */ = { | 		4BD060A41FE49D3C006E14BE /* Speaker */ = { | ||||||
| @@ -4306,8 +4339,7 @@ | |||||||
| 				4BD060A51FE49D3C006E14BE /* Speaker.hpp */, | 				4BD060A51FE49D3C006E14BE /* Speaker.hpp */, | ||||||
| 				4B8EF6051FE5AF830076CCDD /* Implementation */, | 				4B8EF6051FE5AF830076CCDD /* Implementation */, | ||||||
| 			); | 			); | ||||||
| 			name = Speaker; | 			path = Speaker; | ||||||
| 			path = ../../Outputs/Speaker; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BD191D5219113B80042E144 /* OpenGL */ = { | 		4BD191D5219113B80042E144 /* OpenGL */ = { | ||||||
| @@ -4320,8 +4352,7 @@ | |||||||
| 				4B961408222760E0001A7BF2 /* Screenshot.hpp */, | 				4B961408222760E0001A7BF2 /* Screenshot.hpp */, | ||||||
| 				4BD424DC2193B5340097291A /* Primitives */, | 				4BD424DC2193B5340097291A /* Primitives */, | ||||||
| 			); | 			); | ||||||
| 			name = OpenGL; | 			path = OpenGL; | ||||||
| 			path = ../../Outputs/OpenGL; |  | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BD388431FE34E060042B588 /* Implementation */ = { | 		4BD388431FE34E060042B588 /* Implementation */ = { | ||||||
| @@ -4329,7 +4360,7 @@ | |||||||
| 			children = ( | 			children = ( | ||||||
| 				4BD388411FE34E010042B588 /* 9918Base.hpp */, | 				4BD388411FE34E010042B588 /* 9918Base.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = Implementation; | 			path = Implementation; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BD424DC2193B5340097291A /* Primitives */ = { | 		4BD424DC2193B5340097291A /* Primitives */ = { | ||||||
| @@ -4351,7 +4382,7 @@ | |||||||
| 				4BD468F51D8DF41D0084958B /* 1770.cpp */, | 				4BD468F51D8DF41D0084958B /* 1770.cpp */, | ||||||
| 				4BD468F61D8DF41D0084958B /* 1770.hpp */, | 				4BD468F61D8DF41D0084958B /* 1770.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = 1770; | 			path = 1770; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BD67DC8209BE4D600AB2146 /* DiskII */ = { | 		4BD67DC8209BE4D600AB2146 /* DiskII */ = { | ||||||
| @@ -4381,7 +4412,7 @@ | |||||||
| 			children = ( | 			children = ( | ||||||
| 				4BD9137D1F311BC5009BCF85 /* i8255.hpp */, | 				4BD9137D1F311BC5009BCF85 /* i8255.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = 8255; | 			path = 8255; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BDA00DB22E60EE900AC3CD0 /* ROMRequester */ = { | 		4BDA00DB22E60EE900AC3CD0 /* ROMRequester */ = { | ||||||
| @@ -4443,7 +4474,7 @@ | |||||||
| 			children = ( | 			children = ( | ||||||
| 				4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */, | 				4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */, | ||||||
| 			); | 			); | ||||||
| 			name = 6845; | 			path = 6845; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| 		4BE9A6B21EDE294200CBCB47 /* Zexall */ = { | 		4BE9A6B21EDE294200CBCB47 /* Zexall */ = { | ||||||
| @@ -4653,7 +4684,7 @@ | |||||||
| 			isa = PBXProject; | 			isa = PBXProject; | ||||||
| 			attributes = { | 			attributes = { | ||||||
| 				LastSwiftUpdateCheck = 0700; | 				LastSwiftUpdateCheck = 0700; | ||||||
| 				LastUpgradeCheck = 1200; | 				LastUpgradeCheck = 1240; | ||||||
| 				ORGANIZATIONNAME = "Thomas Harte"; | 				ORGANIZATIONNAME = "Thomas Harte"; | ||||||
| 				TargetAttributes = { | 				TargetAttributes = { | ||||||
| 					4B055A691FAE763F0060FFFF = { | 					4B055A691FAE763F0060FFFF = { | ||||||
| @@ -4682,7 +4713,7 @@ | |||||||
| 				}; | 				}; | ||||||
| 			}; | 			}; | ||||||
| 			buildConfigurationList = 4BB73E991B587A5100552FC2 /* Build configuration list for PBXProject "Clock Signal" */; | 			buildConfigurationList = 4BB73E991B587A5100552FC2 /* Build configuration list for PBXProject "Clock Signal" */; | ||||||
| 			compatibilityVersion = "Xcode 3.2"; | 			compatibilityVersion = "Xcode 9.3"; | ||||||
| 			developmentRegion = en; | 			developmentRegion = en; | ||||||
| 			hasScannedForEncodings = 0; | 			hasScannedForEncodings = 0; | ||||||
| 			knownRegions = ( | 			knownRegions = ( | ||||||
| @@ -5207,9 +5238,11 @@ | |||||||
| 				4B1B58F7246CC4E8009C171E /* State.cpp in Sources */, | 				4B1B58F7246CC4E8009C171E /* State.cpp in Sources */, | ||||||
| 				4B0ACC03237756F6008902D0 /* Line.cpp in Sources */, | 				4B0ACC03237756F6008902D0 /* Line.cpp in Sources */, | ||||||
| 				4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */, | 				4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */, | ||||||
|  | 				4B2B946626377C0200E7097C /* SZX.cpp in Sources */, | ||||||
| 				4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */, | 				4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */, | ||||||
| 				4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */, | 				4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */, | ||||||
| 				4B894533201967B4007DE474 /* 6502.cpp in Sources */, | 				4B894533201967B4007DE474 /* 6502.cpp in Sources */, | ||||||
|  | 				4B8DD3872634D37E00B3C866 /* SNA.cpp in Sources */, | ||||||
| 				4B055AA91FAE85EF0060FFFF /* CommodoreGCR.cpp in Sources */, | 				4B055AA91FAE85EF0060FFFF /* CommodoreGCR.cpp in Sources */, | ||||||
| 				4B055ADB1FAE9B460060FFFF /* 6560.cpp in Sources */, | 				4B055ADB1FAE9B460060FFFF /* 6560.cpp in Sources */, | ||||||
| 				4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */, | 				4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */, | ||||||
| @@ -5243,6 +5276,7 @@ | |||||||
| 				4B055AC81FAE9AFB0060FFFF /* C1540.cpp in Sources */, | 				4B055AC81FAE9AFB0060FFFF /* C1540.cpp in Sources */, | ||||||
| 				4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */, | 				4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */, | ||||||
| 				4B055A911FAE85B50060FFFF /* Cartridge.cpp in Sources */, | 				4B055A911FAE85B50060FFFF /* Cartridge.cpp in Sources */, | ||||||
|  | 				4B8DD39826360DDF00B3C866 /* Z80.cpp in Sources */, | ||||||
| 				4B894525201967B4007DE474 /* Tape.cpp in Sources */, | 				4B894525201967B4007DE474 /* Tape.cpp in Sources */, | ||||||
| 				4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */, | 				4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */, | ||||||
| 				4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */, | 				4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */, | ||||||
| @@ -5339,6 +5373,7 @@ | |||||||
| 				4BE211FF253FC80900435408 /* StaticAnalyser.cpp in Sources */, | 				4BE211FF253FC80900435408 /* StaticAnalyser.cpp in Sources */, | ||||||
| 				4B0F1BE22602FF9C00B85C66 /* ZX8081.cpp in Sources */, | 				4B0F1BE22602FF9C00B85C66 /* ZX8081.cpp in Sources */, | ||||||
| 				4B8334951F5E25B60097E338 /* C1540.cpp in Sources */, | 				4B8334951F5E25B60097E338 /* C1540.cpp in Sources */, | ||||||
|  | 				4B8DD39726360DDF00B3C866 /* Z80.cpp in Sources */, | ||||||
| 				4BEDA40C25B2844B000C2DBD /* Decoder.cpp in Sources */, | 				4BEDA40C25B2844B000C2DBD /* Decoder.cpp in Sources */, | ||||||
| 				4B89453C201967B4007DE474 /* StaticAnalyser.cpp in Sources */, | 				4B89453C201967B4007DE474 /* StaticAnalyser.cpp in Sources */, | ||||||
| 				4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, | 				4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, | ||||||
| @@ -5480,6 +5515,7 @@ | |||||||
| 				4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */, | 				4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */, | ||||||
| 				4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */, | 				4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */, | ||||||
| 				4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */, | 				4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */, | ||||||
|  | 				4B8DD3862634D37E00B3C866 /* SNA.cpp in Sources */, | ||||||
| 				4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */, | 				4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */, | ||||||
| 				4B894524201967B4007DE474 /* Tape.cpp in Sources */, | 				4B894524201967B4007DE474 /* Tape.cpp in Sources */, | ||||||
| 				4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, | 				4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, | ||||||
| @@ -5497,6 +5533,7 @@ | |||||||
| 				4BEE0A701D72496600532C7B /* PRG.cpp in Sources */, | 				4BEE0A701D72496600532C7B /* PRG.cpp in Sources */, | ||||||
| 				4BB307BB235001C300457D33 /* 6850.cpp in Sources */, | 				4BB307BB235001C300457D33 /* 6850.cpp in Sources */, | ||||||
| 				4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */, | 				4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */, | ||||||
|  | 				4B2B946526377C0200E7097C /* SZX.cpp in Sources */, | ||||||
| 				4BC131762346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */, | 				4BC131762346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */, | ||||||
| 				4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */, | 				4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */, | ||||||
| 				4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */, | 				4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */, | ||||||
| @@ -5640,6 +5677,7 @@ | |||||||
| 				4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */, | 				4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */, | ||||||
| 				4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */, | 				4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */, | ||||||
| 				4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */, | 				4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */, | ||||||
|  | 				4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */, | ||||||
| 				4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */, | 				4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */, | ||||||
| 				4B778F1223A5EC720000D260 /* CRT.cpp in Sources */, | 				4B778F1223A5EC720000D260 /* CRT.cpp in Sources */, | ||||||
| 				4B778EF423A5DB3A0000D260 /* C1540.cpp in Sources */, | 				4B778EF423A5DB3A0000D260 /* C1540.cpp in Sources */, | ||||||
| @@ -5649,6 +5687,7 @@ | |||||||
| 				4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */, | 				4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */, | ||||||
| 				4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */, | 				4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */, | ||||||
| 				4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */, | 				4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */, | ||||||
|  | 				4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */, | ||||||
| 				4B778F1A23A5ED320000D260 /* Video.cpp in Sources */, | 				4B778F1A23A5ED320000D260 /* Video.cpp in Sources */, | ||||||
| 				4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */, | 				4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */, | ||||||
| 				4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */, | 				4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */, | ||||||
| @@ -5815,7 +5854,6 @@ | |||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				CLANG_ANALYZER_NONNULL = YES; | 				CLANG_ANALYZER_NONNULL = YES; | ||||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; |  | ||||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||||
| 				CODE_SIGN_IDENTITY = "-"; | 				CODE_SIGN_IDENTITY = "-"; | ||||||
| @@ -5836,7 +5874,6 @@ | |||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				CLANG_ANALYZER_NONNULL = YES; | 				CLANG_ANALYZER_NONNULL = YES; | ||||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; |  | ||||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||||
| 				CODE_SIGN_IDENTITY = "-"; | 				CODE_SIGN_IDENTITY = "-"; | ||||||
| @@ -5860,8 +5897,7 @@ | |||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||||
| 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; | ||||||
| 				CLANG_CXX_LIBRARY = "libc++"; |  | ||||||
| 				CLANG_ENABLE_MODULES = YES; | 				CLANG_ENABLE_MODULES = YES; | ||||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | 				CLANG_ENABLE_OBJC_ARC = YES; | ||||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||||
| @@ -5904,13 +5940,14 @@ | |||||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||||
| 				GCC_WARN_UNUSED_PARAMETER = YES; | 				GCC_WARN_UNUSED_PARAMETER = YES; | ||||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||||
| 				MACOSX_DEPLOYMENT_TARGET = 10.14; | 				MACOSX_DEPLOYMENT_TARGET = 10.13; | ||||||
| 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; | 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; | ||||||
| 				MTL_FAST_MATH = YES; | 				MTL_FAST_MATH = YES; | ||||||
| 				ONLY_ACTIVE_ARCH = YES; | 				ONLY_ACTIVE_ARCH = YES; | ||||||
| 				SDKROOT = macosx; | 				SDKROOT = macosx; | ||||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||||
| 				SWIFT_SWIFT3_OBJC_INFERENCE = Default; | 				SWIFT_SWIFT3_OBJC_INFERENCE = Default; | ||||||
|  | 				SWIFT_VERSION = 5.0; | ||||||
| 			}; | 			}; | ||||||
| 			name = Debug; | 			name = Debug; | ||||||
| 		}; | 		}; | ||||||
| @@ -5919,8 +5956,7 @@ | |||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||||
| 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; | ||||||
| 				CLANG_CXX_LIBRARY = "libc++"; |  | ||||||
| 				CLANG_ENABLE_MODULES = YES; | 				CLANG_ENABLE_MODULES = YES; | ||||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | 				CLANG_ENABLE_OBJC_ARC = YES; | ||||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||||
| @@ -5943,7 +5979,7 @@ | |||||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||||
| 				CODE_SIGN_IDENTITY = "-"; | 				CODE_SIGN_IDENTITY = "Mac Developer"; | ||||||
| 				COPY_PHASE_STRIP = NO; | 				COPY_PHASE_STRIP = NO; | ||||||
| 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||||||
| 				ENABLE_NS_ASSERTIONS = NO; | 				ENABLE_NS_ASSERTIONS = NO; | ||||||
| @@ -5957,12 +5993,13 @@ | |||||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||||
| 				GCC_WARN_UNUSED_PARAMETER = YES; | 				GCC_WARN_UNUSED_PARAMETER = YES; | ||||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||||
| 				MACOSX_DEPLOYMENT_TARGET = 10.14; | 				MACOSX_DEPLOYMENT_TARGET = 10.13; | ||||||
| 				MTL_ENABLE_DEBUG_INFO = NO; |  | ||||||
| 				MTL_FAST_MATH = YES; | 				MTL_FAST_MATH = YES; | ||||||
| 				SDKROOT = macosx; | 				SDKROOT = macosx; | ||||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; | 				SWIFT_COMPILATION_MODE = wholemodule; | ||||||
|  | 				SWIFT_OPTIMIZATION_LEVEL = "-O"; | ||||||
| 				SWIFT_SWIFT3_OBJC_INFERENCE = Default; | 				SWIFT_SWIFT3_OBJC_INFERENCE = Default; | ||||||
|  | 				SWIFT_VERSION = 5.0; | ||||||
| 			}; | 			}; | ||||||
| 			name = Release; | 			name = Release; | ||||||
| 		}; | 		}; | ||||||
| @@ -5970,14 +6007,16 @@ | |||||||
| 			isa = XCBuildConfiguration; | 			isa = XCBuildConfiguration; | ||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; | 				CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; | ||||||
| 				CLANG_ENABLE_MODULES = YES; | 				CLANG_ENABLE_MODULES = YES; | ||||||
| 				CLANG_WARN_ASSIGN_ENUM = YES; | 				CLANG_WARN_ASSIGN_ENUM = YES; | ||||||
|  | 				CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; | ||||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||||
| 				CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; | 				CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; | ||||||
|  | 				CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES; | ||||||
| 				CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; | 				CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; | ||||||
| 				CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; | 				CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; | ||||||
| 				CODE_SIGN_IDENTITY = "Apple Development"; | 				CODE_SIGN_IDENTITY = "-"; | ||||||
| 				CODE_SIGN_STYLE = Automatic; | 				CODE_SIGN_STYLE = Automatic; | ||||||
| 				DEVELOPMENT_TEAM = DV3346VVUN; | 				DEVELOPMENT_TEAM = DV3346VVUN; | ||||||
| 				ENABLE_HARDENED_RUNTIME = YES; | 				ENABLE_HARDENED_RUNTIME = YES; | ||||||
| @@ -5990,13 +6029,17 @@ | |||||||
| 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES; | 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES; | ||||||
| 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; | 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; | ||||||
| 				GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; | 				GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; | ||||||
|  | 				GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; | ||||||
| 				GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; | 				GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; | ||||||
| 				GCC_WARN_SHADOW = YES; | 				GCC_WARN_SHADOW = YES; | ||||||
| 				GCC_WARN_SIGN_COMPARE = YES; | 				GCC_WARN_SIGN_COMPARE = YES; | ||||||
| 				GCC_WARN_UNKNOWN_PRAGMAS = YES; | 				GCC_WARN_UNKNOWN_PRAGMAS = YES; | ||||||
| 				GCC_WARN_UNUSED_LABEL = YES; | 				GCC_WARN_UNUSED_LABEL = YES; | ||||||
| 				INFOPLIST_FILE = "Clock Signal/Info.plist"; | 				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; | 				MTL_TREAT_WARNINGS_AS_ERRORS = YES; | ||||||
| 				OTHER_CPLUSPLUSFLAGS = ( | 				OTHER_CPLUSPLUSFLAGS = ( | ||||||
| 					"$(OTHER_CFLAGS)", | 					"$(OTHER_CFLAGS)", | ||||||
| @@ -6007,7 +6050,6 @@ | |||||||
| 				PROVISIONING_PROFILE_SPECIFIER = ""; | 				PROVISIONING_PROFILE_SPECIFIER = ""; | ||||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h"; | 				SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h"; | ||||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||||
| 				SWIFT_VERSION = 5.0; |  | ||||||
| 			}; | 			}; | ||||||
| 			name = Debug; | 			name = Debug; | ||||||
| 		}; | 		}; | ||||||
| @@ -6015,14 +6057,16 @@ | |||||||
| 			isa = XCBuildConfiguration; | 			isa = XCBuildConfiguration; | ||||||
| 			buildSettings = { | 			buildSettings = { | ||||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "c++17"; | 				CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; | ||||||
| 				CLANG_ENABLE_MODULES = YES; | 				CLANG_ENABLE_MODULES = YES; | ||||||
| 				CLANG_WARN_ASSIGN_ENUM = YES; | 				CLANG_WARN_ASSIGN_ENUM = YES; | ||||||
|  | 				CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; | ||||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||||
| 				CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; | 				CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; | ||||||
|  | 				CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES; | ||||||
| 				CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; | 				CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; | ||||||
| 				CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; | 				CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; | ||||||
| 				CODE_SIGN_IDENTITY = "Apple Development"; | 				CODE_SIGN_IDENTITY = "-"; | ||||||
| 				CODE_SIGN_STYLE = Automatic; | 				CODE_SIGN_STYLE = Automatic; | ||||||
| 				DEVELOPMENT_TEAM = DV3346VVUN; | 				DEVELOPMENT_TEAM = DV3346VVUN; | ||||||
| 				ENABLE_HARDENED_RUNTIME = YES; | 				ENABLE_HARDENED_RUNTIME = YES; | ||||||
| @@ -6037,13 +6081,17 @@ | |||||||
| 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES; | 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES; | ||||||
| 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; | 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; | ||||||
| 				GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; | 				GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; | ||||||
|  | 				GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; | ||||||
| 				GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; | 				GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; | ||||||
| 				GCC_WARN_SHADOW = YES; | 				GCC_WARN_SHADOW = YES; | ||||||
| 				GCC_WARN_SIGN_COMPARE = YES; | 				GCC_WARN_SIGN_COMPARE = YES; | ||||||
| 				GCC_WARN_UNKNOWN_PRAGMAS = YES; | 				GCC_WARN_UNKNOWN_PRAGMAS = YES; | ||||||
| 				GCC_WARN_UNUSED_LABEL = YES; | 				GCC_WARN_UNUSED_LABEL = YES; | ||||||
| 				INFOPLIST_FILE = "Clock Signal/Info.plist"; | 				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; | 				MTL_TREAT_WARNINGS_AS_ERRORS = YES; | ||||||
| 				OTHER_CPLUSPLUSFLAGS = ( | 				OTHER_CPLUSPLUSFLAGS = ( | ||||||
| 					"$(OTHER_CFLAGS)", | 					"$(OTHER_CFLAGS)", | ||||||
| @@ -6053,7 +6101,6 @@ | |||||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||||
| 				PROVISIONING_PROFILE_SPECIFIER = ""; | 				PROVISIONING_PROFILE_SPECIFIER = ""; | ||||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h"; | 				SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h"; | ||||||
| 				SWIFT_VERSION = 5.0; |  | ||||||
| 			}; | 			}; | ||||||
| 			name = Release; | 			name = Release; | ||||||
| 		}; | 		}; | ||||||
| @@ -6069,7 +6116,11 @@ | |||||||
| 				DEVELOPMENT_TEAM = ""; | 				DEVELOPMENT_TEAM = ""; | ||||||
| 				ENABLE_HARDENED_RUNTIME = NO; | 				ENABLE_HARDENED_RUNTIME = NO; | ||||||
| 				INFOPLIST_FILE = "Clock SignalTests/Info.plist"; | 				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; | 				MACOSX_DEPLOYMENT_TARGET = 11.0; | ||||||
| 				PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests"; | 				PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests"; | ||||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||||
| @@ -6094,7 +6145,11 @@ | |||||||
| 				ENABLE_HARDENED_RUNTIME = NO; | 				ENABLE_HARDENED_RUNTIME = NO; | ||||||
| 				GCC_OPTIMIZATION_LEVEL = 2; | 				GCC_OPTIMIZATION_LEVEL = 2; | ||||||
| 				INFOPLIST_FILE = "Clock SignalTests/Info.plist"; | 				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; | 				MACOSX_DEPLOYMENT_TARGET = 11.0; | ||||||
| 				ONLY_ACTIVE_ARCH = YES; | 				ONLY_ACTIVE_ARCH = YES; | ||||||
| 				PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests"; | 				PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests"; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <Scheme | <Scheme | ||||||
|    LastUpgradeVersion = "1200" |    LastUpgradeVersion = "1240" | ||||||
|    version = "1.3"> |    version = "1.3"> | ||||||
|    <BuildAction |    <BuildAction | ||||||
|       parallelizeBuildables = "YES" |       parallelizeBuildables = "YES" | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <Scheme | <Scheme | ||||||
|    LastUpgradeVersion = "1200" |    LastUpgradeVersion = "1240" | ||||||
|    version = "1.3"> |    version = "1.3"> | ||||||
|    <BuildAction |    <BuildAction | ||||||
|       parallelizeBuildables = "YES" |       parallelizeBuildables = "YES" | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <Scheme | <Scheme | ||||||
|    LastUpgradeVersion = "1200" |    LastUpgradeVersion = "1240" | ||||||
|    version = "1.3"> |    version = "1.3"> | ||||||
|    <BuildAction |    <BuildAction | ||||||
|       parallelizeBuildables = "YES" |       parallelizeBuildables = "YES" | ||||||
|   | |||||||
| @@ -11,12 +11,24 @@ import Cocoa | |||||||
| @NSApplicationMain | @NSApplicationMain | ||||||
| class AppDelegate: NSObject, NSApplicationDelegate { | class AppDelegate: NSObject, NSApplicationDelegate { | ||||||
|  |  | ||||||
| 	func applicationDidFinishLaunching(_ aNotification: Notification) { | 	private var failedMetalCheck = false | ||||||
|  | 	func applicationDidFinishLaunching(_ notification: Notification) { | ||||||
| 		// Insert code here to initialize your application. | 		// Insert code here to initialize your application. | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	func applicationWillTerminate(_ aNotification: Notification) { | 		// Check for at least one Metal-capable GPU; this check | ||||||
| 		// Insert code here to tear down your application. | 		// 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 | 	private var hasShownOpenDocument = false | ||||||
|   | |||||||
| @@ -592,6 +592,66 @@ | |||||||
| 			<key>NSDocumentClass</key> | 			<key>NSDocumentClass</key> | ||||||
| 			<string>$(PRODUCT_MODULE_NAME).MachineDocument</string> | 			<string>$(PRODUCT_MODULE_NAME).MachineDocument</string> | ||||||
| 		</dict> | 		</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> | 	</array> | ||||||
| 	<key>CFBundleExecutable</key> | 	<key>CFBundleExecutable</key> | ||||||
| 	<string>$(EXECUTABLE_NAME)</string> | 	<string>$(EXECUTABLE_NAME)</string> | ||||||
|   | |||||||
| @@ -368,7 +368,7 @@ API_AVAILABLE(macos(11.0)) | |||||||
| 	return self; | 	return self; | ||||||
| } | } | ||||||
|  |  | ||||||
| -(void)update { | - (void)update { | ||||||
| 	// Update buttons. | 	// Update buttons. | ||||||
| 	for(CSGCJoystickButton *button in _buttons) { | 	for(CSGCJoystickButton *button in _buttons) { | ||||||
| 		// This assumes that the values provided by GCDeviceButtonInput are | 		// This assumes that the values provided by GCDeviceButtonInput are | ||||||
| @@ -379,8 +379,12 @@ API_AVAILABLE(macos(11.0)) | |||||||
| 		float val = axis.axis.value; | 		float val = axis.axis.value; | ||||||
| 		val += 1; | 		val += 1; | ||||||
| 		val /= 2; | 		val /= 2; | ||||||
|  | 		if(axis.type == CSJoystickAxisTypeY) { | ||||||
|  | 			axis.position = 1 - val; | ||||||
|  | 		} else { | ||||||
| 			axis.position = val; | 			axis.position = val; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	for(CSGCJoystickHat *hat in _hats) { | 	for(CSGCJoystickHat *hat in _hats) { | ||||||
| 		// This assumes that the values provided by GCDeviceDirectionPad are | 		// This assumes that the values provided by GCDeviceDirectionPad are | ||||||
| 		// digital. this might not always be the case. | 		// digital. this might not always be the case. | ||||||
|   | |||||||
| @@ -63,6 +63,10 @@ typedef NS_ENUM(NSInteger, CSMachineOricDiskInterface) { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef NS_ENUM(NSInteger, CSMachineSpectrumModel) { | typedef NS_ENUM(NSInteger, CSMachineSpectrumModel) { | ||||||
|  | 	CSMachineSpectrumModelSixteenK, | ||||||
|  | 	CSMachineSpectrumModelFortyEightK, | ||||||
|  | 	CSMachineSpectrumModelOneTwoEightK, | ||||||
|  | 	CSMachineSpectrumModelPlus2, | ||||||
| 	CSMachineSpectrumModelPlus2a, | 	CSMachineSpectrumModelPlus2a, | ||||||
| 	CSMachineSpectrumModelPlus3, | 	CSMachineSpectrumModelPlus3, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -194,6 +194,10 @@ | |||||||
| 		using Target = Analyser::Static::ZXSpectrum::Target; | 		using Target = Analyser::Static::ZXSpectrum::Target; | ||||||
| 		auto target = std::make_unique<Target>(); | 		auto target = std::make_unique<Target>(); | ||||||
| 		switch(model) { | 		switch(model) { | ||||||
|  | 			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 CSMachineSpectrumModelPlus2a:			target->model = Target::Model::Plus2a;			break; | ||||||
| 			case CSMachineSpectrumModelPlus3:			target->model = Target::Model::Plus3;			break; | 			case CSMachineSpectrumModelPlus3:			target->model = Target::Model::Plus3;			break; | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|             <windowStyleMask key="styleMask" titled="YES" documentModal="YES"/> |             <windowStyleMask key="styleMask" titled="YES" documentModal="YES"/> | ||||||
|             <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> |             <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> | ||||||
|             <rect key="contentRect" x="196" y="240" width="590" height="316"/> |             <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"> |             <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ"> | ||||||
|                 <rect key="frame" x="0.0" y="0.0" width="590" height="316"/> |                 <rect key="frame" x="0.0" y="0.0" width="590" height="316"/> | ||||||
|                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||||
| @@ -584,11 +584,11 @@ Gw | |||||||
|                             </tabViewItem> |                             </tabViewItem> | ||||||
|                             <tabViewItem label="ZX81" identifier="zx81" id="Wnn-nQ-gZ6"> |                             <tabViewItem label="ZX81" identifier="zx81" id="Wnn-nQ-gZ6"> | ||||||
|                                 <view key="view" id="bmd-gL-gzT"> |                                 <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"/> |                                     <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||||
|                                     <subviews> |                                     <subviews> | ||||||
|                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5aO-UX-HnX"> |                                         <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"> |                                             <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"/> |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|                                                 <font key="font" metaFont="menu"/> |                                                 <font key="font" metaFont="menu"/> | ||||||
| @@ -602,7 +602,7 @@ Gw | |||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE"> |                                         <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"> |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2"> | ||||||
|                                                 <font key="font" metaFont="system"/> |                                                 <font key="font" metaFont="system"/> | ||||||
|                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
| @@ -622,24 +622,28 @@ Gw | |||||||
|                             </tabViewItem> |                             </tabViewItem> | ||||||
|                             <tabViewItem label="ZX Spectrum" identifier="spectrum" id="HQv-oF-k8b"> |                             <tabViewItem label="ZX Spectrum" identifier="spectrum" id="HQv-oF-k8b"> | ||||||
|                                 <view key="view" id="bMx-F6-JUb"> |                                 <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"/> |                                     <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||||
|                                     <subviews> |                                     <subviews> | ||||||
|                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFZ-d4-WFv"> |                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFZ-d4-WFv"> | ||||||
|                                             <rect key="frame" x="67" y="47" width="62" height="25"/> |                                             <rect key="frame" x="67" y="157" width="76" 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"> |                                             <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"/> |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|                                                 <font key="font" metaFont="menu"/> |                                                 <font key="font" metaFont="menu"/> | ||||||
|                                                 <menu key="menu" id="8lt-dk-zPr"> |                                                 <menu key="menu" id="8lt-dk-zPr"> | ||||||
|                                                     <items> |                                                     <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"/> |                                                         <menuItem title="+3" tag="3" id="jwx-fZ-vXp"/> | ||||||
|                                                     </items> |                                                     </items> | ||||||
|                                                 </menu> |                                                 </menu> | ||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy"> |                                         <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"> |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE"> | ||||||
|                                                 <font key="font" metaFont="system"/> |                                                 <font key="font" metaFont="system"/> | ||||||
|                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
|   | |||||||
| @@ -298,6 +298,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
| 			case "spectrum": | 			case "spectrum": | ||||||
| 				var model: CSMachineSpectrumModel = .plus2a | 				var model: CSMachineSpectrumModel = .plus2a | ||||||
| 				switch spectrumModelTypeButton.selectedItem!.tag { | 				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 21:	model = .plus2a | ||||||
| 					case 3:		model = .plus3 | 					case 3:		model = .plus3 | ||||||
| 					default:	break | 					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,6 +12,12 @@ CONFIG += object_parallel_to_source | |||||||
| INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib | INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib | ||||||
| LIBS += -lz | LIBS += -lz | ||||||
|  |  | ||||||
|  | # If targetting X11, link against that. | ||||||
|  | linux { | ||||||
|  | 	QT += x11extras | ||||||
|  | 	LIBS += -lX11 | ||||||
|  | } | ||||||
|  |  | ||||||
| # Add flags (i) to identify that this is a Qt build; and | # Add flags (i) to identify that this is a Qt build; and | ||||||
| # (ii) to disable asserts in release builds. | # (ii) to disable asserts in release builds. | ||||||
| DEFINES += TARGET_QT | DEFINES += TARGET_QT | ||||||
| @@ -129,6 +135,7 @@ SOURCES += \ | |||||||
| 	$$SRC/Storage/MassStorage/Encodings/*.cpp \ | 	$$SRC/Storage/MassStorage/Encodings/*.cpp \ | ||||||
| 	$$SRC/Storage/MassStorage/Formats/*.cpp \ | 	$$SRC/Storage/MassStorage/Formats/*.cpp \ | ||||||
| 	$$SRC/Storage/MassStorage/SCSI/*.cpp \ | 	$$SRC/Storage/MassStorage/SCSI/*.cpp \ | ||||||
|  | 	$$SRC/Storage/State/*.cpp \ | ||||||
| 	$$SRC/Storage/Tape/*.cpp \ | 	$$SRC/Storage/Tape/*.cpp \ | ||||||
| 	$$SRC/Storage/Tape/Formats/*.cpp \ | 	$$SRC/Storage/Tape/Formats/*.cpp \ | ||||||
| 	$$SRC/Storage/Tape/Parsers/*.cpp \ | 	$$SRC/Storage/Tape/Parsers/*.cpp \ | ||||||
| @@ -136,7 +143,8 @@ SOURCES += \ | |||||||
| 	main.cpp \ | 	main.cpp \ | ||||||
| 	mainwindow.cpp \ | 	mainwindow.cpp \ | ||||||
| 	scantargetwidget.cpp \ | 	scantargetwidget.cpp \ | ||||||
|     timer.cpp | 	timer.cpp \ | ||||||
|  | 	keyboard.cpp | ||||||
|  |  | ||||||
| HEADERS += \ | HEADERS += \ | ||||||
| 	$$SRC/Activity/*.hpp \ | 	$$SRC/Activity/*.hpp \ | ||||||
| @@ -269,6 +277,7 @@ HEADERS += \ | |||||||
| 	$$SRC/Storage/MassStorage/Encodings/*.hpp \ | 	$$SRC/Storage/MassStorage/Encodings/*.hpp \ | ||||||
| 	$$SRC/Storage/MassStorage/Formats/*.hpp \ | 	$$SRC/Storage/MassStorage/Formats/*.hpp \ | ||||||
| 	$$SRC/Storage/MassStorage/SCSI/*.hpp \ | 	$$SRC/Storage/MassStorage/SCSI/*.hpp \ | ||||||
|  | 	$$SRC/Storage/State/*.hpp \ | ||||||
| 	$$SRC/Storage/Tape/*.hpp \ | 	$$SRC/Storage/Tape/*.hpp \ | ||||||
| 	$$SRC/Storage/Tape/Formats/*.hpp \ | 	$$SRC/Storage/Tape/Formats/*.hpp \ | ||||||
| 	$$SRC/Storage/Tape/Parsers/*.hpp \ | 	$$SRC/Storage/Tape/Parsers/*.hpp \ | ||||||
| @@ -278,6 +287,7 @@ HEADERS += \ | |||||||
| 	mainwindow.h \ | 	mainwindow.h \ | ||||||
| 	scantargetwidget.h \ | 	scantargetwidget.h \ | ||||||
| 	settings.h \ | 	settings.h \ | ||||||
|  | 	keyboard.h \ | ||||||
| 	timer.h | 	timer.h | ||||||
|  |  | ||||||
| FORMS += \ | FORMS += \ | ||||||
|   | |||||||
							
								
								
									
										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); | 	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) { | bool MainWindow::processEvent(QKeyEvent *event) { | ||||||
| 	if(!machine) return true; | 	if(!machine) return true; | ||||||
|  |  | ||||||
| 	const auto key = keyForEvent(event); | 	const auto key = keyMapper.keyForEvent(event); | ||||||
| 	if(!key) return true; | 	if(!key) return true; | ||||||
|  |  | ||||||
| 	const bool isPressed = event->type() == QEvent::KeyPress; | 	const bool isPressed = event->type() == QEvent::KeyPress; | ||||||
| @@ -1258,9 +1092,13 @@ void MainWindow::start_spectrum() { | |||||||
| 	using Target = Analyser::Static::ZXSpectrum::Target; | 	using Target = Analyser::Static::ZXSpectrum::Target; | ||||||
| 	auto target = std::make_unique<Target>(); | 	auto target = std::make_unique<Target>(); | ||||||
|  |  | ||||||
| 	switch(ui->oricModelComboBox->currentIndex()) { | 	switch(ui->spectrumModelComboBox->currentIndex()) { | ||||||
| 		default:	target->model = Target::Model::Plus2a;	break; | 		default:	target->model = Target::Model::SixteenK;		break; | ||||||
| 		case 1:		target->model = Target::Model::Plus3;	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)); | 	launchTarget(std::move(target)); | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
| #include "timer.h" | #include "timer.h" | ||||||
| #include "ui_mainwindow.h" | #include "ui_mainwindow.h" | ||||||
| #include "functionthread.h" | #include "functionthread.h" | ||||||
|  | #include "keyboard.h" | ||||||
|  |  | ||||||
| #include "../../Analyser/Static/StaticAnalyser.hpp" | #include "../../Analyser/Static/StaticAnalyser.hpp" | ||||||
| #include "../../Machines/Utility/MachineForTarget.hpp" | #include "../../Machines/Utility/MachineForTarget.hpp" | ||||||
| @@ -144,7 +145,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat | |||||||
|  |  | ||||||
| 		QMenu *inputMenu = nullptr; | 		QMenu *inputMenu = nullptr; | ||||||
|  |  | ||||||
| 		std::optional<Inputs::Keyboard::Key> keyForEvent(QKeyEvent *); | 		KeyboardMapper keyMapper; | ||||||
|  |  | ||||||
| 		void register_led(const std::string &) override; | 		void register_led(const std::string &) override; | ||||||
| 		void set_led_status(const std::string &, bool) override; | 		void set_led_status(const std::string &, bool) override; | ||||||
|   | |||||||
| @@ -551,6 +551,26 @@ | |||||||
|             </item> |             </item> | ||||||
|             <item row="0" column="1"> |             <item row="0" column="1"> | ||||||
|              <widget class="QComboBox" name="spectrumModelComboBox"> |              <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> |               <item> | ||||||
|                <property name="text"> |                <property name="text"> | ||||||
|                 <string>+2a</string> |                 <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/Encodings/*.cpp') | ||||||
| SOURCES += glob.glob('../../Storage/MassStorage/Formats/*.cpp') | SOURCES += glob.glob('../../Storage/MassStorage/Formats/*.cpp') | ||||||
| SOURCES += glob.glob('../../Storage/MassStorage/SCSI/*.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/*.cpp') | ||||||
| SOURCES += glob.glob('../../Storage/Tape/Formats/*.cpp') | SOURCES += glob.glob('../../Storage/Tape/Formats/*.cpp') | ||||||
| SOURCES += glob.glob('../../Storage/Tape/Parsers/*.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::L:						return hl_.halves.low; | ||||||
| 		case Register::HL:						return hl_.full; | 		case Register::HL:						return hl_.full; | ||||||
|  |  | ||||||
| 		case Register::ADash:					return afDash_.halves.high; | 		case Register::ADash:					return af_dash_.halves.high; | ||||||
| 		case Register::FlagsDash:				return afDash_.halves.low; | 		case Register::FlagsDash:				return af_dash_.halves.low; | ||||||
| 		case Register::AFDash:					return afDash_.full; | 		case Register::AFDash:					return af_dash_.full; | ||||||
| 		case Register::BDash:					return bcDash_.halves.high; | 		case Register::BDash:					return bc_dash_.halves.high; | ||||||
| 		case Register::CDash:					return bcDash_.halves.low; | 		case Register::CDash:					return bc_dash_.halves.low; | ||||||
| 		case Register::BCDash:					return bcDash_.full; | 		case Register::BCDash:					return bc_dash_.full; | ||||||
| 		case Register::DDash:					return deDash_.halves.high; | 		case Register::DDash:					return de_dash_.halves.high; | ||||||
| 		case Register::EDash:					return deDash_.halves.low; | 		case Register::EDash:					return de_dash_.halves.low; | ||||||
| 		case Register::DEDash:					return deDash_.full; | 		case Register::DEDash:					return de_dash_.full; | ||||||
| 		case Register::HDash:					return hlDash_.halves.high; | 		case Register::HDash:					return hl_dash_.halves.high; | ||||||
| 		case Register::LDash:					return hlDash_.halves.low; | 		case Register::LDash:					return hl_dash_.halves.low; | ||||||
| 		case Register::HLDash:					return hlDash_.full; | 		case Register::HLDash:					return hl_dash_.full; | ||||||
|  |  | ||||||
| 		case Register::IXh:						return ix_.halves.high; | 		case Register::IXh:						return ix_.halves.high; | ||||||
| 		case Register::IXl:						return ix_.halves.low; | 		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::L:				hl_.halves.low = uint8_t(value);	break; | ||||||
| 		case Register::HL:				hl_.full = value;					break; | 		case Register::HL:				hl_.full = value;					break; | ||||||
|  |  | ||||||
| 		case Register::ADash:			afDash_.halves.high = uint8_t(value);	break; | 		case Register::ADash:			af_dash_.halves.high = uint8_t(value);	break; | ||||||
| 		case Register::FlagsDash:		afDash_.halves.low = uint8_t(value);	break; | 		case Register::FlagsDash:		af_dash_.halves.low = uint8_t(value);	break; | ||||||
| 		case Register::AFDash:			afDash_.full = value;					break; | 		case Register::AFDash:			af_dash_.full = value;					break; | ||||||
| 		case Register::BDash:			bcDash_.halves.high = uint8_t(value);	break; | 		case Register::BDash:			bc_dash_.halves.high = uint8_t(value);	break; | ||||||
| 		case Register::CDash:			bcDash_.halves.low = uint8_t(value);	break; | 		case Register::CDash:			bc_dash_.halves.low = uint8_t(value);	break; | ||||||
| 		case Register::BCDash:			bcDash_.full = value;					break; | 		case Register::BCDash:			bc_dash_.full = value;					break; | ||||||
| 		case Register::DDash:			deDash_.halves.high = uint8_t(value);	break; | 		case Register::DDash:			de_dash_.halves.high = uint8_t(value);	break; | ||||||
| 		case Register::EDash:			deDash_.halves.low = uint8_t(value);	break; | 		case Register::EDash:			de_dash_.halves.low = uint8_t(value);	break; | ||||||
| 		case Register::DEDash:			deDash_.full = value;					break; | 		case Register::DEDash:			de_dash_.full = value;					break; | ||||||
| 		case Register::HDash:			hlDash_.halves.high = uint8_t(value);	break; | 		case Register::HDash:			hl_dash_.halves.high = uint8_t(value);	break; | ||||||
| 		case Register::LDash:			hlDash_.halves.low = uint8_t(value);	break; | 		case Register::LDash:			hl_dash_.halves.low = uint8_t(value);	break; | ||||||
| 		case Register::HLDash:			hlDash_.full = value;					break; | 		case Register::HLDash:			hl_dash_.full = value;					break; | ||||||
|  |  | ||||||
| 		case Register::IXh:				ix_.halves.high = uint8_t(value);		break; | 		case Register::IXh:				ix_.halves.high = uint8_t(value);		break; | ||||||
| 		case Register::IXl:				ix_.halves.low = 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; | 					number_of_cycles_ -= operation->machine_cycle.length; | ||||||
| 					last_request_status_ = request_status_; | 					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); | 					number_of_cycles_ -= bus_handler_.perform_machine_cycle(operation->machine_cycle); | ||||||
| 					if(uses_bus_request && bus_request_line_) goto do_bus_acknowledge; | 					if(uses_bus_request && bus_request_line_) goto do_bus_acknowledge; | ||||||
| 				break; | 				break; | ||||||
| 				case MicroOp::MoveToNextProgram: | 				case MicroOp::MoveToNextProgram: | ||||||
| 					advance_operation(); | 					advance_operation(); | ||||||
| 				break; | 				break; | ||||||
| 				case MicroOp::DecodeOperation: | 				case MicroOp::IncrementR: | ||||||
| 					refresh_addr_ = ir_; | 					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_); | 					pc_.full += pc_increment_ & uint16_t(halt_mask_); | ||||||
| 					scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_]; | 					scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_]; | ||||||
| 					flag_adjustment_history_ <<= 1; | 					flag_adjustment_history_ <<= 1; | ||||||
| 				break; | 				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::Increment8NoFlags:	++ *static_cast<uint8_t *>(operation->source);			break; | ||||||
| 				case MicroOp::Increment16:			++ *static_cast<uint16_t *>(operation->source);			break; | 				case MicroOp::Increment16:			++ *static_cast<uint16_t *>(operation->source);			break; | ||||||
| @@ -460,17 +461,17 @@ template <	class T, | |||||||
| 				case MicroOp::ExAFAFDash: { | 				case MicroOp::ExAFAFDash: { | ||||||
| 					const uint8_t a = a_; | 					const uint8_t a = a_; | ||||||
| 					const uint8_t f = get_flags(); | 					const uint8_t f = get_flags(); | ||||||
| 					set_flags(afDash_.halves.low); | 					set_flags(af_dash_.halves.low); | ||||||
| 					a_ = afDash_.halves.high; | 					a_ = af_dash_.halves.high; | ||||||
| 					afDash_.halves.high = a; | 					af_dash_.halves.high = a; | ||||||
| 					afDash_.halves.low = f; | 					af_dash_.halves.low = f; | ||||||
| 				} break; | 				} break; | ||||||
|  |  | ||||||
| 				case MicroOp::EXX: { | 				case MicroOp::EXX: { | ||||||
| 					uint16_t temp; | 					uint16_t temp; | ||||||
| 					swap(de_, deDash_); | 					swap(de_, de_dash_); | ||||||
| 					swap(bc_, bcDash_); | 					swap(bc_, bc_dash_); | ||||||
| 					swap(hl_, hlDash_); | 					swap(hl_, hl_dash_); | ||||||
| 				} break; | 				} break; | ||||||
|  |  | ||||||
| #undef swap | #undef swap | ||||||
| @@ -927,7 +928,7 @@ template <	class T, | |||||||
| 	return wait_line_; | 	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, | template <	class T, | ||||||
| 			bool uses_bus_request, | 			bool uses_bus_request, | ||||||
|   | |||||||
| @@ -20,14 +20,14 @@ ProcessorStorage::ProcessorStorage() { | |||||||
| #define ReadOpcodeWait(f)			PartialMachineCycle(PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f) | #define ReadOpcodeWait(f)			PartialMachineCycle(PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f) | ||||||
| #define ReadOpcodeEnd()				PartialMachineCycle(PartialMachineCycle::ReadOpcode, HalfCycles(1), &pc_.full, &operation_, false) | #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 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 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 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 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) | #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 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 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 IntAckStart(length, val)	PartialMachineCycle(PartialMachineCycle::InterruptStart, HalfCycles(length), &pc_.full, &val, false) | ||||||
| #define IntWait(val)				PartialMachineCycle(PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true) | #define IntWait(val)				PartialMachineCycle(PartialMachineCycle::InterruptWait, HalfCycles(2), &pc_.full, &val, true) | ||||||
| #define IntAckEnd(val)				PartialMachineCycle(PartialMachineCycle::Interrupt, HalfCycles(3), nullptr, &val, false) | #define IntAckEnd(val)				PartialMachineCycle(PartialMachineCycle::Interrupt, HalfCycles(3), &pc_.full, &val, false) | ||||||
|  |  | ||||||
|  |  | ||||||
| // A wrapper to express a bus operation as a micro-op | // A wrapper to express a bus operation as a micro-op | ||||||
| #define BusOp(op)					{MicroOp::BusOperation, nullptr, nullptr, op} | #define BusOp(op)					{MicroOp::BusOperation, nullptr, nullptr, op} | ||||||
|  |  | ||||||
| // Compound bus operations, as micro-ops | // Compound bus operations, as micro-ops | ||||||
|  | #define InternalOperation(len)		{MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, HalfCycles(len), &last_address_bus_, nullptr, false}} | ||||||
| // Read3 is a standard read cycle: 1.5 cycles, then check the wait line, then 1.5 cycles; | #define Read(addr, val)				BusOp(ReadStart(addr, val)), BusOp(ReadWait(addr, val)), BusOp(ReadEnd(addr, val)) | ||||||
| // 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; | #define Write(addr, val)			BusOp(WriteStart(addr, val)), BusOp(WriteWait(addr, val)), BusOp(WriteEnd(addr, val)) | ||||||
| // 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 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 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}} |  | ||||||
|  |  | ||||||
| /// A sequence is a series of micro-ops that ends in a move-to-next-program operation. | /// A sequence is a series of micro-ops that ends in a move-to-next-program operation. | ||||||
| #define Sequence(...)				{ __VA_ARGS__, {MicroOp::MoveToNextProgram} } | #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 | // 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 | // 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 | // 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 Inc16(r)				{(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full} | ||||||
| #define Inc8NoFlags(r)			{MicroOp::Increment8NoFlags, &r} | #define Inc8NoFlags(r)			{MicroOp::Increment8NoFlags, &r} | ||||||
|  |  | ||||||
| #define ReadInc(addr, val)		Read3(addr, val), Inc16(addr) | #define ReadInc(addr, val)		Read(addr, val), Inc16(addr) | ||||||
| #define Read4Inc(addr, val)		Read4(addr, val), Inc16(addr) | #define WriteInc(addr, val)		Write(addr, val), {MicroOp::Increment16, &addr.full} | ||||||
| #define Read5Inc(addr, val)		Read5(addr, val), Inc16(addr) |  | ||||||
| #define WriteInc(addr, val)		Write3(addr, val), {MicroOp::Increment16, &addr.full} |  | ||||||
|  |  | ||||||
| #define Read16Inc(addr, val)	ReadInc(addr, val.halves.low), ReadInc(addr, val.halves.high) | #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 INDEX()					{MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &index} | ||||||
| #define FINDEX()				{MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), {MicroOp::CalculateIndexAddress, &index} | #define FINDEX()				{MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), {MicroOp::CalculateIndexAddress, &index} | ||||||
| #define INDEX_ADDR()			(add_offsets ? memptr_ : 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 Push(x)					{MicroOp::Decrement16, &sp_.full}, Write(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write(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 Pop(x)					Read(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read(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} |  | ||||||
|  |  | ||||||
| /* The following are actual instructions */ | /* 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 JP(cc)					Sequence(Read16Inc(pc_, memptr_), {MicroOp::cc}, {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 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)					Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {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)					StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {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()					Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {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)				StdInstr({MicroOp::Move8, &b, &a}) | #define LD(a, b)				Sequence({MicroOp::Move8, &b, &a}) | ||||||
|  |  | ||||||
| #define LD_GROUP(r, ri)	\ | #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, 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),		\ | 				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_) | 				LD(r, a_) | ||||||
|  |  | ||||||
| #define READ_OP_GROUP(op)	\ | #define READ_OP_GROUP(op)	\ | ||||||
| 				StdInstr({MicroOp::op, &bc_.halves.high}),		StdInstr({MicroOp::op, &bc_.halves.low}),	\ | 				Sequence({MicroOp::op, &bc_.halves.high}),		Sequence({MicroOp::op, &bc_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &de_.halves.high}),		StdInstr({MicroOp::op, &de_.halves.low}),	\ | 				Sequence({MicroOp::op, &de_.halves.high}),		Sequence({MicroOp::op, &de_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &index.halves.high}),	StdInstr({MicroOp::op, &index.halves.low}),	\ | 				Sequence({MicroOp::op, &index.halves.high}),	Sequence({MicroOp::op, &index.halves.low}),	\ | ||||||
| 				StdInstr(INDEX(), Read3(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr({MicroOp::op, &a_}) | 				Sequence({MicroOp::op, &a_}) | ||||||
|  |  | ||||||
| #define READ_OP_GROUP_D(op)	\ | #define READ_OP_GROUP_D(op)	\ | ||||||
| 				StdInstr({MicroOp::op, &bc_.halves.high}),		StdInstr({MicroOp::op, &bc_.halves.low}),	\ | 				Sequence({MicroOp::op, &bc_.halves.high}),		Sequence({MicroOp::op, &bc_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &de_.halves.high}),		StdInstr({MicroOp::op, &de_.halves.low}),	\ | 				Sequence({MicroOp::op, &de_.halves.high}),		Sequence({MicroOp::op, &de_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &index.halves.high}),	StdInstr({MicroOp::op, &index.halves.low}),	\ | 				Sequence({MicroOp::op, &index.halves.high}),	Sequence({MicroOp::op, &index.halves.low}),	\ | ||||||
| 				StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr({MicroOp::op, &a_}) | 				Sequence({MicroOp::op, &a_}) | ||||||
|  |  | ||||||
| #define RMW(x, op, ...) StdInstr(INDEX(), Read4Pre(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, ...) StdInstr(Read4(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(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)	\ | #define MODIFY_OP_GROUP(op)	\ | ||||||
| 				StdInstr({MicroOp::op, &bc_.halves.high}),		StdInstr({MicroOp::op, &bc_.halves.low}),	\ | 				Sequence({MicroOp::op, &bc_.halves.high}),		Sequence({MicroOp::op, &bc_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &de_.halves.high}),		StdInstr({MicroOp::op, &de_.halves.low}),	\ | 				Sequence({MicroOp::op, &de_.halves.high}),		Sequence({MicroOp::op, &de_.halves.low}),	\ | ||||||
| 				StdInstr({MicroOp::op, &index.halves.high}),	StdInstr({MicroOp::op, &index.halves.low}),	\ | 				Sequence({MicroOp::op, &index.halves.high}),	Sequence({MicroOp::op, &index.halves.low}),	\ | ||||||
| 				RMW(temp8_, op),	\ | 				RMW(temp8_, op),	\ | ||||||
| 				StdInstr({MicroOp::op, &a_}) | 				Sequence({MicroOp::op, &a_}) | ||||||
|  |  | ||||||
| #define IX_MODIFY_OP_GROUP(op)	\ | #define IX_MODIFY_OP_GROUP(op)	\ | ||||||
| 				RMWI(bc_.halves.high, op),	\ | 				RMWI(bc_.halves.high, op),	\ | ||||||
| @@ -153,18 +131,18 @@ ProcessorStorage::ProcessorStorage() { | |||||||
| 				RMWI(a_, op) | 				RMWI(a_, op) | ||||||
|  |  | ||||||
| #define IX_READ_OP_GROUP(op)	\ | #define IX_READ_OP_GROUP(op)	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}),	\ | 				Sequence(Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::op, &temp8_}),	\ | ||||||
| 				StdInstr(Read4(INDEX_ADDR(), temp8_), {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 ADD16(d, s) Sequence(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 ADC16(d, s) Sequence(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 SBC16(d, s) Sequence(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full}) | ||||||
|  |  | ||||||
| void ProcessorStorage::install_default_instruction_set() { | void ProcessorStorage::install_default_instruction_set() { | ||||||
| 	MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, memptr_.halves.high)); | 	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_base_page(fd_page_, iy_, true, fdcb_page_); | ||||||
| 	assemble_ed_page(ed_page_); | 	assemble_ed_page(ed_page_); | ||||||
|  |  | ||||||
| 	fdcb_page_.r_step = 0; |  | ||||||
| 	fd_page_.is_indexed = true; | 	fd_page_.is_indexed = true; | ||||||
| 	fdcb_page_.is_indexed = true; | 	fdcb_page_.is_indexed = true; | ||||||
|  |  | ||||||
| 	ddcb_page_.r_step = 0; |  | ||||||
| 	dd_page_.is_indexed = true; | 	dd_page_.is_indexed = true; | ||||||
| 	ddcb_page_.is_indexed = true; | 	ddcb_page_.is_indexed = true; | ||||||
|  |  | ||||||
| @@ -202,9 +177,10 @@ void ProcessorStorage::install_default_instruction_set() { | |||||||
| 		BusOp(ReadOpcodeStart()), | 		BusOp(ReadOpcodeStart()), | ||||||
| 		BusOp(ReadOpcodeWait(true)), | 		BusOp(ReadOpcodeWait(true)), | ||||||
| 		BusOp(ReadOpcodeEnd()), | 		BusOp(ReadOpcodeEnd()), | ||||||
| 		BusOp(Refresh(6)), | 		BusOp(Refresh()), | ||||||
|  | 		InternalOperation(2), | ||||||
| 		Push(pc_), | 		Push(pc_), | ||||||
| 		{ MicroOp::JumpTo66, nullptr, nullptr}, | 		{ MicroOp::JumpTo66 }, | ||||||
| 		{ MicroOp::MoveToNextProgram } | 		{ MicroOp::MoveToNextProgram } | ||||||
| 	}; | 	}; | ||||||
| 	MicroOp irq_mode0_program[] = { | 	MicroOp irq_mode0_program[] = { | ||||||
| @@ -212,14 +188,14 @@ void ProcessorStorage::install_default_instruction_set() { | |||||||
| 		BusOp(IntAckStart(5, operation_)), | 		BusOp(IntAckStart(5, operation_)), | ||||||
| 		BusOp(IntWait(operation_)), | 		BusOp(IntWait(operation_)), | ||||||
| 		BusOp(IntAckEnd(operation_)), | 		BusOp(IntAckEnd(operation_)), | ||||||
| 		{ MicroOp::DecodeOperationNoRChange } | 		{ MicroOp::DecodeOperation } | ||||||
| 	}; | 	}; | ||||||
| 	MicroOp irq_mode1_program[] = { | 	MicroOp irq_mode1_program[] = { | ||||||
| 		{ MicroOp::BeginIRQ }, | 		{ MicroOp::BeginIRQ }, | ||||||
| 		BusOp(IntAckStart(7, operation_)),	// 7 half cycles (including  + | 		BusOp(IntAckStart(7, operation_)),	// 7 half cycles (including  + | ||||||
| 		BusOp(IntWait(operation_)),			// [potentially 2 half cycles] + | 		BusOp(IntWait(operation_)),			// [potentially 2 half cycles] + | ||||||
| 		BusOp(IntAckEnd(operation_)),		// Implicitly 3 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 | 		Push(pc_),							// 12 half cycles = 26 half cycles = 13 cycles | ||||||
| 		{ MicroOp::Move16, &temp16_.full, &pc_.full }, | 		{ MicroOp::Move16, &temp16_.full, &pc_.full }, | ||||||
| 		{ MicroOp::MoveToNextProgram } | 		{ MicroOp::MoveToNextProgram } | ||||||
| @@ -229,7 +205,7 @@ void ProcessorStorage::install_default_instruction_set() { | |||||||
| 		BusOp(IntAckStart(7, temp16_.halves.low)), | 		BusOp(IntAckStart(7, temp16_.halves.low)), | ||||||
| 		BusOp(IntWait(temp16_.halves.low)), | 		BusOp(IntWait(temp16_.halves.low)), | ||||||
| 		BusOp(IntAckEnd(temp16_.halves.low)), | 		BusOp(IntAckEnd(temp16_.halves.low)), | ||||||
| 		BusOp(Refresh(4)), | 		BusOp(Refresh()), | ||||||
| 		Push(pc_), | 		Push(pc_), | ||||||
| 		{ MicroOp::Move8, &ir_.halves.high, &temp16_.halves.high }, | 		{ MicroOp::Move8, &ir_.halves.high, &temp16_.halves.high }, | ||||||
| 		Read16(temp16_, pc_), | 		Read16(temp16_, pc_), | ||||||
| @@ -244,8 +220,8 @@ void ProcessorStorage::install_default_instruction_set() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void ProcessorStorage::assemble_ed_page(InstructionPage &target) { | 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 IN_C(r)		Sequence({MicroOp::Move16, &bc_.full, &memptr_.full}, Input(bc_, r), {MicroOp::SetInFlags, &r}) | ||||||
| #define OUT_C(r)	StdInstr(Output(bc_, r), {MicroOp::SetOutFlags}) | #define OUT_C(r)	Sequence(Output(bc_, r), {MicroOp::SetOutFlags}) | ||||||
| #define IN_OUT(r)	IN_C(r), OUT_C(r) | #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 | #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(),	/* 0x20 */ | ||||||
| 		NOP_ROW(),	/* 0x30 */ | 		NOP_ROW(),	/* 0x30 */ | ||||||
| 		/* 0x40 IN B, (C);	0x41 OUT (C), B */	IN_OUT(bc_.halves.high), | 		/* 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_)), | 		/* 0x42 SBC HL, BC */	SBC16(hl_, bc_),				/* 0x43 LD (nn), BC */	Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, bc_)), | ||||||
| 		/* 0x44 NEG */			StdInstr({MicroOp::NEG}),		/* 0x45 RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x44 NEG */			Sequence({MicroOp::NEG}),		/* 0x45 RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x46 IM 0 */			StdInstr({MicroOp::IM}),		/* 0x47 LD I, A */		Instr(6, {MicroOp::Move8, &a_, &ir_.halves.high}), | 		/* 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), | 		/* 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_)), | 		/* 0x4a ADC HL, BC */	ADC16(hl_, bc_),				/* 0x4b LD BC, (nn) */	Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, bc_)), | ||||||
| 		/* 0x4c NEG */			StdInstr({MicroOp::NEG}),		/* 0x4d RETI */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x4c NEG */			Sequence({MicroOp::NEG}),		/* 0x4d RETI */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x4e IM 0/1 */		StdInstr({MicroOp::IM}),		/* 0x4f LD R, A */		Instr(6, {MicroOp::Move8, &a_, &ir_.halves.low}), | 		/* 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), | 		/* 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_)), | 		/* 0x52 SBC HL, DE */	SBC16(hl_, de_),				/* 0x53 LD (nn), DE */	Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, de_)), | ||||||
| 		/* 0x54 NEG */			StdInstr({MicroOp::NEG}),		/* 0x55 RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x54 NEG */			Sequence({MicroOp::NEG}),		/* 0x55 RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x56 IM 1 */			StdInstr({MicroOp::IM}),		/* 0x57 LD A, I */		Instr(6, {MicroOp::Move8, &ir_.halves.high, &a_}, {MicroOp::SetAFlags}), | 		/* 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), | 		/* 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_)), | 		/* 0x5a ADC HL, DE */	ADC16(hl_, de_),				/* 0x5b LD DE, (nn) */	Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, de_)), | ||||||
| 		/* 0x5c NEG */			StdInstr({MicroOp::NEG}),		/* 0x5d RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x5c NEG */			Sequence({MicroOp::NEG}),		/* 0x5d RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x5e IM 2 */			StdInstr({MicroOp::IM}),		/* 0x5f LD A, R */		Instr(6, {MicroOp::Move8, &ir_.halves.low, &a_}, {MicroOp::SetAFlags}), | 		/* 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), | 		/* 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_)), | 		/* 0x62 SBC HL, HL */	SBC16(hl_, hl_),				/* 0x63 LD (nn), HL */	Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, hl_)), | ||||||
| 		/* 0x64 NEG */			StdInstr({MicroOp::NEG}),		/* 0x65 RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x64 NEG */			Sequence({MicroOp::NEG}),		/* 0x65 RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x66 IM 0 */			StdInstr({MicroOp::IM}),		/* 0x67 RRD */			StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RRD}, Write3(hl_, temp8_)), | 		/* 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), | 		/* 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_)), | 		/* 0x6a ADC HL, HL */	ADC16(hl_, hl_),				/* 0x6b LD HL, (nn) */	Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, hl_)), | ||||||
| 		/* 0x6c NEG */			StdInstr({MicroOp::NEG}),		/* 0x6d RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x6c NEG */			Sequence({MicroOp::NEG}),		/* 0x6d RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x6e IM 0/1 */		StdInstr({MicroOp::IM}),		/* 0x6f RLD */			StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RLD}, Write3(hl_, temp8_)), | 		/* 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 */	StdInstr({MicroOp::SetZero}, Output(bc_, temp8_), {MicroOp::SetOutFlags}), | 		/* 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 */	StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, sp_)), | 		/* 0x72 SBC HL, SP */	SBC16(hl_, sp_),				/* 0x73 LD (nn), SP */	Sequence(Read16Inc(pc_, memptr_), Write16(memptr_, sp_)), | ||||||
| 		/* 0x74 NEG */			StdInstr({MicroOp::NEG}),		/* 0x75 RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x74 NEG */			Sequence({MicroOp::NEG}),		/* 0x75 RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x76 IM 1 */			StdInstr({MicroOp::IM}),		/* 0x77 XX */			NOP, | 		/* 0x76 IM 1 */			Sequence({MicroOp::IM}),		/* 0x77 XX */			NOP, | ||||||
| 		/* 0x78 IN A, (C);	0x79 OUT (C), A */	IN_OUT(a_), | 		/* 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_)), | 		/* 0x7a ADC HL, SP */	ADC16(hl_, sp_),				/* 0x7b LD SP, (nn) */	Sequence(Read16Inc(pc_, memptr_), Read16(memptr_, sp_)), | ||||||
| 		/* 0x7c NEG */			StdInstr({MicroOp::NEG}),		/* 0x7d RETN */			StdInstr(Pop(pc_), {MicroOp::RETN}), | 		/* 0x7c NEG */			Sequence({MicroOp::NEG}),		/* 0x7d RETN */			Sequence(Pop(pc_), {MicroOp::RETN}), | ||||||
| 		/* 0x7e IM 2 */			StdInstr({MicroOp::IM}),		/* 0x7f XX */			NOP, | 		/* 0x7e IM 2 */			Sequence({MicroOp::IM}),		/* 0x7f XX */			NOP, | ||||||
| 		NOP_ROW(),	/* 0x80 ... 0x8f */ | 		NOP_ROW(),	/* 0x80 ... 0x8f */ | ||||||
| 		NOP_ROW(),	/* 0x90 ... 0x9f */ | 		NOP_ROW(),	/* 0x90 ... 0x9f */ | ||||||
| 		/* 0xa0 LDI */		StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDI}), | 		/* 0xa0 LDI */		Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDI}), | ||||||
| 		/* 0xa1 CPI */		StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPI}), | 		/* 0xa1 CPI */		Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPI}), | ||||||
| 		/* 0xa2 INI */		Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INI}), | 		/* 0xa2 INI */		Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INI}), | ||||||
| 		/* 0xa3 OTI */		Instr(6, Read3(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_)), | 		/* 0xa3 OTI */		Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_)), | ||||||
| 		NOP, NOP, NOP, NOP, | 		NOP, NOP, NOP, NOP, | ||||||
| 		/* 0xa8 LDD */		StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDD}), | 		/* 0xa8 LDD */		Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDD}), | ||||||
| 		/* 0xa9 CPD */		StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPD}), | 		/* 0xa9 CPD */		Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPD}), | ||||||
| 		/* 0xaa IND */		Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::IND}), | 		/* 0xaa IND */		Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::IND}), | ||||||
| 		/* 0xab OTD */		Instr(6, Read3(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_)), | 		/* 0xab OTD */		Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_)), | ||||||
| 		NOP, NOP, NOP, NOP, | 		NOP, NOP, NOP, NOP, | ||||||
| 		/* 0xb0 LDIR */		StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDIR}, InternalOperation(10)), | 		/* 0xb0 LDIR */		Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDIR}, InternalOperation(10)), | ||||||
| 		/* 0xb1 CPIR */		StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPIR}, InternalOperation(10)), | 		/* 0xb1 CPIR */		Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPIR}, InternalOperation(10)), | ||||||
| 		/* 0xb2 INIR */		Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INIR}, InternalOperation(10)), | 		/* 0xb2 INIR */		Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INIR}, InternalOperation(10)), | ||||||
| 		/* 0xb3 OTIR */		Instr(6, Read3(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)), | 		/* 0xb3 OTIR */		Sequence(InternalOperation(2), Read(hl_, temp8_), {MicroOp::OUTI}, Output(bc_, temp8_), {MicroOp::OUT_R}, InternalOperation(10)), | ||||||
| 		NOP, NOP, NOP, NOP, | 		NOP, NOP, NOP, NOP, | ||||||
| 		/* 0xb8 LDDR */		StdInstr(Read3(hl_, temp8_), Write5(de_, temp8_), {MicroOp::LDDR}, InternalOperation(10)), | 		/* 0xb8 LDDR */		Sequence(Read(hl_, temp8_), Write(de_, temp8_), InternalOperation(4), {MicroOp::LDDR}, InternalOperation(10)), | ||||||
| 		/* 0xb9 CPDR */		StdInstr(Read3(hl_, temp8_), InternalOperation(10), {MicroOp::CPDR}, InternalOperation(10)), | 		/* 0xb9 CPDR */		Sequence(Read(hl_, temp8_), InternalOperation(10), {MicroOp::CPDR}, InternalOperation(10)), | ||||||
| 		/* 0xba INDR */		Instr(6, Input(bc_, temp8_), Write3(hl_, temp8_), {MicroOp::INDR}, InternalOperation(10)), | 		/* 0xba INDR */		Sequence(InternalOperation(2), Input(bc_, temp8_), Write(hl_, temp8_), {MicroOp::INDR}, InternalOperation(10)), | ||||||
| 		/* 0xbb OTDR */		Instr(6, Read3(hl_, temp8_), {MicroOp::OUTD}, Output(bc_, temp8_), {MicroOp::OUT_R}, 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, NOP, NOP, NOP, | ||||||
| 		NOP_ROW(),	/* 0xc0 */ | 		NOP_ROW(),	/* 0xc0 */ | ||||||
| 		NOP_ROW(),	/* 0xd0 */ | 		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) { | void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets, InstructionPage &cb_page) { | ||||||
| #define INC_DEC_LD(r)	\ | #define INC_DEC_LD(r)	\ | ||||||
| 				StdInstr({MicroOp::Increment8, &r}),	\ | 				Sequence({MicroOp::Increment8, &r}),	\ | ||||||
| 				StdInstr({MicroOp::Decrement8, &r}),	\ | 				Sequence({MicroOp::Decrement8, &r}),	\ | ||||||
| 				StdInstr(ReadInc(pc_, r)) | 				Sequence(ReadInc(pc_, r)) | ||||||
|  |  | ||||||
| #define INC_INC_DEC_LD(rf, 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)	\ | #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 = { | 	InstructionTable base_program_table = { | ||||||
| 		/* 0x00 NOP */			NOP,								/* 0x01 LD BC, nn */	StdInstr(Read16Inc(pc_, bc_)), | 		/* 0x00 NOP */			NOP,								/* 0x01 LD BC, nn */	Sequence(Read16Inc(pc_, bc_)), | ||||||
| 		/* 0x02 LD (BC), A */	StdInstr({MicroOp::SetAddrAMemptr, &bc_.full}, Write3(bc_, a_)), | 		/* 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 */ | 		/* 0x03 INC BC;	0x04 INC B;	0x05 DEC B;	0x06 LD B, n */ | ||||||
| 		INC_INC_DEC_LD(bc_, bc_.halves.high), | 		INC_INC_DEC_LD(bc_, bc_.halves.high), | ||||||
|  |  | ||||||
| 		/* 0x07 RLCA */			StdInstr({MicroOp::RLCA}), | 		/* 0x07 RLCA */			Sequence({MicroOp::RLCA}), | ||||||
| 		/* 0x08 EX AF, AF' */	StdInstr({MicroOp::ExAFAFDash}),	/* 0x09 ADD HL, BC */	ADD16(index, bc_), | 		/* 0x08 EX AF, AF' */	Sequence({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_)), | 		/* 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 */ | 		/* 0x0b DEC BC;	0x0c INC C; 0x0d DEC C; 0x0e LD C, n */ | ||||||
| 		DEC_INC_DEC_LD(bc_, bc_.halves.low), | 		DEC_INC_DEC_LD(bc_, bc_.halves.low), | ||||||
|  |  | ||||||
| 		/* 0x0f RRCA */			StdInstr({MicroOp::RRCA}), | 		/* 0x0f RRCA */			Sequence({MicroOp::RRCA}), | ||||||
| 		/* 0x10 DJNZ */			Instr(6, ReadInc(pc_, temp8_), {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}), | 		/* 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 */	StdInstr(Read16Inc(pc_, de_)), | 		/* 0x11 LD DE, nn */	Sequence(Read16Inc(pc_, de_)), | ||||||
| 		/* 0x12 LD (DE), A */	StdInstr({MicroOp::SetAddrAMemptr, &de_.full}, Write3(de_, a_)), | 		/* 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 */ | 		/* 0x13 INC DE;	0x14 INC D;	0x15 DEC D;	0x16 LD D, n */ | ||||||
| 		INC_INC_DEC_LD(de_, de_.halves.high), | 		INC_INC_DEC_LD(de_, de_.halves.high), | ||||||
|  |  | ||||||
| 		/* 0x17 RLA */			StdInstr({MicroOp::RLA}), | 		/* 0x17 RLA */			Sequence({MicroOp::RLA}), | ||||||
| 		/* 0x18 JR */			StdInstr(ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}), | 		/* 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_), | 		/* 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 */ | 		/* 0x1b DEC DE;	0x1c INC E; 0x1d DEC E; 0x1e LD E, n */ | ||||||
| 		DEC_INC_DEC_LD(de_, de_.halves.low), | 		DEC_INC_DEC_LD(de_, de_.halves.low), | ||||||
|  |  | ||||||
| 		/* 0x1f RRA */			StdInstr({MicroOp::RRA}), | 		/* 0x1f RRA */			Sequence({MicroOp::RRA}), | ||||||
| 		/* 0x20 JR NZ */		JR(TestNZ),							 /* 0x21 LD HL, nn */	StdInstr(Read16Inc(pc_, index)), | 		/* 0x20 JR NZ */		JR(TestNZ),							 /* 0x21 LD HL, nn */	Sequence(Read16Inc(pc_, index)), | ||||||
| 		/* 0x22 LD (nn), HL */	StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, 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 */ | 		/* 0x23 INC HL;	0x24 INC H;	0x25 DEC H;	0x26 LD H, n */ | ||||||
| 		INC_INC_DEC_LD(index, index.halves.high), | 		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), | 		/* 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 */ | 		/* 0x2b DEC HL;	0x2c INC L; 0x2d DEC L; 0x2e LD L, n */ | ||||||
| 		DEC_INC_DEC_LD(index, index.halves.low), | 		DEC_INC_DEC_LD(index, index.halves.low), | ||||||
|  |  | ||||||
| 		/* 0x2f CPL */			StdInstr({MicroOp::CPL}), | 		/* 0x2f CPL */			Sequence({MicroOp::CPL}), | ||||||
| 		/* 0x30 JR NC */		JR(TestNC),							/* 0x31 LD SP, nn */	StdInstr(Read16Inc(pc_, sp_)), | 		/* 0x30 JR NC */		JR(TestNC),							/* 0x31 LD SP, nn */	Sequence(Read16Inc(pc_, sp_)), | ||||||
| 		/* 0x32 LD (nn), A */	StdInstr(Read16Inc(pc_, temp16_), {MicroOp::SetAddrAMemptr, &temp16_.full}, Write3(temp16_, a_)), | 		/* 0x32 LD (nn), A */	Sequence(Read16Inc(pc_, temp16_), {MicroOp::SetAddrAMemptr, &temp16_.full}, Write(temp16_, a_)), | ||||||
| 		/* 0x33 INC SP */		Instr(8, {MicroOp::Increment16, &sp_.full}), | 		/* 0x33 INC SP */		Sequence(InternalOperation(4), {MicroOp::Increment16, &sp_.full}), | ||||||
| 		/* 0x34 INC (HL) */		StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::Increment8, &temp8_}, Write3(INDEX_ADDR(), temp8_)), | 		/* 0x34 INC (HL) */		Sequence(INDEX(), Read(INDEX_ADDR(), temp8_), InternalOperation(2), {MicroOp::Increment8, &temp8_}, Write(INDEX_ADDR(), temp8_)), | ||||||
| 		/* 0x35 DEC (HL) */		StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::Decrement8, &temp8_}, Write3(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 */	StdInstr(ReadInc(pc_, temp8_), Write3(INDEX_ADDR(), temp8_)), | 		/* 0x36 LD (HL), n */	Sequence(ReadInc(pc_, temp8_), Write(INDEX_ADDR(), temp8_)), | ||||||
| 		/* 0x37 SCF */			StdInstr({MicroOp::SCF}), | 		/* 0x37 SCF */			Sequence({MicroOp::SCF}), | ||||||
| 		/* 0x38 JR C */			JR(TestC), | 		/* 0x38 JR C */			JR(TestC), | ||||||
| 		/* 0x39 ADD HL, SP */	ADD16(index, sp_), | 		/* 0x39 ADD HL, SP */	ADD16(index, sp_), | ||||||
| 		/* 0x3a LD A, (nn) */	StdInstr(Read16Inc(pc_, memptr_), Read3(memptr_, a_), Inc16(memptr_)), | 		/* 0x3a LD A, (nn) */	Sequence(Read16Inc(pc_, memptr_), Read(memptr_, a_), Inc16(memptr_)), | ||||||
| 		/* 0x3b DEC SP */		Instr(8, {MicroOp::Decrement16, &sp_.full}), | 		/* 0x3b DEC SP */		Sequence(InternalOperation(4), {MicroOp::Decrement16, &sp_.full}), | ||||||
|  |  | ||||||
| 		/* 0x3c INC A;	0x3d DEC A;	0x3e LD A, n */ | 		/* 0x3c INC A;	0x3d DEC A;	0x3e LD A, n */ | ||||||
| 		INC_DEC_LD(a_), | 		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 */ | 		/* 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), | 		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 */ | 		/* 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), | 		LD_GROUP(index.halves.low, hl_.halves.low), | ||||||
|  |  | ||||||
| 		/* 0x70 LD (HL), B */	StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.high)), | 		/* 0x70 LD (HL), B */	Sequence(INDEX(), Write(INDEX_ADDR(), bc_.halves.high)), | ||||||
| 		/* 0x71 LD (HL), C */	StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.low)), | 		/* 0x71 LD (HL), C */	Sequence(INDEX(), Write(INDEX_ADDR(), bc_.halves.low)), | ||||||
| 		/* 0x72 LD (HL), D */	StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.high)), | 		/* 0x72 LD (HL), D */	Sequence(INDEX(), Write(INDEX_ADDR(), de_.halves.high)), | ||||||
| 		/* 0x73 LD (HL), E */	StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.low)), | 		/* 0x73 LD (HL), E */	Sequence(INDEX(), Write(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; | 		/* 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 */	StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.halves.low)),	// they always store exactly H and L. | 		/* 0x75 LD (HL), L */	Sequence(INDEX(), Write(INDEX_ADDR(), hl_.halves.low)),	// they always store exactly H and L. | ||||||
| 		/* 0x76 HALT */			StdInstr({MicroOp::HALT}), | 		/* 0x76 HALT */			Sequence({MicroOp::HALT}), | ||||||
| 		/* 0x77 LD (HL), A */	StdInstr(INDEX(), Write3(INDEX_ADDR(), a_)), | 		/* 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 */ | 		/* 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_), | 		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 */ | 		/* 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), | 		READ_OP_GROUP(CP8), | ||||||
|  |  | ||||||
| 		/* 0xc0 RET NZ */	RET(TestNZ),							/* 0xc1 POP BC */	StdInstr(Pop(bc_)), | 		/* 0xc0 RET NZ */	RET(TestNZ),							/* 0xc1 POP BC */	Sequence(Pop(bc_)), | ||||||
| 		/* 0xc2 JP NZ */	JP(TestNZ),								/* 0xc3 JP nn */	StdInstr(Read16(pc_, memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}), | 		/* 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 */	Instr(6, Push(bc_)), | 		/* 0xc4 CALL NZ */	CALL(TestNZ),							/* 0xc5 PUSH BC */	Sequence(InternalOperation(2), Push(bc_)), | ||||||
| 		/* 0xc6 ADD A, n */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}), | 		/* 0xc6 ADD A, n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}), | ||||||
| 		/* 0xc7 RST 00h */	RST(), | 		/* 0xc7 RST 00h */	RST(), | ||||||
| 		/* 0xc8 RET Z */	RET(TestZ),								/* 0xc9 RET */		StdInstr(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}), | 		/* 0xc8 RET Z */	RET(TestZ),								/* 0xc9 RET */		Sequence(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}), | ||||||
| 		/* 0xca JP Z */		JP(TestZ),								/* 0xcb [CB page] */StdInstr(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}), | 		/* 0xca JP Z */		JP(TestZ),								/* 0xcb [CB page] */Sequence(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}), | 		/* 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 */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}), | 		/* 0xce ADC A, n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}), | ||||||
| 		/* 0xcf RST 08h */	RST(), | 		/* 0xcf RST 08h */	RST(), | ||||||
| 		/* 0xd0 RET NC */	RET(TestNC),							/* 0xd1 POP DE */	StdInstr(Pop(de_)), | 		/* 0xd0 RET NC */	RET(TestNC),							/* 0xd1 POP DE */	Sequence(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)), | 		/* 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 */	Instr(6, Push(de_)), | 		/* 0xd4 CALL NC */	CALL(TestNC),							/* 0xd5 PUSH DE */	Sequence(InternalOperation(2), Push(de_)), | ||||||
| 		/* 0xd6 SUB n */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}), | 		/* 0xd6 SUB n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}), | ||||||
| 		/* 0xd7 RST 10h */	RST(), | 		/* 0xd7 RST 10h */	RST(), | ||||||
| 		/* 0xd8 RET C */	RET(TestC),								/* 0xd9 EXX */		StdInstr({MicroOp::EXX}), | 		/* 0xd8 RET C */	RET(TestC),								/* 0xd9 EXX */		Sequence({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_)), | 		/* 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] */StdInstr({MicroOp::SetInstructionPage, &dd_page_}), | 		/* 0xdc CALL C */	CALL(TestC),							/* 0xdd [DD page] */Sequence({MicroOp::SetInstructionPage, &dd_page_}), | ||||||
| 		/* 0xde SBC A, n */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}), | 		/* 0xde SBC A, n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}), | ||||||
| 		/* 0xdf RST 18h */	RST(), | 		/* 0xdf RST 18h */	RST(), | ||||||
| 		/* 0xe0 RET PO */	RET(TestPO),							/* 0xe1 POP HL */	StdInstr(Pop(index)), | 		/* 0xe0 RET PO */	RET(TestPO),							/* 0xe1 POP HL */	Sequence(Pop(index)), | ||||||
| 		/* 0xe2 JP PO */	JP(TestPO),								/* 0xe3 EX (SP), HL */StdInstr(Pop7(memptr_), Push8(index), {MicroOp::Move16, &memptr_.full, &index.full}), | 		/* 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 */	Instr(6, Push(index)), | 		/* 0xe4 CALL PO */	CALL(TestPO),							/* 0xe5 PUSH HL */	Sequence(InternalOperation(2), Push(index)), | ||||||
| 		/* 0xe6 AND n */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::And, &temp8_}), | 		/* 0xe6 AND n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::And, &temp8_}), | ||||||
| 		/* 0xe7 RST 20h */	RST(), | 		/* 0xe7 RST 20h */	RST(), | ||||||
| 		/* 0xe8 RET PE */	RET(TestPE),							/* 0xe9 JP (HL) */	StdInstr({MicroOp::Move16, &index.full, &pc_.full}), | 		/* 0xe8 RET PE */	RET(TestPE),							/* 0xe9 JP (HL) */	Sequence({MicroOp::Move16, &index.full, &pc_.full}), | ||||||
| 		/* 0xea JP PE */	JP(TestPE),								/* 0xeb EX DE, HL */StdInstr({MicroOp::ExDEHL}), | 		/* 0xea JP PE */	JP(TestPE),								/* 0xeb EX DE, HL */Sequence({MicroOp::ExDEHL}), | ||||||
| 		/* 0xec CALL PE */	CALL(TestPE),							/* 0xed [ED page] */StdInstr({MicroOp::SetInstructionPage, &ed_page_}), | 		/* 0xec CALL PE */	CALL(TestPE),							/* 0xed [ED page] */Sequence({MicroOp::SetInstructionPage, &ed_page_}), | ||||||
| 		/* 0xee XOR n */	StdInstr(ReadInc(pc_, temp8_), {MicroOp::Xor, &temp8_}), | 		/* 0xee XOR n */	Sequence(ReadInc(pc_, temp8_), {MicroOp::Xor, &temp8_}), | ||||||
| 		/* 0xef RST 28h */	RST(), | 		/* 0xef RST 28h */	RST(), | ||||||
| 		/* 0xf0 RET p */	RET(TestP),								/* 0xf1 POP AF */	StdInstr(Pop(temp16_), {MicroOp::DisassembleAF}), | 		/* 0xf0 RET p */	RET(TestP),								/* 0xf1 POP AF */	Sequence(Pop(temp16_), {MicroOp::DisassembleAF}), | ||||||
| 		/* 0xf2 JP P */		JP(TestP),								/* 0xf3 DI */		StdInstr({MicroOp::DI}), | 		/* 0xf2 JP P */		JP(TestP),								/* 0xf3 DI */		Sequence({MicroOp::DI}), | ||||||
| 		/* 0xf4 CALL P */	CALL(TestP),							/* 0xf5 PUSH AF */	Instr(6, {MicroOp::AssembleAF}, Push(temp16_)), | 		/* 0xf4 CALL P */	CALL(TestP),							/* 0xf5 PUSH AF */	Sequence(InternalOperation(2), {MicroOp::AssembleAF}, Push(temp16_)), | ||||||
| 		/* 0xf6 OR n */		StdInstr(ReadInc(pc_, temp8_), {MicroOp::Or, &temp8_}), | 		/* 0xf6 OR n */		Sequence(ReadInc(pc_, temp8_), {MicroOp::Or, &temp8_}), | ||||||
| 		/* 0xf7 RST 30h */	RST(), | 		/* 0xf7 RST 30h */	RST(), | ||||||
| 		/* 0xf8 RET M */	RET(TestM),								/* 0xf9 LD SP, HL */Instr(8, {MicroOp::Move16, &index.full, &sp_.full}), | 		/* 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 */		StdInstr({MicroOp::EI}), | 		/* 0xfa JP M */		JP(TestM),								/* 0xfb EI */		Sequence({MicroOp::EI}), | ||||||
| 		/* 0xfc CALL M */	CALL(TestM),							/* 0xfd [FD page] */StdInstr({MicroOp::SetInstructionPage, &fd_page_}), | 		/* 0xfc CALL M */	CALL(TestM),							/* 0xfd [FD page] */Sequence({MicroOp::SetInstructionPage, &fd_page_}), | ||||||
| 		/* 0xfe CP n */		StdInstr(ReadInc(pc_, temp8_), {MicroOp::CP8, &temp8_}), | 		/* 0xfe CP n */		Sequence(ReadInc(pc_, temp8_), {MicroOp::CP8, &temp8_}), | ||||||
| 		/* 0xff RST 38h */	RST(), | 		/* 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 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. | 		// the cycle that fetches the final operand. So patch in a different microprogram if building an indexed table. | ||||||
| 		InstructionTable copy_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])); | 		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) { | 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[] = { | 	const MicroOp normal_fetch_decode_execute[] = { | ||||||
| 		BusOp(ReadOpcodeStart()), | 		BusOp(ReadOpcodeStart()), | ||||||
| 		BusOp(ReadOpcodeWait(true)), | 		BusOp(ReadOpcodeWait(true)), | ||||||
| 		BusOp(ReadOpcodeEnd()), | 		BusOp(ReadOpcodeEnd()), | ||||||
|  | 		{ MicroOp::IncrementR }, | ||||||
|  | 		BusOp(Refresh()), | ||||||
| 		{ MicroOp::DecodeOperation } | 		{ 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[] = { | 	const MicroOp short_fetch_decode_execute[] = { | ||||||
| 		BusOp(ReadOpcodeStart()), | 		BusOp(ReadStart(pc_, operation_)), | ||||||
| 		BusOp(ReadOpcodeWait(false)), | 		BusOp(ReadWait(pc_, operation_)), | ||||||
| 		BusOp(ReadOpcodeWait(true)), | 		BusOp(ReadEnd(pc_, operation_)), | ||||||
| 		BusOp(ReadOpcodeEnd()), | 		InternalOperation(4), | ||||||
| 		{ MicroOp::DecodeOperation } | 		{ MicroOp::DecodeOperation }, | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute); | 	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(); | 	target.fetch_decode_execute_data = target.fetch_decode_execute.data(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,8 +16,8 @@ class ProcessorStorage { | |||||||
| 		struct MicroOp { | 		struct MicroOp { | ||||||
| 			enum Type { | 			enum Type { | ||||||
| 				BusOperation, | 				BusOperation, | ||||||
|  | 				IncrementR, | ||||||
| 				DecodeOperation, | 				DecodeOperation, | ||||||
| 				DecodeOperationNoRChange, |  | ||||||
| 				MoveToNextProgram, | 				MoveToNextProgram, | ||||||
|  |  | ||||||
| 				Increment8NoFlags, | 				Increment8NoFlags, | ||||||
| @@ -121,7 +121,6 @@ class ProcessorStorage { | |||||||
| 			std::vector<MicroOp> all_operations; | 			std::vector<MicroOp> all_operations; | ||||||
| 			std::vector<MicroOp> fetch_decode_execute; | 			std::vector<MicroOp> fetch_decode_execute; | ||||||
| 			MicroOp *fetch_decode_execute_data = nullptr; | 			MicroOp *fetch_decode_execute_data = nullptr; | ||||||
| 			uint8_t r_step = 1; |  | ||||||
| 			bool is_indexed = false; | 			bool is_indexed = false; | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
| @@ -130,7 +129,7 @@ class ProcessorStorage { | |||||||
|  |  | ||||||
| 		uint8_t a_; | 		uint8_t a_; | ||||||
| 		RegisterPair16 bc_, de_, hl_; | 		RegisterPair16 bc_, de_, hl_; | ||||||
| 		RegisterPair16 afDash_, bcDash_, deDash_, hlDash_; | 		RegisterPair16 af_dash_, bc_dash_, de_dash_, hl_dash_; | ||||||
| 		RegisterPair16 ix_, iy_, pc_, sp_; | 		RegisterPair16 ix_, iy_, pc_, sp_; | ||||||
| 		RegisterPair16 ir_, refresh_addr_; | 		RegisterPair16 ir_, refresh_addr_; | ||||||
| 		bool iff1_ = false, iff2_ = false; | 		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 | 													// that knowledge of what the last opcode did is necessary to get bits 5 & 3 | ||||||
| 													// correct for SCF and CCF. | 													// 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_; | 		HalfCycles number_of_cycles_; | ||||||
|  |  | ||||||
| 		enum Interrupt: uint8_t { | 		enum Interrupt: uint8_t { | ||||||
|   | |||||||
| @@ -19,10 +19,10 @@ State::State(const ProcessorBase &src): State() { | |||||||
| 	registers.bc = src.bc_.full; | 	registers.bc = src.bc_.full; | ||||||
| 	registers.de = src.de_.full; | 	registers.de = src.de_.full; | ||||||
| 	registers.hl = src.hl_.full; | 	registers.hl = src.hl_.full; | ||||||
| 	registers.afDash = src.afDash_.full; | 	registers.af_dash = src.af_dash_.full; | ||||||
| 	registers.bcDash = src.bcDash_.full; | 	registers.bc_dash = src.bc_dash_.full; | ||||||
| 	registers.deDash = src.deDash_.full; | 	registers.de_dash = src.de_dash_.full; | ||||||
| 	registers.hlDash = src.hlDash_.full; | 	registers.hl_dash = src.hl_dash_.full; | ||||||
| 	registers.ix = src.ix_.full; | 	registers.ix = src.ix_.full; | ||||||
| 	registers.iy = src.iy_.full; | 	registers.iy = src.iy_.full; | ||||||
| 	registers.ir = src.ir_.full; | 	registers.ir = src.ir_.full; | ||||||
| @@ -108,10 +108,10 @@ void State::apply(ProcessorBase &target) { | |||||||
| 	target.bc_.full = registers.bc; | 	target.bc_.full = registers.bc; | ||||||
| 	target.de_.full = registers.de; | 	target.de_.full = registers.de; | ||||||
| 	target.hl_.full = registers.hl; | 	target.hl_.full = registers.hl; | ||||||
| 	target.afDash_.full = registers.afDash; | 	target.af_dash_.full = registers.af_dash; | ||||||
| 	target.bcDash_.full = registers.bcDash; | 	target.bc_dash_.full = registers.bc_dash; | ||||||
| 	target.deDash_.full = registers.deDash; | 	target.de_dash_.full = registers.de_dash; | ||||||
| 	target.hlDash_.full = registers.hlDash; | 	target.hl_dash_.full = registers.hl_dash; | ||||||
| 	target.ix_.full = registers.ix; | 	target.ix_.full = registers.ix; | ||||||
| 	target.iy_.full = registers.iy; | 	target.iy_.full = registers.iy; | ||||||
| 	target.ir_.full = registers.ir; | 	target.ir_.full = registers.ir; | ||||||
| @@ -179,10 +179,10 @@ State::Registers::Registers() { | |||||||
| 		DeclareField(bc); | 		DeclareField(bc); | ||||||
| 		DeclareField(de); | 		DeclareField(de); | ||||||
| 		DeclareField(hl); | 		DeclareField(hl); | ||||||
| 		DeclareField(afDash); | 		DeclareField(af_dash);	// TODO: is there any disadvantage to declaring these for reflective | ||||||
| 		DeclareField(bcDash); | 		DeclareField(bc_dash);	// purposes as AF', BC', etc? | ||||||
| 		DeclareField(deDash); | 		DeclareField(de_dash); | ||||||
| 		DeclareField(hlDash); | 		DeclareField(hl_dash); | ||||||
| 		DeclareField(ix); | 		DeclareField(ix); | ||||||
| 		DeclareField(iy); | 		DeclareField(iy); | ||||||
| 		DeclareField(ir); | 		DeclareField(ir); | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ struct State: public Reflection::StructImpl<State> { | |||||||
| 		uint8_t a; | 		uint8_t a; | ||||||
| 		uint8_t flags; | 		uint8_t flags; | ||||||
| 		uint16_t bc, de, hl; | 		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 ix, iy, ir; | ||||||
| 		uint16_t program_counter, stack_pointer; | 		uint16_t program_counter, stack_pointer; | ||||||
| 		uint16_t memptr; | 		uint16_t memptr; | ||||||
| @@ -60,25 +60,25 @@ struct State: public Reflection::StructImpl<State> { | |||||||
| 		obviously doesn't. | 		obviously doesn't. | ||||||
| 	*/ | 	*/ | ||||||
| 	struct ExecutionState: public Reflection::StructImpl<ExecutionState> { | 	struct ExecutionState: public Reflection::StructImpl<ExecutionState> { | ||||||
| 		bool is_halted; | 		bool is_halted = false; | ||||||
|  |  | ||||||
| 		uint8_t requests; | 		uint8_t requests = 0; | ||||||
| 		uint8_t last_requests; | 		uint8_t last_requests = 0; | ||||||
| 		uint8_t temp8; | 		uint8_t temp8 = 0; | ||||||
| 		uint8_t operation; | 		uint8_t operation = 0; | ||||||
| 		uint16_t temp16; | 		uint16_t temp16 = 0; | ||||||
| 		unsigned int flag_adjustment_history; | 		unsigned int flag_adjustment_history = 0; | ||||||
| 		uint16_t pc_increment; | 		uint16_t pc_increment = 1; | ||||||
| 		uint16_t refresh_address; | 		uint16_t refresh_address = 0; | ||||||
|  |  | ||||||
| 		ReflectableEnum(Phase, | 		ReflectableEnum(Phase, | ||||||
| 			UntakenConditionalCall, Reset, IRQMode0, IRQMode1, IRQMode2, | 			UntakenConditionalCall, Reset, IRQMode0, IRQMode1, IRQMode2, | ||||||
| 			NMI, FetchDecode, Operation | 			NMI, FetchDecode, Operation | ||||||
| 		); | 		); | ||||||
|  |  | ||||||
| 		Phase phase; | 		Phase phase = Phase::FetchDecode; | ||||||
| 		int half_cycles_into_step; | 		int half_cycles_into_step = 0; | ||||||
| 		int steps_into_phase; | 		int steps_into_phase = 0; | ||||||
| 		uint16_t instruction_page = 0; | 		uint16_t instruction_page = 0; | ||||||
|  |  | ||||||
| 		ExecutionState(); | 		ExecutionState(); | ||||||
|   | |||||||
| @@ -68,29 +68,50 @@ enum Flag: uint8_t { | |||||||
| */ | */ | ||||||
| struct PartialMachineCycle { | struct PartialMachineCycle { | ||||||
| 	enum Operation { | 	enum Operation { | ||||||
|  | 		/// The final half cycle of the opcode fetch part of an M1 cycle. | ||||||
| 		ReadOpcode = 0, | 		ReadOpcode = 0, | ||||||
|  | 		/// The 1.5 cycles of a read cycle. | ||||||
| 		Read, | 		Read, | ||||||
|  | 		/// The 1.5 cycles of a write cycle. | ||||||
| 		Write, | 		Write, | ||||||
|  | 		/// The 1.5 cycles of an input cycle. | ||||||
| 		Input, | 		Input, | ||||||
|  | 		/// The 1.5 cycles of an output cycle. | ||||||
| 		Output, | 		Output, | ||||||
|  | 		/// The 1.5 cycles of an interrupt acknowledgment. | ||||||
| 		Interrupt, | 		Interrupt, | ||||||
|  |  | ||||||
|  | 		/// The two-cycle refresh part of an M1 cycle. | ||||||
| 		Refresh, | 		Refresh, | ||||||
|  | 		/// A period with no changes in bus signalling. | ||||||
| 		Internal, | 		Internal, | ||||||
|  | 		/// A bus acknowledgement cycle. | ||||||
| 		BusAcknowledge, | 		BusAcknowledge, | ||||||
|  |  | ||||||
|  | 		/// A wait state within an M1 cycle. | ||||||
| 		ReadOpcodeWait, | 		ReadOpcodeWait, | ||||||
|  | 		/// A wait state within a read cycle. | ||||||
| 		ReadWait, | 		ReadWait, | ||||||
|  | 		/// A wait state within a write cycle. | ||||||
| 		WriteWait, | 		WriteWait, | ||||||
|  | 		/// A wait state within an input cycle. | ||||||
| 		InputWait, | 		InputWait, | ||||||
|  | 		/// A wait state within an output cycle. | ||||||
| 		OutputWait, | 		OutputWait, | ||||||
|  | 		/// A wait state within an interrupt acknowledge cycle. | ||||||
| 		InterruptWait, | 		InterruptWait, | ||||||
|  |  | ||||||
|  | 		/// The first 1.5 cycles of an M1 bus cycle, up to the sampling of WAIT. | ||||||
| 		ReadOpcodeStart, | 		ReadOpcodeStart, | ||||||
|  | 		/// The first 1.5 cycles of a read cycle, up to the sampling of WAIT. | ||||||
| 		ReadStart, | 		ReadStart, | ||||||
|  | 		/// The first 1.5 cycles of a write cycle, up to the sampling of WAIT. | ||||||
| 		WriteStart, | 		WriteStart, | ||||||
|  | 		/// The first 1.5 samples of an input bus cycle, up to the sampling of WAIT. | ||||||
| 		InputStart, | 		InputStart, | ||||||
|  | 		/// The first 1.5 samples of an output bus cycle, up to the sampling of WAIT. | ||||||
| 		OutputStart, | 		OutputStart, | ||||||
|  | 		/// The first portion of an interrupt acknowledgement — 2.5 or 3.5 cycles, depending on interrupt mode. | ||||||
| 		InterruptStart, | 		InterruptStart, | ||||||
| 	}; | 	}; | ||||||
| 	/// The operation being carried out by the Z80. See the various getters below for better classification. | 	/// 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; | 		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(const PartialMachineCycle &rhs) noexcept; | ||||||
| 	PartialMachineCycle(Operation operation, HalfCycles length, uint16_t *address, uint8_t *value, bool was_requested) noexcept; | 	PartialMachineCycle(Operation operation, HalfCycles length, uint16_t *address, uint8_t *value, bool was_requested) noexcept; | ||||||
| 	PartialMachineCycle() noexcept; | 	PartialMachineCycle() noexcept; | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ It currently contains emulations of the: | |||||||
| * Oric 1/Atmos; | * Oric 1/Atmos; | ||||||
| * Sega Master System; | * Sega Master System; | ||||||
| * Sinclair ZX80/81; and | * Sinclair ZX80/81; and | ||||||
| * Sinclair ZX Spectrum +2a/+3. | * Sinclair ZX Spectrum. | ||||||
|  |  | ||||||
| ## Single-step Loading | ## 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: | 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. | 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 <cassert> | ||||||
| #include <cstdarg> | #include <cstdarg> | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include <cstddef> | ||||||
| #include <string> | #include <string> | ||||||
| #include <typeindex> | #include <typeindex> | ||||||
| #include <typeinfo> | #include <typeinfo> | ||||||
| @@ -318,12 +319,12 @@ template <typename Owner> class StructImpl: public Struct { | |||||||
| 			if(iterator != contents_.end()) { | 			if(iterator != contents_.end()) { | ||||||
| 				return iterator->first; | 				return iterator->first; | ||||||
| 			} else { | 			} else { | ||||||
| 				return ""; | 				return std::string(); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	private: | 	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) { | 			if constexpr (std::is_base_of<Reflection::Struct, Type>::value) { | ||||||
| 				Reflection::Struct *const str = static_cast<Reflection::Struct *>(t); | 				Reflection::Struct *const str = static_cast<Reflection::Struct *>(t); | ||||||
| 				declare_emplace(str, name); | 				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 | 				Rewrite		opens the file for rewriting; none of the original content is preserved; whatever | ||||||
| 							the caller outputs will replace the existing file. | 							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); | 		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) : | OricTAP::OricTAP(const std::string &file_name) : | ||||||
| 	file_(file_name) | 	file_(file_name) | ||||||
| { | { | ||||||
| 	// check the file signature | 	// Check for a sequence of at least three 0x16s followed by a 0x24. | ||||||
| 	if(!file_.check_signature("\x16\x16\x16\x24", 4)) | 	while(true) { | ||||||
|  | 		const uint8_t next = file_.get8(); | ||||||
|  | 		if(next != 0x16 && next != 0x24) { | ||||||
| 			throw ErrorNotOricTAP; | 			throw ErrorNotOricTAP; | ||||||
|  | 		} | ||||||
|  | 		if(next == 0x24) { | ||||||
|  | 			if(file_.tell() < 4) { | ||||||
|  | 				throw ErrorNotOricTAP; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// then rewind and start again | 	// then rewind and start again | ||||||
| 	virtual_reset(); | 	virtual_reset(); | ||||||
|   | |||||||
| @@ -40,8 +40,8 @@ enum Type: IntType { | |||||||
| 	Acorn			=	AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB, | 	Acorn			=	AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB, | ||||||
| 	ZX8081			=	ZX80 | ZX81, | 	ZX8081			=	ZX80 | ZX81, | ||||||
| 	AllCartridge	=	Atari2600 | AcornElectron | Coleco | MSX, | 	AllCartridge	=	Atari2600 | AcornElectron | Coleco | MSX, | ||||||
| 	AllDisk			=	Acorn | AmstradCPC | Commodore | Oric | MSX,	// TODO: | AtariST | 	AllDisk			=	Acorn | AmstradCPC | Commodore | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII, | ||||||
| 	AllTape			=	Acorn | AmstradCPC | Commodore | Oric | ZX80 | ZX81 | MSX | ZXSpectrum, | 	AllTape			=	Acorn | AmstradCPC | Commodore | Oric | ZX8081 | MSX | ZXSpectrum, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class TypeDistinguisher { | class TypeDistinguisher { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user