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 {