/*
 * Copyright (C) 2017 FIX94
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
#include "cpu.h"
#include "apu.h"
#include "ppu.h"
#include "mem.h"
#include "input.h"

#define P_FLAG_C (1<<4)
#define P_FLAG_H (1<<5)
#define P_FLAG_N (1<<6)
#define P_FLAG_Z (1<<7)

//from main.c
extern bool gbEmuGBSPlayback;
extern uint16_t gbsLoadAddr;
extern uint16_t gbsInitAddr;
extern uint16_t gbsPlayAddr;
extern uint16_t gbsSP;
extern uint8_t cpuTimer;
extern bool gbCgbMode;
extern bool gbCgbBootrom;
//initalized elsewhere
uint8_t cpuAddSpeed = 1;
bool cpuCgbSpeed = false;
//used externally
bool cpuDoStopSwitch;
bool cpu_oam_dma;
bool cpu_oam_dma_running;
uint16_t cpu_oam_dma_addr;

static uint16_t sp, pc, cpuTmp16;
static uint8_t a,b,c,d,e,f,h,l,cpuTmp;

//gbs stuff
static bool gbsInitRet, gbsPlayRet;

static uint8_t sub_in_val;
static bool irqEnable;
static bool cpuHaltLoop,cpuStopLoop,cpuHaltBug,cpuPrevInAny;

static uint8_t curInstr;

static bool cpu_oam_dma_started;
static uint8_t cpu_oam_dma_pos;
bool cpuDmaHalt;

static void cpuSetupActionArr();
static const uint8_t *cpu_action_arr;
static uint8_t cpu_arr_pos;
static inline void cpuSetNopArr();

void cpuInit()
{
	sub_in_val=0,cpuTmp=0,cpuTmp16=0;
	if(gbCgbBootrom)
	{
		//will get set up in Bootrom
		a=0,b=0,c=0,d=0,e=0,f=0,h=0,l=0;
		sp=0;
		pc=0;
	}
	else
	{
		if(gbCgbMode) //From GBC Bootrom
			a=0x11,b=0,c=0,d=0,e=0x08,f=0x80,h=0,l=0x7C;
		else //From GB Bootrom
			a=0x01,b=0,c=0x13,d=0,e=0xD8,f=0xB0,h=1,l=0x4D;
		sp = 0xFFFE; //Boot Stack Pointer
		pc = 0x0100; //hardcoded ROM entrypoint
	}
	irqEnable = false;
	cpuHaltLoop = false;
	cpuStopLoop = false;
	cpuHaltBug = false;
	cpuPrevInAny = false;
	cpuDoStopSwitch = false;
	cpu_oam_dma = false;
	cpu_oam_dma_running = false;
	cpu_oam_dma_addr = 0;
	cpuSetupActionArr();
	//gbs stuff
	gbsInitRet = false; //for first init
	gbsPlayRet = true; //for first play call

	curInstr = 0;
	cpu_oam_dma_started = false;
	cpu_oam_dma_pos = 0;
	cpuDmaHalt = false;

	cpuSetNopArr();
}

static inline void setAImmRegStats()
{
	f &= ~(P_FLAG_C|P_FLAG_H|P_FLAG_N|P_FLAG_Z);
	if(a == 0) f |= P_FLAG_Z;
}

static inline void cpuAdd16(uint16_t x)
{
	uint32_t r1,r2;
	r1=((h<<8)|l)+(x);
	r2=(((h<<8)|l)&0xFFF)+((x)&0xFFF);

	uint8_t tf = (uint8_t)(f&P_FLAG_Z);
	if(r1 > 0xFFFF)
		tf |= P_FLAG_C;
	if(r2 > 0x0FFF)
		tf |= P_FLAG_H;
	l = r1;
	h = r1 >> 8;
	f = tf;
}

static inline int16_t cpuAddSp16(uint8_t add)
{
	int32_t n = (int8_t)add;
	uint32_t r1,r2,r3;
	r1=sp+n;
	r2=(sp&0xFF)+(n&0xFF);
	r3=(sp&0xF)+(n&0xF);

	uint8_t tf = 0;

	if(r2 > 0xFF)
		tf |= P_FLAG_C;
	if(r3 > 0x0F)
		tf |= P_FLAG_H;

	f = tf;

	return (uint16_t)r1;
}

static inline void setAddSubCmpFlags(uint16_t r1, uint16_t r2)
{
	if(((uint8_t)r2)==0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	if(r2>0xFF)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	if(r1>0xF)
		f |= P_FLAG_H;
	else
		f &= ~P_FLAG_H;
}

static inline uint8_t cpuDoAdd8(uint8_t x, uint8_t y)
{
	uint16_t r1,r2;
	r1=(uint16_t)((x&0xF)+(y&0xF));
	r2=(uint16_t)(x+y);

	setAddSubCmpFlags(r1, r2);

	f &= ~P_FLAG_N;

	return (uint8_t)r2;
}

static void cpuAdd8(uint8_t *reg)
{
	a = cpuDoAdd8(a,*reg);
}

static inline uint8_t cpuDoSub8(uint8_t x, uint8_t y)
{
	uint16_t r1,r2;
	r1=(uint16_t)((x&0xF)-(y&0xF));
	r2=(uint16_t)(x-y);

	setAddSubCmpFlags(r1, r2);

	f |= P_FLAG_N;

	return (uint8_t)r2;
}

static void cpuSub8(uint8_t *reg)
{
	a = cpuDoSub8(a,*reg);
}

static void cpuCmp8(uint8_t *reg)
{
	cpuDoSub8(a,*reg);
}

static void cpuSbc8(uint8_t *reg)
{
	uint16_t r1,r2;
	uint8_t x = *reg;
	r1=(uint16_t)((a&0xF)-((x)&0xF)-((f&P_FLAG_C)?1:0));
	r2=(uint16_t)(a-(x)-((f&P_FLAG_C)?1:0));

	a=(uint8_t)r2;

	setAddSubCmpFlags(r1, r2);

	f |= P_FLAG_N;
}

static void cpuAdc8(uint8_t *reg)
{
	uint16_t r1,r2;
	uint8_t x = *reg;
	r1=(uint16_t)((a&0xF)+((x)&0xF)+((f&P_FLAG_C)?1:0));
	r2=(uint16_t)(a+(x)+((f&P_FLAG_C)?1:0));

	a=(uint8_t)r2;

	setAddSubCmpFlags(r1, r2);

	f &= ~P_FLAG_N;
}

static void cpuXOR(uint8_t *reg)
{
	a ^= (*reg);
	setAImmRegStats();
}

static void cpuOR(uint8_t *reg)
{
	a |= (*reg);
	setAImmRegStats();
}

static void cpuAND(uint8_t *reg)
{
	a &= (*reg);
	setAImmRegStats();
	f |= P_FLAG_H;
}

static void cpuCPL(uint8_t *reg)
{
	uint8_t val = (*reg);
	f |= (P_FLAG_H|P_FLAG_N);
	*reg = ~val;
}

//rotate WITHOUT old carry used
static void cpuRLC(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&0x80)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val <<= 1;

	if(f & P_FLAG_C)
		val |= 1;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//rotate WITHOUT old carry used
static void cpuRRC(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&1)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val >>= 1;

	if(f & P_FLAG_C)
		val |= 0x80;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//rotate WITH old carry used
static void cpuRL(uint8_t *reg)
{
	uint8_t val = (*reg);
	uint8_t oldF = f;

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&0x80)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val <<= 1;

	if(oldF & P_FLAG_C)
		val |= 1;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//rotate WITH old carry used
static void cpuRR(uint8_t *reg)
{
	uint8_t val = (*reg);
	uint8_t oldF = f;

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&1)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val >>= 1;

	if(oldF & P_FLAG_C)
		val |= 0x80;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//Direct calls for a
static void cpuRLCA()
{
	cpuRLC(&a);
	f &= ~P_FLAG_Z;
}

static void cpuRRCA()
{
	cpuRRC(&a);
	f &= ~P_FLAG_Z;
}

static void cpuRLA()
{
	cpuRL(&a);
	f &= ~P_FLAG_Z;
}

static void cpuRRA()
{
	cpuRR(&a);
	f &= ~P_FLAG_Z;
}

//shift left
static void cpuSLA(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&0x80)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val <<= 1;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//shift right arithmetic
static void cpuSRA(uint8_t *reg)
{
	uint8_t val = (*reg);
	uint8_t oldSign = (val&0x80);

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&1)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val >>= 1;
	val |= oldSign;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

//shift right logical
static void cpuSRL(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~(P_FLAG_H|P_FLAG_N);

	if(val&1)
		f |= P_FLAG_C;
	else
		f &= ~P_FLAG_C;

	val >>= 1;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

static void cpuSWAP(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~(P_FLAG_H|P_FLAG_N|P_FLAG_C);

	uint8_t vL = val&0xF;
	val>>=4;
	val|=vL<<4;

	if(val == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;

	*reg = val;
}

static void cpuBIT(uint8_t *reg)
{
	uint8_t val = (*reg);

	f &= ~P_FLAG_N;
	f |= P_FLAG_H;

	if((val&(1<<sub_in_val)) == 0)
		f |= P_FLAG_Z;
	else
		f &= ~P_FLAG_Z;
}

static void cpuSET(uint8_t *reg)
{
	uint8_t val = (*reg);

	val |= (1<<sub_in_val);

	*reg = val;
}

static void cpuRES(uint8_t *reg)
{
	uint8_t val = (*reg);

	val &= ~(1<<sub_in_val);

	*reg = val;
}

static void cpuHALT(uint8_t *none)
{
	(void)none;
	//HALT bug, PC wont increase next instruction!
	if(!irqEnable && memGetCurIrqList())
		cpuHaltBug = true;
	else //enters HALT mode normall
		cpuHaltLoop = true;
}

static void cpuBcInc()
{
	uint16_t tmp = c | b<<8;
	tmp++;
	c = tmp&0xFF;
	b = tmp>>8;
}

static void cpuDeInc()
{
	uint16_t tmp = e | d<<8;
	tmp++;
	e = tmp&0xFF;
	d = tmp>>8;
}

static void cpuHlInc()
{
	uint16_t tmp = l | h<<8;
	tmp++;
	l = tmp&0xFF;
	h = tmp>>8;
}

static void cpuSpInc()
{
	sp++;
}

static void cpuBcDec()
{
	uint16_t tmp = c | b<<8;
	tmp--;
	c = tmp&0xFF;
	b = tmp>>8;
}

static void cpuDeDec()
{
	uint16_t tmp = e | d<<8;
	tmp--;
	e = tmp&0xFF;
	d = tmp>>8;
}

static void cpuHlDec()
{
	uint16_t tmp = l | h<<8;
	tmp--;
	l = tmp&0xFF;
	h = tmp>>8;
}

static void cpuSpDec()
{
	sp--;
}

static void cpuNoAction(uint8_t *reg) { (void)reg; };

static void cpuLDa(uint8_t *reg) { a = (*reg); }
static void cpuLDb(uint8_t *reg) { b = (*reg); }
static void cpuLDc(uint8_t *reg) { c = (*reg); }
static void cpuLDd(uint8_t *reg) { d = (*reg); }
static void cpuLDe(uint8_t *reg) { e = (*reg); }
static void cpuLDl(uint8_t *reg) { l = (*reg); }
static void cpuLDh(uint8_t *reg) { h = (*reg); }

static void cpuSTbc(uint8_t *reg) { memSet8(c | b<<8, (*reg)); }
static void cpuSTde(uint8_t *reg) { memSet8(e | d<<8, (*reg)); }
static void cpuSThl(uint8_t *reg) { memSet8(l | h<<8, (*reg)); }
static void cpuSTt16(uint8_t *reg) { memSet8(cpuTmp16, (*reg)); }

static void cpuSThlInc(uint8_t *reg) { cpuSThl(reg); cpuHlInc(); }
static void cpuSThlDec(uint8_t *reg) { cpuSThl(reg); cpuHlDec(); }

static void cpuInc(uint8_t *reg)
{
	uint8_t tf = f&P_FLAG_C;
	*reg = cpuDoAdd8(*reg,1);
	f&=~P_FLAG_C;
	f|=tf;
}

static void cpuDec(uint8_t *reg)
{
	uint8_t tf = f&P_FLAG_C;
	*reg = cpuDoSub8(*reg,1);
	f&=~P_FLAG_C;
	f|=tf;
}

static void cpuDAA(uint8_t *reg)
{
	int16_t in = *reg;

	if (!(f & P_FLAG_N))
	{
		if ((f & P_FLAG_H) || (in & 0xF) > 9)
			in += 0x06;

		if ((f & P_FLAG_C) || in > 0x9F)
			in += 0x60;
	}
	else
	{
		if (f & P_FLAG_H)
			in = (in - 6) & 0xFF;

		if (f & P_FLAG_C)
			in -= 0x60;
	}

	f &= ~(P_FLAG_H | P_FLAG_Z);

	if ((in & 0x100) == 0x100)
		f |= P_FLAG_C;

	in &= 0xFF;

	if (in == 0)
		f |= P_FLAG_Z;

	*reg = (uint8_t)in;
}

static void cpuSetStopLoop()
{
	printf("CPU: Frozen until Button is pressed\n");
	cpuStopLoop = true;
}

static void cpuSTOP(uint8_t *none)
{
	(void)none;
	//printf("CPU called STOP instruction\n");
	if(cpuDoStopSwitch)
	{
		cpuSetSpeed(!cpuCgbSpeed);
		cpuDoStopSwitch = false;
	}
	else
		cpuSetStopLoop();
	//takes up 2 instructions?
	pc++;
}

enum {
	CPU_GET_INSTRUCTION = 0,
	CPU_GET_SUBINSTRUCTION,
	CPU_DELAY_CYCLE,
	CPU_ACTION_GET_INSTRUCTION,
	CPU_A_ACTION_GET_INSTRUCTION,
	CPU_B_ACTION_GET_INSTRUCTION,
	CPU_C_ACTION_GET_INSTRUCTION,
	CPU_D_ACTION_GET_INSTRUCTION,
	CPU_E_ACTION_GET_INSTRUCTION,
	CPU_H_ACTION_GET_INSTRUCTION,
	CPU_L_ACTION_GET_INSTRUCTION,
	CPU_ACTION_WRITE,
	CPU_A_ACTION_WRITE,
	CPU_B_ACTION_WRITE,
	CPU_C_ACTION_WRITE,
	CPU_D_ACTION_WRITE,
	CPU_E_ACTION_WRITE,
	CPU_H_ACTION_WRITE,
	CPU_L_ACTION_WRITE,
	CPU_BC_ACTION_ADD,
	CPU_DE_ACTION_ADD,
	CPU_HL_ACTION_ADD,
	CPU_SP_ACTION_ADD,
	CPU_HL_ADD_SPECIAL,
	CPU_SP_ADD_SPECIAL,
	CPU_ACTION_WRITE8_HL,
	CPU_TMP_ADD_PC,
	CPU_TMP_READ8_BC,
	CPU_TMP_READ8_DE,
	CPU_TMP_READ8_HL,
	CPU_TMP_READ8_HL_INC,
	CPU_TMP_READ8_HL_DEC,
	CPU_TMP_READ8_PC_INC,
	CPU_TMP_READ8_PC_INC_JRNZ_CHK,
	CPU_TMP_READ8_PC_INC_JRZ_CHK,
	CPU_TMP_READ8_PC_INC_JRNC_CHK,
	CPU_TMP_READ8_PC_INC_JRC_CHK,
	CPU_TMP_READ8_SP_INC,
	CPU_PCL_FROM_TMP_PCH_READ8_SP_INC,
	CPU_C_FROM_TMP_B_READ8_SP_INC,
	CPU_E_FROM_TMP_D_READ8_SP_INC,
	CPU_L_FROM_TMP_H_READ8_SP_INC,
	CPU_F_FROM_TMP_A_READ8_SP_INC,
	CPU_TMP_READHIGH_A,
	CPU_TMP_WRITEHIGH_A,
	CPU_C_READHIGH_A,
	CPU_C_WRITEHIGH_A,
	CPU_SP_FROM_HL,
	CPU_SP_WRITE8_A_DEC,
	CPU_SP_WRITE8_B_DEC,
	CPU_SP_WRITE8_C_DEC,
	CPU_SP_WRITE8_D_DEC,
	CPU_SP_WRITE8_E_DEC,
	CPU_SP_WRITE8_F_DEC,
	CPU_SP_WRITE8_H_DEC,
	CPU_SP_WRITE8_L_DEC,
	CPU_SP_WRITE8_PCH_DEC,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_00,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_08,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_10,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_18,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_20,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_28,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_30,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_38,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_40,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_48,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_50,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_58,
	CPU_SP_WRITE8_PCL_DEC_PC_FROM_60,
	CPU_A_READ8_TMP16,
	CPU_A_READ8_PC_INC,
	CPU_B_READ8_PC_INC,
	CPU_C_READ8_PC_INC,
	CPU_D_READ8_PC_INC,
	CPU_E_READ8_PC_INC,
	CPU_L_READ8_PC_INC,
	CPU_H_READ8_PC_INC,
	CPU_PCL_FROM_TMP_PCH_READ8_PC,
	CPU_SPL_FROM_TMP_SPH_READ8_PC_INC,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNZ_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNC_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CZ_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CC_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNZ_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNC_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPZ_CHK,
	CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPC_CHK,
	CPU_TMP16_WRITE8_SPL_INC,
	CPU_TMP16_WRITE8_SPH,
	CPU_DI_GET_INSTRUCTION,
	CPU_EI_GET_INSTRUCTION,
	CPU_GET_INSTRUCTION_EI,
	CPU_SCF_GET_INSTRUCTION,
	CPU_CCF_GET_INSTRUCTION,
	CPU_PC_FROM_HL_GET_INSTRUCTION,
	CPU_PC_FROM_T16,
	CPU_RET_NZ_CHK,
	CPU_RET_NC_CHK,
	CPU_RET_Z_CHK,
	CPU_RET_C_CHK,
};

/* arrays for multiple similar instructions */
static const uint8_t cpu_imm_arr[1] = { CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_a_arr[1] = { CPU_A_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_b_arr[1] = { CPU_B_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_c_arr[1] = { CPU_C_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_d_arr[1] = { CPU_D_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_e_arr[1] = { CPU_E_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_h_arr[1] = { CPU_H_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_l_arr[1] = { CPU_L_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_bc_arr[2] = { CPU_TMP_READ8_BC, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_de_arr[2] = { CPU_TMP_READ8_DE, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_hl_arr[2] = { CPU_TMP_READ8_HL, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_pc_arr[2] = { CPU_TMP_READ8_PC_INC, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_hl_inc_arr[2] = { CPU_TMP_READ8_HL_INC, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_hl_dec_arr[2] = { CPU_TMP_READ8_HL_DEC, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_delay_arr[2] = { CPU_DELAY_CYCLE, CPU_ACTION_GET_INSTRUCTION };
static const uint8_t cpu_imm_hl_st_arr[3] = { CPU_TMP_READ8_HL, CPU_ACTION_WRITE8_HL, CPU_GET_INSTRUCTION };

/* arrays for single special instructions */
static const uint8_t cpu_nop_arr[1] = { CPU_GET_INSTRUCTION };
static const uint8_t cpu_sub_arr[1] = { CPU_GET_SUBINSTRUCTION };
static const uint8_t cpu_hljmp_arr[1] = { CPU_PC_FROM_HL_GET_INSTRUCTION };
static const uint8_t cpu_sp_from_hl_arr[2] = { CPU_SP_FROM_HL, CPU_GET_INSTRUCTION };
static const uint8_t cpu_absjmp_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_PCL_FROM_TMP_PCH_READ8_PC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_absjmpnz_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNZ_CHK, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_absjmpnc_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNC_CHK, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_absjmpz_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPZ_CHK, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_absjmpc_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPC_CHK, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };

static const uint8_t cpu_abscall_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_abscallnz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNZ_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_abscallnc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNC_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_abscallz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CZ_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static const uint8_t cpu_abscallc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CC_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ret_arr[4] = { CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_reti_arr[4] = { CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_EI_GET_INSTRUCTION };
static const uint8_t cpu_retnz_arr[5] = { CPU_RET_NZ_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_retnc_arr[5] = { CPU_RET_NC_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_retz_arr[5] = { CPU_RET_Z_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_retc_arr[5] = { CPU_RET_C_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };

static const uint8_t cpu_rst00_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_00, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst08_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_08, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst10_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_10, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst18_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_18, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst20_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_20, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst28_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_28, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst30_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_30, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst38_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_38, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst40_arr[5] = { CPU_DELAY_CYCLE, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_40, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst48_arr[5] = { CPU_DELAY_CYCLE, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_48, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst50_arr[5] = { CPU_DELAY_CYCLE, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_50, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst58_arr[5] = { CPU_DELAY_CYCLE, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_58, CPU_GET_INSTRUCTION };
static const uint8_t cpu_rst60_arr[5] = { CPU_DELAY_CYCLE, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_60, CPU_GET_INSTRUCTION };

static const uint8_t cpu_push_bc_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_B_DEC, CPU_SP_WRITE8_C_DEC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_push_de_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_D_DEC, CPU_SP_WRITE8_E_DEC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_push_hl_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_H_DEC, CPU_SP_WRITE8_L_DEC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_push_af_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_A_DEC, CPU_SP_WRITE8_F_DEC, CPU_GET_INSTRUCTION };

static const uint8_t cpu_pop_bc_arr[3] = { CPU_TMP_READ8_SP_INC, CPU_C_FROM_TMP_B_READ8_SP_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_pop_de_arr[3] = { CPU_TMP_READ8_SP_INC, CPU_E_FROM_TMP_D_READ8_SP_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_pop_hl_arr[3] = { CPU_TMP_READ8_SP_INC, CPU_L_FROM_TMP_H_READ8_SP_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_pop_af_arr[3] = { CPU_TMP_READ8_SP_INC, CPU_F_FROM_TMP_A_READ8_SP_INC, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ld_a_arr[2] = { CPU_A_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_b_arr[2] = { CPU_B_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_c_arr[2] = { CPU_C_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_d_arr[2] = { CPU_D_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_e_arr[2] = { CPU_E_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_h_arr[2] = { CPU_H_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_l_arr[2] = { CPU_L_READ8_PC_INC, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ldhc_a_arr[2] = { CPU_C_READHIGH_A, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ldh_a_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_TMP_READHIGH_A, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ld_bc_arr[3] = { CPU_C_READ8_PC_INC, CPU_B_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_de_arr[3] = { CPU_E_READ8_PC_INC, CPU_D_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_hl_arr[3] = { CPU_L_READ8_PC_INC, CPU_H_READ8_PC_INC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_sp_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_SPL_FROM_TMP_SPH_READ8_PC_INC, CPU_GET_INSTRUCTION };

static const uint8_t cpu_ld16_a_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC, CPU_A_READ8_TMP16, CPU_GET_INSTRUCTION };

static const uint8_t cpu_st_a_arr[2] = { CPU_A_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_b_arr[2] = { CPU_B_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_c_arr[2] = { CPU_C_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_d_arr[2] = { CPU_D_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_e_arr[2] = { CPU_E_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_h_arr[2] = { CPU_H_ACTION_WRITE, CPU_GET_INSTRUCTION };
static const uint8_t cpu_st_l_arr[2] = { CPU_L_ACTION_WRITE, CPU_GET_INSTRUCTION };

static const uint8_t cpu_st_imm_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_ACTION_WRITE, CPU_GET_INSTRUCTION };

static const uint8_t cpu_sthc_a_arr[2] = { CPU_C_WRITEHIGH_A, CPU_GET_INSTRUCTION };
static const uint8_t cpu_sth_a_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_TMP_WRITEHIGH_A, CPU_GET_INSTRUCTION };

static const uint8_t cpu_st16_a_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC, CPU_A_ACTION_WRITE, CPU_GET_INSTRUCTION };

static const uint8_t cpu_st16_sp_arr[5] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC, CPU_TMP16_WRITE8_SPL_INC, CPU_TMP16_WRITE8_SPH, CPU_GET_INSTRUCTION };

static const uint8_t cpu_jr_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_jrnz_arr[3] = { CPU_TMP_READ8_PC_INC_JRNZ_CHK, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_jrz_arr[3] = { CPU_TMP_READ8_PC_INC_JRZ_CHK, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_jrnc_arr[3] = { CPU_TMP_READ8_PC_INC_JRNC_CHK, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };
static const uint8_t cpu_jrc_arr[3] = { CPU_TMP_READ8_PC_INC_JRC_CHK, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };

static const uint8_t cpu_di_arr[1] = { CPU_DI_GET_INSTRUCTION };
static const uint8_t cpu_ei_arr[1] = { CPU_GET_INSTRUCTION_EI };
static const uint8_t cpu_scf_arr[1] = { CPU_SCF_GET_INSTRUCTION };
static const uint8_t cpu_ccf_arr[1] = { CPU_CCF_GET_INSTRUCTION };

static const uint8_t cpu_add_bc_arr[2] = { CPU_BC_ACTION_ADD, CPU_GET_INSTRUCTION };
static const uint8_t cpu_add_de_arr[2] = { CPU_DE_ACTION_ADD, CPU_GET_INSTRUCTION };
static const uint8_t cpu_add_hl_arr[2] = { CPU_HL_ACTION_ADD, CPU_GET_INSTRUCTION };
static const uint8_t cpu_add_sp_arr[2] = { CPU_SP_ACTION_ADD, CPU_GET_INSTRUCTION };
static const uint8_t cpu_ld_hl_add_sp_imm_arr[3] = { CPU_TMP_READ8_PC_INC, CPU_HL_ADD_SPECIAL, CPU_GET_INSTRUCTION };
static const uint8_t cpu_add_sp_imm_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_SP_ADD_SPECIAL, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };

static inline void cpuSetNopArr()
{
	cpu_action_arr = cpu_nop_arr;
	cpu_arr_pos = 0;
}

static const uint8_t *cpu_instr_arr[256] = {
	cpu_nop_arr, cpu_ld_bc_arr, cpu_st_a_arr, cpu_imm_delay_arr, //0x00-0x03
	cpu_imm_b_arr, cpu_imm_b_arr, cpu_ld_b_arr, cpu_imm_a_arr, //0x04-0x07
	cpu_st16_sp_arr, cpu_add_bc_arr, cpu_imm_bc_arr, cpu_imm_delay_arr, //0x08-0x0B
	cpu_imm_c_arr, cpu_imm_c_arr, cpu_ld_c_arr, cpu_imm_a_arr, //0x0C-0x0F

	cpu_imm_arr, cpu_ld_de_arr, cpu_st_a_arr, cpu_imm_delay_arr, //0x10-0x13
	cpu_imm_d_arr, cpu_imm_d_arr, cpu_ld_d_arr, cpu_imm_a_arr, //0x14-0x17
	cpu_jr_arr, cpu_add_de_arr, cpu_imm_de_arr, cpu_imm_delay_arr, //0x18-0x1B
	cpu_imm_e_arr, cpu_imm_e_arr, cpu_ld_e_arr, cpu_imm_a_arr, //0x1C-0x1F

	cpu_jrnz_arr, cpu_ld_hl_arr, cpu_st_a_arr, cpu_imm_delay_arr, //0x20-0x23
	cpu_imm_h_arr, cpu_imm_h_arr, cpu_ld_h_arr, cpu_imm_a_arr, //0x24-0x27
	cpu_jrz_arr, cpu_add_hl_arr, cpu_imm_hl_inc_arr, cpu_imm_delay_arr, //0x28-0x2B
	cpu_imm_l_arr, cpu_imm_l_arr, cpu_ld_l_arr, cpu_imm_a_arr, //0x2C-0x2F

	cpu_jrnc_arr, cpu_ld_sp_arr, cpu_st_a_arr, cpu_imm_delay_arr, //0x30-0x33
	cpu_imm_hl_st_arr, cpu_imm_hl_st_arr, cpu_st_imm_arr, cpu_scf_arr, //0x34-0x37
	cpu_jrc_arr, cpu_add_sp_arr, cpu_imm_hl_dec_arr, cpu_imm_delay_arr, //0x38-0x3B
	cpu_imm_a_arr, cpu_imm_a_arr, cpu_ld_a_arr, cpu_ccf_arr, //0x3C-0x3F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x40-0x43
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x44-0x47
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x48-0x4B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x4C-0x4F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x50-0x53
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x54-0x57
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x58-0x5B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x5C-0x5F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x60-0x63
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x64-0x67
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x68-0x6B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x6C-0x6F

	cpu_st_b_arr, cpu_st_c_arr, cpu_st_d_arr, cpu_st_e_arr, //0x70-0x73
	cpu_st_h_arr, cpu_st_l_arr, cpu_imm_arr, cpu_st_a_arr, //0x74-0x77
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x78-0x7B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x7C-0x7F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x80-0x83
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x84-0x87
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x88-0x8B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x8C-0x8F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x90-0x93
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x94-0x97
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0x98-0x9B
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0x9C-0x9F

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0xA0-0xA3
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0xA4-0xA7
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0xA8-0xAB
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0xAC-0xAF

	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0xB0-0xB3
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0xB4-0xB7
	cpu_imm_b_arr, cpu_imm_c_arr, cpu_imm_d_arr, cpu_imm_e_arr, //0xB8-0xBB
	cpu_imm_h_arr, cpu_imm_l_arr, cpu_imm_hl_arr, cpu_imm_a_arr, //0xBC-0xBF

	cpu_retnz_arr, cpu_pop_bc_arr, cpu_absjmpnz_arr, cpu_absjmp_arr, //0xC0-0xC3
	cpu_abscallnz_arr, cpu_push_bc_arr, cpu_imm_pc_arr, cpu_rst00_arr, //0xC4-0xC7
	cpu_retz_arr, cpu_ret_arr, cpu_absjmpz_arr, cpu_sub_arr, //0xC8-0xCB
	cpu_abscallz_arr, cpu_abscall_arr, cpu_imm_pc_arr, cpu_rst08_arr, //0xCC-0xCF

	cpu_retnc_arr, cpu_pop_de_arr, cpu_absjmpnc_arr, NULL, //0xD0-0xD3 (0xD3=Invalid)
	cpu_abscallnc_arr, cpu_push_de_arr, cpu_imm_pc_arr, cpu_rst10_arr, //0xD4-0xD7
	cpu_retc_arr, cpu_reti_arr, cpu_absjmpc_arr, NULL, //0xD8-0xDB (0xDB=Invalid)
	cpu_abscallc_arr, NULL, cpu_imm_pc_arr, cpu_rst18_arr, //0xDC-0xDF (0xDD=Invalid)

	cpu_sth_a_arr, cpu_pop_hl_arr, cpu_sthc_a_arr, NULL, //0xE0-0xE3 (0xE3=Invalid)
	NULL, cpu_push_hl_arr, cpu_imm_pc_arr, cpu_rst20_arr, //0xE4-0xE7 (0xE4=Invalid)
	cpu_add_sp_imm_arr, cpu_hljmp_arr, cpu_st16_a_arr, NULL, //0xE8-0xEB (0xEB=Invalid)
	NULL, NULL, cpu_imm_pc_arr, cpu_rst28_arr, //0xEC-0xEF (0xEC=Invalid, 0xED=Invalid)

	cpu_ldh_a_arr, cpu_pop_af_arr, cpu_ldhc_a_arr, cpu_di_arr, //0xF0-0xF3
	NULL, cpu_push_af_arr, cpu_imm_pc_arr, cpu_rst30_arr, //0xF4-0xF7 (0xF4=Invalid)
	cpu_ld_hl_add_sp_imm_arr, cpu_sp_from_hl_arr, cpu_ld16_a_arr, cpu_ei_arr, //0xF8-0xFB
	NULL, NULL, cpu_imm_pc_arr, cpu_rst38_arr, //0xFC-0xFF (0xFC=Invalid, 0xFD=Invalid)
};

typedef void (*cpu_action_t)(uint8_t*);
static cpu_action_t cpu_actions_arr[256];
static cpu_action_t cpu_action_func;

static void cpuSetupActionArr()
{
	cpu_actions_arr[0x00] = cpuNoAction; cpu_actions_arr[0x01] = cpuNoAction; cpu_actions_arr[0x02] = cpuSTbc; cpu_actions_arr[0x03] = cpuBcInc;
	cpu_actions_arr[0x04] = cpuInc; cpu_actions_arr[0x05] = cpuDec; cpu_actions_arr[0x06] = cpuNoAction; cpu_actions_arr[0x07] = cpuRLCA;
	cpu_actions_arr[0x08] = cpuNoAction; cpu_actions_arr[0x09] = cpuNoAction; cpu_actions_arr[0x0A] = cpuLDa; cpu_actions_arr[0x0B] = cpuBcDec; 
	cpu_actions_arr[0x0C] = cpuInc; cpu_actions_arr[0x0D] = cpuDec; cpu_actions_arr[0x0E] = cpuNoAction; cpu_actions_arr[0x0F] = cpuRRCA;

	cpu_actions_arr[0x10] = cpuSTOP; cpu_actions_arr[0x11] = cpuNoAction; cpu_actions_arr[0x12] = cpuSTde; cpu_actions_arr[0x13] = cpuDeInc;
	cpu_actions_arr[0x14] = cpuInc; cpu_actions_arr[0x15] = cpuDec; cpu_actions_arr[0x16] = cpuNoAction; cpu_actions_arr[0x17] = cpuRLA;
	cpu_actions_arr[0x18] = cpuNoAction; cpu_actions_arr[0x19] = cpuNoAction; cpu_actions_arr[0x1A] = cpuLDa; cpu_actions_arr[0x1B] = cpuDeDec; 
	cpu_actions_arr[0x1C] = cpuInc; cpu_actions_arr[0x1D] = cpuDec; cpu_actions_arr[0x1E] = cpuNoAction; cpu_actions_arr[0x1F] = cpuRRA;

	cpu_actions_arr[0x20] = cpuNoAction; cpu_actions_arr[0x21] = cpuNoAction; cpu_actions_arr[0x22] = cpuSThlInc; cpu_actions_arr[0x23] = cpuHlInc;
	cpu_actions_arr[0x24] = cpuInc; cpu_actions_arr[0x25] = cpuDec; cpu_actions_arr[0x26] = cpuNoAction; cpu_actions_arr[0x27] = cpuDAA;
	cpu_actions_arr[0x28] = cpuNoAction; cpu_actions_arr[0x29] = cpuNoAction; cpu_actions_arr[0x2A] = cpuLDa; cpu_actions_arr[0x2B] = cpuHlDec; 
	cpu_actions_arr[0x2C] = cpuInc; cpu_actions_arr[0x2D] = cpuDec; cpu_actions_arr[0x2E] = cpuNoAction; cpu_actions_arr[0x2F] = cpuCPL;

	cpu_actions_arr[0x30] = cpuNoAction; cpu_actions_arr[0x31] = cpuNoAction; cpu_actions_arr[0x32] = cpuSThlDec; cpu_actions_arr[0x33] = cpuSpInc;
	cpu_actions_arr[0x34] = cpuInc; cpu_actions_arr[0x35] = cpuDec; cpu_actions_arr[0x36] = cpuSThl; cpu_actions_arr[0x37] = cpuNoAction;
	cpu_actions_arr[0x38] = cpuNoAction; cpu_actions_arr[0x39] = cpuNoAction; cpu_actions_arr[0x3A] = cpuLDa; cpu_actions_arr[0x3B] = cpuSpDec; 
	cpu_actions_arr[0x3C] = cpuInc; cpu_actions_arr[0x3D] = cpuDec; cpu_actions_arr[0x3E] = cpuNoAction; cpu_actions_arr[0x3F] = cpuNoAction;

	cpu_actions_arr[0x40] = cpuLDb; cpu_actions_arr[0x41] = cpuLDb; cpu_actions_arr[0x42] = cpuLDb; cpu_actions_arr[0x43] = cpuLDb;
	cpu_actions_arr[0x44] = cpuLDb; cpu_actions_arr[0x45] = cpuLDb; cpu_actions_arr[0x46] = cpuLDb; cpu_actions_arr[0x47] = cpuLDb;
	cpu_actions_arr[0x48] = cpuLDc; cpu_actions_arr[0x49] = cpuLDc; cpu_actions_arr[0x4A] = cpuLDc; cpu_actions_arr[0x4B] = cpuLDc; 
	cpu_actions_arr[0x4C] = cpuLDc; cpu_actions_arr[0x4D] = cpuLDc; cpu_actions_arr[0x4E] = cpuLDc; cpu_actions_arr[0x4F] = cpuLDc;

	cpu_actions_arr[0x50] = cpuLDd; cpu_actions_arr[0x51] = cpuLDd; cpu_actions_arr[0x52] = cpuLDd; cpu_actions_arr[0x53] = cpuLDd;
	cpu_actions_arr[0x54] = cpuLDd; cpu_actions_arr[0x55] = cpuLDd; cpu_actions_arr[0x56] = cpuLDd; cpu_actions_arr[0x57] = cpuLDd;
	cpu_actions_arr[0x58] = cpuLDe; cpu_actions_arr[0x59] = cpuLDe; cpu_actions_arr[0x5A] = cpuLDe; cpu_actions_arr[0x5B] = cpuLDe; 
	cpu_actions_arr[0x5C] = cpuLDe; cpu_actions_arr[0x5D] = cpuLDe; cpu_actions_arr[0x5E] = cpuLDe; cpu_actions_arr[0x5F] = cpuLDe;

	cpu_actions_arr[0x60] = cpuLDh; cpu_actions_arr[0x61] = cpuLDh; cpu_actions_arr[0x62] = cpuLDh; cpu_actions_arr[0x63] = cpuLDh;
	cpu_actions_arr[0x64] = cpuLDh; cpu_actions_arr[0x65] = cpuLDh; cpu_actions_arr[0x66] = cpuLDh; cpu_actions_arr[0x67] = cpuLDh;
	cpu_actions_arr[0x68] = cpuLDl; cpu_actions_arr[0x69] = cpuLDl; cpu_actions_arr[0x6A] = cpuLDl; cpu_actions_arr[0x6B] = cpuLDl; 
	cpu_actions_arr[0x6C] = cpuLDl; cpu_actions_arr[0x6D] = cpuLDl; cpu_actions_arr[0x6E] = cpuLDl; cpu_actions_arr[0x6F] = cpuLDl;

	cpu_actions_arr[0x70] = cpuSThl; cpu_actions_arr[0x71] = cpuSThl; cpu_actions_arr[0x72] = cpuSThl; cpu_actions_arr[0x73] = cpuSThl;
	cpu_actions_arr[0x74] = cpuSThl; cpu_actions_arr[0x75] = cpuSThl; cpu_actions_arr[0x76] = cpuHALT; cpu_actions_arr[0x77] = cpuSThl;
	cpu_actions_arr[0x78] = cpuLDa; cpu_actions_arr[0x79] = cpuLDa; cpu_actions_arr[0x7A] = cpuLDa; cpu_actions_arr[0x7B] = cpuLDa; 
	cpu_actions_arr[0x7C] = cpuLDa; cpu_actions_arr[0x7D] = cpuLDa; cpu_actions_arr[0x7E] = cpuLDa; cpu_actions_arr[0x7F] = cpuLDa;

	cpu_actions_arr[0x80] = cpuAdd8; cpu_actions_arr[0x81] = cpuAdd8; cpu_actions_arr[0x82] = cpuAdd8; cpu_actions_arr[0x83] = cpuAdd8;
	cpu_actions_arr[0x84] = cpuAdd8; cpu_actions_arr[0x85] = cpuAdd8; cpu_actions_arr[0x86] = cpuAdd8; cpu_actions_arr[0x87] = cpuAdd8;
	cpu_actions_arr[0x88] = cpuAdc8; cpu_actions_arr[0x89] = cpuAdc8; cpu_actions_arr[0x8A] = cpuAdc8; cpu_actions_arr[0x8B] = cpuAdc8; 
	cpu_actions_arr[0x8C] = cpuAdc8; cpu_actions_arr[0x8D] = cpuAdc8; cpu_actions_arr[0x8E] = cpuAdc8; cpu_actions_arr[0x8F] = cpuAdc8;

	cpu_actions_arr[0x90] = cpuSub8; cpu_actions_arr[0x91] = cpuSub8; cpu_actions_arr[0x92] = cpuSub8; cpu_actions_arr[0x93] = cpuSub8;
	cpu_actions_arr[0x94] = cpuSub8; cpu_actions_arr[0x95] = cpuSub8; cpu_actions_arr[0x96] = cpuSub8; cpu_actions_arr[0x97] = cpuSub8;
	cpu_actions_arr[0x98] = cpuSbc8; cpu_actions_arr[0x99] = cpuSbc8; cpu_actions_arr[0x9A] = cpuSbc8; cpu_actions_arr[0x9B] = cpuSbc8; 
	cpu_actions_arr[0x9C] = cpuSbc8; cpu_actions_arr[0x9D] = cpuSbc8; cpu_actions_arr[0x9E] = cpuSbc8; cpu_actions_arr[0x9F] = cpuSbc8;

	cpu_actions_arr[0xA0] = cpuAND; cpu_actions_arr[0xA1] = cpuAND; cpu_actions_arr[0xA2] = cpuAND; cpu_actions_arr[0xA3] = cpuAND;
	cpu_actions_arr[0xA4] = cpuAND; cpu_actions_arr[0xA5] = cpuAND; cpu_actions_arr[0xA6] = cpuAND; cpu_actions_arr[0xA7] = cpuAND;
	cpu_actions_arr[0xA8] = cpuXOR; cpu_actions_arr[0xA9] = cpuXOR; cpu_actions_arr[0xAA] = cpuXOR; cpu_actions_arr[0xAB] = cpuXOR; 
	cpu_actions_arr[0xAC] = cpuXOR; cpu_actions_arr[0xAD] = cpuXOR; cpu_actions_arr[0xAE] = cpuXOR; cpu_actions_arr[0xAF] = cpuXOR;

	cpu_actions_arr[0xB0] = cpuOR; cpu_actions_arr[0xB1] = cpuOR; cpu_actions_arr[0xB2] = cpuOR; cpu_actions_arr[0xB3] = cpuOR;
	cpu_actions_arr[0xB4] = cpuOR; cpu_actions_arr[0xB5] = cpuOR; cpu_actions_arr[0xB6] = cpuOR; cpu_actions_arr[0xB7] = cpuOR;
	cpu_actions_arr[0xB8] = cpuCmp8; cpu_actions_arr[0xB9] = cpuCmp8; cpu_actions_arr[0xBA] = cpuCmp8; cpu_actions_arr[0xBB] = cpuCmp8; 
	cpu_actions_arr[0xBC] = cpuCmp8; cpu_actions_arr[0xBD] = cpuCmp8; cpu_actions_arr[0xBE] = cpuCmp8; cpu_actions_arr[0xBF] = cpuCmp8;

	cpu_actions_arr[0xC0] = cpuNoAction; cpu_actions_arr[0xC1] = cpuNoAction; cpu_actions_arr[0xC2] = cpuNoAction; cpu_actions_arr[0xC3] = cpuNoAction;
	cpu_actions_arr[0xC4] = cpuNoAction; cpu_actions_arr[0xC5] = cpuNoAction; cpu_actions_arr[0xC6] = cpuAdd8; cpu_actions_arr[0xC7] = cpuNoAction;
	cpu_actions_arr[0xC8] = cpuNoAction; cpu_actions_arr[0xC9] = cpuNoAction; cpu_actions_arr[0xCA] = cpuNoAction; cpu_actions_arr[0xCB] = cpuNoAction; 
	cpu_actions_arr[0xCC] = cpuNoAction; cpu_actions_arr[0xCD] = cpuNoAction; cpu_actions_arr[0xCE] = cpuAdc8; cpu_actions_arr[0xCF] = cpuNoAction;

	cpu_actions_arr[0xD0] = cpuNoAction; cpu_actions_arr[0xD1] = cpuNoAction; cpu_actions_arr[0xD2] = cpuNoAction; cpu_actions_arr[0xD3] = cpuNoAction;
	cpu_actions_arr[0xD4] = cpuNoAction; cpu_actions_arr[0xD5] = cpuNoAction; cpu_actions_arr[0xD6] = cpuSub8; cpu_actions_arr[0xD7] = cpuNoAction;
	cpu_actions_arr[0xD8] = cpuNoAction; cpu_actions_arr[0xD9] = cpuNoAction; cpu_actions_arr[0xDA] = cpuNoAction; cpu_actions_arr[0xDB] = cpuNoAction; 
	cpu_actions_arr[0xDC] = cpuNoAction; cpu_actions_arr[0xDD] = cpuNoAction; cpu_actions_arr[0xDE] = cpuSbc8; cpu_actions_arr[0xDF] = cpuNoAction;

	cpu_actions_arr[0xE0] = cpuNoAction; cpu_actions_arr[0xE1] = cpuNoAction; cpu_actions_arr[0xE2] = cpuNoAction; cpu_actions_arr[0xE3] = cpuNoAction;
	cpu_actions_arr[0xE4] = cpuNoAction; cpu_actions_arr[0xE5] = cpuNoAction; cpu_actions_arr[0xE6] = cpuAND; cpu_actions_arr[0xE7] = cpuNoAction;
	cpu_actions_arr[0xE8] = cpuNoAction; cpu_actions_arr[0xE9] = cpuNoAction; cpu_actions_arr[0xEA] = cpuSTt16; cpu_actions_arr[0xEB] = cpuNoAction; 
	cpu_actions_arr[0xEC] = cpuNoAction; cpu_actions_arr[0xED] = cpuNoAction; cpu_actions_arr[0xEE] = cpuXOR; cpu_actions_arr[0xEF] = cpuNoAction;

	cpu_actions_arr[0xF0] = cpuNoAction; cpu_actions_arr[0xF1] = cpuNoAction; cpu_actions_arr[0xF2] = cpuNoAction; cpu_actions_arr[0xF3] = cpuNoAction;
	cpu_actions_arr[0xF4] = cpuNoAction; cpu_actions_arr[0xF5] = cpuNoAction; cpu_actions_arr[0xF6] = cpuOR; cpu_actions_arr[0xF7] = cpuNoAction;
	cpu_actions_arr[0xF8] = cpuNoAction; cpu_actions_arr[0xF9] = cpuNoAction; cpu_actions_arr[0xFA] = cpuNoAction; cpu_actions_arr[0xFB] = cpuNoAction; 
	cpu_actions_arr[0xFC] = cpuNoAction; cpu_actions_arr[0xFD] = cpuNoAction; cpu_actions_arr[0xFE] = cpuCmp8; cpu_actions_arr[0xFF] = cpuNoAction;

	cpu_action_func = cpuNoAction;
}

bool cpuHandleIrqUpdates()
{
	if(gbEmuGBSPlayback) return false;
	if(!irqEnable) return false;
	uint8_t irqList = (memGetCurIrqList());
	if(irqList & 1)
	{
		memClearCurIrqList(1);
		//printf("Beep Bop Interrupt 40 and jmp from %04x\n", pc);
		cpu_action_arr = cpu_rst40_arr;
		irqEnable = false;
		return true;
	}
	else if(irqList & 2)
	{
		memClearCurIrqList(2);
		//printf("Beep Bop Interrupt 48 and jmp from %04x\n", pc);
		cpu_action_arr = cpu_rst48_arr;
		irqEnable = false;
		return true;
	}
	else if(irqList & 4)
	{
		memClearCurIrqList(4);
		//printf("Beep Bop Interrupt 50 and jmp from %04x\n", pc);
		cpu_action_arr = cpu_rst50_arr;
		irqEnable = false;
		return true;
	}
	else if(irqList & 8)
	{
		memClearCurIrqList(8);
		//printf("Beep Bop Interrupt 58 and jmp from %04x\n", pc);
		cpu_action_arr = cpu_rst58_arr;
		irqEnable = false;
		return true;
	}
	else if(irqList & 0x10)
	{
		memClearCurIrqList(0x10);
		//printf("Beep Bop Interrupt 60 and jmp from %04x\n", pc);
		cpu_action_arr = cpu_rst60_arr;
		irqEnable = false;
		return true;
	}
	return false;
}

void cpuGetInstruction()
{
	if(cpuHandleIrqUpdates())
	{
		cpuHaltLoop = false;
		cpu_arr_pos = 0;
		return;
	}
	if(gbEmuGBSPlayback)
	{
		//init return
		if(pc == 0x8764)
		{
			//if(!gbsInitRet)
			//	printf("Init return\n");
			gbsInitRet = true; //allow play call
			cpuSetNopArr();
			return;
		} //play return
		else if(pc == 0x8765)
		{
			//if(!gbsPlayRet)
			//	printf("Play return\n");
			gbsPlayRet = true; //allow next play call
			cpuSetNopArr();
			return;
		}
	}
	if(cpuHaltLoop)
	{
		cpuSetNopArr();
		//happens when IME=0
		if(!irqEnable && memGetCurIrqList())
			cpuHaltLoop = false;
		else //keep waiting
			return;
	}
	if(cpuStopLoop)
	{
		bool cpuInAny = inputAny();
		if(cpuInAny && !cpuPrevInAny)
			cpuStopLoop = false;
		cpuPrevInAny = cpuInAny;
		cpuSetNopArr();
		return;
	}
	curInstr = memGet8(pc);
	cpu_action_arr = cpu_instr_arr[curInstr];
	cpu_arr_pos = 0;
	if(cpu_action_arr == NULL)
	{
		printf("CPU Error: Unsupported Instruction at %04x:%02x!\n", pc-1,curInstr);
		cpuSetNopArr();
		cpuSetStopLoop();
	}
	cpu_action_func = cpu_actions_arr[curInstr];

	//if(pc==0xABC || pc == 0xAC1 || pc == 0x5E0E || pc == 0x5E0F)
	//	printf("%04x %02x a %02x b %02x hl %04x\n", pc, curInstr, a, b, (l|(h<<8)));
	//HALT bug: PC doesnt increase after instruction is parsed!
	if(!cpuHaltBug) pc++;
	cpuHaltBug = false;
}

static void cpuHandleOAMDMA()
{
	if(cpu_oam_dma)
	{
		if(!cpu_oam_dma_started)
			cpu_oam_dma_started = true;
		else
		{
			cpu_oam_dma = false;
			cpu_oam_dma_started = false;
			cpu_oam_dma_running = true;
			cpu_oam_dma_pos = 0;
		}
	}
	if(cpu_oam_dma_running)
	{
		if(cpu_oam_dma_pos == 0xA0)
			cpu_oam_dma_running = false;
		else
		{
			//printf("OAM Copying %02x\n", cpu_oam_dma_pos);
			ppuSetOAMDMAVal(cpu_oam_dma_pos,memGet8(cpu_oam_dma_addr+cpu_oam_dma_pos));
			cpu_oam_dma_pos++;
		}
	}
}

/* Main CPU Interpreter */

void cpuCycle()
{
	if(cpuDmaHalt)
		return;
	cpuHandleOAMDMA();
	uint8_t cpu_action, sub_instr;
	cpu_action = cpu_action_arr[cpu_arr_pos];
	cpu_arr_pos++;
	switch(cpu_action)
	{
		case CPU_GET_INSTRUCTION:
			cpuGetInstruction();
			break;
		case CPU_GET_SUBINSTRUCTION:
			sub_instr = memGet8(pc++);
			//set sub array
			switch(sub_instr&7)
			{
				case 0:
					cpu_action_arr = cpu_imm_b_arr;
					break;
				case 1:
					cpu_action_arr = cpu_imm_c_arr;
					break;
				case 2:
					cpu_action_arr = cpu_imm_d_arr;
					break;
				case 3:
					cpu_action_arr = cpu_imm_e_arr;
					break;
				case 4:
					cpu_action_arr = cpu_imm_h_arr;
					break;
				case 5:
					cpu_action_arr = cpu_imm_l_arr;
					break;
				case 6:
					if((sub_instr&0xC0) == 0x40) //BIT only reads
						cpu_action_arr = cpu_imm_hl_arr;
					else //all other CB instructions do I/O
						cpu_action_arr = cpu_imm_hl_st_arr;
					break;
				case 7:
					cpu_action_arr = cpu_imm_a_arr;
					break;
				default: //should never happen
					printf("CPU Error: Unknown sub %02x\n", sub_instr);
					cpuSetNopArr();
					cpuSetStopLoop();
					break;
			}
			//set sub func
			switch(sub_instr>>3)
			{
				case 0:
					cpu_action_func = cpuRLC;
					break;
				case 1:
					cpu_action_func = cpuRRC;
					break;
				case 2:
					cpu_action_func = cpuRL;
					break;
				case 3:
					cpu_action_func = cpuRR;
					break;
				case 4:
					cpu_action_func = cpuSLA;
					break;
				case 5:
					cpu_action_func = cpuSRA;
					break;
				case 6:
					cpu_action_func = cpuSWAP;
					break;
				case 7:
					cpu_action_func = cpuSRL;
					break;
				case  8: case  9: case 10: case 11:
				case 12: case 13: case 14: case 15:
					sub_in_val = ((sub_instr&0x3F)>>3)&7;
					cpu_action_func = cpuBIT;
					break;
				case 16: case 17: case 18: case 19:
				case 20: case 21: case 22: case 23:
					sub_in_val = ((sub_instr&0x3F)>>3)&7;
					cpu_action_func = cpuRES;
					break;
				default:
					sub_in_val = ((sub_instr&0x3F)>>3)&7;
					cpu_action_func = cpuSET;
					break;
			}
			cpu_arr_pos = 0;
			break;
		case CPU_DELAY_CYCLE:
			break;
		case CPU_ACTION_GET_INSTRUCTION:
			cpu_action_func(&cpuTmp);
			cpuGetInstruction();
			break;
		case CPU_A_ACTION_GET_INSTRUCTION:
			cpu_action_func(&a);
			cpuGetInstruction();
			break;
		case CPU_B_ACTION_GET_INSTRUCTION:
			cpu_action_func(&b);
			cpuGetInstruction();
			break;
		case CPU_C_ACTION_GET_INSTRUCTION:
			cpu_action_func(&c);
			cpuGetInstruction();
			break;
		case CPU_D_ACTION_GET_INSTRUCTION:
			cpu_action_func(&d);
			cpuGetInstruction();
			break;
		case CPU_E_ACTION_GET_INSTRUCTION:
			cpu_action_func(&e);
			cpuGetInstruction();
			break;
		case CPU_H_ACTION_GET_INSTRUCTION:
			cpu_action_func(&h);
			cpuGetInstruction();
			break;
		case CPU_L_ACTION_GET_INSTRUCTION:
			cpu_action_func(&l);
			cpuGetInstruction();
			break;
		case CPU_ACTION_WRITE:
			cpu_action_func(&cpuTmp);
			break;
		case CPU_A_ACTION_WRITE:
			cpu_action_func(&a);
			break;
		case CPU_B_ACTION_WRITE:
			cpu_action_func(&b);
			break;
		case CPU_C_ACTION_WRITE:
			cpu_action_func(&c);
			break;
		case CPU_D_ACTION_WRITE:
			cpu_action_func(&d);
			break;
		case CPU_E_ACTION_WRITE:
			cpu_action_func(&e);
			break;
		case CPU_H_ACTION_WRITE:
			cpu_action_func(&h);
			break;
		case CPU_L_ACTION_WRITE:
			cpu_action_func(&l);
			break;
		case CPU_BC_ACTION_ADD:
			cpuAdd16(c|b<<8);
			break;
		case CPU_DE_ACTION_ADD:
			cpuAdd16(e|d<<8);
			break;
		case CPU_HL_ACTION_ADD:
			cpuAdd16(l|h<<8);
			break;
		case CPU_SP_ACTION_ADD:
			cpuAdd16(sp);
			break;
		case CPU_HL_ADD_SPECIAL:
			cpuTmp16 = cpuAddSp16(cpuTmp);
			h = cpuTmp16>>8;
			l = cpuTmp16&0xFF;
			break;
		case CPU_SP_ADD_SPECIAL:
			sp = cpuAddSp16(cpuTmp);
			break;
		case CPU_ACTION_WRITE8_HL:
			cpu_action_func(&cpuTmp);
			memSet8(l | h<<8, cpuTmp);
			break;
		case CPU_TMP_ADD_PC:
			pc += (int8_t)cpuTmp;
			break;
		case CPU_TMP_READ8_BC:
			cpuTmp = memGet8(c | b<<8);
			break;
		case CPU_TMP_READ8_DE:
			cpuTmp = memGet8(e | d<<8);
			break;
		case CPU_TMP_READ8_HL:
			cpuTmp = memGet8(l | h<<8);
			break;
		case CPU_TMP_READ8_HL_INC:
			cpuTmp = memGet8(l | h<<8);
			cpuHlInc();
			break;
		case CPU_TMP_READ8_HL_DEC:
			cpuTmp = memGet8(l | h<<8);
			cpuHlDec();
			break;
		case CPU_TMP_READ8_PC_INC:
			cpuTmp = memGet8(pc++);
			break;
		case CPU_TMP_READ8_PC_INC_JRNZ_CHK:
			cpuTmp = memGet8(pc++);
			if(f & P_FLAG_Z) cpu_arr_pos++;
			break;
		case CPU_TMP_READ8_PC_INC_JRZ_CHK:
			cpuTmp = memGet8(pc++);
			if(!(f & P_FLAG_Z)) cpu_arr_pos++;
			break;
		case CPU_TMP_READ8_PC_INC_JRNC_CHK:
			cpuTmp = memGet8(pc++);
			if(f & P_FLAG_C) cpu_arr_pos++;
			break;
		case CPU_TMP_READ8_PC_INC_JRC_CHK:
			cpuTmp = memGet8(pc++);
			if(!(f & P_FLAG_C)) cpu_arr_pos++;
			break;
		case CPU_TMP_READ8_SP_INC:
			cpuTmp = memGet8(sp++);
			break;
		case CPU_PCL_FROM_TMP_PCH_READ8_SP_INC:
			pc = (cpuTmp | (memGet8(sp++)<<8));
			break;
		case CPU_C_FROM_TMP_B_READ8_SP_INC:
			c = cpuTmp;
			b = memGet8(sp++);
			break;
		case CPU_E_FROM_TMP_D_READ8_SP_INC:
			e = cpuTmp;
			d = memGet8(sp++);
			break;
		case CPU_L_FROM_TMP_H_READ8_SP_INC:
			l = cpuTmp;
			h = memGet8(sp++);
			break;
		case CPU_F_FROM_TMP_A_READ8_SP_INC:
			f = cpuTmp&0xF0;
			a = memGet8(sp++);
			break;
		case CPU_TMP_READHIGH_A:
			a = memGet8(0xFF00 | cpuTmp);
			break;
		case CPU_TMP_WRITEHIGH_A:
			memSet8(0xFF00 | cpuTmp, a);
			break;
		case CPU_C_READHIGH_A:
			a = memGet8(0xFF00 | c);
			break;
		case CPU_C_WRITEHIGH_A:
			memSet8(0xFF00 | c, a);
			break;
		case CPU_SP_FROM_HL:
			sp = (l | h<<8);
			break;
		case CPU_SP_WRITE8_A_DEC:
			sp--;
			memSet8(sp, a);
			break;
		case CPU_SP_WRITE8_B_DEC:
			sp--;
			memSet8(sp, b);
			break;
		case CPU_SP_WRITE8_C_DEC:
			sp--;
			memSet8(sp, c);
			break;
		case CPU_SP_WRITE8_D_DEC:
			sp--;
			memSet8(sp, d);
			break;
		case CPU_SP_WRITE8_E_DEC:
			sp--;
			memSet8(sp, e);
			break;
		case CPU_SP_WRITE8_F_DEC:
			sp--;
			memSet8(sp, f);
			break;
		case CPU_SP_WRITE8_H_DEC:
			sp--;
			memSet8(sp, h);
			break;
		case CPU_SP_WRITE8_L_DEC:
			sp--;
			memSet8(sp, l);
			break;
		case CPU_SP_WRITE8_PCH_DEC:
			sp--;
			memSet8(sp, pc>>8);
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = cpuTmp16;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_00:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x00+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_08:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x08+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_10:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x10+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_18:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x18+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_20:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x20+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_28:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x28+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_30:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x30+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_38:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x38+gbsLoadAddr;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_40:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x40;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_48:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x48;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_50:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x50;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_58:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x58;
			break;
		case CPU_SP_WRITE8_PCL_DEC_PC_FROM_60:
			sp--;
			memSet8(sp, pc&0xFF);
			pc = 0x60;
			break;
		case CPU_A_READ8_TMP16:
			a = memGet8(cpuTmp16);
			break;
		case CPU_A_READ8_PC_INC:
			a = memGet8(pc++);
			break;
		case CPU_B_READ8_PC_INC:
			b = memGet8(pc++);
			break;
		case CPU_C_READ8_PC_INC:
			c = memGet8(pc++);
			break;
		case CPU_D_READ8_PC_INC:
			d = memGet8(pc++);
			break;
		case CPU_E_READ8_PC_INC:
			e = memGet8(pc++);
			break;
		case CPU_L_READ8_PC_INC:
			l = memGet8(pc++);
			break;
		case CPU_H_READ8_PC_INC:
			h = memGet8(pc++);
			break;
		case CPU_PCL_FROM_TMP_PCH_READ8_PC:
			pc = (cpuTmp | (memGet8(pc)<<8));
			break;
		case CPU_SPL_FROM_TMP_SPH_READ8_PC_INC:
			sp = (cpuTmp | (memGet8(pc++)<<8));
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNZ_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(f & P_FLAG_Z) cpu_arr_pos+=3;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNC_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(f & P_FLAG_C) cpu_arr_pos+=3;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CZ_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(!(f & P_FLAG_Z)) cpu_arr_pos+=3;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CC_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(!(f & P_FLAG_C)) cpu_arr_pos+=3;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNZ_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(f & P_FLAG_Z) cpu_arr_pos++;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPNC_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(f & P_FLAG_C) cpu_arr_pos++;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPZ_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(!(f & P_FLAG_Z)) cpu_arr_pos++;
			break;
		case CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPC_CHK:
			cpuTmp16 = (cpuTmp | (memGet8(pc++)<<8));
			if(!(f & P_FLAG_C)) cpu_arr_pos++;
			break;
		case CPU_TMP16_WRITE8_SPL_INC:
			memSet8(cpuTmp16++, sp&0xFF);
			break;
		case CPU_TMP16_WRITE8_SPH:
			memSet8(cpuTmp16, sp>>8);
			break;
		case CPU_DI_GET_INSTRUCTION:
			//printf("Disabled IRQs at %04x\n", pc);
			irqEnable = false;
			cpuGetInstruction();
			break;
		case CPU_EI_GET_INSTRUCTION:
			irqEnable = true;
			//printf("Enabled IRQs and jmp to %04x ",pc);
			cpuGetInstruction();
			//printf("%04x\n",pc);
			break;
		case CPU_GET_INSTRUCTION_EI:
			//printf("Enabled IRQs and jmp to %04x ",pc);
			cpuGetInstruction();
			//printf("%04x\n",pc);
			irqEnable = true;
			break;
		case CPU_SCF_GET_INSTRUCTION:
			f |= P_FLAG_C;
			f &= ~(P_FLAG_H|P_FLAG_N);
			cpuGetInstruction();
			break;
		case CPU_CCF_GET_INSTRUCTION:
			f ^= P_FLAG_C;
			f &= ~(P_FLAG_H|P_FLAG_N);
			cpuGetInstruction();
			break;
		case CPU_PC_FROM_HL_GET_INSTRUCTION:
			pc = (l|(h<<8));
			cpuGetInstruction();
			break;
		case CPU_PC_FROM_T16:
			pc = cpuTmp16;
			break;
		case CPU_RET_NZ_CHK:
			if(f & P_FLAG_Z) cpu_arr_pos+=3;
			break;
		case CPU_RET_NC_CHK:
			if(f & P_FLAG_C) cpu_arr_pos+=3;
			break;
		case CPU_RET_Z_CHK:
			if(!(f & P_FLAG_Z)) cpu_arr_pos+=3;
			break;
		case CPU_RET_C_CHK:
			if(!(f & P_FLAG_C)) cpu_arr_pos+=3;
			break;
	}
}

uint16_t cpuCurPC()
{
	return pc;
}

void cpuSetSpeed(bool cgb)
{
	if(cgb)
	{
		//printf("CPU: CGB Speed\n");
		cpuCgbSpeed = true;
		cpuAddSpeed = 2;
		cpuTimer = 1;
	}
	else
	{
		//printf("CPU: DMG Speed\n");
		cpuCgbSpeed = false;
		cpuAddSpeed = 1;
		cpuTimer = 3;
	}
}

void cpuPlayGBS()
{
	if(!gbsInitRet || !gbsPlayRet)
		return;
	gbsPlayRet = false;
	//push back detect pc
	sp--;
	memSet8(sp, 0x87);
	sp--;
	memSet8(sp, 0x65);
	//IMPORTANT: some GBS files dont work without this
	//Keep HL though, some GBS files use them as global address
	a = 0, b = 0, c = 0, d = 0, e = 0;//, h = 0, l = 0;
	//jump to play
	pc = gbsPlayAddr;
	cpuSetNopArr();
	//printf("Playback Start at %04x\n", pc);
}
extern uint8_t gbsTMA, gbsTAC;
void cpuLoadGBS(uint8_t song)
{
	//full reset
	cpuInit();
	ppuInit();
	apuInit();
	inputInit();
	memInit(false,true);
	memSet8(0xFF06,gbsTMA);
	memSet8(0xFF07,gbsTAC);
	//set requested sp
	sp = gbsSP;
	//push back detect pc
	sp--;
	memSet8(sp, 0x87);
	sp--;
	memSet8(sp, 0x64);
	//IMPORTANT: some GBS files dont work without this
	a = 0, b = 0, c = 0, d = 0, e = 0, h = 0, l = 0;
	//set song and init routine
	a = song;
	pc = gbsInitAddr;
	//start getting instructions
	cpuSetNopArr();
	//printf("Init Start at %04x\n", pc);
}
