Add Z80 CPU skeleton, RegisterPair struct, and MemoryBus implementation

This commit is contained in:
Marc Parsons
2026-04-08 16:34:49 +01:00
parent 81912da3cb
commit ea828aad2d
9 changed files with 211 additions and 31 deletions

17
Core/Cpu/RegisterPair.cs Normal file
View File

@@ -0,0 +1,17 @@
using System.Runtime.InteropServices;
namespace Core.Cpu
{
[StructLayout(LayoutKind.Explicit)]
public struct RegisterPair
{
[FieldOffset(0)]
public ushort Word;
[FieldOffset(0)]
public byte Low;
[FieldOffset(1)]
public byte High;
}
}

74
Core/Cpu/Z80.cs Normal file
View File

@@ -0,0 +1,74 @@
using System;
using Core.Interfaces;
namespace Core.Cpu
{
public partial class Z80
{
// Main Register Set
public RegisterPair AF;
public RegisterPair BC;
public RegisterPair DE;
public RegisterPair HL;
// Alternate Register Set
public RegisterPair AF_Prime;
public RegisterPair BC_Prime;
public RegisterPair DE_Prime;
public RegisterPair HL_Prime;
// Index Registers
public RegisterPair IX;
public RegisterPair IY;
// Special Purpose Registers
public ushort PC; // Program Counter
public ushort SP; // Stack Pointer
public byte I; // Interrupt Vector
public byte R; // Memory Refresh
// The Memory Bus
private readonly IMemory _memory;
public Z80(IMemory memory)
{
_memory = memory;
Reset();
}
public void Reset()
{
PC = 0x0000;
// The Z80 initializes SP to 0xFFFF on boot
SP = 0xFFFF;
AF.Word = 0;
BC.Word = 0;
DE.Word = 0;
HL.Word = 0;
}
public int Step()
{
// Fetch the next opcode and increment the Program Counter
byte opcode = _memory.Read(PC++);
// Decode and execute
return ExecuteOpcode(opcode);
}
private int ExecuteOpcode(byte opcode)
{
switch (opcode)
{
case 0x00: // NOP (No Operation)
return 4; // Takes 4 T-states
// We will expand this massive list soon!
default:
throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
}
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Core.Interfaces
{
public interface IMemory
{
byte Read(ushort address);
void Write(ushort address, byte value);
}
}

47
Core/Memory/MemoryBus.cs Normal file
View File

@@ -0,0 +1,47 @@
using System;
using Core.Interfaces;
namespace Core.Memory
{
public class MemoryBus : IMemory
{
// The flat 64KB memory space
private readonly byte[] _memory = new byte[0x10000];
public byte Read(ushort address)
{
return _memory[address];
}
public void Write(ushort address, byte value)
{
/* ZX Spectrum 48K Memory Map:
* 0x0000 - 0x3FFF: ROM (16KB) -> Cannot write here!
* 0x4000 - 0x57FF: Display File (Screen Pixels)
* 0x5800 - 0x5AFF: Color Attributes
* 0x5B00 - 0xFFFF: General Purpose RAM
*/
if (address < 0x4000)
{
// Attempted to write to the ROM area.
// We simply ignore the write command, just like real hardware.
return;
}
_memory[address] = value;
}
// Helper method to load the original Sinclair ROM file
public void LoadRom(byte[] romData)
{
if (romData.Length > 0x4000)
{
throw new ArgumentException("ROM file exceeds the 16KB capacity of Bank 0.");
}
// Copy the ROM data into the very beginning of the memory array
Array.Copy(romData, 0, _memory, 0, romData.Length);
}
}
}

View File

@@ -8,4 +8,8 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -28,10 +28,17 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container(); SuspendLayout();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; //
this.ClientSize = new System.Drawing.Size(800, 450); // Form1
this.Text = "Form1"; //
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Name = "Form1";
Text = "Form1";
Load += Form1_Load;
ResumeLayout(false);
} }
#endregion #endregion

View File

@@ -6,5 +6,10 @@ namespace Desktop
{ {
InitializeComponent(); InitializeComponent();
} }
private void Form1_Load(object sender, EventArgs e)
{
}
} }
} }

View File

@@ -3,7 +3,25 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.14.36811.4 d17.14 VisualStudioVersion = 17.14.36811.4 d17.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{0706292A-3AD4-4A04-A3CE-B692AC69D7A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Desktop\Desktop.csproj", "{57408F14-0EEC-4E9E-BC97-3764DEB5738D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0706292A-3AD4-4A04-A3CE-B692AC69D7A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0706292A-3AD4-4A04-A3CE-B692AC69D7A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0706292A-3AD4-4A04-A3CE-B692AC69D7A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0706292A-3AD4-4A04-A3CE-B692AC69D7A4}.Release|Any CPU.Build.0 = Release|Any CPU
{57408F14-0EEC-4E9E-BC97-3764DEB5738D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57408F14-0EEC-4E9E-BC97-3764DEB5738D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57408F14-0EEC-4E9E-BC97-3764DEB5738D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57408F14-0EEC-4E9E-BC97-3764DEB5738D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection