3 ZEXALL tests now pass. Removed timing to speed up (temp)
This commit is contained in:
261
Core/Cpu/Z80.cs
261
Core/Cpu/Z80.cs
@@ -445,6 +445,34 @@ namespace Core.Cpu
|
||||
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);
|
||||
@@ -702,10 +730,67 @@ namespace Core.Cpu
|
||||
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;
|
||||
result = a + operand;
|
||||
int result = a + operand; // Use a local int to easily catch the carry
|
||||
|
||||
AF.High = (byte)result;
|
||||
|
||||
@@ -728,8 +813,49 @@ namespace Core.Cpu
|
||||
|
||||
// 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;
|
||||
@@ -805,6 +931,8 @@ namespace Core.Cpu
|
||||
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;
|
||||
@@ -813,9 +941,11 @@ namespace Core.Cpu
|
||||
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;
|
||||
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
|
||||
@@ -824,6 +954,7 @@ namespace Core.Cpu
|
||||
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 ---
|
||||
@@ -849,19 +980,6 @@ namespace Core.Cpu
|
||||
case 0x06: // LD B, n
|
||||
BC.High = FetchByte();
|
||||
return 7;
|
||||
// --- ADD HL, rr (16-bit Addition) ---
|
||||
case 0x09:
|
||||
Add16(BC.Word);
|
||||
return 11;
|
||||
case 0x19:
|
||||
Add16(DE.Word);
|
||||
return 11;
|
||||
case 0x29:
|
||||
Add16(HL.Word); // This perfectly multiplies HL by 2!
|
||||
return 11;
|
||||
case 0x39:
|
||||
Add16(SP);
|
||||
return 11;
|
||||
case 0x0B: // DEC BC
|
||||
BC.Word--;
|
||||
return 6;
|
||||
@@ -1588,9 +1706,6 @@ namespace Core.Cpu
|
||||
|
||||
switch (extendedOpcode)
|
||||
{
|
||||
case 0x42: // SBC HL, BC
|
||||
Sbc16(BC.Word);
|
||||
return 15;
|
||||
case 0x43: // LD (nn), BC
|
||||
ushort dest43 = FetchWord();
|
||||
WriteMemory(dest43, BC.Low);
|
||||
@@ -1626,10 +1741,7 @@ namespace Core.Cpu
|
||||
return 8; // 8 T-States
|
||||
case 0x47: // LD I, A
|
||||
I = AF.High;
|
||||
return 9;
|
||||
case 0x4A: // ADC HL, BC
|
||||
Adc16(BC.Word);
|
||||
return 15;
|
||||
return 9;
|
||||
case 0x4B: // LD BC, (nn)
|
||||
ushort src4B = FetchWord();
|
||||
BC.Low = ReadMemory(src4B);
|
||||
@@ -1638,9 +1750,6 @@ namespace Core.Cpu
|
||||
case 0x4D: // RETI Does not affect IFF1 or IFF2
|
||||
PC = Pop();
|
||||
return 14;
|
||||
case 0x52: // SBC HL, DE
|
||||
Sbc16(DE.Word);
|
||||
return 15;
|
||||
case 0x53: // LD (nn), DE
|
||||
ushort dest53 = FetchWord();
|
||||
WriteMemory(dest53, DE.Low);
|
||||
@@ -1677,9 +1786,6 @@ namespace Core.Cpu
|
||||
AF.Low = flags58;
|
||||
|
||||
return 12;
|
||||
case 0x5A: // ADC HL, DE
|
||||
Adc16(DE.Word);
|
||||
return 15;
|
||||
case 0x5B: // LD DE, (nn)
|
||||
ushort src5B = FetchWord();
|
||||
DE.Low = ReadMemory(src5B);
|
||||
@@ -1710,46 +1816,11 @@ namespace Core.Cpu
|
||||
AF.Low = newFlags;
|
||||
|
||||
return 9;
|
||||
case 0x62: // SBC HL, HL
|
||||
Sbc16(HL.Word);
|
||||
return 15;
|
||||
case 0x6A: // ADC HL, HL
|
||||
Adc16(HL.Word);
|
||||
return 15;
|
||||
case 0x72: // SBC HL, SP
|
||||
int carryIn = AF.Low & 0x01;
|
||||
int hlVal = HL.Word;
|
||||
int spVal = SP;
|
||||
|
||||
// Perform the full 16-bit subtraction including the carry flag
|
||||
result = hlVal - spVal - carryIn;
|
||||
|
||||
newFlags = 0;
|
||||
|
||||
// S Flag (Bit 7): Set if the 16-bit result is negative (Bit 15 is 1)
|
||||
if ((result & 0x8000) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if the full 16-bit result is exactly 0
|
||||
if ((result & 0xFFFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a borrow from Bit 11
|
||||
if (((hlVal & 0x0FFF) - (spVal & 0x0FFF) - carryIn) < 0) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Set on Overflow
|
||||
// Overflow happens if the signs of the operands are different,
|
||||
// AND the sign of the result is different from the original HL
|
||||
if ((((hlVal ^ spVal) & (hlVal ^ result)) & 0x8000) != 0) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always set to 1 for a subtraction
|
||||
newFlags |= 0x02;
|
||||
|
||||
// C Flag (Bit 0): Set if the total result underflows 0 (Borrow from Bit 15)
|
||||
if (result < 0) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
HL.Word = (ushort)result;
|
||||
|
||||
return 15; // 15 T-States
|
||||
// --- 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);
|
||||
@@ -1777,9 +1848,11 @@ namespace Core.Cpu
|
||||
case 0x79: // OUT (C), A
|
||||
_simpleIoBus.WritePort(BC.Word, AF.High);
|
||||
return 12;
|
||||
case 0x7A: // ADC HL, SP
|
||||
Adc16(SP);
|
||||
return 15;
|
||||
// --- 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();
|
||||
@@ -2386,6 +2459,15 @@ namespace Core.Cpu
|
||||
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);
|
||||
@@ -2517,36 +2599,9 @@ namespace Core.Cpu
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x19: // ADD IY, DE
|
||||
// 1. Perform the 16-bit addition using a 32-bit integer to catch the carry
|
||||
int result19 = IY.Word + DE.Word;
|
||||
|
||||
// 2. Update the Flags Register (F)
|
||||
// We start by stripping out N, H, C, and bits 3/5, but strictly PRESERVING S, Z, and P/V (0xC4)
|
||||
byte flags19 = (byte)(AF.Low & 0xC4);
|
||||
|
||||
// H: Set if there is a carry from bit 11
|
||||
if (((IY.Word & 0x0FFF) + (DE.Word & 0x0FFF)) > 0x0FFF)
|
||||
{
|
||||
flags19 |= 0x10;
|
||||
}
|
||||
|
||||
// C: Set if the result overflows 16 bits (carry from bit 15)
|
||||
if (result19 > 0xFFFF)
|
||||
{
|
||||
flags19 |= 0x01;
|
||||
}
|
||||
|
||||
// Undocumented bits 3 and 5 are copied directly from the high byte of the result
|
||||
flags19 |= (byte)((result19 >> 8) & 0x28);
|
||||
|
||||
// N (Subtract) is naturally left as 0 because of our initial bitmask
|
||||
AF.Low = flags19;
|
||||
|
||||
// 3. Store the clean 16-bit result back into IY
|
||||
IY.Word = (ushort)result19;
|
||||
|
||||
return 15;
|
||||
// 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;
|
||||
@@ -2554,6 +2609,8 @@ namespace Core.Cpu
|
||||
// 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();
|
||||
@@ -2609,6 +2666,8 @@ namespace Core.Cpu
|
||||
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();
|
||||
|
||||
@@ -1112,8 +1112,12 @@ namespace Desktop
|
||||
case 0xFD:
|
||||
{
|
||||
byte fdOpcode = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
if (fdOpcode == 0x19) // ADD IY, DE
|
||||
if (fdOpcode == 0x09) // ADD IY, BC
|
||||
{
|
||||
mnemonic = $"ADD IY, BC";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if (fdOpcode == 0x19) // ADD IY, DE
|
||||
{
|
||||
mnemonic = $"ADD IY, DE";
|
||||
instructionLength = 2;
|
||||
@@ -1124,6 +1128,11 @@ namespace Desktop
|
||||
mnemonic = $"LD IY, 0x{iyVal:X4}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (fdOpcode == 0x29) //Add IY, IY
|
||||
{
|
||||
mnemonic = $"ADD IY, IY";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if (fdOpcode == 0x34) // INC IY
|
||||
{
|
||||
//sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -1290,7 +1299,7 @@ namespace Desktop
|
||||
mnemonic = "POP IY";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if(fdOpcode == 0xE5)
|
||||
else if (fdOpcode == 0xE5)
|
||||
{
|
||||
mnemonic = "PUSH IY";
|
||||
instructionLength = 2;
|
||||
|
||||
@@ -102,12 +102,12 @@ namespace Desktop
|
||||
_tapManager.Update(elapsedTStates);
|
||||
|
||||
//Process audio at the correct time
|
||||
while (_cpu.TotalTStates >= (long)(audioSampleCount * 79.365))
|
||||
{
|
||||
bool finalAudioOutput = _simpleIoBus.BeeperState ^ _tapManager.EarBit;
|
||||
_beeper.AddSample(finalAudioOutput);
|
||||
audioSampleCount++;
|
||||
}
|
||||
//while (_cpu.TotalTStates >= (long)(audioSampleCount * 79.365))
|
||||
//{
|
||||
// bool finalAudioOutput = _simpleIoBus.BeeperState ^ _tapManager.EarBit;
|
||||
// _beeper.AddSample(finalAudioOutput);
|
||||
// audioSampleCount++;
|
||||
//}
|
||||
|
||||
// --- Check for End of Frame ---
|
||||
if (_cpu.TotalTStates >= nextScanlineTarget)
|
||||
@@ -131,13 +131,13 @@ namespace Desktop
|
||||
TotalFrameCount++;
|
||||
|
||||
// Throttle to real-time (50 FPS = 20ms)
|
||||
long targetTimeMs = (scanlineCount / 312) * 20;
|
||||
long elapsedMs = stopwatch.ElapsedMilliseconds;
|
||||
//long targetTimeMs = (scanlineCount / 312) * 20;
|
||||
//long elapsedMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
if (elapsedMs < targetTimeMs)
|
||||
{
|
||||
//Thread.Sleep((int)(targetTimeMs - elapsedMs));
|
||||
}
|
||||
//if (elapsedMs < targetTimeMs)
|
||||
//{
|
||||
// Thread.Sleep((int)(targetTimeMs - elapsedMs));
|
||||
//}
|
||||
TotalFrameTime += fpsStopwatch.Elapsed.TotalMilliseconds;
|
||||
if (TotalFrameCount % 50 == 0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user