8 Commits

Author SHA1 Message Date
parsons
2300e2931e First OS Agnostic Commit 2026-05-31 00:24:14 +01:00
parsons
2f332ff224 Added some GG games 2026-05-26 23:22:04 +01:00
parsons
4a0424a664 Added more games 2026-05-26 22:53:50 +01:00
Marc Parsons
ce46e7ed52 Updated Z80 CPU to fix interrupts etc... 2026-05-26 22:02:47 +01:00
Marc Parsons
b5695b5c2f Some fixes and code refactoring 2026-05-22 16:40:33 +01:00
Marc Parsons
cca1abf0be Added double buffering ish. 2026-05-21 17:25:57 +01:00
Marc Parsons
9316a16ab9 A commit to resync the repository 2026-05-21 16:34:24 +01:00
parsons
cd5e18a2ac Tidy Up 2026-05-19 01:00:28 +01:00
22 changed files with 816 additions and 422 deletions

View File

@@ -33,7 +33,9 @@ namespace Core.Audio
0.1584f, 0.1258f, 0.1000f, 0.0794f, 0.0630f, 0.0501f, 0.0398f, 0.0f 0.1584f, 0.1258f, 0.1000f, 0.0794f, 0.0630f, 0.0501f, 0.0398f, 0.0f
}; };
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public SmsApu() public SmsApu()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
{ {
Registers[1] = 0x0F; Registers[1] = 0x0F;
Registers[3] = 0x0F; Registers[3] = 0x0F;

View File

@@ -25,10 +25,13 @@ namespace Core.Cpu
public int InterruptMode { get; private set; } = 0; public int InterruptMode { get; private set; } = 0;
// Interrupt Flip-Flops // Interrupt Flip-Flops
public bool IFF1 { get; private set; } = false; public bool IFF1 { get; private set; } = false;
public bool IFF2 { get; private set; } = false; public bool IFF2 { get; private set; } = false;
public bool InterruptRequested { get; private set; } = false; public bool InterruptRequested { get; private set; } = false;
private int _eiDelay = 0;
public bool IsHalted { get; private set; } = false;
// Main Register Set // Main Register Set
public RegisterPair AF; public RegisterPair AF;
@@ -48,7 +51,7 @@ namespace Core.Cpu
// Special Purpose Registers // Special Purpose Registers
public ushort PC; // Program Counter public ushort PC; // Program Counter
public ushort SP; // Stack Pointer public ushort SP;
public byte I; // Interrupt Vector public byte I; // Interrupt Vector
public byte R; // Memory Refresh public byte R; // Memory Refresh
@@ -96,6 +99,10 @@ namespace Core.Cpu
IFF2 = false; IFF2 = false;
InterruptMode = 0; InterruptMode = 0;
TotalTStates = 0; TotalTStates = 0;
_eiDelay = 0;
IsHalted = false;
InterruptRequested = false;
} }
public void SaveState(BinaryWriter bw) public void SaveState(BinaryWriter bw)
@@ -135,6 +142,7 @@ namespace Core.Cpu
public int RequestInterrupt() public int RequestInterrupt()
{ {
IsHalted = false;
InterruptRequested = true; InterruptRequested = true;
// 1. If the ROM has disabled interrupts (DI), ignore the request // 1. If the ROM has disabled interrupts (DI), ignore the request
if (!IFF1) return 0; if (!IFF1) return 0;
@@ -143,6 +151,8 @@ namespace Core.Cpu
IFF1 = false; IFF1 = false;
IFF2 = false; IFF2 = false;
_eiDelay = 0;
// 3. Push the current Program Counter to the stack so we can return later // 3. Push the current Program Counter to the stack so we can return later
Push(PC); Push(PC);
@@ -214,14 +224,36 @@ namespace Core.Cpu
public int Step() public int Step()
{ {
int tStates;
// Fetch the next opcode and increment the Program Counter R = (byte)((R & 0x80) | ((R + 1) & 0x7F));
byte opcode = ReadMemory(PC++);
R = (byte)((R + 1) & 0x7F); if (IsHalted)
int tStates = ExecuteOpcode(opcode); {
// The CPU is asleep! Do not fetch instructions, just pass the time.
tStates = 4;
TotalTStates += tStates; TotalTStates += tStates;
}
else
{
// Normal execution
byte opcode = ReadMemory(PC++);
tStates = ExecuteOpcode(opcode);
TotalTStates += tStates;
}
// The interrupt enablement perfectly mimics the physical hardware delay.
// This MUST tick down even if the CPU is halted!
if (_eiDelay > 0)
{
_eiDelay--;
if (_eiDelay == 0)
{
IFF1 = true;
IFF2 = true;
}
}
// Decode and execute
return tStates; return tStates;
} }
@@ -240,9 +272,6 @@ namespace Core.Cpu
$"C:{f & 1}"; $"C:{f & 1}";
} }
// =========================================================================
// MATH AND LOGIC HELPERS
// =========================================================================
private void SubA(byte value, bool isCompare) private void SubA(byte value, bool isCompare)
{ {
@@ -925,16 +954,20 @@ namespace Core.Cpu
case 0x74: WriteMemory(HL.Word, HL.High); return 7; case 0x74: WriteMemory(HL.Word, HL.High); return 7;
case 0x75: WriteMemory(HL.Word, HL.Low); return 7; case 0x75: WriteMemory(HL.Word, HL.Low); return 7;
case 0x76: // HALT case 0x76: // HALT
if (!InterruptRequested) IsHalted = true;
{
PC--;
return 4; return 4;
}
else //case 0x76: //HALT
{ // if (!InterruptRequested)
InterruptRequested = false; // {
return 4; // PC--;
} // return 4;
// }
// else
// {
// InterruptRequested = false;
// return 4;
// }
case 0x77: WriteMemory(HL.Word, AF.High); return 7; case 0x77: WriteMemory(HL.Word, AF.High); return 7;
// --- LD A, r --- // --- LD A, r ---
@@ -1272,6 +1305,11 @@ namespace Core.Cpu
case 0xF3: // DI case 0xF3: // DI
IFF1 = false; IFF1 = false;
IFF2 = false; IFF2 = false;
_eiDelay = 0; // Hard cancel any pending EI delays
return 4;
case 0xFB: // EI
_eiDelay = 2; // Ticks down across the current and subsequent instruction
return 4; return 4;
case 0xf5: //push af case 0xf5: //push af
Push(AF.Word); Push(AF.Word);
@@ -1282,10 +1320,7 @@ namespace Core.Cpu
case 0xF9: // LD SP, HL case 0xF9: // LD SP, HL
SP = HL.Word; SP = HL.Word;
return 6; return 6;
case 0xFB: // EI
IFF1 = true;
IFF2 = true;
return 4;
case 0xFD: case 0xFD:
return ExecuteFDPrefix(); return ExecuteFDPrefix();
case 0xFE: // CP n case 0xFE: // CP n
@@ -1827,7 +1862,8 @@ namespace Core.Cpu
return 16; return 16;
} }
default: default:
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); return 8;
//throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
} }
} }

View File

@@ -6,8 +6,12 @@ namespace Core.Io
{ {
public class SmsIoBus : IIoBus public class SmsIoBus : IIoBus
{ {
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public SmsVdp VideoProcessor { get; set; } public SmsVdp VideoProcessor { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public SmsApu AudioProcessor { get; set; } public SmsApu AudioProcessor { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
// Joypad State (0xFF means no buttons pressed - the SMS uses Active-Low logic!) // Joypad State (0xFF means no buttons pressed - the SMS uses Active-Low logic!)
public byte Joypad1Keyboard = 0xFF; public byte Joypad1Keyboard = 0xFF;

View File

@@ -6,6 +6,7 @@ namespace Core.Memory
{ {
public class SmsMemoryBus : IMemory public class SmsMemoryBus : IMemory
{ {
private bool _isCodemastersMapper = false; //Codemasters used their own memory mapper
// SMS has 8KB of internal Work RAM // SMS has 8KB of internal Work RAM
private readonly byte[] _workRam = new byte[0x2000]; private readonly byte[] _workRam = new byte[0x2000];
@@ -42,41 +43,101 @@ namespace Core.Memory
public byte Read(ushort address) public byte Read(ushort address)
{ {
if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF) if (address < 0xC000 && !_isCartridgeLoaded)
{ {
// SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged. return 0xFF;
if (address < 0x0400) return ReadFromCartridge(0, address); }
return ReadFromCartridge(_romBank0, address); if (address < 0x4000)
}
if (address < 0x8000) // ROM Slot 1 (0x4000 - 0x7FFF)
{ {
return ReadFromCartridge(_romBank1, address & 0x3FFF); if (!_isCodemastersMapper && address < 0x0400)
}
if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF)
{ {
// --- THE MISSING LINK: Read from Save RAM if enabled! --- return _cartridgeRom[address];
}
if (address < 0x0400)
{
return _cartridgeRom[address];
}
// Slot 0
return _cartridgeRom[(_romBank0 * 0x4000) + address];
}
if (address < 0x8000)
{
// Slot 1
return _cartridgeRom[(_romBank1 * 0x4000) + (address - 0x4000)];
}
if (address < 0xC000)
{
// Slot 2 (Or SRAM)
if ((_mapperControl & 0x08) != 0) if ((_mapperControl & 0x08) != 0)
{ return _cartridgeRam[address - 0x8000];
int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; else
int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF); return _cartridgeRom[(_romBank2 * 0x4000) + (address - 0x8000)];
return _cartridgeRam[ramOffset];
} }
// Otherwise, read from the ROM as usual // Bitwise AND perfectly forces 0xE000-0xFFFF to mirror down to 0xC000!
return ReadFromCartridge(_romBank2, address & 0x3FFF);
}
// System RAM (0xC000 - 0xFFFF)
return _workRam[address & 0x1FFF]; return _workRam[address & 0x1FFF];
} }
public void Write(ushort address, byte value)
{
// Codemasters games bank-switch by writing directly to ROM!
if (address == 0x0000 || address == 0x4000 || address == 0x8000)
{
_isCodemastersMapper = true;
int cTotalBanks = _cartridgeRom.Length / 0x4000;
if (cTotalBanks == 0) cTotalBanks = 1;
if (address == 0x0000) _romBank0 = value % cTotalBanks;
else if (address == 0x4000) _romBank1 = value % cTotalBanks;
else if (address == 0x8000) _romBank2 = value % cTotalBanks;
return;
}
if (address < 0x8000) return; // Cannot write to physical ROM
if (address < 0xC000)
{
if ((_mapperControl & 0x08) != 0)
_cartridgeRam[address - 0x8000] = value;
return;
}
// THE FIX 1: Save to RAM using the mirror mask
_workRam[address & 0x1FFF] = value;
// THE FIX 2: Mapper Control Registers live at the very end of the mirrored RAM!
if (address >= 0xFFFC)
{
// Calculate the absolute maximum number of banks inside the currently loaded ROM
int totalBanks = _cartridgeRom.Length / 0x4000;
// Safety catch for empty slots
if (totalBanks == 0) totalBanks = 1;
if (address == 0xFFFC)
{
_mapperControl = value;
}
else if (address == 0xFFFD)
{
_romBank0 = value % totalBanks; // Force the bank to stay inside the array bounds!
}
else if (address == 0xFFFE)
{
_romBank1 = value % totalBanks;
}
else if (address == 0xFFFF)
{
_romBank2 = value % totalBanks;
}
}
}
//public byte Read(ushort address) //public byte Read(ushort address)
//{ //{
// if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF) // if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF)
// { // {
// // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged. // // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged.
// // It is hardwired to Bank 0 so the interrupt handlers don't crash.
// if (address < 0x0400) return ReadFromCartridge(0, address); // if (address < 0x0400) return ReadFromCartridge(0, address);
// return ReadFromCartridge(_romBank0, address); // return ReadFromCartridge(_romBank0, address);
@@ -87,102 +148,72 @@ namespace Core.Memory
// } // }
// if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF) // if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF)
// { // {
// // Check if Cartridge RAM is enabled (Bit 3 of Mapper Control) // // --- THE MISSING LINK: Read from Save RAM if enabled! ---
// if ((_mapperControl & 0x08) != 0) // if ((_mapperControl & 0x08) != 0)
// { // {
// // Bit 2 decides if we read the first 16KB half or the second 16KB half of the RAM chip
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; // int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
// int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); // int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
// return _cartridgeRam[ramOffset]; // return _cartridgeRam[ramOffset];
// } // }
// // Otherwise, read from the ROM as usual
// return ReadFromCartridge(_romBank2, address & 0x3FFF); // return ReadFromCartridge(_romBank2, address & 0x3FFF);
// } // }
// // If we are here, we are in System RAM (0xC000 - 0xFFFF) // // System RAM (0xC000 - 0xFFFF)
// // The & 0x1FFF handles the 8KB mirroring automatically!
// return _workRam[address & 0x1FFF]; // return _workRam[address & 0x1FFF];
//} //}
//public void Write(ushort address, byte value) //public void Write(ushort address, byte value)
//{ //{
// // --- 1. CARTRIDGE RAM (Save Data) ---
// if (address < 0xC000) // if (address < 0xC000)
// { // {
// // Bypass the lock if they are trying to save their game!
// if (address >= 0x8000 && (_mapperControl & 0x08) != 0) // if (address >= 0x8000 && (_mapperControl & 0x08) != 0)
// { // {
// SramUsed = true;
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; // int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
// int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); // int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
// _cartridgeRam[ramOffset] = value; // _cartridgeRam[ramOffset] = value;
// return;
// } // }
// // You cannot write to a ROM cartridge! // // You cannot write to a ROM cartridge!
// return; // return;
// } // }
// // --- 2. SYSTEM RAM --- // // Write to System RAM
// _workRam[address & 0x1FFF] = value; // _workRam[address & 0x1FFF] = value;
// // --- 3. SEGA MAPPER (With Hardware Mirroring!) --- // // --- THE SEGA MAPPER ---
// // By checking the masked address (address & 0x1FFF), we perfectly // if (address == 0xFFFC) // <-- ADD THIS
// // catch writes to 0xFFFC-0xFFFF *and* their 0xDFFC-0xDFFF mirrors! // {
// int mapperAddress = address & 0x1FFF; // _mapperControl = value;
// }
// if (mapperAddress == 0x1FFC) _mapperControl = value; // else if (address == 0xFFFD)
// else if (mapperAddress == 0x1FFD) _romBank0 = value; // {
// else if (mapperAddress == 0x1FFE) _romBank1 = value; // _romBank0 = value;
// else if (mapperAddress == 0x1FFF) _romBank2 = value;
// } // }
public void Write(ushort address, byte value) // // Write to System RAM
{ // _workRam[address & 0x1FFF] = value;
if (address < 0xC000)
{
// Bypass the lock if they are trying to save their game!
if (address >= 0x8000 && (_mapperControl & 0x08) != 0)
{
SramUsed = true;
int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
_cartridgeRam[ramOffset] = value;
return;
}
// You cannot write to a ROM cartridge! // // --- THE SEGA MAPPER ---
return; // // If the CPU wrote to the very top of memory, it is commanding a bank swap!
} // if (address == 0xFFFD)
// {
// Write to System RAM // _romBank0 = value;
_workRam[address & 0x1FFF] = value; // }
// else if (address == 0xFFFE)
// --- THE SEGA MAPPER --- // {
if (address == 0xFFFC) // <-- ADD THIS // _romBank1 = value;
{ // }
_mapperControl = value; // else if (address == 0xFFFF)
} // {
else if (address == 0xFFFD) // _romBank2 = value;
{ // }
_romBank0 = value; //}
}
// Write to System RAM
_workRam[address & 0x1FFF] = value;
// --- THE SEGA MAPPER ---
// If the CPU wrote to the very top of memory, it is commanding a bank swap!
if (address == 0xFFFD)
{
_romBank0 = value;
}
else if (address == 0xFFFE)
{
_romBank1 = value;
}
else if (address == 0xFFFF)
{
_romBank2 = value;
}
}
private byte ReadFromCartridge(int bankIndex, int offset) private byte ReadFromCartridge(int bankIndex, int offset)
{ {
@@ -252,6 +283,7 @@ namespace Core.Memory
{ {
Array.Clear(_workRam, 0, _workRam.Length); Array.Clear(_workRam, 0, _workRam.Length);
_mapperControl = 0; _mapperControl = 0;
_isCodemastersMapper = false;
} }
} }
} }

View File

@@ -17,6 +17,7 @@ namespace Core
public SmsVdp VideoProcessor { get; private set; } public SmsVdp VideoProcessor { get; private set; }
public SmsApu AudioProcessor { get; private set; } public SmsApu AudioProcessor { get; private set; }
public ushort? Breakpoint { get; set; } = null; public ushort? Breakpoint { get; set; } = null;
private int _tStateCarryover = 0;
// NTSC SMS T-States per frame // NTSC SMS T-States per frame
public const int TStatesPerFrame = 59736; //NTSC public const int TStatesPerFrame = 59736; //NTSC
@@ -40,13 +41,15 @@ namespace Core
public void Reset() public void Reset()
{ {
MemoryBus.CleanRAMData(); MemoryBus.CleanRAMData();
VideoProcessor.Reset();
Cpu.Reset(); Cpu.Reset();
_tStateCarryover = 0;
} }
public void RunFrame() public void RunFrame()
{ {
int tStatesThisFrame = 0; int tStatesThisFrame = _tStateCarryover;
while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time
{ {
// 1. Run one CPU instruction // 1. Run one CPU instruction
@@ -58,13 +61,17 @@ namespace Core
AudioProcessor.Update(cycles); AudioProcessor.Update(cycles);
// 3. Check if the VDP is begging for attention! // 3. Check if the VDP is begging for attention!
if (VideoProcessor.InterruptPending && Cpu.IFF1) //if (VideoProcessor.InterruptPending && Cpu.IFF1)
if (VideoProcessor.InterruptPending)
{ {
int intCycles = Cpu.RequestInterrupt(); int intCycles = Cpu.RequestInterrupt();
if (intCycles > 0)
{
tStatesThisFrame += intCycles; tStatesThisFrame += intCycles;
VideoProcessor.Update(intCycles); VideoProcessor.Update(intCycles);
AudioProcessor.Update(intCycles); AudioProcessor.Update(intCycles);
} }
}
// 4. THE RESTORED BREAKPOINT TRAP // 4. THE RESTORED BREAKPOINT TRAP
if (Breakpoint.HasValue && Cpu.PC == Breakpoint.Value) if (Breakpoint.HasValue && Cpu.PC == Breakpoint.Value)

View File

@@ -40,7 +40,7 @@ namespace Core.Video
_isSecondControlByte = false; // Reading data resets the control latch _isSecondControlByte = false; // Reading data resets the control latch
byte value = _readBuffer; byte value = _readBuffer;
_readBuffer = VRAM[_controlWord & 0x3FFF]; _readBuffer = VRAM[_controlWord & 0x3FFF];
_controlWord++; IncrementVdpAddress();
return value; return value;
} }
@@ -82,7 +82,7 @@ namespace Core.Video
} }
// THE FIX: The pointer MUST auto-increment so the CPU can blast data fast! // THE FIX: The pointer MUST auto-increment so the CPU can blast data fast!
_controlWord++; IncrementVdpAddress();
} }
public void WriteControlPort(byte value) // Port 0xBF public void WriteControlPort(byte value) // Port 0xBF
@@ -104,7 +104,7 @@ namespace Core.Video
if (command == 0) // Code 0: Prep for VRAM Read if (command == 0) // Code 0: Prep for VRAM Read
{ {
_readBuffer = VRAM[_controlWord & 0x3FFF]; _readBuffer = VRAM[_controlWord & 0x3FFF];
_controlWord++; IncrementVdpAddress();
} }
else if (command == 2) // Code 2: Write to Internal VDP Register else if (command == 2) // Code 2: Write to Internal VDP Register
{ {
@@ -116,6 +116,18 @@ namespace Core.Video
} }
} }
private void IncrementVdpAddress()
{
// The VDP address register is only 14 bits
// When it increments past 0x3FFF, it rolls over to 0x0000.
// It can't overflow and corrupt the 2-bit command register (bits 14 and 15).
ushort address = (ushort)(_controlWord & 0x3FFF);
ushort command = (ushort)(_controlWord & 0xC000);
address = (ushort)((address + 1) & 0x3FFF);
_controlWord = (ushort)(command | address);
}
public byte ReadVCounter() public byte ReadVCounter()
{ {
// NTSC Math: 262 lines. Counts 0 to 218, jumps to 213 (0xD5), counts to 255. // NTSC Math: 262 lines. Counts 0 to 218, jumps to 213 (0xD5), counts to 255.
@@ -364,6 +376,17 @@ namespace Core.Video
return (255 << 24) | (r << 16) | (g << 8) | b; return (255 << 24) | (r << 16) | (g << 8) | b;
} }
public void Reset()
{
_tStateCounter = 0;
_currentScanline = 0;
_lineCounter = 0;
_statusRegister = 0x00;
_controlWord = 0;
_isSecondControlByte = false;
_readBuffer = 0;
}
public void SaveState(BinaryWriter bw) public void SaveState(BinaryWriter bw)
{ {
bw.Write(VRAM); bw.Write(VRAM);

View File

@@ -56,102 +56,104 @@
lblFPS = new Label(); lblFPS = new Label();
lblFrameTime = new Label(); lblFrameTime = new Label();
richTextBox1 = new RichTextBox(); richTextBox1 = new RichTextBox();
button1 = new Button(); btnCpuStep = new Button();
CpuRun = new Button(); CpuRun = new Button();
groupBox1 = new GroupBox(); groupBox1 = new GroupBox();
lblTone0 = new Label();
lblNoise = new Label();
lblTone2 = new Label();
lblTone1 = new Label(); lblTone1 = new Label();
lblTone2 = new Label();
lblNoise = new Label();
lblTone0 = new Label();
lblHalt = new Label();
lblR = new Label();
groupBox1.SuspendLayout(); groupBox1.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// lblAF // lblAF
// //
lblAF.AutoSize = true; lblAF.AutoSize = true;
lblAF.Location = new Point(12, 9); lblAF.Location = new Point(10, 7);
lblAF.Margin = new Padding(2, 0, 2, 0); lblAF.Margin = new Padding(2, 0, 2, 0);
lblAF.Name = "lblAF"; lblAF.Name = "lblAF";
lblAF.Size = new Size(33, 25); lblAF.Size = new Size(26, 20);
lblAF.TabIndex = 0; lblAF.TabIndex = 0;
lblAF.Text = "AF"; lblAF.Text = "AF";
// //
// lblBC // lblBC
// //
lblBC.AutoSize = true; lblBC.AutoSize = true;
lblBC.Location = new Point(11, 60); lblBC.Location = new Point(9, 48);
lblBC.Margin = new Padding(2, 0, 2, 0); lblBC.Margin = new Padding(2, 0, 2, 0);
lblBC.Name = "lblBC"; lblBC.Name = "lblBC";
lblBC.Size = new Size(33, 25); lblBC.Size = new Size(27, 20);
lblBC.TabIndex = 1; lblBC.TabIndex = 1;
lblBC.Text = "BC"; lblBC.Text = "BC";
// //
// lblDE // lblDE
// //
lblDE.AutoSize = true; lblDE.AutoSize = true;
lblDE.Location = new Point(12, 125); lblDE.Location = new Point(10, 100);
lblDE.Margin = new Padding(2, 0, 2, 0); lblDE.Margin = new Padding(2, 0, 2, 0);
lblDE.Name = "lblDE"; lblDE.Name = "lblDE";
lblDE.Size = new Size(34, 25); lblDE.Size = new Size(28, 20);
lblDE.TabIndex = 2; lblDE.TabIndex = 2;
lblDE.Text = "DE"; lblDE.Text = "DE";
// //
// lblHL // lblHL
// //
lblHL.AutoSize = true; lblHL.AutoSize = true;
lblHL.Location = new Point(12, 188); lblHL.Location = new Point(10, 150);
lblHL.Margin = new Padding(2, 0, 2, 0); lblHL.Margin = new Padding(2, 0, 2, 0);
lblHL.Name = "lblHL"; lblHL.Name = "lblHL";
lblHL.Size = new Size(33, 25); lblHL.Size = new Size(27, 20);
lblHL.TabIndex = 3; lblHL.TabIndex = 3;
lblHL.Text = "HL"; lblHL.Text = "HL";
// //
// lblPC // lblPC
// //
lblPC.AutoSize = true; lblPC.AutoSize = true;
lblPC.Location = new Point(11, 250); lblPC.Location = new Point(9, 200);
lblPC.Margin = new Padding(2, 0, 2, 0); lblPC.Margin = new Padding(2, 0, 2, 0);
lblPC.Name = "lblPC"; lblPC.Name = "lblPC";
lblPC.Size = new Size(33, 25); lblPC.Size = new Size(26, 20);
lblPC.TabIndex = 4; lblPC.TabIndex = 4;
lblPC.Text = "PC"; lblPC.Text = "PC";
// //
// lblSP // lblSP
// //
lblSP.AutoSize = true; lblSP.AutoSize = true;
lblSP.Location = new Point(11, 315); lblSP.Location = new Point(9, 252);
lblSP.Margin = new Padding(2, 0, 2, 0); lblSP.Margin = new Padding(2, 0, 2, 0);
lblSP.Name = "lblSP"; lblSP.Name = "lblSP";
lblSP.Size = new Size(32, 25); lblSP.Size = new Size(25, 20);
lblSP.TabIndex = 6; lblSP.TabIndex = 6;
lblSP.Text = "SP"; lblSP.Text = "SP";
// //
// lblFlags // lblFlags
// //
lblFlags.AutoSize = true; lblFlags.AutoSize = true;
lblFlags.Location = new Point(110, 65); lblFlags.Location = new Point(88, 52);
lblFlags.Margin = new Padding(2, 0, 2, 0); lblFlags.Margin = new Padding(2, 0, 2, 0);
lblFlags.Name = "lblFlags"; lblFlags.Name = "lblFlags";
lblFlags.Size = new Size(53, 25); lblFlags.Size = new Size(43, 20);
lblFlags.TabIndex = 7; lblFlags.TabIndex = 7;
lblFlags.Text = "Flags"; lblFlags.Text = "Flags";
// //
// lblTStates // lblTStates
// //
lblTStates.AutoSize = true; lblTStates.AutoSize = true;
lblTStates.Location = new Point(110, 9); lblTStates.Location = new Point(88, 7);
lblTStates.Margin = new Padding(2, 0, 2, 0); lblTStates.Margin = new Padding(2, 0, 2, 0);
lblTStates.Name = "lblTStates"; lblTStates.Name = "lblTStates";
lblTStates.Size = new Size(75, 25); lblTStates.Size = new Size(63, 20);
lblTStates.TabIndex = 8; lblTStates.TabIndex = 8;
lblTStates.Text = "T-States"; lblTStates.Text = "T-States";
// //
// txtMemoryStart // txtMemoryStart
// //
txtMemoryStart.Location = new Point(375, 26); txtMemoryStart.Location = new Point(300, 21);
txtMemoryStart.Margin = new Padding(2); txtMemoryStart.Margin = new Padding(2);
txtMemoryStart.Name = "txtMemoryStart"; txtMemoryStart.Name = "txtMemoryStart";
txtMemoryStart.Size = new Size(150, 31); txtMemoryStart.Size = new Size(121, 27);
txtMemoryStart.TabIndex = 9; txtMemoryStart.TabIndex = 9;
txtMemoryStart.Text = "Memory Start"; txtMemoryStart.Text = "Memory Start";
txtMemoryStart.TextAlign = HorizontalAlignment.Center; txtMemoryStart.TextAlign = HorizontalAlignment.Center;
@@ -159,10 +161,10 @@
// //
// btnRefreshMemory // btnRefreshMemory
// //
btnRefreshMemory.Location = new Point(531, 26); btnRefreshMemory.Location = new Point(425, 21);
btnRefreshMemory.Margin = new Padding(2); btnRefreshMemory.Margin = new Padding(2);
btnRefreshMemory.Name = "btnRefreshMemory"; btnRefreshMemory.Name = "btnRefreshMemory";
btnRefreshMemory.Size = new Size(112, 34); btnRefreshMemory.Size = new Size(90, 27);
btnRefreshMemory.TabIndex = 14; btnRefreshMemory.TabIndex = 14;
btnRefreshMemory.Text = "Refresh Memory"; btnRefreshMemory.Text = "Refresh Memory";
btnRefreshMemory.UseVisualStyleBackColor = true; btnRefreshMemory.UseVisualStyleBackColor = true;
@@ -170,117 +172,109 @@
// //
// txtMemoryView // txtMemoryView
// //
txtMemoryView.Location = new Point(110, 101); txtMemoryView.Location = new Point(88, 81);
txtMemoryView.Margin = new Padding(2); txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView"; txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(553, 543); txtMemoryView.Size = new Size(443, 435);
txtMemoryView.TabIndex = 15; txtMemoryView.TabIndex = 15;
txtMemoryView.Text = "Memory View Window"; txtMemoryView.Text = "Memory View Window";
// //
// lstDisassembly // lstDisassembly
// //
lstDisassembly.FormattingEnabled = true; lstDisassembly.FormattingEnabled = true;
lstDisassembly.ItemHeight = 25; lstDisassembly.Location = new Point(567, 8);
lstDisassembly.Location = new Point(709, 10);
lstDisassembly.Margin = new Padding(2); lstDisassembly.Margin = new Padding(2);
lstDisassembly.Name = "lstDisassembly"; lstDisassembly.Name = "lstDisassembly";
lstDisassembly.Size = new Size(314, 329); lstDisassembly.Size = new Size(252, 264);
lstDisassembly.TabIndex = 16; lstDisassembly.TabIndex = 16;
// //
// lstStack // lstStack
// //
lstStack.FormattingEnabled = true; lstStack.FormattingEnabled = true;
lstStack.ItemHeight = 25; lstStack.Location = new Point(823, 8);
lstStack.Location = new Point(1029, 10);
lstStack.Margin = new Padding(2); lstStack.Margin = new Padding(2);
lstStack.Name = "lstStack"; lstStack.Name = "lstStack";
lstStack.Size = new Size(162, 329); lstStack.Size = new Size(130, 264);
lstStack.TabIndex = 17; lstStack.TabIndex = 17;
// //
// label1 // label1
// //
label1.AutoSize = true; label1.AutoSize = true;
label1.Location = new Point(710, 372); label1.Location = new Point(568, 298);
label1.Margin = new Padding(4, 0, 4, 0);
label1.Name = "label1"; label1.Name = "label1";
label1.Size = new Size(97, 25); label1.Size = new Size(81, 20);
label1.TabIndex = 19; label1.TabIndex = 19;
label1.Text = "Breakpoint"; label1.Text = "Breakpoint";
// //
// txtBreakpoint // txtBreakpoint
// //
txtBreakpoint.Location = new Point(819, 369); txtBreakpoint.Location = new Point(655, 295);
txtBreakpoint.Margin = new Padding(4);
txtBreakpoint.Name = "txtBreakpoint"; txtBreakpoint.Name = "txtBreakpoint";
txtBreakpoint.Size = new Size(155, 31); txtBreakpoint.Size = new Size(125, 27);
txtBreakpoint.TabIndex = 20; txtBreakpoint.TabIndex = 20;
// //
// label2 // label2
// //
label2.AutoSize = true; label2.AutoSize = true;
label2.Location = new Point(291, 26); label2.Location = new Point(233, 21);
label2.Margin = new Padding(4, 0, 4, 0);
label2.Name = "label2"; label2.Name = "label2";
label2.Size = new Size(77, 25); label2.Size = new Size(62, 20);
label2.TabIndex = 21; label2.TabIndex = 21;
label2.Text = "Address"; label2.Text = "Address";
// //
// lblIX // lblIX
// //
lblIX.AutoSize = true; lblIX.AutoSize = true;
lblIX.Location = new Point(11, 372); lblIX.Location = new Point(9, 298);
lblIX.Margin = new Padding(2, 0, 2, 0); lblIX.Margin = new Padding(2, 0, 2, 0);
lblIX.Name = "lblIX"; lblIX.Name = "lblIX";
lblIX.Size = new Size(28, 25); lblIX.Size = new Size(22, 20);
lblIX.TabIndex = 22; lblIX.TabIndex = 22;
lblIX.Text = "IX"; lblIX.Text = "IX";
// //
// lblIY // lblIY
// //
lblIY.AutoSize = true; lblIY.AutoSize = true;
lblIY.Location = new Point(12, 430); lblIY.Location = new Point(10, 344);
lblIY.Margin = new Padding(2, 0, 2, 0); lblIY.Margin = new Padding(2, 0, 2, 0);
lblIY.Name = "lblIY"; lblIY.Name = "lblIY";
lblIY.Size = new Size(27, 25); lblIY.Size = new Size(21, 20);
lblIY.TabIndex = 23; lblIY.TabIndex = 23;
lblIY.Text = "IY"; lblIY.Text = "IY";
// //
// lblIff1 // lblIff1
// //
lblIff1.AutoSize = true; lblIff1.AutoSize = true;
lblIff1.Location = new Point(11, 533); lblIff1.Location = new Point(10, 472);
lblIff1.Margin = new Padding(4, 0, 4, 0);
lblIff1.Name = "lblIff1"; lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(45, 25); lblIff1.Size = new Size(35, 20);
lblIff1.TabIndex = 24; lblIff1.TabIndex = 24;
lblIff1.Text = "IFF1"; lblIff1.Text = "IFF1";
// //
// lblIff2 // lblIff2
// //
lblIff2.AutoSize = true; lblIff2.AutoSize = true;
lblIff2.Location = new Point(11, 586); lblIff2.Location = new Point(10, 515);
lblIff2.Margin = new Padding(4, 0, 4, 0);
lblIff2.Name = "lblIff2"; lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(45, 25); lblIff2.Size = new Size(35, 20);
lblIff2.TabIndex = 25; lblIff2.TabIndex = 25;
lblIff2.Text = "IFF2"; lblIff2.Text = "IFF2";
// //
// lblIE // lblIE
// //
lblIE.AutoSize = true; lblIE.AutoSize = true;
lblIE.Location = new Point(11, 486); lblIE.Location = new Point(10, 435);
lblIE.Margin = new Padding(4, 0, 4, 0);
lblIE.Name = "lblIE"; lblIE.Name = "lblIE";
lblIE.Size = new Size(33, 25); lblIE.Size = new Size(26, 20);
lblIE.TabIndex = 26; lblIE.TabIndex = 26;
lblIE.Text = "IM"; lblIE.Text = "IM";
// //
// btnReset // btnReset
// //
btnReset.Location = new Point(814, 409); btnReset.Location = new Point(651, 327);
btnReset.Margin = new Padding(2); btnReset.Margin = new Padding(2);
btnReset.Name = "btnReset"; btnReset.Name = "btnReset";
btnReset.Size = new Size(165, 34); btnReset.Size = new Size(132, 27);
btnReset.TabIndex = 27; btnReset.TabIndex = 27;
btnReset.Text = "Set Breakpoint"; btnReset.Text = "Set Breakpoint";
btnReset.UseVisualStyleBackColor = true; btnReset.UseVisualStyleBackColor = true;
@@ -295,61 +289,58 @@
// lblFrames // lblFrames
// //
lblFrames.AutoSize = true; lblFrames.AutoSize = true;
lblFrames.Location = new Point(14, 660); lblFrames.Location = new Point(10, 605);
lblFrames.Margin = new Padding(2, 0, 2, 0); lblFrames.Margin = new Padding(2, 0, 2, 0);
lblFrames.Name = "lblFrames"; lblFrames.Name = "lblFrames";
lblFrames.Size = new Size(149, 25); lblFrames.Size = new Size(124, 20);
lblFrames.TabIndex = 28; lblFrames.TabIndex = 28;
lblFrames.Text = "Frames Rendered"; lblFrames.Text = "Frames Rendered";
// //
// lblFPS // lblFPS
// //
lblFPS.AutoSize = true; lblFPS.AutoSize = true;
lblFPS.Location = new Point(129, 757); lblFPS.Location = new Point(102, 683);
lblFPS.Margin = new Padding(2, 0, 2, 0); lblFPS.Margin = new Padding(2, 0, 2, 0);
lblFPS.Name = "lblFPS"; lblFPS.Name = "lblFPS";
lblFPS.Size = new Size(41, 25); lblFPS.Size = new Size(32, 20);
lblFPS.TabIndex = 29; lblFPS.TabIndex = 29;
lblFPS.Text = "FPS"; lblFPS.Text = "FPS";
// //
// lblFrameTime // lblFrameTime
// //
lblFrameTime.AutoSize = true; lblFrameTime.AutoSize = true;
lblFrameTime.Location = new Point(60, 706); lblFrameTime.Location = new Point(47, 642);
lblFrameTime.Margin = new Padding(2, 0, 2, 0); lblFrameTime.Margin = new Padding(2, 0, 2, 0);
lblFrameTime.Name = "lblFrameTime"; lblFrameTime.Name = "lblFrameTime";
lblFrameTime.Size = new Size(104, 25); lblFrameTime.Size = new Size(87, 20);
lblFrameTime.TabIndex = 30; lblFrameTime.TabIndex = 30;
lblFrameTime.Text = "Frame Time"; lblFrameTime.Text = "Frame Time";
// //
// richTextBox1 // richTextBox1
// //
richTextBox1.Enabled = false; richTextBox1.Enabled = false;
richTextBox1.Location = new Point(682, 474); richTextBox1.Location = new Point(546, 379);
richTextBox1.Margin = new Padding(4);
richTextBox1.Name = "richTextBox1"; richTextBox1.Name = "richTextBox1";
richTextBox1.ReadOnly = true; richTextBox1.ReadOnly = true;
richTextBox1.Size = new Size(379, 159); richTextBox1.Size = new Size(304, 128);
richTextBox1.TabIndex = 32; richTextBox1.TabIndex = 32;
richTextBox1.Text = "Sega Master System Memory Map:\n0x0000 - 0x3FFF: ROM Slot 0 (16KB)\n0x4000 - 0x7FFF: ROM Slot 1 (16KB)\n0x8000 - 0xBFFF: ROM Slot 2 (16KB)\n0xC000 - 0xDFFF: System RAM (8KB)\n0xE000 - 0xFFFF: RAM Mirror"; richTextBox1.Text = "Sega Master System Memory Map:\n0x0000 - 0x3FFF: ROM Slot 0 (16KB)\n0x4000 - 0x7FFF: ROM Slot 1 (16KB)\n0x8000 - 0xBFFF: ROM Slot 2 (16KB)\n0xC000 - 0xDFFF: System RAM (8KB)\n0xE000 - 0xFFFF: RAM Mirror";
// //
// button1 // btnCpuStep
// //
button1.Location = new Point(1075, 756); btnCpuStep.Location = new Point(860, 605);
button1.Margin = new Padding(4); btnCpuStep.Name = "btnCpuStep";
button1.Name = "button1"; btnCpuStep.Size = new Size(94, 29);
button1.Size = new Size(118, 36); btnCpuStep.TabIndex = 33;
button1.TabIndex = 33; btnCpuStep.Text = "CPU Step";
button1.Text = "CPU Step"; btnCpuStep.UseVisualStyleBackColor = true;
button1.UseVisualStyleBackColor = true; btnCpuStep.Click += btnStep_Click;
button1.Click += btnStep_Click;
// //
// CpuRun // CpuRun
// //
CpuRun.Location = new Point(943, 757); CpuRun.Location = new Point(754, 606);
CpuRun.Margin = new Padding(4);
CpuRun.Name = "CpuRun"; CpuRun.Name = "CpuRun";
CpuRun.Size = new Size(118, 36); CpuRun.Size = new Size(94, 29);
CpuRun.TabIndex = 34; CpuRun.TabIndex = 34;
CpuRun.Text = "CPU Run"; CpuRun.Text = "CPU Run";
CpuRun.UseVisualStyleBackColor = true; CpuRun.UseVisualStyleBackColor = true;
@@ -361,57 +352,83 @@
groupBox1.Controls.Add(lblTone2); groupBox1.Controls.Add(lblTone2);
groupBox1.Controls.Add(lblNoise); groupBox1.Controls.Add(lblNoise);
groupBox1.Controls.Add(lblTone0); groupBox1.Controls.Add(lblTone0);
groupBox1.Location = new Point(266, 658); groupBox1.Location = new Point(213, 526);
groupBox1.Margin = new Padding(2);
groupBox1.Name = "groupBox1"; groupBox1.Name = "groupBox1";
groupBox1.Size = new Size(397, 222); groupBox1.Padding = new Padding(2);
groupBox1.Size = new Size(318, 178);
groupBox1.TabIndex = 35; groupBox1.TabIndex = 35;
groupBox1.TabStop = false; groupBox1.TabStop = false;
groupBox1.Text = "Audio (SN76489)"; groupBox1.Text = "Audio (SN76489)";
// //
// lblTone0 // lblTone1
// //
lblTone0.AutoSize = true; lblTone1.AutoSize = true;
lblTone0.Location = new Point(25, 48); lblTone1.Location = new Point(20, 66);
lblTone0.Name = "lblTone0"; lblTone1.Margin = new Padding(2, 0, 2, 0);
lblTone0.Size = new Size(59, 25); lblTone1.Name = "lblTone1";
lblTone0.TabIndex = 0; lblTone1.Size = new Size(49, 20);
lblTone0.Text = "Tone0"; lblTone1.TabIndex = 3;
// lblTone1.Text = "Tone1";
// lblNoise
//
lblNoise.AutoSize = true;
lblNoise.Location = new Point(25, 160);
lblNoise.Name = "lblNoise";
lblNoise.Size = new Size(57, 25);
lblNoise.TabIndex = 1;
lblNoise.Text = "Noise";
// //
// lblTone2 // lblTone2
// //
lblTone2.AutoSize = true; lblTone2.AutoSize = true;
lblTone2.Location = new Point(25, 122); lblTone2.Location = new Point(20, 98);
lblTone2.Margin = new Padding(2, 0, 2, 0);
lblTone2.Name = "lblTone2"; lblTone2.Name = "lblTone2";
lblTone2.Size = new Size(59, 25); lblTone2.Size = new Size(49, 20);
lblTone2.TabIndex = 2; lblTone2.TabIndex = 2;
lblTone2.Text = "Tone2"; lblTone2.Text = "Tone2";
// //
// lblTone1 // lblNoise
// //
lblTone1.AutoSize = true; lblNoise.AutoSize = true;
lblTone1.Location = new Point(25, 83); lblNoise.Location = new Point(20, 128);
lblTone1.Name = "lblTone1"; lblNoise.Margin = new Padding(2, 0, 2, 0);
lblTone1.Size = new Size(59, 25); lblNoise.Name = "lblNoise";
lblTone1.TabIndex = 3; lblNoise.Size = new Size(47, 20);
lblTone1.Text = "Tone1"; lblNoise.TabIndex = 1;
lblNoise.Text = "Noise";
//
// lblTone0
//
lblTone0.AutoSize = true;
lblTone0.Location = new Point(20, 38);
lblTone0.Margin = new Padding(2, 0, 2, 0);
lblTone0.Name = "lblTone0";
lblTone0.Size = new Size(49, 20);
lblTone0.TabIndex = 0;
lblTone0.Text = "Tone0";
//
// lblHalt
//
lblHalt.AutoSize = true;
lblHalt.Location = new Point(9, 564);
lblHalt.Name = "lblHalt";
lblHalt.Size = new Size(37, 20);
lblHalt.TabIndex = 36;
lblHalt.Text = "Halt";
//
// lblR
//
lblR.AutoSize = true;
lblR.Location = new Point(9, 392);
lblR.Name = "lblR";
lblR.Size = new Size(18, 20);
lblR.TabIndex = 37;
lblR.Text = "R";
// //
// DebuggerForm // DebuggerForm
// //
AutoScaleDimensions = new SizeF(10F, 25F); AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1206, 892); ClientSize = new Size(965, 714);
Controls.Add(lblR);
Controls.Add(lblHalt);
Controls.Add(groupBox1); Controls.Add(groupBox1);
Controls.Add(CpuRun); Controls.Add(CpuRun);
Controls.Add(button1); Controls.Add(btnCpuStep);
Controls.Add(richTextBox1); Controls.Add(richTextBox1);
Controls.Add(lblFrameTime); Controls.Add(lblFrameTime);
Controls.Add(lblFPS); Controls.Add(lblFPS);
@@ -475,7 +492,7 @@
private Label lblFPS; private Label lblFPS;
private Label lblFrameTime; private Label lblFrameTime;
private RichTextBox richTextBox1; private RichTextBox richTextBox1;
private Button button1; private Button btnCpuStep;
private Button CpuRun; private Button CpuRun;
public System.Windows.Forms.Timer uiUpdateTimer; public System.Windows.Forms.Timer uiUpdateTimer;
private GroupBox groupBox1; private GroupBox groupBox1;
@@ -483,6 +500,8 @@
private Label lblTone2; private Label lblTone2;
private Label lblNoise; private Label lblNoise;
private Label lblTone0; private Label lblTone0;
private Label lblHalt;
private Label lblR;
//private TextBox textBox4; //private TextBox textBox4;
} }
} }

View File

@@ -27,6 +27,7 @@ namespace Desktop
UpdateStackView(); UpdateStackView();
UpdateDisassemblyView(); UpdateDisassemblyView();
_mainForm = mainForm; _mainForm = mainForm;
CpuRun.Text = _mainForm.IsRunning ? "CPU Stop" : "CPU Run";
} }
private void CpuRun_Click(object sender, EventArgs e) private void CpuRun_Click(object sender, EventArgs e)
@@ -35,7 +36,6 @@ namespace Desktop
{ {
// Stop the machine // Stop the machine
_mainForm.StopEmulator(); _mainForm.StopEmulator();
CpuRun.Text = "CPU Run";
// Stop the live UI updates and do one final manual refresh // Stop the live UI updates and do one final manual refresh
uiUpdateTimer.Stop(); uiUpdateTimer.Stop();
@@ -45,12 +45,12 @@ namespace Desktop
{ {
// Start the machine // Start the machine
_mainForm.StartEmulator(); _mainForm.StartEmulator();
CpuRun.Text = "CPU Stop";
// Start the timer so the debugger screen updates automatically // Start the timer so the debugger screen updates automatically
// (Make sure your uiUpdateTimer Interval in the designer is set to something like 100ms) // (Make sure your uiUpdateTimer Interval in the designer is set to something like 100ms)
uiUpdateTimer.Start(); uiUpdateTimer.Start();
} }
CpuRun.Text = _mainForm.IsRunning ? "CPU Stop" : "CPU Run";
} }
private void btnStep_Click(object sender, EventArgs e) private void btnStep_Click(object sender, EventArgs e)
@@ -92,6 +92,8 @@ namespace Desktop
// Current Emulator State // Current Emulator State
private void UpdateDisplay() private void UpdateDisplay()
{ {
CpuRun.Text = _mainForm.IsRunning ? "CPU Stop" : "CPU Run";
btnCpuStep.Enabled = _mainForm.IsRunning ? false : true;
lblAF.Text = $"AF: {_cpu.AF.Word:X4}"; lblAF.Text = $"AF: {_cpu.AF.Word:X4}";
lblBC.Text = $"BC: {_cpu.BC.Word:X4}"; lblBC.Text = $"BC: {_cpu.BC.Word:X4}";
lblDE.Text = $"DE: {_cpu.DE.Word:X4}"; lblDE.Text = $"DE: {_cpu.DE.Word:X4}";
@@ -108,11 +110,12 @@ namespace Desktop
lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}"; lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms"; lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms";
lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}"; lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}";
lblHalt.Text = $"CPU Halted: {_cpu.IsHalted}";
lblR.Text = $"R: {_cpu.R:X2}";
UpdateMemoryView(); UpdateMemoryView();
UpdateStackView(); UpdateStackView();
UpdateDisassemblyView(); UpdateDisassemblyView();
// --- AUDIO REGISTERS --- // --- AUDIO REGISTERS ---//
// Use _mainForm to access the Machine, and then grab the AudioProcessor!
if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null) if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null)
{ {
ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers; ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers;
@@ -666,6 +669,9 @@ namespace Desktop
case 0xC9: case 0xC9:
mnemonic = "RET"; mnemonic = "RET";
break; break;
case 0x76:
mnemonic = "HALT!";
break;
case 0xCB: case 0xCB:
cbOp = _memoryBus.Read((ushort)(currentPc + 1)); cbOp = _memoryBus.Read((ushort)(currentPc + 1));

View File

@@ -14,10 +14,10 @@
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<StartupObject>Desktop.Program</StartupObject> <StartupObject>Desktop.Program</StartupObject>
<AssemblyName>Parsons Master System</AssemblyName> <AssemblyName>Parsons Master System 2026</AssemblyName>
<ApplicationIcon>favicon.ico</ApplicationIcon> <ApplicationIcon>favicon.ico</ApplicationIcon>
<Title>Sega Master System EMulator 2026</Title> <Title>Sega Master System EMulator 2026</Title>
<Version>0.9</Version> <Version>1.0</Version>
<Authors>Marc Parsons</Authors> <Authors>Marc Parsons</Authors>
<Company>Parsons Limited</Company> <Company>Parsons Limited</Company>
<Description>Parsons Master System 2026</Description> <Description>Parsons Master System 2026</Description>
@@ -27,6 +27,7 @@
<ItemGroup> <ItemGroup>
<None Remove="ROMS\After Burner %28UE%29 [!].sms" /> <None Remove="ROMS\After Burner %28UE%29 [!].sms" />
<None Remove="ROMS\Aladdin %28USA, Europe, Brazil%29.gg" />
<None Remove="ROMS\Alex Kidd in High Tech World %28UE%29 [!].sms" /> <None Remove="ROMS\Alex Kidd in High Tech World %28UE%29 [!].sms" />
<None Remove="ROMS\Alex Kidd in Miracle World %28UE%29 [!].sms" /> <None Remove="ROMS\Alex Kidd in Miracle World %28UE%29 [!].sms" />
<None Remove="ROMS\Back to the Future 2 %28UE%29 [!].sms" /> <None Remove="ROMS\Back to the Future 2 %28UE%29 [!].sms" />
@@ -34,6 +35,7 @@
<None Remove="ROMS\Bart vs. the Space Mutants %28UE%29 [!].sms" /> <None Remove="ROMS\Bart vs. the Space Mutants %28UE%29 [!].sms" />
<None Remove="ROMS\Bart vs. the World %28UE%29 [!].sms" /> <None Remove="ROMS\Bart vs. the World %28UE%29 [!].sms" />
<None Remove="ROMS\Black Belt %28UE%29 [!].sms" /> <None Remove="ROMS\Black Belt %28UE%29 [!].sms" />
<None Remove="ROMS\Bonanza Bros. %28Europe%29.sms" />
<None Remove="ROMS\California Games %28UE%29 [!].sms" /> <None Remove="ROMS\California Games %28UE%29 [!].sms" />
<None Remove="ROMS\California Games 2 %28UE%29 [!].sms" /> <None Remove="ROMS\California Games 2 %28UE%29 [!].sms" />
<None Remove="ROMS\Casino Games %28UE%29 [!].sms" /> <None Remove="ROMS\Casino Games %28UE%29 [!].sms" />
@@ -68,9 +70,11 @@
<None Remove="ROMS\Impossible Mission %28UE%29 [!].sms" /> <None Remove="ROMS\Impossible Mission %28UE%29 [!].sms" />
<None Remove="ROMS\Japanese SMS BIOS v2.1 %28J%29 [!].sms" /> <None Remove="ROMS\Japanese SMS BIOS v2.1 %28J%29 [!].sms" />
<None Remove="ROMS\Jurassic Park %28UE%29 [!].sms" /> <None Remove="ROMS\Jurassic Park %28UE%29 [!].sms" />
<None Remove="ROMS\Lucky Dime Caper Starring Donald Duck, The %28Europe, Brazil%29 %28En%29.sms" />
<None Remove="ROMS\Mickey Mouse - Castle of Illusion %28UE%29 [!].sms" /> <None Remove="ROMS\Mickey Mouse - Castle of Illusion %28UE%29 [!].sms" />
<None Remove="ROMS\Mickey Mouse - Land of Illusion %28UE%29 [!].sms" /> <None Remove="ROMS\Mickey Mouse - Land of Illusion %28UE%29 [!].sms" />
<None Remove="ROMS\Mickey Mouse - Legend of Illusion %28UE%29 [!].sms" /> <None Remove="ROMS\Mickey Mouse - Legend of Illusion %28UE%29 [!].sms" />
<None Remove="ROMS\Micro Machines %28Europe%29.sms" />
<None Remove="ROMS\Mortal Kombat %28UE%29 [!].sms" /> <None Remove="ROMS\Mortal Kombat %28UE%29 [!].sms" />
<None Remove="ROMS\Mortal Kombat 2 %28UE%29 [!].sms" /> <None Remove="ROMS\Mortal Kombat 2 %28UE%29 [!].sms" />
<None Remove="ROMS\Mortal Kombat 3 %28UE%29 [!].sms" /> <None Remove="ROMS\Mortal Kombat 3 %28UE%29 [!].sms" />
@@ -80,6 +84,7 @@
<None Remove="ROMS\Parlour Games %28UE%29 [!].sms" /> <None Remove="ROMS\Parlour Games %28UE%29 [!].sms" />
<None Remove="ROMS\Phantasy Star %28UE%29 [!].sms" /> <None Remove="ROMS\Phantasy Star %28UE%29 [!].sms" />
<None Remove="ROMS\Populous %28UE%29 [!].sms" /> <None Remove="ROMS\Populous %28UE%29 [!].sms" />
<None Remove="ROMS\Prince of Persia %28Europe%29.sms" />
<None Remove="ROMS\Psychic World %28UE%29 [!].sms" /> <None Remove="ROMS\Psychic World %28UE%29 [!].sms" />
<None Remove="ROMS\Psycho Fox %28UE%29 [!].sms" /> <None Remove="ROMS\Psycho Fox %28UE%29 [!].sms" />
<None Remove="ROMS\R-Type.sms" /> <None Remove="ROMS\R-Type.sms" />
@@ -90,6 +95,8 @@
<None Remove="ROMS\Sonic 1 GG.gg" /> <None Remove="ROMS\Sonic 1 GG.gg" />
<None Remove="ROMS\Sonic 2.sms" /> <None Remove="ROMS\Sonic 2.sms" />
<None Remove="ROMS\Sonic Chaos %28UE%29 [!].sms" /> <None Remove="ROMS\Sonic Chaos %28UE%29 [!].sms" />
<None Remove="ROMS\Sonic The Hedgehog - Triple Trouble %28USA, Europe, Brazil%29.gg" />
<None Remove="ROMS\Sonic The Hedgehog 2 %28World%29.gg" />
<None Remove="ROMS\Sonic.sms" /> <None Remove="ROMS\Sonic.sms" />
<None Remove="ROMS\Space Harrier %28USA%29.sms" /> <None Remove="ROMS\Space Harrier %28USA%29.sms" />
<None Remove="ROMS\Speedball %28UE%29 %28Virgin%29 [!].sms" /> <None Remove="ROMS\Speedball %28UE%29 %28Virgin%29 [!].sms" />
@@ -122,6 +129,7 @@
<EmbeddedResource Include="ROMS\After Burner (UE) [!].sms"> <EmbeddedResource Include="ROMS\After Burner (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Aladdin (USA, Europe, Brazil).gg" />
<EmbeddedResource Include="ROMS\Alex Kidd in High Tech World (UE) [!].sms"> <EmbeddedResource Include="ROMS\Alex Kidd in High Tech World (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
@@ -143,6 +151,7 @@
<EmbeddedResource Include="ROMS\Black Belt (UE) [!].sms"> <EmbeddedResource Include="ROMS\Black Belt (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Bonanza Bros. (Europe).sms" />
<EmbeddedResource Include="ROMS\California Games (UE) [!].sms"> <EmbeddedResource Include="ROMS\California Games (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
@@ -241,6 +250,7 @@
<EmbeddedResource Include="ROMS\Jurassic Park (UE) [!].sms"> <EmbeddedResource Include="ROMS\Jurassic Park (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Lucky Dime Caper Starring Donald Duck, The (Europe, Brazil) (En).sms" />
<EmbeddedResource Include="ROMS\Mickey Mouse - Castle of Illusion (UE) [!].sms"> <EmbeddedResource Include="ROMS\Mickey Mouse - Castle of Illusion (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
@@ -250,6 +260,7 @@
<EmbeddedResource Include="ROMS\Mickey Mouse - Legend of Illusion (UE) [!].sms"> <EmbeddedResource Include="ROMS\Mickey Mouse - Legend of Illusion (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Micro Machines (Europe).sms" />
<EmbeddedResource Include="ROMS\Mortal Kombat (UE) [!].sms"> <EmbeddedResource Include="ROMS\Mortal Kombat (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
@@ -273,6 +284,7 @@
<EmbeddedResource Include="ROMS\Populous (UE) [!].sms"> <EmbeddedResource Include="ROMS\Populous (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Prince of Persia (Europe).sms" />
<EmbeddedResource Include="ROMS\Psychic World (UE) [!].sms"> <EmbeddedResource Include="ROMS\Psychic World (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
@@ -301,6 +313,8 @@
<EmbeddedResource Include="ROMS\Sonic Chaos (UE) [!].sms"> <EmbeddedResource Include="ROMS\Sonic Chaos (UE) [!].sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\Sonic The Hedgehog - Triple Trouble (USA, Europe, Brazil).gg" />
<EmbeddedResource Include="ROMS\Sonic The Hedgehog 2 (World).gg" />
<EmbeddedResource Include="ROMS\Sonic.sms"> <EmbeddedResource Include="ROMS\Sonic.sms">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>

View File

@@ -32,6 +32,8 @@
fileToolStripMenuItem = new ToolStripMenuItem(); fileToolStripMenuItem = new ToolStripMenuItem();
openToolStripMenuItem = new ToolStripMenuItem(); openToolStripMenuItem = new ToolStripMenuItem();
includedToolStripMenuItem = new ToolStripMenuItem(); includedToolStripMenuItem = new ToolStripMenuItem();
masterSystemToolStripMenuItem = new ToolStripMenuItem();
gameGearToolStripMenuItem = new ToolStripMenuItem();
selectROMToolStripMenuItem1 = new ToolStripMenuItem(); selectROMToolStripMenuItem1 = new ToolStripMenuItem();
exitToolStripMenuItem = new ToolStripMenuItem(); exitToolStripMenuItem = new ToolStripMenuItem();
viewToolStripMenuItem = new ToolStripMenuItem(); viewToolStripMenuItem = new ToolStripMenuItem();
@@ -44,8 +46,6 @@
turboModeToolStripMenuItem = new ToolStripMenuItem(); turboModeToolStripMenuItem = new ToolStripMenuItem();
helpToolStripMenuItem = new ToolStripMenuItem(); helpToolStripMenuItem = new ToolStripMenuItem();
aboutToolStripMenuItem = new ToolStripMenuItem(); aboutToolStripMenuItem = new ToolStripMenuItem();
masterSystemToolStripMenuItem = new ToolStripMenuItem();
gameGearToolStripMenuItem = new ToolStripMenuItem();
menuStrip1.SuspendLayout(); menuStrip1.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
@@ -71,27 +71,39 @@
// //
openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { includedToolStripMenuItem, selectROMToolStripMenuItem1 }); openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { includedToolStripMenuItem, selectROMToolStripMenuItem1 });
openToolStripMenuItem.Name = "openToolStripMenuItem"; openToolStripMenuItem.Name = "openToolStripMenuItem";
openToolStripMenuItem.Size = new Size(224, 26); openToolStripMenuItem.Size = new Size(128, 26);
openToolStripMenuItem.Text = "Open"; openToolStripMenuItem.Text = "Open";
// //
// includedToolStripMenuItem // includedToolStripMenuItem
// //
includedToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { masterSystemToolStripMenuItem, gameGearToolStripMenuItem }); includedToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { masterSystemToolStripMenuItem, gameGearToolStripMenuItem });
includedToolStripMenuItem.Name = "includedToolStripMenuItem"; includedToolStripMenuItem.Name = "includedToolStripMenuItem";
includedToolStripMenuItem.Size = new Size(224, 26); includedToolStripMenuItem.Size = new Size(178, 26);
includedToolStripMenuItem.Text = "Included"; includedToolStripMenuItem.Text = "Included";
// //
// masterSystemToolStripMenuItem
//
masterSystemToolStripMenuItem.Name = "masterSystemToolStripMenuItem";
masterSystemToolStripMenuItem.Size = new Size(188, 26);
masterSystemToolStripMenuItem.Text = "Master System";
//
// gameGearToolStripMenuItem
//
gameGearToolStripMenuItem.Name = "gameGearToolStripMenuItem";
gameGearToolStripMenuItem.Size = new Size(188, 26);
gameGearToolStripMenuItem.Text = "Game Gear";
//
// selectROMToolStripMenuItem1 // selectROMToolStripMenuItem1
// //
selectROMToolStripMenuItem1.Name = "selectROMToolStripMenuItem1"; selectROMToolStripMenuItem1.Name = "selectROMToolStripMenuItem1";
selectROMToolStripMenuItem1.Size = new Size(224, 26); selectROMToolStripMenuItem1.Size = new Size(178, 26);
selectROMToolStripMenuItem1.Text = "Select ROM..."; selectROMToolStripMenuItem1.Text = "Select ROM...";
selectROMToolStripMenuItem1.Click += selectROMToolStripMenuItem_Click; selectROMToolStripMenuItem1.Click += selectROMToolStripMenuItem_Click;
// //
// exitToolStripMenuItem // exitToolStripMenuItem
// //
exitToolStripMenuItem.Name = "exitToolStripMenuItem"; exitToolStripMenuItem.Name = "exitToolStripMenuItem";
exitToolStripMenuItem.Size = new Size(224, 26); exitToolStripMenuItem.Size = new Size(128, 26);
exitToolStripMenuItem.Text = "Exit"; exitToolStripMenuItem.Text = "Exit";
exitToolStripMenuItem.Click += exitToolStripMenuItem_Click; exitToolStripMenuItem.Click += exitToolStripMenuItem_Click;
// //
@@ -126,28 +138,28 @@
// resetToolStripMenuItem // resetToolStripMenuItem
// //
resetToolStripMenuItem.Name = "resetToolStripMenuItem"; resetToolStripMenuItem.Name = "resetToolStripMenuItem";
resetToolStripMenuItem.Size = new Size(224, 26); resetToolStripMenuItem.Size = new Size(174, 26);
resetToolStripMenuItem.Text = "Reset"; resetToolStripMenuItem.Text = "Reset";
resetToolStripMenuItem.Click += resetToolStripMenuItem_Click; resetToolStripMenuItem.Click += resetToolStripMenuItem_Click;
// //
// saveStateToolStripMenuItem // saveStateToolStripMenuItem
// //
saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem"; saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem";
saveStateToolStripMenuItem.Size = new Size(224, 26); saveStateToolStripMenuItem.Size = new Size(174, 26);
saveStateToolStripMenuItem.Text = "Save State"; saveStateToolStripMenuItem.Text = "Save State";
saveStateToolStripMenuItem.Click += saveStateToolStripMenuItem_Click; saveStateToolStripMenuItem.Click += saveStateToolStripMenuItem_Click;
// //
// loadStateToolStripMenuItem // loadStateToolStripMenuItem
// //
loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem"; loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem";
loadStateToolStripMenuItem.Size = new Size(224, 26); loadStateToolStripMenuItem.Size = new Size(174, 26);
loadStateToolStripMenuItem.Text = "Load State"; loadStateToolStripMenuItem.Text = "Load State";
loadStateToolStripMenuItem.Click += loadStateToolStripMenuItem_Click; loadStateToolStripMenuItem.Click += loadStateToolStripMenuItem_Click;
// //
// turboModeToolStripMenuItem // turboModeToolStripMenuItem
// //
turboModeToolStripMenuItem.Name = "turboModeToolStripMenuItem"; turboModeToolStripMenuItem.Name = "turboModeToolStripMenuItem";
turboModeToolStripMenuItem.Size = new Size(224, 26); turboModeToolStripMenuItem.Size = new Size(174, 26);
turboModeToolStripMenuItem.Text = "Turbo Mode"; turboModeToolStripMenuItem.Text = "Turbo Mode";
turboModeToolStripMenuItem.Click += turboModeToolStripMenuItem_Click; turboModeToolStripMenuItem.Click += turboModeToolStripMenuItem_Click;
// //
@@ -161,20 +173,9 @@
// aboutToolStripMenuItem // aboutToolStripMenuItem
// //
aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
aboutToolStripMenuItem.Size = new Size(133, 26); aboutToolStripMenuItem.Size = new Size(224, 26);
aboutToolStripMenuItem.Text = "About"; aboutToolStripMenuItem.Text = "About";
// aboutToolStripMenuItem.Click += aboutToolStripMenuItem_Click;
// masterSystemToolStripMenuItem
//
masterSystemToolStripMenuItem.Name = "masterSystemToolStripMenuItem";
masterSystemToolStripMenuItem.Size = new Size(224, 26);
masterSystemToolStripMenuItem.Text = "Master System";
//
// gameGearToolStripMenuItem
//
gameGearToolStripMenuItem.Name = "gameGearToolStripMenuItem";
gameGearToolStripMenuItem.Size = new Size(224, 26);
gameGearToolStripMenuItem.Text = "Game Gear";
// //
// ParsonsForm1 // ParsonsForm1
// //

View File

@@ -24,7 +24,7 @@ namespace Desktop
private string _currentRomName = "No ROM"; private string _currentRomName = "No ROM";
private string _currentRomPath = ""; private string _currentRomPath = "";
private Stopwatch _stopwatch = new Stopwatch(); private Stopwatch _stopwatch = new Stopwatch();
private string _romType = ""; private string _romType = "TBC";
private bool isTurboMode = false; private bool isTurboMode = false;
public bool IsRunning { get; private set; } = false; public bool IsRunning { get; private set; } = false;
@@ -34,12 +34,18 @@ namespace Desktop
set { if (_machine != null) _machine.Breakpoint = value; } set { if (_machine != null) _machine.Breakpoint = value; }
} }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public ParsonsForm1() public ParsonsForm1()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
{ {
InitializeComponent(); InitializeComponent();
this.KeyPreview = true; this.KeyPreview = true;
#pragma warning disable CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
this.KeyDown += Form1_KeyDown; this.KeyDown += Form1_KeyDown;
#pragma warning restore CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
#pragma warning disable CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
this.KeyUp += Form1_KeyUp; this.KeyUp += Form1_KeyUp;
#pragma warning restore CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
this.DoubleBuffered = true; this.DoubleBuffered = true;
this.ResizeRedraw = true; this.ResizeRedraw = true;
} }
@@ -47,21 +53,28 @@ namespace Desktop
{ {
base.OnLoad(e); base.OnLoad(e);
this.Text = $"Parsons Master System 2026 - {_currentRomName}"; this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName}";
this.BackColor = Color.Black; this.BackColor = Color.Black;
_machine = new SmsMachine(); _machine = new SmsMachine();
_audioPlayer = new NAudioPlayer(); _audioPlayer = new NAudioPlayer();
_machine.AudioProcessor.AudioDevice = _audioPlayer; _machine.AudioProcessor.AudioDevice = _audioPlayer;
IsRunning = false;
PopulateIncludedRomsMenu(); PopulateIncludedRomsMenu();
} }
private void DrawScreen() private void DrawScreen(int[] safeFrame)
{ {
// Rapidly copy our VDP FrameBuffer into the Windows Bitmap // Rapidly the Frame into the Windows Bitmap
var data = _screenBitmap.LockBits(new Rectangle(0, 0, 256, 192), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); var data = _screenBitmap.LockBits(new Rectangle(0, 0, 256, 192), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(_machine.VideoProcessor.FrameBuffer, 0, data.Scan0, _machine.VideoProcessor.FrameBuffer.Length);
//Frame buffer draw
Marshal.Copy(safeFrame, 0, data.Scan0, safeFrame.Length);
//Direct VDP draw
//Marshal.Copy(_machine.VideoProcessor.FrameBuffer, 0, data.Scan0, _machine.VideoProcessor.FrameBuffer.Length);
_screenBitmap.UnlockBits(data); _screenBitmap.UnlockBits(data);
this.Invalidate(); this.Invalidate();
TotalFrameCount++; TotalFrameCount++;
@@ -115,42 +128,7 @@ namespace Desktop
e.Graphics.DrawImage(_screenBitmap, destRect, sourceRect, GraphicsUnit.Pixel); e.Graphics.DrawImage(_screenBitmap, destRect, sourceRect, GraphicsUnit.Pixel);
} }
} }
//protected override void OnPaint(PaintEventArgs e)
//{
// base.OnPaint(e);
// if (_screenBitmap != null && menuStrip1 != null)
// {
// // 1. Maintain perfect, chunky retro pixels
// e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
// e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
// // 2. Calculate the actual usable window space
// int topOffset = menuStrip1.Height;
// int availableWidth = this.ClientSize.Width;
// int availableHeight = this.ClientSize.Height - topOffset;
// // 3. Calculate the maximum scale factor that fits perfectly
// float scaleX = (float)availableWidth / 256f;
// float scaleY = (float)availableHeight / 192f;
// // Pick the smaller scale so the image never bleeds off the edge
// float scale = Math.Min(scaleX, scaleY);
// // 4. Calculate the new physical pixel dimensions
// int newWidth = (int)(256 * scale);
// int newHeight = (int)(192 * scale);
// // 5. Center the image (Letterboxing / Pillarboxing)
// int offsetX = (availableWidth - newWidth) / 2;
// int offsetY = topOffset + ((availableHeight - newHeight) / 2);
// Rectangle renderArea = new Rectangle(offsetX, offsetY, newWidth, newHeight);
// // 6. Draw the scaled image
// e.Graphics.DrawImage(_screenBitmap, renderArea);
// }
//}
public void StartEmulator() public void StartEmulator()
{ {
@@ -166,7 +144,8 @@ namespace Desktop
double nextFrameTargetTime = _stopwatch.Elapsed.TotalMilliseconds + TargetFrameTime; double nextFrameTargetTime = _stopwatch.Elapsed.TotalMilliseconds + TargetFrameTime;
double lastFpsUpdate = _stopwatch.Elapsed.TotalMilliseconds; double lastFpsUpdate = _stopwatch.Elapsed.TotalMilliseconds;
int framesThisSecond = 0; int framesThisSecond = 0;
try
{
while (IsRunning) while (IsRunning)
{ {
//targetFps = isTurboMode ? 1000 : 59.94; //targetFps = isTurboMode ? 1000 : 59.94;
@@ -197,14 +176,18 @@ namespace Desktop
// -------------------------------- // --------------------------------
_machine.RunFrame(); _machine.RunFrame();
int[] frozenFrame = (int[])_machine.VideoProcessor.FrameBuffer.Clone();
// 2. FIRE AND FORGET! Tell Windows to draw, but DO NOT WAIT for it to finish! // 2. FIRE AND FORGET! Tell Windows to draw, but DO NOT WAIT for it to finish!
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { DrawScreen(); }); BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { DrawScreen(frozenFrame); });
// 3. Safety catch // 3. Safety catch
if (_machine.Breakpoint.HasValue && _machine.Cpu.PC == _machine.Breakpoint.Value) if (_machine.Breakpoint.HasValue && _machine.Cpu.PC == _machine.Breakpoint.Value)
{ {
IsRunning = false; IsRunning = false;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { _debugger?.uiUpdateTimer_Tick(null, EventArgs.Empty); }); BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { _debugger?.uiUpdateTimer_Tick(null, EventArgs.Empty); });
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
break; break;
} }
@@ -247,6 +230,23 @@ namespace Desktop
nextFrameTargetTime = currentTime + TargetFrameTime; nextFrameTargetTime = currentTime + TargetFrameTime;
} }
} }
}
catch (Exception ex)
{
IsRunning = false;
this.Invoke((System.Windows.Forms.MethodInvoker)delegate
{
MessageBox.Show(
$"The Z80 CPU Panicked!\n\n" +
$"PC Address: 0x{_machine.Cpu.PC:X4}\n" +
$"Error: {ex.Message}\n\n" +
$"Stack Trace:\n{ex.StackTrace}",
"Fatal Emulator Crash",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
});
}
}); });
} }
@@ -254,55 +254,58 @@ namespace Desktop
{ {
IsRunning = false; IsRunning = false;
} }
private async void LoadRomAndStart(string filePath)
private async void LoadRomDataAndStart(byte[] romData, string fileName, string uniqueId)
{ {
StopEmulator(); StopEmulator();
if (_emulatorTask != null) if (_emulatorTask != null)
{ {
await _emulatorTask; await _emulatorTask;
} }
_romType = Path.GetExtension(filePath);
if (_romType == ".sms")
{
_machine.VideoProcessor.IsGameGear = false;
}
else if (_romType == ".gg")
{
_machine.VideoProcessor.IsGameGear = true;
}
else
throw new Exception("Incorrect ROM Type!");
_romType = _machine.VideoProcessor.IsGameGear ? "GG" : "SMS";
// 1. SAVE THE PREVIOUS GAME!
SaveCurrentSram(); SaveCurrentSram();
// 2. Load the file // Blast the bytes straight into the virtual cartridge slot!
byte[] rom = File.ReadAllBytes(filePath); _machine.LoadCartridge(romData);
// 3. Jam it into the Sega Mapper // Check the hardware mode based on the file name
_machine.LoadCartridge(rom); bool isGG = fileName.EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
_machine.VideoProcessor.IsGameGear = isGG;
// 4. Update the path tracking // We use uniqueId to track the SRAM file path (works for both real paths and resource names)
_currentRomPath = filePath; _currentRomPath = uniqueId;
_currentRomName = Path.GetFileNameWithoutExtension(filePath); _currentRomName = Path.GetFileNameWithoutExtension(fileName);
//this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName}";
// Update the window title dynamically
_romType = isGG ? "GG" : "MS";
this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName}";
// 5. LOAD THE NEW SAVE DATA FROM THE EXE FOLDER!
string savPath = GetSaveFilePath(); string savPath = GetSaveFilePath();
if (savPath != null) if (savPath != null)
{ {
_machine.MemoryBus.LoadSaveData(savPath); _machine.MemoryBus.LoadSaveData(savPath);
} }
// 6. Turn the power on!
StartEmulator(); StartEmulator();
} }
private void LoadRomAndStart(string filePath)
{
if (!File.Exists(filePath)) return;
// Read from the hard drive, then pass to the universal loader
byte[] romData = File.ReadAllBytes(filePath);
LoadRomDataAndStart(romData, Path.GetFileName(filePath), filePath);
}
private string GetSaveFilePath() private string GetSaveFilePath()
{ {
// Don't try to save if a game hasn't been loaded yet! // Don't try to save if a game hasn't been loaded yet!
if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM") if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM")
#pragma warning disable CS8603 // Possible null reference return.
return null; return null;
#pragma warning restore CS8603 // Possible null reference return.
// Application.StartupPath is the exact folder containing your .exe // Application.StartupPath is the exact folder containing your .exe
string exeFolder = Application.StartupPath; string exeFolder = Application.StartupPath;
@@ -324,37 +327,80 @@ namespace Desktop
private void PopulateIncludedRomsMenu() private void PopulateIncludedRomsMenu()
{ {
string romsDirectory = @"C:\Parsons\Local Code Projects\ParsonsMasterSystem2026\Desktop\ROMS\"; string romsDirectory = "Desktop.ROMS";
string[] files = GetFilesInResourceDirectory(romsDirectory);
if (Directory.Exists(romsDirectory)) foreach (string resourceName in files)
{ {
string[] romFiles = Directory.GetFiles(romsDirectory, "*.sms"); // 1. Strip the "Desktop.ROMS." prefix
foreach (string file in romFiles) string cleanFileName = resourceName.Replace("Desktop.ROMS.", "");
// 2. Strip the extension for a beautiful menu item (e.g. "Sonic")
string menuName = Path.GetFileNameWithoutExtension(cleanFileName);
ToolStripMenuItem romMenuItem = new ToolStripMenuItem(menuName);
// 3. Point the click event to our new extraction helper!
romMenuItem.Click += (sender, e) => LoadEmbeddedRom(resourceName);
// 4. Sort into the correct menu based on extension!
if (resourceName.EndsWith(".gg", StringComparison.OrdinalIgnoreCase))
{ {
// Create a new dropdown item for each .sms file it finds
string romName = Path.GetFileNameWithoutExtension(file);
ToolStripMenuItem romMenuItem = new ToolStripMenuItem(romName);
// When clicked, pass the file path to our helper method
romMenuItem.Click += (sender, e) => LoadRomAndStart(file);
// Add it to the "Included" submenu (make sure the name matches your designer element!)
masterSystemToolStripMenuItem.DropDownItems.Add(romMenuItem);
}
romFiles = Directory.GetFiles(romsDirectory, "*.gg");
foreach (string file in romFiles)
{
// Create a new dropdown item for each .gg file it finds
string romName = Path.GetFileNameWithoutExtension(file);
ToolStripMenuItem romMenuItem = new ToolStripMenuItem(romName);
// When clicked, pass the file path to the helper method
romMenuItem.Click += (sender, e) => LoadRomAndStart(file);
gameGearToolStripMenuItem.DropDownItems.Add(romMenuItem); gameGearToolStripMenuItem.DropDownItems.Add(romMenuItem);
} }
else if (resourceName.EndsWith(".sms", StringComparison.OrdinalIgnoreCase))
{
masterSystemToolStripMenuItem.DropDownItems.Add(romMenuItem);
} }
} }
}
private void LoadEmbeddedRom(string resourceName)
{
// 1. Strip off the ugly prefix to get the real file name for the UI and the .gg check
string cleanFileName = resourceName.Replace("Desktop.ROMS.", "");
Assembly assembly = Assembly.GetExecutingAssembly();
// 2. Open a direct pipe to the internal resource
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return;
// 3. Copy the stream entirely in RAM—never touching the hard drive!
using (MemoryStream ms = new MemoryStream())
{
stream.CopyTo(ms);
byte[] romData = ms.ToArray();
// 4. Pass the raw bytes to our universal loader
LoadRomDataAndStart(romData, cleanFileName, resourceName);
}
}
}
public static string[] GetFilesInResourceDirectory(string directoryPrefix)
{
// Get the assembly containing the embedded resources.
// Change this if your resources are in a different project/assembly.
Assembly assembly = Assembly.GetExecutingAssembly();
// Ensure the prefix ends with a dot so we don't accidentally match
// similarly named folders (e.g., matching "Folder" but not "FolderTwo")
if (!directoryPrefix.EndsWith("."))
{
directoryPrefix += ".";
}
// Get all resource names and filter by our "directory" prefix
string[] allResources = assembly.GetManifestResourceNames();
string[] filesInDirectory = allResources
.Where(resource => resource.StartsWith(directoryPrefix, StringComparison.OrdinalIgnoreCase))
.ToArray();
return filesInDirectory;
}
private void selectROMToolStripMenuItem_Click(object sender, EventArgs e) private void selectROMToolStripMenuItem_Click(object sender, EventArgs e)
{ {
@@ -484,5 +530,28 @@ namespace Desktop
turboModeToolStripMenuItem.Checked = !turboModeToolStripMenuItem.Checked; turboModeToolStripMenuItem.Checked = !turboModeToolStripMenuItem.Checked;
isTurboMode = turboModeToolStripMenuItem.Checked ? true : false; isTurboMode = turboModeToolStripMenuItem.Checked ? true : false;
} }
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show(
$"First full release of Parsons Master System Emulator 2026!\r\n\r\nFeatures (v1.0):\r\n\r\n" +
$" *Full compatibility with all Master System and Game Gear ROMs\r\n" +
$" *Full window scaling with fixed aspect ratio (SMS & GG)\r\n" +
$" *Full 4 channel SN76489 implememtation using NAudio\r\n" +
$" *SRAM auto save and load (Battery Backed RAM)\r\n" +
$" *Keyboard and XInput control pad support\r\n" +
$" *Comprehensive Debugger\r\n" +
$" *VRAM Viewer\r\n" +
$" *Single point Save State\r\n" +
$" *Turbo mode\r\n" +
$" *Includes a selection of commercial ROMs\r\n\r\n" +
$"Planned updates (v1.1):\r\n\r\n" +
$" *Architectural double buffering (prevent screen tear)\r\n" +
$" *Graphics shaders (scanlines etc...)\r\n" +
$" *Add multiple Save States per game\r\n\r\n\r\n" +
$"(c) Marc Parsons 2026","Parsons Master System 2026 v1.0",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishAot>true</PublishAot>
<OptimizationPreference>Size</OptimizationPreference>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Raylib-cs" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,125 @@
using System;
using System.IO;
using System.Numerics;
using Core;
using Raylib_cs;
namespace Parsons.SteamDeck
{
class Program
{
static void Main(string[] args)
{
// 1. Initialize Cross-Platform OpenGL Window
// THE FIX: Use PascalCase C# enums
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
Raylib.InitWindow(1280, 800, "Parsons Master System"); // Native Steam Deck Resolution
Raylib.InitAudioDevice();
// 2. Initialize Hardware-Agnostic Audio Stream (44.1kHz, 32-bit float, Mono)
AudioStream audioStream = Raylib.LoadAudioStream(44100, 32, 1);
Raylib.PlayAudioStream(audioStream);
// 3. Boot the Core Emulator
SmsMachine machine = new SmsMachine();
RaylibAudioPlayer audioPlayer = new RaylibAudioPlayer();
machine.AudioProcessor.AudioDevice = audioPlayer;
// Load a ROM (Passed via command line argument, e.g. ./Parsons.SteamDeck sonic.sms)
if (args.Length > 0 && File.Exists(args[0]))
{
machine.LoadCartridge(File.ReadAllBytes(args[0]));
bool isGG = args[0].EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
machine.VideoProcessor.IsGameGear = isGG;
}
// Lock the engine loop to 60 FPS natively
Raylib.SetTargetFPS(60);
// 4. Prepare the Video Texture Pipeline
// THE FIX: Use Color.Black
Image img = Raylib.GenImageColor(256, 192, Color.Black);
Texture2D vdpTexture = Raylib.LoadTextureFromImage(img);
byte[] pixelBuffer = new byte[256 * 192 * 4]; // Fast RGBA translation array
// --- THE MAIN GAME LOOP ---
while (!Raylib.WindowShouldClose())
{
// A. OS-Agnostic Input Polling (Works on Keyboard AND Steam Deck Controller)
// THE FIX: PascalCase KeyboardKey and GamepadButton enums
byte pad = 0xFF;
if (Raylib.IsKeyDown(KeyboardKey.Up) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp)) pad &= 0xFE;
if (Raylib.IsKeyDown(KeyboardKey.Down) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown)) pad &= 0xFD;
if (Raylib.IsKeyDown(KeyboardKey.Left) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceLeft)) pad &= 0xFB;
if (Raylib.IsKeyDown(KeyboardKey.Right) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceRight)) pad &= 0xF7;
// Steam Deck "A" Button is RightFaceDown. "B" Button is RightFaceRight.
if (Raylib.IsKeyDown(KeyboardKey.Z) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown)) pad &= 0xEF; // B1 / A Button
if (Raylib.IsKeyDown(KeyboardKey.X) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceRight)) pad &= 0xDF; // B2 / B Button
machine.IoBus.Joypad1Keyboard = pad;
// B. Execute exactly one frame of T-States
machine.RunFrame();
// C. Flush Audio Buffer to the Sound Card
if (Raylib.IsAudioStreamProcessed(audioStream))
{
float[] samples = audioPlayer.GetQueuedSamples();
if (samples.Length > 0)
{
unsafe
{
fixed (float* p = samples)
{
Raylib.UpdateAudioStream(audioStream, p, samples.Length);
}
}
}
}
// D. Fast Pixel Translation (ARGB int -> RGBA bytes)
int[] frameBuffer = machine.VideoProcessor.FrameBuffer;
for (int i = 0; i < frameBuffer.Length; i++)
{
int argb = frameBuffer[i];
pixelBuffer[i * 4 + 0] = (byte)((argb >> 16) & 0xFF); // R
pixelBuffer[i * 4 + 1] = (byte)((argb >> 8) & 0xFF); // G
pixelBuffer[i * 4 + 2] = (byte)(argb & 0xFF); // B
pixelBuffer[i * 4 + 3] = 255; // A
}
// Push bytes directly to the GPU Texture
unsafe
{
fixed (byte* p = pixelBuffer)
{
Raylib.UpdateTexture(vdpTexture, p);
}
}
// E. Render to Screen (Perfect Aspect Ratio Scaling)
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.Black);
float scale = Math.Min((float)Raylib.GetScreenWidth() / 256, (float)Raylib.GetScreenHeight() / 192);
Rectangle sourceRec = new Rectangle(0, 0, 256, 192);
Rectangle destRec = new Rectangle(
(Raylib.GetScreenWidth() - (256 * scale)) * 0.5f,
(Raylib.GetScreenHeight() - (192 * scale)) * 0.5f,
256 * scale, 192 * scale);
// Draw the VDP Frame
Raylib.DrawTexturePro(vdpTexture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
Raylib.EndDrawing();
}
// Clean up hardware resources on exit
Raylib.UnloadTexture(vdpTexture);
Raylib.UnloadAudioStream(audioStream);
Raylib.CloseAudioDevice();
Raylib.CloseWindow();
}
}
}

View File

@@ -0,0 +1,28 @@
using Core.Interfaces;
using System.Collections.Concurrent;
namespace Parsons.SteamDeck
{
public class RaylibAudioPlayer : IAudioDevice
{
private ConcurrentQueue<float> _sampleQueue = new ConcurrentQueue<float>();
public void AddSample(float sample)
{
// Queue the sample from the emulator core
_sampleQueue.Enqueue(sample);
}
public float[] GetQueuedSamples()
{
// Pull all pending samples out of the queue to send to the sound card
int count = _sampleQueue.Count;
float[] buffer = new float[count];
for (int i = 0; i < count; i++)
{
_sampleQueue.TryDequeue(out buffer[i]);
}
return buffer;
}
}
}

View File

@@ -1,12 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.14.37216.2 d17.14 VisualStudioVersion = 17.14.37216.2
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Desktop\Desktop.csproj", "{E245F3A5-E541-43BB-B64E-B4B2BB3D8C51}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Desktop\Desktop.csproj", "{E245F3A5-E541-43BB-B64E-B4B2BB3D8C51}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{B0A741E4-CE3E-4DF0-96B2-E314973F9201}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{B0A741E4-CE3E-4DF0-96B2-E314973F9201}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parsons.Steamdeck", "Parsons.Steamdeck\Parsons.Steamdeck.csproj", "{B619DF68-7EBF-4C6A-AC24-AD5250404943}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.Build.0 = Release|Any CPU {B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.Build.0 = Release|Any CPU
{B619DF68-7EBF-4C6A-AC24-AD5250404943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B619DF68-7EBF-4C6A-AC24-AD5250404943}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B619DF68-7EBF-4C6A-AC24-AD5250404943}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B619DF68-7EBF-4C6A-AC24-AD5250404943}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE