#include <stdlib.h>
#include <string.h>
#include <stdio.h>

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

static const char* CPU_REGISTER_NAME_IX = "IX";
static const char* CPU_REGISTER_NAME_IXH = "IXH";
static const char* CPU_REGISTER_NAME_IXL = "IXL";

static const char* CPU_REGISTER_NAME_IY = "IY";
static const char* CPU_REGISTER_NAME_IYH = "IYH";
static const char* CPU_REGISTER_NAME_IYL = "IYL";

char _itoaBuffer[64];
Uint8 _oldFlags;

#define _DEBUG_BUFFER_SIZE 1024
static char _debugBuffer0[_DEBUG_BUFFER_SIZE];
static char _debugBuffer1[_DEBUG_BUFFER_SIZE];


Uint16 cpu_neg8(Uint8 val8) {
	Uint16 neg = (Uint8)(0 - val8);
	return neg;
}

void cpu_increment_R() {
	// keep bit 7 unchanged
	Uint8 oldBit7 = 0x80 & cpu_regs()->R;
	cpu_regs()->R++;
	cpu_regs()->R &= 0x7F;
	cpu_regs()->R |= oldBit7;
}

void cpu_set_instruction_disassembled_name_formatted_indexreg16_plus_imm8(struct instruction* instruction, const char* sourceNonNegative, const char* sourceNegative, const char* indexRegName) {
#ifdef _____DISABLE_DEBUG_FUNCTIONALITY
	return;
#endif

	if ((Sint8)instruction->immediate.value.eightBit < 0) {
		cpu_set_instruction_disassembled_name_formatted_str(instruction, sourceNegative, cpu_neg8(instruction->immediate.value.eightBit), UNUSED, indexRegName, UNUSEDSTR);
	}
	else {
		cpu_set_instruction_disassembled_name_formatted_str(instruction, sourceNonNegative, instruction->immediate.value.eightBit, UNUSED, indexRegName, UNUSEDSTR);
	}
}

Uint16 cpu_compute_absolute_address(Uint16 reg16, Uint8 offset) {
	Uint16 address = reg16 + (Sint8)offset;
	return address;
}

Uint16* cpu_get_indexreg16_ptr(struct instruction* instruction) {
	Uint16* indexReg = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? cpu_regs()->IX : cpu_regs()->IY;
	return indexReg;
}

Uint8* cpu_get_indexreg8h_ptr(struct instruction* instruction) {
	Uint8* indexReg = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? &(cpu_regs()->IXH) : &(cpu_regs()->IYH);
	return indexReg;
}

Uint8* cpu_get_indexreg8l_ptr(struct instruction* instruction) {
	Uint8* indexReg = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? &(cpu_regs()->IXL) : &(cpu_regs()->IYL);
	return indexReg;
}

const char* cpu_get_indexreg16_name(struct instruction* instruction) {
	const char* indexRegName = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? CPU_REGISTER_NAME_IX : CPU_REGISTER_NAME_IY;
	return indexRegName;
}

const char* cpu_get_indexreg8h_name(struct instruction* instruction) {
	const char* indexRegName = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? CPU_REGISTER_NAME_IXH : CPU_REGISTER_NAME_IYH;
	return indexRegName;
}

const char* cpu_get_indexreg8l_name(struct instruction* instruction) {
	const char* indexRegName = instruction->prefix.type == DD || instruction->prefix.type == DDCB ? CPU_REGISTER_NAME_IXL : CPU_REGISTER_NAME_IYL;
	return indexRegName;
}

char* _util_replace_one_substring(const char* source, const char* oldW, const char* newW, Uint16 maxChars) {
	Uint64 oldWlen = strlen(oldW);
	Uint64 newWlen = strlen(newW);

	char* occurrenceStart = strstr(source, oldW);
	char* destination = (char*)malloc((maxChars - 1) * sizeof(char));

	char* p = destination;
	if (p == NULL) {
		return NULL;
	}

	while (*source) {
		if (source == occurrenceStart) {
			strcpy_s(p, maxChars - 20, newW);
			p += newWlen;
			source += oldWlen;
		}
		else {
			*p++ = *source++;
		}
	}
	*p = 0;

	return destination;
}

void cpu_save_flags() {
	_oldFlags = cpu_regs()->F;
}

void cpu_restore_flags( Uint8 positions ) {
	for (Uint8 pos = 0; pos < 8; pos++) {
		Uint8 bit = 1 << pos;
		if (positions & bit) {
			// we restore this bit
			if (_oldFlags & bit) cpu_regs()->F |= bit; else cpu_regs()->F &= ~bit;
		}
	}
}

// frees previous disassembled name and replaces it with a newly-allocated one
// based on the specified source
void cpu_set_instruction_disassembled_name(struct instruction* instruction, char* source) {
#ifdef _____DISABLE_DEBUG_FUNCTIONALITY
	return;
#endif

	if (instruction->disassembledNameReadonly) {
		return;
	}

	if (instruction->dissassembledName != NULL) {
		free(instruction->dissassembledName);
	}

	char* newName = (char*)malloc(CPU_MAX_DISASSEMBLED_NAME * sizeof(char));
	if (newName != NULL) {
		int r = strcpy_s(newName, CPU_MAX_DISASSEMBLED_NAME - 2, source);
		if (r) {
			exit(r);
		}
	}
	instruction->dissassembledName = newName;
}

void cpu_mark_instruction_unknown(struct instruction* instruction) {
	instruction->outcome = UnknownInstruction;
	cpu_set_instruction_disassembled_name(instruction, "unknown");
}

void cpu_mark_instruction_executed(struct instruction* instruction) {
	instruction->outcome = Executed;
}

void cpu_finalize_instruction_decode(struct instruction* instruction, Uint16 additionalTStates) {
	instruction->tstatesElapsed += additionalTStates;
}

Uint8 cpu_must_execute() {
#ifdef _____DISABLE_DEBUG_FUNCTIONALITY
	return 1;
#endif

	return cpu_mode() & CPU_RUN_MODE_FLAG__EXECUTE;
}

void cpu_read_imm16(struct instruction* instruction) {
	instruction->immediate.value.sixteenBit = memory_read16(cpu_regs()->PC);
	cpu_regs()->PC += 2;
}

void cpu_read_imm8(struct instruction* instruction) {
	instruction->immediate.value.eightBit = memory_read8(cpu_regs()->PC);
	cpu_regs()->PC += 1;
}

void cpu_set_instruction_disassembled_name_formatted2(struct instruction* instruction, const char* source, Uint16 number0, Uint16 number1) {
#ifdef _____DISABLE_DEBUG_FUNCTIONALITY
	return;
#endif

	if (instruction->disassembledNameReadonly) {
		return;
	}

	if (instruction->dissassembledName != NULL) {
		free(instruction->dissassembledName);
	}

	strcpy_s(_itoaBuffer, 3, "0x");

	_itoa_s(number0, _itoaBuffer + 2, 30, 16);
	char* replacedNumber0 = _util_replace_one_substring(source, "/n0", _itoaBuffer, CPU_MAX_DISASSEMBLED_NAME - 2);

	_itoa_s(number1, _itoaBuffer + 2, 30, 16);
	char* replacedNumber1 = _util_replace_one_substring(replacedNumber0, "/n1", _itoaBuffer, CPU_MAX_DISASSEMBLED_NAME - 2);
	free(replacedNumber0);

	instruction->dissassembledName = replacedNumber1;
}

void cpu_set_instruction_disassembled_name_formatted_str(struct instruction* instruction, const char* source, Uint16 number0, Uint16 number1, const char* string0, const char* string1) {
#ifdef _____DISABLE_DEBUG_FUNCTIONALITY
	return;
#endif

	if (instruction->disassembledNameReadonly) {
		return;
	}

	if (instruction->dissassembledName != NULL) {
		free(instruction->dissassembledName);
	}

	char* newName = (char*)malloc(CPU_MAX_DISASSEMBLED_NAME * sizeof(char));
	if (newName != NULL) {
		int r = strcpy_s(newName, CPU_MAX_DISASSEMBLED_NAME - 2, source);
		if (r) {
			exit(r);
		}
	}
	free(newName);

	strcpy_s(_itoaBuffer, 3, "0x");

	_itoa_s(number0, _itoaBuffer + 2, 30, 16);
	char* replacedNumber0 = _util_replace_one_substring(source, "/n0", _itoaBuffer, CPU_MAX_DISASSEMBLED_NAME - 2);

	_itoa_s(number1, _itoaBuffer + 2, 30, 16);
	char* replacedNumber1 = _util_replace_one_substring(replacedNumber0, "/n1", _itoaBuffer, CPU_MAX_DISASSEMBLED_NAME - 2);
	free(replacedNumber0);

	char* replacedString0 = _util_replace_one_substring(replacedNumber1, "/s0", string0, CPU_MAX_DISASSEMBLED_NAME - 2);
	free(replacedNumber1);

	char* replacedString1 = _util_replace_one_substring(replacedString0, "/s1", string1, CPU_MAX_DISASSEMBLED_NAME - 2);
	free(replacedString0);

	instruction->dissassembledName = replacedString1;
}

// returns 1 for even parity, 0 for odd
Uint8 cpu_parity(Uint8 val) {
	int sum = 0;
	for (int i = 0; i < 8; i++) {
		sum += (val >> i) & 1;
	}
	return (sum & 1) ^ 1;
}

void cpu_swap16(Uint16* first, Uint16* second) {
	Uint16 temp16;
	temp16 = *first;
	*first = *second;
	*second = temp16;
}

Uint16 cpu_pop16() {
	Uint16 value16 = memory_read16(cpu_regs()->SP);
	cpu_regs()->SP += 2;
	return value16;
}

void cpu_push16(Uint16 val16) {
	Uint8 msb = (Uint8)(val16 >> 8);
	Uint8 lsb = (Uint8)val16;
	cpu_regs()->SP--;
	memory_write8(cpu_regs()->SP, msb);
	cpu_regs()->SP--;
	memory_write8(cpu_regs()->SP, lsb);
}

void cpu_push_PC() {
	cpu_push16(cpu_regs()->PC);
}



cpu_addc_binary8(Uint8* value, Uint8 increment, Uint8 carry) {
	Uint8 oldValue = *value;
	(*value) += increment + carry;
	Uint8 valueAfterAdd = *value;

	Sint16 twosComplementSum = (Sint16)(Sint8)oldValue + (Sint16)(Sint8)increment + (Sint16)carry;
	if (twosComplementSum < -128 || twosComplementSum > 127) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

	Uint16 unsignedSum = (Uint16)oldValue + (Uint16)increment + (Uint16)carry;
	if (unsignedSum > 255) cpu_regs()->F |= CPU_FLAG_C_CARRY; else cpu_regs()->F &= ~CPU_FLAG_C_CARRY;

	if ((Sint8)valueAfterAdd < 0) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
	if (valueAfterAdd == 0) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;

	oldValue &= 0x0F;			// keep LS bits
	valueAfterAdd &= 0x0F;		// keep LS bits
	if (valueAfterAdd < oldValue) cpu_regs()->F |= CPU_FLAG_H_HALF_CARRY; else cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;

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

cpu_addc_binary16(Uint16* value, Uint16 increment, Uint16 carry) {
	Uint16 oldValue = *value;
	(*value) += increment + carry;
	Uint16 valueAfterAdd = *value;

	Sint32 twosComplementSum = (Sint32)(Sint16)oldValue + (Sint32)(Sint16)increment + (Sint32)carry;
	if (twosComplementSum < -32768 || twosComplementSum > 32767) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

	Uint32 unsignedSum = (Uint32)oldValue + (Uint32)increment + (Uint32)carry;
	if (unsignedSum > 65535) cpu_regs()->F |= CPU_FLAG_C_CARRY; else cpu_regs()->F &= ~CPU_FLAG_C_CARRY;

	if ((Sint16)valueAfterAdd < 0) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
	if (valueAfterAdd == 0) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;

	oldValue &= 0x0FFF;				// keep LS bits
	valueAfterAdd &= 0x0FFF;		// keep LS bits
	if (valueAfterAdd < oldValue) cpu_regs()->F |= CPU_FLAG_H_HALF_CARRY; else cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;

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

cpu_subc_binary8(Uint8* value, Uint8 increment, Uint8 carry) {
	Uint8 oldValue = *value;
	(*value) -= (increment + carry);
	Uint8 valueAfterSubtract = *value;

	Sint16 twosComplementDifference = (Sint16)(Sint8)oldValue - (Sint16)(Sint8)increment - (Sint16)carry;
	if (twosComplementDifference < -128 || twosComplementDifference > 127) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

	Uint16 unsignedDifference = (Uint16)oldValue - (Uint16)increment - (Uint16)carry;
	if (unsignedDifference > 255) cpu_regs()->F |= CPU_FLAG_C_CARRY; else cpu_regs()->F &= ~CPU_FLAG_C_CARRY;

	if ((Sint8)valueAfterSubtract < 0) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
	if (valueAfterSubtract == 0) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;

	oldValue &= 0x0F;				// keep LS bits
	valueAfterSubtract &= 0x0F;		// keep LS bits
	if (valueAfterSubtract > oldValue) cpu_regs()->F |= CPU_FLAG_H_HALF_CARRY; else cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;

	cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;
}

cpu_subc_binary16(Uint16* value, Uint16 increment, Uint16 carry) {
	Uint16 oldValue = *value;
	(*value) -= (increment + carry);
	Uint16 valueAfterSubtract = *value;

	Sint32 twosComplementDifference = (Sint32)(Sint16)oldValue - (Sint32)(Sint16)increment - (Sint32)carry;
	if (twosComplementDifference < -32768 || twosComplementDifference > 32767) cpu_regs()->F |= CPU_FLAG_PV_PARITY_OVERFLOW; else cpu_regs()->F &= ~CPU_FLAG_PV_PARITY_OVERFLOW;

	Uint32 unsignedDifference = (Uint32)oldValue - (Uint32)increment - (Uint32)carry;
	if (unsignedDifference > 65535) cpu_regs()->F |= CPU_FLAG_C_CARRY; else cpu_regs()->F &= ~CPU_FLAG_C_CARRY;

	if ((Sint16)valueAfterSubtract < 0) cpu_regs()->F |= CPU_FLAG_S_SIGN; else cpu_regs()->F &= ~CPU_FLAG_S_SIGN;
	if (valueAfterSubtract == 0) cpu_regs()->F |= CPU_FLAG_Z_ZERO; else cpu_regs()->F &= ~CPU_FLAG_Z_ZERO;

	oldValue &= 0x0FFF;				// keep LS bits
	valueAfterSubtract &= 0x0FFF;	// keep LS bits
	if (valueAfterSubtract > oldValue) cpu_regs()->F |= CPU_FLAG_H_HALF_CARRY; else cpu_regs()->F &= ~CPU_FLAG_H_HALF_CARRY;

	cpu_regs()->F |= CPU_FLAG_N_SUBTRACT;
}


void cpu_instruction__add_reg16_reg16(struct instruction* instruction, Uint16* reg, Uint16 reg16, 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

	cpu_save_flags();
	cpu_addc_binary16(reg, reg16, 0);
	cpu_restore_flags(CPU_FLAG_S_SIGN | CPU_FLAG_Z_ZERO | CPU_FLAG_PV_PARITY_OVERFLOW);	// restore unaffected flags
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__pop_reg16(struct instruction* instruction, Uint16* reg, const char* disassembledName) {
	cpu_finalize_instruction_decode(instruction, 6);
	cpu_set_instruction_disassembled_name_formatted2(instruction, disassembledName, UNUSED, UNUSED);

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

	*reg = cpu_pop16();
	cpu_mark_instruction_executed(instruction);
}

void cpu_instruction__push_reg16(struct instruction* instruction, Uint16 val16, 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

	cpu_push16(val16);
	cpu_mark_instruction_executed(instruction);
}

const char* cpu_make_regs_dump_string__static_mem_ptr() {
	sprintf_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, "  ");
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "AF=%04x   ", *cpu_regs()->AF); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "BC=%04x   ", *cpu_regs()->BC); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "DE=%04x   ", *cpu_regs()->DE); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "HL=%04x   ", *cpu_regs()->HL); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   FLAGS:  "); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_S_SIGN)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "S  "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_Z_ZERO)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "Z  "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_H_HALF_CARRY)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "H  "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_PV_PARITY_OVERFLOW)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "PV "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_N_SUBTRACT)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "N  "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->F & CPU_FLAG_C_CARRY)sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "C  "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "\n"); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, " "); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "AF'=%04x  ", *cpu_regs()->AF_alt); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "BC'=%04x  ", *cpu_regs()->BC_alt); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "DE'=%04x  ", *cpu_regs()->DE_alt); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "HL'=%04x  ", *cpu_regs()->HL_alt); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "    "); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IX=%04x  ", *cpu_regs()->IX); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IY=%04x  ", *cpu_regs()->IY); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "\n"); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "  "); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "PC=%04x   ", cpu_regs()->PC); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "SP=%04x   ", cpu_regs()->SP); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->IFF & CPU_IFF1_BIT) sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IFF1=1   "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IFF1=0   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	if (cpu_regs()->IFF & CPU_IFF2_BIT) sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IFF2=1   "); else sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "IFF2=0   ");
	strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "     "); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "I=%02x  ", cpu_regs()->I); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);
	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "R=%02x  ", cpu_regs()->R); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	sprintf_s(_debugBuffer0, _DEBUG_BUFFER_SIZE - 10, "\n"); strcat_s(_debugBuffer1, _DEBUG_BUFFER_SIZE - 10, _debugBuffer0);

	return (const char*)_debugBuffer1;
}

char* ____cpu_make_regs_dump_string__static_mem_ptr() {
	const Uint16 size = 1000;
	char* message = (char*)malloc(size * sizeof(char));
	if (!message) {
		return (char*)UNUSEDSTR;
	}
	char* temp = (char*)malloc(size * sizeof(char));
	if (!temp) {
		return (char*)UNUSEDSTR;
	}

	sprintf_s(temp, size - 10, "  ");
	sprintf_s(message, size - 10, "AF=%04x   ", *cpu_regs()->AF); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "BC=%04x   ", *cpu_regs()->BC); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "DE=%04x   ", *cpu_regs()->DE); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "HL=%04x   ", *cpu_regs()->HL); strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "   FLAGS:  "); strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_S_SIGN)sprintf_s(message, size - 10, "S  "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_Z_ZERO)sprintf_s(message, size - 10, "Z  "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_H_HALF_CARRY)sprintf_s(message, size - 10, "H  "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_PV_PARITY_OVERFLOW)sprintf_s(message, size - 10, "PV "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_N_SUBTRACT)sprintf_s(message, size - 10, "N  "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->F & CPU_FLAG_C_CARRY)sprintf_s(message, size - 10, "C  "); else sprintf_s(message, size - 10, "   ");
	strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "\n"); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, " "); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "AF'=%04x  ", *cpu_regs()->AF_alt); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "BC'=%04x  ", *cpu_regs()->BC_alt); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "DE'=%04x  ", *cpu_regs()->DE_alt); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "HL'=%04x  ", *cpu_regs()->HL_alt); strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "    "); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "IX=%04x  ", *cpu_regs()->IX); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "IY=%04x  ", *cpu_regs()->IY); strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "\n"); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "  "); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "PC=%04x   ", cpu_regs()->PC); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "SP=%04x   ", cpu_regs()->SP); strcat_s(temp, size - 10, message);
	if (cpu_regs()->IFF & CPU_IFF1_BIT) sprintf_s(message, size - 10, "IFF1=1   "); else sprintf_s(message, size - 10, "IFF1=0   ");
	strcat_s(temp, size - 10, message);
	if (cpu_regs()->IFF & CPU_IFF2_BIT) sprintf_s(message, size - 10, "IFF2=1   "); else sprintf_s(message, size - 10, "IFF2=0   ");
	strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "     "); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "I=%02x  ", cpu_regs()->I); strcat_s(temp, size - 10, message);
	sprintf_s(message, size - 10, "R=%02x  ", cpu_regs()->R); strcat_s(temp, size - 10, message);

	sprintf_s(message, size - 10, "\n"); strcat_s(temp, size - 10, message);

	free(message);
	return temp;
}

void cpu_print_regs() {
	char* message = (char*)cpu_make_regs_dump_string__static_mem_ptr();
	printf(message);
}
