#include "Constants.h"
#include "Cpu.h"
#include "Memory.h"
#include "Io.h"
#include "CpuUtilities.h"
#include "CpuEDPrefix.h"

void cpu_instruction__in_Cptr(struct instruction* instruction, Uint8* reg, const char* disassembledName) {
	cpu_finalize_instruction_decode(instruction, 4);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint16 address = (cpu_regs()->B << 8) + cpu_regs()->C;
	Uint8 temp8 = io_read8_16bitaddr(address);
	if (temp8 & (1 << 7)) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
	if (!temp8) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
	if (cpu_parity(temp8)) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

	cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
	cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

	*reg = temp8;
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__out_Cptr(struct instruction* instruction, Uint8 val8, const char* disassembledName) {
	cpu_finalize_instruction_decode(instruction, 4);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint16 address = (cpu_regs()->B << 8) + cpu_regs()->C;
	io_write8_16bitaddr(address, val8);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__adc_HL_reg16(struct instruction* instruction, Uint16 value16, const char* disassembledName) {
	cpu_finalize_instruction_decode(instruction, 7);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint16 carry = cpu_regs()->F & CPU_FLAG_C_CARRY ? 1 : 0;
	cpu_addc_binary16(cpu_regs()->HL, value16, carry);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__sbc_HL_reg16(struct instruction* instruction, Uint16 value16, const char* disassembledName) {
	cpu_finalize_instruction_decode(instruction, 7);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint16 carry = cpu_regs()->F & CPU_FLAG_C_CARRY ? 1 : 0;
	cpu_subc_binary16( cpu_regs()->HL, value16, carry);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__ld_mem16_reg16(struct instruction* instruction, Uint16 value16, const char* disassembledName) {
	cpu_read_imm16(instruction);
	cpu_finalize_instruction_decode(instruction, 12);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, instruction->immediate.value.sixteenBit, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	memory_write16(instruction->immediate.value.sixteenBit, value16);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__neg(struct instruction* instruction) {
	cpu_finalize_instruction_decode(instruction, 0);
	cpu_set_instruction_disassembled_name_formatted2(instruction, "neg", UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint8 temp8 = 0;
	cpu_subc_binary8(&temp8, cpu_regs()->A, 0);
	cpu_regs()->A = temp8;
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__retn(struct instruction* instruction) {
	cpu_finalize_instruction_decode(instruction, 6);
	cpu_set_instruction_disassembled_name_formatted2(instruction, "retn", UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	Uint16 branchTarget = cpu_pop16();
	cpu_regs()->PC = branchTarget;

	if (cpu_regs()->IFF & CPU_IFF2_BIT) cpu_regs()->IFF |= CPU_IFF1_BIT; else cpu_regs()->IFF &= ~CPU_IFF1_BIT;
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__im0(struct instruction* instruction) {
	cpu_finalize_instruction_decode(instruction, 0);
	cpu_set_instruction_disassembled_name_formatted2(instruction, "im 0", UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	cpu_set_interrupt_mode(Mode0);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__im1(struct instruction* instruction) {
	cpu_finalize_instruction_decode(instruction, 0);
	cpu_set_instruction_disassembled_name_formatted2(instruction, "im 1", UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	cpu_set_interrupt_mode(Mode1);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__im2(struct instruction* instruction) {
	cpu_finalize_instruction_decode(instruction, 0);
	cpu_set_instruction_disassembled_name_formatted2(instruction, "im 2", UNUSED, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	cpu_set_interrupt_mode(Mode2);
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__ld_reg16_mem16(struct instruction* instruction, Uint16* reg, const char* disassembledName) {
	cpu_read_imm16(instruction);
	cpu_finalize_instruction_decode(instruction, 12);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, instruction->immediate.value.sixteenBit, UNUSED);

	if (!cpu_must_execute()) return;
	//execution

	*reg = memory_read16(instruction->immediate.value.sixteenBit);
	cpu_mark_instruction_executed(instruction);
}


void cpu_run_ED_prefix(struct instruction* instruction) {
	Uint8 temp8;
	Uint16 address16;
	Uint8 keepLooping;

	switch (instruction->opcodeValue) {
	case 0x40:	// in b, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->B), "in B, (C)");
		break;
	case 0x41:	// out (c), b
		cpu_instruction__out_Cptr(instruction, cpu_regs()->B, "out (C), B");
		break;
	case 0x42:	// sbc hl, bc
		cpu_instruction__sbc_HL_reg16(instruction, *cpu_regs()->BC, "sbc HL, BC");
		break;
	case 0x43:	// ld (mem16), bc
		cpu_instruction__ld_mem16_reg16(instruction, *cpu_regs()->BC, "ld (/n0), BC");
		break;
	case 0x44:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x45:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x46:	// im 0
		cpu_instruction__im0(instruction);
		break;
	case 0x47:	// ld i, a
		cpu_finalize_instruction_decode(instruction, 1);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ld I, A", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		cpu_regs()->I = cpu_regs()->A;
		cpu_mark_instruction_executed(instruction);
		break;
	case 0x48:	// in c, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->C), "in C, (C)");
		break;
	case 0x49:	// out (c), c
		cpu_instruction__out_Cptr(instruction, cpu_regs()->C, "out (C), C");
		break;
	case 0x4A:	// adc hl, bc
		cpu_instruction__adc_HL_reg16(instruction, *cpu_regs()->BC, "adc HL, BC");
		break;
	case 0x4B:	// ld bc, (mem16)
		cpu_instruction__ld_reg16_mem16(instruction, cpu_regs()->BC, "ld BC, (/n0)");
		break;
	case 0x4C:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x4D:	// reti
		cpu_finalize_instruction_decode(instruction, 6);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "reti", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		Uint16 branchTarget = cpu_pop16();
		cpu_regs()->PC = branchTarget;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0x4E:	// im 0/1
		cpu_instruction__im0(instruction);
		break;
	case 0x4F:	// ld r, a
		cpu_finalize_instruction_decode(instruction, 1);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ld R, A", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		cpu_regs()->R = cpu_regs()->A;
		cpu_mark_instruction_executed(instruction);
		break;
	case 0x50:	// in d, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->D), "in D, (C)");
		break;
	case 0x51:	// out (c), d
		cpu_instruction__out_Cptr(instruction, cpu_regs()->D, "out (C), D");
		break;
	case 0x52:	// sbc hl, de
		cpu_instruction__sbc_HL_reg16(instruction, *cpu_regs()->DE, "sbc HL, DE");
		break;
	case 0x53:	// ld (mem16), de
		cpu_instruction__ld_mem16_reg16(instruction, *cpu_regs()->DE, "ld (/n0), DE");
		break;
	case 0x54:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x55:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x56:	// im 1
		cpu_instruction__im1(instruction);
		break;
	case 0x57:	// ld a, i
		cpu_finalize_instruction_decode(instruction, 1);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ld A, I", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		cpu_regs()->A = cpu_regs()->I;
		cpu_mark_instruction_executed(instruction);
		break;
	case 0x58:	// in e, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->E), "in E, (C)");
		break;
	case 0x59:	// out (c), e
		cpu_instruction__out_Cptr(instruction, cpu_regs()->E, "out (C), E");
		break;
	case 0x5A:	// adc hl, de
		cpu_instruction__adc_HL_reg16(instruction, *cpu_regs()->DE, "adc HL, DE");
		break;
	case 0x5B:	// ld de, (mem16)
		cpu_instruction__ld_reg16_mem16(instruction, cpu_regs()->DE, "ld DE, (/n0)");
		break;
	case 0x5C:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x5D:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x5E:	// im 2
		cpu_instruction__im2(instruction);
		break;
	case 0x5F:	// ld a, r
		cpu_finalize_instruction_decode(instruction, 1);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ld A, R", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		cpu_regs()->A = cpu_regs()->R;

		if (cpu_regs()->A & (1 << 7)) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
		if (!cpu_regs()->A) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		if (cpu_regs()->IFF & CPU_IFF2_BIT) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

		cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
		cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0x60:	// in h, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->H), "in H, (C)");
		break;
	case 0x61:	// out (c), h
		cpu_instruction__out_Cptr(instruction, cpu_regs()->H, "out (C), H");
		break;
	case 0x62:	// sbc hl, hl
		cpu_instruction__sbc_HL_reg16(instruction, *cpu_regs()->HL, "sbc HL, HL");
		break;
	case 0x63:	// ld (mem16), hl
		cpu_instruction__ld_mem16_reg16(instruction, *cpu_regs()->HL, "ld (/n0), HL");
		break;
	case 0x64:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x65:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x66:	// im 0
		cpu_instruction__im0(instruction);
		break;
	case 0x67:	// rrd
		cpu_finalize_instruction_decode(instruction, 10);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "rrd", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		Uint8 acc = cpu_regs()->A << 4;		// MS nibble := LS nibble
		Uint8 mem8 = memory_read8(*cpu_regs()->HL);

		cpu_regs()->A = (cpu_regs()->A & 0xF0) | (mem8 & 0x0F);		// A's LS nibble := (HL)'s LS nibble
		mem8 = mem8 >> 4;	// (HL)'s LS nibble := (HL)'s MS nibble
		mem8 |= acc;		// (HL)'s MS nibble := A's previous LS nibble
		memory_write8(*cpu_regs()->HL, mem8);

		if (cpu_regs()->A & (1 << 7)) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
		if (!cpu_regs()->A) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		if (cpu_parity(cpu_regs()->A)) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

		cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
		cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0x68:	// in l, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->L), "in L, (C)");
		break;
	case 0x69:	// out (c), l
		cpu_instruction__out_Cptr(instruction, cpu_regs()->L, "out (C), L");
		break;
	case 0x6A:	// adc hl, hl
		cpu_instruction__adc_HL_reg16(instruction, *cpu_regs()->HL, "adc HL, HL");
		break;
	case 0x6B:	// ld hl, (mem16)
		cpu_instruction__ld_reg16_mem16(instruction, cpu_regs()->HL, "ld HL, (/n0)");
		break;
	case 0x6C:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x6D:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x6E:	// im 0/1
		cpu_instruction__im0(instruction);
		break;
	case 0x6F:	// rld
		cpu_finalize_instruction_decode(instruction, 10);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "rld", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		Uint8 acc8 = cpu_regs()->A & 0x0F;		// keep only LS nibble
		mem8 = memory_read8(*cpu_regs()->HL);

		cpu_regs()->A = (cpu_regs()->A & 0xF0) | (mem8 >> 4);	// A's LS nibble := (HL)'s MS nibble
		mem8 = mem8 << 4;										// (HL)'s MS nibble := (HL)'s LS nibble
		mem8 |= acc8;											// (HL)'s LS nibble := A's previous LS nibble
		memory_write8(*cpu_regs()->HL, mem8);

		if (cpu_regs()->A & (1 << 7)) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
		if (!cpu_regs()->A) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		if (cpu_parity(cpu_regs()->A)) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

		cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
		cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0x70:	// in (c)
		// we throw away the byte read, only affecting flags
		cpu_instruction__in_Cptr(instruction, &temp8, "in (C)");
		break;
	case 0x71:	// out (c), 0
		cpu_instruction__out_Cptr(instruction, 0, "out (C), 0");
		break;
	case 0x72:	// sbc hl, sp
		cpu_instruction__sbc_HL_reg16(instruction, cpu_regs()->SP, "sbc HL, SP");
		break;
	case 0x73:	// ld (mem16), sp
		cpu_instruction__ld_mem16_reg16(instruction, cpu_regs()->SP, "ld (/n0), SP");
		break;
	case 0x74:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x75:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x76:	// im 1
		cpu_instruction__im1(instruction);
		break;
	// 0x77 no instruction
	case 0x78:	// in a, (c)
		cpu_instruction__in_Cptr(instruction, &(cpu_regs()->A), "in A, (C)");
		break;
	case 0x79:	// out (c), a
		cpu_instruction__out_Cptr(instruction, cpu_regs()->A, "out (C), A");
		break;
	case 0x7A:	// adc hl, sp
		cpu_instruction__adc_HL_reg16(instruction, cpu_regs()->SP, "adc HL, SP");
		break;
	case 0x7B:	// ld sp, (mem16)
		cpu_instruction__ld_reg16_mem16(instruction, &cpu_regs()->SP, "ld SP, (/n0)");
		break;
	case 0x7C:	// neg
		cpu_instruction__neg(instruction);
		break;
	case 0x7D:	// retn
		cpu_instruction__retn(instruction);
		break;
	case 0x7E:	// im 2
		cpu_instruction__im2(instruction);
		break;
	// 0x7F no instruction
	case 0xA0:	// ldi
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ldi", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		temp8 = memory_read8(*cpu_regs()->HL);
		memory_write8(*cpu_regs()->DE, temp8);
		(*cpu_regs()->HL)++;
		(*cpu_regs()->DE)++;
		(*cpu_regs()->BC)--;

		if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;
		cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
		cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xA1:	// cpi
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "cpi", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		Uint8 throwaway8 = cpu_regs()->A;
		temp8 = memory_read8(*cpu_regs()->HL);
		(*cpu_regs()->HL)++;
		(*cpu_regs()->BC)--;

		cpu_save_flags();
		cpu_subc_binary8(&throwaway8, temp8, 0);
		cpu_restore_flags(CPU_FLAG_C_CARRY);	// restore unaffected flags
		if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xA2:	// ini
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ini", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
		temp8 = io_read8_16bitaddr(address16);
		memory_write8(*cpu_regs()->HL, temp8);

		(*cpu_regs()->HL)++;
		cpu_regs()->B--;

		if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xA3:	// outi
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "outi", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		temp8 = memory_read8(*cpu_regs()->HL);
		address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
		io_write8_16bitaddr(address16, temp8);

		(*cpu_regs()->HL)++;
		cpu_regs()->B--;

		if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	// 0xA4 no instruction
	// 0xA5 no instruction
	// 0xA6 no instruction
	// 0xA7 no instruction
	case 0xA8:	// ldd
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ldd", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		temp8 = memory_read8(*cpu_regs()->HL);
		memory_write8(*cpu_regs()->DE, temp8);
		(*cpu_regs()->HL)--;
		(*cpu_regs()->DE)--;
		(*cpu_regs()->BC)--;

		if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;
		cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
		cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xA9:	// cpd
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "cpd", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		throwaway8 = cpu_regs()->A;
		temp8 = memory_read8(*cpu_regs()->HL);
		(*cpu_regs()->HL)--;
		(*cpu_regs()->BC)--;

		cpu_save_flags();
		cpu_subc_binary8(&throwaway8, temp8, 0);
		cpu_restore_flags(CPU_FLAG_C_CARRY);	// restore unaffected flags
		if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xAA:	// ind
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ind", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
		temp8 = io_read8_16bitaddr(address16);
		memory_write8(*cpu_regs()->HL, temp8);

		(*cpu_regs()->HL)--;
		cpu_regs()->B--;

		if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xAB:	// outd
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "outd", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		temp8 = memory_read8(*cpu_regs()->HL);
		address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
		io_write8_16bitaddr(address16, temp8);

		(*cpu_regs()->HL)--;
		cpu_regs()->B--;

		if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
		cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

		cpu_mark_instruction_executed(instruction);
		break;
	// 0xAC no instruction
	// 0xAD no instruction
	// 0xAE no instruction
	// 0xAF no instruction
	case 0xB0:	// ldir
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "ldir", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			temp8 = memory_read8(*cpu_regs()->HL);
			memory_write8(*cpu_regs()->DE, temp8);
			(*cpu_regs()->HL)++;
			(*cpu_regs()->DE)++;
			(*cpu_regs()->BC)--;

			if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;
			cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
			cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (*cpu_regs()->BC != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xB1:	// cpir
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "cpir", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			throwaway8 = cpu_regs()->A;
			temp8 = memory_read8(*cpu_regs()->HL);
			(*cpu_regs()->HL)++;
			(*cpu_regs()->BC)--;

			cpu_save_flags();
			cpu_subc_binary8(&throwaway8, temp8, 0);
			cpu_restore_flags(CPU_FLAG_C_CARRY);	// restore unaffected flags
			if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0 && (throwaway8 != 0)) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while ((*cpu_regs()->BC != 0) && (throwaway8 != 0) && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xB2:	// inir
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "inir", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
			temp8 = io_read8_16bitaddr(address16);
			memory_write8(*cpu_regs()->HL, temp8);

			(*cpu_regs()->HL)++;
			cpu_regs()->B--;

			if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
			cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;
			cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (cpu_regs()->B != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xB3:	// otir
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "otir", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			temp8 = memory_read8(*cpu_regs()->HL);
			address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
			io_write8_16bitaddr(address16, temp8);

			(*cpu_regs()->HL)++;
			cpu_regs()->B--;

			if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
			cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (cpu_regs()->B != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	// 0xB4 no instruction
	// 0xB5 no instruction
	// 0xB6 no instruction
	// 0xB7 no instruction
	case 0xB8:	// lddr
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "lddr", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			temp8 = memory_read8(*cpu_regs()->HL);
			memory_write8(*cpu_regs()->DE, temp8);
			(*cpu_regs()->HL)--;
			(*cpu_regs()->DE)--;
			(*cpu_regs()->BC)--;

			if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;
			cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;
			cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (*cpu_regs()->BC != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xB9:	// cpdr
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "cpdr", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			throwaway8 = cpu_regs()->A;
			temp8 = memory_read8(*cpu_regs()->HL);
			(*cpu_regs()->HL)--;
			(*cpu_regs()->BC)--;

			cpu_save_flags();
			cpu_subc_binary8(&throwaway8, temp8, 0);
			cpu_restore_flags(CPU_FLAG_C_CARRY);	// restore unaffected flags
			if (*cpu_regs()->BC) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0 && (throwaway8 != 0)) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while ((*cpu_regs()->BC != 0) && (throwaway8 != 0) && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xBA:	// indr
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "indr", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
			temp8 = io_read8_16bitaddr(address16);
			memory_write8(*cpu_regs()->HL, temp8);

			(*cpu_regs()->HL)--;
			cpu_regs()->B--;

			if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
			cpu_regs()->F &= ~CPU_FLAG_N_SUBTRACT;
			cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (cpu_regs()->B != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	case 0xBB:	// otdr
		cpu_finalize_instruction_decode(instruction, 8);
		cpu_set_instruction_disassembled_name_formatted2(instruction, "otdr", UNUSED, UNUSED);

		if (!cpu_must_execute()) break;
		//execution

		keepLooping = 1;
		do {
			temp8 = memory_read8(*cpu_regs()->HL);
			address16 = (cpu_regs()->B << 8) + cpu_regs()->C;
			io_write8_16bitaddr(address16, temp8);

			(*cpu_regs()->HL)--;
			cpu_regs()->B--;

			if (!cpu_regs()->B) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;
			cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;

			instruction->tstatesElapsed += 21;

			if (!cpu_can_continue_string_operation(instruction->tstatesElapsed) && *cpu_regs()->BC != 0) {
				// main CPU runner has told us we have to stop, and we still have iterations to complete
				cpu_regs()->PC -= 2;	// reverse PC to before this instruction
				keepLooping = 0;
			}
			else {
				// we increment on all iterations except the last, because we also increment R
				// at the "overall end" of instruction execution
				cpu_increment_R();
			}
		} while (cpu_regs()->B != 0 && keepLooping);
		instruction->tstatesElapsed -= 21;	// clean up accumulation after last iteration

		cpu_mark_instruction_executed(instruction);
		break;
	// 0xBC no instruction
	// 0xBD no instruction
	// 0xBE no instruction
	// 0xBF no instruction

	default:
		cpu_finalize_instruction_decode(instruction, 0);
		cpu_mark_instruction_unknown(instruction);
		break;
	}
}
