Updated VDP to handle GameGear. Added Turbo mode. Versio 1.0 me thinks!
This commit is contained in:
143
Desktop/Form1.cs
143
Desktop/Form1.cs
@@ -18,14 +18,14 @@ namespace Desktop
|
||||
private Bitmap _screenBitmap = new Bitmap(256, 192, PixelFormat.Format32bppArgb);
|
||||
private NAudioPlayer _audioPlayer;
|
||||
private Task _emulatorTask;
|
||||
private double TargetFrameTime = 16.667f; //NTSC
|
||||
//private double TargetFrameTime = 20; //PAL
|
||||
public int TotalFrameCount = 0;
|
||||
public double FrameTime { get; private set; } = 0;
|
||||
public double FramesPerSecond { get; private set; } = 0;
|
||||
private string _currentRomName = "No ROM";
|
||||
private string _currentRomPath = "";
|
||||
private Stopwatch _stopwatch = new System.Diagnostics.Stopwatch();
|
||||
private Stopwatch _stopwatch = new Stopwatch();
|
||||
private string _romType = "";
|
||||
private bool isTurboMode = false;
|
||||
public bool IsRunning { get; private set; } = false;
|
||||
|
||||
public ushort? Breakpoint
|
||||
@@ -37,21 +37,17 @@ namespace Desktop
|
||||
public ParsonsForm1()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// These are perfectly safe for the Visual Studio Designer!
|
||||
this.KeyPreview = true;
|
||||
this.KeyDown += Form1_KeyDown;
|
||||
this.KeyUp += Form1_KeyUp;
|
||||
this.DoubleBuffered = true;
|
||||
this.ResizeRedraw = true;
|
||||
}
|
||||
|
||||
// The Designer ignores this completely, but the compiled game runs it instantly!
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
|
||||
this.Text = $"Parsons Master System - {_currentRomName}";
|
||||
this.Text = $"Parsons Master System 2026 - {_currentRomName}";
|
||||
|
||||
this.BackColor = Color.Black;
|
||||
_machine = new SmsMachine();
|
||||
@@ -70,6 +66,7 @@ namespace Desktop
|
||||
this.Invalidate();
|
||||
TotalFrameCount++;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
@@ -80,44 +77,90 @@ namespace Desktop
|
||||
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;
|
||||
// 2. THE LENS LENS: Determine what part of the VDP buffer we actually want to show!
|
||||
// (Safely check if the machine/video processor exists yet)
|
||||
bool isGG = _machine?.VideoProcessor?.IsGameGear ?? false;
|
||||
|
||||
int sourceWidth = isGG ? 160 : 256;
|
||||
int sourceHeight = isGG ? 144 : 192;
|
||||
int sourceX = isGG ? 48 : 0; // Skip 48 pixels in!
|
||||
int sourceY = isGG ? 24 : 0; // Skip 24 pixels down!
|
||||
|
||||
// Create a rectangle defining the specific pixels to extract from the raw frame buffer
|
||||
Rectangle sourceRect = new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight);
|
||||
|
||||
// 3. Calculate the maximum scale factor for the cropped image
|
||||
float scaleX = (float)availableWidth / sourceWidth;
|
||||
float scaleY = (float)availableHeight / sourceHeight;
|
||||
|
||||
// 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);
|
||||
int newWidth = (int)(sourceWidth * scale);
|
||||
int newHeight = (int)(sourceHeight * 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);
|
||||
// Create a rectangle defining exactly where on the Windows form to draw the extracted pixels
|
||||
Rectangle destRect = new Rectangle(offsetX, offsetY, newWidth, newHeight);
|
||||
|
||||
// 6. Draw the scaled image
|
||||
e.Graphics.DrawImage(_screenBitmap, renderArea);
|
||||
// 6. Blast the perfectly cropped and scaled bitmap onto the screen!
|
||||
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()
|
||||
{
|
||||
if (IsRunning) return;
|
||||
IsRunning = true;
|
||||
TotalFrameCount = 0;
|
||||
|
||||
double targetFps = _machine.VideoProcessor.IsPalRegion ? 50.0 : 59.92274;
|
||||
double TargetFrameTime = 1000.0 / targetFps;
|
||||
|
||||
_emulatorTask = Task.Run(() =>
|
||||
{
|
||||
double targetFps = 59.94;
|
||||
double TargetFrameTime = 1000.0 / targetFps;
|
||||
_stopwatch.Restart();
|
||||
|
||||
double nextFrameTargetTime = _stopwatch.Elapsed.TotalMilliseconds + TargetFrameTime;
|
||||
@@ -126,7 +169,13 @@ namespace Desktop
|
||||
|
||||
while (IsRunning)
|
||||
{
|
||||
// Mark exactly when the emulator starts thinking
|
||||
//targetFps = isTurboMode ? 1000 : 59.94;
|
||||
//TargetFrameTime = 1000.0 / targetFps;
|
||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
||||
{
|
||||
targetFps = isTurboMode ? 1000 : 59.94;
|
||||
TargetFrameTime = 1000.0 / targetFps;
|
||||
});
|
||||
double frameStartTime = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
|
||||
// --- POLL PHYSICAL CONTROLLER ---
|
||||
@@ -187,7 +236,7 @@ namespace Desktop
|
||||
|
||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
||||
{
|
||||
this.Text = $"Parsons Master System 2026 - {_currentRomName} [FPS/FT: {FramesPerSecond:F0}/{FrameTime:F1}]";
|
||||
this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName} [FPS: {FramesPerSecond:F0}]";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,6 +261,18 @@ namespace Desktop
|
||||
{
|
||||
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();
|
||||
@@ -225,7 +286,7 @@ namespace Desktop
|
||||
// 4. Update the path tracking
|
||||
_currentRomPath = filePath;
|
||||
_currentRomName = Path.GetFileNameWithoutExtension(filePath);
|
||||
this.Text = $"Parsons Master System - {_currentRomName}";
|
||||
//this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName}";
|
||||
|
||||
// 5. LOAD THE NEW SAVE DATA FROM THE EXE FOLDER!
|
||||
string savPath = GetSaveFilePath();
|
||||
@@ -263,13 +324,11 @@ namespace Desktop
|
||||
|
||||
private void PopulateIncludedRomsMenu()
|
||||
{
|
||||
// The folder you used for Golden Axe Warrior
|
||||
string romsDirectory = @"C:\Parsons\Local Code Projects\ParsonsMasterSystem2026\Desktop\ROMS\";
|
||||
|
||||
if (Directory.Exists(romsDirectory))
|
||||
{
|
||||
string[] romFiles = Directory.GetFiles(romsDirectory, "*.sms");
|
||||
|
||||
foreach (string file in romFiles)
|
||||
{
|
||||
// Create a new dropdown item for each .sms file it finds
|
||||
@@ -280,8 +339,20 @@ namespace Desktop
|
||||
romMenuItem.Click += (sender, e) => LoadRomAndStart(file);
|
||||
|
||||
// Add it to the "Included" submenu (make sure the name matches your designer element!)
|
||||
includedToolStripMenuItem.DropDownItems.Add(romMenuItem);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +361,7 @@ namespace Desktop
|
||||
using (OpenFileDialog ofd = new OpenFileDialog())
|
||||
{
|
||||
ofd.Title = "Select Master System ROM";
|
||||
ofd.Filter = "SMS ROMs (*.sms)|*.sms|All Files (*.*)|*.*";
|
||||
ofd.Filter = "SMS ROMs (*.sms)|*.sms|GG ROMs (*.gg)|*.gg|All Files (*.*)|*.*";
|
||||
|
||||
if (ofd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
@@ -324,11 +395,7 @@ namespace Desktop
|
||||
_vramViewer.Show();
|
||||
}
|
||||
|
||||
private void includedToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
@@ -412,14 +479,10 @@ namespace Desktop
|
||||
StartEmulator();
|
||||
}
|
||||
|
||||
private void pALRegionToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private void turboModeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (pALRegionToolStripMenuItem.Checked)
|
||||
_machine.VideoProcessor.IsPalRegion = true;
|
||||
else _machine.VideoProcessor.IsPalRegion= false;
|
||||
|
||||
pALRegionToolStripMenuItem.Checked = !pALRegionToolStripMenuItem.Checked;
|
||||
|
||||
turboModeToolStripMenuItem.Checked = !turboModeToolStripMenuItem.Checked;
|
||||
isTurboMode = turboModeToolStripMenuItem.Checked ? true : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user