diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 1bafaf8..dc68aa4 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -2480,3012 +2480,3 @@ namespace Core.Cpu } } } -//using System; -//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 -// } -// } - -// public bool IsZexDocMode { get; set; } = false; -// //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; - -// // 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; // Stack Pointer -// public byte I; // Interrupt Vector -// public byte R; // Memory Refresh - -// // The Memory Bus -// private readonly IMemory _memory; -// private readonly IO_Bus _simpleIoBus; -// public TapManager _tapManager; - -// //External Timing interface -// public Func? WaitStateCallback { get; set; } - -// //Misc Variables -// public bool EnableFastLoad { get; set; } = true; -// byte newFlags = 0; -// int result = 0; - -// public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager) -// { -// _memory = memory; -// _simpleIoBus = ioBus; -// _tapManager = tapManager; -// 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; -// //_memory.CleanRAMData(); -// } - -// 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() -// { -// 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; - -// // 3. Push the current Program Counter to the stack so we can return later -// Push(PC); - -// // --- Interrupt Mode Dispatch --- -// if (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 -// { -// // (IM 0 is theoretically possible but essentially unused on the standard Spectrum) -// 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); -// } - -// // Helper method to calculate if a byte has an Even Parity of 1s -// private bool CalculateParity(byte b) -// { -// int bits = 0; -// for (int i = 0; i < 8; i++) -// { -// if ((b & (1 << i)) != 0) bits++; -// } -// return (bits % 2) == 0; -// } - -// // Placeholder for your hardware I/O -// private byte ReadPort(ushort portAddress) -// { -// return _simpleIoBus.ReadPort(portAddress); -// } - -// public int Step() -// { -// if (IsZexDocMode && PC == 0x0005) -// { -// // CP/M System Call Hook -// if (BC.Low == 2) // C = 2: Print a single character stored in register E -// { -// System.Diagnostics.Debug.Write((char)DE.Low); -// } -// else if (BC.Low == 9) // C = 9: Print a string starting at memory address DE, terminated by '$' -// { -// ushort addr = DE.Word; -// while (true) -// { -// char c = (char)ReadMemory(addr++); -// if (c == '$') break; -// System.Diagnostics.Debug.Write(c); -// } -// } - -// // Emulate a 'RET' instruction to return from the CALL 0x0005 -// byte retLow = ReadMemory(SP); -// SP++; -// byte retHigh = ReadMemory(SP); -// SP++; -// PC = (ushort)((retHigh << 8) | retLow); - -// return 10; // Skip normal execution for this cycle -// } -// if (PC == 0x0556) -// { -// if (EnableFastLoad) -// { -// HandleInstantTapeLoad(); -// return 100; -// } -// else -// { -// _tapManager.Play(); -// } -// } - -// // Fetch the next opcode and increment the Program Counter -// byte opcode = ReadMemory(PC++); -// int tStates = ExecuteOpcode(opcode); -// TotalTStates += tStates; - -// // Decode and execute -// return tStates; -// } - -// public void LoadSNA(byte[] snaData) -// { -// if (snaData.Length != 49179) -// throw new Exception("Invalid 48K SNA File Size!"); - -// // --- 1. Load CPU Registers --- -// I = snaData[0]; -// HL_Prime.Word = (ushort)(snaData[1] | (snaData[2] << 8)); -// DE_Prime.Word = (ushort)(snaData[3] | (snaData[4] << 8)); -// BC_Prime.Word = (ushort)(snaData[5] | (snaData[6] << 8)); -// AF_Prime.Word = (ushort)(snaData[7] | (snaData[8] << 8)); - -// HL.Word = (ushort)(snaData[9] | (snaData[10] << 8)); -// DE.Word = (ushort)(snaData[11] | (snaData[12] << 8)); -// BC.Word = (ushort)(snaData[13] | (snaData[14] << 8)); -// IY.Word = (ushort)(snaData[15] | (snaData[16] << 8)); -// IX.Word = (ushort)(snaData[17] | (snaData[18] << 8)); - -// IFF2 = (snaData[19] & 0x04) != 0; -// IFF1 = IFF2; -// R = snaData[20]; -// AF.Word = (ushort)(snaData[21] | (snaData[22] << 8)); -// SP = (ushort)(snaData[23] | (snaData[24] << 8)); -// InterruptMode = snaData[25]; - -// // --- 2. Load the 48K RAM Dump --- -// // The RAM dump starts at byte 27 and maps perfectly to 0x4000 -> 0xFFFF -// for (int i = 0; i < 49152; i++) -// { -// WriteMemory((ushort)(0x4000 + i), snaData[27 + i]); -// } - -// // --- 3. The Magic Bullet --- -// // In the SNA format, the Program Counter (PC) isn't in the header. -// // It was PUSHED to the stack exactly 1 instruction before the snapshot was saved. -// // So, we just pop it off the stack to resume execution! -// PC = Pop(); -// } - -// private void HandleInstantTapeLoad() -// { -// // 1. Grab the next block from the virtual cassette -// byte[] block = _tapManager.GetNextBlock(); -// if (block == null) return; // Tape ended unexpectedly - -// // 2. Verify the block type. -// // The ROM passes the expected flag (0x00 for Header, 0xFF for Data) in the A register. -// byte expectedFlag = AF.High; -// if (block[0] != expectedFlag) -// { -// // Tape loading error! Simulate a failure by clearing the Carry flag and returning. -// AF.Low &= unchecked((byte)~0x01); -// ExecuteRet(); -// return; -// } - -// // 3. Copy the data block straight into the Spectrum's RAM! -// // DE holds the number of bytes the ROM *wants*. We copy that much, skipping the Flag byte. -// int bytesToCopy = DE.Word; -// for (int i = 0; i < bytesToCopy; i++) -// { -// WriteMemory((ushort)(IX.Word + i), block[i + 1]); -// } - -// // 4. Update the registers exactly how the ROM would after a successful load -// IX.Word = (ushort)(IX.Word + bytesToCopy); -// DE.Word = 0; - -// // 5. Simulate the Checksum Match (Accumulator becomes 0) -// AF.High = 0x00; - -// // 6. Set Carry Flag (Bit 0) for Success, and Zero Flag (Bit 6) for Checksum Match -// AF.Low |= 0x41; // 0x41 is binary 0100 0001 (Zero and Carry both set) - -// // 7. Simulate a standard 'RET' instruction to exit the LD-BYTES routine safely -// ExecuteRet(); -// } - -// // A quick helper to simulate a RET instruction manually -// private void ExecuteRet() -// { -// byte pcLow = ReadMemory(SP); -// SP++; -// byte pcHigh = ReadMemory(SP); -// SP++; -// PC = (ushort)((pcHigh << 8) | pcLow); -// } - -// // Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2 - - -// 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 Sub(byte value) -// { -// byte a = AF.High; -// result = a - value; - -// // Save the result back to the Accumulator -// AF.High = (byte)result; - -// // --- Update Flags (F Register) --- -// AF.Low = 0; // Clear all flags - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if ((byte)result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if borrow from bit 4 -// if (((a & 0x0F) - (value & 0x0F)) < 0) AF.Low |= 0x10; - -// // Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes -// if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS set for CP/SUB -// AF.Low |= 0x02; - -// // Carry Flag (Bit 0) - Set if the overall result dropped below 0 -// if (result < 0) AF.Low |= 0x01; -// } - -// private void Sbc(byte value) -// { -// byte a = AF.High; -// byte carry = (byte)(AF.Low & 0x01); // Get the current Carry flag (Bit 0) - -// // Calculate the raw integer result to check for borrows/underflows -// result = a - value - carry; - -// // Update the Accumulator -// AF.High = (byte)result; - -// // --- Update Flags (F Register) --- -// AF.Low = 0; // Clear all flags - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if ((byte)result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if borrow from bit 4 -// if (((a & 0x0F) - (value & 0x0F) - carry) < 0) AF.Low |= 0x10; - -// // Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes -// if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS set for subtraction -// AF.Low |= 0x02; - -// // Carry Flag (Bit 0) - Set if the overall result dropped below 0 -// if (result < 0) AF.Low |= 0x01; -// } - -// private void Sbc16(ushort value) -// { -// int hl = HL.Word; -// int carry = AF.Low & 0x01; - -// // Calculate the raw integer result to check for underflows -// result = hl - value - carry; - -// // Update the HL register -// HL.Word = (ushort)result; - -// // --- Update Flags (F Register) --- -// AF.Low = 0; // Clear all flags - -// // Sign Flag (Bit 7) - Set if the 16-bit result is negative (bit 15 is 1) -// if ((result & 0x8000) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) - Set if the entire 16-bit result is exactly 0 -// if ((ushort)result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if borrow from bit 11 -// if (((hl & 0x0FFF) - (value & 0x0FFF) - carry) < 0) AF.Low |= 0x10; - -// // Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes -// if ((((hl ^ value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS set for subtraction -// AF.Low |= 0x02; - -// // Carry Flag (Bit 0) - Set if the overall 16-bit result dropped below 0 -// if (result < 0) AF.Low |= 0x01; -// } - -// private void SbcHl(ushort value) -// { -// int op1 = HL.Word; -// int op2 = value; -// int carry = AF.Low & 0x01; // Current C flag -// int result = op1 - op2 - carry; - -// byte flags = 0x02; // N: Always 1 (Subtract) - -// if (((result >> 8) & 0x80) != 0) flags |= 0x80; // S: Sign flag (Bit 15) -// if ((result & 0xFFFF) == 0) flags |= 0x40; // Z: Zero flag - -// // H: Half-borrow from Bit 12 -// if ((((op1 & 0x0FFF) - (op2 & 0x0FFF) - carry) & 0x1000) != 0) flags |= 0x10; - -// // P/V: 16-bit Overflow logic -// if ((((op1 ^ op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04; - -// // C: Borrow from Bit 15 -// if (result < 0) flags |= 0x01; - -// // Undocumented bits 3 and 5 come from the High byte of the calculated result -// flags |= (byte)((result >> 8) & 0x28); - -// AF.Low = flags; -// HL.Word = (ushort)(result & 0xFFFF); -// } - -// private byte Dec8(byte value) -// { -// byte result = (byte)(value - 1); - -// // Store the existing Carry flag so we can preserve it -// byte carry = (byte)(AF.Low & 0x01); - -// // Clear all flags -// AF.Low = 0; - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if (result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if borrow from bit 4 (happens if the lower nibble was 0) -// if ((value & 0x0F) == 0) AF.Low |= 0x10; - -// // Parity/Overflow Flag (Bit 2) - Set if the original value was 0x80 (maximum negative) -// if (value == 0x80) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS SET for decrements -// AF.Low |= 0x02; - -// // Restore the original Carry Flag (Bit 0) -// AF.Low |= carry; - -// return result; -// } - -// private byte Inc8(byte value) -// { -// byte result = (byte)(value + 1); - -// // Store the existing Carry flag so we can preserve it -// byte carry = (byte)(AF.Low & 0x01); - -// // Clear all flags -// AF.Low = 0; - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if (result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if carry from bit 3 (happens if lower nibble was 0x0F) -// if ((value & 0x0F) == 0x0F) AF.Low |= 0x10; - -// // Parity/Overflow Flag (Bit 2) - Set if the original value was 0x7F (maximum positive) -// if (value == 0x7F) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS 0 for increments (already 0 because we cleared AF.Low) - -// // Restore the original Carry Flag (Bit 0) -// AF.Low |= carry; - -// return result; -// } - -// private void Cp(byte value) -// { -// byte a = AF.High; -// result = a - value; - -// // --- Update Flags (F Register) --- -// AF.Low = 0; // Clear all flags - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if ((byte)result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if borrow from bit 4 -// if (((a & 0x0F) - (value & 0x0F)) < 0) AF.Low |= 0x10; - -// // Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes -// if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04; - -// // Subtract Flag (Bit 1) - ALWAYS set for CP/SUB -// AF.Low |= 0x02; - -// // Carry Flag (Bit 0) - Set if the overall result dropped below 0 -// if (result < 0) AF.Low |= 0x01; -// } - -// private void And(byte value) -// { -// AF.High = (byte)(AF.High & value); - -// // --- Update Flags --- -// AF.Low = 0; // Clear all flags - -// // Sign Flag (Bit 7) - Set if the highest bit is 1 -// if ((AF.High & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) - Set if the result is 0 -// if (AF.High == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - ALWAYS SET to 1 for Z80 AND instructions! -// AF.Low |= 0x10; - -// // Parity Flag (Bit 2) - Set if the result has an even number of 1 bits -// if (HasEvenParity(AF.High)) AF.Low |= 0x04; - -// // Subtract Flag (N) and Carry Flag (C) are ALWAYS 0 -// } - -// private void Or(byte value) -// { -// AF.High = (byte)(AF.High | value); - -// // --- Update Flags --- -// AF.Low = 0; // Clear all flags (H, N, and C are always 0 for OR) - -// // Sign Flag (Bit 7) - Set if the highest bit is 1 -// if ((AF.High & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) - Set if the result is 0 -// if (AF.High == 0) AF.Low |= 0x40; - -// // Parity Flag (Bit 2) - Set if the result has an even number of 1 bits -// if (HasEvenParity(AF.High)) AF.Low |= 0x04; -// } - -// private void Xor(byte value) -// { -// // The caret (^) is the C# Bitwise XOR operator -// AF.High = (byte)(AF.High ^ value); - -// // --- Update Flags --- -// AF.Low = 0; // Clear all flags (H, N, and C are always 0 for XOR) - -// // Sign Flag (Bit 7) - Set if the highest bit is 1 -// if ((AF.High & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) - Set if the result is 0 -// if (AF.High == 0) AF.Low |= 0x40; - -// // Parity Flag (Bit 2) - Set if the result has an even number of 1 bits -// if (HasEvenParity(AF.High)) AF.Low |= 0x04; -// } - -// private void Add16(ushort value) -// { -// int hl = HL.Word; -// result = hl + value; - -// // Update the HL register -// HL.Word = (ushort)result; -// AF.Low &= 0xEC; - -// // Half-Carry Flag (Bit 4) - Set if there is a carry from bit 11 -// if (((hl & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) AF.Low |= 0x10; - -// // Carry Flag (Bit 0) - Set if the result overflows 16 bits -// if (result > 0xFFFF) AF.Low |= 0x01; -// } -// private void Add16IX(ushort value) -// { -// int ixVal = IX.Word; -// result = ixVal + value; - -// // --- 16-Bit ADD IX Flag Calculation --- -// // Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2). -// // This perfectly resets N (Bit 1) to 0 at the same time. -// newFlags = (byte)(AF.Low & 0xC4); - -// // Half-Carry (H - Bit 4): Set if carry from Bit 11 -// if (((ixVal & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) newFlags |= 0x10; - -// // Carry (C - Bit 0): Set if the total result overflows 16 bits -// if (result > 0xFFFF) newFlags |= 0x01; - -// AF.Low = newFlags; -// IX.Word = (ushort)result; -// } - -// private void Add(byte value) -// { -// byte a = AF.High; -// result = a + value; - -// // Save the result back to the Accumulator -// AF.High = (byte)result; - -// // --- Update Flags (F Register) --- -// AF.Low = 0; // Clear all flags (This also correctly resets the N flag to 0) - -// // Sign Flag (Bit 7) -// if ((result & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if ((byte)result == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if carry from bit 3 -// if (((a & 0x0F) + (value & 0x0F)) > 0x0F) AF.Low |= 0x10; - -// // Overflow/Parity Flag (Bit 2) - For addition, overflow happens if two numbers -// // with the SAME sign are added and produce a result with a DIFFERENT sign. -// if ((((a ^ ~value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04; - -// // Carry Flag (Bit 0) - Set if the result is greater than 255 -// if (result > 0xFF) AF.Low |= 0x01; -// } - -// private void AdcA(byte operand) -// { -// int aVal = AF.High; -// int carryIn = AF.Low & 0x01; - -// result = aVal + operand + carryIn; - -// byte newFlags = 0; - -// if ((result & 0x80) != 0) newFlags |= 0x80; // S Flag -// if ((result & 0xFF) == 0) newFlags |= 0x40; // Z Flag -// if (((aVal & 0x0F) + (operand & 0x0F) + carryIn) > 0x0F) newFlags |= 0x10; // H Flag -// if ((((aVal ^ ~operand) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04; // P/V Flag -// if (result > 0xFF) newFlags |= 0x01; // C Flag - -// AF.Low = newFlags; -// AF.High = (byte)result; -// } - -// private void Adc16(ushort value) -// { -// int hl = HL.Word; -// int carry = AF.Low & 0x01; - -// // Calculate the raw integer result to check for overflows -// result = hl + value + carry; - -// // --- Update Flags (F Register) --- -// byte newFlags = 0; // Clear all flags (which forces N to 0, correctly!) - -// // Sign Flag (Bit 7) - Set if the 16-bit result is negative (bit 15 is 1) -// if ((result & 0x8000) != 0) newFlags |= 0x80; - -// // Zero Flag (Bit 6) - Set if the entire 16-bit result is exactly 0 -// if ((result & 0xFFFF) == 0) newFlags |= 0x40; - -// // Half-Carry Flag (Bit 4) - Set if there is a carry out of bit 11 -// if (((hl & 0x0FFF) + (value & 0x0FFF) + carry) > 0x0FFF) newFlags |= 0x10; - -// // Overflow Flag (Bit 2) - Set if operands have the SAME sign, but result sign changes -// if ((((hl ^ ~value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) newFlags |= 0x04; - -// // Carry Flag (Bit 0) - Set if the overall 16-bit result overflowed 0xFFFF -// if (result > 0xFFFF) newFlags |= 0x01; - -// AF.Low = newFlags; -// HL.Word = (ushort)result; -// } - -// private void AdcHl(ushort value) -// { -// int op1 = HL.Word; -// int op2 = value; -// int carry = AF.Low & 0x01; // Current C flag -// int result = op1 + op2 + carry; - -// byte flags = 0; - -// if (((result >> 8) & 0x80) != 0) flags |= 0x80; // S: Sign flag (Bit 15) -// if ((result & 0xFFFF) == 0) flags |= 0x40; // Z: Zero flag - -// // H: Half-carry from Bit 11 -// if ((((op1 & 0x0FFF) + (op2 & 0x0FFF) + carry) & 0x1000) != 0) flags |= 0x10; - -// // P/V: 16-bit Overflow logic -// if ((((op1 ^ ~op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04; - -// // N: Always 0 for Add - -// // C: Carry from Bit 15 -// if ((result & 0x10000) != 0) flags |= 0x01; - -// // Undocumented bits 3 and 5 come from the High byte of the calculated result -// flags |= (byte)((result >> 8) & 0x28); - -// AF.Low = flags; -// HL.Word = (ushort)(result & 0xFFFF); -// } - -// private void AddHl(ushort value) -// { -// int result = HL.Word + value; - -// // 1. Preserve S (0x80), Z (0x40), and P/V (0x04) from the current flag register -// byte flags = (byte)(AF.Low & 0xC4); - -// // 2. Calculate H (Half-carry from bit 11) -// if (((HL.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) -// { -// flags |= 0x10; -// } - -// // 3. Calculate C (Carry from bit 15) -// if (result > 0xFFFF) -// { -// flags |= 0x01; -// } - -// // 4. Undocumented bits 3 and 5 come from the High byte of the calculated result -// flags |= (byte)((result >> 8) & 0x28); - -// // N is naturally left as 0 because of our initial bitmask -// AF.Low = flags; -// HL.Word = (ushort)result; -// } - -// private void AddA(byte operand) -// { -// byte a = AF.High; -// int result = a + operand; // Use a local int to easily catch the carry - -// AF.High = (byte)result; - -// // --- Update Flags --- -// AF.Low = 0; // Clear all flags initially (Forces N to 0) - -// // Sign Flag (Bit 7) -// if ((AF.High & 0x80) != 0) AF.Low |= 0x80; - -// // Zero Flag (Bit 6) -// if (AF.High == 0) AF.Low |= 0x40; - -// // Half-Carry Flag (Bit 4) - Check if bits 0-3 overflowed -// if (((a & 0x0F) + (operand & 0x0F)) > 0x0F) AF.Low |= 0x10; - -// // Parity/Overflow Flag (Bit 2) -// bool sameSign = ((a ^ operand) & 0x80) == 0; // Did inputs have the same sign? -// bool changedSign = ((a ^ AF.High) & 0x80) != 0; // Did the result's sign flip? -// if (sameSign && changedSign) AF.Low |= 0x04; - -// // Carry Flag (Bit 0) - Check if the whole 8-bit addition overflowed -// if (result > 0xFF) AF.Low |= 0x01; - -// // UNDOCUMENTED FLAGS (Bits 3 and 5) - Copied directly from the result -// AF.Low |= (byte)(AF.High & 0x28); -// } -// private void AddIx(ushort value) -// { -// int result = IX.Word + value; - -// // Preserve S, Z, and P/V -// byte flags = (byte)(AF.Low & 0xC4); - -// // Calculate H (Half-carry from bit 11) -// if (((IX.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) flags |= 0x10; - -// // Calculate C (Carry from bit 15) -// if (result > 0xFFFF) flags |= 0x01; - -// // Undocumented bits 3 and 5 from the High byte of the result -// flags |= (byte)((result >> 8) & 0x28); - -// AF.Low = flags; -// IX.Word = (ushort)result; -// } - -// private void AddIy(ushort value) -// { -// int result = IY.Word + value; - -// // Preserve S, Z, and P/V -// byte flags = (byte)(AF.Low & 0xC4); - -// // Calculate H (Half-carry from bit 11) -// if (((IY.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) flags |= 0x10; - -// // Calculate C (Carry from bit 15) -// if (result > 0xFFFF) flags |= 0x01; - -// // Undocumented bits 3 and 5 from the High byte of the result -// flags |= (byte)((result >> 8) & 0x28); - -// AF.Low = flags; -// IY.Word = (ushort)result; -// } -// private bool HasEvenParity(byte value) -// { -// int bits = 0; -// for (int i = 0; i < 8; i++) -// { -// if ((value & (1 << i)) != 0) bits++; -// } -// return (bits % 2) == 0; -// } - -// 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); -// } - -// private 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 -// // 1. Grab the top bit (Bit 7) before it rotates -// byte topBit = (byte)(AF.High >> 7); - -// // 2. Shift A left by 1, and drop the top bit into the newly empty Bit 0 -// AF.High = (byte)((AF.High << 1) | topBit); - -// // --- RLCA Specific Flag Rules --- -// // Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2). -// byte newFlags = (byte)(AF.Low & 0xC4); - -// // H (Bit 4) and N (Bit 1) are forcefully reset to 0 (which the bitwise AND above just did). - -// // C (Bit 0): Set if the bit that rotated off the top was a 1 -// if (topBit != 0) newFlags |= 0x01; - -// AF.Low = newFlags; - -// return 4; // 4 T-States -// case 0x08: // EX AF, AF' -// ushort tempAF = AF.Word; -// AF.Word = AF_Prime.Word; -// AF_Prime.Word = tempAF; -// return 4; -// // Inside your base switch(opcode) statement: -// case 0x09: AddHl(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: AddHl(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: AddHl(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) takes 11 T-States -// case 0x39: AddHl(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 -// // Flip all bits in the Accumulator -// AF.High = (byte)(~AF.High); - -// // Set Half-Carry (Bit 4) and Subtract (Bit 1). -// // Bitwise OR forces them to 1 while perfectly preserving S, Z, P/V, and C. -// AF.Low |= 0x12; - -// return 4; -// case 0x35: -// WriteMemory(HL.Word, Dec8(ReadMemory(HL.Word))); -// return 11; // DEC (HL) takes 11 T-States -// 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 -// { -// // 1. Grab the bit that is about to fall off -// byte bit0 = (byte)(AF.High & 0x01); - -// // 2. Shift right, and force the old Bit 0 into the Bit 7 position -// AF.High = (byte)((AF.High >> 1) | (bit0 << 7)); - -// // 3. Update Flags -// // S (0x80), Z (0x40), and P/V (0x04) are completely PRESERVED. -// // H (0x10) and N (0x02) are forcefully RESET to 0. -// // ANDing with 0xC4 (Binary 1100 0100) does exactly this. -// AF.Low &= 0xC4; - -// // Set the Carry Flag (Bit 0) to whatever fell off -// AF.Low |= bit0; -// 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 -// // 1. Grab the current Carry flag (Bit 0 of AF.Low) -// oldCarry = (byte)(AF.Low & 0x01); - -// // 2. See if Bit 7 of the Accumulator is about to fall off -// bool newCarry = (AF.High & 0x80) != 0; - -// // 3. Shift A left, and drop the OLD carry directly into Bit 0 -// AF.High = (byte)((AF.High << 1) | oldCarry); - -// // 4. Update the flags -// // Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2) while wiping H and N. -// AF.Low &= 0xC4; - -// // 5. Apply the new Carry flag if necessary -// if (newCarry) AF.Low |= 0x01; - -// return 4; // 4 T-States -// case 0x18: // JR d -// sbyte jumpDistance = (sbyte)FetchByte(); - -// // PC has already been incremented by FetchByte(), so it is -// // pointing exactly where it needs to be for the relative addition. -// 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 -// { -// // 1. Grab the current Carry Flag (0 or 1) -// oldCarry = (byte)(AF.Low & 0x01); - -// // 2. Grab the bit that is about to fall off the Accumulator -// byte bit0 = (byte)(AF.High & 0x01); - -// // 3. Shift right, and force the OLD Carry flag into the Bit 7 position -// AF.High = (byte)((AF.High >> 1) | (oldCarry << 7)); - -// // 4. Update Flags -// // S (0x80), Z (0x40), and P/V (0x04) are PRESERVED exactly as they are. -// // H (0x10) and N (0x02) are forcefully RESET to 0. -// AF.Low &= 0xC4; - -// // Set the new Carry Flag (Bit 0) to whatever fell off the Accumulator -// AF.Low |= bit0; - -// 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 flags = AF.Low; - -// bool carry = (flags & 0x01) != 0; -// bool halfCarry = (flags & 0x10) != 0; -// bool isSub = (flags & 0x02) != 0; // The N flag tells us if we should add or subtract! - -// // 1. Check if the lower nibble needs adjustment -// if (halfCarry || (a & 0x0F) > 9) -// { -// correction |= 0x06; -// } - -// // 2. Check if the upper nibble needs adjustment -// if (carry || a > 0x99 || (a >= 0x90 && (a & 0x0F) > 9)) -// { -// correction |= 0x60; -// carry = true; // The final carry flag will be true -// } - -// // 3. Apply the correction and calculate the new Half-Carry -// 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; - -// // 4. Build the new flags -// flags &= 0x02; // Wipe everything except the N flag (which is strictly preserved) -// if (carry) flags |= 0x01; -// if (newHalfCarry) flags |= 0x10; -// if ((a & 0x80) != 0) flags |= 0x80; // S flag -// if (a == 0) flags |= 0x40; // Z flag -// if (CalculateParity(a)) flags |= 0x04; // P/V flag - -// AF.Low = flags; -// return 4; -// case 0x28: // JR Z, e -// offset = (sbyte)FetchByte(); -// // Check if the Zero Flag is set -// 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(); - -// // Check if the Carry Flag (Bit 0) is NOT set -// if ((AF.Low & 0x01) == 0) -// { -// PC = (ushort)(PC + offset); -// return 12; // Jump taken -// } -// 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; // Force Carry Flag (Bit 0) to 1 -// AF.Low &= 0xED; -// return 4; -// case 0x38: // JR C, d -// sbyte jrCOffset = (sbyte)FetchByte(); -// // Check if the Carry Flag (Bit 0) IS set (1) -// 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; // Invert Carry Flag (Bit 0) -// AF.Low &= 0xFD; // Force Subtract Flag (N, Bit 1) to 0 -// // Set Half-Carry (H, Bit 4) to the previous Carry state -// if (previousCarry) -// AF.Low |= 0x10; -// else -// AF.Low &= 0xEF; -// return 4; -// case 0x40: //BC.High = BC.High; -// return 4; -// 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: //BC.Low = BC.Low; -// 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: //DE.High = DE.High; -// 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: //DE.Low = DE.Low; -// 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: //HL.High = HL.High; -// 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: //HL.Low = HL.Low; -// return 4; -// case 0x6E: HL.Low = ReadMemory(HL.Word); return 7; -// case 0x6F: HL.Low = AF.High; return 4; - -// // --- LD (HL), r --- (Note: 0x76 is HALT, so it is skipped here) -// 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 -// 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: //AF.High = AF.High; -// return 4; -// case 0x80: Add(BC.High); return 4; // ADD A, B -// case 0x81: Add(BC.Low); return 4; // ADD A, C -// case 0x82: Add(DE.High); return 4; // ADD A, D -// case 0x83: Add(DE.Low); return 4; // ADD A, E -// case 0x84: Add(HL.High); return 4; // ADD A, H -// case 0x85: Add(HL.Low); return 4; // ADD A, L -// case 0x86: Add(ReadMemory(HL.Word)); return 7; // ADD A, (HL) -// case 0x87: Add(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 - -// // --- ADC A, Memory --- -// case 0x8E: // ADC A, (HL) -// AdcA(ReadMemory(HL.Word)); -// return 7; - -// // --- ADC A, Immediate --- -// case 0xCE: // ADC A, n -// AdcA(FetchByte()); -// return 7; -// case 0x90: Sub(BC.High); return 4; // SUB B -// case 0x91: Sub(BC.Low); return 4; // SUB C -// case 0x92: Sub(DE.High); return 4; // SUB D -// case 0x93: Sub(DE.Low); return 4; // SUB E -// case 0x94: Sub(HL.High); return 4; // SUB H -// case 0x95: Sub(HL.Low); return 4; // SUB L -// case 0x96: Sub(ReadMemory(HL.Word)); return 7; // SUB (HL) -// case 0x97: Sub(AF.High); return 4; // SUB A -// // --- SBC A, r --- -// case 0x98: Sbc(BC.High); return 4; // SBC A, B -// case 0x99: Sbc(BC.Low); return 4; // SBC A, C -// case 0x9A: Sbc(DE.High); return 4; // SBC A, D -// case 0x9B: Sbc(DE.Low); return 4; // SBC A, E -// case 0x9C: Sbc(HL.High); return 4; // SBC A, H -// case 0x9D: Sbc(HL.Low); return 4; // SBC A, L -// case 0x9E: Sbc(ReadMemory(HL.Word)); return 7; // SBC A, (HL) -// case 0x9F: Sbc(AF.High); return 4; // SBC A, A -// 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: Cp(BC.High); return 4; // CP B -// case 0xB9: Cp(BC.Low); return 4; // CP C -// case 0xBA: Cp(DE.High); return 4; // CP D -// case 0xBB: Cp(DE.Low); return 4; // CP E -// case 0xBC: Cp(HL.High); return 4; // CP H -// case 0xBD: Cp(HL.Low); return 4; // CP L -// case 0xBE: Cp(ReadMemory(HL.Word)); return 7; // CP (HL) -// case 0xBF: Cp(AF.High); return 4; // CP A -// // --- 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) -// // 1. Fetch the immediate port offset byte -// byte portOffsetDB = FetchByte(); - -// // 2. The Z80 puts 'A' on the top 8 bits, and 'n' on the bottom 8 bits -// ushort portAddressDB = (ushort)((AF.High << 8) | portOffsetDB); - -// // 3. Read from the I/O bus and store the result straight into the Accumulator -// AF.High = _simpleIoBus.ReadPort(portAddressDB); - -// return 11; -// case 0xE2: // JP PO, nn (Parity Odd / No Overflow) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x04) == 0) PC = addr; -// return 10; -// } -// case 0xEA: // JP PE, nn (Parity Even / Overflow) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x04) != 0) PC = addr; -// return 10; -// } -// case 0xF2: // JP P, nn (Sign Positive) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x80) == 0) PC = addr; -// return 10; -// } -// case 0xFA: // JP M, nn (Sign Minus) -// { -// 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 (Parity Odd) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x04) == 0) { Push(PC); PC = addr; return 17; } -// return 10; -// } -// case 0xEC: // CALL PE, nn (Parity Even) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x04) != 0) { Push(PC); PC = addr; return 17; } -// return 10; -// } -// case 0xF4: // CALL P, nn (Sign Positive) -// { -// ushort addr = FetchWord(); -// if ((AF.Low & 0x80) == 0) { Push(PC); PC = addr; return 17; } -// return 10; -// } -// case 0xFC: // CALL M, nn (Sign Minus) -// { -// 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 -// Add(FetchByte()); -// return 7; -// // --- RST Instructions (11 T-States) --- -// // An RST is effectively a 1-byte CALL to a fixed Page 0 address. -// case 0xC7: Push(PC); PC = 0x0000; return 11; // RST 00h (Equivalent to a hardware reset) -// case 0xCF: Push(PC); PC = 0x0008; return 11; // RST 08h (Spectrum Error handler) -// case 0xD7: Push(PC); PC = 0x0010; return 11; // RST 10h (Spectrum Print Character) -// case 0xDF: Push(PC); PC = 0x0018; return 11; // RST 18h (Spectrum Collect Next Char) -// case 0xE7: Push(PC); PC = 0x0020; return 11; // RST 20h (Spectrum Collect Next Char/Space) -// case 0xEF: Push(PC); PC = 0x0028; return 11; // RST 28h (Spectrum Floating Point Calculator) -// case 0xF7: Push(PC); PC = 0x0030; return 11; // RST 30h (Spectrum Make BC Spaces) -// case 0xFF: Push(PC); PC = 0x0038; return 11; // RST 38h (Maskable Interrupt Handler) -// case 0xC8: // RET Z -// // Check if the Zero Flag (Bit 6) IS set -// if ((AF.Low & 0x40) != 0) -// { -// PC = Pop(); -// return 11; // Condition met, took the return -// } -// return 5; // Condition not met, skipped -// 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 -// // Check if the Carry Flag (Bit 0) is NOT set (0) -// if ((AF.Low & 0x01) == 0) -// { -// PC = Pop(); -// return 11; // Condition met, took the return -// } -// return 5; // Condition not met, skipped -// case 0xD1: // POP DE -// DE.Word = Pop(); -// return 10; -// case 0xD3: // OUT (n), A -// byte portOffset = FetchByte(); - -// // The Z80 puts 'A' on the top 8 bits, and 'n' on the bottom 8 bits of the port address -// ushort portAddress = (ushort)((AF.High << 8) | portOffset); - -// _simpleIoBus.WritePort(portAddress, AF.High); - -// return 11; -// case 0xd5: //push bc -// Push(DE.Word); -// return 11; -// case 0xD6: // SUB n -// Sub(FetchByte()); -// return 7; -// case 0xD8: // RET C -// // Check if the Carry Flag (Bit 0) IS set (1) -// if ((AF.Low & 0x01) != 0) -// { -// PC = Pop(); -// return 11; // Condition met, took the return -// } -// 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 -// Sbc(FetchByte()); -// return 7; -// case 0xE1: // POP HL -// HL.Word = Pop(); -// return 10; -// case 0xE3: // EX (SP), HL -// // 1. Read the 16-bit value currently on top of the stack -// byte spLow = ReadMemory(SP); -// byte spHigh = ReadMemory((ushort)(SP + 1)); - -// // 2. Write the current HL registers onto the stack in its place -// WriteMemory(SP, HL.Low); -// WriteMemory((ushort)(SP + 1), HL.High); - -// // 3. Update HL with the data we pulled off the stack -// HL.Low = spLow; -// HL.High = spHigh; - -// return 19; -// case 0xe5: //push bc -// Push(HL.Word); -// return 11; -// case 0xE6: // AND n -// And(FetchByte()); -// return 7; -// case 0xE9: // JP (HL) -// PC = HL.Word; -// return 4; // Takes 4 T-States -// case 0xEB: // EX DE, HL -// ushort tempEx = DE.Word; -// DE.Word = HL.Word; -// HL.Word = tempEx; -// return 4; // Takes 4 T-States -// case 0xED: -// return ExecuteExtendedPrefix(); -// case 0xEE: // XOR n -// byte xorImm = FetchByte(); - -// // Perform the bitwise XOR against the Accumulator -// AF.High = (byte)(AF.High ^ xorImm); - -// // --- Update Flags --- -// // Start with a clean slate of 0 to perfectly clear C, H, and N! -// newFlags = 0; - -// if ((AF.High & 0x80) != 0) newFlags |= 0x80; // Sign Flag -// if (AF.High == 0) newFlags |= 0x40; // Zero Flag -// if (CalculateParity(AF.High)) newFlags |= 0x04; // Parity/Overflow Flag - -// AF.Low = newFlags; - -// return 7; // Takes 7 T-States -// case 0xF1: // POP AF -// AF.Word = Pop(); -// return 10; -// case 0xF3: // DI (Disable Interrupts) -// IFF1 = false; -// IFF2 = false; -// return 4; -// case 0xf5: //push bc -// Push(AF.Word); -// return 11; -// case 0xF6: // OR n -// Or(FetchByte()); -// return 7; -// case 0xF9: // LD SP, HL -// SP = HL.Word; // (Use SP.Word = HL.Word if you made SP a RegisterPair) -// return 6; -// case 0xFB: // EI -// IFF1 = true; -// IFF2 = true; -// return 4; -// case 0xFD: -// return ExecuteFDPrefix(); -// case 0xFE: // CP n -// Cp(FetchByte()); -// return 7; -// default: -// throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); -// } -// } -// private int ExecuteExtendedPrefix() //ED -// { -// // Fetch the actual extended instruction -// byte extendedOpcode = FetchByte(); -// byte val = 0; - -// switch (extendedOpcode) -// { -// 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; -// result = 0 - aBefore; - -// newFlags = 0; - -// // S Flag (Bit 7): Set if the result is negative -// if ((result & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if the result is exactly zero -// if ((result & 0xFF) == 0) newFlags |= 0x40; - -// // H Flag (Bit 4): Set if there was a borrow from Bit 3 -// if ((0 - (aBefore & 0x0F)) < 0) newFlags |= 0x10; - -// // P/V Flag (Bit 2): Overflow happens ONLY if A was 0x80 (-128) -// if (aBefore == 0x80) newFlags |= 0x04; - -// // N Flag (Bit 1): Always set to 1 for NEG -// newFlags |= 0x02; - -// // C Flag (Bit 0): Set if A was not 0 before the operation -// if (aBefore != 0) newFlags |= 0x01; - -// AF.Low = newFlags; -// AF.High = (byte)result; - -// return 8; // 8 T-States -// 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 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 0x58: // IN E, (C) -// // 1. Read from the I/O port. -// // CRITICAL: We must pass the FULL BC register, not just C! -// byte inVal58 = ReadPort(BC.Word); - -// // 2. Store the hardware data in register E (Low byte of DE) -// DE.Low = inVal58; - -// // 3. Update the Flags Register (F) -// // The Carry flag (C) is strictly preserved. H and N are always reset to 0. -// byte flags58 = (byte)(AF.Low & 0x01); - -// if ((inVal58 & 0x80) != 0) flags58 |= 0x80; // S: Sign flag -// if (inVal58 == 0) flags58 |= 0x40; // Z: Zero flag - -// // P/V: Parity flag. Collapse the bits to check if the number of 1s is even -// byte p58 = inVal58; -// p58 ^= (byte)(p58 >> 4); -// p58 ^= (byte)(p58 >> 2); -// p58 ^= (byte)(p58 >> 1); -// if ((p58 & 1) == 0) flags58 |= 0x04; // Set if Parity is Even - -// // Undocumented bits 3 and 5 are copied directly from the input byte -// flags58 |= (byte)(inVal58 & 0x28); - -// AF.Low = flags58; - -// return 12; -// case 0x5B: // LD DE, (nn) -// ushort src5B = FetchWord(); -// DE.Low = ReadMemory(src5B); -// DE.High = ReadMemory((ushort)(src5B + 1)); -// return 20; -// case 0x5E: // IM 2 -// // Set the CPU's internal interrupt mode state -// InterruptMode = 2; -// return 8; -// case 0x5F: // LD A, R -// // 1. Load the Refresh register into the Accumulator -// AF.High = R; - -// // 2. Calculate Flags -// // CRITICAL: Preserve the existing Carry flag (Bit 0). -// // H (Bit 4) and N (Bit 1) are forcefully reset to 0. -// newFlags = (byte)(AF.Low & 0x01); - -// // S Flag (Bit 7): Set if the result is negative -// if ((AF.High & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if the result is zero -// if (AF.High == 0) newFlags |= 0x40; - -// // P/V Flag (Bit 2): Set if IFF2 is true (This is the interrupt check hack!) -// if (IFF2) newFlags |= 0x04; - -// AF.Low = newFlags; - -// return 9; -// // --- SBC HL, rr --- -// case 0x42: SbcHl(BC.Word); return 15; -// 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) -// // Read from the hardware port using the full BC register as the address -// byte portVal78 = ReadPort(BC.Word); -// AF.High = portVal78; - -// // --- Update Flags --- -// // S (Bit 7), Z (Bit 6), P/V (Bit 2) are set based on the input. -// // H (Bit 4) and N (Bit 1) are RESET. -// // C (Bit 0) is PRESERVED. - -// newFlags = (byte)(AF.Low & 0x01); // Preserve Carry - -// if ((portVal78 & 0x80) != 0) newFlags |= 0x80; // Sign Flag -// if (portVal78 == 0) newFlags |= 0x40; // Zero Flag -// if (CalculateParity(portVal78)) newFlags |= 0x04; // Parity/Overflow Flag - -// AF.Low = newFlags; - -// return 12; -// case 0x79: // OUT (C), A -// _simpleIoBus.WritePort(BC.Word, AF.High); -// return 12; -// // --- ADC HL, rr --- -// case 0x4A: AdcHl(BC.Word); return 15; -// case 0x5A: AdcHl(DE.Word); return 15; -// case 0x6A: AdcHl(HL.Word); return 15; -// case 0x7A: AdcHl(SP); return 15; -// case 0x7B: // LD SP, (nn) -// // 1. Fetch the absolute 16-bit memory address from the instruction stream -// byte addrLow = FetchByte(); -// byte addrHigh = FetchByte(); -// ushort address7B = (ushort)((addrHigh << 8) | addrLow); - -// // 2. Read the 16-bit value stored at that exact memory location (Little-Endian!) -// byte spLow = ReadMemory(address7B); -// byte spHigh = ReadMemory((ushort)(address7B + 1)); - -// // 3. Load the resulting 16-bit value directly into the Stack Pointer -// SP = (ushort)((spHigh << 8) | spLow); - -// return 20; -// case 0xA0: // LDI -// // 1. Read byte from (HL) -// val = ReadMemory(HL.Word); - -// // 2. Write byte to (DE) -// WriteMemory(DE.Word, val); - -// // 3. Increment memory pointers, Decrement byte counter -// HL.Word++; -// DE.Word++; -// BC.Word--; - -// // 4. Update Flags -// // Preserve S (0x80), Z (0x40), and C (0x01). -// // H (0x10) and N (0x02) are forcefully reset to 0. -// AF.Low &= 0xC1; - -// // P/V Flag (Bit 2) is set to 1 if BC is not 0 after the decrement -// if (BC.Word != 0) -// { -// AF.Low |= 0x04; -// } - -// return 16; -// case 0xB0: // LDIR -// // 1. Read byte from (HL) -// val = ReadMemory(HL.Word); - -// // 2. Write byte to (DE) -// WriteMemory(DE.Word, val); - -// // 3. Increment memory pointers, Decrement byte counter -// HL.Word++; -// DE.Word++; -// BC.Word--; - -// // 4. Update Flags -// // Preserve S (0x80), Z (0x40), and C (0x01). -// // H (0x10) and N (0x02) are always reset to 0. -// AF.Low &= 0xC1; - -// // P/V Flag (Bit 2) is set to 1 if BC is not 0 -// if (BC.Word != 0) -// { -// AF.Low |= 0x04; - -// // Rewind the PC so the CPU executes this instruction again! -// PC -= 2; -// return 21; // Looping -// } -// return 16; -// case 0xB1: // CPIR -// // 1. Read the memory byte at HL -// byte memValB1 = ReadMemory(HL.Word); - -// // 2. Calculate the difference (A - (HL)) to set the flags -// byte resultB1 = (byte)(AF.High - memValB1); - -// // 3. Increment HL and Decrement BC -// HL.Word++; -// BC.Word--; - -// // 4. Update the Flags (F Register / AF.Low) -// // CPIR modifies S, Z, H, P/V, and N, but strictly PRESERVES the C flag. -// byte currentCarry = (byte)(AF.Low & 0x01); -// byte newFlagsB1 = currentCarry; - -// newFlagsB1 |= 0x02; // N flag is always set to 1 for CPIR - -// if (BC.Word != 0) newFlagsB1 |= 0x04; // P/V is set if BC is not 0 (Counter not empty) -// if (((AF.High ^ memValB1 ^ resultB1) & 0x10) != 0) newFlagsB1 |= 0x10; // H flag (Half-borrow) -// if (resultB1 == 0) newFlagsB1 |= 0x40; // Z flag (Match found!) -// if ((resultB1 & 0x80) != 0) newFlagsB1 |= 0x80; // S flag (Sign) - -// // (Note: Undocumented bits 3 and 5 are ignored here as they are highly esoteric for CPIR, -// // but you can add `newFlagsB1 |= (byte)(resultB1 & 0x28);` if your engine tracks them strictly). - -// AF.Low = newFlagsB1; - -// // 5. The Repeat Check -// // If we haven't hit 0 in BC, AND we didn't find a match (result != 0)... -// if (BC.Word != 0 && resultB1 != 0) -// { -// // Rewind the Program Counter by 2 bytes (back to the 0xED prefix) -// PC -= 2; -// return 21; // 21 T-States for a repeating loop -// } - -// // If we found the byte or BC hit 0, the loop ends. -// return 16; // 16 T-States when the loop terminates -// case 0xB8: // LDDR -// // 1. Read byte from (HL) -// val = ReadMemory(HL.Word); - -// // 2. Write byte to (DE) -// WriteMemory(DE.Word, val); - -// // 3. Decrement all three pointers -// HL.Word--; -// DE.Word--; -// BC.Word--; - -// // 4. Update Flags -// // Preserve S (0x80), Z (0x40), and C (0x01). -// // H (0x10) and N (0x02) are always reset to 0. -// AF.Low &= 0xC1; - -// // P/V Flag (Bit 2) is set to 1 if BC is not 0 -// if (BC.Word != 0) -// { -// AF.Low |= 0x04; - -// // Rewind the PC so the CPU executes this instruction again! -// PC -= 2; -// return 21; // Looping -// } - -// return 16; // Finished! -// default: -// 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; - -// // Extract the exact same mathematical properties -// int operation = cbOpcode >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET -// int bitIndex = (cbOpcode >> 3) & 0x07; // Extracts a number 0-7 -// int regIndex = cbOpcode & 0x07; // Extracts register index 0-7 - -// 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 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 -// } - -// // BIT (HL) takes 12 T-States. Standard register BIT takes 8. -// return (regIndex == 6) ? 12 : 8; - -// case 2: // ALL RES Instructions -// val &= (byte)(~bitMask); -// break; // Proceed to write-back - -// case 3: // ALL SET Instructions -// val |= bitMask; -// break; // Proceed to write-back - -// case 0: // ALL Shift/Rotate Instructions -// // The specific shift type is in the same bits we previously used for 'bitIndex' -// int shiftType = (cbOpcode >> 3) & 0x07; -// bool carryOut = false; - -// switch (shiftType) -// { -// case 0: // RLC -// // Grab Bit 7 to see if it's going to fall off -// carryOut = (val & 0x80) != 0; -// // Shift left, and loop the falling bit back into Bit 0 -// val = (byte)((val << 1) | (carryOut ? 1 : 0)); -// break; -// case 1: // RRC (Rotate Right Circular) -// // 1. Grab Bit 0 before it falls off to set the Carry flag and loop to Bit 7 -// carryOut = (val & 0x01) != 0; - -// // 2. Shift right by 1, and loop the falling bit directly back into Bit 7 -// val = (byte)((val >> 1) | (carryOut ? 0x80 : 0x00)); -// break; -// case 2: // RL (Rotate Left through Carry) -// // 1. Grab the CURRENT Carry flag from the AF register -// oldCarry = (AF.Low & 0x01) != 0; - -// // 2. Grab Bit 7 before it falls off to become the NEW Carry flag -// carryOut = (val & 0x80) != 0; - -// // 3. Shift left by 1, and drop the OLD carry flag directly into Bit 0 -// val = (byte)((val << 1) | (oldCarry ? 0x01 : 0x00)); -// break; -// case 3: // RR (Rotate Right through Carry) -// // 1. Grab the CURRENT Carry flag from the AF register -// oldCarry = (AF.Low & 0x01) != 0; - -// // 2. Grab Bit 0 before it falls off to become the NEW Carry flag -// carryOut = (val & 0x01) != 0; - -// // 3. Shift right by 1, and drop the OLD carry flag into Bit 7 -// val = (byte)((val >> 1) | (oldCarry ? 0x80 : 0x00)); -// break; -// case 4: // SLA (Shift Left Arithmetic) -// // 1. Grab Bit 7 before it falls off to set the Carry flag -// carryOut = (val & 0x80) != 0; - -// // 2. Shift the byte left by 1. -// // (In C#, a standard left shift automatically pads Bit 0 with a 0) -// val = (byte)(val << 1); -// break; -// case 5: // SRA (Shift Right Arithmetic) -// // 1. Grab Bit 0 before it falls off to set the Carry flag -// carryOut = (val & 0x01) != 0; - -// // 2. Grab the current Sign bit (Bit 7) so we can preserve it -// byte signBit = (byte)(val & 0x80); - -// // 3. Shift the byte right by 1. -// // (Because 'val' is unsigned, C# naturally pads the top with a 0) -// val = (byte)(val >> 1); - -// // 4. Force the preserved sign bit back into Bit 7 -// val |= signBit; -// break; -// case 7: // SRL (Shift Right Logical) -// // 1. Grab Bit 0 before it falls off to set the Carry flag -// carryOut = (val & 0x01) != 0; - -// // 2. Shift the byte right by 1. -// // (In C#, a standard right shift on a positive byte automatically pads Bit 7 with a 0) -// val = (byte)(val >> 1); -// break; -// // (We will add RRC, RL, RR, SLA, SRA, here as the ROM asks for them!) -// default: -// throw new NotImplementedException($"CB Shift instruction type {shiftType} not implemented!"); -// } - -// // --- Update Flags --- -// // All CB Shift instructions calculate flags the exact same way! -// // They set S, Z, P/V, and C. They forcefully clear H and N. -// byte newFlags = 0; - -// if (carryOut) newFlags |= 0x01; // C Flag -// if ((val & 0x80) != 0) newFlags |= 0x80; // S Flag -// if (val == 0) newFlags |= 0x40; // Z Flag -// if (CalculateParity(val)) newFlags |= 0x04; // P/V Flag - -// AF.Low = newFlags; // Apply the new flags - -// break; // Proceed to the write-back phase - -// default: -// throw new Exception("Invalid CB operation."); -// } - -// // --- PHASE 3: Write back the modified value (RES and SET only) --- -// 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; -// } - -// // RES/SET (HL) takes 15 T-States. Standard register RES/SET takes 8. -// return (regIndex == 6) ? 15 : 8; -// } - -// private int ExecuteDDPrefix() -// { -// byte ddOpcode = FetchByte(); // Fetch the actual instruction after 0xDD - -// switch (ddOpcode) -// { -// case 0x09: // ADD IX, BC -// Add16IX(BC.Word); -// return 15; -// case 0x19: // ADD IX, DE -// Add16IX(DE.Word); -// return 15; -// case 0x29: // ADD IX, IX -// Add16IX(IX.Word); // Multiplies IX by 2! -// return 15; -// case 0x39: // ADD IX, SP -// Add16IX(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 -// // 1. Fetch the absolute 16-bit memory address from the instruction stream -// byte addrLow22 = FetchByte(); -// byte addrHigh22 = FetchByte(); -// ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22); - -// // 2. Write the LOW byte of IX to the exact address -// WriteMemory(address22, IX.Low); - -// // 3. Write the HIGH byte of IX to the address + 1 -// WriteMemory((ushort)(address22 + 1), IX.High); - -// return 20; -// case 0x23: // INC IX -// // Increment the full 16-bit register. Do NOT touch AF.Low! -// IX.Word++; -// return 10; -// case 0x24: // INC IXH -// // Increment the high byte of IX and let the helper perfectly map the flags -// IX.High = Inc8(IX.High); -// return 8; -// case 0x25: // DEC IXH -// // Decrement the high byte of IX and let the helper handle all the Z80 flags -// IX.High = Dec8(IX.High); -// return 8; -// case 0x26: // LD IXH, n -// // Fetch the immediate 8-bit value and drop it straight into the high byte of IX -// IX.High = FetchByte(); -// return 11; -// case 0x2A: // LD IX, (nn) -// // 1. Fetch the absolute 16-bit memory address from the instruction stream -// byte addrLow2A = FetchByte(); -// byte addrHigh2A = FetchByte(); -// ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A); - -// // 2. Read the LOW byte from that specific memory location -// byte ixLow = ReadMemory(address2A); - -// // 3. Read the HIGH byte from the next consecutive memory location -// byte ixHigh = ReadMemory((ushort)(address2A + 1)); - -// // 4. Combine them and drop them into the IX register pair -// IX.Word = (ushort)((ixHigh << 8) | ixLow); - -// return 20; -// case 0x2B: // DEC IX -// // Decrement the full 16-bit register. The F register remains completely untouched. -// IX.Word--; -// return 10; -// case 0x2D: // DEC IXL -// IX.Low = Dec8(IX.Low); -// return 8; -// case 0x2E: // LD IXL, n -// // Fetch the immediate 8-bit value and drop it straight into the low byte of IX -// IX.Low = FetchByte(); -// return 11; -// case 0x34: // INC (IX+d) -// // 1. Fetch the displacement byte and cast to a signed sbyte -// sbyte offset34 = (sbyte)FetchByte(); - -// // 2. Calculate the target memory address -// ushort address34 = (ushort)(IX.Word + offset34); - -// // 3. Read the value from memory -// byte val34 = ReadMemory(address34); - -// // 4. Pass it through your helper to increment and set the flags perfectly -// byte result34 = Inc8(val34); - -// // 5. Write the incremented value back to memory -// WriteMemory(address34, result34); - -// return 23; -// case 0x35: // DEC (IX+d) -// // 1. Fetch the displacement byte and cast to a signed sbyte -// sbyte offset35 = (sbyte)FetchByte(); - -// // 2. Calculate the target memory address -// ushort address35 = (ushort)(IX.Word + offset35); - -// // 3. Read the value from memory -// byte val35 = ReadMemory(address35); - -// // 4. Pass it through your helper to decrement and set the flags -// byte result35 = Dec8(val35); - -// // 5. Write the decremented value back to memory -// WriteMemory(address35, result35); - -// return 23; -// case 0x36: // LD (IX+d), n -// // 1. Fetch the displacement byte first -// sbyte offset36 = (sbyte)FetchByte(); - -// // 2. Fetch the immediate 8-bit value second -// byte n36 = FetchByte(); - -// // 3. Calculate the exact memory address (IX + offset) -// ushort address36 = (ushort)(IX.Word + offset36); - -// // 4. Write the immediate value directly into memory -// WriteMemory(address36, n36); - -// return 19; -// case 0x46: // LD B, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset46 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address46 = (ushort)(IX.Word + offset46); - -// // 3. Read the byte from memory and drop it into the C register (Low byte of BC) -// BC.High = ReadMemory(address46); - -// return 19; -// case 0x4E: // LD C, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset4E = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address4E = (ushort)(IX.Word + offset4E); - -// // 3. Read the byte from memory and drop it into the C register (Low byte of BC) -// BC.Low = ReadMemory(address4E); - -// return 19; -// case 0x56: // LD D, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset56 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address56 = (ushort)(IX.Word + offset56); - -// // 3. Read the byte from memory and drop it into the D register (High byte of DE) -// DE.High = ReadMemory(address56); - -// return 19; -// case 0x5E: // LD E, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset5E = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address5E = (ushort)(IX.Word + offset5E); - -// // 3. Read the byte from memory and drop it into the E register -// DE.Low = ReadMemory(address5E); - -// return 19; -// case 0x66: // LD H, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset66 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address66 = (ushort)(IX.Word + offset66); - -// // 3. Read the byte from memory and drop it into the H register (High byte of HL) -// HL.High = ReadMemory(address66); - -// return 19; -// case 0x67: // LD IXH, A -// // Load the Accumulator (AF.High) directly into the high byte of IX -// IX.High = AF.High; -// return 8; -// case 0x68: // LD IXL, B -// IX.Low = BC.High; -// return 8; -// case 0x69: // LD IXL, C -// // Load the C register (BC.Low) into the low byte of IX -// IX.Low = BC.Low; -// return 8; - -// case 0x6A: // LD IXL, D -// // Load the D register (DE.High) into the low byte of IX -// IX.Low = DE.High; -// return 8; -// case 0x6E: // LD L, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset6E = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address6E = (ushort)(IX.Word + offset6E); - -// // 3. Read the byte from memory and drop it into the L register (Low byte of HL) -// HL.Low = ReadMemory(address6E); - -// return 19; -// case 0x6F: // LD IXL, A -// IX.Low = AF.High; -// return 8; -// case 0x70: // LD (IX+d), B -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset70 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address70 = (ushort)(IX.Word + offset70); - -// // 3. Write the B register (High byte of BC) into memory -// WriteMemory(address70, BC.High); - -// return 19; -// case 0x71: // LD (IX+d), C -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset71 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address71 = (ushort)(IX.Word + offset71); - -// // 3. Write the C register (Low byte of BC) into memory -// WriteMemory(address71, BC.Low); - -// return 19; -// case 0x72: // LD (IX+d), D -// // 1. Fetch the displacement byte and cast to a signed sbyte -// sbyte offset72 = (sbyte)FetchByte(); - -// // 2. Calculate the target memory address -// ushort address72 = (ushort)(IX.Word + offset72); - -// // 3. Write the D register (DE.High) to memory -// WriteMemory(address72, DE.High); - -// return 19; // 19 T-States -// case 0x73: // LD (IX+d), E -// // 1. Fetch the displacement byte and cast to a signed sbyte -// sbyte offset73 = (sbyte)FetchByte(); - -// // 2. Calculate the target memory address -// ushort address73 = (ushort)(IX.Word + offset73); - -// // 3. Write the E register (DE.Low) to memory -// WriteMemory(address73, DE.Low); - -// return 19; - -// case 0x74: // LD (IX+d), H -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset74 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address74 = (ushort)(IX.Word + offset74); - -// // 3. Write the contents of the L register (Low byte of HL) into memory -// WriteMemory(address74, HL.High); - -// return 19; -// case 0x75: // LD (IX+d), L -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset75 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address75 = (ushort)(IX.Word + offset75); - -// // 3. Write the contents of the L register (Low byte of HL) into memory -// WriteMemory(address75, HL.Low); - -// return 19; -// case 0x77: // LD (IX+d), A -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset77 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address77 = (ushort)(IX.Word + offset77); - -// // 3. Write the Accumulator (AF.High) into memory at that address -// WriteMemory(address77, AF.High); - -// return 19; -// case 0x7C: // LD A, IXH -// // Load the high byte of IX directly into the Accumulator -// AF.High = IX.High; -// return 8; -// case 0x7E: // LD A, (IX+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset7E = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IX + offset) -// ushort address7E = (ushort)(IX.Word + offset7E); - -// // 3. Read the byte from memory and drop it straight into the Accumulator (A) -// AF.High = ReadMemory(address7E); - -// return 19; -// // Inside ExecuteDDPrefix(): - -// case 0x84: // ADD A, IXH -// AddA(IX.High); // Assuming your 16-bit register struct has a .High property -// return 8; - -// case 0x85: // ADD A, IXL -// AddA(IX.Low); -// return 8; -// case 0x86: // ADD A, (IX+d) -// sbyte offset86 = (sbyte)FetchByte(); -// ushort address86 = (ushort)(IX.Word + offset86); - -// // Read the memory and pass it straight into your flawless helper! -// Add(ReadMemory(address86)); -// return 19; - -// case 0x96: // SUB (IX+d) -// sbyte offset96 = (sbyte)FetchByte(); -// ushort address96 = (ushort)(IX.Word + offset96); - -// // Read the memory and pass it straight into your flawless helper! -// Sub(ReadMemory(address96)); -// return 19; -// case 0xBE: // CP (IX+d) -// // 1. Fetch the displacement byte and calculate the address -// sbyte offsetBE = (sbyte)FetchByte(); -// ushort addressBE = (ushort)(IX.Word + offsetBE); - -// // 2. Read the value from memory -// byte cpVal = ReadMemory(addressBE); - -// // 3. Perform the phantom subtraction -// int aVal = AF.High; -// result = aVal - cpVal; - -// // --- 8-Bit Compare Flag Calculation (Identical to SUB) --- -// newFlags = 0; - -// // S Flag (Bit 7): Set if the phantom result is negative -// if ((result & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if A perfectly matches the memory value (A - value == 0) -// if ((result & 0xFF) == 0) newFlags |= 0x40; - -// // H Flag (Bit 4): Set if there was a borrow from Bit 3 -// if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10; - -// // P/V Flag (Bit 2): Set on Overflow -// if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04; - -// // N Flag (Bit 1): Always set to 1 for Subtractions/Compares -// newFlags |= 0x02; - -// // C Flag (Bit 0): Set if A was smaller than the memory value -// if (aVal < cpVal) newFlags |= 0x01; - -// AF.Low = newFlags; - -// // CRITICAL: Notice we do NOT update AF.High! The Accumulator is preserved. - -// return 19; -// case 0xCB: // The DD CB nested prefix -// { -// // 1. Fetch the displacement byte first -// sbyte displacement = (sbyte)FetchByte(); - -// // 2. Fetch the actual operation opcode (like your 0x72) second -// byte cbOpcode = FetchByte(); - -// ushort targetAddress = (ushort)(IX.Word + displacement); -// byte memVal = ReadMemory(targetAddress); - -// // Extract the mathematical properties of the opcode -// int operation = cbOpcode >> 6; // 01 = BIT, 10 = RES, 11 = SET -// int bitIndex = (cbOpcode >> 3) & 0x07; // Extracts a number 0-7 -// byte bitMask = (byte)(1 << bitIndex); // Creates the bitmask - -// switch (operation) -// { -// case 1: // ALL BIT Instructions -// AF.Low &= 0x01; // Preserve ONLY Carry -// AF.Low |= 0x10; // Set Half-Carry - -// if ((memVal & bitMask) == 0) -// { -// AF.Low |= 0x44; // Set Zero (Bit 6) and P/V (Bit 2) -// } -// else if (bitIndex == 7) -// { -// AF.Low |= 0x80; // If testing Bit 7 and it is 1, set Sign (Bit 7) -// } -// return 20; // 20 T-States - -// // (You can copy your RES and SET logic from ExecuteFDPrefix here later!) - -// default: -// throw new NotImplementedException($"DD CB opcode {cbOpcode:X2} not fully implemented!"); -// } -// } -// case 0xE1: // POP IX -// // 1. Read the low byte from the top of the stack -// byte popLow = ReadMemory(SP); -// SP++; // Move stack pointer up - -// // 2. Read the high byte -// byte popHigh = ReadMemory(SP); -// SP++; // Move stack pointer up again - -// // 3. Combine them and store in IX -// IX.Word = (ushort)((popHigh << 8) | popLow); - -// return 14; -// case 0xE5: // PUSH IX -// // 1. Decrement the stack pointer and write the HIGH byte -// SP--; -// WriteMemory(SP, IX.High); - -// // 2. Decrement the stack pointer again and write the LOW byte -// SP--; -// WriteMemory(SP, IX.Low); - -// return 15; -// case 0xE9: // JP (IX) -// PC = IX.Word; -// return 8; - -// default: -// throw new NotImplementedException($"DD Prefix opcode 0x{ddOpcode:X2} not implemented!"); -// } -// } - -// private int ExecuteFDPrefix() -// { -// byte opcode = FetchByte(); -// ushort targetAddress = 0; -// byte memVal = 0; - -// switch (opcode) -// { -// // Inside ExecuteFDPrefix() -// case 0x09: AddIy(BC.Word); return 15; -// case 0x19: AddIy(DE.Word); return 15; // This is the exact instruction that crashed! -// case 0x21: // LD IY, nn -// IY.Word = FetchWord(); -// return 14; -// case 0x23: // INC IY -// // Increment the full 16-bit register. The F register remains completely untouched. -// IY.Word++; -// return 10; - -// case 0x29: AddIy(IY.Word); return 15; -// case 0x34: // INC (IY+d) -// // 1. Fetch displacement and calculate memory address -// sbyte offset34 = (sbyte)FetchByte(); -// ushort address34 = (ushort)(IY.Word + offset34); - -// // 2. Read the value from memory -// byte valBefore = ReadMemory(address34); - -// // 3. Increment the value -// int result = valBefore + 1; - -// // --- 8-Bit Increment Flag Calculation --- -// // CRITICAL: We must preserve the existing Carry flag (Bit 0)! -// byte newFlags = (byte)(AF.Low & 0x01); - -// // S Flag (Bit 7): Set if the result is negative -// if ((result & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if the result is exactly zero (wrapped from 255 to 0) -// if ((result & 0xFF) == 0) newFlags |= 0x40; - -// // H Flag (Bit 4): Set if there was a carry out of Bit 3 -// if ((valBefore & 0x0F) == 0x0F) newFlags |= 0x10; - -// // P/V Flag (Bit 2): Set on Overflow -// // For INC, overflow ONLY happens if we increment 0x7F (+127) and it wraps to 0x80 (-128) -// if (valBefore == 0x7F) newFlags |= 0x04; - -// // N Flag (Bit 1): Always reset to 0 for an increment -// // (Our bitwise AND at the top already cleared it) - -// AF.Low = newFlags; - -// // 4. Write the modified value back to memory -// WriteMemory(address34, (byte)result); - -// return 23; // 23 T-States -// case 0x35: // DEC (IY+d) -// sbyte offset = (sbyte)FetchByte(); -// targetAddress = (ushort)(IY.Word + offset); - -// // Read, decrement using your existing helper, and write back -// 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; // Takes 19 T-States -// } - -// case 0x39: AddIy(SP); return 15; -// case 0x46: // LD B, (IY+d) -// { -// sbyte displacement = (sbyte)FetchByte(); -// targetAddress = (ushort)(IY.Word + displacement); - -// BC.High = ReadMemory(targetAddress); -// return 19; // Takes 19 T-States -// } -// case 0x4E: // LD C, (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset4E = (sbyte)FetchByte(); - -// // 2. Calculate the final address (IY + offset) -// ushort address4E = (ushort)(IY.Word + offset4E); - -// // 3. Read the memory and store it in C -// BC.Low = ReadMemory(address4E); - -// return 19; -// case 0x56: // LD D, (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset56 = (sbyte)FetchByte(); - -// // 2. Calculate the final address (IY + offset) -// ushort address56 = (ushort)(IY.Word + offset56); - -// // 3. Read the memory and store it in D (the high byte of DE) -// DE.High = ReadMemory(address56); - -// return 19; -// case 0x5E: // LD E, (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset5E = (sbyte)FetchByte(); - -// // 2. Calculate the final address (IY + offset) -// ushort address5E = (ushort)(IY.Word + offset5E); - -// // 3. Read the memory and store it in E (the low byte of DE) -// DE.Low = ReadMemory(address5E); - -// return 19; -// case 0x66: // LD H, (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset66 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address66 = (ushort)(IY.Word + offset66); - -// // 3. Read the byte from memory and drop it into the H register (High byte of HL) -// 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 0x71: // LD (IY+d), C -// { -// sbyte offset71 = (sbyte)FetchByte(); -// targetAddress = (ushort)(IY.Word + offset71); - -// // Write the C register (low byte of BC) to memory -// WriteMemory(targetAddress, BC.Low); -// return 19; // Takes 19 T-States -// } -// case 0x72: // LD (IY+d), D -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset72 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address72 = (ushort)(IY.Word + offset72); - -// // 3. Write the contents of the D register (High byte of DE) into memory -// WriteMemory(address72, DE.High); - -// return 19; -// case 0x73: // LD (IY+d), E -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset73 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address73 = (ushort)(IY.Word + offset73); - -// // 3. Write the contents of the E register (Low byte of DE) into memory -// WriteMemory(address73, DE.Low); -// return 19; -// case 0x74: // LD (IY+d), H -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset74 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address74 = (ushort)(IY.Word + offset74); - -// // 3. Write the contents of the H register into memory at that address -// WriteMemory(address74, HL.High); -// return 19; -// case 0x75: // LD (IY+d), L -// sbyte offset75 = (sbyte)FetchByte(); -// targetAddress = (ushort)(IY.Word + offset75); -// // Write the low byte of HL to memory -// WriteMemory(targetAddress, HL.Low); -// return 19; -// case 0x77: // LD (IY+d), A -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset77 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address77 = (ushort)(IY.Word + offset77); - -// // 3. Write the Accumulator (A) into memory -// WriteMemory(address77, AF.High); - -// return 19; -// case 0x7E: // LD A, (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offset7E = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort address7E = (ushort)(IY.Word + offset7E); - -// // 3. Read the byte from memory and drop it straight into the Accumulator (A) -// 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 0x96: // SUB (IY+d) -// // 1. Fetch the displacement byte and calculate the address -// sbyte offset96 = (sbyte)FetchByte(); -// ushort address96 = (ushort)(IY.Word + offset96); - -// // 2. Read the value from memory -// byte subVal = ReadMemory(address96); - -// // 3. Perform the subtraction from the Accumulator -// int aVal = AF.High; -// result = aVal - subVal; - -// // --- 8-Bit Subtraction Flag Calculation --- -// newFlags = 0; - -// // S Flag (Bit 7): Set if the result is negative -// if ((result & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if the result is exactly zero -// if ((result & 0xFF) == 0) newFlags |= 0x40; - -// // H Flag (Bit 4): Set if there was a borrow from Bit 3 -// if (((aVal & 0x0F) - (subVal & 0x0F)) < 0) newFlags |= 0x10; - -// // P/V Flag (Bit 2): Set on Overflow -// // (Happens if subtracting a negative from a positive gives a negative, or vice versa) -// if ((((aVal ^ subVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04; - -// // N Flag (Bit 1): Always set to 1 for a subtraction -// newFlags |= 0x02; - -// // C Flag (Bit 0): Set if a borrow was needed (Accumulator was smaller than memory value) -// if (aVal < subVal) newFlags |= 0x01; - -// AF.Low = newFlags; -// AF.High = (byte)result; - -// return 19; -// case 0xA6: // AND (IY+d) -// // 1. Fetch the displacement byte and cast it to a signed sbyte -// sbyte offsetA6 = (sbyte)FetchByte(); - -// // 2. Calculate the exact memory address (IY + offset) -// ushort addressA6 = (ushort)(IY.Word + offsetA6); - -// // 3. Read the operand from memory -// byte operandA6 = ReadMemory(addressA6); - -// // 4. Perform the logical AND with the Accumulator -// AF.High &= operandA6; - -// // 5. Update the Flags Register (F) -// byte flagsA6 = 0; - -// if ((AF.High & 0x80) != 0) flagsA6 |= 0x80; // S: Sign flag (Set if result is negative) -// if (AF.High == 0) flagsA6 |= 0x40; // Z: Zero flag (Set if result is 0) - -// flagsA6 |= 0x10; // H: Half-carry is ALWAYS set to 1 for Z80 AND instructions - -// // P/V: Parity flag. We collapse the bits to check if the number of 1s is even -// byte p = AF.High; -// p ^= (byte)(p >> 4); -// p ^= (byte)(p >> 2); -// p ^= (byte)(p >> 1); -// if ((p & 1) == 0) flagsA6 |= 0x04; // Set if Parity is Even - -// // Undocumented bits 3 and 5 are copied directly from the resulting Accumulator -// flagsA6 |= (byte)(AF.High & 0x28); - -// // N (Subtract) and C (Carry) are always reset to 0 for AND instructions, -// // which happens naturally since we started with flagsA6 = 0. - -// AF.Low = flagsA6; - -// return 19; -// case 0xBE: // CP (IY+d) -// // 1. Fetch the displacement byte and calculate the address using IY -// sbyte offsetBE = (sbyte)FetchByte(); -// ushort addressBE = (ushort)(IY.Word + offsetBE); - -// // 2. Read the value from memory -// byte cpVal = ReadMemory(addressBE); - -// // 3. Perform the phantom subtraction -// aVal = AF.High; -// result = aVal - cpVal; - -// // --- 8-Bit Compare Flag Calculation (Identical to SUB) --- -// newFlags = 0; - -// // S Flag (Bit 7): Set if the phantom result is negative -// if ((result & 0x80) != 0) newFlags |= 0x80; - -// // Z Flag (Bit 6): Set if A perfectly matches the memory value -// if ((result & 0xFF) == 0) newFlags |= 0x40; - -// // H Flag (Bit 4): Set if there was a borrow from Bit 3 -// if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10; - -// // P/V Flag (Bit 2): Set on Overflow -// if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04; - -// // N Flag (Bit 1): Always set to 1 for Subtractions/Compares -// newFlags |= 0x02; - -// // C Flag (Bit 0): Set if A was smaller than the memory value -// if (aVal < cpVal) newFlags |= 0x01; - -// AF.Low = newFlags; - -// return 19; -// case 0xCB: // The FD CB nested prefix -// { -// sbyte displacement = (sbyte)FetchByte(); -// byte cbOpcode = FetchByte(); -// targetAddress = (ushort)(IY.Word + displacement); -// memVal = ReadMemory(targetAddress); - -// // Extract the mathematical properties of the opcode -// int operation = cbOpcode >> 6; // 01 = BIT, 10 = RES, 11 = SET -// int bitIndex = (cbOpcode >> 3) & 0x07; // Extracts a number 0-7 -// byte bitMask = (byte)(1 << bitIndex); // Creates the bitmask (e.g., 0x01, 0x02, 0x80) - -// switch (operation) -// { -// case 1: // ALL BIT Instructions -// AF.Low &= 0x01; // Preserve ONLY Carry -// AF.Low |= 0x10; // Set Half-Carry - -// if ((memVal & bitMask) == 0) -// { -// AF.Low |= 0x44; // Set Zero (Bit 6) and P/V (Bit 2) -// } -// else if (bitIndex == 7) -// { -// AF.Low |= 0x80; // If testing Bit 7 and it is 1, set Sign (Bit 7) -// } -// return 20; - -// case 2: // ALL RES Instructions -// memVal &= (byte)(~bitMask); // Invert mask and AND it to clear the bit -// WriteMemory(targetAddress, memVal); -// return 23; - -// case 3: // ALL SET Instructions -// memVal |= bitMask; // OR the mask to force the bit to 1 -// WriteMemory(targetAddress, memVal); -// return 23; - -// case 0: -// // Shift/Rotate instructions will go here later -// throw new NotImplementedException($"FD CB Shift/Rotate opcode {cbOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!"); - -// default: -// throw new Exception("Invalid bitwise operation."); -// } -// } -// case 0xE1: // POP IY -// // 1. Read the Low byte from the current Stack Pointer, then increment SP -// IY.Low = ReadMemory(SP); -// SP++; - -// // 2. Read the High byte from the new Stack Pointer, then increment SP -// IY.High = ReadMemory(SP); -// SP++; - -// return 14; // 14 T-States -// case 0xE5: // PUSH IY -// // 1. Decrement SP and write the High byte -// SP--; -// WriteMemory(SP, IY.High); - -// // 2. Decrement SP again and write the Low byte -// SP--; -// WriteMemory(SP, IY.Low); - -// return 15; // 15 T-States -// default: -// throw new NotImplementedException($"FD prefix opcode {opcode:X2} at PC 0x{(PC - 2):X4} not implemented!"); -// } -// } -// } -//} \ No newline at end of file