diff --git a/Assets/dll/libgambatte.dll b/Assets/dll/libgambatte.dll
index 654e96c23e..05b42a7f48 100644
Binary files a/Assets/dll/libgambatte.dll and b/Assets/dll/libgambatte.dll differ
diff --git a/Assets/dll/libgambatte.so b/Assets/dll/libgambatte.so
index b590e91d69..469aebf60a 100644
Binary files a/Assets/dll/libgambatte.so and b/Assets/dll/libgambatte.so differ
diff --git a/src/BizHawk.Client.Common/config/Config.cs b/src/BizHawk.Client.Common/config/Config.cs
index e84ee45d03..5cc794b8e3 100644
--- a/src/BizHawk.Client.Common/config/Config.cs
+++ b/src/BizHawk.Client.Common/config/Config.cs
@@ -24,7 +24,7 @@ namespace BizHawk.Client.Common
(new[] { "SNES" },
new[] { CoreNames.Faust, CoreNames.Snes9X, CoreNames.Bsnes, CoreNames.Bsnes115 }),
(new[] { "SGB" },
- new[] { CoreNames.SameBoy, CoreNames.Bsnes, CoreNames.Bsnes115}),
+ new[] { CoreNames.Gambatte, CoreNames.SameBoy, CoreNames.Bsnes, CoreNames.Bsnes115}),
(new[] { "GB", "GBC" },
new[] { CoreNames.Gambatte, CoreNames.GbHawk, CoreNames.SubGbHawk }),
(new[] { "DGB" },
diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs
index 7015c740d0..40f335074c 100644
--- a/src/BizHawk.Client.EmuHawk/MainForm.cs
+++ b/src/BizHawk.Client.EmuHawk/MainForm.cs
@@ -2004,6 +2004,7 @@ namespace BizHawk.Client.EmuHawk
case "GB":
case "GBC":
case "SGB" when Emulator is Sameboy:
+ case "SGB" when Emulator is Gameboy:
GBSubMenu.Visible = true;
break;
case "SNES" when Emulator is LibsnesCore { IsSGB: true }: // doesn't use "SGB" sysID
diff --git a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.Designer.cs b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.Designer.cs
index 29eab0f680..b281d3cbef 100644
--- a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.Designer.cs
@@ -28,84 +28,98 @@
///
private void InitializeComponent()
{
- this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
- this.buttonDefaults = new System.Windows.Forms.Button();
- this.buttonPalette = new System.Windows.Forms.Button();
- this.cbRgbdsSyntax = new System.Windows.Forms.CheckBox();
- this.checkBoxMuted = new System.Windows.Forms.CheckBox();
- this.SuspendLayout();
- //
- // propertyGrid1
- //
- this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
+ this.buttonDefaults = new System.Windows.Forms.Button();
+ this.buttonPalette = new System.Windows.Forms.Button();
+ this.cbRgbdsSyntax = new System.Windows.Forms.CheckBox();
+ this.checkBoxMuted = new System.Windows.Forms.CheckBox();
+ this.cbShowBorder = new System.Windows.Forms.CheckBox();
+ this.SuspendLayout();
+ //
+ // propertyGrid1
+ //
+ this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
- this.propertyGrid1.Location = new System.Drawing.Point(3, 3);
- this.propertyGrid1.Name = "propertyGrid1";
- this.propertyGrid1.PropertySort = System.Windows.Forms.PropertySort.NoSort;
- this.propertyGrid1.Size = new System.Drawing.Size(338, 279);
- this.propertyGrid1.TabIndex = 0;
- this.propertyGrid1.ToolbarVisible = false;
- this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.PropertyGrid1_PropertyValueChanged);
- //
- // buttonDefaults
- //
- this.buttonDefaults.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonDefaults.Location = new System.Drawing.Point(266, 288);
- this.buttonDefaults.Name = "buttonDefaults";
- this.buttonDefaults.Size = new System.Drawing.Size(75, 23);
- this.buttonDefaults.TabIndex = 1;
- this.buttonDefaults.Text = "Defaults";
- this.buttonDefaults.UseVisualStyleBackColor = true;
- this.buttonDefaults.Click += new System.EventHandler(this.ButtonDefaults_Click);
- //
- // buttonPalette
- //
- this.buttonPalette.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.buttonPalette.Location = new System.Drawing.Point(3, 288);
- this.buttonPalette.Name = "buttonPalette";
- this.buttonPalette.Size = new System.Drawing.Size(75, 23);
- this.buttonPalette.TabIndex = 2;
- this.buttonPalette.Text = "Palette...";
- this.buttonPalette.UseVisualStyleBackColor = true;
- this.buttonPalette.Click += new System.EventHandler(this.ButtonPalette_Click);
- //
- // checkBoxMuted
- //
- this.checkBoxMuted.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.checkBoxMuted.AutoSize = true;
- this.checkBoxMuted.Location = new System.Drawing.Point(82, 292);
- this.checkBoxMuted.Name = "checkBoxMuted";
- this.checkBoxMuted.Size = new System.Drawing.Size(50, 17);
- this.checkBoxMuted.TabIndex = 3;
- this.checkBoxMuted.Text = "Mute";
- this.checkBoxMuted.UseVisualStyleBackColor = true;
- this.checkBoxMuted.CheckedChanged += new System.EventHandler(this.CheckBoxMuted_CheckedChanged);
- //
- // cbRgbdsSyntax
- //
- this.cbRgbdsSyntax.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.cbRgbdsSyntax.AutoSize = true;
- this.cbRgbdsSyntax.Location = new System.Drawing.Point(130, 292);
- this.cbRgbdsSyntax.Name = "cbRgbdsSyntax";
- this.cbRgbdsSyntax.Size = new System.Drawing.Size(150, 17);
- this.cbRgbdsSyntax.TabIndex = 7;
- this.cbRgbdsSyntax.Text = "RGBDS Syntax";
- this.cbRgbdsSyntax.UseVisualStyleBackColor = true;
- this.cbRgbdsSyntax.CheckedChanged += new System.EventHandler(this.CbRgbdsSyntax_CheckedChanged);
- //
- // GBPrefControl
- //
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
- this.Controls.Add(this.cbRgbdsSyntax);
- this.Controls.Add(this.checkBoxMuted);
- this.Controls.Add(this.buttonPalette);
- this.Controls.Add(this.buttonDefaults);
- this.Controls.Add(this.propertyGrid1);
- this.Name = "GBPrefControl";
- this.Size = new System.Drawing.Size(344, 314);
- this.ResumeLayout(false);
- this.PerformLayout();
+ this.propertyGrid1.Location = new System.Drawing.Point(3, 3);
+ this.propertyGrid1.Name = "propertyGrid1";
+ this.propertyGrid1.PropertySort = System.Windows.Forms.PropertySort.NoSort;
+ this.propertyGrid1.Size = new System.Drawing.Size(402, 368);
+ this.propertyGrid1.TabIndex = 0;
+ this.propertyGrid1.ToolbarVisible = false;
+ this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.PropertyGrid1_PropertyValueChanged);
+ //
+ // buttonDefaults
+ //
+ this.buttonDefaults.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonDefaults.Location = new System.Drawing.Point(330, 377);
+ this.buttonDefaults.Name = "buttonDefaults";
+ this.buttonDefaults.Size = new System.Drawing.Size(75, 23);
+ this.buttonDefaults.TabIndex = 1;
+ this.buttonDefaults.Text = "Defaults";
+ this.buttonDefaults.UseVisualStyleBackColor = true;
+ this.buttonDefaults.Click += new System.EventHandler(this.ButtonDefaults_Click);
+ //
+ // buttonPalette
+ //
+ this.buttonPalette.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.buttonPalette.Location = new System.Drawing.Point(3, 377);
+ this.buttonPalette.Name = "buttonPalette";
+ this.buttonPalette.Size = new System.Drawing.Size(75, 23);
+ this.buttonPalette.TabIndex = 2;
+ this.buttonPalette.Text = "Palette...";
+ this.buttonPalette.UseVisualStyleBackColor = true;
+ this.buttonPalette.Click += new System.EventHandler(this.ButtonPalette_Click);
+ //
+ // cbRgbdsSyntax
+ //
+ this.cbRgbdsSyntax.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.cbRgbdsSyntax.AutoSize = true;
+ this.cbRgbdsSyntax.Location = new System.Drawing.Point(138, 381);
+ this.cbRgbdsSyntax.Name = "cbRgbdsSyntax";
+ this.cbRgbdsSyntax.Size = new System.Drawing.Size(99, 17);
+ this.cbRgbdsSyntax.TabIndex = 3;
+ this.cbRgbdsSyntax.Text = "RGBDS Syntax";
+ this.cbRgbdsSyntax.UseVisualStyleBackColor = true;
+ this.cbRgbdsSyntax.CheckedChanged += new System.EventHandler(this.CbRgbdsSyntax_CheckedChanged);
+ //
+ // checkBoxMuted
+ //
+ this.checkBoxMuted.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.checkBoxMuted.AutoSize = true;
+ this.checkBoxMuted.Location = new System.Drawing.Point(82, 381);
+ this.checkBoxMuted.Name = "checkBoxMuted";
+ this.checkBoxMuted.Size = new System.Drawing.Size(50, 17);
+ this.checkBoxMuted.TabIndex = 4;
+ this.checkBoxMuted.Text = "Mute";
+ this.checkBoxMuted.UseVisualStyleBackColor = true;
+ this.checkBoxMuted.CheckedChanged += new System.EventHandler(this.CheckBoxMuted_CheckedChanged);
+ //
+ // cbShowBorder
+ //
+ this.cbShowBorder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.cbShowBorder.AutoSize = true;
+ this.cbShowBorder.Location = new System.Drawing.Point(243, 381);
+ this.cbShowBorder.Name = "cbShowBorder";
+ this.cbShowBorder.Size = new System.Drawing.Size(87, 17);
+ this.cbShowBorder.TabIndex = 5;
+ this.cbShowBorder.Text = "Show Border";
+ this.cbShowBorder.UseVisualStyleBackColor = true;
+ this.cbShowBorder.CheckedChanged += new System.EventHandler(this.CbShowBorder_CheckedChanged);
+ //
+ // GBPrefControl
+ //
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+ this.Controls.Add(this.cbRgbdsSyntax);
+ this.Controls.Add(this.checkBoxMuted);
+ this.Controls.Add(this.cbShowBorder);
+ this.Controls.Add(this.buttonPalette);
+ this.Controls.Add(this.buttonDefaults);
+ this.Controls.Add(this.propertyGrid1);
+ this.Name = "GBPrefControl";
+ this.Size = new System.Drawing.Size(408, 403);
+ this.ResumeLayout(false);
+ this.PerformLayout();
}
@@ -116,5 +130,6 @@
private System.Windows.Forms.Button buttonPalette;
private System.Windows.Forms.CheckBox cbRgbdsSyntax;
private System.Windows.Forms.CheckBox checkBoxMuted;
+ private System.Windows.Forms.CheckBox cbShowBorder;
}
}
diff --git a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.cs b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.cs
index 28df5296f9..cfafd88dff 100644
--- a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.cs
+++ b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefControl.cs
@@ -43,6 +43,7 @@ namespace BizHawk.Client.EmuHawk
propertyGrid1.Enabled = movieSession.Movie.NotActive();
checkBoxMuted.Checked = _s.Muted;
cbRgbdsSyntax.Checked = _s.RgbdsSyntax;
+ cbShowBorder.Checked = _s.ShowBorder;
}
public void GetSettings(out Gameboy.GambatteSettings s, out Gameboy.GambatteSyncSettings ss)
@@ -86,5 +87,10 @@ namespace BizHawk.Client.EmuHawk
{
_s.RgbdsSyntax = ((CheckBox)sender).Checked;
}
+
+ private void CbShowBorder_CheckedChanged(object sender, EventArgs e)
+ {
+ _s.ShowBorder = ((CheckBox)sender).Checked;
+ }
}
}
diff --git a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.Designer.cs b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.Designer.cs
index cd3bbe6bc8..f77a3fe4a0 100644
--- a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.Designer.cs
@@ -28,58 +28,58 @@
///
private void InitializeComponent()
{
- this.buttonOK = new System.Windows.Forms.Button();
- this.buttonCancel = new System.Windows.Forms.Button();
- this.gbPrefControl1 = new GBPrefControl();
- this.SuspendLayout();
- //
- // buttonOK
- //
- this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK;
- this.buttonOK.Location = new System.Drawing.Point(280, 363);
- this.buttonOK.Name = "buttonOK";
- this.buttonOK.Size = new System.Drawing.Size(75, 23);
- this.buttonOK.TabIndex = 1;
- this.buttonOK.Text = "OK";
- this.buttonOK.UseVisualStyleBackColor = true;
- //
- // buttonCancel
- //
- this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.buttonCancel.Location = new System.Drawing.Point(361, 363);
- this.buttonCancel.Name = "buttonCancel";
- this.buttonCancel.Size = new System.Drawing.Size(75, 23);
- this.buttonCancel.TabIndex = 2;
- this.buttonCancel.Text = "Cancel";
- this.buttonCancel.UseVisualStyleBackColor = true;
- //
- // gbPrefControl1
- //
- this.gbPrefControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ this.buttonOK = new System.Windows.Forms.Button();
+ this.buttonCancel = new System.Windows.Forms.Button();
+ this.gbPrefControl1 = new BizHawk.Client.EmuHawk.GBPrefControl();
+ this.SuspendLayout();
+ //
+ // buttonOK
+ //
+ this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.buttonOK.Location = new System.Drawing.Point(280, 363);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(75, 23);
+ this.buttonOK.TabIndex = 1;
+ this.buttonOK.Text = "OK";
+ this.buttonOK.UseVisualStyleBackColor = true;
+ //
+ // buttonCancel
+ //
+ this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.buttonCancel.Location = new System.Drawing.Point(361, 363);
+ this.buttonCancel.Name = "buttonCancel";
+ this.buttonCancel.Size = new System.Drawing.Size(75, 23);
+ this.buttonCancel.TabIndex = 2;
+ this.buttonCancel.Text = "Cancel";
+ this.buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // gbPrefControl1
+ //
+ this.gbPrefControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
- this.gbPrefControl1.ColorGameBoy = false;
- this.gbPrefControl1.Location = new System.Drawing.Point(12, 12);
- this.gbPrefControl1.Name = "gbPrefControl1";
- this.gbPrefControl1.Size = new System.Drawing.Size(424, 345);
- this.gbPrefControl1.TabIndex = 0;
- //
- // GBPrefs
- //
- this.AcceptButton = this.buttonOK;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.CancelButton = this.buttonCancel;
- this.ClientSize = new System.Drawing.Size(448, 398);
- this.Controls.Add(this.buttonCancel);
- this.Controls.Add(this.buttonOK);
- this.Controls.Add(this.gbPrefControl1);
- this.Name = "GBPrefs";
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Game Boy Settings";
- this.ResumeLayout(false);
+ this.gbPrefControl1.ColorGameBoy = false;
+ this.gbPrefControl1.Location = new System.Drawing.Point(12, 12);
+ this.gbPrefControl1.Name = "gbPrefControl1";
+ this.gbPrefControl1.Size = new System.Drawing.Size(424, 345);
+ this.gbPrefControl1.TabIndex = 0;
+ //
+ // GBPrefs
+ //
+ this.AcceptButton = this.buttonOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.buttonCancel;
+ this.ClientSize = new System.Drawing.Size(448, 398);
+ this.Controls.Add(this.buttonCancel);
+ this.Controls.Add(this.buttonOK);
+ this.Controls.Add(this.gbPrefControl1);
+ this.Name = "GBPrefs";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Game Boy Settings";
+ this.ResumeLayout(false);
}
diff --git a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.cs b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.cs
index c2e4f3ca49..5c459e5dc2 100644
--- a/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.cs
+++ b/src/BizHawk.Client.EmuHawk/config/GB/GBPrefs.cs
@@ -24,7 +24,7 @@ namespace BizHawk.Client.EmuHawk
using var dlg = new GBPrefs(mainForm.DialogController);
dlg.gbPrefControl1.PutSettings(config, game, movieSession, s, ss);
- dlg.gbPrefControl1.ColorGameBoy = gb.IsCGBMode();
+ dlg.gbPrefControl1.ColorGameBoy = gb.IsCGBMode() || gb.IsSgb;
if (mainForm.ShowDialogAsChild(dlg).IsOk())
{
dlg.gbPrefControl1.GetSettings(out s, out ss);
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs
index 4d36da2364..298c06bd6f 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs
@@ -9,12 +9,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public IEmulatorServiceProvider ServiceProvider { get; }
- public ControllerDefinition ControllerDefinition => (_syncSettings.FrameLength == GambatteSyncSettings.FrameLengthType.UserDefinedFrames) ? SubGbController : GbController;
+ public ControllerDefinition ControllerDefinition { get; set; }
public bool FrameAdvance(IController controller, bool render, bool rendersound)
{
FrameAdvancePrep(controller);
uint samplesEmitted;
+ uint samplesEmittedInFrame = 0; // for sgb
switch (_syncSettings.FrameLength)
{
case GambatteSyncSettings.FrameLengthType.VBlankDrivenFrames:
@@ -25,10 +26,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (LibGambatte.gambatte_runfor(GambatteState, FrameBuffer, 160, _soundbuff, ref samplesEmitted) > 0)
{
Array.Copy(FrameBuffer, VideoBuffer, FrameBuffer.Length);
+ if (IsSgb)
+ {
+ if (LibGambatte.gambatte_updatescreenborder(GambatteState, SgbVideoBuffer, 256) != 0)
+ {
+ throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_updatescreenborder)}() returned non-zero (border error???)");
+ }
+ }
}
_cycleCount += samplesEmitted;
+ samplesEmittedInFrame += samplesEmitted;
frameOverflow = 0;
+
if (rendersound && !Muted)
{
ProcessSound((int)samplesEmitted);
@@ -43,10 +53,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (LibGambatte.gambatte_runfor(GambatteState, FrameBuffer, 160, _soundbuff, ref samplesEmitted) > 0)
{
Array.Copy(FrameBuffer, VideoBuffer, FrameBuffer.Length);
+ if (IsSgb)
+ {
+ if (LibGambatte.gambatte_updatescreenborder(GambatteState, SgbVideoBuffer, 256) != 0)
+ {
+ throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_updatescreenborder)}() returned non-zero (border error???)");
+ }
+ }
}
// account for actual number of samples emitted
_cycleCount += samplesEmitted;
+ samplesEmittedInFrame += samplesEmitted;
frameOverflow += samplesEmitted;
if (rendersound && !Muted)
@@ -76,10 +94,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (LibGambatte.gambatte_runfor(GambatteState, FrameBuffer, 160, _soundbuff, ref samplesEmitted) > 0)
{
Array.Copy(FrameBuffer, VideoBuffer, FrameBuffer.Length);
+ if (IsSgb)
+ {
+ if (LibGambatte.gambatte_updatescreenborder(GambatteState, SgbVideoBuffer, 256) != 0)
+ {
+ throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_updatescreenborder)}() returned non-zero (border error???)");
+ }
+ }
}
// account for actual number of samples emitted
_cycleCount += samplesEmitted;
+ samplesEmittedInFrame += samplesEmitted;
frameOverflow += samplesEmitted;
if (rendersound && !Muted)
@@ -96,6 +122,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
break;
}
+ if (IsSgb)
+ {
+ ProcessSgbSound((int)samplesEmittedInFrame, (rendersound && !Muted));
+ }
+
if (rendersound && !Muted)
{
ProcessSoundEnd();
@@ -108,7 +139,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public int Frame { get; private set; }
- public string SystemId => "GB";
+ public string SystemId => IsSgb ? "SGB" : "GB";
public string BoardName { get; }
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs
index 55d0d2369e..de7b9d6367 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs
@@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
_settings = o;
_disassembler.UseRGBDSSyntax = _settings.RgbdsSyntax;
- if (IsCGBMode())
+ if (IsCGBMode() || IsSgb)
{
SetCGBColors(_settings.CGBColors);
}
@@ -77,11 +77,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
public bool RgbdsSyntax;
+ ///
+ /// true to show sgb border (sgb mode only)
+ ///
+ public bool ShowBorder;
+
public GambatteSettings()
{
GBPalette = (int[])DefaultPalette.Clone();
CGBColors = GBColors.ColorType.gambatte;
RgbdsSyntax = true;
+ ShowBorder = true;
}
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISoundProvider.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISoundProvider.cs
index 422a845182..c2b779b52a 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISoundProvider.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISoundProvider.cs
@@ -37,6 +37,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
// sample pairs before resampling
private readonly short[] _soundbuff = new short[(35112 + 2064) * 2];
+ private readonly short[] _sgbsoundbuff = new short[2048 * 2];
private int _soundoutbuffcontains = 0;
@@ -45,6 +46,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private int _latchL = 0;
private int _latchR = 0;
+ private int _sgbLatchL = 0;
+ private int _sgbLatchR = 0;
+
private BlipBuffer _blipL, _blipR;
private uint _blipAccumulate;
@@ -74,6 +78,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
}
+ private void ProcessSgbSound(int nsamp, bool processSound)
+ {
+ int remainder = LibGambatte.gambatte_generatesgbsamples(GambatteState, _sgbsoundbuff, out uint samples);
+ if (remainder < 0)
+ {
+ throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_generatesgbsamples)}() returned negative (spc error???)");
+ }
+ uint t = 65 - (uint)remainder;
+ for (int i = 0; i < samples; i++, t += 65)
+ {
+ int ls = _sgbsoundbuff[i * 2] - _sgbLatchL;
+ int rs = _sgbsoundbuff[(i * 2) + 1] - _sgbLatchR;
+ if (ls != 0 && processSound)
+ {
+ _blipL.AddDelta(t, ls);
+ }
+ if (rs != 0 && processSound)
+ {
+ _blipR.AddDelta(t, rs);
+ }
+ _sgbLatchL = _sgbsoundbuff[i * 2];
+ _sgbLatchR = _sgbsoundbuff[(i * 2) + 1];
+ }
+ }
+
private void ProcessSoundEnd()
{
_blipL.EndFrame(_blipAccumulate);
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
index 8fe0fbf2ab..85c82d1010 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
@@ -1,4 +1,6 @@
-using System;
+//#define USE_UPSTREAM_STATES
+
+using System;
using System.IO;
using Newtonsoft.Json;
@@ -22,13 +24,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void SaveStateBinary(BinaryWriter writer)
{
- if (!LibGambatte.gambatte_newstatesave(GambatteState, _savebuff, _savebuff.Length))
+#if USE_UPSTREAM_STATES
+ int size = LibGambatte.gambatte_savestate(GambatteState, null, 160, _stateBuf);
+ if (size != _stateBuf.Length)
+ {
+ throw new InvalidOperationException("Savestate buffer size mismatch!");
+ }
+#else
+ if (!LibGambatte.gambatte_newstatesave(GambatteState, _stateBuf, _stateBuf.Length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_newstatesave)}() returned false");
}
+#endif
- writer.Write(_savebuff.Length);
- writer.Write(_savebuff);
+ writer.Write(_stateBuf.Length);
+ writer.Write(_stateBuf);
// other variables
writer.Write(IsLagFrame);
@@ -37,22 +47,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
writer.Write(frameOverflow);
writer.Write(_cycleCount);
writer.Write(IsCgb);
+ writer.Write(IsSgb);
}
public void LoadStateBinary(BinaryReader reader)
{
int length = reader.ReadInt32();
- if (length != _savebuff.Length)
+ if (length != _stateBuf.Length)
{
throw new InvalidOperationException("Savestate buffer size mismatch!");
}
- reader.Read(_savebuff, 0, _savebuff.Length);
+ reader.Read(_stateBuf, 0, _stateBuf.Length);
- if (!LibGambatte.gambatte_newstateload(GambatteState, _savebuff, _savebuff.Length))
+#if USE_UPSTREAM_STATES
+ if (!LibGambatte.gambatte_loadstate(GambatteState, _stateBuf, _stateBuf.Length))
+ {
+ throw new Exception($"{nameof(LibGambatte.gambatte_loadstate)}() returned false");
+ }
+#else
+ if (!LibGambatte.gambatte_newstateload(GambatteState, _stateBuf, _stateBuf.Length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_newstateload)}() returned false");
}
+#endif
// other variables
IsLagFrame = reader.ReadBoolean();
@@ -61,13 +79,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
frameOverflow = reader.ReadUInt32();
_cycleCount = reader.ReadUInt64();
IsCgb = reader.ReadBoolean();
+ IsSgb = reader.ReadBoolean();
}
- private byte[] _savebuff;
+ private byte[] _stateBuf;
private void NewSaveCoreSetBuff()
{
- _savebuff = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
+#if USE_UPSTREAM_STATES
+ _stateBuf = new byte[LibGambatte.gambatte_savestate(GambatteState, null, 160, null)];
+#else
+ _stateBuf = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
+#endif
}
private readonly JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented };
@@ -81,6 +104,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public ulong _cycleCount;
public uint frameOverflow;
public bool IsCgb;
+ public bool IsSgb;
}
internal TextState SaveState()
@@ -95,6 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
s.ExtraData.frameOverflow = frameOverflow;
s.ExtraData._cycleCount = _cycleCount;
s.ExtraData.IsCgb = IsCgb;
+ s.ExtraData.IsSgb = IsSgb;
return s;
}
@@ -109,6 +134,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
frameOverflow = s.ExtraData.frameOverflow;
_cycleCount = s.ExtraData._cycleCount;
IsCgb = s.ExtraData.IsCgb;
+ IsSgb = s.ExtraData.IsSgb;
}
}
}
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs
index 4b828a3b88..17f1ab92bb 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs
@@ -11,7 +11,12 @@
/// stored image of most recent frame
///
private readonly int[] VideoBuffer = CreateVideoBuffer();
-
+
+ ///
+ /// stored image of most recent sgb frame
+ ///
+ private readonly int[] SgbVideoBuffer = new int[256 * 244];
+
private static int[] CreateVideoBuffer()
{
var b = new int[160 * 144];
@@ -24,16 +29,16 @@
public int[] GetVideoBuffer()
{
- return VideoBuffer;
+ return (IsSgb && _settings.ShowBorder) ? SgbVideoBuffer : VideoBuffer;
}
- public int VirtualWidth => 160; // only sgb changes this, which we don't emulate here
+ public int VirtualWidth => (IsSgb && _settings.ShowBorder) ? 256 : 160;
- public int VirtualHeight => 144;
+ public int VirtualHeight => (IsSgb && _settings.ShowBorder) ? 224 : 144;
- public int BufferWidth => 160;
+ public int BufferWidth => (IsSgb && _settings.ShowBorder) ? 256 : 160;
- public int BufferHeight => 144;
+ public int BufferHeight => (IsSgb && _settings.ShowBorder) ? 224 : 144;
public int BackgroundColor => 0;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
index 1782e7f345..819d7abea2 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
@@ -20,6 +21,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
[CoreConstructor("GB")]
[CoreConstructor("GBC")]
+ [CoreConstructor("SGB")]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
{
var ser = new BasicServiceProvider(this);
@@ -66,6 +68,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
break;
}
+ if (game.System == "SGB")
+ {
+ flags &= ~(LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG);
+ flags |= LibGambatte.LoadFlags.SGB_MODE;
+ IsSgb = true;
+ }
+
if (_syncSettings.MulticartCompat)
{
flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
@@ -77,7 +86,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE;
biosSystemId = IsCgb ? "GBC" : "GB";
- biosId = ((_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA) && !_syncSettings.PatchBIOS) ? "AGB" : "World";
+ biosId = (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA) && !_syncSettings.PatchBIOS ? "AGB" : "World";
+
+ if (IsSgb)
+ {
+ biosId = "SGB2";
+ }
if (_syncSettings.EnableBIOS)
{
@@ -86,15 +100,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
if (!IsCgb)
{
- bios[0xFD] ^= 0xFE; // patch from dmg<->mgb
+ bios[0xFD] ^= 0xFE; // patch from dmg<->mgb, or sgb1<->sgb2
}
else if (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA)
{
// patch from cgb->agb re
bios[0xF3] ^= 0x03;
- for (var i = 0xF5; i < 0xFB;)
+ for (var i = 0xF5; i < 0xFB; i++)
{
- bios[i] = bios[++i];
+ bios[i] = bios[i + 1];
}
bios[0xFB] ^= 0x74;
}
@@ -118,6 +132,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)");
}
+ if (IsSgb)
+ {
+ ResetStallTicks = 128 * (2 << 14);
+ }
+ else if (_syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA))
+ {
+ ResetStallTicks = 485808; // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time.
+ }
+ else
+ {
+ ResetStallTicks = 0;
+ }
+
// set real default colors (before anyone mucks with them at all)
PutSettings((GambatteSettings)settings ?? new GambatteSettings());
@@ -196,6 +223,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
_cdCallback = new LibGambatte.CDCallback(CDCallbackProc);
+ ControllerDefinition = CreateControllerDefinition(IsSgb, _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames);
+
NewSaveCoreSetBuff();
}
catch
@@ -219,6 +248,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
private const uint TICKSPERSECOND = 2097152;
+ ///
+ /// number of reset stall ticks
+ ///
+ private uint ResetStallTicks { get; set; } = 0;
+
///
/// keep a copy of the input callback delegate so it doesn't get GCed
///
@@ -237,8 +271,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public bool IsCgb { get; set; }
+ public bool IsSgb { get; set; }
- // all cycle counts are relative to a 2*1024*1024 mhz refclock
+ // all cycle counts are relative to a 2*1024*1024 hz refclock
///
/// total cycles actually executed
@@ -253,29 +288,52 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public long CycleCount => (long)_cycleCount;
public double ClockRate => TICKSPERSECOND;
- public static readonly ControllerDefinition GbController = new ControllerDefinition
+ public static ControllerDefinition CreateControllerDefinition(bool sgb, bool sub)
{
- Name = "Gameboy Controller",
- BoolButtons =
+ var ret = sub
+ ? new ControllerDefinition { Name = "Subframe Gameboy Controller" }.AddAxis("Input Length", 0.RangeTo(35112), 35112)
+ : new ControllerDefinition { Name = "Gameboy Controller" };
+ if (sgb)
{
- "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
+ for (int i = 0; i < 4; i++)
+ {
+ ret.BoolButtons.AddRange(
+ new[] { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A" }
+ .Select(s => $"P{i + 1} {s}"));
+ }
+ ret.BoolButtons.Add("Power");
}
- };
-
- public static readonly ControllerDefinition SubGbController = new ControllerDefinition
- {
- Name = "Subframe Gameboy Controller",
- BoolButtons =
+ else
{
- "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
+ ret.BoolButtons.AddRange(new[] { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" });
}
- }.AddAxis("Input Length", 0.RangeTo(35112), 35112);
+ return ret;
+ }
private LibGambatte.Buttons ControllerCallback()
{
InputCallbacks.Call();
IsLagFrame = false;
- return CurrentButtons;
+ if (IsSgb)
+ {
+ int index = LibGambatte.gambatte_getjoypadindex(GambatteState);
+ uint b = (uint)CurrentButtons;
+ b >>= index * 8;
+ b &= 0xFF;
+ if ((b & 0x30) == 0x30) // snes software side blocks l+r
+ {
+ b &= ~0x30u;
+ }
+ if ((b & 0xC0) == 0xC0) // same for u+d
+ {
+ b &= ~0xC0u;
+ }
+ return (LibGambatte.Buttons)b;
+ }
+ else
+ {
+ return CurrentButtons;
+ }
}
///
@@ -310,16 +368,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
// needs to match the reverse order of Libgambatte's button enum
- private static readonly IReadOnlyList BUTTON_ORDER_IN_BITMASK = new[] { "Down", "Up", "Left", "Right", "Start", "Select", "B", "A" };
+ private static readonly IReadOnlyList GB_BUTTON_ORDER_IN_BITMASK = new[] { "Down", "Up", "Left", "Right", "Start", "Select", "B", "A" };
+
+ // input callback assumes buttons are ordered from first player in lsbs to last player in msbs
+ private static readonly IReadOnlyList SGB_BUTTON_ORDER_IN_BITMASK = new[] {
+ "P4 Down", "P4 Up", "P4 Left", "P4 Right", "P4 Start", "P4 Select", "P4 B", "P4 A",
+ "P3 Down", "P3 Up", "P3 Left", "P3 Right", "P3 Start", "P3 Select", "P3 B", "P3 A",
+ "P2 Down", "P2 Up", "P2 Left", "P2 Right", "P2 Start", "P2 Select", "P2 B", "P2 A",
+ "P1 Down", "P1 Up", "P1 Left", "P1 Right", "P1 Start", "P1 Select", "P1 B", "P1 A" };
internal void FrameAdvancePrep(IController controller)
{
// update our local copy of the controller data
- byte b = 0;
- for (var i = 0; i < 8; i++)
+ uint b = 0;
+ if (IsSgb)
{
- b <<= 1;
- if (controller.IsPressed(BUTTON_ORDER_IN_BITMASK[i])) b |= 1;
+ for (var i = 0; i < 32; i++)
+ {
+ b <<= 1;
+ if (controller.IsPressed(SGB_BUTTON_ORDER_IN_BITMASK[i])) b |= 1;
+ }
+ }
+ else
+ {
+ for (var i = 0; i < 8; i++)
+ {
+ b <<= 1;
+ if (controller.IsPressed(GB_BUTTON_ORDER_IN_BITMASK[i])) b |= 1;
+ }
}
CurrentButtons = (LibGambatte.Buttons)b;
@@ -328,8 +404,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (controller.IsPressed("Power"))
{
- bool stall = _syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA); // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time.
- LibGambatte.gambatte_reset(GambatteState, stall ? 485808u : 0u);
+ LibGambatte.gambatte_reset(GambatteState, ResetStallTicks);
}
if (Tracer.IsEnabled())
@@ -779,8 +854,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
else
{
LinkConnected = false;
- printer.Disconnect();
- printer = null;
+ if (printer != null) // have no idea how this is ever null???
+ {
+ printer.Disconnect();
+ printer = null;
+ }
}
}
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
index 985840295c..2a16894ff4 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
@@ -71,8 +71,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private const int SampPerFrame = 35112;
- private readonly SaveController LCont = new SaveController(Gameboy.GbController);
- private readonly SaveController RCont = new SaveController(Gameboy.GbController);
+ private readonly SaveController LCont = new SaveController(Gameboy.CreateControllerDefinition(false, false));
+ private readonly SaveController RCont = new SaveController(Gameboy.CreateControllerDefinition(false, false));
public bool IsCGBMode(bool right)
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
index d98d31cf57..767099e975 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -93,6 +93,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe int gambatte_runfor(IntPtr core, int* videobuf, int pitch, short* soundbuf, ref uint samples);
+ [DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int gambatte_updatescreenborder(IntPtr core, int[] videobuf, int pitch);
+
+ [DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int gambatte_generatesgbsamples(IntPtr core, short[] soundbuf, out uint samples);
+
///
/// Reset to initial state.
/// Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again.
@@ -156,6 +162,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setinputgetter(IntPtr core, InputGetter getinput);
+ ///
+ /// Gets which SGB controller is in use, 0 indexed.
+ ///
+ /// opaque state pointer
+ [DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int gambatte_getjoypadindex(IntPtr core);
+
///
/// type of the read\write memory callbacks
///
@@ -405,6 +418,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gambatte_getmemoryarea(IntPtr core, MemoryAreas which, ref IntPtr data, ref int length);
+ ///
+ /// Saves emulator state to the buffer given by 'stateBuf'.
+ ///
+ /// opaque state pointer
+ /// 160x144 RGB32 (native endian) video frame buffer or 0. Used for saving a thumbnail.
+ /// pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
+ /// buffer for savestate
+ /// size
+ [DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int gambatte_savestate(IntPtr core, int[] videoBuf, int pitch, byte[] stateBuf);
+
+ ///
+ /// Loads emulator state from the buffer given by 'stateBuf' of size 'size'.
+ ///
+ /// opaque state pointer
+ /// buffer for savestate
+ /// size of savestate buffer
+ /// success
+ [DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool gambatte_loadstate(IntPtr core, byte[] stateBuf, int size);
+
///
/// read a single byte from the cpu bus. this includes all ram, rom, mmio, etc, as it is visible to the cpu (including mappers).
/// while there is no cycle cost to these reads, there may be other side effects! use at your own risk.
diff --git a/submodules/gambatte b/submodules/gambatte
index c3e44f8fde..dc09a5882c 160000
--- a/submodules/gambatte
+++ b/submodules/gambatte
@@ -1 +1 @@
-Subproject commit c3e44f8fde85317219f233d0f2876e2a2fb354ee
+Subproject commit dc09a5882c962c426b5c56f4dec541e5fd22335e