Got Chuckie Egg TAP to load to the title screen!
This commit is contained in:
273
Core/Cpu/Z80.cs
273
Core/Cpu/Z80.cs
@@ -585,6 +585,7 @@ namespace Core.Cpu
|
||||
private int ExecuteOpcode(byte opcode)
|
||||
{
|
||||
sbyte offset = 0;
|
||||
byte oldCarry = 0;
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00: // NOP
|
||||
@@ -723,6 +724,24 @@ namespace Core.Cpu
|
||||
case 0x16: // LD D, n
|
||||
DE.High = FetchByte();
|
||||
return 7;
|
||||
case 0x17: // RLA
|
||||
// 1. Grab the current Carry flag (Bit 0 of AF.Low)
|
||||
oldCarry = (byte)(AF.Low & 0x01);
|
||||
|
||||
// 2. See if Bit 7 of the Accumulator is about to fall off
|
||||
bool newCarry = (AF.High & 0x80) != 0;
|
||||
|
||||
// 3. Shift A left, and drop the OLD carry directly into Bit 0
|
||||
AF.High = (byte)((AF.High << 1) | oldCarry);
|
||||
|
||||
// 4. Update the flags
|
||||
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2) while wiping H and N.
|
||||
AF.Low &= 0xC4;
|
||||
|
||||
// 5. Apply the new Carry flag if necessary
|
||||
if (newCarry) AF.Low |= 0x01;
|
||||
|
||||
return 4; // 4 T-States
|
||||
case 0x18: // JR d
|
||||
sbyte jumpDistance = (sbyte)FetchByte();
|
||||
|
||||
@@ -740,7 +759,7 @@ namespace Core.Cpu
|
||||
case 0x1F: // RRA
|
||||
{
|
||||
// 1. Grab the current Carry Flag (0 or 1)
|
||||
byte oldCarry = (byte)(AF.Low & 0x01);
|
||||
oldCarry = (byte)(AF.Low & 0x01);
|
||||
|
||||
// 2. Grab the bit that is about to fall off the Accumulator
|
||||
byte bit0 = (byte)(AF.High & 0x01);
|
||||
@@ -1583,6 +1602,14 @@ namespace Core.Cpu
|
||||
// Shift left, and loop the falling bit back into Bit 0
|
||||
val = (byte)((val << 1) | (carryOut ? 1 : 0));
|
||||
break;
|
||||
case 4: // SLA (Shift Left Arithmetic)
|
||||
// 1. Grab Bit 7 before it falls off to set the Carry flag
|
||||
carryOut = (val & 0x80) != 0;
|
||||
|
||||
// 2. Shift the byte left by 1.
|
||||
// (In C#, a standard left shift automatically pads Bit 0 with a 0)
|
||||
val = (byte)(val << 1);
|
||||
break;
|
||||
case 7: // SRL (Shift Right Logical)
|
||||
// 1. Grab Bit 0 before it falls off to set the Carry flag
|
||||
carryOut = (val & 0x01) != 0;
|
||||
@@ -1664,6 +1691,35 @@ namespace Core.Cpu
|
||||
IX.Word = (ushort)((high << 8) | low);
|
||||
|
||||
return 14;
|
||||
case 0x22: // LD (nn), IX
|
||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||
byte addrLow22 = FetchByte();
|
||||
byte addrHigh22 = FetchByte();
|
||||
ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22);
|
||||
|
||||
// 2. Write the LOW byte of IX to the exact address
|
||||
_memory.Write(address22, IX.Low);
|
||||
|
||||
// 3. Write the HIGH byte of IX to the address + 1
|
||||
_memory.Write((ushort)(address22 + 1), IX.High);
|
||||
|
||||
return 20;
|
||||
case 0x2A: // LD IX, (nn)
|
||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||
byte addrLow2A = FetchByte();
|
||||
byte addrHigh2A = FetchByte();
|
||||
ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A);
|
||||
|
||||
// 2. Read the LOW byte from that specific memory location
|
||||
byte ixLow = _memory.Read(address2A);
|
||||
|
||||
// 3. Read the HIGH byte from the next consecutive memory location
|
||||
byte ixHigh = _memory.Read((ushort)(address2A + 1));
|
||||
|
||||
// 4. Combine them and drop them into the IX register pair
|
||||
IX.Word = (ushort)((ixHigh << 8) | ixLow);
|
||||
|
||||
return 20; // 20 T-States
|
||||
case 0x36: // LD (IX+d), n
|
||||
// 1. Fetch the displacement byte first
|
||||
sbyte offset36 = (sbyte)FetchByte();
|
||||
@@ -1677,6 +1733,28 @@ namespace Core.Cpu
|
||||
// 4. Write the immediate value directly into memory
|
||||
_memory.Write(address36, n36);
|
||||
|
||||
return 19;
|
||||
case 0x46: // LD B, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset46 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
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);
|
||||
|
||||
return 19;
|
||||
case 0x4E: // LD C, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset4E = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
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);
|
||||
|
||||
return 19;
|
||||
case 0x56: // LD D, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
@@ -1699,6 +1777,17 @@ namespace Core.Cpu
|
||||
// 3. Read the byte from memory and drop it into the E register
|
||||
DE.Low = _memory.Read(address5E);
|
||||
|
||||
return 19;
|
||||
case 0x66: // LD H, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset66 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
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);
|
||||
|
||||
return 19;
|
||||
case 0x6E: // LD L, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
@@ -1711,6 +1800,7 @@ namespace Core.Cpu
|
||||
HL.Low = _memory.Read(address6E);
|
||||
|
||||
return 19;
|
||||
|
||||
case 0x74: // LD (IX+d), H
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset74 = (sbyte)FetchByte();
|
||||
@@ -2098,183 +2188,4 @@ namespace Core.Cpu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sbyte offsetCB = (sbyte)FetchByte(); // This is the '01'
|
||||
//byte bitOpcode = FetchByte(); // This is the 'CE'
|
||||
//targetAddress = (ushort)(IY.Word + offsetCB);
|
||||
|
||||
//switch (bitOpcode)
|
||||
//{
|
||||
// case 0x46: // BIT 0, (IY+d)
|
||||
// byte memValBit0 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve the existing Carry Flag (Bit 0)
|
||||
// byte newFlags = (byte)(AF.Low & 0x01);
|
||||
|
||||
// newFlags |= 0x10; // Force Half-Carry (Bit 4) to 1
|
||||
|
||||
// // Test Bit 0. If it is 0, turn ON the Zero Flag (Bit 6)
|
||||
// if ((memValBit0 & 0x01) == 0)
|
||||
// {
|
||||
// newFlags |= 0x40;
|
||||
// }
|
||||
|
||||
// AF.Low = newFlags;
|
||||
// return 20;
|
||||
// case 0x4E: // BIT 1, (IY+d)
|
||||
// {
|
||||
// byte memValBit = _memory.Read(targetAddress);
|
||||
|
||||
// // Check if bit 1 is 0
|
||||
// bool bitIsZero = (memValBit & 0x02) == 0;
|
||||
|
||||
// // Preserve the Carry flag (Bit 0), clear everything else
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// if (bitIsZero)
|
||||
// {
|
||||
// AF.Low |= 0x40; // Set Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x04; // Set P/V Flag (Bit 2)
|
||||
// }
|
||||
|
||||
// return 20;
|
||||
// }
|
||||
// case 0x66: // BIT 4, (IY+d)
|
||||
// byte memValBit4 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 4 (0x10 is Binary 0001 0000)
|
||||
// if ((memValBit4 & 0x10) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x6E: // BIT 5, (IY+d)
|
||||
// byte memValBit5 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 5 (0x20 is Binary 0010 0000)
|
||||
// if ((memValBit5 & 0x20) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x76: // BIT 6, (IY+d)
|
||||
// byte memValBit6 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 6 (0x40 is Binary 0100 0000)
|
||||
// if ((memValBit6 & 0x40) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x86: // RES 0, (IY+d)
|
||||
// byte memValRes0 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xFE is Binary 1111 1110.
|
||||
// // ANDing preserves all bits except Bit 0, which becomes 0.
|
||||
// memValRes0 &= 0xFE;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes0);
|
||||
// return 23; // Takes 23 T-States
|
||||
// case 0x8E: // RES 1, (IY+d)
|
||||
// byte memValRes = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xFD is Binary 1111 1101.
|
||||
// // ANDing with this preserves all bits except Bit 1, which becomes 0.
|
||||
// memValRes &= 0xFD;
|
||||
// _memory.Write(targetAddress, memValRes);
|
||||
// return 23;
|
||||
// case 0xA6: // RES 4, (IY+d)
|
||||
// byte memValRes4 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xEF is Binary 1110 1111
|
||||
// // ANDing preserves all bits except Bit 4, which becomes 0.
|
||||
// memValRes4 &= 0xEF;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes4);
|
||||
// return 23;
|
||||
// case 0xAE: // RES 5, (IY+d)
|
||||
// byte memValRes5 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xDF is Binary 1101 1111
|
||||
// // ANDing perfectly preserves all other bits while forcing Bit 5 to 0
|
||||
// memValRes5 &= 0xDF;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes5);
|
||||
// return 23;
|
||||
// case 0xC6: // SET 0, (IY+d)
|
||||
// byte memValSet0 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x01 is Binary 0000 0001
|
||||
// // ORing forces Bit 0 to 1 and perfectly preserves all other bits
|
||||
// memValSet0 |= 0x01;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet0);
|
||||
// return 23; // Takes 23 T-States
|
||||
// case 0xCE: // SET 1, (IY+d)
|
||||
// memVal = _memory.Read(targetAddress);
|
||||
// memVal |= 0x02; // 0x02 is Binary 0000 0010 (Bit 1)
|
||||
// _memory.Write(targetAddress, memVal);
|
||||
// return 23;
|
||||
// case 0xE6: // SET 4, (IY+d)
|
||||
// byte memValSet4 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x10 is Binary 0001 0000
|
||||
// // ORing perfectly preserves all other bits while forcing Bit 4 to 1
|
||||
// memValSet4 |= 0x10;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet4);
|
||||
// return 23;
|
||||
// case 0xEE: // SET 5, (IY+d)
|
||||
// byte memValSet5 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x20 is Binary 0010 0000
|
||||
// // ORing perfectly preserves all other bits while forcing Bit 5 to 1
|
||||
// memValSet5 |= 0x20;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet5);
|
||||
// return 23;
|
||||
|
||||
// default:
|
||||
// throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!");
|
||||
//}
|
||||
}
|
||||
@@ -385,6 +385,10 @@ namespace Desktop
|
||||
mnemonic = $"LD D, 0x{dImm:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x17: // RLA
|
||||
mnemonic = "RLA";
|
||||
instructionLength = 1;
|
||||
break;
|
||||
case 0x18:
|
||||
sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||
// Calculate the target address based on the PC *after* this 2-byte instruction
|
||||
@@ -753,12 +757,11 @@ namespace Desktop
|
||||
case 0xCB:
|
||||
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
// --- THE MISSING MATH EXTRACTION ---
|
||||
opGroup = cbOp >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
|
||||
targetBit = (cbOp >> 3) & 0x07; // Extracts a number 0-7
|
||||
regIdx = cbOp & 0x07; // Extracts register index 0-7
|
||||
|
||||
// Map the 0-7 index directly to the Z80 register names
|
||||
// Map the 0-7 index directly to the Z80 register names
|
||||
targetReg = regNames[regIdx];
|
||||
|
||||
if (opGroup == 0) // Shift/Rotate Group (0x00 to 0x3F)
|
||||
@@ -779,13 +782,9 @@ namespace Desktop
|
||||
{
|
||||
mnemonic = $"SET {targetBit}, {targetReg}";
|
||||
}
|
||||
else if (opGroup == 7) // SRL Group (0x38 to 0x3F)
|
||||
{
|
||||
mnemonic = $"SET {targetBit}, {targetReg}";
|
||||
}
|
||||
else
|
||||
{
|
||||
mnemonic = $"EXT UNKNOWN (ED {cbOp:X2})";
|
||||
mnemonic = $"CB UNKNOWN (CB {cbOp:X2})";
|
||||
}
|
||||
instructionLength = 2;
|
||||
break;
|
||||
@@ -841,6 +840,18 @@ namespace Desktop
|
||||
mnemonic = $"LD IX, 0x{ixVal:X4}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (ddOpcode == 0x22) // LD (nn), IX
|
||||
{
|
||||
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD (0x{nn:X4}), IX";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (ddOpcode == 0x2A) // LD IX, (nn)
|
||||
{
|
||||
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD IX, (0x{nn:X4})";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (ddOpcode == 0x36) // LD (IX+d), n
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -850,6 +861,20 @@ namespace Desktop
|
||||
mnemonic = $"LD (IX{sign}{d}), 0x{n:X2}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (ddOpcode == 0x46) // LD B, (IX+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD B, (IX{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (ddOpcode == 0x4E) // LD C, (IX+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD C, (IX{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (ddOpcode == 0x56) // LD D, (IX+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -864,6 +889,13 @@ namespace Desktop
|
||||
mnemonic = $"LD E, (IX{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (ddOpcode == 0x66) // LD H, (IX+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD H, (IX{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (ddOpcode == 0x6E) // LD L, (IX+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Desktop
|
||||
// The pure Core logic processes the bytes
|
||||
_cpu._tapManager.LoadTapData(tapBytes);
|
||||
|
||||
MessageBox.Show("Tape inserted! Type LOAD \"\" and press Enter.", "Tape Deck");
|
||||
//MessageBox.Show("Tape inserted! Type LOAD \"\" and press Enter.", "Tape Deck");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,16 +198,14 @@ namespace Desktop
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
//1:6 -
|
||||
|
||||
//Row 0: CAPS_SHIFT, Z, X, C, V
|
||||
//Row 0:
|
||||
case Keys.ShiftKey: UpdateMatrix(0, 0, isPressed); break;
|
||||
case Keys.Z: UpdateMatrix(0, 1, isPressed); break;
|
||||
case Keys.X: UpdateMatrix(0, 2, isPressed); break;
|
||||
case Keys.C: UpdateMatrix(0, 3, isPressed); break;
|
||||
case Keys.V: UpdateMatrix(0, 4, isPressed); break;
|
||||
|
||||
// Row 1: A, S, D, F, G
|
||||
// Row 1
|
||||
case Keys.A: UpdateMatrix(1, 0, isPressed); break;
|
||||
case Keys.S: UpdateMatrix(1, 1, isPressed); break;
|
||||
case Keys.D: UpdateMatrix(1, 2, isPressed); break;
|
||||
@@ -242,14 +240,14 @@ namespace Desktop
|
||||
case Keys.U: UpdateMatrix(5, 3, isPressed); break;
|
||||
case Keys.Y: UpdateMatrix(5, 4, isPressed); break;
|
||||
|
||||
// Row 6: ENTER, L, K, J, H
|
||||
// Row 6
|
||||
case Keys.Enter: UpdateMatrix(6, 0, isPressed); break;
|
||||
case Keys.L: UpdateMatrix(6, 1, isPressed); break;
|
||||
case Keys.K: UpdateMatrix(6, 2, isPressed); break;
|
||||
case Keys.J: UpdateMatrix(6, 3, isPressed); break;
|
||||
case Keys.H: UpdateMatrix(6, 4, isPressed); break;
|
||||
|
||||
// Row 7: SPACE, SYM SHIFT, M, N, B
|
||||
// Row 7
|
||||
case Keys.Space: UpdateMatrix(7, 0, isPressed); break;
|
||||
case Keys.ControlKey: UpdateMatrix(7, 1, isPressed); break; // Symbol Shift
|
||||
case Keys.M: UpdateMatrix(7, 2, isPressed); break;
|
||||
|
||||
Reference in New Issue
Block a user