diff --git a/InstructionSets/M68k/Executor.cpp b/InstructionSets/M68k/Executor.cpp
index 242da4a07..926271cc0 100644
--- a/InstructionSets/M68k/Executor.cpp
+++ b/InstructionSets/M68k/Executor.cpp
@@ -21,24 +21,83 @@ Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handle
 
 template <Model model, typename BusHandler>
 void Executor<model, BusHandler>::reset() {
-	// TODO.
+	// Establish: supervisor state, all interrupts blocked.
+	status_.set_status(0b0010'0011'1000'0000);
+
+	// Seed stack pointer and program counter.
+	data_[7] = bus_handler_.template read<uint32_t>(0);
+	program_counter_.l = bus_handler_.template read<uint32_t>(4);
 }
 
+template <Model model, typename BusHandler>
+typename Executor<model, BusHandler>::EffectiveAddress Executor<model, BusHandler>::calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index) {
+	EffectiveAddress ea;
+	switch(instruction.mode(index)) {
+		case AddressingMode::None:
+			// Permit an uninitialised effective address to be returned;
+			// this value shouldn't be used.
+		break;
+
+		//
+		// Operands that don't have effective addresses, which are returned as values.
+		//
+		case AddressingMode::DataRegisterDirect:
+			ea.value = data_[instruction.reg(index)];
+			ea.is_address = false;
+		break;
+		case AddressingMode::AddressRegisterDirect:
+			ea.value = address_[instruction.reg(index)];
+			ea.is_address = false;
+		break;
+		case AddressingMode::Quick:
+			ea.value = quick(instruction.operation, opcode);
+			ea.is_address = false;
+		break;
+
+		//
+		// Operands that are effective addresses.
+		//
+
+		default:
+			// TODO.
+			assert(false);
+		break;
+	}
+
+	return ea;
+}
+
+
 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;
+		const uint16_t opcode = bus_handler_.template read<uint16_t>(program_counter_.l);
+		const Preinstruction instruction = decoder_.decode(opcode);
 		program_counter_.l += 2;
 
-		// Obtain the appropriate sequence.
-		Sequence<model> sequence(instruction.operation);
-
 		// Temporary storage.
-		CPU::SlicedInt32 source, dest;
+		CPU::SlicedInt32 operand_[2];
+		EffectiveAddress effective_address_[2];
+
+		// Calculate effective addresses; copy 'addresses' into the
+		// operands by default both: (i) because they might be values,
+		// rather than addresses; and (ii) then they'll be there for use
+		// by LEA and PEA.
+		//
+		// TODO: this work should be performed by a full Decoder, so that it can be cached.
+		effective_address_[0] = calculate_effective_address(instruction, opcode, 0);
+		effective_address_[1] = calculate_effective_address(instruction, opcode, 1);
+		operand_[0].l = effective_address_[0].value;
+		operand_[1].l = effective_address_[1].value;
+
+		// Obtain the appropriate sequence.
+		//
+		// TODO: make a decision about whether this goes into a fully-decoded Instruction.
+		Sequence<model> sequence(instruction.operation);
 
 		// Perform it.
 		while(!sequence.empty()) {
@@ -47,9 +106,67 @@ void Executor<model, BusHandler>::run_for_instructions(int count) {
 			switch(step) {
 				default: assert(false);	// i.e. TODO
 
+				case Step::FetchOp1:
+				case Step::FetchOp2: {
+					const auto index = int(step) & 1;
+
+					// If the operand wasn't indirect, it's already fetched.
+					if(!effective_address_[index].is_address) continue;
+
+					// TODO: potential bus alignment exception.
+
+					switch(instruction.size()) {
+						case DataSize::Byte:
+							operand_[index].l = bus_handler_.template read<uint8_t>(effective_address_[index].value);
+						break;
+						case DataSize::Word:
+							operand_[index].l = bus_handler_.template read<uint16_t>(effective_address_[index].value);
+						break;
+						case DataSize::LongWord:
+							operand_[index].l = bus_handler_.template read<uint32_t>(effective_address_[index].value);
+						break;
+					}
+				} break;
+
 				case Step::Perform:
-					perform<model>(instruction, source, dest, status_, this);
+					perform<model>(instruction, operand_[0], operand_[1], status_, this);
 				break;
+
+				case Step::StoreOp1:
+				case Step::StoreOp2: {
+					const auto index = int(step) & 1;
+
+					// If the operand wasn't indirect, it's already fetched.
+					if(!effective_address_[index].is_address) {
+						// This must be either address or data register indirect.
+						assert(
+							instruction.mode(index) == AddressingMode::DataRegisterDirect ||
+							instruction.mode(index) == AddressingMode::AddressRegisterDirect);
+
+						// TODO: is it worth holding registers as a single block to avoid this conditional?
+						if(instruction.mode(index) == AddressingMode::DataRegisterDirect) {
+							data_[instruction.reg(index)] = operand_[index];
+						} else {
+							address_[instruction.reg(index)] = operand_[index];
+						}
+
+						break;
+					}
+
+					// TODO: potential bus alignment exception.
+
+					switch(instruction.size()) {
+						case DataSize::Byte:
+							 bus_handler_.write(effective_address_[index].value, operand_[index].b);
+						break;
+						case DataSize::Word:
+							bus_handler_.write(effective_address_[index].value, operand_[index].w);
+						break;
+						case DataSize::LongWord:
+							bus_handler_.write(effective_address_[index].value, operand_[index].l);
+						break;
+					}
+				} break;
 			}
 		}
 	}
diff --git a/InstructionSets/M68k/Executor.hpp b/InstructionSets/M68k/Executor.hpp
index b9a28ebec..52ae19ec7 100644
--- a/InstructionSets/M68k/Executor.hpp
+++ b/InstructionSets/M68k/Executor.hpp
@@ -42,6 +42,11 @@ template <Model model, typename BusHandler> class Executor {
 		Predecoder<model> decoder_;
 
 		void reset();
+		struct EffectiveAddress {
+			uint32_t value;
+			bool is_address;
+		};
+		EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
 
 		// Processor state.
 		Status status_;
diff --git a/InstructionSets/M68k/Instruction.hpp b/InstructionSets/M68k/Instruction.hpp
index 252fb5e77..4e9236eda 100644
--- a/InstructionSets/M68k/Instruction.hpp
+++ b/InstructionSets/M68k/Instruction.hpp
@@ -356,27 +356,37 @@ class Preinstruction {
 		// For one-operand instructions, only argument 0 will
 		// be provided, and will be a source and/or destination as
 		// per the semantics of the operation.
+		//
+		// The versions templated on index do a range check;
+		// if using the runtime versions then results for indices
+		// other than 0 and 1 are undefined.
 
+		AddressingMode mode(int index) const {
+			return AddressingMode(operands_[index] & 0x1f);
+		}
 		template <int index> AddressingMode mode() const {
 			if constexpr (index > 1) {
 				return AddressingMode::None;
 			}
-			return AddressingMode(operands_[index] & 0x1f);
+			return mode(index);
+		}
+		int reg(int index) const {
+			return operands_[index] >> 5;
 		}
 		template <int index> int reg() const {
 			if constexpr (index > 1) {
 				return 0;
 			}
-			return operands_[index] >> 5;
+			return reg(index);
 		}
 
-		bool requires_supervisor() {
+		bool requires_supervisor() const {
 			return flags_ & 0x80;
 		}
-		DataSize size() {
+		DataSize size() const {
 			return DataSize(flags_ & 0x03);
 		}
-		Condition condition() {
+		Condition condition() const {
 			return Condition((flags_ >> 2) & 0x0f);
 		}
 
diff --git a/InstructionSets/M68k/Sequence.cpp b/InstructionSets/M68k/Sequence.cpp
index 07ec25bb4..ef95f9c37 100644
--- a/InstructionSets/M68k/Sequence.cpp
+++ b/InstructionSets/M68k/Sequence.cpp
@@ -28,7 +28,6 @@ template<Model model> uint32_t Sequence<model>::steps_for(Operation operation) {
 		//
 		case Operation::LEA:
 		return Steps<
-			Step::CalcEA1,
 			Step::Perform
 		>::value;
 
diff --git a/InstructionSets/M68k/Sequence.hpp b/InstructionSets/M68k/Sequence.hpp
index 2c011cf31..922150654 100644
--- a/InstructionSets/M68k/Sequence.hpp
+++ b/InstructionSets/M68k/Sequence.hpp
@@ -15,7 +15,7 @@
 namespace InstructionSet {
 namespace M68k {
 
-/// Additional guarantees: [Fetch/Store/CalcEA][1/2] have an LSB of 0 for
+/// Additional guarantees: [Fetch/Store][1/2] have an LSB of 0 for
 /// operand 1, and an LSB of 1 for operand 2.
 enum class Step {
 	/// No further steps remain.
@@ -30,10 +30,6 @@ enum class Step {
 	StoreOp1,
 	/// Store the value of operand 2.
 	StoreOp2,
-	/// Calculate effective address of operand 1.
-	CalcEA1,
-	/// Calculate effective address of operand 2.
-	CalcEA2,
 	/// A catch-all for bus activity that doesn't fit the pattern
 	/// of fetch/stop operand 1/2, e.g. this opaquely covers almost
 	/// the entirety of MOVEM.
diff --git a/InstructionSets/M68k/Status.hpp b/InstructionSets/M68k/Status.hpp
index 8e7652762..f28489088 100644
--- a/InstructionSets/M68k/Status.hpp
+++ b/InstructionSets/M68k/Status.hpp
@@ -22,7 +22,7 @@ struct Status {
 	int is_supervisor_ = 0;				// 1 => processor is in supervisor mode; 0 => it isn't.
 
 	/* b7–b9 */
-	int interrupt_level_ = 0;			// The direct integer value of thee current interrupt level.
+	int interrupt_level_ = 0;			// The direct integer value of the current interrupt level.
 
 	/* b0–b4 */
 	uint_fast32_t zero_result_ = 0;		// The zero flag is set if this value is zero.