snes-add scanline render callbacks and add scanline selector to graphics debugger

This commit is contained in:
zeromus 2012-09-22 05:03:52 +00:00
parent 6f28003f48
commit 03cb238ae3
9 changed files with 254 additions and 51 deletions

View File

@ -2,7 +2,7 @@
//TODO
//libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
//rename snes.dll so nobody thinks it's a stock snes.dll (we'll be editing it substantially at some point)
// edit - this is a lot of work
//wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailable
using System;
@ -17,27 +17,27 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
public unsafe static class LibsnesDll
{
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern string snes_library_id();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_library_revision_major();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_library_revision_minor();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_init();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_power();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_reset();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_run();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_term();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_unload_cartridge();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_load_cartridge_normal(
[MarshalAs(UnmanagedType.LPStr)]
string rom_xml,
@ -53,49 +53,53 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public delegate ushort snes_input_state_t(int port, int device, int index, int id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_audio_sample_t(ushort left, ushort right);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_scanlineStart_t(int line);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_video_refresh(snes_video_refresh_t video_refresh);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_input_poll(snes_input_poll_t input_poll);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_input_state(snes_input_state_t input_state);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_audio_sample(snes_audio_sample_t audio_sample);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_check_cartridge(
[MarshalAs(UnmanagedType.LPArray)] byte[] rom_data,
int rom_size);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern SNES_REGION snes_get_region();
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_get_memory_size(SNES_MEMORY id);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr snes_get_memory_data(SNES_MEMORY id);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_serialize_size();
[return: MarshalAs(UnmanagedType.U1)]
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool snes_serialize(IntPtr data, int size);
[return: MarshalAs(UnmanagedType.U1)]
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool snes_unserialize(IntPtr data, int size);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_layer_enable(int layer, int priority,
[MarshalAs(UnmanagedType.U1)]
bool enable
);
[DllImport("snes.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_peek_logical_register(SNES_REG reg);
public enum SNES_REG : int
@ -180,7 +184,43 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
}
}
public class ScanlineHookManager
{
public void Register(object tag, Action<int> callback)
{
var rr = new RegistrationRecord();
rr.tag = tag;
rr.callback = callback;
Unregister(tag);
records.Add(rr);
OnHooksChanged();
}
public int HookCount { get { return records.Count; } }
public virtual void OnHooksChanged() { }
public void Unregister(object tag)
{
records.RemoveAll((r) => r.tag == tag);
}
public void HandleScanline(int scanline)
{
foreach (var rr in records) rr.callback(scanline);
}
List<RegistrationRecord> records = new List<RegistrationRecord>();
class RegistrationRecord
{
public object tag;
public int scanline;
public Action<int> callback;
}
}
public unsafe class LibsnesCore : IEmulator, IVideoProvider, ISoundProvider
{
bool disposed = false;
@ -195,6 +235,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_poll(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_state(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_scanlineStart(null);
LibsnesDll.snes_unload_cartridge();
LibsnesDll.snes_term();
@ -205,11 +246,36 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
//that will be necessary to get it saving to disk
byte[] disposedSaveRam;
//we can only have one active snes core at a time, due to libsnes being so static.
//so we'll track the current one here and detach the previous one whenever a new one is booted up.
static LibsnesCore CurrLibsnesCore;
public class MyScanlineHookManager : ScanlineHookManager
{
public MyScanlineHookManager(LibsnesCore core)
{
this.core = core;
}
LibsnesCore core;
public override void OnHooksChanged()
{
core.OnScanlineHooksChanged();
}
}
public MyScanlineHookManager ScanlineHookManager;
void OnScanlineHooksChanged()
{
if (disposed) return;
if (ScanlineHookManager.HookCount == 0) LibsnesDll.snes_set_scanlineStart(null);
else LibsnesDll.snes_set_scanlineStart(scanlineStart_cb);
}
void snes_scanlineStart(int line)
{
ScanlineHookManager.HandleScanline(line);
}
public LibsnesCore(byte[] romData)
{
//attach this core as the current
@ -217,6 +283,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
CurrLibsnesCore.Dispose();
CurrLibsnesCore = this;
ScanlineHookManager = new MyScanlineHookManager(this);
LibsnesDll.snes_init();
vidcb = new LibsnesDll.snes_video_refresh_t(snes_video_refresh);
@ -231,6 +299,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
soundcb = new LibsnesDll.snes_audio_sample_t(snes_audio_sample);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(soundcb);
scanlineStart_cb = new LibsnesDll.snes_scanlineStart_t(snes_scanlineStart);
// start up audio resampler
InitAudio();
@ -253,6 +323,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
LibsnesDll.snes_input_poll_t pollcb;
LibsnesDll.snes_input_state_t inputcb;
LibsnesDll.snes_audio_sample_t soundcb;
LibsnesDll.snes_scanlineStart_t scanlineStart_cb;
ushort snes_input_state(int port, int device, int index, int id)
{

View File

@ -245,6 +245,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
int tileEntry = vram[tidx * 2];
int src = tileEntry * 64;
if (tileEntry != 0)
{
int zzz = 9;
}
for (int py = 0, pix=src; py < 8; py++)
{
for (int px = 0; px < 8; px++, pix++)

View File

@ -2210,7 +2210,11 @@ namespace BizHawk.MultiClient
NESNameTableViewer1.UpdateValues();
NESPPU1.UpdateValues();
PCEBGViewer1.UpdateValues();
SNESGraphicsDebugger1.UpdateValues();
}
public void UpdateToolsLoadstate()
{
SNESGraphicsDebugger1.UpdateToolsLoadstate();
}
/// <summary>
@ -2222,6 +2226,7 @@ namespace BizHawk.MultiClient
//frame of execution in its list view.
LuaConsole1.LuaImp.FrameRegisterAfter();
TAStudio1.UpdateValues();
SNESGraphicsDebugger1.UpdateToolsAfter();
}
private unsafe Image MakeScreenshotImage()
@ -2376,6 +2381,7 @@ namespace BizHawk.MultiClient
Global.OSD.ClearGUIText();
UpdateToolsBefore();
UpdateToolsAfter();
UpdateToolsLoadstate();
Global.OSD.AddMessage("Loaded state: " + name);
}
else

View File

@ -95,6 +95,7 @@
this.saveWindowPositionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.groupBox4 = new System.Windows.Forms.GroupBox();
this.radioButton15 = new System.Windows.Forms.RadioButton();
this.radioButton14 = new System.Windows.Forms.RadioButton();
this.groupBox5 = new System.Windows.Forms.GroupBox();
this.tabctrlDetails = new System.Windows.Forms.TabControl();
@ -111,9 +112,11 @@
this.tabPage2 = new System.Windows.Forms.TabPage();
this.label17 = new System.Windows.Forms.Label();
this.label18 = new System.Windows.Forms.Label();
this.nudScanline = new System.Windows.Forms.NumericUpDown();
this.sliderScanline = new System.Windows.Forms.TrackBar();
this.label19 = new System.Windows.Forms.Label();
this.paletteViewer = new BizHawk.MultiClient.SNESGraphicsViewer();
this.viewer = new BizHawk.MultiClient.SNESGraphicsViewer();
this.radioButton15 = new System.Windows.Forms.RadioButton();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.menuStrip1.SuspendLayout();
@ -122,6 +125,8 @@
this.groupBox5.SuspendLayout();
this.tabctrlDetails.SuspendLayout();
this.tpPalette.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudScanline)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.sliderScanline)).BeginInit();
this.SuspendLayout();
//
// label1
@ -136,6 +141,7 @@
// groupBox1
//
this.groupBox1.Controls.Add(this.rbBG4);
this.groupBox1.Controls.Add(this.label18);
this.groupBox1.Controls.Add(this.rbBG3);
this.groupBox1.Controls.Add(this.rbBG2);
this.groupBox1.Controls.Add(this.rbBG1);
@ -876,6 +882,18 @@
this.groupBox4.TabIndex = 35;
this.groupBox4.TabStop = false;
//
// radioButton15
//
this.radioButton15.AutoSize = true;
this.radioButton15.Enabled = false;
this.radioButton15.Location = new System.Drawing.Point(169, 83);
this.radioButton15.Name = "radioButton15";
this.radioButton15.Size = new System.Drawing.Size(58, 17);
this.radioButton15.TabIndex = 33;
this.radioButton15.TabStop = true;
this.radioButton15.Text = "Mode7";
this.radioButton15.UseVisualStyleBackColor = true;
//
// radioButton14
//
this.radioButton14.AutoSize = true;
@ -891,7 +909,7 @@
// groupBox5
//
this.groupBox5.Controls.Add(this.paletteViewer);
this.groupBox5.Location = new System.Drawing.Point(6, 378);
this.groupBox5.Location = new System.Drawing.Point(6, 387);
this.groupBox5.Name = "groupBox5";
this.groupBox5.Size = new System.Drawing.Size(319, 328);
this.groupBox5.TabIndex = 36;
@ -905,7 +923,7 @@
this.tabctrlDetails.Location = new System.Drawing.Point(6, 238);
this.tabctrlDetails.Name = "tabctrlDetails";
this.tabctrlDetails.SelectedIndex = 0;
this.tabctrlDetails.Size = new System.Drawing.Size(319, 134);
this.tabctrlDetails.Size = new System.Drawing.Size(282, 143);
this.tabctrlDetails.TabIndex = 0;
//
// tpPalette
@ -922,7 +940,7 @@
this.tpPalette.Location = new System.Drawing.Point(4, 22);
this.tpPalette.Name = "tpPalette";
this.tpPalette.Padding = new System.Windows.Forms.Padding(3);
this.tpPalette.Size = new System.Drawing.Size(311, 108);
this.tpPalette.Size = new System.Drawing.Size(274, 117);
this.tpPalette.TabIndex = 0;
this.tpPalette.Text = "Palette";
this.tpPalette.UseVisualStyleBackColor = true;
@ -1020,7 +1038,7 @@
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(311, 108);
this.tabPage2.Size = new System.Drawing.Size(274, 117);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "tabPage2";
this.tabPage2.UseVisualStyleBackColor = true;
@ -1038,11 +1056,47 @@
// label18
//
this.label18.AutoSize = true;
this.label18.Location = new System.Drawing.Point(233, 162);
this.label18.Location = new System.Drawing.Point(161, 170);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(70, 26);
this.label18.Size = new System.Drawing.Size(56, 26);
this.label18.TabIndex = 38;
this.label18.Text = "Todo: BG pal\r\ninfo";
this.label18.Text = "Todo: BG \r\npal info";
//
// nudScanline
//
this.nudScanline.Location = new System.Drawing.Point(242, 159);
this.nudScanline.Maximum = new decimal(new int[] {
224,
0,
0,
0});
this.nudScanline.Name = "nudScanline";
this.nudScanline.Size = new System.Drawing.Size(77, 20);
this.nudScanline.TabIndex = 39;
this.nudScanline.ValueChanged += new System.EventHandler(this.nudScanline_ValueChanged);
//
// sliderScanline
//
this.sliderScanline.AutoSize = false;
this.sliderScanline.Location = new System.Drawing.Point(294, 180);
this.sliderScanline.Maximum = 224;
this.sliderScanline.Name = "sliderScanline";
this.sliderScanline.Orientation = System.Windows.Forms.Orientation.Vertical;
this.sliderScanline.Size = new System.Drawing.Size(30, 210);
this.sliderScanline.TabIndex = 40;
this.sliderScanline.Text = "label14";
this.sliderScanline.TickFrequency = 16;
this.sliderScanline.Value = 224;
this.sliderScanline.ValueChanged += new System.EventHandler(this.sliderScanline_ValueChanged);
//
// label19
//
this.label19.AutoSize = true;
this.label19.Location = new System.Drawing.Point(242, 182);
this.label19.Name = "label19";
this.label19.Size = new System.Drawing.Size(48, 13);
this.label19.TabIndex = 41;
this.label19.Text = "Scanline";
//
// paletteViewer
//
@ -1065,24 +1119,14 @@
this.viewer.TabIndex = 17;
this.viewer.TabStop = false;
//
// radioButton15
//
this.radioButton15.AutoSize = true;
this.radioButton15.Enabled = false;
this.radioButton15.Location = new System.Drawing.Point(169, 83);
this.radioButton15.Name = "radioButton15";
this.radioButton15.Size = new System.Drawing.Size(58, 17);
this.radioButton15.TabIndex = 33;
this.radioButton15.TabStop = true;
this.radioButton15.Text = "Mode7";
this.radioButton15.UseVisualStyleBackColor = true;
//
// SNESGraphicsDebugger
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(905, 727);
this.Controls.Add(this.label18);
this.Controls.Add(this.label19);
this.Controls.Add(this.sliderScanline);
this.Controls.Add(this.nudScanline);
this.Controls.Add(this.label17);
this.Controls.Add(this.tabctrlDetails);
this.Controls.Add(this.groupBox5);
@ -1108,6 +1152,8 @@
this.tabctrlDetails.ResumeLayout(false);
this.tpPalette.ResumeLayout(false);
this.tpPalette.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.nudScanline)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.sliderScanline)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@ -1201,5 +1247,8 @@
private System.Windows.Forms.Label lblDetailsOBJOrBG;
private System.Windows.Forms.TextBox txtPaletteDetailsIndexHex;
private System.Windows.Forms.RadioButton radioButton15;
private System.Windows.Forms.NumericUpDown nudScanline;
private System.Windows.Forms.TrackBar sliderScanline;
private System.Windows.Forms.Label label19;
}
}

View File

@ -27,6 +27,15 @@ namespace BizHawk.MultiClient
tabctrlDetails.SelectedIndex = 1;
}
LibsnesCore currentSnesCore;
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (currentSnesCore != null)
currentSnesCore.ScanlineHookManager.Unregister(this);
currentSnesCore = null;
}
string FormatBpp(int bpp)
{
if (bpp == 0) return "---";
@ -47,11 +56,57 @@ namespace BizHawk.MultiClient
else return string.Format("@{0} ({1}K)", address.ToHexString(4), address / 1024);
}
public void UpdateValues()
public void UpdateToolsAfter()
{
SyncCore();
}
public void UpdateToolsLoadstate()
{
SyncCore();
UpdateValues();
}
private void nudScanline_ValueChanged(object sender, EventArgs e)
{
if (suppression) return;
SyncCore();
suppression = true;
sliderScanline.Value = 224 - (int)nudScanline.Value;
suppression = false;
}
private void sliderScanline_ValueChanged(object sender, EventArgs e)
{
if (suppression) return;
SyncCore();
suppression = true;
nudScanline.Value = 224 - sliderScanline.Value;
suppression = false;
}
void SyncCore()
{
LibsnesCore core = Global.Emulator as LibsnesCore;
if (currentSnesCore != core && currentSnesCore != null)
currentSnesCore.ScanlineHookManager.Unregister(this);
currentSnesCore = core;
if(currentSnesCore != null)
currentSnesCore.ScanlineHookManager.Register(this, ScanlineHook);
}
void ScanlineHook(int line)
{
int target = (int)nudScanline.Value;
if (target == line) UpdateValues();
}
void UpdateValues()
{
if (!this.IsHandleCreated || this.IsDisposed) return;
var snes = Global.Emulator as LibsnesCore;
if (snes == null) return;
if (currentSnesCore == null) return;
var gd = new SNESGraphicsDecoder();
var si = gd.ScanScreenInfo();
@ -315,6 +370,5 @@ namespace BizHawk.MultiClient
//cd.ShowDialog(this);
}
}
}

View File

@ -84,6 +84,8 @@ void PPU::scanline() {
regs.range_over = false;
}
interface->scanlineStart(line);
if(line == 1) {
//mosaic reset
for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1;

View File

@ -7,6 +7,9 @@ struct Interface {
virtual void message(const string &text);
virtual time_t currentTime();
virtual time_t randomSeed();
//zero 27-sep-2012
virtual void scanlineStart(int line) = 0;
};
extern Interface *interface;

View File

@ -40,6 +40,13 @@ struct Interface : public SNES::Interface {
if(paudio_sample) return paudio_sample(left, right);
}
//zero 27-sep-2012
snes_scanlineStart_t pScanlineStart;
void scanlineStart(int line)
{
if(pScanlineStart) pScanlineStart((int)line);
}
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
if(pinput_state) return pinput_state(port?1:0, (unsigned)device, index, id);
return 0;
@ -249,6 +256,11 @@ void snes_cheat_set(unsigned index, bool enable, const char *code) {
SNES::cheat.synchronize();
}
//zero 21-sep-2012
void snes_set_scanlineStart(snes_scanlineStart_t cb)
{
interface.pScanlineStart = cb;
}
//zero 03-sep-2012
bool snes_check_cartridge(const uint8_t *rom_data, unsigned rom_size)

View File

@ -131,6 +131,8 @@ unsigned snes_get_memory_size(unsigned id);
//zeromus additions
bool snes_check_cartridge(const uint8_t *rom_data, unsigned rom_size);
void snes_set_layer_enable(int layer, int priority, bool enable);
typedef void (*snes_scanlineStart_t)(int);
void snes_set_scanlineStart(snes_scanlineStart_t);
//$2105
#define SNES_REG_BG_MODE 0