ULA Implemented. Scanline renderer so cycle accurate

This commit is contained in:
2026-04-21 15:34:10 +01:00
parent ad3a0b5040
commit dcbb505145
6 changed files with 424 additions and 310 deletions

View File

@@ -43,6 +43,9 @@ namespace Core.Cpu
private readonly IO_Bus _simpleIoBus;
public TapManager _tapManager;
//External Timing interface
public Func<ushort, long, int>? WaitStateCallback { get; set; }
//Misc Variables
byte newFlags = 0;
int result = 0;
@@ -88,6 +91,15 @@ namespace Core.Cpu
//_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;
@@ -116,8 +128,8 @@ namespace Core.Cpu
ushort vectorAddress = (ushort)((I << 8) | 0xFF);
// B. Read the actual 16-bit ISR address from that location in memory (Little-Endian)
byte pcLow = _memory.Read(vectorAddress);
byte pcHigh = _memory.Read((ushort)(vectorAddress + 1));
byte pcLow = ReadMemory(vectorAddress);
byte pcHigh = ReadMemory((ushort)(vectorAddress + 1));
// C. Jump to the custom game routine!
PC = (ushort)((pcHigh << 8) | pcLow);
@@ -131,6 +143,38 @@ namespace Core.Cpu
}
}
// 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)
{
@@ -157,7 +201,7 @@ namespace Core.Cpu
}
// Fetch the next opcode and increment the Program Counter
byte opcode = _memory.Read(PC++);
byte opcode = ReadMemory(PC++);
int tStates = ExecuteOpcode(opcode);
TotalTStates += tStates;
@@ -194,7 +238,7 @@ namespace Core.Cpu
// The RAM dump starts at byte 27 and maps perfectly to 0x4000 -> 0xFFFF
for (int i = 0; i < 49152; i++)
{
_memory.Write((ushort)(0x4000 + i), snaData[27 + i]);
WriteMemory((ushort)(0x4000 + i), snaData[27 + i]);
}
// --- 3. The Magic Bullet ---
@@ -226,7 +270,7 @@ namespace Core.Cpu
int bytesToCopy = DE.Word;
for (int i = 0; i < bytesToCopy; i++)
{
_memory.Write((ushort)(IX.Word + i), block[i + 1]);
WriteMemory((ushort)(IX.Word + i), block[i + 1]);
}
// 4. Update the registers exactly how the ROM would after a successful load
@@ -246,25 +290,15 @@ namespace Core.Cpu
// A quick helper to simulate a RET instruction manually
private void ExecuteRet()
{
byte pcLow = _memory.Read(SP);
byte pcLow = ReadMemory(SP);
SP++;
byte pcHigh = _memory.Read(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
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()
{
@@ -674,20 +708,20 @@ namespace Core.Cpu
{
// High byte goes first
SP--;
_memory.Write(SP, (byte)(value >> 8));
WriteMemory(SP, (byte)(value >> 8));
// Low byte goes second
SP--;
_memory.Write(SP, (byte)(value & 0xFF));
WriteMemory(SP, (byte)(value & 0xFF));
}
private ushort Pop()
{
// The Z80 is Little-Endian. Low byte comes off the stack first.
byte low = _memory.Read(SP++);
byte low = ReadMemory(SP++);
// High byte comes off second.
byte high = _memory.Read(SP++);
byte high = ReadMemory(SP++);
return (ushort)((high << 8) | low);
}
@@ -704,7 +738,7 @@ namespace Core.Cpu
BC.Word = FetchWord();
return 10;
case 0x02: // LD (BC), A
_memory.Write(BC.Word, AF.High);
WriteMemory(BC.Word, AF.High);
return 7;
case 0x03: // INC BC
BC.Word++;
@@ -736,11 +770,11 @@ namespace Core.Cpu
AF_Prime.Word = tempAF;
return 4;
case 0x0A: //LD A (BC)
AF.High = _memory.Read(BC.Word);
AF.High = ReadMemory(BC.Word);
return 7;
case 0x0C: BC.Low = Inc8(BC.Low); return 4; // INC C
case 0x12: // LD (DE), A
_memory.Write(DE.Word, AF.High);
WriteMemory(DE.Word, AF.High);
return 7;
case 0x14: DE.High = Inc8(DE.High); return 4; // INC D
case 0x1C: DE.Low = Inc8(DE.Low); return 4; // INC E
@@ -752,7 +786,7 @@ namespace Core.Cpu
HL.Low = FetchByte();
return 7;
case 0x34:
_memory.Write(HL.Word, Inc8(_memory.Read(HL.Word)));
WriteMemory(HL.Word, Inc8(ReadMemory(HL.Word)));
return 11; // INC (HL) takes 11 T-States
case 0x3C: AF.High = Inc8(AF.High); return 4; // INC A
@@ -773,7 +807,7 @@ namespace Core.Cpu
return 4;
case 0x35:
_memory.Write(HL.Word, Dec8(_memory.Read(HL.Word)));
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
@@ -864,7 +898,7 @@ namespace Core.Cpu
return 12;
case 0x1A: // LD A, (DE)
AF.High = _memory.Read(DE.Word);
AF.High = ReadMemory(DE.Word);
return 7;
case 0x1B: // DEC DE
DE.Word--;
@@ -903,8 +937,8 @@ namespace Core.Cpu
return 10;
case 0x22: // LD (nn), HL
ushort dest22 = FetchWord();
_memory.Write(dest22, HL.Low);
_memory.Write((ushort)(dest22 + 1), HL.High);
WriteMemory(dest22, HL.Low);
WriteMemory((ushort)(dest22 + 1), HL.High);
return 16;
case 0x23: // INC HL
HL.Word++;
@@ -971,8 +1005,8 @@ namespace Core.Cpu
case 0x2A: // LD HL, (nn)
{
ushort srcAddress = FetchWord();
HL.Low = _memory.Read(srcAddress);
HL.High = _memory.Read((ushort)(srcAddress + 1));
HL.Low = ReadMemory(srcAddress);
HL.High = ReadMemory((ushort)(srcAddress + 1));
return 16;
}
case 0x2B: // DEC HL
@@ -991,7 +1025,7 @@ namespace Core.Cpu
case 0x32: // LD (nn), A
{
ushort destAddress = FetchWord();
_memory.Write(destAddress, AF.High);
WriteMemory(destAddress, AF.High);
return 13;
}
case 0x33: // INC SP
@@ -999,7 +1033,7 @@ namespace Core.Cpu
return 6;
case 0x36: // LD (HL), n
byte nValue = FetchByte();
_memory.Write(HL.Word, nValue);
WriteMemory(HL.Word, nValue);
return 10;
case 0x37: // SCF
AF.Low |= 0x01; // Force Carry Flag (Bit 0) to 1
@@ -1016,7 +1050,7 @@ namespace Core.Cpu
return 7;
case 0x3A: // LD A, (nn)
ushort address3A = FetchWord();
AF.High = _memory.Read(address3A);
AF.High = ReadMemory(address3A);
return 13;
case 0x3B: // DEC SP
SP--;
@@ -1041,7 +1075,7 @@ namespace Core.Cpu
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 = _memory.Read(HL.Word); return 7;
case 0x46: BC.High = ReadMemory(HL.Word); return 7;
case 0x47: BC.High = AF.High; return 4;
// --- LD C, r ---
@@ -1052,7 +1086,7 @@ namespace Core.Cpu
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 = _memory.Read(HL.Word); return 7;
case 0x4E: BC.Low = ReadMemory(HL.Word); return 7;
case 0x4F: BC.Low = AF.High; return 4;
// --- LD D, r ---
@@ -1063,7 +1097,7 @@ namespace Core.Cpu
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 = _memory.Read(HL.Word); return 7;
case 0x56: DE.High = ReadMemory(HL.Word); return 7;
case 0x57: DE.High = AF.High; return 4;
// --- LD E, r ---
@@ -1074,7 +1108,7 @@ namespace Core.Cpu
return 4;
case 0x5C: DE.Low = HL.High; return 4;
case 0x5D: DE.Low = HL.Low; return 4;
case 0x5E: DE.Low = _memory.Read(HL.Word); return 7;
case 0x5E: DE.Low = ReadMemory(HL.Word); return 7;
case 0x5F: DE.Low = AF.High; return 4;
// --- LD H, r ---
@@ -1085,7 +1119,7 @@ namespace Core.Cpu
case 0x64: //HL.High = HL.High;
return 4;
case 0x65: HL.High = HL.Low; return 4;
case 0x66: HL.High = _memory.Read(HL.Word); return 7;
case 0x66: HL.High = ReadMemory(HL.Word); return 7;
case 0x67: HL.High = AF.High; return 4;
// --- LD L, r ---
@@ -1096,16 +1130,16 @@ namespace Core.Cpu
case 0x6C: HL.Low = HL.High; return 4;
case 0x6D: //HL.Low = HL.Low;
return 4;
case 0x6E: HL.Low = _memory.Read(HL.Word); return 7;
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: _memory.Write(HL.Word, BC.High); return 7;
case 0x71: _memory.Write(HL.Word, BC.Low); return 7;
case 0x72: _memory.Write(HL.Word, DE.High); return 7;
case 0x73: _memory.Write(HL.Word, DE.Low); return 7;
case 0x74: _memory.Write(HL.Word, HL.High); return 7;
case 0x75: _memory.Write(HL.Word, HL.Low); return 7;
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)
{
@@ -1117,7 +1151,7 @@ namespace Core.Cpu
InterruptRequested = false;
return 4;
}
case 0x77: _memory.Write(HL.Word, AF.High); return 7;
case 0x77: WriteMemory(HL.Word, AF.High); return 7;
// --- LD A, r ---
case 0x78: AF.High = BC.High; return 4;
@@ -1126,7 +1160,7 @@ namespace Core.Cpu
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 = _memory.Read(HL.Word); return 7;
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
@@ -1135,7 +1169,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // ADD A, (HL)
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
@@ -1148,7 +1182,7 @@ namespace Core.Cpu
// --- ADC A, Memory ---
case 0x8E: // ADC A, (HL)
AdcA(_memory.Read(HL.Word));
AdcA(ReadMemory(HL.Word));
return 7;
// --- ADC A, Immediate ---
@@ -1161,7 +1195,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // SUB (HL)
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
@@ -1170,7 +1204,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // SBC A, (HL)
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
@@ -1178,7 +1212,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // AND (HL)
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
@@ -1186,7 +1220,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // XOR (HL)
case 0xAE: Xor(ReadMemory(HL.Word)); return 7; // XOR (HL)
case 0xAF: Xor(AF.High); return 4; // XOR A
// --- OR r ---
@@ -1196,7 +1230,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // OR (HL)
case 0xB6: Or(ReadMemory(HL.Word)); return 7; // OR (HL)
case 0xB7: Or(AF.High); return 4; // OR A
// --- CP r ---
@@ -1206,7 +1240,7 @@ namespace Core.Cpu
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(_memory.Read(HL.Word)); return 7; // CP (HL)
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
@@ -1431,12 +1465,12 @@ namespace Core.Cpu
return 10;
case 0xE3: // EX (SP), HL
// 1. Read the 16-bit value currently on top of the stack
byte spLow = _memory.Read(SP);
byte spHigh = _memory.Read((ushort)(SP + 1));
byte spLow = ReadMemory(SP);
byte spHigh = ReadMemory((ushort)(SP + 1));
// 2. Write the current HL registers onto the stack in its place
_memory.Write(SP, HL.Low);
_memory.Write((ushort)(SP + 1), HL.High);
WriteMemory(SP, HL.Low);
WriteMemory((ushort)(SP + 1), HL.High);
// 3. Update HL with the data we pulled off the stack
HL.Low = spLow;
@@ -1508,7 +1542,7 @@ namespace Core.Cpu
private int ExecuteExtendedPrefix() //ED
{
// Fetch the actual extended instruction
byte extendedOpcode = _memory.Read(PC++);
byte extendedOpcode = FetchByte();
byte val = 0;
switch (extendedOpcode)
@@ -1518,8 +1552,8 @@ namespace Core.Cpu
return 15;
case 0x43: // LD (nn), BC
ushort dest43 = FetchWord();
_memory.Write(dest43, BC.Low);
_memory.Write((ushort)(dest43 + 1), BC.High);
WriteMemory(dest43, BC.Low);
WriteMemory((ushort)(dest43 + 1), BC.High);
return 20;
case 0x44: // NEG
int aBefore = AF.High;
@@ -1557,8 +1591,8 @@ namespace Core.Cpu
return 15;
case 0x4B: // LD BC, (nn)
ushort src4B = FetchWord();
BC.Low = _memory.Read(src4B);
BC.High = _memory.Read((ushort)(src4B + 1));
BC.Low = ReadMemory(src4B);
BC.High = ReadMemory((ushort)(src4B + 1));
return 20;
case 0x4D: // RETI Does not affect IFF1 or IFF2
PC = Pop();
@@ -1568,8 +1602,8 @@ namespace Core.Cpu
return 15;
case 0x53: // LD (nn), DE
ushort dest53 = FetchWord();
_memory.Write(dest53, DE.Low);
_memory.Write((ushort)(dest53 + 1), DE.High);
WriteMemory(dest53, DE.Low);
WriteMemory((ushort)(dest53 + 1), DE.High);
return 20;
case 0x56: // IM 1
InterruptMode = 1;
@@ -1579,8 +1613,8 @@ namespace Core.Cpu
return 15;
case 0x5B: // LD DE, (nn)
ushort src5B = FetchWord();
DE.Low = _memory.Read(src5B);
DE.High = _memory.Read((ushort)(src5B + 1));
DE.Low = ReadMemory(src5B);
DE.High = ReadMemory((ushort)(src5B + 1));
return 20;
case 0x5E: // IM 2
// Set the CPU's internal interrupt mode state
@@ -1649,8 +1683,8 @@ namespace Core.Cpu
return 15; // 15 T-States
case 0x73: // LD (nn), SP
ushort dest73 = FetchWord();
_memory.Write(dest73, (byte)SP);
_memory.Write((ushort)(dest73 + 1), (byte)(SP >> 8));
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
@@ -1684,8 +1718,8 @@ namespace Core.Cpu
ushort address7B = (ushort)((addrHigh << 8) | addrLow);
// 2. Read the 16-bit value stored at that exact memory location (Little-Endian!)
byte spLow = _memory.Read(address7B);
byte spHigh = _memory.Read((ushort)(address7B + 1));
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);
@@ -1693,10 +1727,10 @@ namespace Core.Cpu
return 20;
case 0xA0: // LDI
// 1. Read byte from (HL)
val = _memory.Read(HL.Word);
val = ReadMemory(HL.Word);
// 2. Write byte to (DE)
_memory.Write(DE.Word, val);
WriteMemory(DE.Word, val);
// 3. Increment memory pointers, Decrement byte counter
HL.Word++;
@@ -1717,10 +1751,10 @@ namespace Core.Cpu
return 16;
case 0xB0: // LDIR
// 1. Read byte from (HL)
val = _memory.Read(HL.Word);
val = ReadMemory(HL.Word);
// 2. Write byte to (DE)
_memory.Write(DE.Word, val);
WriteMemory(DE.Word, val);
// 3. Increment memory pointers, Decrement byte counter
HL.Word++;
@@ -1744,10 +1778,10 @@ namespace Core.Cpu
return 16;
case 0xB8: // LDDR
// 1. Read byte from (HL)
val = _memory.Read(HL.Word);
val = ReadMemory(HL.Word);
// 2. Write byte to (DE)
_memory.Write(DE.Word, val);
WriteMemory(DE.Word, val);
// 3. Decrement all three pointers
HL.Word--;
@@ -1797,7 +1831,7 @@ namespace Core.Cpu
case 3: val = DE.Low; break;
case 4: val = HL.High; break;
case 5: val = HL.Low; break;
case 6: val = _memory.Read(HL.Word); break; // The 0x110 (HL) exception
case 6: val = ReadMemory(HL.Word); break; // The 0x110 (HL) exception
case 7: val = AF.High; break;
}
@@ -1930,7 +1964,7 @@ namespace Core.Cpu
case 3: DE.Low = val; break;
case 4: HL.High = val; break;
case 5: HL.Low = val; break;
case 6: _memory.Write(HL.Word, val); break;
case 6: WriteMemory(HL.Word, val); break;
case 7: AF.High = val; break;
}
@@ -1969,10 +2003,10 @@ namespace Core.Cpu
ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22);
// 2. Write the LOW byte of IX to the exact address
_memory.Write(address22, IX.Low);
WriteMemory(address22, IX.Low);
// 3. Write the HIGH byte of IX to the address + 1
_memory.Write((ushort)(address22 + 1), IX.High);
WriteMemory((ushort)(address22 + 1), IX.High);
return 20;
case 0x23: // INC IX
@@ -1998,10 +2032,10 @@ namespace Core.Cpu
ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A);
// 2. Read the LOW byte from that specific memory location
byte ixLow = _memory.Read(address2A);
byte ixLow = ReadMemory(address2A);
// 3. Read the HIGH byte from the next consecutive memory location
byte ixHigh = _memory.Read((ushort)(address2A + 1));
byte ixHigh = ReadMemory((ushort)(address2A + 1));
// 4. Combine them and drop them into the IX register pair
IX.Word = (ushort)((ixHigh << 8) | ixLow);
@@ -2026,13 +2060,13 @@ namespace Core.Cpu
ushort address34 = (ushort)(IX.Word + offset34);
// 3. Read the value from memory
byte val34 = _memory.Read(address34);
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
_memory.Write(address34, result34);
WriteMemory(address34, result34);
return 23;
case 0x35: // DEC (IX+d)
@@ -2043,13 +2077,13 @@ namespace Core.Cpu
ushort address35 = (ushort)(IX.Word + offset35);
// 3. Read the value from memory
byte val35 = _memory.Read(address35);
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
_memory.Write(address35, result35);
WriteMemory(address35, result35);
return 23;
case 0x36: // LD (IX+d), n
@@ -2063,7 +2097,7 @@ namespace Core.Cpu
ushort address36 = (ushort)(IX.Word + offset36);
// 4. Write the immediate value directly into memory
_memory.Write(address36, n36);
WriteMemory(address36, n36);
return 19;
case 0x46: // LD B, (IX+d)
@@ -2074,7 +2108,7 @@ namespace Core.Cpu
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 = _memory.Read(address46);
BC.High = ReadMemory(address46);
return 19;
case 0x4E: // LD C, (IX+d)
@@ -2085,7 +2119,7 @@ namespace Core.Cpu
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 = _memory.Read(address4E);
BC.Low = ReadMemory(address4E);
return 19;
case 0x56: // LD D, (IX+d)
@@ -2096,7 +2130,7 @@ namespace Core.Cpu
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 = _memory.Read(address56);
DE.High = ReadMemory(address56);
return 19;
case 0x5E: // LD E, (IX+d)
@@ -2107,7 +2141,7 @@ namespace Core.Cpu
ushort address5E = (ushort)(IX.Word + offset5E);
// 3. Read the byte from memory and drop it into the E register
DE.Low = _memory.Read(address5E);
DE.Low = ReadMemory(address5E);
return 19;
case 0x66: // LD H, (IX+d)
@@ -2118,7 +2152,7 @@ namespace Core.Cpu
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 = _memory.Read(address66);
HL.High = ReadMemory(address66);
return 19;
case 0x67: // LD IXH, A
@@ -2145,7 +2179,7 @@ namespace Core.Cpu
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 = _memory.Read(address6E);
HL.Low = ReadMemory(address6E);
return 19;
case 0x72: // LD (IX+d), D
@@ -2156,7 +2190,7 @@ namespace Core.Cpu
ushort address72 = (ushort)(IX.Word + offset72);
// 3. Write the D register (DE.High) to memory
_memory.Write(address72, DE.High);
WriteMemory(address72, DE.High);
return 19; // 19 T-States
case 0x73: // LD (IX+d), E
@@ -2167,7 +2201,7 @@ namespace Core.Cpu
ushort address73 = (ushort)(IX.Word + offset73);
// 3. Write the E register (DE.Low) to memory
_memory.Write(address73, DE.Low);
WriteMemory(address73, DE.Low);
return 19;
@@ -2179,7 +2213,7 @@ namespace Core.Cpu
ushort address74 = (ushort)(IX.Word + offset74);
// 3. Write the contents of the L register (Low byte of HL) into memory
_memory.Write(address74, HL.High);
WriteMemory(address74, HL.High);
return 19;
case 0x75: // LD (IX+d), L
@@ -2190,7 +2224,7 @@ namespace Core.Cpu
ushort address75 = (ushort)(IX.Word + offset75);
// 3. Write the contents of the L register (Low byte of HL) into memory
_memory.Write(address75, HL.Low);
WriteMemory(address75, HL.Low);
return 19;
case 0x77: // LD (IX+d), A
@@ -2201,7 +2235,7 @@ namespace Core.Cpu
ushort address77 = (ushort)(IX.Word + offset77);
// 3. Write the Accumulator (AF.High) into memory at that address
_memory.Write(address77, AF.High);
WriteMemory(address77, AF.High);
return 19;
case 0x7C: // LD A, IXH
@@ -2216,7 +2250,7 @@ namespace Core.Cpu
ushort address7E = (ushort)(IX.Word + offset7E);
// 3. Read the byte from memory and drop it straight into the Accumulator (A)
AF.High = _memory.Read(address7E);
AF.High = ReadMemory(address7E);
return 19;
case 0x86: // ADD A, (IX+d)
@@ -2224,7 +2258,7 @@ namespace Core.Cpu
ushort address86 = (ushort)(IX.Word + offset86);
// Read the memory and pass it straight into your flawless helper!
Add(_memory.Read(address86));
Add(ReadMemory(address86));
return 19;
case 0x96: // SUB (IX+d)
@@ -2232,7 +2266,7 @@ namespace Core.Cpu
ushort address96 = (ushort)(IX.Word + offset96);
// Read the memory and pass it straight into your flawless helper!
Sub(_memory.Read(address96));
Sub(ReadMemory(address96));
return 19;
case 0xBE: // CP (IX+d)
// 1. Fetch the displacement byte and calculate the address
@@ -2240,7 +2274,7 @@ namespace Core.Cpu
ushort addressBE = (ushort)(IX.Word + offsetBE);
// 2. Read the value from memory
byte cpVal = _memory.Read(addressBE);
byte cpVal = ReadMemory(addressBE);
// 3. Perform the phantom subtraction
int aVal = AF.High;
@@ -2281,7 +2315,7 @@ namespace Core.Cpu
byte cbOpcode = FetchByte();
ushort targetAddress = (ushort)(IX.Word + displacement);
byte memVal = _memory.Read(targetAddress);
byte memVal = ReadMemory(targetAddress);
// Extract the mathematical properties of the opcode
int operation = cbOpcode >> 6; // 01 = BIT, 10 = RES, 11 = SET
@@ -2312,11 +2346,11 @@ namespace Core.Cpu
}
case 0xE1: // POP IX
// 1. Read the low byte from the top of the stack
byte popLow = _memory.Read(SP);
byte popLow = ReadMemory(SP);
SP++; // Move stack pointer up
// 2. Read the high byte
byte popHigh = _memory.Read(SP);
byte popHigh = ReadMemory(SP);
SP++; // Move stack pointer up again
// 3. Combine them and store in IX
@@ -2326,11 +2360,11 @@ namespace Core.Cpu
case 0xE5: // PUSH IX
// 1. Decrement the stack pointer and write the HIGH byte
SP--;
_memory.Write(SP, IX.High);
WriteMemory(SP, IX.High);
// 2. Decrement the stack pointer again and write the LOW byte
SP--;
_memory.Write(SP, IX.Low);
WriteMemory(SP, IX.Low);
return 15;
case 0xE9: // JP (IX)
@@ -2359,7 +2393,7 @@ namespace Core.Cpu
ushort address34 = (ushort)(IY.Word + offset34);
// 2. Read the value from memory
byte valBefore = _memory.Read(address34);
byte valBefore = ReadMemory(address34);
// 3. Increment the value
int result = valBefore + 1;
@@ -2387,7 +2421,7 @@ namespace Core.Cpu
AF.Low = newFlags;
// 4. Write the modified value back to memory
_memory.Write(address34, (byte)result);
WriteMemory(address34, (byte)result);
return 23; // 23 T-States
case 0x35: // DEC (IY+d)
@@ -2395,9 +2429,9 @@ namespace Core.Cpu
targetAddress = (ushort)(IY.Word + offset);
// Read, decrement using your existing helper, and write back
memVal = _memory.Read(targetAddress);
memVal = ReadMemory(targetAddress);
byte decVal = Dec8(memVal);
_memory.Write(targetAddress, decVal);
WriteMemory(targetAddress, decVal);
return 23;
case 0x36: // LD (IY+d), n
{
@@ -2405,7 +2439,7 @@ namespace Core.Cpu
byte nValue = FetchByte();
targetAddress = (ushort)(IY.Word + offset36);
_memory.Write(targetAddress, nValue);
WriteMemory(targetAddress, nValue);
return 19; // Takes 19 T-States
}
case 0x46: // LD B, (IY+d)
@@ -2413,7 +2447,7 @@ namespace Core.Cpu
sbyte displacement = (sbyte)FetchByte();
targetAddress = (ushort)(IY.Word + displacement);
BC.High = _memory.Read(targetAddress);
BC.High = ReadMemory(targetAddress);
return 19; // Takes 19 T-States
}
case 0x4E: // LD C, (IY+d)
@@ -2424,7 +2458,7 @@ namespace Core.Cpu
ushort address4E = (ushort)(IY.Word + offset4E);
// 3. Read the memory and store it in C
BC.Low = _memory.Read(address4E);
BC.Low = ReadMemory(address4E);
return 19;
case 0x56: // LD D, (IY+d)
@@ -2435,7 +2469,7 @@ namespace Core.Cpu
ushort address56 = (ushort)(IY.Word + offset56);
// 3. Read the memory and store it in D (the high byte of DE)
DE.High = _memory.Read(address56);
DE.High = ReadMemory(address56);
return 19;
case 0x5E: // LD E, (IY+d)
@@ -2446,14 +2480,14 @@ namespace Core.Cpu
ushort address5E = (ushort)(IY.Word + offset5E);
// 3. Read the memory and store it in E (the low byte of DE)
DE.Low = _memory.Read(address5E);
DE.Low = ReadMemory(address5E);
return 19;
case 0x6E: // LD L, (IY+d)
sbyte displacementVal = (sbyte)FetchByte();
ushort targetAddr = (ushort)(IY.Word + displacementVal);
HL.Low = _memory.Read(targetAddr);
HL.Low = ReadMemory(targetAddr);
return 19;
case 0x71: // LD (IY+d), C
{
@@ -2461,7 +2495,7 @@ namespace Core.Cpu
targetAddress = (ushort)(IY.Word + offset71);
// Write the C register (low byte of BC) to memory
_memory.Write(targetAddress, BC.Low);
WriteMemory(targetAddress, BC.Low);
return 19; // Takes 19 T-States
}
case 0x72: // LD (IY+d), D
@@ -2472,7 +2506,7 @@ namespace Core.Cpu
ushort address72 = (ushort)(IY.Word + offset72);
// 3. Write the contents of the D register (High byte of DE) into memory
_memory.Write(address72, DE.High);
WriteMemory(address72, DE.High);
return 19; // 19 T-States
case 0x74: // LD (IY+d), H
@@ -2483,20 +2517,20 @@ namespace Core.Cpu
ushort address74 = (ushort)(IY.Word + offset74);
// 3. Write the contents of the H register into memory at that address
_memory.Write(address74, HL.High);
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
_memory.Write(targetAddress, HL.Low);
WriteMemory(targetAddress, HL.Low);
return 19;
case 0x86: // ADD A, (IY+d)
{
sbyte displacementAdd = (sbyte)FetchByte();
ushort targetAddressAdd = (ushort)(IY.Word + displacementAdd);
byte valueToAdd = _memory.Read(targetAddressAdd);
byte valueToAdd = ReadMemory(targetAddressAdd);
AddA(valueToAdd);
return 19;
@@ -2507,7 +2541,7 @@ namespace Core.Cpu
ushort address96 = (ushort)(IY.Word + offset96);
// 2. Read the value from memory
byte subVal = _memory.Read(address96);
byte subVal = ReadMemory(address96);
// 3. Perform the subtraction from the Accumulator
int aVal = AF.High;
@@ -2545,7 +2579,7 @@ namespace Core.Cpu
ushort addressBE = (ushort)(IY.Word + offsetBE);
// 2. Read the value from memory
byte cpVal = _memory.Read(addressBE);
byte cpVal = ReadMemory(addressBE);
// 3. Perform the phantom subtraction
aVal = AF.High;
@@ -2580,7 +2614,7 @@ namespace Core.Cpu
sbyte displacement = (sbyte)FetchByte();
byte cbOpcode = FetchByte();
targetAddress = (ushort)(IY.Word + displacement);
memVal = _memory.Read(targetAddress);
memVal = ReadMemory(targetAddress);
// Extract the mathematical properties of the opcode
int operation = cbOpcode >> 6; // 01 = BIT, 10 = RES, 11 = SET
@@ -2605,12 +2639,12 @@ namespace Core.Cpu
case 2: // ALL RES Instructions
memVal &= (byte)(~bitMask); // Invert mask and AND it to clear the bit
_memory.Write(targetAddress, memVal);
WriteMemory(targetAddress, memVal);
return 23;
case 3: // ALL SET Instructions
memVal |= bitMask; // OR the mask to force the bit to 1
_memory.Write(targetAddress, memVal);
WriteMemory(targetAddress, memVal);
return 23;
case 0: