diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs
index 921fb8917d..8e6b134d63 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs
@@ -250,5 +250,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
///
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_writebus(uint addr, byte val);
+
+ ///
+ /// type of the scanline callback
+ ///
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void ScanlineCallback();
+
+ ///
+ /// set a callback to coincide with vcount interrupts
+ ///
+ /// null to clear
+ /// 0-227, 160 occurring first in a frame
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_setscanlinecallback(ScanlineCallback callback, int scanline);
}
}
diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs
index f0bef366ab..0e624d6883 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs
@@ -25,6 +25,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
{
if (bios.Length != 16384)
throw new Exception("GBA bios must be exactly 16384 bytes!");
+ if (rom.Length > 32 * 1024 * 1024)
+ throw new Exception("Rom is too big!");
Init();
LibMeteor.libmeteor_hardreset();
LibMeteor.libmeteor_loadbios(bios, (uint)bios.Length);
@@ -46,6 +48,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
LibMeteor.libmeteor_frameadvance();
if (IsLagFrame)
LagCount++;
+ if (EndOfFrameCallback != null)
+ EndOfFrameCallback();
}
public int Frame { get; private set; }
@@ -376,6 +380,40 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
CoreInputComm.Tracer.Put(msg);
}
+ Action EndOfFrameCallback = null;
+ LibMeteor.ScanlineCallback scanlinecb = null;
+
+ ///
+ ///
+ ///
+ /// null to cancel
+ /// 0-227, null = end of frame
+ public void SetScanlineCallback(Action callback, int? scanline)
+ {
+ if (callback == null)
+ {
+ LibMeteor.libmeteor_setscanlinecallback(null, 400);
+ EndOfFrameCallback = null;
+ scanlinecb = null;
+ }
+ else if (scanline == null)
+ {
+ LibMeteor.libmeteor_setscanlinecallback(null, 400);
+ EndOfFrameCallback = callback;
+ scanlinecb = null;
+ }
+ else if (scanline >= 0 && scanline <= 227)
+ {
+ scanlinecb = new LibMeteor.ScanlineCallback(callback);
+ LibMeteor.libmeteor_setscanlinecallback(scanlinecb, (int)scanline);
+ EndOfFrameCallback = null;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException("Scanline must be in [0, 227]!");
+ }
+ }
+
void Init()
{
if (attachedcore != null)
diff --git a/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs b/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs
index 70422c79ae..2b7ab296d1 100644
--- a/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs
+++ b/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs
@@ -32,6 +32,13 @@
this.panel1 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label();
this.buttonShowWidget = new System.Windows.Forms.Button();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.radioButtonFrame = new System.Windows.Forms.RadioButton();
+ this.radioButtonScanline = new System.Windows.Forms.RadioButton();
+ this.radioButtonManual = new System.Windows.Forms.RadioButton();
+ this.hScrollBar1 = new System.Windows.Forms.HScrollBar();
+ this.buttonRefresh = new System.Windows.Forms.Button();
+ this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// listBoxWidgets
@@ -72,11 +79,80 @@
this.buttonShowWidget.UseVisualStyleBackColor = true;
this.buttonShowWidget.Click += new System.EventHandler(this.buttonShowWidget_Click);
//
+ // groupBox1
+ //
+ this.groupBox1.Controls.Add(this.buttonRefresh);
+ this.groupBox1.Controls.Add(this.hScrollBar1);
+ this.groupBox1.Controls.Add(this.radioButtonManual);
+ this.groupBox1.Controls.Add(this.radioButtonScanline);
+ this.groupBox1.Controls.Add(this.radioButtonFrame);
+ this.groupBox1.Location = new System.Drawing.Point(15, 220);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(117, 173);
+ this.groupBox1.TabIndex = 4;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "Refresh";
+ //
+ // radioButtonFrame
+ //
+ this.radioButtonFrame.AutoSize = true;
+ this.radioButtonFrame.Location = new System.Drawing.Point(6, 19);
+ this.radioButtonFrame.Name = "radioButtonFrame";
+ this.radioButtonFrame.Size = new System.Drawing.Size(54, 17);
+ this.radioButtonFrame.TabIndex = 0;
+ this.radioButtonFrame.Text = "Frame";
+ this.radioButtonFrame.UseVisualStyleBackColor = true;
+ this.radioButtonFrame.CheckedChanged += new System.EventHandler(this.radioButtonFrame_CheckedChanged);
+ //
+ // radioButtonScanline
+ //
+ this.radioButtonScanline.AutoSize = true;
+ this.radioButtonScanline.Location = new System.Drawing.Point(6, 42);
+ this.radioButtonScanline.Name = "radioButtonScanline";
+ this.radioButtonScanline.Size = new System.Drawing.Size(66, 17);
+ this.radioButtonScanline.TabIndex = 1;
+ this.radioButtonScanline.Text = "Scanline";
+ this.radioButtonScanline.UseVisualStyleBackColor = true;
+ this.radioButtonScanline.CheckedChanged += new System.EventHandler(this.radioButtonScanline_CheckedChanged);
+ //
+ // radioButtonManual
+ //
+ this.radioButtonManual.AutoSize = true;
+ this.radioButtonManual.Location = new System.Drawing.Point(6, 81);
+ this.radioButtonManual.Name = "radioButtonManual";
+ this.radioButtonManual.Size = new System.Drawing.Size(60, 17);
+ this.radioButtonManual.TabIndex = 2;
+ this.radioButtonManual.TabStop = true;
+ this.radioButtonManual.Text = "Manual";
+ this.radioButtonManual.UseVisualStyleBackColor = true;
+ this.radioButtonManual.CheckedChanged += new System.EventHandler(this.radioButtonManual_CheckedChanged);
+ //
+ // hScrollBar1
+ //
+ this.hScrollBar1.LargeChange = 20;
+ this.hScrollBar1.Location = new System.Drawing.Point(3, 62);
+ this.hScrollBar1.Maximum = 246;
+ this.hScrollBar1.Name = "hScrollBar1";
+ this.hScrollBar1.Size = new System.Drawing.Size(111, 16);
+ this.hScrollBar1.TabIndex = 3;
+ this.hScrollBar1.ValueChanged += new System.EventHandler(this.hScrollBar1_ValueChanged);
+ //
+ // buttonRefresh
+ //
+ this.buttonRefresh.Location = new System.Drawing.Point(6, 104);
+ this.buttonRefresh.Name = "buttonRefresh";
+ this.buttonRefresh.Size = new System.Drawing.Size(75, 23);
+ this.buttonRefresh.TabIndex = 4;
+ this.buttonRefresh.Text = "Refresh";
+ this.buttonRefresh.UseVisualStyleBackColor = true;
+ this.buttonRefresh.Click += new System.EventHandler(this.buttonRefresh_Click);
+ //
// GBAGPUView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(636, 405);
+ this.Controls.Add(this.groupBox1);
this.Controls.Add(this.buttonShowWidget);
this.Controls.Add(this.label1);
this.Controls.Add(this.panel1);
@@ -84,7 +160,10 @@
this.Name = "GBAGPUView";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
this.Text = "GBA GPU Viewer";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.GBAGPUView_FormClosed);
this.Load += new System.EventHandler(this.GBAGPUView_Load);
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@@ -96,6 +175,12 @@
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button buttonShowWidget;
+ private System.Windows.Forms.GroupBox groupBox1;
+ private System.Windows.Forms.Button buttonRefresh;
+ private System.Windows.Forms.HScrollBar hScrollBar1;
+ private System.Windows.Forms.RadioButton radioButtonManual;
+ private System.Windows.Forms.RadioButton radioButtonScanline;
+ private System.Windows.Forms.RadioButton radioButtonFrame;
}
}
\ No newline at end of file
diff --git a/BizHawk.MultiClient/GBAtools/GBAGPUView.cs b/BizHawk.MultiClient/GBAtools/GBAGPUView.cs
index 734f49fc49..31559a97a4 100644
--- a/BizHawk.MultiClient/GBAtools/GBAGPUView.cs
+++ b/BizHawk.MultiClient/GBAtools/GBAGPUView.cs
@@ -35,6 +35,9 @@ namespace BizHawk.MultiClient.GBAtools
Buffer.BlockCopy(tmp, 0, ColorConversion, sizeof(int) * tmp.Length, sizeof(int) * tmp.Length);
GenerateWidgets();
+ radioButtonFrame.Checked = true;
+ hScrollBar1_ValueChanged(null, null);
+ RecomputeRefresh();
}
#region drawing primitives
@@ -659,7 +662,22 @@ namespace BizHawk.MultiClient.GBAtools
return;
if (gba != null)
{
- DrawEverything();
+ if (cbscanline_emu != cbscanline)
+ {
+ cbscanline_emu = cbscanline;
+ if (cbscanline == -2) // manual, do nothing
+ {
+ gba.SetScanlineCallback(null, null);
+ }
+ else if (cbscanline == -1) // end of frame
+ {
+ gba.SetScanlineCallback(DrawEverything, null);
+ }
+ else
+ {
+ gba.SetScanlineCallback(DrawEverything, cbscanline);
+ }
+ }
}
}
@@ -688,5 +706,65 @@ namespace BizHawk.MultiClient.GBAtools
{
ShowSelectedWidget();
}
+
+ int cbscanline;
+ int cbscanline_emu = 500;
+
+ void RecomputeRefresh()
+ {
+ if (radioButtonFrame.Checked)
+ {
+ hScrollBar1.Enabled = false;
+ buttonRefresh.Enabled = false;
+ cbscanline = -1;
+ }
+ else if (radioButtonScanline.Checked)
+ {
+ hScrollBar1.Enabled = true;
+ buttonRefresh.Enabled = false;
+ cbscanline = (hScrollBar1.Value + 160) % 228;
+ }
+ else if (radioButtonManual.Checked)
+ {
+ hScrollBar1.Enabled = false;
+ buttonRefresh.Enabled = true;
+ cbscanline = -2;
+ }
+ }
+
+ private void radioButtonFrame_CheckedChanged(object sender, EventArgs e)
+ {
+ RecomputeRefresh();
+ }
+
+ private void radioButtonScanline_CheckedChanged(object sender, EventArgs e)
+ {
+ RecomputeRefresh();
+ }
+
+ private void hScrollBar1_ValueChanged(object sender, EventArgs e)
+ {
+ cbscanline = (hScrollBar1.Value + 160) % 228;
+ radioButtonScanline.Text = "Scanline " + cbscanline;
+ }
+
+ private void radioButtonManual_CheckedChanged(object sender, EventArgs e)
+ {
+ RecomputeRefresh();
+ }
+
+ private void buttonRefresh_Click(object sender, EventArgs e)
+ {
+ DrawEverything();
+ }
+
+ private void GBAGPUView_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ if (gba != null)
+ {
+ gba.SetScanlineCallback(null, null);
+ gba = null;
+ }
+ }
}
}
diff --git a/BizHawk.MultiClient/GBtools/GBGPUView.Designer.cs b/BizHawk.MultiClient/GBtools/GBGPUView.Designer.cs
index 70e64e6ff3..c471265e2c 100644
--- a/BizHawk.MultiClient/GBtools/GBGPUView.Designer.cs
+++ b/BizHawk.MultiClient/GBtools/GBGPUView.Designer.cs
@@ -211,7 +211,7 @@
// hScrollBarScanline
//
this.hScrollBarScanline.Location = new System.Drawing.Point(76, 45);
- this.hScrollBarScanline.Maximum = 153;
+ this.hScrollBarScanline.Maximum = 162;
this.hScrollBarScanline.Name = "hScrollBarScanline";
this.hScrollBarScanline.Size = new System.Drawing.Size(192, 16);
this.hScrollBarScanline.TabIndex = 21;
diff --git a/BizHawk.MultiClient/output/dll/libmeteor.dll b/BizHawk.MultiClient/output/dll/libmeteor.dll
index 1f05942894..75ccb90052 100644
Binary files a/BizHawk.MultiClient/output/dll/libmeteor.dll and b/BizHawk.MultiClient/output/dll/libmeteor.dll differ
diff --git a/libmeteor/cinterface.cpp b/libmeteor/cinterface.cpp
index 66b2f93df8..5421b44bb5 100644
--- a/libmeteor/cinterface.cpp
+++ b/libmeteor/cinterface.cpp
@@ -217,3 +217,21 @@ EXPORT void libmeteor_writebus(uint32_t addr, uint8_t val)
{
AMeteor::_memory.Write8(addr, val);
}
+
+int slcallbackline = 400;
+void (*slcallback)() = NULL;
+
+EXPORT void libmeteor_setscanlinecallback(void (*callback)(), int scanline)
+{
+ if (!callback)
+ slcallbackline = 400;
+ else
+ slcallbackline = scanline;
+ slcallback = callback;
+}
+
+void scanlinecallback_bizhawk()
+{
+ if (slcallback)
+ slcallback();
+}
diff --git a/libmeteor/source/debug.hpp b/libmeteor/source/debug.hpp
index c47e0813a3..64d0973709 100644
--- a/libmeteor/source/debug.hpp
+++ b/libmeteor/source/debug.hpp
@@ -32,6 +32,8 @@ void abort_bizhawk(const char *msg);
void keyupdate_bizhawk();
extern bool traceenabled;
void trace_bizhawk(std::string msg);
+extern int slcallbackline;
+void scanlinecallback_bizhawk();
#if 0
#define met_abort(str) \
diff --git a/libmeteor/source/lcd.cpp b/libmeteor/source/lcd.cpp
index a8d045c80c..74c5b4c7ba 100644
--- a/libmeteor/source/lcd.cpp
+++ b/libmeteor/source/lcd.cpp
@@ -94,7 +94,8 @@ namespace AMeteor
else // no vcount match
dispstat &= ~(uint16_t)0x4;
// scanline callback for frontend
- // ...
+ if (slcallbackline == vcount)
+ scanlinecallback_bizhawk();
}
else // if we were not H-Blanking
{