using System;
using System.Text;

namespace BizHawk.Emulation.Cores.Components.M68000
{
	partial class MC68000
	{
		void MOVE()
		{
			int size = ((op >> 12) & 0x03);
			int dstMode = ((op >> 6) & 0x07);
			int dstReg = ((op >> 9) & 0x07);
			int srcMode = ((op >> 3) & 0x07);
			int srcReg = (op & 0x07);

			int value = 0;
			switch (size)
			{
				case 1: // Byte
					value = ReadValueB(srcMode, srcReg);
					WriteValueB(dstMode, dstReg, (sbyte)value);
					PendingCycles -= MoveCyclesBW[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
					N = (value & 0x80) != 0;
					break;
				case 3: // Word
					value = ReadValueW(srcMode, srcReg);
					WriteValueW(dstMode, dstReg, (short)value);
					PendingCycles -= MoveCyclesBW[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
					N = (value & 0x8000) != 0;
					break;
				case 2: // Long
					value = ReadValueL(srcMode, srcReg);
					WriteValueL(dstMode, dstReg, value);
					PendingCycles -= MoveCyclesL[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
					N = (value & 0x80000000) != 0;
					break;
			}

			V = false;
			C = false;
			Z = (value == 0);
		}

		void MOVE_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int size = ((op >> 12) & 0x03);
			int dstMode = ((op >> 6) & 0x07);
			int dstReg = ((op >> 9) & 0x07);
			int srcMode = ((op >> 3) & 0x07);
			int srcReg = (op & 0x07);

			switch (size)
			{
				case 1:
					info.Mnemonic = "move.b";
					info.Args = DisassembleValue(srcMode, srcReg, 1, ref pc) + ", ";
					info.Args += DisassembleValue(dstMode, dstReg, 1, ref pc);
					break;
				case 3:
					info.Mnemonic = "move.w";
					info.Args = DisassembleValue(srcMode, srcReg, 2, ref pc) + ", ";
					info.Args += DisassembleValue(dstMode, dstReg, 2, ref pc);
					break;
				case 2:
					info.Mnemonic = "move.l";
					info.Args = DisassembleValue(srcMode, srcReg, 4, ref pc) + ", ";
					info.Args += DisassembleValue(dstMode, dstReg, 4, ref pc);
					break;
			}

			info.Length = pc - info.PC;
		}

		void MOVEA()
		{
			int size = ((op >> 12) & 0x03);
			int dstReg = ((op >> 9) & 0x07);
			int srcMode = ((op >> 3) & 0x07);
			int srcReg = (op & 0x07);

			if (size == 3) // Word
			{
				A[dstReg].s32 = ReadValueW(srcMode, srcReg);
				switch (srcMode)
				{
					case 0: PendingCycles -= 4; break;
					case 1: PendingCycles -= 4; break;
					case 2: PendingCycles -= 8; break;
					case 3: PendingCycles -= 8; break;
					case 4: PendingCycles -= 10; break;
					case 5: PendingCycles -= 12; break;
					case 6: PendingCycles -= 14; break;
					case 7:
						switch (srcReg)
						{
							case 0: PendingCycles -= 12; break;
							case 1: PendingCycles -= 16; break;
							case 2: PendingCycles -= 12; break;
							case 3: PendingCycles -= 14; break;
							case 4: PendingCycles -= 8; break;
							default: throw new InvalidOperationException();
						}
						break;
				}
			}
			else
			{ // Long
				A[dstReg].s32 = ReadValueL(srcMode, srcReg);
				switch (srcMode)
				{
					case 0: PendingCycles -= 4; break;
					case 1: PendingCycles -= 4; break;
					case 2: PendingCycles -= 12; break;
					case 3: PendingCycles -= 12; break;
					case 4: PendingCycles -= 14; break;
					case 5: PendingCycles -= 16; break;
					case 6: PendingCycles -= 18; break;
					case 7:
						switch (srcReg)
						{
							case 0: PendingCycles -= 16; break;
							case 1: PendingCycles -= 20; break;
							case 2: PendingCycles -= 16; break;
							case 3: PendingCycles -= 18; break;
							case 4: PendingCycles -= 12; break;
							default: throw new InvalidOperationException();
						}
						break;
				}
			}
		}

		void MOVEA_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int size = ((op >> 12) & 0x03);
			int dstReg = ((op >> 9) & 0x07);
			int srcMode = ((op >> 3) & 0x07);
			int srcReg = (op & 0x07);

			if (size == 3)
			{
				info.Mnemonic = "movea.w";
				info.Args = DisassembleValue(srcMode, srcReg, 2, ref pc) + ", A" + dstReg;
			}
			else
			{
				info.Mnemonic = "movea.l";
				info.Args = DisassembleValue(srcMode, srcReg, 4, ref pc) + ", A" + dstReg;
			}
			info.Length = pc - info.PC;
		}

		void MOVEQ()
		{
			int value = (sbyte)op; // 8-bit data payload is sign-extended to 32-bits.
			N = (value & 0x80) != 0;
			Z = (value == 0);
			V = false;
			C = false;
			D[(op >> 9) & 7].s32 = value;
			PendingCycles -= 4;
		}

		void MOVEQ_Disasm(DisassemblyInfo info)
		{
			info.Mnemonic = "moveq";
			info.Args = String.Format("{0}, D{1}", (sbyte)op, (op >> 9) & 7);
		}

		void MOVEM0()
		{
			// Move register to memory
			int size = (op >> 6) & 1;
			int dstMode = (op >> 3) & 7;
			int dstReg = (op >> 0) & 7;

			ushort registers = (ushort)ReadWord(PC); PC += 2;
			int address = ReadAddress(dstMode, dstReg);
			int regCount = 0;

			if (size == 0)
			{
				// word-assign
				if (dstMode == 4) // decrement address
				{
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							address -= 2;
							WriteWord(address, A[i].s16);
							regCount++;
						}
						registers >>= 1;
					}
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							address -= 2;
							WriteWord(address, D[i].s16);
							regCount++;
						}
						registers >>= 1;
					}
					A[dstReg].s32 = address;
				}
				else
				{ // increment address
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							WriteWord(address, A[i].s16);
							address += 2;
							regCount++;
						}
						registers >>= 1;
					}
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							WriteWord(address, D[i].s16);
							address += 2;
							regCount++;
						}
						registers >>= 1;
					}
				}
				PendingCycles -= regCount * 4;
			}
			else
			{
				// long-assign
				if (dstMode == 4) // decrement address
				{
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							address -= 4;
							WriteLong(address, A[i].s32);
							regCount++;
						}
						registers >>= 1;
					}
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							address -= 4;
							WriteLong(address, D[i].s32);
							regCount++;
						}
						registers >>= 1;
					}
					A[dstReg].s32 = address;
				}
				else
				{ // increment address
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							WriteLong(address, A[i].s32);
							address += 4;
							regCount++;
						}
						registers >>= 1;
					}
					for (int i = 7; i >= 0; i--)
					{
						if ((registers & 1) == 1)
						{
							WriteLong(address, D[i].s32);
							address += 4;
							regCount++;
						}
						registers >>= 1;
					}
				}
				PendingCycles -= regCount * 8;
			}

			switch (dstMode)
			{
				case 2: PendingCycles -= 8; break;
				case 3: PendingCycles -= 8; break;
				case 4: PendingCycles -= 8; break;
				case 5: PendingCycles -= 12; break;
				case 6: PendingCycles -= 14; break;
				case 7:
					switch (dstReg)
					{
						case 0: PendingCycles -= 12; break;
						case 1: PendingCycles -= 16; break;
					}
					break;
			}
		}

		void MOVEM1()
		{
			// Move memory to register
			int size = (op >> 6) & 1;
			int srcMode = (op >> 3) & 7;
			int srcReg = (op >> 0) & 7;

			ushort registers = (ushort)ReadWord(PC); PC += 2;
			int address = ReadAddress(srcMode, srcReg);
			int regCount = 0;

			if (size == 0)
			{
				// word-assign
				for (int i = 0; i < 8; i++)
				{
					if ((registers & 1) == 1)
					{
						D[i].s32 = ReadWord(address);
						address += 2;
						regCount++;
					}
					registers >>= 1;
				}
				for (int i = 0; i < 8; i++)
				{
					if ((registers & 1) == 1)
					{
						A[i].s32 = ReadWord(address);
						address += 2;
						regCount++;
					}
					registers >>= 1;
				}
				PendingCycles -= regCount * 4;
				if (srcMode == 3)
					A[srcReg].s32 = address;
			}
			else
			{
				// long-assign
				for (int i = 0; i < 8; i++)
				{
					if ((registers & 1) == 1)
					{
						D[i].s32 = ReadLong(address);
						address += 4;
						regCount++;
					}
					registers >>= 1;
				}
				for (int i = 0; i < 8; i++)
				{
					if ((registers & 1) == 1)
					{
						A[i].s32 = ReadLong(address);
						address += 4;
						regCount++;
					}
					registers >>= 1;
				}
				PendingCycles -= regCount * 8;
				if (srcMode == 3)
					A[srcReg].s32 = address;
			}

			switch (srcMode)
			{
				case 2: PendingCycles -= 12; break;
				case 3: PendingCycles -= 12; break;
				case 4: PendingCycles -= 12; break;
				case 5: PendingCycles -= 16; break;
				case 6: PendingCycles -= 18; break;
				case 7:
					switch (srcReg)
					{
						case 0: PendingCycles -= 16; break;
						case 1: PendingCycles -= 20; break;
						case 2: PendingCycles -= 16; break;
						case 3: PendingCycles -= 18; break;
					}
					break;
			}
		}

		static string DisassembleRegisterList0(ushort registers)
		{
			var str = new StringBuilder();
			int count = 0;
			for (int i = 0; i < 8; i++)
			{
				if ((registers & 0x8000) != 0)
				{
					if (count > 0) str.Append(",");
					str.Append("D" + i);
					count++;
				}
				registers <<= 1;
			}
			for (int i = 0; i < 8; i++)
			{
				if ((registers & 0x8000) != 0)
				{
					if (count > 0) str.Append(",");
					str.Append("A" + i);
					count++;
				}
				registers <<= 1;
			}
			return str.ToString();
		}

		static string DisassembleRegisterList1(ushort registers)
		{
			var str = new StringBuilder();
			int count = 0;
			for (int i = 0; i < 8; i++)
			{
				if ((registers & 1) != 0)
				{
					if (count > 0) str.Append(",");
					str.Append("D" + i);
					count++;
				}
				registers >>= 1;
			}
			for (int i = 0; i < 8; i++)
			{
				if ((registers & 1) != 0)
				{
					if (count > 0) str.Append(",");
					str.Append("A" + i);
					count++;
				}
				registers >>= 1;
			}
			return str.ToString();
		}

		void MOVEM0_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int size = (op >> 6) & 1;
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;

			ushort registers = (ushort)ReadWord(pc); pc += 2;
			string address = DisassembleAddress(mode, reg, ref pc);

			info.Mnemonic = size == 0 ? "movem.w" : "movem.l";
			info.Args = DisassembleRegisterList0(registers) + ", " + address;
			info.Length = pc - info.PC;
		}

		void MOVEM1_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int size = (op >> 6) & 1;
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;

			ushort registers = (ushort)ReadWord(pc); pc += 2;
			string address = DisassembleAddress(mode, reg, ref pc);

			info.Mnemonic = size == 0 ? "movem.w" : "movem.l";
			info.Args = address + ", " + DisassembleRegisterList1(registers);
			info.Length = pc - info.PC;
		}

		void LEA()
		{
			int mode = (op >> 3) & 7;
			int sReg = (op >> 0) & 7;
			int dReg = (op >> 9) & 7;

			A[dReg].u32 = (uint)ReadAddress(mode, sReg);
			switch (mode)
			{
				case 2: PendingCycles -= 4; break;
				case 5: PendingCycles -= 8; break;
				case 6: PendingCycles -= 12; break;
				case 7:
					switch (sReg)
					{
						case 0: PendingCycles -= 8; break;
						case 1: PendingCycles -= 12; break;
						case 2: PendingCycles -= 8; break;
						case 3: PendingCycles -= 12; break;
					}
					break;
			}
		}

		void LEA_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int mode = (op >> 3) & 7;
			int sReg = (op >> 0) & 7;
			int dReg = (op >> 9) & 7;

			info.Mnemonic = "lea";
			info.Args = DisassembleAddress(mode, sReg, ref pc);
			info.Args += ", A" + dReg;

			info.Length = pc - info.PC;
		}

		void CLR()
		{
			int size = (op >> 6) & 3;
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;

			switch (size)
			{
				case 0: WriteValueB(mode, reg, 0); PendingCycles -= mode == 0 ? 4 : 8 + EACyclesBW[mode, reg]; break;
				case 1: WriteValueW(mode, reg, 0); PendingCycles -= mode == 0 ? 4 : 8 + EACyclesBW[mode, reg]; break;
				case 2: WriteValueL(mode, reg, 0); PendingCycles -= mode == 0 ? 6 : 12 + EACyclesL[mode, reg]; break;
			}

			N = V = C = false;
			Z = true;
		}

		void CLR_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int size = (op >> 6) & 3;
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;

			switch (size)
			{
				case 0: info.Mnemonic = "clr.b"; info.Args = DisassembleValue(mode, reg, 1, ref pc); break;
				case 1: info.Mnemonic = "clr.w"; info.Args = DisassembleValue(mode, reg, 2, ref pc); break;
				case 2: info.Mnemonic = "clr.l"; info.Args = DisassembleValue(mode, reg, 4, ref pc); break;
			}
			info.Length = pc - info.PC;
		}

		void EXT()
		{
			int size = (op >> 6) & 1;
			int reg = op & 7;

			switch (size)
			{
				case 0: // ext.w
					D[reg].s16 = D[reg].s8;
					N = (D[reg].s16 & 0x8000) != 0;
					Z = (D[reg].s16 == 0);
					break;
				case 1: // ext.l
					D[reg].s32 = D[reg].s16;
					N = (D[reg].s32 & 0x80000000) != 0;
					Z = (D[reg].s32 == 0);
					break;
			}

			V = false;
			C = false;
			PendingCycles -= 4;
		}

		void EXT_Disasm(DisassemblyInfo info)
		{
			int size = (op >> 6) & 1;
			int reg = op & 7;

			switch (size)
			{
				case 0: info.Mnemonic = "ext.w"; info.Args = "D" + reg; break;
				case 1: info.Mnemonic = "ext.l"; info.Args = "D" + reg; break;
			}
		}

		void PEA()
		{
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;
			int ea = ReadAddress(mode, reg);

			A[7].s32 -= 4;
			WriteLong(A[7].s32, ea);

			switch (mode)
			{
				case 2: PendingCycles -= 12; break;
				case 5: PendingCycles -= 16; break;
				case 6: PendingCycles -= 20; break;
				case 7:
					switch (reg)
					{
						case 0: PendingCycles -= 16; break;
						case 1: PendingCycles -= 20; break;
						case 2: PendingCycles -= 16; break;
						case 3: PendingCycles -= 20; break;
					}
					break;
			}
		}

		void PEA_Disasm(DisassemblyInfo info)
		{
			int pc = info.PC + 2;
			int mode = (op >> 3) & 7;
			int reg = (op >> 0) & 7;

			info.Mnemonic = "pea";
			info.Args = DisassembleAddress(mode, reg, ref pc);
			info.Length = pc - info.PC;
		}
	}
}
