using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Windows.Forms; using Core.Cpu; using Core.Io; using Core.Memory; namespace Desktop { public partial class Form1 : Form { private Z80 _cpu = null!; private MemoryBus _memoryBus = null!; private SimpleIoBus _simpleIoBus = null!; // The 16 physical colors of the ZX Spectrum (ARGB format) private readonly int[] SpectrumColors = new int[] { // Normal Colors (Bright = 0) unchecked((int)0xFF000000), // 0: Black unchecked((int)0xFF0000D7), // 1: Blue unchecked((int)0xFFD70000), // 2: Red unchecked((int)0xFFD700D7), // 3: Magenta unchecked((int)0xFF00D700), // 4: Green unchecked((int)0xFF00D7D7), // 5: Cyan unchecked((int)0xFFD7D700), // 6: Yellow unchecked((int)0xFFD7D7D7), // 7: White // Bright Colors (Bright = 1) unchecked((int)0xFF000000), // 8: Bright Black unchecked((int)0xFF0000FF), // 9: Bright Blue unchecked((int)0xFFFF0000), // 10: Bright Red unchecked((int)0xFFFF00FF), // 11: Bright Magenta unchecked((int)0xFF00FF00), // 12: Bright Green unchecked((int)0xFF00FFFF), // 13: Bright Cyan unchecked((int)0xFFFFFF00), // 14: Bright Yellow unchecked((int)0xFFFFFFFF) // 15: Bright White }; public Form1() { InitializeComponent(); InitializeEmulator(); } private void InitializeEmulator() { try { _memoryBus = new MemoryBus(); _simpleIoBus = new SimpleIoBus(); _memoryBus.CrapRAMData(); byte[] romData = RomLoader.Load("48.rom"); _memoryBus.LoadRom(romData); _cpu = new Z80(_memoryBus, _simpleIoBus); // Pass 'this' so the DebuggerForm can talk back to this main window DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this); debugger.Show(); } catch (Exception ex) { MessageBox.Show($"Failed to initialize emulator:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // Public so the Debugger's background thread can call it 50 times a second public void RenderScreen() { int[] pixelData = new int[256 * 192]; // Loop through the 6144 bytes of Pixel RAM for (int offset = 0; offset < 6144; offset++) { ushort address = (ushort)(0x4000 + offset); byte pixels = _memoryBus.Read(address); // Unwind the Sinclair interlace int y = ((offset & 0x0700) >> 8) | ((offset & 0x00E0) >> 2) | ((offset & 0x1800) >> 5); int x = (offset & 0x001F) * 8; // Fetch Color Attributes int attrRow = y / 8; int attrCol = x / 8; ushort attrAddress = (ushort)(0x5800 + (attrRow * 32) + attrCol); byte attr = _memoryBus.Read(attrAddress); int ink = attr & 0x07; int paper = (attr >> 3) & 0x07; int brightOffset = (attr & 0x40) != 0 ? 8 : 0; int inkColor = SpectrumColors[ink + brightOffset]; int paperColor = SpectrumColors[paper + brightOffset]; // Draw the 8 pixels for (int bit = 0; bit < 8; bit++) { bool isPixelSet = (pixels & (1 << (7 - bit))) != 0; pixelData[(y * 256) + (x + bit)] = isPixelSet ? inkColor : paperColor; } } // Blast it to the PictureBox Bitmap bmp = new Bitmap(256, 192, PixelFormat.Format32bppArgb); BitmapData bmpData = bmp.LockBits( new Rectangle(0, 0, 256, 192), ImageLockMode.WriteOnly, bmp.PixelFormat); Marshal.Copy(pixelData, 0, bmpData.Scan0, pixelData.Length); bmp.UnlockBits(bmpData); if (picScreen.Image != null) picScreen.Image.Dispose(); picScreen.Image = bmp; } } }