Implemented SNA files. More OpCodes. Chuckie Egg Title SCreen!
This commit is contained in:
186
Core/Cpu/Z80.cs
186
Core/Cpu/Z80.cs
@@ -89,27 +89,40 @@ namespace Core.Cpu
|
|||||||
if (!IFF1) return 0;
|
if (!IFF1) return 0;
|
||||||
|
|
||||||
// 2. Acknowledge the interrupt by immediately disabling further interrupts
|
// 2. Acknowledge the interrupt by immediately disabling further interrupts
|
||||||
// This prevents an endless loop of interrupts triggering each other!
|
|
||||||
IFF1 = false;
|
IFF1 = false;
|
||||||
IFF2 = false;
|
IFF2 = false;
|
||||||
|
|
||||||
// 3. Push the current Program Counter to the stack so we can return
|
// 3. Push the current Program Counter to the stack so we can return later
|
||||||
Push(PC);
|
Push(PC);
|
||||||
|
|
||||||
// 4. Jump to the Interrupt Service Routine
|
// --- Interrupt Mode Dispatch ---
|
||||||
// The ZX Spectrum standard is Mode 1, which maps directly to 0x0038
|
|
||||||
if (InterruptMode == 1)
|
if (InterruptMode == 1)
|
||||||
{
|
{
|
||||||
|
// IM 1: Hardcoded jump to ROM address 0x0038
|
||||||
PC = 0x0038;
|
PC = 0x0038;
|
||||||
|
return 13; // IM 1 hardware call takes 13 T-States
|
||||||
|
}
|
||||||
|
else if (InterruptMode == 2)
|
||||||
|
{
|
||||||
|
// IM 2: Dynamic Vectored Interrupts
|
||||||
|
|
||||||
|
// A. Form the pointer address: High byte is 'I', Low byte is the floating bus (0xFF)
|
||||||
|
ushort vectorAddress = (ushort)((I << 8) | 0xFF);
|
||||||
|
|
||||||
|
// B. Read the actual 16-bit ISR address from that location in memory (Little-Endian)
|
||||||
|
byte pcLow = _memory.Read(vectorAddress);
|
||||||
|
byte pcHigh = _memory.Read((ushort)(vectorAddress + 1));
|
||||||
|
|
||||||
|
// C. Jump to the custom game routine!
|
||||||
|
PC = (ushort)((pcHigh << 8) | pcLow);
|
||||||
|
|
||||||
|
return 19; // IM 2 hardware call takes 19 T-States
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// (Games will use Mode 2 later, but the ROM uses Mode 1)
|
// (IM 0 is theoretically possible but essentially unused on the standard Spectrum)
|
||||||
throw new NotImplementedException($"Interrupt Mode {InterruptMode} not implemented!");
|
throw new NotImplementedException($"Interrupt Mode {InterruptMode} not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. The CPU takes exactly 13 T-States to process this hardware CALL
|
|
||||||
return 13;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to calculate if a byte has an Even Parity of 1s
|
// Helper method to calculate if a byte has an Even Parity of 1s
|
||||||
@@ -146,6 +159,45 @@ namespace Core.Cpu
|
|||||||
return tStates;
|
return tStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadSNA(byte[] snaData)
|
||||||
|
{
|
||||||
|
if (snaData.Length != 49179)
|
||||||
|
throw new Exception("Invalid 48K SNA File Size!");
|
||||||
|
|
||||||
|
// --- 1. Load CPU Registers ---
|
||||||
|
I = snaData[0];
|
||||||
|
HL_Prime.Word = (ushort)(snaData[1] | (snaData[2] << 8));
|
||||||
|
DE_Prime.Word = (ushort)(snaData[3] | (snaData[4] << 8));
|
||||||
|
BC_Prime.Word = (ushort)(snaData[5] | (snaData[6] << 8));
|
||||||
|
AF_Prime.Word = (ushort)(snaData[7] | (snaData[8] << 8));
|
||||||
|
|
||||||
|
HL.Word = (ushort)(snaData[9] | (snaData[10] << 8));
|
||||||
|
DE.Word = (ushort)(snaData[11] | (snaData[12] << 8));
|
||||||
|
BC.Word = (ushort)(snaData[13] | (snaData[14] << 8));
|
||||||
|
IY.Word = (ushort)(snaData[15] | (snaData[16] << 8));
|
||||||
|
IX.Word = (ushort)(snaData[17] | (snaData[18] << 8));
|
||||||
|
|
||||||
|
IFF2 = (snaData[19] & 0x04) != 0;
|
||||||
|
IFF1 = IFF2;
|
||||||
|
R = snaData[20];
|
||||||
|
AF.Word = (ushort)(snaData[21] | (snaData[22] << 8));
|
||||||
|
SP = (ushort)(snaData[23] | (snaData[24] << 8));
|
||||||
|
InterruptMode = snaData[25];
|
||||||
|
|
||||||
|
// --- 2. Load the 48K RAM Dump ---
|
||||||
|
// The RAM dump starts at byte 27 and maps perfectly to 0x4000 -> 0xFFFF
|
||||||
|
for (int i = 0; i < 49152; i++)
|
||||||
|
{
|
||||||
|
_memory.Write((ushort)(0x4000 + i), snaData[27 + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. The Magic Bullet ---
|
||||||
|
// In the SNA format, the Program Counter (PC) isn't in the header.
|
||||||
|
// It was PUSHED to the stack exactly 1 instruction before the snapshot was saved.
|
||||||
|
// So, we just pop it off the stack to resume execution!
|
||||||
|
PC = Pop();
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleInstantTapeLoad()
|
private void HandleInstantTapeLoad()
|
||||||
{
|
{
|
||||||
// 1. Grab the next block from the virtual cassette
|
// 1. Grab the next block from the virtual cassette
|
||||||
@@ -175,10 +227,13 @@ namespace Core.Cpu
|
|||||||
IX.Word = (ushort)(IX.Word + bytesToCopy);
|
IX.Word = (ushort)(IX.Word + bytesToCopy);
|
||||||
DE.Word = 0;
|
DE.Word = 0;
|
||||||
|
|
||||||
// 5. Set the Carry Flag to 1 (Success)
|
// 5. Simulate the Checksum Match (Accumulator becomes 0)
|
||||||
AF.Low |= 0x01;
|
AF.High = 0x00;
|
||||||
|
|
||||||
// 6. Simulate a standard 'RET' instruction to exit the LD-BYTES routine safely
|
// 6. Set Carry Flag (Bit 0) for Success, and Zero Flag (Bit 6) for Checksum Match
|
||||||
|
AF.Low |= 0x41; // 0x41 is binary 0100 0001 (Zero and Carry both set)
|
||||||
|
|
||||||
|
// 7. Simulate a standard 'RET' instruction to exit the LD-BYTES routine safely
|
||||||
ExecuteRet();
|
ExecuteRet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,6 +577,36 @@ namespace Core.Cpu
|
|||||||
AF.High = (byte)result;
|
AF.High = (byte)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Adc16(ushort value)
|
||||||
|
{
|
||||||
|
int hl = HL.Word;
|
||||||
|
int carry = AF.Low & 0x01;
|
||||||
|
|
||||||
|
// Calculate the raw integer result to check for overflows
|
||||||
|
int result = hl + value + carry;
|
||||||
|
|
||||||
|
// --- Update Flags (F Register) ---
|
||||||
|
byte newFlags = 0; // Clear all flags (which forces N to 0, correctly!)
|
||||||
|
|
||||||
|
// Sign Flag (Bit 7) - Set if the 16-bit result is negative (bit 15 is 1)
|
||||||
|
if ((result & 0x8000) != 0) newFlags |= 0x80;
|
||||||
|
|
||||||
|
// Zero Flag (Bit 6) - Set if the entire 16-bit result is exactly 0
|
||||||
|
if ((result & 0xFFFF) == 0) newFlags |= 0x40;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - Set if there is a carry out of bit 11
|
||||||
|
if (((hl & 0x0FFF) + (value & 0x0FFF) + carry) > 0x0FFF) newFlags |= 0x10;
|
||||||
|
|
||||||
|
// Overflow Flag (Bit 2) - Set if operands have the SAME sign, but result sign changes
|
||||||
|
if ((((hl ^ ~value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) newFlags |= 0x04;
|
||||||
|
|
||||||
|
// Carry Flag (Bit 0) - Set if the overall 16-bit result overflowed 0xFFFF
|
||||||
|
if (result > 0xFFFF) newFlags |= 0x01;
|
||||||
|
|
||||||
|
AF.Low = newFlags;
|
||||||
|
HL.Word = (ushort)result;
|
||||||
|
}
|
||||||
|
|
||||||
private void AddA(byte operand)
|
private void AddA(byte operand)
|
||||||
{
|
{
|
||||||
byte a = AF.High;
|
byte a = AF.High;
|
||||||
@@ -625,6 +710,9 @@ namespace Core.Cpu
|
|||||||
AF.Word = AF_Prime.Word;
|
AF.Word = AF_Prime.Word;
|
||||||
AF_Prime.Word = tempAF;
|
AF_Prime.Word = tempAF;
|
||||||
return 4;
|
return 4;
|
||||||
|
case 0x0A: //LD A (BC)
|
||||||
|
AF.High = _memory.Read(BC.Word);
|
||||||
|
return 7;
|
||||||
case 0x0C: BC.Low = Inc8(BC.Low); return 4; // INC C
|
case 0x0C: BC.Low = Inc8(BC.Low); return 4; // INC C
|
||||||
case 0x12: // LD (DE), A
|
case 0x12: // LD (DE), A
|
||||||
_memory.Write(DE.Word, AF.High);
|
_memory.Write(DE.Word, AF.High);
|
||||||
@@ -801,20 +889,19 @@ namespace Core.Cpu
|
|||||||
return 7;
|
return 7;
|
||||||
case 0x28: // JR Z, e
|
case 0x28: // JR Z, e
|
||||||
offset = (sbyte)FetchByte();
|
offset = (sbyte)FetchByte();
|
||||||
|
// Check if the Zero Flag is set
|
||||||
// Check if the Zero Flag (Bit 6) IS set
|
|
||||||
if ((AF.Low & 0x40) != 0)
|
if ((AF.Low & 0x40) != 0)
|
||||||
{
|
{
|
||||||
PC = (ushort)(PC + offset);
|
PC = (ushort)(PC + offset);
|
||||||
return 12; // Jump taken
|
return 12;
|
||||||
}
|
}
|
||||||
return 7; // Jump not taken
|
return 7;
|
||||||
case 0x2A: // LD HL, (nn)
|
case 0x2A: // LD HL, (nn)
|
||||||
{
|
{
|
||||||
ushort srcAddress = FetchWord();
|
ushort srcAddress = FetchWord();
|
||||||
HL.Low = _memory.Read(srcAddress);
|
HL.Low = _memory.Read(srcAddress);
|
||||||
HL.High = _memory.Read((ushort)(srcAddress + 1));
|
HL.High = _memory.Read((ushort)(srcAddress + 1));
|
||||||
return 16; // Takes 16 T-States
|
return 16;
|
||||||
}
|
}
|
||||||
case 0x2B: // DEC HL
|
case 0x2B: // DEC HL
|
||||||
HL.Word--;
|
HL.Word--;
|
||||||
@@ -1345,6 +1432,9 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
switch (extendedOpcode)
|
switch (extendedOpcode)
|
||||||
{
|
{
|
||||||
|
case 0x42: // SBC HL, BC
|
||||||
|
Sbc16(BC.Word);
|
||||||
|
return 15;
|
||||||
case 0x43: // LD (nn), BC
|
case 0x43: // LD (nn), BC
|
||||||
ushort dest43 = FetchWord();
|
ushort dest43 = FetchWord();
|
||||||
_memory.Write(dest43, BC.Low);
|
_memory.Write(dest43, BC.Low);
|
||||||
@@ -1381,11 +1471,17 @@ namespace Core.Cpu
|
|||||||
case 0x47: // LD I, A
|
case 0x47: // LD I, A
|
||||||
I = AF.High;
|
I = AF.High;
|
||||||
return 9;
|
return 9;
|
||||||
|
case 0x4A: // ADC HL, BC
|
||||||
|
Adc16(BC.Word);
|
||||||
|
return 15;
|
||||||
case 0x4B: // LD BC, (nn)
|
case 0x4B: // LD BC, (nn)
|
||||||
ushort src4B = FetchWord();
|
ushort src4B = FetchWord();
|
||||||
BC.Low = _memory.Read(src4B);
|
BC.Low = _memory.Read(src4B);
|
||||||
BC.High = _memory.Read((ushort)(src4B + 1));
|
BC.High = _memory.Read((ushort)(src4B + 1));
|
||||||
return 20; // Takes 20 T-States
|
return 20;
|
||||||
|
case 0x4D: // RETI Does not affect IFF1 or IFF2
|
||||||
|
PC = Pop();
|
||||||
|
return 14;
|
||||||
case 0x52: // SBC HL, DE
|
case 0x52: // SBC HL, DE
|
||||||
Sbc16(DE.Word);
|
Sbc16(DE.Word);
|
||||||
return 15;
|
return 15;
|
||||||
@@ -1397,11 +1493,20 @@ namespace Core.Cpu
|
|||||||
case 0x56: // IM 1
|
case 0x56: // IM 1
|
||||||
InterruptMode = 1;
|
InterruptMode = 1;
|
||||||
return 8;
|
return 8;
|
||||||
|
case 0x5A: // ADC HL, DE
|
||||||
|
Adc16(DE.Word);
|
||||||
|
return 15;
|
||||||
case 0x5B: // LD DE, (nn)
|
case 0x5B: // LD DE, (nn)
|
||||||
ushort src5B = FetchWord();
|
ushort src5B = FetchWord();
|
||||||
DE.Low = _memory.Read(src5B);
|
DE.Low = _memory.Read(src5B);
|
||||||
DE.High = _memory.Read((ushort)(src5B + 1));
|
DE.High = _memory.Read((ushort)(src5B + 1));
|
||||||
return 20;
|
return 20;
|
||||||
|
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
|
case 0x72: // SBC HL, SP
|
||||||
int carryIn = AF.Low & 0x01;
|
int carryIn = AF.Low & 0x01;
|
||||||
int hlVal = HL.Word;
|
int hlVal = HL.Word;
|
||||||
@@ -1462,7 +1567,10 @@ namespace Core.Cpu
|
|||||||
return 12;
|
return 12;
|
||||||
case 0x79: // OUT (C), A
|
case 0x79: // OUT (C), A
|
||||||
_simpleIoBus.WritePort(BC.Word, AF.High);
|
_simpleIoBus.WritePort(BC.Word, AF.High);
|
||||||
return 12; // 12 T-States
|
return 12;
|
||||||
|
case 0x7A: // ADC HL, SP
|
||||||
|
Adc16(SP);
|
||||||
|
return 15;
|
||||||
case 0x7B: // LD SP, (nn)
|
case 0x7B: // LD SP, (nn)
|
||||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||||
byte addrLow = FetchByte();
|
byte addrLow = FetchByte();
|
||||||
@@ -1540,6 +1648,7 @@ namespace Core.Cpu
|
|||||||
private int ExecuteCBPrefix()
|
private int ExecuteCBPrefix()
|
||||||
{
|
{
|
||||||
byte cbOpcode = FetchByte();
|
byte cbOpcode = FetchByte();
|
||||||
|
bool oldCarry = false;
|
||||||
|
|
||||||
// Extract the exact same mathematical properties
|
// Extract the exact same mathematical properties
|
||||||
int operation = cbOpcode >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
|
int operation = cbOpcode >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
|
||||||
@@ -1602,6 +1711,33 @@ namespace Core.Cpu
|
|||||||
// Shift left, and loop the falling bit back into Bit 0
|
// Shift left, and loop the falling bit back into Bit 0
|
||||||
val = (byte)((val << 1) | (carryOut ? 1 : 0));
|
val = (byte)((val << 1) | (carryOut ? 1 : 0));
|
||||||
break;
|
break;
|
||||||
|
case 1: // RRC (Rotate Right Circular)
|
||||||
|
// 1. Grab Bit 0 before it falls off to set the Carry flag and loop to Bit 7
|
||||||
|
carryOut = (val & 0x01) != 0;
|
||||||
|
|
||||||
|
// 2. Shift right by 1, and loop the falling bit directly back into Bit 7
|
||||||
|
val = (byte)((val >> 1) | (carryOut ? 0x80 : 0x00));
|
||||||
|
break;
|
||||||
|
case 2: // RL (Rotate Left through Carry)
|
||||||
|
// 1. Grab the CURRENT Carry flag from the AF register
|
||||||
|
oldCarry = (AF.Low & 0x01) != 0;
|
||||||
|
|
||||||
|
// 2. Grab Bit 7 before it falls off to become the NEW Carry flag
|
||||||
|
carryOut = (val & 0x80) != 0;
|
||||||
|
|
||||||
|
// 3. Shift left by 1, and drop the OLD carry flag directly into Bit 0
|
||||||
|
val = (byte)((val << 1) | (oldCarry ? 0x01 : 0x00));
|
||||||
|
break;
|
||||||
|
case 3: // RR (Rotate Right through Carry)
|
||||||
|
// 1. Grab the CURRENT Carry flag from the AF register
|
||||||
|
oldCarry = (AF.Low & 0x01) != 0;
|
||||||
|
|
||||||
|
// 2. Grab Bit 0 before it falls off to become the NEW Carry flag
|
||||||
|
carryOut = (val & 0x01) != 0;
|
||||||
|
|
||||||
|
// 3. Shift right by 1, and drop the OLD carry flag into Bit 7
|
||||||
|
val = (byte)((val >> 1) | (oldCarry ? 0x80 : 0x00));
|
||||||
|
break;
|
||||||
case 4: // SLA (Shift Left Arithmetic)
|
case 4: // SLA (Shift Left Arithmetic)
|
||||||
// 1. Grab Bit 7 before it falls off to set the Carry flag
|
// 1. Grab Bit 7 before it falls off to set the Carry flag
|
||||||
carryOut = (val & 0x80) != 0;
|
carryOut = (val & 0x80) != 0;
|
||||||
@@ -1610,6 +1746,20 @@ namespace Core.Cpu
|
|||||||
// (In C#, a standard left shift automatically pads Bit 0 with a 0)
|
// (In C#, a standard left shift automatically pads Bit 0 with a 0)
|
||||||
val = (byte)(val << 1);
|
val = (byte)(val << 1);
|
||||||
break;
|
break;
|
||||||
|
case 5: // SRA (Shift Right Arithmetic)
|
||||||
|
// 1. Grab Bit 0 before it falls off to set the Carry flag
|
||||||
|
carryOut = (val & 0x01) != 0;
|
||||||
|
|
||||||
|
// 2. Grab the current Sign bit (Bit 7) so we can preserve it
|
||||||
|
byte signBit = (byte)(val & 0x80);
|
||||||
|
|
||||||
|
// 3. Shift the byte right by 1.
|
||||||
|
// (Because 'val' is unsigned, C# naturally pads the top with a 0)
|
||||||
|
val = (byte)(val >> 1);
|
||||||
|
|
||||||
|
// 4. Force the preserved sign bit back into Bit 7
|
||||||
|
val |= signBit;
|
||||||
|
break;
|
||||||
case 7: // SRL (Shift Right Logical)
|
case 7: // SRL (Shift Right Logical)
|
||||||
// 1. Grab Bit 0 before it falls off to set the Carry flag
|
// 1. Grab Bit 0 before it falls off to set the Carry flag
|
||||||
carryOut = (val & 0x01) != 0;
|
carryOut = (val & 0x01) != 0;
|
||||||
|
|||||||
@@ -323,6 +323,7 @@ namespace Desktop
|
|||||||
case 0x08:
|
case 0x08:
|
||||||
mnemonic = "EX AF, AF'";
|
mnemonic = "EX AF, AF'";
|
||||||
break;
|
break;
|
||||||
|
case 0x0A: mnemonic = "LD A, (BC)"; break;
|
||||||
case 0x12: mnemonic = "LD (DE), A"; break;
|
case 0x12: mnemonic = "LD (DE), A"; break;
|
||||||
case 0x13: mnemonic = "INC DE"; break;
|
case 0x13: mnemonic = "INC DE"; break;
|
||||||
case 0x33: mnemonic = "INC SP"; break;
|
case 0x33: mnemonic = "INC SP"; break;
|
||||||
@@ -391,9 +392,7 @@ namespace Desktop
|
|||||||
break;
|
break;
|
||||||
case 0x18:
|
case 0x18:
|
||||||
sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||||
// Calculate the target address based on the PC *after* this 2-byte instruction
|
|
||||||
ushort targetAddressUnconditional = (ushort)(currentPc + 2 + dUnconditional);
|
ushort targetAddressUnconditional = (ushort)(currentPc + 2 + dUnconditional);
|
||||||
|
|
||||||
mnemonic = $"JR 0x{targetAddressUnconditional:X4}";
|
mnemonic = $"JR 0x{targetAddressUnconditional:X4}";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
@@ -981,6 +980,7 @@ namespace Desktop
|
|||||||
|
|
||||||
switch (extendedOp)
|
switch (extendedOp)
|
||||||
{
|
{
|
||||||
|
case 0x42: mnemonic = "SBC HL, BC"; instructionLength = 2; break;
|
||||||
case 0x43:
|
case 0x43:
|
||||||
ushort bcAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort bcAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD (0x{bcAddr:X4}), BC";
|
mnemonic = $"LD (0x{bcAddr:X4}), BC";
|
||||||
@@ -994,11 +994,17 @@ namespace Desktop
|
|||||||
mnemonic = "LD I, A";
|
mnemonic = "LD I, A";
|
||||||
instructionLength = 2; // 0xED + 0x47
|
instructionLength = 2; // 0xED + 0x47
|
||||||
break;
|
break;
|
||||||
|
// Inside your ED prefix switch statement in the debugger:
|
||||||
|
case 0x4A: mnemonic = "ADC HL, BC"; instructionLength = 2; break;
|
||||||
case 0x4B:
|
case 0x4B:
|
||||||
ushort addr4B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort addr4B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD BC, (0x{addr4B:X4})";
|
mnemonic = $"LD BC, (0x{addr4B:X4})";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
break;
|
break;
|
||||||
|
case 0x4D:
|
||||||
|
mnemonic = "RETI";
|
||||||
|
instructionLength = 2;
|
||||||
|
break;
|
||||||
case 0x52:
|
case 0x52:
|
||||||
mnemonic = "SBC HL, DE";
|
mnemonic = "SBC HL, DE";
|
||||||
instructionLength = 2; // ED 52
|
instructionLength = 2; // ED 52
|
||||||
@@ -1012,11 +1018,14 @@ namespace Desktop
|
|||||||
mnemonic = "IM 1";
|
mnemonic = "IM 1";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x5A: mnemonic = "ADC HL, DE"; instructionLength = 2; break;
|
||||||
case 0x5B:
|
case 0x5B:
|
||||||
ushort addr5B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort addr5B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
break;
|
break;
|
||||||
|
case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break;
|
||||||
|
case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break;
|
||||||
case 0x73:
|
case 0x73:
|
||||||
ushort addr73 = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort addr73 = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD (0x{addr73:X4}), SP";
|
mnemonic = $"LD (0x{addr73:X4}), SP";
|
||||||
@@ -1030,6 +1039,7 @@ namespace Desktop
|
|||||||
mnemonic = "IN A, (C)";
|
mnemonic = "IN A, (C)";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x7A: mnemonic = "ADC HL, SP"; instructionLength = 2; break;
|
||||||
case 0x7B: // LD SP, (nn)
|
case 0x7B: // LD SP, (nn)
|
||||||
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD SP, (0x{nn:X4})";
|
mnemonic = $"LD SP, (0x{nn:X4})";
|
||||||
|
|||||||
15
Desktop/Form1.Designer.cs
generated
15
Desktop/Form1.Designer.cs
generated
@@ -32,6 +32,7 @@
|
|||||||
menuStrip1 = new MenuStrip();
|
menuStrip1 = new MenuStrip();
|
||||||
fileToolStripMenuItem = new ToolStripMenuItem();
|
fileToolStripMenuItem = new ToolStripMenuItem();
|
||||||
openToolStripMenuItem = new ToolStripMenuItem();
|
openToolStripMenuItem = new ToolStripMenuItem();
|
||||||
|
openSnapshotToolStripMenuItem = new ToolStripMenuItem();
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
||||||
menuStrip1.SuspendLayout();
|
menuStrip1.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
//
|
//
|
||||||
// fileToolStripMenuItem
|
// fileToolStripMenuItem
|
||||||
//
|
//
|
||||||
fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem });
|
fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, openSnapshotToolStripMenuItem });
|
||||||
fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||||
fileToolStripMenuItem.Size = new Size(54, 29);
|
fileToolStripMenuItem.Size = new Size(54, 29);
|
||||||
fileToolStripMenuItem.Text = "File";
|
fileToolStripMenuItem.Text = "File";
|
||||||
@@ -67,10 +68,17 @@
|
|||||||
// openToolStripMenuItem
|
// openToolStripMenuItem
|
||||||
//
|
//
|
||||||
openToolStripMenuItem.Name = "openToolStripMenuItem";
|
openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||||
openToolStripMenuItem.Size = new Size(158, 34);
|
openToolStripMenuItem.Size = new Size(270, 34);
|
||||||
openToolStripMenuItem.Text = "Open";
|
openToolStripMenuItem.Text = "Open TAP";
|
||||||
openToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click;
|
openToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click;
|
||||||
//
|
//
|
||||||
|
// openSnapshotToolStripMenuItem
|
||||||
|
//
|
||||||
|
openSnapshotToolStripMenuItem.Name = "openSnapshotToolStripMenuItem";
|
||||||
|
openSnapshotToolStripMenuItem.Size = new Size(270, 34);
|
||||||
|
openSnapshotToolStripMenuItem.Text = "Open Snapshot";
|
||||||
|
openSnapshotToolStripMenuItem.Click += openSNAToolStripMenuItem_Click;
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(10F, 25F);
|
AutoScaleDimensions = new SizeF(10F, 25F);
|
||||||
@@ -96,5 +104,6 @@
|
|||||||
private MenuStrip menuStrip1;
|
private MenuStrip menuStrip1;
|
||||||
private ToolStripMenuItem fileToolStripMenuItem;
|
private ToolStripMenuItem fileToolStripMenuItem;
|
||||||
private ToolStripMenuItem openToolStripMenuItem;
|
private ToolStripMenuItem openToolStripMenuItem;
|
||||||
|
private ToolStripMenuItem openSnapshotToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,18 @@ namespace Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void openSNAToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using (OpenFileDialog ofd = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
ofd.Filter = "Spectrum Snapshot Files (*.sna)|*.sna";
|
||||||
|
if (ofd.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
byte[] snaBytes = System.IO.File.ReadAllBytes(ofd.FileName);
|
||||||
|
_cpu.LoadSNA(snaBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Public so the Debugger's background thread can call it 50 times a second
|
// Public so the Debugger's background thread can call it 50 times a second
|
||||||
public void RenderScreen()
|
public void RenderScreen()
|
||||||
|
|||||||
Reference in New Issue
Block a user