namespace BizHawk.Emulation.Cores.Components.Z80A
{
	public partial class Z80A
	{
		// this contains the vectors of instrcution operations
		// NOTE: This list is NOT confirmed accurate for each individual cycle

		private void NOP_()
		{
			PopulateCURINSTR
				(IDLE);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		// NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers
		// but it's simpler to emulate just by exchanging the register with it's shadow
		private void EXCH_()
		{
			PopulateCURINSTR
				(EXCH);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void EXX_()
		{
			PopulateCURINSTR
				(EXX);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		// this exchanges 2 16 bit registers
		private void EXCH_16_(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
				(EXCH_16, dest_l, dest_h, src_l, src_h);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void INC_16(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
				(INC16, src_l, src_h,
						IDLE,
						IDLE);

			PopulateBUSRQ(0, I, I);
			PopulateMEMRQ(0, 0, 0);
			IRQS = 3;
		}


		private void DEC_16(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
				(DEC16, src_l, src_h,
						IDLE,
						IDLE);

			PopulateBUSRQ(0, I, I);
			PopulateMEMRQ(0, 0, 0);
			IRQS = 3;
		}

		// this is done in two steps technically, but the flags don't work out using existing funcitons
		// so let's use a different function since it's an internal operation anyway
		private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
				(IDLE,
						TR16, Z, W, dest_l, dest_h,
						IDLE,
						INC16, Z, W,
						IDLE,
						ADD16, dest_l, dest_h, src_l, src_h,
						IDLE,
						IDLE);

			PopulateBUSRQ(0, I, I, I, I, I, I, I);
			PopulateMEMRQ(0, 0, 0, 0, 0, 0, 0, 0);
			IRQS = 8;
		}

		private void REG_OP(ushort operation, ushort dest, ushort src)
		{
			PopulateCURINSTR
				(operation, dest, src);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		// Operations using the I and R registers take one T-cycle longer
		private void REG_OP_IR(ushort operation, ushort dest, ushort src)
		{
			PopulateCURINSTR
				(IDLE,
						SET_FL_IR, dest, src);

			PopulateBUSRQ(0, I);
			PopulateMEMRQ(0, 0);
			IRQS = 2;
		}

		// note: do not use DEC here since no flags are affected by this operation
		private void DJNZ_()
		{
			if ((Regs[B] - 1) != 0)
			{
				PopulateCURINSTR
				(IDLE,
							IDLE,
							ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							IDLE,
							ASGN, W, 0,
							ADDS, PCl, PCh, Z, W,
							TR16, Z, W, PCl, PCh);

				PopulateBUSRQ(0, I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh);
				PopulateMEMRQ(0, 0, PCh, 0, 0, 0, 0, 0, 0, 0);
				IRQS = 10;
			}
			else
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
							WAIT,
							RD_INC, ALU, PCl, PCh);

				PopulateBUSRQ(0, I, PCh, 0, 0);
				PopulateMEMRQ(0, 0, PCh, 0, 0);
				IRQS = 5;
			}
		}

		private void HALT_()
		{
			PopulateCURINSTR
					(HALT);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void JR_COND(bool cond)
		{
			if (cond)
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							ASGN, W, 0,
							IDLE,
							ADDS, PCl, PCh, Z, W,
							TR16, Z, W, PCl, PCh);

				PopulateBUSRQ(0, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh);
				PopulateMEMRQ(0, PCh, 0, 0, 0, 0, 0, 0, 0);
				IRQS = 9;
			}
			else
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, ALU, PCl, PCh);

				PopulateBUSRQ(0, PCh, 0, 0);
				PopulateMEMRQ(0, PCh, 0, 0);
				IRQS = 4;
			}
		}

		private void JP_COND(bool cond)
		{
			if (cond)
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							WAIT,
							RD_INC_TR_PC, Z, W, PCl, PCh);

				PopulateBUSRQ(0, PCh, 0, 0, PCh, 0, 0);
				PopulateMEMRQ(0, PCh, 0, 0, PCh, 0, 0);
				IRQS = 7;
			}
			else
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							WAIT,
							RD_INC, W, PCl, PCh);

				PopulateBUSRQ(0, PCh, 0, 0, PCh, 0, 0);
				PopulateMEMRQ(0, PCh, 0, 0, PCh, 0, 0);
				IRQS = 7;
			}
		}

		private void RET_()
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						RD_INC, Z, SPl, SPh,
						IDLE,
						WAIT,
						RD_INC_TR_PC, Z, W, SPl, SPh);

			PopulateBUSRQ(0, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 7;
		}

		private void RETI_()
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						RD_INC, Z, SPl, SPh,
						IDLE,
						WAIT,
						RD_INC_TR_PC, Z, W, SPl, SPh);

			PopulateBUSRQ(0, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 7;
		}

		private void RETN_()
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						RD_INC, Z, SPl, SPh,
						EI_RETN,
						WAIT,
						RD_INC_TR_PC, Z, W, SPl, SPh);

			PopulateBUSRQ(0, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 7;
		}


		private void RET_COND(bool cond)
		{
			if (cond)
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, SPl, SPh,
							IDLE,
							WAIT,
							RD_INC_TR_PC, Z, W, SPl, SPh);

				PopulateBUSRQ(0, I, SPh, 0, 0, SPh, 0, 0);
				PopulateMEMRQ(0, 0, SPh, 0, 0, SPh, 0, 0);
				IRQS = 8;
			}
			else
			{
				PopulateCURINSTR
					(IDLE,
							IDLE);

				PopulateBUSRQ(0, I);
				PopulateMEMRQ(0, 0);
				IRQS = 2;
			}
		}

		private void CALL_COND(bool cond)
		{
			if (cond)
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							WAIT,
							RD, W, PCl, PCh,
							INC16, PCl, PCh,
							DEC16, SPl, SPh,
							WAIT,
							WR_DEC, SPl, SPh, PCh,
							IDLE,
							WAIT,
							WR_TR_PC, SPl, SPh, PCl);

				PopulateBUSRQ(0, PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0);
				PopulateMEMRQ(0, PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0);
				IRQS = 14;
			}
			else
			{
				PopulateCURINSTR
					(IDLE,
							IDLE,
							WAIT,
							RD_INC, Z, PCl, PCh,
							IDLE,
							WAIT,
							RD_INC, W, PCl, PCh);

				PopulateBUSRQ(0, PCh, 0, 0, PCh, 0, 0);
				PopulateMEMRQ(0, PCh, 0, 0, PCh, 0, 0);
				IRQS = 7;
			}
		}

		private void INT_OP(ushort operation, ushort src)
		{
			PopulateCURINSTR
					(operation, src);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void BIT_OP(ushort operation, ushort bit, ushort src)
		{
			PopulateCURINSTR
					(operation, bit, src);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void PUSH_(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
					(IDLE,
						DEC16, SPl, SPh,
						IDLE,
						WAIT,
						WR_DEC, SPl, SPh, src_h,
						IDLE,
						WAIT,
						WR, SPl, SPh, src_l);

			PopulateBUSRQ(0, I, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, 0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 8;
		}


		private void POP_(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						RD_INC, src_l, SPl, SPh,
						IDLE,
						WAIT,
						RD_INC, src_h, SPl, SPh);

			PopulateBUSRQ(0, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 7;
		}

		private void RST_(ushort n)
		{
			PopulateCURINSTR
					(IDLE,
						DEC16, SPl, SPh,
						IDLE,
						WAIT,
						WR_DEC, SPl, SPh, PCh,
						RST, n,
						WAIT,
						WR_TR_PC, SPl, SPh, PCl);

			PopulateBUSRQ(0, I, SPh, 0, 0, SPh, 0, 0);
			PopulateMEMRQ(0, 0, SPh, 0, 0, SPh, 0, 0);
			IRQS = 8;
		}

		private void PREFIX_(ushort src)
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						PREFIX);

			PRE_SRC = src;

			PopulateBUSRQ(0, PCh, 0, 0);
			PopulateMEMRQ(0, PCh, 0, 0);
			IRQS = -1; // prefix does not get interrupted
		}

		private void PREFETCH_(ushort src)
		{
			if (src == IXCBpre)
			{
				Regs[W] = Regs[Ixh];
				Regs[Z] = Regs[Ixl];
			}
			else
			{
				Regs[W] = Regs[Iyh];
				Regs[Z] = Regs[Iyl];
			}

			PopulateCURINSTR
					(IDLE,
						IDLE,
						WAIT,
						RD_INC, ALU, PCl, PCh,
						ADDS, Z, W, ALU, ZERO,
						WAIT,
						IDLE,
						PREFIX);

			PRE_SRC = src;

			//Console.WriteLine(TotalExecutedCycles);

			PopulateBUSRQ(0, PCh, 0, 0, PCh, 0, 0, PCh);
			PopulateMEMRQ(0, PCh, 0, 0, PCh, 0, 0, 0);
			IRQS = -1; // prefetch does not get interrupted
		}

		private void DI_()
		{
			PopulateCURINSTR
					(DI);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void EI_()
		{
			PopulateCURINSTR
					(EI);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void JP_16(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
					(TR16, PCl, PCh, src_l, src_h);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void LD_SP_16(ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						TR16, SPl, SPh, src_l, src_h);

			PopulateBUSRQ(0, I, I);
			PopulateMEMRQ(0, 0, 0);
			IRQS = 3;
		}

		private void OUT_()
		{
			PopulateCURINSTR
					(IDLE,
						TR, W, A,
						WAIT,
						RD_INC, Z, PCl, PCh,
						TR, ALU, A,
						WAIT,
						WAIT,
						OUT_INC, Z, ALU, A);

			PopulateBUSRQ(0, PCh, 0, 0, WIO1, WIO2, WIO3, WIO4);
			PopulateMEMRQ(0, PCh, 0, 0, WIO1, WIO2, WIO3, WIO4);
			IRQS = 8;
		}

		private void OUT_REG_(ushort dest, ushort src)
		{
			PopulateCURINSTR
					(IDLE,
						TR16, Z, W, C, B,
						IDLE,
						IDLE,
						OUT_INC, Z, W, src);

			PopulateBUSRQ(0, BIO1, BIO2, BIO3, BIO4);
			PopulateMEMRQ(0, BIO1, BIO2, BIO3, BIO4);
			IRQS = 5;
		}

		private void IN_()
		{
			PopulateCURINSTR
					(IDLE,
						TR, W, A,
						WAIT,
						RD_INC, Z, PCl, PCh,
						IDLE,
						WAIT,
						WAIT,
						IN_A_N_INC, A, Z, W);

			PopulateBUSRQ(0, PCh, 0, 0, WIO1, WIO2, WIO3, WIO4);
			PopulateMEMRQ(0, PCh, 0, 0, WIO1, WIO2, WIO3, WIO4);
			IRQS = 8;
		}

		private void IN_REG_(ushort dest, ushort src)
		{
			PopulateCURINSTR
					(IDLE,
						TR16, Z, W, C, B,
						WAIT,
						WAIT,
						IN_INC, dest, Z, W);

			PopulateBUSRQ(0, BIO1, BIO2, BIO3, BIO4);
			PopulateMEMRQ(0, BIO1, BIO2, BIO3, BIO4);
			IRQS = 5;
		}

		private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
		{
			PopulateCURINSTR
					(IDLE,
						IDLE,
						IDLE,
						TR16, Z, W, dest_l, dest_h,
						INC16, Z, W,
						IDLE,
						IDLE,
						op, dest_l, dest_h, src_l, src_h);

			PopulateBUSRQ(0, I, I, I, I, I, I, I);
			PopulateMEMRQ(0, 0, 0, 0, 0, 0, 0, 0);
			IRQS = 8;
		}

		private void INT_MODE_(ushort src)
		{
			PopulateCURINSTR
					(INT_MODE, src);

			PopulateBUSRQ(0);
			PopulateMEMRQ(0);
			IRQS = 1;
		}

		private void RRD_()
		{
			PopulateCURINSTR
					(IDLE,
						TR16, Z, W, L, H,
						WAIT,
						RD, ALU, Z, W,
						IDLE,
						RRD, ALU, A,
						IDLE,
						IDLE,
						IDLE,
						WAIT,
						WR_INC, Z, W, ALU);

			PopulateBUSRQ(0, H, 0, 0, H, H, H, H, W, 0, 0);
			PopulateMEMRQ(0, H, 0, 0, 0, 0, 0, 0, W, 0, 0);
			IRQS = 11;
		}

		private void RLD_()
		{
			PopulateCURINSTR
					(IDLE,
						TR16, Z, W, L, H,
						WAIT,
						RD, ALU, Z, W,
						IDLE,
						RLD, ALU, A,
						IDLE,
						IDLE,
						IDLE,
						WAIT,
						WR_INC, Z, W, ALU);

			PopulateBUSRQ(0, H, 0, 0, H, H, H, H, W, 0, 0);
			PopulateMEMRQ(0, H, 0, 0, 0, 0, 0, 0, W, 0, 0);
			IRQS = 11;
		}
	}
}
