diff --git a/Core/Cpu/RegisterPair.cs b/Core/Cpu/RegisterPair.cs new file mode 100644 index 0000000..30bf18f --- /dev/null +++ b/Core/Cpu/RegisterPair.cs @@ -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; + } +} \ No newline at end of file diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs new file mode 100644 index 0000000..0e1b210 --- /dev/null +++ b/Core/Cpu/Z80.cs @@ -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."); + } + } + } +} \ No newline at end of file diff --git a/Core/Interfaces/IMemory.cs b/Core/Interfaces/IMemory.cs new file mode 100644 index 0000000..49cdbb0 --- /dev/null +++ b/Core/Interfaces/IMemory.cs @@ -0,0 +1,8 @@ +namespace Core.Interfaces +{ + public interface IMemory + { + byte Read(ushort address); + void Write(ushort address, byte value); + } +} diff --git a/Core/Memory/MemoryBus.cs b/Core/Memory/MemoryBus.cs new file mode 100644 index 0000000..c135787 --- /dev/null +++ b/Core/Memory/MemoryBus.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Desktop/Desktop.csproj b/Desktop/Desktop.csproj index 663fdb8..454196b 100644 --- a/Desktop/Desktop.csproj +++ b/Desktop/Desktop.csproj @@ -8,4 +8,8 @@ enable + + + + \ No newline at end of file diff --git a/Desktop/Form1.Designer.cs b/Desktop/Form1.Designer.cs index 7cf6955..52f7cd9 100644 --- a/Desktop/Form1.Designer.cs +++ b/Desktop/Form1.Designer.cs @@ -28,10 +28,17 @@ /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; + SuspendLayout(); + // + // Form1 + // + AutoScaleDimensions = new SizeF(8F, 20F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Name = "Form1"; + Text = "Form1"; + Load += Form1_Load; + ResumeLayout(false); } #endregion diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index 3612dfe..d5006e3 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -6,5 +6,10 @@ namespace Desktop { InitializeComponent(); } + + private void Form1_Load(object sender, EventArgs e) + { + + } } } diff --git a/Desktop/Form1.resx b/Desktop/Form1.resx index 1af7de1..8b2ff64 100644 --- a/Desktop/Form1.resx +++ b/Desktop/Form1.resx @@ -1,17 +1,17 @@  - diff --git a/ZxSpectrumEmulation.sln b/ZxSpectrumEmulation.sln index a6a7b08..8c9846e 100644 --- a/ZxSpectrumEmulation.sln +++ b/ZxSpectrumEmulation.sln @@ -3,7 +3,25 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.36811.4 d17.14 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 + 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 HideSolutionNode = FALSE EndGlobalSection