using System;

namespace BizHawk.Emulation.Cores.Components.CP1610
{
	public sealed partial class CP1610
	{
		private const string UNEXPECTED_HLT = "HLT is an unexpected behavior; Intellivision should run so long as the power"
			+ " is on.";
		private const string UNEXPECTED_TCI = "TCI is an unexpected behavior; Intellivision doesn't connect the pin of the"
			+ " same name to anything useful.";
		private const string UNEXPECTED_SIN = "SIN is an unexpected behavior; Intellivision doesn't connect the CPU's PCIT"
			+ " pin to anything else.";
		private const string UNEXPECTED_BEXT = "BEXT is an unexpected behavior; Intellivision not connected to the External"
			+ " Branch Condition Address pins (EBCA0-EBCA3).";

		private void Calc_FlagC(int result)
		{
			FlagC = ((result & 0x10000) != 0);
		}

		private void Calc_FlagO_Add(int op1, int op2, int result)
		{
			bool op1_neg = ((op1 & 0x8000) != 0);
			bool op2_neg = ((op2 & 0x8000) != 0);
			bool result_neg = ((op1 & 0x8000) != 0);
			FlagO = (
				(op1_neg && op2_neg && !result_neg) ||
				(!op1_neg && !op2_neg && result_neg)
			);
		}

		private void Calc_FlagS(int result)
		{
			FlagS = ((result & 0x8000) != 0);
		}

		private void Calc_FlagS_7(int result)
		{
			FlagS = ((result & 0x80) != 0);
		}

		private void Calc_FlagZ(int result)
		{
			FlagZ = (result == 0);
		}

		private ushort Indirect_Get(byte mem)
		{
			ushort value;
			// Auto-decrement the stack pointer if it's the memory register.
			if (mem == 0x6)
			{
				RegisterSP--;
			}
			if (!FlagD)
			{
				value = ReadMemory(Register[mem]);
			}
			else
			{
				// Double Byte Data.
				value = (ushort)(ReadMemory(Register[mem]++) & 0xFF);
				value |= (ushort)(ReadMemory(Register[mem]) << 8);
			}
			// Auto-increment the memory register if it does so on write.
			if (mem >= 0x4 && mem != 0x6)
			{
				Register[mem]++;
			}
			return value;
		}

		public int Indirect_Get_Cycles(byte mem)
		{
			if (!FlagD)
			{
				if (mem != 0x6)
				{
					return 8;
				}
				else
				{
					return 11;
				}
			}
			else
			{
				// Double Byte Data.
				return 10;
			}
		}

		public void Indirect_Set(byte mem, byte src)
		{
			WriteMemory(Register[mem], Register[src]);
			// Auto-increment the memory register if it does so on read.
			if (mem >= 0x4)
			{
				Register[mem]++;
			}
		}

		public int Execute()
		{
			int cycles = 0;
			/*
			 Take an interrupt if the previous instruction was interruptible, interrupts are enabled, and IntRM has a
			 falling edge.
			*/
			if (FlagI && Interruptible && !IntRM && !Interrupted)
			{
				if (Logging)
				{
					Log.WriteLine("------");
					Log.WriteLine();
					Log.WriteLine("Interrupt");
					Log.Flush();
				}
				Interrupted = true;
				Interruptible = false;
				Indirect_Set(6, 7);
				RegisterPC = INTERRUPT;
				cycles = 28;
				PendingCycles -= cycles;
				TotalExecutedCycles += cycles;
				return cycles;
			}
			if (Logging)
			{
				int addrToAdvance;
				Log.WriteLine("------");
				Log.WriteLine();
				Log.WriteLine(Disassemble(RegisterPC, out addrToAdvance));
				Log.Flush();
			}
			byte dest, src, mem;
			ushort dest_value, src_value, mem_read, addr, addr_read, offset;
			int decle2, decle3, result = 0, twos, status_word, lower, sign, cond, ext;
			//int ones, carry;
			bool branch = false;
			bool FlagD_prev = FlagD;
			int opcode = ReadMemory(RegisterPC++) & 0x3FF;
			switch (opcode)
			{
				case 0x000: // HLT
					throw new ArgumentException(UNEXPECTED_HLT);
				case 0x001: // SDBD
					FlagD = true;
					cycles = 4;
					Interruptible = false;
					break;
				case 0x002: // EIS
					FlagI = true;
					cycles = 4;
					Interruptible = false;
					break;
				case 0x003: // DIS
					FlagI = false;
					cycles = 4;
					Interruptible = false;
					break;
				case 0x004: // J, JE, JD, JSR, JSRE, JSRD
					// 0000:0000:0000:0100    0000:00rr:aaaa:aaff    0000:00aa:aaaa:aaaa
					decle2 = ReadMemory(RegisterPC++);
					decle3 = ReadMemory(RegisterPC++);
					// rr indicates the register into which to store the return address
					dest = (byte)(((decle2 >> 8) & 0x3) + 4);
					// aaaaaaaaaaaaaaaa indicates the address to where the CP1610 should Jump
					addr = (ushort)(((decle2 << 8) & 0xFC00) | (decle3 & 0x3FF));
					if (dest != 0x7)
					{
						// Store the return address.
						Register[dest] = (ushort)(RegisterPC & 0xFFFF);
					}
					// ff indicates how to affect the Interrupt (I) flag in the CP1610
					switch (decle2 & 0x3)
					{
						case 0x1:
							FlagI = true;
							break;
						case 0x2:
							FlagI = false;
							break;
						case 0x3:
							// Unknown opcode.
							throw new ArgumentException();
					}
					RegisterPC = addr;
					cycles = 12;
					Interruptible = true;
					break;
				case 0x005: // TCI
					throw new ArgumentException(UNEXPECTED_TCI);
				case 0x006: // CLRC
					FlagC = false;
					cycles = 4;
					Interruptible = false;
					break;
				case 0x007: // SETC
					FlagC = true;
					cycles = 4;
					Interruptible = false;
					break;
				// INCR
				case 0x008:
				case 0x009:
				case 0x00A:
				case 0x00B:
				case 0x00C:
				case 0x00D:
				case 0x00E:
				case 0x00F:
					dest = (byte)(opcode & 0x7);
					result = (Register[dest] + 1) & 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// DECR
				case 0x010:
				case 0x011:
				case 0x012:
				case 0x013:
				case 0x014:
				case 0x015:
				case 0x016:
				case 0x017:
					dest = (byte)(opcode & 0x7);
					result = (Register[dest] - 1) & 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// COMR
				case 0x018:
				case 0x019:
				case 0x01A:
				case 0x01B:
				case 0x01C:
				case 0x01D:
				case 0x01E:
				case 0x01F:
					dest = (byte)(opcode & 0x7);
					result = (Register[dest] ^ 0xFFFF);
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// NEGR
				case 0x020:
				case 0x021:
				case 0x022:
				case 0x023:
				case 0x024:
				case 0x025:
				case 0x026:
				case 0x027:
					throw new NotImplementedException();
                    //dest = (byte)(opcode & 0x7);
                    //dest_value = Register[dest];
                    //ones = (dest_value ^ 0xFFFF);
                    //result = ones + 1;
                    //Calc_FlagC(result);
                    //Calc_FlagO_Add(ones, 1, result);
                    //result &= 0xFFFF;
                    //Calc_FlagS(result);
                    //Calc_FlagZ(result);
                    //Register[dest] = (ushort)result;
                    //cycles = 6;
                    //Interruptible = true;
                    //break;
				// ADCR
				case 0x028:
				case 0x029:
				case 0x02A:
				case 0x02B:
				case 0x02C:
				case 0x02D:
				case 0x02E:
				case 0x02F:
					throw new NotImplementedException();
                    //dest = (byte)(opcode & 0x7);
                    //dest_value = Register[dest];
                    //carry = FlagC ? 1 : 0;
                    //result = dest_value + carry;
                    //Calc_FlagC(result);
                    //Calc_FlagO_Add(dest_value, carry, result);
                    //result &= 0xFFFF;
                    //Calc_FlagS(result);
                    //Calc_FlagZ(result);
                    //Register[dest] = (ushort)result;
                    //cycles = 6;
                    //Interruptible = true;
                    //break;
				// GSWD
				case 0x030:
				case 0x031:
				case 0x032:
				case 0x033:
					dest = (byte)(opcode & 0x3);
					status_word = ((FlagS ? 1 : 0) << 3) | ((FlagZ ? 1 : 0) << 2) | ((FlagO ? 1 : 0) << 1) |
						(FlagC ? 1 : 0);
					Register[dest] = (ushort)((status_word << 12) | (status_word << 4));
					cycles = 6;
					Interruptible = true;
					break;
				// NOP
				case 0x034:
				case 0x035:
					cycles = 6;
					Interruptible = true;
					break;
				// SIN
				case 0x036:
				case 0x037:
					throw new ArgumentException(UNEXPECTED_SIN);
				// RSWD
				case 0x038:
				case 0x039:
				case 0x03A:
				case 0x03B:
				case 0x03C:
				case 0x03D:
				case 0x03E:
				case 0x03F:
					src = (byte)(opcode & 0x7);
					src_value = Register[src];
					FlagS = ((src_value & 0x80) != 0);
					FlagZ = ((src_value & 0x40) != 0);
					FlagO = ((src_value & 0x20) != 0);
					FlagC = ((src_value & 0x10) != 0);
					cycles = 6;
					Interruptible = true;
					break;
				// SWAP
				case 0x040:
				case 0x041:
				case 0x042:
				case 0x043:
				case 0x044:
				case 0x045:
				case 0x046:
				case 0x047:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					lower = dest_value & 0xFF;
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single swap.
						result = (lower << 8) | ((dest_value >> 8) & 0xFF);
						cycles = 6;
					}
					else
					{
						// Double swap.
						result = (lower << 8) | lower;
						cycles = 8;
					}
					Calc_FlagS_7(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// SLL
				case 0x048:
				case 0x049:
				case 0x04A:
				case 0x04B:
				case 0x04C:
				case 0x04D:
				case 0x04E:
				case 0x04F:
					dest = (byte)(opcode & 0x3);
					result = Register[dest] << 1;
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single shift.
						cycles = 6;
					}
					else
					{
						// Double shift.
						result <<= 1;
						cycles = 8;
					}
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// RLC
				case 0x050:
				case 0x051:
				case 0x052:
				case 0x053:
				case 0x054:
				case 0x055:
				case 0x056:
				case 0x057:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					result = (dest_value << 1) | (FlagC ? 1 : 0);
					FlagC = ((dest_value & 0x8000) != 0);
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single rotate.
						cycles = 6;
					}
					else
					{
						// Double rotate.
						result <<= 1;
						result |= (FlagO ? 1 : 0);
						FlagO = ((dest_value & 0x4000) != 0);
						cycles = 8;
					}
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// SLLC
				case 0x058:
				case 0x059:
				case 0x05A:
				case 0x05B:
				case 0x05C:
				case 0x05D:
				case 0x05E:
				case 0x05F:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					result = dest_value << 1;
					FlagC = ((dest_value & 0x8000) != 0);
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single shift.
						cycles = 6;
					}
					else
					{
						// Double shift.
						result <<= 1;
						FlagO = ((dest_value & 0x4000) != 0);
						cycles = 8;
					}
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// SLR
				case 0x060:
				case 0x061:
				case 0x062:
				case 0x063:
				case 0x064:
				case 0x065:
				case 0x066:
				case 0x067:
					dest = (byte)(opcode & 0x3);
					result = Register[dest] >> 1;
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single shift.
						cycles = 6;
					}
					else
					{
						// Double shift.
						result >>= 1;
						cycles = 8;
					}
					Calc_FlagS_7(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// SAR
				case 0x068:
				case 0x069:
				case 0x06A:
				case 0x06B:
				case 0x06C:
				case 0x06D:
				case 0x06E:
				case 0x06F:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					sign = dest_value & 0x8000;
					result = (dest_value >> 1) | sign;
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single shift.
						cycles = 6;
					}
					else
					{
						// Double shift.
						result >>= 1;
						result |= sign;
						cycles = 8;
					}
					Calc_FlagS_7(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// RRC
				case 0x070:
				case 0x071:
				case 0x072:
				case 0x073:
				case 0x074:
				case 0x075:
				case 0x076:
				case 0x077:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					result = (dest_value >> 1) | ((FlagC ? 1 : 0) << 15);
					FlagC = ((dest_value & 0x1) != 0);
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single rotate.
						cycles = 6;
					}
					else
					{
						// Double rotate.
						result >>= 1;
						result |= (FlagO ? 1 : 0) << 15;
						FlagO = ((dest_value & 0x2) != 0);
						cycles = 8;
					}
					Calc_FlagS_7(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// SARC
				case 0x078:
				case 0x079:
				case 0x07A:
				case 0x07B:
				case 0x07C:
				case 0x07D:
				case 0x07E:
				case 0x07F:
					dest = (byte)(opcode & 0x3);
					dest_value = Register[dest];
					sign = dest_value & 0x8000;
					result = (dest_value >> 1) | sign;
					FlagC = ((dest_value & 0x1) != 0);
					if (((opcode >> 2) & 0x1) == 0)
					{
						// Single shift.
						cycles = 6;
					}
					else
					{
						// Double shift.
						result >>= 1;
						result |= sign;
						FlagO = ((dest_value & 0x2) != 0);
						cycles = 8;
					}
					Calc_FlagS_7(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = false;
					break;
				// MOVR
				case 0x080:
				case 0x081:
				case 0x082:
				case 0x083:
				case 0x084:
				case 0x085:
				case 0x086:
				case 0x087:
				case 0x088:
				case 0x089:
				case 0x08A:
				case 0x08B:
				case 0x08C:
				case 0x08D:
				case 0x08E:
				case 0x08F:
				case 0x090:
				case 0x091:
				case 0x092:
				case 0x093:
				case 0x094:
				case 0x095:
				case 0x096:
				case 0x097:
				case 0x098:
				case 0x099:
				case 0x09A:
				case 0x09B:
				case 0x09C:
				case 0x09D:
				case 0x09E:
				case 0x09F:
				case 0x0A0:
				case 0x0A1:
				case 0x0A2:
				case 0x0A3:
				case 0x0A4:
				case 0x0A5:
				case 0x0A6:
				case 0x0A7:
				case 0x0A8:
				case 0x0A9:
				case 0x0AA:
				case 0x0AB:
				case 0x0AC:
				case 0x0AD:
				case 0x0AE:
				case 0x0AF:
				case 0x0B0:
				case 0x0B1:
				case 0x0B2:
				case 0x0B3:
				case 0x0B4:
				case 0x0B5:
				case 0x0B6:
				case 0x0B7:
				case 0x0B8:
				case 0x0B9:
				case 0x0BA:
				case 0x0BB:
				case 0x0BC:
				case 0x0BD:
				case 0x0BE:
				case 0x0BF:
					src = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					result = Register[src];
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					if (dest == 0x6 || dest == 0x7)
					{
						cycles = 7;
					}
					else
					{
						cycles = 6;
					}
					Interruptible = true;
					break;
				// ADDR
				case 0x0C0:
				case 0x0C1:
				case 0x0C2:
				case 0x0C3:
				case 0x0C4:
				case 0x0C5:
				case 0x0C6:
				case 0x0C7:
				case 0x0C8:
				case 0x0C9:
				case 0x0CA:
				case 0x0CB:
				case 0x0CC:
				case 0x0CD:
				case 0x0CE:
				case 0x0CF:
				case 0x0D0:
				case 0x0D1:
				case 0x0D2:
				case 0x0D3:
				case 0x0D4:
				case 0x0D5:
				case 0x0D6:
				case 0x0D7:
				case 0x0D8:
				case 0x0D9:
				case 0x0DA:
				case 0x0DB:
				case 0x0DC:
				case 0x0DD:
				case 0x0DE:
				case 0x0DF:
				case 0x0E0:
				case 0x0E1:
				case 0x0E2:
				case 0x0E3:
				case 0x0E4:
				case 0x0E5:
				case 0x0E6:
				case 0x0E7:
				case 0x0E8:
				case 0x0E9:
				case 0x0EA:
				case 0x0EB:
				case 0x0EC:
				case 0x0ED:
				case 0x0EE:
				case 0x0EF:
				case 0x0F0:
				case 0x0F1:
				case 0x0F2:
				case 0x0F3:
				case 0x0F4:
				case 0x0F5:
				case 0x0F6:
				case 0x0F7:
				case 0x0F8:
				case 0x0F9:
				case 0x0FA:
				case 0x0FB:
				case 0x0FC:
				case 0x0FD:
				case 0x0FE:
				case 0x0FF:
					src = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					src_value = Register[src];
					dest_value = Register[dest];
					result = dest_value + src_value;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, src_value, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// SUBR
				case 0x100:
				case 0x101:
				case 0x102:
				case 0x103:
				case 0x104:
				case 0x105:
				case 0x106:
				case 0x107:
				case 0x108:
				case 0x109:
				case 0x10A:
				case 0x10B:
				case 0x10C:
				case 0x10D:
				case 0x10E:
				case 0x10F:
				case 0x110:
				case 0x111:
				case 0x112:
				case 0x113:
				case 0x114:
				case 0x115:
				case 0x116:
				case 0x117:
				case 0x118:
				case 0x119:
				case 0x11A:
				case 0x11B:
				case 0x11C:
				case 0x11D:
				case 0x11E:
				case 0x11F:
				case 0x120:
				case 0x121:
				case 0x122:
				case 0x123:
				case 0x124:
				case 0x125:
				case 0x126:
				case 0x127:
				case 0x128:
				case 0x129:
				case 0x12A:
				case 0x12B:
				case 0x12C:
				case 0x12D:
				case 0x12E:
				case 0x12F:
				case 0x130:
				case 0x131:
				case 0x132:
				case 0x133:
				case 0x134:
				case 0x135:
				case 0x136:
				case 0x137:
				case 0x138:
				case 0x139:
				case 0x13A:
				case 0x13B:
				case 0x13C:
				case 0x13D:
				case 0x13E:
				case 0x13F:
					src = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					src_value = Register[src];
					dest_value = Register[dest];
					twos = (0xFFFF ^ src_value) + 1;
					result = dest_value + twos;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, src_value, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// CMPR
				case 0x140:
				case 0x141:
				case 0x142:
				case 0x143:
				case 0x144:
				case 0x145:
				case 0x146:
				case 0x147:
				case 0x148:
				case 0x149:
				case 0x14A:
				case 0x14B:
				case 0x14C:
				case 0x14D:
				case 0x14E:
				case 0x14F:
				case 0x150:
				case 0x151:
				case 0x152:
				case 0x153:
				case 0x154:
				case 0x155:
				case 0x156:
				case 0x157:
				case 0x158:
				case 0x159:
				case 0x15A:
				case 0x15B:
				case 0x15C:
				case 0x15D:
				case 0x15E:
				case 0x15F:
				case 0x160:
				case 0x161:
				case 0x162:
				case 0x163:
				case 0x164:
				case 0x165:
				case 0x166:
				case 0x167:
				case 0x168:
				case 0x169:
				case 0x16A:
				case 0x16B:
				case 0x16C:
				case 0x16D:
				case 0x16E:
				case 0x16F:
				case 0x170:
				case 0x171:
				case 0x172:
				case 0x173:
				case 0x174:
				case 0x175:
				case 0x176:
				case 0x177:
				case 0x178:
				case 0x179:
				case 0x17A:
				case 0x17B:
				case 0x17C:
				case 0x17D:
				case 0x17E:
				case 0x17F:
					src = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					src_value = Register[src];
					dest_value = Register[dest];
					twos = (0xFFFF ^ src_value) + 1;
					result = dest_value + twos;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, src_value, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					cycles = 6;
					Interruptible = true;
					break;
				// ANDR
				case 0x180:
				case 0x181:
				case 0x182:
				case 0x183:
				case 0x184:
				case 0x185:
				case 0x186:
				case 0x187:
				case 0x188:
				case 0x189:
				case 0x18A:
				case 0x18B:
				case 0x18C:
				case 0x18D:
				case 0x18E:
				case 0x18F:
				case 0x190:
				case 0x191:
				case 0x192:
				case 0x193:
				case 0x194:
				case 0x195:
				case 0x196:
				case 0x197:
				case 0x198:
				case 0x199:
				case 0x19A:
				case 0x19B:
				case 0x19C:
				case 0x19D:
				case 0x19E:
				case 0x19F:
				case 0x1A0:
				case 0x1A1:
				case 0x1A2:
				case 0x1A3:
				case 0x1A4:
				case 0x1A5:
				case 0x1A6:
				case 0x1A7:
				case 0x1A8:
				case 0x1A9:
				case 0x1AA:
				case 0x1AB:
				case 0x1AC:
				case 0x1AD:
				case 0x1AE:
				case 0x1AF:
				case 0x1B0:
				case 0x1B1:
				case 0x1B2:
				case 0x1B3:
				case 0x1B4:
				case 0x1B5:
				case 0x1B6:
				case 0x1B7:
				case 0x1B8:
				case 0x1B9:
				case 0x1BA:
				case 0x1BB:
				case 0x1BC:
				case 0x1BD:
				case 0x1BE:
				case 0x1BF:
                    src = (byte)((opcode >> 3) & 0x7);
                    dest = (byte)(opcode & 0x7);
                    result = Register[dest] & Register[src];
                    Calc_FlagS(result);
                    Calc_FlagZ(result);
                    Register[dest] = (ushort)result;
                    cycles = 6;
                    Interruptible = true;
                    break;
				// XORR
				case 0x1C0:
				case 0x1C1:
				case 0x1C2:
				case 0x1C3:
				case 0x1C4:
				case 0x1C5:
				case 0x1C6:
				case 0x1C7:
				case 0x1C8:
				case 0x1C9:
				case 0x1CA:
				case 0x1CB:
				case 0x1CC:
				case 0x1CD:
				case 0x1CE:
				case 0x1CF:
				case 0x1D0:
				case 0x1D1:
				case 0x1D2:
				case 0x1D3:
				case 0x1D4:
				case 0x1D5:
				case 0x1D6:
				case 0x1D7:
				case 0x1D8:
				case 0x1D9:
				case 0x1DA:
				case 0x1DB:
				case 0x1DC:
				case 0x1DD:
				case 0x1DE:
				case 0x1DF:
				case 0x1E0:
				case 0x1E1:
				case 0x1E2:
				case 0x1E3:
				case 0x1E4:
				case 0x1E5:
				case 0x1E6:
				case 0x1E7:
				case 0x1E8:
				case 0x1E9:
				case 0x1EA:
				case 0x1EB:
				case 0x1EC:
				case 0x1ED:
				case 0x1EE:
				case 0x1EF:
				case 0x1F0:
				case 0x1F1:
				case 0x1F2:
				case 0x1F3:
				case 0x1F4:
				case 0x1F5:
				case 0x1F6:
				case 0x1F7:
				case 0x1F8:
				case 0x1F9:
				case 0x1FA:
				case 0x1FB:
				case 0x1FC:
				case 0x1FD:
				case 0x1FE:
				case 0x1FF:
					src = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					result = Register[dest] ^ Register[src];
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 6;
					Interruptible = true;
					break;
				// Branch Forward, no External Condition
				case 0x200: // B
				case 0x201: // BC
				case 0x202: // BOV
				case 0x203: // BPL
				case 0x204: // BEQ
				case 0x205: // BLT
				case 0x206: // BLE
				case 0x207: // BUSC
				case 0x208: // NOPP
				case 0x209: // BNC
				case 0x20A: // BNOV
				case 0x20B: // BMI
				case 0x20C: // BNEQ
				case 0x20D: // BGE
				case 0x20E: // BGT
				case 0x20F: // BESC
				// Branch Forward, Exteranl Condition (BEXT)
				case 0x210:
				case 0x211:
				case 0x212:
				case 0x213:
				case 0x214:
				case 0x215:
				case 0x216:
				case 0x217:
				case 0x218:
				case 0x219:
				case 0x21A:
				case 0x21B:
				case 0x21C:
				case 0x21D:
				case 0x21E:
				case 0x21F:
				// Branch Reverse, no External Condition
				case 0x220: // B
				case 0x221: // BC
				case 0x222: // BOV
				case 0x223: // BPL
				case 0x224: // BEQ
				case 0x225: // BLT
				case 0x226: // BLE
				case 0x227: // BUSC
				case 0x228: // NOPP
				case 0x229: // BNC
				case 0x22A: // BNOV
				case 0x22B: // BMI
				case 0x22C: // BNEQ
				case 0x22D: // BGE
				case 0x22E: // BGT
				case 0x22F: // BESC
				// Branch Reverse, Exteranl Condition (BEXT)
				case 0x230:
				case 0x231:
				case 0x232:
				case 0x233:
				case 0x234:
				case 0x235:
				case 0x236:
				case 0x237:
				case 0x238:
				case 0x239:
				case 0x23A:
				case 0x23B:
				case 0x23C:
				case 0x23D:
				case 0x23E:
				case 0x23F:
					offset = ReadMemory(RegisterPC++);
					cond = opcode & 0xF;
					ext = opcode & 0x10;
					// BEXT
					if (ext != 0)
					{
						throw new ArgumentException(UNEXPECTED_BEXT);
					}
					else
					{
						switch (cond)
						{
							// B
							case 0x0:
								branch = true;
								break;
							// BC
							case 0x1:
								branch = FlagC;
								break;
							// BOV
							case 0x2:
								branch = FlagO;
								break;
							// BPL
							case 0x3:
								branch = !FlagS;
								break;
							// BEQ
							case 0x4:
								branch = FlagZ;
								break;
							// BLT
							case 0x5:
								branch = (FlagS != FlagO);
								break;
							// BLE
							case 0x6:
								branch = (FlagZ || FlagS != FlagO);
								break;
							// BUSC
							case 0x7:
								branch = (FlagC != FlagS);
								break;
							// NOPP
							case 0x8:
								branch = false;
								break;
							// BNC
							case 0x9:
								branch = !FlagC;
								break;
							// BNOV
							case 0xA:
								branch = !FlagO;
								break;
							// BMI
							case 0xB:
								branch = FlagS;
								break;
							// BNEQ
							case 0xC:
								branch = !FlagZ;
								break;
							// BGE
							case 0xD:
								branch = (FlagS == FlagO);
								break;
							// BGT
							case 0xE:
								branch = (!FlagZ && FlagS == FlagO);
								break;
							// BESC
							case 0xF:
								branch = (FlagC == FlagS);
								break;
						}
					}
					if (branch)
					{
						// Branch in the reverse direction by negating the offset and subtracting 1.
						if (((opcode >> 5) & 0x1) != 0)
							offset = (ushort)(-offset - 1);
						RegisterPC += offset;
						cycles = 9;
					}
					else
					{
						cycles = 7;
					}
					Interruptible = true;
					break;
				// MVO
				case 0x240:
				case 0x241:
				case 0x242:
				case 0x243:
				case 0x244:
				case 0x245:
				case 0x246:
				case 0x247:
					src = (byte)(opcode & 0x7);
					addr = ReadMemory(RegisterPC++);
					WriteMemory(addr, Register[src]);
					cycles = 11;
					Interruptible = false;
					break;
				// MVO@
				case 0x248:
				case 0x249:
				case 0x24A:
				case 0x24B:
				case 0x24C:
				case 0x24D:
				case 0x24E:
				case 0x24F:
				case 0x250:
				case 0x251:
				case 0x252:
				case 0x253:
				case 0x254:
				case 0x255:
				case 0x256:
				case 0x257:
				case 0x258:
				case 0x259:
				case 0x25A:
				case 0x25B:
				case 0x25C:
				case 0x25D:
				case 0x25E:
				case 0x25F:
				case 0x260:
				case 0x261:
				case 0x262:
				case 0x263:
				case 0x264:
				case 0x265:
				case 0x266:
				case 0x267:
				case 0x268:
				case 0x269:
				case 0x26A:
				case 0x26B:
				case 0x26C:
				case 0x26D:
				case 0x26E:
				case 0x26F:
				case 0x270:
				case 0x271:
				case 0x272:
				case 0x273:
				case 0x274:
				case 0x275:
				case 0x276:
				case 0x277:
				case 0x278:
				case 0x279:
				case 0x27A:
				case 0x27B:
				case 0x27C:
				case 0x27D:
				case 0x27E:
				case 0x27F:
					mem = (byte)((opcode >> 3) & 0x7);
					src = (byte)(opcode & 0x7);
					Indirect_Set(mem, src);
					cycles = 9;
					Interruptible = false;
					break;
				// MVI
				case 0x280:
				case 0x281:
				case 0x282:
				case 0x283:
				case 0x284:
				case 0x285:
				case 0x286:
				case 0x287:
					dest = (byte)(opcode & 0x7);
					addr = ReadMemory(RegisterPC++);
					Register[dest] = ReadMemory(addr);
					cycles = 10;
					Interruptible = true;
					break;
				// MVI@
				case 0x288:
				case 0x289:
				case 0x28A:
				case 0x28B:
				case 0x28C:
				case 0x28D:
				case 0x28E:
				case 0x28F:
				case 0x290:
				case 0x291:
				case 0x292:
				case 0x293:
				case 0x294:
				case 0x295:
				case 0x296:
				case 0x297:
				case 0x298:
				case 0x299:
				case 0x29A:
				case 0x29B:
				case 0x29C:
				case 0x29D:
				case 0x29E:
				case 0x29F:
				case 0x2A0:
				case 0x2A1:
				case 0x2A2:
				case 0x2A3:
				case 0x2A4:
				case 0x2A5:
				case 0x2A6:
				case 0x2A7:
				case 0x2A8:
				case 0x2A9:
				case 0x2AA:
				case 0x2AB:
				case 0x2AC:
				case 0x2AD:
				case 0x2AE:
				case 0x2AF:
				case 0x2B0:
				case 0x2B1:
				case 0x2B2:
				case 0x2B3:
				case 0x2B4:
				case 0x2B5:
				case 0x2B6:
				case 0x2B7:
				case 0x2B8:
				case 0x2B9:
				case 0x2BA:
				case 0x2BB:
				case 0x2BC:
				case 0x2BD:
				case 0x2BE:
				case 0x2BF:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					Register[dest] = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					Interruptible = true;
					break;
				// ADD
				case 0x2C0:
				case 0x2C1:
				case 0x2C2:
				case 0x2C3:
				case 0x2C4:
				case 0x2C5:
				case 0x2C6:
				case 0x2C7:
					dest = (byte)(opcode & 0x7);
					addr = ReadMemory(RegisterPC++);
					dest_value = Register[dest];
					addr_read = ReadMemory(addr);
					result = dest_value + addr_read;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, addr_read, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					cycles = 10;
					Interruptible = true;
					break;
				// ADD@
				case 0x2C8:
				case 0x2C9:
				case 0x2CA:
				case 0x2CB:
				case 0x2CC:
				case 0x2CD:
				case 0x2CE:
				case 0x2CF:
				case 0x2D0:
				case 0x2D1:
				case 0x2D2:
				case 0x2D3:
				case 0x2D4:
				case 0x2D5:
				case 0x2D6:
				case 0x2D7:
				case 0x2D8:
				case 0x2D9:
				case 0x2DA:
				case 0x2DB:
				case 0x2DC:
				case 0x2DD:
				case 0x2DE:
				case 0x2DF:
				case 0x2E0:
				case 0x2E1:
				case 0x2E2:
				case 0x2E3:
				case 0x2E4:
				case 0x2E5:
				case 0x2E6:
				case 0x2E7:
				case 0x2E8:
				case 0x2E9:
				case 0x2EA:
				case 0x2EB:
				case 0x2EC:
				case 0x2ED:
				case 0x2EE:
				case 0x2EF:
				case 0x2F0:
				case 0x2F1:
				case 0x2F2:
				case 0x2F3:
				case 0x2F4:
				case 0x2F5:
				case 0x2F6:
				case 0x2F7:
				case 0x2F8:
				case 0x2F9:
				case 0x2FA:
				case 0x2FB:
				case 0x2FC:
				case 0x2FD:
				case 0x2FE:
				case 0x2FF:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					mem_read = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					dest_value = Register[dest];
					result = dest_value + mem_read;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, mem_read, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = true;
					break;
				// SUB
				case 0x300:
				case 0x301:
				case 0x302:
				case 0x303:
				case 0x304:
				case 0x305:
				case 0x306:
				case 0x307:
					throw new NotImplementedException();
                    //dest = (byte)(opcode & 0x7);
                    //addr = ReadMemory(RegisterPC++);
                    //dest_value = Register[dest];
                    //addr_read = ReadMemory(addr);
                    //twos = (0xFFFF ^ addr_read) + 1;
                    //result = dest_value + twos;
                    //Calc_FlagC(result);
                    //Calc_FlagO_Add(dest_value, addr_read, result);
                    //result &= 0xFFFF;
                    //Calc_FlagS(result);
                    //Calc_FlagZ(result);
                    //Register[dest] = (ushort)result;
                    //cycles = 10;
                    //Interruptible = true;
                    //break;
				// SUB@
				case 0x308:
				case 0x309:
				case 0x30A:
				case 0x30B:
				case 0x30C:
				case 0x30D:
				case 0x30E:
				case 0x30F:
				case 0x310:
				case 0x311:
				case 0x312:
				case 0x313:
				case 0x314:
				case 0x315:
				case 0x316:
				case 0x317:
				case 0x318:
				case 0x319:
				case 0x31A:
				case 0x31B:
				case 0x31C:
				case 0x31D:
				case 0x31E:
				case 0x31F:
				case 0x320:
				case 0x321:
				case 0x322:
				case 0x323:
				case 0x324:
				case 0x325:
				case 0x326:
				case 0x327:
				case 0x328:
				case 0x329:
				case 0x32A:
				case 0x32B:
				case 0x32C:
				case 0x32D:
				case 0x32E:
				case 0x32F:
				case 0x330:
				case 0x331:
				case 0x332:
				case 0x333:
				case 0x334:
				case 0x335:
				case 0x336:
				case 0x337:
				case 0x338:
				case 0x339:
				case 0x33A:
				case 0x33B:
				case 0x33C:
				case 0x33D:
				case 0x33E:
				case 0x33F:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					mem_read = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					dest_value = Register[dest];
					twos = (0xFFFF ^ mem_read) + 1;
					result = dest_value + twos;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, mem_read, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = true;
					break;
				// CMP
				case 0x340:
				case 0x341:
				case 0x342:
				case 0x343:
				case 0x344:
				case 0x345:
				case 0x346:
				case 0x347:
					dest = (byte)(opcode & 0x7);
					addr = ReadMemory(RegisterPC++);
					dest_value = Register[dest];
					addr_read = ReadMemory(addr);
					twos = (0xFFFF ^ addr_read) + 1;
					result = dest_value + twos;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, addr_read, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					cycles = 10;
					Interruptible = true;
					break;
				// CMP@
				case 0x348:
				case 0x349:
				case 0x34A:
				case 0x34B:
				case 0x34C:
				case 0x34D:
				case 0x34E:
				case 0x34F:
				case 0x350:
				case 0x351:
				case 0x352:
				case 0x353:
				case 0x354:
				case 0x355:
				case 0x356:
				case 0x357:
				case 0x358:
				case 0x359:
				case 0x35A:
				case 0x35B:
				case 0x35C:
				case 0x35D:
				case 0x35E:
				case 0x35F:
				case 0x360:
				case 0x361:
				case 0x362:
				case 0x363:
				case 0x364:
				case 0x365:
				case 0x366:
				case 0x367:
				case 0x368:
				case 0x369:
				case 0x36A:
				case 0x36B:
				case 0x36C:
				case 0x36D:
				case 0x36E:
				case 0x36F:
				case 0x370:
				case 0x371:
				case 0x372:
				case 0x373:
				case 0x374:
				case 0x375:
				case 0x376:
				case 0x377:
				case 0x378:
				case 0x379:
				case 0x37A:
				case 0x37B:
				case 0x37C:
				case 0x37D:
				case 0x37E:
				case 0x37F:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					mem_read = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					dest_value = Register[dest];
					twos = (0xFFFF ^ mem_read) + 1;
					result = dest_value + twos;
					Calc_FlagC(result);
					Calc_FlagO_Add(dest_value, mem_read, result);
					result &= 0xFFFF;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Interruptible = true;
					break;
				// AND
				case 0x380:
				case 0x381:
				case 0x382:
				case 0x383:
				case 0x384:
				case 0x385:
				case 0x386:
				case 0x387:
					throw new NotImplementedException();
                    //dest = (byte)(opcode & 0x7);
                    //addr = ReadMemory(RegisterPC++);
                    //dest_value = Register[dest];
                    //addr_read = ReadMemory(addr);
                    //result = dest_value & addr_read;
                    //Calc_FlagS(result);
                    //Calc_FlagZ(result);
                    //Register[dest] = (ushort)result;
                    //cycles = 10;
                    //Interruptible = true;
                    //break;
				// AND@
				case 0x388:
				case 0x389:
				case 0x38A:
				case 0x38B:
				case 0x38C:
				case 0x38D:
				case 0x38E:
				case 0x38F:
				case 0x390:
				case 0x391:
				case 0x392:
				case 0x393:
				case 0x394:
				case 0x395:
				case 0x396:
				case 0x397:
				case 0x398:
				case 0x399:
				case 0x39A:
				case 0x39B:
				case 0x39C:
				case 0x39D:
				case 0x39E:
				case 0x39F:
				case 0x3A0:
				case 0x3A1:
				case 0x3A2:
				case 0x3A3:
				case 0x3A4:
				case 0x3A5:
				case 0x3A6:
				case 0x3A7:
				case 0x3A8:
				case 0x3A9:
				case 0x3AA:
				case 0x3AB:
				case 0x3AC:
				case 0x3AD:
				case 0x3AE:
				case 0x3AF:
				case 0x3B0:
				case 0x3B1:
				case 0x3B2:
				case 0x3B3:
				case 0x3B4:
				case 0x3B5:
				case 0x3B6:
				case 0x3B7:
				case 0x3B8:
				case 0x3B9:
				case 0x3BA:
				case 0x3BB:
				case 0x3BC:
				case 0x3BD:
				case 0x3BE:
				case 0x3BF:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					mem_read = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					result = Register[dest] & mem_read;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = true;
					break;
				// XOR
				case 0x3C0:
				case 0x3C1:
				case 0x3C2:
				case 0x3C3:
				case 0x3C4:
				case 0x3C5:
				case 0x3C6:
				case 0x3C7:
                    dest = (byte)(opcode & 0x7);
                    addr = ReadMemory(RegisterPC++);
                    dest_value = Register[dest];
                    addr_read = ReadMemory(addr);
                    result = dest_value ^ addr_read;
                    Calc_FlagS(result);
                    Calc_FlagZ(result);
                    Register[dest] = (ushort)result;
                    cycles = 10;
                    Interruptible = true;
                    break;
				// XOR@
				case 0x3C8:
				case 0x3C9:
				case 0x3CA:
				case 0x3CB:
				case 0x3CC:
				case 0x3CD:
				case 0x3CE:
				case 0x3CF:
				case 0x3D0:
				case 0x3D1:
				case 0x3D2:
				case 0x3D3:
				case 0x3D4:
				case 0x3D5:
				case 0x3D6:
				case 0x3D7:
				case 0x3D8:
				case 0x3D9:
				case 0x3DA:
				case 0x3DB:
				case 0x3DC:
				case 0x3DD:
				case 0x3DE:
				case 0x3DF:
				case 0x3E0:
				case 0x3E1:
				case 0x3E2:
				case 0x3E3:
				case 0x3E4:
				case 0x3E5:
				case 0x3E6:
				case 0x3E7:
				case 0x3E8:
				case 0x3E9:
				case 0x3EA:
				case 0x3EB:
				case 0x3EC:
				case 0x3ED:
				case 0x3EE:
				case 0x3EF:
				case 0x3F0:
				case 0x3F1:
				case 0x3F2:
				case 0x3F3:
				case 0x3F4:
				case 0x3F5:
				case 0x3F6:
				case 0x3F7:
				case 0x3F8:
				case 0x3F9:
				case 0x3FA:
				case 0x3FB:
				case 0x3FC:
				case 0x3FD:
				case 0x3FE:
				case 0x3FF:
					mem = (byte)((opcode >> 3) & 0x7);
					dest = (byte)(opcode & 0x7);
					mem_read = Indirect_Get(mem);
					cycles = Indirect_Get_Cycles(mem);
					result = Register[dest] ^ mem_read;
					Calc_FlagS(result);
					Calc_FlagZ(result);
					Register[dest] = (ushort)result;
					Interruptible = true;
					break;
			}
			if (FlagD == FlagD_prev)
			{
				FlagD = false;
			}
			PendingCycles -= cycles;
			TotalExecutedCycles += cycles;
			return cycles;
		}
	}
}
