Implemented a load more Z80 OpCodes
This commit is contained in:
146
Core/Cpu/Z80.cs
146
Core/Cpu/Z80.cs
@@ -127,8 +127,121 @@ namespace Core.Cpu
|
|||||||
if (result < 0) AF.Low |= 0x01;
|
if (result < 0) AF.Low |= 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Sbc16(ushort value)
|
||||||
|
{
|
||||||
|
int hl = HL.Word;
|
||||||
|
int carry = AF.Low & 0x01;
|
||||||
|
|
||||||
|
// Calculate the raw integer result to check for underflows
|
||||||
|
int result = hl - value - carry;
|
||||||
|
|
||||||
|
// Update the HL register
|
||||||
|
HL.Word = (ushort)result;
|
||||||
|
|
||||||
|
// --- Update Flags (F Register) ---
|
||||||
|
AF.Low = 0; // Clear all flags
|
||||||
|
|
||||||
|
// Sign Flag (Bit 7) - Set if the 16-bit result is negative (bit 15 is 1)
|
||||||
|
if ((result & 0x8000) != 0) AF.Low |= 0x80;
|
||||||
|
|
||||||
|
// Zero Flag (Bit 6) - Set if the entire 16-bit result is exactly 0
|
||||||
|
if ((ushort)result == 0) AF.Low |= 0x40;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - Set if borrow from bit 11
|
||||||
|
if (((hl & 0x0FFF) - (value & 0x0FFF) - carry) < 0) AF.Low |= 0x10;
|
||||||
|
|
||||||
|
// Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes
|
||||||
|
if ((((hl ^ value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) AF.Low |= 0x04;
|
||||||
|
|
||||||
|
// Subtract Flag (Bit 1) - ALWAYS set for subtraction
|
||||||
|
AF.Low |= 0x02;
|
||||||
|
|
||||||
|
// Carry Flag (Bit 0) - Set if the overall 16-bit result dropped below 0
|
||||||
|
if (result < 0) AF.Low |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cp(byte value)
|
||||||
|
{
|
||||||
|
byte a = AF.High;
|
||||||
|
int result = a - value;
|
||||||
|
|
||||||
|
// --- Update Flags (F Register) ---
|
||||||
|
AF.Low = 0; // Clear all flags
|
||||||
|
|
||||||
|
// Sign Flag (Bit 7)
|
||||||
|
if ((result & 0x80) != 0) AF.Low |= 0x80;
|
||||||
|
|
||||||
|
// Zero Flag (Bit 6)
|
||||||
|
if ((byte)result == 0) AF.Low |= 0x40;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - Set if borrow from bit 4
|
||||||
|
if (((a & 0x0F) - (value & 0x0F)) < 0) AF.Low |= 0x10;
|
||||||
|
|
||||||
|
// Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes
|
||||||
|
if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04;
|
||||||
|
|
||||||
|
// Subtract Flag (Bit 1) - ALWAYS set for CP/SUB
|
||||||
|
AF.Low |= 0x02;
|
||||||
|
|
||||||
|
// Carry Flag (Bit 0) - Set if the overall result dropped below 0
|
||||||
|
if (result < 0) AF.Low |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void And(byte value)
|
||||||
|
{
|
||||||
|
AF.High = (byte)(AF.High & value);
|
||||||
|
|
||||||
|
// --- Update Flags ---
|
||||||
|
AF.Low = 0; // Clear all flags
|
||||||
|
|
||||||
|
// Sign Flag (Bit 7) - Set if the highest bit is 1
|
||||||
|
if ((AF.High & 0x80) != 0) AF.Low |= 0x80;
|
||||||
|
|
||||||
|
// Zero Flag (Bit 6) - Set if the result is 0
|
||||||
|
if (AF.High == 0) AF.Low |= 0x40;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - ALWAYS SET to 1 for Z80 AND instructions!
|
||||||
|
AF.Low |= 0x10;
|
||||||
|
|
||||||
|
// Parity Flag (Bit 2) - Set if the result has an even number of 1 bits
|
||||||
|
if (HasEvenParity(AF.High)) AF.Low |= 0x04;
|
||||||
|
|
||||||
|
// Subtract Flag (N) and Carry Flag (C) are ALWAYS 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Add16(ushort value)
|
||||||
|
{
|
||||||
|
int hl = HL.Word;
|
||||||
|
int result = hl + value;
|
||||||
|
|
||||||
|
// Update the HL register
|
||||||
|
HL.Word = (ushort)result;
|
||||||
|
|
||||||
|
// --- Update Flags (F Register) ---
|
||||||
|
// 16-bit ADD preserves S, Z, P/V (and the undocumented X/Y flags).
|
||||||
|
// We clear H (Bit 4), N (Bit 1), and C (Bit 0) using a bitwise AND mask (0xEC = 1110 1100)
|
||||||
|
AF.Low &= 0xEC;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - Set if there is a carry from bit 11
|
||||||
|
if (((hl & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) AF.Low |= 0x10;
|
||||||
|
|
||||||
|
// Carry Flag (Bit 0) - Set if the result overflows 16 bits
|
||||||
|
if (result > 0xFFFF) AF.Low |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasEvenParity(byte value)
|
||||||
|
{
|
||||||
|
int bits = 0;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if ((value & (1 << i)) != 0) bits++;
|
||||||
|
}
|
||||||
|
return (bits % 2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
private int ExecuteOpcode(byte opcode)
|
private int ExecuteOpcode(byte opcode)
|
||||||
{
|
{
|
||||||
|
sbyte offset = 0;
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case 0x00: // NOP
|
case 0x00: // NOP
|
||||||
@@ -136,9 +249,33 @@ namespace Core.Cpu
|
|||||||
case 0x11: //LD DE, nn
|
case 0x11: //LD DE, nn
|
||||||
DE.Word = FetchWord();
|
DE.Word = FetchWord();
|
||||||
return 10;
|
return 10;
|
||||||
|
case 0x19: // ADD HL, DE
|
||||||
|
Add16(DE.Word);
|
||||||
|
return 11;
|
||||||
|
case 0x20: // JR NZ, e
|
||||||
|
offset = (sbyte)FetchByte();
|
||||||
|
if ((AF.Low & 0x40) == 0)
|
||||||
|
{
|
||||||
|
PC = (ushort)(PC + offset);
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
return 7;
|
||||||
|
case 0x23: // INC HL
|
||||||
|
HL.Word++;
|
||||||
|
return 6;
|
||||||
case 0x2B: // DEC HL
|
case 0x2B: // DEC HL
|
||||||
HL.Word--;
|
HL.Word--;
|
||||||
return 6;
|
return 6;
|
||||||
|
case 0x30: // JR NC, e
|
||||||
|
offset = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// Check if the Carry Flag (Bit 0) is NOT set
|
||||||
|
if ((AF.Low & 0x01) == 0)
|
||||||
|
{
|
||||||
|
PC = (ushort)(PC + offset);
|
||||||
|
return 12; // Jump taken
|
||||||
|
}
|
||||||
|
return 7; // Jump not taken
|
||||||
case 0x36: // LD (HL), n
|
case 0x36: // LD (HL), n
|
||||||
byte nValue = FetchByte();
|
byte nValue = FetchByte();
|
||||||
_memory.Write(HL.Word, nValue);
|
_memory.Write(HL.Word, nValue);
|
||||||
@@ -155,6 +292,12 @@ namespace Core.Cpu
|
|||||||
case 0x6B: // LD L, E
|
case 0x6B: // LD L, E
|
||||||
HL.Low = DE.Low;
|
HL.Low = DE.Low;
|
||||||
return 4;
|
return 4;
|
||||||
|
case 0xA7: // AND A
|
||||||
|
And(AF.High);
|
||||||
|
return 4;
|
||||||
|
case 0xBC: // CP H
|
||||||
|
Cp(HL.High);
|
||||||
|
return 4;
|
||||||
case 0xC3:
|
case 0xC3:
|
||||||
PC = FetchWord();
|
PC = FetchWord();
|
||||||
return 10;
|
return 10;
|
||||||
@@ -195,6 +338,9 @@ namespace Core.Cpu
|
|||||||
case 0x47: // LD I, A
|
case 0x47: // LD I, A
|
||||||
I = AF.High;
|
I = AF.High;
|
||||||
return 9;
|
return 9;
|
||||||
|
case 0x52: // SBC HL, DE
|
||||||
|
Sbc16(DE.Word);
|
||||||
|
return 15; // Takes 15 T-States
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||||
}
|
}
|
||||||
|
|||||||
10
Desktop/DebuggerForm.Designer.cs
generated
10
Desktop/DebuggerForm.Designer.cs
generated
@@ -182,19 +182,19 @@
|
|||||||
// lstDisassembly
|
// lstDisassembly
|
||||||
//
|
//
|
||||||
lstDisassembly.FormattingEnabled = true;
|
lstDisassembly.FormattingEnabled = true;
|
||||||
lstDisassembly.Location = new Point(533, 11);
|
lstDisassembly.Location = new Point(523, 11);
|
||||||
lstDisassembly.Margin = new Padding(2);
|
lstDisassembly.Margin = new Padding(2);
|
||||||
lstDisassembly.Name = "lstDisassembly";
|
lstDisassembly.Name = "lstDisassembly";
|
||||||
lstDisassembly.Size = new Size(186, 264);
|
lstDisassembly.Size = new Size(252, 264);
|
||||||
lstDisassembly.TabIndex = 16;
|
lstDisassembly.TabIndex = 16;
|
||||||
//
|
//
|
||||||
// lstStack
|
// lstStack
|
||||||
//
|
//
|
||||||
lstStack.FormattingEnabled = true;
|
lstStack.FormattingEnabled = true;
|
||||||
lstStack.Location = new Point(723, 11);
|
lstStack.Location = new Point(779, 11);
|
||||||
lstStack.Margin = new Padding(2);
|
lstStack.Margin = new Padding(2);
|
||||||
lstStack.Name = "lstStack";
|
lstStack.Name = "lstStack";
|
||||||
lstStack.Size = new Size(186, 264);
|
lstStack.Size = new Size(130, 264);
|
||||||
lstStack.TabIndex = 17;
|
lstStack.TabIndex = 17;
|
||||||
//
|
//
|
||||||
// btnExit
|
// btnExit
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(927, 350);
|
ClientSize = new Size(928, 350);
|
||||||
Controls.Add(btnExit);
|
Controls.Add(btnExit);
|
||||||
Controls.Add(lstStack);
|
Controls.Add(lstStack);
|
||||||
Controls.Add(lstDisassembly);
|
Controls.Add(lstDisassembly);
|
||||||
|
|||||||
@@ -208,9 +208,27 @@ namespace Desktop
|
|||||||
mnemonic = $"LD DE, 0x{deHigh:X2}{deLow:X2}";
|
mnemonic = $"LD DE, 0x{deHigh:X2}{deLow:X2}";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
break;
|
break;
|
||||||
|
case 0x19:
|
||||||
|
mnemonic = "ADD HL, DE";
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||||
|
ushort destination = (ushort)(currentPc + 2 + jrOffset);
|
||||||
|
mnemonic = $"JR NZ, 0x{destination:X4}";
|
||||||
|
instructionLength = 2;
|
||||||
|
break;
|
||||||
|
case 0x23:
|
||||||
|
mnemonic = "INC HL";
|
||||||
|
break;
|
||||||
case 0x2B:
|
case 0x2B:
|
||||||
mnemonic = "DEC HL";
|
mnemonic = "DEC HL";
|
||||||
break;
|
break;
|
||||||
|
case 0x30:
|
||||||
|
sbyte jrNcOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||||
|
ushort dest = (ushort)(currentPc + 2 + jrNcOffset);
|
||||||
|
mnemonic = $"JR NC, 0x{dest:X4}";
|
||||||
|
instructionLength = 2;
|
||||||
|
break;
|
||||||
case 0x36:
|
case 0x36:
|
||||||
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
||||||
@@ -229,9 +247,15 @@ namespace Desktop
|
|||||||
case 0x6B:
|
case 0x6B:
|
||||||
mnemonic = "LD L, E";
|
mnemonic = "LD L, E";
|
||||||
break;
|
break;
|
||||||
|
case 0xA7:
|
||||||
|
mnemonic = "AND A";
|
||||||
|
break;
|
||||||
case 0xAF:
|
case 0xAF:
|
||||||
mnemonic = "XOR A";
|
mnemonic = "XOR A";
|
||||||
break;
|
break;
|
||||||
|
case 0xBC:
|
||||||
|
mnemonic = "CP H";
|
||||||
|
break;
|
||||||
case 0xC3:
|
case 0xC3:
|
||||||
// JP nn
|
// JP nn
|
||||||
byte jpLow = _memoryBus.Read((ushort)(currentPc + 1));
|
byte jpLow = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
@@ -259,7 +283,10 @@ namespace Desktop
|
|||||||
mnemonic = "LD I, A";
|
mnemonic = "LD I, A";
|
||||||
instructionLength = 2; // 0xED + 0x47
|
instructionLength = 2; // 0xED + 0x47
|
||||||
break;
|
break;
|
||||||
|
case 0x52:
|
||||||
|
mnemonic = "SBC HL, DE";
|
||||||
|
instructionLength = 2; // ED 52
|
||||||
|
break;
|
||||||
// Example: ED B0 is LDIR (a massive block copy instruction)
|
// Example: ED B0 is LDIR (a massive block copy instruction)
|
||||||
case 0xB0:
|
case 0xB0:
|
||||||
mnemonic = "LDIR";
|
mnemonic = "LDIR";
|
||||||
|
|||||||
Reference in New Issue
Block a user