###
Z80 core.
To avoid mass repetition of code across the numerous instruction variants
the code for this component is built up programmatically and evaluated in
the global scope. CoffeeScript is used here for its support of multi-line
strings, and expression interpolation in strings.
###


###
Registers are stored in a typed array as a way of automatically casting
calculations to 8/16 bit, and to allow accessing them interchangeably as
register pairs or individual registers by having two arrays backed by the
same buffer. For the latter to work, we need to find out the endianness
of the host processor, as typed arrays are native-endian
	(http://lists.w3.org/Archives/Public/public-script-coord/2010AprJun/0048.html,
	http://cat-in-136.blogspot.com/2011/03/javascript-typed-array-use-native.html)
###

window.buildZ80 = (opts) ->
	opts ?= {}
	endianTestBuffer = new ArrayBuffer(2)
	endianTestUint16 = new Uint16Array(endianTestBuffer)
	endianTestUint8 = new Uint8Array(endianTestBuffer)
	endianTestUint16[0] = 0x0100
	isBigEndian = (endianTestUint8[0] == 0x01)

	# Offsets into register set when read as register pairs
	rpAF = 0
	rpBC = 1
	rpDE = 2
	rpHL = 3
	rpAF_ = 4
	rpBC_ = 5
	rpDE_ = 6
	rpHL_ = 7
	rpIX = 8
	rpIY = 9
	rpIR = 10
	rpSP = 11
	rpPC = 12

	registerPairIndexes = {
		'IX': 8, 'IY': 9
	}

	if isBigEndian
		rA = 0; rF = 1
		rB = 2; rC = 3
		rD = 4; rE = 5
		rH = 6; rL = 7
		rA_ = 8; rF_ = 9
		rB_ = 10; rC_ = 11
		rD_ = 12; rE_ = 13
		rH_ = 14; rL_ = 15
		rIXH = 16; rIXL = 17
		rIYH = 18; rIYL = 19
		rI = 20; rR = 21
		registerIndexes = {
			A: 0, F: 1
			B: 2, C: 3
			D: 4, E: 5
			H: 6, L: 7
			IXH: 16, IXL: 17
			IYH: 18, IYL: 19
		}
	else
		# little-endian
		rF = 0; rA = 1
		rC = 2; rB = 3
		rE = 4; rD = 5
		rL = 6; rH = 7
		rF_ = 8; rA_ = 9
		rC_ = 10; rB_ = 11
		rE_ = 12; rD_ = 13
		rL_ = 14; rH_ = 15
		rIXL = 16; rIXH = 17
		rIYL = 18; rIYH = 19
		rR = 20; rI = 21
		registerIndexes = {
			F: 0, A: 1
			C: 2, B: 3
			E: 4, D: 5
			L: 6, H: 7
			IXL: 16, IXH: 17
			IYL: 18, IYH: 19
		}

	FLAG_C = 0x01
	FLAG_N = 0x02
	FLAG_P = 0x04
	FLAG_V = 0x04
	FLAG_3 = 0x08
	FLAG_H = 0x10
	FLAG_5 = 0x20
	FLAG_Z = 0x40
	FLAG_S = 0x80

	# JS block setting up internal Z80 state and lookup tables

	setUpStateJS = """
		var memory = opts.memory;
		var ioBus = opts.ioBus;
		var display = opts.display;

		var registerBuffer = new ArrayBuffer(26);
		/* Expose registerBuffer as both register pairs and individual registers */
		var regPairs = new Uint16Array(registerBuffer);
		var regs = new Uint8Array(registerBuffer);

		var tstates = 0; /* number of tstates since start of this frame */
		var iff1 = 0;
		var iff2 = 0;
		var im = 0;
		var halted = false;

		/* tables for setting Z80 flags */

		/*
			Whether a half carry occurred or not can be determined by looking at
			the 3rd bit of the two arguments and the result; these are hashed
			into this table in the form r12, where r is the 3rd bit of the
			result, 1 is the 3rd bit of the 1st argument and 2 is the
			third bit of the 2nd argument; the tables differ for add and subtract
			operations
		*/
		var halfcarryAddTable = new Uint8Array([0, #{FLAG_H}, #{FLAG_H}, #{FLAG_H}, 0, 0, 0, #{FLAG_H}]);
		var halfcarrySubTable = new Uint8Array([0, 0, #{FLAG_H}, 0, #{FLAG_H}, 0, #{FLAG_H}, #{FLAG_H}]);

		/*
			Similarly, overflow can be determined by looking at the 7th bits; again
			the hash into this table is r12
		*/
		var overflowAddTable = new Uint8Array([0, 0, 0, #{FLAG_V}, #{FLAG_V}, 0, 0, 0]);
		var overflowSubTable = new Uint8Array([0, #{FLAG_V}, 0, 0, 0, 0, #{FLAG_V}, 0]);

		var sz53Table = new Uint8Array(0x100); /* The S, Z, 5 and 3 bits of the index */
		var parityTable = new Uint8Array(0x100); /* The parity of the lookup value */
		var sz53pTable = new Uint8Array(0x100); /* OR the above two tables together */

		for (var i = 0; i < 0x100; i++) {
			sz53Table[i] = i & ( #{FLAG_3 | FLAG_5 | FLAG_S} );
			var j = i;
			var parity = 0;
			for (var k = 0; k < 8; k++) {
				parity ^= j & 1;
				j >>=1;
			}

			parityTable[i] = (parity ? 0 : #{FLAG_P});
			sz53pTable[i] = sz53Table[i] | parityTable[i];

			sz53Table[0] |= #{FLAG_Z};
			sz53pTable[0] |= #{FLAG_Z};
		}

		var interruptible = true;
		var interruptPending = false;
		var interruptDataBus = 0;
		var opcodePrefix = '';
	"""

	###
		Boilerplate generator: a helper to deal with classes of opcodes which perform
		the same task on different types of operands: e.g. XOR B, XOR (HL), XOR nn, XOR (IX+nn).
		This function accepts the parameter in question, and returns a set of canned strings
		for use in the opcode runner body:
		'getter': a block of code that performs any necessary memory access etc in order to
			make 'v' a valid expression;
		'v': an expression with no side effects, evaluating to the operand's value. (Must also be a valid lvalue for assignment)
		'trunc': an expression such as '& 0xff' to truncate v back to its proper range, if appropriate
		'setter': a block of code that writes an updated value back to its proper location, if any

		Passing hasIXOffsetAlready = true indicates that we have already read the offset value of (IX+nn)/(IY+nn)
		into a variable 'offset' (necessary because DDCB/FFCB instructions put this before the final opcode byte).
	###
	getParamBoilerplate = (param, hasIXOffsetAlready = false) ->
		if param.match(/^[AFBCDEHL]|I[XY][HL]$/)
			regNum = registerIndexes[param]
			{
				'getter': ''
				'v': "regs[#{regNum}]"
				'trunc': ''
				'setter': ''
			}
		else if param == '(HL)'
			{
				'getter': "var val = READMEM(regPairs[#{rpHL}]);"
				'v': 'val'
				'trunc': '& 0xff'
				'setter': """
					CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
					WRITEMEM(regPairs[#{rpHL}], val);
				"""
			}
		else if param == 'nn'
			{
				'getter': "var val = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;"
				'v': 'val'
				'trunc': '& 0xff'
				'setter': ''
			}
		else if (match = param.match(/^\((I[XY])\+nn\)$/))
			rp = registerPairIndexes[match[1]]
			if hasIXOffsetAlready
				getter = ''
			else
				getter = """
					var offset = READMEM(regPairs[#{rpPC}]);
					if (offset & 0x80) offset -= 0x100;
					CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
					CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
					CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
					CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
					CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
					regPairs[#{rpPC}]++;
				"""
			getter += """
				var addr = (regPairs[#{rp}] + offset) & 0xffff;
				var val = READMEM(addr);
			"""
			{
				'getter': getter
				'v': 'val'
				'trunc': '& 0xff'
				'setter': """
					CONTEND_READ_NO_MREQ(addr, 1);
					WRITEMEM(addr, val);
				"""
			}
		else if param == 'add'
			# special case for incorporating ADD/SUB into DAA calculation using a custom variable 'add'
			{
				'getter': ''
				'v': 'add'
				'trunc': ''
				'setter': ''
			}
		else
			throw "Unknown param format: #{param}"

	###
		Opcode generator functions: each returns a string of Javascript that performs the opcode
		when executed within this module's scope. Note that instructions with DDCBnn opcodes also
		require an 'offset' variable to be defined as nn (as a signed byte).
	###
	ADC_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			var adctemp = regs[#{rA}] + #{operand.v} + (regs[#{rF}] & #{FLAG_C});
			var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (adctemp & 0x88) >> 1 );
			regs[#{rA}] = adctemp;
			regs[#{rF}] = ( adctemp & 0x100 ? #{FLAG_C} : 0 ) | halfcarryAddTable[lookup & 0x07] | overflowAddTable[lookup >> 4] | sz53Table[regs[#{rA}]];
		"""

	ADC_HL_RR = (rp2) ->
		"""
			var add16temp = regPairs[#{rpHL}] + regPairs[#{rp2}] + (regs[#{rF}] & #{FLAG_C});
			var lookup = (
				( (regPairs[#{rpHL}] & 0x8800) >> 11 ) |
				( (regPairs[#{rp2}] & 0x8800) >> 10 ) |
				( (add16temp & 0x8800) >>  9 )
			);
			regPairs[#{rpHL}] = add16temp;
			regs[#{rF}] = (
				(add16temp & 0x10000 ? #{FLAG_C} : 0) |
				overflowAddTable[lookup >> 4] |
				(regs[#{rH}] & #{FLAG_3 | FLAG_5 | FLAG_S}) |
				halfcarryAddTable[lookup & 0x07] |
				(regPairs[#{rpHL}] ? 0 : #{FLAG_Z})
			);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	ADD_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			var addtemp = regs[#{rA}] + #{operand.v};
			var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (addtemp & 0x88) >> 1 );
			regs[#{rA}] = addtemp;
			regs[#{rF}] = ( addtemp & 0x100 ? #{FLAG_C} : 0 ) | halfcarryAddTable[lookup & 0x07] | overflowAddTable[lookup >> 4] | sz53Table[regs[#{rA}]];
		"""

	ADD_RR_RR = (rp1, rp2) ->
		"""
			var add16temp = regPairs[#{rp1}] + regPairs[#{rp2}];
			var lookup = ( (regPairs[#{rp1}] & 0x0800) >> 11 ) | ( (regPairs[#{rp2}] & 0x0800) >> 10 ) | ( (add16temp & 0x0800) >>  9 );
			regPairs[#{rp1}] = add16temp;
			regs[#{rF}] = ( regs[#{rF}] & ( #{FLAG_V | FLAG_Z | FLAG_S} ) ) | ( add16temp & 0x10000 ? #{FLAG_C} : 0 ) | ( ( add16temp >> 8 ) & ( #{FLAG_3 | FLAG_5} ) ) | halfcarryAddTable[lookup];
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	AND_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			regs[#{rA}] &= #{operand.v};
			regs[#{rF}] = #{FLAG_H} | sz53pTable[regs[#{rA}]];
		"""

	BIT_N_iRRpNNi = (bit, rp) -> # requires 'offset'
		if bit == 7
			updateSignFlag = "if (value & 0x80) regs[#{rF}] |= #{FLAG_S};"
		else
			updateSignFlag = ""

		"""
			var addr = (regPairs[#{rp}] + offset) & 0xffff;
			var value = READMEM(addr);
			regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( ( addr >> 8 ) & #{FLAG_3 | FLAG_5} );
			if ( !(value & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z};
			#{updateSignFlag}
			CONTEND_READ_NO_MREQ(addr, 1);
		"""

	BIT_N_iHLi = (bit) ->
		if bit == 7
			updateSignFlag = "if (value & 0x80) regs[#{rF}] |= #{FLAG_S};"
		else
			updateSignFlag = ""

		"""
			var addr = regPairs[#{rpHL}];
			var value = READMEM(addr);
			CONTEND_READ_NO_MREQ(addr, 1);
			regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( value & #{FLAG_3 | FLAG_5} );
			if( !(value & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z};
			#{updateSignFlag}
		"""

	BIT_N_R = (bit, r) ->
		if bit == 7
			updateSignFlag = "if (regs[#{r}] & 0x80) regs[#{rF}] |= #{FLAG_S};"
		else
			updateSignFlag = ""
		"""
			regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( regs[#{r}] & #{FLAG_3 | FLAG_5} );
			if( !(regs[#{r}] & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z};
			#{updateSignFlag}
		"""

	CALL_C_NN = (flag, sense) ->
		condition = "regs[#{rF}] & #{flag}"
		condition = "!(#{condition})" if not sense
		"""
			if (#{condition}) {
				var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
				var h = READMEM(regPairs[#{rpPC}]);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				regPairs[#{rpPC}]++;
				regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8);
				regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff);
				regPairs[#{rpPC}] = (h<<8) | l;
			} else {
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++;
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++;
			}
		"""

	CALL_NN = () ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			regPairs[#{rpPC}]++;
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8);
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff);
			regPairs[#{rpPC}] = (h<<8) | l;
		"""

	CCF = () ->
		"""
			regs[#{rF}] = ( regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S} ) | ( (regs[#{rF}] & #{FLAG_C}) ? #{FLAG_H} : #{FLAG_C} ) | ( regs[#{rA}] & #{FLAG_3 | FLAG_5} );
		"""

	CP_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			var cptemp = regs[#{rA}] - #{operand.v};
			var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (cptemp & 0x88) >> 1 );
			regs[#{rF}] = ( cptemp & 0x100 ? #{FLAG_C} : ( cptemp ? 0 : #{FLAG_Z} ) ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | ( #{operand.v} & #{FLAG_3 | FLAG_5} ) | ( cptemp & #{FLAG_S} );
		"""

	CPI_CPD = (modifier) ->
		"""
			var value = READMEM(regPairs[#{rpHL}]);
			var bytetemp = (regs[#{rA}] - value) & 0xff;
			var lookup = ((regs[#{rA}] & 0x08) >> 3) | ((value & 0x08) >> 2) | ((bytetemp & 0x08) >> 1);
			var originalHL = regPairs[#{rpHL}];
			CONTEND_READ_NO_MREQ(originalHL, 1);
			CONTEND_READ_NO_MREQ(originalHL, 1);
			CONTEND_READ_NO_MREQ(originalHL, 1);
			CONTEND_READ_NO_MREQ(originalHL, 1);
			CONTEND_READ_NO_MREQ(originalHL, 1);
			regPairs[#{rpHL}]#{modifier}; regPairs[#{rpBC}]--;
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | (regPairs[#{rpBC}] ? #{FLAG_V | FLAG_N} : #{FLAG_N}) | halfcarrySubTable[lookup] | (bytetemp ? 0 : #{FLAG_Z}) | (bytetemp & #{FLAG_S});
			if (regs[#{rF}] & #{FLAG_H}) bytetemp--;
			regs[#{rF}] |= (bytetemp & #{FLAG_3}) | ( (bytetemp & 0x02) ? #{FLAG_5} : 0 );
		"""

	CPIR_CPDR = (modifier) ->
		"""
			#{CPI_CPD(modifier)}
			if ((regs[#{rF}] & #{FLAG_V | FLAG_Z}) == #{FLAG_V}) {
				regPairs[#{rpPC}] -= 2;
				CONTEND_READ_NO_MREQ(originalHL, 1);
				CONTEND_READ_NO_MREQ(originalHL, 1);
				CONTEND_READ_NO_MREQ(originalHL, 1);
				CONTEND_READ_NO_MREQ(originalHL, 1);
				CONTEND_READ_NO_MREQ(originalHL, 1);
			}
		"""

	CPD = () ->
		CPI_CPD('--')

	CPI = () ->
		CPI_CPD('++')

	CPDR = () ->
		CPIR_CPDR('--')

	CPIR = () ->
		CPIR_CPDR('++')


	DAA = () ->
		subClause = SUB_A('add')
		addClause = ADD_A('add')
		"""
			var add = 0;
			var carry = regs[#{rF}] & #{FLAG_C};
			if( ( regs[#{rF}] & #{FLAG_H} ) || ( ( regs[#{rA}] & 0x0f ) > 9 ) ) add = 6;
			if( carry || ( regs[#{rA}] > 0x99 ) ) add |= 0x60;
			if( regs[#{rA}] > 0x99 ) carry = #{FLAG_C};
			if( regs[#{rF}] & #{FLAG_N} ) {
				#{subClause}
			} else {
				#{addClause}
			}
			regs[#{rF}] = ( regs[#{rF}] & #{~( FLAG_C | FLAG_P )} ) | carry | parityTable[regs[#{rA}]];
		"""

	CPL = () ->
		"""
			regs[#{rA}] ^= 0xff;
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C | FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | #{FLAG_N | FLAG_H};
		"""

	DEC = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C} ) | ( #{operand.v} & 0x0f ? 0 : #{FLAG_H} ) | #{FLAG_N};
			#{operand.v} = (#{operand.v} - 1) #{operand.trunc};

			#{operand.setter}
			regs[#{rF}] |= (#{operand.v} == 0x7f ? #{FLAG_V} : 0) | sz53Table[#{operand.v}];
		"""

	DEC_RR = (rp) ->
		"""
			regPairs[#{rp}]--;
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	DI = () ->
		"""
			iff1 = iff2 = 0;
		"""

	DJNZ_N = () ->
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			regs[#{rB}]--;
			if (regs[#{rB}]) {
				/* take branch */
				var offset = READMEM(regPairs[#{rpPC}]);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				regPairs[#{rpPC}]++;
				regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset);
			} else {
				/* do not take branch */
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++;
			}
		"""

	EI = () ->
		"""
			iff1 = iff2 = 1;
			interruptible = false;
		"""

	EX_iSPi_RR = (rp) ->
		"""
			var l = READMEM(regPairs[#{rpSP}]);
			var spPlus1 = (regPairs[#{rpSP}] + 1) & 0xffff;
			var h = READMEM(spPlus1);
			CONTEND_READ_NO_MREQ(spPlus1, 1);
			WRITEMEM(spPlus1, regPairs[#{rp}] >> 8);
			WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] & 0xff);
			regPairs[#{rp}] = (h<<8) | l;
			CONTEND_WRITE_NO_MREQ(regPairs[#{rpSP}], 1);
			CONTEND_WRITE_NO_MREQ(regPairs[#{rpSP}], 1);
		"""

	EX_RR_RR = (rp1, rp2) ->
		"""
			var temp = regPairs[#{rp1}];
			regPairs[#{rp1}] = regPairs[#{rp2}];
			regPairs[#{rp2}] = temp;
		"""

	EXX = () ->
		"""
			var wordtemp;
			wordtemp = regPairs[#{rpBC}]; regPairs[#{rpBC}] = regPairs[#{rpBC_}]; regPairs[#{rpBC_}] = wordtemp;
			wordtemp = regPairs[#{rpDE}]; regPairs[#{rpDE}] = regPairs[#{rpDE_}]; regPairs[#{rpDE_}] = wordtemp;
			wordtemp = regPairs[#{rpHL}]; regPairs[#{rpHL}] = regPairs[#{rpHL_}]; regPairs[#{rpHL_}] = wordtemp;
		"""

	HALT = () ->
		"""
			halted = true;
			regPairs[#{rpPC}]--;
		"""

	IM = (val) ->
		"""
			im = #{val};
		"""

	IN_A_N = () ->
		"""
			var val = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var port = (regs[#{rA}] << 8) | val;
			CONTEND_PORT_EARLY(port);
			regs[#{rA}] = ioBus.read(port);
			CONTEND_PORT_LATE(port);
		"""

	IN_F_iCi = () ->
		# as IN_R_iCi, but result is written to a local variable rather than a register
		"""
			var port = regPairs[#{rpBC}];
			CONTEND_PORT_EARLY(port);
			var result = ioBus.read(port);
			CONTEND_PORT_LATE(port);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[result];
		"""

	IN_R_iCi = (r) ->
		"""
			var port = regPairs[#{rpBC}];
			CONTEND_PORT_EARLY(port);
			regs[#{r}] = ioBus.read(port);
			CONTEND_PORT_LATE(port);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{r}]];
		"""

	INC = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | (#{operand.v} & 0x0f ? 0 : #{FLAG_H}) | #{FLAG_N};
			#{operand.v} = (#{operand.v} + 1) #{operand.trunc};

			#{operand.setter}
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | ( #{operand.v} == 0x80 ? #{FLAG_V} : 0 ) | ( #{operand.v} & 0x0f ? 0 : #{FLAG_H} ) | sz53Table[#{operand.v}];
		"""

	INC_RR = (rp) ->
		"""
			regPairs[#{rp}]++;
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	INI_IND = (modifier) ->
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_PORT_EARLY(regPairs[#{rpBC}]);
			var initemp = ioBus.read(regPairs[#{rpBC}]);
			CONTEND_PORT_LATE(regPairs[#{rpBC}]);
			WRITEMEM(regPairs[#{rpHL}], initemp);
			regs[#{rB}]--;
			var originalHL = regPairs[#{rpHL}];
			regPairs[#{rpHL}]#{modifier}#{modifier};
			var initemp2 = (initemp + regs[#{rC}] #{modifier} 1) & 0xff;

			regs[#{rF}] = (initemp & 0x80 ? #{FLAG_N} : 0) | ((initemp2 < initemp) ? #{FLAG_H | FLAG_C} : 0 ) | ( parityTable[ (initemp2 & 0x07) ^ regs[#{rB}] ] ? #{FLAG_P} : 0 ) | sz53Table[regs[#{rB}]];
		"""

	INIR_INDR = (modifier) ->
		"""
			#{INI_IND(modifier)}
			if (regs[#{rB}]) {
				CONTEND_WRITE_NO_MREQ(originalHL, 1);
				CONTEND_WRITE_NO_MREQ(originalHL, 1);
				CONTEND_WRITE_NO_MREQ(originalHL, 1);
				CONTEND_WRITE_NO_MREQ(originalHL, 1);
				CONTEND_WRITE_NO_MREQ(originalHL, 1);
				regPairs[#{rpPC}] -= 2;
			}
		"""

	INI = () ->
		INI_IND('+')

	IND = () ->
		INI_IND('-')

	INIR = () ->
		INIR_INDR('+')

	INDR = () ->
		INIR_INDR('-')

	JP_C_NN = (flag, sense) ->
		condition = "regs[#{rF}] & #{flag}"
		condition = "!(#{condition})" if not sense
		"""
			if (#{condition}) {
				var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
				var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
				regPairs[#{rpPC}] = (h<<8) | l;
			} else {
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++;
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++;
			}
		"""

	JP_RR = (rp) ->
		"""
			regPairs[#{rpPC}] = regPairs[#{rp}];
		"""

	JP_NN = () ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			regPairs[#{rpPC}] = (h<<8) | l;
		"""

	JR_C_N = (flag, sense) ->
		condition = "regs[#{rF}] & #{flag}"
		condition = "!(#{condition})" if not sense
		"""
			if (#{condition}) {
				var offset = READMEM(regPairs[#{rpPC}]);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
				regPairs[#{rpPC}]++;
				regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset);
			} else {
				CONTEND_READ(regPairs[#{rpPC}], 3);
				regPairs[#{rpPC}]++; /* skip past offset byte */
			}
		"""

	JR_N = () ->
		"""
			var offset = READMEM(regPairs[#{rpPC}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			regPairs[#{rpPC}]++;
			regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset);
		"""

	LD_A_iNNi = () ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var addr = (h<<8) | l;
			regs[#{rA}] = READMEM(addr);
		"""

	LD_iNNi_A = () ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var addr = (h<<8) | l;
			WRITEMEM(addr, regs[#{rA}]);
		"""

	LD_iNNi_RR = (rp) ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var addr = (h<<8) | l;
			WRITEMEM(addr, regPairs[#{rp}] & 0xff);
			addr = (addr + 1) & 0xffff;
			WRITEMEM(addr, regPairs[#{rp}] >> 8);
		"""

	LD_iRRi_N = (rp) ->
		"""
			var n = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			WRITEMEM(regPairs[#{rp}], n);
		"""

	LD_iRRi_R = (rp, r) ->
		"""
			WRITEMEM(regPairs[#{rp}], regs[#{r}]);
		"""

	LD_iRRpNNi_N = (rp) ->
		"""
			var offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			if (offset & 0x80) offset -= 0x100;
			var addr = (regPairs[#{rp}] + offset) & 0xffff;

			var val = READMEM(regPairs[#{rpPC}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			regPairs[#{rpPC}]++;
			WRITEMEM(addr, val);
		"""

	LD_iRRpNNi_R = (rp, r) ->
		"""
			var offset = READMEM(regPairs[#{rpPC}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			regPairs[#{rpPC}]++;
			if (offset & 0x80) offset -= 0x100;
			var addr = (regPairs[#{rp}] + offset) & 0xffff;

			WRITEMEM(addr, regs[#{r}]);
		"""

	LD_R_iRRi = (r, rp) ->
		"""
			regs[#{r}] = READMEM(regPairs[#{rp}]);
		"""

	LD_R_iRRpNNi = (r, rp) ->
		"""
			var offset = READMEM(regPairs[#{rpPC}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
			regPairs[#{rpPC}]++;
			if (offset & 0x80) offset -= 0x100;
			var addr = (regPairs[#{rp}] + offset) & 0xffff;

			regs[#{r}] = READMEM(addr);
		"""

	LD_R_N = (r) ->
		"""
			regs[#{r}] = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
		"""

	LD_R_R = (r1, r2) ->
		if r1 == rI || r2 == rI || r1 == rR || r2 == rR
			output = """
				CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
				regs[#{r1}] = regs[#{r2}];
			"""
			if (r1 == rA)
				output += """
					regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53Table[regs[#{rA}]] | ( iff2 ? #{FLAG_V} : 0 );
				"""
			output
		else
			"""
				regs[#{r1}] = regs[#{r2}];
			"""

	LD_RR_iNNi = (rp, shifted) ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var addr = (h<<8) | l;
			l = READMEM(addr);
			addr = (addr + 1) & 0xffff;
			h = READMEM(addr);
			regPairs[#{rp}] = (h<<8) | l;
		"""

	LD_RR_NN = (rp) ->
		"""
			var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			regPairs[#{rp}] = (h<<8) | l;
		"""

	LD_RR_RR = (rp1, rp2) ->
		# only used for LD SP,HL/IX/IY
		"""
			regPairs[#{rp1}] = regPairs[#{rp2}];
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	LDBITOP = (regName, opcode, bit, rp) ->
		# load (rp+nn) into register regName, perform opcode, write back to (rp+nn)
		regNum = registerIndexes[regName]
		"""
			var addr = (regPairs[#{rp}] + offset) & 0xffff;
			regs[#{regNum}] = READMEM(addr);
			#{opcode(bit, regName)}
			CONTEND_READ_NO_MREQ(addr, 1);
			WRITEMEM(addr, regs[#{regNum}]);
		"""

	LDI_LDD = (modifier) ->
		"""
			var bytetemp = READMEM(regPairs[#{rpHL}]);
			regPairs[#{rpBC}]--;
			WRITEMEM(regPairs[#{rpDE}],bytetemp);
			var originalDE = regPairs[#{rpDE}];
			regPairs[#{rpDE}]#{modifier}; regPairs[#{rpHL}]#{modifier};
			bytetemp = (bytetemp + regs[#{rA}]) & 0xff;
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C | FLAG_Z | FLAG_S}) | (regPairs[#{rpBC}] ? #{FLAG_V} : 0) | (bytetemp & #{FLAG_3}) | ((bytetemp & 0x02) ? #{FLAG_5} : 0);
			CONTEND_READ_NO_MREQ(originalDE, 1);
			CONTEND_READ_NO_MREQ(originalDE, 1);
		"""

	LDIR_LDDR = (modifier) ->
		"""
			#{LDI_LDD(modifier)}
			if (regPairs[#{rpBC}]) {
				regPairs[#{rpPC}]-=2;
				CONTEND_READ_NO_MREQ(originalDE, 1);
				CONTEND_READ_NO_MREQ(originalDE, 1);
				CONTEND_READ_NO_MREQ(originalDE, 1);
				CONTEND_READ_NO_MREQ(originalDE, 1);
				CONTEND_READ_NO_MREQ(originalDE, 1);
			}
		"""

	LDI = () ->
		LDI_LDD('++')

	LDD = () ->
		LDI_LDD('--')

	LDIR = () ->
		LDIR_LDDR('++')

	LDDR = () ->
		LDIR_LDDR('--')

	LDSHIFTOP = (regName, opcode, rp) ->
		# load (rp+nn) into register regName, perform opcode, write back to (rp+nn)
		regNum = registerIndexes[regName]
		"""
			var addr = (regPairs[#{rp}] + offset) & 0xffff;
			regs[#{regNum}] = READMEM(addr);
			#{opcode(regName)}
			CONTEND_READ_NO_MREQ(addr, 1);
			WRITEMEM(addr, regs[#{regNum}]);
		"""

	NEG = () ->
		"""
			var val = regs[#{rA}];
			var subtemp = -val;
			var lookup = ( (val & 0x88) >> 2 ) | ( (subtemp & 0x88) >> 1 );
			regs[#{rA}] = subtemp;
			regs[#{rF}] = ( subtemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]];
		"""

	NOP = () ->
		"""
		"""

	OR_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}

			regs[#{rA}] |= #{operand.v};
			regs[#{rF}] = sz53pTable[regs[#{rA}]];
		"""

	OUT_iCi_0 = (r) ->
		"""
			CONTEND_PORT_EARLY(regPairs[#{rpBC}]);
			ioBus.write(regPairs[#{rpBC}], 0, tstates);
			CONTEND_PORT_LATE(regPairs[#{rpBC}]);
		"""

	OUT_iCi_R = (r) ->
		"""
			CONTEND_PORT_EARLY(regPairs[#{rpBC}]);
			ioBus.write(regPairs[#{rpBC}], regs[#{r}], tstates);
			CONTEND_PORT_LATE(regPairs[#{rpBC}]);
		"""

	OUT_iNi_A = () ->
		"""
			var port = (regs[#{rA}] << 8) | READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
			CONTEND_PORT_EARLY(port);
			ioBus.write(port, regs[#{rA}], tstates);
			CONTEND_PORT_LATE(port);
		"""

	OUTI_OUTD = (modifier) ->
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			var outitemp = READMEM(regPairs[#{rpHL}]);
			regs[#{rB}]--;	/* This does happen first, despite what the specs say */
			CONTEND_PORT_EARLY(regPairs[#{rpBC}]);
			ioBus.write(regPairs[#{rpBC}], outitemp, tstates);
			CONTEND_PORT_LATE(regPairs[#{rpBC}]);

			regPairs[#{rpHL}]#{modifier};
			outitemp2 = (outitemp + regs[#{rL}]) & 0xff;
			regs[#{rF}] = (outitemp & 0x80 ? #{FLAG_N} : 0) | ( (outitemp2 < outitemp) ? #{FLAG_H | FLAG_C} : 0) | (parityTable[ (outitemp2 & 0x07) ^ regs[#{rB}] ] ? #{FLAG_P} : 0 ) | sz53Table[ regs[#{rB}] ];
		"""

	OTIR_OTDR = (modifier) ->
		"""
			#{OUTI_OUTD(modifier)}
			if (regs[#{rB}]) {
				regPairs[#{rpPC}]-=2;
				CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1);
				CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1);
			}
		"""

	OUTD = () ->
		OUTI_OUTD('--');

	OUTI = () ->
		OUTI_OUTD('++');

	OTDR = () ->
		OTIR_OTDR('--');

	OTIR = () ->
		OTIR_OTDR('++');

	POP_RR = (rp) ->
		"""
			var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
			var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
			regPairs[#{rp}] = (h<<8) | l;
		"""

	PUSH_RR = (rp) ->
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] >> 8);
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] & 0xff);
		"""

	RES = (bit, param) ->
		operand = getParamBoilerplate(param, true)
		hexMask = 0xff ^ (1 << bit)
		"""
			#{operand.getter}
			#{operand.v} &= #{hexMask};
			#{operand.setter}
		"""

	RET = () ->
		"""
			var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
			var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
			regPairs[#{rpPC}] = (h<<8) | l;
		"""

	RET_C = (flag, sense) ->
		condition = "regs[#{rF}] & #{flag}"
		condition = "!(#{condition})" if not sense
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			if (#{condition}) {
				var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
				var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++;
				regPairs[#{rpPC}] = (h<<8) | l;
			}
		"""

	RETN = () ->
		"""
			iff1 = iff2;
			#{RET()}
		"""

	RL = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			var rltemp = #{operand.v};
			#{operand.v} = ( (#{operand.v} << 1) | (regs[#{rF}] & #{FLAG_C}) ) #{operand.trunc};
			regs[#{rF}] = ( rltemp >> 7 ) | sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	RLA = () ->
		"""
			var bytetemp = regs[#{rA}];
			regs[#{rA}] = (regs[#{rA}] << 1) | (regs[#{rF}] & #{FLAG_C});
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | (bytetemp >> 7);
		"""

	RLC = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			#{operand.v} = ( (#{operand.v} << 1) | (#{operand.v} >> 7) ) #{operand.trunc};
			regs[#{rF}] = (#{operand.v} & #{FLAG_C}) | sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	RLD = () ->
		"""
			var bytetemp =  READMEM(regPairs[#{rpHL}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			var val = (bytetemp << 4) | (regs[#{rA}] & 0x0f);
			WRITEMEM(regPairs[#{rpHL}], val);
			regs[#{rA}] = (regs[#{rA}] & 0xf0) | (bytetemp >> 4);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{rA}]];
		"""

	RLCA = () ->
		"""
			regs[#{rA}] = (regs[#{rA}] << 1) | (regs[#{rA}] >> 7);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_C | FLAG_3 | FLAG_5});
		"""

	RR = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			var rrtemp = #{operand.v};
			#{operand.v} = ( (#{operand.v} >> 1) | ( regs[#{rF}] << 7 ) ) #{operand.trunc};
			regs[#{rF}] = (rrtemp & #{FLAG_C}) | sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	RRA = () ->
		"""
			var bytetemp = regs[#{rA}];
			regs[#{rA}] = (bytetemp >> 1) | (regs[#{rF}] << 7);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | (bytetemp & #{FLAG_C});
		"""

	RRC = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			regs[#{rF}] = #{operand.v} & #{FLAG_C};
			#{operand.v} = ( (#{operand.v} >> 1) | (#{operand.v} << 7) ) #{operand.trunc};
			regs[#{rF}] |= sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	RRCA = () ->
		"""
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_C});
			regs[#{rA}] = (regs[#{rA}] >> 1) | (regs[#{rA}] << 7);
			regs[#{rF}] |= (regs[#{rA}] & #{FLAG_3 | FLAG_5});
		"""

	RRD = () ->
		"""
			var bytetemp = READMEM(regPairs[#{rpHL}]);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1);
			var val = (regs[#{rA}] << 4) | (bytetemp >> 4);
			WRITEMEM(regPairs[#{rpHL}], val);
			regs[#{rA}] = (regs[#{rA}] & 0xf0) | (bytetemp & 0x0f);
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{rA}]];
		"""

	RST = (addr) ->
		"""
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8);
			regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff);
			regPairs[#{rpPC}] = #{addr};
		"""

	SBC_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}
			var sbctemp = regs[#{rA}] - #{operand.v} - (regs[#{rF}] & #{FLAG_C});
			var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (sbctemp & 0x88) >> 1 );
			regs[#{rA}] = sbctemp;
			regs[#{rF}] = ( sbctemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]];
		"""

	SBC_HL_RR = (rp) ->
		"""
			var sub16temp = regPairs[#{rpHL}] - regPairs[#{rp}] - (regs[#{rF}] & #{FLAG_C});
			var lookup = ( (regPairs[#{rpHL}] & 0x8800) >> 11 ) | ( (regPairs[#{rp}] & 0x8800) >> 10 ) | ( (sub16temp & 0x8800) >>  9 );
			regPairs[#{rpHL}] = sub16temp;
			regs[#{rF}] = ( sub16temp & 0x10000 ? #{FLAG_C} : 0 ) | #{FLAG_N} | overflowSubTable[lookup >> 4] | (regs[#{rH}] & #{FLAG_3 | FLAG_5 | FLAG_S}) | halfcarrySubTable[lookup&0x07] | (regPairs[#{rpHL}] ? 0 : #{FLAG_Z});
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
			CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1);
		"""

	SCF = () ->
		"""
			regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | #{FLAG_C};
		"""

	SET = (bit, param) ->
		hexMask = 1 << bit
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			#{operand.v} |= #{hexMask};
			#{operand.setter}
		"""

	SHIFT = (prefix) ->
		# Fake instruction for shifted opcodes - passes control to a secondary opcode table
		"""
			opcodePrefix = '#{prefix}';
			interruptible = false;
		"""

	SLA = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			regs[#{rF}] = #{operand.v} >> 7;
			#{operand.v} = (#{operand.v} << 1) #{operand.trunc};
			regs[#{rF}] |= sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	SLL = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			regs[#{rF}] =  #{operand.v} >> 7;
			#{operand.v} = (((#{operand.v}) << 1) #{operand.trunc}) | 0x01;
			regs[#{rF}] |= sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	SRA = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			regs[#{rF}] = #{operand.v} & #{FLAG_C};
			#{operand.v} = ( (#{operand.v} & 0x80) | (#{operand.v} >> 1) ) #{operand.trunc};
			regs[#{rF}] |= sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	SRL = (param) ->
		operand = getParamBoilerplate(param, true)
		"""
			#{operand.getter}
			regs[#{rF}] =  #{operand.v} & #{FLAG_C};
			#{operand.v} >>= 1;
			regs[#{rF}] |= sz53pTable[#{operand.v}];
			#{operand.setter}
		"""

	SUB_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}
			var subtemp = regs[#{rA}] - #{operand.v};
			var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (subtemp & 0x88) >> 1 );
			regs[#{rA}] = subtemp;
			regs[#{rF}] = ( subtemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]];
		"""

	XOR_A = (param) ->
		operand = getParamBoilerplate(param)
		"""
			#{operand.getter}
			regs[#{rA}] ^= #{operand.v};
			regs[#{rF}] = sz53pTable[regs[#{rA}]];
		"""

	###
	Given a table mapping opcodes to Javascript snippets (and optionally a fallback
	table for opcodes that aren't defined in the first one), build an enormous
	switch statement for them
	###
	opcodeSwitch = (runStringTable, fallbackTable = {}, traps = []) ->
		clauses = []
		for i in [0...0x100]
			runString = runStringTable[i]
			if not runString?
				runString = fallbackTable[i]
			if runString?
				relevantTraps = ([address, action] for [address, opcode, action] in traps when opcode == i)
				trapCode = (
					"if (regPairs[#{rpPC}] == #{(address + 1) & 0xffff} && !(#{action})) break;" for [address, action] in relevantTraps
				)
				clauses.push """
					case #{i}: { var fn#{i} = function() {
						#{trapCode.join("\n")}
						#{runString}
					}; fn#{i}(); }
						break;
				"""
		"""
			switch (opcode) {
				#{clauses.join('')}
				default:
					var addr = regPairs[#{rpPC}] - 1;
					throw("Unimplemented opcode " + opcode + " in page #{runStringTable[0x100]} - PC = " + addr);
			}
		"""

	# Tables mapping opcodes to Javascript snippets
	OPCODE_RUN_STRINGS_CB = {
		0x00: RLC "B"         # RLC B
		0x01: RLC "C"         # RLC C
		0x02: RLC "D"         # RLC D
		0x03: RLC "E"         # RLC E
		0x04: RLC "H"         # RLC H
		0x05: RLC "L"         # RLC L
		0x06: RLC "(HL)"         # RLC (HL)
		0x07: RLC "A"         # RLC A
		0x08: RRC "B"         # RRC B
		0x09: RRC "C"         # RRC C
		0x0a: RRC "D"         # RRC D
		0x0b: RRC "E"         # RRC E
		0x0c: RRC "H"         # RRC H
		0x0d: RRC "L"         # RRC L
		0x0e: RRC "(HL)"         # RRC (HL)
		0x0f: RRC "A"         # RRC A
		0x10: RL 'B'         # RL B
		0x11: RL 'C'         # RL C
		0x12: RL 'D'         # RL D
		0x13: RL 'E'         # RL E
		0x14: RL 'H'         # RL H
		0x15: RL 'L'         # RL L
		0x16: RL '(HL)'         # RL (HL)
		0x17: RL 'A'         # RL A
		0x18: RR 'B'         # RR B
		0x19: RR 'C'         # RR C
		0x1a: RR 'D'         # RR D
		0x1b: RR 'E'         # RR E
		0x1c: RR 'H'         # RR H
		0x1d: RR 'L'         # RR L
		0x1e: RR '(HL)'         # RR (HL)
		0x1f: RR 'A'         # RR A
		0x20: SLA 'B'         # SLA B
		0x21: SLA 'C'         # SLA C
		0x22: SLA 'D'         # SLA D
		0x23: SLA 'E'         # SLA E
		0x24: SLA 'H'         # SLA H
		0x25: SLA 'L'         # SLA L
		0x26: SLA '(HL)'         # SLA (HL)
		0x27: SLA 'A'         # SLA A
		0x28: SRA 'B'         # SRA B
		0x29: SRA 'C'         # SRA C
		0x2a: SRA 'D'         # SRA D
		0x2b: SRA 'E'         # SRA E
		0x2c: SRA 'H'         # SRA H
		0x2d: SRA 'L'         # SRA L
		0x2e: SRA '(HL)'         # SRA (HL)
		0x2f: SRA 'A'         # SRA A
		0x30: SLL 'B'         # SLL B
		0x31: SLL 'C'         # SLL C
		0x32: SLL 'D'         # SLL D
		0x33: SLL 'E'         # SLL E
		0x34: SLL 'H'         # SLL H
		0x35: SLL 'L'         # SLL L
		0x36: SLL '(HL)'         # SLL (HL)
		0x37: SLL 'A'         # SLL A
		0x38: SRL 'B'         # SRL B
		0x39: SRL 'C'         # SRL C
		0x3a: SRL 'D'         # SRL D
		0x3b: SRL 'E'         # SRL E
		0x3c: SRL 'H'         # SRL H
		0x3d: SRL 'L'         # SRL L
		0x3e: SRL '(HL)'         # SRL (HL)
		0x3f: SRL 'A'         # SRL A
		0x40: BIT_N_R(0, rB)        # BIT 0,B
		0x41: BIT_N_R(0, rC)        # BIT 0,C
		0x42: BIT_N_R(0, rD)        # BIT 0,D
		0x43: BIT_N_R(0, rE)        # BIT 0,E
		0x44: BIT_N_R(0, rH)        # BIT 0,H
		0x45: BIT_N_R(0, rL)        # BIT 0,L
		0x46: BIT_N_iHLi(0)        # BIT 0,(HL)
		0x47: BIT_N_R(0, rA)        # BIT 0,A
		0x48: BIT_N_R(1, rB)        # BIT 1,B
		0x49: BIT_N_R(1, rC)        # BIT 1,C
		0x4A: BIT_N_R(1, rD)        # BIT 1,D
		0x4B: BIT_N_R(1, rE)        # BIT 1,E
		0x4C: BIT_N_R(1, rH)        # BIT 1,H
		0x4D: BIT_N_R(1, rL)        # BIT 1,L
		0x4E: BIT_N_iHLi(1)        # BIT 1,(HL)
		0x4F: BIT_N_R(1, rA)        # BIT 1,A
		0x50: BIT_N_R(2, rB)        # BIT 2,B
		0x51: BIT_N_R(2, rC)        # BIT 2,C
		0x52: BIT_N_R(2, rD)        # BIT 2,D
		0x53: BIT_N_R(2, rE)        # BIT 2,E
		0x54: BIT_N_R(2, rH)        # BIT 2,H
		0x55: BIT_N_R(2, rL)        # BIT 2,L
		0x56: BIT_N_iHLi(2)        # BIT 2,(HL)
		0x57: BIT_N_R(2, rA)        # BIT 2,A
		0x58: BIT_N_R(3, rB)        # BIT 3,B
		0x59: BIT_N_R(3, rC)        # BIT 3,C
		0x5A: BIT_N_R(3, rD)        # BIT 3,D
		0x5B: BIT_N_R(3, rE)        # BIT 3,E
		0x5C: BIT_N_R(3, rH)        # BIT 3,H
		0x5D: BIT_N_R(3, rL)        # BIT 3,L
		0x5E: BIT_N_iHLi(3)        # BIT 3,(HL)
		0x5F: BIT_N_R(3, rA)        # BIT 3,A
		0x60: BIT_N_R(4, rB)        # BIT 4,B
		0x61: BIT_N_R(4, rC)        # BIT 4,C
		0x62: BIT_N_R(4, rD)        # BIT 4,D
		0x63: BIT_N_R(4, rE)        # BIT 4,E
		0x64: BIT_N_R(4, rH)        # BIT 4,H
		0x65: BIT_N_R(4, rL)        # BIT 4,L
		0x66: BIT_N_iHLi(4)        # BIT 4,(HL)
		0x67: BIT_N_R(4, rA)        # BIT 4,A
		0x68: BIT_N_R(5, rB)        # BIT 5,B
		0x69: BIT_N_R(5, rC)        # BIT 5,C
		0x6A: BIT_N_R(5, rD)        # BIT 5,D
		0x6B: BIT_N_R(5, rE)        # BIT 5,E
		0x6C: BIT_N_R(5, rH)        # BIT 5,H
		0x6D: BIT_N_R(5, rL)        # BIT 5,L
		0x6E: BIT_N_iHLi(5)        # BIT 5,(HL)
		0x6F: BIT_N_R(5, rA)        # BIT 5,A
		0x70: BIT_N_R(6, rB)        # BIT 6,B
		0x71: BIT_N_R(6, rC)        # BIT 6,C
		0x72: BIT_N_R(6, rD)        # BIT 6,D
		0x73: BIT_N_R(6, rE)        # BIT 6,E
		0x74: BIT_N_R(6, rH)        # BIT 6,H
		0x75: BIT_N_R(6, rL)        # BIT 6,L
		0x76: BIT_N_iHLi(6)        # BIT 6,(HL)
		0x77: BIT_N_R(6, rA)        # BIT 6,A
		0x78: BIT_N_R(7, rB)        # BIT 7,B
		0x79: BIT_N_R(7, rC)        # BIT 7,C
		0x7A: BIT_N_R(7, rD)        # BIT 7,D
		0x7B: BIT_N_R(7, rE)        # BIT 7,E
		0x7C: BIT_N_R(7, rH)        # BIT 7,H
		0x7D: BIT_N_R(7, rL)        # BIT 7,L
		0x7E: BIT_N_iHLi(7)        # BIT 7,(HL)
		0x7F: BIT_N_R(7, rA)        # BIT 7,A
		0x80: RES 0, 'B'         # RES 0,B
		0x81: RES 0, 'C'         # RES 0,C
		0x82: RES 0, 'D'         # RES 0,D
		0x83: RES 0, 'E'         # RES 0,E
		0x84: RES 0, 'H'         # RES 0,H
		0x85: RES 0, 'L'         # RES 0,L
		0x86: RES 0, '(HL)'         # RES 0,(HL)
		0x87: RES 0, 'A'         # RES 0,A
		0x88: RES 1, 'B'         # RES 1,B
		0x89: RES 1, 'C'         # RES 1,C
		0x8A: RES 1, 'D'         # RES 1,D
		0x8B: RES 1, 'E'         # RES 1,E
		0x8C: RES 1, 'H'         # RES 1,H
		0x8D: RES 1, 'L'         # RES 1,L
		0x8E: RES 1, '(HL)'         # RES 1,(HL)
		0x8F: RES 1, 'A'         # RES 1,A
		0x90: RES 2, 'B'         # RES 2,B
		0x91: RES 2, 'C'         # RES 2,C
		0x92: RES 2, 'D'         # RES 2,D
		0x93: RES 2, 'E'         # RES 2,E
		0x94: RES 2, 'H'         # RES 2,H
		0x95: RES 2, 'L'         # RES 2,L
		0x96: RES 2, '(HL)'         # RES 2,(HL)
		0x97: RES 2, 'A'         # RES 2,A
		0x98: RES 3, 'B'         # RES 3,B
		0x99: RES 3, 'C'         # RES 3,C
		0x9A: RES 3, 'D'         # RES 3,D
		0x9B: RES 3, 'E'         # RES 3,E
		0x9C: RES 3, 'H'         # RES 3,H
		0x9D: RES 3, 'L'         # RES 3,L
		0x9E: RES 3, '(HL)'         # RES 3,(HL)
		0x9F: RES 3, 'A'         # RES 3,A
		0xA0: RES 4, 'B'         # RES 4,B
		0xA1: RES 4, 'C'         # RES 4,C
		0xA2: RES 4, 'D'         # RES 4,D
		0xA3: RES 4, 'E'         # RES 4,E
		0xA4: RES 4, 'H'         # RES 4,H
		0xA5: RES 4, 'L'         # RES 4,L
		0xA6: RES 4, '(HL)'         # RES 4,(HL)
		0xA7: RES 4, 'A'         # RES 4,A
		0xA8: RES 5, 'B'         # RES 5,B
		0xA9: RES 5, 'C'         # RES 5,C
		0xAA: RES 5, 'D'         # RES 5,D
		0xAB: RES 5, 'E'         # RES 5,E
		0xAC: RES 5, 'H'         # RES 5,H
		0xAD: RES 5, 'L'         # RES 5,L
		0xAE: RES 5, '(HL)'         # RES 5,(HL)
		0xAF: RES 5, 'A'         # RES 5,A
		0xB0: RES 6, 'B'         # RES 6,B
		0xB1: RES 6, 'C'         # RES 6,C
		0xB2: RES 6, 'D'         # RES 6,D
		0xB3: RES 6, 'E'         # RES 6,E
		0xB4: RES 6, 'H'         # RES 6,H
		0xB5: RES 6, 'L'         # RES 6,L
		0xB6: RES 6, '(HL)'         # RES 6,(HL)
		0xB7: RES 6, 'A'         # RES 6,A
		0xB8: RES 7, 'B'         # RES 7,B
		0xB9: RES 7, 'C'         # RES 7,C
		0xBA: RES 7, 'D'         # RES 7,D
		0xBB: RES 7, 'E'         # RES 7,E
		0xBC: RES 7, 'H'         # RES 7,H
		0xBD: RES 7, 'L'         # RES 7,L
		0xBE: RES 7, '(HL)'         # RES 7,(HL)
		0xBF: RES 7, 'A'         # RES 7,A
		0xC0: SET 0, 'B'         # SET 0,B
		0xC1: SET 0, 'C'         # SET 0,C
		0xC2: SET 0, 'D'         # SET 0,D
		0xC3: SET 0, 'E'         # SET 0,E
		0xC4: SET 0, 'H'         # SET 0,H
		0xC5: SET 0, 'L'         # SET 0,L
		0xC6: SET 0, '(HL)'         # SET 0,(HL)
		0xC7: SET 0, 'A'         # SET 0,A
		0xC8: SET 1, 'B'         # SET 1,B
		0xC9: SET 1, 'C'         # SET 1,C
		0xCA: SET 1, 'D'         # SET 1,D
		0xCB: SET 1, 'E'         # SET 1,E
		0xCC: SET 1, 'H'         # SET 1,H
		0xCD: SET 1, 'L'         # SET 1,L
		0xCE: SET 1, '(HL)'         # SET 1,(HL)
		0xCF: SET 1, 'A'         # SET 1,A
		0xD0: SET 2, 'B'         # SET 2,B
		0xD1: SET 2, 'C'         # SET 2,C
		0xD2: SET 2, 'D'         # SET 2,D
		0xD3: SET 2, 'E'         # SET 2,E
		0xD4: SET 2, 'H'         # SET 2,H
		0xD5: SET 2, 'L'         # SET 2,L
		0xD6: SET 2, '(HL)'         # SET 2,(HL)
		0xD7: SET 2, 'A'         # SET 2,A
		0xD8: SET 3, 'B'         # SET 3,B
		0xD9: SET 3, 'C'         # SET 3,C
		0xDA: SET 3, 'D'         # SET 3,D
		0xDB: SET 3, 'E'         # SET 3,E
		0xDC: SET 3, 'H'         # SET 3,H
		0xDD: SET 3, 'L'         # SET 3,L
		0xDE: SET 3, '(HL)'         # SET 3,(HL)
		0xDF: SET 3, 'A'         # SET 3,A
		0xE0: SET 4, 'B'         # SET 4,B
		0xE1: SET 4, 'C'         # SET 4,C
		0xE2: SET 4, 'D'         # SET 4,D
		0xE3: SET 4, 'E'         # SET 4,E
		0xE4: SET 4, 'H'         # SET 4,H
		0xE5: SET 4, 'L'         # SET 4,L
		0xE6: SET 4, '(HL)'         # SET 4,(HL)
		0xE7: SET 4, 'A'         # SET 4,A
		0xE8: SET 5, 'B'         # SET 5,B
		0xE9: SET 5, 'C'         # SET 5,C
		0xEA: SET 5, 'D'         # SET 5,D
		0xEB: SET 5, 'E'         # SET 5,E
		0xEC: SET 5, 'H'         # SET 5,H
		0xED: SET 5, 'L'         # SET 5,L
		0xEE: SET 5, '(HL)'         # SET 5,(HL)
		0xEF: SET 5, 'A'         # SET 5,A
		0xF0: SET 6, 'B'         # SET 6,B
		0xF1: SET 6, 'C'         # SET 6,C
		0xF2: SET 6, 'D'         # SET 6,D
		0xF3: SET 6, 'E'         # SET 6,E
		0xF4: SET 6, 'H'         # SET 6,H
		0xF5: SET 6, 'L'         # SET 6,L
		0xF6: SET 6, '(HL)'         # SET 6,(HL)
		0xF7: SET 6, 'A'         # SET 6,A
		0xF8: SET 7, 'B'         # SET 7,B
		0xF9: SET 7, 'C'         # SET 7,C
		0xFA: SET 7, 'D'         # SET 7,D
		0xFB: SET 7, 'E'         # SET 7,E
		0xFC: SET 7, 'H'         # SET 7,H
		0xFD: SET 7, 'L'         # SET 7,L
		0xFE: SET 7, '(HL)'         # SET 7,(HL)
		0xFF: SET 7, 'A'         # SET 7,A
		0x100: 'cb'
	}

	# Generate the opcode runner lookup table for either the DDCB or FDCB set
	generateddfdcbOpcodeSet = (prefix) ->
		if prefix == 'DDCB'
			rp = rpIX
			rh = rIXH
			rl = rIXL

			rpn = 'IX'
			rhn = 'IXH'
			rln = 'IXL'
		else # prefix == 'FDCB'
			rp = rpIY
			rh = rIYH
			rl = rIYL

			rpn = 'IY'
			rhn = 'IYH'
			rln = 'IYL'
		return {
			0x00: LDSHIFTOP('B', RLC, rp)        # LD B,RLC (REGISTER+dd)
			0x01: LDSHIFTOP('C', RLC, rp)        # LD C,RLC (REGISTER+dd)
			0x02: LDSHIFTOP('D', RLC, rp)        # LD D,RLC (REGISTER+dd)
			0x03: LDSHIFTOP('E', RLC, rp)        # LD E,RLC (REGISTER+dd)
			0x04: LDSHIFTOP('H', RLC, rp)        # LD H,RLC (REGISTER+dd)
			0x05: LDSHIFTOP('L', RLC, rp)        # LD L,RLC (REGISTER+dd)
			0x06: RLC "(#{rpn}+nn)"         # RLC (IX+nn)
			0x07: LDSHIFTOP('A', RLC, rp)        # LD A,RLC (REGISTER+dd)
			0x08: LDSHIFTOP('B', RRC, rp)        # LD B,RRC (REGISTER+dd)
			0x09: LDSHIFTOP('C', RRC, rp)        # LD C,RRC (REGISTER+dd)
			0x0A: LDSHIFTOP('D', RRC, rp)        # LD D,RRC (REGISTER+dd)
			0x0B: LDSHIFTOP('E', RRC, rp)        # LD E,RRC (REGISTER+dd)
			0x0C: LDSHIFTOP('H', RRC, rp)        # LD H,RRC (REGISTER+dd)
			0x0D: LDSHIFTOP('L', RRC, rp)        # LD L,RRC (REGISTER+dd)
			0x0E: RRC "(#{rpn}+nn)"         # RRC (IX+nn)
			0x0F: LDSHIFTOP('A', RRC, rp)        # LD A,RRC (REGISTER+dd)
			0x10: LDSHIFTOP('B', RL, rp)        # LD B,RL (REGISTER+dd)
			0x11: LDSHIFTOP('C', RL, rp)        # LD C,RL (REGISTER+dd)
			0x12: LDSHIFTOP('D', RL, rp)        # LD D,RL (REGISTER+dd)
			0x13: LDSHIFTOP('E', RL, rp)        # LD E,RL (REGISTER+dd)
			0x14: LDSHIFTOP('H', RL, rp)        # LD H,RL (REGISTER+dd)
			0x15: LDSHIFTOP('L', RL, rp)        # LD L,RL (REGISTER+dd)
			0x16: RL "(#{rpn}+nn)"         # RL (IX+nn)
			0x17: LDSHIFTOP('A', RL, rp)        # LD A,RL (REGISTER+dd)
			0x18: LDSHIFTOP('B', RR, rp)        # LD B,RR (REGISTER+dd)
			0x19: LDSHIFTOP('C', RR, rp)        # LD C,RR (REGISTER+dd)
			0x1A: LDSHIFTOP('D', RR, rp)        # LD D,RR (REGISTER+dd)
			0x1B: LDSHIFTOP('E', RR, rp)        # LD E,RR (REGISTER+dd)
			0x1C: LDSHIFTOP('H', RR, rp)        # LD H,RR (REGISTER+dd)
			0x1D: LDSHIFTOP('L', RR, rp)        # LD L,RR (REGISTER+dd)
			0x1E: RR "(#{rpn}+nn)"         # RR (IX+nn)
			0x1F: LDSHIFTOP('A', RR, rp)        # LD A,RR (REGISTER+dd)
			0x20: LDSHIFTOP('B', SLA, rp)        # LD B,SLA (REGISTER+dd)
			0x21: LDSHIFTOP('C', SLA, rp)        # LD C,SLA (REGISTER+dd)
			0x22: LDSHIFTOP('D', SLA, rp)        # LD D,SLA (REGISTER+dd)
			0x23: LDSHIFTOP('E', SLA, rp)        # LD E,SLA (REGISTER+dd)
			0x24: LDSHIFTOP('H', SLA, rp)        # LD H,SLA (REGISTER+dd)
			0x25: LDSHIFTOP('L', SLA, rp)        # LD L,SLA (REGISTER+dd)
			0x26: SLA "(#{rpn}+nn)"         # SLA (IX+nn)
			0x27: LDSHIFTOP('A', SLA, rp)        # LD A,SLA (REGISTER+dd)
			0x28: LDSHIFTOP('B', SRA, rp)        # LD B,SRA (REGISTER+dd)
			0x29: LDSHIFTOP('C', SRA, rp)        # LD C,SRA (REGISTER+dd)
			0x2A: LDSHIFTOP('D', SRA, rp)        # LD D,SRA (REGISTER+dd)
			0x2B: LDSHIFTOP('E', SRA, rp)        # LD E,SRA (REGISTER+dd)
			0x2C: LDSHIFTOP('H', SRA, rp)        # LD H,SRA (REGISTER+dd)
			0x2D: LDSHIFTOP('L', SRA, rp)        # LD L,SRA (REGISTER+dd)
			0x2E: SRA "(#{rpn}+nn)"         # SRA (IX+nn)
			0x2F: LDSHIFTOP('A', SRA, rp)        # LD A,SRA (REGISTER+dd)
			0x30: LDSHIFTOP('B', SLL, rp)        # LD B,SLL (REGISTER+dd)
			0x31: LDSHIFTOP('C', SLL, rp)        # LD C,SLL (REGISTER+dd)
			0x32: LDSHIFTOP('D', SLL, rp)        # LD D,SLL (REGISTER+dd)
			0x33: LDSHIFTOP('E', SLL, rp)        # LD E,SLL (REGISTER+dd)
			0x34: LDSHIFTOP('H', SLL, rp)        # LD H,SLL (REGISTER+dd)
			0x35: LDSHIFTOP('L', SLL, rp)        # LD L,SLL (REGISTER+dd)
			0x36: SLL "(#{rpn}+nn)"         # SLL (IX+nn)
			0x37: LDSHIFTOP('A', SLL, rp)        # LD A,SLL (REGISTER+dd)
			0x38: LDSHIFTOP('B', SRL, rp)        # LD B,SRL (REGISTER+dd)
			0x39: LDSHIFTOP('C', SRL, rp)        # LD C,SRL (REGISTER+dd)
			0x3A: LDSHIFTOP('D', SRL, rp)        # LD D,SRL (REGISTER+dd)
			0x3B: LDSHIFTOP('E', SRL, rp)        # LD E,SRL (REGISTER+dd)
			0x3C: LDSHIFTOP('H', SRL, rp)        # LD H,SRL (REGISTER+dd)
			0x3D: LDSHIFTOP('L', SRL, rp)        # LD L,SRL (REGISTER+dd)
			0x3E: SRL "(#{rpn}+nn)"         # SRL (IX+nn)
			0x3F: LDSHIFTOP('A', SRL, rp)        # LD A,SRL (REGISTER+dd)
			0x40: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x41: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x42: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x43: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x44: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x45: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x46: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x47: BIT_N_iRRpNNi(0, rp)         # BIT 0,(IX+nn)
			0x48: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x49: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4A: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4B: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4C: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4D: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4E: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x4F: BIT_N_iRRpNNi(1, rp)         # BIT 1,(IX+nn)
			0x50: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x51: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x52: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x53: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x54: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x55: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x56: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x57: BIT_N_iRRpNNi(2, rp)         # BIT 2,(IX+nn)
			0x58: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x59: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5A: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5B: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5C: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5D: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5E: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x5F: BIT_N_iRRpNNi(3, rp)         # BIT 3,(IX+nn)
			0x60: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x61: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x62: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x63: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x64: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x65: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x66: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x67: BIT_N_iRRpNNi(4, rp)         # BIT 4,(IX+nn)
			0x68: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x69: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6A: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6B: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6C: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6D: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6E: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x6F: BIT_N_iRRpNNi(5, rp)         # BIT 5,(IX+nn)
			0x70: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x71: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x72: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x73: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x74: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x75: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x76: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x77: BIT_N_iRRpNNi(6, rp)         # BIT 6,(IX+nn)
			0x78: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x79: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7A: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7B: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7C: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7D: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7E: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x7F: BIT_N_iRRpNNi(7, rp)         # BIT 7,(IX+nn)
			0x80: LDBITOP('B', RES, 0, rp)        # LD B,RES 0,(IX+dd)
			0x81: LDBITOP('C', RES, 0, rp)        # LD C,RES 0,(IX+dd)
			0x82: LDBITOP('D', RES, 0, rp)        # LD D,RES 0,(IX+dd)
			0x83: LDBITOP('E', RES, 0, rp)        # LD E,RES 0,(IX+dd)
			0x84: LDBITOP('H', RES, 0, rp)        # LD H,RES 0,(IX+dd)
			0x85: LDBITOP('L', RES, 0, rp)        # LD L,RES 0,(IX+dd)
			0x86: RES 0, "(#{rpn}+nn)"         # RES 0,(IX+nn)
			0x87: LDBITOP('A', RES, 0, rp)        # LD A,RES 0,(IX+dd)
			0x88: LDBITOP('B', RES, 1, rp)        # LD B,RES 1,(IX+dd)
			0x89: LDBITOP('C', RES, 1, rp)        # LD C,RES 1,(IX+dd)
			0x8A: LDBITOP('D', RES, 1, rp)        # LD D,RES 1,(IX+dd)
			0x8B: LDBITOP('E', RES, 1, rp)        # LD E,RES 1,(IX+dd)
			0x8C: LDBITOP('H', RES, 1, rp)        # LD H,RES 1,(IX+dd)
			0x8D: LDBITOP('L', RES, 1, rp)        # LD L,RES 1,(IX+dd)
			0x8E: RES 1, "(#{rpn}+nn)"         # RES 1,(IX+nn)
			0x8F: LDBITOP('A', RES, 1, rp)        # LD A,RES 1,(IX+dd)
			0x90: LDBITOP('B', RES, 2, rp)        # LD B,RES 2,(IX+dd)
			0x91: LDBITOP('C', RES, 2, rp)        # LD C,RES 2,(IX+dd)
			0x92: LDBITOP('D', RES, 2, rp)        # LD D,RES 2,(IX+dd)
			0x93: LDBITOP('E', RES, 2, rp)        # LD E,RES 2,(IX+dd)
			0x94: LDBITOP('H', RES, 2, rp)        # LD H,RES 2,(IX+dd)
			0x95: LDBITOP('L', RES, 2, rp)        # LD L,RES 2,(IX+dd)
			0x96: RES 2, "(#{rpn}+nn)"         # RES 2,(IX+nn)
			0x97: LDBITOP('A', RES, 2, rp)        # LD A,RES 2,(IX+dd)
			0x98: LDBITOP('B', RES, 3, rp)        # LD B,RES 3,(IX+dd)
			0x99: LDBITOP('C', RES, 3, rp)        # LD C,RES 3,(IX+dd)
			0x9A: LDBITOP('D', RES, 3, rp)        # LD D,RES 3,(IX+dd)
			0x9B: LDBITOP('E', RES, 3, rp)        # LD E,RES 3,(IX+dd)
			0x9C: LDBITOP('H', RES, 3, rp)        # LD H,RES 3,(IX+dd)
			0x9D: LDBITOP('L', RES, 3, rp)        # LD L,RES 3,(IX+dd)
			0x9E: RES 3, "(#{rpn}+nn)"         # RES 3,(IX+nn)
			0x9F: LDBITOP('A', RES, 3, rp)        # LD A,RES 3,(IX+dd)
			0xA0: LDBITOP('B', RES, 4, rp)        # LD B,RES 4,(IX+dd)
			0xA1: LDBITOP('C', RES, 4, rp)        # LD C,RES 4,(IX+dd)
			0xA2: LDBITOP('D', RES, 4, rp)        # LD D,RES 4,(IX+dd)
			0xA3: LDBITOP('E', RES, 4, rp)        # LD E,RES 4,(IX+dd)
			0xA4: LDBITOP('H', RES, 4, rp)        # LD H,RES 4,(IX+dd)
			0xA5: LDBITOP('L', RES, 4, rp)        # LD L,RES 4,(IX+dd)
			0xA6: RES 4, "(#{rpn}+nn)"         # RES 4,(IX+nn)
			0xA7: LDBITOP('A', RES, 4, rp)        # LD A,RES 4,(IX+dd)
			0xA8: LDBITOP('B', RES, 5, rp)        # LD B,RES 5,(IX+dd)
			0xA9: LDBITOP('C', RES, 5, rp)        # LD C,RES 5,(IX+dd)
			0xAA: LDBITOP('D', RES, 5, rp)        # LD D,RES 5,(IX+dd)
			0xAB: LDBITOP('E', RES, 5, rp)        # LD E,RES 5,(IX+dd)
			0xAC: LDBITOP('H', RES, 5, rp)        # LD H,RES 5,(IX+dd)
			0xAD: LDBITOP('L', RES, 5, rp)        # LD L,RES 5,(IX+dd)
			0xAE: RES 5, "(#{rpn}+nn)"         # RES 5,(IX+nn)
			0xAF: LDBITOP('A', RES, 5, rp)        # LD A,RES 5,(IX+dd)
			0xB0: LDBITOP('B', RES, 6, rp)        # LD B,RES 6,(IX+dd)
			0xB1: LDBITOP('C', RES, 6, rp)        # LD C,RES 6,(IX+dd)
			0xB2: LDBITOP('D', RES, 6, rp)        # LD D,RES 6,(IX+dd)
			0xB3: LDBITOP('E', RES, 6, rp)        # LD E,RES 6,(IX+dd)
			0xB4: LDBITOP('H', RES, 6, rp)        # LD H,RES 6,(IX+dd)
			0xB5: LDBITOP('L', RES, 6, rp)        # LD L,RES 6,(IX+dd)
			0xB6: RES 6, "(#{rpn}+nn)"         # RES 6,(IX+nn)
			0xB7: LDBITOP('A', RES, 6, rp)        # LD A,RES 6,(IX+dd)
			0xB8: LDBITOP('B', RES, 7, rp)        # LD B,RES 7,(IX+dd)
			0xB9: LDBITOP('C', RES, 7, rp)        # LD C,RES 7,(IX+dd)
			0xBA: LDBITOP('D', RES, 7, rp)        # LD D,RES 7,(IX+dd)
			0xBB: LDBITOP('E', RES, 7, rp)        # LD E,RES 7,(IX+dd)
			0xBC: LDBITOP('H', RES, 7, rp)        # LD H,RES 7,(IX+dd)
			0xBD: LDBITOP('L', RES, 7, rp)        # LD L,RES 7,(IX+dd)
			0xBE: RES 7, "(#{rpn}+nn)"         # RES 7,(IX+nn)
			0xBF: LDBITOP('A', RES, 7, rp)        # LD A,RES 7,(IX+dd)
			0xC0: LDBITOP('B', SET, 0, rp)        # LD B,SET 0,(IX+dd)
			0xC1: LDBITOP('C', SET, 0, rp)        # LD C,SET 0,(IX+dd)
			0xC2: LDBITOP('D', SET, 0, rp)        # LD D,SET 0,(IX+dd)
			0xC3: LDBITOP('E', SET, 0, rp)        # LD E,SET 0,(IX+dd)
			0xC4: LDBITOP('H', SET, 0, rp)        # LD H,SET 0,(IX+dd)
			0xC5: LDBITOP('L', SET, 0, rp)        # LD L,SET 0,(IX+dd)
			0xC6: SET 0, "(#{rpn}+nn)"         # SET 0,(IX+nn)
			0xC7: LDBITOP('A', SET, 0, rp)        # LD A,SET 0,(IX+dd)
			0xC8: LDBITOP('B', SET, 1, rp)        # LD B,SET 1,(IX+dd)
			0xC9: LDBITOP('C', SET, 1, rp)        # LD C,SET 1,(IX+dd)
			0xCA: LDBITOP('D', SET, 1, rp)        # LD D,SET 1,(IX+dd)
			0xCB: LDBITOP('E', SET, 1, rp)        # LD E,SET 1,(IX+dd)
			0xCC: LDBITOP('H', SET, 1, rp)        # LD H,SET 1,(IX+dd)
			0xCD: LDBITOP('L', SET, 1, rp)        # LD L,SET 1,(IX+dd)
			0xCE: SET 1, "(#{rpn}+nn)"         # SET 1,(IX+nn)
			0xCF: LDBITOP('A', SET, 1, rp)        # LD A,SET 1,(IX+dd)
			0xD0: LDBITOP('B', SET, 2, rp)        # LD B,SET 2,(IX+dd)
			0xD1: LDBITOP('C', SET, 2, rp)        # LD C,SET 2,(IX+dd)
			0xD2: LDBITOP('D', SET, 2, rp)        # LD D,SET 2,(IX+dd)
			0xD3: LDBITOP('E', SET, 2, rp)        # LD E,SET 2,(IX+dd)
			0xD4: LDBITOP('H', SET, 2, rp)        # LD H,SET 2,(IX+dd)
			0xD5: LDBITOP('L', SET, 2, rp)        # LD L,SET 2,(IX+dd)
			0xD6: SET 2, "(#{rpn}+nn)"         # SET 2,(IX+nn)
			0xD7: LDBITOP('A', SET, 2, rp)        # LD A,SET 2,(IX+dd)
			0xD8: LDBITOP('B', SET, 3, rp)        # LD B,SET 3,(IX+dd)
			0xD9: LDBITOP('C', SET, 3, rp)        # LD C,SET 3,(IX+dd)
			0xDA: LDBITOP('D', SET, 3, rp)        # LD D,SET 3,(IX+dd)
			0xDB: LDBITOP('E', SET, 3, rp)        # LD E,SET 3,(IX+dd)
			0xDC: LDBITOP('H', SET, 3, rp)        # LD H,SET 3,(IX+dd)
			0xDD: LDBITOP('L', SET, 3, rp)        # LD L,SET 3,(IX+dd)
			0xDE: SET 3, "(#{rpn}+nn)"         # SET 3,(IX+nn)
			0xDF: LDBITOP('A', SET, 3, rp)        # LD A,SET 3,(IX+dd)
			0xE0: LDBITOP('B', SET, 4, rp)        # LD B,SET 4,(IX+dd)
			0xE1: LDBITOP('C', SET, 4, rp)        # LD C,SET 4,(IX+dd)
			0xE2: LDBITOP('D', SET, 4, rp)        # LD D,SET 4,(IX+dd)
			0xE3: LDBITOP('E', SET, 4, rp)        # LD E,SET 4,(IX+dd)
			0xE4: LDBITOP('H', SET, 4, rp)        # LD H,SET 4,(IX+dd)
			0xE5: LDBITOP('L', SET, 4, rp)        # LD L,SET 4,(IX+dd)
			0xE6: SET 4, "(#{rpn}+nn)"         # SET 4,(IX+nn)
			0xE7: LDBITOP('A', SET, 4, rp)        # LD A,SET 4,(IX+dd)
			0xE8: LDBITOP('B', SET, 5, rp)        # LD B,SET 5,(IX+dd)
			0xE9: LDBITOP('C', SET, 5, rp)        # LD C,SET 5,(IX+dd)
			0xEA: LDBITOP('D', SET, 5, rp)        # LD D,SET 5,(IX+dd)
			0xEB: LDBITOP('E', SET, 5, rp)        # LD E,SET 5,(IX+dd)
			0xEC: LDBITOP('H', SET, 5, rp)        # LD H,SET 5,(IX+dd)
			0xED: LDBITOP('L', SET, 5, rp)        # LD L,SET 5,(IX+dd)
			0xEE: SET 5, "(#{rpn}+nn)"         # SET 5,(IX+nn)
			0xEF: LDBITOP('A', SET, 5, rp)        # LD A,SET 5,(IX+dd)
			0xF0: LDBITOP('B', SET, 6, rp)        # LD B,SET 6,(IX+dd)
			0xF1: LDBITOP('C', SET, 6, rp)        # LD C,SET 6,(IX+dd)
			0xF2: LDBITOP('D', SET, 6, rp)        # LD D,SET 6,(IX+dd)
			0xF3: LDBITOP('E', SET, 6, rp)        # LD E,SET 6,(IX+dd)
			0xF4: LDBITOP('H', SET, 6, rp)        # LD H,SET 6,(IX+dd)
			0xF5: LDBITOP('L', SET, 6, rp)        # LD L,SET 6,(IX+dd)
			0xF6: SET 6, "(#{rpn}+nn)"         # SET 6,(IX+nn)
			0xF7: LDBITOP('A', SET, 6, rp)        # LD A,SET 6,(IX+dd)
			0xF8: LDBITOP('B', SET, 7, rp)        # LD B,SET 7,(IX+dd)
			0xF9: LDBITOP('C', SET, 7, rp)        # LD C,SET 7,(IX+dd)
			0xFA: LDBITOP('D', SET, 7, rp)        # LD D,SET 7,(IX+dd)
			0xFB: LDBITOP('E', SET, 7, rp)        # LD E,SET 7,(IX+dd)
			0xFC: LDBITOP('H', SET, 7, rp)        # LD H,SET 7,(IX+dd)
			0xFD: LDBITOP('L', SET, 7, rp)        # LD L,SET 7,(IX+dd)
			0xFE: SET 7, "(#{rpn}+nn)"         # SET 7,(IX+nn)
			0xFF: LDBITOP('A', SET, 7, rp)        # LD A,SET 7,(IX+dd)
			0x100: 'ddcb'
		}

	OPCODE_RUN_STRINGS_DDCB = generateddfdcbOpcodeSet('DDCB')
	OPCODE_RUN_STRINGS_FDCB = generateddfdcbOpcodeSet('FDCB')

	# Generate the opcode runner lookup table for either the DD or FD set, acting on the
	# specified register pair (IX or IY)
	generateddfdOpcodeSet = (prefix) ->
		if prefix == 'DD'
			rp = rpIX
			rh = rIXH
			rl = rIXL

			rpn = 'IX'
			rhn = 'IXH'
			rln = 'IXL'
		else # prefix == 'FD'
			rp = rpIY
			rh = rIYH
			rl = rIYL

			rpn = 'IY'
			rhn = 'IYH'
			rln = 'IYL'
		return {
			0x09: ADD_RR_RR(rp, rpBC)         # ADD IX,BC

			0x19: ADD_RR_RR(rp, rpDE)         # ADD IX,DE

			0x21: LD_RR_NN(rp)         # LD IX,nnnn
			0x22: LD_iNNi_RR(rp)         # LD (nnnn),IX
			0x23: INC_RR(rp)         # INC IX
			0x24: INC rhn          # INC IXh
			0x25: DEC rhn          # DEC IXh
			0x26: LD_R_N(rh)         # LD IXh, nn

			0x29: ADD_RR_RR(rp, rp)         # ADD IX,IX
			0x2A: LD_RR_iNNi(rp)         # LD IX,(nnnn)
			0x2B: DEC_RR(rp)         # DEC IX
			0x2C: INC rln          # INC IXl
			0x2D: DEC rln          # DEC IXl
			0x2E: LD_R_N(rl)         # LD IXl, nn

			0x34: INC "(#{rpn}+nn)"         # INC (IX+nn)
			0x35: DEC "(#{rpn}+nn)"         # DEC (IX+nn)
			0x36: LD_iRRpNNi_N(rp)         # LD (IX+nn),nn

			0x39: ADD_RR_RR(rp, rpSP)         # ADD IX,SP

			0x44: LD_R_R(rB, rh)         # LD B,IXh
			0x45: LD_R_R(rB, rl)         # LD B,IXl
			0x46: LD_R_iRRpNNi(rB, rp)         # LD B,(IX+nn)

			0x4C: LD_R_R(rC, rh)         # LD C,IXh
			0x4D: LD_R_R(rC, rl)         # LD C,IXl
			0x4E: LD_R_iRRpNNi(rC, rp)         # LD C,(IX+nn)

			0x54: LD_R_R(rD, rh)         # LD D,IXh
			0x55: LD_R_R(rD, rl)         # LD D,IXl
			0x56: LD_R_iRRpNNi(rD, rp)         # LD D,(IX+nn)

			0x5C: LD_R_R(rE, rh)         # LD E,IXh
			0x5D: LD_R_R(rE, rl)         # LD E,IXl
			0x5E: LD_R_iRRpNNi(rE, rp)         # LD E,(IX+nn)

			0x60: LD_R_R(rh, rB)         # LD IXh,B
			0x61: LD_R_R(rh, rC)         # LD IXh,C
			0x62: LD_R_R(rh, rD)         # LD IXh,D
			0x63: LD_R_R(rh, rE)         # LD IXh,E
			0x64: LD_R_R(rh, rh)         # LD IXh,IXh
			0x65: LD_R_R(rh, rl)         # LD IXh,IXl
			0x66: LD_R_iRRpNNi(rH, rp)         # LD H,(IX+nn)
			0x67: LD_R_R(rh, rA)         # LD IXh,A
			0x68: LD_R_R(rl, rB)         # LD IXl,B
			0x69: LD_R_R(rl, rC)         # LD IXl,C
			0x6A: LD_R_R(rl, rD)         # LD IXl,D
			0x6B: LD_R_R(rl, rE)         # LD IXl,E
			0x6C: LD_R_R(rl, rh)         # LD IXl,IXh
			0x6D: LD_R_R(rl, rl)         # LD IXl,IXl
			0x6E: LD_R_iRRpNNi(rL, rp)         # LD L,(IX+nn)
			0x6F: LD_R_R(rl, rA)         # LD IXl,A
			0x70: LD_iRRpNNi_R(rp, rB)         # LD (IX+nn),B
			0x71: LD_iRRpNNi_R(rp, rC)         # LD (IX+nn),C
			0x72: LD_iRRpNNi_R(rp, rD)         # LD (IX+nn),D
			0x73: LD_iRRpNNi_R(rp, rE)         # LD (IX+nn),E
			0x74: LD_iRRpNNi_R(rp, rH)         # LD (IX+nn),H
			0x75: LD_iRRpNNi_R(rp, rL)         # LD (IX+nn),L
			0x77: LD_iRRpNNi_R(rp, rA)         # LD (IX+nn),A

			0x7C: LD_R_R(rA, rh)         # LD A,IXh
			0x7D: LD_R_R(rA, rl)         # LD A,IXl
			0x7E: LD_R_iRRpNNi(rA, rp)         # LD A,(IX+nn)

			0x84: ADD_A rhn            # ADD A,IXh
			0x85: ADD_A rln            # ADD A,IXl
			0x86: ADD_A "(#{rpn}+nn)"         # ADD A,(IX+nn)

			0x8C: ADC_A rhn            # ADC A,IXh
			0x8D: ADC_A rln            # ADC A,IXl
			0x8E: ADC_A "(#{rpn}+nn)"         # ADC A,(IX+nn)

			0x94: SUB_A rhn            # SUB IXh
			0x95: SUB_A rln            # SUB IXl
			0x96: SUB_A "(#{rpn}+nn)"         # SUB A,(IX+dd)

			0x9C: SBC_A rhn            # SBC IXh
			0x9D: SBC_A rln            # SBC IXl
			0x9E: SBC_A "(#{rpn}+nn)"         # SBC A,(IX+dd)

			0xA4: AND_A rhn            # AND IXh
			0xA5: AND_A rln            # AND IXl
			0xA6: AND_A "(#{rpn}+nn)"         # AND (IX+dd)

			0xAC: XOR_A rhn            # XOR IXh
			0xAD: XOR_A rln            # XOR IXl
			0xAE: XOR_A "(#{rpn}+nn)"         # XOR A,(IX+dd)

			0xB4: OR_A rhn            # OR IXh
			0xB5: OR_A rln            # OR IXl
			0xB6: OR_A "(#{rpn}+nn)"         # OR A,(IX+dd)

			0xBC: CP_A rhn            # CP IXh
			0xBD: CP_A rln            # CP IXl
			0xBE: CP_A "(#{rpn}+nn)"         # CP (IX+dd)

			0xCB: SHIFT(prefix + 'CB')        # shift code

			0xDD: SHIFT('DD')         # shift code

			0xE1: POP_RR(rp)         # POP IX

			0xE3: EX_iSPi_RR(rp)         # EX (SP),IX

			0xE5: PUSH_RR(rp)         # PUSH IX

			0xE9: JP_RR(rp)         # JP (IX)

			0xF9: LD_RR_RR(rpSP, rp)         # LD SP,IX

			0xFD: SHIFT('FD')         # shift code

			0x100: 'dd'
		}

	OPCODE_RUN_STRINGS_DD = generateddfdOpcodeSet('DD')

	OPCODE_RUN_STRINGS_ED = {

		0x40: IN_R_iCi(rB)         # IN B,(C)
		0x41: OUT_iCi_R(rB)         # OUT (C),B
		0x42: SBC_HL_RR(rpBC)         # SBC HL,BC
		0x43: LD_iNNi_RR(rpBC)         # LD (nnnn),BC
		0x44: NEG()         # NEG
		0x45: RETN()        # RETN
		0x46: IM(0)         # IM 0
		0x47: LD_R_R(rI, rA)         # LD I,A
		0x48: IN_R_iCi(rC)         # IN C,(C)
		0x49: OUT_iCi_R(rC)         # OUT (C),C
		0x4A: ADC_HL_RR(rpBC)        # ADC HL,BC
		0x4B: LD_RR_iNNi(rpBC)         # LD BC,(nnnn)
		0x4C: NEG()         # NEG
		0x4D: RETN()        # RETN
		0x4E: IM(0)         # IM 0
		0x4F: LD_R_R(rR, rA)         # LD R,A
		0x50: IN_R_iCi(rD)         # IN D,(C)
		0x51: OUT_iCi_R(rD)         # OUT (C),D
		0x52: SBC_HL_RR(rpDE)         # SBC HL,DE
		0x53: LD_iNNi_RR(rpDE)         # LD (nnnn),DE
		0x54: NEG()         # NEG
		0x55: RETN()        # RETN
		0x56: IM(1)         # IM 1
		0x57: LD_R_R(rA, rI)         # LD A,I
		0x58: IN_R_iCi(rE)         # IN E,(C)
		0x59: OUT_iCi_R(rE)         # OUT (C),E
		0x5A: ADC_HL_RR(rpDE)        # ADC HL,DE
		0x5B: LD_RR_iNNi(rpDE)         # LD DE,(nnnn)
		0x5C: NEG()         # NEG
		0x5D: RETN()        # RETN
		0x5E: IM(2)         # IM 2
		0x5F: LD_R_R(rA, rR)         # LD A,R
		0x60: IN_R_iCi(rH)         # IN H,(C)
		0x61: OUT_iCi_R(rH)         # OUT (C),H
		0x62: SBC_HL_RR(rpHL)         # SBC HL,HL
		0x63: LD_iNNi_RR(rpHL)         # LD (nnnn),HL
		0x64: NEG()         # NEG
		0x65: RETN()        # RETN
		0x66: IM(0)         # IM 0
		0x67: RRD()        # RRD
		0x68: IN_R_iCi(rL)         # IN L,(C)
		0x69: OUT_iCi_R(rL)         # OUT (C),L
		0x6A: ADC_HL_RR(rpHL)        # ADC HL,HL
		0x6B: LD_RR_iNNi(rpHL, true)         # LD HL,(nnnn)
		0x6C: NEG()         # NEG
		0x6D: RETN()        # RETN
		0x6E: IM(0)         # IM 0
		0x6F: RLD()        # RLD
		0x70: IN_F_iCi()        # IN F,(C)
		0x71: OUT_iCi_0()        # OUT (C),0
		0x72: SBC_HL_RR(rpSP)         # SBC HL,SP
		0x73: LD_iNNi_RR(rpSP)         # LD (nnnn),SP
		0x74: NEG()         # NEG
		0x75: RETN()        # RETN
		0x76: IM(1)         # IM 1

		0x78: IN_R_iCi(rA)         # IN A,(C)
		0x79: OUT_iCi_R(rA)         # OUT (C),A
		0x7A: ADC_HL_RR(rpSP)        # ADC HL,SP
		0x7B: LD_RR_iNNi(rpSP)         # LD SP,(nnnn)
		0x7C: NEG()         # NEG
		0x7D: RETN()        # RETN
		0x7E: IM(2)         # IM 2

		0xA0: LDI()         # LDI
		0xA1: CPI()         # CPI
		0xA2: INI()         # INI
		0xA3: OUTI()        # OUTI

		0xA8: LDD()         # LDD
		0xA9: CPD()         # CPD
		0xAA: IND()         # IND
		0xAB: OUTD()        # OUTD

		0xB0: LDIR()         # LDIR
		0xb1: CPIR()         # CPIR
		0xB2: INIR()         # INIR
		0xB3: OTIR()         # OTIR

		0xB8: LDDR()         # LDDR
		0xb9: CPDR()         # CPDR
		0xBA: INDR()         # INDR
		0xBB: OTDR()         # OTDR

		0x100: 'ed'
	}

	OPCODE_RUN_STRINGS_FD = generateddfdOpcodeSet('FD')

	OPCODE_RUN_STRINGS = {
		0x00: NOP()         # NOP
		0x01: LD_RR_NN(rpBC)         # LD BC,nnnn
		0x02: LD_iRRi_R(rpBC, rA)         # LD (BC),A
		0x03: INC_RR(rpBC)         # INC BC
		0x04: INC "B"         # INC B
		0x05: DEC "B"         # DEC B
		0x06: LD_R_N(rB)         # LD B,nn
		0x07: RLCA()         # RLCA
		0x08: EX_RR_RR(rpAF, rpAF_)         # EX AF,AF'
		0x09: ADD_RR_RR(rpHL, rpBC)         # ADD HL,BC
		0x0A: LD_R_iRRi(rA, rpBC)         # LD A,(BC)
		0x0B: DEC_RR(rpBC)         # DEC BC
		0x0C: INC "C"         # INC C
		0x0D: DEC "C"         # DEC C
		0x0E: LD_R_N(rC)         # LD C,nn
		0x0F: RRCA()         # RRCA
		0x10: DJNZ_N()         # DJNZ nn
		0x11: LD_RR_NN(rpDE)         # LD DE,nnnn
		0x12: LD_iRRi_R(rpDE, rA)         # LD (DE),A
		0x13: INC_RR(rpDE)         # INC DE
		0x14: INC "D"         # INC D
		0x15: DEC "D"         # DEC D
		0x16: LD_R_N(rD)         # LD D,nn
		0x17: RLA()         # RLA
		0x18: JR_N()         # JR nn
		0x19: ADD_RR_RR(rpHL, rpDE)         # ADD HL,DE
		0x1A: LD_R_iRRi(rA, rpDE)         # LD A,(DE)
		0x1B: DEC_RR(rpDE)         # DEC DE
		0x1C: INC "E"         # INC E
		0x1D: DEC "E"         # DEC E
		0x1E: LD_R_N(rE)         # LD E,nn
		0x1F: RRA()         # RRA
		0x20: JR_C_N(FLAG_Z, false)         # JR NZ,nn
		0x21: LD_RR_NN(rpHL)         # LD HL,nnnn
		0x22: LD_iNNi_RR(rpHL)         # LD (nnnn),HL
		0x23: INC_RR(rpHL)         # INC HL
		0x24: INC "H"         # INC H
		0x25: DEC "H"         # DEC H
		0x26: LD_R_N(rH)         # LD H,nn
		0x27: DAA()           # DAA
		0x28: JR_C_N(FLAG_Z, true)         # JR Z,nn
		0x29: ADD_RR_RR(rpHL, rpHL)         # ADD HL,HL
		0x2A: LD_RR_iNNi(rpHL)         # LD HL,(nnnn)
		0x2B: DEC_RR(rpHL)         # DEC HL
		0x2C: INC "L"         # INC L
		0x2D: DEC "L"         # DEC L
		0x2E: LD_R_N(rL)         # LD L,nn
		0x2F: CPL()         # CPL
		0x30: JR_C_N(FLAG_C, false)         # JR NC,nn
		0x31: LD_RR_NN(rpSP)         # LD SP,nnnn
		0x32: LD_iNNi_A()         # LD (nnnn),a
		0x33: INC_RR(rpSP)         # INC SP
		0x34: INC "(HL)"         # INC (HL)
		0x35: DEC "(HL)"         # DEC (HL)
		0x36: LD_iRRi_N(rpHL)         # LD (HL),nn
		0x37: SCF()         # SCF
		0x38: JR_C_N(FLAG_C, true)         # JR C,nn
		0x39: ADD_RR_RR(rpHL, rpSP)         # ADD HL,SP
		0x3A: LD_A_iNNi()         # LD A,(nnnn)
		0x3B: DEC_RR(rpSP)         # DEC SP
		0x3C: INC "A"         # INC A
		0x3D: DEC "A"         # DEC A
		0x3E: LD_R_N(rA)         # LD A,nn
		0x3F: CCF()         # CCF
		0x40: LD_R_R(rB, rB)         # LD B,B
		0x41: LD_R_R(rB, rC)         # LD B,C
		0x42: LD_R_R(rB, rD)         # LD B,D
		0x43: LD_R_R(rB, rE)         # LD B,E
		0x44: LD_R_R(rB, rH)         # LD B,H
		0x45: LD_R_R(rB, rL)         # LD B,L
		0x46: LD_R_iRRi(rB, rpHL)         # LD B,(HL)
		0x47: LD_R_R(rB, rA)         # LD B,A
		0x48: LD_R_R(rC, rB)         # LD C,B
		0x49: LD_R_R(rC, rC)         # LD C,C
		0x4a: LD_R_R(rC, rD)         # LD C,D
		0x4b: LD_R_R(rC, rE)         # LD C,E
		0x4c: LD_R_R(rC, rH)         # LD C,H
		0x4d: LD_R_R(rC, rL)         # LD C,L
		0x4e: LD_R_iRRi(rC, rpHL)         # LD C,(HL)
		0x4f: LD_R_R(rC, rA)         # LD C,A
		0x50: LD_R_R(rD, rB)         # LD D,B
		0x51: LD_R_R(rD, rC)         # LD D,C
		0x52: LD_R_R(rD, rD)         # LD D,D
		0x53: LD_R_R(rD, rE)         # LD D,E
		0x54: LD_R_R(rD, rH)         # LD D,H
		0x55: LD_R_R(rD, rL)         # LD D,L
		0x56: LD_R_iRRi(rD, rpHL)         # LD D,(HL)
		0x57: LD_R_R(rD, rA)         # LD D,A
		0x58: LD_R_R(rE, rB)         # LD E,B
		0x59: LD_R_R(rE, rC)         # LD E,C
		0x5a: LD_R_R(rE, rD)         # LD E,D
		0x5b: LD_R_R(rE, rE)         # LD E,E
		0x5c: LD_R_R(rE, rH)         # LD E,H
		0x5d: LD_R_R(rE, rL)         # LD E,L
		0x5e: LD_R_iRRi(rE, rpHL)         # LD E,(HL)
		0x5f: LD_R_R(rE, rA)         # LD E,A
		0x60: LD_R_R(rH, rB)         # LD H,B
		0x61: LD_R_R(rH, rC)         # LD H,C
		0x62: LD_R_R(rH, rD)         # LD H,D
		0x63: LD_R_R(rH, rE)         # LD H,E
		0x64: LD_R_R(rH, rH)         # LD H,H
		0x65: LD_R_R(rH, rL)         # LD H,L
		0x66: LD_R_iRRi(rH, rpHL)         # LD H,(HL)
		0x67: LD_R_R(rH, rA)         # LD H,A
		0x68: LD_R_R(rL, rB)         # LD L,B
		0x69: LD_R_R(rL, rC)         # LD L,C
		0x6a: LD_R_R(rL, rD)         # LD L,D
		0x6b: LD_R_R(rL, rE)         # LD L,E
		0x6c: LD_R_R(rL, rH)         # LD L,H
		0x6d: LD_R_R(rL, rL)         # LD L,L
		0x6e: LD_R_iRRi(rL, rpHL)         # LD L,(HL)
		0x6f: LD_R_R(rL, rA)         # LD L,A
		0x70: LD_iRRi_R(rpHL, rB)         # LD (HL),B
		0x71: LD_iRRi_R(rpHL, rC)         # LD (HL),C
		0x72: LD_iRRi_R(rpHL, rD)         # LD (HL),D
		0x73: LD_iRRi_R(rpHL, rE)         # LD (HL),E
		0x74: LD_iRRi_R(rpHL, rH)         # LD (HL),H
		0x75: LD_iRRi_R(rpHL, rL)         # LD (HL),L
		0x76: HALT()         # HALT
		0x77: LD_iRRi_R(rpHL, rA)         # LD (HL),A
		0x78: LD_R_R(rA, rB)         # LD A,B
		0x79: LD_R_R(rA, rC)         # LD A,C
		0x7a: LD_R_R(rA, rD)         # LD A,D
		0x7b: LD_R_R(rA, rE)         # LD A,E
		0x7c: LD_R_R(rA, rH)         # LD A,H
		0x7d: LD_R_R(rA, rL)         # LD A,L
		0x7e: LD_R_iRRi(rA, rpHL)         # LD A,(HL)
		0x7f: LD_R_R(rA, rA)         # LD A,A
		0x80: ADD_A "B"         # ADD A,B
		0x81: ADD_A "C"         # ADD A,C
		0x82: ADD_A "D"         # ADD A,D
		0x83: ADD_A "E"         # ADD A,E
		0x84: ADD_A "H"         # ADD A,H
		0x85: ADD_A "L"         # ADD A,L
		0x86: ADD_A "(HL)"         # ADD A,(HL)
		0x87: ADD_A "A"         # ADD A,A
		0x88: ADC_A "B"         # ADC A,B
		0x89: ADC_A "C"         # ADC A,C
		0x8a: ADC_A "D"         # ADC A,D
		0x8b: ADC_A "E"         # ADC A,E
		0x8c: ADC_A "H"         # ADC A,H
		0x8d: ADC_A "L"         # ADC A,L
		0x8e: ADC_A "(HL)"         # ADC A,(HL)
		0x8f: ADC_A "A"         # ADC A,A
		0x90: SUB_A "B"         # SUB A,B
		0x91: SUB_A "C"         # SUB A,C
		0x92: SUB_A "D"         # SUB A,D
		0x93: SUB_A "E"         # SUB A,E
		0x94: SUB_A "H"         # SUB A,H
		0x95: SUB_A "L"         # SUB A,L
		0x96: SUB_A "(HL)"         # SUB A,(HL)
		0x97: SUB_A "A"         # SUB A,A
		0x98: SBC_A "B"         # SBC A,B
		0x99: SBC_A "C"         # SBC A,C
		0x9a: SBC_A "D"         # SBC A,D
		0x9b: SBC_A "E"         # SBC A,E
		0x9c: SBC_A "H"         # SBC A,H
		0x9d: SBC_A "L"         # SBC A,L
		0x9e: SBC_A "(HL)"         # SBC A,(HL)
		0x9f: SBC_A "A"         # SBC A,A
		0xa0: AND_A "B"         # AND A,B
		0xa1: AND_A "C"         # AND A,C
		0xa2: AND_A "D"         # AND A,D
		0xa3: AND_A "E"         # AND A,E
		0xa4: AND_A "H"         # AND A,H
		0xa5: AND_A "L"         # AND A,L
		0xa6: AND_A "(HL)"         # AND A,(HL)
		0xa7: AND_A "A"         # AND A,A
		0xA8: XOR_A "B"         # XOR B
		0xA9: XOR_A "C"         # XOR C
		0xAA: XOR_A "D"         # XOR D
		0xAB: XOR_A "E"         # XOR E
		0xAC: XOR_A "H"         # XOR H
		0xAD: XOR_A "L"         # XOR L
		0xAE: XOR_A "(HL)"         # XOR (HL)
		0xAF: XOR_A "A"         # XOR A
		0xb0: OR_A "B"         # OR B
		0xb1: OR_A "C"         # OR C
		0xb2: OR_A "D"         # OR D
		0xb3: OR_A "E"         # OR E
		0xb4: OR_A "H"         # OR H
		0xb5: OR_A "L"         # OR L
		0xb6: OR_A "(HL)"         # OR (HL)
		0xb7: OR_A "A"         # OR A
		0xb8: CP_A "B"         # CP B
		0xb9: CP_A "C"         # CP C
		0xba: CP_A "D"         # CP D
		0xbb: CP_A "E"         # CP E
		0xbc: CP_A "H"         # CP H
		0xbd: CP_A "L"         # CP L
		0xbe: CP_A "(HL)"         # CP (HL)
		0xbf: CP_A "A"         # CP A
		0xC0: RET_C(FLAG_Z, false)         # RET NZ
		0xC1: POP_RR(rpBC)         # POP BC
		0xC2: JP_C_NN(FLAG_Z, false)         # JP NZ,nnnn
		0xC3: JP_NN()         # JP nnnn
		0xC4: CALL_C_NN(FLAG_Z, false)         # CALL NZ,nnnn
		0xC5: PUSH_RR(rpBC)         # PUSH BC
		0xC6: ADD_A "nn"         # ADD A,nn
		0xC7: RST(0x0000)         # RST 0x00
		0xC8: RET_C(FLAG_Z, true)         # RET Z
		0xC9: RET()         # RET
		0xCA: JP_C_NN(FLAG_Z, true)         # JP Z,nnnn
		0xCB: SHIFT('CB')         # shift code
		0xCC: CALL_C_NN(FLAG_Z, true)         # CALL Z,nnnn
		0xCD: CALL_NN()         # CALL nnnn
		0xCE: ADC_A "nn"         # ADC A,nn
		0xCF: RST(0x0008)         # RST 0x08
		0xD0: RET_C(FLAG_C, false)         # RET NC
		0xD1: POP_RR(rpDE)         # POP DE
		0xD2: JP_C_NN(FLAG_C, false)         # JP NC,nnnn
		0xD3: OUT_iNi_A()         # OUT (nn),A
		0xD4: CALL_C_NN(FLAG_C, false)         # CALL NC,nnnn
		0xD5: PUSH_RR(rpDE)         # PUSH DE
		0xD6: SUB_A "nn"         # SUB nn
		0xD7: RST(0x0010)         # RST 0x10
		0xD8: RET_C(FLAG_C, true)         # RET C
		0xD9: EXX()         # EXX
		0xDA: JP_C_NN(FLAG_C, true)         # JP C,nnnn
		0xDB: IN_A_N()         # IN A,(nn)
		0xDC: CALL_C_NN(FLAG_C, true)         # CALL C,nnnn
		0xDD: SHIFT('DD')         # shift code
		0xDE: SBC_A "nn"         # SBC A,nn
		0xDF: RST(0x0018)         # RST 0x18
		0xE0: RET_C(FLAG_P, false)         # RET PO
		0xE1: POP_RR(rpHL)         # POP HL
		0xE2: JP_C_NN(FLAG_P, false)         # JP PO,nnnn
		0xE3: EX_iSPi_RR(rpHL)         # EX (SP),HL
		0xE4: CALL_C_NN(FLAG_P, false)         # CALL PO,nnnn
		0xE5: PUSH_RR(rpHL)         # PUSH HL
		0xE6: AND_A "nn"         # AND nn
		0xE7: RST(0x0020)         # RST 0x20
		0xE8: RET_C(FLAG_P, true)         # RET PE
		0xE9: JP_RR(rpHL)         # JP (HL)
		0xEA: JP_C_NN(FLAG_P, true)         # JP PE,nnnn
		0xEB: EX_RR_RR(rpDE, rpHL)         # EX DE,HL
		0xEC: CALL_C_NN(FLAG_P, true)         # CALL PE,nnnn
		0xED: SHIFT('ED')         # shift code
		0xEE: XOR_A "nn"         # XOR nn
		0xEF: RST(0x0028)         # RST 0x28
		0xF0: RET_C(FLAG_S, false)         # RET P
		0xF1: POP_RR(rpAF)         # POP AF
		0xF2: JP_C_NN(FLAG_S, false)         # JP NZ,nnnn
		0xF3: DI()         # DI
		0xF4: CALL_C_NN(FLAG_S, false)         # CALL P,nnnn
		0xF5: PUSH_RR(rpAF)         # PUSH AF
		0xF6: OR_A "nn"         # OR nn
		0xF7: RST(0x0030)         # RST 0x30
		0xF8: RET_C(FLAG_S, true)         # RET M
		0xF9: LD_RR_RR(rpSP, rpHL)         # LD SP,HL
		0xFA: JP_C_NN(FLAG_S, true)         # JP M,nnnn
		0xFB: EI()         # EI
		0xFC: CALL_C_NN(FLAG_S, true)         # CALL M,nnnn
		0xFD: SHIFT('FD')         # shift code
		0xFE: CP_A "nn"         # CP nn
		0xFF: RST(0x0038)         # RST 0x38
		0x100: 0
	}

	###
	Assemble and evaluate the final JS code for the Z80 component.
	The indirection on 'eval' causes most browsers to evaluate it in the global
	scope, giving a significant speed boost
	###
	defineZ80JS = """
		window.Z80 = function(opts) {
			var self = {};

			#{setUpStateJS}

			self.requestInterrupt = function(dataBus) {
				interruptPending = true;
				interruptDataBus = dataBus & 0xffff;
				/* TODO: use event scheduling to keep the interrupt line active for a fixed
				~48T window, to support retriggered interrupts and interrupt blocking via
				chains of EI or DD/FD prefixes */
			}
			self.nonMaskableInterrupt = function() {
				iff1 = 1;
				self.requestInterrupt(0x66);
			}
			var z80Interrupt = function() {
				if (iff1) {
					if (halted) {
						/* move PC on from the HALT opcode */
						regPairs[#{rpPC}]++;
						halted = false;
					}

					iff1 = iff2 = 0;

					/* push current PC in readiness for call to interrupt handler */
					regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8);
					regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff);

					/* TODO: R register */

					switch (im) {
						case 0:
							regPairs[#{rpPC}] = interruptDataBus; // assume always RST
							tstates += 6;
							break;
						case 1:
							regPairs[#{rpPC}] = 0x0038;
							tstates += 7;
							break;
						case 2:
							inttemp = (regs[#{rI}] << 8) | (interruptDataBus & 0xff);
							l = READMEM(inttemp);
							inttemp = (inttemp+1) & 0xffff;
							h = READMEM(inttemp);
							console.log(hex(interruptDataBus), hex(inttemp), hex(l), hex(h));
							regPairs[#{rpPC}] = (h<<8) | l;
							tstates += 7;
							break;
					}
				}
			};

			self.runFrame = function(frameLength) {
				var lastOpcodePrefix, offset, opcode;

				while (tstates < frameLength || opcodePrefix) {
					if (interruptible && interruptPending) {
						z80Interrupt();
						interruptPending = false;
					}
					interruptible = true; /* unless overridden by opcode */
					lastOpcodePrefix = opcodePrefix;
					opcodePrefix = '';
					switch (lastOpcodePrefix) {
						case '':
							CONTEND_READ(regPairs[#{rpPC}], 4);
							opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80);
							#{opcodeSwitch(OPCODE_RUN_STRINGS, null, opts.traps)}
							break;
						case 'CB':
							CONTEND_READ(regPairs[#{rpPC}], 4);
							opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80);
							#{opcodeSwitch(OPCODE_RUN_STRINGS_CB)}
							break;
						case 'DD':
							CONTEND_READ(regPairs[#{rpPC}], 4);
							opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80);
							#{opcodeSwitch(OPCODE_RUN_STRINGS_DD, OPCODE_RUN_STRINGS)}
							break;
						case 'DDCB':
							offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							if (offset & 0x80) offset -= 0x100;
							CONTEND_READ(regPairs[#{rpPC}], 3);
							opcode = memory.read(regPairs[#{rpPC}]);
							CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
							CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
							regPairs[#{rpPC}]++;
							#{opcodeSwitch(OPCODE_RUN_STRINGS_DDCB)}
							break;
						case 'ED':
							CONTEND_READ(regPairs[#{rpPC}], 4);
							opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80);
							#{opcodeSwitch(OPCODE_RUN_STRINGS_ED)}
							break;
						case 'FD':
							CONTEND_READ(regPairs[#{rpPC}], 4);
							opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80);
							#{opcodeSwitch(OPCODE_RUN_STRINGS_FD, OPCODE_RUN_STRINGS)}
							break;
						case 'FDCB':
							offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;
							if (offset & 0x80) offset -= 0x100;
							CONTEND_READ(regPairs[#{rpPC}], 3);
							opcode = memory.read(regPairs[#{rpPC}]);
							CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
							CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1);
							regPairs[#{rpPC}]++;
							#{opcodeSwitch(OPCODE_RUN_STRINGS_FDCB)}
							break;
						default:
							throw("Unknown opcode prefix: " + lastOpcodePrefix);
					}
				}
				while (display.nextEventTime != null && display.nextEventTime <= tstates) display.doEvent();
			};

			self.reset = function() {
				regPairs[#{rpPC}] = regPairs[#{rpIR}] = 0;
				iff1 = 0; iff2 = 0; im = 0; halted = false;
			};

			self.loadState = function(snapRegs) {
				regPairs[#{rpAF}] = snapRegs['AF'];
				regPairs[#{rpBC}] = snapRegs['BC'];
				regPairs[#{rpDE}] = snapRegs['DE'];
				regPairs[#{rpHL}] = snapRegs['HL'];
				regPairs[#{rpAF_}] = snapRegs['AF_'];
				regPairs[#{rpBC_}] = snapRegs['BC_'];
				regPairs[#{rpDE_}] = snapRegs['DE_'];
				regPairs[#{rpHL_}] = snapRegs['HL_'];
				regPairs[#{rpIX}] = snapRegs['IX'];
				regPairs[#{rpIY}] = snapRegs['IY'];
				regPairs[#{rpSP}] = snapRegs['SP'];
				regPairs[#{rpPC}] = snapRegs['PC'];
				regPairs[#{rpIR}] = snapRegs['IR'];
				iff1 = snapRegs['iff1'] & 1;
				iff2 = snapRegs['iff2'] & 1;
				im = snapRegs['im'] & 1;
				halted = !!snapRegs['halted'];
				tstates = snapRegs['T'] * 1;
				interruptPending = !!snapRegs['intp'];
				interruptDataBus = snapRegs['intd'] & 0xffff;
			};

			self.saveState = function() {
				return {
					AF: regPairs[#{rpAF}],
					BC: regPairs[#{rpBC}],
					DE: regPairs[#{rpDE}],
					HL: regPairs[#{rpHL}],
					AF_: regPairs[#{rpAF_}],
					BC_: regPairs[#{rpBC_}],
					DE_: regPairs[#{rpDE_}],
					HL_: regPairs[#{rpHL_}],
					IX: regPairs[#{rpIX}],
					IY: regPairs[#{rpIY}],
					SP: regPairs[#{rpSP}],
					PC: regPairs[#{rpPC}],
					IR: regPairs[#{rpIR}],
					iff1: iff1,
					iff2: iff2,
					im: im,
					halted: halted,
					T: tstates,
					intp: interruptPending,
					intd: interruptDataBus,
				};
			};

			/* Register / flag accessors (used for tape trapping and test harness) */
			self.getAF = function() {
				return regPairs[#{rpAF}];
			}
			self.getBC = function() {
				return regPairs[#{rpBC}];
			}
			self.getDE = function() {
				return regPairs[#{rpDE}];
			}
			self.getHL = function() {
				return regPairs[#{rpHL}];
			}
			self.getAF_ = function() {
				return regPairs[#{rpAF_}];
			}
			self.getBC_ = function() {
				return regPairs[#{rpBC_}];
			}
			self.getDE_ = function() {
				return regPairs[#{rpDE_}];
			}
			self.getHL_ = function() {
				return regPairs[#{rpHL_}];
			}
			self.getIX = function() {
				return regPairs[#{rpIX}];
			}
			self.getIY = function() {
				return regPairs[#{rpIY}];
			}
			self.getI = function() {
				return regs[#{rI}];
			}
			self.getR = function() {
				return regs[#{rR}];
			}
			self.getSP = function() {
				return regPairs[#{rpSP}];
			}
			self.getPC = function() {
				return regPairs[#{rpPC}];
			}
			self.getIFF1 = function() {
				return iff1;
			}
			self.getIFF2 = function() {
				return iff2;
			}
			self.getIM = function() {
				return im;
			}
			self.getHalted = function() {
				return halted;
			}

			self.setAF = function(val) {
				regPairs[#{rpAF}] = val;
			}
			self.setBC = function(val) {
				regPairs[#{rpBC}] = val;
			}
			self.setDE = function(val) {
				regPairs[#{rpDE}] = val;
			}
			self.setHL = function(val) {
				regPairs[#{rpHL}] = val;
			}
			self.setAF_ = function(val) {
				regPairs[#{rpAF_}] = val;
			}
			self.setBC_ = function(val) {
				regPairs[#{rpBC_}] = val;
			}
			self.setDE_ = function(val) {
				regPairs[#{rpDE_}] = val;
			}
			self.setHL_ = function(val) {
				regPairs[#{rpHL_}] = val;
			}
			self.setIX = function(val) {
				regPairs[#{rpIX}] = val;
			}
			self.setIY = function(val) {
				regPairs[#{rpIY}] = val;
			}
			self.setI = function(val) {
				regs[#{rI}] = val;
			}
			self.setR = function(val) {
				regs[#{rR}] = val;
			}
			self.setSP = function(val) {
				regPairs[#{rpSP}] = val;
			}
			self.setPC = function(val) {
				regPairs[#{rpPC}] = val;
			}
			self.setIFF1 = function(val) {
				iff1 = val & 1;
			}
			self.setIFF2 = function(val) {
				iff2 = val & 1;
			}
			self.setIM = function(val) {
				im = val & 1;
			}
			self.setHalted = function(val) {
				halted = !!val;
			}

			self.getTstates = function() {
				return tstates;
			}
			self.setTstates = function(val) {
				tstates = val * 1;
			}

			self.getCarry_ = function() {
				return regs[#{rF_}] & #{FLAG_C};
			};
			self.setCarry = function(val) {
				if (val) {
					regs[#{rF}] |= #{FLAG_C};
				} else {
					regs[#{rF}] &= #{~FLAG_C};
				}
			};
			self.getA_ = function() {
				return regs[#{rA_}];
			};

			return self;
		};
	"""
	# Apply macro expansions
	defineZ80JS = defineZ80JS.replace(/READMEM\((.*?)\)/g, '(CONTEND_READ($1, 3), memory.read($1))');
	defineZ80JS = defineZ80JS.replace(/WRITEMEM\((.*?),(.*?)\)/g, """
		CONTEND_WRITE($1, 3);
		while (display.nextEventTime != null && display.nextEventTime < tstates) display.doEvent();
		memory.write($1,$2);
	""");
	if opts.applyContention
		defineZ80JS = defineZ80JS.replace(/CONTEND_READ\((.*?),(.*?)\)/g,
			'(tstates += memory.contend($1, tstates) + ($2))');
		defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE\((.*?),(.*?)\)/g,
			'(tstates += memory.contend($1, tstates) + ($2))');
		defineZ80JS = defineZ80JS.replace(/CONTEND_READ_NO_MREQ\((.*?),(.*?)\)/g,
			'(tstates += memory.contend($1, tstates) + ($2))');
		defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE_NO_MREQ\((.*?),(.*?)\)/g,
			'(tstates += memory.contend($1, tstates) + ($2))');
		defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_EARLY\((.*?)\)/g,
			"""
				var isContendedMemory = memory.isContended($1);
				var isULAPort = ioBus.isULAPort($1);
				if (isContendedMemory) tstates += ioBus.contend($1, tstates);
				tstates += 1;
				while (display.nextEventTime != null && display.nextEventTime < tstates) display.doEvent();
			""");
		defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_LATE\((.*?)\)/g,
			"""
				if (isContendedMemory || isULAPort) {
					ioBus.contend($1);
					tstates += 1;
					if (!isULAPort) {
						ioBus.contend($1); tstates += 1;
						ioBus.contend($1); tstates += 1;
					} else {
						tstates += 2;
					}
				} else {
					tstates += 3;
				}
			""");
	else
		defineZ80JS = defineZ80JS.replace(/CONTEND_READ\((.*?),(.*?)\)/g, 'tstates += ($2)');
		defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE\((.*?),(.*?)\)/g, 'tstates += ($2)');
		defineZ80JS = defineZ80JS.replace(/CONTEND_READ_NO_MREQ\((.*?),(.*?)\)/g, 'tstates += ($2)');
		defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE_NO_MREQ\((.*?),(.*?)\)/g, 'tstates += ($2)');
		defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_EARLY\((.*?)\)/g, 'tstates += 1');
		defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_LATE\((.*?)\)/g, 'tstates += 3');

	# console.log(defineZ80JS);
	indirectEval = eval
	indirectEval(defineZ80JS);
