diff --git a/InstructionSets/M68k/Instruction.cpp b/InstructionSets/M68k/Instruction.cpp
new file mode 100644
index 000000000..b4184c868
--- /dev/null
+++ b/InstructionSets/M68k/Instruction.cpp
@@ -0,0 +1,295 @@
+//
+//  Instruction.cpp
+//  Clock Signal
+//
+//  Created by Thomas Harte on 12/05/2022.
+//  Copyright © 2022 Thomas Harte. All rights reserved.
+//
+
+#include "Instruction.hpp"
+
+#include <cassert>
+
+using namespace InstructionSet::M68k;
+
+std::string Preinstruction::operand_description(int index, int opcode) const {
+	switch(mode(index)) {
+		default:	assert(false);
+
+		case AddressingMode::None:
+			return "";
+
+		case AddressingMode::DataRegisterDirect:
+			return std::string("D") + std::to_string(reg(index));
+
+		case AddressingMode::AddressRegisterDirect:
+			return std::string("A") + std::to_string(reg(index));
+		case AddressingMode::AddressRegisterIndirect:
+			return std::string("(A") + std::to_string(reg(index)) + ")";
+		case AddressingMode::AddressRegisterIndirectWithPostincrement:
+			return std::string("(A") + std::to_string(reg(index)) + ")+";
+		case AddressingMode::AddressRegisterIndirectWithPredecrement:
+			return std::string("-(A") + std::to_string(reg(index)) + ")";
+		case AddressingMode::AddressRegisterIndirectWithDisplacement:
+			return std::string("(d16, A") + std::to_string(reg(index)) + ")";
+		case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
+			return std::string("(d8, A") + std::to_string(reg(index)) + ", Xn)";
+
+		case AddressingMode::ProgramCounterIndirectWithDisplacement:
+			return "(d16, PC)";
+		case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
+			return "(d8, PC, Xn)";
+
+		case AddressingMode::AbsoluteShort:
+			return "(xxx).w";
+		case AddressingMode::AbsoluteLong:
+			return "(xxx).l";
+
+		case AddressingMode::ImmediateData:
+			return "#";
+
+		case AddressingMode::Quick:
+			if(opcode == -1) {
+				return "Q";
+			}
+			return std::to_string(int(quick(uint16_t(opcode), operation)));
+	}
+}
+
+std::string Preinstruction::to_string(int opcode) const {
+	bool flip_operands = false;
+	const char *instruction;
+
+	switch(operation) {
+		case Operation::Undefined:	instruction = "None";		break;
+		case Operation::NOP:		instruction = "NOP";		break;
+		case Operation::ABCD:		instruction = "ABCD";		break;
+		case Operation::SBCD:		instruction = "SBCD";		break;
+		case Operation::NBCD:		instruction = "NBCD";		break;
+
+		case Operation::ADDb:		instruction = "ADD.b";		break;
+		case Operation::ADDw:		instruction = "ADD.w";		break;
+		case Operation::ADDl:		instruction = "ADD.l";		break;
+
+		case Operation::ADDAw:
+			if(mode<0>() == AddressingMode::Quick) {
+				instruction = "ADD.w";
+			} else {
+				instruction = "ADDA.w";
+			}
+		break;
+		case Operation::ADDAl:
+			if(mode<0>() == AddressingMode::Quick) {
+				instruction = "ADD.l";
+			} else {
+				instruction = "ADDA.l";
+			}
+		break;
+
+		case Operation::ADDXb:		instruction = "ADDX.b";	break;
+		case Operation::ADDXw:		instruction = "ADDX.w";	break;
+		case Operation::ADDXl:		instruction = "ADDX.l";	break;
+
+		case Operation::SUBb:		instruction = "SUB.b";	break;
+		case Operation::SUBw:		instruction = "SUB.w";	break;
+		case Operation::SUBl:		instruction = "SUB.l";	break;
+
+		case Operation::SUBAw:
+			if(mode<0>() == AddressingMode::Quick) {
+				instruction = "SUB.w";
+			} else {
+				instruction = "SUBA.w";
+			}
+		break;
+		case Operation::SUBAl:
+			if(mode<0>() == AddressingMode::Quick) {
+				instruction = "SUB.l";
+			} else {
+				instruction = "SUBA.l";
+			}
+		break;
+
+		case Operation::SUBXb:		instruction = "SUBX.b";	break;
+		case Operation::SUBXw:		instruction = "SUBX.w";	break;
+		case Operation::SUBXl:		instruction = "SUBX.l";	break;
+
+		case Operation::MOVEb:		instruction = "MOVE.b";	break;
+		case Operation::MOVEw:		instruction = "MOVE.w";	break;
+		case Operation::MOVEl:
+			if(mode<0>() == AddressingMode::Quick) {
+				instruction = "MOVE.q";
+			} else {
+				instruction = "MOVE.l";
+			}
+		break;
+
+		case Operation::MOVEAw:		instruction = "MOVEA.w";	break;
+		case Operation::MOVEAl:		instruction = "MOVEA.l";	break;
+
+		case Operation::LEA:		instruction = "LEA";		break;
+		case Operation::PEA:		instruction = "PEA";		break;
+
+		case Operation::MOVEtoSR:		instruction = "MOVEtoSR";		break;
+		case Operation::MOVEfromSR:		instruction = "MOVEfromSR";		break;
+		case Operation::MOVEtoCCR:		instruction = "MOVEtoCCR";		break;
+		case Operation::MOVEtoUSP:		instruction = "MOVEtoUSP";		break;
+		case Operation::MOVEfromUSP:	instruction = "MOVEfromUSP";	break;
+
+		case Operation::ORItoSR:	instruction = "ORItoSR";	break;
+		case Operation::ORItoCCR:	instruction = "ORItoCCR";	break;
+		case Operation::ANDItoSR:	instruction = "ANDItoSR";	break;
+		case Operation::ANDItoCCR:	instruction = "ANDItoCCR";	break;
+		case Operation::EORItoSR:	instruction = "EORItoSR";	break;
+		case Operation::EORItoCCR:	instruction = "EORItoCCR";	break;
+
+		case Operation::BTST:	instruction = "BTST";	break;
+		case Operation::BCLR:	instruction = "BCLR";	break;
+		case Operation::BCHG:	instruction = "BCHG";	break;
+		case Operation::BSET:	instruction = "BSET";	break;
+
+		case Operation::CMPb:	instruction = "CMP.b";	break;
+		case Operation::CMPw:	instruction = "CMP.w";	break;
+		case Operation::CMPl:	instruction = "CMP.l";	break;
+
+		case Operation::CMPAw:	instruction = "CMPA.w";	break;
+		case Operation::CMPAl:	instruction = "CMPA.l";	break;
+
+		case Operation::TSTb:	instruction = "TST.b";	break;
+		case Operation::TSTw:	instruction = "TST.w";	break;
+		case Operation::TSTl:	instruction = "TST.l";	break;
+
+		case Operation::JMP:	instruction = "JMP";	break;
+		case Operation::JSR:	instruction = "JSR";	break;
+		case Operation::RTS:	instruction = "RTS";	break;
+		case Operation::DBcc:	instruction = "DBcc";	break;
+		case Operation::Scc:	instruction = "Scc";	break;
+
+		case Operation::Bccb:
+		case Operation::Bccl:
+		case Operation::Bccw:	instruction = "Bcc";	break;
+
+		case Operation::BSRb:
+		case Operation::BSRl:
+		case Operation::BSRw:	instruction = "BSR";	break;
+
+		case Operation::CLRb:	instruction = "CLR.b";	break;
+		case Operation::CLRw:	instruction = "CLR.w";	break;
+		case Operation::CLRl:	instruction = "CLR.l";	break;
+
+		case Operation::NEGXb:	instruction = "NEGX.b";	break;
+		case Operation::NEGXw:	instruction = "NEGX.w";	break;
+		case Operation::NEGXl:	instruction = "NEGX.l";	break;
+
+		case Operation::NEGb:	instruction = "NEG.b";	break;
+		case Operation::NEGw:	instruction = "NEG.w";	break;
+		case Operation::NEGl:	instruction = "NEG.l";	break;
+
+		case Operation::ASLb:	instruction = "ASL.b";	break;
+		case Operation::ASLw:	instruction = "ASL.w";	break;
+		case Operation::ASLl:	instruction = "ASL.l";	break;
+		case Operation::ASLm:	instruction = "ASL.w";	break;
+
+		case Operation::ASRb:	instruction = "ASR.b";	break;
+		case Operation::ASRw:	instruction = "ASR.w";	break;
+		case Operation::ASRl:	instruction = "ASR.l";	break;
+		case Operation::ASRm:	instruction = "ASR.w";	break;
+
+		case Operation::LSLb:	instruction = "LSL.b";	break;
+		case Operation::LSLw:	instruction = "LSL.w";	break;
+		case Operation::LSLl:	instruction = "LSL.l";	break;
+		case Operation::LSLm:	instruction = "LSL.w";	break;
+
+		case Operation::LSRb:	instruction = "LSR.b";	break;
+		case Operation::LSRw:	instruction = "LSR.w";	break;
+		case Operation::LSRl:	instruction = "LSR.l";	break;
+		case Operation::LSRm:	instruction = "LSR.w";	break;
+
+		case Operation::ROLb:	instruction = "ROL.b";	break;
+		case Operation::ROLw:	instruction = "ROL.w";	break;
+		case Operation::ROLl:	instruction = "ROL.l";	break;
+		case Operation::ROLm:	instruction = "ROL.w";	break;
+
+		case Operation::RORb:	instruction = "ROR.b";	break;
+		case Operation::RORw:	instruction = "ROR.w";	break;
+		case Operation::RORl:	instruction = "ROR.l";	break;
+		case Operation::RORm:	instruction = "ROR.w";	break;
+
+		case Operation::ROXLb:	instruction = "ROXL.b";	break;
+		case Operation::ROXLw:	instruction = "ROXL.w";	break;
+		case Operation::ROXLl:	instruction = "ROXL.l";	break;
+		case Operation::ROXLm:	instruction = "ROXL.w";	break;
+
+		case Operation::ROXRb:	instruction = "ROXR.b";	break;
+		case Operation::ROXRw:	instruction = "ROXR.w";	break;
+		case Operation::ROXRl:	instruction = "ROXR.l";	break;
+		case Operation::ROXRm:	instruction = "ROXR.w";	break;
+
+		case Operation::MOVEMtoMl:	instruction = "MOVEM.l";	break;
+		case Operation::MOVEMtoMw:	instruction = "MOVEM.w";	break;
+		case Operation::MOVEMtoRl:
+			instruction = "MOVEM.l";
+			flip_operands = true;
+		break;
+		case Operation::MOVEMtoRw:
+			instruction = "MOVEM.w";
+			flip_operands = true;
+		break;
+
+		case Operation::MOVEPl:	instruction = "MOVEP.l";	break;
+		case Operation::MOVEPw:	instruction = "MOVEP.w";	break;
+
+		case Operation::ANDb:	instruction = "AND.b";	break;
+		case Operation::ANDw:	instruction = "AND.w";	break;
+		case Operation::ANDl:	instruction = "AND.l";	break;
+
+		case Operation::EORb:	instruction = "EOR.b";	break;
+		case Operation::EORw:	instruction = "EOR.w";	break;
+		case Operation::EORl:	instruction = "EOR.l";	break;
+
+		case Operation::NOTb:	instruction = "NOT.b";	break;
+		case Operation::NOTw:	instruction = "NOT.w";	break;
+		case Operation::NOTl:	instruction = "NOT.l";	break;
+
+		case Operation::ORb:	instruction = "OR.b";	break;
+		case Operation::ORw:	instruction = "OR.w";	break;
+		case Operation::ORl:	instruction = "OR.l";	break;
+
+		case Operation::MULU:	instruction = "MULU";	break;
+		case Operation::MULS:	instruction = "MULS";	break;
+		case Operation::DIVU:	instruction = "DIVU";	break;
+		case Operation::DIVS:	instruction = "DIVS";	break;
+
+		case Operation::RTE:	instruction = "RTE";	break;
+		case Operation::RTR:	instruction = "RTR";	break;
+
+		case Operation::TRAP:	instruction = "TRAP";	break;
+		case Operation::TRAPV:	instruction = "TRAPV";	break;
+		case Operation::CHK:	instruction = "CHK";	break;
+
+		case Operation::EXG:	instruction = "EXG";	break;
+		case Operation::SWAP:	instruction = "SWAP";	break;
+
+		case Operation::TAS:	instruction = "TAS";	break;
+
+		case Operation::EXTbtow:	instruction = "EXT.w";	break;
+		case Operation::EXTwtol:	instruction = "EXT.l";	break;
+
+		case Operation::LINKw:	instruction = "LINK";	break;
+		case Operation::UNLINK:	instruction = "UNLINK";	break;
+
+		case Operation::STOP:	instruction = "STOP";	break;
+		case Operation::RESET:	instruction = "RESET";	break;
+
+		default:
+			assert(false);
+	}
+
+	const std::string operand1 = operand_description(0 ^ int(flip_operands), opcode);
+	const std::string operand2 = operand_description(1 ^ int(flip_operands), opcode);
+
+	std::string result = instruction;
+	if(!operand1.empty()) result += std::string(" ") + operand1;
+	if(!operand2.empty()) result += std::string(", ") + operand2;
+
+	return result;
+}
diff --git a/InstructionSets/M68k/Instruction.hpp b/InstructionSets/M68k/Instruction.hpp
index 77e0ffed2..3b805b941 100644
--- a/InstructionSets/M68k/Instruction.hpp
+++ b/InstructionSets/M68k/Instruction.hpp
@@ -13,6 +13,7 @@
 
 #include <cassert>
 #include <cstdint>
+#include <string>
 
 namespace InstructionSet {
 namespace M68k {
@@ -308,6 +309,8 @@ class Preinstruction {
 		uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
 		uint8_t flags_ = 0;
 
+		std::string operand_description(int index, int opcode) const;
+
 	public:
 		Preinstruction(
 			Operation operation,
@@ -327,6 +330,11 @@ class Preinstruction {
 		}
 
 		Preinstruction() {}
+
+		/// Produces a string description of this instruction; if @c opcode
+		/// is supplied then any quick fields in this instruction will be decoded;
+		/// otherwise they'll be printed as just 'Q'.
+		std::string to_string(int opcode = -1) const;
 };
 
 }
diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index daa0b830e..93b7c032d 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -131,6 +131,9 @@
 		4B0ACC3223775819008902D0 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0ACC2223775819008902D0 /* Atari2600.cpp */; };
 		4B0ACC3323775819008902D0 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0ACC2223775819008902D0 /* Atari2600.cpp */; };
 		4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
+		4B0DA67B282DCDF100C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
+		4B0DA67C282DCDF300C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
+		4B0DA67D282DCDF300C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
 		4B0E04EA1FC9E5DA00F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
 		4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
 		4B0E04F11FC9EA9500F43484 /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */; };
@@ -1166,6 +1169,7 @@
 		4B0ACC2523775819008902D0 /* TIA.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TIA.hpp; sourceTree = "<group>"; };
 		4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
 		4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
+		4B0DA67A282DCC4200C12F17 /* Instruction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Instruction.cpp; sourceTree = "<group>"; };
 		4B0E04E81FC9E5DA00F43484 /* CAS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAS.cpp; sourceTree = "<group>"; };
 		4B0E04E91FC9E5DA00F43484 /* CAS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CAS.hpp; sourceTree = "<group>"; };
 		4B0E04F81FC9FA3000F43484 /* 9918.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 9918.hpp; sourceTree = "<group>"; };
@@ -3201,6 +3205,7 @@
 			isa = PBXGroup;
 			children = (
 				4B79629F2819681F008130F9 /* Decoder.cpp */,
+				4B0DA67A282DCC4200C12F17 /* Instruction.cpp */,
 				4B79629E2819681F008130F9 /* Decoder.hpp */,
 				4B7C79FE282AFA9B002D6C0B /* ExceptionVectors.hpp */,
 				4BB5B99C281C805300522DA9 /* Executor.hpp */,
@@ -5623,6 +5628,7 @@
 				4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */,
 				4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */,
 				4B894527201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
+				4B0DA67C282DCDF300C12F17 /* Instruction.cpp in Sources */,
 				4BB244D622AABAF600BE20E5 /* z8530.cpp in Sources */,
 				4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */,
 				4B055AD01FAE9B030060FFFF /* Tape.cpp in Sources */,
@@ -5829,6 +5835,7 @@
 				4BC080D926A25ADA00D03FD8 /* Amiga.cpp in Sources */,
 				4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */,
 				4B4518821F75E91A00926311 /* PCMSegment.cpp in Sources */,
+				4B0DA67B282DCDF100C12F17 /* Instruction.cpp in Sources */,
 				4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */,
 				4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */,
 				4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
@@ -6039,6 +6046,7 @@
 				4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */,
 				4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
 				4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
+				4B0DA67D282DCDF300C12F17 /* Instruction.cpp in Sources */,
 				4BFCA12B1ECBE7C400AC40C1 /* ZexallTests.swift in Sources */,
 				4B778F2223A5EDDD0000D260 /* PulseQueuedTape.cpp in Sources */,
 				4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */,
diff --git a/OSBindings/Mac/Clock SignalTests/68000DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/68000DecoderTests.mm
index 7e717f77f..32844d16f 100644
--- a/OSBindings/Mac/Clock SignalTests/68000DecoderTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/68000DecoderTests.mm	
@@ -15,51 +15,6 @@ using namespace InstructionSet::M68k;
 @interface M68000DecoderTests : XCTestCase
 @end
 
-namespace {
-
-NSString *operand(Preinstruction instruction, uint16_t opcode, int index) {
-	switch(instruction.mode(index)) {
-		default:	return [NSString stringWithFormat:@"[Mode %d?]", int(instruction.mode(index))];
-
-		case AddressingMode::None:
-			return @"";
-
-		case AddressingMode::DataRegisterDirect:
-			return [NSString stringWithFormat:@"D%d", instruction.reg(index)];
-
-		case AddressingMode::AddressRegisterDirect:
-			return [NSString stringWithFormat:@"A%d", instruction.reg(index)];
-		case AddressingMode::AddressRegisterIndirect:
-			return [NSString stringWithFormat:@"(A%d)", instruction.reg(index)];
-		case AddressingMode::AddressRegisterIndirectWithPostincrement:
-			return [NSString stringWithFormat:@"(A%d)+", instruction.reg(index)];
-		case AddressingMode::AddressRegisterIndirectWithPredecrement:
-			return [NSString stringWithFormat:@"-(A%d)", instruction.reg(index)];
-		case AddressingMode::AddressRegisterIndirectWithDisplacement:
-			return [NSString stringWithFormat:@"(d16, A%d)", instruction.reg(index)];
-		case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
-			return [NSString stringWithFormat:@"(d8, A%d, Xn)", instruction.reg(index)];
-
-		case AddressingMode::ProgramCounterIndirectWithDisplacement:
-			return @"(d16, PC)";
-		case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
-			return @"(d8, PC, Xn)";
-
-		case AddressingMode::AbsoluteShort:
-			return @"(xxx).w";
-		case AddressingMode::AbsoluteLong:
-			return @"(xxx).l";
-
-		case AddressingMode::ImmediateData:
-			return @"#";
-
-		case AddressingMode::Quick:
-			return [NSString stringWithFormat:@"%d", quick(opcode, instruction.operation)];
-	}
-}
-
-}
-
 @implementation M68000DecoderTests
 
 - (void)testInstructionSpecs {
@@ -81,244 +36,7 @@ NSString *operand(Preinstruction instruction, uint16_t opcode, int index) {
 
 		const auto found = decoder.decode(uint16_t(instr));
 
-		NSString *instruction;
-		bool flipOperands = false;
-		switch(found.operation) {
-			case Operation::Undefined:	instruction = @"None";		break;
-			case Operation::NOP:		instruction = @"NOP";		break;
-			case Operation::ABCD:		instruction = @"ABCD";		break;
-			case Operation::SBCD:		instruction = @"SBCD";		break;
-			case Operation::NBCD:		instruction = @"NBCD";		break;
-
-			case Operation::ADDb:		instruction = @"ADD.b";		break;
-			case Operation::ADDw:		instruction = @"ADD.w";		break;
-			case Operation::ADDl:		instruction = @"ADD.l";		break;
-
-			case Operation::ADDAw:
-				if(found.mode<0>() == AddressingMode::Quick) {
-					instruction = @"ADD.w";
-				} else {
-					instruction = @"ADDA.w";
-				}
-			break;
-			case Operation::ADDAl:
-				if(found.mode<0>() == AddressingMode::Quick) {
-					instruction = @"ADD.l";
-				} else {
-					instruction = @"ADDA.l";
-				}
-			break;
-
-			case Operation::ADDXb:		instruction = @"ADDX.b";	break;
-			case Operation::ADDXw:		instruction = @"ADDX.w";	break;
-			case Operation::ADDXl:		instruction = @"ADDX.l";	break;
-
-			case Operation::SUBb:		instruction = @"SUB.b";		break;
-			case Operation::SUBw:		instruction = @"SUB.w";		break;
-			case Operation::SUBl:		instruction = @"SUB.l";		break;
-
-			case Operation::SUBAw:
-				if(found.mode<0>() == AddressingMode::Quick) {
-					instruction = @"SUB.w";
-				} else {
-					instruction = @"SUBA.w";
-				}
-			break;
-			case Operation::SUBAl:
-				if(found.mode<0>() == AddressingMode::Quick) {
-					instruction = @"SUB.l";
-				} else {
-					instruction = @"SUBA.l";
-				}
-			break;
-
-			case Operation::SUBXb:		instruction = @"SUBX.b";	break;
-			case Operation::SUBXw:		instruction = @"SUBX.w";	break;
-			case Operation::SUBXl:		instruction = @"SUBX.l";	break;
-
-			case Operation::MOVEb:		instruction = @"MOVE.b";	break;
-			case Operation::MOVEw:		instruction = @"MOVE.w";	break;
-			case Operation::MOVEl:
-				if(found.mode<0>() == AddressingMode::Quick) {
-					instruction = @"MOVE.q";
-				} else {
-					instruction = @"MOVE.l";
-				}
-			break;
-
-			case Operation::MOVEAw:		instruction = @"MOVEA.w";	break;
-			case Operation::MOVEAl:		instruction = @"MOVEA.l";	break;
-
-			case Operation::LEA:		instruction = @"LEA";		break;
-			case Operation::PEA:		instruction = @"PEA";		break;
-
-			case Operation::MOVEtoSR:		instruction = @"MOVEtoSR";		break;
-			case Operation::MOVEfromSR:		instruction = @"MOVEfromSR";	break;
-			case Operation::MOVEtoCCR:		instruction = @"MOVEtoCCR";		break;
-			case Operation::MOVEtoUSP:		instruction = @"MOVEtoUSP";		break;
-			case Operation::MOVEfromUSP:	instruction = @"MOVEfromUSP";	break;
-
-			case Operation::ORItoSR:	instruction = @"ORItoSR";	break;
-			case Operation::ORItoCCR:	instruction = @"ORItoCCR";	break;
-			case Operation::ANDItoSR:	instruction = @"ANDItoSR";	break;
-			case Operation::ANDItoCCR:	instruction = @"ANDItoCCR";	break;
-			case Operation::EORItoSR:	instruction = @"EORItoSR";	break;
-			case Operation::EORItoCCR:	instruction = @"EORItoCCR";	break;
-
-			case Operation::BTST:	instruction = @"BTST";	break;
-			case Operation::BCLR:	instruction = @"BCLR";	break;
-			case Operation::BCHG:	instruction = @"BCHG";	break;
-			case Operation::BSET:	instruction = @"BSET";	break;
-
-			case Operation::CMPb:	instruction = @"CMP.b";	break;
-			case Operation::CMPw:	instruction = @"CMP.w";	break;
-			case Operation::CMPl:	instruction = @"CMP.l";	break;
-
-			case Operation::CMPAw:	instruction = @"CMPA.w";	break;
-			case Operation::CMPAl:	instruction = @"CMPA.l";	break;
-
-			case Operation::TSTb:	instruction = @"TST.b";	break;
-			case Operation::TSTw:	instruction = @"TST.w";	break;
-			case Operation::TSTl:	instruction = @"TST.l";	break;
-
-			case Operation::JMP:	instruction = @"JMP";	break;
-			case Operation::JSR:	instruction = @"JSR";	break;
-			case Operation::RTS:	instruction = @"RTS";	break;
-			case Operation::DBcc:	instruction = @"DBcc";	break;
-			case Operation::Scc:	instruction = @"Scc";	break;
-
-			case Operation::Bccb:
-			case Operation::Bccl:
-			case Operation::Bccw:	instruction = @"Bcc";	break;
-
-			case Operation::BSRb:
-			case Operation::BSRl:
-			case Operation::BSRw:	instruction = @"BSR";	break;
-
-			case Operation::CLRb:	instruction = @"CLR.b";	break;
-			case Operation::CLRw:	instruction = @"CLR.w";	break;
-			case Operation::CLRl:	instruction = @"CLR.l";	break;
-
-			case Operation::NEGXb:	instruction = @"NEGX.b";	break;
-			case Operation::NEGXw:	instruction = @"NEGX.w";	break;
-			case Operation::NEGXl:	instruction = @"NEGX.l";	break;
-
-			case Operation::NEGb:	instruction = @"NEG.b";	break;
-			case Operation::NEGw:	instruction = @"NEG.w";	break;
-			case Operation::NEGl:	instruction = @"NEG.l";	break;
-
-			case Operation::ASLb:	instruction = @"ASL.b";	break;
-			case Operation::ASLw:	instruction = @"ASL.w";	break;
-			case Operation::ASLl:	instruction = @"ASL.l";	break;
-			case Operation::ASLm:	instruction = @"ASL.w";	break;
-
-			case Operation::ASRb:	instruction = @"ASR.b";	break;
-			case Operation::ASRw:	instruction = @"ASR.w";	break;
-			case Operation::ASRl:	instruction = @"ASR.l";	break;
-			case Operation::ASRm:	instruction = @"ASR.w";	break;
-
-			case Operation::LSLb:	instruction = @"LSL.b";	break;
-			case Operation::LSLw:	instruction = @"LSL.w";	break;
-			case Operation::LSLl:	instruction = @"LSL.l";	break;
-			case Operation::LSLm:	instruction = @"LSL.w";	break;
-
-			case Operation::LSRb:	instruction = @"LSR.b";	break;
-			case Operation::LSRw:	instruction = @"LSR.w";	break;
-			case Operation::LSRl:	instruction = @"LSR.l";	break;
-			case Operation::LSRm:	instruction = @"LSR.w";	break;
-
-			case Operation::ROLb:	instruction = @"ROL.b";	break;
-			case Operation::ROLw:	instruction = @"ROL.w";	break;
-			case Operation::ROLl:	instruction = @"ROL.l";	break;
-			case Operation::ROLm:	instruction = @"ROL.w";	break;
-
-			case Operation::RORb:	instruction = @"ROR.b";	break;
-			case Operation::RORw:	instruction = @"ROR.w";	break;
-			case Operation::RORl:	instruction = @"ROR.l";	break;
-			case Operation::RORm:	instruction = @"ROR.w";	break;
-
-			case Operation::ROXLb:	instruction = @"ROXL.b";	break;
-			case Operation::ROXLw:	instruction = @"ROXL.w";	break;
-			case Operation::ROXLl:	instruction = @"ROXL.l";	break;
-			case Operation::ROXLm:	instruction = @"ROXL.w";	break;
-
-			case Operation::ROXRb:	instruction = @"ROXR.b";	break;
-			case Operation::ROXRw:	instruction = @"ROXR.w";	break;
-			case Operation::ROXRl:	instruction = @"ROXR.l";	break;
-			case Operation::ROXRm:	instruction = @"ROXR.w";	break;
-
-			case Operation::MOVEMtoMl:	instruction = @"MOVEM.l";	break;
-			case Operation::MOVEMtoMw:	instruction = @"MOVEM.w";	break;
-			case Operation::MOVEMtoRl:
-				instruction = @"MOVEM.l";
-				flipOperands = true;
-			break;
-			case Operation::MOVEMtoRw:
-				instruction = @"MOVEM.w";
-				flipOperands = true;
-			break;
-
-			case Operation::MOVEPl:	instruction = @"MOVEP.l";	break;
-			case Operation::MOVEPw:	instruction = @"MOVEP.w";	break;
-
-			case Operation::ANDb:	instruction = @"AND.b";	break;
-			case Operation::ANDw:	instruction = @"AND.w";	break;
-			case Operation::ANDl:	instruction = @"AND.l";	break;
-
-			case Operation::EORb:	instruction = @"EOR.b";	break;
-			case Operation::EORw:	instruction = @"EOR.w";	break;
-			case Operation::EORl:	instruction = @"EOR.l";	break;
-
-			case Operation::NOTb:	instruction = @"NOT.b";	break;
-			case Operation::NOTw:	instruction = @"NOT.w";	break;
-			case Operation::NOTl:	instruction = @"NOT.l";	break;
-
-			case Operation::ORb:	instruction = @"OR.b";	break;
-			case Operation::ORw:	instruction = @"OR.w";	break;
-			case Operation::ORl:	instruction = @"OR.l";	break;
-
-			case Operation::MULU:	instruction = @"MULU";	break;
-			case Operation::MULS:	instruction = @"MULS";	break;
-			case Operation::DIVU:	instruction = @"DIVU";	break;
-			case Operation::DIVS:	instruction = @"DIVS";	break;
-
-			case Operation::RTE:	instruction = @"RTE";	break;
-			case Operation::RTR:	instruction = @"RTR";	break;
-
-			case Operation::TRAP:	instruction = @"TRAP";	break;
-			case Operation::TRAPV:	instruction = @"TRAPV";	break;
-			case Operation::CHK:	instruction = @"CHK";	break;
-
-			case Operation::EXG:	instruction = @"EXG";	break;
-			case Operation::SWAP:	instruction = @"SWAP";	break;
-
-			case Operation::TAS:	instruction = @"TAS";	break;
-
-			case Operation::EXTbtow:	instruction = @"EXT.w";		break;
-			case Operation::EXTwtol:	instruction = @"EXT.l";		break;
-
-			case Operation::LINKw:	instruction = @"LINK";		break;
-			case Operation::UNLINK:	instruction = @"UNLINK";	break;
-
-			case Operation::STOP:	instruction = @"STOP";		break;
-			case Operation::RESET:	instruction = @"RESET";		break;
-
-			// For now, skip any unmapped operations.
-			default:
-				XCTAssert(false, @"Operation %d unhandled by test case", int(found.operation));
-			continue;
-		}
-
-		NSString *const operand1 = operand(found, uint16_t(instr), 0 ^ int(flipOperands));
-		NSString *const operand2 = operand(found, uint16_t(instr), 1 ^ int(flipOperands));
-
-		if(operand1.length) instruction = [instruction stringByAppendingFormat:@" %@", operand1];
-		if(operand2.length) instruction = [instruction stringByAppendingFormat:@", %@", operand2];
-
-		XCTAssertFalse(
-			found.mode(0 ^ int(flipOperands)) == AddressingMode::None &&
-			found.mode(1 ^ int(flipOperands)) != AddressingMode::None,
-			@"Decoding of %@ provided a second operand but not a first", instrName);
+		NSString *const instruction = [NSString stringWithUTF8String:found.to_string(instr).c_str()];
 		XCTAssertEqualObjects(instruction, expected, "%@ should decode as %@; got %@", instrName, expected, instruction);
 	}
 }