diff --git a/InstructionSets/M68k/Decoder.cpp b/InstructionSets/M68k/Decoder.cpp
index a52c501d8..414b9768d 100644
--- a/InstructionSets/M68k/Decoder.cpp
+++ b/InstructionSets/M68k/Decoder.cpp
@@ -8,10 +8,6 @@
 
 #include "Decoder.hpp"
 
-// TODO: remove this include. It's a temporary hack to get the performer into the automated build
-// while I work on this branch.
-#include "Perform.hpp"
-
 #include <cassert>
 
 using namespace InstructionSet::M68k;
diff --git a/InstructionSets/M68k/Executor.cpp b/InstructionSets/M68k/Executor.cpp
new file mode 100644
index 000000000..2da4177a0
--- /dev/null
+++ b/InstructionSets/M68k/Executor.cpp
@@ -0,0 +1,41 @@
+//
+//  Executor.cpp
+//  Clock Signal
+//
+//  Created by Thomas Harte on 29/04/2022.
+//  Copyright © 2022 Thomas Harte. All rights reserved.
+//
+
+#include "Executor.hpp"
+
+using namespace InstructionSet::M68k;
+
+template <Model model, typename BusHandler>
+Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handler) {
+	reset();
+}
+
+template <Model model, typename BusHandler>
+void Executor<model, BusHandler>::reset() {
+
+}
+
+template <Model model, typename BusHandler>
+void Executor<model, BusHandler>::run_for_instructions(int count) {
+	while(count--) {
+		// TODO: check interrupt level, trace flag.
+
+		// Read the next instruction.
+		const Preinstruction instruction = decoder_.decode(bus_handler_.template read<uint16_t>(program_counter_.l));
+		const auto instruction_address = program_counter_.l;
+		program_counter_.l += 2;
+
+		// Obtain the appropriate sequence.
+		Sequence<model> sequence(instruction.operation);
+
+		// Perform it.
+		while(!sequence.empty()) {
+			const auto step = sequence.pop_front();
+		}
+	}
+}
diff --git a/InstructionSets/M68k/Executor.hpp b/InstructionSets/M68k/Executor.hpp
new file mode 100644
index 000000000..a51af6fce
--- /dev/null
+++ b/InstructionSets/M68k/Executor.hpp
@@ -0,0 +1,54 @@
+//
+//  Executor.hpp
+//  Clock Signal
+//
+//  Created by Thomas Harte on 29/04/2022.
+//  Copyright © 2022 Thomas Harte. All rights reserved.
+//
+
+#ifndef InstructionSets_M68k_Executor_hpp
+#define InstructionSets_M68k_Executor_hpp
+
+#include "Decoder.hpp"
+#include "Instruction.hpp"
+#include "Model.hpp"
+#include "Perform.hpp"
+#include "Sequence.hpp"
+#include "Status.hpp"
+
+namespace InstructionSet {
+namespace M68k {
+
+struct BusHandler {
+	template <typename IntT> void write(uint32_t address, IntT value);
+	template <typename IntT> IntT read(uint32_t address);
+};
+
+/// Ties together the decoder, sequencer and performer to provide an executor for 680x0 instruction streams.
+/// As is standard for these executors, no bus- or cache-level fidelity to any real 680x0 is attempted. This is
+/// simply an executor of 680x0 code.
+template <Model model, typename BusHandler> class Executor {
+	public:
+		Executor(BusHandler &);
+
+		/// Executes the number of instructions specified;
+		/// other events — such as initial reset or branching
+		/// to exceptions — may be zero costed, and interrupts
+		/// will not necessarily take effect immediately when signalled.
+		void run_for_instructions(int);
+
+	private:
+		BusHandler &bus_handler_;
+		Predecoder<model> decoder_;
+
+		void reset();
+
+		// Processor state.
+		Status status_;
+		CPU::SlicedInt32 program_counter_;
+};
+
+}
+}
+
+#endif /* InstructionSets_M68k_Executor_hpp */
diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp
index f24a2c938..2dad77da5 100644
--- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp
+++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp
@@ -243,21 +243,6 @@ template <
 			dest.l -= src.l;
 		break;
 
-
-		// BRA: alters the program counter, exclusively via the prefetch queue.
-//		case Operation::BRA: {
-//			const int8_t byte_offset = int8_t(prefetch_queue_.halves.high.halves.low);
-//
-//			// A non-zero offset byte branches by just that amount; otherwise use the word
-//			// after as an offset. In both cases, treat as signed.
-//			if(byte_offset) {
-//				program_counter_.l += uint32_t(byte_offset);
-//			} else {
-//				program_counter_.l += u_extend16(prefetch_queue_.w);
-//			}
-//			program_counter_.l -= 2;
-//		} break;
-
 		// Two BTSTs: set the zero flag according to the value of the destination masked by
 		// the bit named in the source modulo the operation size.
 //		case Operation::BTSTb:
@@ -309,63 +294,42 @@ template <
 		//
 		// Special case: the condition code is 1, which is ordinarily false. In that case this
 		// is the trailing step of a BSR.
-//		case Operation::Bcc: {
-//			// Grab the 8-bit offset.
-//			const int8_t byte_offset = int8_t(prefetch_queue_.halves.high.halves.low);
-//
-//			// Check whether this is secretly BSR.
-//			const bool is_bsr = ((decoded_instruction_.l >> 8) & 0xf) == 1;
-//
-//			// Test the conditional, treating 'false' as true.
-//			const bool should_branch = is_bsr || evaluate_condition(decoded_instruction_.l >> 8);
-//
-//			// Schedule something appropriate, by rewriting the program for this instruction temporarily.
-//			if(should_branch) {
-//				if(byte_offset) {
-//					program_counter_.l += decltype(program_counter_.l)(byte_offset);
-//				} else {
-//					program_counter_.l += u_extend16(prefetch_queue_.w);
-//				}
-//				program_counter_.l -= 2;
-//				bus_program = is_bsr ? bsr_bus_steps_ : branch_taken_bus_steps_;
-//			} else {
-//				if(byte_offset) {
-//					bus_program = branch_byte_not_taken_bus_steps_;
-//				} else {
-//					bus_program = branch_word_not_taken_bus_steps_;
-//				}
-//			}
-//		} break;
-//
-//		case Operation::DBcc: {
-//			// Decide what sort of DBcc this is.
-//			if(!evaluate_condition(decoded_instruction_.l >> 8)) {
-//				-- src.w;
-//				const auto target_program_counter = program_counter_.l + u_extend16(prefetch_queue_.w) - 2;
-//
-//				if(src.w == 0xffff) {
-//					// This DBcc will be ignored as the counter has underflowed.
-//					// Schedule n np np np and continue. Assumed: the first np
-//					// is from where the branch would have been if taken?
-//					bus_program = dbcc_condition_false_no_branch_steps_;
-//					dbcc_false_address_ = target_program_counter;
-//				} else {
-//					// Take the branch. Change PC and schedule n np np.
-//					bus_program = dbcc_condition_false_branch_steps_;
-//					program_counter_.l = target_program_counter;
-//				}
-//			} else {
-//				// This DBcc will be ignored as the condition is true;
-//				// perform nn np np and continue.
-//				bus_program = dbcc_condition_true_steps_;
-//			}
-//		} break;
+		case Operation::Bccb:
+		case Operation::Bccw:
+		case Operation::Bccl: {
+			// Test the conditional, treating 'false' as true.
+			const bool should_branch = status.evaluate_condition(flow_controller.decode_conditional());
 
-		case Operation::Scc: {
-			dest.b =
-				status.evaluate_condition(src.l) ? 0xff : 0x00;
+			// Schedule something appropriate, by rewriting the program for this instruction temporarily.
+			if(should_branch) {
+				flow_controller.add_pc(src.l);
+			} else {
+				flow_controller.decline_branch();
+			}
 		} break;
 
+		case Operation::DBcc:
+			// Decide what sort of DBcc this is.
+			if(!status.evaluate_condition(flow_controller.decode_conditional())) {
+				-- src.w;
+
+				if(src.w == 0xffff) {
+					// This DBcc will be ignored as the counter has underflowed.
+					flow_controller.decline_branch();
+				} else {
+					// Take the branch.
+					flow_controller.add_pc(dest.l);
+				}
+			} else {
+				// This DBcc will be ignored as the condition is true.
+				flow_controller.decline_branch();
+			}
+		break;
+
+		case Operation::Scc:
+			dest.b = status.evaluate_condition(src.l) ? 0xff : 0x00;
+		break;
+
 		/*
 			CLRs: store 0 to the destination, set the zero flag, and clear
 			negative, overflow and carry.
@@ -434,13 +398,9 @@ template <
 		} break;
 
 		// JMP: copies EA(0) to the program counter.
-//		case Operation::JMP:
-//			flow_controller.set_pc(effective_address_[0]);
-//		break;
-
-//		case Operation::RTS:
-//			program_counter_ = source_bus_data_;
-//		break;
+		case Operation::JMP:
+			flow_controller.set_pc(flow_controller.effective_address(0));
+		break;
 
 		/*
 			MOVE.b, MOVE.l and MOVE.w: move the least significant byte or word, or the entire long word,
@@ -477,6 +437,10 @@ template <
 			dest.l = src.l;
 		break;
 
+		case Operation::LEA:
+			dest.l = flow_controller.effective_address(0);
+		break;
+
 //		case Operation::PEA:
 //			destination_bus_data_ = effective_address_[0];
 //		break;
@@ -823,8 +787,7 @@ template <
 		/*
 			The no-op.
 		*/
-		case Operation::NOP:
-		break;
+		case Operation::NOP:	break;
 
 		/*
 			LINK and UNLINK help with stack frames, allowing a certain
@@ -1248,6 +1211,12 @@ template <
 //					uint8_t(current_status >> 8);
 //			}
 //			apply_status(source_bus_data_.l);
+//		break;
+
+//		case Operation::RTE:
+//		break;
+//
+//		case Operation::RTR:
 //		break;
 
 		/*
diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index 771e04656..af932c3f9 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -894,6 +894,8 @@
 		4BB4BFB022A42F290069048D /* MacintoshIMG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4BFAE22A42F290069048D /* MacintoshIMG.cpp */; };
 		4BB4BFB922A4372F0069048D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4BFB822A4372E0069048D /* StaticAnalyser.cpp */; };
 		4BB4BFBA22A4372F0069048D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4BFB822A4372E0069048D /* StaticAnalyser.cpp */; };
+		4BB5B99D281C814E00522DA9 /* Executor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB5B99B281C805300522DA9 /* Executor.cpp */; };
+		4BB5B99E281C814F00522DA9 /* Executor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB5B99B281C805300522DA9 /* Executor.cpp */; };
 		4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
 		4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; };
 		4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
@@ -1939,6 +1941,8 @@
 		4BB5B996281B1E3F00522DA9 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = "<group>"; };
 		4BB5B997281B1F7B00522DA9 /* Status.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Status.hpp; sourceTree = "<group>"; };
 		4BB5B99A281B244400522DA9 /* PerformImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PerformImplementation.hpp; sourceTree = "<group>"; };
+		4BB5B99B281C805300522DA9 /* Executor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Executor.cpp; sourceTree = "<group>"; };
+		4BB5B99C281C805300522DA9 /* Executor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Executor.hpp; sourceTree = "<group>"; };
 		4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
 		4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
 		4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreGCR.cpp; path = Encodings/CommodoreGCR.cpp; sourceTree = "<group>"; };
@@ -3165,8 +3169,10 @@
 			isa = PBXGroup;
 			children = (
 				4B79629F2819681F008130F9 /* Decoder.cpp */,
+				4BB5B99B281C805300522DA9 /* Executor.cpp */,
 				4B7962A4281C2EE3008130F9 /* Sequence.cpp */,
 				4B79629E2819681F008130F9 /* Decoder.hpp */,
+				4BB5B99C281C805300522DA9 /* Executor.hpp */,
 				4B79629C2819681F008130F9 /* Instruction.hpp */,
 				4B79629D2819681F008130F9 /* Model.hpp */,
 				4BB5B996281B1E3F00522DA9 /* Perform.hpp */,
@@ -5458,6 +5464,7 @@
 				4B1B88C1202E3DB200B67DFF /* MultiConfigurable.cpp in Sources */,
 				4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */,
 				4B8318B322D3E540006DB630 /* Audio.cpp in Sources */,
+				4BB5B99E281C814F00522DA9 /* Executor.cpp in Sources */,
 				4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */,
 				4B89452B201967B4007DE474 /* File.cpp in Sources */,
 				4B6AAEAC230E40250078E864 /* SCSI.cpp in Sources */,
@@ -5708,6 +5715,7 @@
 				4BC890D3230F86020025A55A /* DirectAccessDevice.cpp in Sources */,
 				4B7BA03723CEB86000B98D9E /* BD500.cpp in Sources */,
 				4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */,
+				4BB5B99D281C814E00522DA9 /* Executor.cpp in Sources */,
 				4B8FE2221DA19FB20090D3CE /* MachineController.swift in Sources */,
 				4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */,
 				4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */,