3 ZEXALL tests now pass. Removed timing to speed up (temp)

This commit is contained in:
2026-04-23 12:34:25 +01:00
parent 02680cb92d
commit 112b6d15fe
3 changed files with 184 additions and 116 deletions

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)
{