ULA Implemented. Scanline renderer so cycle accurate
This commit is contained in:
296
Core/Cpu/Z80.cs
296
Core/Cpu/Z80.cs
@@ -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:
|
||||
|
||||
128
Core/Io/ULA.cs
Normal file
128
Core/Io/ULA.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using Core.Memory;
|
||||
using System;
|
||||
|
||||
namespace Core.Io
|
||||
{
|
||||
public class ULA
|
||||
{
|
||||
private readonly MemoryBus _memoryBus;
|
||||
private readonly IO_Bus _simpleIoBus;
|
||||
|
||||
// The ULA owns the frame buffer now!
|
||||
public const int ScreenWidth = 320;
|
||||
public const int ScreenHeight = 256; // Perfectly cropped size
|
||||
public int[] FrameBuffer { get; private set; }
|
||||
|
||||
private int _ulaFrameCount = 0;
|
||||
|
||||
// The hardware color palette belongs to the ULA, not the Windows Form!
|
||||
private readonly int[] SpectrumColors = new int[]
|
||||
{
|
||||
unchecked((int)0xFF000000), unchecked((int)0xFF0000D7),
|
||||
unchecked((int)0xFFD70000), unchecked((int)0xFFD700D7),
|
||||
unchecked((int)0xFF00D700), unchecked((int)0xFF00D7D7),
|
||||
unchecked((int)0xFFD7D700), unchecked((int)0xFFD7D7D7),
|
||||
|
||||
unchecked((int)0xFF000000), unchecked((int)0xFF0000FF),
|
||||
unchecked((int)0xFFFF0000), unchecked((int)0xFFFF00FF),
|
||||
unchecked((int)0xFF00FF00), unchecked((int)0xFF00FFFF),
|
||||
unchecked((int)0xFFFFFF00), unchecked((int)0xFFFFFFFF)
|
||||
};
|
||||
|
||||
public ULA(MemoryBus memoryBus, IO_Bus ioBus)
|
||||
{
|
||||
_memoryBus = memoryBus;
|
||||
_simpleIoBus = ioBus;
|
||||
FrameBuffer = new int[ScreenWidth * ScreenHeight];
|
||||
}
|
||||
|
||||
// We will wire this up to the Z80 in Phase 3 so the CPU stays system-agnostic!
|
||||
public int GetContentionDelay(ushort address, long currentTStates)
|
||||
{
|
||||
if (address < 0x4000 || address > 0x7FFF) return 0;
|
||||
long frameT = currentTStates % 69888;
|
||||
if (frameT < 14336 || frameT >= 57344) return 0;
|
||||
int lineT = (int)(frameT % 224);
|
||||
if (lineT < 14 || lineT > 141) return 0;
|
||||
|
||||
int delayOffset = (lineT - 14) % 8;
|
||||
int[] delayPattern = { 6, 5, 4, 3, 2, 1, 0, 0 };
|
||||
return delayPattern[delayOffset];
|
||||
}
|
||||
|
||||
// The perfectly cropped, 320x256 Scanline Renderer
|
||||
public void RenderScanline(int scanline)
|
||||
{
|
||||
// 1. Drop the invisible lines instantly (VBlank/Overscan)
|
||||
if (scanline < 32 || scanline > 287) return;
|
||||
|
||||
// 2. Calculate our visual Y coordinate (0 to 255) for the bitmap array
|
||||
int renderY = scanline - 32;
|
||||
int currentBorderColor = SpectrumColors[_simpleIoBus.BorderColorIndex];
|
||||
|
||||
// --- Are we in the Top or Bottom Border? ---
|
||||
if (scanline < 64 || scanline > 255)
|
||||
{
|
||||
int yOffset = renderY * ScreenWidth;
|
||||
for (int x = 0; x < ScreenWidth; x++)
|
||||
{
|
||||
FrameBuffer[yOffset + x] = currentBorderColor;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// --- We are in the Visible Display Area (Lines 64 to 255) ---
|
||||
int y = scanline - 64;
|
||||
|
||||
// Handle Flash Phase (only increment once per frame when y == 0)
|
||||
if (y == 0) _ulaFrameCount++;
|
||||
bool invertFlashPhase = (_ulaFrameCount % 32) >= 16;
|
||||
|
||||
int rowStartIndex = renderY * ScreenWidth;
|
||||
|
||||
// Draw the 32 pixels of left border
|
||||
for (int b = 0; b < 32; b++) FrameBuffer[rowStartIndex + b] = currentBorderColor;
|
||||
|
||||
int third = y / 64;
|
||||
int characterRow = (y % 64) / 8;
|
||||
int pixelRow = y % 8;
|
||||
|
||||
// Draw the 32 horizontal character blocks of the visible screen
|
||||
for (int col = 0; col < 32; col++)
|
||||
{
|
||||
ushort pixelAddress = (ushort)(0x4000 | (third << 11) | (pixelRow << 8) | (characterRow << 5) | col);
|
||||
ushort attrAddress = (ushort)(0x5800 + (y / 8) * 32 + col);
|
||||
|
||||
byte pixels = _memoryBus.Read(pixelAddress);
|
||||
byte attr = _memoryBus.Read(attrAddress);
|
||||
|
||||
int ink = attr & 0x07;
|
||||
int paper = (attr >> 3) & 0x07;
|
||||
int brightOffset = (attr & 0x40) != 0 ? 8 : 0;
|
||||
bool isFlashSet = (attr & 0x80) != 0;
|
||||
|
||||
int inkColor = SpectrumColors[ink + brightOffset];
|
||||
int paperColor = SpectrumColors[paper + brightOffset];
|
||||
|
||||
if (isFlashSet && invertFlashPhase)
|
||||
{
|
||||
int temp = inkColor; inkColor = paperColor; paperColor = temp;
|
||||
}
|
||||
|
||||
// Draw the 8 pixels in this block
|
||||
for (int bit = 0; bit < 8; bit++)
|
||||
{
|
||||
bool isPixelSet = (pixels & (1 << (7 - bit))) != 0;
|
||||
int renderX = 32 + (col * 8) + bit;
|
||||
FrameBuffer[rowStartIndex + renderX] = isPixelSet ? inkColor : paperColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the 32 pixels of right border
|
||||
for (int b = ScreenWidth - 32; b < ScreenWidth; b++)
|
||||
{
|
||||
FrameBuffer[rowStartIndex + b] = currentBorderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user