mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-31 05:16:08 +00:00 
			
		
		
		
	Compare commits
	
		
			60 Commits
		
	
	
		
			2021-04-06
			...
			2021-04-16
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | 
| @@ -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) { | ||||||
|   | |||||||
| @@ -18,7 +18,9 @@ namespace Sinclair { | |||||||
| namespace ZXSpectrum { | namespace ZXSpectrum { | ||||||
|  |  | ||||||
| enum class VideoTiming { | enum class VideoTiming { | ||||||
| 	Plus3 | 	FortyEightK, | ||||||
|  | 	OneTwoEightK, | ||||||
|  | 	Plus3, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -67,6 +69,7 @@ template <VideoTiming timing> class Video { | |||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
| 		static constexpr Timings get_timings() { | 		static constexpr Timings get_timings() { | ||||||
|  | 			if constexpr (timing == VideoTiming::Plus3) { | ||||||
| 				// Amstrad gate array timings, classic statement: | 				// Amstrad gate array timings, classic statement: | ||||||
| 				// | 				// | ||||||
| 				// Contention begins 14361 cycles "after interrupt" and follows the pattern [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]. | ||||||
| @@ -108,9 +111,63 @@ template <VideoTiming timing> class Video { | |||||||
| 						4, 3, | 						4, 3, | ||||||
| 					} | 					} | ||||||
| 				}; | 				}; | ||||||
|  |  | ||||||
| 				return result; | 				return result; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			// TODO: fix 48kb and 128kb timings, below. | ||||||
|  |  | ||||||
|  | 			if constexpr (timing == VideoTiming::OneTwoEightK) { | ||||||
|  | 				constexpr Timings result = { | ||||||
|  | 					.cycles_per_line = 228 * 2, | ||||||
|  | 					.lines_per_frame = 311, | ||||||
|  |  | ||||||
|  | 					.contention_leadin = 2 * 2, | ||||||
|  | 					.contention_duration = 128 * 2, | ||||||
|  |  | ||||||
|  | 					.interrupt_time = (228*311 - 14366) * 2, | ||||||
|  |  | ||||||
|  | 					.delays = { | ||||||
|  | 						12, 11, | ||||||
|  | 						10, 9, | ||||||
|  | 						8, 7, | ||||||
|  | 						6, 5, | ||||||
|  | 						4, 3, | ||||||
|  | 						2, 1, | ||||||
|  | 						0, 0, | ||||||
|  | 						0, 0, | ||||||
|  | 					} | ||||||
|  | 				}; | ||||||
|  |  | ||||||
|  | 				return result; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if constexpr (timing == VideoTiming::FortyEightK) { | ||||||
|  | 				constexpr Timings result = { | ||||||
|  | 					.cycles_per_line = 224 * 2, | ||||||
|  | 					.lines_per_frame = 312, | ||||||
|  |  | ||||||
|  | 					.contention_leadin = 2 * 2, | ||||||
|  | 					.contention_duration = 128 * 2, | ||||||
|  |  | ||||||
|  | 					.interrupt_time = (224*312 - 14339) * 2, | ||||||
|  |  | ||||||
|  | 					.delays = { | ||||||
|  | 						12, 11, | ||||||
|  | 						10, 9, | ||||||
|  | 						8, 7, | ||||||
|  | 						6, 5, | ||||||
|  | 						4, 3, | ||||||
|  | 						2, 1, | ||||||
|  | 						0, 0, | ||||||
|  | 						0, 0, | ||||||
|  | 					} | ||||||
|  | 				}; | ||||||
|  |  | ||||||
|  | 				return result; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// TODO: how long is the interrupt line held for? | 		// TODO: how long is the interrupt line held for? | ||||||
| 		static constexpr int interrupt_duration = 48; | 		static constexpr int interrupt_duration = 48; | ||||||
|  |  | ||||||
| @@ -236,9 +293,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 >= VideoTiming::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) { | ||||||
| @@ -259,9 +321,21 @@ template <VideoTiming timing> class Video { | |||||||
| 			crt_.output_level(duration); | 			crt_.output_level(duration); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		static constexpr int half_cycles_per_line() { | ||||||
|  | 			if constexpr (timing == VideoTiming::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; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	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); | ||||||
| @@ -327,20 +401,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 uint8_t out_of_bounds = (timing == VideoTiming::Plus3) ? last_contended_access_ : 0xff; | ||||||
|  |  | ||||||
| 			const int line = time_into_frame_ / timings.cycles_per_line; | 			const int line = time_into_frame_ / timings.cycles_per_line; | ||||||
| 			if(line >= 192) return 0xff; | 			if(line >= 192) { | ||||||
|  | 				return out_of_bounds; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			const int time_into_line = time_into_frame_ % timings.cycles_per_line; | 			const int time_into_line = time_into_frame_ % timings.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. | ||||||
|  | 			const uint8_t value = last_fetches_[(time_into_line >> 1) & 3]; | ||||||
| 			if constexpr (timing == VideoTiming::Plus3) { | 			if constexpr (timing == VideoTiming::Plus3) { | ||||||
| 				return last_fetches_[(time_into_line >> 1) & 3] | 1; | 				return value | 1; | ||||||
| 			} | 			} | ||||||
|  | 			return value; | ||||||
| 			return last_fetches_[(time_into_line >> 1) & 3]; |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/*! | 		/*! | ||||||
|   | |||||||
| @@ -81,8 +81,29 @@ 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())); | ||||||
|  |  | ||||||
| @@ -110,7 +131,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 +158,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. | ||||||
| @@ -189,19 +210,91 @@ 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 HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1)); | ||||||
| 					advance(cycle.length + delay); | 					advance(cycle.length + delay); | ||||||
| 					return delay; | 					return delay; | ||||||
| 				} | 				} | ||||||
|  | 			} else { | ||||||
|  | 				switch(cycle.operation) { | ||||||
|  | 					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() + HalfCycles(1); | ||||||
|  |  | ||||||
|  | 						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; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					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 HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1)); | ||||||
|  |  | ||||||
|  | 							advance(cycle.length + delay); | ||||||
|  | 							return delay; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					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(1); | ||||||
|  | 							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; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					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: | ||||||
|  | 						// For these, carry on into the actual handler, below. | ||||||
|  | 					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 +307,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); | ||||||
| @@ -228,11 +321,22 @@ template<Model model> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 				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 +347,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,7 +372,10 @@ 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(); | ||||||
|  |  | ||||||
| @@ -276,6 +388,7 @@ template<Model model> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					// 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 +398,43 @@ 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&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 | ||||||
| @@ -339,28 +462,40 @@ 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; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				break; |  | ||||||
|  | 					if constexpr (model <= Model::Plus2) { | ||||||
|  | 						if(!did_match) { | ||||||
|  | 							*cycle.value = video_->get_floating_value(); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} break; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return HalfCycles(0); | 			return HalfCycles(0); | ||||||
| @@ -571,10 +706,14 @@ template<Model model> class ConcreteMachine: | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		void set_memory(int bank, uint8_t source) { | 		void set_memory(int bank, uint8_t source) { | ||||||
|  | 			if constexpr (model >= Model::Plus2a) { | ||||||
| 				is_contended_[bank] = (source >= 4 && source < 8); | 				is_contended_[bank] = (source >= 4 && source < 8); | ||||||
|  | 			} else { | ||||||
|  | 				is_contended_[bank] = 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 +746,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<VideoTiming::FortyEightK>, | ||||||
|  | 				std::conditional_t< | ||||||
|  | 					model <= Model::Plus2, Video<VideoTiming::OneTwoEightK>, | ||||||
|  | 					Video<VideoTiming::Plus3> | ||||||
|  | 				> | ||||||
|  | 			>; | ||||||
|  | 		JustInTimeActor<VideoType> video_; | ||||||
|  |  | ||||||
| 		// MARK: - Keyboard. | 		// MARK: - Keyboard. | ||||||
| 		Sinclair::ZX::Keyboard::Keyboard keyboard_; | 		Sinclair::ZX::Keyboard::Keyboard keyboard_; | ||||||
| @@ -695,6 +841,22 @@ 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_; | ||||||
|  |  | ||||||
| @@ -712,6 +874,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); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -917,6 +917,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 */; }; | ||||||
| @@ -1934,6 +1935,7 @@ | |||||||
| 		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>"; }; | ||||||
| @@ -3964,6 +3966,7 @@ | |||||||
| 				4B2AF8681E513FC20027EE29 /* TIATests.mm */, | 				4B2AF8681E513FC20027EE29 /* TIATests.mm */, | ||||||
| 				4B1D08051E0F7A1100763741 /* TimeTests.mm */, | 				4B1D08051E0F7A1100763741 /* TimeTests.mm */, | ||||||
| 				4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */, | 				4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */, | ||||||
|  | 				4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */, | ||||||
| 				4BB73EB81B587A5100552FC2 /* Info.plist */, | 				4BB73EB81B587A5100552FC2 /* Info.plist */, | ||||||
| 				4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, | 				4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, | ||||||
| 				4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, | 				4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, | ||||||
| @@ -5649,6 +5652,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 */, | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										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
											
										
									
								
							| @@ -1258,9 +1258,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)); | ||||||
|   | |||||||
| @@ -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> | ||||||
|   | |||||||
| @@ -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; | ||||||
| @@ -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) | ||||||
| @@ -47,32 +47,15 @@ ProcessorStorage::ProcessorStorage() { | |||||||
| #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, nullptr}, {MicroOp::Move16, &memptr_.full, &pc_.full}) | ||||||
| #define CALL(cc)				StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}) | #define 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, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}) | ||||||
| #define JR(cc)					StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}) | #define JR(cc)					Sequence(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}) | ||||||
| #define RST()					Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}) | #define 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,7 +177,8 @@ 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, nullptr, nullptr}, | ||||||
| 		{ MicroOp::MoveToNextProgram } | 		{ MicroOp::MoveToNextProgram } | ||||||
| @@ -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; | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
| @@ -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 { | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
							
								
								
									
										
											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. | ||||||
		Reference in New Issue
	
	Block a user