This commit is contained in:
2026-05-19 01:00:28 +01:00
parent 66f18d0510
commit cd5e18a2ac
4 changed files with 90 additions and 62 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
};
#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()
#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[3] = 0x0F;

View File

@@ -6,8 +6,12 @@ namespace Core.Io
{
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; }
#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; }
#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!)
public byte Joypad1Keyboard = 0xFF;

View File

@@ -14,10 +14,10 @@
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<StartupObject>Desktop.Program</StartupObject>
<AssemblyName>Parsons Master System</AssemblyName>
<AssemblyName>Parsons Master System 2026</AssemblyName>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<Title>Sega Master System EMulator 2026</Title>
<Version>0.9</Version>
<Version>1.0</Version>
<Authors>Marc Parsons</Authors>
<Company>Parsons Limited</Company>
<Description>Parsons Master System 2026</Description>

View File

@@ -34,12 +34,18 @@ namespace Desktop
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()
#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();
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;
#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;
#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.ResizeRedraw = true;
}
@@ -115,42 +121,7 @@ namespace Desktop
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()
{
@@ -204,7 +175,9 @@ namespace Desktop
if (_machine.Breakpoint.HasValue && _machine.Cpu.PC == _machine.Breakpoint.Value)
{
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); });
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
break;
}
@@ -302,7 +275,9 @@ namespace Desktop
{
// Don't try to save if a game hasn't been loaded yet!
if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM")
#pragma warning disable CS8603 // Possible null reference return.
return null;
#pragma warning restore CS8603 // Possible null reference return.
// Application.StartupPath is the exact folder containing your .exe
string exeFolder = Application.StartupPath;
@@ -324,38 +299,85 @@ namespace Desktop
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");
foreach (string file in romFiles)
// 1. Strip the "Desktop.ROMS." prefix
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) => ExtractAndLoadEmbeddedRom(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);
}
else if (resourceName.EndsWith(".sms", StringComparison.OrdinalIgnoreCase))
{
masterSystemToolStripMenuItem.DropDownItems.Add(romMenuItem);
}
}
}
private void ExtractAndLoadEmbeddedRom(string resourceName)
{
// 1. Strip off the ugly "Desktop.ROMS." prefix to get the real file name
string cleanFileName = resourceName.Replace("Desktop.ROMS.", "");
// 2. Create a physical path right next to your emulator .exe
string physicalPath = Path.Combine(Application.StartupPath, cleanFileName);
// 3. If we haven't extracted this game yet, extract it now!
if (!File.Exists(physicalPath))
{
Assembly assembly = Assembly.GetExecutingAssembly();
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return;
using (FileStream fileStream = new FileStream(physicalPath, FileMode.Create))
{
stream.CopyTo(fileStream);
}
}
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
// 4. Now that it is a REAL file on the hard drive, pass it to your normal loader!
LoadRomAndStart(physicalPath);
}
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)
{
using (OpenFileDialog ofd = new OpenFileDialog())