Added flash attribute. Implemented more OpCodes
This commit is contained in:
266
Core/Cpu/Z80.cs
266
Core/Cpu/Z80.cs
@@ -449,6 +449,25 @@ namespace Core.Cpu
|
||||
if (result > 0xFF) AF.Low |= 0x01;
|
||||
}
|
||||
|
||||
private void AdcA(byte operand)
|
||||
{
|
||||
int aVal = AF.High;
|
||||
int carryIn = AF.Low & 0x01;
|
||||
|
||||
int result = aVal + operand + carryIn;
|
||||
|
||||
byte newFlags = 0;
|
||||
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80; // S Flag
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40; // Z Flag
|
||||
if (((aVal & 0x0F) + (operand & 0x0F) + carryIn) > 0x0F) newFlags |= 0x10; // H Flag
|
||||
if ((((aVal ^ ~operand) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04; // P/V Flag
|
||||
if (result > 0xFF) newFlags |= 0x01; // C Flag
|
||||
|
||||
AF.Low = newFlags;
|
||||
AF.High = (byte)result;
|
||||
}
|
||||
|
||||
private void AddA(byte operand)
|
||||
{
|
||||
byte a = AF.High;
|
||||
@@ -527,6 +546,25 @@ namespace Core.Cpu
|
||||
return 6;
|
||||
// --- 8-Bit Increments ---
|
||||
case 0x04: BC.High = Inc8(BC.High); return 4; // INC B
|
||||
case 0x07: // RLCA
|
||||
// 1. Grab the top bit (Bit 7) before it rotates
|
||||
byte topBit = (byte)(AF.High >> 7);
|
||||
|
||||
// 2. Shift A left by 1, and drop the top bit into the newly empty Bit 0
|
||||
AF.High = (byte)((AF.High << 1) | topBit);
|
||||
|
||||
// --- RLCA Specific Flag Rules ---
|
||||
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2).
|
||||
byte newFlags = (byte)(AF.Low & 0xC4);
|
||||
|
||||
// H (Bit 4) and N (Bit 1) are forcefully reset to 0 (which the bitwise AND above just did).
|
||||
|
||||
// C (Bit 0): Set if the bit that rotated off the top was a 1
|
||||
if (topBit != 0) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
return 4; // 4 T-States
|
||||
case 0x08: // EX AF, AF'
|
||||
ushort tempAF = AF.Word;
|
||||
AF.Word = AF_Prime.Word;
|
||||
@@ -856,6 +894,24 @@ namespace Core.Cpu
|
||||
case 0x85: Add(HL.Low); return 4; // ADD A, L
|
||||
case 0x86: Add(_memory.Read(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
|
||||
case 0x89: AdcA(BC.Low); return 4; // ADC A, C
|
||||
case 0x8A: AdcA(DE.High); return 4; // ADC A, D
|
||||
case 0x8B: AdcA(DE.Low); return 4; // ADC A, E
|
||||
case 0x8C: AdcA(HL.High); return 4; // ADC A, H
|
||||
case 0x8D: AdcA(HL.Low); return 4; // ADC A, L
|
||||
case 0x8F: AdcA(AF.High); return 4; // ADC A, A
|
||||
|
||||
// --- ADC A, Memory ---
|
||||
case 0x8E: // ADC A, (HL)
|
||||
AdcA(_memory.Read(HL.Word));
|
||||
return 7;
|
||||
|
||||
// --- ADC A, Immediate ---
|
||||
case 0xCE: // ADC A, n
|
||||
AdcA(FetchByte());
|
||||
return 7;
|
||||
case 0x90: Sub(BC.High); return 4; // SUB B
|
||||
case 0x91: Sub(BC.Low); return 4; // SUB C
|
||||
case 0x92: Sub(DE.High); return 4; // SUB D
|
||||
@@ -1149,6 +1205,23 @@ namespace Core.Cpu
|
||||
return 4; // Takes 4 T-States
|
||||
case 0xED:
|
||||
return ExecuteExtendedPrefix();
|
||||
case 0xEE: // XOR n
|
||||
byte xorImm = FetchByte();
|
||||
|
||||
// Perform the bitwise XOR against the Accumulator
|
||||
AF.High = (byte)(AF.High ^ xorImm);
|
||||
|
||||
// --- Update Flags ---
|
||||
// Start with a clean slate of 0 to perfectly clear C, H, and N!
|
||||
newFlags = 0;
|
||||
|
||||
if ((AF.High & 0x80) != 0) newFlags |= 0x80; // Sign Flag
|
||||
if (AF.High == 0) newFlags |= 0x40; // Zero Flag
|
||||
if (CalculateParity(AF.High)) newFlags |= 0x04; // Parity/Overflow Flag
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
return 7; // Takes 7 T-States
|
||||
case 0xF1: // POP AF
|
||||
AF.Word = Pop();
|
||||
return 10;
|
||||
@@ -1183,6 +1256,8 @@ namespace Core.Cpu
|
||||
// Fetch the actual extended instruction
|
||||
byte extendedOpcode = _memory.Read(PC++);
|
||||
byte val = 0;
|
||||
byte newFlags = 0;
|
||||
int result = 0;
|
||||
|
||||
switch (extendedOpcode)
|
||||
{
|
||||
@@ -1191,6 +1266,34 @@ namespace Core.Cpu
|
||||
_memory.Write(dest43, BC.Low);
|
||||
_memory.Write((ushort)(dest43 + 1), BC.High);
|
||||
return 20;
|
||||
case 0x44: // NEG
|
||||
int aBefore = AF.High;
|
||||
result = 0 - aBefore;
|
||||
|
||||
newFlags = 0;
|
||||
|
||||
// S Flag (Bit 7): Set if the result is negative
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if the result is exactly zero
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||
if ((0 - (aBefore & 0x0F)) < 0) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Overflow happens ONLY if A was 0x80 (-128)
|
||||
if (aBefore == 0x80) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always set to 1 for NEG
|
||||
newFlags |= 0x02;
|
||||
|
||||
// C Flag (Bit 0): Set if A was not 0 before the operation
|
||||
if (aBefore != 0) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
AF.High = (byte)result;
|
||||
|
||||
return 8; // 8 T-States
|
||||
case 0x47: // LD I, A
|
||||
I = AF.High;
|
||||
return 9;
|
||||
@@ -1215,6 +1318,40 @@ namespace Core.Cpu
|
||||
DE.Low = _memory.Read(src5B);
|
||||
DE.High = _memory.Read((ushort)(src5B + 1));
|
||||
return 20;
|
||||
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
|
||||
case 0x73: // LD (nn), SP
|
||||
ushort dest73 = FetchWord();
|
||||
_memory.Write(dest73, (byte)SP);
|
||||
@@ -1230,7 +1367,7 @@ namespace Core.Cpu
|
||||
// H (Bit 4) and N (Bit 1) are RESET.
|
||||
// C (Bit 0) is PRESERVED.
|
||||
|
||||
byte newFlags = (byte)(AF.Low & 0x01); // Preserve Carry
|
||||
newFlags = (byte)(AF.Low & 0x01); // Preserve Carry
|
||||
|
||||
if ((portVal78 & 0x80) != 0) newFlags |= 0x80; // Sign Flag
|
||||
if (portVal78 == 0) newFlags |= 0x40; // Zero Flag
|
||||
@@ -1238,7 +1375,21 @@ namespace Core.Cpu
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
return 12; // Takes 12 T-States
|
||||
return 12;
|
||||
case 0x7B: // LD SP, (nn)
|
||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||
byte addrLow = FetchByte();
|
||||
byte addrHigh = FetchByte();
|
||||
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));
|
||||
|
||||
// 3. Load the resulting 16-bit value directly into the Stack Pointer
|
||||
SP = (ushort)((spHigh << 8) | spLow);
|
||||
|
||||
return 20;
|
||||
case 0xB0: // LDIR
|
||||
// 1. Read byte from (HL)
|
||||
val = _memory.Read(HL.Word);
|
||||
@@ -1418,12 +1569,36 @@ namespace Core.Cpu
|
||||
|
||||
switch (ddOpcode)
|
||||
{
|
||||
case 0x09: // ADD IX, BC
|
||||
int result = IX.Word + BC.Word;
|
||||
|
||||
// --- 16-Bit Flag Calculation ---
|
||||
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2).
|
||||
byte newFlags = (byte)(AF.Low & 0xC4);
|
||||
|
||||
// Half-Carry (H - Bit 4): Set if carry from Bit 11
|
||||
if (((IX.Word & 0x0FFF) + (BC.Word & 0x0FFF)) > 0x0FFF)
|
||||
newFlags |= 0x10;
|
||||
|
||||
// Carry (C - Bit 0): Set if the total result overflows 16 bits
|
||||
if (result > 0xFFFF)
|
||||
newFlags |= 0x01;
|
||||
|
||||
// (N - Bit 1 is left at 0 because the bitwise AND above cleared it)
|
||||
|
||||
AF.Low = newFlags;
|
||||
IX.Word = (ushort)result;
|
||||
|
||||
return 15;
|
||||
case 0x21: // LD IX, nn
|
||||
byte low = FetchByte();
|
||||
byte high = FetchByte();
|
||||
IX.Word = (ushort)((high << 8) | low);
|
||||
|
||||
return 14; // 14 T-States
|
||||
return 14;
|
||||
case 0xE9: // JP (IX)
|
||||
PC = IX.Word;
|
||||
return 8;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"DD Prefix opcode 0x{ddOpcode:X2} not implemented!");
|
||||
@@ -1441,6 +1616,43 @@ namespace Core.Cpu
|
||||
case 0x21: // LD IY, nn
|
||||
IY.Word = FetchWord();
|
||||
return 14;
|
||||
case 0x34: // INC (IY+d)
|
||||
// 1. Fetch displacement and calculate memory address
|
||||
sbyte offset34 = (sbyte)FetchByte();
|
||||
ushort address34 = (ushort)(IY.Word + offset34);
|
||||
|
||||
// 2. Read the value from memory
|
||||
byte valBefore = _memory.Read(address34);
|
||||
|
||||
// 3. Increment the value
|
||||
int result = valBefore + 1;
|
||||
|
||||
// --- 8-Bit Increment Flag Calculation ---
|
||||
// CRITICAL: We must preserve the existing Carry flag (Bit 0)!
|
||||
byte newFlags = (byte)(AF.Low & 0x01);
|
||||
|
||||
// S Flag (Bit 7): Set if the result is negative
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if the result is exactly zero (wrapped from 255 to 0)
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a carry out of Bit 3
|
||||
if ((valBefore & 0x0F) == 0x0F) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Set on Overflow
|
||||
// For INC, overflow ONLY happens if we increment 0x7F (+127) and it wraps to 0x80 (-128)
|
||||
if (valBefore == 0x7F) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always reset to 0 for an increment
|
||||
// (Our bitwise AND at the top already cleared it)
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
// 4. Write the modified value back to memory
|
||||
_memory.Write(address34, (byte)result);
|
||||
|
||||
return 23; // 23 T-States
|
||||
case 0x35: // DEC (IY+d)
|
||||
sbyte offset = (sbyte)FetchByte();
|
||||
targetAddress = (ushort)(IY.Word + offset);
|
||||
@@ -1515,6 +1727,16 @@ namespace Core.Cpu
|
||||
_memory.Write(targetAddress, BC.Low);
|
||||
return 19; // Takes 19 T-States
|
||||
}
|
||||
case 0x74: // LD (IY+d), H
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset74 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IY + offset)
|
||||
ushort address74 = (ushort)(IY.Word + offset74);
|
||||
|
||||
// 3. Write the contents of the H register into memory at that address
|
||||
_memory.Write(address74, HL.High);
|
||||
return 19;
|
||||
case 0x75: // LD (IY+d), L
|
||||
sbyte offset75 = (sbyte)FetchByte();
|
||||
targetAddress = (ushort)(IY.Word + offset75);
|
||||
@@ -1531,6 +1753,44 @@ namespace Core.Cpu
|
||||
|
||||
return 19;
|
||||
}
|
||||
case 0x96: // SUB (IY+d)
|
||||
// 1. Fetch the displacement byte and calculate the address
|
||||
sbyte offset96 = (sbyte)FetchByte();
|
||||
ushort address96 = (ushort)(IY.Word + offset96);
|
||||
|
||||
// 2. Read the value from memory
|
||||
byte subVal = _memory.Read(address96);
|
||||
|
||||
// 3. Perform the subtraction from the Accumulator
|
||||
int aVal = AF.High;
|
||||
result = aVal - subVal;
|
||||
|
||||
// --- 8-Bit Subtraction Flag Calculation ---
|
||||
newFlags = 0;
|
||||
|
||||
// S Flag (Bit 7): Set if the result is negative
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if the result is exactly zero
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||
if (((aVal & 0x0F) - (subVal & 0x0F)) < 0) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Set on Overflow
|
||||
// (Happens if subtracting a negative from a positive gives a negative, or vice versa)
|
||||
if ((((aVal ^ subVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always set to 1 for a subtraction
|
||||
newFlags |= 0x02;
|
||||
|
||||
// C Flag (Bit 0): Set if a borrow was needed (Accumulator was smaller than memory value)
|
||||
if (aVal < subVal) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
AF.High = (byte)result;
|
||||
|
||||
return 19;
|
||||
case 0xCB: // The FD CB nested prefix
|
||||
{
|
||||
sbyte displacement = (sbyte)FetchByte();
|
||||
|
||||
@@ -582,7 +582,18 @@ namespace Desktop
|
||||
case 0x85: mnemonic = "ADD A, L"; break;
|
||||
case 0x86: mnemonic = "ADD A, (HL)"; break;
|
||||
case 0x87: mnemonic = "ADD A, A"; break;
|
||||
|
||||
case 0x88:
|
||||
case 0x89:
|
||||
case 0x8A:
|
||||
case 0x8B:
|
||||
case 0x8C:
|
||||
case 0x8D:
|
||||
case 0x8E:
|
||||
case 0x8F:
|
||||
string[] registers = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
|
||||
mnemonic = $"ADC A, {registers[opcode - 0x88]}";
|
||||
instructionLength = 1;
|
||||
break;
|
||||
// --- SUB r ---
|
||||
case 0x90: mnemonic = "SUB B"; break;
|
||||
case 0x91: mnemonic = "SUB C"; break;
|
||||
@@ -779,10 +790,15 @@ namespace Desktop
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xCD:
|
||||
ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"CALL 0x{callDest:X4}";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"CALL 0x{callDest:X4}";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
case 0xCE: // ADC A, n
|
||||
byte n = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"ADC A, 0x{n:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xD0:
|
||||
mnemonic = "RET NC";
|
||||
break;
|
||||
@@ -809,13 +825,22 @@ namespace Desktop
|
||||
case 0xDD:
|
||||
{
|
||||
byte ddOpcode = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
if (ddOpcode == 0x21) // LD IX, nn
|
||||
if (ddOpcode == 0x09) // ADD IX, BC
|
||||
{
|
||||
mnemonic = "ADD IX, BC";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if (ddOpcode == 0x21) // LD IX, nn
|
||||
{
|
||||
ushort ixVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD IX, 0x{ixVal:X4}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (ddOpcode == 0xE9) // JP (IX)
|
||||
{
|
||||
mnemonic = "JP (IX)";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
mnemonic = $"DD PREFIX UNKNOWN (0x{ddOpcode:X2})";
|
||||
@@ -856,6 +881,10 @@ namespace Desktop
|
||||
mnemonic = $"LD (0x{bcAddr:X4}), BC";
|
||||
instructionLength = 4;
|
||||
break;
|
||||
case 0x44: // NEG
|
||||
mnemonic = "NEG";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x47:
|
||||
mnemonic = "LD I, A";
|
||||
instructionLength = 2; // 0xED + 0x47
|
||||
@@ -888,10 +917,19 @@ namespace Desktop
|
||||
mnemonic = $"LD (0x{addr73:X4}), SP";
|
||||
instructionLength = 4;
|
||||
break;
|
||||
case 0x72: // SBC HL, SP
|
||||
mnemonic = "SBC HL, SP";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x78:
|
||||
mnemonic = "IN A, (C)";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x7B: // LD SP, (nn)
|
||||
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD SP, (0x{nn:X4})";
|
||||
instructionLength = 4;
|
||||
break;
|
||||
case 0xB0:
|
||||
mnemonic = "LDIR";
|
||||
instructionLength = 2;
|
||||
@@ -907,6 +945,11 @@ namespace Desktop
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xEE:
|
||||
byte xorVal = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"XOR 0x{xorVal:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xF1: mnemonic = "POP AF"; break;
|
||||
case 0xF3:
|
||||
mnemonic = "DI";
|
||||
@@ -935,6 +978,13 @@ namespace Desktop
|
||||
mnemonic = $"LD IY, 0x{iyVal:X4}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (fdOpcode == 0x34) // INC (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"INC (IY{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x35) // DEC (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -945,7 +995,7 @@ namespace Desktop
|
||||
else if (fdOpcode == 0x36) // LD (IY+d), n
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
byte n = _memoryBus.Read((ushort)(currentPc + 3));
|
||||
n = _memoryBus.Read((ushort)(currentPc + 3));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}";
|
||||
instructionLength = 4;
|
||||
@@ -987,6 +1037,13 @@ namespace Desktop
|
||||
mnemonic = $"LD L, (IY{signL}{offsetL})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x74) // LD (IY+d), H
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD (IY{sign}{d}), H";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x86) // ADD A, (IY+d)
|
||||
{
|
||||
sbyte dAdd = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -995,6 +1052,13 @@ namespace Desktop
|
||||
mnemonic = $"ADD A, (IY{signAdd}{dAdd})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x96) // SUB (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"SUB (IY{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0xCB) // FD CB prefix
|
||||
{
|
||||
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Desktop
|
||||
private Z80 _cpu = null!;
|
||||
private MemoryBus _memoryBus = null!;
|
||||
private IO_Bus _simpleIoBus = null!;
|
||||
private int _ulaFrameCount = 0;
|
||||
|
||||
// The 16 physical colors of the ZX Spectrum (ARGB format)
|
||||
private readonly int[] SpectrumColors = new int[]
|
||||
@@ -71,6 +72,10 @@ namespace Desktop
|
||||
// Public so the Debugger's background thread can call it 50 times a second
|
||||
public void RenderScreen()
|
||||
{
|
||||
_ulaFrameCount++;
|
||||
// The Spectrum flashes every 16 frames.
|
||||
// This boolean flips between true and false every 16 frames.
|
||||
bool invertFlashPhase = (_ulaFrameCount % 32) >= 16;
|
||||
int[] pixelData = new int[256 * 192];
|
||||
|
||||
// Loop through the 6144 bytes of Pixel RAM
|
||||
@@ -95,10 +100,18 @@ namespace Desktop
|
||||
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)
|
||||
{
|
||||
// Swap the ink and paper colors for this character block!
|
||||
int temp = inkColor;
|
||||
inkColor = paperColor;
|
||||
paperColor = temp;
|
||||
}
|
||||
|
||||
// Draw the 8 pixels
|
||||
for (int bit = 0; bit < 8; bit++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user