Got Chuckie Egg TAP to load to the title screen!

This commit is contained in:
2026-04-18 21:57:25 +01:00
parent 7bc85a485b
commit 717c431b9c
3 changed files with 136 additions and 195 deletions

View File

@@ -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!");
//}
}

View File

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

View File

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