using System; using Core.Interfaces; namespace Core.Cpu { public partial class Z80 { //T-State counter public long TotalTStates { get; set; } // Interrupt Flip-Flops public bool IFF1; public bool IFF2; // 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 IIoBus _ioBus; public Z80(IMemory memory, IIoBus ioBus) { _memory = memory; _ioBus = ioBus; Reset(); } public void Reset() { PC = 0x0000; // The Z80 initializes SP to 0xFFFF on boot SP = 0xFFFF; AF.Word = 0; BC.Word = 0; DE.Word = 0; HL.Word = 0; } public int Step() { // Fetch the next opcode and increment the Program Counter byte opcode = _memory.Read(PC++); int tStates = ExecuteOpcode(opcode); TotalTStates += tStates; // Decode and execute return tStates; } // Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2 private ushort FetchWord() { byte low = _memory.Read(PC++); byte high = _memory.Read(PC++); return (ushort)((high << 8) | low); } private byte FetchByte() { return _memory.Read(PC++); } 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 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 int 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 int ExecuteOpcode(byte opcode) { switch (opcode) { case 0x00: // NOP return 4; case 0x11: //LD DE, nn DE.Word = FetchWord(); return 10; case 0x2B: // DEC HL HL.Word--; return 6; case 0x36: // LD (HL), n byte nValue = FetchByte(); _memory.Write(HL.Word, nValue); return 10; case 0x3E: //LD A, n AF.High = FetchByte(); return 7; case 0x47: // LD B, A BC.High = AF.High; return 4; case 0x62: // LD H, D HL.High = DE.High; return 4; case 0x6B: // LD L, E HL.Low = DE.Low; return 4; case 0xC3: PC = FetchWord(); 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); _ioBus.Write(portAddress, AF.High); return 11; // Takes 11 T-States case 0xDE: // SBC A, n Sbc(FetchByte()); return 7; case 0xED: return ExecuteExtendedPrefix(); case 0xF3: // DI (Disable Interrupts) IFF1 = false; IFF2 = false; return 4; case 0xAF: // XOR A AF.High = 0; AF.Low = 0x44; return 4; default: throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); } } private int ExecuteExtendedPrefix() { // Fetch the actual extended instruction byte extendedOpcode = _memory.Read(PC++); switch (extendedOpcode) { case 0x47: // LD I, A I = AF.High; return 9; default: throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); } } } }