using System;
using System.IO;
using Core.Interfaces;
using Core.Io;

namespace Core.Cpu
{
    public partial class Z80
    {
        private static readonly byte[] ParityTable = new byte[256];

        // Static constructor to build the table once when the emulator starts
        static Z80()
        {
            for (int i = 0; i < 256; i++)
            {
                int ones = 0;
                for (int b = 0; b < 8; b++) if ((i & (1 << b)) != 0) ones++;
                ParityTable[i] = (byte)((ones % 2 == 0) ? 0x04 : 0x00); // 0x04 if Even Parity
            }
        }

        //T-State counter
        public long TotalTStates { get; set; }

        public int InterruptMode { get; private set; } = 0;


        // Interrupt Flip-Flops
        public bool IFF1 { get; private set; } = false;
        public bool IFF2 { get; private set; } = false;
        public bool InterruptRequested { get; private set; } = false;
        private int _eiDelay = 0;
        public bool IsHalted { get; private set; } = false;

        // Main Register Set
        public RegisterPair AF;
        public RegisterPair BC;
        public RegisterPair DE;
        public RegisterPair HL;

        // Alternate Register Set
        public RegisterPair AF_Prime;
        public RegisterPair BC_Prime;
        public RegisterPair DE_Prime;
        public RegisterPair HL_Prime;

        // Index Registers
        public RegisterPair IX;
        public RegisterPair IY;

        // Special Purpose Registers
        public ushort PC; // Program Counter
        public ushort SP;
        public byte I;    // Interrupt Vector
        public byte R;    // Memory Refresh

        // The Memory Bus
        private readonly IMemory _memory;
        private readonly IIoBus _simpleIoBus;

        //External Timing interface
        public Func<ushort, long, int>? WaitStateCallback { get; set; }

        public Z80(IMemory memory, IIoBus ioBus)
        {
            _memory = memory;
            _simpleIoBus = ioBus;
            Reset();
        }

        public void Reset()
        {
            PC = 0x0000;
            SP = 0xFFFF;

            // Main Registers
            AF.Word = 0;
            BC.Word = 0;
            DE.Word = 0;
            HL.Word = 0;

            // Alternate Registers
            AF_Prime.Word = 0;
            BC_Prime.Word = 0;
            DE_Prime.Word = 0;
            HL_Prime.Word = 0;

            // Index Registers
            IX.Word = 0;
            IY.Word = 0;

            // Internal Registers
            I = 0;
            R = 0;

            // Hardware State
            IFF1 = false;
            IFF2 = false;
            InterruptMode = 0;
            TotalTStates = 0;

            _eiDelay = 0;
            IsHalted = false;
            InterruptRequested = false;
        }

        public void SaveState(BinaryWriter bw)
        {
            bw.Write(TotalTStates);
            bw.Write(InterruptMode);
            bw.Write(IFF1);
            bw.Write(IFF2);
            bw.Write(InterruptRequested);
            bw.Write(AF.Word); bw.Write(BC.Word); bw.Write(DE.Word); bw.Write(HL.Word);
            bw.Write(AF_Prime.Word); bw.Write(BC_Prime.Word); bw.Write(DE_Prime.Word); bw.Write(HL_Prime.Word);
            bw.Write(IX.Word); bw.Write(IY.Word);
            bw.Write(PC); bw.Write(SP); bw.Write(I); bw.Write(R);
        }

        public void LoadState(BinaryReader br)
        {
            TotalTStates = br.ReadInt64();
            InterruptMode = br.ReadInt32();
            IFF1 = br.ReadBoolean();
            IFF2 = br.ReadBoolean();
            InterruptRequested = br.ReadBoolean();
            AF.Word = br.ReadUInt16(); BC.Word = br.ReadUInt16(); DE.Word = br.ReadUInt16(); HL.Word = br.ReadUInt16();
            AF_Prime.Word = br.ReadUInt16(); BC_Prime.Word = br.ReadUInt16(); DE_Prime.Word = br.ReadUInt16(); HL_Prime.Word = br.ReadUInt16();
            IX.Word = br.ReadUInt16(); IY.Word = br.ReadUInt16();
            PC = br.ReadUInt16(); SP = br.ReadUInt16(); I = br.ReadByte(); R = br.ReadByte();
        }

        private void ApplyWaitStates(ushort address)
        {
            // If a system (like a ULA) is attached and listening, ask it for the delay
            if (WaitStateCallback != null)
            {
                TotalTStates += WaitStateCallback(address, TotalTStates);
            }
        }

        public int RequestInterrupt()
        {
            IsHalted = false;
            InterruptRequested = true;
            // 1. If the ROM has disabled interrupts (DI), ignore the request
            if (!IFF1) return 0;

            // 2. Acknowledge the interrupt by immediately disabling further interrupts
            IFF1 = false;
            IFF2 = false;

            _eiDelay = 0;

            // 3. Push the current Program Counter to the stack so we can return later
            Push(PC);

            // --- Interrupt Mode Dispatch ---
            if (InterruptMode == 0 || InterruptMode == 1)
            {
                // IM 1: Hardcoded jump to ROM address 0x0038
                PC = 0x0038;
                return 13; // IM 1 hardware call takes 13 T-States
            }
            else if (InterruptMode == 2)
            {
                // IM 2: Dynamic Vectored Interrupts
                // A. Form the pointer address: High byte is 'I', Low byte is the floating bus (0xFF)
                ushort vectorAddress = (ushort)((I << 8) | 0xFF);

                // B. Read the actual 16-bit ISR address from that location in memory (Little-Endian)
                byte pcLow = ReadMemory(vectorAddress);
                byte pcHigh = ReadMemory((ushort)(vectorAddress + 1));

                // C. Jump to the custom game routine!
                PC = (ushort)((pcHigh << 8) | pcLow);

                return 19; // IM 2 hardware call takes 19 T-States
            }
            else
            {
                throw new NotImplementedException($"Interrupt Mode {InterruptMode} not implemented!");
            }
        }

        // 1. For fetching opcodes and immediate values (Advances PC)
        public byte FetchByte()
        {
            ApplyWaitStates(PC);
            byte data = _memory.Read(PC);
            PC++;
            return data;
        }

        // 2. For fetching 16-bit immediate values
        private ushort FetchWord()
        {
            // By using FetchByte twice, we perfectly apply wait states to BOTH memory reads!
            byte low = FetchByte();
            byte high = FetchByte();
            return (ushort)((high << 8) | low);
        }

        // 3. For standard memory reads (e.g., LD A, (HL))
        public byte ReadMemory(ushort address)
        {
            ApplyWaitStates(address);
            return _memory.Read(address);
        }

        // 4. For standard memory writes (e.g., LD (HL), A)
        public void WriteMemory(ushort address, byte data)
        {
            ApplyWaitStates(address);
            _memory.Write(address, data);
        }

        // Placeholder for your hardware I/O
        private byte ReadPort(ushort portAddress)
        {
            return _simpleIoBus.ReadPort(portAddress);
        }

        public int Step()
        {
            int tStates;

            R = (byte)((R & 0x80) | ((R + 1) & 0x7F));

            if (IsHalted)
            {
                // The CPU is asleep! Do not fetch instructions, just pass the time.
                tStates = 4;
                TotalTStates += tStates;
            }
            else
            {
                // Normal execution
                byte opcode = ReadMemory(PC++);
                tStates = ExecuteOpcode(opcode);
                TotalTStates += tStates;
            }

            // The interrupt enablement perfectly mimics the physical hardware delay.
            // This MUST tick down even if the CPU is halted!
            if (_eiDelay > 0)
            {
                _eiDelay--;
                if (_eiDelay == 0)
                {
                    IFF1 = true;
                    IFF2 = true;
                }
            }

            return tStates;
        }



        public string GetFlagsString()
        {
            byte f = AF.Low;
            return $"S:{(f >> 7) & 1} " +
                   $"Z:{(f >> 6) & 1} " +
                   $"Y:{(f >> 5) & 1} " + // Undocumented flag
                   $"H:{(f >> 4) & 1} " +
                   $"X:{(f >> 3) & 1} " + // Undocumented flag
                   $"P/V:{(f >> 2) & 1} " +
                   $"N:{(f >> 1) & 1} " +
                   $"C:{f & 1}";
        }


        private void SubA(byte value, bool isCompare)
        {
            int result = AF.High - value;
            byte flags = 0;

            if ((result & 0x80) != 0) flags |= 0x80;
            if ((byte)result == 0) flags |= 0x40;
            if (((AF.High & 0x0F) - (value & 0x0F)) < 0) flags |= 0x10;
            if ((((AF.High ^ value) & 0x80) != 0) && (((AF.High ^ result) & 0x80) != 0)) flags |= 0x04;

            flags |= 0x02; // N flag is always 1 for SUB and CP
            if (result < 0) flags |= 0x01;

            // The CP Trap: CP uses the operand, SUB uses the result
            flags |= (byte)((isCompare ? value : result) & 0x28);

            AF.Low = flags;

            if (!isCompare) AF.High = (byte)result;
        }

        private void SbcA(byte value)
        {
            byte a = AF.High;
            byte carry = (byte)(AF.Low & 0x01);
            int result = a - value - carry;

            byte flags = 0;

            if ((result & 0x80) != 0) flags |= 0x80;
            if ((byte)result == 0) flags |= 0x40;
            if (((a & 0x0F) - (value & 0x0F) - carry) < 0) flags |= 0x10;
            if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) flags |= 0x04;

            flags |= 0x02; // N is 1
            if (result < 0) flags |= 0x01;

            flags |= (byte)(result & 0x28);

            AF.Low = flags;
            AF.High = (byte)result;
        }

        private void Sbc16(ushort value)
        {
            int hl = HL.Word;
            int carry = AF.Low & 0x01;
            int result = hl - value - carry;

            byte flags = 0;

            if ((result & 0x8000) != 0) flags |= 0x80;
            if ((ushort)result == 0) flags |= 0x40;
            if (((hl & 0x0FFF) - (value & 0x0FFF) - carry) < 0) flags |= 0x10;
            if ((((hl ^ value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) flags |= 0x04;

            flags |= 0x02; // N
            if (result < 0) flags |= 0x01;

            flags |= (byte)((result >> 8) & 0x28);

            AF.Low = flags;
            HL.Word = (ushort)result;
        }

        private void SbcHl(ushort value)
        {
            int op1 = HL.Word;
            int op2 = value;
            int carry = AF.Low & 0x01;
            int result = op1 - op2 - carry;

            byte flags = 0x02; // N: Always 1 

            if (((result >> 8) & 0x80) != 0) flags |= 0x80;
            if ((result & 0xFFFF) == 0) flags |= 0x40;

            if ((((op1 & 0x0FFF) - (op2 & 0x0FFF) - carry) & 0x1000) != 0) flags |= 0x10;
            if ((((op1 ^ op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04;
            if (result < 0) flags |= 0x01;

            flags |= (byte)((result >> 8) & 0x28);

            AF.Low = flags;
            HL.Word = (ushort)(result & 0xFFFF);
        }

        private byte Dec8(byte value)
        {
            byte result = (byte)(value - 1);
            byte carry = (byte)(AF.Low & 0x01);
            byte flags = 0;

            if ((result & 0x80) != 0) flags |= 0x80;
            if (result == 0) flags |= 0x40;
            if ((value & 0x0F) == 0) flags |= 0x10;
            if (value == 0x80) flags |= 0x04;
            flags |= 0x02; // N flag
            flags |= carry; // Preserve C flag

            flags |= (byte)(result & 0x28); // Undocumented bits

            AF.Low = flags;
            return result;
        }

        private byte Inc8(byte value)
        {
            byte result = (byte)(value + 1);
            byte carry = (byte)(AF.Low & 0x01);
            byte flags = 0;

            if ((result & 0x80) != 0) flags |= 0x80;
            if (result == 0) flags |= 0x40;
            if ((value & 0x0F) == 0x0F) flags |= 0x10;
            if (value == 0x7F) flags |= 0x04;
            flags |= carry; // Preserve C flag

            flags |= (byte)(result & 0x28); // Undocumented bits

            AF.Low = flags;
            return result;
        }

        private void And(byte value)
        {
            AF.High &= value;
            AF.Low = 0x10; // AND forces H to 1, N to 0, C to 0

            if ((AF.High & 0x80) != 0) AF.Low |= 0x80;
            if (AF.High == 0) AF.Low |= 0x40;
            AF.Low |= ParityTable[AF.High];
            AF.Low |= (byte)(AF.High & 0x28);
        }

        private void Or(byte value)
        {
            AF.High |= value;
            AF.Low = 0; // OR forces H, N, C to 0

            if ((AF.High & 0x80) != 0) AF.Low |= 0x80;
            if (AF.High == 0) AF.Low |= 0x40;
            AF.Low |= ParityTable[AF.High];
            AF.Low |= (byte)(AF.High & 0x28);
        }

        private void Xor(byte value)
        {
            AF.High ^= value;
            AF.Low = 0; // XOR forces H, N, C to 0

            if ((AF.High & 0x80) != 0) AF.Low |= 0x80;
            if (AF.High == 0) AF.Low |= 0x40;
            AF.Low |= ParityTable[AF.High];
            AF.Low |= (byte)(AF.High & 0x28);
        }

        private void Add16(ref RegisterPair dest, ushort value)
        {
            int result = dest.Word + value;
            byte flags = (byte)(AF.Low & 0xC4); // Preserve S, Z, P/V

            if (((dest.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) flags |= 0x10; // H
            if (result > 0xFFFF) flags |= 0x01; // C
            flags |= (byte)((result >> 8) & 0x28); // Undocumented bits 3 & 5

            AF.Low = flags;
            dest.Word = (ushort)result;
        }

        private void AddA(byte operand)
        {
            byte a = AF.High;
            int result = a + operand;

            AF.High = (byte)result;
            byte flags = 0;

            if ((AF.High & 0x80) != 0) flags |= 0x80;
            if (AF.High == 0) flags |= 0x40;
            if (((a & 0x0F) + (operand & 0x0F)) > 0x0F) flags |= 0x10;

            bool sameSign = ((a ^ operand) & 0x80) == 0;
            bool changedSign = ((a ^ AF.High) & 0x80) != 0;
            if (sameSign && changedSign) flags |= 0x04;

            if (result > 0xFF) flags |= 0x01;
            flags |= (byte)(AF.High & 0x28);

            AF.Low = flags;
        }

        private void AdcA(byte operand)
        {
            int aVal = AF.High;
            int carryIn = AF.Low & 0x01;
            int result = aVal + operand + carryIn;

            byte flags = 0;

            if ((result & 0x80) != 0) flags |= 0x80;
            if ((result & 0xFF) == 0) flags |= 0x40;
            if (((aVal & 0x0F) + (operand & 0x0F) + carryIn) > 0x0F) flags |= 0x10;
            if ((((aVal ^ ~operand) & (aVal ^ result)) & 0x80) != 0) flags |= 0x04;
            if (result > 0xFF) flags |= 0x01;

            flags |= (byte)(result & 0x28);

            AF.Low = flags;
            AF.High = (byte)result;
        }

        private void Adc16(ushort value)
        {
            int hl = HL.Word;
            int carry = AF.Low & 0x01;
            int result = hl + value + carry;

            byte flags = 0;

            if ((result & 0x8000) != 0) flags |= 0x80;
            if ((result & 0xFFFF) == 0) flags |= 0x40;
            if (((hl & 0x0FFF) + (value & 0x0FFF) + carry) > 0x0FFF) flags |= 0x10;
            if ((((hl ^ ~value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) flags |= 0x04;
            if (result > 0xFFFF) flags |= 0x01;

            flags |= (byte)((result >> 8) & 0x28);

            AF.Low = flags;
            HL.Word = (ushort)result;
        }

        private void AdcHl(ushort value)
        {
            int op1 = HL.Word;
            int op2 = value;
            int carry = AF.Low & 0x01;
            int result = op1 + op2 + carry;

            byte flags = 0;

            if (((result >> 8) & 0x80) != 0) flags |= 0x80;
            if ((result & 0xFFFF) == 0) flags |= 0x40;

            if ((((op1 & 0x0FFF) + (op2 & 0x0FFF) + carry) & 0x1000) != 0) flags |= 0x10;
            if ((((op1 ^ ~op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04;
            if ((result & 0x10000) != 0) flags |= 0x01;

            flags |= (byte)((result >> 8) & 0x28);

            AF.Low = flags;
            HL.Word = (ushort)(result & 0xFFFF);
        }

        private byte PerformShift(int shiftType, byte val)
        {
            bool carryOut = false;
            bool oldCarry = (AF.Low & 0x01) != 0;

            switch (shiftType)
            {
                case 0: // RLC
                    carryOut = (val & 0x80) != 0;
                    val = (byte)((val << 1) | (carryOut ? 1 : 0));
                    break;
                case 1: // RRC 
                    carryOut = (val & 0x01) != 0;
                    val = (byte)((val >> 1) | (carryOut ? 0x80 : 0x00));
                    break;
                case 2: // RL 
                    carryOut = (val & 0x80) != 0;
                    val = (byte)((val << 1) | (oldCarry ? 1 : 0));
                    break;
                case 3: // RR 
                    carryOut = (val & 0x01) != 0;
                    val = (byte)((val >> 1) | (oldCarry ? 0x80 : 0x00));
                    break;
                case 4: // SLA 
                    carryOut = (val & 0x80) != 0;
                    val = (byte)(val << 1);
                    break;
                case 5: // SRA 
                    carryOut = (val & 0x01) != 0;
                    byte signBit = (byte)(val & 0x80);
                    val = (byte)((val >> 1) | signBit);
                    break;
                case 6: // SLL (Undocumented - sometimes called SAA)
                    carryOut = (val & 0x80) != 0;
                    val = (byte)((val << 1) | 0x01); // SLL always shifts in a 1!
                    break;
                case 7: // SRL 
                    carryOut = (val & 0x01) != 0;
                    val = (byte)(val >> 1);
                    break;
                default:
                    throw new NotImplementedException($"Shift type {shiftType} not implemented!");
            }

            byte newFlags = 0;
            if (carryOut) newFlags |= 0x01;
            if ((val & 0x80) != 0) newFlags |= 0x80;
            if (val == 0) newFlags |= 0x40;
            newFlags |= ParityTable[val];
            newFlags |= (byte)(val & 0x28);
            AF.Low = newFlags;

            return val;
        }

        private void Push(ushort value)
        {
            // High byte goes first
            SP--;
            WriteMemory(SP, (byte)(value >> 8));

            // Low byte goes second
            SP--;
            WriteMemory(SP, (byte)(value & 0xFF));
        }

        private ushort Pop()
        {
            // The Z80 is Little-Endian. Low byte comes off the stack first.
            byte low = ReadMemory(SP++);

            // High byte comes off second.
            byte high = ReadMemory(SP++);

            return (ushort)((high << 8) | low);
        }

        internal int ExecuteOpcode(byte opcode)
        {
            sbyte offset = 0;
            byte oldCarry = 0;
            switch (opcode)
            {
                case 0x00: // NOP
                    return 4;
                case 0x01: // LD BC, nn
                    BC.Word = FetchWord();
                    return 10;
                case 0x02: // LD (BC), A
                    WriteMemory(BC.Word, AF.High);
                    return 7;
                case 0x03: // INC BC
                    BC.Word++;
                    return 6;
                // --- 8-Bit Increments ---
                case 0x04: BC.High = Inc8(BC.High); return 4; // INC B
                case 0x07: // RLCA
                    byte topBit = (byte)(AF.High >> 7);
                    AF.High = (byte)((AF.High << 1) | topBit);

                    byte flags07 = (byte)(AF.Low & 0xC4);
                    if (topBit != 0) flags07 |= 0x01;
                    flags07 |= (byte)(AF.High & 0x28);

                    AF.Low = flags07;
                    return 4;
                case 0x08: // EX AF, AF'
                    ushort tempAF = AF.Word;
                    AF.Word = AF_Prime.Word;
                    AF_Prime.Word = tempAF;
                    return 4;
                case 0x09: Add16(ref HL, BC.Word); return 11;
                case 0x0A: //LD A (BC)
                    AF.High = ReadMemory(BC.Word);
                    return 7;
                case 0x0C: BC.Low = Inc8(BC.Low); return 4;   // INC C
                case 0x12: // LD (DE), A
                    WriteMemory(DE.Word, AF.High);
                    return 7;
                case 0x14: DE.High = Inc8(DE.High); return 4; // INC D
                case 0x19: Add16(ref HL, DE.Word); return 11;
                case 0x1C: DE.Low = Inc8(DE.Low); return 4;   // INC E
                case 0x1E:
                    DE.Low = FetchByte(); // LD E, n
                    return 7;
                case 0x29: Add16(ref HL, HL.Word); return 11;
                case 0x24: HL.High = Inc8(HL.High); return 4; // INC H
                case 0x2C: HL.Low = Inc8(HL.Low); return 4;   // INC L
                case 0x2E: // LD L, n
                    HL.Low = FetchByte();
                    return 7;
                case 0x34:
                    WriteMemory(HL.Word, Inc8(ReadMemory(HL.Word)));
                    return 11; // INC (HL)
                case 0x39: Add16(ref HL, SP); return 11;
                case 0x3C: AF.High = Inc8(AF.High); return 4; // INC A

                // --- 8-Bit Decrements ---
                case 0x05: BC.High = Dec8(BC.High); return 4; // DEC B
                case 0x0D: BC.Low = Dec8(BC.Low); return 4;   // DEC C
                case 0x15: DE.High = Dec8(DE.High); return 4; // DEC D
                case 0x1D: DE.Low = Dec8(DE.Low); return 4;   // DEC E
                case 0x25: HL.High = Dec8(HL.High); return 4; // DEC H
                case 0x2D: HL.Low = Dec8(HL.Low); return 4;   // DEC L
                case 0x2F: // CPL
                    AF.High = (byte)(~AF.High);
                    AF.Low |= 0x12;
                    AF.Low &= 0xD7; // Ensure undocumented bits follow A
                    AF.Low |= (byte)(AF.High & 0x28);
                    return 4;
                case 0x35:
                    WriteMemory(HL.Word, Dec8(ReadMemory(HL.Word)));
                    return 11; // DEC (HL)
                case 0x3D: AF.High = Dec8(AF.High); return 4; // DEC A
                case 0x06: // LD B, n
                    BC.High = FetchByte();
                    return 7;
                case 0x0B: // DEC BC
                    BC.Word--;
                    return 6;
                case 0x0E: // LD C, n
                    BC.Low = FetchByte();
                    return 7;
                case 0x0F: // RRCA
                    {
                        byte bit0 = (byte)(AF.High & 0x01);
                        AF.High = (byte)((AF.High >> 1) | (bit0 << 7));
                        byte flags0F = (byte)(AF.Low & 0xC4);
                        flags0F |= bit0;
                        flags0F |= (byte)(AF.High & 0x28);
                        AF.Low = flags0F;
                        return 4;
                    }
                case 0x10: // DJNZ d
                    sbyte djnzOffset = (sbyte)FetchByte();
                    BC.High--; // Decrement the B register
                    if (BC.High != 0)
                    {
                        PC = (ushort)(PC + djnzOffset);
                        return 13; // Jump taken
                    }
                    return 8; // Loop finished, no jump
                case 0x11: //LD DE, nn
                    DE.Word = FetchWord();
                    return 10;
                case 0x13: // INC DE
                    DE.Word++;
                    return 6;
                case 0x16: // LD D, n
                    DE.High = FetchByte();
                    return 7;
                case 0x17: // RLA
                    oldCarry = (byte)(AF.Low & 0x01);
                    bool newCarry = (AF.High & 0x80) != 0;
                    AF.High = (byte)((AF.High << 1) | oldCarry);
                    byte flags17 = (byte)(AF.Low & 0xC4);
                    if (newCarry) flags17 |= 0x01;
                    flags17 |= (byte)(AF.High & 0x28);
                    AF.Low = flags17;
                    return 4;
                case 0x18: // JR d
                    sbyte jumpDistance = (sbyte)FetchByte();
                    PC = (ushort)(PC + jumpDistance);
                    return 12;
                case 0x1A: // LD A, (DE)
                    AF.High = ReadMemory(DE.Word);
                    return 7;
                case 0x1B: // DEC DE
                    DE.Word--;
                    return 6;
                case 0x1F: // RRA
                    {
                        oldCarry = (byte)(AF.Low & 0x01);
                        byte bit0 = (byte)(AF.High & 0x01);
                        AF.High = (byte)((AF.High >> 1) | (oldCarry << 7));
                        byte flags1F = (byte)(AF.Low & 0xC4);
                        flags1F |= bit0;
                        flags1F |= (byte)(AF.High & 0x28);
                        AF.Low = flags1F;
                        return 4;
                    }
                case 0x20: // JR NZ, e
                    offset = (sbyte)FetchByte();
                    if ((AF.Low & 0x40) == 0)
                    {
                        PC = (ushort)(PC + offset);
                        return 12;
                    }
                    return 7;
                case 0x21: // LD HL, nn
                    HL.Word = FetchWord();
                    return 10;
                case 0x22: // LD (nn), HL
                    ushort dest22 = FetchWord();
                    WriteMemory(dest22, HL.Low);
                    WriteMemory((ushort)(dest22 + 1), HL.High);
                    return 16;
                case 0x23: // INC HL
                    HL.Word++;
                    return 6;
                case 0x26: // LD H, n
                    HL.High = FetchByte();
                    return 7;
                case 0x27: // DAA
                    byte a = AF.High;
                    int correction = 0;
                    byte flags27 = AF.Low;

                    bool carry = (flags27 & 0x01) != 0;
                    bool halfCarry = (flags27 & 0x10) != 0;
                    bool isSub = (flags27 & 0x02) != 0;

                    if (halfCarry || (a & 0x0F) > 9) correction |= 0x06;

                    if (carry || a > 0x99 || (a >= 0x90 && (a & 0x0F) > 9))
                    {
                        correction |= 0x60;
                        carry = true;
                    }

                    bool newHalfCarry = false;
                    if (isSub)
                    {
                        newHalfCarry = halfCarry && (a & 0x0F) < 0x06;
                        a = (byte)(a - correction);
                    }
                    else
                    {
                        newHalfCarry = ((a & 0x0F) + (correction & 0x0F)) > 0x0F;
                        a = (byte)(a + correction);
                    }

                    AF.High = a;

                    flags27 &= 0x02;
                    if (carry) flags27 |= 0x01;
                    if (newHalfCarry) flags27 |= 0x10;
                    if ((a & 0x80) != 0) flags27 |= 0x80;
                    if (a == 0) flags27 |= 0x40;
                    flags27 |= ParityTable[a];
                    flags27 |= (byte)(a & 0x28);

                    AF.Low = flags27;
                    return 4;
                case 0x28: // JR Z, e
                    offset = (sbyte)FetchByte();
                    if ((AF.Low & 0x40) != 0)
                    {
                        PC = (ushort)(PC + offset);
                        return 12;
                    }
                    return 7;
                case 0x2A: // LD HL, (nn)
                    {
                        ushort srcAddress = FetchWord();
                        HL.Low = ReadMemory(srcAddress);
                        HL.High = ReadMemory((ushort)(srcAddress + 1));
                        return 16;
                    }
                case 0x2B: // DEC HL
                    HL.Word--;
                    return 6;
                case 0x30: // JR NC, e
                    offset = (sbyte)FetchByte();
                    if ((AF.Low & 0x01) == 0)
                    {
                        PC = (ushort)(PC + offset);
                        return 12;
                    }
                    return 7;
                case 0x31: // LD SP, nn
                    {
                        SP = FetchWord();
                        return 10;
                    }
                case 0x32: // LD (nn), A
                    {
                        ushort destAddress = FetchWord();
                        WriteMemory(destAddress, AF.High);
                        return 13;
                    }
                case 0x33: // INC SP
                    SP++;
                    return 6;
                case 0x36: // LD (HL), n
                    byte nValue = FetchByte();
                    WriteMemory(HL.Word, nValue);
                    return 10;
                case 0x37: // SCF
                    AF.Low |= 0x01;
                    AF.Low &= 0xED;
                    AF.Low |= (byte)(AF.High & 0x28);
                    return 4;
                case 0x38: // JR C, d
                    sbyte jrCOffset = (sbyte)FetchByte();
                    if ((AF.Low & 0x01) != 0)
                    {
                        PC = (ushort)(PC + jrCOffset);
                        return 12;
                    }
                    return 7;
                case 0x3A: // LD A, (nn)
                    ushort address3A = FetchWord();
                    AF.High = ReadMemory(address3A);
                    return 13;
                case 0x3B: // DEC SP
                    SP--;
                    return 6;
                case 0x3E: //LD A, n
                    AF.High = FetchByte();
                    return 7;
                case 0x3F: // CCF
                    bool previousCarry = (AF.Low & 0x01) != 0;
                    AF.Low ^= 0x01;
                    AF.Low &= 0xFD;
                    if (previousCarry) AF.Low |= 0x10;
                    else AF.Low &= 0xEF;
                    AF.Low &= 0xD7;
                    AF.Low |= (byte)(AF.High & 0x28);
                    return 4;
                case 0x40: return 4; // LD B, B
                case 0x41: BC.High = BC.Low; return 4;
                case 0x42: BC.High = DE.High; return 4;
                case 0x43: BC.High = DE.Low; return 4;
                case 0x44: BC.High = HL.High; return 4;
                case 0x45: BC.High = HL.Low; return 4;
                case 0x46: BC.High = ReadMemory(HL.Word); return 7;
                case 0x47: BC.High = AF.High; return 4;

                // --- LD C, r ---
                case 0x48: BC.Low = BC.High; return 4;
                case 0x49: return 4;
                case 0x4A: BC.Low = DE.High; return 4;
                case 0x4B: BC.Low = DE.Low; return 4;
                case 0x4C: BC.Low = HL.High; return 4;
                case 0x4D: BC.Low = HL.Low; return 4;
                case 0x4E: BC.Low = ReadMemory(HL.Word); return 7;
                case 0x4F: BC.Low = AF.High; return 4;

                // --- LD D, r ---
                case 0x50: DE.High = BC.High; return 4;
                case 0x51: DE.High = BC.Low; return 4;
                case 0x52: return 4;
                case 0x53: DE.High = DE.Low; return 4;
                case 0x54: DE.High = HL.High; return 4;
                case 0x55: DE.High = HL.Low; return 4;
                case 0x56: DE.High = ReadMemory(HL.Word); return 7;
                case 0x57: DE.High = AF.High; return 4;

                // --- LD E, r ---
                case 0x58: DE.Low = BC.High; return 4;
                case 0x59: DE.Low = BC.Low; return 4;
                case 0x5A: DE.Low = DE.High; return 4;
                case 0x5B: return 4;
                case 0x5C: DE.Low = HL.High; return 4;
                case 0x5D: DE.Low = HL.Low; return 4;
                case 0x5E: DE.Low = ReadMemory(HL.Word); return 7;
                case 0x5F: DE.Low = AF.High; return 4;

                // --- LD H, r ---
                case 0x60: HL.High = BC.High; return 4;
                case 0x61: HL.High = BC.Low; return 4;
                case 0x62: HL.High = DE.High; return 4;
                case 0x63: HL.High = DE.Low; return 4;
                case 0x64: return 4;
                case 0x65: HL.High = HL.Low; return 4;
                case 0x66: HL.High = ReadMemory(HL.Word); return 7;
                case 0x67: HL.High = AF.High; return 4;

                // --- LD L, r ---
                case 0x68: HL.Low = BC.High; return 4;
                case 0x69: HL.Low = BC.Low; return 4;
                case 0x6A: HL.Low = DE.High; return 4;
                case 0x6B: HL.Low = DE.Low; return 4;
                case 0x6C: HL.Low = HL.High; return 4;
                case 0x6D: return 4;
                case 0x6E: HL.Low = ReadMemory(HL.Word); return 7;
                case 0x6F: HL.Low = AF.High; return 4;

                // --- LD (HL), r --- 
                case 0x70: WriteMemory(HL.Word, BC.High); return 7;
                case 0x71: WriteMemory(HL.Word, BC.Low); return 7;
                case 0x72: WriteMemory(HL.Word, DE.High); return 7;
                case 0x73: WriteMemory(HL.Word, DE.Low); return 7;
                case 0x74: WriteMemory(HL.Word, HL.High); return 7;
                case 0x75: WriteMemory(HL.Word, HL.Low); return 7;
                case 0x76: // HALT
                    IsHalted = true;
                    return 4;

                //case 0x76: //HALT
                //    if (!InterruptRequested)
                //    {
                //        PC--;
                //        return 4;
                //    }
                //    else
                //    {
                //        InterruptRequested = false;
                //        return 4;
                //    }
                case 0x77: WriteMemory(HL.Word, AF.High); return 7;

                // --- LD A, r ---
                case 0x78: AF.High = BC.High; return 4;
                case 0x79: AF.High = BC.Low; return 4;
                case 0x7A: AF.High = DE.High; return 4;
                case 0x7B: AF.High = DE.Low; return 4;
                case 0x7C: AF.High = HL.High; return 4;
                case 0x7D: AF.High = HL.Low; return 4;
                case 0x7E: AF.High = ReadMemory(HL.Word); return 7;
                case 0x7F: return 4;
                case 0x80: AddA(BC.High); return 4; // ADD A, B
                case 0x81: AddA(BC.Low); return 4;  // ADD A, C
                case 0x82: AddA(DE.High); return 4; // ADD A, D
                case 0x83: AddA(DE.Low); return 4;  // ADD A, E
                case 0x84: AddA(HL.High); return 4; // ADD A, H
                case 0x85: AddA(HL.Low); return 4;  // ADD A, L
                case 0x86: AddA(ReadMemory(HL.Word)); return 7; // ADD A, (HL)
                case 0x87: AddA(AF.High); return 4; // ADD A, A

                // --- ADC A, Register Family ---
                case 0x88: AdcA(BC.High); return 4; // ADC A, B
                case 0x89: AdcA(BC.Low); return 4; // ADC A, C
                case 0x8A: AdcA(DE.High); return 4; // ADC A, D
                case 0x8B: AdcA(DE.Low); return 4; // ADC A, E
                case 0x8C: AdcA(HL.High); return 4; // ADC A, H
                case 0x8D: AdcA(HL.Low); return 4; // ADC A, L
                case 0x8F: AdcA(AF.High); return 4; // ADC A, A

                case 0x8E: // ADC A, (HL)
                    AdcA(ReadMemory(HL.Word));
                    return 7;

                case 0xCE: // ADC A, n
                    AdcA(FetchByte());
                    return 7;

                // --- SUB A, r ---
                case 0x90: SubA(BC.High, false); return 4;
                case 0x91: SubA(BC.Low, false); return 4;
                case 0x92: SubA(DE.High, false); return 4;
                case 0x93: SubA(DE.Low, false); return 4;
                case 0x94: SubA(HL.High, false); return 4;
                case 0x95: SubA(HL.Low, false); return 4;
                case 0x96: SubA(ReadMemory(HL.Word), false); return 7;
                case 0x97: SubA(AF.High, false); return 4;

                // --- SBC A, r ---
                case 0x98: SbcA(BC.High); return 4;
                case 0x99: SbcA(BC.Low); return 4;
                case 0x9A: SbcA(DE.High); return 4;
                case 0x9B: SbcA(DE.Low); return 4;
                case 0x9C: SbcA(HL.High); return 4;
                case 0x9D: SbcA(HL.Low); return 4;
                case 0x9E: SbcA(ReadMemory(HL.Word)); return 7;
                case 0x9F: SbcA(AF.High); return 4;

                case 0xA0: And(BC.High); return 4; // AND B
                case 0xA1: And(BC.Low); return 4;  // AND C
                case 0xA2: And(DE.High); return 4; // AND D
                case 0xA3: And(DE.Low); return 4;  // AND E
                case 0xA4: And(HL.High); return 4; // AND H
                case 0xA5: And(HL.Low); return 4;  // AND L
                case 0xA6: And(ReadMemory(HL.Word)); return 7; // AND (HL)
                case 0xA7: And(AF.High); return 4; // AND A
                case 0xA8: Xor(BC.High); return 4; // XOR B
                case 0xA9: Xor(BC.Low); return 4;  // XOR C
                case 0xAA: Xor(DE.High); return 4; // XOR D
                case 0xAB: Xor(DE.Low); return 4;  // XOR E
                case 0xAC: Xor(HL.High); return 4; // XOR H
                case 0xAD: Xor(HL.Low); return 4;  // XOR L
                case 0xAE: Xor(ReadMemory(HL.Word)); return 7; // XOR (HL)
                case 0xAF: Xor(AF.High); return 4; // XOR A

                // --- OR r ---
                case 0xB0: Or(BC.High); return 4; // OR B
                case 0xB1: Or(BC.Low); return 4;  // OR C
                case 0xB2: Or(DE.High); return 4; // OR D
                case 0xB3: Or(DE.Low); return 4;  // OR E
                case 0xB4: Or(HL.High); return 4; // OR H
                case 0xB5: Or(HL.Low); return 4;  // OR L
                case 0xB6: Or(ReadMemory(HL.Word)); return 7; // OR (HL)
                case 0xB7: Or(AF.High); return 4; // OR A

                // --- CP r ---
                case 0xB8: SubA(BC.High, true); return 4;
                case 0xB9: SubA(BC.Low, true); return 4;
                case 0xBA: SubA(DE.High, true); return 4;
                case 0xBB: SubA(DE.Low, true); return 4;
                case 0xBC: SubA(HL.High, true); return 4;
                case 0xBD: SubA(HL.Low, true); return 4;
                case 0xBE: SubA(ReadMemory(HL.Word), true); return 7;
                case 0xBF: SubA(AF.High, true); return 4;

                // --- Conditional Returns (11 T-States if taken, 5 if not) ---
                case 0xC0: // RET NZ
                    if ((AF.Low & 0x40) == 0) { PC = Pop(); return 11; }
                    return 5;
                case 0xE0: // RET PO (Parity Odd / No Overflow)
                    if ((AF.Low & 0x04) == 0) { PC = Pop(); return 11; }
                    return 5;
                case 0xE8: // RET PE (Parity Even / Overflow)
                    if ((AF.Low & 0x04) != 0) { PC = Pop(); return 11; }
                    return 5;
                case 0xF0: // RET P (Sign Positive)
                    if ((AF.Low & 0x80) == 0) { PC = Pop(); return 11; }
                    return 5;
                case 0xF8: // RET M (Sign Minus)
                    if ((AF.Low & 0x80) != 0) { PC = Pop(); return 11; }
                    return 5;
                case 0xC1: // POP BC
                    BC.Word = Pop();
                    return 10;
                // --- Absolute Conditional Jumps (Always 10 T-States) ---
                case 0xC2: // JP NZ, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x40) == 0) PC = addr;
                        return 10;
                    }
                case 0xCA: // JP Z, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x40) != 0) PC = addr;
                        return 10;
                    }
                case 0xD2: // JP NC, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x01) == 0) PC = addr;
                        return 10;
                    }
                case 0xDA: // JP C, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x01) != 0) PC = addr;
                        return 10;
                    }
                case 0xDB: // IN A, (n)
                    byte portOffsetDB = FetchByte();
                    ushort portAddressDB = (ushort)((AF.High << 8) | portOffsetDB);
                    AF.High = _simpleIoBus.ReadPort(portAddressDB);
                    return 11;
                case 0xE2: // JP PO, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x04) == 0) PC = addr;
                        return 10;
                    }
                case 0xEA: // JP PE, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x04) != 0) PC = addr;
                        return 10;
                    }
                case 0xF2: // JP P, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x80) == 0) PC = addr;
                        return 10;
                    }
                case 0xFA: // JP M, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x80) != 0) PC = addr;
                        return 10;
                    }
                case 0xC3:
                    PC = FetchWord();
                    return 10;
                // --- Absolute Conditional Calls (17 T-States if taken, 10 if not) ---
                case 0xC4: // CALL NZ, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x40) == 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xCC: // CALL Z, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x40) != 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xD4: // CALL NC, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x01) == 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xDC: // CALL C, nn
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x01) != 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xE4: // CALL PO, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x04) == 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xEC: // CALL PE, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x04) != 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xF4: // CALL P, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x80) == 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xFC: // CALL M, nn 
                    {
                        ushort addr = FetchWord();
                        if ((AF.Low & 0x80) != 0) { Push(PC); PC = addr; return 17; }
                        return 10;
                    }
                case 0xc5: //push bc
                    Push(BC.Word);
                    return 11;
                case 0xC6: // ADD A, n
                    AddA(FetchByte());
                    return 7;
                // --- RST Instructions (11 T-States) ---
                case 0xC7: Push(PC); PC = 0x0000; return 11;
                case 0xCF: Push(PC); PC = 0x0008; return 11;
                case 0xD7: Push(PC); PC = 0x0010; return 11;
                case 0xDF: Push(PC); PC = 0x0018; return 11;
                case 0xE7: Push(PC); PC = 0x0020; return 11;
                case 0xEF: Push(PC); PC = 0x0028; return 11;
                case 0xF7: Push(PC); PC = 0x0030; return 11;
                case 0xFF: Push(PC); PC = 0x0038; return 11;
                case 0xC8: // RET Z
                    if ((AF.Low & 0x40) != 0)
                    {
                        PC = Pop();
                        return 11;
                    }
                    return 5;
                case 0xC9: // RET
                    PC = Pop();
                    return 10;
                case 0xCB:
                    return ExecuteCBPrefix();
                case 0xCD: // CALL nn
                    ushort callAddress = FetchWord();
                    Push(PC);
                    PC = callAddress;
                    return 17;
                case 0xD0: // RET NC
                    if ((AF.Low & 0x01) == 0)
                    {
                        PC = Pop();
                        return 11;
                    }
                    return 5;
                case 0xD1: // POP DE
                    DE.Word = Pop();
                    return 10;
                case 0xD3: // OUT (n), A
                    byte portOffset = FetchByte();
                    ushort portAddress = (ushort)((AF.High << 8) | portOffset);
                    _simpleIoBus.WritePort(portAddress, AF.High);
                    return 11;
                case 0xd5: //push de
                    Push(DE.Word);
                    return 11;
                case 0xD6: // SUB n
                    SubA(FetchByte(), false);
                    return 7;
                case 0xD8: // RET C
                    if ((AF.Low & 0x01) != 0)
                    {
                        PC = Pop();
                        return 11;
                    }
                    return 5;
                case 0xD9: // EXX
                    ushort tempBC = BC.Word;
                    BC.Word = BC_Prime.Word;
                    BC_Prime.Word = tempBC;

                    ushort tempDE = DE.Word;
                    DE.Word = DE_Prime.Word;
                    DE_Prime.Word = tempDE;

                    ushort tempHL = HL.Word;
                    HL.Word = HL_Prime.Word;
                    HL_Prime.Word = tempHL;

                    return 4;
                case 0xDD:
                    return ExecuteDDPrefix();
                case 0xDE: // SBC A, n
                    SbcA(FetchByte());
                    return 7;
                case 0xE1: // POP HL
                    HL.Word = Pop();
                    return 10;
                case 0xE3: // EX (SP), HL
                    byte spLow = ReadMemory(SP);
                    byte spHigh = ReadMemory((ushort)(SP + 1));

                    WriteMemory(SP, HL.Low);
                    WriteMemory((ushort)(SP + 1), HL.High);

                    HL.Low = spLow;
                    HL.High = spHigh;

                    return 19;
                case 0xe5: //push hl
                    Push(HL.Word);
                    return 11;
                case 0xE6: // AND n
                    And(FetchByte());
                    return 7;
                case 0xE9: // JP (HL)
                    PC = HL.Word;
                    return 4;
                case 0xEB: // EX DE, HL
                    ushort tempEx = DE.Word;
                    DE.Word = HL.Word;
                    HL.Word = tempEx;
                    return 4;
                case 0xED:
                    return ExecuteExtendedPrefix();
                case 0xEE: // XOR n
                    Xor(FetchByte());
                    return 7;
                case 0xF1: // POP AF
                    AF.Word = Pop();
                    return 10;
                case 0xF3: // DI 
                    IFF1 = false;
                    IFF2 = false;
                    _eiDelay = 0; // Hard cancel any pending EI delays
                    return 4;

                case 0xFB: // EI
                    _eiDelay = 2; // Ticks down across the current and subsequent instruction
                    return 4;
                case 0xf5: //push af
                    Push(AF.Word);
                    return 11;
                case 0xF6: // OR n
                    Or(FetchByte());
                    return 7;
                case 0xF9: // LD SP, HL
                    SP = HL.Word;
                    return 6;
                
                case 0xFD:
                    return ExecuteFDPrefix();
                case 0xFE: // CP n
                    SubA(FetchByte(), true);
                    return 7;
                default:
                    throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
            }
        }

        private int ExecuteExtendedPrefix() //ED
        {
            byte extendedOpcode = FetchByte();
            //byte val = 0;

            switch (extendedOpcode)
            {
                case 0x40: // IN B, (C)
                    {
                        byte inVal40 = ReadPort(BC.Word);
                        BC.High = inVal40;

                        byte flags40 = (byte)(AF.Low & 0x01); // Preserve Carry
                        if ((inVal40 & 0x80) != 0) flags40 |= 0x80; // S
                        if (inVal40 == 0) flags40 |= 0x40;          // Z
                        flags40 |= ParityTable[inVal40];            // P/V
                        flags40 |= (byte)(inVal40 & 0x28);          // Undocumented bits 3 and 5

                        AF.Low = flags40;
                        return 12;
                    }

                case 0x50: // IN D, (C)
                    {
                        byte inVal50 = ReadPort(BC.Word);
                        DE.High = inVal50;

                        byte flags50 = (byte)(AF.Low & 0x01); // Preserve Carry
                        if ((inVal50 & 0x80) != 0) flags50 |= 0x80; // S
                        if (inVal50 == 0) flags50 |= 0x40;          // Z
                        flags50 |= ParityTable[inVal50];            // P/V
                        flags50 |= (byte)(inVal50 & 0x28);          // Undocumented bits 3 and 5

                        AF.Low = flags50;
                        return 12;
                    }
                case 0x41: // OUT (C), B
                    _simpleIoBus.WritePort(BC.Word, BC.High);
                    return 12;
                case 0x49: // OUT (C), C
                    _simpleIoBus.WritePort(BC.Word, BC.Low);
                    return 12;
                case 0x61: // OUT (C), H
                    _simpleIoBus.WritePort(BC.Word, HL.High);
                    return 12;
                case 0x69: // OUT (C), L
                    _simpleIoBus.WritePort(BC.Word, HL.Low);
                    return 12;
                case 0x43: // LD (nn), BC
                    ushort dest43 = FetchWord();
                    WriteMemory(dest43, BC.Low);
                    WriteMemory((ushort)(dest43 + 1), BC.High);
                    return 20;
                case 0x44: // NEG
                    {
                        int aBefore = AF.High;
                        int resultNeg = 0 - aBefore;

                        byte flagsNeg = 0;

                        if ((resultNeg & 0x80) != 0) flagsNeg |= 0x80;
                        if ((resultNeg & 0xFF) == 0) flagsNeg |= 0x40;
                        if ((0 - (aBefore & 0x0F)) < 0) flagsNeg |= 0x10;
                        if (aBefore == 0x80) flagsNeg |= 0x04;
                        flagsNeg |= 0x02;
                        if (aBefore != 0) flagsNeg |= 0x01;

                        flagsNeg |= (byte)(resultNeg & 0x28);

                        AF.Low = flagsNeg;
                        AF.High = (byte)resultNeg;

                        return 8;
                    }
                case 0x47: // LD I, A
                    I = AF.High;
                    return 9;
                case 0x4B: // LD BC, (nn)
                    ushort src4B = FetchWord();
                    BC.Low = ReadMemory(src4B);
                    BC.High = ReadMemory((ushort)(src4B + 1));
                    return 20;
                case 0x4D: // RETI Does not affect IFF1 or IFF2
                    PC = Pop();
                    return 14;
                case 0x4F: // LD R, A
                    R = AF.High;
                    return 9;
                case 0x51: // OUT (C), D
                           // BC.Word goes to the address bus, D (DE.High) goes to the data bus
                    _simpleIoBus.WritePort(BC.Word, DE.High);
                    return 12; // 2 M-cycles, 12 T-States
                case 0x53: // LD (nn), DE
                    ushort dest53 = FetchWord();
                    WriteMemory(dest53, DE.Low);
                    WriteMemory((ushort)(dest53 + 1), DE.High);
                    return 20;
                case 0x56: // IM 1
                    InterruptMode = 1;
                    return 8;
                case 0x57: // LD A, I
                    {
                        AF.High = I; // Copy I into A
                        byte flags57 = (byte)(AF.Low & 0x01); // Preserve Carry

                        if ((AF.High & 0x80) != 0) flags57 |= 0x80; // S flag
                        if (AF.High == 0) flags57 |= 0x40;          // Z flag
                        if (IFF2) flags57 |= 0x04;                  // P/V flag gets IFF2
                        flags57 |= (byte)(AF.High & 0x28);
                        AF.Low = flags57;
                        return 9;
                    }
                case 0x58: // IN E, (C)
                    byte inVal58 = ReadPort(BC.Word);
                    DE.Low = inVal58;

                    byte flags58 = (byte)(AF.Low & 0x01);
                    if ((inVal58 & 0x80) != 0) flags58 |= 0x80;
                    if (inVal58 == 0) flags58 |= 0x40;
                    flags58 |= ParityTable[inVal58];
                    flags58 |= (byte)(inVal58 & 0x28);

                    AF.Low = flags58;
                    return 12;
                case 0x59: // OUT (C), E
                           // BC.Word goes to the address bus, E (DE.Low) goes to the data bus
                    _simpleIoBus.WritePort(BC.Word, DE.Low);
                    return 12; // 2 M-cycles, 12 T-States
                case 0x5B: // LD DE, (nn)
                    ushort src5B = FetchWord();
                    DE.Low = ReadMemory(src5B);
                    DE.High = ReadMemory((ushort)(src5B + 1));
                    return 20;
                case 0x5E: // IM 2
                    InterruptMode = 2;
                    return 8;
                case 0x5F: // LD A, R
                    {
                        AF.High = R;
                        byte flags5F = (byte)(AF.Low & 0x01);
                        if ((AF.High & 0x80) != 0) flags5F |= 0x80;
                        if (AF.High == 0) flags5F |= 0x40;
                        if (IFF2) flags5F |= 0x04;
                        flags5F |= (byte)(AF.High & 0x28);
                        AF.Low = flags5F;
                        return 9;
                    }
                case 0x67: // RRD (Rotate Right Decimal)
                    {
                        // 1. Fetch the operands
                        byte memVal = ReadMemory(HL.Word);
                        byte aVal = AF.High;

                        // 2. Extract the three 4-bit nibbles we are rotating
                        byte aLow = (byte)(aVal & 0x0F);
                        byte memHigh = (byte)(memVal >> 4);
                        byte memLow = (byte)(memVal & 0x0F);

                        // 3. Perform the Right Rotation
                        // The old Accumulator low nibble goes to the top of memory.
                        // The old top of memory goes to the bottom of memory.
                        byte newMemVal = (byte)((aLow << 4) | memHigh);

                        // The old bottom of memory goes to the Accumulator low nibble.
                        // The Accumulator high nibble is completely untouched.
                        byte newAVal = (byte)((aVal & 0xF0) | memLow);

                        // 4. Write the results back
                        WriteMemory(HL.Word, newMemVal);
                        AF.High = newAVal;

                        // 5. Update Flags
                        // Carry (Bit 0) is PRESERVED. Half-Carry (Bit 4) and Subtract (Bit 1) are RESET.
                        byte flags = (byte)(AF.Low & 0x01);

                        if ((newAVal & 0x80) != 0) flags |= 0x80; // S: Set if A is negative
                        if (newAVal == 0) flags |= 0x40;          // Z: Set if A is zero
                        flags |= ParityTable[newAVal];            // P/V: Parity of A
                        flags |= (byte)(newAVal & 0x28);          // Undocumented bits 3 and 5 copy from A

                        AF.Low = flags;
                        return 18; // 18 T-States
                    }

                case 0x6F: // RLD (Rotate Left Decimal)
                    {
                        // 1. Fetch the operands
                        byte memVal = ReadMemory(HL.Word);
                        byte aVal = AF.High;

                        // 2. Extract the three 4-bit nibbles we are rotating
                        byte aLow = (byte)(aVal & 0x0F);
                        byte memHigh = (byte)(memVal >> 4);
                        byte memLow = (byte)(memVal & 0x0F);

                        // 3. Perform the Left Rotation
                        // The old bottom of memory goes to the top of memory.
                        // The old Accumulator low nibble goes to the bottom of memory.
                        byte newMemVal = (byte)((memLow << 4) | aLow);

                        // The old top of memory goes to the Accumulator low nibble.
                        // The Accumulator high nibble is completely untouched.
                        byte newAVal = (byte)((aVal & 0xF0) | memHigh);

                        // 4. Write the results back
                        WriteMemory(HL.Word, newMemVal);
                        AF.High = newAVal;

                        // 5. Update Flags
                        // Carry (Bit 0) is PRESERVED. Half-Carry (Bit 4) and Subtract (Bit 1) are RESET.
                        byte flags = (byte)(AF.Low & 0x01);

                        if ((newAVal & 0x80) != 0) flags |= 0x80; // S: Set if A is negative
                        if (newAVal == 0) flags |= 0x40;          // Z: Set if A is zero
                        flags |= ParityTable[newAVal];            // P/V: Parity of A
                        flags |= (byte)(newAVal & 0x28);          // Undocumented bits 3 and 5 copy from A

                        AF.Low = flags;
                        return 18; // 18 T-States
                    }
                // --- SBC HL, rr ---
                case 0x42: SbcHl(BC.Word); return 15;
                case 0x45: // RETN (Return from NMI)
                    IFF1 = IFF2; // Restore the interrupt state
                    PC = Pop();  // Jump back to where we came from
                    return 14;
                case 0x52: SbcHl(DE.Word); return 15;
                case 0x62: SbcHl(HL.Word); return 15;
                case 0x72: SbcHl(SP); return 15;
                case 0x73: // LD (nn), SP
                    ushort dest73 = FetchWord();
                    WriteMemory(dest73, (byte)SP);
                    WriteMemory((ushort)(dest73 + 1), (byte)(SP >> 8));
                    return 20;
                case 0x78: // IN A, (C)
                    byte portVal78 = ReadPort(BC.Word);
                    AF.High = portVal78;

                    byte flags78 = (byte)(AF.Low & 0x01);
                    if ((portVal78 & 0x80) != 0) flags78 |= 0x80;
                    if (portVal78 == 0) flags78 |= 0x40;
                    flags78 |= ParityTable[portVal78];
                    flags78 |= (byte)(portVal78 & 0x28);

                    AF.Low = flags78;
                    return 12;
                case 0x79: // OUT (C), A
                    _simpleIoBus.WritePort(BC.Word, AF.High);
                    return 12;
                // --- ADC HL, rr ---
                case 0x4A: Adc16(BC.Word); return 15;
                case 0x5A: Adc16(DE.Word); return 15;
                case 0x6A: Adc16(HL.Word); return 15;
                case 0x7A: Adc16(SP); return 15;
                case 0x7B: // LD SP, (nn)
                    byte addrLow = FetchByte();
                    byte addrHigh = FetchByte();
                    ushort address7B = (ushort)((addrHigh << 8) | addrLow);

                    byte spLow = ReadMemory(address7B);
                    byte spHigh = ReadMemory((ushort)(address7B + 1));

                    SP = (ushort)((spHigh << 8) | spLow);
                    return 20;
                // --- BLOCK LOADS ---
                case 0xA0: // LDI
                    {
                        byte val0 = ReadMemory(HL.Word);
                        WriteMemory(DE.Word, val0);
                        HL.Word++; DE.Word++; BC.Word--;

                        AF.Low &= 0xC1; // Preserve S, Z, C. Wipe H, N.
                        if (BC.Word != 0) AF.Low |= 0x04; // P/V

                        byte n = (byte)(AF.High + val0);
                        AF.Low |= (byte)(n & 0x08); // Bit 3
                        if ((n & 0x02) != 0) AF.Low |= 0x20; // Bit 5 from bit 1
                        return 16;
                    }
                case 0xA2: // INI
                    {
                        // 1. Read from the port specified by BC
                        byte inValA2 = _simpleIoBus.ReadPort(BC.Word);

                        // 2. Write the value to memory at HL
                        WriteMemory(HL.Word, inValA2);

                        // 3. Decrement B (the byte counter)
                        BC.High--;

                        // 4. Increment HL (the memory pointer)
                        HL.Word++;

                        // 5. Update Flags
                        AF.Low |= 0x02; // N is always set (1)
                        if (BC.High == 0) AF.Low |= 0x40; // Z is set if B reaches 0
                        else AF.Low &= 0xBF;              // Z is cleared otherwise

                        return 16; // Takes 16 T-States
                    }
                case 0xB2: // INIR
                    {
                        // This does exactly the same thing as INI, but loops until B == 0
                        byte inValB2 = _simpleIoBus.ReadPort(BC.Word);
                        WriteMemory(HL.Word, inValB2);
                        BC.High--;
                        HL.Word++;

                        AF.Low |= 0x02; // N is always set

                        if (BC.High != 0)
                        {
                            AF.Low &= 0xBF; // Z is reset
                            PC -= 2;        // Loop back and execute ED B2 again!
                            return 21;
                        }
                        else
                        {
                            AF.Low |= 0x40; // Z is set
                            return 16;
                        }
                    }
                case 0xA3: // OUTI
                    {
                        // 1. Read data from memory at HL
                        byte valA3 = ReadMemory(HL.Word);

                        // 2. Decrement the B register
                        BC.High--;

                        // 3. Output the data to the port specified by C
                        _simpleIoBus.WritePort(BC.Word, valA3);

                        // 4. Increment the memory pointer
                        HL.Word++;

                        // 5. Update Flags (N is always set. Z is set if B reached 0)
                        AF.Low |= 0x02;
                        if (BC.High == 0) AF.Low |= 0x40;
                        else AF.Low &= 0xBF;

                        return 16;
                    }
                case 0xB3: // OTIR
                    {
                        // This does exactly the same thing as OUTI, but loops until B == 0
                        byte valB3 = ReadMemory(HL.Word);
                        BC.High--;
                        _simpleIoBus.WritePort(BC.Word, valB3);
                        HL.Word++;

                        AF.Low |= 0x02;
                        if (BC.High != 0)
                        {
                            AF.Low &= 0xBF; // Z is reset
                            PC -= 2;        // Loop back and execute ED B3 again!
                            return 21;
                        }
                        else
                        {
                            AF.Low |= 0x40; // Z is set
                            return 16;
                        }
                    }
                case 0xB0: // LDIR
                    {
                        byte val00 = ReadMemory(HL.Word);
                        WriteMemory(DE.Word, val00);
                        HL.Word++; DE.Word++; BC.Word--;

                        AF.Low &= 0xC1;
                        if (BC.Word != 0)
                        {
                            AF.Low |= 0x04;
                            PC -= 2; // Loop

                            byte n1 = (byte)(AF.High + val00);
                            AF.Low |= (byte)(n1 & 0x08);
                            if ((n1 & 0x02) != 0) AF.Low |= 0x20;
                            return 21;
                        }
                        byte n2 = (byte)(AF.High + val00);
                        AF.Low |= (byte)(n2 & 0x08);
                        if ((n2 & 0x02) != 0) AF.Low |= 0x20;
                        return 16;
                    }
                case 0xA1: // CPI
                    {
                        byte memVal = ReadMemory(HL.Word);
                        byte result = (byte)(AF.High - memVal);
                        HL.Word++; BC.Word--;

                        byte flags = (byte)(AF.Low & 0x01); // Preserve Carry
                        flags |= 0x02; // N is 1
                        if (BC.Word != 0) flags |= 0x04;
                        if (((AF.High ^ memVal ^ result) & 0x10) != 0) flags |= 0x10;
                        if (result == 0) flags |= 0x40;
                        if ((result & 0x80) != 0) flags |= 0x80;

                        byte n = (byte)(AF.High - memVal - ((flags & 0x10) != 0 ? 1 : 0));
                        flags |= (byte)(n & 0x08);
                        if ((n & 0x02) != 0) flags |= 0x20; // Bit 5 from Bit 1

                        AF.Low = flags;
                        return 16;
                    }
                case 0xB1: // CPIR
                    {
                        byte memVal = ReadMemory(HL.Word);
                        byte result = (byte)(AF.High - memVal);
                        HL.Word++; BC.Word--;

                        byte flags = (byte)(AF.Low & 0x01);
                        flags |= 0x02;
                        if (BC.Word != 0) flags |= 0x04;
                        if (((AF.High ^ memVal ^ result) & 0x10) != 0) flags |= 0x10;
                        if (result == 0) flags |= 0x40;
                        if ((result & 0x80) != 0) flags |= 0x80;

                        byte n = (byte)(AF.High - memVal - ((flags & 0x10) != 0 ? 1 : 0));
                        flags |= (byte)(n & 0x08);
                        if ((n & 0x02) != 0) flags |= 0x20;

                        AF.Low = flags;
                        if (BC.Word != 0 && result != 0) { PC -= 2; return 21; }
                        return 16;
                    }
                case 0xA8: // LDD
                    {
                        byte val8 = ReadMemory(HL.Word);
                        WriteMemory(DE.Word, val8);
                        HL.Word--; DE.Word--; BC.Word--;

                        AF.Low &= 0xC1;
                        if (BC.Word != 0) AF.Low |= 0x04;

                        byte n = (byte)(AF.High + val8);
                        AF.Low |= (byte)(n & 0x08);
                        if ((n & 0x02) != 0) AF.Low |= 0x20;
                        return 16;
                    }
                case 0xA9: // CPD 
                    {
                        byte memVal = ReadMemory(HL.Word);
                        byte result = (byte)(AF.High - memVal);
                        HL.Word--; BC.Word--; // Decrement HL

                        byte flags = (byte)(AF.Low & 0x01);
                        flags |= 0x02;
                        if (BC.Word != 0) flags |= 0x04;
                        if (((AF.High ^ memVal ^ result) & 0x10) != 0) flags |= 0x10;
                        if (result == 0) flags |= 0x40;
                        if ((result & 0x80) != 0) flags |= 0x80;

                        byte n = (byte)(AF.High - memVal - ((flags & 0x10) != 0 ? 1 : 0));
                        flags |= (byte)(n & 0x08);
                        if ((n & 0x02) != 0) flags |= 0x20;

                        AF.Low = flags;
                        return 16;
                    }
                case 0xAB: // OUTD
                    {
                        // 1. Read the byte from memory at the HL address (Applying Wait States!)
                        byte outdVal = ReadMemory(HL.Word);

                        // 2. Decrement the B register
                        BC.High--;

                        // 3. Output that byte to the hardware port stored in BC
                        _simpleIoBus.WritePort(BC.Word, outdVal);

                        // 4. Decrement HL
                        HL.Word--;

                        // 5. Update Flags
                        // The N flag (Bit 1) is always set.
                        AF.Low |= 0x02;

                        // The Z flag (Bit 6) is set if B reaches 0, otherwise cleared.
                        if (BC.High == 0)
                            AF.Low |= 0x40;
                        else
                            AF.Low &= 0xBF;

                        return 16; // Takes 16 T-States
                    }
                case 0xB9: // CPDR
                    {
                        byte memVal = ReadMemory(HL.Word);
                        byte result = (byte)(AF.High - memVal);
                        HL.Word--; BC.Word--;

                        byte flags = (byte)(AF.Low & 0x01);
                        flags |= 0x02;
                        if (BC.Word != 0) flags |= 0x04;
                        if (((AF.High ^ memVal ^ result) & 0x10) != 0) flags |= 0x10;
                        if (result == 0) flags |= 0x40;
                        if ((result & 0x80) != 0) flags |= 0x80;

                        byte n = (byte)(AF.High - memVal - ((flags & 0x10) != 0 ? 1 : 0));
                        flags |= (byte)(n & 0x08);
                        if ((n & 0x02) != 0) flags |= 0x20;

                        AF.Low = flags;
                        if (BC.Word != 0 && result != 0) { PC -= 2; return 21; }
                        return 16;
                    }
                case 0xB8: // LDDR
                    {
                        byte val88 = ReadMemory(HL.Word);
                        WriteMemory(DE.Word, val88);
                        HL.Word--; DE.Word--; BC.Word--;

                        AF.Low &= 0xC1;
                        if (BC.Word != 0)
                        {
                            AF.Low |= 0x04;
                            PC -= 2; // Loop

                            byte n1 = (byte)(AF.High + val88);
                            AF.Low |= (byte)(n1 & 0x08);
                            if ((n1 & 0x02) != 0) AF.Low |= 0x20;
                            return 21;
                        }
                        byte n2 = (byte)(AF.High + val88);
                        AF.Low |= (byte)(n2 & 0x08);
                        if ((n2 & 0x02) != 0) AF.Low |= 0x20;
                        return 16;
                    }
                default:
                    return 8;
                    //throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
            }
        }

        private int ExecuteCBPrefix()
        {
            byte cbOpcode = FetchByte();
            //bool oldCarry = false;

            int operation = cbOpcode >> 6;           // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
            int bitIndex = (cbOpcode >> 3) & 0x07;
            int regIndex = cbOpcode & 0x07;

            byte bitMask = (byte)(1 << bitIndex);

            // --- PHASE 1: Fetch the target value ---
            byte val = 0;
            switch (regIndex)
            {
                case 0: val = BC.High; break;
                case 1: val = BC.Low; break;
                case 2: val = DE.High; break;
                case 3: val = DE.Low; break;
                case 4: val = HL.High; break;
                case 5: val = HL.Low; break;
                case 6: val = ReadMemory(HL.Word); break; // The 0x110 (HL) exception
                case 7: val = AF.High; break;
            }

            // --- PHASE 2: Perform the bitwise math ---
            switch (operation)
            {
                case 0: // ALL Shift/Rotate Instructions
                    int shiftType = (cbOpcode >> 3) & 0x07;

                    // Pass the value to the helper, which handles all the math and flags
                    val = PerformShift(shiftType, val);

                    break; // Break to proceed to PHASE 3 and write the value back!
                case 1: // ALL BIT Instructions
                    AF.Low &= 0x01; // Preserve ONLY Carry
                    AF.Low |= 0x10; // Set Half-Carry

                    if ((val & bitMask) == 0)
                    {
                        AF.Low |= 0x44; // Set Zero and P/V
                    }
                    else if (bitIndex == 7)
                    {
                        AF.Low |= 0x80; // If testing Bit 7 and it is 1, set Sign
                    }

                    // --- ZEXALL Strict Undocumented Bits ---
                    // For (HL) memory tests, bits 3/5 come from the High byte of the address (HL.High).
                    // For all standard registers, bits 3/5 come from the register itself (val).
                    byte undocumented = (regIndex == 6) ? HL.High : val;
                    AF.Low |= (byte)(undocumented & 0x28);
                    return (regIndex == 6) ? 12 : 8;
                case 2: // ALL RES Instructions
                    val &= (byte)(~bitMask);
                    break;

                case 3: // ALL SET Instructions
                    val |= bitMask;
                    break;
                default:
                    throw new Exception("Invalid CB operation.");
            }

            // --- PHASE 3: Write back the modified value ---
            switch (regIndex)
            {
                case 0: BC.High = val; break;
                case 1: BC.Low = val; break;
                case 2: DE.High = val; break;
                case 3: DE.Low = val; break;
                case 4: HL.High = val; break;
                case 5: HL.Low = val; break;
                case 6: WriteMemory(HL.Word, val); break;
                case 7: AF.High = val; break;
            }

            return (regIndex == 6) ? 15 : 8;
        }

        private int ExecuteDDPrefix()
        {
            byte ddOpcode = FetchByte();

            switch (ddOpcode)
            {
                case 0x09: Add16(ref IX, BC.Word); return 15;
                case 0x19: Add16(ref IX, DE.Word); return 15;
                case 0x29: Add16(ref IX, IX.Word); return 15;
                case 0x39: Add16(ref IX, SP); return 15;
                case 0x21: // LD IX, nn
                    byte low = FetchByte();
                    byte high = FetchByte();
                    IX.Word = (ushort)((high << 8) | low);
                    return 14;
                case 0x22: // LD (nn), IX
                    byte addrLow22 = FetchByte();
                    byte addrHigh22 = FetchByte();
                    ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22);
                    WriteMemory(address22, IX.Low);
                    WriteMemory((ushort)(address22 + 1), IX.High);
                    return 20;
                case 0x23: // INC IX
                    IX.Word++;
                    return 10;
                case 0x24: // INC IXH
                    IX.High = Inc8(IX.High);
                    return 8;
                case 0x25: // DEC IXH
                    IX.High = Dec8(IX.High);
                    return 8;
                case 0x26: // LD IXH, n
                    IX.High = FetchByte();
                    return 11;
                case 0x2A: // LD IX, (nn)
                    byte addrLow2A = FetchByte();
                    byte addrHigh2A = FetchByte();
                    ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A);
                    byte ixLow = ReadMemory(address2A);
                    byte ixHigh = ReadMemory((ushort)(address2A + 1));
                    IX.Word = (ushort)((ixHigh << 8) | ixLow);
                    return 20;
                case 0x2B: // DEC IX
                    IX.Word--;
                    return 10;
                case 0x2C: // INC IXL
                    IX.Low = Inc8(IX.Low);
                    return 8;
                case 0x2D: // DEC IXL
                    IX.Low = Dec8(IX.Low);
                    return 8;
                case 0x2E: // LD IXL, n
                    IX.Low = FetchByte();
                    return 11;
                case 0x34: // INC (IX+d)
                    {
                        sbyte offset34 = (sbyte)FetchByte();
                        ushort address34 = (ushort)(IX.Word + offset34);
                        byte val34 = ReadMemory(address34);
                        byte result34 = Inc8(val34);
                        WriteMemory(address34, result34);
                        return 23;
                    }
                case 0x35: // DEC (IX+d)
                    {
                        sbyte offset35 = (sbyte)FetchByte();
                        ushort address35 = (ushort)(IX.Word + offset35);
                        byte val35 = ReadMemory(address35);
                        byte result35 = Dec8(val35);
                        WriteMemory(address35, result35);
                        return 23;
                    }
                case 0x36: // LD (IX+d), n
                    sbyte offset36 = (sbyte)FetchByte();
                    byte n36 = FetchByte();
                    ushort address36 = (ushort)(IX.Word + offset36);
                    WriteMemory(address36, n36);
                    return 19;
                case 0x46: // LD B, (IX+d)
                    sbyte offset46 = (sbyte)FetchByte();
                    ushort address46 = (ushort)(IX.Word + offset46);
                    BC.High = ReadMemory(address46);
                    return 19;
                case 0x4E: // LD C, (IX+d)
                    sbyte offset4E = (sbyte)FetchByte();
                    ushort address4E = (ushort)(IX.Word + offset4E);
                    BC.Low = ReadMemory(address4E);
                    return 19;
                case 0x56: // LD D, (IX+d)
                    sbyte offset56 = (sbyte)FetchByte();
                    ushort address56 = (ushort)(IX.Word + offset56);
                    DE.High = ReadMemory(address56);
                    return 19;
                case 0x5E: // LD E, (IX+d)
                    sbyte offset5E = (sbyte)FetchByte();
                    ushort address5E = (ushort)(IX.Word + offset5E);
                    DE.Low = ReadMemory(address5E);
                    return 19;
                case 0x66: // LD H, (IX+d)
                    sbyte offset66 = (sbyte)FetchByte();
                    ushort address66 = (ushort)(IX.Word + offset66);
                    HL.High = ReadMemory(address66);
                    return 19;
                case 0x67: // LD IXH, A
                    IX.High = AF.High;
                    return 8;
                case 0x6E: // LD L, (IX+d)
                    sbyte offset6E = (sbyte)FetchByte();
                    ushort address6E = (ushort)(IX.Word + offset6E);
                    HL.Low = ReadMemory(address6E);
                    return 19;
                case 0x6F: // LD IXL, A
                    IX.Low = AF.High;
                    return 8;
                case 0x70: // LD (IX+d), B
                    sbyte offset70 = (sbyte)FetchByte();
                    ushort address70 = (ushort)(IX.Word + offset70);
                    WriteMemory(address70, BC.High);
                    return 19;
                case 0x71: // LD (IX+d), C
                    sbyte offset71 = (sbyte)FetchByte();
                    ushort address71 = (ushort)(IX.Word + offset71);
                    WriteMemory(address71, BC.Low);
                    return 19;
                case 0x72: // LD (IX+d), D
                    sbyte offset72 = (sbyte)FetchByte();
                    ushort address72 = (ushort)(IX.Word + offset72);
                    WriteMemory(address72, DE.High);
                    return 19;
                case 0x73: // LD (IX+d), E
                    sbyte offset73 = (sbyte)FetchByte();
                    ushort address73 = (ushort)(IX.Word + offset73);
                    WriteMemory(address73, DE.Low);
                    return 19;
                case 0x74: // LD (IX+d), H
                    sbyte offset74 = (sbyte)FetchByte();
                    ushort address74 = (ushort)(IX.Word + offset74);
                    WriteMemory(address74, HL.High);
                    return 19;
                case 0x75: // LD (IX+d), L
                    sbyte offset75 = (sbyte)FetchByte();
                    ushort address75 = (ushort)(IX.Word + offset75);
                    WriteMemory(address75, HL.Low);
                    return 19;
                case 0x77: // LD (IX+d), A
                    sbyte offset77 = (sbyte)FetchByte();
                    ushort address77 = (ushort)(IX.Word + offset77);
                    WriteMemory(address77, AF.High);
                    return 19;
                case 0x7C: // LD A, IXH
                    AF.High = IX.High;
                    return 8;
                case 0x7D: // LD A, IXL
                    AF.High = IX.Low;
                    return 8;
                case 0x7E: // LD A, (IX+d)
                    sbyte offset7E = (sbyte)FetchByte();
                    ushort address7E = (ushort)(IX.Word + offset7E);
                    AF.High = ReadMemory(address7E);
                    return 19;
                case 0x84: // ADD A, IXH
                    AddA(IX.High);
                    return 8;
                case 0x85: // ADD A, IXL
                    AddA(IX.Low);
                    return 8;
                case 0x86: // ADD A, (IX+d)
                    {
                        sbyte displacementAdd = (sbyte)FetchByte();
                        ushort targetAddressAdd = (ushort)(IX.Word + displacementAdd);
                        AddA(ReadMemory(targetAddressAdd));
                        return 19;
                    }
                case 0x8E: // ADC A, (IX+d)
                    {
                        sbyte offset8E = (sbyte)FetchByte();
                        ushort address8E = (ushort)(IX.Word + offset8E);
                        AdcA(ReadMemory(address8E));
                        return 19;
                    }
                case 0x9E: // SBC A, (IX+d)
                    {
                        sbyte offset9E = (sbyte)FetchByte();
                        ushort address9E = (ushort)(IX.Word + offset9E);
                        SbcA(ReadMemory(address9E));
                        return 19;
                    }
                case 0xA6: // AND (IX+d) 
                    {
                        sbyte offsetA6 = (sbyte)FetchByte();
                        ushort addressA6 = (ushort)(IX.Word + offsetA6);
                        And(ReadMemory(addressA6));
                        return 19;
                    }
                case 0xAE: // XOR (IX+d)
                    {
                        sbyte offsetAE = (sbyte)FetchByte();
                        ushort addressAE = (ushort)(IX.Word + offsetAE);
                        Xor(ReadMemory(addressAE));
                        return 19;
                    }
                // --- UNDOCUMENTED IX ALU OPERATIONS ---
                case 0x8C: AdcA(IX.High); return 8;
                case 0x8D: AdcA(IX.Low); return 8;
                case 0x94: SubA(IX.High, false); return 8;
                case 0x95: SubA(IX.Low, false); return 8;
                case 0x9C: SbcA(IX.High); return 8;
                case 0x9D: SbcA(IX.Low); return 8;
                case 0xA4: And(IX.High); return 8;
                case 0xA5: And(IX.Low); return 8;
                case 0xAC: Xor(IX.High); return 8;
                case 0xAD: Xor(IX.Low); return 8;
                case 0xB4: Or(IX.High); return 8;
                case 0xB5: Or(IX.Low); return 8;
                case 0xBC: SubA(IX.High, true); return 8;
                case 0xBD: SubA(IX.Low, true); return 8;

                // --- UNDOCUMENTED IX LOAD OPERATIONS ---
                case 0x44: BC.High = IX.High; return 8; // LD B, IXH
                case 0x45: BC.High = IX.Low; return 8;  // LD B, IXL
                case 0x4C: BC.Low = IX.High; return 8;  // LD C, IXH
                case 0x4D: BC.Low = IX.Low; return 8;   // LD C, IXL
                case 0x54: DE.High = IX.High; return 8; // LD D, IXH
                case 0x55: DE.High = IX.Low; return 8;  // LD D, IXL
                case 0x5C: DE.Low = IX.High; return 8;  // LD E, IXH
                case 0x5D: DE.Low = IX.Low; return 8;   // LD E, IXL
                case 0x60: IX.High = BC.High; return 8; // LD IXH, B
                case 0x61: IX.High = BC.Low; return 8;  // LD IXH, C
                case 0x62: IX.High = DE.High; return 8; // LD IXH, D
                case 0x63: IX.High = DE.Low; return 8;  // LD IXH, E
                case 0x64:  return 8; // LD IXH, IXH
                case 0x65: IX.High = IX.Low; return 8;  // LD IXH, IXL
                case 0x68: IX.Low = BC.High; return 8;  // LD IXL, B
                case 0x69: IX.Low = BC.Low; return 8;   // LD IXL, C
                case 0x6A: IX.Low = DE.High; return 8;  // LD IXL, D
                case 0x6B: IX.Low = DE.Low; return 8;   // LD IXL, E
                case 0x6C: IX.Low = IX.High; return 8;  // LD IXL, IXH
                case 0x6D:  return 8;   // LD IXL, IXL
                case 0x96: // SUB (IX+d)
                    {
                        sbyte offset96 = (sbyte)FetchByte();
                        ushort address96 = (ushort)(IX.Word + offset96);
                        SubA(ReadMemory(address96), false);
                        return 19;
                    }
                case 0xB6: // OR (IX+d)
                    {
                        sbyte offsetB6 = (sbyte)FetchByte();
                        ushort addressB6 = (ushort)(IX.Word + offsetB6);

                        // Read the memory and pass it straight into your helper
                        Or(ReadMemory(addressB6));

                        return 19; // Takes 19 T-States
                    }
                case 0xBE: // CP (IX+d)
                    {
                        sbyte offsetBE = (sbyte)FetchByte();
                        ushort addressBE = (ushort)(IX.Word + offsetBE);
                        SubA(ReadMemory(addressBE), true);
                        return 19;
                    }
                case 0xCB: // The DD CB nested prefix
                    {
                        sbyte displacement = (sbyte)FetchByte();
                        byte cbOpcode = FetchByte();
                        ushort targetAddress = (ushort)(IX.Word + displacement);
                        byte memVal = ReadMemory(targetAddress);

                        int operation = cbOpcode >> 6;
                        int bitIndex = (cbOpcode >> 3) & 0x07;
                        byte bitMask = (byte)(1 << bitIndex);
                        int regIndex = cbOpcode & 0x07;

                        switch (operation)
                        {
                            case 0: // ALL Shift/Rotate Instructions
                                int shiftType = (cbOpcode >> 3) & 0x07;

                                // Perform the shift and flag math
                                memVal = PerformShift(shiftType, memVal);
                                WriteMemory(targetAddress, memVal);

                                // Z80 UNDOCUMENTED QUIRK:
                                // DDCB and FDCB shift instructions ALSO copy the result into a standard register
                                // unless the target register index is 6 (which is purely memory).
                                if (regIndex != 6)
                                {
                                    switch (regIndex)
                                    {
                                        case 0: BC.High = memVal; break;
                                        case 1: BC.Low = memVal; break;
                                        case 2: DE.High = memVal; break;
                                        case 3: DE.Low = memVal; break;
                                        case 4: HL.High = memVal; break;
                                        case 5: HL.Low = memVal; break;
                                        case 7: AF.High = memVal; break;
                                    }
                                }
                                return 23;
                            case 1: // ALL BIT Instructions
                                AF.Low &= 0x01;
                                AF.Low |= 0x10;

                                if ((memVal & bitMask) == 0)
                                {
                                    AF.Low |= 0x44;
                                }
                                else if (bitIndex == 7)
                                {
                                    AF.Low |= 0x80;
                                }
                                return 20;

                            case 2: // ALL RES Instructions
                                memVal &= (byte)(~bitMask);
                                WriteMemory(targetAddress, memVal);
                                return 23;

                            case 3: // ALL SET Instructions
                                memVal |= bitMask;
                                WriteMemory(targetAddress, memVal);
                                return 23;

                            default:
                                throw new Exception("Invalid bitwise operation.");
                        }
                    }
                case 0xE1: // POP IX
                    byte popLow = ReadMemory(SP);
                    SP++;
                    byte popHigh = ReadMemory(SP);
                    SP++;
                    IX.Word = (ushort)((popHigh << 8) | popLow);
                    return 14;
                case 0xE3: // EX (SP), IX
                    byte spLowIX = ReadMemory(SP);
                    byte spHighIX = ReadMemory((ushort)(SP + 1));
                    WriteMemory(SP, IX.Low);
                    WriteMemory((ushort)(SP + 1), IX.High);
                    IX.Low = spLowIX;
                    IX.High = spHighIX;
                    return 23;
                case 0xE5: // PUSH IX
                    SP--;
                    WriteMemory(SP, IX.High);
                    SP--;
                    WriteMemory(SP, IX.Low);
                    return 15;
                case 0xE9: // JP (IX)
                    PC = IX.Word;
                    return 8;
                case 0xF9: // LD SP, IX
                    SP = IX.Word;
                    return 10;
                default:
                    // The Z80 Ignored Prefix Quirk!
                    // If the instruction doesn't involve IX, ignore the prefix, 
                    // run the base instruction, and charge 4 T-States for the delay.
                    return ExecuteOpcode(ddOpcode) + 4;
            }
        }

        private int ExecuteFDPrefix()
        {
            byte ddOpcode = FetchByte();
            ushort targetAddress = 0;
            byte memVal = 0;

            switch (ddOpcode)
            {
                case 0x09: Add16(ref IY, BC.Word); return 15;
                case 0x19: Add16(ref IY, DE.Word); return 15;
                case 0x21: // LD IY, nn
                    IY.Word = FetchWord();
                    return 14;
                case 0x22: // LD (nn), IY
                    {
                        byte addrLow = FetchByte();
                        byte addrHigh = FetchByte();
                        ushort address = (ushort)((addrHigh << 8) | addrLow);

                        WriteMemory(address, IY.Low);
                        WriteMemory((ushort)(address + 1), IY.High);

                        return 20;
                    }
                case 0x23: // INC IY
                    IY.Word++;
                    return 10;
                case 0x24: // INC IYH
                    IY.High = Inc8(IY.High);
                    return 8;
                case 0x25: // DEC IYH
                    IY.High = Dec8(IY.High);
                    return 8;
                case 0x26: // LD IYH, n
                    IY.High = FetchByte();
                    return 11;
                case 0x29: Add16(ref IY, IY.Word); return 15;
                case 0x2A: // LD IY, (nn)
                    {
                        byte addrLow = FetchByte();
                        byte addrHigh = FetchByte();
                        ushort address = (ushort)((addrHigh << 8) | addrLow);

                        IY.Low = ReadMemory(address);
                        IY.High = ReadMemory((ushort)(address + 1));

                        return 20;
                    }
                case 0x2B: // DEC IY
                    IY.Word--;
                    return 10;
                case 0x2C: // INC IYL
                    IY.Low = Inc8(IY.Low);
                    return 8;
                case 0x2D: // DEC IYL
                    IY.Low = Dec8(IY.Low);
                    return 8;
                case 0x2E: // LD IYL, n
                    IY.Low = FetchByte();
                    return 11;
                case 0x34: // INC (IY+d)
                    {
                        sbyte offset34 = (sbyte)FetchByte();
                        ushort address34 = (ushort)(IY.Word + offset34);
                        byte valBefore = ReadMemory(address34);
                        byte result34 = Inc8(valBefore);
                        WriteMemory(address34, result34);
                        return 23;
                    }
                case 0x35: // DEC (IY+d)
                    sbyte offset = (sbyte)FetchByte();
                    targetAddress = (ushort)(IY.Word + offset);
                    memVal = ReadMemory(targetAddress);
                    byte decVal = Dec8(memVal);
                    WriteMemory(targetAddress, decVal);
                    return 23;
                case 0x36: // LD (IY+d), n
                    {
                        sbyte offset36 = (sbyte)FetchByte();
                        byte nValue = FetchByte();
                        targetAddress = (ushort)(IY.Word + offset36);
                        WriteMemory(targetAddress, nValue);
                        return 19;
                    }
                case 0x39: Add16(ref IY, SP); return 15;
                case 0x46: // LD B, (IY+d)
                    {
                        sbyte displacement = (sbyte)FetchByte();
                        targetAddress = (ushort)(IY.Word + displacement);
                        BC.High = ReadMemory(targetAddress);
                        return 19;
                    }
                case 0x4E: // LD C, (IY+d)
                    sbyte offset4E = (sbyte)FetchByte();
                    ushort address4E = (ushort)(IY.Word + offset4E);
                    BC.Low = ReadMemory(address4E);
                    return 19;
                case 0x56: // LD D, (IY+d)
                    sbyte offset56 = (sbyte)FetchByte();
                    ushort address56 = (ushort)(IY.Word + offset56);
                    DE.High = ReadMemory(address56);
                    return 19;
                case 0x5E: // LD E, (IY+d)
                    sbyte offset5E = (sbyte)FetchByte();
                    ushort address5E = (ushort)(IY.Word + offset5E);
                    DE.Low = ReadMemory(address5E);
                    return 19;
                case 0x66: // LD H, (IY+d)
                    sbyte offset66 = (sbyte)FetchByte();
                    ushort address66 = (ushort)(IY.Word + offset66);
                    HL.High = ReadMemory(address66);
                    return 19;
                case 0x6E: // LD L, (IY+d)
                    sbyte displacementVal = (sbyte)FetchByte();
                    ushort targetAddr = (ushort)(IY.Word + displacementVal);
                    HL.Low = ReadMemory(targetAddr);
                    return 19;
                case 0x70: // LD (IY+d), B
                    {
                        sbyte offset70 = (sbyte)FetchByte();
                        ushort address70 = (ushort)(IY.Word + offset70);
                        WriteMemory(address70, BC.High);
                        return 19;
                    }
                case 0x71: // LD (IY+d), C
                    {
                        sbyte offset71 = (sbyte)FetchByte();
                        targetAddress = (ushort)(IY.Word + offset71);
                        WriteMemory(targetAddress, BC.Low);
                        return 19;
                    }
                case 0x72: // LD (IY+d), D
                    sbyte offset72 = (sbyte)FetchByte();
                    ushort address72 = (ushort)(IY.Word + offset72);
                    WriteMemory(address72, DE.High);
                    return 19;
                case 0x73: // LD (IY+d), E
                    sbyte offset73 = (sbyte)FetchByte();
                    ushort address73 = (ushort)(IY.Word + offset73);
                    WriteMemory(address73, DE.Low);
                    return 19;
                case 0x74: // LD (IY+d), H
                    sbyte offset74 = (sbyte)FetchByte();
                    ushort address74 = (ushort)(IY.Word + offset74);
                    WriteMemory(address74, HL.High);
                    return 19;
                case 0x75: // LD (IY+d), L                    
                    sbyte offset75 = (sbyte)FetchByte();
                    targetAddress = (ushort)(IY.Word + offset75);
                    WriteMemory(targetAddress, HL.Low);
                    return 19;
                case 0x77: // LD (IY+d), A
                    sbyte offset77 = (sbyte)FetchByte();
                    ushort address77 = (ushort)(IY.Word + offset77);
                    WriteMemory(address77, AF.High);
                    return 19;
                case 0x7E: // LD A, (IY+d)
                    sbyte offset7E = (sbyte)FetchByte();
                    ushort address7E = (ushort)(IY.Word + offset7E);
                    AF.High = ReadMemory(address7E);
                    return 19;
                case 0x84: // ADD A, IYH
                    AddA(IY.High);
                    return 8;
                case 0x85: // ADD A, IYL
                    AddA(IY.Low);
                    return 8;
                case 0x86: // ADD A, (IY+d)
                    {
                        sbyte displacementAdd = (sbyte)FetchByte();
                        ushort targetAddressAdd = (ushort)(IY.Word + displacementAdd);
                        byte valueToAdd = ReadMemory(targetAddressAdd);
                        AddA(valueToAdd);
                        return 19;
                    }
                case 0x8E: // ADC A, (IY+d)
                    {
                        sbyte offset8E = (sbyte)FetchByte();
                        ushort address8E = (ushort)(IY.Word + offset8E);
                        AdcA(ReadMemory(address8E));
                        return 19;
                    }
                case 0x9E: // SBC A, (IY+d)
                    {
                        sbyte offset9E = (sbyte)FetchByte();
                        ushort address9E = (ushort)(IY.Word + offset9E);
                        SbcA(ReadMemory(address9E));
                        return 19;
                    }
                case 0xA6: // AND (IY+d) 
                    {
                        sbyte offsetA6 = (sbyte)FetchByte();
                        ushort addressA6 = (ushort)(IY.Word + offsetA6);
                        And(ReadMemory(addressA6));
                        return 19;
                    }
                case 0xAE: // XOR (IY+d)
                    {
                        sbyte offsetAE = (sbyte)FetchByte();
                        ushort addressAE = (ushort)(IY.Word + offsetAE);
                        Xor(ReadMemory(addressAE));
                        return 19;
                    }
                // --- UNDOCUMENTED IY ALU OPERATIONS ---
                case 0x8C: AdcA(IY.High); return 8;
                case 0x8D: AdcA(IY.Low); return 8;
                case 0x94: SubA(IY.High, false); return 8;
                case 0x95: SubA(IY.Low, false); return 8;
                case 0x9C: SbcA(IY.High); return 8;
                case 0x9D: SbcA(IY.Low); return 8;
                case 0xA4: And(IY.High); return 8;
                case 0xA5: And(IY.Low); return 8;
                case 0xAC: Xor(IY.High); return 8;
                case 0xAD: Xor(IY.Low); return 8;
                case 0xB4: Or(IY.High); return 8;
                case 0xB5: Or(IY.Low); return 8;
                case 0xBC: SubA(IY.High, true); return 8;
                case 0xBD: SubA(IY.Low, true); return 8;

                // --- UNDOCUMENTED IY LOAD OPERATIONS ---
                case 0x44: BC.High = IY.High; return 8; // LD B, IYH
                case 0x45: BC.High = IY.Low; return 8;  // LD B, IYL
                case 0x4C: BC.Low = IY.High; return 8;  // LD C, IYH
                case 0x4D: BC.Low = IY.Low; return 8;   // LD C, IYL
                case 0x54: DE.High = IY.High; return 8; // LD D, IYH
                case 0x55: DE.High = IY.Low; return 8;  // LD D, IYL
                case 0x5C: DE.Low = IY.High; return 8;  // LD E, IYH
                case 0x5D: DE.Low = IY.Low; return 8;   // LD E, IYL
                case 0x60: IY.High = BC.High; return 8; // LD IYH, B
                case 0x61: IY.High = BC.Low; return 8;  // LD IYH, C
                case 0x62: IY.High = DE.High; return 8; // LD IYH, D
                case 0x63: IY.High = DE.Low; return 8;  // LD IYH, E
                case 0x64: return 8; // LD IYH, IYH
                case 0x65: IY.High = IY.Low; return 8;  // LD IYH, IYL
                case 0x67: // LD IYH, A
                    IY.High = AF.High;
                    return 8;
                case 0x6F: // LD IYL, A
                    IY.Low = AF.High;
                    return 8;
                case 0x7C: // LD A, IYH
                    AF.High = IY.High;
                    return 8;
                case 0x7D: // LD A, IYL
                    AF.High = IY.Low;
                    return 8;
                case 0x68: IY.Low = BC.High; return 8;  // LD IYL, B
                case 0x69: IY.Low = BC.Low; return 8;   // LD IYL, C
                case 0x6A: IY.Low = DE.High; return 8;  // LD IYL, D
                case 0x6B: IY.Low = DE.Low; return 8;   // LD IYL, E
                case 0x6C: IY.Low = IY.High; return 8;  // LD IYL, IYH
                case 0x6D: return 8;   // LD IYL, IYL
                case 0x96: // SUB (IY+d)
                    {
                        sbyte offset96 = (sbyte)FetchByte();
                        ushort address96 = (ushort)(IY.Word + offset96);
                        SubA(ReadMemory(address96), false);
                        return 19;
                    }
                case 0xB6: // OR (IY+d)
                    {
                        sbyte offsetB6 = (sbyte)FetchByte();
                        ushort addressB6 = (ushort)(IY.Word + offsetB6);

                        // Read the memory and pass it straight into your helper
                        Or(ReadMemory(addressB6));

                        return 19; // Takes 19 T-States
                    }
                case 0xBE: // CP (IY+d)
                    {
                        sbyte offsetBE = (sbyte)FetchByte();
                        ushort addressBE = (ushort)(IY.Word + offsetBE);
                        SubA(ReadMemory(addressBE), true);
                        return 19;
                    }
                case 0xCB: // The FD CB nested prefix
                    {
                        sbyte displacement = (sbyte)FetchByte();
                        byte cbOpcode = FetchByte();
                        targetAddress = (ushort)(IY.Word + displacement);
                        memVal = ReadMemory(targetAddress);

                        int operation = cbOpcode >> 6;
                        int bitIndex = (cbOpcode >> 3) & 0x07;
                        byte bitMask = (byte)(1 << bitIndex);
                        int regIndex = cbOpcode & 0x07;

                        switch (operation)
                        {
                            case 0: // ALL Shift/Rotate Instructions
                                int shiftType = (cbOpcode >> 3) & 0x07;

                                // Perform the shift and flag math
                                memVal = PerformShift(shiftType, memVal);
                                WriteMemory(targetAddress, memVal);

                                // Z80 UNDOCUMENTED QUIRK:
                                // DDCB and FDCB shift instructions ALSO copy the result into a standard register
                                // unless the target register index is 6 (which is purely memory).
                                if (regIndex != 6)
                                {
                                    switch (regIndex)
                                    {
                                        case 0: BC.High = memVal; break;
                                        case 1: BC.Low = memVal; break;
                                        case 2: DE.High = memVal; break;
                                        case 3: DE.Low = memVal; break;
                                        case 4: HL.High = memVal; break;
                                        case 5: HL.Low = memVal; break;
                                        case 7: AF.High = memVal; break;
                                    }
                                }
                                return 23;
                            case 1: // ALL BIT Instructions
                                AF.Low &= 0x01;
                                AF.Low |= 0x10;

                                if ((memVal & bitMask) == 0)
                                {
                                    AF.Low |= 0x44;
                                }
                                else if (bitIndex == 7)
                                {
                                    AF.Low |= 0x80;
                                }
                                return 20;

                            case 2: // ALL RES Instructions
                                memVal &= (byte)(~bitMask);
                                WriteMemory(targetAddress, memVal);
                                return 23;

                            case 3: // ALL SET Instructions
                                memVal |= bitMask;
                                WriteMemory(targetAddress, memVal);
                                return 23;

                            default:
                                throw new Exception("Invalid bitwise operation.");
                        }
                    }
                case 0xE1: // POP IY
                    IY.Low = ReadMemory(SP);
                    SP++;
                    IY.High = ReadMemory(SP);
                    SP++;
                    return 14;
                case 0xE3: // EX (SP), IY
                    byte spLowIY = ReadMemory(SP);
                    byte spHighIY = ReadMemory((ushort)(SP + 1));
                    WriteMemory(SP, IY.Low);
                    WriteMemory((ushort)(SP + 1), IY.High);
                    IY.Low = spLowIY;
                    IY.High = spHighIY;
                    return 23;
                case 0xE5: // PUSH IY
                    SP--;
                    WriteMemory(SP, IY.High);
                    SP--;
                    WriteMemory(SP, IY.Low);
                    return 15;
                case 0xF9: // LD SP, IY
                    SP = IY.Word;
                    return 10;
                default:
                    // Z80 nonsense loopback bug
                    return ExecuteOpcode(ddOpcode) + 4;
            }
        }
    }
}
