From ce5aae3f7da67eedef681eb87d19ba5058acb1c2 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 4 Dec 2024 22:29:08 -0500
Subject: [PATCH 1/2] Adjust more dangling indentation changes.

---
 Analyser/Static/Commodore/Disk.cpp            |    4 +-
 .../Apple/AppleII/AuxiliaryMemorySwitches.hpp |  450 ++---
 Machines/Apple/AppleIIgs/ADB.hpp              |   86 +-
 Machines/Apple/AppleIIgs/AppleIIgs.cpp        | 1618 ++++++++---------
 Machines/Apple/AppleIIgs/MemoryMap.hpp        |  216 +--
 Machines/Apple/AppleIIgs/Sound.hpp            |  138 +-
 Machines/Apple/AppleIIgs/Video.hpp            |  430 ++---
 Machines/Apple/Macintosh/Audio.hpp            |   88 +-
 .../Apple/Macintosh/DriveSpeedAccumulator.hpp |   38 +-
 Machines/Apple/Macintosh/Keyboard.hpp         |  368 ++--
 Machines/Apple/Macintosh/Macintosh.cpp        | 1382 +++++++-------
 Machines/Apple/Macintosh/Video.hpp            |  112 +-
 Machines/Atari/2600/Atari2600.cpp             |  272 +--
 Machines/Atari/ST/AtariST.cpp                 | 1190 ++++++------
 Machines/Atari/ST/DMAController.hpp           |  134 +-
 Machines/Atari/ST/IntelligentKeyboard.hpp     |  264 +--
 Machines/Atari/ST/Video.hpp                   |  400 ++--
 .../1540/Implementation/C1540Base.hpp         |  128 +-
 Machines/Utility/ROMCatalogue.hpp             |  100 +-
 Machines/Utility/StringSerialiser.hpp         |   14 +-
 Machines/Utility/TypedDynamicMachine.hpp      |   70 +-
 Machines/Utility/Typer.hpp                    |  120 +-
 Numeric/CRC.hpp                               |  127 +-
 Numeric/LFSR.hpp                              |   58 +-
 Numeric/NumericCoder.hpp                      |   70 +-
 Outputs/CRT/CRT.hpp                           |  502 ++---
 Outputs/CRT/Internals/Flywheel.hpp            |   38 +-
 Outputs/DisplayMetrics.hpp                    |   44 +-
 Outputs/Log.hpp                               |   40 +-
 Outputs/OpenGL/Primitives/Rectangle.hpp       |   26 +-
 Outputs/OpenGL/Primitives/TextureTarget.hpp   |  104 +-
 Outputs/OpenGL/ScanTarget.hpp                 |  186 +-
 Outputs/ScanTargets/BufferingScanTarget.hpp   |  392 ++--
 .../Speaker/Implementation/BufferSource.hpp   |   80 +-
 .../Speaker/Implementation/CompoundSource.hpp |  237 +--
 .../Speaker/Implementation/LowpassSpeaker.hpp |  654 +++----
 Outputs/Speaker/Speaker.hpp                   |  220 +--
 Processors/6502/6502.hpp                      |   34 +-
 Processors/6502/AllRAM/6502AllRAM.cpp         |  198 +-
 Processors/65816/65816.hpp                    |   80 +-
 .../65816/Implementation/65816Storage.cpp     |    2 +-
 .../65816/Implementation/65816Storage.hpp     |   18 +-
 Processors/68000/68000.hpp                    |   86 +-
 Processors/AllRAMProcessor.hpp                |   42 +-
 Processors/Z80/Z80.hpp                        |  198 +-
 45 files changed, 5530 insertions(+), 5528 deletions(-)

diff --git a/Analyser/Static/Commodore/Disk.cpp b/Analyser/Static/Commodore/Disk.cpp
index 0551fd997..658b0e938 100644
--- a/Analyser/Static/Commodore/Disk.cpp
+++ b/Analyser/Static/Commodore/Disk.cpp
@@ -70,7 +70,7 @@ private:
 	uint8_t track_;
 	std::shared_ptr<Sector> sector_cache_[65536];
 
-	void process_input_bit(const int value) {
+	void process_input_bit(const int value) override {
 		shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff;
 		bit_count_++;
 	}
@@ -107,7 +107,7 @@ private:
 		}
 	}
 
-	void process_index_hole() {
+	void process_index_hole() override {
 		index_count_++;
 	}
 
diff --git a/Machines/Apple/AppleII/AuxiliaryMemorySwitches.hpp b/Machines/Apple/AppleII/AuxiliaryMemorySwitches.hpp
index f4abba555..1d2dab9bb 100644
--- a/Machines/Apple/AppleII/AuxiliaryMemorySwitches.hpp
+++ b/Machines/Apple/AppleII/AuxiliaryMemorySwitches.hpp
@@ -24,261 +24,261 @@ namespace Apple::II {
 	Implementation observation: as implemented on the IIe, the zero page setting also affects what happens in the language card area.
 */
 template <typename Machine> class AuxiliaryMemorySwitches {
-	public:
-		static constexpr bool Auxiliary = true;
-		static constexpr bool Main = false;
-		static constexpr bool ROM = true;
-		static constexpr bool Card = false;
+public:
+	static constexpr bool Auxiliary = true;
+	static constexpr bool Main = false;
+	static constexpr bool ROM = true;
+	static constexpr bool Card = false;
 
-		/// Describes banking state between $0200 and $BFFF.
-		struct MainState {
-			struct Region {
-				/// @c true indicates auxiliary memory should be read from; @c false indicates main.
-				bool read = false;
-				/// @c true indicates auxiliary memory should be written to; @c false indicates main.
-				bool write = false;
-			};
-
-			/// Describes banking state in the ranges $0200–$03FF, $0800–$1FFF and $4000–$BFFF.
-			Region base;
-			/// Describes banking state in the range $0400–$07FF.
-			Region region_04_08;
-			/// Describes banking state in the range $2000–$3FFF.
-			Region region_20_40;
-
-			bool operator != (const MainState &rhs) const {
-				return
-					base.read != rhs.base.read || base.write != rhs.base.write ||
-					region_04_08.read != rhs.region_04_08.read || region_04_08.write != rhs.region_04_08.write ||
-					region_20_40.read != rhs.region_20_40.read || region_20_40.write != rhs.region_20_40.write;
-			}
+	/// Describes banking state between $0200 and $BFFF.
+	struct MainState {
+		struct Region {
+			/// @c true indicates auxiliary memory should be read from; @c false indicates main.
+			bool read = false;
+			/// @c true indicates auxiliary memory should be written to; @c false indicates main.
+			bool write = false;
 		};
 
-		/// Describes banking state between $C100 and $Cfff.
-		struct CardState {
-			/// @c true indicates that the built-in ROM should appear from $C100 to $C2FF @c false indicates that cards should service those accesses.
-			bool region_C1_C3 = false;
-			/// @c true indicates that the built-in ROM should appear from $C300 to $C3FF; @c false indicates that cards should service those accesses.
-			bool region_C3 = false;
-			/// @c true indicates that the built-in ROM should appear from $C400 to $C7FF; @c false indicates that cards should service those accesses.
-			bool region_C4_C8 = false;
-			/// @c true indicates that the built-in ROM should appear from $C800 to $CFFF; @c false indicates that cards should service those accesses.
-			bool region_C8_D0 = false;
+		/// Describes banking state in the ranges $0200–$03FF, $0800–$1FFF and $4000–$BFFF.
+		Region base;
+		/// Describes banking state in the range $0400–$07FF.
+		Region region_04_08;
+		/// Describes banking state in the range $2000–$3FFF.
+		Region region_20_40;
 
-			bool operator != (const CardState &rhs) const {
-				return
-					region_C1_C3 != rhs.region_C1_C3 ||
-					region_C3 != rhs.region_C3 ||
-					region_C4_C8 != rhs.region_C4_C8 ||
-					region_C8_D0 != rhs.region_C8_D0;
-			}
-		};
-
-		/// Descibes banking state between $0000 and $01ff; @c true indicates that auxiliary memory should be used; @c false indicates main memory.
-		using ZeroState = bool;
-
-		/// Returns raw switch state for all switches that affect banking, even if they're logically video switches.
-		struct SwitchState {
-			bool read_auxiliary_memory = false;
-			bool write_auxiliary_memory = false;
-
-			bool internal_CX_rom = false;
-			bool slot_C3_rom = false;
-			bool internal_C8_rom = false;
-
-			bool store_80 = false;
-			bool alternative_zero_page = false;
-			bool video_page_2 = false;
-			bool high_resolution = false;
-
-			void reset() {
-				*this = SwitchState();
-			}
-		};
-
-		AuxiliaryMemorySwitches(Machine &machine) : machine_(machine) {}
-
-		/// Used by an owner to forward, at least, any access in the range $C000 to $C00B,
-		/// in $C054 to $C058, or in the range $C300 to $CFFF. Safe to call for any [16-bit] address.
-		void access(uint16_t address, bool is_read) {
-			if(address >= 0xc300 && address < 0xd000) {
-				switches_.internal_C8_rom |= ((address >> 8) == 0xc3) && !switches_.slot_C3_rom;
-				switches_.internal_C8_rom &= (address != 0xcfff);
-				set_card_paging();
-				return;
-			}
-
-			if(address < 0xc000 || address >= 0xc058) return;
-
-			switch(address) {
-				default: break;
-
-				case 0xc000: case 0xc001:
-					if(!is_read) {
-						switches_.store_80 = address & 1;
-						set_main_paging();
-					}
-				break;
-
-				case 0xc002: case 0xc003:
-					if(!is_read) {
-						switches_.read_auxiliary_memory = address & 1;
-						set_main_paging();
-					}
-				break;
-
-				case 0xc004: case 0xc005:
-					if(!is_read) {
-						switches_.write_auxiliary_memory = address & 1;
-						set_main_paging();
-					}
-				break;
-
-				case 0xc006: case 0xc007:
-					if(!is_read) {
-						switches_.internal_CX_rom = address & 1;
-						set_card_paging();
-					}
-				break;
-
-				case 0xc008: case 0xc009: {
-					const bool alternative_zero_page = address & 1;
-					if(!is_read && switches_.alternative_zero_page != alternative_zero_page) {
-						switches_.alternative_zero_page = alternative_zero_page;
-						set_zero_page_paging();
-					}
-				} break;
-
-				case 0xc00a: case 0xc00b:
-					if(!is_read) {
-						switches_.slot_C3_rom = address & 1;
-						set_card_paging();
-					}
-				break;
-
-				case 0xc054: case 0xc055:
-					switches_.video_page_2 = address & 1;
-					set_main_paging();
-				break;
-
-				case 0xc056: case 0xc057:
-					switches_.high_resolution = address & 1;
-					set_main_paging();
-				break;
-			}
-		}
-
-		/// Provides part of the IIgs interface.
-		void set_state(uint8_t value) {
-			// b7: 1 => use auxiliary memory for zero page; 0 => use main.	[I think the Hardware Reference gets this the wrong way around]
-			// b6: 1 => text page 2 is selected; 0 => text page 1.
-			// b5: 1 => auxiliary RAM bank is selected for reads; 0 => main.
-			// b4: 1 => auxiliary RAM bank is selected for writes; 0 => main.
-			// b0: 1 => the internal ROM is selected for C800+; 0 => card ROM.
-			switches_.alternative_zero_page = value & 0x80;
-			switches_.video_page_2 = value & 0x40;
-			switches_.read_auxiliary_memory = value & 0x20;
-			switches_.write_auxiliary_memory = value & 0x10;
-			switches_.internal_CX_rom = value & 0x01;
-
-			set_main_paging();
-			set_zero_page_paging();
-			set_card_paging();
-		}
-
-		uint8_t get_state() const {
+		bool operator != (const MainState &rhs) const {
 			return
-				(switches_.alternative_zero_page ? 0x80 : 0x00) |
-				(switches_.video_page_2 ? 0x40 : 0x00) |
-				(switches_.read_auxiliary_memory ? 0x20 : 0x00) |
-				(switches_.write_auxiliary_memory ? 0x10 : 0x00) |
-				(switches_.internal_CX_rom ? 0x01 : 0x00);
+				base.read != rhs.base.read || base.write != rhs.base.write ||
+				region_04_08.read != rhs.region_04_08.read || region_04_08.write != rhs.region_04_08.write ||
+				region_20_40.read != rhs.region_20_40.read || region_20_40.write != rhs.region_20_40.write;
 		}
+	};
 
-		const MainState &main_state() const {
-			return main_state_;
-		}
+	/// Describes banking state between $C100 and $Cfff.
+	struct CardState {
+		/// @c true indicates that the built-in ROM should appear from $C100 to $C2FF @c false indicates that cards should service those accesses.
+		bool region_C1_C3 = false;
+		/// @c true indicates that the built-in ROM should appear from $C300 to $C3FF; @c false indicates that cards should service those accesses.
+		bool region_C3 = false;
+		/// @c true indicates that the built-in ROM should appear from $C400 to $C7FF; @c false indicates that cards should service those accesses.
+		bool region_C4_C8 = false;
+		/// @c true indicates that the built-in ROM should appear from $C800 to $CFFF; @c false indicates that cards should service those accesses.
+		bool region_C8_D0 = false;
 
-		const CardState &card_state() const {
-			return card_state_;
+		bool operator != (const CardState &rhs) const {
+			return
+				region_C1_C3 != rhs.region_C1_C3 ||
+				region_C3 != rhs.region_C3 ||
+				region_C4_C8 != rhs.region_C4_C8 ||
+				region_C8_D0 != rhs.region_C8_D0;
 		}
+	};
 
-		/// @returns @c true if the alternative zero page should be used; @c false otherwise.
-		ZeroState zero_state() const {
-			return switches_.alternative_zero_page;
-		}
+	/// Descibes banking state between $0000 and $01ff; @c true indicates that auxiliary memory should be used; @c false indicates main memory.
+	using ZeroState = bool;
 
-		const SwitchState switches() const {
-			return switches_;
-		}
+	/// Returns raw switch state for all switches that affect banking, even if they're logically video switches.
+	struct SwitchState {
+		bool read_auxiliary_memory = false;
+		bool write_auxiliary_memory = false;
+
+		bool internal_CX_rom = false;
+		bool slot_C3_rom = false;
+		bool internal_C8_rom = false;
+
+		bool store_80 = false;
+		bool alternative_zero_page = false;
+		bool video_page_2 = false;
+		bool high_resolution = false;
 
 		void reset() {
-			switches_.reset();
+			*this = SwitchState();
+		}
+	};
 
-			set_main_paging();
-			set_zero_page_paging();
+	AuxiliaryMemorySwitches(Machine &machine) : machine_(machine) {}
+
+	/// Used by an owner to forward, at least, any access in the range $C000 to $C00B,
+	/// in $C054 to $C058, or in the range $C300 to $CFFF. Safe to call for any [16-bit] address.
+	void access(uint16_t address, bool is_read) {
+		if(address >= 0xc300 && address < 0xd000) {
+			switches_.internal_C8_rom |= ((address >> 8) == 0xc3) && !switches_.slot_C3_rom;
+			switches_.internal_C8_rom &= (address != 0xcfff);
 			set_card_paging();
+			return;
 		}
 
-	private:
-		Machine &machine_;
-		SwitchState switches_;
+		if(address < 0xc000 || address >= 0xc058) return;
 
-		MainState main_state_;
-		void set_main_paging() {
-			const auto previous_state = main_state_;
+		switch(address) {
+			default: break;
 
-			// The two appropriately named switches provide the base case.
-			main_state_.base.read = switches_.read_auxiliary_memory;
-			main_state_.base.write = switches_.write_auxiliary_memory;
-
-			if(switches_.store_80) {
-				// If store 80 is set, use the page 2 flag for the lower carve out;
-				// if both store 80 and high resolution are set, use the page 2 flag for both carve outs.
-				main_state_.region_04_08.read = main_state_.region_04_08.write = switches_.video_page_2;
-
-				if(switches_.high_resolution) {
-					main_state_.region_20_40.read = main_state_.region_20_40.write = switches_.video_page_2;
-				} else {
-					main_state_.region_20_40 = main_state_.base;
+			case 0xc000: case 0xc001:
+				if(!is_read) {
+					switches_.store_80 = address & 1;
+					set_main_paging();
 				}
+			break;
+
+			case 0xc002: case 0xc003:
+				if(!is_read) {
+					switches_.read_auxiliary_memory = address & 1;
+					set_main_paging();
+				}
+			break;
+
+			case 0xc004: case 0xc005:
+				if(!is_read) {
+					switches_.write_auxiliary_memory = address & 1;
+					set_main_paging();
+				}
+			break;
+
+			case 0xc006: case 0xc007:
+				if(!is_read) {
+					switches_.internal_CX_rom = address & 1;
+					set_card_paging();
+				}
+			break;
+
+			case 0xc008: case 0xc009: {
+				const bool alternative_zero_page = address & 1;
+				if(!is_read && switches_.alternative_zero_page != alternative_zero_page) {
+					switches_.alternative_zero_page = alternative_zero_page;
+					set_zero_page_paging();
+				}
+			} break;
+
+			case 0xc00a: case 0xc00b:
+				if(!is_read) {
+					switches_.slot_C3_rom = address & 1;
+					set_card_paging();
+				}
+			break;
+
+			case 0xc054: case 0xc055:
+				switches_.video_page_2 = address & 1;
+				set_main_paging();
+			break;
+
+			case 0xc056: case 0xc057:
+				switches_.high_resolution = address & 1;
+				set_main_paging();
+			break;
+		}
+	}
+
+	/// Provides part of the IIgs interface.
+	void set_state(uint8_t value) {
+		// b7: 1 => use auxiliary memory for zero page; 0 => use main.	[I think the Hardware Reference gets this the wrong way around]
+		// b6: 1 => text page 2 is selected; 0 => text page 1.
+		// b5: 1 => auxiliary RAM bank is selected for reads; 0 => main.
+		// b4: 1 => auxiliary RAM bank is selected for writes; 0 => main.
+		// b0: 1 => the internal ROM is selected for C800+; 0 => card ROM.
+		switches_.alternative_zero_page = value & 0x80;
+		switches_.video_page_2 = value & 0x40;
+		switches_.read_auxiliary_memory = value & 0x20;
+		switches_.write_auxiliary_memory = value & 0x10;
+		switches_.internal_CX_rom = value & 0x01;
+
+		set_main_paging();
+		set_zero_page_paging();
+		set_card_paging();
+	}
+
+	uint8_t get_state() const {
+		return
+			(switches_.alternative_zero_page ? 0x80 : 0x00) |
+			(switches_.video_page_2 ? 0x40 : 0x00) |
+			(switches_.read_auxiliary_memory ? 0x20 : 0x00) |
+			(switches_.write_auxiliary_memory ? 0x10 : 0x00) |
+			(switches_.internal_CX_rom ? 0x01 : 0x00);
+	}
+
+	const MainState &main_state() const {
+		return main_state_;
+	}
+
+	const CardState &card_state() const {
+		return card_state_;
+	}
+
+	/// @returns @c true if the alternative zero page should be used; @c false otherwise.
+	ZeroState zero_state() const {
+		return switches_.alternative_zero_page;
+	}
+
+	const SwitchState switches() const {
+		return switches_;
+	}
+
+	void reset() {
+		switches_.reset();
+
+		set_main_paging();
+		set_zero_page_paging();
+		set_card_paging();
+	}
+
+private:
+	Machine &machine_;
+	SwitchState switches_;
+
+	MainState main_state_;
+	void set_main_paging() {
+		const auto previous_state = main_state_;
+
+		// The two appropriately named switches provide the base case.
+		main_state_.base.read = switches_.read_auxiliary_memory;
+		main_state_.base.write = switches_.write_auxiliary_memory;
+
+		if(switches_.store_80) {
+			// If store 80 is set, use the page 2 flag for the lower carve out;
+			// if both store 80 and high resolution are set, use the page 2 flag for both carve outs.
+			main_state_.region_04_08.read = main_state_.region_04_08.write = switches_.video_page_2;
+
+			if(switches_.high_resolution) {
+				main_state_.region_20_40.read = main_state_.region_20_40.write = switches_.video_page_2;
 			} else {
-				main_state_.region_04_08 = main_state_.region_20_40 = main_state_.base;
-			}
-
-			if(previous_state != main_state_) {
-				machine_.template set_paging<PagingType::Main>();
+				main_state_.region_20_40 = main_state_.base;
 			}
+		} else {
+			main_state_.region_04_08 = main_state_.region_20_40 = main_state_.base;
 		}
 
-		CardState card_state_;
-		void set_card_paging() {
-			const auto previous_state = card_state_;
+		if(previous_state != main_state_) {
+			machine_.template set_paging<PagingType::Main>();
+		}
+	}
 
-			// By default apply the CX switch through to $C7FF.
-			card_state_.region_C1_C3 = card_state_.region_C4_C8 = switches_.internal_CX_rom;
+	CardState card_state_;
+	void set_card_paging() {
+		const auto previous_state = card_state_;
 
-			// Allow the C3 region to be switched to internal ROM in isolation even if the rest of the
-			// first half of the CX region is diabled, if its specific switch is also disabled.
-			if(!switches_.internal_CX_rom && !switches_.slot_C3_rom) {
-				card_state_.region_C3 = true;
-			} else {
-				card_state_.region_C3 = card_state_.region_C1_C3;
-			}
+		// By default apply the CX switch through to $C7FF.
+		card_state_.region_C1_C3 = card_state_.region_C4_C8 = switches_.internal_CX_rom;
 
-			// Apply the CX switch to $C800+, but also allow the C8 switch to select that region in isolation.
-			card_state_.region_C8_D0 = switches_.internal_CX_rom || switches_.internal_C8_rom;
-
-			if(previous_state != card_state_) {
-				machine_.template set_paging<PagingType::CardArea>();
-			}
+		// Allow the C3 region to be switched to internal ROM in isolation even if the rest of the
+		// first half of the CX region is diabled, if its specific switch is also disabled.
+		if(!switches_.internal_CX_rom && !switches_.slot_C3_rom) {
+			card_state_.region_C3 = true;
+		} else {
+			card_state_.region_C3 = card_state_.region_C1_C3;
 		}
 
-		void set_zero_page_paging() {
-			// Believe it or not, the zero page is just set or cleared by a single flag.
-			// As though life were rational.
-			machine_.template set_paging<PagingType::ZeroPage>();
+		// Apply the CX switch to $C800+, but also allow the C8 switch to select that region in isolation.
+		card_state_.region_C8_D0 = switches_.internal_CX_rom || switches_.internal_C8_rom;
+
+		if(previous_state != card_state_) {
+			machine_.template set_paging<PagingType::CardArea>();
 		}
+	}
+
+	void set_zero_page_paging() {
+		// Believe it or not, the zero page is just set or cleared by a single flag.
+		// As though life were rational.
+		machine_.template set_paging<PagingType::ZeroPage>();
+	}
 };
 
 }
diff --git a/Machines/Apple/AppleIIgs/ADB.hpp b/Machines/Apple/AppleIIgs/ADB.hpp
index 9669c0f19..125687789 100644
--- a/Machines/Apple/AppleIIgs/ADB.hpp
+++ b/Machines/Apple/AppleIIgs/ADB.hpp
@@ -19,67 +19,67 @@
 namespace Apple::IIgs::ADB {
 
 class GLU: public InstructionSet::M50740::PortHandler {
-	public:
-		GLU();
+public:
+	GLU();
 
-		uint8_t get_keyboard_data();
-		uint8_t get_mouse_data();
-		uint8_t get_modifier_status();
-		uint8_t get_any_key_down();
+	uint8_t get_keyboard_data();
+	uint8_t get_mouse_data();
+	uint8_t get_modifier_status();
+	uint8_t get_any_key_down();
 
-		uint8_t get_data();
-		uint8_t get_status();
+	uint8_t get_data();
+	uint8_t get_status();
 
-		void set_command(uint8_t);
-		void set_status(uint8_t);
-		void clear_key_strobe();
+	void set_command(uint8_t);
+	void set_status(uint8_t);
+	void clear_key_strobe();
 
-		void set_microcontroller_rom(const std::vector<uint8_t> &rom);
+	void set_microcontroller_rom(const std::vector<uint8_t> &rom);
 
-		void run_for(Cycles cycles);
+	void run_for(Cycles cycles);
 
-		bool get_command_button() const;
-		bool get_option_button() const;
+	bool get_command_button() const;
+	bool get_option_button() const;
 
-		void set_vertical_blank(bool);
-		bool get_vertical_blank() {
-			return vertical_blank_;
-		}
+	void set_vertical_blank(bool);
+	bool get_vertical_blank() {
+		return vertical_blank_;
+	}
 
-		Apple::ADB::Keyboard &keyboard() {
-			return keyboard_;
-		}
-		Inputs::Mouse &get_mouse() {
-			return mouse_;
-		}
+	Apple::ADB::Keyboard &keyboard() {
+		return keyboard_;
+	}
+	Inputs::Mouse &get_mouse() {
+		return mouse_;
+	}
 
-	private:
-		InstructionSet::M50740::Executor executor_;
+private:
+	InstructionSet::M50740::Executor executor_;
 
-		void run_ports_for(Cycles) override;
-		void set_port_output(int port, uint8_t value) override;
-		uint8_t get_port_input(int port) override;
+	void run_ports_for(Cycles) override;
+	void set_port_output(int port, uint8_t value) override;
+	uint8_t get_port_input(int port) override;
 
-		uint8_t registers_[16]{};
+	uint8_t registers_[16]{};
 
-		uint8_t register_address_;
-		uint8_t register_latch_ = 0xff;
-		bool register_strobe_ = false;
+	uint8_t register_address_;
+	uint8_t register_latch_ = 0xff;
+	bool register_strobe_ = false;
 
-		uint8_t status_ = 0x00;
+	uint8_t status_ = 0x00;
 
-		Apple::ADB::Bus bus_;
-		size_t controller_id_;
+	Apple::ADB::Bus bus_;
+	size_t controller_id_;
 
-		uint8_t modifier_state_ = 0;
+	uint8_t modifier_state_ = 0;
 
-		bool vertical_blank_ = false;
+	bool vertical_blank_ = false;
 
-		int visible_mouse_register_ = 2;
+	int visible_mouse_register_ = 2;
 
-		// For now, attach only a keyboard and mouse.
-		Apple::ADB::Mouse mouse_;
-		Apple::ADB::Keyboard keyboard_;
+	// For now, attach only a keyboard and mouse.
+	Apple::ADB::Mouse mouse_;
+	Apple::ADB::Keyboard keyboard_;
 };
 
 }
diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp
index a0c4ac564..674905f07 100644
--- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp
+++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp
@@ -88,921 +88,921 @@ class ConcreteMachine:
 	public MachineTypes::TimedMachine,
 	public CPU::MOS6502Esque::BusHandler<uint32_t> {
 
-	public:
-		ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
-			m65816_(*this),
-			memory_(target.model >= Analyser::Static::AppleIIgs::Target::Model::ROM03),
-			iwm_(CLOCK_RATE / 2),
-			drives35_{
-				{CLOCK_RATE / 2, true},
-				{CLOCK_RATE / 2, true}
-			},
-			drives525_{
-				{CLOCK_RATE / 2},
-				{CLOCK_RATE / 2}
-			},
-			sound_glu_(audio_queue_),
-			audio_toggle_(audio_queue_),
-			mixer_(sound_glu_, audio_toggle_),
-			speaker_(mixer_) {
+public:
+	ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
+		m65816_(*this),
+		memory_(target.model >= Analyser::Static::AppleIIgs::Target::Model::ROM03),
+		iwm_(CLOCK_RATE / 2),
+		drives35_{
+			{CLOCK_RATE / 2, true},
+			{CLOCK_RATE / 2, true}
+		},
+		drives525_{
+			{CLOCK_RATE / 2},
+			{CLOCK_RATE / 2}
+		},
+		sound_glu_(audio_queue_),
+		audio_toggle_(audio_queue_),
+		mixer_(sound_glu_, audio_toggle_),
+		speaker_(mixer_) {
 
-			set_clock_rate(double(CLOCK_RATE));
-			speaker_.set_input_rate(float(CLOCK_RATE) / float(audio_divider));
-			clock_.ClockStorage::set_data(std::begin(default_bram), std::end(default_bram));
+		set_clock_rate(double(CLOCK_RATE));
+		speaker_.set_input_rate(float(CLOCK_RATE) / float(audio_divider));
+		clock_.ClockStorage::set_data(std::begin(default_bram), std::end(default_bram));
 
-			using Target = Analyser::Static::AppleIIgs::Target;
-			ROM::Name system;
-			switch(target.model) {
-				case Target::Model::ROM00:	system = ROM::Name::AppleIIgsROM00;	break;
-				case Target::Model::ROM01:	system = ROM::Name::AppleIIgsROM01;	break;
-				default:					system = ROM::Name::AppleIIgsROM03;	break;
-			}
-			constexpr ROM::Name characters = ROM::Name::AppleIIEnhancedECharacter;
-			constexpr ROM::Name microcontroller = ROM::Name::AppleIIgsMicrocontrollerROM03;
+		using Target = Analyser::Static::AppleIIgs::Target;
+		ROM::Name system;
+		switch(target.model) {
+			case Target::Model::ROM00:	system = ROM::Name::AppleIIgsROM00;	break;
+			case Target::Model::ROM01:	system = ROM::Name::AppleIIgsROM01;	break;
+			default:					system = ROM::Name::AppleIIgsROM03;	break;
+		}
+		constexpr ROM::Name characters = ROM::Name::AppleIIEnhancedECharacter;
+		constexpr ROM::Name microcontroller = ROM::Name::AppleIIgsMicrocontrollerROM03;
 
-			ROM::Request request = ROM::Request(system) && ROM::Request(characters) && ROM::Request(microcontroller);
-			auto roms = rom_fetcher(request);
-			if(!request.validate(roms)) {
-				throw ROMMachine::Error::MissingROMs;
-			}
-			rom_ = roms.find(system)->second;
-			video_->set_character_rom(roms.find(characters)->second);
-			adb_glu_->set_microcontroller_rom(roms.find(microcontroller)->second);
+		ROM::Request request = ROM::Request(system) && ROM::Request(characters) && ROM::Request(microcontroller);
+		auto roms = rom_fetcher(request);
+		if(!request.validate(roms)) {
+			throw ROMMachine::Error::MissingROMs;
+		}
+		rom_ = roms.find(system)->second;
+		video_->set_character_rom(roms.find(characters)->second);
+		adb_glu_->set_microcontroller_rom(roms.find(microcontroller)->second);
 
-			// Run only the currently-interesting self test.
-//			rom_[0x36402] = 2;
-//			rom_[0x36403] = 0x7c;	// ROM_CHECKSUM	[working, when hacks like this are removed]
-//			rom_[0x36404] = 0x6c;
+		// Run only the currently-interesting self test.
+//		rom_[0x36402] = 2;
+//		rom_[0x36403] = 0x7c;	// ROM_CHECKSUM	[working, when hacks like this are removed]
+//		rom_[0x36404] = 0x6c;
+//
+//		rom_[0x36403] = 0x82;	// MOVIRAM		[working]
+//		rom_[0x36404] = 0x67;
+//
+//		rom_[0x36403] = 0x2c;	// SOFT_SW		[working]
+//		rom_[0x36404] = 0x6a;
+//
+//		rom_[0x36403] = 0xe8;	// RAM_ADDR		[working]
+//		rom_[0x36404] = 0x6f;
+//
+//		rom_[0x36403] = 0xc7;	// FPI_SPEED	[working]
+//		rom_[0x36404] = 0x6a;
+//
+//		rom_[0x36403] = 0xd7;	// SER_TST		[broken]
+//		rom_[0x36404] = 0x68;
+//
+//		rom_[0x36403] = 0xdc;	// CLOCK		[broken]
+//		rom_[0x36404] = 0x6c;
+//
+//		rom_[0x36403] = 0x1b;	// BAT_RAM		[broken]
+//		rom_[0x36404] = 0x6e;
+//
+//		rom_[0x36403] = 0x11;	// FDB (/ADB?)	[broken]
+//		rom_[0x36404] = 0x6f;
+//
+//		rom_[0x36403] = 0x41;	// SHADOW_TST	[working]
+//		rom_[0x36404] = 0x6d;
+//
+//		rom_[0x36403] = 0x09;	// CUSTOM_IRQ	[broken?]
+//		rom_[0x36404] = 0x6b;
+//
+//		rom_[0x36403] = 0xf4;	// DOC_EXEC
+//		rom_[0x36404] = 0x70;
+//
+//		rom_[0x36403] = 0xab;	// ECT_SEQ
+//		rom_[0x36404] = 0x64;
+//
+//		rom_[0xfc146f] = rom_[0xfc1470] = 0xea;
 
-//			rom_[0x36403] = 0x82;	// MOVIRAM		[working]
-//			rom_[0x36404] = 0x67;
+		size_t ram_size = 0;
+		switch(target.memory_model) {
+			case Target::MemoryModel::TwoHundredAndFiftySixKB:
+				ram_size = 256;
+			break;
 
-//			rom_[0x36403] = 0x2c;	// SOFT_SW		[working]
-//			rom_[0x36404] = 0x6a;
+			case Target::MemoryModel::OneMB:
+				ram_size = 128 + 1024;
+			break;
 
-//			rom_[0x36403] = 0xe8;	// RAM_ADDR		[working]
-//			rom_[0x36404] = 0x6f;
+			case Target::MemoryModel::EightMB:
+				ram_size = 128 + 8 * 1024;
+			break;
+		}
+		ram_.resize(ram_size * 1024);
 
-//			rom_[0x36403] = 0xc7;	// FPI_SPEED	[working]
-//			rom_[0x36404] = 0x6a;
+		memory_.set_storage(ram_, rom_);
+		video_->set_internal_ram(&ram_[ram_.size() - 128*1024]);
 
-//			rom_[0x36403] = 0xd7;	// SER_TST		[broken]
-//			rom_[0x36404] = 0x68;
+		// Attach drives to the IWM.
+		iwm_->set_drive(0, &drives35_[0]);
+		iwm_->set_drive(1, &drives35_[1]);
 
-//			rom_[0x36403] = 0xdc;	// CLOCK		[broken]
-//			rom_[0x36404] = 0x6c;
+		// Randomise RAM contents.
+		Memory::Fuzz(ram_);
 
-//			rom_[0x36403] = 0x1b;	// BAT_RAM		[broken]
-//			rom_[0x36404] = 0x6e;
-
-//			rom_[0x36403] = 0x11;	// FDB (/ADB?)	[broken]
-//			rom_[0x36404] = 0x6f;
-
-//			rom_[0x36403] = 0x41;	// SHADOW_TST	[working]
-//			rom_[0x36404] = 0x6d;
-
-//			rom_[0x36403] = 0x09;	// CUSTOM_IRQ	[broken?]
-//			rom_[0x36404] = 0x6b;
-
-//			rom_[0x36403] = 0xf4;	// DOC_EXEC
-//			rom_[0x36404] = 0x70;
-
-//			rom_[0x36403] = 0xab;	// ECT_SEQ
-//			rom_[0x36404] = 0x64;
-
-//			rom_[0xfc146f] = rom_[0xfc1470] = 0xea;
-
-			size_t ram_size = 0;
-			switch(target.memory_model) {
-				case Target::MemoryModel::TwoHundredAndFiftySixKB:
-					ram_size = 256;
-				break;
-
-				case Target::MemoryModel::OneMB:
-					ram_size = 128 + 1024;
-				break;
-
-				case Target::MemoryModel::EightMB:
-					ram_size = 128 + 8 * 1024;
-				break;
-			}
-			ram_.resize(ram_size * 1024);
-
-			memory_.set_storage(ram_, rom_);
-			video_->set_internal_ram(&ram_[ram_.size() - 128*1024]);
-
-			// Attach drives to the IWM.
-			iwm_->set_drive(0, &drives35_[0]);
-			iwm_->set_drive(1, &drives35_[1]);
-
-			// Randomise RAM contents.
-			Memory::Fuzz(ram_);
-
-			// Prior to ROM03 there's no power-on bit.
-			if(target.model != Target::Model::ROM03) {
-				speed_register_ &= ~0x40;
-			}
-
-			// Sync up initial values.
-			memory_.set_speed_register(speed_register_ ^ 0x80);
-
-			insert_media(target.media);
+		// Prior to ROM03 there's no power-on bit.
+		if(target.model != Target::Model::ROM03) {
+			speed_register_ &= ~0x40;
 		}
 
-		~ConcreteMachine() {
-			audio_queue_.flush();
+		// Sync up initial values.
+		memory_.set_speed_register(speed_register_ ^ 0x80);
+
+		insert_media(target.media);
+	}
+
+	~ConcreteMachine() {
+		audio_queue_.flush();
+	}
+
+	void run_for(const Cycles cycles) override {
+		m65816_.run_for(cycles);
+	}
+
+	void flush_output(int outputs) final {
+		iwm_.flush();
+		adb_glu_.flush();
+
+		if(outputs & Output::Video) {
+			video_.flush();
 		}
-
-		void run_for(const Cycles cycles) override {
-			m65816_.run_for(cycles);
+		if(outputs & Output::Audio) {
+			AudioUpdater updater(this);
+			audio_queue_.perform();
 		}
+	}
 
-		void flush_output(int outputs) final {
-			iwm_.flush();
-			adb_glu_.flush();
+	void set_scan_target(Outputs::Display::ScanTarget *target) override {
+		video_->set_scan_target(target);
+	}
 
-			if(outputs & Output::Video) {
-				video_.flush();
-			}
-			if(outputs & Output::Audio) {
-				AudioUpdater updater(this);
-				audio_queue_.perform();
+	Outputs::Display::ScanStatus get_scaled_scan_status() const override {
+		return video_->get_scaled_scan_status() * 2.0f;	// TODO: expose multiplier and divider via the JustInTime template?
+	}
+
+	void set_display_type(Outputs::Display::DisplayType display_type) final {
+		video_->set_display_type(display_type);
+	}
+
+	Outputs::Display::DisplayType get_display_type() const final {
+		return video_->get_display_type();
+	}
+
+	Outputs::Speaker::Speaker *get_speaker() final {
+		return &speaker_;
+	}
+
+	// MARK: MediaTarget.
+	bool insert_media(const Analyser::Static::Media &media) final {
+		if(!media.disks.empty()) {
+			const auto disk = media.disks[0];
+			if(disk->get_maximum_head_position().as_int() > 35) {
+				drives35_[0].set_disk(media.disks[0]);
+			} else {
+				drives525_[0].set_disk(media.disks[0]);
 			}
 		}
+		return true;
+	}
 
-		void set_scan_target(Outputs::Display::ScanTarget *target) override {
-			video_->set_scan_target(target);
-		}
+	// MARK: Activity::Source
+	void set_activity_observer(Activity::Observer *observer) final {
+		drives35_[0].set_activity_observer(observer, "First 3.5\" Drive", true);
+		drives35_[1].set_activity_observer(observer, "Second 3.5\" Drive", true);
+		drives525_[0].set_activity_observer(observer, "First 5.25\" Drive", true);
+		drives525_[1].set_activity_observer(observer, "Second 5.25\" Drive", true);
+	}
 
-		Outputs::Display::ScanStatus get_scaled_scan_status() const override {
-			return video_->get_scaled_scan_status() * 2.0f;	// TODO: expose multiplier and divider via the JustInTime template?
-		}
+	// MARK: BusHandler.
+	forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
+		const auto &region = memory_.region(address);
+		bool is_1Mhz = false;
 
-		void set_display_type(Outputs::Display::DisplayType display_type) final {
-			video_->set_display_type(display_type);
-		}
+		if(operation == CPU::WDC65816::BusOperation::ReadVector && !(memory_.get_shadow_register()&0x40)) {
+			// I think vector pulls always go to ROM?
+			// That's slightly implied in the documentation, and doing so makes GS/OS boot, so...
+			// TODO: but is my guess above re: not doing that if IOLC shadowing is disabled correct?
+			assert(address <= 0xffff && address >= 0xffe4);
+			*value = rom_[rom_.size() - 65536 + address];
+		} else if(region.flags & MemoryMap::Region::IsIO) {
+			// Ensure classic auxiliary and language card accesses have effect.
+			const bool is_read = isReadOperation(operation);
+			memory_.access(uint16_t(address), is_read);
 
-		Outputs::Display::DisplayType get_display_type() const final {
-			return video_->get_display_type();
-		}
-
-		Outputs::Speaker::Speaker *get_speaker() final {
-			return &speaker_;
-		}
-
-		// MARK: MediaTarget.
-		bool insert_media(const Analyser::Static::Media &media) final {
-			if(!media.disks.empty()) {
-				const auto disk = media.disks[0];
-				if(disk->get_maximum_head_position().as_int() > 35) {
-					drives35_[0].set_disk(media.disks[0]);
-				} else {
-					drives525_[0].set_disk(media.disks[0]);
-				}
-			}
-			return true;
-		}
-
-		// MARK: Activity::Source
-		void set_activity_observer(Activity::Observer *observer) final {
-			drives35_[0].set_activity_observer(observer, "First 3.5\" Drive", true);
-			drives35_[1].set_activity_observer(observer, "Second 3.5\" Drive", true);
-			drives525_[0].set_activity_observer(observer, "First 5.25\" Drive", true);
-			drives525_[1].set_activity_observer(observer, "Second 5.25\" Drive", true);
-		}
-
-		// MARK: BusHandler.
-		forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
-			const auto &region = memory_.region(address);
-			bool is_1Mhz = false;
-
-			if(operation == CPU::WDC65816::BusOperation::ReadVector && !(memory_.get_shadow_register()&0x40)) {
-				// I think vector pulls always go to ROM?
-				// That's slightly implied in the documentation, and doing so makes GS/OS boot, so...
-				// TODO: but is my guess above re: not doing that if IOLC shadowing is disabled correct?
-				assert(address <= 0xffff && address >= 0xffe4);
-				*value = rom_[rom_.size() - 65536 + address];
-			} else if(region.flags & MemoryMap::Region::IsIO) {
-				// Ensure classic auxiliary and language card accesses have effect.
-				const bool is_read = isReadOperation(operation);
-				memory_.access(uint16_t(address), is_read);
-
-				const auto address_suffix = address & 0xffff;
-				assert(address_suffix >= 0xc000 && address_suffix < 0xd000);
+			const auto address_suffix = address & 0xffff;
+			assert(address_suffix >= 0xc000 && address_suffix < 0xd000);
 #define ReadWrite(x)	(x) | (is_read * 0x10000)
 #define Read(x)			(x) | 0x10000
 #define Write(x)		(x)
-				switch(ReadWrite(address_suffix)) {
+			switch(ReadWrite(address_suffix)) {
 
-					// New video register.
-					case Read(0xc029):
-						*value = video_->get_new_video();
-					break;
-					case Write(0xc029):
-						video_->set_new_video(*value);
-						assert(*value & 1);
+				// New video register.
+				case Read(0xc029):
+					*value = video_->get_new_video();
+				break;
+				case Write(0xc029):
+					video_->set_new_video(*value);
+					assert(*value & 1);
 
-						// TODO: I think bits 7 and 0 might also affect the memory map.
-						// The descripton isn't especially clear — P.90 of the Hardware Reference.
-						// Revisit if necessary.
-					break;
+					// TODO: I think bits 7 and 0 might also affect the memory map.
+					// The descripton isn't especially clear — P.90 of the Hardware Reference.
+					// Revisit if necessary.
+				break;
 
-					// Video [and clock] interrupt register.
-					case Read(0xc023):
-						*value = video_->get_interrupt_register();
-					break;
-					case Write(0xc023):
-						video_->set_interrupt_register(*value);
-					break;
+				// Video [and clock] interrupt register.
+				case Read(0xc023):
+					*value = video_->get_interrupt_register();
+				break;
+				case Write(0xc023):
+					video_->set_interrupt_register(*value);
+				break;
 
-					// Video interrupt-clear register.
-					case Write(0xc032):
-						video_->clear_interrupts(*value);
-					break;
-					case Read(0xc032):
-						// TODO: this seems to be undocumented, but used. What value is likely?
-						*value = 0xff;
-					break;
+				// Video interrupt-clear register.
+				case Write(0xc032):
+					video_->clear_interrupts(*value);
+				break;
+				case Read(0xc032):
+					// TODO: this seems to be undocumented, but used. What value is likely?
+					*value = 0xff;
+				break;
 
-					// Shadow register.
-					case Read(0xc035):
-						*value = memory_.get_shadow_register();
-					break;
-					case Write(0xc035):
-						memory_.set_shadow_register(*value);
-					break;
+				// Shadow register.
+				case Read(0xc035):
+					*value = memory_.get_shadow_register();
+				break;
+				case Write(0xc035):
+					memory_.set_shadow_register(*value);
+				break;
 
-					// Clock data.
-					case Read(0xc033):
-						*value = clock_.get_data();
-					break;
-					case Write(0xc033):
-						clock_.set_data(*value);
-					break;
+				// Clock data.
+				case Read(0xc033):
+					*value = clock_.get_data();
+				break;
+				case Write(0xc033):
+					clock_.set_data(*value);
+				break;
 
-					// Clock and border control.
-					case Read(0xc034):
-						*value = (clock_.get_control() & 0xf0) | (video_.last_valid()->get_border_colour() & 0x0f);
-					break;
-					case Write(0xc034):
-						clock_.set_control(*value);
-						video_->set_border_colour(*value);
-					break;
+				// Clock and border control.
+				case Read(0xc034):
+					*value = (clock_.get_control() & 0xf0) | (video_.last_valid()->get_border_colour() & 0x0f);
+				break;
+				case Write(0xc034):
+					clock_.set_control(*value);
+					video_->set_border_colour(*value);
+				break;
 
-					// Colour text control.
-					case Write(0xc022):
-						video_->set_text_colour(*value);
-					break;
-					case Read(0xc022):
-						*value = video_.last_valid()->get_text_colour();
-					break;
+				// Colour text control.
+				case Write(0xc022):
+					video_->set_text_colour(*value);
+				break;
+				case Read(0xc022):
+					*value = video_.last_valid()->get_text_colour();
+				break;
 
-					// Speed register.
-					case Read(0xc036):
-						*value = speed_register_ ^ 0x80;
-					break;
-					case Write(0xc036):
-						// b7: 1 => operate at 2.8Mhz; 0 => 1Mhz.
-						// b6: power-on status; 1 => system has been turned on by the power switch (TODO: what clears this?)
-						// b5: reserved
-						// b4: [bank shadowing bit; cf. the memory map]
-						// b0–3: motor on-off speed detectors;
-						//		1 => switch to 1Mhz if motor is on; 0 => don't;
-						//		b0 = slot 4 (i.e. watches addresses c0c9, c0c8)
-						//		b1 = slot 5 (i.e. c0d9, c0d8)
-						//		b2 = slot 6 (i.e. c0e9, c0e8)
-						//		b3 = slot 7 (i.e. c0f9, c0f8)
-						memory_.set_speed_register(*value);
-						speed_register_ = *value ^ 0x80;
-					break;
+				// Speed register.
+				case Read(0xc036):
+					*value = speed_register_ ^ 0x80;
+				break;
+				case Write(0xc036):
+					// b7: 1 => operate at 2.8Mhz; 0 => 1Mhz.
+					// b6: power-on status; 1 => system has been turned on by the power switch (TODO: what clears this?)
+					// b5: reserved
+					// b4: [bank shadowing bit; cf. the memory map]
+					// b0–3: motor on-off speed detectors;
+					//		1 => switch to 1Mhz if motor is on; 0 => don't;
+					//		b0 = slot 4 (i.e. watches addresses c0c9, c0c8)
+					//		b1 = slot 5 (i.e. c0d9, c0d8)
+					//		b2 = slot 6 (i.e. c0e9, c0e8)
+					//		b3 = slot 7 (i.e. c0f9, c0f8)
+					memory_.set_speed_register(*value);
+					speed_register_ = *value ^ 0x80;
+				break;
 
-					// [Memory] State register.
-					case Read(0xc068):
-						*value = memory_.get_state_register();
-					break;
-					case Write(0xc068):
-						memory_.set_state_register(*value);
-						video_->set_page2(*value & 0x40);
-					break;
+				// [Memory] State register.
+				case Read(0xc068):
+					*value = memory_.get_state_register();
+				break;
+				case Write(0xc068):
+					memory_.set_state_register(*value);
+					video_->set_page2(*value & 0x40);
+				break;
 
-					case Read(0xc069):
-					case Write(0xc069):
-						// Swallow silently; often hit as a side effect of a 16-bit write to 0xc068.
-					break;
+				case Read(0xc069):
+				case Write(0xc069):
+					// Swallow silently; often hit as a side effect of a 16-bit write to 0xc068.
+				break;
 
-					// Various independent memory switch reads [TODO: does the IIe-style keyboard provide the low seven?].
+				// Various independent memory switch reads [TODO: does the IIe-style keyboard provide the low seven?].
 #define SwitchRead(s) *value = memory_.s ? 0x80 : 0x00; is_1Mhz = true;
 #define LanguageRead(s) SwitchRead(language_card_switches().state().s)
 #define AuxiliaryRead(s) SwitchRead(auxiliary_switches().switches().s)
 #define VideoRead(s) *value = video_.last_valid()->s ? 0x80 : 0x00; is_1Mhz = true;
-					case Read(0xc011):	LanguageRead(bank2);						break;
-					case Read(0xc012):	LanguageRead(read);							break;
-					case Read(0xc013):	AuxiliaryRead(read_auxiliary_memory);		break;
-					case Read(0xc014):	AuxiliaryRead(write_auxiliary_memory);		break;
-					case Read(0xc015):	AuxiliaryRead(internal_CX_rom);				break;
-					case Read(0xc016):	AuxiliaryRead(alternative_zero_page);		break;
-					case Read(0xc017):	AuxiliaryRead(slot_C3_rom);					break;
-					case Read(0xc018):	VideoRead(get_80_store());					break;
-					case Read(0xc019):
-						VideoRead(get_is_vertical_blank(video_.time_since_flush()));
-					break;
-					case Read(0xc01a):	VideoRead(get_text());						break;
-					case Read(0xc01b):	VideoRead(get_mixed());						break;
-					case Read(0xc01c):	VideoRead(get_page2());						break;
-					case Read(0xc01d):	VideoRead(get_high_resolution());			break;
-					case Read(0xc01e):	VideoRead(get_alternative_character_set());	break;
-					case Read(0xc01f):	VideoRead(get_80_columns());				break;
+				case Read(0xc011):	LanguageRead(bank2);						break;
+				case Read(0xc012):	LanguageRead(read);							break;
+				case Read(0xc013):	AuxiliaryRead(read_auxiliary_memory);		break;
+				case Read(0xc014):	AuxiliaryRead(write_auxiliary_memory);		break;
+				case Read(0xc015):	AuxiliaryRead(internal_CX_rom);				break;
+				case Read(0xc016):	AuxiliaryRead(alternative_zero_page);		break;
+				case Read(0xc017):	AuxiliaryRead(slot_C3_rom);					break;
+				case Read(0xc018):	VideoRead(get_80_store());					break;
+				case Read(0xc019):
+					VideoRead(get_is_vertical_blank(video_.time_since_flush()));
+				break;
+				case Read(0xc01a):	VideoRead(get_text());						break;
+				case Read(0xc01b):	VideoRead(get_mixed());						break;
+				case Read(0xc01c):	VideoRead(get_page2());						break;
+				case Read(0xc01d):	VideoRead(get_high_resolution());			break;
+				case Read(0xc01e):	VideoRead(get_alternative_character_set());	break;
+				case Read(0xc01f):	VideoRead(get_80_columns());				break;
 #undef VideoRead
 #undef AuxiliaryRead
 #undef LanguageRead
 #undef SwitchRead
 
-					// Video switches (and annunciators).
-					case Read(0xc050): case Read(0xc051):
-					case Write(0xc050): case Write(0xc051):
-						video_->set_text(address & 1);
-						is_1Mhz = true;
-					break;
-					case Read(0xc052): case Read(0xc053):
-					case Write(0xc052): case Write(0xc053):
-						video_->set_mixed(address & 1);
-						is_1Mhz = true;
-					break;
-					case Read(0xc054): case Read(0xc055):
-					case Write(0xc054): case Write(0xc055):
-						video_->set_page2(address & 1);
-						is_1Mhz = true;
-					break;
-					case Read(0xc056): case Read(0xc057):
-					case Write(0xc056): case Write(0xc057):
-						video_->set_high_resolution(address&1);
-						is_1Mhz = true;
-					break;
-					case Read(0xc058): case Read(0xc059):
-					case Write(0xc058): case Write(0xc059):
-					case Read(0xc05a): case Read(0xc05b):
-					case Write(0xc05a): case Write(0xc05b):
-					case Read(0xc05c): case Read(0xc05d):
-					case Write(0xc05c): case Write(0xc05d):
-						// Annunciators 0, 1 and 2.
-						is_1Mhz = true;
-					break;
-					case Read(0xc05e): case Read(0xc05f):
-					case Write(0xc05e): case Write(0xc05f):
-						video_->set_annunciator_3(!(address&1));
-						is_1Mhz = true;
-					break;
-					case Write(0xc000): case Write(0xc001):
-						video_->set_80_store(address & 1);
-						is_1Mhz = true;
-					break;
-					case Write(0xc00c): case Write(0xc00d):
-						video_->set_80_columns(address & 1);
-						is_1Mhz = true;
-					break;
-					case Write(0xc00e): case Write(0xc00f):
-						video_->set_alternative_character_set(address & 1);
-						is_1Mhz = true;
-					break;
+				// Video switches (and annunciators).
+				case Read(0xc050): case Read(0xc051):
+				case Write(0xc050): case Write(0xc051):
+					video_->set_text(address & 1);
+					is_1Mhz = true;
+				break;
+				case Read(0xc052): case Read(0xc053):
+				case Write(0xc052): case Write(0xc053):
+					video_->set_mixed(address & 1);
+					is_1Mhz = true;
+				break;
+				case Read(0xc054): case Read(0xc055):
+				case Write(0xc054): case Write(0xc055):
+					video_->set_page2(address & 1);
+					is_1Mhz = true;
+				break;
+				case Read(0xc056): case Read(0xc057):
+				case Write(0xc056): case Write(0xc057):
+					video_->set_high_resolution(address&1);
+					is_1Mhz = true;
+				break;
+				case Read(0xc058): case Read(0xc059):
+				case Write(0xc058): case Write(0xc059):
+				case Read(0xc05a): case Read(0xc05b):
+				case Write(0xc05a): case Write(0xc05b):
+				case Read(0xc05c): case Read(0xc05d):
+				case Write(0xc05c): case Write(0xc05d):
+					// Annunciators 0, 1 and 2.
+					is_1Mhz = true;
+				break;
+				case Read(0xc05e): case Read(0xc05f):
+				case Write(0xc05e): case Write(0xc05f):
+					video_->set_annunciator_3(!(address&1));
+					is_1Mhz = true;
+				break;
+				case Write(0xc000): case Write(0xc001):
+					video_->set_80_store(address & 1);
+					is_1Mhz = true;
+				break;
+				case Write(0xc00c): case Write(0xc00d):
+					video_->set_80_columns(address & 1);
+					is_1Mhz = true;
+				break;
+				case Write(0xc00e): case Write(0xc00f):
+					video_->set_alternative_character_set(address & 1);
+					is_1Mhz = true;
+				break;
 
-					// ADB and keyboard.
-					case Read(0xc000):
-						*value = adb_glu_->get_keyboard_data();
-					break;
-					case Read(0xc010):
-						*value = adb_glu_->get_any_key_down() ? 0x80 : 0x00;
-						[[fallthrough]];
-					case Write(0xc010):
-						adb_glu_->clear_key_strobe();
-					break;
+				// ADB and keyboard.
+				case Read(0xc000):
+					*value = adb_glu_->get_keyboard_data();
+				break;
+				case Read(0xc010):
+					*value = adb_glu_->get_any_key_down() ? 0x80 : 0x00;
+					[[fallthrough]];
+				case Write(0xc010):
+					adb_glu_->clear_key_strobe();
+				break;
 
-					case Read(0xc024):
-						*value = adb_glu_->get_mouse_data();
-					break;
-					case Read(0xc025):
-						*value = adb_glu_->get_modifier_status();
-					break;
-					case Read(0xc026):
-						*value = adb_glu_->get_data();
-					break;
-					case Write(0xc026):
-						adb_glu_->set_command(*value);
-					break;
-					case Read(0xc027):
-						*value = adb_glu_->get_status();
-					break;
-					case Write(0xc027):
-						adb_glu_->set_status(*value);
-					break;
+				case Read(0xc024):
+					*value = adb_glu_->get_mouse_data();
+				break;
+				case Read(0xc025):
+					*value = adb_glu_->get_modifier_status();
+				break;
+				case Read(0xc026):
+					*value = adb_glu_->get_data();
+				break;
+				case Write(0xc026):
+					adb_glu_->set_command(*value);
+				break;
+				case Read(0xc027):
+					*value = adb_glu_->get_status();
+				break;
+				case Write(0xc027):
+					adb_glu_->set_status(*value);
+				break;
 
-					// The SCC.
-					case Read(0xc038): case Read(0xc039): case Read(0xc03a): case Read(0xc03b):
-						*value = scc_.read(int(address));
-					break;
-					case Write(0xc038): case Write(0xc039): case Write(0xc03a): case Write(0xc03b):
-						scc_.write(int(address), *value);
-					break;
+				// The SCC.
+				case Read(0xc038): case Read(0xc039): case Read(0xc03a): case Read(0xc03b):
+					*value = scc_.read(int(address));
+				break;
+				case Write(0xc038): case Write(0xc039): case Write(0xc03a): case Write(0xc03b):
+					scc_.write(int(address), *value);
+				break;
 
-					// The audio GLU.
-					case Read(0xc03c): {
-						AudioUpdater updater(this);
-						*value = sound_glu_.get_control();
-					} break;
-					case Write(0xc03c): {
-						AudioUpdater updater(this);
-						sound_glu_.set_control(*value);
-					} break;
-					case Read(0xc03d): {
-						AudioUpdater updater(this);
-						*value = sound_glu_.get_data();
-					} break;
-					case Write(0xc03d): {
-						AudioUpdater updater(this);
-						sound_glu_.set_data(*value);
-					} break;
-					case Read(0xc03e): {
-						AudioUpdater updater(this);
-						*value = sound_glu_.get_address_low();
-					} break;
-					case Write(0xc03e): {
-						AudioUpdater updater(this);
-						sound_glu_.set_address_low(*value);
-					} break;
-					case Read(0xc03f): {
-						AudioUpdater updater(this);
-						*value = sound_glu_.get_address_high();
-					} break;
-					case Write(0xc03f): {
-						AudioUpdater updater(this);
-						sound_glu_.set_address_high(*value);
-					} break;
+				// The audio GLU.
+				case Read(0xc03c): {
+					AudioUpdater updater(this);
+					*value = sound_glu_.get_control();
+				} break;
+				case Write(0xc03c): {
+					AudioUpdater updater(this);
+					sound_glu_.set_control(*value);
+				} break;
+				case Read(0xc03d): {
+					AudioUpdater updater(this);
+					*value = sound_glu_.get_data();
+				} break;
+				case Write(0xc03d): {
+					AudioUpdater updater(this);
+					sound_glu_.set_data(*value);
+				} break;
+				case Read(0xc03e): {
+					AudioUpdater updater(this);
+					*value = sound_glu_.get_address_low();
+				} break;
+				case Write(0xc03e): {
+					AudioUpdater updater(this);
+					sound_glu_.set_address_low(*value);
+				} break;
+				case Read(0xc03f): {
+					AudioUpdater updater(this);
+					*value = sound_glu_.get_address_high();
+				} break;
+				case Write(0xc03f): {
+					AudioUpdater updater(this);
+					sound_glu_.set_address_high(*value);
+				} break;
 
 
-					// These were all dealt with by the call to memory_.access.
-					// TODO: subject to read data? Does vapour lock apply?
-					case Read(0xc002): case Read(0xc003): case Read(0xc004): case Read(0xc005):
-					case Read(0xc006): case Read(0xc007): case Read(0xc008): case Read(0xc009): case Read(0xc00a): case Read(0xc00b):
-						*value = 0xff;
-					break;
-					case Write(0xc002): case Write(0xc003): case Write(0xc004): case Write(0xc005):
-					case Write(0xc006): case Write(0xc007): case Write(0xc008): case Write(0xc009): case Write(0xc00a): case Write(0xc00b):
-					break;
+				// These were all dealt with by the call to memory_.access.
+				// TODO: subject to read data? Does vapour lock apply?
+				case Read(0xc002): case Read(0xc003): case Read(0xc004): case Read(0xc005):
+				case Read(0xc006): case Read(0xc007): case Read(0xc008): case Read(0xc009): case Read(0xc00a): case Read(0xc00b):
+					*value = 0xff;
+				break;
+				case Write(0xc002): case Write(0xc003): case Write(0xc004): case Write(0xc005):
+				case Write(0xc006): case Write(0xc007): case Write(0xc008): case Write(0xc009): case Write(0xc00a): case Write(0xc00b):
+				break;
 
-					// Interrupt ROM addresses; Cf. P25 of the Hardware Reference.
-					case Read(0xc071): case Read(0xc072): case Read(0xc073):
-					case Read(0xc074): case Read(0xc075): case Read(0xc076): case Read(0xc077):
-					case Read(0xc078): case Read(0xc079): case Read(0xc07a): case Read(0xc07b):
-					case Read(0xc07c): case Read(0xc07d): case Read(0xc07e): case Read(0xc07f):
-						*value = rom_[rom_.size() - 65536 + address_suffix];
-					break;
+				// Interrupt ROM addresses; Cf. P25 of the Hardware Reference.
+				case Read(0xc071): case Read(0xc072): case Read(0xc073):
+				case Read(0xc074): case Read(0xc075): case Read(0xc076): case Read(0xc077):
+				case Read(0xc078): case Read(0xc079): case Read(0xc07a): case Read(0xc07b):
+				case Read(0xc07c): case Read(0xc07d): case Read(0xc07e): case Read(0xc07f):
+					*value = rom_[rom_.size() - 65536 + address_suffix];
+				break;
 
+				// Analogue inputs.
+				case Read(0xc061):
+					*value = (adb_glu_->get_command_button() || joysticks_.button(0)) ? 0x80 : 0x00;
+					is_1Mhz = true;
+				break;
+
+				case Read(0xc062):
+					*value = (adb_glu_->get_option_button() || joysticks_.button(1)) ? 0x80 : 0x00;
+					is_1Mhz = true;
+				break;
+
+				case Read(0xc063):
+					*value = joysticks_.button(2) ? 0x80 : 0x00;
+					is_1Mhz = true;
+				break;
+
+				case Read(0xc064):
+				case Read(0xc065):
+				case Read(0xc066):
+				case Read(0xc067): {
 					// Analogue inputs.
-					case Read(0xc061):
-						*value = (adb_glu_->get_command_button() || joysticks_.button(0)) ? 0x80 : 0x00;
-						is_1Mhz = true;
-					break;
+					const size_t input = address_suffix - 0xc064;
+					*value = joysticks_.analogue_channel_is_discharged(input) ? 0x00 : 0x80;
+					is_1Mhz = true;
+				} break;
 
-					case Read(0xc062):
-						*value = (adb_glu_->get_option_button() || joysticks_.button(1)) ? 0x80 : 0x00;
-						is_1Mhz = true;
-					break;
+				case Read(0xc070): case Write(0xc070):
+					joysticks_.access_c070();
+					is_1Mhz = true;
+				break;
 
-					case Read(0xc063):
-						*value = joysticks_.button(2) ? 0x80 : 0x00;
-						is_1Mhz = true;
-					break;
+				// Monochome/colour register.
+				case Read(0xc021):
+					// "Uses bit 7 to determine whether composite output is colour 9) or gray scale (1)."
+					*value = video_.last_valid()->get_composite_is_colour() ? 0x00 : 0x80;
+				break;
+				case Write(0xc021):
+					video_->set_composite_is_colour(!(*value & 0x80));
+				break;
 
-					case Read(0xc064):
-					case Read(0xc065):
-					case Read(0xc066):
-					case Read(0xc067): {
-						// Analogue inputs.
-						const size_t input = address_suffix - 0xc064;
-						*value = joysticks_.analogue_channel_is_discharged(input) ? 0x00 : 0x80;
-						is_1Mhz = true;
-					} break;
+				case Read(0xc02e):
+					*value = video_.last_valid()->get_vertical_counter(video_.time_since_flush());
+					is_1Mhz = true;
+				break;
+				case Read(0xc02f):
+					*value = video_.last_valid()->get_horizontal_counter(video_.time_since_flush());
+					is_1Mhz = true;
+				break;
 
-					case Read(0xc070): case Write(0xc070):
-						joysticks_.access_c070();
-						is_1Mhz = true;
-					break;
+				// C037 seems to be just a full-speed storage register.
+				case Read(0xc037):
+					*value = c037_;
+				break;
+				case Write(0xc037):
+					c037_ = *value;
+				break;
 
-					// Monochome/colour register.
-					case Read(0xc021):
-						// "Uses bit 7 to determine whether composite output is colour 9) or gray scale (1)."
-						*value = video_.last_valid()->get_composite_is_colour() ? 0x00 : 0x80;
-					break;
-					case Write(0xc021):
-						video_->set_composite_is_colour(!(*value & 0x80));
-					break;
+				case Read(0xc041):
+					*value = megaii_interrupt_mask_;
+					is_1Mhz = true;
+				break;
+				case Write(0xc041):
+					megaii_interrupt_mask_ = *value;
+					video_->set_megaii_interrupts_enabled(*value);
+					is_1Mhz = true;
+				break;
+				case Read(0xc044):
+					// MMDELTAX byte.
+					*value = 0;
+					is_1Mhz = true;
+				break;
+				case Read(0xc045):
+					// MMDELTAY byte.
+					*value = 0;
+					is_1Mhz = true;
+				break;
+				case Read(0xc046):
+					*value = video_->get_megaii_interrupt_status();
+					is_1Mhz = true;
+				break;
+				case Read(0xc047): case Write(0xc047):
+					video_->clear_megaii_interrupts();
+					is_1Mhz = true;
+				break;
+				case Read(0xc048): case Write(0xc048):
+					// No-op: Clear Mega II mouse interrupt flags
+					is_1Mhz = true;
+				break;
 
-					case Read(0xc02e):
-						*value = video_.last_valid()->get_vertical_counter(video_.time_since_flush());
-						is_1Mhz = true;
-					break;
-					case Read(0xc02f):
-						*value = video_.last_valid()->get_horizontal_counter(video_.time_since_flush());
-						is_1Mhz = true;
-					break;
+				// Language select.
+				// b7, b6, b5: character generator language select;
+				// b4: NTSC/PAL (0 = NTC);
+				// b3: language select — primary or secondary.
+				case Read(0xc02b):
+					*value = language_;
+				break;
+				case Write(0xc02b):
+					language_ = *value;
+				break;
 
-					// C037 seems to be just a full-speed storage register.
-					case Read(0xc037):
-						*value = c037_;
-					break;
-					case Write(0xc037):
-						c037_ = *value;
-					break;
+				// TODO: 0xc02c is "Addr for tst mode read of character ROM". So it reads... what?
 
-					case Read(0xc041):
-						*value = megaii_interrupt_mask_;
-						is_1Mhz = true;
-					break;
-					case Write(0xc041):
-						megaii_interrupt_mask_ = *value;
-						video_->set_megaii_interrupts_enabled(*value);
-						is_1Mhz = true;
-					break;
-					case Read(0xc044):
-						// MMDELTAX byte.
-						*value = 0;
-						is_1Mhz = true;
-					break;
-					case Read(0xc045):
-						// MMDELTAY byte.
-						*value = 0;
-						is_1Mhz = true;
-					break;
-					case Read(0xc046):
-						*value = video_->get_megaii_interrupt_status();
-						is_1Mhz = true;
-					break;
-					case Read(0xc047): case Write(0xc047):
-						video_->clear_megaii_interrupts();
-						is_1Mhz = true;
-					break;
-					case Read(0xc048): case Write(0xc048):
-						// No-op: Clear Mega II mouse interrupt flags
-						is_1Mhz = true;
-					break;
+				// Slot select.
+				case Read(0xc02d):
+					// b7: 0 = internal ROM code for slot 7;
+					// b6: 0 = internal ROM code for slot 6;
+					// b5: 0 = internal ROM code for slot 5;
+					// b4: 0 = internal ROM code for slot 4;
+					// b3: reserved;
+					// b2: internal ROM code for slot 2;
+					// b1: internal ROM code for slot 1;
+					// b0: reserved.
+					*value = card_mask_;
+				break;
+				case Write(0xc02d):
+					card_mask_ = *value;
+				break;
 
-					// Language select.
-					// b7, b6, b5: character generator language select;
-					// b4: NTSC/PAL (0 = NTC);
-					// b3: language select — primary or secondary.
-					case Read(0xc02b):
-						*value = language_;
-					break;
-					case Write(0xc02b):
-						language_ = *value;
-					break;
+				case Read(0xc030): case Write(0xc030): {
+					AudioUpdater updater(this);
+					audio_toggle_.set_output(!audio_toggle_.get_output());
+				} break;
 
-					// TODO: 0xc02c is "Addr for tst mode read of character ROM". So it reads... what?
+				// 'Test Mode', whatever that is (?)
+				case Read(0xc06e): case Read(0xc06f):
+				case Write(0xc06e): case Write(0xc06f):
+					test_mode_ = address & 1;
+				break;
+				case Read(0xc06d):
+					*value = test_mode_ * 0x80;
+				break;
 
-					// Slot select.
-					case Read(0xc02d):
-						// b7: 0 = internal ROM code for slot 7;
-						// b6: 0 = internal ROM code for slot 6;
-						// b5: 0 = internal ROM code for slot 5;
-						// b4: 0 = internal ROM code for slot 4;
-						// b3: reserved;
-						// b2: internal ROM code for slot 2;
-						// b1: internal ROM code for slot 1;
-						// b0: reserved.
-						*value = card_mask_;
-					break;
-					case Write(0xc02d):
-						card_mask_ = *value;
-					break;
+				// Disk drive controls additional to the IWM.
+				case Read(0xc031):
+					*value = disk_select_;
+				break;
+				case Write(0xc031):
+					// b7: 0 = use head 0; 1 = use head 1.
+					// b6: 0 = use 5.25" disks; 1 = use 3.5".
+					disk_select_ = *value;
+					iwm_->set_select(*value & 0x80);
 
-					case Read(0xc030): case Write(0xc030): {
-						AudioUpdater updater(this);
-						audio_toggle_.set_output(!audio_toggle_.get_output());
-					} break;
+					// Presumably bit 6 selects between two 5.25" drives rather than the two 3.5"?
+					if(*value & 0x40) {
+						iwm_->set_drive(0, &drives35_[0]);
+						iwm_->set_drive(1, &drives35_[1]);
+					} else {
+						iwm_->set_drive(0, &drives525_[0]);
+						iwm_->set_drive(1, &drives525_[1]);
+					}
+				break;
 
-					// 'Test Mode', whatever that is (?)
-					case Read(0xc06e): case Read(0xc06f):
-					case Write(0xc06e): case Write(0xc06f):
-						test_mode_ = address & 1;
-					break;
-					case Read(0xc06d):
-						*value = test_mode_ * 0x80;
-					break;
+				// Addresses on other Apple II devices which do nothing on the GS.
+				case Read(0xc020): case Write(0xc020):	// Reserved for future system expansion.
+				case Read(0xc028): case Write(0xc028):	// ROMBANK; "not used in Apple IIGS".
+				case Read(0xc02a): case Write(0xc02a):	// Reserved for future system expansion.
+				case Read(0xc040): case Write(0xc040):	// Reserved for future system expansion.
+				case Read(0xc042): case Write(0xc042):	// Reserved for future system expansion.
+				case Read(0xc043): case Write(0xc043):	// Reserved for future system expansion.
+				case Read(0xc049): case Write(0xc049):	// Reserved for future system expansion.
+				case Read(0xc04a): case Write(0xc04a):	// Reserved for future system expansion.
+				case Read(0xc04b): case Write(0xc04b):	// Reserved for future system expansion.
+				case Read(0xc04c): case Write(0xc04c):	// Reserved for future system expansion.
+				case Read(0xc04d): case Write(0xc04d):	// Reserved for future system expansion.
+				case Read(0xc04e): case Write(0xc04e):	// Reserved for future system expansion.
+				case Read(0xc04f): case Write(0xc04f):	// Reserved for future system expansion.
+				case Read(0xc06b): case Write(0xc06b):	// Reserved for future system expansion.
+				case Read(0xc06c): case Write(0xc06c):	// Reserved for future system expansion.
+				case Write(0xc07e):
+				break;
 
-					// Disk drive controls additional to the IWM.
-					case Read(0xc031):
-						*value = disk_select_;
-					break;
-					case Write(0xc031):
-						// b7: 0 = use head 0; 1 = use head 1.
-						// b6: 0 = use 5.25" disks; 1 = use 3.5".
-						disk_select_ = *value;
-						iwm_->set_select(*value & 0x80);
+				default:
+					// Update motor mask bits.
+					switch(address_suffix) {
+						case 0xc0c8: motor_flags_ &= ~0x01;	break;
+						case 0xc0c9: motor_flags_ |= 0x01;	break;
+						case 0xc0d8: motor_flags_ &= ~0x02;	break;
+						case 0xc0d9: motor_flags_ |= 0x02;	break;
+						case 0xc0e8: motor_flags_ &= ~0x04;	break;
+						case 0xc0e9: motor_flags_ |= 0x04;	break;
+						case 0xc0f8: motor_flags_ &= ~0x08;	break;
+						case 0xc0f9: motor_flags_ |= 0x08;	break;
+					}
 
-						// Presumably bit 6 selects between two 5.25" drives rather than the two 3.5"?
-						if(*value & 0x40) {
-							iwm_->set_drive(0, &drives35_[0]);
-							iwm_->set_drive(1, &drives35_[1]);
+					// Check for a card access.
+					if(address_suffix >= 0xc080 && address_suffix < 0xc800) {
+						// This is an abridged version of the similar code in AppleII.cpp from
+						// line 653; it would be good to factor that out and support cards here.
+						// For now just either supply the internal ROM or nothing as per the
+						// current card mask.
+
+						size_t card_number = 0;
+						if(address_suffix >= 0xc100) {
+							/*
+								Decode the area conventionally used by cards for ROMs:
+									0xCn00 to 0xCnff: card n.
+							*/
+							card_number = (address_suffix - 0xc000) >> 8;
 						} else {
-							iwm_->set_drive(0, &drives525_[0]);
-							iwm_->set_drive(1, &drives525_[1]);
-						}
-					break;
-
-					// Addresses on other Apple II devices which do nothing on the GS.
-					case Read(0xc020): case Write(0xc020):	// Reserved for future system expansion.
-					case Read(0xc028): case Write(0xc028):	// ROMBANK; "not used in Apple IIGS".
-					case Read(0xc02a): case Write(0xc02a):	// Reserved for future system expansion.
-					case Read(0xc040): case Write(0xc040):	// Reserved for future system expansion.
-					case Read(0xc042): case Write(0xc042):	// Reserved for future system expansion.
-					case Read(0xc043): case Write(0xc043):	// Reserved for future system expansion.
-					case Read(0xc049): case Write(0xc049):	// Reserved for future system expansion.
-					case Read(0xc04a): case Write(0xc04a):	// Reserved for future system expansion.
-					case Read(0xc04b): case Write(0xc04b):	// Reserved for future system expansion.
-					case Read(0xc04c): case Write(0xc04c):	// Reserved for future system expansion.
-					case Read(0xc04d): case Write(0xc04d):	// Reserved for future system expansion.
-					case Read(0xc04e): case Write(0xc04e):	// Reserved for future system expansion.
-					case Read(0xc04f): case Write(0xc04f):	// Reserved for future system expansion.
-					case Read(0xc06b): case Write(0xc06b):	// Reserved for future system expansion.
-					case Read(0xc06c): case Write(0xc06c):	// Reserved for future system expansion.
-					case Write(0xc07e):
-					break;
-
-					default:
-						// Update motor mask bits.
-						switch(address_suffix) {
-							case 0xc0c8: motor_flags_ &= ~0x01;	break;
-							case 0xc0c9: motor_flags_ |= 0x01;	break;
-							case 0xc0d8: motor_flags_ &= ~0x02;	break;
-							case 0xc0d9: motor_flags_ |= 0x02;	break;
-							case 0xc0e8: motor_flags_ &= ~0x04;	break;
-							case 0xc0e9: motor_flags_ |= 0x04;	break;
-							case 0xc0f8: motor_flags_ &= ~0x08;	break;
-							case 0xc0f9: motor_flags_ |= 0x08;	break;
+							/*
+								Decode the area conventionally used by cards for registers:
+									C0n0 to C0nF: card n - 8.
+							*/
+							card_number = (address_suffix - 0xc080) >> 4;
 						}
 
-						// Check for a card access.
-						if(address_suffix >= 0xc080 && address_suffix < 0xc800) {
-							// This is an abridged version of the similar code in AppleII.cpp from
-							// line 653; it would be good to factor that out and support cards here.
-							// For now just either supply the internal ROM or nothing as per the
-							// current card mask.
-
-							size_t card_number = 0;
-							if(address_suffix >= 0xc100) {
-								/*
-									Decode the area conventionally used by cards for ROMs:
-										0xCn00 to 0xCnff: card n.
-								*/
-								card_number = (address_suffix - 0xc000) >> 8;
-							} else {
-								/*
-									Decode the area conventionally used by cards for registers:
-										C0n0 to C0nF: card n - 8.
-								*/
-								card_number = (address_suffix - 0xc080) >> 4;
+						const uint8_t permitted_card_mask_ = card_mask_ & 0xf6;
+						if(permitted_card_mask_ & (1 << card_number)) {
+							// TODO: Access an actual card.
+							assert(operation != CPU::WDC65816::BusOperation::ReadOpcode);
+							if(is_read) {
+								*value = 0xff;
 							}
+						} else {
+							switch(address_suffix) {
+								default:
+									// Temporary: log _potential_ mistakes.
+									if((address_suffix < 0xc100 && address_suffix >= 0xc090) || (address_suffix < 0xc080)) {
+										printf("Internal card-area access: %04x\n", address_suffix);
+									}
+									if(is_read) {
+										*value = rom_[rom_.size() - 65536 + address_suffix];
+									}
+								break;
 
-							const uint8_t permitted_card_mask_ = card_mask_ & 0xf6;
-							if(permitted_card_mask_ & (1 << card_number)) {
-								// TODO: Access an actual card.
-								assert(operation != CPU::WDC65816::BusOperation::ReadOpcode);
-								if(is_read) {
-									*value = 0xff;
-								}
-							} else {
-								switch(address_suffix) {
-									default:
-										// Temporary: log _potential_ mistakes.
-										if((address_suffix < 0xc100 && address_suffix >= 0xc090) || (address_suffix < 0xc080)) {
-											printf("Internal card-area access: %04x\n", address_suffix);
-										}
-										if(is_read) {
-											*value = rom_[rom_.size() - 65536 + address_suffix];
-										}
-									break;
-
-									// IWM.
-									case 0xc0e0:	case 0xc0e1:	case 0xc0e2:	case 0xc0e3:
-									case 0xc0e4:	case 0xc0e5:	case 0xc0e6:	case 0xc0e7:
-									case 0xc0e8:	case 0xc0e9:	case 0xc0ea:	case 0xc0eb:
-									case 0xc0ec:	case 0xc0ed:	case 0xc0ee:	case 0xc0ef:
-										if(is_read) {
-											*value = iwm_->read(int(address_suffix));
-										} else {
-											iwm_->write(int(address_suffix), *value);
-										}
-									break;
-								}
+								// IWM.
+								case 0xc0e0:	case 0xc0e1:	case 0xc0e2:	case 0xc0e3:
+								case 0xc0e4:	case 0xc0e5:	case 0xc0e6:	case 0xc0e7:
+								case 0xc0e8:	case 0xc0e9:	case 0xc0ea:	case 0xc0eb:
+								case 0xc0ec:	case 0xc0ed:	case 0xc0ee:	case 0xc0ef:
+									if(is_read) {
+										*value = iwm_->read(int(address_suffix));
+									} else {
+										iwm_->write(int(address_suffix), *value);
+									}
+								break;
 							}
+						}
 #undef ReadWrite
 #undef Read
 #undef Write
-						} else {
-							// Access the internal ROM.
-							//
-							// TODO: should probably occur only if there was a preceding access to a built-in
-							// card ROM?
-							if(is_read) {
-								*value = rom_[rom_.size() - 65536 + address_suffix];
-							}
-
-							if(address_suffix < 0xc080) {
-								// TODO: all other IO accesses.
-								printf("Unhandled IO %s: %04x\n", is_read ? "read" : "write", address_suffix);
-							}
+					} else {
+						// Access the internal ROM.
+						//
+						// TODO: should probably occur only if there was a preceding access to a built-in
+						// card ROM?
+						if(is_read) {
+							*value = rom_[rom_.size() - 65536 + address_suffix];
 						}
-				}
-			} else {
-				// For debugging purposes; if execution heads off into an unmapped page then
-				// it's pretty certain that my 65816 still has issues.
-				assert(operation != CPU::WDC65816::BusOperation::ReadOpcode || region.read);
-				is_1Mhz = region.flags & MemoryMap::Region::Is1Mhz;
 
-				if(isReadOperation(operation)) {
-					*value = memory_.read(region, address);
-				} else {
-					// Shadowed writes also occur "at 1Mhz".
-					// TODO: this is probably an approximation. I'm assuming that there's the ability asynchronously to post
-					// both a 1Mhz cycle and a 2.8Mhz cycle and since the latter always fits into the former, this is sufficiently
-					// descriptive. I suspect this isn't true as it wouldn't explain the speed boost that Wolfenstein and others
-					// get by adding periodic NOPs within their copy-to-shadow step.
-					//
-					// Maybe the interaction with 2.8Mhz refresh isn't as straightforward as I think?
-					const bool is_shadowed = memory_.is_shadowed(region, address);
-					is_1Mhz |= is_shadowed;
-
-					// Use a very broad test for flushing video: any write to $e0 or $e1, or any write that is shadowed.
-					// TODO: at least restrict the e0/e1 test to possible video buffers!
-					if((address >= 0xe0'0400 && address < 0xe1'a000) || is_shadowed) {
-						video_.flush();
+						if(address_suffix < 0xc080) {
+							// TODO: all other IO accesses.
+							printf("Unhandled IO %s: %04x\n", is_read ? "read" : "write", address_suffix);
+						}
 					}
-
-					memory_.write(region, address, *value);
-				}
 			}
+		} else {
+			// For debugging purposes; if execution heads off into an unmapped page then
+			// it's pretty certain that my 65816 still has issues.
+			assert(operation != CPU::WDC65816::BusOperation::ReadOpcode || region.read);
+			is_1Mhz = region.flags & MemoryMap::Region::Is1Mhz;
 
-			Cycles duration;
-
-			// In preparation for this test: the top bit of speed_register_ has been inverted,
-			// so 1 => 1Mhz, 0 => 2.8Mhz, and motor_flags_ always has that bit set.
-			if(is_1Mhz || (speed_register_ & motor_flags_)) {
-				// TODO: this is very implicitly linked to the video timing; make that overt somehow. Even if it's just with a redundant video setter at construction.
-				const int current_length = 14 + 2*(slow_access_phase_ / 896);						// Length of cycle currently ongoing.
-				const int phase_adjust = (current_length - slow_access_phase_%14)%current_length;	// Amount of time to expand waiting until end of cycle, if not actually at start.
-				const int access_phase = (slow_access_phase_ + phase_adjust)%912;					// Phase at which access will begin.
-				const int next_length = 14 + 2*(access_phase / 896);								// Length of cycle that this access will occur within.
-				duration = Cycles(next_length + phase_adjust);
+			if(isReadOperation(operation)) {
+				*value = memory_.read(region, address);
 			} else {
-				// Clues as to 'fast' refresh timing:
+				// Shadowed writes also occur "at 1Mhz".
+				// TODO: this is probably an approximation. I'm assuming that there's the ability asynchronously to post
+				// both a 1Mhz cycle and a 2.8Mhz cycle and since the latter always fits into the former, this is sufficiently
+				// descriptive. I suspect this isn't true as it wouldn't explain the speed boost that Wolfenstein and others
+				// get by adding periodic NOPs within their copy-to-shadow step.
 				//
-				//	(i)		"The time required for the refresh cycles reduces the effective
-				//			processor speed for programs in RAM by about 8 percent.";
-				//	(ii)	"These cycles occur approximately every 3.5 microseconds"
-				//
-				// 3.5µs @ 14,318,180Hz => one every 50.11 cycles. Safe to assume every 10th fast cycle
-				// is refresh? That feels like a lot.
-				//
-				// (and the IIgs is smart enough that refresh is applicable only to RAM accesses).
-				const int phase_adjust = (5 - fast_access_phase_%5)%5;
-				const int refresh = (fast_access_phase_ / 45) * bool(region.write) * 5;
-				duration = Cycles(5 + phase_adjust + refresh);
-			}
-			// TODO: lookup tables to avoid the above? LCM of the two phases is 22,800 so probably 912+50 bytes plus two counters.
-			fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 50;
-			slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 912;
+				// Maybe the interaction with 2.8Mhz refresh isn't as straightforward as I think?
+				const bool is_shadowed = memory_.is_shadowed(region, address);
+				is_1Mhz |= is_shadowed;
 
-			// Propagate time far and wide.
-			cycles_since_clock_tick_ += duration;
-			auto ticks = cycles_since_clock_tick_.divide(Cycles(CLOCK_RATE)).as_integral();
-			while(ticks--) {
-				clock_.update();
-				video_.last_valid()->notify_clock_tick();	// The video controller marshalls the one-second interrupt.
-															// TODO: I think I may have made a false assumption here; does
-															// the VGC have an independent 1-second interrupt?
-				update_interrupts();
-			}
-
-			video_ += duration;
-			iwm_ += duration;
-			cycles_since_audio_update_ += duration;
-			adb_glu_ += duration;
-
-			if(cycles_since_audio_update_ >= cycles_until_audio_event_) {
-				AudioUpdater updater(this);
-				update_interrupts();
-			}
-			if(video_.did_flush()) {
-				update_interrupts();
-
-				const bool is_vertical_blank = video_.last_valid()->get_is_vertical_blank(video_.time_since_flush());
-				if(is_vertical_blank != adb_glu_.last_valid()->get_vertical_blank()) {
-					adb_glu_->set_vertical_blank(is_vertical_blank);
+				// Use a very broad test for flushing video: any write to $e0 or $e1, or any write that is shadowed.
+				// TODO: at least restrict the e0/e1 test to possible video buffers!
+				if((address >= 0xe0'0400 && address < 0xe1'a000) || is_shadowed) {
+					video_.flush();
 				}
+
+				memory_.write(region, address, *value);
 			}
-
-			joysticks_.update_charge(duration.as<float>() / 14.0f);
-
-			return duration;
 		}
 
-		void update_interrupts() {
-			// Update the interrupt line.
-			// TODO: add ADB controller as event source.
-			m65816_.set_irq_line(video_.last_valid()->get_interrupt_line() || sound_glu_.get_interrupt_line());
+		Cycles duration;
+
+		// In preparation for this test: the top bit of speed_register_ has been inverted,
+		// so 1 => 1Mhz, 0 => 2.8Mhz, and motor_flags_ always has that bit set.
+		if(is_1Mhz || (speed_register_ & motor_flags_)) {
+			// TODO: this is very implicitly linked to the video timing; make that overt somehow. Even if it's just with a redundant video setter at construction.
+			const int current_length = 14 + 2*(slow_access_phase_ / 896);						// Length of cycle currently ongoing.
+			const int phase_adjust = (current_length - slow_access_phase_%14)%current_length;	// Amount of time to expand waiting until end of cycle, if not actually at start.
+			const int access_phase = (slow_access_phase_ + phase_adjust)%912;					// Phase at which access will begin.
+			const int next_length = 14 + 2*(access_phase / 896);								// Length of cycle that this access will occur within.
+			duration = Cycles(next_length + phase_adjust);
+		} else {
+			// Clues as to 'fast' refresh timing:
+			//
+			//	(i)		"The time required for the refresh cycles reduces the effective
+			//			processor speed for programs in RAM by about 8 percent.";
+			//	(ii)	"These cycles occur approximately every 3.5 microseconds"
+			//
+			// 3.5µs @ 14,318,180Hz => one every 50.11 cycles. Safe to assume every 10th fast cycle
+			// is refresh? That feels like a lot.
+			//
+			// (and the IIgs is smart enough that refresh is applicable only to RAM accesses).
+			const int phase_adjust = (5 - fast_access_phase_%5)%5;
+			const int refresh = (fast_access_phase_ / 45) * bool(region.write) * 5;
+			duration = Cycles(5 + phase_adjust + refresh);
+		}
+		// TODO: lookup tables to avoid the above? LCM of the two phases is 22,800 so probably 912+50 bytes plus two counters.
+		fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 50;
+		slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 912;
+
+		// Propagate time far and wide.
+		cycles_since_clock_tick_ += duration;
+		auto ticks = cycles_since_clock_tick_.divide(Cycles(CLOCK_RATE)).as_integral();
+		while(ticks--) {
+			clock_.update();
+			video_.last_valid()->notify_clock_tick();	// The video controller marshalls the one-second interrupt.
+														// TODO: I think I may have made a false assumption here; does
+														// the VGC have an independent 1-second interrupt?
+			update_interrupts();
 		}
 
-		// MARK: - Input.
-		KeyboardMapper *get_keyboard_mapper() final {
-			return &keyboard_mapper_;
+		video_ += duration;
+		iwm_ += duration;
+		cycles_since_audio_update_ += duration;
+		adb_glu_ += duration;
+
+		if(cycles_since_audio_update_ >= cycles_until_audio_event_) {
+			AudioUpdater updater(this);
+			update_interrupts();
+		}
+		if(video_.did_flush()) {
+			update_interrupts();
+
+			const bool is_vertical_blank = video_.last_valid()->get_is_vertical_blank(video_.time_since_flush());
+			if(is_vertical_blank != adb_glu_.last_valid()->get_vertical_blank()) {
+				adb_glu_->set_vertical_blank(is_vertical_blank);
+			}
 		}
 
-		void set_key_state(uint16_t key, bool is_pressed) final {
-			adb_glu_.last_valid()->keyboard().set_key_pressed(Apple::ADB::Key(key), is_pressed);
-		}
+		joysticks_.update_charge(duration.as<float>() / 14.0f);
 
-		void clear_all_keys() final {
-			adb_glu_.last_valid()->keyboard().clear_all_keys();
-		}
+		return duration;
+	}
 
-		Inputs::Mouse &get_mouse() final {
-			return adb_glu_.last_valid()->get_mouse();
-		}
+	void update_interrupts() {
+		// Update the interrupt line.
+		// TODO: add ADB controller as event source.
+		m65816_.set_irq_line(video_.last_valid()->get_interrupt_line() || sound_glu_.get_interrupt_line());
+	}
 
-		const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
-			return joysticks_.get_joysticks();
-		}
+	// MARK: - Input.
+	KeyboardMapper *get_keyboard_mapper() final {
+		return &keyboard_mapper_;
+	}
 
+	void set_key_state(uint16_t key, bool is_pressed) final {
+		adb_glu_.last_valid()->keyboard().set_key_pressed(Apple::ADB::Key(key), is_pressed);
+	}
+
+	void clear_all_keys() final {
+		adb_glu_.last_valid()->keyboard().clear_all_keys();
+	}
+
+	Inputs::Mouse &get_mouse() final {
+		return adb_glu_.last_valid()->get_mouse();
+	}
+
+	const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
+		return joysticks_.get_joysticks();
+	}
+
+private:
+	CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
+	MemoryMap memory_;
+
+	// MARK: - Timing.
+
+	int fast_access_phase_ = 0;
+	int slow_access_phase_ = 0;
+
+	uint8_t speed_register_ = 0x40;	// i.e. Power-on status. (TODO: only if ROM03?)
+	uint8_t motor_flags_ = 0x80;
+
+	// MARK: - Memory storage.
+
+	std::vector<uint8_t> ram_;
+	std::vector<uint8_t> rom_;
+	uint8_t c037_ = 0;
+
+	// MARK: - Other components.
+
+	Apple::Clock::ParallelClock clock_;
+	JustInTimeActor<Apple::IIgs::Video::Video, Cycles, 1, 2> video_;	// i.e. run video at 7Mhz.
+	JustInTimeActor<Apple::IIgs::ADB::GLU, Cycles, 1, 4> adb_glu_;		// i.e. 3,579,545Mhz.
+	Zilog::SCC::z8530 scc_;
+	JustInTimeActor<Apple::IWM, Cycles, 1, 2> iwm_;
+	Cycles cycles_since_clock_tick_;
+	Apple::Macintosh::DoubleDensityDrive drives35_[2];
+	Apple::Disk::DiskIIDrive drives525_[2];
+
+	// The audio parts.
+	Concurrency::AsyncTaskQueue<false> audio_queue_;
+	Apple::IIgs::Sound::GLU sound_glu_;
+	Audio::Toggle audio_toggle_;
+	using AudioSource = Outputs::Speaker::CompoundSource<Apple::IIgs::Sound::GLU, Audio::Toggle>;
+	AudioSource mixer_;
+	Outputs::Speaker::PullLowpass<AudioSource> speaker_;
+	Cycles cycles_since_audio_update_;
+	Cycles cycles_until_audio_event_;
+	static constexpr int audio_divider = 16;
+	void update_audio() {
+		const auto divided_cycles = cycles_since_audio_update_.divide(Cycles(audio_divider));
+		sound_glu_.run_for(divided_cycles);
+		speaker_.run_for(audio_queue_, divided_cycles);
+	}
+	class AudioUpdater {
+	public:
+		AudioUpdater(ConcreteMachine *machine) : machine_(machine) {
+			machine_->update_audio();
+		}
+		~AudioUpdater() {
+			machine_->cycles_until_audio_event_ = machine_->sound_glu_.next_sequence_point();
+		}
 	private:
-		CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
-		MemoryMap memory_;
+		ConcreteMachine *machine_;
+	};
+	friend AudioUpdater;
 
-		// MARK: - Timing.
+	// MARK: - Keyboard and joystick.
+	Apple::ADB::KeyboardMapper keyboard_mapper_;
+	Apple::II::JoystickPair joysticks_;
 
-		int fast_access_phase_ = 0;
-		int slow_access_phase_ = 0;
+	// MARK: - Cards.
 
-		uint8_t speed_register_ = 0x40;	// i.e. Power-on status. (TODO: only if ROM03?)
-		uint8_t motor_flags_ = 0x80;
+	// TODO: most of cards.
+	uint8_t card_mask_ = 0x00;
 
-		// MARK: - Memory storage.
+	bool test_mode_ = false;
+	uint8_t language_ = 0;
+	uint8_t disk_select_ = 0;
 
-		std::vector<uint8_t> ram_;
-		std::vector<uint8_t> rom_;
-		uint8_t c037_ = 0;
-
-		// MARK: - Other components.
-
-		Apple::Clock::ParallelClock clock_;
-		JustInTimeActor<Apple::IIgs::Video::Video, Cycles, 1, 2> video_;	// i.e. run video at 7Mhz.
-		JustInTimeActor<Apple::IIgs::ADB::GLU, Cycles, 1, 4> adb_glu_;		// i.e. 3,579,545Mhz.
-		Zilog::SCC::z8530 scc_;
-		JustInTimeActor<Apple::IWM, Cycles, 1, 2> iwm_;
-		Cycles cycles_since_clock_tick_;
-		Apple::Macintosh::DoubleDensityDrive drives35_[2];
-		Apple::Disk::DiskIIDrive drives525_[2];
-
-		// The audio parts.
-		Concurrency::AsyncTaskQueue<false> audio_queue_;
-		Apple::IIgs::Sound::GLU sound_glu_;
-		Audio::Toggle audio_toggle_;
-		using AudioSource = Outputs::Speaker::CompoundSource<Apple::IIgs::Sound::GLU, Audio::Toggle>;
-		AudioSource mixer_;
-		Outputs::Speaker::PullLowpass<AudioSource> speaker_;
-		Cycles cycles_since_audio_update_;
-		Cycles cycles_until_audio_event_;
-		static constexpr int audio_divider = 16;
-		void update_audio() {
-			const auto divided_cycles = cycles_since_audio_update_.divide(Cycles(audio_divider));
-			sound_glu_.run_for(divided_cycles);
-			speaker_.run_for(audio_queue_, divided_cycles);
-		}
-		class AudioUpdater {
-			public:
-				AudioUpdater(ConcreteMachine *machine) : machine_(machine) {
-					machine_->update_audio();
-				}
-				~AudioUpdater() {
-					machine_->cycles_until_audio_event_ = machine_->sound_glu_.next_sequence_point();
-				}
-			private:
-				ConcreteMachine *machine_;
-		};
-		friend AudioUpdater;
-
-		// MARK: - Keyboard and joystick.
-		Apple::ADB::KeyboardMapper keyboard_mapper_;
-		Apple::II::JoystickPair joysticks_;
-
-		// MARK: - Cards.
-
-		// TODO: most of cards.
-		uint8_t card_mask_ = 0x00;
-
-		bool test_mode_ = false;
-		uint8_t language_ = 0;
-		uint8_t disk_select_ = 0;
-
-		uint8_t megaii_interrupt_mask_ = 0;
+	uint8_t megaii_interrupt_mask_ = 0;
 };
 
 }
diff --git a/Machines/Apple/AppleIIgs/MemoryMap.hpp b/Machines/Apple/AppleIIgs/MemoryMap.hpp
index 363758475..94fef9c05 100644
--- a/Machines/Apple/AppleIIgs/MemoryMap.hpp
+++ b/Machines/Apple/AppleIIgs/MemoryMap.hpp
@@ -20,144 +20,144 @@
 namespace Apple::IIgs {
 
 class MemoryMap {
-	public:
-		// MARK: - Initial construction and configuration.
+public:
+	// MARK: - Initial construction and configuration.
 
-		MemoryMap(bool is_rom03) : auxiliary_switches_(*this), language_card_(*this) {
-			setup_shadow_maps(is_rom03);
-		}
+	MemoryMap(bool is_rom03) : auxiliary_switches_(*this), language_card_(*this) {
+		setup_shadow_maps(is_rom03);
+	}
 
-		/// Sets the ROM and RAM storage underlying this MemoryMap.
-		void set_storage(std::vector<uint8_t> &ram, std::vector<uint8_t> &rom);
+	/// Sets the ROM and RAM storage underlying this MemoryMap.
+	void set_storage(std::vector<uint8_t> &ram, std::vector<uint8_t> &rom);
 
-		// MARK: - Live bus access notifications and register access.
+	// MARK: - Live bus access notifications and register access.
 
-		void set_shadow_register(uint8_t value);
-		uint8_t get_shadow_register() const;
+	void set_shadow_register(uint8_t value);
+	uint8_t get_shadow_register() const;
 
-		void set_speed_register(uint8_t value);
+	void set_speed_register(uint8_t value);
 
-		void set_state_register(uint8_t value);
-		uint8_t get_state_register() const;
+	void set_state_register(uint8_t value);
+	uint8_t get_state_register() const;
 
-		void access(uint16_t address, bool is_read);
+	void access(uint16_t address, bool is_read);
 
-		using AuxiliaryMemorySwitches = Apple::II::AuxiliaryMemorySwitches<MemoryMap>;
-		const AuxiliaryMemorySwitches &auxiliary_switches() const {
-			return auxiliary_switches_;
-		}
+	using AuxiliaryMemorySwitches = Apple::II::AuxiliaryMemorySwitches<MemoryMap>;
+	const AuxiliaryMemorySwitches &auxiliary_switches() const {
+		return auxiliary_switches_;
+	}
 
-		using LanguageCardSwitches = Apple::II::LanguageCardSwitches<MemoryMap>;
-		const LanguageCardSwitches &language_card_switches() const {
-			return language_card_;
-		}
+	using LanguageCardSwitches = Apple::II::LanguageCardSwitches<MemoryMap>;
+	const LanguageCardSwitches &language_card_switches() const {
+		return language_card_;
+	}
 
-		// MARK: - Accessors for reading and writing RAM.
+	// MARK: - Accessors for reading and writing RAM.
 
-		struct Region {
-			uint8_t *write = nullptr;
-			const uint8_t *read = nullptr;
-			uint8_t flags = 0;
+	struct Region {
+		uint8_t *write = nullptr;
+		const uint8_t *read = nullptr;
+		uint8_t flags = 0;
 
-			enum Flag: uint8_t {
-				Is1Mhz = 1 << 0,		// Both reads and writes should be synchronised with the 1Mhz clock.
-				IsIO = 1 << 1,			// Indicates that this region should be checked for soft switches, registers, etc.
-			};
+		enum Flag: uint8_t {
+			Is1Mhz = 1 << 0,		// Both reads and writes should be synchronised with the 1Mhz clock.
+			IsIO = 1 << 1,			// Indicates that this region should be checked for soft switches, registers, etc.
 		};
+	};
 
-		const Region &region(uint32_t address) const {	return regions_[region_map_[address >> 8]];	}
-		uint8_t read(const Region &region, uint32_t address) const {
-			return region.read ? region.read[address] : 0xff;
+	const Region &region(uint32_t address) const {	return regions_[region_map_[address >> 8]];	}
+	uint8_t read(const Region &region, uint32_t address) const {
+		return region.read ? region.read[address] : 0xff;
+	}
+
+	bool is_shadowed(const Region &region, uint32_t address) const {
+		// ROM is never shadowed.
+		if(!region.write) {
+			return false;
 		}
 
-		bool is_shadowed(const Region &region, uint32_t address) const {
-			// ROM is never shadowed.
-			if(!region.write) {
-				return false;
-			}
-
-			const auto physical = physical_address(region, address);
-			assert(physical <= 0xff'ffff);
-			return shadow_pages_[(physical >> 10) & 127] && shadow_banks_[physical >> 17];
-		}
-		void write(const Region &region, uint32_t address, uint8_t value) {
-			if(!region.write) {
-				return;
-			}
-
-			// Write once.
-			region.write[address] = value;
-
-			// Write again, either to the same place (if unshadowed) or to the shadow destination.
-			static constexpr std::size_t shadow_mask[2] = {0xff'ffff, 0x01'ffff};
-			const bool shadowed = is_shadowed(region, address);
-			shadow_base_[shadowed][physical_address(region, address) & shadow_mask[shadowed]] = value;
+		const auto physical = physical_address(region, address);
+		assert(physical <= 0xff'ffff);
+		return shadow_pages_[(physical >> 10) & 127] && shadow_banks_[physical >> 17];
+	}
+	void write(const Region &region, uint32_t address, uint8_t value) {
+		if(!region.write) {
+			return;
 		}
 
-		// The objective is to support shadowing:
-		//	1. without storing a whole extra pointer, and such that the shadowing flags
-		//		are orthogonal to the current auxiliary memory settings;
-		//	2. in such a way as to support shadowing both in banks $00/$01 and elsewhere; and
-		//	3. to do so without introducing too much in the way of branching.
-		//
-		// Hence the implemented solution: if shadowing is enabled then use the distance from the start of
-		// physical RAM modulo 128k indexed into the bank $e0/$e1 RAM.
-		//
-		// With a further twist: the modulo and pointer are indexed on ::IsShadowed to eliminate a branch
-		// even on that.
+		// Write once.
+		region.write[address] = value;
 
-	private:
-		AuxiliaryMemorySwitches auxiliary_switches_;
-		LanguageCardSwitches language_card_;
-		friend AuxiliaryMemorySwitches;
-		friend LanguageCardSwitches;
+		// Write again, either to the same place (if unshadowed) or to the shadow destination.
+		static constexpr std::size_t shadow_mask[2] = {0xff'ffff, 0x01'ffff};
+		const bool shadowed = is_shadowed(region, address);
+		shadow_base_[shadowed][physical_address(region, address) & shadow_mask[shadowed]] = value;
+	}
 
-		uint8_t shadow_register_ = 0x00;
-		uint8_t speed_register_ = 0x00;
+	// The objective is to support shadowing:
+	//	1. without storing a whole extra pointer, and such that the shadowing flags
+	//		are orthogonal to the current auxiliary memory settings;
+	//	2. in such a way as to support shadowing both in banks $00/$01 and elsewhere; and
+	//	3. to do so without introducing too much in the way of branching.
+	//
+	// Hence the implemented solution: if shadowing is enabled then use the distance from the start of
+	// physical RAM modulo 128k indexed into the bank $e0/$e1 RAM.
+	//
+	// With a further twist: the modulo and pointer are indexed on ::IsShadowed to eliminate a branch
+	// even on that.
 
-		// MARK: - Banking.
+private:
+	AuxiliaryMemorySwitches auxiliary_switches_;
+	LanguageCardSwitches language_card_;
+	friend AuxiliaryMemorySwitches;
+	friend LanguageCardSwitches;
 
-		void assert_is_region(uint8_t start, uint8_t end);
-		template <int type> void set_paging();
+	uint8_t shadow_register_ = 0x00;
+	uint8_t speed_register_ = 0x00;
 
-		uint8_t *ram_base_ = nullptr;
+	// MARK: - Banking.
 
-		// Memory layout here is done via double indirection; the main loop should:
-		//	(i) use the top two bytes of the address to get an index from region_map; and
-		//	(ii) use that to index the memory_regions table.
-		//
-		// Pointers are eight bytes at the time of writing, so the extra level of indirection
-		// reduces what would otherwise be a 1.25mb table down to not a great deal more than 64kb.
-		std::array<uint8_t, 65536> region_map_{};
-		std::array<Region, 40> regions_;	// An assert above ensures that this is large enough; there's no
-											// doctrinal reason for it to be whatever size it is now, just
-											// adjust as required.
+	void assert_is_region(uint8_t start, uint8_t end);
+	template <int type> void set_paging();
 
-		std::size_t physical_address(const Region &region, uint32_t address) const {
-			return std::size_t(&region.write[address] - ram_base_);
-		}
+	uint8_t *ram_base_ = nullptr;
 
-		// MARK: - Shadowing
+	// Memory layout here is done via double indirection; the main loop should:
+	//	(i) use the top two bytes of the address to get an index from region_map; and
+	//	(ii) use that to index the memory_regions table.
+	//
+	// Pointers are eight bytes at the time of writing, so the extra level of indirection
+	// reduces what would otherwise be a 1.25mb table down to not a great deal more than 64kb.
+	std::array<uint8_t, 65536> region_map_{};
+	std::array<Region, 40> regions_;	// An assert above ensures that this is large enough; there's no
+										// doctrinal reason for it to be whatever size it is now, just
+										// adjust as required.
 
-		// Various precomputed bitsets describing key regions; std::bitset doesn't support constexpr instantiation
-		// beyond the first 64 bits at the time of writing, alas, so these are generated at runtime.
-		std::bitset<128> shadow_text1_;
-		std::bitset<128> shadow_text2_;
-		std::bitset<128> shadow_highres1_, shadow_highres1_aux_;
-		std::bitset<128> shadow_highres2_, shadow_highres2_aux_;
-		std::bitset<128> shadow_superhighres_;
-		void setup_shadow_maps(bool is_rom03);
-		void set_shadowing();
+	std::size_t physical_address(const Region &region, uint32_t address) const {
+		return std::size_t(&region.write[address] - ram_base_);
+	}
 
-		uint8_t *shadow_base_[2] = {nullptr, nullptr};
+	// MARK: - Shadowing
 
-		// Divide the final 128kb of memory into 1kb chunks and flag to indicate whether
-		// each is a potential destination for shadowing.
-		std::bitset<128> shadow_pages_{};
+	// Various precomputed bitsets describing key regions; std::bitset doesn't support constexpr instantiation
+	// beyond the first 64 bits at the time of writing, alas, so these are generated at runtime.
+	std::bitset<128> shadow_text1_;
+	std::bitset<128> shadow_text2_;
+	std::bitset<128> shadow_highres1_, shadow_highres1_aux_;
+	std::bitset<128> shadow_highres2_, shadow_highres2_aux_;
+	std::bitset<128> shadow_superhighres_;
+	void setup_shadow_maps(bool is_rom03);
+	void set_shadowing();
 
-		// Divide the whole 16mb of memory into 128kb chunks and flag to indicate whether
-		// each is a potential source of shadowing.
-		std::bitset<128> shadow_banks_{};
+	uint8_t *shadow_base_[2] = {nullptr, nullptr};
+
+	// Divide the final 128kb of memory into 1kb chunks and flag to indicate whether
+	// each is a potential destination for shadowing.
+	std::bitset<128> shadow_pages_{};
+
+	// Divide the whole 16mb of memory into 128kb chunks and flag to indicate whether
+	// each is a potential source of shadowing.
+	std::bitset<128> shadow_banks_{};
 };
 
 }
diff --git a/Machines/Apple/AppleIIgs/Sound.hpp b/Machines/Apple/AppleIIgs/Sound.hpp
index 711873cbd..b217a59dc 100644
--- a/Machines/Apple/AppleIIgs/Sound.hpp
+++ b/Machines/Apple/AppleIIgs/Sound.hpp
@@ -17,90 +17,90 @@
 namespace Apple::IIgs::Sound {
 
 class GLU: public Outputs::Speaker::BufferSource<GLU, false> {	// TODO: isn't this stereo?
-	public:
-		GLU(Concurrency::AsyncTaskQueue<false> &audio_queue);
+public:
+	GLU(Concurrency::AsyncTaskQueue<false> &audio_queue);
 
-		void set_control(uint8_t);
-		uint8_t get_control();
-		void set_data(uint8_t);
-		uint8_t get_data();
-		void set_address_low(uint8_t);
-		uint8_t get_address_low();
-		void set_address_high(uint8_t);
-		uint8_t get_address_high();
+	void set_control(uint8_t);
+	uint8_t get_control();
+	void set_data(uint8_t);
+	uint8_t get_data();
+	void set_address_low(uint8_t);
+	uint8_t get_address_low();
+	void set_address_high(uint8_t);
+	uint8_t get_address_high();
 
-		void run_for(Cycles);
-		Cycles next_sequence_point() const;
-		bool get_interrupt_line();
+	void run_for(Cycles);
+	Cycles next_sequence_point() const;
+	bool get_interrupt_line();
 
-		// SampleSource.
-		template <Outputs::Speaker::Action action>
-		void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
-		void set_sample_volume_range(std::int16_t range);
-		bool is_zero_level() const { return false; }	// TODO.
+	// SampleSource.
+	template <Outputs::Speaker::Action action>
+	void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
+	void set_sample_volume_range(std::int16_t range);
+	bool is_zero_level() const { return false; }	// TODO.
 
-	private:
-		Concurrency::AsyncTaskQueue<false> &audio_queue_;
+private:
+	Concurrency::AsyncTaskQueue<false> &audio_queue_;
 
-		uint16_t address_ = 0;
+	uint16_t address_ = 0;
 
-		// Use a circular buffer for piping memory alterations onto the audio
-		// thread; it would be prohibitive to defer every write individually.
-		//
-		// Assumed: on most modern architectures, an atomic 64-bit read or
-		// write can be achieved locklessly.
-		struct MemoryWrite {
-			uint32_t time;
-			uint16_t address;
-			uint8_t value;
-			bool enabled;
-		};
-		static_assert(sizeof(MemoryWrite) == 8);
-		constexpr static int StoreBufferSize = 16384;
+	// Use a circular buffer for piping memory alterations onto the audio
+	// thread; it would be prohibitive to defer every write individually.
+	//
+	// Assumed: on most modern architectures, an atomic 64-bit read or
+	// write can be achieved locklessly.
+	struct MemoryWrite {
+		uint32_t time;
+		uint16_t address;
+		uint8_t value;
+		bool enabled;
+	};
+	static_assert(sizeof(MemoryWrite) == 8);
+	constexpr static int StoreBufferSize = 16384;
 
-		std::atomic<MemoryWrite> pending_stores_[StoreBufferSize];
-		uint32_t pending_store_read_ = 0, pending_store_read_time_ = 0;
-		uint32_t pending_store_write_ = 0, pending_store_write_time_ = 0;
+	std::atomic<MemoryWrite> pending_stores_[StoreBufferSize];
+	uint32_t pending_store_read_ = 0, pending_store_read_time_ = 0;
+	uint32_t pending_store_write_ = 0, pending_store_write_time_ = 0;
 
-		// Maintain state both 'locally' (i.e. on the emulation thread) and
-		// 'remotely' (i.e. on the audio thread).
-		struct EnsoniqState {
-			uint8_t ram_[65536];
-			struct Oscillator {
-				uint32_t position;
+	// Maintain state both 'locally' (i.e. on the emulation thread) and
+	// 'remotely' (i.e. on the audio thread).
+	struct EnsoniqState {
+		uint8_t ram_[65536];
+		struct Oscillator {
+			uint32_t position;
 
-				// Programmer-set values.
-				uint16_t velocity;
-				uint8_t volume;
-				uint8_t address;
-				uint8_t control;
-				uint8_t table_size;
+			// Programmer-set values.
+			uint16_t velocity;
+			uint8_t volume;
+			uint8_t address;
+			uint8_t control;
+			uint8_t table_size;
 
-				// Derived state.
-				uint32_t overflow_mask;			// If a non-zero bit gets anywhere into the overflow mask, this channel
-												// has wrapped around. It's a function of table_size.
-				bool interrupt_request = false;	// Will be non-zero if this channel would request an interrupt, were
-												// it currently enabled to do so.
+			// Derived state.
+			uint32_t overflow_mask;			// If a non-zero bit gets anywhere into the overflow mask, this channel
+											// has wrapped around. It's a function of table_size.
+			bool interrupt_request = false;	// Will be non-zero if this channel would request an interrupt, were
+											// it currently enabled to do so.
 
-				uint8_t sample(uint8_t *ram);
-				int16_t output(uint8_t *ram);
-			} oscillators[32];
+			uint8_t sample(uint8_t *ram);
+			int16_t output(uint8_t *ram);
+		} oscillators[32];
 
-			// Some of these aren't actually needed on both threads.
-			uint8_t control = 0;
-			int oscillator_count = 1;
+		// Some of these aren't actually needed on both threads.
+		uint8_t control = 0;
+		int oscillator_count = 1;
 
-			void set_register(uint16_t address, uint8_t value);
-		} local_, remote_;
+		void set_register(uint16_t address, uint8_t value);
+	} local_, remote_;
 
-		// Functions to update an EnsoniqState; these don't belong to the state itself
-		// because they also access the pending stores (inter alia).
-		template <Outputs::Speaker::Action action>
-		void generate_audio(size_t number_of_samples, Outputs::Speaker::MonoSample *target);
-		void skip_audio(EnsoniqState &state, size_t number_of_samples);
+	// Functions to update an EnsoniqState; these don't belong to the state itself
+	// because they also access the pending stores (inter alia).
+	template <Outputs::Speaker::Action action>
+	void generate_audio(size_t number_of_samples, Outputs::Speaker::MonoSample *target);
+	void skip_audio(EnsoniqState &state, size_t number_of_samples);
 
-		// Audio-thread state.
-		int16_t output_range_ = 0;
+	// Audio-thread state.
+	int16_t output_range_ = 0;
 };
 
 }
diff --git a/Machines/Apple/AppleIIgs/Video.hpp b/Machines/Apple/AppleIIgs/Video.hpp
index c7d17e9bd..8845cd20c 100644
--- a/Machines/Apple/AppleIIgs/Video.hpp
+++ b/Machines/Apple/AppleIIgs/Video.hpp
@@ -20,236 +20,236 @@ namespace Apple::IIgs::Video {
 	stretched cycle.
 */
 class Video: public Apple::II::VideoSwitches<Cycles> {
+public:
+	Video();
+	void set_internal_ram(const uint8_t *);
+
+	bool get_is_vertical_blank(Cycles offset);
+	uint8_t get_horizontal_counter(Cycles offset);
+	uint8_t get_vertical_counter(Cycles offset);
+
+	void set_new_video(uint8_t);
+	uint8_t get_new_video();
+
+	void clear_interrupts(uint8_t);
+	uint8_t get_interrupt_register();
+	void set_interrupt_register(uint8_t);
+	bool get_interrupt_line();
+
+	void notify_clock_tick();
+
+	void set_border_colour(uint8_t);
+	void set_text_colour(uint8_t);
+	uint8_t get_text_colour();
+	uint8_t get_border_colour();
+
+	void set_composite_is_colour(bool);
+	bool get_composite_is_colour();
+
+	/// Sets the scan target.
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target);
+
+	/// Gets the current scan status.
+	Outputs::Display::ScanStatus get_scaled_scan_status() const;
+
+	/// Sets the type of output.
+	void set_display_type(Outputs::Display::DisplayType);
+
+	/// Gets the type of output.
+	Outputs::Display::DisplayType get_display_type() const;
+
+	/// Determines the period until video might autonomously update its interrupt lines.
+	Cycles next_sequence_point() const;
+
+	/// Sets the Mega II interrupt enable state — 1/4-second and VBL interrupts are
+	/// generated here.
+	void set_megaii_interrupts_enabled(uint8_t);
+
+	uint8_t get_megaii_interrupt_status();
+
+	void clear_megaii_interrupts();
+
+private:
+	Outputs::CRT::CRT crt_;
+
+	// This is coupled to Apple::II::GraphicsMode, but adds detail for the IIgs.
+	enum class GraphicsMode {
+		Text = 0,
+		DoubleText,
+		HighRes,
+		DoubleHighRes,
+		LowRes,
+		DoubleLowRes,
+		FatLowRes,
+
+		// Additions:
+		DoubleHighResMono,
+		SuperHighRes
+	};
+	constexpr bool is_colour_ntsc(const GraphicsMode m) {
+		return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes;
+	}
+
+	GraphicsMode graphics_mode(int row) const {
+		if(new_video_ & 0x80) {
+			return GraphicsMode::SuperHighRes;
+		}
+
+		const auto ii_mode = Apple::II::VideoSwitches<Cycles>::graphics_mode(row);
+		switch(ii_mode) {
+			// Coupling very much assumed here.
+			case Apple::II::GraphicsMode::DoubleHighRes:
+				if(new_video_ & 0x20) {
+					return GraphicsMode::DoubleHighResMono;
+				}
+			[[fallthrough]];
+
+			default: return GraphicsMode(int(ii_mode));	break;
+		}
+	}
+
+	enum class PixelBufferFormat {
+		Text, DoubleText, NTSC, NTSCMono, SuperHighRes
+	};
+	constexpr PixelBufferFormat format_for_mode(GraphicsMode m) {
+		switch(m) {
+			case GraphicsMode::Text:				return PixelBufferFormat::Text;
+			case GraphicsMode::DoubleText:			return PixelBufferFormat::DoubleText;
+			default:								return PixelBufferFormat::NTSC;
+			case GraphicsMode::DoubleHighResMono:	return PixelBufferFormat::NTSCMono;
+			case GraphicsMode::SuperHighRes:		return PixelBufferFormat::SuperHighRes;
+		}
+	}
+
+	void advance(Cycles);
+
+	uint8_t new_video_ = 0x01;
+
+	class Interrupts {
 	public:
-		Video();
-		void set_internal_ram(const uint8_t *);
+		void add(uint8_t value) {
+			// Taken literally, status accumulates regardless of being enabled,
+			// potentially to be polled, it simply doesn't trigger an interrupt.
+			value_ |= value;
+			test();
+		}
 
-		bool get_is_vertical_blank(Cycles offset);
-		uint8_t get_horizontal_counter(Cycles offset);
-		uint8_t get_vertical_counter(Cycles offset);
+		void clear(uint8_t value) {
+			// Zeroes in bits 5 or 6 clear the respective interrupts.
+			value_ &= value | ~0x60;
+			test();
+		}
 
-		void set_new_video(uint8_t);
-		uint8_t get_new_video();
+		void set_control(uint8_t value) {
+			// Ones in bits 1 or 2 enable the respective interrupts.
+			value_ = (value_ & ~0x6) | (value & 0x6);
+			test();
+		}
 
-		void clear_interrupts(uint8_t);
-		uint8_t get_interrupt_register();
-		void set_interrupt_register(uint8_t);
-		bool get_interrupt_line();
+		uint8_t status() const {
+			return value_;
+		}
 
-		void notify_clock_tick();
-
-		void set_border_colour(uint8_t);
-		void set_text_colour(uint8_t);
-		uint8_t get_text_colour();
-		uint8_t get_border_colour();
-
-		void set_composite_is_colour(bool);
-		bool get_composite_is_colour();
-
-		/// Sets the scan target.
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target);
-
-		/// Gets the current scan status.
-		Outputs::Display::ScanStatus get_scaled_scan_status() const;
-
-		/// Sets the type of output.
-		void set_display_type(Outputs::Display::DisplayType);
-
-		/// Gets the type of output.
-		Outputs::Display::DisplayType get_display_type() const;
-
-		/// Determines the period until video might autonomously update its interrupt lines.
-		Cycles next_sequence_point() const;
-
-		/// Sets the Mega II interrupt enable state — 1/4-second and VBL interrupts are
-		/// generated here.
-		void set_megaii_interrupts_enabled(uint8_t);
-
-		uint8_t get_megaii_interrupt_status();
-
-		void clear_megaii_interrupts();
+		bool active() const {
+			return value_ & 0x80;
+		}
 
 	private:
-		Outputs::CRT::CRT crt_;
-
-		// This is coupled to Apple::II::GraphicsMode, but adds detail for the IIgs.
-		enum class GraphicsMode {
-			Text = 0,
-			DoubleText,
-			HighRes,
-			DoubleHighRes,
-			LowRes,
-			DoubleLowRes,
-			FatLowRes,
-
-			// Additions:
-			DoubleHighResMono,
-			SuperHighRes
-		};
-		constexpr bool is_colour_ntsc(const GraphicsMode m) {
-			return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes;
-		}
-
-		GraphicsMode graphics_mode(int row) const {
-			if(new_video_ & 0x80) {
-				return GraphicsMode::SuperHighRes;
-			}
-
-			const auto ii_mode = Apple::II::VideoSwitches<Cycles>::graphics_mode(row);
-			switch(ii_mode) {
-				// Coupling very much assumed here.
-				case Apple::II::GraphicsMode::DoubleHighRes:
-					if(new_video_ & 0x20) {
-						return GraphicsMode::DoubleHighResMono;
-					}
-				[[fallthrough]];
-
-				default: return GraphicsMode(int(ii_mode));	break;
+		void test() {
+			value_ &= 0x7f;
+			if((value_ >> 4) & value_ & 0x6) {
+				value_ |= 0x80;
 			}
 		}
 
-		enum class PixelBufferFormat {
-			Text, DoubleText, NTSC, NTSCMono, SuperHighRes
-		};
-		constexpr PixelBufferFormat format_for_mode(GraphicsMode m) {
-			switch(m) {
-				case GraphicsMode::Text:				return PixelBufferFormat::Text;
-				case GraphicsMode::DoubleText:			return PixelBufferFormat::DoubleText;
-				default:								return PixelBufferFormat::NTSC;
-				case GraphicsMode::DoubleHighResMono:	return PixelBufferFormat::NTSCMono;
-				case GraphicsMode::SuperHighRes:		return PixelBufferFormat::SuperHighRes;
-			}
-		}
-
-		void advance(Cycles);
-
-		uint8_t new_video_ = 0x01;
-
-		class Interrupts {
-			public:
-				void add(uint8_t value) {
-					// Taken literally, status accumulates regardless of being enabled,
-					// potentially to be polled, it simply doesn't trigger an interrupt.
-					value_ |= value;
-					test();
-				}
-
-				void clear(uint8_t value) {
-					// Zeroes in bits 5 or 6 clear the respective interrupts.
-					value_ &= value | ~0x60;
-					test();
-				}
-
-				void set_control(uint8_t value) {
-					// Ones in bits 1 or 2 enable the respective interrupts.
-					value_ = (value_ & ~0x6) | (value & 0x6);
-					test();
-				}
-
-				uint8_t status() const {
-					return value_;
-				}
-
-				bool active() const {
-					return value_ & 0x80;
-				}
-
-			private:
-				void test() {
-					value_ &= 0x7f;
-					if((value_ >> 4) & value_ & 0x6) {
-						value_ |= 0x80;
-					}
-				}
-
-				// Overall meaning of value is as per the VGC interrupt register, i.e.
-				//
-				//	b7: interrupt status;
-				//	b6: 1-second interrupt status;
-				//	b5: scan-line interrupt status;
-				//	b4: reserved;
-				//	b3: reserved;
-				//	b2: 1-second interrupt enable;
-				//	b1: scan-line interrupt enable;
-				//	b0: reserved.
-				uint8_t value_ = 0x00;
-		} interrupts_;
-
-		int cycles_into_frame_ = 0;
-		const uint8_t *ram_ = nullptr;
-
-		// The modal colours.
-		uint16_t border_colour_ = 0;
-		uint8_t border_colour_entry_ = 0;
-		uint8_t text_colour_entry_ = 0xf0;
-		uint16_t text_colour_ = 0xffff;
-		uint16_t background_colour_ = 0;
-
-		// Current pixel output buffer and conceptual format.
-		PixelBufferFormat pixels_format_;
-		uint16_t *pixels_ = nullptr, *next_pixel_ = nullptr;
-		int pixels_start_column_;
-
-		void output_row(int row, int start, int end);
-
-		uint16_t *output_super_high_res(uint16_t *target, int start, int end, int row) const;
-
-		uint16_t *output_text(uint16_t *target, int start, int end, int row) const;
-		uint16_t *output_double_text(uint16_t *target, int start, int end, int row) const;
-		uint16_t *output_char(uint16_t *target, uint8_t source, int row) const;
-
-		uint16_t *output_low_resolution(uint16_t *target, int start, int end, int row);
-		uint16_t *output_fat_low_resolution(uint16_t *target, int start, int end, int row);
-		uint16_t *output_double_low_resolution(uint16_t *target, int start, int end, int row);
-
-		uint16_t *output_high_resolution(uint16_t *target, int start, int end, int row);
-		uint16_t *output_double_high_resolution(uint16_t *target, int start, int end, int row);
-		uint16_t *output_double_high_resolution_mono(uint16_t *target, int start, int end, int row);
-
-		// Super high-res per-line state.
-		uint8_t line_control_;
-		uint16_t palette_[16];
-
-		// Storage used for fill mode.
-		uint16_t *palette_zero_[4] = {nullptr, nullptr, nullptr, nullptr}, palette_throwaway_;
-
-		// Lookup tables and state to assist in the IIgs' mapping from NTSC to RGB.
+		// Overall meaning of value is as per the VGC interrupt register, i.e.
 		//
-		// My understanding of the real-life algorithm is: maintain a four-bit buffer.
-		// Fill it in a circular fashion. Ordinarily, output the result of looking
-		// up the RGB mapping of those four bits of Apple II output (which outputs four
-		// bits per NTSC colour cycle), commuted as per current phase. But if the bit
-		// being inserted differs from that currently in its position in the shift
-		// register, hold the existing output for three shifts.
-		//
-		// From there I am using the following:
+		//	b7: interrupt status;
+		//	b6: 1-second interrupt status;
+		//	b5: scan-line interrupt status;
+		//	b4: reserved;
+		//	b3: reserved;
+		//	b2: 1-second interrupt enable;
+		//	b1: scan-line interrupt enable;
+		//	b0: reserved.
+		uint8_t value_ = 0x00;
+	} interrupts_;
 
-		// Maps from:
-		//
-		//	b0 = b0 of the shift register
-		//	b1 = b4 of the shift register
-		//	b2– = current delay count
-		//
-		// to a new delay count.
-		uint8_t ntsc_delay_lookup_[20];
-		uint32_t ntsc_shift_ = 0;	// Assumption here: logical shifts will ensue, rather than arithmetic.
-		int ntsc_delay_ = 0;
+	int cycles_into_frame_ = 0;
+	const uint8_t *ram_ = nullptr;
 
-		/// Outputs the lowest 14 bits from @c ntsc_shift_, mapping to RGB.
-		/// Phase is derived from @c column.
-		uint16_t *output_shift(uint16_t *target, int column);
+	// The modal colours.
+	uint16_t border_colour_ = 0;
+	uint8_t border_colour_entry_ = 0;
+	uint8_t text_colour_entry_ = 0xf0;
+	uint16_t text_colour_ = 0xffff;
+	uint16_t background_colour_ = 0;
 
-		// Common getter for the two counters.
-		struct Counters {
-			Counters(int v, int h) : vertical(v), horizontal(h) {}
-			const int vertical, horizontal;
-		};
-		Counters get_counters(Cycles offset);
+	// Current pixel output buffer and conceptual format.
+	PixelBufferFormat pixels_format_;
+	uint16_t *pixels_ = nullptr, *next_pixel_ = nullptr;
+	int pixels_start_column_;
 
-		// Marshalls the Mega II-style interrupt state.
-		uint8_t megaii_interrupt_mask_ = 0;
-		uint8_t megaii_interrupt_state_ = 0;
-		int megaii_frame_counter_ = 0;	// To count up to quarter-second interrupts.
+	void output_row(int row, int start, int end);
+
+	uint16_t *output_super_high_res(uint16_t *target, int start, int end, int row) const;
+
+	uint16_t *output_text(uint16_t *target, int start, int end, int row) const;
+	uint16_t *output_double_text(uint16_t *target, int start, int end, int row) const;
+	uint16_t *output_char(uint16_t *target, uint8_t source, int row) const;
+
+	uint16_t *output_low_resolution(uint16_t *target, int start, int end, int row);
+	uint16_t *output_fat_low_resolution(uint16_t *target, int start, int end, int row);
+	uint16_t *output_double_low_resolution(uint16_t *target, int start, int end, int row);
+
+	uint16_t *output_high_resolution(uint16_t *target, int start, int end, int row);
+	uint16_t *output_double_high_resolution(uint16_t *target, int start, int end, int row);
+	uint16_t *output_double_high_resolution_mono(uint16_t *target, int start, int end, int row);
+
+	// Super high-res per-line state.
+	uint8_t line_control_;
+	uint16_t palette_[16];
+
+	// Storage used for fill mode.
+	uint16_t *palette_zero_[4] = {nullptr, nullptr, nullptr, nullptr}, palette_throwaway_;
+
+	// Lookup tables and state to assist in the IIgs' mapping from NTSC to RGB.
+	//
+	// My understanding of the real-life algorithm is: maintain a four-bit buffer.
+	// Fill it in a circular fashion. Ordinarily, output the result of looking
+	// up the RGB mapping of those four bits of Apple II output (which outputs four
+	// bits per NTSC colour cycle), commuted as per current phase. But if the bit
+	// being inserted differs from that currently in its position in the shift
+	// register, hold the existing output for three shifts.
+	//
+	// From there I am using the following:
+
+	// Maps from:
+	//
+	//	b0 = b0 of the shift register
+	//	b1 = b4 of the shift register
+	//	b2– = current delay count
+	//
+	// to a new delay count.
+	uint8_t ntsc_delay_lookup_[20];
+	uint32_t ntsc_shift_ = 0;	// Assumption here: logical shifts will ensue, rather than arithmetic.
+	int ntsc_delay_ = 0;
+
+	/// Outputs the lowest 14 bits from @c ntsc_shift_, mapping to RGB.
+	/// Phase is derived from @c column.
+	uint16_t *output_shift(uint16_t *target, int column);
+
+	// Common getter for the two counters.
+	struct Counters {
+		Counters(int v, int h) : vertical(v), horizontal(h) {}
+		const int vertical, horizontal;
+	};
+	Counters get_counters(Cycles offset);
+
+	// Marshalls the Mega II-style interrupt state.
+	uint8_t megaii_interrupt_mask_ = 0;
+	uint8_t megaii_interrupt_state_ = 0;
+	int megaii_frame_counter_ = 0;	// To count up to quarter-second interrupts.
 };
 
 }
diff --git a/Machines/Apple/Macintosh/Audio.hpp b/Machines/Apple/Macintosh/Audio.hpp
index 1adb4b06a..762593aec 100644
--- a/Machines/Apple/Macintosh/Audio.hpp
+++ b/Machines/Apple/Macintosh/Audio.hpp
@@ -24,61 +24,61 @@ namespace Apple::Macintosh {
 	a shade less than 4Mhz.
 */
 class Audio: public ::Outputs::Speaker::BufferSource<Audio, false> {
-	public:
-		Audio(Concurrency::AsyncTaskQueue<false> &task_queue);
+public:
+	Audio(Concurrency::AsyncTaskQueue<false> &task_queue);
 
-		/*!
-			Macintosh audio is (partly) sourced by the same scanning
-			hardware as the video; each line it collects an additional
-			word of memory, half of which is used for audio output.
+	/*!
+		Macintosh audio is (partly) sourced by the same scanning
+		hardware as the video; each line it collects an additional
+		word of memory, half of which is used for audio output.
 
-			Use this method to add a newly-collected sample to the queue.
-		*/
-		void post_sample(uint8_t sample);
+		Use this method to add a newly-collected sample to the queue.
+	*/
+	void post_sample(uint8_t sample);
 
-		/*!
-			Macintosh audio also separately receives an output volume
-			level, in the range 0 to 7.
+	/*!
+		Macintosh audio also separately receives an output volume
+		level, in the range 0 to 7.
 
-			Use this method to set the current output volume.
-		*/
-		void set_volume(int volume);
+		Use this method to set the current output volume.
+	*/
+	void set_volume(int volume);
 
-		/*!
-			A further factor in audio output is the on-off toggle.
-		*/
-		void set_enabled(bool on);
+	/*!
+		A further factor in audio output is the on-off toggle.
+	*/
+	void set_enabled(bool on);
 
-		// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
-		template <Outputs::Speaker::Action action>
-		void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
-		bool is_zero_level() const;
-		void set_sample_volume_range(std::int16_t range);
+	// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
+	template <Outputs::Speaker::Action action>
+	void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
+	bool is_zero_level() const;
+	void set_sample_volume_range(std::int16_t range);
 
-	private:
-		Concurrency::AsyncTaskQueue<false> &task_queue_;
+private:
+	Concurrency::AsyncTaskQueue<false> &task_queue_;
 
-		// A queue of fetched samples; read from by one thread,
-		// written to by another.
-		struct {
-			std::array<std::atomic<uint8_t>, 740> buffer;
-			size_t read_pointer = 0, write_pointer = 0;
-		} sample_queue_;
+	// A queue of fetched samples; read from by one thread,
+	// written to by another.
+	struct {
+		std::array<std::atomic<uint8_t>, 740> buffer;
+		size_t read_pointer = 0, write_pointer = 0;
+	} sample_queue_;
 
-		// Emulator-thread stateful variables, to avoid work posting
-		// deferral updates if possible.
-		int posted_volume_ = 0;
-		int posted_enable_mask_ = 0;
+	// Emulator-thread stateful variables, to avoid work posting
+	// deferral updates if possible.
+	int posted_volume_ = 0;
+	int posted_enable_mask_ = 0;
 
-		// Stateful variables, modified from the audio generation
-		// thread only.
-		int volume_ = 0;
-		int enabled_mask_ = 0;
-		std::int16_t output_volume_ = 0;
+	// Stateful variables, modified from the audio generation
+	// thread only.
+	int volume_ = 0;
+	int enabled_mask_ = 0;
+	std::int16_t output_volume_ = 0;
 
-		std::int16_t volume_multiplier_ = 0;
-		std::size_t subcycle_offset_ = 0;
-		void set_volume_multiplier();
+	std::int16_t volume_multiplier_ = 0;
+	std::size_t subcycle_offset_ = 0;
+	void set_volume_multiplier();
 };
 
 }
diff --git a/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp b/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp
index 21a28066b..8bad1a572 100644
--- a/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp
+++ b/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp
@@ -15,27 +15,27 @@
 namespace Apple::Macintosh {
 
 class DriveSpeedAccumulator {
-	public:
-		/*!
-			Accepts fetched motor control values.
-		*/
-		void post_sample(uint8_t sample);
+public:
+	/*!
+		Accepts fetched motor control values.
+	*/
+	void post_sample(uint8_t sample);
 
-		struct Delegate {
-			virtual void drive_speed_accumulator_set_drive_speed(DriveSpeedAccumulator *, float speed) = 0;
-		};
-		/*!
-			Sets the delegate to receive drive speed changes.
-		*/
-		void set_delegate(Delegate *delegate) {
-			delegate_ = delegate;
-		}
+	struct Delegate {
+		virtual void drive_speed_accumulator_set_drive_speed(DriveSpeedAccumulator *, float speed) = 0;
+	};
+	/*!
+		Sets the delegate to receive drive speed changes.
+	*/
+	void set_delegate(Delegate *delegate) {
+		delegate_ = delegate;
+	}
 
-	private:
-		static constexpr int samples_per_bucket = 20;
-		int sample_count_ = 0;
-		int sample_total_ = 0;
-		Delegate *delegate_ = nullptr;
+private:
+	static constexpr int samples_per_bucket = 20;
+	int sample_count_ = 0;
+	int sample_total_ = 0;
+	Delegate *delegate_ = nullptr;
 };
 
 }
diff --git a/Machines/Apple/Macintosh/Keyboard.hpp b/Machines/Apple/Macintosh/Keyboard.hpp
index 2efd603c4..a5a74ea10 100644
--- a/Machines/Apple/Macintosh/Keyboard.hpp
+++ b/Machines/Apple/Macintosh/Keyboard.hpp
@@ -83,206 +83,206 @@ enum class Key: uint16_t {
 };
 
 class Keyboard {
-	public:
-		void set_input(bool data) {
-			switch(mode_) {
-				case Mode::Waiting:
-					/*
-						"Only the computer can initiate communication over the keyboard lines. When the computer and keyboard
-						are turned on, the computer is in charge of the keyboard interface and the keyboard is passive. The
-						computer signals that it is ready to begin communication by pulling the Keyboard Data line low."
-					*/
-					if(!data) {
-						mode_ = Mode::AcceptingCommand;
-						phase_ = 0;
-						command_ = 0;
-					}
-				break;
+public:
+	void set_input(bool data) {
+		switch(mode_) {
+			case Mode::Waiting:
+				/*
+					"Only the computer can initiate communication over the keyboard lines. When the computer and keyboard
+					are turned on, the computer is in charge of the keyboard interface and the keyboard is passive. The
+					computer signals that it is ready to begin communication by pulling the Keyboard Data line low."
+				*/
+				if(!data) {
+					mode_ = Mode::AcceptingCommand;
+					phase_ = 0;
+					command_ = 0;
+				}
+			break;
 
-				case Mode::AcceptingCommand:
-					/* Note value, so that it can be latched upon a clock transition. */
-					data_input_ = data;
-				break;
+			case Mode::AcceptingCommand:
+				/* Note value, so that it can be latched upon a clock transition. */
+				data_input_ = data;
+			break;
 
-				case Mode::AwaitingEndOfCommand:
-					/*
-						The last bit of the command leaves the Keyboard Data line low; the computer then indicates that it is ready
-						to receive the keyboard's response by setting the Keyboard Data line high.
-					*/
-					if(data) {
-						mode_ = Mode::PerformingCommand;
-						phase_ = 0;
-					}
-				break;
+			case Mode::AwaitingEndOfCommand:
+				/*
+					The last bit of the command leaves the Keyboard Data line low; the computer then indicates that it is ready
+					to receive the keyboard's response by setting the Keyboard Data line high.
+				*/
+				if(data) {
+					mode_ = Mode::PerformingCommand;
+					phase_ = 0;
+				}
+			break;
 
-				default:
-				case Mode::SendingResponse:
-					/* This line isn't currently an input; do nothing. */
-				break;
-			}
+			default:
+			case Mode::SendingResponse:
+				/* This line isn't currently an input; do nothing. */
+			break;
 		}
+	}
 
-		bool get_clock() {
-			return clock_output_;
+	bool get_clock() {
+		return clock_output_;
+	}
+
+	bool get_data() {
+		return !!(response_ & 0x80);
+	}
+
+	/*!
+		The keyboard expects ~10 µs-frequency ticks, i.e. a clock rate of just around 100 kHz.
+	*/
+	void run_for(HalfCycles) {	// TODO: honour the HalfCycles argument.
+		switch(mode_) {
+			default:
+			case Mode::Waiting: return;
+
+			case Mode::AcceptingCommand: {
+				/*
+					"When the computer is sending data to the keyboard, the keyboard transmits eight cycles of 400 µS each (180 µS low,
+					220 µS high) on the Keyboard Clock line. On the falling edge of each keyboard clock cycle, the Macintosh Plus places
+					a data bit on the data line and holds it there for 400 µS. The keyboard reads the data bit 80 µS after the rising edge
+					of the Keyboard Clock signal."
+				*/
+				const auto offset = phase_ % 40;
+				clock_output_ = offset >= 18;
+
+				if(offset == 26) {
+					command_ = (command_ << 1) | (data_input_ ? 1 : 0);
+				}
+
+				++phase_;
+				if(phase_ == 8*40) {
+					mode_ = Mode::AwaitingEndOfCommand;
+					phase_ = 0;
+					clock_output_ = false;
+				}
+			} break;
+
+			case Mode::AwaitingEndOfCommand:
+				// Time out if the end-of-command seems not to be forthcoming.
+				// This is an elaboration on my part; a guess.
+				++phase_;
+				if(phase_ == 1000) {
+					clock_output_ = false;
+					mode_ = Mode::Waiting;
+					phase_ = 0;
+				}
+			return;
+
+			case Mode::PerformingCommand: {
+				response_ = perform_command(command_);
+
+				// Inquiry has a 0.25-second timeout; everything else is instant.
+				++phase_;
+				if(phase_ == 25000 || command_ != 0x10 || response_ != 0x7b) {
+					mode_ = Mode::SendingResponse;
+					phase_ = 0;
+				}
+			} break;
+
+			case Mode::SendingResponse: {
+				/*
+					"When sending data to the computer, the keyboard transmits eight cycles of 330 µS each (160 µS low, 170 µS high)
+					on the normally high Keyboard Clock line. It places a data bit on the data line 40 µS before the falling edge of each
+					clock cycle and maintains it for 330 µS. The VIA in the computer latches the data bit into its shift register on the
+					rising edge of the Keyboard Clock signal."
+				*/
+				const auto offset = phase_ % 33;
+				clock_output_ = offset >= 16;
+
+				if(offset == 29) {
+					response_ <<= 1;
+				}
+
+				++phase_;
+				if(phase_ == 8*33) {
+					clock_output_ = false;
+					mode_ = Mode::Waiting;
+					phase_ = 0;
+				}
+			} break;
 		}
+	}
 
-		bool get_data() {
-			return !!(response_ & 0x80);
+	void enqueue_key_state(uint16_t key, bool is_pressed) {
+		// Front insert; messages will be pop_back'd.
+		std::lock_guard lock(key_queue_mutex_);
+
+		// Keys on the keypad are preceded by a $79 keycode; in the internal naming scheme
+		// they are indicated by having bit 8 set. So add the $79 prefix if required.
+		if(key & KeypadMask) {
+			key_queue_.insert(key_queue_.begin(), 0x79);
 		}
+		key_queue_.insert(key_queue_.begin(), (is_pressed ? 0x00 : 0x80) | uint8_t(key));
+	}
 
-		/*!
-			The keyboard expects ~10 µs-frequency ticks, i.e. a clock rate of just around 100 kHz.
-		*/
-		void run_for(HalfCycles) {	// TODO: honour the HalfCycles argument.
-			switch(mode_) {
-				default:
-				case Mode::Waiting: return;
+private:
+	/// Performs the pre-ADB Apple keyboard protocol command @c command, returning
+	/// the proper result if the command were to terminate now. So, it treats inquiry
+	/// and instant as the same command.
+	int perform_command(int command) {
+		switch(command) {
+			case 0x10:		// Inquiry.
+			case 0x14: {	// Instant.
+				std::lock_guard lock(key_queue_mutex_);
+				if(!key_queue_.empty()) {
+					const auto new_message = key_queue_.back();
+					key_queue_.pop_back();
+					return new_message;
+				}
+			} break;
 
-				case Mode::AcceptingCommand: {
-					/*
-						"When the computer is sending data to the keyboard, the keyboard transmits eight cycles of 400 µS each (180 µS low,
-						220 µS high) on the Keyboard Clock line. On the falling edge of each keyboard clock cycle, the Macintosh Plus places
-						a data bit on the data line and holds it there for 400 µS. The keyboard reads the data bit 80 µS after the rising edge
-						of the Keyboard Clock signal."
-					*/
-					const auto offset = phase_ % 40;
-					clock_output_ = offset >= 18;
+			case 0x16:	// Model number.
+			return
+				0x01 |			// b0: always 1
+				(1 << 1) |		// keyboard model number
+				(1 << 4);		// next device number
+								// (b7 not set => no next device)
 
-					if(offset == 26) {
-						command_ = (command_ << 1) | (data_input_ ? 1 : 0);
-					}
-
-					++phase_;
-					if(phase_ == 8*40) {
-						mode_ = Mode::AwaitingEndOfCommand;
-						phase_ = 0;
-						clock_output_ = false;
-					}
-				} break;
-
-				case Mode::AwaitingEndOfCommand:
-					// Time out if the end-of-command seems not to be forthcoming.
-					// This is an elaboration on my part; a guess.
-					++phase_;
-					if(phase_ == 1000) {
-						clock_output_ = false;
-						mode_ = Mode::Waiting;
-						phase_ = 0;
-					}
-				return;
-
-				case Mode::PerformingCommand: {
-					response_ = perform_command(command_);
-
-					// Inquiry has a 0.25-second timeout; everything else is instant.
-					++phase_;
-					if(phase_ == 25000 || command_ != 0x10 || response_ != 0x7b) {
-						mode_ = Mode::SendingResponse;
-						phase_ = 0;
-					}
-				} break;
-
-				case Mode::SendingResponse: {
-					/*
-						"When sending data to the computer, the keyboard transmits eight cycles of 330 µS each (160 µS low, 170 µS high)
-						on the normally high Keyboard Clock line. It places a data bit on the data line 40 µS before the falling edge of each
-						clock cycle and maintains it for 330 µS. The VIA in the computer latches the data bit into its shift register on the
-						rising edge of the Keyboard Clock signal."
-					*/
-					const auto offset = phase_ % 33;
-					clock_output_ = offset >= 16;
-
-					if(offset == 29) {
-						response_ <<= 1;
-					}
-
-					++phase_;
-					if(phase_ == 8*33) {
-						clock_output_ = false;
-						mode_ = Mode::Waiting;
-						phase_ = 0;
-					}
-				} break;
-			}
+			case 0x36:	// Test
+			return 0x7d;		// 0x7d = ACK, 0x77 = not ACK.
 		}
+		return 0x7b;	// No key transition.
+	}
 
-		void enqueue_key_state(uint16_t key, bool is_pressed) {
-			// Front insert; messages will be pop_back'd.
-			std::lock_guard lock(key_queue_mutex_);
+	/// Maintains the current operating mode — a record of what the
+	/// keyboard is doing now.
+	enum class Mode {
+		/// The keyboard is waiting to begin a transaction.
+		Waiting,
+		/// The keyboard is currently clocking in a new command.
+		AcceptingCommand,
+		/// The keyboard is waiting for the computer to indicate that it is ready for a response.
+		AwaitingEndOfCommand,
+		/// The keyboard is in the process of performing the command it most-recently received.
+		/// If the command was an 'inquiry', this state may persist for a non-neglibible period of time.
+		PerformingCommand,
+		/// The keyboard is currently shifting a response back to the computer.
+		SendingResponse,
+	} mode_ = Mode::Waiting;
 
-			// Keys on the keypad are preceded by a $79 keycode; in the internal naming scheme
-			// they are indicated by having bit 8 set. So add the $79 prefix if required.
-			if(key & KeypadMask) {
-				key_queue_.insert(key_queue_.begin(), 0x79);
-			}
-			key_queue_.insert(key_queue_.begin(), (is_pressed ? 0x00 : 0x80) | uint8_t(key));
-		}
+	/// Holds a count of progress through the current @c Mode. Exact meaning depends on mode.
+	int phase_ = 0;
+	/// Holds the most-recently-received command; the command is shifted into here as it is received
+	/// so this may not be valid prior to Mode::PerformingCommand.
+	int command_ = 0;
+	/// Populated during PerformingCommand as the response to the most-recently-received command, this
+	/// is then shifted out to the host computer. So it is guaranteed valid at the beginning of Mode::SendingResponse,
+	/// but not afterwards.
+	int response_ = 0;
 
-	private:
-		/// Performs the pre-ADB Apple keyboard protocol command @c command, returning
-		/// the proper result if the command were to terminate now. So, it treats inquiry
-		/// and instant as the same command.
-		int perform_command(int command) {
-			switch(command) {
-				case 0x10:		// Inquiry.
-				case 0x14: {	// Instant.
-					std::lock_guard lock(key_queue_mutex_);
-					if(!key_queue_.empty()) {
-						const auto new_message = key_queue_.back();
-						key_queue_.pop_back();
-						return new_message;
-					}
-				} break;
+	/// The current state of the serial connection's data input.
+	bool data_input_ = false;
+	/// The current clock output from this keyboard.
+	bool clock_output_ = false;
 
-				case 0x16:	// Model number.
-				return
-					0x01 |			// b0: always 1
-					(1 << 1) |		// keyboard model number
-					(1 << 4);		// next device number
-									// (b7 not set => no next device)
-
-				case 0x36:	// Test
-				return 0x7d;		// 0x7d = ACK, 0x77 = not ACK.
-			}
-			return 0x7b;	// No key transition.
-		}
-
-		/// Maintains the current operating mode — a record of what the
-		/// keyboard is doing now.
-		enum class Mode {
-			/// The keyboard is waiting to begin a transaction.
-			Waiting,
-			/// The keyboard is currently clocking in a new command.
-			AcceptingCommand,
-			/// The keyboard is waiting for the computer to indicate that it is ready for a response.
-			AwaitingEndOfCommand,
-			/// The keyboard is in the process of performing the command it most-recently received.
-			/// If the command was an 'inquiry', this state may persist for a non-neglibible period of time.
-			PerformingCommand,
-			/// The keyboard is currently shifting a response back to the computer.
-			SendingResponse,
-		} mode_ = Mode::Waiting;
-
-		/// Holds a count of progress through the current @c Mode. Exact meaning depends on mode.
-		int phase_ = 0;
-		/// Holds the most-recently-received command; the command is shifted into here as it is received
-		/// so this may not be valid prior to Mode::PerformingCommand.
-		int command_ = 0;
-		/// Populated during PerformingCommand as the response to the most-recently-received command, this
-		/// is then shifted out to the host computer. So it is guaranteed valid at the beginning of Mode::SendingResponse,
-		/// but not afterwards.
-		int response_ = 0;
-
-		/// The current state of the serial connection's data input.
-		bool data_input_ = false;
-		/// The current clock output from this keyboard.
-		bool clock_output_ = false;
-
-		/// Guards multithread access to key_queue_.
-		std::mutex key_queue_mutex_;
-		/// A FIFO queue for key events, in the form they'd be communicated to the Macintosh,
-		/// with the newest events towards the front.
-		std::vector<uint8_t> key_queue_;
+	/// Guards multithread access to key_queue_.
+	std::mutex key_queue_mutex_;
+	/// A FIFO queue for key events, in the form they'd be communicated to the Macintosh,
+	/// with the newest events towards the front.
+	std::vector<uint8_t> key_queue_;
 };
 
 /*!
diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp
index e7c271408..a33dffed2 100644
--- a/Machines/Apple/Macintosh/Macintosh.cpp
+++ b/Machines/Apple/Macintosh/Macintosh.cpp
@@ -79,760 +79,760 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
 	public Configurable::Device,
 	public DriveSpeedAccumulator::Delegate,
 	public ClockingHint::Observer {
-	public:
-		using Target = Analyser::Static::Macintosh::Target;
+public:
+	using Target = Analyser::Static::Macintosh::Target;
 
-		ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
-			MachineTypes::MappedKeyboardMachine({
-				Inputs::Keyboard::Key::LeftShift, Inputs::Keyboard::Key::RightShift,
-				Inputs::Keyboard::Key::LeftOption, Inputs::Keyboard::Key::RightOption,
-				Inputs::Keyboard::Key::LeftMeta, Inputs::Keyboard::Key::RightMeta,
-			}),
-			mc68000_(*this),
-			iwm_(CLOCK_RATE),
-			video_(audio_, drive_speed_accumulator_),
-			via_port_handler_(*this, clock_, keyboard_, audio_, iwm_, mouse_),
-			via_(via_port_handler_),
-			scsi_bus_(CLOCK_RATE * 2),
-			scsi_(scsi_bus_, CLOCK_RATE * 2),
-			hard_drive_(scsi_bus_, 6 /* SCSI ID */),
-			drives_{
-				{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke},
-				{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke}
-			},
-			mouse_(1) {
+	ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
+		MachineTypes::MappedKeyboardMachine({
+			Inputs::Keyboard::Key::LeftShift, Inputs::Keyboard::Key::RightShift,
+			Inputs::Keyboard::Key::LeftOption, Inputs::Keyboard::Key::RightOption,
+			Inputs::Keyboard::Key::LeftMeta, Inputs::Keyboard::Key::RightMeta,
+		}),
+		mc68000_(*this),
+		iwm_(CLOCK_RATE),
+		video_(audio_, drive_speed_accumulator_),
+		via_port_handler_(*this, clock_, keyboard_, audio_, iwm_, mouse_),
+		via_(via_port_handler_),
+		scsi_bus_(CLOCK_RATE * 2),
+		scsi_(scsi_bus_, CLOCK_RATE * 2),
+		hard_drive_(scsi_bus_, 6 /* SCSI ID */),
+		drives_{
+			{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke},
+			{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke}
+		},
+		mouse_(1) {
 
-			// Select a ROM name and determine the proper ROM and RAM sizes
-			// based on the machine model.
-			using Model = Analyser::Static::Macintosh::Target::Model;
-			const std::string machine_name = "Macintosh";
-			uint32_t ram_size, rom_size;
-			ROM::Name rom_name;
-			switch(model) {
-				default:
-				case Model::Mac128k:
-					ram_size = 128*1024;
-					rom_size = 64*1024;
-					rom_name = ROM::Name::Macintosh128k;
-				break;
-				case Model::Mac512k:
-					ram_size = 512*1024;
-					rom_size = 64*1024;
-					rom_name = ROM::Name::Macintosh512k;
-				break;
-				case Model::Mac512ke:
-				case Model::MacPlus: {
-					ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024;
-					rom_size = 128*1024;
-					rom_name = ROM::Name::MacintoshPlus;
-				} break;
-			}
-			ram_mask_ = ram_size - 1;
-			rom_mask_ = rom_size - 1;
-			ram_.resize(ram_size);
-			video_.set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_mask_ >> 1);
+		// Select a ROM name and determine the proper ROM and RAM sizes
+		// based on the machine model.
+		using Model = Analyser::Static::Macintosh::Target::Model;
+		const std::string machine_name = "Macintosh";
+		uint32_t ram_size, rom_size;
+		ROM::Name rom_name;
+		switch(model) {
+			default:
+			case Model::Mac128k:
+				ram_size = 128*1024;
+				rom_size = 64*1024;
+				rom_name = ROM::Name::Macintosh128k;
+			break;
+			case Model::Mac512k:
+				ram_size = 512*1024;
+				rom_size = 64*1024;
+				rom_name = ROM::Name::Macintosh512k;
+			break;
+			case Model::Mac512ke:
+			case Model::MacPlus: {
+				ram_size = ((model == Model::MacPlus) ? 4096 : 512)*1024;
+				rom_size = 128*1024;
+				rom_name = ROM::Name::MacintoshPlus;
+			} break;
+		}
+		ram_mask_ = ram_size - 1;
+		rom_mask_ = rom_size - 1;
+		ram_.resize(ram_size);
+		video_.set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_mask_ >> 1);
 
-			// Grab a copy of the ROM and convert it into big-endian data.
-			ROM::Request request(rom_name);
-			auto roms = rom_fetcher(request);
-			if(!request.validate(roms)) {
-				throw ROMMachine::Error::MissingROMs;
-			}
-			Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
+		// Grab a copy of the ROM and convert it into big-endian data.
+		ROM::Request request(rom_name);
+		auto roms = rom_fetcher(request);
+		if(!request.validate(roms)) {
+			throw ROMMachine::Error::MissingROMs;
+		}
+		Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
 
-			// Randomise memory contents.
-			Memory::Fuzz(ram_);
+		// Randomise memory contents.
+		Memory::Fuzz(ram_);
 
-			// Attach the drives to the IWM.
-			iwm_->set_drive(0, &drives_[0]);
-			iwm_->set_drive(1, &drives_[1]);
+		// Attach the drives to the IWM.
+		iwm_->set_drive(0, &drives_[0]);
+		iwm_->set_drive(1, &drives_[1]);
 
-			// If they are 400kb drives, also attach them to the drive-speed accumulator.
-			if(!drives_[0].is_800k() || !drives_[1].is_800k()) {
-				drive_speed_accumulator_.set_delegate(this);
-			}
-
-			// Make sure interrupt changes from the SCC are observed.
-			scc_.set_delegate(this);
-
-			// Also watch for changes in clocking requirement from the SCSI chip.
-			if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
-				scsi_bus_.set_clocking_hint_observer(this);
-			}
-
-			// The Mac runs at 7.8336mHz.
-			set_clock_rate(double(CLOCK_RATE));
-			audio_.speaker.set_input_rate(float(CLOCK_RATE) / 2.0f);
-
-			// Insert any supplied media.
-			insert_media(target.media);
-
-			// Set the immutables of the memory map.
-			setup_memory_map();
+		// If they are 400kb drives, also attach them to the drive-speed accumulator.
+		if(!drives_[0].is_800k() || !drives_[1].is_800k()) {
+			drive_speed_accumulator_.set_delegate(this);
 		}
 
-		~ConcreteMachine() {
-			audio_.queue.flush();
+		// Make sure interrupt changes from the SCC are observed.
+		scc_.set_delegate(this);
+
+		// Also watch for changes in clocking requirement from the SCSI chip.
+		if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
+			scsi_bus_.set_clocking_hint_observer(this);
 		}
 
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
-			video_.set_scan_target(scan_target);
-		}
+		// The Mac runs at 7.8336mHz.
+		set_clock_rate(double(CLOCK_RATE));
+		audio_.speaker.set_input_rate(float(CLOCK_RATE) / 2.0f);
 
-		Outputs::Display::ScanStatus get_scaled_scan_status() const final {
-			return video_.get_scaled_scan_status();
-		}
+		// Insert any supplied media.
+		insert_media(target.media);
 
-		Outputs::Speaker::Speaker *get_speaker() final {
-			return &audio_.speaker;
-		}
+		// Set the immutables of the memory map.
+		setup_memory_map();
+	}
 
-		void run_for(const Cycles cycles) final {
-			mc68000_.run_for(cycles);
-		}
+	~ConcreteMachine() {
+		audio_.queue.flush();
+	}
 
-		template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
-			// Advance time.
-			advance_time(cycle.length);
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
+		video_.set_scan_target(scan_target);
+	}
 
-			// A null cycle leaves nothing else to do.
-			if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
+	Outputs::Display::ScanStatus get_scaled_scan_status() const final {
+		return video_.get_scaled_scan_status();
+	}
 
-			// Grab the address.
-			auto address = cycle.host_endian_byte_address();
+	Outputs::Speaker::Speaker *get_speaker() final {
+		return &audio_.speaker;
+	}
 
-			// Everything above E0 0000 is signalled as being on the peripheral bus.
-			//
-			// This will also act to autovector interrupts, since an interrupt acknowledge
-			// cycle posts an address with all the higher-order bits set, and VPA doubles
-			// as the input to request an autovector.
-			mc68000_.set_is_peripheral_address(address >= 0xe0'0000);
+	void run_for(const Cycles cycles) final {
+		mc68000_.run_for(cycles);
+	}
 
-			// All code below deals only with reads and writes — cycles in which a
-			// data select is active. So quit now if this is not the active part of
-			// a read or write.
-			//
-			// The 68000 uses 6800-style autovectored interrupts, so the mere act of
-			// having set VPA above deals with those given that the generated address
-			// for interrupt acknowledge cycles always has all bits set except the
-			// lowest explicit address lines.
-			if(
-				!cycle.data_select_active() ||
-				(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge)
-			) return HalfCycles(0);
+	template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
+		// Advance time.
+		advance_time(cycle.length);
 
-			// Grab the word-precision address being accessed.
-			uint8_t *memory_base = nullptr;
-			HalfCycles delay;
-			switch(memory_map_[address >> 17]) {
-				default: assert(false);
+		// A null cycle leaves nothing else to do.
+		if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
 
-				case BusDevice::Unassigned:
-					fill_unmapped(cycle);
-				return delay;
+		// Grab the address.
+		auto address = cycle.host_endian_byte_address();
 
-				case BusDevice::VIA: {
-					if(*cycle.address & 1) {
-						fill_unmapped(cycle);
-					} else {
-						const int register_address = address >> 9;
+		// Everything above E0 0000 is signalled as being on the peripheral bus.
+		//
+		// This will also act to autovector interrupts, since an interrupt acknowledge
+		// cycle posts an address with all the higher-order bits set, and VPA doubles
+		// as the input to request an autovector.
+		mc68000_.set_is_peripheral_address(address >= 0xe0'0000);
 
-						// VIA accesses are via address 0xefe1fe + register*512,
-						// which at word precision is 0x77f0ff + register*256.
-						if(cycle.operation & CPU::MC68000::Operation::Read) {
-							cycle.set_value8_high(via_.read(register_address));
-						} else {
-							via_.write(register_address, cycle.value8_high());
-						}
-					}
-				} return delay;
+		// All code below deals only with reads and writes — cycles in which a
+		// data select is active. So quit now if this is not the active part of
+		// a read or write.
+		//
+		// The 68000 uses 6800-style autovectored interrupts, so the mere act of
+		// having set VPA above deals with those given that the generated address
+		// for interrupt acknowledge cycles always has all bits set except the
+		// lowest explicit address lines.
+		if(
+			!cycle.data_select_active() ||
+			(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge)
+		) return HalfCycles(0);
 
-				case BusDevice::PhaseRead: {
-					if(cycle.operation & CPU::MC68000::Operation::Read) {
-						cycle.set_value8_low(phase_ & 7);
-					}
-				} return delay;
-
-				case BusDevice::IWM: {
-					if(*cycle.address & 1) {
-						const int register_address = address >> 9;
-
-						// The IWM; this is a purely polled device, so can be run on demand.
-						if(cycle.operation & CPU::MC68000::Operation::Read) {
-							cycle.set_value8_low(iwm_->read(register_address));
-						} else {
-							iwm_->write(register_address, cycle.value8_low());
-						}
-					} else {
-						fill_unmapped(cycle);
-					}
-				} return delay;
-
-				case BusDevice::SCSI: {
-					const int register_address = address >> 4;
-					const bool dma_acknowledge = address & 0x200;
-
-					// Even accesses = read; odd = write.
-					if(*cycle.address & 1) {
-						// Odd access => this is a write. Data will be in the upper byte.
-						if(cycle.operation & CPU::MC68000::Operation::Read) {
-							scsi_.write(register_address, 0xff, dma_acknowledge);
-						} else {
-							scsi_.write(register_address, cycle.value8_high());
-						}
-					} else {
-						// Even access => this is a read.
-						if(cycle.operation & CPU::MC68000::Operation::Read) {
-							cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
-						}
-					}
-				} return delay;
-
-				case BusDevice::SCCReadResetPhase: {
-					// Any word access here adjusts phase.
-					if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
-						adjust_phase();
-					} else {
-						// A0 = 1 => reset; A0 = 0 => read.
-						if(*cycle.address & 1) {
-							scc_.reset();
-
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value16(0xffff);
-							}
-						} else {
-							const auto read = scc_.read(int(address >> 1));
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value8_high(read);
-							}
-						}
-					}
-				} return delay;
-
-				case BusDevice::SCCWrite: {
-					// Any word access here adjusts phase.
-					if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
-						adjust_phase();
-					} else {
-						// This is definitely a byte access; either it's to an odd address, in which
-						// case it will reach the SCC, or it isn't, in which case it won't.
-						if(*cycle.address & 1) {
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								scc_.write(int(address >> 1), 0xff);
-								cycle.value->b = 0xff;
-							} else {
-								scc_.write(int(address >> 1), cycle.value->b);
-							}
-						} else {
-							fill_unmapped(cycle);
-						}
-					}
-				} return delay;
-
-				case BusDevice::RAM: {
-					// This is coupled with the Macintosh implementation of video; the magic
-					// constant should probably be factored into the Video class.
-					// It embodies knowledge of the fact that video (and audio) will always
-					// be fetched from the final $d900 bytes of memory.
-					// (And that ram_mask_ = ram size - 1).
-					if(address > ram_mask_ - 0xd900)
-						update_video();
-
-					memory_base = ram_.data();
-					address &= ram_mask_;
-
-					// Apply a delay due to video contention if applicable; scheme applied:
-					// only every other access slot is available during the period of video
-					// output. I believe this to be correct for the 128k, 512k and Plus.
-					// More research to do on other models.
-					if(video_is_outputting() && ram_subcycle_ < 8) {
-						delay = HalfCycles(8 - ram_subcycle_);
-						advance_time(delay);
-					}
-				} break;
-
-				case BusDevice::ROM: {
-					if(!(cycle.operation & CPU::MC68000::Operation::Read)) return delay;
-					memory_base = rom_;
-					address &= rom_mask_;
-				} break;
-			}
-
-			// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
-			// Potential writes to ROM and all hardware accesses have already been weeded out.
-			cycle.apply(&memory_base[address]);
+		// Grab the word-precision address being accessed.
+		uint8_t *memory_base = nullptr;
+		HalfCycles delay;
+		switch(memory_map_[address >> 17]) {
+			default: assert(false);
 
+			case BusDevice::Unassigned:
+				fill_unmapped(cycle);
 			return delay;
-		}
 
-		void flush_output(int) {
-			// Flush the video before the audio queue; in a Mac the
-			// video is responsible for providing part of the
-			// audio signal, so the two aren't as distinct as in
-			// most machines.
-			update_video();
+			case BusDevice::VIA: {
+				if(*cycle.address & 1) {
+					fill_unmapped(cycle);
+				} else {
+					const int register_address = address >> 9;
 
-			// As above: flush audio after video.
-			via_.flush();
-			audio_.queue.perform();
-
-			// This avoids deferring IWM costs indefinitely, until
-			// they become artbitrarily large.
-			iwm_.flush();
-		}
-
-		void set_rom_is_overlay(bool rom_is_overlay) {
-			ROM_is_overlay_ = rom_is_overlay;
-
-			using Model = Analyser::Static::Macintosh::Target::Model;
-			switch(model) {
-				case Model::Mac128k:
-				case Model::Mac512k:
-				case Model::Mac512ke:
-					populate_memory_map(0, [rom_is_overlay] (std::function<void(int target, BusDevice device)> map_to) {
-						// Addresses up to $80 0000 aren't affected by this bit.
-						if(rom_is_overlay) {
-							// Up to $60 0000 mirrors of the ROM alternate with unassigned areas every $10 0000 byes.
-							for(int c = 0; c < 0x600000; c += 0x100000) {
-								map_to(c + 0x100000, (c & 0x100000) ? BusDevice::Unassigned : BusDevice::ROM);
-							}
-							map_to(0x800000, BusDevice::RAM);
-						} else {
-							map_to(0x400000, BusDevice::RAM);
-							map_to(0x500000, BusDevice::ROM);
-							map_to(0x800000, BusDevice::Unassigned);
-						}
-					});
-				break;
-
-				case Model::MacPlus:
-					populate_memory_map(0, [rom_is_overlay] (std::function<void(int target, BusDevice device)> map_to) {
-						// Addresses up to $80 0000 aren't affected by this bit.
-						if(rom_is_overlay) {
-							for(int c = 0; c < 0x580000; c += 0x20000) {
-								map_to(c + 0x20000, ((c & 0x100000) || (c & 0x20000)) ? BusDevice::Unassigned : BusDevice::ROM);
-							}
-							map_to(0x600000, BusDevice::SCSI);
-							map_to(0x800000, BusDevice::RAM);
-						} else {
-							map_to(0x400000, BusDevice::RAM);
-							for(int c = 0x400000; c < 0x580000; c += 0x20000) {
-								map_to(c + 0x20000, ((c & 0x100000) || (c & 0x20000)) ? BusDevice::Unassigned : BusDevice::ROM);
-							}
-							map_to(0x600000, BusDevice::SCSI);
-							map_to(0x800000, BusDevice::Unassigned);
-						}
-					});
-				break;
-			}
-		}
-
-		bool video_is_outputting() {
-			return video_.is_outputting(time_since_video_update_);
-		}
-
-		void set_use_alternate_buffers(bool use_alternate_screen_buffer, bool use_alternate_audio_buffer) {
-			update_video();
-			video_.set_use_alternate_buffers(use_alternate_screen_buffer, use_alternate_audio_buffer);
-		}
-
-		bool insert_media(const Analyser::Static::Media &media) final {
-			if(media.disks.empty() && media.mass_storage_devices.empty())
-				return false;
-
-			// TODO: shouldn't allow disks to be replaced like this, as the Mac
-			// uses software eject. Will need to expand messaging ability of
-			// insert_media.
-			if(!media.disks.empty()) {
-				if(drives_[0].has_disk())
-					drives_[1].set_disk(media.disks[0]);
-				else
-					drives_[0].set_disk(media.disks[0]);
-			}
-
-			// TODO: allow this only at machine startup?
-			if(!media.mass_storage_devices.empty()) {
-				const auto volume = dynamic_cast<Storage::MassStorage::Encodings::Macintosh::Volume *>(media.mass_storage_devices.front().get());
-				if(volume) {
-					volume->set_drive_type(Storage::MassStorage::Encodings::Macintosh::DriveType::SCSI);
+					// VIA accesses are via address 0xefe1fe + register*512,
+					// which at word precision is 0x77f0ff + register*256.
+					if(cycle.operation & CPU::MC68000::Operation::Read) {
+						cycle.set_value8_high(via_.read(register_address));
+					} else {
+						via_.write(register_address, cycle.value8_high());
+					}
 				}
-				hard_drive_->set_storage(media.mass_storage_devices.front());
+			} return delay;
+
+			case BusDevice::PhaseRead: {
+				if(cycle.operation & CPU::MC68000::Operation::Read) {
+					cycle.set_value8_low(phase_ & 7);
+				}
+			} return delay;
+
+			case BusDevice::IWM: {
+				if(*cycle.address & 1) {
+					const int register_address = address >> 9;
+
+					// The IWM; this is a purely polled device, so can be run on demand.
+					if(cycle.operation & CPU::MC68000::Operation::Read) {
+						cycle.set_value8_low(iwm_->read(register_address));
+					} else {
+						iwm_->write(register_address, cycle.value8_low());
+					}
+				} else {
+					fill_unmapped(cycle);
+				}
+			} return delay;
+
+			case BusDevice::SCSI: {
+				const int register_address = address >> 4;
+				const bool dma_acknowledge = address & 0x200;
+
+				// Even accesses = read; odd = write.
+				if(*cycle.address & 1) {
+					// Odd access => this is a write. Data will be in the upper byte.
+					if(cycle.operation & CPU::MC68000::Operation::Read) {
+						scsi_.write(register_address, 0xff, dma_acknowledge);
+					} else {
+						scsi_.write(register_address, cycle.value8_high());
+					}
+				} else {
+					// Even access => this is a read.
+					if(cycle.operation & CPU::MC68000::Operation::Read) {
+						cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
+					}
+				}
+			} return delay;
+
+			case BusDevice::SCCReadResetPhase: {
+				// Any word access here adjusts phase.
+				if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
+					adjust_phase();
+				} else {
+					// A0 = 1 => reset; A0 = 0 => read.
+					if(*cycle.address & 1) {
+						scc_.reset();
+
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value16(0xffff);
+						}
+					} else {
+						const auto read = scc_.read(int(address >> 1));
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value8_high(read);
+						}
+					}
+				}
+			} return delay;
+
+			case BusDevice::SCCWrite: {
+				// Any word access here adjusts phase.
+				if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
+					adjust_phase();
+				} else {
+					// This is definitely a byte access; either it's to an odd address, in which
+					// case it will reach the SCC, or it isn't, in which case it won't.
+					if(*cycle.address & 1) {
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							scc_.write(int(address >> 1), 0xff);
+							cycle.value->b = 0xff;
+						} else {
+							scc_.write(int(address >> 1), cycle.value->b);
+						}
+					} else {
+						fill_unmapped(cycle);
+					}
+				}
+			} return delay;
+
+			case BusDevice::RAM: {
+				// This is coupled with the Macintosh implementation of video; the magic
+				// constant should probably be factored into the Video class.
+				// It embodies knowledge of the fact that video (and audio) will always
+				// be fetched from the final $d900 bytes of memory.
+				// (And that ram_mask_ = ram size - 1).
+				if(address > ram_mask_ - 0xd900)
+					update_video();
+
+				memory_base = ram_.data();
+				address &= ram_mask_;
+
+				// Apply a delay due to video contention if applicable; scheme applied:
+				// only every other access slot is available during the period of video
+				// output. I believe this to be correct for the 128k, 512k and Plus.
+				// More research to do on other models.
+				if(video_is_outputting() && ram_subcycle_ < 8) {
+					delay = HalfCycles(8 - ram_subcycle_);
+					advance_time(delay);
+				}
+			} break;
+
+			case BusDevice::ROM: {
+				if(!(cycle.operation & CPU::MC68000::Operation::Read)) return delay;
+				memory_base = rom_;
+				address &= rom_mask_;
+			} break;
+		}
+
+		// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
+		// Potential writes to ROM and all hardware accesses have already been weeded out.
+		cycle.apply(&memory_base[address]);
+
+		return delay;
+	}
+
+	void flush_output(int) {
+		// Flush the video before the audio queue; in a Mac the
+		// video is responsible for providing part of the
+		// audio signal, so the two aren't as distinct as in
+		// most machines.
+		update_video();
+
+		// As above: flush audio after video.
+		via_.flush();
+		audio_.queue.perform();
+
+		// This avoids deferring IWM costs indefinitely, until
+		// they become artbitrarily large.
+		iwm_.flush();
+	}
+
+	void set_rom_is_overlay(bool rom_is_overlay) {
+		ROM_is_overlay_ = rom_is_overlay;
+
+		using Model = Analyser::Static::Macintosh::Target::Model;
+		switch(model) {
+			case Model::Mac128k:
+			case Model::Mac512k:
+			case Model::Mac512ke:
+				populate_memory_map(0, [rom_is_overlay] (std::function<void(int target, BusDevice device)> map_to) {
+					// Addresses up to $80 0000 aren't affected by this bit.
+					if(rom_is_overlay) {
+						// Up to $60 0000 mirrors of the ROM alternate with unassigned areas every $10 0000 byes.
+						for(int c = 0; c < 0x600000; c += 0x100000) {
+							map_to(c + 0x100000, (c & 0x100000) ? BusDevice::Unassigned : BusDevice::ROM);
+						}
+						map_to(0x800000, BusDevice::RAM);
+					} else {
+						map_to(0x400000, BusDevice::RAM);
+						map_to(0x500000, BusDevice::ROM);
+						map_to(0x800000, BusDevice::Unassigned);
+					}
+				});
+			break;
+
+			case Model::MacPlus:
+				populate_memory_map(0, [rom_is_overlay] (std::function<void(int target, BusDevice device)> map_to) {
+					// Addresses up to $80 0000 aren't affected by this bit.
+					if(rom_is_overlay) {
+						for(int c = 0; c < 0x580000; c += 0x20000) {
+							map_to(c + 0x20000, ((c & 0x100000) || (c & 0x20000)) ? BusDevice::Unassigned : BusDevice::ROM);
+						}
+						map_to(0x600000, BusDevice::SCSI);
+						map_to(0x800000, BusDevice::RAM);
+					} else {
+						map_to(0x400000, BusDevice::RAM);
+						for(int c = 0x400000; c < 0x580000; c += 0x20000) {
+							map_to(c + 0x20000, ((c & 0x100000) || (c & 0x20000)) ? BusDevice::Unassigned : BusDevice::ROM);
+						}
+						map_to(0x600000, BusDevice::SCSI);
+						map_to(0x800000, BusDevice::Unassigned);
+					}
+				});
+			break;
+		}
+	}
+
+	bool video_is_outputting() {
+		return video_.is_outputting(time_since_video_update_);
+	}
+
+	void set_use_alternate_buffers(bool use_alternate_screen_buffer, bool use_alternate_audio_buffer) {
+		update_video();
+		video_.set_use_alternate_buffers(use_alternate_screen_buffer, use_alternate_audio_buffer);
+	}
+
+	bool insert_media(const Analyser::Static::Media &media) final {
+		if(media.disks.empty() && media.mass_storage_devices.empty())
+			return false;
+
+		// TODO: shouldn't allow disks to be replaced like this, as the Mac
+		// uses software eject. Will need to expand messaging ability of
+		// insert_media.
+		if(!media.disks.empty()) {
+			if(drives_[0].has_disk())
+				drives_[1].set_disk(media.disks[0]);
+			else
+				drives_[0].set_disk(media.disks[0]);
+		}
+
+		// TODO: allow this only at machine startup?
+		if(!media.mass_storage_devices.empty()) {
+			const auto volume = dynamic_cast<Storage::MassStorage::Encodings::Macintosh::Volume *>(media.mass_storage_devices.front().get());
+			if(volume) {
+				volume->set_drive_type(Storage::MassStorage::Encodings::Macintosh::DriveType::SCSI);
+			}
+			hard_drive_->set_storage(media.mass_storage_devices.front());
+		}
+
+		return true;
+	}
+
+	// MARK: Keyboard input.
+
+	KeyboardMapper *get_keyboard_mapper() final {
+		return &keyboard_mapper_;
+	}
+
+	void set_key_state(uint16_t key, bool is_pressed) final {
+		keyboard_.enqueue_key_state(key, is_pressed);
+	}
+
+	// TODO: clear all keys.
+
+	// MARK: Interrupt updates.
+
+	void did_change_interrupt_status(Zilog::SCC::z8530 *, bool) final {
+		update_interrupt_input();
+	}
+
+	void update_interrupt_input() {
+		// Update interrupt input.
+		// TODO: does this really cascade like this?
+		if(scc_.get_interrupt_line()) {
+			mc68000_.set_interrupt_level(2);
+		} else if(via_.get_interrupt_line()) {
+			mc68000_.set_interrupt_level(1);
+		} else {
+			mc68000_.set_interrupt_level(0);
+		}
+	}
+
+	// MARK: - Activity Source
+	void set_activity_observer(Activity::Observer *observer) final {
+		iwm_->set_activity_observer(observer);
+
+		if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
+			scsi_bus_.set_activity_observer(observer);
+		}
+	}
+
+	// MARK: - Configuration options.
+	std::unique_ptr<Reflection::Struct> get_options() const final {
+		auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
+		options->quickboot = quickboot_;
+		return options;
+	}
+
+	void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
+		// TODO: should this really be a runtime option?
+		// It should probably be a construction option.
+
+		const auto options = dynamic_cast<Options *>(str.get());
+		quickboot_ = options->quickboot;
+
+		using Model = Analyser::Static::Macintosh::Target::Model;
+		const bool is_plus_rom = model == Model::Mac512ke || model == Model::MacPlus;
+		if(quickboot_ && is_plus_rom) {
+			// Cf. Big Mess o' Wires' disassembly of the Mac Plus ROM, and the
+			// test at $E00. TODO: adapt as(/if?) necessary for other Macs.
+			ram_[0x02ae] = 0x40;
+			ram_[0x02af] = 0x00;
+			ram_[0x02b0] = 0x00;
+			ram_[0x02b1] = 0x00;
+		}
+	}
+
+private:
+	bool quickboot_ = false;
+
+	void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final {
+		scsi_bus_is_clocked_ = scsi_bus_.preferred_clocking() != ClockingHint::Preference::None;
+	}
+
+	void drive_speed_accumulator_set_drive_speed(DriveSpeedAccumulator *, float speed) final {
+		iwm_.flush();
+		drives_[0].set_rotation_speed(speed);
+		drives_[1].set_rotation_speed(speed);
+	}
+
+	forceinline void adjust_phase() {
+		++phase_;
+	}
+
+	template <typename Microcycle>
+	forceinline void fill_unmapped(const Microcycle &cycle) {
+		if(!(cycle.operation & CPU::MC68000::Operation::Read)) return;
+		cycle.set_value16(0xffff);
+	}
+
+	/// Advances all non-CPU components by @c duration half cycles.
+	forceinline void advance_time(HalfCycles duration) {
+		time_since_video_update_ += duration;
+		iwm_ += duration;
+		ram_subcycle_ = (ram_subcycle_ + duration.as_integral()) & 15;
+
+		// The VIA runs at one-tenth of the 68000's clock speed, in sync with the E clock.
+		// See: Guide to the Macintosh Hardware Family p149 (PDF p188). Some extra division
+		// may occur here in order to provide VSYNC at a proper moment.
+
+		// Possibly route vsync.
+		if(time_since_video_update_ < time_until_video_event_) {
+			via_clock_ += duration;
+			via_.run_for(via_clock_.divide(HalfCycles(10)));
+		} else {
+			auto via_time_base = time_since_video_update_ - duration;
+			auto via_cycles_outstanding = duration;
+			while(time_until_video_event_ < time_since_video_update_) {
+				const auto via_cycles = time_until_video_event_ - via_time_base;
+				via_time_base = HalfCycles(0);
+				via_cycles_outstanding -= via_cycles;
+
+				via_clock_ += via_cycles;
+				via_.run_for(via_clock_.divide(HalfCycles(10)));
+
+				video_.run_for(time_until_video_event_);
+				time_since_video_update_ -= time_until_video_event_;
+				time_until_video_event_ = video_.next_sequence_point();
+
+				via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !video_.vsync());
 			}
 
-			return true;
+			via_clock_ += via_cycles_outstanding;
+			via_.run_for(via_clock_.divide(HalfCycles(10)));
 		}
 
-		// MARK: Keyboard input.
-
-		KeyboardMapper *get_keyboard_mapper() final {
-			return &keyboard_mapper_;
+		// The keyboard also has a clock, albeit a very slow one — 100,000 cycles/second.
+		// Its clock and data lines are connected to the VIA.
+		keyboard_clock_ += duration;
+		if(keyboard_clock_ >= KEYBOARD_CLOCK_RATE) {
+			const auto keyboard_ticks = keyboard_clock_.divide(KEYBOARD_CLOCK_RATE);
+			keyboard_.run_for(keyboard_ticks);
+			via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::Two, keyboard_.get_data());
+			via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::One, keyboard_.get_clock());
 		}
 
-		void set_key_state(uint16_t key, bool is_pressed) final {
-			keyboard_.enqueue_key_state(key, is_pressed);
-		}
-
-		// TODO: clear all keys.
-
-		// MARK: Interrupt updates.
-
-		void did_change_interrupt_status(Zilog::SCC::z8530 *, bool) final {
-			update_interrupt_input();
-		}
-
-		void update_interrupt_input() {
-			// Update interrupt input.
-			// TODO: does this really cascade like this?
-			if(scc_.get_interrupt_line()) {
-				mc68000_.set_interrupt_level(2);
-			} else if(via_.get_interrupt_line()) {
-				mc68000_.set_interrupt_level(1);
-			} else {
-				mc68000_.set_interrupt_level(0);
+		// Feed mouse inputs within at most 1250 cycles of each other.
+		if(mouse_.has_steps()) {
+			time_since_mouse_update_ += duration;
+			const auto mouse_ticks = time_since_mouse_update_.divide(HalfCycles(2500));
+			if(mouse_ticks > HalfCycles(0)) {
+				mouse_.prepare_step();
+				scc_.set_dcd(0, mouse_.get_channel(1) & 1);
+				scc_.set_dcd(1, mouse_.get_channel(0) & 1);
 			}
 		}
 
-		// MARK: - Activity Source
-		void set_activity_observer(Activity::Observer *observer) final {
-			iwm_->set_activity_observer(observer);
+		// TODO: SCC should be clocked at a divide-by-two, if and when it actually has
+		// anything connected.
 
-			if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
-				scsi_bus_.set_activity_observer(observer);
+		// Consider updating the real-time clock.
+		real_time_clock_ += duration;
+		auto ticks = real_time_clock_.divide_cycles(Cycles(CLOCK_RATE)).as_integral();
+		while(ticks--) {
+			clock_.update();
+			// TODO: leave a delay between toggling the input rather than using this coupled hack.
+			via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, true);
+			via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, false);
+		}
+
+		// Update the SCSI if currently active.
+		if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
+			if(scsi_bus_is_clocked_) scsi_bus_.run_for(duration);
+		}
+	}
+
+	forceinline void update_video() {
+		video_.run_for(time_since_video_update_.flush<HalfCycles>());
+		time_until_video_event_ = video_.next_sequence_point();
+	}
+
+	Inputs::Mouse &get_mouse() final {
+		return mouse_;
+	}
+
+	using IWMActor = JustInTimeActor<IWM>;
+
+	class VIAPortHandler: public MOS::MOS6522::PortHandler {
+	public:
+		VIAPortHandler(ConcreteMachine &machine, Apple::Clock::SerialClock &clock, Keyboard &keyboard, DeferredAudio &audio, IWMActor &iwm, Inputs::QuadratureMouse &mouse) :
+			machine_(machine), clock_(clock), keyboard_(keyboard), audio_(audio), iwm_(iwm), mouse_(mouse) {}
+
+		using Port = MOS::MOS6522::Port;
+		using Line = MOS::MOS6522::Line;
+
+		void set_port_output(Port port, uint8_t value, uint8_t) {
+			/*
+				Peripheral lines: keyboard data, interrupt configuration.
+				(See p176 [/215])
+			*/
+			switch(port) {
+				case Port::A:
+					/*
+						Port A:
+							b7:	[input] SCC wait/request (/W/REQA and /W/REQB wired together for a logical OR)
+							b6:	0 = alternate screen buffer, 1 = main screen buffer
+							b5:	floppy disk SEL state control (upper/lower head "among other things")
+							b4:	1 = use ROM overlay memory map, 0 = use ordinary memory map
+							b3:	0 = use alternate sound buffer, 1 = use ordinary sound buffer
+							b2–b0:	audio output volume
+					*/
+					iwm_->set_select(!!(value & 0x20));
+
+					machine_.set_use_alternate_buffers(!(value & 0x40), !(value&0x08));
+					machine_.set_rom_is_overlay(!!(value & 0x10));
+
+					audio_.flush();
+					audio_.audio.set_volume(value & 7);
+				break;
+
+				case Port::B:
+					/*
+						Port B:
+							b7:	0 = sound enabled, 1 = sound disabled
+							b6:	[input] 0 = video beam in visible portion of line, 1 = outside
+							b5:	[input] mouse y2
+							b4:	[input] mouse x2
+							b3:	[input] 0 = mouse button down, 1 = up
+							b2:	0 = real-time clock enabled, 1 = disabled
+							b1:	clock's data-clock line
+							b0:	clock's serial data line
+					*/
+					if(value & 0x4) clock_.abort();
+					else clock_.set_input(!!(value & 0x2), !!(value & 0x1));
+
+					audio_.flush();
+					audio_.audio.set_enabled(!(value & 0x80));
+				break;
 			}
 		}
 
-		// MARK: - Configuration options.
-		std::unique_ptr<Reflection::Struct> get_options() const final {
-			auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
-			options->quickboot = quickboot_;
-			return options;
+		uint8_t get_port_input(Port port) {
+			switch(port) {
+				case Port::A:
+//							printf("6522 r A\n");
+				return 0x00;	// TODO: b7 = SCC wait/request
+
+				case Port::B:
+				return uint8_t(
+					((mouse_.get_button_mask() & 1) ? 0x00 : 0x08) |
+					((mouse_.get_channel(0) & 2) << 3) |
+					((mouse_.get_channel(1) & 2) << 4) |
+					(clock_.get_data() ? 0x02 : 0x00) |
+					(machine_.video_is_outputting() ? 0x00 : 0x40)
+				);
+			}
+
+			// Should be unreachable.
+			return 0xff;
 		}
 
-		void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
-			// TODO: should this really be a runtime option?
-			// It should probably be a construction option.
+		void set_control_line_output(Port port, Line line, bool value) {
+			/*
+				Keyboard wiring (I believe):
+				CB2 = data		(input/output)
+				CB1 = clock		(input)
 
-			const auto options = dynamic_cast<Options *>(str.get());
-			quickboot_ = options->quickboot;
-
-			using Model = Analyser::Static::Macintosh::Target::Model;
-			const bool is_plus_rom = model == Model::Mac512ke || model == Model::MacPlus;
-			if(quickboot_ && is_plus_rom) {
-				// Cf. Big Mess o' Wires' disassembly of the Mac Plus ROM, and the
-				// test at $E00. TODO: adapt as(/if?) necessary for other Macs.
-				ram_[0x02ae] = 0x40;
-				ram_[0x02af] = 0x00;
-				ram_[0x02b0] = 0x00;
-				ram_[0x02b1] = 0x00;
+				CA2 is used for receiving RTC interrupts.
+				CA1 is used for receiving vsync.
+			*/
+			if(port == Port::B && line == Line::Two) {
+				keyboard_.set_input(value);
 			}
+			else logger.error().append("Unhandled 6522 control line output: %c%d", port ? 'B' : 'A', int(line));
+		}
+
+		void run_for(HalfCycles duration) {
+			// The 6522 enjoys a divide-by-ten, so multiply back up here to make the
+			// divided-by-two clock the audio works on.
+			audio_.time_since_update += HalfCycles(duration.as_integral() * 5);
+		}
+
+		void flush() {
+			audio_.flush();
+		}
+
+		void set_interrupt_status(bool) {
+			machine_.update_interrupt_input();
 		}
 
 	private:
-		bool quickboot_ = false;
+		ConcreteMachine &machine_;
+		Apple::Clock::SerialClock &clock_;
+		Keyboard &keyboard_;
+		DeferredAudio &audio_;
+		IWMActor &iwm_;
+		Inputs::QuadratureMouse &mouse_;
+	};
 
-		void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final {
-			scsi_bus_is_clocked_ = scsi_bus_.preferred_clocking() != ClockingHint::Preference::None;
-		}
+	CPU::MC68000::Processor<ConcreteMachine, true, true> mc68000_;
 
-		void drive_speed_accumulator_set_drive_speed(DriveSpeedAccumulator *, float speed) final {
-			iwm_.flush();
-			drives_[0].set_rotation_speed(speed);
-			drives_[1].set_rotation_speed(speed);
-		}
+	DriveSpeedAccumulator drive_speed_accumulator_;
+	IWMActor iwm_;
 
-		forceinline void adjust_phase() {
-			++phase_;
-		}
+	DeferredAudio audio_;
+	Video video_;
 
-		template <typename Microcycle>
-		forceinline void fill_unmapped(const Microcycle &cycle) {
-			if(!(cycle.operation & CPU::MC68000::Operation::Read)) return;
-			cycle.set_value16(0xffff);
-		}
+	Apple::Clock::SerialClock clock_;
+	Keyboard keyboard_;
 
-		/// Advances all non-CPU components by @c duration half cycles.
-		forceinline void advance_time(HalfCycles duration) {
-			time_since_video_update_ += duration;
-			iwm_ += duration;
-			ram_subcycle_ = (ram_subcycle_ + duration.as_integral()) & 15;
+	VIAPortHandler via_port_handler_;
+	MOS::MOS6522::MOS6522<VIAPortHandler> via_;
 
-			// The VIA runs at one-tenth of the 68000's clock speed, in sync with the E clock.
-			// See: Guide to the Macintosh Hardware Family p149 (PDF p188). Some extra division
-			// may occur here in order to provide VSYNC at a proper moment.
+	Zilog::SCC::z8530 scc_;
+	SCSI::Bus scsi_bus_;
+	NCR::NCR5380::NCR5380 scsi_;
+	SCSI::Target::Target<SCSI::DirectAccessDevice> hard_drive_;
+	bool scsi_bus_is_clocked_ = false;
 
-			// Possibly route vsync.
-			if(time_since_video_update_ < time_until_video_event_) {
-				via_clock_ += duration;
-				via_.run_for(via_clock_.divide(HalfCycles(10)));
-			} else {
-				auto via_time_base = time_since_video_update_ - duration;
-				auto via_cycles_outstanding = duration;
-				while(time_until_video_event_ < time_since_video_update_) {
-					const auto via_cycles = time_until_video_event_ - via_time_base;
-					via_time_base = HalfCycles(0);
-					via_cycles_outstanding -= via_cycles;
+	HalfCycles via_clock_;
+	HalfCycles real_time_clock_;
+	HalfCycles keyboard_clock_;
+	HalfCycles time_since_video_update_;
+	HalfCycles time_until_video_event_;
+	HalfCycles time_since_mouse_update_;
 
-					via_clock_ += via_cycles;
-					via_.run_for(via_clock_.divide(HalfCycles(10)));
+	bool ROM_is_overlay_ = true;
+	int phase_ = 1;
+	int ram_subcycle_ = 0;
 
-					video_.run_for(time_until_video_event_);
-					time_since_video_update_ -= time_until_video_event_;
-					time_until_video_event_ = video_.next_sequence_point();
+	DoubleDensityDrive drives_[2];
+	Inputs::QuadratureMouse mouse_;
 
-					via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !video_.vsync());
-				}
+	Apple::Macintosh::KeyboardMapper keyboard_mapper_;
 
-				via_clock_ += via_cycles_outstanding;
-				via_.run_for(via_clock_.divide(HalfCycles(10)));
+	enum class BusDevice {
+		RAM, ROM, VIA, IWM, SCCWrite, SCCReadResetPhase, SCSI, PhaseRead, Unassigned
+	};
+
+	/// Divides the 24-bit address space up into $20000 (i.e. 128kb) segments, recording
+	/// which device is current mapped in each area. Keeping it in a table is a bit faster
+	/// than the multi-level address inspection that is otherwise required, as well as
+	/// simplifying slightly the handling of different models.
+	///
+	/// So: index with the top 7 bits of the 24-bit address.
+	BusDevice memory_map_[128];
+
+	void setup_memory_map() {
+		// Apply the power-up memory map, i.e. assume that ROM_is_overlay_ = true;
+		// start by calling into set_rom_is_overlay to seed everything up to $800000.
+		set_rom_is_overlay(true);
+
+		populate_memory_map(0x800000, [] (std::function<void(int target, BusDevice device)> map_to) {
+			map_to(0x900000, BusDevice::Unassigned);
+			map_to(0xa00000, BusDevice::SCCReadResetPhase);
+			map_to(0xb00000, BusDevice::Unassigned);
+			map_to(0xc00000, BusDevice::SCCWrite);
+			map_to(0xd00000, BusDevice::Unassigned);
+			map_to(0xe00000, BusDevice::IWM);
+			map_to(0xe80000, BusDevice::Unassigned);
+			map_to(0xf00000, BusDevice::VIA);
+			map_to(0xf80000, BusDevice::PhaseRead);
+			map_to(0x1000000, BusDevice::Unassigned);
+		});
+	}
+
+	void populate_memory_map(int start_address, std::function<void(std::function<void(int, BusDevice)>)> populator) {
+		// Define semantics for below; map_to will write from the current cursor position
+		// to the supplied 24-bit address, setting a particular mapped device.
+		int segment = start_address >> 17;
+		auto map_to = [&segment, this](int address, BusDevice device) {
+			for(; segment < address >> 17; ++segment) {
+				this->memory_map_[segment] = device;
 			}
-
-			// The keyboard also has a clock, albeit a very slow one — 100,000 cycles/second.
-			// Its clock and data lines are connected to the VIA.
-			keyboard_clock_ += duration;
-			if(keyboard_clock_ >= KEYBOARD_CLOCK_RATE) {
-				const auto keyboard_ticks = keyboard_clock_.divide(KEYBOARD_CLOCK_RATE);
-				keyboard_.run_for(keyboard_ticks);
-				via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::Two, keyboard_.get_data());
-				via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::One, keyboard_.get_clock());
-			}
-
-			// Feed mouse inputs within at most 1250 cycles of each other.
-			if(mouse_.has_steps()) {
-				time_since_mouse_update_ += duration;
-				const auto mouse_ticks = time_since_mouse_update_.divide(HalfCycles(2500));
-				if(mouse_ticks > HalfCycles(0)) {
-					mouse_.prepare_step();
-					scc_.set_dcd(0, mouse_.get_channel(1) & 1);
-					scc_.set_dcd(1, mouse_.get_channel(0) & 1);
-				}
-			}
-
-			// TODO: SCC should be clocked at a divide-by-two, if and when it actually has
-			// anything connected.
-
-			// Consider updating the real-time clock.
-			real_time_clock_ += duration;
-			auto ticks = real_time_clock_.divide_cycles(Cycles(CLOCK_RATE)).as_integral();
-			while(ticks--) {
-				clock_.update();
-				// TODO: leave a delay between toggling the input rather than using this coupled hack.
-				via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, true);
-				via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, false);
-			}
-
-			// Update the SCSI if currently active.
-			if constexpr (model == Analyser::Static::Macintosh::Target::Model::MacPlus) {
-				if(scsi_bus_is_clocked_) scsi_bus_.run_for(duration);
-			}
-		}
-
-		forceinline void update_video() {
-			video_.run_for(time_since_video_update_.flush<HalfCycles>());
-			time_until_video_event_ = video_.next_sequence_point();
-		}
-
-		Inputs::Mouse &get_mouse() final {
-			return mouse_;
-		}
-
-		using IWMActor = JustInTimeActor<IWM>;
-
-		class VIAPortHandler: public MOS::MOS6522::PortHandler {
-			public:
-				VIAPortHandler(ConcreteMachine &machine, Apple::Clock::SerialClock &clock, Keyboard &keyboard, DeferredAudio &audio, IWMActor &iwm, Inputs::QuadratureMouse &mouse) :
-					machine_(machine), clock_(clock), keyboard_(keyboard), audio_(audio), iwm_(iwm), mouse_(mouse) {}
-
-				using Port = MOS::MOS6522::Port;
-				using Line = MOS::MOS6522::Line;
-
-				void set_port_output(Port port, uint8_t value, uint8_t) {
-					/*
-						Peripheral lines: keyboard data, interrupt configuration.
-						(See p176 [/215])
-					*/
-					switch(port) {
-						case Port::A:
-							/*
-								Port A:
-									b7:	[input] SCC wait/request (/W/REQA and /W/REQB wired together for a logical OR)
-									b6:	0 = alternate screen buffer, 1 = main screen buffer
-									b5:	floppy disk SEL state control (upper/lower head "among other things")
-									b4:	1 = use ROM overlay memory map, 0 = use ordinary memory map
-									b3:	0 = use alternate sound buffer, 1 = use ordinary sound buffer
-									b2–b0:	audio output volume
-							*/
-							iwm_->set_select(!!(value & 0x20));
-
-							machine_.set_use_alternate_buffers(!(value & 0x40), !(value&0x08));
-							machine_.set_rom_is_overlay(!!(value & 0x10));
-
-							audio_.flush();
-							audio_.audio.set_volume(value & 7);
-						break;
-
-						case Port::B:
-							/*
-								Port B:
-									b7:	0 = sound enabled, 1 = sound disabled
-									b6:	[input] 0 = video beam in visible portion of line, 1 = outside
-									b5:	[input] mouse y2
-									b4:	[input] mouse x2
-									b3:	[input] 0 = mouse button down, 1 = up
-									b2:	0 = real-time clock enabled, 1 = disabled
-									b1:	clock's data-clock line
-									b0:	clock's serial data line
-							*/
-							if(value & 0x4) clock_.abort();
-							else clock_.set_input(!!(value & 0x2), !!(value & 0x1));
-
-							audio_.flush();
-							audio_.audio.set_enabled(!(value & 0x80));
-						break;
-					}
-				}
-
-				uint8_t get_port_input(Port port) {
-					switch(port) {
-						case Port::A:
-//							printf("6522 r A\n");
-						return 0x00;	// TODO: b7 = SCC wait/request
-
-						case Port::B:
-						return uint8_t(
-							((mouse_.get_button_mask() & 1) ? 0x00 : 0x08) |
-							((mouse_.get_channel(0) & 2) << 3) |
-							((mouse_.get_channel(1) & 2) << 4) |
-							(clock_.get_data() ? 0x02 : 0x00) |
-							(machine_.video_is_outputting() ? 0x00 : 0x40)
-						);
-					}
-
-					// Should be unreachable.
-					return 0xff;
-				}
-
-				void set_control_line_output(Port port, Line line, bool value) {
-					/*
-						Keyboard wiring (I believe):
-						CB2 = data		(input/output)
-						CB1 = clock		(input)
-
-						CA2 is used for receiving RTC interrupts.
-						CA1 is used for receiving vsync.
-					*/
-					if(port == Port::B && line == Line::Two) {
-						keyboard_.set_input(value);
-					}
-					else logger.error().append("Unhandled 6522 control line output: %c%d", port ? 'B' : 'A', int(line));
-				}
-
-				void run_for(HalfCycles duration) {
-					// The 6522 enjoys a divide-by-ten, so multiply back up here to make the
-					// divided-by-two clock the audio works on.
-					audio_.time_since_update += HalfCycles(duration.as_integral() * 5);
-				}
-
-				void flush() {
-					audio_.flush();
-				}
-
-				void set_interrupt_status(bool) {
-					machine_.update_interrupt_input();
-				}
-
-			private:
-				ConcreteMachine &machine_;
-				Apple::Clock::SerialClock &clock_;
-				Keyboard &keyboard_;
-				DeferredAudio &audio_;
-				IWMActor &iwm_;
-				Inputs::QuadratureMouse &mouse_;
 		};
 
-		CPU::MC68000::Processor<ConcreteMachine, true, true> mc68000_;
+		populator(map_to);
+	}
 
-		DriveSpeedAccumulator drive_speed_accumulator_;
-		IWMActor iwm_;
-
-		DeferredAudio audio_;
-		Video video_;
-
-		Apple::Clock::SerialClock clock_;
-		Keyboard keyboard_;
-
-		VIAPortHandler via_port_handler_;
-		MOS::MOS6522::MOS6522<VIAPortHandler> via_;
-
-		Zilog::SCC::z8530 scc_;
-		SCSI::Bus scsi_bus_;
-		NCR::NCR5380::NCR5380 scsi_;
-		SCSI::Target::Target<SCSI::DirectAccessDevice> hard_drive_;
-		bool scsi_bus_is_clocked_ = false;
-
-		HalfCycles via_clock_;
-		HalfCycles real_time_clock_;
-		HalfCycles keyboard_clock_;
-		HalfCycles time_since_video_update_;
-		HalfCycles time_until_video_event_;
-		HalfCycles time_since_mouse_update_;
-
-		bool ROM_is_overlay_ = true;
-		int phase_ = 1;
-		int ram_subcycle_ = 0;
-
-		DoubleDensityDrive drives_[2];
-		Inputs::QuadratureMouse mouse_;
-
-		Apple::Macintosh::KeyboardMapper keyboard_mapper_;
-
-		enum class BusDevice {
-			RAM, ROM, VIA, IWM, SCCWrite, SCCReadResetPhase, SCSI, PhaseRead, Unassigned
-		};
-
-		/// Divides the 24-bit address space up into $20000 (i.e. 128kb) segments, recording
-		/// which device is current mapped in each area. Keeping it in a table is a bit faster
-		/// than the multi-level address inspection that is otherwise required, as well as
-		/// simplifying slightly the handling of different models.
-		///
-		/// So: index with the top 7 bits of the 24-bit address.
-		BusDevice memory_map_[128];
-
-		void setup_memory_map() {
-			// Apply the power-up memory map, i.e. assume that ROM_is_overlay_ = true;
-			// start by calling into set_rom_is_overlay to seed everything up to $800000.
-			set_rom_is_overlay(true);
-
-			populate_memory_map(0x800000, [] (std::function<void(int target, BusDevice device)> map_to) {
-				map_to(0x900000, BusDevice::Unassigned);
-				map_to(0xa00000, BusDevice::SCCReadResetPhase);
-				map_to(0xb00000, BusDevice::Unassigned);
-				map_to(0xc00000, BusDevice::SCCWrite);
-				map_to(0xd00000, BusDevice::Unassigned);
-				map_to(0xe00000, BusDevice::IWM);
-				map_to(0xe80000, BusDevice::Unassigned);
-				map_to(0xf00000, BusDevice::VIA);
-				map_to(0xf80000, BusDevice::PhaseRead);
-				map_to(0x1000000, BusDevice::Unassigned);
-			});
-		}
-
-		void populate_memory_map(int start_address, std::function<void(std::function<void(int, BusDevice)>)> populator) {
-			// Define semantics for below; map_to will write from the current cursor position
-			// to the supplied 24-bit address, setting a particular mapped device.
-			int segment = start_address >> 17;
-			auto map_to = [&segment, this](int address, BusDevice device) {
-				for(; segment < address >> 17; ++segment) {
-					this->memory_map_[segment] = device;
-				}
-			};
-
-			populator(map_to);
-		}
-
-		uint32_t ram_mask_ = 0;
-		uint32_t rom_mask_ = 0;
-		uint8_t rom_[128*1024];
-		std::vector<uint8_t> ram_;
+	uint32_t ram_mask_ = 0;
+	uint32_t rom_mask_ = 0;
+	uint8_t rom_[128*1024];
+	std::vector<uint8_t> ram_;
 };
 
 }
diff --git a/Machines/Apple/Macintosh/Video.hpp b/Machines/Apple/Macintosh/Video.hpp
index 6f0ed7658..9149232d4 100644
--- a/Machines/Apple/Macintosh/Video.hpp
+++ b/Machines/Apple/Macintosh/Video.hpp
@@ -28,76 +28,76 @@ constexpr int sync_end = 38;
 	This class also collects audio and 400kb drive-speed data, forwarding those values.
 */
 class Video {
-	public:
-		/*!
-			Constructs an instance of @c Video sourcing its pixel data from @c ram and
-			providing audio and drive-speed bytes to @c audio and @c drive_speed_accumulator.
-		*/
-		Video(DeferredAudio &audio, DriveSpeedAccumulator &drive_speed_accumulator);
+public:
+	/*!
+		Constructs an instance of @c Video sourcing its pixel data from @c ram and
+		providing audio and drive-speed bytes to @c audio and @c drive_speed_accumulator.
+	*/
+	Video(DeferredAudio &audio, DriveSpeedAccumulator &drive_speed_accumulator);
 
-		/*!
-			Sets the target device for video data.
-		*/
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target);
+	/*!
+		Sets the target device for video data.
+	*/
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target);
 
-		/// Gets the current scan status.
-		Outputs::Display::ScanStatus get_scaled_scan_status() const;
+	/// Gets the current scan status.
+	Outputs::Display::ScanStatus get_scaled_scan_status() const;
 
-		/*!
-			Produces the next @c duration period of pixels.
-		*/
-		void run_for(HalfCycles duration);
+	/*!
+		Produces the next @c duration period of pixels.
+	*/
+	void run_for(HalfCycles duration);
 
-		/*!
-			Sets whether the alternate screen and/or audio buffers should be used to source data.
-		*/
-		void set_use_alternate_buffers(bool use_alternate_screen_buffer, bool use_alternate_audio_buffer);
+	/*!
+		Sets whether the alternate screen and/or audio buffers should be used to source data.
+	*/
+	void set_use_alternate_buffers(bool use_alternate_screen_buffer, bool use_alternate_audio_buffer);
 
-		/*!
-			Provides a base address and a mask indicating which parts of the generated video and audio/drive addresses are
-			actually decoded, accessing *word-sized memory*; e.g. for a 128kb Macintosh this should be (1 << 16) - 1 = 0xffff.
-		*/
-		void set_ram(uint16_t *ram, uint32_t mask);
+	/*!
+		Provides a base address and a mask indicating which parts of the generated video and audio/drive addresses are
+		actually decoded, accessing *word-sized memory*; e.g. for a 128kb Macintosh this should be (1 << 16) - 1 = 0xffff.
+	*/
+	void set_ram(uint16_t *ram, uint32_t mask);
 
-		/*!
-			@returns @c true if the video is currently outputting a vertical sync, @c false otherwise.
-		*/
-		bool vsync();
+	/*!
+		@returns @c true if the video is currently outputting a vertical sync, @c false otherwise.
+	*/
+	bool vsync();
 
-		/*
-			@returns @c true if in @c offset half cycles from now, the video will be outputting pixels;
-				@c false otherwise.
-		*/
-		bool is_outputting(HalfCycles offset = HalfCycles(0)) {
-			const auto offset_position = frame_position_ + offset % frame_length;
-			const int column = int((offset_position % line_length).as_integral()) >> 4;
-			const int line = int((offset_position / line_length).as_integral());
-			return line < 342 && column < 32;
-		}
+	/*
+		@returns @c true if in @c offset half cycles from now, the video will be outputting pixels;
+			@c false otherwise.
+	*/
+	bool is_outputting(HalfCycles offset = HalfCycles(0)) {
+		const auto offset_position = frame_position_ + offset % frame_length;
+		const int column = int((offset_position % line_length).as_integral()) >> 4;
+		const int line = int((offset_position / line_length).as_integral());
+		return line < 342 && column < 32;
+	}
 
-		/*!
-			@returns the amount of time until there is next a transition on the
-				vsync signal.
-		*/
-		HalfCycles next_sequence_point();
+	/*!
+		@returns the amount of time until there is next a transition on the
+			vsync signal.
+	*/
+	HalfCycles next_sequence_point();
 
-	private:
-		DeferredAudio &audio_;
-		DriveSpeedAccumulator &drive_speed_accumulator_;
+private:
+	DeferredAudio &audio_;
+	DriveSpeedAccumulator &drive_speed_accumulator_;
 
-		Outputs::CRT::CRT crt_;
-		uint16_t *ram_ = nullptr;
-		uint32_t ram_mask_ = 0;
+	Outputs::CRT::CRT crt_;
+	uint16_t *ram_ = nullptr;
+	uint32_t ram_mask_ = 0;
 
-		HalfCycles frame_position_;
+	HalfCycles frame_position_;
 
-		size_t video_address_ = 0;
-		size_t audio_address_ = 0;
+	size_t video_address_ = 0;
+	size_t audio_address_ = 0;
 
-		uint64_t *pixel_buffer_ = nullptr;
+	uint64_t *pixel_buffer_ = nullptr;
 
-		bool use_alternate_screen_buffer_ = false;
-		bool use_alternate_audio_buffer_ = false;
+	bool use_alternate_screen_buffer_ = false;
+	bool use_alternate_audio_buffer_ = false;
 };
 
 }
diff --git a/Machines/Atari/2600/Atari2600.cpp b/Machines/Atari/2600/Atari2600.cpp
index 36db0226a..1e6eb9b8a 100644
--- a/Machines/Atari/2600/Atari2600.cpp
+++ b/Machines/Atari/2600/Atari2600.cpp
@@ -36,39 +36,39 @@ namespace {
 namespace Atari2600 {
 
 class Joystick: public Inputs::ConcreteJoystick {
-	public:
-		Joystick(Bus *bus, std::size_t shift, std::size_t fire_tia_input) :
-			ConcreteJoystick({
-				Input(Input::Up),
-				Input(Input::Down),
-				Input(Input::Left),
-				Input(Input::Right),
-				Input(Input::Fire)
-			}),
-			bus_(bus), shift_(shift), fire_tia_input_(fire_tia_input) {}
+public:
+	Joystick(Bus *bus, std::size_t shift, std::size_t fire_tia_input) :
+		ConcreteJoystick({
+			Input(Input::Up),
+			Input(Input::Down),
+			Input(Input::Left),
+			Input(Input::Right),
+			Input(Input::Fire)
+		}),
+		bus_(bus), shift_(shift), fire_tia_input_(fire_tia_input) {}
 
-		void did_set_input(const Input &digital_input, bool is_active) final {
-			switch(digital_input.type) {
-				case Input::Up:		bus_->mos6532_.update_port_input(0, 0x10 >> shift_, is_active);		break;
-				case Input::Down:	bus_->mos6532_.update_port_input(0, 0x20 >> shift_, is_active);		break;
-				case Input::Left:	bus_->mos6532_.update_port_input(0, 0x40 >> shift_, is_active);		break;
-				case Input::Right:	bus_->mos6532_.update_port_input(0, 0x80 >> shift_, is_active);		break;
+	void did_set_input(const Input &digital_input, bool is_active) final {
+		switch(digital_input.type) {
+			case Input::Up:		bus_->mos6532_.update_port_input(0, 0x10 >> shift_, is_active);		break;
+			case Input::Down:	bus_->mos6532_.update_port_input(0, 0x20 >> shift_, is_active);		break;
+			case Input::Left:	bus_->mos6532_.update_port_input(0, 0x40 >> shift_, is_active);		break;
+			case Input::Right:	bus_->mos6532_.update_port_input(0, 0x80 >> shift_, is_active);		break;
 
-				// TODO: latching
-				case Input::Fire:
-					if(is_active)
-						bus_->tia_input_value_[fire_tia_input_] &= ~0x80;
-					else
-						bus_->tia_input_value_[fire_tia_input_] |= 0x80;
-				break;
+			// TODO: latching
+			case Input::Fire:
+				if(is_active)
+					bus_->tia_input_value_[fire_tia_input_] &= ~0x80;
+				else
+					bus_->tia_input_value_[fire_tia_input_] |= 0x80;
+			break;
 
-				default: break;
-			}
+			default: break;
 		}
+	}
 
-	private:
-		Bus *bus_;
-		std::size_t shift_, fire_tia_input_;
+private:
+	Bus *bus_;
+	std::size_t shift_, fire_tia_input_;
 };
 
 using Target = Analyser::Static::Atari2600::Target;
@@ -79,133 +79,133 @@ class ConcreteMachine:
 	public MachineTypes::AudioProducer,
 	public MachineTypes::ScanProducer,
 	public MachineTypes::JoystickMachine {
-	public:
-		ConcreteMachine(const Target &target) : frequency_mismatch_warner_(*this) {
-			const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
+public:
+	ConcreteMachine(const Target &target) : frequency_mismatch_warner_(*this) {
+		const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
 
-			using PagingModel = Target::PagingModel;
-			switch(target.paging_model) {
-				case PagingModel::ActivisionStack:	bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::ActivisionStack>>(rom);	break;
-				case PagingModel::CBSRamPlus:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::CBSRAMPlus>>(rom);		break;
-				case PagingModel::CommaVid:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::CommaVid>>(rom);		break;
-				case PagingModel::MegaBoy:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::MegaBoy>>(rom);			break;
-				case PagingModel::MNetwork:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::MNetwork>>(rom);		break;
-				case PagingModel::None:				bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Unpaged>>(rom);			break;
-				case PagingModel::ParkerBros:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::ParkerBros>>(rom);		break;
-				case PagingModel::Pitfall2:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Pitfall2>>(rom);		break;
-				case PagingModel::Tigervision:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Tigervision>>(rom);		break;
+		using PagingModel = Target::PagingModel;
+		switch(target.paging_model) {
+			case PagingModel::ActivisionStack:	bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::ActivisionStack>>(rom);	break;
+			case PagingModel::CBSRamPlus:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::CBSRAMPlus>>(rom);		break;
+			case PagingModel::CommaVid:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::CommaVid>>(rom);		break;
+			case PagingModel::MegaBoy:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::MegaBoy>>(rom);			break;
+			case PagingModel::MNetwork:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::MNetwork>>(rom);		break;
+			case PagingModel::None:				bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Unpaged>>(rom);			break;
+			case PagingModel::ParkerBros:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::ParkerBros>>(rom);		break;
+			case PagingModel::Pitfall2:			bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Pitfall2>>(rom);		break;
+			case PagingModel::Tigervision:		bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Tigervision>>(rom);		break;
 
-				case PagingModel::Atari8k:
-					if(target.uses_superchip) {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari8kSuperChip>>(rom);
-					} else {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari8k>>(rom);
-					}
-				break;
-				case PagingModel::Atari16k:
-					if(target.uses_superchip) {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari16kSuperChip>>(rom);
-					} else {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari16k>>(rom);
-					}
-				break;
-				case PagingModel::Atari32k:
-					if(target.uses_superchip) {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari32kSuperChip>>(rom);
-					} else {
-						bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari32k>>(rom);
-					}
-				break;
-			}
-
-			joysticks_.emplace_back(new Joystick(bus_.get(), 0, 0));
-			joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
-
-			set_is_ntsc(is_ntsc_);
+			case PagingModel::Atari8k:
+				if(target.uses_superchip) {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari8kSuperChip>>(rom);
+				} else {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari8k>>(rom);
+				}
+			break;
+			case PagingModel::Atari16k:
+				if(target.uses_superchip) {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari16kSuperChip>>(rom);
+				} else {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari16k>>(rom);
+				}
+			break;
+			case PagingModel::Atari32k:
+				if(target.uses_superchip) {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari32kSuperChip>>(rom);
+				} else {
+					bus_ = std::make_unique<Cartridge::Cartridge<Cartridge::Atari32k>>(rom);
+				}
+			break;
 		}
 
-		const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
-			return joysticks_;
+		joysticks_.emplace_back(new Joystick(bus_.get(), 0, 0));
+		joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
+
+		set_is_ntsc(is_ntsc_);
+	}
+
+	const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
+		return joysticks_;
+	}
+
+	void set_switch_is_enabled(Atari2600Switch input, bool state) final {
+		switch(input) {
+			case Atari2600SwitchReset:					bus_->mos6532_.update_port_input(1, 0x01, state);	break;
+			case Atari2600SwitchSelect:					bus_->mos6532_.update_port_input(1, 0x02, state);	break;
+			case Atari2600SwitchColour:					bus_->mos6532_.update_port_input(1, 0x08, state);	break;
+			case Atari2600SwitchLeftPlayerDifficulty:	bus_->mos6532_.update_port_input(1, 0x40, state);	break;
+			case Atari2600SwitchRightPlayerDifficulty:	bus_->mos6532_.update_port_input(1, 0x80, state);	break;
 		}
+	}
 
-		void set_switch_is_enabled(Atari2600Switch input, bool state) final {
-			switch(input) {
-				case Atari2600SwitchReset:					bus_->mos6532_.update_port_input(1, 0x01, state);	break;
-				case Atari2600SwitchSelect:					bus_->mos6532_.update_port_input(1, 0x02, state);	break;
-				case Atari2600SwitchColour:					bus_->mos6532_.update_port_input(1, 0x08, state);	break;
-				case Atari2600SwitchLeftPlayerDifficulty:	bus_->mos6532_.update_port_input(1, 0x40, state);	break;
-				case Atari2600SwitchRightPlayerDifficulty:	bus_->mos6532_.update_port_input(1, 0x80, state);	break;
-			}
+	bool get_switch_is_enabled(Atari2600Switch input) const final {
+		uint8_t port_input = bus_->mos6532_.get_port_input(1);
+		switch(input) {
+			case Atari2600SwitchReset:					return !!(port_input & 0x01);
+			case Atari2600SwitchSelect:					return !!(port_input & 0x02);
+			case Atari2600SwitchColour:					return !!(port_input & 0x08);
+			case Atari2600SwitchLeftPlayerDifficulty:	return !!(port_input & 0x40);
+			case Atari2600SwitchRightPlayerDifficulty:	return !!(port_input & 0x80);
+			default:									return false;
 		}
+	}
 
-		bool get_switch_is_enabled(Atari2600Switch input) const final {
-			uint8_t port_input = bus_->mos6532_.get_port_input(1);
-			switch(input) {
-				case Atari2600SwitchReset:					return !!(port_input & 0x01);
-				case Atari2600SwitchSelect:					return !!(port_input & 0x02);
-				case Atari2600SwitchColour:					return !!(port_input & 0x08);
-				case Atari2600SwitchLeftPlayerDifficulty:	return !!(port_input & 0x40);
-				case Atari2600SwitchRightPlayerDifficulty:	return !!(port_input & 0x80);
-				default:									return false;
-			}
-		}
+	void set_reset_switch(bool state) final {
+		bus_->set_reset_line(state);
+	}
 
-		void set_reset_switch(bool state) final {
-			bus_->set_reset_line(state);
-		}
+	// to satisfy CRTMachine::Machine
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
+		bus_->speaker_.set_input_rate(float(get_clock_rate() / double(CPUTicksPerAudioTick)));
+		bus_->tia_.set_crt_delegate(&frequency_mismatch_warner_);
+		bus_->tia_.set_scan_target(scan_target);
+	}
 
-		// to satisfy CRTMachine::Machine
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
-			bus_->speaker_.set_input_rate(float(get_clock_rate() / double(CPUTicksPerAudioTick)));
-			bus_->tia_.set_crt_delegate(&frequency_mismatch_warner_);
-			bus_->tia_.set_scan_target(scan_target);
-		}
+	Outputs::Display::ScanStatus get_scaled_scan_status() const final {
+		return bus_->tia_.get_scaled_scan_status() / 3.0f;
+	}
 
-		Outputs::Display::ScanStatus get_scaled_scan_status() const final {
-			return bus_->tia_.get_scaled_scan_status() / 3.0f;
-		}
+	Outputs::Speaker::Speaker *get_speaker() final {
+		return &bus_->speaker_;
+	}
 
-		Outputs::Speaker::Speaker *get_speaker() final {
-			return &bus_->speaker_;
-		}
+	void run_for(const Cycles cycles) final {
+		bus_->run_for(cycles);
+		bus_->apply_confidence(confidence_counter_);
+	}
 
-		void run_for(const Cycles cycles) final {
-			bus_->run_for(cycles);
-			bus_->apply_confidence(confidence_counter_);
-		}
+	void flush_output(int) final {
+		bus_->flush();
+	}
 
-		void flush_output(int) final {
-			bus_->flush();
-		}
+	void register_crt_frequency_mismatch() {
+		is_ntsc_ ^= true;
+		set_is_ntsc(is_ntsc_);
+	}
 
-		void register_crt_frequency_mismatch() {
-			is_ntsc_ ^= true;
-			set_is_ntsc(is_ntsc_);
-		}
+	float get_confidence() final {
+		return confidence_counter_.get_confidence();
+	}
 
-		float get_confidence() final {
-			return confidence_counter_.get_confidence();
-		}
+private:
+	// The bus.
+	std::unique_ptr<Bus> bus_;
 
-	private:
-		// The bus.
-		std::unique_ptr<Bus> bus_;
+	// Output frame rate tracker.
+	Outputs::CRT::CRTFrequencyMismatchWarner<ConcreteMachine> frequency_mismatch_warner_;
+	bool is_ntsc_ = true;
+	std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
 
-		// Output frame rate tracker.
-		Outputs::CRT::CRTFrequencyMismatchWarner<ConcreteMachine> frequency_mismatch_warner_;
-		bool is_ntsc_ = true;
-		std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
+	// a confidence counter
+	Analyser::Dynamic::ConfidenceCounter confidence_counter_;
 
-		// a confidence counter
-		Analyser::Dynamic::ConfidenceCounter confidence_counter_;
-
-		void set_is_ntsc(bool is_ntsc) {
-			bus_->tia_.set_output_mode(is_ntsc ? TIA::OutputMode::NTSC : TIA::OutputMode::PAL);
-			const double clock_rate = is_ntsc ? NTSC_clock_rate : PAL_clock_rate;
-			bus_->speaker_.set_input_rate(float(clock_rate) / float(CPUTicksPerAudioTick));
-			bus_->speaker_.set_high_frequency_cutoff(float(clock_rate) / float(CPUTicksPerAudioTick * 2));
-			set_clock_rate(clock_rate);
-		}
+	void set_is_ntsc(bool is_ntsc) {
+		bus_->tia_.set_output_mode(is_ntsc ? TIA::OutputMode::NTSC : TIA::OutputMode::PAL);
+		const double clock_rate = is_ntsc ? NTSC_clock_rate : PAL_clock_rate;
+		bus_->speaker_.set_input_rate(float(clock_rate) / float(CPUTicksPerAudioTick));
+		bus_->speaker_.set_high_frequency_cutoff(float(clock_rate) / float(CPUTicksPerAudioTick * 2));
+		set_clock_rate(clock_rate);
+	}
 };
 
 }
diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp
index c853acec6..2743933d5 100644
--- a/Machines/Atari/ST/AtariST.cpp
+++ b/Machines/Atari/ST/AtariST.cpp
@@ -64,644 +64,644 @@ class ConcreteMachine:
 	public GI::AY38910::PortHandler,
 	public Configurable::Device,
 	public Video::RangeObserver {
-	public:
-		ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
-			mc68000_(*this),
-			keyboard_acia_(Cycles(500000)),
-			midi_acia_(Cycles(500000)),
-			ay_(GI::AY38910::Personality::YM2149F, audio_queue_),
-			speaker_(ay_),
-			ikbd_(keyboard_acia_->transmit, keyboard_acia_->receive) {
-			set_clock_rate(CLOCK_RATE);
-			speaker_.set_input_rate(float(CLOCK_RATE) / 4.0f);
+public:
+	ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
+		mc68000_(*this),
+		keyboard_acia_(Cycles(500000)),
+		midi_acia_(Cycles(500000)),
+		ay_(GI::AY38910::Personality::YM2149F, audio_queue_),
+		speaker_(ay_),
+		ikbd_(keyboard_acia_->transmit, keyboard_acia_->receive) {
+		set_clock_rate(CLOCK_RATE);
+		speaker_.set_input_rate(float(CLOCK_RATE) / 4.0f);
 
-			switch(target.memory_size) {
-				default:
-				case Target::MemorySize::FiveHundredAndTwelveKilobytes:
-					ram_.resize(512 * 1024);
-				break;
-				case Target::MemorySize::OneMegabyte:
-					ram_.resize(1024 * 1024);
-				break;
-				case Target::MemorySize::FourMegabytes:
-					ram_.resize(4 * 1024 * 1024);
-				break;
-			}
-			Memory::Fuzz(ram_);
+		switch(target.memory_size) {
+			default:
+			case Target::MemorySize::FiveHundredAndTwelveKilobytes:
+				ram_.resize(512 * 1024);
+			break;
+			case Target::MemorySize::OneMegabyte:
+				ram_.resize(1024 * 1024);
+			break;
+			case Target::MemorySize::FourMegabytes:
+				ram_.resize(4 * 1024 * 1024);
+			break;
+		}
+		Memory::Fuzz(ram_);
 
-			video_->set_ram(
-				reinterpret_cast<uint16_t *>(ram_.data()),
-				ram_.size() >> 1
-			);
+		video_->set_ram(
+			reinterpret_cast<uint16_t *>(ram_.data()),
+			ram_.size() >> 1
+		);
 
-			constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100;
-			ROM::Request request(rom_name);
-			auto roms = rom_fetcher(request);
-			if(!request.validate(roms)) {
-				throw ROMMachine::Error::MissingROMs;
-			}
-			Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
+		constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100;
+		ROM::Request request(rom_name);
+		auto roms = rom_fetcher(request);
+		if(!request.validate(roms)) {
+			throw ROMMachine::Error::MissingROMs;
+		}
+		Memory::PackBigEndian16(roms.find(rom_name)->second, rom_);
 
-			// Set up basic memory map.
-			int c = 0;
-			for(; c < int(ram_.size() >> 16); ++c) memory_map_[c] = BusDevice::RAM;
-			for(; c < 0x40; ++c) memory_map_[c] = BusDevice::Floating;
-			for(; c < 0xff; ++c) memory_map_[c] = BusDevice::Unassigned;
+		// Set up basic memory map.
+		int c = 0;
+		for(; c < int(ram_.size() >> 16); ++c) memory_map_[c] = BusDevice::RAM;
+		for(; c < 0x40; ++c) memory_map_[c] = BusDevice::Floating;
+		for(; c < 0xff; ++c) memory_map_[c] = BusDevice::Unassigned;
 
-			const bool is_early_tos = true;
-			if(is_early_tos) {
-				rom_start_ = 0xfc0000;
-				for(c = 0xfc; c < 0xff; ++c) memory_map_[c] = BusDevice::ROM;
+		const bool is_early_tos = true;
+		if(is_early_tos) {
+			rom_start_ = 0xfc0000;
+			for(c = 0xfc; c < 0xff; ++c) memory_map_[c] = BusDevice::ROM;
+		} else {
+			rom_start_ = 0xe00000;
+			for(c = 0xe0; c < 0xe4; ++c) memory_map_[c] = BusDevice::ROM;
+		}
+
+		memory_map_[0xfa] = memory_map_[0xfb] = BusDevice::Cartridge;
+		memory_map_[0xff] = BusDevice::IO;
+
+		// Copy the first 8 bytes of ROM into RAM.
+		reinstall_rom_vector();
+
+		midi_acia_->set_interrupt_delegate(this);
+		keyboard_acia_->set_interrupt_delegate(this);
+
+		midi_acia_->set_clocking_hint_observer(this);
+		keyboard_acia_->set_clocking_hint_observer(this);
+		ikbd_.set_clocking_hint_observer(this);
+		mfp_->set_clocking_hint_observer(this);
+		dma_->set_clocking_hint_observer(this);
+
+		mfp_->set_interrupt_delegate(this);
+		dma_->set_delegate(this);
+		ay_.set_port_handler(this);
+
+		set_gpip_input();
+
+		video_->set_range_observer(this);
+
+		// Insert any supplied media.
+		insert_media(target.media);
+	}
+
+	~ConcreteMachine() {
+		audio_queue_.flush();
+	}
+
+	// MARK: CRTMachine::Machine
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
+		video_->set_scan_target(scan_target);
+	}
+
+	Outputs::Display::ScanStatus get_scaled_scan_status() const final {
+		return video_->get_scaled_scan_status();
+	}
+
+	void set_display_type(Outputs::Display::DisplayType display_type) final {
+		video_->set_display_type(display_type);
+	}
+
+	Outputs::Display::DisplayType get_display_type() const final {
+		return video_->get_display_type();
+	}
+
+	Outputs::Speaker::Speaker *get_speaker() final {
+		return &speaker_;
+	}
+
+	void run_for(const Cycles cycles) final {
+		// Give the keyboard an opportunity to consume any events.
+		if(!keyboard_needs_clock_) {
+			ikbd_.run_for(HalfCycles(0));
+		}
+
+		mc68000_.run_for(cycles);
+	}
+
+	// MARK: MC68000::BusHandler
+	template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
+		// Just in case the last cycle was an interrupt acknowledge or bus error. TODO: find a better solution?
+		mc68000_.set_is_peripheral_address(false);
+		mc68000_.set_bus_error(false);
+
+		// Advance time.
+		advance_time(cycle.length);
+
+		// Check for assertion of reset.
+		if(cycle.operation & CPU::MC68000::Operation::Reset) {
+			logger.error().append("Unhandled Reset");
+		}
+
+		// A null cycle leaves nothing else to do.
+		if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
+
+		// An interrupt acknowledge, perhaps?
+		if(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) {
+			// Current implementation: everything other than 6 (i.e. the MFP) is autovectored.
+			const int interrupt_level = cycle.word_address()&7;
+			if(interrupt_level != 6) {
+				video_interrupts_pending_ &= ~interrupt_level;
+				update_interrupt_input();
+				mc68000_.set_is_peripheral_address(true);
+				return HalfCycles(0);
 			} else {
-				rom_start_ = 0xe00000;
-				for(c = 0xe0; c < 0xe4; ++c) memory_map_[c] = BusDevice::ROM;
-			}
-
-			memory_map_[0xfa] = memory_map_[0xfb] = BusDevice::Cartridge;
-			memory_map_[0xff] = BusDevice::IO;
-
-			// Copy the first 8 bytes of ROM into RAM.
-			reinstall_rom_vector();
-
-			midi_acia_->set_interrupt_delegate(this);
-			keyboard_acia_->set_interrupt_delegate(this);
-
-			midi_acia_->set_clocking_hint_observer(this);
-			keyboard_acia_->set_clocking_hint_observer(this);
-			ikbd_.set_clocking_hint_observer(this);
-			mfp_->set_clocking_hint_observer(this);
-			dma_->set_clocking_hint_observer(this);
-
-			mfp_->set_interrupt_delegate(this);
-			dma_->set_delegate(this);
-			ay_.set_port_handler(this);
-
-			set_gpip_input();
-
-			video_->set_range_observer(this);
-
-			// Insert any supplied media.
-			insert_media(target.media);
-		}
-
-		~ConcreteMachine() {
-			audio_queue_.flush();
-		}
-
-		// MARK: CRTMachine::Machine
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
-			video_->set_scan_target(scan_target);
-		}
-
-		Outputs::Display::ScanStatus get_scaled_scan_status() const final {
-			return video_->get_scaled_scan_status();
-		}
-
-		void set_display_type(Outputs::Display::DisplayType display_type) final {
-			video_->set_display_type(display_type);
-		}
-
-		Outputs::Display::DisplayType get_display_type() const final {
-			return video_->get_display_type();
-		}
-
-		Outputs::Speaker::Speaker *get_speaker() final {
-			return &speaker_;
-		}
-
-		void run_for(const Cycles cycles) final {
-			// Give the keyboard an opportunity to consume any events.
-			if(!keyboard_needs_clock_) {
-				ikbd_.run_for(HalfCycles(0));
-			}
-
-			mc68000_.run_for(cycles);
-		}
-
-		// MARK: MC68000::BusHandler
-		template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
-			// Just in case the last cycle was an interrupt acknowledge or bus error. TODO: find a better solution?
-			mc68000_.set_is_peripheral_address(false);
-			mc68000_.set_bus_error(false);
-
-			// Advance time.
-			advance_time(cycle.length);
-
-			// Check for assertion of reset.
-			if(cycle.operation & CPU::MC68000::Operation::Reset) {
-				logger.error().append("Unhandled Reset");
-			}
-
-			// A null cycle leaves nothing else to do.
-			if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
-
-			// An interrupt acknowledge, perhaps?
-			if(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) {
-				// Current implementation: everything other than 6 (i.e. the MFP) is autovectored.
-				const int interrupt_level = cycle.word_address()&7;
-				if(interrupt_level != 6) {
-					video_interrupts_pending_ &= ~interrupt_level;
-					update_interrupt_input();
-					mc68000_.set_is_peripheral_address(true);
-					return HalfCycles(0);
-				} else {
-					if(cycle.operation & CPU::MC68000::Operation::SelectByte) {
-						const int interrupt = mfp_->acknowledge_interrupt();
-						if(interrupt != Motorola::MFP68901::MFP68901::NoAcknowledgement) {
-							cycle.value->b = uint8_t(interrupt);
-						} else {
-							// TODO: this should take a while. Find out how long.
-							mc68000_.set_bus_error(true);
-						}
-					}
-					return HalfCycles(0);
-				}
-			}
-
-			auto address = cycle.host_endian_byte_address();
-
-			// If this is a new strobing of the address signal, test for bus error and pre-DTack delay.
-			HalfCycles delay(0);
-			if(cycle.operation & CPU::MC68000::Operation::NewAddress) {
-				// Bus error test.
-				if(
-					// Anything unassigned should generate a bus error.
-					(memory_map_[address >> 16] == BusDevice::Unassigned) ||
-
-					// Bus errors also apply to unprivileged access to the first 0x800 bytes, or the IO area.
-					(!is_supervisor && (address < 0x800 || memory_map_[address >> 16] == BusDevice::IO))
-				) {
-					mc68000_.set_bus_error(true);
-					return delay;	// TODO: there should be an extra delay here.
-				}
-
-				// DTack delay rule: if accessing RAM or the shifter, align with the two cycles next available
-				// for the CPU to access that side of the bus.
-				if(address < ram_.size() || (address == 0xff8260)) {
-					// DTack will be implicit; work out how long until that should be,
-					// and apply bus error constraints.
-					const int i_phase = bus_phase_.as<int>() & 7;
-					if(i_phase < 4) {
-						delay = HalfCycles(4 - i_phase);
-						advance_time(delay);
+				if(cycle.operation & CPU::MC68000::Operation::SelectByte) {
+					const int interrupt = mfp_->acknowledge_interrupt();
+					if(interrupt != Motorola::MFP68901::MFP68901::NoAcknowledgement) {
+						cycle.value->b = uint8_t(interrupt);
+					} else {
+						// TODO: this should take a while. Find out how long.
+						mc68000_.set_bus_error(true);
 					}
 				}
-			}
-
-			uint8_t *memory = nullptr;
-			switch(memory_map_[address >> 16]) {
-				default:
-				case BusDevice::RAM:
-					memory = ram_.data();
-				break;
-
-				case BusDevice::ROM:
-					memory = rom_.data();
-					if(!(cycle.operation & CPU::MC68000::Operation::Read)) {
-						return delay;
-					}
-					address -= rom_start_;
-				break;
-
-				case BusDevice::Floating:
-					// TODO: provide vapour reads here. But: will these always be of the last video fetch?
-				case BusDevice::Unassigned:
-				case BusDevice::Cartridge:
-					/*
-						TOS 1.0 appears to attempt to read from the catridge before it has setup
-						the bus error vector. Therefore I assume no bus error flows.
-					*/
-					switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
-						default: break;
-						case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
-							cycle.value->w = 0xffff;
-						break;
-						case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
-							cycle.value->b = 0xff;
-						break;
-					}
-				return delay;
-
-				case BusDevice::IO:
-					switch(address & 0xfffe) {	// TODO: surely it's going to be even less precise than this?
-						default:
-//							assert(false);
-
-						case 0x8000:
-							/* Memory controller configuration:
-									b0, b1: bank 1
-									b2, b3: bank 0
-
-									00 = 128k
-									01 = 512k
-									10 = 2mb
-									11 = reserved
-							*/
-						break;
-
-						// Video controls.
-						case 0x8200:	case 0x8202:	case 0x8204:	case 0x8206:
-						case 0x8208:	case 0x820a:	case 0x820c:	case 0x820e:
-						case 0x8210:	case 0x8212:	case 0x8214:	case 0x8216:
-						case 0x8218:	case 0x821a:	case 0x821c:	case 0x821e:
-						case 0x8220:	case 0x8222:	case 0x8224:	case 0x8226:
-						case 0x8228:	case 0x822a:	case 0x822c:	case 0x822e:
-						case 0x8230:	case 0x8232:	case 0x8234:	case 0x8236:
-						case 0x8238:	case 0x823a:	case 0x823c:	case 0x823e:
-						case 0x8240:	case 0x8242:	case 0x8244:	case 0x8246:
-						case 0x8248:	case 0x824a:	case 0x824c:	case 0x824e:
-						case 0x8250:	case 0x8252:	case 0x8254:	case 0x8256:
-						case 0x8258:	case 0x825a:	case 0x825c:	case 0x825e:
-						case 0x8260:	case 0x8262:
-							if(!cycle.data_select_active()) return delay;
-
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value16(video_->read(int(address >> 1)));
-							} else {
-								video_->write(int(address >> 1), cycle.value16());
-							}
-						break;
-
-						// DMA.
-						case 0x8604:	case 0x8606:	case 0x8608:	case 0x860a:	case 0x860c:
-							if(!cycle.data_select_active()) return delay;
-
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value16(dma_->read(int(address >> 1)));
-							} else {
-								dma_->write(int(address >> 1), cycle.value16());
-							}
-						break;
-
-						// Audio.
-						//
-						// Re: mirrors, Dan Hollis' hardware register list asserts:
-						//
-						// "Note: PSG Registers are now fixed at these addresses. All other addresses are masked out on the Falcon. Any
-						// writes to the shadow registers $8804-$88FF will cause bus errors.", which I am taking to imply that those shadow
-						// registers exist on the Atari ST.
-						case 0x8800: case 0x8802: case 0x8804: case 0x8806: case 0x8808: case 0x880a: case 0x880c: case 0x880e:
-						case 0x8810: case 0x8812: case 0x8814: case 0x8816: case 0x8818: case 0x881a: case 0x881c: case 0x881e:
-						case 0x8820: case 0x8822: case 0x8824: case 0x8826: case 0x8828: case 0x882a: case 0x882c: case 0x882e:
-						case 0x8830: case 0x8832: case 0x8834: case 0x8836: case 0x8838: case 0x883a: case 0x883c: case 0x883e:
-						case 0x8840: case 0x8842: case 0x8844: case 0x8846: case 0x8848: case 0x884a: case 0x884c: case 0x884e:
-						case 0x8850: case 0x8852: case 0x8854: case 0x8856: case 0x8858: case 0x885a: case 0x885c: case 0x885e:
-						case 0x8860: case 0x8862: case 0x8864: case 0x8866: case 0x8868: case 0x886a: case 0x886c: case 0x886e:
-						case 0x8870: case 0x8872: case 0x8874: case 0x8876: case 0x8878: case 0x887a: case 0x887c: case 0x887e:
-						case 0x8880: case 0x8882: case 0x8884: case 0x8886: case 0x8888: case 0x888a: case 0x888c: case 0x888e:
-						case 0x8890: case 0x8892: case 0x8894: case 0x8896: case 0x8898: case 0x889a: case 0x889c: case 0x889e:
-						case 0x88a0: case 0x88a2: case 0x88a4: case 0x88a6: case 0x88a8: case 0x88aa: case 0x88ac: case 0x88ae:
-						case 0x88b0: case 0x88b2: case 0x88b4: case 0x88b6: case 0x88b8: case 0x88ba: case 0x88bc: case 0x88be:
-						case 0x88c0: case 0x88c2: case 0x88c4: case 0x88c6: case 0x88c8: case 0x88ca: case 0x88cc: case 0x88ce:
-						case 0x88d0: case 0x88d2: case 0x88d4: case 0x88d6: case 0x88d8: case 0x88da: case 0x88dc: case 0x88de:
-						case 0x88e0: case 0x88e2: case 0x88e4: case 0x88e6: case 0x88e8: case 0x88ea: case 0x88ec: case 0x88ee:
-						case 0x88f0: case 0x88f2: case 0x88f4: case 0x88f6: case 0x88f8: case 0x88fa: case 0x88fc: case 0x88fe:
-							if(!cycle.data_select_active()) return delay;
-
-							advance_time(HalfCycles(2));
-							update_audio();
-
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
-							} else {
-								// Net effect here: addresses with bit 1 set write to a register,
-								// addresses with bit 1 clear select a register.
-								GI::AY38910::Utility::write(ay_, address&2, cycle.value8_high());
-							}
-						return delay + HalfCycles(2);
-
-						// The MFP block:
-						case 0xfa00:	case 0xfa02:	case 0xfa04:	case 0xfa06:
-						case 0xfa08:	case 0xfa0a:	case 0xfa0c:	case 0xfa0e:
-						case 0xfa10:	case 0xfa12:	case 0xfa14:	case 0xfa16:
-						case 0xfa18:	case 0xfa1a:	case 0xfa1c:	case 0xfa1e:
-						case 0xfa20:	case 0xfa22:	case 0xfa24:	case 0xfa26:
-						case 0xfa28:	case 0xfa2a:	case 0xfa2c:	case 0xfa2e:
-						case 0xfa30:	case 0xfa32:	case 0xfa34:	case 0xfa36:
-						case 0xfa38:	case 0xfa3a:	case 0xfa3c:	case 0xfa3e:
-							if(!cycle.data_select_active()) return delay;
-
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value8_low(mfp_->read(int(address >> 1)));
-							} else {
-								mfp_->write(int(address >> 1), cycle.value8_low());
-							}
-						break;
-
-						// ACIAs.
-						case 0xfc00:	case 0xfc02:	case 0xfc04:	case 0xfc06: {
-							// Set VPA.
-							mc68000_.set_is_peripheral_address(!cycle.data_select_active());
-							if(!cycle.data_select_active()) return delay;
-
-							const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
-							if(cycle.operation & CPU::MC68000::Operation::Read) {
-								cycle.set_value8_high((*acia_)->read(int(address >> 1)));
-							} else {
-								(*acia_)->write(int(address >> 1), cycle.value8_high());
-							}
-						} break;
-					}
 				return HalfCycles(0);
 			}
+		}
 
-			// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
-			//
-			// In both write cases, immediately reinstall the first eight bytes of RAM from ROM, so that any write to
-			// that area is in effect a no-op. This is cheaper than the conditionality of actually checking.
-			switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
-				default:
-				break;
+		auto address = cycle.host_endian_byte_address();
 
-				case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
-					cycle.value->w = *reinterpret_cast<uint16_t *>(&memory[address]);
-				break;
-				case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
-					cycle.value->b = memory[address];
-				break;
-				case CPU::MC68000::Operation::SelectWord:
-					if(address >= video_range_.low_address && address < video_range_.high_address)
-						video_.flush();
-					*reinterpret_cast<uint16_t *>(&memory[address]) = cycle.value->w;
-					reinstall_rom_vector();
-				break;
-				case CPU::MC68000::Operation::SelectByte:
-					if(address >= video_range_.low_address && address < video_range_.high_address)
-						video_.flush();
-					memory[address] = cycle.value->b;
-					reinstall_rom_vector();
-				break;
+		// If this is a new strobing of the address signal, test for bus error and pre-DTack delay.
+		HalfCycles delay(0);
+		if(cycle.operation & CPU::MC68000::Operation::NewAddress) {
+			// Bus error test.
+			if(
+				// Anything unassigned should generate a bus error.
+				(memory_map_[address >> 16] == BusDevice::Unassigned) ||
+
+				// Bus errors also apply to unprivileged access to the first 0x800 bytes, or the IO area.
+				(!is_supervisor && (address < 0x800 || memory_map_[address >> 16] == BusDevice::IO))
+			) {
+				mc68000_.set_bus_error(true);
+				return delay;	// TODO: there should be an extra delay here.
 			}
 
+			// DTack delay rule: if accessing RAM or the shifter, align with the two cycles next available
+			// for the CPU to access that side of the bus.
+			if(address < ram_.size() || (address == 0xff8260)) {
+				// DTack will be implicit; work out how long until that should be,
+				// and apply bus error constraints.
+				const int i_phase = bus_phase_.as<int>() & 7;
+				if(i_phase < 4) {
+					delay = HalfCycles(4 - i_phase);
+					advance_time(delay);
+				}
+			}
+		}
+
+		uint8_t *memory = nullptr;
+		switch(memory_map_[address >> 16]) {
+			default:
+			case BusDevice::RAM:
+				memory = ram_.data();
+			break;
+
+			case BusDevice::ROM:
+				memory = rom_.data();
+				if(!(cycle.operation & CPU::MC68000::Operation::Read)) {
+					return delay;
+				}
+				address -= rom_start_;
+			break;
+
+			case BusDevice::Floating:
+				// TODO: provide vapour reads here. But: will these always be of the last video fetch?
+			case BusDevice::Unassigned:
+			case BusDevice::Cartridge:
+				/*
+					TOS 1.0 appears to attempt to read from the catridge before it has setup
+					the bus error vector. Therefore I assume no bus error flows.
+				*/
+				switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
+					default: break;
+					case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
+						cycle.value->w = 0xffff;
+					break;
+					case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
+						cycle.value->b = 0xff;
+					break;
+				}
+			return delay;
+
+			case BusDevice::IO:
+				switch(address & 0xfffe) {	// TODO: surely it's going to be even less precise than this?
+					default:
+//							assert(false);
+
+					case 0x8000:
+						/* Memory controller configuration:
+								b0, b1: bank 1
+								b2, b3: bank 0
+
+								00 = 128k
+								01 = 512k
+								10 = 2mb
+								11 = reserved
+						*/
+					break;
+
+					// Video controls.
+					case 0x8200:	case 0x8202:	case 0x8204:	case 0x8206:
+					case 0x8208:	case 0x820a:	case 0x820c:	case 0x820e:
+					case 0x8210:	case 0x8212:	case 0x8214:	case 0x8216:
+					case 0x8218:	case 0x821a:	case 0x821c:	case 0x821e:
+					case 0x8220:	case 0x8222:	case 0x8224:	case 0x8226:
+					case 0x8228:	case 0x822a:	case 0x822c:	case 0x822e:
+					case 0x8230:	case 0x8232:	case 0x8234:	case 0x8236:
+					case 0x8238:	case 0x823a:	case 0x823c:	case 0x823e:
+					case 0x8240:	case 0x8242:	case 0x8244:	case 0x8246:
+					case 0x8248:	case 0x824a:	case 0x824c:	case 0x824e:
+					case 0x8250:	case 0x8252:	case 0x8254:	case 0x8256:
+					case 0x8258:	case 0x825a:	case 0x825c:	case 0x825e:
+					case 0x8260:	case 0x8262:
+						if(!cycle.data_select_active()) return delay;
+
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value16(video_->read(int(address >> 1)));
+						} else {
+							video_->write(int(address >> 1), cycle.value16());
+						}
+					break;
+
+					// DMA.
+					case 0x8604:	case 0x8606:	case 0x8608:	case 0x860a:	case 0x860c:
+						if(!cycle.data_select_active()) return delay;
+
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value16(dma_->read(int(address >> 1)));
+						} else {
+							dma_->write(int(address >> 1), cycle.value16());
+						}
+					break;
+
+					// Audio.
+					//
+					// Re: mirrors, Dan Hollis' hardware register list asserts:
+					//
+					// "Note: PSG Registers are now fixed at these addresses. All other addresses are masked out on the Falcon. Any
+					// writes to the shadow registers $8804-$88FF will cause bus errors.", which I am taking to imply that those shadow
+					// registers exist on the Atari ST.
+					case 0x8800: case 0x8802: case 0x8804: case 0x8806: case 0x8808: case 0x880a: case 0x880c: case 0x880e:
+					case 0x8810: case 0x8812: case 0x8814: case 0x8816: case 0x8818: case 0x881a: case 0x881c: case 0x881e:
+					case 0x8820: case 0x8822: case 0x8824: case 0x8826: case 0x8828: case 0x882a: case 0x882c: case 0x882e:
+					case 0x8830: case 0x8832: case 0x8834: case 0x8836: case 0x8838: case 0x883a: case 0x883c: case 0x883e:
+					case 0x8840: case 0x8842: case 0x8844: case 0x8846: case 0x8848: case 0x884a: case 0x884c: case 0x884e:
+					case 0x8850: case 0x8852: case 0x8854: case 0x8856: case 0x8858: case 0x885a: case 0x885c: case 0x885e:
+					case 0x8860: case 0x8862: case 0x8864: case 0x8866: case 0x8868: case 0x886a: case 0x886c: case 0x886e:
+					case 0x8870: case 0x8872: case 0x8874: case 0x8876: case 0x8878: case 0x887a: case 0x887c: case 0x887e:
+					case 0x8880: case 0x8882: case 0x8884: case 0x8886: case 0x8888: case 0x888a: case 0x888c: case 0x888e:
+					case 0x8890: case 0x8892: case 0x8894: case 0x8896: case 0x8898: case 0x889a: case 0x889c: case 0x889e:
+					case 0x88a0: case 0x88a2: case 0x88a4: case 0x88a6: case 0x88a8: case 0x88aa: case 0x88ac: case 0x88ae:
+					case 0x88b0: case 0x88b2: case 0x88b4: case 0x88b6: case 0x88b8: case 0x88ba: case 0x88bc: case 0x88be:
+					case 0x88c0: case 0x88c2: case 0x88c4: case 0x88c6: case 0x88c8: case 0x88ca: case 0x88cc: case 0x88ce:
+					case 0x88d0: case 0x88d2: case 0x88d4: case 0x88d6: case 0x88d8: case 0x88da: case 0x88dc: case 0x88de:
+					case 0x88e0: case 0x88e2: case 0x88e4: case 0x88e6: case 0x88e8: case 0x88ea: case 0x88ec: case 0x88ee:
+					case 0x88f0: case 0x88f2: case 0x88f4: case 0x88f6: case 0x88f8: case 0x88fa: case 0x88fc: case 0x88fe:
+						if(!cycle.data_select_active()) return delay;
+
+						advance_time(HalfCycles(2));
+						update_audio();
+
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
+						} else {
+							// Net effect here: addresses with bit 1 set write to a register,
+							// addresses with bit 1 clear select a register.
+							GI::AY38910::Utility::write(ay_, address&2, cycle.value8_high());
+						}
+					return delay + HalfCycles(2);
+
+					// The MFP block:
+					case 0xfa00:	case 0xfa02:	case 0xfa04:	case 0xfa06:
+					case 0xfa08:	case 0xfa0a:	case 0xfa0c:	case 0xfa0e:
+					case 0xfa10:	case 0xfa12:	case 0xfa14:	case 0xfa16:
+					case 0xfa18:	case 0xfa1a:	case 0xfa1c:	case 0xfa1e:
+					case 0xfa20:	case 0xfa22:	case 0xfa24:	case 0xfa26:
+					case 0xfa28:	case 0xfa2a:	case 0xfa2c:	case 0xfa2e:
+					case 0xfa30:	case 0xfa32:	case 0xfa34:	case 0xfa36:
+					case 0xfa38:	case 0xfa3a:	case 0xfa3c:	case 0xfa3e:
+						if(!cycle.data_select_active()) return delay;
+
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value8_low(mfp_->read(int(address >> 1)));
+						} else {
+							mfp_->write(int(address >> 1), cycle.value8_low());
+						}
+					break;
+
+					// ACIAs.
+					case 0xfc00:	case 0xfc02:	case 0xfc04:	case 0xfc06: {
+						// Set VPA.
+						mc68000_.set_is_peripheral_address(!cycle.data_select_active());
+						if(!cycle.data_select_active()) return delay;
+
+						const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
+						if(cycle.operation & CPU::MC68000::Operation::Read) {
+							cycle.set_value8_high((*acia_)->read(int(address >> 1)));
+						} else {
+							(*acia_)->write(int(address >> 1), cycle.value8_high());
+						}
+					} break;
+				}
 			return HalfCycles(0);
 		}
 
-		void reinstall_rom_vector() {
-			std::copy(rom_.begin(), rom_.begin() + 8, ram_.begin());
+		// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
+		//
+		// In both write cases, immediately reinstall the first eight bytes of RAM from ROM, so that any write to
+		// that area is in effect a no-op. This is cheaper than the conditionality of actually checking.
+		switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
+			default:
+			break;
+
+			case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
+				cycle.value->w = *reinterpret_cast<uint16_t *>(&memory[address]);
+			break;
+			case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
+				cycle.value->b = memory[address];
+			break;
+			case CPU::MC68000::Operation::SelectWord:
+				if(address >= video_range_.low_address && address < video_range_.high_address)
+					video_.flush();
+				*reinterpret_cast<uint16_t *>(&memory[address]) = cycle.value->w;
+				reinstall_rom_vector();
+			break;
+			case CPU::MC68000::Operation::SelectByte:
+				if(address >= video_range_.low_address && address < video_range_.high_address)
+					video_.flush();
+				memory[address] = cycle.value->b;
+				reinstall_rom_vector();
+			break;
 		}
 
-		void flush_output(int outputs) final {
-			dma_.flush();
-			mfp_.flush();
+		return HalfCycles(0);
+	}
+
+	void reinstall_rom_vector() {
+		std::copy(rom_.begin(), rom_.begin() + 8, ram_.begin());
+	}
+
+	void flush_output(int outputs) final {
+		dma_.flush();
+		mfp_.flush();
+		keyboard_acia_.flush();
+		midi_acia_.flush();
+
+		if(outputs & Output::Video) {
+			video_.flush();
+		}
+		if(outputs & Output::Audio) {
+			update_audio();
+			audio_queue_.perform();
+		}
+	}
+
+private:
+	forceinline void advance_time(HalfCycles length) {
+		// Advance the relevant counters.
+		cycles_since_audio_update_ += length;
+		mfp_ += length;
+		if(dma_clocking_preference_ != ClockingHint::Preference::None)
+			dma_ += length;
+		keyboard_acia_ += length;
+		midi_acia_ += length;
+		bus_phase_ += length;
+
+		// Don't even count time for the keyboard unless it has requested it.
+		if(keyboard_needs_clock_) {
+			cycles_since_ikbd_update_ += length;
+			ikbd_.run_for(cycles_since_ikbd_update_.divide(HalfCycles(512)));
+		}
+
+		// Flush anything that needs real-time updating.
+		if(!may_defer_acias_) {
 			keyboard_acia_.flush();
 			midi_acia_.flush();
-
-			if(outputs & Output::Video) {
-				video_.flush();
-			}
-			if(outputs & Output::Audio) {
-				update_audio();
-				audio_queue_.perform();
-			}
 		}
 
-	private:
-		forceinline void advance_time(HalfCycles length) {
-			// Advance the relevant counters.
-			cycles_since_audio_update_ += length;
-			mfp_ += length;
-			if(dma_clocking_preference_ != ClockingHint::Preference::None)
-				dma_ += length;
-			keyboard_acia_ += length;
-			midi_acia_ += length;
-			bus_phase_ += length;
-
-			// Don't even count time for the keyboard unless it has requested it.
-			if(keyboard_needs_clock_) {
-				cycles_since_ikbd_update_ += length;
-				ikbd_.run_for(cycles_since_ikbd_update_.divide(HalfCycles(512)));
-			}
-
-			// Flush anything that needs real-time updating.
-			if(!may_defer_acias_) {
-				keyboard_acia_.flush();
-				midi_acia_.flush();
-			}
-
-			if(mfp_is_realtime_) {
-				mfp_.flush();
-			}
-
-			if(dma_clocking_preference_ == ClockingHint::Preference::RealTime) {
-				dma_.flush();
-			}
-
-			// Update the video output, checking whether a sequence point has been hit.
-			if(video_.will_flush(length)) {
-				length -= video_.cycles_until_implicit_flush();
-				video_ += video_.cycles_until_implicit_flush();
-
-				mfp_->set_timer_event_input<1>(video_->display_enabled());
-				update_interrupt_input();
-			}
-
-			video_ += length;
+		if(mfp_is_realtime_) {
+			mfp_.flush();
 		}
 
-		void update_audio() {
-			speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide_cycles(Cycles(4)));
+		if(dma_clocking_preference_ == ClockingHint::Preference::RealTime) {
+			dma_.flush();
 		}
 
-		CPU::MC68000::Processor<ConcreteMachine, true, true> mc68000_;
-		HalfCycles bus_phase_;
+		// Update the video output, checking whether a sequence point has been hit.
+		if(video_.will_flush(length)) {
+			length -= video_.cycles_until_implicit_flush();
+			video_ += video_.cycles_until_implicit_flush();
 
-		JustInTimeActor<Video> video_;
-
-		// The MFP runs at 819200/2673749ths of the CPU clock rate.
-		JustInTimeActor<Motorola::MFP68901::MFP68901, HalfCycles, 819200, 2673749> mfp_;
-		JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> keyboard_acia_;
-		JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> midi_acia_;
-
-		Concurrency::AsyncTaskQueue<false> audio_queue_;
-		GI::AY38910::AY38910<false> ay_;
-		Outputs::Speaker::PullLowpass<GI::AY38910::AY38910<false>> speaker_;
-		HalfCycles cycles_since_audio_update_;
-
-		JustInTimeActor<DMAController> dma_;
-
-		HalfCycles cycles_since_ikbd_update_;
-		IntelligentKeyboard ikbd_;
-
-		std::vector<uint8_t> ram_;
-		std::vector<uint8_t> rom_;
-		uint32_t rom_start_ = 0;
-
-		enum class BusDevice {
-			/// Allows reads and writes to ram_.
-			RAM,
-			/// Nothing is mapped to this area, and it also doesn't trigger an exception upon access.
-			Floating,
-			/// Allows reading from rom_; writes do nothing.
-			ROM,
-			/// Allows interaction with a cartrige_.
-			Cartridge,
-			/// Marks the IO page, in which finer decoding will occur.
-			IO,
-			/// An unassigned page has nothing below it, in a way that triggers exceptions.
-			Unassigned
-		};
-		BusDevice memory_map_[256];
-
-		// MARK: - Clocking Management.
-		bool may_defer_acias_ = true;
-		bool keyboard_needs_clock_ = false;
-		bool mfp_is_realtime_ = false;
-		ClockingHint::Preference dma_clocking_preference_ = ClockingHint::Preference::None;
-		void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final {
-			// This is being called by one of the components; avoid any time flushing here as that's
-			// already dealt with (and, just to be absolutely sure, to avoid recursive mania).
-			may_defer_acias_ =
-				(keyboard_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime) &&
-				(midi_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime);
-			keyboard_needs_clock_ = ikbd_.preferred_clocking() != ClockingHint::Preference::None;
-			mfp_is_realtime_ = mfp_.last_valid()->preferred_clocking() == ClockingHint::Preference::RealTime;
-			dma_clocking_preference_ = dma_.last_valid()->preferred_clocking();
-		}
-
-		// MARK: - GPIP input.
-		void acia6850_did_change_interrupt_status(Motorola::ACIA::ACIA *) final {
-			set_gpip_input();
-		}
-		void dma_controller_did_change_output(DMAController *) final {
-			set_gpip_input();
-
-			// Filty hack, here! Should: set the 68000's bus request line. But until
-			// that's implemented, just offers magical zero-cost DMA insertion and
-			// extrication.
-			if(dma_->get_bus_request_line()) {
-				dma_->bus_grant(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size() >> 1);
-			}
-		}
-		void set_gpip_input() {
-			/*
-				Atari ST GPIP bits:
-
-					GPIP 7: monochrome monitor detect
-					GPIP 6: RS-232 ring indicator
-					GPIP 5: FD/HD interrupt
-					GPIP 4: keyboard/MIDI interrupt
-					GPIP 3: unused
-					GPIP 2: RS-232 clear to send
-					GPIP 1: RS-232 carrier detect
-					GPIP 0: centronics busy
-			*/
-			mfp_->set_port_input(
-				0x80 |	// b7: Monochrome monitor detect (0 = is monochrome).
-				0x40 |	// b6: RS-232 ring indicator.
-				(dma_->get_interrupt_line() ? 0x00 : 0x20) |	// b5: FD/HS interrupt (0 = interrupt requested).
-				((keyboard_acia_->get_interrupt_line() || midi_acia_->get_interrupt_line()) ? 0x00 : 0x10) |	// b4: Keyboard/MIDI interrupt (0 = interrupt requested).
-				0x08 |	// b3: Unused
-				0x04 |	// b2: RS-232 clear to send.
-				0x02 |	// b1 : RS-232 carrier detect.
-				0x00	// b0: Centronics busy (1 = busy).
-			);
-		}
-
-		// MARK - MFP input.
-		void mfp68901_did_change_interrupt_status(Motorola::MFP68901::MFP68901 *) final {
+			mfp_->set_timer_event_input<1>(video_->display_enabled());
 			update_interrupt_input();
 		}
 
-		int video_interrupts_pending_ = 0;
-		bool previous_hsync_ = false, previous_vsync_ = false;
-		void update_interrupt_input() {
-			// Complete guess: set video interrupts pending if/when hsync of vsync
-			// go inactive. Reset upon IACK.
-			const bool hsync = video_.last_valid()->hsync();
-			const bool vsync = video_.last_valid()->vsync();
-			if(previous_hsync_ != hsync && previous_hsync_) {
-				video_interrupts_pending_ |= 2;
-			}
-			if(previous_vsync_ != vsync && previous_vsync_) {
-				video_interrupts_pending_ |= 4;
-			}
-			previous_vsync_ = vsync;
-			previous_hsync_ = hsync;
+		video_ += length;
+	}
 
-			if(mfp_->get_interrupt_line()) {
-				mc68000_.set_interrupt_level(6);
-			} else if(video_interrupts_pending_ & 4) {
-				mc68000_.set_interrupt_level(4);
-			} else if(video_interrupts_pending_ & 2) {
-				mc68000_.set_interrupt_level(2);
-			} else {
-				mc68000_.set_interrupt_level(0);
-			}
-		}
+	void update_audio() {
+		speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide_cycles(Cycles(4)));
+	}
 
-		// MARK: - MouseMachine
-		Inputs::Mouse &get_mouse() final {
-			return ikbd_;
-		}
+	CPU::MC68000::Processor<ConcreteMachine, true, true> mc68000_;
+	HalfCycles bus_phase_;
 
-		// MARK: - KeyboardMachine
-		void set_key_state(uint16_t key, bool is_pressed) final {
-			ikbd_.set_key_state(Key(key), is_pressed);
-		}
+	JustInTimeActor<Video> video_;
 
-		IntelligentKeyboard::KeyboardMapper keyboard_mapper_;
-		KeyboardMapper *get_keyboard_mapper() final {
-			return &keyboard_mapper_;
-		}
+	// The MFP runs at 819200/2673749ths of the CPU clock rate.
+	JustInTimeActor<Motorola::MFP68901::MFP68901, HalfCycles, 819200, 2673749> mfp_;
+	JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> keyboard_acia_;
+	JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> midi_acia_;
 
-		// MARK: - JoystickMachine
-		const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
-			return ikbd_.get_joysticks();
-		}
+	Concurrency::AsyncTaskQueue<false> audio_queue_;
+	GI::AY38910::AY38910<false> ay_;
+	Outputs::Speaker::PullLowpass<GI::AY38910::AY38910<false>> speaker_;
+	HalfCycles cycles_since_audio_update_;
 
-		// MARK: - AYPortHandler
-		void set_port_output(bool port_b, uint8_t value) final {
-			if(port_b) {
-				// TODO: ?
-			} else {
-				/*
-					Port A:
-						b7: reserved
-						b6: "freely usable output (monitor jack)"
-						b5: centronics strobe
-						b4: RS-232 DTR output
-						b3: RS-232 RTS output
-						b2: select floppy drive 1
-						b1: select floppy drive 0
-						b0: "page choice signal for double-sided floppy drive"
-				*/
-				dma_->set_floppy_drive_selection(!(value & 2), !(value & 4), !(value & 1));
-			}
-		}
+	JustInTimeActor<DMAController> dma_;
 
-		// MARK: - MediaTarget
-		bool insert_media(const Analyser::Static::Media &media) final {
-			size_t c = 0;
-			for(const auto &disk: media.disks) {
-				dma_->set_floppy_disk(disk, c);
-				++c;
-				if(c == 2) break;
-			}
-			return true;
-		}
+	HalfCycles cycles_since_ikbd_update_;
+	IntelligentKeyboard ikbd_;
 
-		// MARK: - Activity Source
-		void set_activity_observer(Activity::Observer *observer) final {
-			dma_->set_activity_observer(observer);
-		}
+	std::vector<uint8_t> ram_;
+	std::vector<uint8_t> rom_;
+	uint32_t rom_start_ = 0;
 
-		// MARK: - Video Range
-		Video::Range video_range_;
-		void video_did_change_access_range(Video *video) final {
-			video_range_ = video->get_memory_access_range();
-		}
+	enum class BusDevice {
+		/// Allows reads and writes to ram_.
+		RAM,
+		/// Nothing is mapped to this area, and it also doesn't trigger an exception upon access.
+		Floating,
+		/// Allows reading from rom_; writes do nothing.
+		ROM,
+		/// Allows interaction with a cartrige_.
+		Cartridge,
+		/// Marks the IO page, in which finer decoding will occur.
+		IO,
+		/// An unassigned page has nothing below it, in a way that triggers exceptions.
+		Unassigned
+	};
+	BusDevice memory_map_[256];
 
-		// MARK: - Configuration options.
-		std::unique_ptr<Reflection::Struct> get_options() const final {
-			auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
-			options->output = get_video_signal_configurable();
-			return options;
-		}
+	// MARK: - Clocking Management.
+	bool may_defer_acias_ = true;
+	bool keyboard_needs_clock_ = false;
+	bool mfp_is_realtime_ = false;
+	ClockingHint::Preference dma_clocking_preference_ = ClockingHint::Preference::None;
+	void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final {
+		// This is being called by one of the components; avoid any time flushing here as that's
+		// already dealt with (and, just to be absolutely sure, to avoid recursive mania).
+		may_defer_acias_ =
+			(keyboard_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime) &&
+			(midi_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime);
+		keyboard_needs_clock_ = ikbd_.preferred_clocking() != ClockingHint::Preference::None;
+		mfp_is_realtime_ = mfp_.last_valid()->preferred_clocking() == ClockingHint::Preference::RealTime;
+		dma_clocking_preference_ = dma_.last_valid()->preferred_clocking();
+	}
 
-		void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
-			const auto options = dynamic_cast<Options *>(str.get());
-			set_video_signal_configurable(options->output);
+	// MARK: - GPIP input.
+	void acia6850_did_change_interrupt_status(Motorola::ACIA::ACIA *) final {
+		set_gpip_input();
+	}
+	void dma_controller_did_change_output(DMAController *) final {
+		set_gpip_input();
+
+		// Filty hack, here! Should: set the 68000's bus request line. But until
+		// that's implemented, just offers magical zero-cost DMA insertion and
+		// extrication.
+		if(dma_->get_bus_request_line()) {
+			dma_->bus_grant(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size() >> 1);
 		}
+	}
+	void set_gpip_input() {
+		/*
+			Atari ST GPIP bits:
+
+				GPIP 7: monochrome monitor detect
+				GPIP 6: RS-232 ring indicator
+				GPIP 5: FD/HD interrupt
+				GPIP 4: keyboard/MIDI interrupt
+				GPIP 3: unused
+				GPIP 2: RS-232 clear to send
+				GPIP 1: RS-232 carrier detect
+				GPIP 0: centronics busy
+		*/
+		mfp_->set_port_input(
+			0x80 |	// b7: Monochrome monitor detect (0 = is monochrome).
+			0x40 |	// b6: RS-232 ring indicator.
+			(dma_->get_interrupt_line() ? 0x00 : 0x20) |	// b5: FD/HS interrupt (0 = interrupt requested).
+			((keyboard_acia_->get_interrupt_line() || midi_acia_->get_interrupt_line()) ? 0x00 : 0x10) |	// b4: Keyboard/MIDI interrupt (0 = interrupt requested).
+			0x08 |	// b3: Unused
+			0x04 |	// b2: RS-232 clear to send.
+			0x02 |	// b1 : RS-232 carrier detect.
+			0x00	// b0: Centronics busy (1 = busy).
+		);
+	}
+
+	// MARK - MFP input.
+	void mfp68901_did_change_interrupt_status(Motorola::MFP68901::MFP68901 *) final {
+		update_interrupt_input();
+	}
+
+	int video_interrupts_pending_ = 0;
+	bool previous_hsync_ = false, previous_vsync_ = false;
+	void update_interrupt_input() {
+		// Complete guess: set video interrupts pending if/when hsync of vsync
+		// go inactive. Reset upon IACK.
+		const bool hsync = video_.last_valid()->hsync();
+		const bool vsync = video_.last_valid()->vsync();
+		if(previous_hsync_ != hsync && previous_hsync_) {
+			video_interrupts_pending_ |= 2;
+		}
+		if(previous_vsync_ != vsync && previous_vsync_) {
+			video_interrupts_pending_ |= 4;
+		}
+		previous_vsync_ = vsync;
+		previous_hsync_ = hsync;
+
+		if(mfp_->get_interrupt_line()) {
+			mc68000_.set_interrupt_level(6);
+		} else if(video_interrupts_pending_ & 4) {
+			mc68000_.set_interrupt_level(4);
+		} else if(video_interrupts_pending_ & 2) {
+			mc68000_.set_interrupt_level(2);
+		} else {
+			mc68000_.set_interrupt_level(0);
+		}
+	}
+
+	// MARK: - MouseMachine
+	Inputs::Mouse &get_mouse() final {
+		return ikbd_;
+	}
+
+	// MARK: - KeyboardMachine
+	void set_key_state(uint16_t key, bool is_pressed) final {
+		ikbd_.set_key_state(Key(key), is_pressed);
+	}
+
+	IntelligentKeyboard::KeyboardMapper keyboard_mapper_;
+	KeyboardMapper *get_keyboard_mapper() final {
+		return &keyboard_mapper_;
+	}
+
+	// MARK: - JoystickMachine
+	const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
+		return ikbd_.get_joysticks();
+	}
+
+	// MARK: - AYPortHandler
+	void set_port_output(bool port_b, uint8_t value) final {
+		if(port_b) {
+			// TODO: ?
+		} else {
+			/*
+				Port A:
+					b7: reserved
+					b6: "freely usable output (monitor jack)"
+					b5: centronics strobe
+					b4: RS-232 DTR output
+					b3: RS-232 RTS output
+					b2: select floppy drive 1
+					b1: select floppy drive 0
+					b0: "page choice signal for double-sided floppy drive"
+			*/
+			dma_->set_floppy_drive_selection(!(value & 2), !(value & 4), !(value & 1));
+		}
+	}
+
+	// MARK: - MediaTarget
+	bool insert_media(const Analyser::Static::Media &media) final {
+		size_t c = 0;
+		for(const auto &disk: media.disks) {
+			dma_->set_floppy_disk(disk, c);
+			++c;
+			if(c == 2) break;
+		}
+		return true;
+	}
+
+	// MARK: - Activity Source
+	void set_activity_observer(Activity::Observer *observer) final {
+		dma_->set_activity_observer(observer);
+	}
+
+	// MARK: - Video Range
+	Video::Range video_range_;
+	void video_did_change_access_range(Video *video) final {
+		video_range_ = video->get_memory_access_range();
+	}
+
+	// MARK: - Configuration options.
+	std::unique_ptr<Reflection::Struct> get_options() const final {
+		auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
+		options->output = get_video_signal_configurable();
+		return options;
+	}
+
+	void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
+		const auto options = dynamic_cast<Options *>(str.get());
+		set_video_signal_configurable(options->output);
+	}
 };
 
 }
diff --git a/Machines/Atari/ST/DMAController.hpp b/Machines/Atari/ST/DMAController.hpp
index ab4280595..5501d89ea 100644
--- a/Machines/Atari/ST/DMAController.hpp
+++ b/Machines/Atari/ST/DMAController.hpp
@@ -19,93 +19,93 @@
 namespace Atari::ST {
 
 class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, public ClockingHint::Observer {
-	public:
-		DMAController();
+public:
+	DMAController();
 
-		uint16_t read(int address);
-		void write(int address, uint16_t value);
-		void run_for(HalfCycles duration);
+	uint16_t read(int address);
+	void write(int address, uint16_t value);
+	void run_for(HalfCycles duration);
 
-		bool get_interrupt_line();
-		bool get_bus_request_line();
+	bool get_interrupt_line();
+	bool get_bus_request_line();
 
-		/*!
-			Indicates that the DMA controller has been granted bus access to the block of memory at @c ram, which
-			is of size @c size.
+	/*!
+		Indicates that the DMA controller has been granted bus access to the block of memory at @c ram, which
+		is of size @c size.
 
-			@returns The number of words read or written.
-		*/
-		int bus_grant(uint16_t *ram, size_t size);
+		@returns The number of words read or written.
+	*/
+	int bus_grant(uint16_t *ram, size_t size);
 
-		void set_floppy_drive_selection(bool drive1, bool drive2, bool side2);
-		void set_floppy_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
+	void set_floppy_drive_selection(bool drive1, bool drive2, bool side2);
+	void set_floppy_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
 
-		struct Delegate {
-			virtual void dma_controller_did_change_output(DMAController *) = 0;
-		};
-		void set_delegate(Delegate *delegate);
+	struct Delegate {
+		virtual void dma_controller_did_change_output(DMAController *) = 0;
+	};
+	void set_delegate(Delegate *delegate);
 
-		void set_activity_observer(Activity::Observer *observer);
+	void set_activity_observer(Activity::Observer *observer);
 
-		// ClockingHint::Source.
-		ClockingHint::Preference preferred_clocking() const final;
+	// ClockingHint::Source.
+	ClockingHint::Preference preferred_clocking() const final;
 
-	private:
-		HalfCycles running_time_;
-		struct WD1772: public WD::WD1770 {
-			WD1772(): WD::WD1770(WD::WD1770::P1772) {
-				emplace_drives(2, 8000000, 300, 2);
-				set_is_double_density(true);	// TODO: is this selectable on the ST?
-			}
+private:
+	HalfCycles running_time_;
+	struct WD1772: public WD::WD1770 {
+		WD1772(): WD::WD1770(WD::WD1770::P1772) {
+			emplace_drives(2, 8000000, 300, 2);
+			set_is_double_density(true);	// TODO: is this selectable on the ST?
+		}
 
-			void set_motor_on(bool motor_on) final {
-				for_all_drives([motor_on] (Storage::Disk::Drive &drive, size_t) {
-					drive.set_motor_on(motor_on);
-				});
-			}
+		void set_motor_on(bool motor_on) final {
+			for_all_drives([motor_on] (Storage::Disk::Drive &drive, size_t) {
+				drive.set_motor_on(motor_on);
+			});
+		}
 
-			void set_floppy_drive_selection(bool drive1, bool drive2, bool side2) {
-				set_drive(
-					(drive1 ? 1 : 0) |
-					(drive2 ? 2 : 0)
-				);
+		void set_floppy_drive_selection(bool drive1, bool drive2, bool side2) {
+			set_drive(
+				(drive1 ? 1 : 0) |
+				(drive2 ? 2 : 0)
+			);
 
-				for_all_drives([side2] (Storage::Disk::Drive &drive, size_t) {
-					drive.set_head(side2);
-				});
-			}
+			for_all_drives([side2] (Storage::Disk::Drive &drive, size_t) {
+				drive.set_head(side2);
+			});
+		}
 
-			void set_activity_observer(Activity::Observer *observer) {
-				get_drive(0).set_activity_observer(observer, "Internal", true);
-				get_drive(1).set_activity_observer(observer, "External", true);
-			}
+		void set_activity_observer(Activity::Observer *observer) {
+			get_drive(0).set_activity_observer(observer, "Internal", true);
+			get_drive(1).set_activity_observer(observer, "External", true);
+		}
 
-			void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
-				get_drive(drive).set_disk(disk);
-			}
+		void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
+			get_drive(drive).set_disk(disk);
+		}
 
-		} fdc_;
+	} fdc_;
 
-		void wd1770_did_change_output(WD::WD1770 *) final;
+	void wd1770_did_change_output(WD::WD1770 *) final;
 
-		uint16_t control_ = 0;
+	uint16_t control_ = 0;
 
-		Delegate *delegate_ = nullptr;
-		bool interrupt_line_ = false;
-		bool bus_request_line_ = false;
+	Delegate *delegate_ = nullptr;
+	bool interrupt_line_ = false;
+	bool bus_request_line_ = false;
 
-		void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final;
+	void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final;
 
-		// MARK: - DMA State.
-		struct Buffer {
-			uint8_t contents[16];
-			bool is_full = false;
-		} buffer_[2];
-		int active_buffer_ = 0;
-		int bytes_received_ = 0;
-		bool error_ = false;
-		int address_ = 0;
-		int byte_count_ = 0;
+	// MARK: - DMA State.
+	struct Buffer {
+		uint8_t contents[16];
+		bool is_full = false;
+	} buffer_[2];
+	int active_buffer_ = 0;
+	int bytes_received_ = 0;
+	bool error_ = false;
+	int address_ = 0;
+	int byte_count_ = 0;
 };
 
 }
diff --git a/Machines/Atari/ST/IntelligentKeyboard.hpp b/Machines/Atari/ST/IntelligentKeyboard.hpp
index 54f9306ee..64ca0b18e 100644
--- a/Machines/Atari/ST/IntelligentKeyboard.hpp
+++ b/Machines/Atari/ST/IntelligentKeyboard.hpp
@@ -54,144 +54,144 @@ class IntelligentKeyboard:
 	public Serial::Line<false>::ReadDelegate,
 	public ClockingHint::Source,
 	public Inputs::Mouse {
+public:
+	IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output);
+	ClockingHint::Preference preferred_clocking() const final;
+	void run_for(HalfCycles duration);
+
+	void set_key_state(Key key, bool is_pressed);
+	class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
+		uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const final;
+	};
+
+	const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() {
+		return joysticks_;
+	}
+
+private:
+	// MARK: - Key queue.
+	std::mutex key_queue_mutex_;
+	std::vector<uint8_t> key_queue_;
+
+	// MARK: - Serial line state.
+	int bit_count_ = 0;
+	int command_ = 0;
+	Serial::Line<false> &output_line_;
+
+	void output_bytes(std::initializer_list<uint8_t> value);
+	bool serial_line_did_produce_bit(Serial::Line<false> *, int bit) final;
+
+	// MARK: - Command dispatch.
+	std::vector<uint8_t> command_sequence_;
+	void dispatch_command(uint8_t command);
+
+	// MARK: - Flow control.
+	void reset();
+	void resume();
+	void pause();
+
+	// MARK: - Mouse.
+	void disable_mouse();
+	void set_relative_mouse_position_reporting();
+	void set_absolute_mouse_position_reporting(uint16_t max_x, uint16_t max_y);
+	void set_mouse_position(uint16_t x, uint16_t y);
+	void set_mouse_keycode_reporting(uint8_t delta_x, uint8_t delta_y);
+	void set_mouse_threshold(uint8_t x, uint8_t y);
+	void set_mouse_scale(uint8_t x, uint8_t y);
+	void set_mouse_y_downward();
+	void set_mouse_y_upward();
+	void set_mouse_button_actions(uint8_t actions);
+	void interrogate_mouse_position();
+
+	// Inputs::Mouse.
+	void move(int x, int y) final;
+	int get_number_of_buttons() const final;
+	void set_button_pressed(int index, bool is_pressed) final;
+	void reset_all_buttons() final;
+
+	enum class MouseMode {
+		Relative, Absolute, Disabled
+	} mouse_mode_ = MouseMode::Relative;
+
+	// Absolute positioning state.
+	int mouse_range_[2] = {320, 200};
+	int mouse_scale_[2] = {1, 1};
+	int mouse_position_[2] = {0, 0};
+	int mouse_y_multiplier_ = 1;
+
+	// Relative positioning state.
+	int posted_button_state_ = 0;
+	int mouse_threshold_[2] = {1, 1};
+	void post_relative_mouse_event(int x, int y);
+
+	// Received mouse state.
+	std::atomic<int> mouse_movement_[2]{0, 0};
+	std::atomic<int> mouse_button_state_{0};
+	std::atomic<int> mouse_button_events_{0};
+
+	// MARK: - Joystick.
+	void disable_joysticks();
+	void set_joystick_event_mode();
+	void set_joystick_interrogation_mode();
+	void set_joystick_monitoring_mode(uint8_t rate);
+	void set_joystick_fire_button_monitoring_mode();
+	struct VelocityThreshold {
+		uint8_t threshold;
+		uint8_t prior_rate;
+		uint8_t post_rate;
+	};
+	void set_joystick_keycode_mode(VelocityThreshold horizontal, VelocityThreshold vertical);
+	void interrogate_joysticks();
+
+	void clear_joystick_events();
+
+	enum class JoystickMode {
+		Disabled, Event, Interrogation, KeyCode
+	} joystick_mode_ = JoystickMode::Event;
+
+	class Joystick: public Inputs::ConcreteJoystick {
 	public:
-		IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output);
-		ClockingHint::Preference preferred_clocking() const final;
-		void run_for(HalfCycles duration);
+		Joystick() :
+			ConcreteJoystick({
+				Input(Input::Up),
+				Input(Input::Down),
+				Input(Input::Left),
+				Input(Input::Right),
+				Input(Input::Fire, 0),
+			}) {}
 
-		void set_key_state(Key key, bool is_pressed);
-		class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
-			uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const final;
-		};
+		void did_set_input(const Input &input, bool is_active) final {
+			uint8_t mask = 0;
+			switch(input.type) {
+				default: return;
+				case Input::Up:		mask = 0x01;	break;
+				case Input::Down:	mask = 0x02;	break;
+				case Input::Left:	mask = 0x04;	break;
+				case Input::Right:	mask = 0x08;	break;
+				case Input::Fire:	mask = 0x80;	break;
+			}
 
-		const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() {
-			return joysticks_;
+			if(is_active) state_ |= mask; else state_ &= ~mask;
+		}
+
+		uint8_t get_state() {
+			returned_state_ = state_;
+			return state_;
+		}
+
+		bool has_event() {
+			return returned_state_ != state_;
+		}
+
+		uint8_t event_mask() {
+			return returned_state_ ^ state_;
 		}
 
 	private:
-		// MARK: - Key queue.
-		std::mutex key_queue_mutex_;
-		std::vector<uint8_t> key_queue_;
-
-		// MARK: - Serial line state.
-		int bit_count_ = 0;
-		int command_ = 0;
-		Serial::Line<false> &output_line_;
-
-		void output_bytes(std::initializer_list<uint8_t> value);
-		bool serial_line_did_produce_bit(Serial::Line<false> *, int bit) final;
-
-		// MARK: - Command dispatch.
-		std::vector<uint8_t> command_sequence_;
-		void dispatch_command(uint8_t command);
-
-		// MARK: - Flow control.
-		void reset();
-		void resume();
-		void pause();
-
-		// MARK: - Mouse.
-		void disable_mouse();
-		void set_relative_mouse_position_reporting();
-		void set_absolute_mouse_position_reporting(uint16_t max_x, uint16_t max_y);
-		void set_mouse_position(uint16_t x, uint16_t y);
-		void set_mouse_keycode_reporting(uint8_t delta_x, uint8_t delta_y);
-		void set_mouse_threshold(uint8_t x, uint8_t y);
-		void set_mouse_scale(uint8_t x, uint8_t y);
-		void set_mouse_y_downward();
-		void set_mouse_y_upward();
-		void set_mouse_button_actions(uint8_t actions);
-		void interrogate_mouse_position();
-
-		// Inputs::Mouse.
-		void move(int x, int y) final;
-		int get_number_of_buttons() const final;
-		void set_button_pressed(int index, bool is_pressed) final;
-		void reset_all_buttons() final;
-
-		enum class MouseMode {
-			Relative, Absolute, Disabled
-		} mouse_mode_ = MouseMode::Relative;
-
-		// Absolute positioning state.
-		int mouse_range_[2] = {320, 200};
-		int mouse_scale_[2] = {1, 1};
-		int mouse_position_[2] = {0, 0};
-		int mouse_y_multiplier_ = 1;
-
-		// Relative positioning state.
-		int posted_button_state_ = 0;
-		int mouse_threshold_[2] = {1, 1};
-		void post_relative_mouse_event(int x, int y);
-
-		// Received mouse state.
-		std::atomic<int> mouse_movement_[2]{0, 0};
-		std::atomic<int> mouse_button_state_{0};
-		std::atomic<int> mouse_button_events_{0};
-
-		// MARK: - Joystick.
-		void disable_joysticks();
-		void set_joystick_event_mode();
-		void set_joystick_interrogation_mode();
-		void set_joystick_monitoring_mode(uint8_t rate);
-		void set_joystick_fire_button_monitoring_mode();
-		struct VelocityThreshold {
-			uint8_t threshold;
-			uint8_t prior_rate;
-			uint8_t post_rate;
-		};
-		void set_joystick_keycode_mode(VelocityThreshold horizontal, VelocityThreshold vertical);
-		void interrogate_joysticks();
-
-		void clear_joystick_events();
-
-		enum class JoystickMode {
-			Disabled, Event, Interrogation, KeyCode
-		} joystick_mode_ = JoystickMode::Event;
-
-		class Joystick: public Inputs::ConcreteJoystick {
-			public:
-				Joystick() :
-					ConcreteJoystick({
-						Input(Input::Up),
-						Input(Input::Down),
-						Input(Input::Left),
-						Input(Input::Right),
-						Input(Input::Fire, 0),
-					}) {}
-
-				void did_set_input(const Input &input, bool is_active) final {
-					uint8_t mask = 0;
-					switch(input.type) {
-						default: return;
-						case Input::Up:		mask = 0x01;	break;
-						case Input::Down:	mask = 0x02;	break;
-						case Input::Left:	mask = 0x04;	break;
-						case Input::Right:	mask = 0x08;	break;
-						case Input::Fire:	mask = 0x80;	break;
-					}
-
-					if(is_active) state_ |= mask; else state_ &= ~mask;
-				}
-
-				uint8_t get_state() {
-					returned_state_ = state_;
-					return state_;
-				}
-
-				bool has_event() {
-					return returned_state_ != state_;
-				}
-
-				uint8_t event_mask() {
-					return returned_state_ ^ state_;
-				}
-
-			private:
-				uint8_t state_ = 0x00;
-				uint8_t returned_state_ = 0x00;
-		};
-		std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
+		uint8_t state_ = 0x00;
+		uint8_t returned_state_ = 0x00;
+	};
+	std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
 };
 
 }
diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp
index dc3184073..ed1a6894e 100644
--- a/Machines/Atari/ST/Video.hpp
+++ b/Machines/Atari/ST/Video.hpp
@@ -31,220 +31,220 @@ struct LineLength {
 	(hopefully) to a subsystem.
 */
 class Video {
+public:
+	Video();
+
+	/*!
+		Sets the memory pool that provides video, and its size in words.
+	*/
+	void set_ram(uint16_t *, size_t size);
+
+	/*!
+		Sets the target device for video data.
+	*/
+	void set_scan_target(Outputs::Display::ScanTarget *scan_target);
+
+	/// Gets the current scan status.
+	Outputs::Display::ScanStatus get_scaled_scan_status() const;
+
+	/*!
+		Sets the type of output.
+	*/
+	void set_display_type(Outputs::Display::DisplayType);
+
+	/*!
+		Gets the type of output.
+	*/
+	Outputs::Display::DisplayType get_display_type() const;
+
+	/*!
+		Produces the next @c duration period of pixels.
+	*/
+	void run_for(HalfCycles duration);
+
+
+	/*!
+		@returns the number of cycles until there is next a change in the hsync,
+		vsync or display_enable outputs.
+	*/
+	HalfCycles next_sequence_point();
+
+	/*!
+		@returns @c true if the horizontal sync output is currently active; @c false otherwise.
+
+		@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
+		documented as being triggered by horizontal blank.
+	*/
+	bool hsync();
+
+	/*!
+		@returns @c true if the vertical sync output is currently active; @c false otherwise.
+
+		@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
+		documented as being triggered by vertical blank.
+	*/
+	bool vsync();
+
+	/*!
+		@returns @c true if the display enabled output is currently active; @c false otherwise.
+
+		@discussion On an Atari ST this is fed to the MFP. The documentation that I've been able to
+		find implies a total 28-cycle delay between the real delay enabled signal changing and its effect
+		on the 68000 interrupt input via the MFP. As I have yet to determine how much delay is caused
+		by the MFP a full 28-cycle delay is applied by this class. This should be dialled down when the
+		MFP's responsibility is clarified.
+	*/
+	bool display_enabled();
+
+	/// @returns the effect of reading from @c address; only the low 6 bits are decoded.
+	uint16_t read(int address);
+
+	/// Writes @c value to @c address, of which only the low 6 bits are decoded.
+	void write(int address, uint16_t value);
+
+	/// Used internally to track state.
+	enum class FieldFrequency {
+		Fifty = 0, Sixty = 1, SeventyTwo = 2
+	};
+
+	struct RangeObserver {
+		/// Indicates to the observer that the memory access range has changed.
+		virtual void video_did_change_access_range(Video *) = 0;
+	};
+
+	/// Sets a range observer, which is an actor that will be notified if the memory access range changes.
+	void set_range_observer(RangeObserver *);
+
+	struct Range {
+		uint32_t low_address, high_address;
+	};
+	/*!
+		@returns the range of addresses that the video might read from.
+	*/
+	Range get_memory_access_range();
+
+private:
+	DeferredQueue<HalfCycles> deferrer_;
+
+	Outputs::CRT::CRT crt_;
+	RangeObserver *range_observer_ = nullptr;
+
+	uint16_t raw_palette_[16];
+	uint16_t palette_[16];
+	int base_address_ = 0;
+	int previous_base_address_ = 0;
+	int current_address_ = 0;
+
+	uint16_t *ram_ = nullptr;
+	int ram_mask_ = 0;
+
+	int x_ = 0, y_ = 0, next_y_ = 0;
+	bool load_ = false;
+	int load_base_ = 0;
+
+	uint16_t video_mode_ = 0;
+	uint16_t sync_mode_ = 0;
+
+	FieldFrequency field_frequency_ = FieldFrequency::Fifty;
+	enum class OutputBpp {
+		One, Two, Four
+	} output_bpp_ = OutputBpp::Four;
+	void update_output_mode();
+
+	struct HorizontalState {
+		bool enable = false;
+		bool blank = false;
+		bool sync = false;
+	} horizontal_;
+	struct VerticalState {
+		bool enable = false;
+		bool blank = false;
+
+		enum class SyncSchedule {
+			/// No sync events this line.
+			None,
+			/// Sync should begin during this horizontal line.
+			Begin,
+			/// Sync should end during this horizontal line.
+			End,
+		} sync_schedule = SyncSchedule::None;
+		bool sync = false;
+	} vertical_, next_vertical_;
+	LineLength line_length_;
+
+	int data_latch_position_ = 0;
+	int data_latch_read_position_ = 0;
+	uint16_t data_latch_[128];
+	void push_latched_data();
+
+	void reset_fifo();
+
+	/*!
+		Provides a target for control over the output video stream, which is considered to be
+		a permanently shifting shifter, that you need to reload when appropriate, which can be
+		overridden by the blank and sync levels.
+
+		This stream will automatically insert a colour burst.
+	*/
+	class VideoStream {
 	public:
-		Video();
+		VideoStream(Outputs::CRT::CRT &crt, uint16_t *palette) : crt_(crt), palette_(palette) {}
 
-		/*!
-			Sets the memory pool that provides video, and its size in words.
-		*/
-		void set_ram(uint16_t *, size_t size);
-
-		/*!
-			Sets the target device for video data.
-		*/
-		void set_scan_target(Outputs::Display::ScanTarget *scan_target);
-
-		/// Gets the current scan status.
-		Outputs::Display::ScanStatus get_scaled_scan_status() const;
-
-		/*!
-			Sets the type of output.
-		*/
-		void set_display_type(Outputs::Display::DisplayType);
-
-		/*!
-			Gets the type of output.
-		*/
-		Outputs::Display::DisplayType get_display_type() const;
-
-		/*!
-			Produces the next @c duration period of pixels.
-		*/
-		void run_for(HalfCycles duration);
-
-
-		/*!
-			@returns the number of cycles until there is next a change in the hsync,
-			vsync or display_enable outputs.
-		*/
-		HalfCycles next_sequence_point();
-
-		/*!
-			@returns @c true if the horizontal sync output is currently active; @c false otherwise.
-
-			@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
-			documented as being triggered by horizontal blank.
-		*/
-		bool hsync();
-
-		/*!
-			@returns @c true if the vertical sync output is currently active; @c false otherwise.
-
-			@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
-			documented as being triggered by vertical blank.
-		*/
-		bool vsync();
-
-		/*!
-			@returns @c true if the display enabled output is currently active; @c false otherwise.
-
-			@discussion On an Atari ST this is fed to the MFP. The documentation that I've been able to
-			find implies a total 28-cycle delay between the real delay enabled signal changing and its effect
-			on the 68000 interrupt input via the MFP. As I have yet to determine how much delay is caused
-			by the MFP a full 28-cycle delay is applied by this class. This should be dialled down when the
-			MFP's responsibility is clarified.
-		*/
-		bool display_enabled();
-
-		/// @returns the effect of reading from @c address; only the low 6 bits are decoded.
-		uint16_t read(int address);
-
-		/// Writes @c value to @c address, of which only the low 6 bits are decoded.
-		void write(int address, uint16_t value);
-
-		/// Used internally to track state.
-		enum class FieldFrequency {
-			Fifty = 0, Sixty = 1, SeventyTwo = 2
+		enum class OutputMode {
+			Sync, Blank, ColourBurst, Pixels,
 		};
 
-		struct RangeObserver {
-			/// Indicates to the observer that the memory access range has changed.
-			virtual void video_did_change_access_range(Video *) = 0;
-		};
+		/// Sets the current data format for the shifter. Changes in output BPP flush the shifter.
+		void set_bpp(OutputBpp bpp);
 
-		/// Sets a range observer, which is an actor that will be notified if the memory access range changes.
-		void set_range_observer(RangeObserver *);
+		/// Outputs signal of type @c mode for @c duration.
+		void output(int duration, OutputMode mode);
 
-		struct Range {
-			uint32_t low_address, high_address;
-		};
-		/*!
-			@returns the range of addresses that the video might read from.
-		*/
-		Range get_memory_access_range();
+		/// Warns the video stream that the border colour, included in the palette that it holds a pointer to,
+		/// will change momentarily. This should be called after the relevant @c output() updates, and
+		/// is used to help elide border-regio output.
+		void will_change_border_colour();
+
+		/// Loads 64 bits into the Shifter. The shifter shifts continuously. If you also declare
+		/// a pixels region then whatever is being shifted will reach the display, in a form that
+		/// depends on the current output BPP.
+		void load(uint64_t value);
 
 	private:
-		DeferredQueue<HalfCycles> deferrer_;
+		// The target CRT and the palette to use.
+		Outputs::CRT::CRT &crt_;
+		uint16_t *palette_ = nullptr;
 
-		Outputs::CRT::CRT crt_;
-		RangeObserver *range_observer_ = nullptr;
+		// Internal stateful processes.
+		void generate(int duration, OutputMode mode, bool is_terminal);
 
-		uint16_t raw_palette_[16];
-		uint16_t palette_[16];
-		int base_address_ = 0;
-		int previous_base_address_ = 0;
-		int current_address_ = 0;
+		void flush_border();
+		void flush_pixels();
+		void shift(int duration);
+		void output_pixels(int duration);
 
-		uint16_t *ram_ = nullptr;
-		int ram_mask_ = 0;
+		// Internal state that is a function of output intent.
+		int duration_ = 0;
+		OutputMode output_mode_ = OutputMode::Sync;
+		OutputBpp bpp_ = OutputBpp::Four;
+		union {
+			uint64_t output_shifter_;
+			uint32_t shifter_halves_[2];
+		};
 
-		int x_ = 0, y_ = 0, next_y_ = 0;
-		bool load_ = false;
-		int load_base_ = 0;
+		// Internal state for handling output serialisation.
+		uint16_t *pixel_buffer_ = nullptr;
+		int pixel_pointer_ = 0;
+	} video_stream_;
 
-		uint16_t video_mode_ = 0;
-		uint16_t sync_mode_ = 0;
+	/// Contains copies of the various observeable fields, after the relevant propagation delay.
+	struct PublicState {
+		bool display_enable = false;
+		bool hsync = false;
+		bool vsync = false;
+	} public_state_;
 
-		FieldFrequency field_frequency_ = FieldFrequency::Fifty;
-		enum class OutputBpp {
-			One, Two, Four
-		} output_bpp_ = OutputBpp::Four;
-		void update_output_mode();
-
-		struct HorizontalState {
-			bool enable = false;
-			bool blank = false;
-			bool sync = false;
-		} horizontal_;
-		struct VerticalState {
-			bool enable = false;
-			bool blank = false;
-
-			enum class SyncSchedule {
-				/// No sync events this line.
-				None,
-				/// Sync should begin during this horizontal line.
-				Begin,
-				/// Sync should end during this horizontal line.
-				End,
-			} sync_schedule = SyncSchedule::None;
-			bool sync = false;
-		} vertical_, next_vertical_;
-		LineLength line_length_;
-
-		int data_latch_position_ = 0;
-		int data_latch_read_position_ = 0;
-		uint16_t data_latch_[128];
-		void push_latched_data();
-
-		void reset_fifo();
-
-		/*!
-			Provides a target for control over the output video stream, which is considered to be
-			a permanently shifting shifter, that you need to reload when appropriate, which can be
-			overridden by the blank and sync levels.
-
-			This stream will automatically insert a colour burst.
-		*/
-		class VideoStream {
-			public:
-				VideoStream(Outputs::CRT::CRT &crt, uint16_t *palette) : crt_(crt), palette_(palette) {}
-
-				enum class OutputMode {
-					Sync, Blank, ColourBurst, Pixels,
-				};
-
-				/// Sets the current data format for the shifter. Changes in output BPP flush the shifter.
-				void set_bpp(OutputBpp bpp);
-
-				/// Outputs signal of type @c mode for @c duration.
-				void output(int duration, OutputMode mode);
-
-				/// Warns the video stream that the border colour, included in the palette that it holds a pointer to,
-				/// will change momentarily. This should be called after the relevant @c output() updates, and
-				/// is used to help elide border-regio output.
-				void will_change_border_colour();
-
-				/// Loads 64 bits into the Shifter. The shifter shifts continuously. If you also declare
-				/// a pixels region then whatever is being shifted will reach the display, in a form that
-				/// depends on the current output BPP.
-				void load(uint64_t value);
-
-			private:
-				// The target CRT and the palette to use.
-				Outputs::CRT::CRT &crt_;
-				uint16_t *palette_ = nullptr;
-
-				// Internal stateful processes.
-				void generate(int duration, OutputMode mode, bool is_terminal);
-
-				void flush_border();
-				void flush_pixels();
-				void shift(int duration);
-				void output_pixels(int duration);
-
-				// Internal state that is a function of output intent.
-				int duration_ = 0;
-				OutputMode output_mode_ = OutputMode::Sync;
-				OutputBpp bpp_ = OutputBpp::Four;
-				union {
-					uint64_t output_shifter_;
-					uint32_t shifter_halves_[2];
-				};
-
-				// Internal state for handling output serialisation.
-				uint16_t *pixel_buffer_ = nullptr;
-				int pixel_pointer_ = 0;
-		} video_stream_;
-
-		/// Contains copies of the various observeable fields, after the relevant propagation delay.
-		struct PublicState {
-			bool display_enable = false;
-			bool hsync = false;
-			bool vsync = false;
-		} public_state_;
-
-		friend class ::VideoTester;
+	friend class ::VideoTester;
 };
 
 }
diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp
index ecb9c879a..774afb0a5 100644
--- a/Machines/Commodore/1540/Implementation/C1540Base.hpp
+++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp
@@ -38,25 +38,25 @@ namespace Commodore::C1540 {
 	The attention input is also connected to CA1, similarly invertedl; the CA1 wire will be high when the bus is low and vice versa.
 */
 class SerialPortVIA: public MOS::MOS6522::IRQDelegatePortHandler {
-	public:
-		SerialPortVIA(MOS::MOS6522::MOS6522<SerialPortVIA> &via);
+public:
+	SerialPortVIA(MOS::MOS6522::MOS6522<SerialPortVIA> &via);
 
-		uint8_t get_port_input(MOS::MOS6522::Port);
+	uint8_t get_port_input(MOS::MOS6522::Port);
 
-		void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t mask);
-		void set_serial_line_state(::Commodore::Serial::Line, bool);
+	void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t mask);
+	void set_serial_line_state(::Commodore::Serial::Line, bool);
 
-		void set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &);
+	void set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &);
 
-	private:
-		MOS::MOS6522::MOS6522<SerialPortVIA> &via_;
-		uint8_t port_b_ = 0x0;
-		std::weak_ptr<::Commodore::Serial::Port> serial_port_;
-		bool attention_acknowledge_level_ = false;
-		bool attention_level_input_ = true;
-		bool data_level_output_ = false;
+private:
+	MOS::MOS6522::MOS6522<SerialPortVIA> &via_;
+	uint8_t port_b_ = 0x0;
+	std::weak_ptr<::Commodore::Serial::Port> serial_port_;
+	bool attention_acknowledge_level_ = false;
+	bool attention_level_input_ = true;
+	bool data_level_output_ = false;
 
-		void update_data_line();
+	void update_data_line();
 };
 
 /*!
@@ -76,46 +76,46 @@ class SerialPortVIA: public MOS::MOS6522::IRQDelegatePortHandler {
 	whether the disk head is being told to read or write, but it's unclear and I've yet to investigate. So, TODO.
 */
 class DriveVIA: public MOS::MOS6522::IRQDelegatePortHandler {
-	public:
-		class Delegate {
-			public:
-				virtual void drive_via_did_step_head(void *driveVIA, int direction) = 0;
-				virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 0;
-		};
-		void set_delegate(Delegate *);
+public:
+	class Delegate {
+		public:
+			virtual void drive_via_did_step_head(void *driveVIA, int direction) = 0;
+			virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 0;
+	};
+	void set_delegate(Delegate *);
 
-		uint8_t get_port_input(MOS::MOS6522::Port port);
+	uint8_t get_port_input(MOS::MOS6522::Port port);
 
-		void set_sync_detected(bool);
-		void set_data_input(uint8_t);
-		bool get_should_set_overflow();
-		bool get_motor_enabled();
+	void set_sync_detected(bool);
+	void set_data_input(uint8_t);
+	bool get_should_set_overflow();
+	bool get_motor_enabled();
 
-		void set_control_line_output(MOS::MOS6522::Port, MOS::MOS6522::Line, bool value);
+	void set_control_line_output(MOS::MOS6522::Port, MOS::MOS6522::Line, bool value);
 
-		void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t direction_mask);
+	void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t direction_mask);
 
-		void set_activity_observer(Activity::Observer *observer);
+	void set_activity_observer(Activity::Observer *observer);
 
-	private:
-		uint8_t port_b_ = 0xff, port_a_ = 0xff;
-		bool should_set_overflow_ = false;
-		bool drive_motor_ = false;
-		uint8_t previous_port_b_output_ = 0;
-		Delegate *delegate_ = nullptr;
-		Activity::Observer *observer_ = nullptr;
+private:
+	uint8_t port_b_ = 0xff, port_a_ = 0xff;
+	bool should_set_overflow_ = false;
+	bool drive_motor_ = false;
+	uint8_t previous_port_b_output_ = 0;
+	Delegate *delegate_ = nullptr;
+	Activity::Observer *observer_ = nullptr;
 };
 
 /*!
 	An implementation of the C1540's serial port; this connects incoming line levels to the serial-port VIA.
 */
 class SerialPort : public ::Commodore::Serial::Port {
-	public:
-		void set_input(::Commodore::Serial::Line, ::Commodore::Serial::LineLevel);
-		void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
+public:
+	void set_input(::Commodore::Serial::Line, ::Commodore::Serial::LineLevel);
+	void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
 
-	private:
-		std::weak_ptr<SerialPortVIA> serial_port_VIA_;
+private:
+	std::weak_ptr<SerialPortVIA> serial_port_VIA_;
 };
 
 class MachineBase:
@@ -124,38 +124,38 @@ class MachineBase:
 	public DriveVIA::Delegate,
 	public Storage::Disk::Controller {
 
-	public:
-		MachineBase(Personality personality, const ROM::Map &roms);
+public:
+	MachineBase(Personality personality, const ROM::Map &roms);
 
-		// to satisfy CPU::MOS6502::Processor
-		Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
+	// to satisfy CPU::MOS6502::Processor
+	Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
 
-		// to satisfy MOS::MOS6522::Delegate
-		virtual void mos6522_did_change_interrupt_status(void *mos6522);
+	// to satisfy MOS::MOS6522::Delegate
+	virtual void mos6522_did_change_interrupt_status(void *mos6522);
 
-		// to satisfy DriveVIA::Delegate
-		void drive_via_did_step_head(void *driveVIA, int direction);
-		void drive_via_did_set_data_density(void *driveVIA, int density);
+	// to satisfy DriveVIA::Delegate
+	void drive_via_did_step_head(void *driveVIA, int direction);
+	void drive_via_did_set_data_density(void *driveVIA, int density);
 
-		/// Attaches the activity observer to this C1540.
-		void set_activity_observer(Activity::Observer *observer);
+	/// Attaches the activity observer to this C1540.
+	void set_activity_observer(Activity::Observer *observer);
 
-	protected:
-		CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
+protected:
+	CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
 
-		uint8_t ram_[0x800];
-		uint8_t rom_[0x4000];
+	uint8_t ram_[0x800];
+	uint8_t rom_[0x4000];
 
-		std::shared_ptr<SerialPortVIA> serial_port_VIA_port_handler_;
-		std::shared_ptr<SerialPort> serial_port_;
-		DriveVIA drive_VIA_port_handler_;
+	std::shared_ptr<SerialPortVIA> serial_port_VIA_port_handler_;
+	std::shared_ptr<SerialPort> serial_port_;
+	DriveVIA drive_VIA_port_handler_;
 
-		MOS::MOS6522::MOS6522<DriveVIA> drive_VIA_;
-		MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_;
+	MOS::MOS6522::MOS6522<DriveVIA> drive_VIA_;
+	MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_;
 
-		int shift_register_ = 0, bit_window_offset_;
-		virtual void process_input_bit(int value);
-		virtual void process_index_hole();
+	int shift_register_ = 0, bit_window_offset_;
+	virtual void process_input_bit(int value);
+	virtual void process_index_hole();
 };
 
 }
diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp
index 44d9022bc..a176ffc0e 100644
--- a/Machines/Utility/ROMCatalogue.hpp
+++ b/Machines/Utility/ROMCatalogue.hpp
@@ -209,17 +209,17 @@ struct Description {
 	/// plus all the fields provided as @c flags .
 	std::string description(int flags) const;
 
-	private:
-		template <typename FileNameT, typename CRC32T> Description(
-			Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0)
-		) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
-			// Slightly lazy: deal with the case where the constructor wasn't provided with any
-			// CRCs by spotting that the set has exactly one member, which has value 0. The alternative
-			// would be to provide a partial specialisation that never put anything into the set.
-			if(this->crc32s.size() == 1 && !*this->crc32s.begin()) {
-				this->crc32s.clear();
-			}
+private:
+	template <typename FileNameT, typename CRC32T> Description(
+		Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0)
+	) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
+		// Slightly lazy: deal with the case where the constructor wasn't provided with any
+		// CRCs by spotting that the set has exactly one member, which has value 0. The alternative
+		// would be to provide a partial specialisation that never put anything into the set.
+		if(this->crc32s.size() == 1 && !*this->crc32s.begin()) {
+			this->crc32s.clear();
 		}
+	}
 };
 
 /// @returns a vector of all possible instances of ROM::Description — i.e. descriptions of every ROM
@@ -273,48 +273,48 @@ struct Request {
 	/// portion of a sentence, e.g. "Please supply" + request.description(0, L'*').
 	std::wstring description(int description_flags, wchar_t bullet_point);
 
-	private:
-		struct Node {
-			enum class Type {
-				Any, All, One
-			};
-			Type type = Type::One;
-			Name name = Name::None;
-			/// @c true if this ROM is optional for machine startup. Generally indicates something
-			/// that would make emulation more accurate, but not sufficiently so to make it
-			/// a necessity.
-			bool is_optional = false;
-			std::vector<Node> children;
-
-			bool empty() const {
-				return type == Type::One && name == Name::None;
-			}
-
-			void add_descriptions(std::vector<Description> &) const;
-			bool validate(Map &) const;
-			void visit(
-				const std::function<void(ListType, size_t)> &enter_list,
-				const std::function<void(void)> &exit_list,
-				const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
-			) const;
-			bool subtract(const ROM::Map &map);
-			void sort() {
-				// Don't do a full sort, but move anything optional to the back.
-				// This makes them print more nicely; it's a human-facing tweak only.
-				ssize_t index = ssize_t(children.size() - 1);
-				bool has_seen_non_optional = false;
-				while(index >= 0) {
-					has_seen_non_optional |= !children[size_t(index)].is_optional;
-					if(children[size_t(index)].is_optional && has_seen_non_optional) {
-						std::rotate(children.begin() + index, children.begin() + index + 1, children.end());
-					}
-					--index;
-				}
-			}
+private:
+	struct Node {
+		enum class Type {
+			Any, All, One
 		};
-		Node node;
+		Type type = Type::One;
+		Name name = Name::None;
+		/// @c true if this ROM is optional for machine startup. Generally indicates something
+		/// that would make emulation more accurate, but not sufficiently so to make it
+		/// a necessity.
+		bool is_optional = false;
+		std::vector<Node> children;
 
-		Request append(Node::Type type, const Request &rhs);
+		bool empty() const {
+			return type == Type::One && name == Name::None;
+		}
+
+		void add_descriptions(std::vector<Description> &) const;
+		bool validate(Map &) const;
+		void visit(
+			const std::function<void(ListType, size_t)> &enter_list,
+			const std::function<void(void)> &exit_list,
+			const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
+		) const;
+		bool subtract(const ROM::Map &map);
+		void sort() {
+			// Don't do a full sort, but move anything optional to the back.
+			// This makes them print more nicely; it's a human-facing tweak only.
+			ssize_t index = ssize_t(children.size() - 1);
+			bool has_seen_non_optional = false;
+			while(index >= 0) {
+				has_seen_non_optional |= !children[size_t(index)].is_optional;
+				if(children[size_t(index)].is_optional && has_seen_non_optional) {
+					std::rotate(children.begin() + index, children.begin() + index + 1, children.end());
+				}
+				--index;
+			}
+		}
+	};
+	Node node;
+
+	Request append(Node::Type type, const Request &rhs);
 };
 
 }
diff --git a/Machines/Utility/StringSerialiser.hpp b/Machines/Utility/StringSerialiser.hpp
index 135b6020b..d0ee5522c 100644
--- a/Machines/Utility/StringSerialiser.hpp
+++ b/Machines/Utility/StringSerialiser.hpp
@@ -14,15 +14,15 @@
 namespace Utility {
 
 class StringSerialiser {
-	public:
-		StringSerialiser(const std::string &source, bool use_linefeed_only = false);
+public:
+	StringSerialiser(const std::string &source, bool use_linefeed_only = false);
 
-		uint8_t head();
-		bool advance();
+	uint8_t head();
+	bool advance();
 
-	private:
-		std::string input_string_;
-		std::size_t input_string_pointer_ = 0;
+private:
+	std::string input_string_;
+	std::size_t input_string_pointer_ = 0;
 };
 
 }
diff --git a/Machines/Utility/TypedDynamicMachine.hpp b/Machines/Utility/TypedDynamicMachine.hpp
index 933e03192..857e90bcf 100644
--- a/Machines/Utility/TypedDynamicMachine.hpp
+++ b/Machines/Utility/TypedDynamicMachine.hpp
@@ -15,55 +15,55 @@
 namespace Machine {
 
 template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine {
-	public:
-		TypedDynamicMachine(std::unique_ptr<T> &&machine) : machine_(std::move(machine)) {}
-		T *get() { return machine_.get(); }
+public:
+	TypedDynamicMachine(std::unique_ptr<T> &&machine) : machine_(std::move(machine)) {}
+	T *get() { return machine_.get(); }
 
-		TypedDynamicMachine() : TypedDynamicMachine(nullptr) {}
-		TypedDynamicMachine(TypedDynamicMachine &&rhs) : machine_(std::move(rhs.machine_)) {}
-		TypedDynamicMachine &operator=(TypedDynamicMachine &&rhs) {
-			machine_ = std::move(rhs.machine_);
-			return *this;
-		}
+	TypedDynamicMachine() : TypedDynamicMachine(nullptr) {}
+	TypedDynamicMachine(TypedDynamicMachine &&rhs) : machine_(std::move(rhs.machine_)) {}
+	TypedDynamicMachine &operator=(TypedDynamicMachine &&rhs) {
+		machine_ = std::move(rhs.machine_);
+		return *this;
+	}
 
 #define Provide(type, name)	\
-		type *name() final {	\
-			return get<type>();	\
-		}
+	type *name() final {	\
+		return get<type>();	\
+	}
 
-		Provide(Activity::Source, activity_source)
-		Provide(Configurable::Device, configurable_device)
-		Provide(MachineTypes::TimedMachine, timed_machine)
-		Provide(MachineTypes::ScanProducer, scan_producer)
-		Provide(MachineTypes::AudioProducer, audio_producer)
-		Provide(MachineTypes::JoystickMachine, joystick_machine)
-		Provide(MachineTypes::KeyboardMachine, keyboard_machine)
-		Provide(MachineTypes::MouseMachine, mouse_machine)
-		Provide(MachineTypes::MediaTarget, media_target)
+	Provide(Activity::Source, activity_source)
+	Provide(Configurable::Device, configurable_device)
+	Provide(MachineTypes::TimedMachine, timed_machine)
+	Provide(MachineTypes::ScanProducer, scan_producer)
+	Provide(MachineTypes::AudioProducer, audio_producer)
+	Provide(MachineTypes::JoystickMachine, joystick_machine)
+	Provide(MachineTypes::KeyboardMachine, keyboard_machine)
+	Provide(MachineTypes::MouseMachine, mouse_machine)
+	Provide(MachineTypes::MediaTarget, media_target)
 
 #undef Provide
 
-		void *raw_pointer() final {
-			return get();
-		}
+	void *raw_pointer() final {
+		return get();
+	}
 
-	private:
-		template <typename Class> Class *get() {
-			return dynamic_cast<Class *>(machine_.get());
+private:
+	template <typename Class> Class *get() {
+		return dynamic_cast<Class *>(machine_.get());
 
-			// Note to self: the below is not [currently] used
-			// because in practice TypedDynamicMachine is instantiated
-			// with an abstract parent of the actual class.
-			//
-			// TODO: rethink type hiding here. I think I've boxed myself
-			// into an uncomfortable corner.
+		// Note to self: the below is not [currently] used
+		// because in practice TypedDynamicMachine is instantiated
+		// with an abstract parent of the actual class.
+		//
+		// TODO: rethink type hiding here. I think I've boxed myself
+		// into an uncomfortable corner.
 //			if constexpr (std::is_base_of_v<Class, T>) {
 //				return static_cast<Class *>(machine_.get());
 //			} else {
 //				return nullptr;
 //			}
-		}
-		std::unique_ptr<T> machine_;
+	}
+	std::unique_ptr<T> machine_;
 };
 
 }
diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp
index e0c6e0fdf..9aba38e1a 100644
--- a/Machines/Utility/Typer.hpp
+++ b/Machines/Utility/Typer.hpp
@@ -63,41 +63,41 @@ class CharacterMapper {
 	fresh key transition is ready to be consumed.
 */
 class Typer {
-	public:
-		class Delegate: public MachineTypes::KeyActions {
-			public:
-				/// Informs the delegate that this typer has reached the end of its content.
-				virtual void typer_reset(Typer *typer) = 0;
-		};
+public:
+	class Delegate: public MachineTypes::KeyActions {
+		public:
+			/// Informs the delegate that this typer has reached the end of its content.
+			virtual void typer_reset(Typer *typer) = 0;
+	};
 
-		Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate);
+	Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate);
 
-		/// Advances for @c duration.
-		void run_for(const HalfCycles duration);
+	/// Advances for @c duration.
+	void run_for(const HalfCycles duration);
 
-		/// Types the next character now, if there is one.
-		/// @returns @c true if there was anything left to type; @c false otherwise.
-		bool type_next_character();
+	/// Types the next character now, if there is one.
+	/// @returns @c true if there was anything left to type; @c false otherwise.
+	bool type_next_character();
 
-		/// Adds the contents of @c str to the end of the current string.
-		void append(const std::string &str);
+	/// Adds the contents of @c str to the end of the current string.
+	void append(const std::string &str);
 
-		const char BeginString = 0x02;	// i.e. ASCII start of text
-		const char EndString = 0x03;	// i.e. ASCII end of text
+	const char BeginString = 0x02;	// i.e. ASCII start of text
+	const char EndString = 0x03;	// i.e. ASCII end of text
 
-	private:
-		std::string string_;
-		std::size_t string_pointer_ = 0;
+private:
+	std::string string_;
+	std::size_t string_pointer_ = 0;
 
-		const HalfCycles frequency_;
-		HalfCycles counter_;
-		int phase_ = 0;
+	const HalfCycles frequency_;
+	HalfCycles counter_;
+	int phase_ = 0;
 
-		Delegate *const delegate_;
-		CharacterMapper &character_mapper_;
+	Delegate *const delegate_;
+	CharacterMapper &character_mapper_;
 
-		uint16_t try_type_next_character();
-		const uint16_t *sequence_for_character(char) const;
+	uint16_t try_type_next_character();
+	const uint16_t *sequence_for_character(char) const;
 };
 
 /*!
@@ -106,47 +106,47 @@ class Typer {
 */
 template <typename CMapper>
 class TypeRecipient: public Typer::Delegate {
-	protected:
-		template <typename... Args> TypeRecipient(Args&&... args) : character_mapper(std::forward<Args>(args)...) {}
+protected:
+	template <typename... Args> TypeRecipient(Args&&... args) : character_mapper(std::forward<Args>(args)...) {}
 
-		/// Attaches a typer to this class that will type @c string using @c character_mapper as a source.
-		void add_typer(const std::string &string) {
-			if(!typer_) {
-				typer_ = std::make_unique<Typer>(string, get_typer_delay(string), get_typer_frequency(), character_mapper, this);
-			} else {
-				typer_->append(string);
-			}
+	/// Attaches a typer to this class that will type @c string using @c character_mapper as a source.
+	void add_typer(const std::string &string) {
+		if(!typer_) {
+			typer_ = std::make_unique<Typer>(string, get_typer_delay(string), get_typer_frequency(), character_mapper, this);
+		} else {
+			typer_->append(string);
 		}
+	}
 
-		/*!
-			@returns @c true if the character mapper provides a mapping for @c c; @c false otherwise.
-		*/
-		bool can_type(char c) const {
-			const auto sequence = character_mapper.sequence_for_character(c);
-			return sequence && sequence[0] != MachineTypes::MappedKeyboardMachine::KeyNotMapped;
-		}
+	/*!
+		@returns @c true if the character mapper provides a mapping for @c c; @c false otherwise.
+	*/
+	bool can_type(char c) const {
+		const auto sequence = character_mapper.sequence_for_character(c);
+		return sequence && sequence[0] != MachineTypes::MappedKeyboardMachine::KeyNotMapped;
+	}
 
-		/*!
-			Provided in order to conform to that part of the Typer::Delegate interface that goes above and
-			beyond KeyboardMachine::Machine; responds to the end of typing by clearing all keys.
-		*/
-		void typer_reset(Typer *) override {
-			clear_all_keys();
+	/*!
+		Provided in order to conform to that part of the Typer::Delegate interface that goes above and
+		beyond KeyboardMachine::Machine; responds to the end of typing by clearing all keys.
+	*/
+	void typer_reset(Typer *) override {
+		clear_all_keys();
 
-			// It's unsafe to deallocate typer right now, since it is the caller, but also it has a small
-			// memory footprint and it's desireable not to imply that the subclass need call it any more.
-			// So shuffle it off into a siding.
-			previous_typer_ = std::move(typer_);
-			typer_ = nullptr;
-		}
+		// It's unsafe to deallocate typer right now, since it is the caller, but also it has a small
+		// memory footprint and it's desireable not to imply that the subclass need call it any more.
+		// So shuffle it off into a siding.
+		previous_typer_ = std::move(typer_);
+		typer_ = nullptr;
+	}
 
-		virtual HalfCycles get_typer_delay(const std::string &) const { return HalfCycles(0); }
-		virtual HalfCycles get_typer_frequency() const { return HalfCycles(0); }
-		std::unique_ptr<Typer> typer_;
+	virtual HalfCycles get_typer_delay(const std::string &) const { return HalfCycles(0); }
+	virtual HalfCycles get_typer_frequency() const { return HalfCycles(0); }
+	std::unique_ptr<Typer> typer_;
 
-	private:
-		std::unique_ptr<Typer> previous_typer_;
-		CMapper character_mapper;
+private:
+	std::unique_ptr<Typer> previous_typer_;
+	CMapper character_mapper;
 };
 
 }
diff --git a/Numeric/CRC.hpp b/Numeric/CRC.hpp
index 9b99b5726..76fa186a9 100644
--- a/Numeric/CRC.hpp
+++ b/Numeric/CRC.hpp
@@ -26,81 +26,82 @@ constexpr uint8_t reverse_byte(uint8_t byte) {
 }
 
 /*! Provides a class capable of generating a CRC from source data. */
-template <typename IntType, IntType reset_value, IntType output_xor, bool reflect_input, bool reflect_output> class Generator {
-	public:
-		/*!
-			Instantiates a CRC16 that will compute the CRC16 specified by the supplied
-			@c polynomial and @c reset_value.
-		*/
-		constexpr Generator(IntType polynomial) noexcept: value_(reset_value) {
-			const IntType top_bit = IntType(~(IntType(~0) >> 1));
-			for(int c = 0; c < 256; c++) {
-				IntType shift_value = IntType(c << multibyte_shift);
-				for(int b = 0; b < 8; b++) {
-					IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0;
-					shift_value = IntType(shift_value << 1) ^ exclusive_or;
-				}
-				xor_table[c] = shift_value;
+template <typename IntType, IntType reset_value, IntType output_xor, bool reflect_input, bool reflect_output>
+class Generator {
+public:
+	/*!
+		Instantiates a CRC16 that will compute the CRC16 specified by the supplied
+		@c polynomial and @c reset_value.
+	*/
+	constexpr Generator(IntType polynomial) noexcept: value_(reset_value) {
+		const IntType top_bit = IntType(~(IntType(~0) >> 1));
+		for(int c = 0; c < 256; c++) {
+			IntType shift_value = IntType(c << multibyte_shift);
+			for(int b = 0; b < 8; b++) {
+				IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0;
+				shift_value = IntType(shift_value << 1) ^ exclusive_or;
 			}
+			xor_table[c] = shift_value;
 		}
+	}
 
-		/// Resets the CRC to the reset value.
-		void reset() { value_ = reset_value; }
+	/// Resets the CRC to the reset value.
+	void reset() { value_ = reset_value; }
 
-		/// Updates the CRC to include @c byte.
-		void add(uint8_t byte) {
-			if constexpr (reflect_input) byte = reverse_byte(byte);
-			value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]);
-		}
+	/// Updates the CRC to include @c byte.
+	void add(uint8_t byte) {
+		if constexpr (reflect_input) byte = reverse_byte(byte);
+		value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]);
+	}
 
-		/// @returns The current value of the CRC.
-		inline IntType get_value() const {
-			IntType result = value_ ^ output_xor;
-			if constexpr (reflect_output) {
-				IntType reflected_output = 0;
-				for(std::size_t c = 0; c < sizeof(IntType); ++c) {
-					reflected_output = IntType(reflected_output << 8) | IntType(reverse_byte(result & 0xff));
-					result >>= 8;
-				}
-				return reflected_output;
+	/// @returns The current value of the CRC.
+	inline IntType get_value() const {
+		IntType result = value_ ^ output_xor;
+		if constexpr (reflect_output) {
+			IntType reflected_output = 0;
+			for(std::size_t c = 0; c < sizeof(IntType); ++c) {
+				reflected_output = IntType(reflected_output << 8) | IntType(reverse_byte(result & 0xff));
+				result >>= 8;
 			}
-			return result;
+			return reflected_output;
 		}
+		return result;
+	}
 
-		/// Sets the current value of the CRC.
-		inline void set_value(IntType value) { value_ = value; }
+	/// Sets the current value of the CRC.
+	inline void set_value(IntType value) { value_ = value; }
 
-		/*!
-			A compound for:
+	/*!
+		A compound for:
 
-				reset()
-				[add all data from @c data]
-				get_value()
-		*/
-		template <typename Collection> IntType compute_crc(const Collection &data) {
-			return compute_crc(data.begin(), data.end());
+			reset()
+			[add all data from @c data]
+			get_value()
+	*/
+	template <typename Collection> IntType compute_crc(const Collection &data) {
+		return compute_crc(data.begin(), data.end());
+	}
+
+	/*!
+		A compound for:
+
+			reset()
+			[add all data from @c begin to @c end]
+			get_value()
+	*/
+	template <typename Iterator> IntType compute_crc(Iterator begin, Iterator end) {
+		reset();
+		while(begin != end) {
+			add(*begin);
+			++begin;
 		}
+		return get_value();
+	}
 
-		/*!
-			A compound for:
-
-				reset()
-				[add all data from @c begin to @c end]
-				get_value()
-		*/
-		template <typename Iterator> IntType compute_crc(Iterator begin, Iterator end) {
-			reset();
-			while(begin != end) {
-				add(*begin);
-				++begin;
-			}
-			return get_value();
-		}
-
-	private:
-		static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8;
-		IntType xor_table[256];
-		IntType value_;
+private:
+	static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8;
+	IntType xor_table[256];
+	IntType value_;
 };
 
 /*!
diff --git a/Numeric/LFSR.hpp b/Numeric/LFSR.hpp
index ae9019efe..117ae7dad 100644
--- a/Numeric/LFSR.hpp
+++ b/Numeric/LFSR.hpp
@@ -40,41 +40,41 @@ template <> struct LSFRPolynomial<uint8_t> {
 	in the specified int type.
 */
 template <typename IntType = uint64_t, IntType polynomial = LSFRPolynomial<IntType>::value> class LFSR {
-	public:
-		/*!
-			Constructs an LFSR with a random initial value.
-		*/
-		constexpr LFSR() noexcept {
-			// Randomise the value, ensuring it doesn't end up being 0;
-			// don't set any top bits, in case this is a signed type.
-			while(!value_) {
-				uint8_t *value_byte = reinterpret_cast<uint8_t *>(&value_);
-				for(size_t c = 0; c < sizeof(IntType); ++c) {
-					*value_byte = uint8_t(uint64_t(rand()) * 127 / RAND_MAX);
-					++value_byte;
-				}
+public:
+	/*!
+		Constructs an LFSR with a random initial value.
+	*/
+	constexpr LFSR() noexcept {
+		// Randomise the value, ensuring it doesn't end up being 0;
+		// don't set any top bits, in case this is a signed type.
+		while(!value_) {
+			uint8_t *value_byte = reinterpret_cast<uint8_t *>(&value_);
+			for(size_t c = 0; c < sizeof(IntType); ++c) {
+				*value_byte = uint8_t(uint64_t(rand()) * 127 / RAND_MAX);
+				++value_byte;
 			}
 		}
+	}
 
-		/*!
-			Constructs an LFSR with the specified initial value.
+	/*!
+		Constructs an LFSR with the specified initial value.
 
-			An initial value of 0 is invalid.
-		*/
-		LFSR(IntType initial_value) : value_(initial_value) {}
+		An initial value of 0 is invalid.
+	*/
+	LFSR(IntType initial_value) : value_(initial_value) {}
 
-		/*!
-			Advances the LSFR, returning either an @c IntType of value @c 1 or @c 0,
-			determining the bit that was just shifted out.
-		*/
-		IntType next() {
-			const auto result = IntType(value_ & 1);
-			value_ = IntType((value_ >> 1) ^ (result * polynomial));
-			return result;
-		}
+	/*!
+		Advances the LSFR, returning either an @c IntType of value @c 1 or @c 0,
+		determining the bit that was just shifted out.
+	*/
+	IntType next() {
+		const auto result = IntType(value_ & 1);
+		value_ = IntType((value_ >> 1) ^ (result * polynomial));
+		return result;
+	}
 
-	private:
-		IntType value_ = 0;
+private:
+	IntType value_ = 0;
 };
 
 template <uint64_t polynomial> class LFSRv: public LFSR<typename MinIntTypeValue<polynomial>::type, polynomial> {};
diff --git a/Numeric/NumericCoder.hpp b/Numeric/NumericCoder.hpp
index d33280310..6644a5ead 100644
--- a/Numeric/NumericCoder.hpp
+++ b/Numeric/NumericCoder.hpp
@@ -28,47 +28,47 @@ namespace Numeric {
 ///				= 65
 ///
 template <int... Sizes> class NumericCoder {
-	public:
-		/// Modifies @c target to hold @c value at @c index.
-		template <int index> static void encode(int &target, int value) {
-			static_assert(index < sizeof...(Sizes), "Index must be within range");
-			NumericEncoder<Sizes...>::template encode<index>(target, value);
-		}
+public:
+	/// Modifies @c target to hold @c value at @c index.
+	template <int index> static void encode(int &target, int value) {
+		static_assert(index < sizeof...(Sizes), "Index must be within range");
+		NumericEncoder<Sizes...>::template encode<index>(target, value);
+	}
 
-		/// @returns The value from @c source at @c index.
-		template <int index> static int decode(int source) {
-			static_assert(index < sizeof...(Sizes), "Index must be within range");
-			return NumericDecoder<Sizes...>::template decode<index>(source);
-		}
+	/// @returns The value from @c source at @c index.
+	template <int index> static int decode(int source) {
+		static_assert(index < sizeof...(Sizes), "Index must be within range");
+		return NumericDecoder<Sizes...>::template decode<index>(source);
+	}
 
-	private:
+private:
 
-		template <int size, int... Tail>
-		struct NumericEncoder {
-			template <int index, int i = 0, int divider = 1> static void encode(int &target, int value) {
-				if constexpr (i == index) {
-					const int suffix = target % divider;
-					target /= divider;
-					target -= target % size;
-					target += value % size;
-					target *= divider;
-					target += suffix;
-				} else {
-					NumericEncoder<Tail...>::template encode<index, i+1, divider*size>(target, value);
-				}
+	template <int size, int... Tail>
+	struct NumericEncoder {
+		template <int index, int i = 0, int divider = 1> static void encode(int &target, int value) {
+			if constexpr (i == index) {
+				const int suffix = target % divider;
+				target /= divider;
+				target -= target % size;
+				target += value % size;
+				target *= divider;
+				target += suffix;
+			} else {
+				NumericEncoder<Tail...>::template encode<index, i+1, divider*size>(target, value);
 			}
-		};
+		}
+	};
 
-		template <int size, int... Tail>
-		struct NumericDecoder {
-			template <int index, int i = 0, int divider = 1> static int decode(int source) {
-				if constexpr (i == index) {
-					return (source / divider) % size;
-				} else {
-					return NumericDecoder<Tail...>::template decode<index, i+1, divider*size>(source);
-				}
+	template <int size, int... Tail>
+	struct NumericDecoder {
+		template <int index, int i = 0, int divider = 1> static int decode(int source) {
+			if constexpr (i == index) {
+				return (source / divider) % size;
+			} else {
+				return NumericDecoder<Tail...>::template decode<index, i+1, divider*size>(source);
 			}
-		};
+		}
+	};
 };
 
 }
diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp
index eb69fa3ff..a8204bab1 100644
--- a/Outputs/CRT/CRT.hpp
+++ b/Outputs/CRT/CRT.hpp
@@ -55,297 +55,297 @@ class Delegate {
 	colour phase for colour composite video.
 */
 class CRT {
-	private:
-		// The incoming clock lengths will be multiplied by @c time_multiplier_; this increases
-		// precision across the line.
-		int time_multiplier_ = 1;
+private:
+	// The incoming clock lengths will be multiplied by @c time_multiplier_; this increases
+	// precision across the line.
+	int time_multiplier_ = 1;
 
-		// Two flywheels regulate scanning; the vertical will have a range much greater than the horizontal;
-		// the output divider is what that'll need to be divided by to reduce it into a 16-bit range as
-		// posted on to the scan target.
-		std::unique_ptr<Flywheel> horizontal_flywheel_, vertical_flywheel_;
-		int vertical_flywheel_output_divider_ = 1;
-		int cycles_since_horizontal_sync_ = 0;
-		Display::ScanTarget::Scan::EndPoint end_point(uint16_t data_offset);
+	// Two flywheels regulate scanning; the vertical will have a range much greater than the horizontal;
+	// the output divider is what that'll need to be divided by to reduce it into a 16-bit range as
+	// posted on to the scan target.
+	std::unique_ptr<Flywheel> horizontal_flywheel_, vertical_flywheel_;
+	int vertical_flywheel_output_divider_ = 1;
+	int cycles_since_horizontal_sync_ = 0;
+	Display::ScanTarget::Scan::EndPoint end_point(uint16_t data_offset);
 
-		struct Scan {
-			enum Type {
-				Sync, Level, Data, Blank, ColourBurst
-			} type = Scan::Blank;
-			int number_of_cycles = 0, number_of_samples = 0;
-			uint8_t phase = 0, amplitude = 0;
-		};
-		void output_scan(const Scan *scan);
+	struct Scan {
+		enum Type {
+			Sync, Level, Data, Blank, ColourBurst
+		} type = Scan::Blank;
+		int number_of_cycles = 0, number_of_samples = 0;
+		uint8_t phase = 0, amplitude = 0;
+	};
+	void output_scan(const Scan *scan);
 
-		uint8_t colour_burst_amplitude_ = 30;
-		int colour_burst_phase_adjustment_ = 0xff;
+	uint8_t colour_burst_amplitude_ = 30;
+	int colour_burst_phase_adjustment_ = 0xff;
 
-		int64_t phase_denominator_ = 1;
-		int64_t phase_numerator_ = 0;
-		int64_t colour_cycle_numerator_ = 1;
-		bool is_alternate_line_ = false, phase_alternates_ = false, should_be_alternate_line_ = false;
+	int64_t phase_denominator_ = 1;
+	int64_t phase_numerator_ = 0;
+	int64_t colour_cycle_numerator_ = 1;
+	bool is_alternate_line_ = false, phase_alternates_ = false, should_be_alternate_line_ = false;
 
-		void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type, int number_of_samples);
-		Flywheel::SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, int cycles_to_run_for, int *cycles_advanced);
-		Flywheel::SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced);
+	void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type, int number_of_samples);
+	Flywheel::SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, int cycles_to_run_for, int *cycles_advanced);
+	Flywheel::SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced);
 
-		Delegate *delegate_ = nullptr;
-		int frames_since_last_delegate_call_ = 0;
+	Delegate *delegate_ = nullptr;
+	int frames_since_last_delegate_call_ = 0;
 
-		bool is_receiving_sync_ = false;			// @c true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync); @c false otherwise.
-		bool is_accumulating_sync_ = false;			// @c true if a sync level has triggered the suspicion that a vertical sync might be in progress; @c false otherwise.
-		bool is_refusing_sync_ = false;				// @c true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing vertical sync.
-		int sync_capacitor_charge_threshold_ = 0;	// Charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync.
-		int cycles_of_sync_ = 0;					// The number of cycles since the potential vertical sync began.
-		int cycles_since_sync_ = 0;					// The number of cycles since last in sync, for defeating the possibility of this being a vertical sync.
+	bool is_receiving_sync_ = false;			// @c true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync); @c false otherwise.
+	bool is_accumulating_sync_ = false;			// @c true if a sync level has triggered the suspicion that a vertical sync might be in progress; @c false otherwise.
+	bool is_refusing_sync_ = false;				// @c true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing vertical sync.
+	int sync_capacitor_charge_threshold_ = 0;	// Charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync.
+	int cycles_of_sync_ = 0;					// The number of cycles since the potential vertical sync began.
+	int cycles_since_sync_ = 0;					// The number of cycles since last in sync, for defeating the possibility of this being a vertical sync.
 
-		int cycles_per_line_ = 1;
+	int cycles_per_line_ = 1;
 
-		Outputs::Display::ScanTarget *scan_target_ = &Outputs::Display::NullScanTarget::singleton;
-		Outputs::Display::ScanTarget::Modals scan_target_modals_;
-		static constexpr uint8_t DefaultAmplitude = 41;	// Based upon a black level to maximum excursion and positive burst peak of: NTSC: 882 & 143; PAL: 933 & 150.
+	Outputs::Display::ScanTarget *scan_target_ = &Outputs::Display::NullScanTarget::singleton;
+	Outputs::Display::ScanTarget::Modals scan_target_modals_;
+	static constexpr uint8_t DefaultAmplitude = 41;	// Based upon a black level to maximum excursion and positive burst peak of: NTSC: 882 & 143; PAL: 933 & 150.
 
 #ifndef NDEBUG
-		size_t allocated_data_length_ = std::numeric_limits<size_t>::min();
+	size_t allocated_data_length_ = std::numeric_limits<size_t>::min();
 #endif
 
-	public:
-		/*!	Constructs the CRT with a specified clock rate, height and colour subcarrier frequency.
-			The requested number of buffers, each with the requested number of bytes per pixel,
-			is created for the machine to write raw pixel data to.
+public:
+	/*!	Constructs the CRT with a specified clock rate, height and colour subcarrier frequency.
+		The requested number of buffers, each with the requested number of bytes per pixel,
+		is created for the machine to write raw pixel data to.
 
-			@param cycles_per_line The clock rate at which this CRT will be driven, specified as the number
-			of cycles expected to take up one whole scanline of the display.
+		@param cycles_per_line The clock rate at which this CRT will be driven, specified as the number
+		of cycles expected to take up one whole scanline of the display.
 
-			@param clocks_per_pixel_greatest_common_divisor The GCD of all potential lengths of a pixel
-			in terms of the clock rate given as @c cycles_per_line.
+		@param clocks_per_pixel_greatest_common_divisor The GCD of all potential lengths of a pixel
+		in terms of the clock rate given as @c cycles_per_line.
 
-			@param height_of_display The number of lines that nominally form one field of the display, rounded
-			up to the next whole integer.
+		@param height_of_display The number of lines that nominally form one field of the display, rounded
+		up to the next whole integer.
 
-			@param colour_cycle_numerator Specifies the numerator for the per-line frequency of the colour subcarrier.
+		@param colour_cycle_numerator Specifies the numerator for the per-line frequency of the colour subcarrier.
 
-			@param colour_cycle_denominator Specifies the denominator for the per-line frequency of the colour subcarrier.
-			The colour subcarrier is taken to have colour_cycle_numerator/colour_cycle_denominator cycles per line.
+		@param colour_cycle_denominator Specifies the denominator for the per-line frequency of the colour subcarrier.
+		The colour subcarrier is taken to have colour_cycle_numerator/colour_cycle_denominator cycles per line.
 
-			@param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside),
-			in multiples of half a line.
+		@param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside),
+		in multiples of half a line.
 
-			@param data_type The format that the caller will use for input data.
-		*/
-		CRT(int cycles_per_line,
-			int clocks_per_pixel_greatest_common_divisor,
-			int height_of_display,
-			Outputs::Display::ColourSpace colour_space,
-			int colour_cycle_numerator,
-			int colour_cycle_denominator,
-			int vertical_sync_half_lines,
-			bool should_alternate,
-			Outputs::Display::InputDataType data_type);
+		@param data_type The format that the caller will use for input data.
+	*/
+	CRT(int cycles_per_line,
+		int clocks_per_pixel_greatest_common_divisor,
+		int height_of_display,
+		Outputs::Display::ColourSpace colour_space,
+		int colour_cycle_numerator,
+		int colour_cycle_denominator,
+		int vertical_sync_half_lines,
+		bool should_alternate,
+		Outputs::Display::InputDataType data_type);
 
-		/*! Constructs a monitor-style CRT — one that will take only an RGB or monochrome signal, and therefore has
-			no colour space or colour subcarrier frequency. This monitor will automatically map colour bursts to the black level.
-		*/
-		CRT(int cycles_per_line,
-			int clocks_per_pixel_greatest_common_divisor,
-			int height_of_display,
-			int vertical_sync_half_lines,
-			Outputs::Display::InputDataType data_type);
+	/*! Constructs a monitor-style CRT — one that will take only an RGB or monochrome signal, and therefore has
+		no colour space or colour subcarrier frequency. This monitor will automatically map colour bursts to the black level.
+	*/
+	CRT(int cycles_per_line,
+		int clocks_per_pixel_greatest_common_divisor,
+		int height_of_display,
+		int vertical_sync_half_lines,
+		Outputs::Display::InputDataType data_type);
 
-		/*!	Exactly identical to calling the designated constructor with colour subcarrier information
-			looked up by display type.
-		*/
-		CRT(int cycles_per_line,
-			int minimum_cycles_per_pixel,
-			Outputs::Display::Type display_type,
-			Outputs::Display::InputDataType data_type);
+	/*!	Exactly identical to calling the designated constructor with colour subcarrier information
+		looked up by display type.
+	*/
+	CRT(int cycles_per_line,
+		int minimum_cycles_per_pixel,
+		Outputs::Display::Type display_type,
+		Outputs::Display::InputDataType data_type);
 
-		/*!	Constructs a CRT with no guaranteed expectations as to input signal other than data type;
-			this allows for callers that intend to rely on @c set_new_timing.
-		*/
-		CRT(Outputs::Display::InputDataType data_type);
+	/*!	Constructs a CRT with no guaranteed expectations as to input signal other than data type;
+		this allows for callers that intend to rely on @c set_new_timing.
+	*/
+	CRT(Outputs::Display::InputDataType data_type);
 
-		/*!	Resets the CRT with new timing information. The CRT then continues as though the new timing had
-			been provided at construction. */
-		void set_new_timing(
-			int cycles_per_line,
-			int height_of_display,
-			Outputs::Display::ColourSpace colour_space,
-			int colour_cycle_numerator,
-			int colour_cycle_denominator,
-			int vertical_sync_half_lines,
-			bool should_alternate);
+	/*!	Resets the CRT with new timing information. The CRT then continues as though the new timing had
+		been provided at construction. */
+	void set_new_timing(
+		int cycles_per_line,
+		int height_of_display,
+		Outputs::Display::ColourSpace colour_space,
+		int colour_cycle_numerator,
+		int colour_cycle_denominator,
+		int vertical_sync_half_lines,
+		bool should_alternate);
 
-		/*!	Resets the CRT with new timing information derived from a new display type. The CRT then continues
-			as though the new timing had been provided at construction. */
-		void set_new_display_type(
-			int cycles_per_line,
-			Outputs::Display::Type display_type);
+	/*!	Resets the CRT with new timing information derived from a new display type. The CRT then continues
+		as though the new timing had been provided at construction. */
+	void set_new_display_type(
+		int cycles_per_line,
+		Outputs::Display::Type display_type);
 
-		/*!	Changes the type of data being supplied as input.
-		*/
-		void set_new_data_type(Outputs::Display::InputDataType data_type);
+	/*!	Changes the type of data being supplied as input.
+	*/
+	void set_new_data_type(Outputs::Display::InputDataType data_type);
 
-		/*!	Sets the CRT's intended aspect ratio — 4.0/3.0 by default.
-		*/
-		void set_aspect_ratio(float aspect_ratio);
+	/*!	Sets the CRT's intended aspect ratio — 4.0/3.0 by default.
+	*/
+	void set_aspect_ratio(float aspect_ratio);
 
-		/*!	Output at the sync level.
+	/*!	Output at the sync level.
 
-			@param number_of_cycles The amount of time to putput sync for.
-		*/
-		void output_sync(int number_of_cycles);
+		@param number_of_cycles The amount of time to putput sync for.
+	*/
+	void output_sync(int number_of_cycles);
 
-		/*!	Output at the blanking level.
+	/*!	Output at the blanking level.
 
-			@param number_of_cycles The amount of time to putput the blanking level for.
-		*/
-		void output_blank(int number_of_cycles);
+		@param number_of_cycles The amount of time to putput the blanking level for.
+	*/
+	void output_blank(int number_of_cycles);
 
-		/*!	Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
+	/*!	Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
 
-			@param number_of_cycles The number of cycles to repeat the output for.
-		*/
-		void output_level(int number_of_cycles);
+		@param number_of_cycles The number of cycles to repeat the output for.
+	*/
+	void output_level(int number_of_cycles);
 
-		/*!	Outputs @c value for @c number_of_cycles .
+	/*!	Outputs @c value for @c number_of_cycles .
 
-			@param value The value to output.
-			@param number_of_cycles The number of cycles to repeat the output for.
-		*/
-		template <typename IntT>
-		void output_level(int number_of_cycles, IntT value) {
-			auto colour_pointer = reinterpret_cast<IntT *>(begin_data(1));
-			if(colour_pointer) *colour_pointer = value;
-			output_level(number_of_cycles);
-		}
+		@param value The value to output.
+		@param number_of_cycles The number of cycles to repeat the output for.
+	*/
+	template <typename IntT>
+	void output_level(int number_of_cycles, IntT value) {
+		auto colour_pointer = reinterpret_cast<IntT *>(begin_data(1));
+		if(colour_pointer) *colour_pointer = value;
+		output_level(number_of_cycles);
+	}
 
-		/*!	Declares that the caller has created a run of data via @c begin_data that is at least @c number_of_samples
-			long, and that the first @c number_of_samples should be spread over @c number_of_cycles.
+	/*!	Declares that the caller has created a run of data via @c begin_data that is at least @c number_of_samples
+		long, and that the first @c number_of_samples should be spread over @c number_of_cycles.
 
-			@param number_of_cycles The amount of data to output.
+		@param number_of_cycles The amount of data to output.
 
-			@param number_of_samples The number of samples of input data to output.
+		@param number_of_samples The number of samples of input data to output.
 
-			@see @c begin_data
-		*/
-		void output_data(int number_of_cycles, size_t number_of_samples);
+		@see @c begin_data
+	*/
+	void output_data(int number_of_cycles, size_t number_of_samples);
 
-		/*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */
-		void output_data(int number_of_cycles) {
-			output_data(number_of_cycles, size_t(number_of_cycles));
-		}
+	/*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */
+	void output_data(int number_of_cycles) {
+		output_data(number_of_cycles, size_t(number_of_cycles));
+	}
 
-		/*!	Outputs a colour burst.
+	/*!	Outputs a colour burst.
 
-			@param number_of_cycles The length of the colour burst.
+		@param number_of_cycles The length of the colour burst.
 
-			@param phase The initial phase of the colour burst in a measuring system with 256 units
-			per circle, e.g. 0 = 0 degrees, 128 = 180 degrees, 256 = 360 degree.
+		@param phase The initial phase of the colour burst in a measuring system with 256 units
+		per circle, e.g. 0 = 0 degrees, 128 = 180 degrees, 256 = 360 degree.
 
-			@param amplitude The amplitude of the colour burst in 1/255ths of the amplitude of the
-			positive portion of the wave.
-		*/
-		void output_colour_burst(int number_of_cycles, uint8_t phase, bool is_alternate_line = false, uint8_t amplitude = DefaultAmplitude);
+		@param amplitude The amplitude of the colour burst in 1/255ths of the amplitude of the
+		positive portion of the wave.
+	*/
+	void output_colour_burst(int number_of_cycles, uint8_t phase, bool is_alternate_line = false, uint8_t amplitude = DefaultAmplitude);
 
-		/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
+	/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
 
-			@param number_of_cycles The length of the colour burst;
-		*/
-		void output_default_colour_burst(int number_of_cycles, uint8_t amplitude = DefaultAmplitude);
+		@param number_of_cycles The length of the colour burst;
+	*/
+	void output_default_colour_burst(int number_of_cycles, uint8_t amplitude = DefaultAmplitude);
 
-		/*! Sets the current phase of the colour subcarrier used by output_default_colour_burst.
+	/*! Sets the current phase of the colour subcarrier used by output_default_colour_burst.
 
-			@param phase The normalised instantaneous phase. 0.0f is the start of a colour cycle, 1.0f is the
-			end of a colour cycle, 0.25f is a quarter of the way through a colour cycle, etc.
-		*/
-		void set_immediate_default_phase(float phase);
+		@param phase The normalised instantaneous phase. 0.0f is the start of a colour cycle, 1.0f is the
+		end of a colour cycle, 0.25f is a quarter of the way through a colour cycle, etc.
+	*/
+	void set_immediate_default_phase(float phase);
 
-		/*!	Attempts to allocate the given number of output samples for writing.
+	/*!	Attempts to allocate the given number of output samples for writing.
 
-			The beginning of the most recently allocated area is used as the start
-			of data written by a call to @c output_data; it is acceptable to write and to
-			output less data than the amount requested but that may be less efficient.
+		The beginning of the most recently allocated area is used as the start
+		of data written by a call to @c output_data; it is acceptable to write and to
+		output less data than the amount requested but that may be less efficient.
 
-			Allocation should fail only if emulation is running significantly below real speed.
+		Allocation should fail only if emulation is running significantly below real speed.
 
-			@param required_length The number of samples to allocate.
-			@returns A pointer to the allocated area if room is available; @c nullptr otherwise.
-		*/
-		inline uint8_t *begin_data(std::size_t required_length, std::size_t required_alignment = 1) {
-			const auto result = scan_target_->begin_data(required_length, required_alignment);
+		@param required_length The number of samples to allocate.
+		@returns A pointer to the allocated area if room is available; @c nullptr otherwise.
+	*/
+	inline uint8_t *begin_data(std::size_t required_length, std::size_t required_alignment = 1) {
+		const auto result = scan_target_->begin_data(required_length, required_alignment);
 #ifndef NDEBUG
-			// If data was allocated, make a record of how much so as to be able to hold the caller to that
-			// contract later. If allocation failed, don't constrain the caller. This allows callers that
-			// allocate on demand but may allow one failure to hold for a longer period — e.g. until the
-			// next line.
-			allocated_data_length_ = result ? required_length : std::numeric_limits<size_t>::max();
+		// If data was allocated, make a record of how much so as to be able to hold the caller to that
+		// contract later. If allocation failed, don't constrain the caller. This allows callers that
+		// allocate on demand but may allow one failure to hold for a longer period — e.g. until the
+		// next line.
+		allocated_data_length_ = result ? required_length : std::numeric_limits<size_t>::max();
 #endif
-			return result;
-		}
+		return result;
+	}
 
-		/*!	Sets the gamma exponent for the simulated screen. */
-		void set_input_gamma(float gamma);
+	/*!	Sets the gamma exponent for the simulated screen. */
+	void set_input_gamma(float gamma);
 
-		enum CompositeSourceType {
-			/// The composite function provides continuous output.
-			Continuous,
-			/// The composite function provides discrete output with four unique values per colour cycle.
-			DiscreteFourSamplesPerCycle
-		};
+	enum CompositeSourceType {
+		/// The composite function provides continuous output.
+		Continuous,
+		/// The composite function provides discrete output with four unique values per colour cycle.
+		DiscreteFourSamplesPerCycle
+	};
 
-		/*! Provides information about the type of output the composite sampling function provides, discrete or continuous.
+	/*! Provides information about the type of output the composite sampling function provides, discrete or continuous.
 
-			This is necessary because the CRT implementation samples discretely and therefore can use fewer intermediate
-			samples if it can exactly duplicate the sampling rate and placement of the composite sampling function.
+		This is necessary because the CRT implementation samples discretely and therefore can use fewer intermediate
+		samples if it can exactly duplicate the sampling rate and placement of the composite sampling function.
 
-			A continuous function is assumed by default.
+		A continuous function is assumed by default.
 
-			@param type The type of output provided by the function supplied to `set_composite_sampling_function`.
-			@param offset_of_first_sample The relative position within a full cycle of the colour subcarrier at which the
-			first sample falls. E.g. 0.125 means "at 1/8th of the way through the complete cycle".
-		*/
-		void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f);
+		@param type The type of output provided by the function supplied to `set_composite_sampling_function`.
+		@param offset_of_first_sample The relative position within a full cycle of the colour subcarrier at which the
+		first sample falls. E.g. 0.125 means "at 1/8th of the way through the complete cycle".
+	*/
+	void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f);
 
-		/*!	Nominates a section of the display to crop to for output. */
-		void set_visible_area(Outputs::Display::Rect visible_area);
+	/*!	Nominates a section of the display to crop to for output. */
+	void set_visible_area(Outputs::Display::Rect visible_area);
 
-		/*!	@returns The rectangle describing a subset of the display, allowing for sync periods. */
-		Outputs::Display::Rect get_rect_for_area(
-			int first_line_after_sync,
-			int number_of_lines,
-			int first_cycle_after_sync,
-			int number_of_cycles,
-			float aspect_ratio) const;
+	/*!	@returns The rectangle describing a subset of the display, allowing for sync periods. */
+	Outputs::Display::Rect get_rect_for_area(
+		int first_line_after_sync,
+		int number_of_lines,
+		int first_cycle_after_sync,
+		int number_of_cycles,
+		float aspect_ratio) const;
 
-		/*!	Sets the CRT delegate; set to @c nullptr if no delegate is desired. */
-		inline void set_delegate(Delegate *delegate) {
-			delegate_ = delegate;
-		}
+	/*!	Sets the CRT delegate; set to @c nullptr if no delegate is desired. */
+	inline void set_delegate(Delegate *delegate) {
+		delegate_ = delegate;
+	}
 
-		/*! Sets the scan target for CRT output. */
-		void set_scan_target(Outputs::Display::ScanTarget *);
+	/*! Sets the scan target for CRT output. */
+	void set_scan_target(Outputs::Display::ScanTarget *);
 
-		/*!
-			Gets current scan status, with time based fields being in the input scale — e.g. if you're supplying
-			86 cycles/line and 98 lines/field then it'll return a field duration of 86*98.
-		*/
-		Outputs::Display::ScanStatus get_scaled_scan_status() const;
+	/*!
+		Gets current scan status, with time based fields being in the input scale — e.g. if you're supplying
+		86 cycles/line and 98 lines/field then it'll return a field duration of 86*98.
+	*/
+	Outputs::Display::ScanStatus get_scaled_scan_status() const;
 
-		/*! Sets the display type that will be nominated to the scan target. */
-		void set_display_type(Outputs::Display::DisplayType);
+	/*! Sets the display type that will be nominated to the scan target. */
+	void set_display_type(Outputs::Display::DisplayType);
 
-		/*! Gets the last display type provided to set_display_type. */
-		Outputs::Display::DisplayType get_display_type() const;
+	/*! Gets the last display type provided to set_display_type. */
+	Outputs::Display::DisplayType get_display_type() const;
 
-		/*! Sets the offset to apply to phase when using the PhaseLinkedLuminance8 input data type. */
-		void set_phase_linked_luminance_offset(float);
+	/*! Sets the offset to apply to phase when using the PhaseLinkedLuminance8 input data type. */
+	void set_phase_linked_luminance_offset(float);
 
-		/*! Sets the input data type. */
-		void set_input_data_type(Outputs::Display::InputDataType);
+	/*! Sets the input data type. */
+	void set_input_data_type(Outputs::Display::InputDataType);
 
-		/*! Sets the output brightness. */
-		void set_brightness(float);
+	/*! Sets the output brightness. */
+	void set_brightness(float);
 };
 
 /*!
@@ -353,44 +353,44 @@ class CRT {
 	ask its receiver to try a different display frequency.
 */
 template <typename Receiver> class CRTFrequencyMismatchWarner: public Outputs::CRT::Delegate {
-	public:
-		CRTFrequencyMismatchWarner(Receiver &receiver) : receiver_(receiver) {}
+public:
+	CRTFrequencyMismatchWarner(Receiver &receiver) : receiver_(receiver) {}
 
-		void crt_did_end_batch_of_frames(Outputs::CRT::CRT *, int number_of_frames, int number_of_unexpected_vertical_syncs) final {
-			frame_records_[frame_record_pointer_ % frame_records_.size()].number_of_frames = number_of_frames;
-			frame_records_[frame_record_pointer_ % frame_records_.size()].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs;
-			++frame_record_pointer_;
+	void crt_did_end_batch_of_frames(Outputs::CRT::CRT *, int number_of_frames, int number_of_unexpected_vertical_syncs) final {
+		frame_records_[frame_record_pointer_ % frame_records_.size()].number_of_frames = number_of_frames;
+		frame_records_[frame_record_pointer_ % frame_records_.size()].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs;
+		++frame_record_pointer_;
 
-			if(frame_record_pointer_*2 >= frame_records_.size()*3) {
-				int total_number_of_frames = 0;
-				int total_number_of_unexpected_vertical_syncs = 0;
-				for(const auto &record: frame_records_) {
-					total_number_of_frames += record.number_of_frames;
-					total_number_of_unexpected_vertical_syncs += record.number_of_unexpected_vertical_syncs;
-				}
+		if(frame_record_pointer_*2 >= frame_records_.size()*3) {
+			int total_number_of_frames = 0;
+			int total_number_of_unexpected_vertical_syncs = 0;
+			for(const auto &record: frame_records_) {
+				total_number_of_frames += record.number_of_frames;
+				total_number_of_unexpected_vertical_syncs += record.number_of_unexpected_vertical_syncs;
+			}
 
-				if(total_number_of_unexpected_vertical_syncs >= total_number_of_frames >> 1) {
-					reset();
-					receiver_.register_crt_frequency_mismatch();
-				}
+			if(total_number_of_unexpected_vertical_syncs >= total_number_of_frames >> 1) {
+				reset();
+				receiver_.register_crt_frequency_mismatch();
 			}
 		}
+	}
 
-		void reset() {
-			for(auto &record: frame_records_) {
-				record.number_of_frames = 0;
-				record.number_of_unexpected_vertical_syncs = 0;
-			}
+	void reset() {
+		for(auto &record: frame_records_) {
+			record.number_of_frames = 0;
+			record.number_of_unexpected_vertical_syncs = 0;
 		}
+	}
 
-	private:
-		Receiver &receiver_;
-		struct FrameRecord {
-			int number_of_frames = 0;
-			int number_of_unexpected_vertical_syncs = 0;
-		};
-		std::array<FrameRecord, 4> frame_records_;
-		size_t frame_record_pointer_ = 0;
+private:
+	Receiver &receiver_;
+	struct FrameRecord {
+		int number_of_frames = 0;
+		int number_of_unexpected_vertical_syncs = 0;
+	};
+	std::array<FrameRecord, 4> frame_records_;
+	size_t frame_record_pointer_ = 0;
 };
 
 }
diff --git a/Outputs/CRT/Internals/Flywheel.hpp b/Outputs/CRT/Internals/Flywheel.hpp
index b6914f197..abc3e52ed 100644
--- a/Outputs/CRT/Internals/Flywheel.hpp
+++ b/Outputs/CRT/Internals/Flywheel.hpp
@@ -229,32 +229,32 @@ struct Flywheel {
 			(counter_ >= expected_next_sync_ - (standard_period_ / 100));
 	}
 
-	private:
-		const int standard_period_;		// The idealised length of time between syncs.
-		const int retrace_time_;		// A constant indicating the amount of time it takes to perform a retrace.
-		const int sync_error_window_;	// A constant indicating the window either side of the next expected sync in which we'll accept other syncs.
+private:
+	const int standard_period_;		// The idealised length of time between syncs.
+	const int retrace_time_;		// A constant indicating the amount of time it takes to perform a retrace.
+	const int sync_error_window_;	// A constant indicating the window either side of the next expected sync in which we'll accept other syncs.
 
-		int counter_ = 0;				// Time since the _start_ of the last sync.
-		int counter_before_retrace_;	// The value of _counter immediately before retrace began.
-		int expected_next_sync_;		// Our current expection of when the next sync will be encountered (which implies velocity).
+	int counter_ = 0;				// Time since the _start_ of the last sync.
+	int counter_before_retrace_;	// The value of _counter immediately before retrace began.
+	int expected_next_sync_;		// Our current expection of when the next sync will be encountered (which implies velocity).
 
-		int number_of_surprises_ = 0;	// A count of the surprising syncs.
-		int number_of_retraces_ = 0;	// A count of the number of retraces to date.
+	int number_of_surprises_ = 0;	// A count of the surprising syncs.
+	int number_of_retraces_ = 0;	// A count of the number of retraces to date.
 
-		int last_adjustment_ = 0;		// The amount by which expected_next_sync_ was adjusted at the last sync.
+	int last_adjustment_ = 0;		// The amount by which expected_next_sync_ was adjusted at the last sync.
 
-		/*
-			Implementation notes:
+	/*
+		Implementation notes:
 
-			Retrace takes a fixed amount of time and runs during [0, _retrace_time).
+		Retrace takes a fixed amount of time and runs during [0, _retrace_time).
 
-			For the current line, scan then occurs from [_retrace_time, _expected_next_sync), at which point
-			retrace begins and the internal counter is reset.
+		For the current line, scan then occurs from [_retrace_time, _expected_next_sync), at which point
+		retrace begins and the internal counter is reset.
 
-			All synchronisation events that occur within (-_sync_error_window, _sync_error_window) of the
-			expected synchronisation time will cause a proportional adjustment in the expected time for the next
-			synchronisation. Other synchronisation events are clamped as though they occurred in that range.
-		*/
+		All synchronisation events that occur within (-_sync_error_window, _sync_error_window) of the
+		expected synchronisation time will cause a proportional adjustment in the expected time for the next
+		synchronisation. Other synchronisation events are clamped as though they occurred in that range.
+	*/
 };
 
 }
diff --git a/Outputs/DisplayMetrics.hpp b/Outputs/DisplayMetrics.hpp
index a5eb0c059..6583278d2 100644
--- a/Outputs/DisplayMetrics.hpp
+++ b/Outputs/DisplayMetrics.hpp
@@ -22,36 +22,36 @@ namespace Outputs::Display {
 	to allow for host-client frame synchronisation.
 */
 class Metrics {
-	public:
-		/// Notifies Metrics of a beam event.
-		void announce_event(ScanTarget::Event event);
+public:
+	/// Notifies Metrics of a beam event.
+	void announce_event(ScanTarget::Event);
 
-		/// Notifies Metrics that the size of the output buffer has changed.
-		void announce_did_resize();
+	/// Notifies Metrics that the size of the output buffer has changed.
+	void announce_did_resize();
 
-		/// Provides Metrics with a new data point for output speed estimation.
-		void announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration duration, bool complete);
+	/// Provides Metrics with a new data point for output speed estimation.
+	void announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration, bool complete);
 
-		/// Provides Metrics with a new data point for output speed estimation, albeit without line-specific information.
-		void announce_draw_status(bool complete);
+	/// Provides Metrics with a new data point for output speed estimation, albeit without line-specific information.
+	void announce_draw_status(bool complete);
 
-		/// @returns @c true if Metrics thinks a lower output buffer resolution is desirable in the abstract; @c false otherwise.
-		bool should_lower_resolution() const;
+	/// @returns @c true if Metrics thinks a lower output buffer resolution is desirable in the abstract; @c false otherwise.
+	bool should_lower_resolution() const;
 
-		/// @returns An estimate of the number of lines being produced per frame, excluding vertical sync.
-		float visible_lines_per_frame_estimate() const;
+	/// @returns An estimate of the number of lines being produced per frame, excluding vertical sync.
+	float visible_lines_per_frame_estimate() const;
 
-		/// @returns The number of lines since vertical retrace ended.
-		int current_line() const;
+	/// @returns The number of lines since vertical retrace ended.
+	int current_line() const;
 
-	private:
-		int lines_this_frame_ = 0;
-		std::array<int, 20> line_total_history_;
-		size_t line_total_history_pointer_ = 0;
-		void add_line_total(int);
+private:
+	int lines_this_frame_ = 0;
+	std::array<int, 20> line_total_history_;
+	size_t line_total_history_pointer_ = 0;
+	void add_line_total(int);
 
-		std::atomic<int> frames_hit_ = 0;
-		std::atomic<int> frames_missed_ = 0;
+	std::atomic<int> frames_hit_ = 0;
+	std::atomic<int> frames_missed_ = 0;
 };
 
 }
diff --git a/Outputs/Log.hpp b/Outputs/Log.hpp
index 7e1e234d9..c935687fa 100644
--- a/Outputs/Log.hpp
+++ b/Outputs/Log.hpp
@@ -137,31 +137,31 @@ public:
 	static constexpr bool enabled = is_enabled(source);
 
 	struct LogLine {
-		public:
-			LogLine(FILE *const stream) : stream_(stream) {
-				if constexpr (!enabled) return;
+	public:
+		LogLine(FILE *const stream) : stream_(stream) {
+			if constexpr (!enabled) return;
 
-				const auto source_prefix = prefix(source);
-				if(source_prefix) {
-					fprintf(stream_, "[%s] ", source_prefix);
-				}
+			const auto source_prefix = prefix(source);
+			if(source_prefix) {
+				fprintf(stream_, "[%s] ", source_prefix);
 			}
+		}
 
-			~LogLine() {
-				if constexpr (!enabled) return;
-				fprintf(stream_, "\n");
-			}
+		~LogLine() {
+			if constexpr (!enabled) return;
+			fprintf(stream_, "\n");
+		}
 
-			void append(const char *const format, ...) {
-				if constexpr (!enabled) return;
-				va_list args;
-				va_start(args, format);
-				vfprintf(stream_, format, args);
-				va_end(args);
-			}
+		void append(const char *const format, ...) {
+			if constexpr (!enabled) return;
+			va_list args;
+			va_start(args, format);
+			vfprintf(stream_, format, args);
+			va_end(args);
+		}
 
-		private:
-			FILE *stream_;
+	private:
+		FILE *stream_;
 	};
 
 	LogLine info()	{	return LogLine(stdout);		}
diff --git a/Outputs/OpenGL/Primitives/Rectangle.hpp b/Outputs/OpenGL/Primitives/Rectangle.hpp
index 4895bb391..bbe6d1fb2 100644
--- a/Outputs/OpenGL/Primitives/Rectangle.hpp
+++ b/Outputs/OpenGL/Primitives/Rectangle.hpp
@@ -18,21 +18,21 @@ namespace Outputs::Display::OpenGL {
 	Provides a wrapper for drawing a solid, single-colour rectangle.
 */
 class Rectangle {
-	public:
-		/*!
-			Instantiates an instance of Rectange with the coordinates given.
-		*/
-		Rectangle(float x, float y, float width, float height);
+public:
+	/*!
+		Instantiates an instance of Rectange with the coordinates given.
+	*/
+	Rectangle(float x, float y, float width, float height);
 
-		/*!
-			Draws this rectangle in the colour supplied.
-		*/
-		void draw(float red, float green, float blue);
+	/*!
+		Draws this rectangle in the colour supplied.
+	*/
+	void draw(float red, float green, float blue);
 
-	private:
-		Shader pixel_shader_;
-		GLuint drawing_vertex_array_ = 0, drawing_array_buffer_ = 0;
-		GLint colour_uniform_;
+private:
+	Shader pixel_shader_;
+	GLuint drawing_vertex_array_ = 0, drawing_array_buffer_ = 0;
+	GLint colour_uniform_;
 };
 
 }
diff --git a/Outputs/OpenGL/Primitives/TextureTarget.hpp b/Outputs/OpenGL/Primitives/TextureTarget.hpp
index 60292c621..a26fc13b3 100644
--- a/Outputs/OpenGL/Primitives/TextureTarget.hpp
+++ b/Outputs/OpenGL/Primitives/TextureTarget.hpp
@@ -19,70 +19,70 @@ namespace Outputs::Display::OpenGL {
 	handles render-to-texture framebuffer objects.
 */
 class TextureTarget {
-	public:
-		/*!
-			Creates a new texture target. Contents are initially undefined.
+public:
+	/*!
+		Creates a new texture target. Contents are initially undefined.
 
-			Leaves both the generated texture and framebuffer bound.
+		Leaves both the generated texture and framebuffer bound.
 
-			@throws std::runtime_error if creation fails.
+		@throws std::runtime_error if creation fails.
 
-			@param width The width of target to create.
-			@param height The height of target to create.
-			@param texture_unit A texture unit on which to bind the texture.
-			@param has_stencil_buffer An 8-bit stencil buffer is attached if this is @c true; no stencil buffer is attached otherwise.
-		*/
-		TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, GLint mag_filter, bool has_stencil_buffer);
-		~TextureTarget();
+		@param width The width of target to create.
+		@param height The height of target to create.
+		@param texture_unit A texture unit on which to bind the texture.
+		@param has_stencil_buffer An 8-bit stencil buffer is attached if this is @c true; no stencil buffer is attached otherwise.
+	*/
+	TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, GLint mag_filter, bool has_stencil_buffer);
+	~TextureTarget();
 
-		/*!
-			Binds this target as a framebuffer and sets the @c glViewport accordingly.
-		*/
-		void bind_framebuffer();
+	/*!
+		Binds this target as a framebuffer and sets the @c glViewport accordingly.
+	*/
+	void bind_framebuffer();
 
-		/*!
-			Binds this target as a texture.
-		*/
-		void bind_texture() const;
+	/*!
+		Binds this target as a texture.
+	*/
+	void bind_texture() const;
 
-		/*!
-			@returns the width of the texture target.
-		*/
-		GLsizei get_width() const {
-			return width_;
-		}
+	/*!
+		@returns the width of the texture target.
+	*/
+	GLsizei get_width() const {
+		return width_;
+	}
 
-		/*!
-			@returns the height of the texture target.
-		*/
-		GLsizei get_height() const {
-			return height_;
-		}
+	/*!
+		@returns the height of the texture target.
+	*/
+	GLsizei get_height() const {
+		return height_;
+	}
 
-		/*!
-			Draws this texture to the currently-bound framebuffer, which has the aspect ratio
-			@c aspect_ratio. This texture will fill the height of the frame buffer, and pick
-			an appropriate width based on the aspect ratio.
+	/*!
+		Draws this texture to the currently-bound framebuffer, which has the aspect ratio
+		@c aspect_ratio. This texture will fill the height of the frame buffer, and pick
+		an appropriate width based on the aspect ratio.
 
-			@c colour_threshold sets a threshold test that each colour must satisfy to be
-			output. A threshold of 0.0f means that all colours will pass through. A threshold
-			of 0.5f means that only colour components above 0.5f will pass through, with
-			0.5f being substituted elsewhere. This provides a way to ensure that the sort of
-			persistent low-value errors that can result from an IIR are hidden.
-		*/
-		void draw(float aspect_ratio, float colour_threshold = 0.0f) const;
+		@c colour_threshold sets a threshold test that each colour must satisfy to be
+		output. A threshold of 0.0f means that all colours will pass through. A threshold
+		of 0.5f means that only colour components above 0.5f will pass through, with
+		0.5f being substituted elsewhere. This provides a way to ensure that the sort of
+		persistent low-value errors that can result from an IIR are hidden.
+	*/
+	void draw(float aspect_ratio, float colour_threshold = 0.0f) const;
 
-	private:
-		GLuint framebuffer_ = 0, texture_ = 0, renderbuffer_ = 0;
-		const GLsizei width_ = 0, height_ = 0;
-		GLsizei expanded_width_ = 0, expanded_height_ = 0;
-		const GLenum texture_unit_ = 0;
+private:
+	GLuint framebuffer_ = 0, texture_ = 0, renderbuffer_ = 0;
+	const GLsizei width_ = 0, height_ = 0;
+	GLsizei expanded_width_ = 0, expanded_height_ = 0;
+	const GLenum texture_unit_ = 0;
 
-		mutable std::unique_ptr<Shader> pixel_shader_;
-		mutable GLuint drawing_vertex_array_ = 0, drawing_array_buffer_ = 0;
-		mutable float set_aspect_ratio_ = 0.0f;
+	mutable std::unique_ptr<Shader> pixel_shader_;
+	mutable GLuint drawing_vertex_array_ = 0, drawing_array_buffer_ = 0;
+	mutable float set_aspect_ratio_ = 0.0f;
 
-		mutable GLint threshold_uniform_;
+	mutable GLint threshold_uniform_;
 };
 
 }
diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp
index aa4572b69..cb66eee60 100644
--- a/Outputs/OpenGL/ScanTarget.hpp
+++ b/Outputs/OpenGL/ScanTarget.hpp
@@ -36,124 +36,124 @@ namespace Outputs::Display::OpenGL {
 	drawn to the target framebuffer is a quad.
 */
 class ScanTarget: public Outputs::Display::BufferingScanTarget {	// TODO: use private inheritance and expose only display_metrics() and a custom cast?
-	public:
-		ScanTarget(GLuint target_framebuffer = 0, float output_gamma = 2.2f);
-		~ScanTarget();
+public:
+	ScanTarget(GLuint target_framebuffer = 0, float output_gamma = 2.2f);
+	~ScanTarget();
 
-		void set_target_framebuffer(GLuint);
+	void set_target_framebuffer(GLuint);
 
-		/*! Pushes the current state of output to the target framebuffer. */
-		void draw(int output_width, int output_height);
-		/*! Processes all the latest input, at a resolution suitable for later output to a framebuffer of the specified size. */
-		void update(int output_width, int output_height);
+	/*! Pushes the current state of output to the target framebuffer. */
+	void draw(int output_width, int output_height);
+	/*! Processes all the latest input, at a resolution suitable for later output to a framebuffer of the specified size. */
+	void update(int output_width, int output_height);
 
-	private:
-		static constexpr int LineBufferWidth = 2048;
-		static constexpr int LineBufferHeight = 2048;
+private:
+	static constexpr int LineBufferWidth = 2048;
+	static constexpr int LineBufferHeight = 2048;
 
 #ifndef NDEBUG
-		struct OpenGLVersionDumper {
-			OpenGLVersionDumper() {
-				// Note the OpenGL version, as the first thing this class does prior to construction.
-				Log::Logger<Log::Source::OpenGL>().info().append(
-					"Constructing scan target with OpenGL %s; shading language version %s",
-					glGetString(GL_VERSION),
-					glGetString(GL_SHADING_LANGUAGE_VERSION));
-			}
-		} dumper_;
+	struct OpenGLVersionDumper {
+		OpenGLVersionDumper() {
+			// Note the OpenGL version, as the first thing this class does prior to construction.
+			Log::Logger<Log::Source::OpenGL>().info().append(
+				"Constructing scan target with OpenGL %s; shading language version %s",
+				glGetString(GL_VERSION),
+				glGetString(GL_SHADING_LANGUAGE_VERSION));
+		}
+	} dumper_;
 #endif
 
-		GLuint target_framebuffer_;
-		const float output_gamma_;
+	GLuint target_framebuffer_;
+	const float output_gamma_;
 
-		int resolution_reduction_level_ = 1;
-		int output_height_ = 0;
+	int resolution_reduction_level_ = 1;
+	int output_height_ = 0;
 
-		size_t lines_submitted_ = 0;
-		std::chrono::high_resolution_clock::time_point line_submission_begin_time_;
+	size_t lines_submitted_ = 0;
+	std::chrono::high_resolution_clock::time_point line_submission_begin_time_;
 
-		// Contains the first composition of scans into lines;
-		// they're accumulated prior to output to allow for continuous
-		// application of any necessary conversions — e.g. composite processing.
-		TextureTarget unprocessed_line_texture_;
+	// Contains the first composition of scans into lines;
+	// they're accumulated prior to output to allow for continuous
+	// application of any necessary conversions — e.g. composite processing.
+	TextureTarget unprocessed_line_texture_;
 
-		// Contains pre-lowpass-filtered chrominance information that is
-		// part-QAM-demoduled, if dealing with a QAM data source.
-		std::unique_ptr<TextureTarget> qam_chroma_texture_;
+	// Contains pre-lowpass-filtered chrominance information that is
+	// part-QAM-demoduled, if dealing with a QAM data source.
+	std::unique_ptr<TextureTarget> qam_chroma_texture_;
 
-		// Scans are accumulated to the accumulation texture; the full-display
-		// rectangle is used to ensure untouched pixels properly decay.
-		std::unique_ptr<TextureTarget> accumulation_texture_;
-		Rectangle full_display_rectangle_;
-		bool stencil_is_valid_ = false;
+	// Scans are accumulated to the accumulation texture; the full-display
+	// rectangle is used to ensure untouched pixels properly decay.
+	std::unique_ptr<TextureTarget> accumulation_texture_;
+	Rectangle full_display_rectangle_;
+	bool stencil_is_valid_ = false;
 
-		// OpenGL storage handles for buffer data.
-		GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0;
-		GLuint line_buffer_name_ = 0, line_vertex_array_ = 0;
+	// OpenGL storage handles for buffer data.
+	GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0;
+	GLuint line_buffer_name_ = 0, line_vertex_array_ = 0;
 
-		template <typename T> void allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name);
-		template <typename T> void patch_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer);
+	template <typename T> void allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name);
+	template <typename T> void patch_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer);
 
-		GLuint write_area_texture_name_ = 0;
-		bool texture_exists_ = false;
+	GLuint write_area_texture_name_ = 0;
+	bool texture_exists_ = false;
 
-		// Receives scan target modals.
-		void setup_pipeline();
+	// Receives scan target modals.
+	void setup_pipeline();
 
-		enum class ShaderType {
-			Composition,
-			Conversion,
-			QAMSeparation
-		};
+	enum class ShaderType {
+		Composition,
+		Conversion,
+		QAMSeparation
+	};
 
-		/*!
-			Calls @c taret.enable_vertex_attribute_with_pointer to attach all
-			globals for shaders of @c type to @c target.
-		*/
-		static void enable_vertex_attributes(ShaderType type, Shader &target);
-		void set_uniforms(ShaderType type, Shader &target) const;
-		std::vector<std::string> bindings(ShaderType type) const;
+	/*!
+		Calls @c taret.enable_vertex_attribute_with_pointer to attach all
+		globals for shaders of @c type to @c target.
+	*/
+	static void enable_vertex_attributes(ShaderType type, Shader &target);
+	void set_uniforms(ShaderType type, Shader &target) const;
+	std::vector<std::string> bindings(ShaderType type) const;
 
-		GLsync fence_ = nullptr;
-		std::atomic_flag is_drawing_to_accumulation_buffer_;
+	GLsync fence_ = nullptr;
+	std::atomic_flag is_drawing_to_accumulation_buffer_;
 
-		std::unique_ptr<Shader> input_shader_;
-		std::unique_ptr<Shader> output_shader_;
-		std::unique_ptr<Shader> qam_separation_shader_;
+	std::unique_ptr<Shader> input_shader_;
+	std::unique_ptr<Shader> output_shader_;
+	std::unique_ptr<Shader> qam_separation_shader_;
 
-		/*!
-			Produces a shader that composes fragment of the input stream to a single buffer,
-			normalising the data into one of four forms: RGB, 8-bit luminance,
-			phase-linked luminance or luminance+phase offset.
-		*/
-		std::unique_ptr<Shader> composition_shader() const;
-		/*!
-			Produces a shader that reads from a composition buffer and converts to host
-			output RGB, decoding composite or S-Video as necessary.
-		*/
-		std::unique_ptr<Shader> conversion_shader() const;
-		/*!
-			Produces a shader that writes separated but not-yet filtered QAM components
-			from the unprocessed line texture to the QAM chroma texture, at a fixed
-			size of four samples per colour clock, point sampled.
-		*/
-		std::unique_ptr<Shader> qam_separation_shader() const;
+	/*!
+		Produces a shader that composes fragment of the input stream to a single buffer,
+		normalising the data into one of four forms: RGB, 8-bit luminance,
+		phase-linked luminance or luminance+phase offset.
+	*/
+	std::unique_ptr<Shader> composition_shader() const;
+	/*!
+		Produces a shader that reads from a composition buffer and converts to host
+		output RGB, decoding composite or S-Video as necessary.
+	*/
+	std::unique_ptr<Shader> conversion_shader() const;
+	/*!
+		Produces a shader that writes separated but not-yet filtered QAM components
+		from the unprocessed line texture to the QAM chroma texture, at a fixed
+		size of four samples per colour clock, point sampled.
+	*/
+	std::unique_ptr<Shader> qam_separation_shader() const;
 
-		void set_sampling_window(int output_Width, int output_height, Shader &target);
+	void set_sampling_window(int output_Width, int output_height, Shader &target);
 
-		std::string sampling_function() const;
+	std::string sampling_function() const;
 
-		/*!
-			@returns true if the current display type is a 'soft' one, i.e. one in which
-			contrast tends to be low, such as a composite colour display.
-		*/
-		bool is_soft_display_type();
+	/*!
+		@returns true if the current display type is a 'soft' one, i.e. one in which
+		contrast tends to be low, such as a composite colour display.
+	*/
+	bool is_soft_display_type();
 
-		// Storage for the various buffers.
-		std::vector<uint8_t> write_area_texture_;
-		std::array<Scan, LineBufferHeight*5> scan_buffer_;
-		std::array<Line, LineBufferHeight> line_buffer_;
-		std::array<LineMetadata, LineBufferHeight> line_metadata_buffer_;
+	// Storage for the various buffers.
+	std::vector<uint8_t> write_area_texture_;
+	std::array<Scan, LineBufferHeight*5> scan_buffer_;
+	std::array<Line, LineBufferHeight> line_buffer_;
+	std::array<LineMetadata, LineBufferHeight> line_metadata_buffer_;
 };
 
 }
diff --git a/Outputs/ScanTargets/BufferingScanTarget.hpp b/Outputs/ScanTargets/BufferingScanTarget.hpp
index 9d43c22c7..2c95a1d76 100644
--- a/Outputs/ScanTargets/BufferingScanTarget.hpp
+++ b/Outputs/ScanTargets/BufferingScanTarget.hpp
@@ -33,241 +33,241 @@ namespace Outputs::Display {
 	This buffer rejects new data when full.
 */
 class BufferingScanTarget: public Outputs::Display::ScanTarget {
-	public:
-		/*! @returns The DisplayMetrics object that this ScanTarget has been providing with announcements and draw overages. */
-		const Metrics &display_metrics();
+public:
+	/*! @returns The DisplayMetrics object that this ScanTarget has been providing with announcements and draw overages. */
+	const Metrics &display_metrics();
 
-		static constexpr int WriteAreaWidth = 2048;
-		static constexpr int WriteAreaHeight = 2048;
+	static constexpr int WriteAreaWidth = 2048;
+	static constexpr int WriteAreaHeight = 2048;
 
-		BufferingScanTarget();
+	BufferingScanTarget();
 
-		// This is included because it's assumed that scan targets will want to expose one.
-		// It is the subclass's responsibility to post timings.
-		Metrics display_metrics_;
+	// This is included because it's assumed that scan targets will want to expose one.
+	// It is the subclass's responsibility to post timings.
+	Metrics display_metrics_;
 
-		/// Extends the definition of a Scan to include two extra fields,
-		/// completing this scan's source data and destination locations.
-		struct Scan {
-			Outputs::Display::ScanTarget::Scan scan;
+	/// Extends the definition of a Scan to include two extra fields,
+	/// completing this scan's source data and destination locations.
+	struct Scan {
+		Outputs::Display::ScanTarget::Scan scan;
 
-			/// Stores the y coordinate for this scan's data within the write area texture.
-			/// Use this plus the scan's endpoints' data_offsets to locate this data in 2d.
-			/// Note that the data_offsets will have been adjusted to be relative to the line
-			/// they fall within, not the data allocation.
-			uint16_t data_y;
-			/// Stores the y coordinate assigned to this scan within the intermediate buffers.
-			/// Use this plus this scan's endpoints' x locations to determine where to composite
-			/// this data for intermediate processing.
-			uint16_t line;
+		/// Stores the y coordinate for this scan's data within the write area texture.
+		/// Use this plus the scan's endpoints' data_offsets to locate this data in 2d.
+		/// Note that the data_offsets will have been adjusted to be relative to the line
+		/// they fall within, not the data allocation.
+		uint16_t data_y;
+		/// Stores the y coordinate assigned to this scan within the intermediate buffers.
+		/// Use this plus this scan's endpoints' x locations to determine where to composite
+		/// this data for intermediate processing.
+		uint16_t line;
+	};
+
+	/// Defines the boundaries of a complete line of video — a 2d start and end location,
+	/// composite phase and amplitude (if relevant), the source line in the intermediate buffer
+	/// plus the start and end offsets of the area that is visible from the intermediate buffer.
+	struct Line {
+		struct EndPoint {
+			uint16_t x, y;
+			int16_t composite_angle;
+			uint16_t cycles_since_end_of_horizontal_retrace;
+		} end_points[2];
+
+		uint8_t composite_amplitude;
+		uint16_t line;
+	};
+
+	/// Provides additional metadata about lines; this is separate because it's unlikely to be of
+	/// interest to the GPU, unlike the fields in Line.
+	struct LineMetadata {
+		/// @c true if this line was the first drawn after vertical sync; @c false otherwise.
+		bool is_first_in_frame;
+		/// @c true if this line is the first in the frame and if every single piece of output
+		/// from the previous frame was recorded; @c false otherwise. Data can be dropped
+		/// from a frame if performance problems mean that the emulated machine is running
+		/// more quickly than complete frames can be generated.
+		bool previous_frame_was_complete;
+		/// The index of the first scan that will appear on this line.
+		size_t first_scan;
+	};
+
+	/// Sets the area of memory to use as a scan buffer.
+	void set_scan_buffer(Scan *buffer, size_t size);
+
+	/// Sets the area of memory to use as line and line metadata buffers.
+	void set_line_buffer(Line *line_buffer, LineMetadata *metadata_buffer, size_t size);
+
+	/// Sets a new base address for the texture.
+	/// When called this will flush all existing data and load up the
+	/// new data size.
+	void set_write_area(uint8_t *base);
+
+	/// @returns The number of bytes per input sample, as per the latest modals.
+	size_t write_area_data_size() const;
+
+	/// Defines a segment of data now ready for output, consisting of start and endpoints for:
+	///
+	///	(i) the region of the write area that has been modified; if the caller is using shared memory
+	/// for the write area then it can ignore this information;
+	///
+	/// (ii) the number of scans that have been completed; and
+	///
+	/// (iii) the number of lines that have been completed.
+	///
+	/// New write areas and scans are exposed only upon completion of the corresponding lines.
+	/// The values indicated by the start point are the first that should be drawn. Those indicated
+	/// by the end point are one after the final that should be drawn.
+	///
+	/// So e.g. start.scan = 23, end.scan = 24 means draw a single scan, index 23.
+	struct OutputArea {
+		struct Endpoint {
+			int write_area_x, write_area_y;
+			size_t scan;
+			size_t line;
 		};
 
-		/// Defines the boundaries of a complete line of video — a 2d start and end location,
-		/// composite phase and amplitude (if relevant), the source line in the intermediate buffer
-		/// plus the start and end offsets of the area that is visible from the intermediate buffer.
-		struct Line {
-			struct EndPoint {
-				uint16_t x, y;
-				int16_t composite_angle;
-				uint16_t cycles_since_end_of_horizontal_retrace;
-			} end_points[2];
-
-			uint8_t composite_amplitude;
-			uint16_t line;
-		};
-
-		/// Provides additional metadata about lines; this is separate because it's unlikely to be of
-		/// interest to the GPU, unlike the fields in Line.
-		struct LineMetadata {
-			/// @c true if this line was the first drawn after vertical sync; @c false otherwise.
-			bool is_first_in_frame;
-			/// @c true if this line is the first in the frame and if every single piece of output
-			/// from the previous frame was recorded; @c false otherwise. Data can be dropped
-			/// from a frame if performance problems mean that the emulated machine is running
-			/// more quickly than complete frames can be generated.
-			bool previous_frame_was_complete;
-			/// The index of the first scan that will appear on this line.
-			size_t first_scan;
-		};
-
-		/// Sets the area of memory to use as a scan buffer.
-		void set_scan_buffer(Scan *buffer, size_t size);
-
-		/// Sets the area of memory to use as line and line metadata buffers.
-		void set_line_buffer(Line *line_buffer, LineMetadata *metadata_buffer, size_t size);
-
-		/// Sets a new base address for the texture.
-		/// When called this will flush all existing data and load up the
-		/// new data size.
-		void set_write_area(uint8_t *base);
-
-		/// @returns The number of bytes per input sample, as per the latest modals.
-		size_t write_area_data_size() const;
-
-		/// Defines a segment of data now ready for output, consisting of start and endpoints for:
-		///
-		///	(i) the region of the write area that has been modified; if the caller is using shared memory
-		/// for the write area then it can ignore this information;
-		///
-		/// (ii) the number of scans that have been completed; and
-		///
-		/// (iii) the number of lines that have been completed.
-		///
-		/// New write areas and scans are exposed only upon completion of the corresponding lines.
-		/// The values indicated by the start point are the first that should be drawn. Those indicated
-		/// by the end point are one after the final that should be drawn.
-		///
-		/// So e.g. start.scan = 23, end.scan = 24 means draw a single scan, index 23.
-		struct OutputArea {
-			struct Endpoint {
-				int write_area_x, write_area_y;
-				size_t scan;
-				size_t line;
-			};
-
-			Endpoint start, end;
+		Endpoint start, end;
 
 #ifndef NDEBUG
-			size_t counter;
+		size_t counter;
 #endif
-		};
+	};
 
-		/// Gets the current range of content that has been posted but not yet returned by
-		/// a previous call to get_output_area().
-		///
-		/// Does not require the caller to be within a @c perform block.
-		OutputArea get_output_area();
+	/// Gets the current range of content that has been posted but not yet returned by
+	/// a previous call to get_output_area().
+	///
+	/// Does not require the caller to be within a @c perform block.
+	OutputArea get_output_area();
 
-		/// Announces that the output area has now completed output, freeing up its memory for
-		/// further modification.
-		///
-		/// It is the caller's responsibility to ensure that the areas passed to complete_output_area
-		/// are those from get_output_area and are marked as completed in the same order that
-		/// they were originally provided.
-		///
-		/// Does not require the caller to be within a @c perform block.
-		void complete_output_area(const OutputArea &);
+	/// Announces that the output area has now completed output, freeing up its memory for
+	/// further modification.
+	///
+	/// It is the caller's responsibility to ensure that the areas passed to complete_output_area
+	/// are those from get_output_area and are marked as completed in the same order that
+	/// they were originally provided.
+	///
+	/// Does not require the caller to be within a @c perform block.
+	void complete_output_area(const OutputArea &);
 
-		/// Performs @c action ensuring that no other @c perform actions, or any
-		/// change to modals, occurs simultaneously.
-		void perform(const std::function<void(void)> &action);
+	/// Performs @c action ensuring that no other @c perform actions, or any
+	/// change to modals, occurs simultaneously.
+	void perform(const std::function<void(void)> &action);
 
-		/// @returns new Modals if any have been set since the last call to get_new_modals().
-		///		The caller must be within a @c perform block.
-		const Modals *new_modals();
+	/// @returns new Modals if any have been set since the last call to get_new_modals().
+	///		The caller must be within a @c perform block.
+	const Modals *new_modals();
 
-		/// @returns the current @c Modals.
-		const Modals &modals() const;
+	/// @returns the current @c Modals.
+	const Modals &modals() const;
 
-		/// @returns @c true if new modals are available; @c false otherwise.
-		///
-		/// Safe to call from any thread.
-		bool has_new_modals() const;
+	/// @returns @c true if new modals are available; @c false otherwise.
+	///
+	/// Safe to call from any thread.
+	bool has_new_modals() const;
 
-	private:
-		// ScanTarget overrides.
-		void set_modals(Modals) final;
-		Outputs::Display::ScanTarget::Scan *begin_scan() final;
-		void end_scan() final;
-		uint8_t *begin_data(size_t required_length, size_t required_alignment) final;
-		void end_data(size_t actual_length) final;
-		void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t colour_burst_amplitude) final;
-		void will_change_owner() final;
+private:
+	// ScanTarget overrides.
+	void set_modals(Modals) final;
+	Outputs::Display::ScanTarget::Scan *begin_scan() final;
+	void end_scan() final;
+	uint8_t *begin_data(size_t required_length, size_t required_alignment) final;
+	void end_data(size_t actual_length) final;
+	void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t colour_burst_amplitude) final;
+	void will_change_owner() final;
 
-		// Uses a texture to vend write areas.
-		uint8_t *write_area_ = nullptr;
-		size_t data_type_size_ = 0;
+	// Uses a texture to vend write areas.
+	uint8_t *write_area_ = nullptr;
+	size_t data_type_size_ = 0;
 
-		// Tracks changes in raster visibility in order to populate
-		// Lines and LineMetadatas.
-		bool output_is_visible_ = false;
+	// Tracks changes in raster visibility in order to populate
+	// Lines and LineMetadatas.
+	bool output_is_visible_ = false;
 
-		// Track allocation failures.
-		bool data_is_allocated_ = false;
-		bool allocation_has_failed_ = false;
+	// Track allocation failures.
+	bool data_is_allocated_ = false;
+	bool allocation_has_failed_ = false;
 
-		// Ephemeral information for the begin/end functions.
-		Scan *vended_scan_ = nullptr;
-		int vended_write_area_pointer_ = 0;
+	// Ephemeral information for the begin/end functions.
+	Scan *vended_scan_ = nullptr;
+	int vended_write_area_pointer_ = 0;
 
-		// Ephemeral state that helps in line composition.
-		int provided_scans_ = 0;
-		bool is_first_in_frame_ = true;
-		bool frame_is_complete_ = true;
-		bool previous_frame_was_complete_ = true;
+	// Ephemeral state that helps in line composition.
+	int provided_scans_ = 0;
+	bool is_first_in_frame_ = true;
+	bool frame_is_complete_ = true;
+	bool previous_frame_was_complete_ = true;
 
-		// By convention everything in the PointerSet points to the next instance
-		// of whatever it is that will be used. So a client should start with whatever
-		// is pointed to by the read pointers and carry until it gets to a value that
-		// is equal to whatever is in the submit pointers.
-		struct PointerSet {
-			// This constructor is here to appease GCC's interpretation of
-			// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377
-			PointerSet() noexcept = default;
+	// By convention everything in the PointerSet points to the next instance
+	// of whatever it is that will be used. So a client should start with whatever
+	// is pointed to by the read pointers and carry until it gets to a value that
+	// is equal to whatever is in the submit pointers.
+	struct PointerSet {
+		// This constructor is here to appease GCC's interpretation of
+		// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377
+		PointerSet() noexcept = default;
 
-			// Squeezing this struct into 64 bits makes the std::atomics more likely
-			// to be lock free; they are under LLVM x86-64.
+		// Squeezing this struct into 64 bits makes the std::atomics more likely
+		// to be lock free; they are under LLVM x86-64.
 
-			// Points to the vended area in the write area texture.
-			// The vended area is always preceded by a guard pixel, so a
-			// sensible default construction is write_area = 1.
-			int32_t write_area = 1;
+		// Points to the vended area in the write area texture.
+		// The vended area is always preceded by a guard pixel, so a
+		// sensible default construction is write_area = 1.
+		int32_t write_area = 1;
 
-			// Points into the scan buffer.
-			uint16_t scan = 0;
+		// Points into the scan buffer.
+		uint16_t scan = 0;
 
-			// Points into the line buffer.
-			uint16_t line = 0;
-		};
+		// Points into the line buffer.
+		uint16_t line = 0;
+	};
 
-		/// A pointer to the final thing currently cleared for submission.
-		std::atomic<PointerSet> submit_pointers_;
+	/// A pointer to the final thing currently cleared for submission.
+	std::atomic<PointerSet> submit_pointers_;
 
-		/// A pointer to the first thing not yet submitted for display; this is
-		/// atomic since it also acts as the buffer into which the write_pointers_
-		/// may run and is therefore used by both producer and consumer.
-		std::atomic<PointerSet> read_pointers_;
+	/// A pointer to the first thing not yet submitted for display; this is
+	/// atomic since it also acts as the buffer into which the write_pointers_
+	/// may run and is therefore used by both producer and consumer.
+	std::atomic<PointerSet> read_pointers_;
 
-		std::atomic<PointerSet> read_ahead_pointers_;
+	std::atomic<PointerSet> read_ahead_pointers_;
 
-		/// This is used as a spinlock to guard `perform` calls.
-		std::atomic_flag is_updating_;
+	/// This is used as a spinlock to guard `perform` calls.
+	std::atomic_flag is_updating_;
 
-		/// A mutex for gettng access to anything the producer modifies — i.e. the write_pointers_,
-		/// data_type_size_ and write_area_texture_, and all other state to do with capturing
-		/// data, scans and lines.
-		///
-		/// This is almost never contended. The main collision is a user-prompted change of modals while the
-		/// emulation thread is running.
-		std::mutex producer_mutex_;
+	/// A mutex for gettng access to anything the producer modifies — i.e. the write_pointers_,
+	/// data_type_size_ and write_area_texture_, and all other state to do with capturing
+	/// data, scans and lines.
+	///
+	/// This is almost never contended. The main collision is a user-prompted change of modals while the
+	/// emulation thread is running.
+	std::mutex producer_mutex_;
 
-		/// A pointer to the next thing that should be provided to the caller for data.
-		PointerSet write_pointers_;
+	/// A pointer to the next thing that should be provided to the caller for data.
+	PointerSet write_pointers_;
 
-		// The owner-supplied scan buffer and size.
-		Scan *scan_buffer_ = nullptr;
-		size_t scan_buffer_size_ = 0;
+	// The owner-supplied scan buffer and size.
+	Scan *scan_buffer_ = nullptr;
+	size_t scan_buffer_size_ = 0;
 
-		// The owner-supplied line buffer and size.
-		Line *line_buffer_ = nullptr;
-		LineMetadata *line_metadata_buffer_ = nullptr;
-		size_t line_buffer_size_ = 0;
+	// The owner-supplied line buffer and size.
+	Line *line_buffer_ = nullptr;
+	LineMetadata *line_metadata_buffer_ = nullptr;
+	size_t line_buffer_size_ = 0;
 
-		// Current modals and whether they've yet been returned
-		// from a call to @c get_new_modals.
-		Modals modals_;
-		std::atomic<bool> modals_are_dirty_ = false;
+	// Current modals and whether they've yet been returned
+	// from a call to @c get_new_modals.
+	Modals modals_;
+	std::atomic<bool> modals_are_dirty_ = false;
 
-		// Provides a per-data size implementation of end_data; a previous
-		// implementation used blind memcpy and that turned into something
-		// of a profiling hot spot.
-		template <typename DataUnit> void end_data(size_t actual_length);
+	// Provides a per-data size implementation of end_data; a previous
+	// implementation used blind memcpy and that turned into something
+	// of a profiling hot spot.
+	template <typename DataUnit> void end_data(size_t actual_length);
 
 #ifndef NDEBUG
-		// Debug features; these amount to API validation.
-		bool scan_is_ongoing_ = false;
-		size_t output_area_counter_ = 0;
-		size_t output_area_next_returned_ = 0;
+	// Debug features; these amount to API validation.
+	bool scan_is_ongoing_ = false;
+	size_t output_area_counter_ = 0;
+	size_t output_area_next_returned_ = 0;
 #endif
 };
 
diff --git a/Outputs/Speaker/Implementation/BufferSource.hpp b/Outputs/Speaker/Implementation/BufferSource.hpp
index 3ac7eb323..e806a7951 100644
--- a/Outputs/Speaker/Implementation/BufferSource.hpp
+++ b/Outputs/Speaker/Implementation/BufferSource.hpp
@@ -100,52 +100,52 @@ class BufferSource {
 ///
 template <typename SourceT, bool stereo, int divider = 1>
 struct SampleSource: public BufferSource<SourceT, stereo> {
-	public:
-		template <Action action>
-		void apply_samples(std::size_t number_of_samples, typename SampleT<stereo>::type *target) {
-			auto &source = *static_cast<SourceT *>(this);
+public:
+	template <Action action>
+	void apply_samples(std::size_t number_of_samples, typename SampleT<stereo>::type *target) {
+		auto &source = *static_cast<SourceT *>(this);
 
-			if constexpr (divider == 1) {
-				while(number_of_samples--) {
-					apply<action>(*target, source.level());
-					++target;
-					source.advance();
-				}
-			} else {
-				std::size_t c = 0;
-
-				// Fill in the tail of any partially-captured level.
-				auto level = source.level();
-				while(c < number_of_samples && master_divider_ != divider) {
-					apply<action>(target[c], level);
-					++c;
-					++master_divider_;
-				}
+		if constexpr (divider == 1) {
+			while(number_of_samples--) {
+				apply<action>(*target, source.level());
+				++target;
 				source.advance();
-
-				// Provide all full levels.
-				auto whole_steps = static_cast<int>((number_of_samples - c) / divider);
-				while(whole_steps--) {
-					fill<action>(&target[c], &target[c + divider], source.level());
-					c += divider;
-					source.advance();
-				}
-
-				// Provide the head of a further partial capture.
-				level = source.level();
-				master_divider_ = static_cast<int>(number_of_samples - c);
-				fill<action>(&target[c], &target[number_of_samples], source.level());
 			}
+		} else {
+			std::size_t c = 0;
+
+			// Fill in the tail of any partially-captured level.
+			auto level = source.level();
+			while(c < number_of_samples && master_divider_ != divider) {
+				apply<action>(target[c], level);
+				++c;
+				++master_divider_;
+			}
+			source.advance();
+
+			// Provide all full levels.
+			auto whole_steps = static_cast<int>((number_of_samples - c) / divider);
+			while(whole_steps--) {
+				fill<action>(&target[c], &target[c + divider], source.level());
+				c += divider;
+				source.advance();
+			}
+
+			// Provide the head of a further partial capture.
+			level = source.level();
+			master_divider_ = static_cast<int>(number_of_samples - c);
+			fill<action>(&target[c], &target[number_of_samples], source.level());
 		}
+	}
 
-		// TODO: use a concept here, when C++20 filters through.
-		//
-		// Until then: sample sources should implement this.
-//		typename SampleT<stereo>::type level() const;
-//		void advance();
+	// TODO: use a concept here, when C++20 filters through.
+	//
+	// Until then: sample sources should implement this.
+//	typename SampleT<stereo>::type level() const;
+//	void advance();
 
-	private:
-		int master_divider_{};
+private:
+	int master_divider_{};
 };
 
 }
diff --git a/Outputs/Speaker/Implementation/CompoundSource.hpp b/Outputs/Speaker/Implementation/CompoundSource.hpp
index 2cafa10c7..8ff10d9fa 100644
--- a/Outputs/Speaker/Implementation/CompoundSource.hpp
+++ b/Outputs/Speaker/Implementation/CompoundSource.hpp
@@ -45,139 +45,140 @@ template <typename... S> constexpr bool are_properly_ordered() {
 	An owner may optionally assign relative volumes.
 */
 template <typename... T> class CompoundSource:
-	public Outputs::Speaker::BufferSource<CompoundSource<T...>, ::Outputs::Speaker::is_stereo<T...>()> {
-	private:
-		template <typename... S> class CompoundSourceHolder {
-			public:
-				static constexpr bool is_stereo = false;
-				void set_scaled_volume_range(int16_t, double *, double) {}
-				static constexpr std::size_t size() {	return 0;	}
-				double total_scale(double *) const {	return 0.0;	}
-		};
-
-		template <typename S, typename... R> class CompoundSourceHolder<S, R...> {
-			public:
-				CompoundSourceHolder(S &source, R &...next) : source_(source), next_source_(next...) {}
-
-				static constexpr bool is_stereo = S::is_stereo || CompoundSourceHolder<R...>::is_stereo;
-
-				template <Outputs::Speaker::Action action, bool output_stereo>
-				void apply_samples(std::size_t number_of_samples, typename ::Outputs::Speaker::SampleT<output_stereo>::type *target) {
-
-					// If this is the step at which a mono-to-stereo adaptation happens, apply it.
-					if constexpr (output_stereo && !S::is_stereo) {
-						// There'll be only one place in the chain that this conversion happens, but it'll
-						// happen there often. So avoid continuously reallocating.
-						if(conversion_source_.size() < number_of_samples) {
-							conversion_source_.resize(number_of_samples);
-						}
-
-						// Populate the conversion buffer with this source and all below.
-						apply_samples<Action::Store, false>(number_of_samples, conversion_source_.data());
-
-						// Map up and return.
-						for(std::size_t c = 0; c < number_of_samples; c++) {
-							Outputs::Speaker::apply<action>(target[c], StereoSample(conversion_source_[c]));
-						}
-					} else {
-						constexpr bool is_final_source = sizeof...(R) == 0;
-
-						// Get the rest of the output, if any.
-						if constexpr (!is_final_source) {
-							next_source_.template apply_samples<action, output_stereo>(number_of_samples, target);
-						}
-
-						if(source_.is_zero_level()) {
-							// This component is currently outputting silence; therefore don't add anything to the output
-							// audio. Zero fill only if this is the final source (as everything above will be additive).
-							if constexpr (is_final_source) {
-								Outputs::Speaker::fill<action>(target, target + number_of_samples, typename SampleT<output_stereo>::type());
-							}
-							return;
-						}
-
-						// Add in this component's output.
-						source_.template apply_samples<is_final_source ? Action::Store : Action::Mix>(number_of_samples, target);
-					}
-				}
-
-				void set_scaled_volume_range(int16_t range, double *volumes, double scale) {
-					const auto scaled_range = volumes[0] / double(source_.average_output_peak()) * double(range) / scale;
-					source_.set_sample_volume_range(int16_t(scaled_range));
-					next_source_.set_scaled_volume_range(range, &volumes[1], scale);
-				}
-
-				static constexpr std::size_t size() {
-					return 1 + CompoundSourceHolder<R...>::size();
-				}
-
-				double total_scale(double *volumes) const {
-					return (volumes[0] / source_.average_output_peak()) + next_source_.total_scale(&volumes[1]);
-				}
-
-			private:
-				S &source_;
-				CompoundSourceHolder<R...> next_source_;
-				std::vector<MonoSample> conversion_source_;
-		};
+	public Outputs::Speaker::BufferSource<CompoundSource<T...>, ::Outputs::Speaker::is_stereo<T...>()>
+{
+private:
+	template <typename... S> class CompoundSourceHolder {
+		public:
+			static constexpr bool is_stereo = false;
+			void set_scaled_volume_range(int16_t, double *, double) {}
+			static constexpr std::size_t size() {	return 0;	}
+			double total_scale(double *) const {	return 0.0;	}
+	};
 
+	template <typename S, typename... R> class CompoundSourceHolder<S, R...> {
 	public:
-		using Sample = typename SampleT<::Outputs::Speaker::is_stereo<T...>()>::type;
+		CompoundSourceHolder(S &source, R &...next) : source_(source), next_source_(next...) {}
 
-		// To ensure at most one mono to stereo conversion, require appropriate source ordering.
-		static_assert(are_properly_ordered<T...>(), "Sources should be listed with all stereo sources before all mono sources");
+		static constexpr bool is_stereo = S::is_stereo || CompoundSourceHolder<R...>::is_stereo;
 
-		CompoundSource(T &... sources) : source_holder_(sources...) {
-			// Default: give all sources equal volume.
-			const auto volume = 1.0 / double(source_holder_.size());
-			for(std::size_t c = 0; c < source_holder_.size(); ++c) {
-				volumes_.push_back(volume);
+		template <Outputs::Speaker::Action action, bool output_stereo>
+		void apply_samples(std::size_t number_of_samples, typename ::Outputs::Speaker::SampleT<output_stereo>::type *target) {
+
+			// If this is the step at which a mono-to-stereo adaptation happens, apply it.
+			if constexpr (output_stereo && !S::is_stereo) {
+				// There'll be only one place in the chain that this conversion happens, but it'll
+				// happen there often. So avoid continuously reallocating.
+				if(conversion_source_.size() < number_of_samples) {
+					conversion_source_.resize(number_of_samples);
+				}
+
+				// Populate the conversion buffer with this source and all below.
+				apply_samples<Action::Store, false>(number_of_samples, conversion_source_.data());
+
+				// Map up and return.
+				for(std::size_t c = 0; c < number_of_samples; c++) {
+					Outputs::Speaker::apply<action>(target[c], StereoSample(conversion_source_[c]));
+				}
+			} else {
+				constexpr bool is_final_source = sizeof...(R) == 0;
+
+				// Get the rest of the output, if any.
+				if constexpr (!is_final_source) {
+					next_source_.template apply_samples<action, output_stereo>(number_of_samples, target);
+				}
+
+				if(source_.is_zero_level()) {
+					// This component is currently outputting silence; therefore don't add anything to the output
+					// audio. Zero fill only if this is the final source (as everything above will be additive).
+					if constexpr (is_final_source) {
+						Outputs::Speaker::fill<action>(target, target + number_of_samples, typename SampleT<output_stereo>::type());
+					}
+					return;
+				}
+
+				// Add in this component's output.
+				source_.template apply_samples<is_final_source ? Action::Store : Action::Mix>(number_of_samples, target);
 			}
 		}
 
-		template <Outputs::Speaker::Action action>
-		void apply_samples(std::size_t number_of_samples, Sample *target) {
-			source_holder_.template apply_samples<action, ::Outputs::Speaker::is_stereo<T...>()>(number_of_samples, target);
+		void set_scaled_volume_range(int16_t range, double *volumes, double scale) {
+			const auto scaled_range = volumes[0] / double(source_.average_output_peak()) * double(range) / scale;
+			source_.set_sample_volume_range(int16_t(scaled_range));
+			next_source_.set_scaled_volume_range(range, &volumes[1], scale);
 		}
 
-		/*!
-			Sets the total output volume of this CompoundSource.
-		*/
-		void set_sample_volume_range(int16_t range) {
-			volume_range_ = range;
-			push_volumes();
+		static constexpr std::size_t size() {
+			return 1 + CompoundSourceHolder<R...>::size();
 		}
 
-		/*!
-			Sets the relative volumes of the various sources underlying this
-			compound. The caller should ensure that the number of items supplied
-			matches the number of sources and that the values in it sum to 1.0.
-		*/
-		void set_relative_volumes(const std::vector<double> &volumes) {
-			assert(volumes.size() == source_holder_.size());
-			volumes_ = volumes;
-			push_volumes();
-			average_output_peak_ = 1.0 / source_holder_.total_scale(volumes_.data());
-		}
-
-		/*!
-			@returns the average output peak given the sources owned by this CompoundSource and the
-				current relative volumes.
-		*/
-		double average_output_peak() const {
-			return average_output_peak_;
+		double total_scale(double *volumes) const {
+			return (volumes[0] / source_.average_output_peak()) + next_source_.total_scale(&volumes[1]);
 		}
 
 	private:
-		void push_volumes() {
-			const double scale = source_holder_.total_scale(volumes_.data());
-			source_holder_.set_scaled_volume_range(volume_range_, volumes_.data(), scale);
-		}
+		S &source_;
+		CompoundSourceHolder<R...> next_source_;
+		std::vector<MonoSample> conversion_source_;
+	};
 
-		CompoundSourceHolder<T...> source_holder_;
-		std::vector<double> volumes_;
-		int16_t volume_range_ = 0;
-		std::atomic<double> average_output_peak_{1.0};
+public:
+	using Sample = typename SampleT<::Outputs::Speaker::is_stereo<T...>()>::type;
+
+	// To ensure at most one mono to stereo conversion, require appropriate source ordering.
+	static_assert(are_properly_ordered<T...>(), "Sources should be listed with all stereo sources before all mono sources");
+
+	CompoundSource(T &... sources) : source_holder_(sources...) {
+		// Default: give all sources equal volume.
+		const auto volume = 1.0 / double(source_holder_.size());
+		for(std::size_t c = 0; c < source_holder_.size(); ++c) {
+			volumes_.push_back(volume);
+		}
+	}
+
+	template <Outputs::Speaker::Action action>
+	void apply_samples(std::size_t number_of_samples, Sample *target) {
+		source_holder_.template apply_samples<action, ::Outputs::Speaker::is_stereo<T...>()>(number_of_samples, target);
+	}
+
+	/*!
+		Sets the total output volume of this CompoundSource.
+	*/
+	void set_sample_volume_range(int16_t range) {
+		volume_range_ = range;
+		push_volumes();
+	}
+
+	/*!
+		Sets the relative volumes of the various sources underlying this
+		compound. The caller should ensure that the number of items supplied
+		matches the number of sources and that the values in it sum to 1.0.
+	*/
+	void set_relative_volumes(const std::vector<double> &volumes) {
+		assert(volumes.size() == source_holder_.size());
+		volumes_ = volumes;
+		push_volumes();
+		average_output_peak_ = 1.0 / source_holder_.total_scale(volumes_.data());
+	}
+
+	/*!
+		@returns the average output peak given the sources owned by this CompoundSource and the
+			current relative volumes.
+	*/
+	double average_output_peak() const {
+		return average_output_peak_;
+	}
+
+private:
+	void push_volumes() {
+		const double scale = source_holder_.total_scale(volumes_.data());
+		source_holder_.set_scaled_volume_range(volume_range_, volumes_.data(), scale);
+	}
+
+	CompoundSourceHolder<T...> source_holder_;
+	std::vector<double> volumes_;
+	int16_t volume_range_ = 0;
+	std::atomic<double> average_output_peak_{1.0};
 };
 
 }
diff --git a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp
index 1730022e8..b552ea91e 100644
--- a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp
+++ b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp
@@ -23,324 +23,324 @@
 namespace Outputs::Speaker {
 
 template <typename ConcreteT, bool is_stereo> class LowpassBase: public Speaker {
-	public:
-		/*!
-			Sets the clock rate of the input audio.
-		*/
-		void set_input_rate(float cycles_per_second) {
-			std::lock_guard lock_guard(filter_parameters_mutex_);
-			if(filter_parameters_.input_cycles_per_second == cycles_per_second) {
-				return;
-			}
-			filter_parameters_.input_cycles_per_second = cycles_per_second;
-			filter_parameters_.parameters_are_dirty = true;
-			filter_parameters_.input_rate_changed = true;
+public:
+	/*!
+		Sets the clock rate of the input audio.
+	*/
+	void set_input_rate(float cycles_per_second) {
+		std::lock_guard lock_guard(filter_parameters_mutex_);
+		if(filter_parameters_.input_cycles_per_second == cycles_per_second) {
+			return;
+		}
+		filter_parameters_.input_cycles_per_second = cycles_per_second;
+		filter_parameters_.parameters_are_dirty = true;
+		filter_parameters_.input_rate_changed = true;
+	}
+
+	/*!
+		Allows a cut-off frequency to be specified for audio. Ordinarily this low-pass speaker
+		will determine a cut-off based on the output audio rate. A caller can manually select
+		an alternative cut-off. This allows machines with a low-pass filter on their audio output
+		path to be explicit about its effect, and get that simulation for free.
+	*/
+	void set_high_frequency_cutoff(float high_frequency) {
+		std::lock_guard lock_guard(filter_parameters_mutex_);
+		if(filter_parameters_.high_frequency_cutoff == high_frequency) {
+			return;
+		}
+		filter_parameters_.high_frequency_cutoff = high_frequency;
+		filter_parameters_.parameters_are_dirty = true;
+	}
+
+private:
+	float get_ideal_clock_rate_in_range(float minimum, float maximum) final {
+		std::lock_guard lock_guard(filter_parameters_mutex_);
+
+		// Return twice the cut off, if applicable.
+		if(	filter_parameters_.high_frequency_cutoff > 0.0f &&
+			filter_parameters_.input_cycles_per_second >= filter_parameters_.high_frequency_cutoff * 3.0f &&
+			filter_parameters_.input_cycles_per_second <= filter_parameters_.high_frequency_cutoff * 3.0f)
+				return filter_parameters_.high_frequency_cutoff * 3.0f;
+
+		// Return exactly the input rate if possible.
+		if(	filter_parameters_.input_cycles_per_second >= minimum &&
+			filter_parameters_.input_cycles_per_second <= maximum)
+				return filter_parameters_.input_cycles_per_second;
+
+		// If the input rate is lower, return the minimum...
+		if(filter_parameters_.input_cycles_per_second < minimum)
+			return minimum;
+
+		// ... otherwise, return the maximum.
+		return maximum;
+	}
+
+	// Implemented as per Speaker.
+	void set_computed_output_rate(float cycles_per_second, int buffer_size, bool) final {
+		std::lock_guard lock_guard(filter_parameters_mutex_);
+		if(filter_parameters_.output_cycles_per_second == cycles_per_second && size_t(buffer_size) == output_buffer_.size()) {
+			return;
 		}
 
-		/*!
-			Allows a cut-off frequency to be specified for audio. Ordinarily this low-pass speaker
-			will determine a cut-off based on the output audio rate. A caller can manually select
-			an alternative cut-off. This allows machines with a low-pass filter on their audio output
-			path to be explicit about its effect, and get that simulation for free.
-		*/
-		void set_high_frequency_cutoff(float high_frequency) {
-			std::lock_guard lock_guard(filter_parameters_mutex_);
-			if(filter_parameters_.high_frequency_cutoff == high_frequency) {
-				return;
-			}
-			filter_parameters_.high_frequency_cutoff = high_frequency;
-			filter_parameters_.parameters_are_dirty = true;
+		filter_parameters_.output_cycles_per_second = cycles_per_second;
+		filter_parameters_.parameters_are_dirty = true;
+		output_buffer_.resize(std::size_t(buffer_size) * (is_stereo + 1));
+	}
+
+	// MARK: - Filtering.
+
+	std::size_t output_buffer_pointer_ = 0;
+	std::size_t input_buffer_depth_ = 0;
+	std::vector<int16_t> input_buffer_;
+	std::vector<int16_t> output_buffer_;
+
+	float step_rate_ = 0.0f;
+	float position_error_ = 0.0f;
+	std::unique_ptr<SignalProcessing::FIRFilter> filter_;
+
+	std::mutex filter_parameters_mutex_;
+	struct FilterParameters {
+		float input_cycles_per_second = 0.0f;
+		float output_cycles_per_second = 0.0f;
+		float high_frequency_cutoff = -1.0;
+
+		bool parameters_are_dirty = true;
+		bool input_rate_changed = false;
+	} filter_parameters_;
+
+	void update_filter_coefficients(const FilterParameters &filter_parameters) {
+		float high_pass_frequency = filter_parameters.output_cycles_per_second / 2.0f;
+		if(filter_parameters.high_frequency_cutoff > 0.0) {
+			high_pass_frequency = std::min(filter_parameters.high_frequency_cutoff, high_pass_frequency);
 		}
 
-	private:
-		float get_ideal_clock_rate_in_range(float minimum, float maximum) final {
-			std::lock_guard lock_guard(filter_parameters_mutex_);
+		// Make a guess at a good number of taps.
+		std::size_t number_of_taps = std::size_t(
+			ceilf((filter_parameters.input_cycles_per_second + high_pass_frequency) / high_pass_frequency)
+		);
+		number_of_taps = (number_of_taps * 2) | 1;
 
-			// Return twice the cut off, if applicable.
-			if(	filter_parameters_.high_frequency_cutoff > 0.0f &&
-				filter_parameters_.input_cycles_per_second >= filter_parameters_.high_frequency_cutoff * 3.0f &&
-				filter_parameters_.input_cycles_per_second <= filter_parameters_.high_frequency_cutoff * 3.0f)
-					return filter_parameters_.high_frequency_cutoff * 3.0f;
+		step_rate_ = filter_parameters.input_cycles_per_second / filter_parameters.output_cycles_per_second;
+		position_error_ = 0.0f;
 
-			// Return exactly the input rate if possible.
-			if(	filter_parameters_.input_cycles_per_second >= minimum &&
-				filter_parameters_.input_cycles_per_second <= maximum)
-					return filter_parameters_.input_cycles_per_second;
+		filter_ = std::make_unique<SignalProcessing::FIRFilter>(
+			unsigned(number_of_taps),
+			filter_parameters.input_cycles_per_second,
+			0.0,
+			high_pass_frequency,
+			SignalProcessing::FIRFilter::DefaultAttenuation);
 
-			// If the input rate is lower, return the minimum...
-			if(filter_parameters_.input_cycles_per_second < minimum)
-				return minimum;
-
-			// ... otherwise, return the maximum.
-			return maximum;
+		// Pick the new conversion function.
+		if(	filter_parameters.input_cycles_per_second == filter_parameters.output_cycles_per_second &&
+			filter_parameters.high_frequency_cutoff < 0.0) {
+			// If input and output rates exactly match, and no additional cut-off has been specified,
+			// just accumulate results and pass on.
+			conversion_ = Conversion::Copy;
+		} else if(	filter_parameters.input_cycles_per_second > filter_parameters.output_cycles_per_second ||
+			(filter_parameters.input_cycles_per_second == filter_parameters.output_cycles_per_second && filter_parameters.high_frequency_cutoff >= 0.0)) {
+			// If the output rate is less than the input rate, or an additional cut-off has been specified, use the filter.
+			conversion_ = Conversion::ResampleSmaller;
+		} else {
+			conversion_ = Conversion::ResampleLarger;
 		}
 
-		// Implemented as per Speaker.
-		void set_computed_output_rate(float cycles_per_second, int buffer_size, bool) final {
-			std::lock_guard lock_guard(filter_parameters_mutex_);
-			if(filter_parameters_.output_cycles_per_second == cycles_per_second && size_t(buffer_size) == output_buffer_.size()) {
-				return;
-			}
+		// Do something sensible with any dangling input, if necessary.
+		const int scale = static_cast<ConcreteT *>(this)->get_scale();
+		switch(conversion_) {
+			// Neither direct copying nor resampling larger currently use any temporary input.
+			// Although in the latter case that's just because it's unimplemented. But, regardless,
+			// that means nothing to do.
+			default: break;
 
-			filter_parameters_.output_cycles_per_second = cycles_per_second;
-			filter_parameters_.parameters_are_dirty = true;
-			output_buffer_.resize(std::size_t(buffer_size) * (is_stereo + 1));
-		}
-
-		// MARK: - Filtering.
-
-		std::size_t output_buffer_pointer_ = 0;
-		std::size_t input_buffer_depth_ = 0;
-		std::vector<int16_t> input_buffer_;
-		std::vector<int16_t> output_buffer_;
-
-		float step_rate_ = 0.0f;
-		float position_error_ = 0.0f;
-		std::unique_ptr<SignalProcessing::FIRFilter> filter_;
-
-		std::mutex filter_parameters_mutex_;
-		struct FilterParameters {
-			float input_cycles_per_second = 0.0f;
-			float output_cycles_per_second = 0.0f;
-			float high_frequency_cutoff = -1.0;
-
-			bool parameters_are_dirty = true;
-			bool input_rate_changed = false;
-		} filter_parameters_;
-
-		void update_filter_coefficients(const FilterParameters &filter_parameters) {
-			float high_pass_frequency = filter_parameters.output_cycles_per_second / 2.0f;
-			if(filter_parameters.high_frequency_cutoff > 0.0) {
-				high_pass_frequency = std::min(filter_parameters.high_frequency_cutoff, high_pass_frequency);
-			}
-
-			// Make a guess at a good number of taps.
-			std::size_t number_of_taps = std::size_t(
-				ceilf((filter_parameters.input_cycles_per_second + high_pass_frequency) / high_pass_frequency)
-			);
-			number_of_taps = (number_of_taps * 2) | 1;
-
-			step_rate_ = filter_parameters.input_cycles_per_second / filter_parameters.output_cycles_per_second;
-			position_error_ = 0.0f;
-
-			filter_ = std::make_unique<SignalProcessing::FIRFilter>(
-				unsigned(number_of_taps),
-				filter_parameters.input_cycles_per_second,
-				0.0,
-				high_pass_frequency,
-				SignalProcessing::FIRFilter::DefaultAttenuation);
-
-			// Pick the new conversion function.
-			if(	filter_parameters.input_cycles_per_second == filter_parameters.output_cycles_per_second &&
-				filter_parameters.high_frequency_cutoff < 0.0) {
-				// If input and output rates exactly match, and no additional cut-off has been specified,
-				// just accumulate results and pass on.
-				conversion_ = Conversion::Copy;
-			} else if(	filter_parameters.input_cycles_per_second > filter_parameters.output_cycles_per_second ||
-				(filter_parameters.input_cycles_per_second == filter_parameters.output_cycles_per_second && filter_parameters.high_frequency_cutoff >= 0.0)) {
-				// If the output rate is less than the input rate, or an additional cut-off has been specified, use the filter.
-				conversion_ = Conversion::ResampleSmaller;
-			} else {
-				conversion_ = Conversion::ResampleLarger;
-			}
-
-			// Do something sensible with any dangling input, if necessary.
-			const int scale = static_cast<ConcreteT *>(this)->get_scale();
-			switch(conversion_) {
-				// Neither direct copying nor resampling larger currently use any temporary input.
-				// Although in the latter case that's just because it's unimplemented. But, regardless,
-				// that means nothing to do.
-				default: break;
-
-				case Conversion::ResampleSmaller: {
-					// Reize the input buffer only if absolutely necessary; if sizing downward
-					// such that a sample would otherwise be lost then output it now. Keep anything
-					// currently in the input buffer that hasn't yet been processed.
-					const size_t required_buffer_size = size_t(number_of_taps) * (is_stereo + 1);
-					if(input_buffer_.size() != required_buffer_size) {
-						if(input_buffer_depth_ >= required_buffer_size) {
-							resample_input_buffer(scale);
-							input_buffer_depth_ %= required_buffer_size;
-						}
-						input_buffer_.resize(required_buffer_size);
+			case Conversion::ResampleSmaller: {
+				// Reize the input buffer only if absolutely necessary; if sizing downward
+				// such that a sample would otherwise be lost then output it now. Keep anything
+				// currently in the input buffer that hasn't yet been processed.
+				const size_t required_buffer_size = size_t(number_of_taps) * (is_stereo + 1);
+				if(input_buffer_.size() != required_buffer_size) {
+					if(input_buffer_depth_ >= required_buffer_size) {
+						resample_input_buffer(scale);
+						input_buffer_depth_ %= required_buffer_size;
 					}
-				} break;
-			}
+					input_buffer_.resize(required_buffer_size);
+				}
+			} break;
+		}
+	}
+
+	inline void resample_input_buffer(int scale) {
+		if(output_buffer_.empty()) {
+			return;
 		}
 
-		inline void resample_input_buffer(int scale) {
-			if(output_buffer_.empty()) {
-				return;
-			}
+		if constexpr (is_stereo) {
+			output_buffer_[output_buffer_pointer_ + 0] = filter_->apply(input_buffer_.data(), 2);
+			output_buffer_[output_buffer_pointer_ + 1] = filter_->apply(input_buffer_.data() + 1, 2);
+			output_buffer_pointer_+= 2;
+		} else {
+			output_buffer_[output_buffer_pointer_] = filter_->apply(input_buffer_.data());
+			output_buffer_pointer_++;
+		}
 
+		// Apply scale, if supplied, clamping appropriately.
+		if(scale != 65536) {
+			#define SCALE(x) x = int16_t(std::clamp((int(x) * scale) >> 16, -32768, 32767))
 			if constexpr (is_stereo) {
-				output_buffer_[output_buffer_pointer_ + 0] = filter_->apply(input_buffer_.data(), 2);
-				output_buffer_[output_buffer_pointer_ + 1] = filter_->apply(input_buffer_.data() + 1, 2);
-				output_buffer_pointer_+= 2;
+				SCALE(output_buffer_[output_buffer_pointer_ - 2]);
+				SCALE(output_buffer_[output_buffer_pointer_ - 1]);
 			} else {
-				output_buffer_[output_buffer_pointer_] = filter_->apply(input_buffer_.data());
-				output_buffer_pointer_++;
-			}
-
-			// Apply scale, if supplied, clamping appropriately.
-			if(scale != 65536) {
-				#define SCALE(x) x = int16_t(std::clamp((int(x) * scale) >> 16, -32768, 32767))
-				if constexpr (is_stereo) {
-					SCALE(output_buffer_[output_buffer_pointer_ - 2]);
-					SCALE(output_buffer_[output_buffer_pointer_ - 1]);
-				} else {
-					SCALE(output_buffer_[output_buffer_pointer_ - 1]);
-				}
-				#undef SCALE
-			}
-
-			// Announce to delegate if full.
-			if(output_buffer_pointer_ == output_buffer_.size()) {
-				output_buffer_pointer_ = 0;
-				did_complete_samples(this, output_buffer_, is_stereo);
-			}
-
-			// If the next loop around is going to reuse some of the samples just collected, use a memmove to
-			// preserve them in the correct locations (TODO: use a longer buffer to fix that?) and don't skip
-			// anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse.
-			const size_t steps = size_t(step_rate_ + position_error_) * (is_stereo + 1);
-			position_error_ = fmodf(step_rate_ + position_error_, 1.0f);
-			if(steps < input_buffer_.size()) {
-				auto *const input_buffer = input_buffer_.data();
-				std::memmove(	input_buffer,
-								&input_buffer[steps],
-								sizeof(int16_t) * (input_buffer_.size() - steps));
-				input_buffer_depth_ -= steps;
-			} else {
-				if(steps > input_buffer_.size()) {
-					static_cast<ConcreteT *>(this)->skip_samples((steps - input_buffer_.size()) / (1 + is_stereo));
-				}
-				input_buffer_depth_ = 0;
+				SCALE(output_buffer_[output_buffer_pointer_ - 1]);
 			}
+			#undef SCALE
 		}
 
-		enum class Conversion {
-			ResampleSmaller,
-			Copy,
-			ResampleLarger
-		} conversion_ = Conversion::Copy;
-
-		bool recalculate_filter_if_dirty() {
-			FilterParameters filter_parameters;
-			{
-				std::lock_guard lock_guard(filter_parameters_mutex_);
-				filter_parameters = filter_parameters_;
-				filter_parameters_.parameters_are_dirty = false;
-				filter_parameters_.input_rate_changed = false;
-			}
-			if(filter_parameters.parameters_are_dirty) update_filter_coefficients(filter_parameters);
-			return filter_parameters.input_rate_changed;
+		// Announce to delegate if full.
+		if(output_buffer_pointer_ == output_buffer_.size()) {
+			output_buffer_pointer_ = 0;
+			did_complete_samples(this, output_buffer_, is_stereo);
 		}
 
-	protected:
-		bool process(size_t length) {
-			const auto delegate = delegate_.load(std::memory_order_relaxed);
-			if(!delegate) return false;
-
-			const int scale = static_cast<ConcreteT *>(this)->get_scale();
-
-			if(recalculate_filter_if_dirty()) {
-				delegate->speaker_did_change_input_clock(this);
+		// If the next loop around is going to reuse some of the samples just collected, use a memmove to
+		// preserve them in the correct locations (TODO: use a longer buffer to fix that?) and don't skip
+		// anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse.
+		const size_t steps = size_t(step_rate_ + position_error_) * (is_stereo + 1);
+		position_error_ = fmodf(step_rate_ + position_error_, 1.0f);
+		if(steps < input_buffer_.size()) {
+			auto *const input_buffer = input_buffer_.data();
+			std::memmove(	input_buffer,
+							&input_buffer[steps],
+							sizeof(int16_t) * (input_buffer_.size() - steps));
+			input_buffer_depth_ -= steps;
+		} else {
+			if(steps > input_buffer_.size()) {
+				static_cast<ConcreteT *>(this)->skip_samples((steps - input_buffer_.size()) / (1 + is_stereo));
 			}
+			input_buffer_depth_ = 0;
+		}
+	}
 
-			switch(conversion_) {
-				case Conversion::Copy:
-					while(length) {
-						const auto samples_to_read = std::min((output_buffer_.size() - output_buffer_pointer_) / (1 + is_stereo), length);
-						static_cast<ConcreteT *>(this)->get_samples(samples_to_read, &output_buffer_[output_buffer_pointer_ ]);
-						output_buffer_pointer_ += samples_to_read * (1 + is_stereo);
+	enum class Conversion {
+		ResampleSmaller,
+		Copy,
+		ResampleLarger
+	} conversion_ = Conversion::Copy;
 
-						// TODO: apply scale.
+	bool recalculate_filter_if_dirty() {
+		FilterParameters filter_parameters;
+		{
+			std::lock_guard lock_guard(filter_parameters_mutex_);
+			filter_parameters = filter_parameters_;
+			filter_parameters_.parameters_are_dirty = false;
+			filter_parameters_.input_rate_changed = false;
+		}
+		if(filter_parameters.parameters_are_dirty) update_filter_coefficients(filter_parameters);
+		return filter_parameters.input_rate_changed;
+	}
 
-						// Announce to delegate if full.
-						if(output_buffer_pointer_ == output_buffer_.size()) {
-							output_buffer_pointer_ = 0;
-							did_complete_samples(this, output_buffer_, is_stereo);
-						}
+protected:
+	bool process(size_t length) {
+		const auto delegate = delegate_.load(std::memory_order_relaxed);
+		if(!delegate) return false;
 
-						length -= samples_to_read;
+		const int scale = static_cast<ConcreteT *>(this)->get_scale();
+
+		if(recalculate_filter_if_dirty()) {
+			delegate->speaker_did_change_input_clock(this);
+		}
+
+		switch(conversion_) {
+			case Conversion::Copy:
+				while(length) {
+					const auto samples_to_read = std::min((output_buffer_.size() - output_buffer_pointer_) / (1 + is_stereo), length);
+					static_cast<ConcreteT *>(this)->get_samples(samples_to_read, &output_buffer_[output_buffer_pointer_ ]);
+					output_buffer_pointer_ += samples_to_read * (1 + is_stereo);
+
+					// TODO: apply scale.
+
+					// Announce to delegate if full.
+					if(output_buffer_pointer_ == output_buffer_.size()) {
+						output_buffer_pointer_ = 0;
+						did_complete_samples(this, output_buffer_, is_stereo);
 					}
-				break;
 
-				case Conversion::ResampleSmaller:
-					while(length) {
-						const auto cycles_to_read = std::min((input_buffer_.size() - input_buffer_depth_) / (1 + is_stereo), length);
-						static_cast<ConcreteT *>(this)->get_samples(cycles_to_read, &input_buffer_[input_buffer_depth_]);
-						input_buffer_depth_ += cycles_to_read * (1 + is_stereo);
+					length -= samples_to_read;
+				}
+			break;
 
-						if(input_buffer_depth_ == input_buffer_.size()) {
-							resample_input_buffer(scale);
-						}
+			case Conversion::ResampleSmaller:
+				while(length) {
+					const auto cycles_to_read = std::min((input_buffer_.size() - input_buffer_depth_) / (1 + is_stereo), length);
+					static_cast<ConcreteT *>(this)->get_samples(cycles_to_read, &input_buffer_[input_buffer_depth_]);
+					input_buffer_depth_ += cycles_to_read * (1 + is_stereo);
 
-						length -= cycles_to_read;
+					if(input_buffer_depth_ == input_buffer_.size()) {
+						resample_input_buffer(scale);
 					}
-				break;
 
-				case Conversion::ResampleLarger:
-					// TODO: input rate is less than output rate.
-				break;
-			}
+					length -= cycles_to_read;
+				}
+			break;
 
-			return true;
+			case Conversion::ResampleLarger:
+				// TODO: input rate is less than output rate.
+			break;
 		}
+
+		return true;
+	}
 };
 
 /*!
 	Provides a low-pass speaker to which blocks of samples are pushed.
 */
 template <bool is_stereo> class PushLowpass: public LowpassBase<PushLowpass<is_stereo>, is_stereo> {
-	private:
-		using BaseT = LowpassBase<PushLowpass<is_stereo>, is_stereo>;
-		friend BaseT;
-		using BaseT::process;
+private:
+	using BaseT = LowpassBase<PushLowpass<is_stereo>, is_stereo>;
+	friend BaseT;
+	using BaseT::process;
 
-		std::atomic<int> scale_ = 65536;
-		int get_scale() const {
-			return scale_.load(std::memory_order_relaxed);
-		}
+	std::atomic<int> scale_ = 65536;
+	int get_scale() const {
+		return scale_.load(std::memory_order_relaxed);
+	}
 
-		const int16_t *buffer_ = nullptr;
+	const int16_t *buffer_ = nullptr;
 
-		void skip_samples(size_t count) {
-			buffer_ += count;
-		}
+	void skip_samples(size_t count) {
+		buffer_ += count;
+	}
 
-		void get_samples(size_t length, int16_t *target) {
-			const auto word_length = length * (1 + is_stereo);
-			memcpy(target, buffer_, word_length * sizeof(int16_t));
-			buffer_ += word_length;
-		}
+	void get_samples(size_t length, int16_t *target) {
+		const auto word_length = length * (1 + is_stereo);
+		memcpy(target, buffer_, word_length * sizeof(int16_t));
+		buffer_ += word_length;
+	}
 
-	public:
-		void set_output_volume(float volume) final {
-			scale_.store(int(std::clamp(volume * 65536.0f, 0.0f, 65536.0f)));
-		}
+public:
+	void set_output_volume(float volume) final {
+		scale_.store(int(std::clamp(volume * 65536.0f, 0.0f, 65536.0f)));
+	}
 
-		bool get_is_stereo() final {
-			return is_stereo;
-		}
+	bool get_is_stereo() final {
+		return is_stereo;
+	}
 
-		/*!
-			Filters and posts onward the provided buffer, on the calling thread.
+	/*!
+		Filters and posts onward the provided buffer, on the calling thread.
 
-			@param buffer The source for samples.
-			@param length The number of samples provided; in mono this will be the number of int16_ts
-				it is safe to read from @c buffer, and in stereo it will be half the number — it is a count
-				of the number of time points at which audio was sampled.
-		*/
-		void push(const int16_t *buffer, size_t length) {
-			buffer_ = buffer;
+		@param buffer The source for samples.
+		@param length The number of samples provided; in mono this will be the number of int16_ts
+			it is safe to read from @c buffer, and in stereo it will be half the number — it is a count
+			of the number of time points at which audio was sampled.
+	*/
+	void push(const int16_t *buffer, size_t length) {
+		buffer_ = buffer;
 #ifndef NDEBUG
-			const bool did_process =
+		const bool did_process =
 #endif
-				process(length);
-			assert(!did_process || buffer_ == buffer + (length * (1 + is_stereo)));
-		}
+			process(length);
+		assert(!did_process || buffer_ == buffer + (length * (1 + is_stereo)));
+	}
 };
 
 /*!
@@ -350,68 +350,68 @@ template <bool is_stereo> class PushLowpass: public LowpassBase<PushLowpass<is_s
 	lower-frequency output.
 */
 template <typename SampleSource> class PullLowpass: public LowpassBase<PullLowpass<SampleSource>, SampleSource::is_stereo> {
-	public:
-		PullLowpass(SampleSource &sample_source) : sample_source_(sample_source) {
-			// Propagate an initial volume level.
-			sample_source.set_sample_volume_range(32767);
+public:
+	PullLowpass(SampleSource &sample_source) : sample_source_(sample_source) {
+		// Propagate an initial volume level.
+		sample_source.set_sample_volume_range(32767);
+	}
+
+	void set_output_volume(float volume) final {
+		// Clamp to the acceptable range, and set.
+		volume = std::clamp(volume, 0.0f, 1.0f);
+		sample_source_.set_sample_volume_range(int16_t(32767.0f * volume));
+	}
+
+	bool get_is_stereo() final {
+		return SampleSource::is_stereo;
+	}
+
+	/*!
+		Schedules an advancement by the number of cycles specified on the provided queue.
+		The speaker will advance by obtaining data from the sample source supplied
+		at construction, filtering it and passing it on to the speaker's delegate if there is one.
+	*/
+	void run_for(Concurrency::AsyncTaskQueue<false> &queue, const Cycles cycles) {
+		if(cycles == Cycles(0)) {
+			return;
 		}
 
-		void set_output_volume(float volume) final {
-			// Clamp to the acceptable range, and set.
-			volume = std::clamp(volume, 0.0f, 1.0f);
-			sample_source_.set_sample_volume_range(int16_t(32767.0f * volume));
-		}
-
-		bool get_is_stereo() final {
-			return SampleSource::is_stereo;
-		}
-
-		/*!
-			Schedules an advancement by the number of cycles specified on the provided queue.
-			The speaker will advance by obtaining data from the sample source supplied
-			at construction, filtering it and passing it on to the speaker's delegate if there is one.
-		*/
-		void run_for(Concurrency::AsyncTaskQueue<false> &queue, const Cycles cycles) {
-			if(cycles == Cycles(0)) {
-				return;
-			}
-
-			queue.enqueue([this, cycles] {
-				run_for(cycles);
-			});
-		}
-
-	private:
-		using BaseT = LowpassBase<PullLowpass<SampleSource>, SampleSource::is_stereo>;
-		friend BaseT;
-		using BaseT::process;
-
-		/*!
-			Advances by the number of cycles specified, obtaining data from the sample source supplied
-			at construction, filtering it and passing it on to the speaker's delegate if there is one.
-		*/
-		void run_for(const Cycles cycles) {
-			process(size_t(cycles.as_integral()));
-		}
-
-		SampleSource &sample_source_;
-
-		void skip_samples(size_t count) {
-			sample_source_.template apply_samples<Action::Ignore>(count, nullptr);
-		}
-
-		int get_scale() {
-			return int(65536.0 / sample_source_.average_output_peak());
-		}
-
-		void get_samples(size_t length, int16_t *target) {
-			if constexpr (SampleSource::is_stereo) {
-				StereoSample *const stereo_target = reinterpret_cast<StereoSample *>(target);
-				sample_source_.template apply_samples<Action::Store>(length, stereo_target);
-			} else {
-				sample_source_.template apply_samples<Action::Store>(length, target);
-			}
+		queue.enqueue([this, cycles] {
+			run_for(cycles);
+		});
+	}
+
+private:
+	using BaseT = LowpassBase<PullLowpass<SampleSource>, SampleSource::is_stereo>;
+	friend BaseT;
+	using BaseT::process;
+
+	/*!
+		Advances by the number of cycles specified, obtaining data from the sample source supplied
+		at construction, filtering it and passing it on to the speaker's delegate if there is one.
+	*/
+	void run_for(const Cycles cycles) {
+		process(size_t(cycles.as_integral()));
+	}
+
+	SampleSource &sample_source_;
+
+	void skip_samples(size_t count) {
+		sample_source_.template apply_samples<Action::Ignore>(count, nullptr);
+	}
+
+	int get_scale() {
+		return int(65536.0 / sample_source_.average_output_peak());
+	}
+
+	void get_samples(size_t length, int16_t *target) {
+		if constexpr (SampleSource::is_stereo) {
+			StereoSample *const stereo_target = reinterpret_cast<StereoSample *>(target);
+			sample_source_.template apply_samples<Action::Store>(length, stereo_target);
+		} else {
+			sample_source_.template apply_samples<Action::Store>(length, target);
 		}
+	}
 };
 
 }
diff --git a/Outputs/Speaker/Speaker.hpp b/Outputs/Speaker/Speaker.hpp
index c6a934156..aaed2b77f 100644
--- a/Outputs/Speaker/Speaker.hpp
+++ b/Outputs/Speaker/Speaker.hpp
@@ -61,128 +61,128 @@ template <bool stereo> struct SampleT {
 	audio output.
 */
 class Speaker {
-	public:
-		virtual ~Speaker() = default;
+public:
+	virtual ~Speaker() = default;
+
+	/*!
+		@returns The best output clock rate for the audio being supplied to this speaker, from the range given.
+	*/
+	virtual float get_ideal_clock_rate_in_range(float minimum, float maximum) = 0;
+
+	/*!
+		@returns @c true if the device would most ideally output stereo sound; @c false otherwise.
+	*/
+	virtual bool get_is_stereo() = 0;
+
+	/*!
+		Sets the actual output rate; packets provided to the delegate will conform to these
+		specifications regardless of the input.
+	*/
+	void set_output_rate(float cycles_per_second, int buffer_size, bool stereo) {
+		output_cycles_per_second_ = cycles_per_second;
+		output_buffer_size_ = buffer_size;
+		stereo_output_ = stereo;
+		compute_output_rate();
+	}
+
+	/*!
+		Takes a copy of the most recent output rate provided to @c rhs.
+	*/
+	void copy_output_rate(const Speaker &rhs) {
+		output_cycles_per_second_ = rhs.output_cycles_per_second_;
+		output_buffer_size_ = rhs.output_buffer_size_;
+		stereo_output_.store(rhs.stereo_output_.load(std::memory_order_relaxed), std::memory_order_relaxed);
+		compute_output_rate();
+	}
+
+	/// Sets the output volume, in the range [0, 1].
+	virtual void set_output_volume(float) = 0;
+
+	/*!
+		Speeds a speed multiplier for this machine, e.g. that it is currently being run at 2.0x its normal rate.
+		This will affect the number of input samples that are combined to produce one output sample.
+	*/
+	void set_input_rate_multiplier(float multiplier) {
+		input_rate_multiplier_ = multiplier;
+		compute_output_rate();
+	}
+
+	/*!
+		@returns The number of sample sets so far delivered to the delegate.
+	*/
+	int completed_sample_sets() const { return completed_sample_sets_; }
+
+	/*!
+		Defines a receiver for audio packets.
+	*/
+	struct Delegate {
+		/*!
+			Indicates that a new audio packet is ready. If the output is stereo, samples will be interleaved with the first
+			being left, the second being right, etc.
+		*/
+		virtual void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) = 0;
 
 		/*!
-			@returns The best output clock rate for the audio being supplied to this speaker, from the range given.
+			Provides the delegate with a hint that the input clock rate has changed, which provides an opportunity to
+			renegotiate the ideal clock rate, if desired.
 		*/
-		virtual float get_ideal_clock_rate_in_range(float minimum, float maximum) = 0;
+		virtual void speaker_did_change_input_clock([[maybe_unused]] Speaker *speaker) {}
+	};
+	virtual void set_delegate(Delegate *delegate) {
+		delegate_.store(delegate, std::memory_order_relaxed);
+	}
 
-		/*!
-			@returns @c true if the device would most ideally output stereo sound; @c false otherwise.
-		*/
-		virtual bool get_is_stereo() = 0;
 
-		/*!
-			Sets the actual output rate; packets provided to the delegate will conform to these
-			specifications regardless of the input.
-		*/
-		void set_output_rate(float cycles_per_second, int buffer_size, bool stereo) {
-			output_cycles_per_second_ = cycles_per_second;
-			output_buffer_size_ = buffer_size;
-			stereo_output_ = stereo;
-			compute_output_rate();
+	// This is primarily exposed for MultiSpeaker et al; it's not for general callers.
+	virtual void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) = 0;
+
+protected:
+	void did_complete_samples(Speaker *, const std::vector<int16_t> &buffer, bool is_stereo) {
+		// Test the delegate for existence again, as it may have changed.
+		const auto delegate = delegate_.load(std::memory_order_relaxed);
+		if(!delegate) return;
+
+		++completed_sample_sets_;
+
+		// Hope for the fast path first: producer and consumer agree about
+		// number of channels.
+		if(is_stereo == stereo_output_) {
+			delegate->speaker_did_complete_samples(this, buffer);
+			return;
 		}
 
-		/*!
-			Takes a copy of the most recent output rate provided to @c rhs.
-		*/
-		void copy_output_rate(const Speaker &rhs) {
-			output_cycles_per_second_ = rhs.output_cycles_per_second_;
-			output_buffer_size_ = rhs.output_buffer_size_;
-			stereo_output_.store(rhs.stereo_output_.load(std::memory_order_relaxed), std::memory_order_relaxed);
-			compute_output_rate();
-		}
-
-		/// Sets the output volume, in the range [0, 1].
-		virtual void set_output_volume(float) = 0;
-
-		/*!
-			Speeds a speed multiplier for this machine, e.g. that it is currently being run at 2.0x its normal rate.
-			This will affect the number of input samples that are combined to produce one output sample.
-		*/
-		void set_input_rate_multiplier(float multiplier) {
-			input_rate_multiplier_ = multiplier;
-			compute_output_rate();
-		}
-
-		/*!
-			@returns The number of sample sets so far delivered to the delegate.
-		*/
-		int completed_sample_sets() const { return completed_sample_sets_; }
-
-		/*!
-			Defines a receiver for audio packets.
-		*/
-		struct Delegate {
-			/*!
-				Indicates that a new audio packet is ready. If the output is stereo, samples will be interleaved with the first
-				being left, the second being right, etc.
-			*/
-			virtual void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) = 0;
-
-			/*!
-				Provides the delegate with a hint that the input clock rate has changed, which provides an opportunity to
-				renegotiate the ideal clock rate, if desired.
-			*/
-			virtual void speaker_did_change_input_clock([[maybe_unused]] Speaker *speaker) {}
-		};
-		virtual void set_delegate(Delegate *delegate) {
-			delegate_.store(delegate, std::memory_order_relaxed);
-		}
-
-
-		// This is primarily exposed for MultiSpeaker et al; it's not for general callers.
-		virtual void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) = 0;
-
-	protected:
-		void did_complete_samples(Speaker *, const std::vector<int16_t> &buffer, bool is_stereo) {
-			// Test the delegate for existence again, as it may have changed.
-			const auto delegate = delegate_.load(std::memory_order_relaxed);
-			if(!delegate) return;
-
-			++completed_sample_sets_;
-
-			// Hope for the fast path first: producer and consumer agree about
-			// number of channels.
-			if(is_stereo == stereo_output_) {
-				delegate->speaker_did_complete_samples(this, buffer);
-				return;
+		// Producer and consumer don't agree, so mix two channels to one, or double out one to two.
+		if(is_stereo) {
+			// Mix down.
+			mix_buffer_.resize(buffer.size() / 2);
+			for(size_t c = 0; c < mix_buffer_.size(); ++c) {
+				mix_buffer_[c] = (buffer[(c << 1) + 0] + buffer[(c << 1) + 1]) >> 1;
+				// TODO: is there an Accelerate framework solution to this?
 			}
-
-			// Producer and consumer don't agree, so mix two channels to one, or double out one to two.
-			if(is_stereo) {
-				// Mix down.
-				mix_buffer_.resize(buffer.size() / 2);
-				for(size_t c = 0; c < mix_buffer_.size(); ++c) {
-					mix_buffer_[c] = (buffer[(c << 1) + 0] + buffer[(c << 1) + 1]) >> 1;
-					// TODO: is there an Accelerate framework solution to this?
-				}
-			} else {
-				// Double up.
-				mix_buffer_.resize(buffer.size() * 2);
-				for(size_t c = 0; c < buffer.size(); ++c) {
-					mix_buffer_[(c << 1) + 0] = mix_buffer_[(c << 1) + 1] = buffer[c];
-				}
+		} else {
+			// Double up.
+			mix_buffer_.resize(buffer.size() * 2);
+			for(size_t c = 0; c < buffer.size(); ++c) {
+				mix_buffer_[(c << 1) + 0] = mix_buffer_[(c << 1) + 1] = buffer[c];
 			}
-			delegate->speaker_did_complete_samples(this, mix_buffer_);
 		}
-		std::atomic<Delegate *> delegate_{nullptr};
+		delegate->speaker_did_complete_samples(this, mix_buffer_);
+	}
+	std::atomic<Delegate *> delegate_{nullptr};
 
-	private:
-		void compute_output_rate() {
-			// The input rate multiplier is actually used as an output rate divider,
-			// to confirm to the public interface of a generic speaker being output-centric.
-			set_computed_output_rate(output_cycles_per_second_ / input_rate_multiplier_, output_buffer_size_, stereo_output_);
-		}
+private:
+	void compute_output_rate() {
+		// The input rate multiplier is actually used as an output rate divider,
+		// to confirm to the public interface of a generic speaker being output-centric.
+		set_computed_output_rate(output_cycles_per_second_ / input_rate_multiplier_, output_buffer_size_, stereo_output_);
+	}
 
-		int completed_sample_sets_ = 0;
-		float input_rate_multiplier_ = 1.0f;
-		float output_cycles_per_second_ = 1.0f;
-		int output_buffer_size_ = 1;
-		std::atomic<bool> stereo_output_{false};
-		std::vector<int16_t> mix_buffer_;
+	int completed_sample_sets_ = 0;
+	float input_rate_multiplier_ = 1.0f;
+	float output_cycles_per_second_ = 1.0f;
+	int output_buffer_size_ = 1;
+	std::atomic<bool> stereo_output_{false};
+	std::vector<int16_t> mix_buffer_;
 };
 
 }
diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp
index 74983db96..a1b009d86 100644
--- a/Processors/6502/6502.hpp
+++ b/Processors/6502/6502.hpp
@@ -142,28 +142,28 @@ class ProcessorBase: public ProcessorStorage {
 	can produce a minor runtime performance improvement.
 */
 template <Personality personality, typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
-	public:
-		/*!
-			Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
-		*/
-		Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
+public:
+	/*!
+		Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
+	*/
+	Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
 
-		/*!
-			Runs the 6502 for a supplied number of cycles.
+	/*!
+		Runs the 6502 for a supplied number of cycles.
 
-			@param cycles The number of cycles to run the 6502 for.
-		*/
-		void run_for(const Cycles cycles);
+		@param cycles The number of cycles to run the 6502 for.
+	*/
+	void run_for(const Cycles cycles);
 
-		/*!
-			Sets the current level of the RDY line.
+	/*!
+		Sets the current level of the RDY line.
 
-			@param active @c true if the line is logically active; @c false otherwise.
-		*/
-		void set_ready_line(bool active);
+		@param active @c true if the line is logically active; @c false otherwise.
+	*/
+	void set_ready_line(bool active);
 
-	private:
-		BusHandler &bus_handler_;
+private:
+	BusHandler &bus_handler_;
 };
 
 #include "Implementation/6502Implementation.hpp"
diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp
index ca7963432..e5497a4d5 100644
--- a/Processors/6502/AllRAM/6502AllRAM.cpp
+++ b/Processors/6502/AllRAM/6502AllRAM.cpp
@@ -28,131 +28,131 @@ using Type = CPU::MOS6502Esque::Type;
 template <Type type, bool has_cias> class ConcreteAllRAMProcessor:
 	public AllRAMProcessor, public CPU::MOS6502Esque::BusHandlerT<type>
 {
-	public:
-		using typename CPU::MOS6502Esque::BusHandlerT<type>::AddressType;
+public:
+	using typename CPU::MOS6502Esque::BusHandlerT<type>::AddressType;
 
-		ConcreteAllRAMProcessor(size_t memory_size) :
-			AllRAMProcessor(memory_size),
-			mos6502_(*this),
-			cia1_(cia1_handler_),
-			cia2_(cia2_handler_) {
-			mos6502_.set_power_on(false);
+	ConcreteAllRAMProcessor(size_t memory_size) :
+		AllRAMProcessor(memory_size),
+		mos6502_(*this),
+		cia1_(cia1_handler_),
+		cia2_(cia2_handler_) {
+		mos6502_.set_power_on(false);
+	}
+
+	Cycles perform_bus_operation(BusOperation operation, AddressType address, uint8_t *value) {
+		timestamp_ += Cycles(1);
+
+		if constexpr (has_cias) {
+			cia1_.run_for(HalfCycles(2));
+			cia2_.run_for(HalfCycles(2));
 		}
 
-		Cycles perform_bus_operation(BusOperation operation, AddressType address, uint8_t *value) {
-			timestamp_ += Cycles(1);
-
-			if constexpr (has_cias) {
-				cia1_.run_for(HalfCycles(2));
-				cia2_.run_for(HalfCycles(2));
+		if(isAccessOperation(operation)) {
+			if(operation == BusOperation::ReadOpcode) {
+				if constexpr (LogProgramCounter) {
+					printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address],
+						mos6502_.value_of(Register::A),
+						mos6502_.value_of(Register::X),
+						mos6502_.value_of(Register::Y),
+						mos6502_.value_of(Register::Flags) & 0xff,
+						mos6502_.value_of(Register::StackPointer) & 0xff);
+				}
+				check_address_for_trap(address);
+				--instructions_;
 			}
 
-			if(isAccessOperation(operation)) {
-				if(operation == BusOperation::ReadOpcode) {
-					if constexpr (LogProgramCounter) {
-						printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address],
-							mos6502_.value_of(Register::A),
-							mos6502_.value_of(Register::X),
-							mos6502_.value_of(Register::Y),
-							mos6502_.value_of(Register::Flags) & 0xff,
-							mos6502_.value_of(Register::StackPointer) & 0xff);
-					}
-					check_address_for_trap(address);
-					--instructions_;
-				}
+			if(isReadOperation(operation)) {
+				*value = memory_[address];
 
-				if(isReadOperation(operation)) {
-					*value = memory_[address];
-
-					if constexpr (has_cias) {
-						if((address & 0xff00) == 0xdc00) {
-							*value = cia1_.read(address);
-							if constexpr (LogCIAAccesses) {
-								printf("[%d] CIA1: %04x -> %02x\n", timestamp_.as<int>(), address, *value);
-							}
-						} else if((address & 0xff00) == 0xdd00) {
-							*value = cia2_.read(address);
-							if constexpr (LogCIAAccesses) {
-								printf("[%d] CIA2: %04x -> %02x\n", timestamp_.as<int>(), address, *value);
-							}
+				if constexpr (has_cias) {
+					if((address & 0xff00) == 0xdc00) {
+						*value = cia1_.read(address);
+						if constexpr (LogCIAAccesses) {
+							printf("[%d] CIA1: %04x -> %02x\n", timestamp_.as<int>(), address, *value);
+						}
+					} else if((address & 0xff00) == 0xdd00) {
+						*value = cia2_.read(address);
+						if constexpr (LogCIAAccesses) {
+							printf("[%d] CIA2: %04x -> %02x\n", timestamp_.as<int>(), address, *value);
 						}
 					}
+				}
 
-					if constexpr (LogAllReads) {
-//						if((address&0xff00) == 0x100) {
-							printf("%04x -> %02x\n", address, *value);
-//						}
-					}
-				} else {
-					memory_[address] = *value;
+				if constexpr (LogAllReads) {
+//					if((address&0xff00) == 0x100) {
+						printf("%04x -> %02x\n", address, *value);
+//					}
+				}
+			} else {
+				memory_[address] = *value;
 
-					if constexpr (has_cias) {
-						if((address & 0xff00) == 0xdc00) {
-							cia1_.write(address, *value);
-							if constexpr (LogCIAAccesses) {
-								printf("[%d] CIA1: %04x <- %02x\n", timestamp_.as<int>(), address, *value);
-							}
-						} else if((address & 0xff00) == 0xdd00) {
-							cia2_.write(address, *value);
-							if constexpr (LogCIAAccesses) {
-								printf("[%d] CIA2: %04x <- %02x\n", timestamp_.as<int>(), address, *value);
-							}
+				if constexpr (has_cias) {
+					if((address & 0xff00) == 0xdc00) {
+						cia1_.write(address, *value);
+						if constexpr (LogCIAAccesses) {
+							printf("[%d] CIA1: %04x <- %02x\n", timestamp_.as<int>(), address, *value);
+						}
+					} else if((address & 0xff00) == 0xdd00) {
+						cia2_.write(address, *value);
+						if constexpr (LogCIAAccesses) {
+							printf("[%d] CIA2: %04x <- %02x\n", timestamp_.as<int>(), address, *value);
 						}
 					}
+				}
 
-					if constexpr (LogAllWrites) {
-//						if((address&0xff00) == 0x100) {
-							printf("%04x <- %02x\n", address, *value);
-//						}
-					}
+				if constexpr (LogAllWrites) {
+//					if((address&0xff00) == 0x100) {
+						printf("%04x <- %02x\n", address, *value);
+//					}
 				}
 			}
-
-			mos6502_.set_irq_line(cia1_.get_interrupt_line());
-			mos6502_.set_nmi_line(cia2_.get_interrupt_line());
-
-			return Cycles(1);
 		}
 
-		void run_for(const Cycles cycles) {
-			mos6502_.run_for(cycles);
+		mos6502_.set_irq_line(cia1_.get_interrupt_line());
+		mos6502_.set_nmi_line(cia2_.get_interrupt_line());
+
+		return Cycles(1);
+	}
+
+	void run_for(const Cycles cycles) {
+		mos6502_.run_for(cycles);
+	}
+
+	void run_for_instructions(int count) {
+		instructions_ = count;
+		while(instructions_) {
+			mos6502_.run_for(Cycles(1));
 		}
+	}
 
-		void run_for_instructions(int count) {
-			instructions_ = count;
-			while(instructions_) {
-				mos6502_.run_for(Cycles(1));
-			}
-		}
+	bool is_jammed() {
+		return mos6502_.is_jammed();
+	}
 
-		bool is_jammed() {
-			return mos6502_.is_jammed();
-		}
+	void set_irq_line(bool value) {
+		mos6502_.set_irq_line(value);
+	}
 
-		void set_irq_line(bool value) {
-			mos6502_.set_irq_line(value);
-		}
+	void set_nmi_line(bool value) {
+		mos6502_.set_nmi_line(value);
+	}
 
-		void set_nmi_line(bool value) {
-			mos6502_.set_nmi_line(value);
-		}
+	uint16_t value_of(Register r) {
+		return mos6502_.value_of(r);
+	}
 
-		uint16_t value_of(Register r) {
-			return mos6502_.value_of(r);
-		}
+	void set_value_of(Register r, uint16_t value) {
+		mos6502_.set_value_of(r, value);
+	}
 
-		void set_value_of(Register r, uint16_t value) {
-			mos6502_.set_value_of(r, value);
-		}
+private:
+	CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
+	int instructions_ = 0;
 
-	private:
-		CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
-		int instructions_ = 0;
+	class PortHandler: public MOS::MOS6526::PortHandler {};
+	PortHandler cia1_handler_, cia2_handler_;
 
-		class PortHandler: public MOS::MOS6526::PortHandler {};
-		PortHandler cia1_handler_, cia2_handler_;
-
-		MOS::MOS6526::MOS6526<PortHandler, MOS::MOS6526::Personality::P6526> cia1_, cia2_;
+	MOS::MOS6526::MOS6526<PortHandler, MOS::MOS6526::Personality::P6526> cia1_, cia2_;
 };
 
 }
diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp
index 0e03845ba..a27012734 100644
--- a/Processors/65816/65816.hpp
+++ b/Processors/65816/65816.hpp
@@ -34,58 +34,58 @@ enum ExtendedBusOutput {
 #include "Implementation/65816Storage.hpp"
 
 class ProcessorBase: protected ProcessorStorage {
-	public:
-		inline void set_power_on(bool);
-		inline void set_irq_line(bool);
-		inline void set_nmi_line(bool);
-		inline void set_reset_line(bool);
-		inline void set_abort_line(bool);
-		inline bool get_is_resetting() const;
+public:
+	inline void set_power_on(bool);
+	inline void set_irq_line(bool);
+	inline void set_nmi_line(bool);
+	inline void set_reset_line(bool);
+	inline void set_abort_line(bool);
+	inline bool get_is_resetting() const;
 
-		/*!
-			Returns the current state of all lines not ordinarily pushed to the BusHandler,
-			as listed in the ExtendedBusOutput enum.
-		*/
-		inline int get_extended_bus_output();
+	/*!
+		Returns the current state of all lines not ordinarily pushed to the BusHandler,
+		as listed in the ExtendedBusOutput enum.
+	*/
+	inline int get_extended_bus_output();
 
-		/*!
-			Provided for symmetry with the 6502; a 65816 is never jammed.
-		*/
-		inline bool is_jammed() const;
+	/*!
+		Provided for symmetry with the 6502; a 65816 is never jammed.
+	*/
+	inline bool is_jammed() const;
 
-		/*!
-			FOR TESTING PURPOSES ONLY: forces the processor into a state where
-			the next thing it intends to do is fetch a new opcode.
-		*/
-		inline void restart_operation_fetch();
+	/*!
+		FOR TESTING PURPOSES ONLY: forces the processor into a state where
+		the next thing it intends to do is fetch a new opcode.
+	*/
+	inline void restart_operation_fetch();
 
-		void set_value_of(Register r, uint16_t value);
-		uint16_t value_of(Register r) const;
+	void set_value_of(Register r, uint16_t value);
+	uint16_t value_of(Register r) const;
 };
 
 template <typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
-	public:
-		/*!
-			Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
-		*/
-		Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {}
+public:
+	/*!
+		Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
+	*/
+	Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {}
 
-		/*!
-			Runs the 6502 for a supplied number of cycles.
+	/*!
+		Runs the 6502 for a supplied number of cycles.
 
-			@param cycles The number of cycles to run the 6502 for.
-		*/
-		void run_for(const Cycles cycles);
+		@param cycles The number of cycles to run the 6502 for.
+	*/
+	void run_for(const Cycles);
 
-		/*!
-			Sets the current level of the RDY line.
+	/*!
+		Sets the current level of the RDY line.
 
-			@param active @c true if the line is logically active; @c false otherwise.
-		*/
-		void set_ready_line(bool active);
+		@param active @c true if the line is logically active; @c false otherwise.
+	*/
+	void set_ready_line(bool active);
 
-	private:
-		BusHandler &bus_handler_;
+private:
+	BusHandler &bus_handler_;
 };
 
 #include "Implementation/65816Implementation.hpp"
diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index 5d7f43f31..0f9eae345 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -108,7 +108,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		storage_.micro_ops_.push_back(OperationDecode);
 	}
 
-	private:
+private:
 
 	PatternTable::iterator install(Generator generator, AccessType access_type = AccessType::Read) {
 		// Check whether this access type + addressing mode generator has already been generated.
diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp
index d227dd899..aa5cc265c 100644
--- a/Processors/65816/Implementation/65816Storage.hpp
+++ b/Processors/65816/Implementation/65816Storage.hpp
@@ -395,15 +395,15 @@ struct ProcessorStorage {
 			return reinterpret_cast<uint8_t *>(&value);
 		}
 
-		private:
-			uint8_t *byte(int pointer) {
-				assert(pointer >= 0 && pointer < 4);
-				#if TARGET_RT_BIG_ENDIAN
-					return reinterpret_cast<uint8_t *>(&value) + (3 ^ pointer);
-				#else
-					return reinterpret_cast<uint8_t *>(&value) + pointer;
-				#endif
-			}
+	private:
+		uint8_t *byte(int pointer) {
+			assert(pointer >= 0 && pointer < 4);
+			#if TARGET_RT_BIG_ENDIAN
+				return reinterpret_cast<uint8_t *>(&value) + (3 ^ pointer);
+			#else
+				return reinterpret_cast<uint8_t *>(&value) + pointer;
+			#endif
+		}
 	};
 	Buffer instruction_buffer_, data_buffer_;
 	uint32_t data_address_;
diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp
index 013d36145..537173122 100644
--- a/Processors/68000/68000.hpp
+++ b/Processors/68000/68000.hpp
@@ -438,61 +438,61 @@ namespace CPU::MC68000 {
 */
 template <class BusHandler, bool dtack_is_implicit = true, bool permit_overrun = true, bool signal_will_perform = false>
 class Processor: private ProcessorBase {
-	public:
-		Processor(BusHandler &bus_handler) : ProcessorBase(), bus_handler_(bus_handler) {}
-		Processor(const Processor& rhs) = delete;
-		Processor& operator=(const Processor& rhs) = delete;
+public:
+	Processor(BusHandler &bus_handler) : ProcessorBase(), bus_handler_(bus_handler) {}
+	Processor(const Processor& rhs) = delete;
+	Processor& operator=(const Processor& rhs) = delete;
 
-		void run_for(HalfCycles duration);
+	void run_for(HalfCycles duration);
 
-		/// @returns The current processor state.
-		CPU::MC68000::State get_state();
+	/// @returns The current processor state.
+	CPU::MC68000::State get_state();
 
-		/// Sets the current processor state.
-		void set_state(const CPU::MC68000::State &);
+	/// Sets the current processor state.
+	void set_state(const CPU::MC68000::State &);
 
-		/// Sets all registers to the values provided, fills the prefetch queue and ensures the
-		/// next action the processor will take is to decode whatever is in the queue.
-		///
-		/// The queue is filled synchronously, during this call, causing calls to the bus handler.
-		void decode_from_state(const InstructionSet::M68k::RegisterSet &);
+	/// Sets all registers to the values provided, fills the prefetch queue and ensures the
+	/// next action the processor will take is to decode whatever is in the queue.
+	///
+	/// The queue is filled synchronously, during this call, causing calls to the bus handler.
+	void decode_from_state(const InstructionSet::M68k::RegisterSet &);
 
-		// TODO: bus ack/grant, halt,
+	// TODO: bus ack/grant, halt,
 
-		/// Sets the DTack line — @c true for active, @c false for inactive.
-		inline void set_dtack(bool dtack) {
-			dtack_ = dtack;
-		}
+	/// Sets the DTack line — @c true for active, @c false for inactive.
+	inline void set_dtack(bool dtack) {
+		dtack_ = dtack;
+	}
 
-		/// Sets the VPA (valid peripheral address) line — @c true for active, @c false for inactive.
-		inline void set_is_peripheral_address(bool is_peripheral_address) {
-			vpa_ = is_peripheral_address;
-		}
+	/// Sets the VPA (valid peripheral address) line — @c true for active, @c false for inactive.
+	inline void set_is_peripheral_address(bool is_peripheral_address) {
+		vpa_ = is_peripheral_address;
+	}
 
-		/// Sets the bus error line — @c true for active, @c false for inactive.
-		inline void set_bus_error(bool bus_error) {
-			berr_ = bus_error;
-		}
+	/// Sets the bus error line — @c true for active, @c false for inactive.
+	inline void set_bus_error(bool bus_error) {
+		berr_ = bus_error;
+	}
 
-		/// Sets the interrupt lines, IPL0, IPL1 and IPL2.
-		inline void set_interrupt_level(int interrupt_level) {
-			bus_interrupt_level_ = interrupt_level;
-		}
+	/// Sets the interrupt lines, IPL0, IPL1 and IPL2.
+	inline void set_interrupt_level(int interrupt_level) {
+		bus_interrupt_level_ = interrupt_level;
+	}
 
-		/// @returns The current phase of the E clock; this will be a number of
-		/// half-cycles between 0 and 19 inclusive, indicating how far the 68000
-		/// is into the current E cycle.
-		///
-		/// This is guaranteed to be 0 at initial 68000 construction. It is not guaranteed
-		/// to return the correct result if called during a bus transaction.
-		HalfCycles get_e_clock_phase() {
-			return e_clock_phase_;
-		}
+	/// @returns The current phase of the E clock; this will be a number of
+	/// half-cycles between 0 and 19 inclusive, indicating how far the 68000
+	/// is into the current E cycle.
+	///
+	/// This is guaranteed to be 0 at initial 68000 construction. It is not guaranteed
+	/// to return the correct result if called during a bus transaction.
+	HalfCycles get_e_clock_phase() {
+		return e_clock_phase_;
+	}
 
-		void reset();
+	void reset();
 
-	private:
-		BusHandler &bus_handler_;
+private:
+	BusHandler &bus_handler_;
 };
 
 }
diff --git a/Processors/AllRAMProcessor.hpp b/Processors/AllRAMProcessor.hpp
index e155baf4e..c59342f15 100644
--- a/Processors/AllRAMProcessor.hpp
+++ b/Processors/AllRAMProcessor.hpp
@@ -17,32 +17,32 @@
 namespace CPU {
 
 class AllRAMProcessor {
-	public:
-		AllRAMProcessor(std::size_t memory_size);
-		HalfCycles get_timestamp();
-		void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
-		void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
+public:
+	AllRAMProcessor(std::size_t memory_size);
+	HalfCycles get_timestamp();
+	void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
+	void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
 
-		class TrapHandler {
-			public:
-				virtual void processor_did_trap(AllRAMProcessor &, uint16_t address) = 0;
-		};
-		void set_trap_handler(TrapHandler *trap_handler);
-		void add_trap_address(uint16_t address);
+	class TrapHandler {
+		public:
+			virtual void processor_did_trap(AllRAMProcessor &, uint16_t address) = 0;
+	};
+	void set_trap_handler(TrapHandler *trap_handler);
+	void add_trap_address(uint16_t address);
 
-	protected:
-		std::vector<uint8_t> memory_;
-		HalfCycles timestamp_;
+protected:
+	std::vector<uint8_t> memory_;
+	HalfCycles timestamp_;
 
-		inline void check_address_for_trap(uint16_t address) {
-			if(traps_[address]) {
-				trap_handler_->processor_did_trap(*this, address);
-			}
+	inline void check_address_for_trap(uint16_t address) {
+		if(traps_[address]) {
+			trap_handler_->processor_did_trap(*this, address);
 		}
+	}
 
-	private:
-		TrapHandler *trap_handler_;
-		std::vector<bool> traps_;
+private:
+	TrapHandler *trap_handler_;
+	std::vector<bool> traps_;
 };
 
 }
diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp
index 6ff3543db..4eff3ff27 100644
--- a/Processors/Z80/Z80.hpp
+++ b/Processors/Z80/Z80.hpp
@@ -376,7 +376,7 @@ struct PartialMachineCycle {
 	}
 
 	PartialMachineCycle(const PartialMachineCycle &rhs) noexcept;
-	PartialMachineCycle(Operation operation, HalfCycles length, uint16_t *address, uint8_t *value, bool was_requested) noexcept;
+	PartialMachineCycle(Operation, HalfCycles, uint16_t *address, uint8_t *value, bool was_requested) noexcept;
 	PartialMachineCycle() noexcept;
 };
 
@@ -386,18 +386,18 @@ struct PartialMachineCycle {
 	handler.
 */
 class BusHandler {
-	public:
-		/*!
-			Announces that the Z80 has performed the partial machine cycle defined by @c cycle.
+public:
+	/*!
+		Announces that the Z80 has performed the partial machine cycle defined by @c cycle.
 
-			@returns The number of additional HalfCycles that passed in objective time while this Z80 operation was ongoing.
-			On an archetypal machine this will be HalfCycles(0) but some architectures may choose not to clock the Z80
-			during some periods or may impose wait states so predictably that it's more efficient just to add them
-			via this mechanism.
-		*/
-		HalfCycles perform_machine_cycle([[maybe_unused]] const PartialMachineCycle &cycle) {
-			return HalfCycles(0);
-		}
+		@returns The number of additional HalfCycles that passed in objective time while this Z80 operation was ongoing.
+		On an archetypal machine this will be HalfCycles(0) but some architectures may choose not to clock the Z80
+		during some periods or may impose wait states so predictably that it's more efficient just to add them
+		via this mechanism.
+	*/
+	HalfCycles perform_machine_cycle([[maybe_unused]] const PartialMachineCycle &) {
+		return HalfCycles(0);
+	}
 };
 
 #include "Implementation/Z80Storage.hpp"
@@ -406,82 +406,82 @@ class BusHandler {
 	A base class from which the Z80 descends; separated for implementation reasons only.
 */
 class ProcessorBase: public ProcessorStorage {
-	public:
-		/*!
-			Gets the value of a register.
+public:
+	/*!
+		Gets the value of a register.
 
-			@see set_value_of
+		@see set_value_of
 
-			@param r The register to set.
-			@returns The value of the register. 8-bit registers will be returned as unsigned.
-		*/
-		uint16_t value_of(Register r) const;
+		@param r The register to set.
+		@returns The value of the register. 8-bit registers will be returned as unsigned.
+	*/
+	uint16_t value_of(Register r) const;
 
-		/*!
-			Sets the value of a register.
+	/*!
+		Sets the value of a register.
 
-			@see value_of
+		@see value_of
 
-			@param r The register to set.
-			@param value The value to set. If the register is only 8 bit, the value will be truncated.
-		*/
-		void set_value_of(Register r, uint16_t value);
+		@param r The register to set.
+		@param value The value to set. If the register is only 8 bit, the value will be truncated.
+	*/
+	void set_value_of(Register r, uint16_t value);
 
-		/*!
-			Gets the value of the HALT output line.
-		*/
-		inline bool get_halt_line() const;
+	/*!
+		Gets the value of the HALT output line.
+	*/
+	inline bool get_halt_line() const;
 
-		/*!
-			Sets the logical value of the interrupt line.
+	/*!
+		Sets the logical value of the interrupt line.
 
-			@param offset If called while within perform_machine_cycle this may be a value indicating
-			how many cycles before now the line changed state. The value may not be longer than the
-			current machine cycle. If called at any other time, this must be zero.
-		*/
-		inline void set_interrupt_line(bool value, HalfCycles offset = 0);
+		@param offset If called while within perform_machine_cycle this may be a value indicating
+		how many cycles before now the line changed state. The value may not be longer than the
+		current machine cycle. If called at any other time, this must be zero.
+	*/
+	inline void set_interrupt_line(bool, HalfCycles offset = 0);
 
-		/*!
-			Gets the value of the interrupt line.
-		*/
-		inline bool get_interrupt_line() const;
+	/*!
+		Gets the value of the interrupt line.
+	*/
+	inline bool get_interrupt_line() const;
 
-		/*!
-			Sets the logical value of the non-maskable interrupt line.
+	/*!
+		Sets the logical value of the non-maskable interrupt line.
 
-			@param offset See discussion in set_interrupt_line.
-		*/
-		inline void set_non_maskable_interrupt_line(bool value, HalfCycles offset = 0);
+		@param offset See discussion in set_interrupt_line.
+	*/
+	inline void set_non_maskable_interrupt_line(bool, HalfCycles offset = 0);
 
-		/*!
-			Gets the value of the non-maskable interrupt line.
-		*/
-		inline bool get_non_maskable_interrupt_line() const;
+	/*!
+		Gets the value of the non-maskable interrupt line.
+	*/
+	inline bool get_non_maskable_interrupt_line() const;
 
-		/*!
-			Sets the logical value of the reset line.
-		*/
-		inline void set_reset_line(bool value);
+	/*!
+		Sets the logical value of the reset line.
+	*/
+	inline void set_reset_line(bool);
 
-		/*!
-			Gets whether the Z80 would reset at the next opportunity.
+	/*!
+		Gets whether the Z80 would reset at the next opportunity.
 
-			@returns @c true if the line is logically active; @c false otherwise.
-		*/
-		bool get_is_resetting() const;
+		@returns @c true if the line is logically active; @c false otherwise.
+	*/
+	bool get_is_resetting() const;
 
-		/*!
-			This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
-			reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
-		*/
-		void reset_power_on();
+	/*!
+		This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
+		reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
+	*/
+	void reset_power_on();
 
-		/*!
-			@returns @c true if the Z80 is currently beginning to fetch a new instruction; @c false otherwise.
+	/*!
+		@returns @c true if the Z80 is currently beginning to fetch a new instruction; @c false otherwise.
 
-			This is not a speedy operation.
-		*/
-		bool is_starting_new_instruction() const;
+		This is not a speedy operation.
+	*/
+	bool is_starting_new_instruction() const;
 };
 
 /*!
@@ -493,45 +493,45 @@ class ProcessorBase: public ProcessorStorage {
 	support either can produce a minor runtime performance improvement.
 */
 template <class T, bool uses_bus_request, bool uses_wait_line> class Processor: public ProcessorBase {
-	public:
-		Processor(T &bus_handler);
+public:
+	Processor(T &bus_handler);
 
-		/*!
-			Runs the Z80 for a supplied number of cycles.
+	/*!
+		Runs the Z80 for a supplied number of cycles.
 
-			@discussion Subclasses must implement @c perform_machine_cycle(const PartialMachineCycle &cycle) .
+		@discussion Subclasses must implement @c perform_machine_cycle(const PartialMachineCycle &cycle) .
 
-			If it is a read operation then @c value will be seeded with the value 0xff.
+		If it is a read operation then @c value will be seeded with the value 0xff.
 
-			@param cycles The number of cycles to run for.
-		*/
-		void run_for(const HalfCycles cycles);
+		@param cycles The number of cycles to run for.
+	*/
+	void run_for(const HalfCycles);
 
-		/*!
-			Sets the logical value of the bus request line, having asserted that this Z80 supports the bus request line.
-		*/
-		void set_bus_request_line(bool value);
+	/*!
+		Sets the logical value of the bus request line, having asserted that this Z80 supports the bus request line.
+	*/
+	void set_bus_request_line(bool);
 
-		/*!
-			Gets the logical value of the bus request line.
-		*/
-		bool get_bus_request_line() const;
+	/*!
+		Gets the logical value of the bus request line.
+	*/
+	bool get_bus_request_line() const;
 
-		/*!
-			Sets the logical value of the wait line, having asserted that this Z80 supports the wait line.
-		*/
-		void set_wait_line(bool value);
+	/*!
+		Sets the logical value of the wait line, having asserted that this Z80 supports the wait line.
+	*/
+	void set_wait_line(bool);
 
-		/*!
-			Gets the logical value of the bus request line.
-		*/
-		bool get_wait_line() const;
+	/*!
+		Gets the logical value of the bus request line.
+	*/
+	bool get_wait_line() const;
 
-	private:
-		T &bus_handler_;
+private:
+	T &bus_handler_;
 
-		void assemble_page(InstructionPage &target, InstructionTable &table, bool add_offsets);
-		void copy_program(const MicroOp *source, std::vector<MicroOp> &destination);
+	void assemble_page(InstructionPage &, InstructionTable &, bool add_offsets);
+	void copy_program(const MicroOp *source, std::vector<MicroOp> &destination);
 };
 
 #include "Implementation/Z80Implementation.hpp"

From 9cb28d23a3ecd13cac1e3d9c7800ffe35cc4e541 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 4 Dec 2024 22:39:29 -0500
Subject: [PATCH 2/2] Remove deprecated macos-12; add macos-15.

---
 .github/workflows/build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6ce5ed315..e504c9dc5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,7 +5,7 @@ jobs:
     name: Mac UI / xcodebuild / ${{ matrix.os }}
     strategy:
       matrix:
-        os: [macos-12, macos-13, macos-14]
+        os: [macos-13, macos-14, macos-15]
     runs-on: ${{ matrix.os }}
     steps:
     - name: Checkout
@@ -53,7 +53,7 @@ jobs:
     name: SDL UI / scons / ${{ matrix.os }}
     strategy:
       matrix:
-        os: [macos-14, ubuntu-latest]
+        os: [macos-latest, ubuntu-latest]
     runs-on: ${{ matrix.os }}
     steps:
     - name: Checkout