BizHawk/BizHawk.MultiClient/Gameboy/Debugger.cs

405 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using BizHawk.Core;
using BizHawk.MultiClient;
namespace BizHawk.Emulation.Consoles.Gameboy
{
public partial class Debugger : Form, Gameboy.IDebuggerAPI
{
readonly Gameboy gb;
public Debugger(Gameboy gb)
{
this.gb = gb;
gb.DebuggerAPI = this;
InitializeComponent();
Refresh();
}
void Gameboy.IDebuggerAPI.DoEvents()
{
System.Windows.Forms.Application.DoEvents();
}
private void viewDisassembly_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(SystemColors.Control);
StringBuilder sb = new StringBuilder();
ushort addr = (ushort)vScrollBar1.Value;
for (int i = 0; i < 16; i++)
{
ushort size;
string str = BizHawk.Emulation.CPUs.Z80GB.Disassembler.DAsm(addr, gb.Cpu.ReadMemory, out size);
addr += size;
sb.AppendLine(str);
}
using (Font font = new Font("Courier New", 8))
{
e.Graphics.DrawString(sb.ToString(), font, Brushes.Black, 0, 0);
}
}
public override void Refresh()
{
txtRegAF.Text = string.Format("{0:X4}", gb.Cpu.RegisterAF);
txtRegDE.Text = string.Format("{0:X4}", gb.Cpu.RegisterDE);
txtRegPC.Text = string.Format("{0:X4}", gb.Cpu.RegisterPC);
txtRegBC.Text = string.Format("{0:X4}", gb.Cpu.RegisterBC);
txtRegHL.Text = string.Format("{0:X4}", gb.Cpu.RegisterHL);
txtRegSP.Text = string.Format("{0:X4}", gb.Cpu.RegisterSP);
checkFlag_Z.Checked = gb.Cpu.FlagZ;
checkFlag_N.Checked = gb.Cpu.FlagN;
checkFlag_H.Checked = gb.Cpu.FlagH;
checkFlag_C.Checked = gb.Cpu.FlagC;
txtFrame.Text = gb.Registers.Timing.frame.ToString();
txtLine.Text = gb.Registers.Timing.line.ToString();
txtDot.Text = gb.Registers.Timing.dot.ToString();
base.Refresh();
}
void DoStepInto()
{
DoBreak();
gb.SingleStepInto();
vScrollBar1.Value = gb.Cpu.RegisterPC;
Refresh();
}
bool Running = false;
void DoRun()
{
Running = true;
gb.RunForever();
Running = false;
}
void DoBreak()
{
gb.DebugBreak = true;
}
private void btnStepInto_Click(object sender, EventArgs e)
{
DoStepInto();
}
bool TryParse16(string str, out ushort val)
{
return ushort.TryParse(str, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out val);
}
private void reg16_Validating(object sender, CancelEventArgs e)
{
var tb = (TextBox)sender;
ushort val = 0;
TryParse16(tb.Text, out val);
if (sender == txtRegAF) gb.Cpu.RegisterAF = val;
if (sender == txtRegDE) gb.Cpu.RegisterDE = val;
if (sender == txtRegPC) gb.Cpu.RegisterPC = val;
if (sender == txtRegBC) gb.Cpu.RegisterBC = val;
if (sender == txtRegHL) gb.Cpu.RegisterHL = val;
if (sender == txtRegSP) gb.Cpu.RegisterSP = val;
Refresh();
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
Refresh();
}
private void btnSeekPC_Click(object sender, EventArgs e)
{
vScrollBar1.Value = gb.Cpu.RegisterPC;
Refresh();
}
private void btnSeekUser_Click(object sender, EventArgs e)
{
ushort val;
if (TryParse16(txtSeekUser.Text, out val))
{
vScrollBar1.Value = val;
Refresh();
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.F11:
DoStepInto();
break;
case Keys.F5:
DoRun();
break;
case Keys.Escape:
DoBreak();
break;
default:
return false;
}
return true;
}
private void menuContextBreakpoints_Opening(object sender, CancelEventArgs e)
{
miBreakpointDelete.Enabled = false;
}
private void viewTiles0x8000_Paint(object sender, PaintEventArgs e)
{
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
{
var linebuf = new byte[128];
for (int y = 0; y < 144; y++)
{
gb.RenderTileLine(y, linebuf, 0x8000);
for (int x = 0; x < 128; x++)
{
int gray = linebuf[x] << 6;
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
}
}
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
}
}
private void viewTiles0x9000_Paint(object sender, PaintEventArgs e)
{
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
{
var linebuf = new byte[128];
for (int y = 0; y < 144; y++)
{
gb.RenderTileLine(y, linebuf, 0x8800);
for (int x = 0; x < 128; x++)
{
int gray = linebuf[x] << 6;
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
}
}
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
}
}
private void viewBG_Paint(object sender, PaintEventArgs e)
{
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
{
var linebuf = new byte[160];
for (int y = 0; y < 144; y++)
{
if(checkViewBg.Checked) gb.RenderBGLine(y, linebuf, false);
if (checkViewObj.Checked) gb.RenderOBJLine(y, linebuf, !checkViewObjNoLimit.Checked);
for (int x = 0; x < 160; x++)
{
int gray = linebuf[x]<<6;
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
}
}
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
}
}
private void timerRunUpdate_Tick(object sender, EventArgs e)
{
if(Running)
Refresh();
}
private void btnRun_Click(object sender, EventArgs e)
{
DoRun();
}
private void btnBreak_Click(object sender, EventArgs e)
{
DoBreak();
}
private void checkFlag_Z_CheckedChanged(object sender, EventArgs e)
{
}
private void cpuflag_checkChanged(object sender, EventArgs e)
{
var cb = (CheckBox)sender;
if (sender == checkFlag_Z) gb.Cpu.FlagZ = cb.Checked;
if (sender == checkFlag_N) gb.Cpu.FlagN = cb.Checked;
if (sender == checkFlag_H) gb.Cpu.FlagH = cb.Checked;
if (sender == checkFlag_C) gb.Cpu.FlagC = cb.Checked;
Refresh();
}
static char Remap(byte val)
{
if (val < ' ') return '.';
else if (val >= 0x80) return '.';
else return (char)val;
}
private void panelMemory_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(SystemColors.Control);
StringBuilder sb = new StringBuilder();
ushort addr = (ushort)(panelMemory.Scrollbar.Value * 16);
for (int i = 0; i < 16; i++)
{
sb.AppendFormat("{0}:{1:X4} ", gb.DescribeParagraph(addr),addr);
for (int x = 0; x < 16; x++)
sb.AppendFormat("{0:X2} ", gb.ReadMemory((ushort)(addr+x)));
sb.AppendFormat("| ");
for (int x = 0; x < 16; x++)
sb.Append(Remap(gb.ReadMemory((ushort)(addr + x))));
sb.AppendLine();
addr += 16;
}
using (Font font = new Font("Courier New", 8))
{
e.Graphics.DrawString(sb.ToString(), font, Brushes.Black, 0, 0);
}
}
private void panelMemory_Scroll(object sender, ScrollEventArgs e)
{
Refresh();
}
private void checkViewBg_CheckedChanged(object sender, EventArgs e)
{
Refresh();
}
private void checkViewObj_CheckedChanged(object sender, EventArgs e)
{
Refresh();
}
private void checkViewObjNoLimit_CheckedChanged(object sender, EventArgs e)
{
Refresh();
}
private void viewBG_KeyUp(object sender, KeyEventArgs e)
{
UpdateKeys();
}
private void viewBG_KeyPress(object sender, KeyPressEventArgs e)
{
UpdateKeys();
}
private void viewBG_KeyDown(object sender, KeyEventArgs e)
{
UpdateKeys();
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (viewBG.Focused) return true;
else return base.ProcessDialogKey(keyData);
}
void UpdateKeys()
{
gb.Registers.Input.up = Keyboard.IsKeyDown(Keys.Up);
gb.Registers.Input.down = Keyboard.IsKeyDown(Keys.Down);
gb.Registers.Input.left = Keyboard.IsKeyDown(Keys.Left);
gb.Registers.Input.right = Keyboard.IsKeyDown(Keys.Right);
gb.Registers.Input.a = Keyboard.IsKeyDown(Keys.X);
gb.Registers.Input.b = Keyboard.IsKeyDown(Keys.Z);
gb.Registers.Input.select = Keyboard.IsKeyDown(Keys.E);
gb.Registers.Input.right = Keyboard.IsKeyDown(Keys.R);
}
private void viewBG_Enter(object sender, EventArgs e)
{
lblInputActive.ForeColor = Color.Red;
UpdateKeys();
}
private void viewBG_Leave(object sender, EventArgs e)
{
lblInputActive.ForeColor = SystemColors.ControlText;
gb.Registers.Input.up = false;
gb.Registers.Input.down = false;
gb.Registers.Input.left = false;
gb.Registers.Input.right = false;
gb.Registers.Input.a = false;
gb.Registers.Input.b = false;
gb.Registers.Input.select = false;
gb.Registers.Input.right = false;
}
static class Keyboard
{
[Flags]
private enum KeyStates
{
None = 0,
Down = 1,
Toggled = 2
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private static KeyStates GetKeyState(Keys key)
{
KeyStates state = KeyStates.None;
short retVal = GetKeyState((int)key);
//If the high-order bit is 1, the key is down
//otherwise, it is up.
if ((retVal & 0x8000) == 0x8000)
state |= KeyStates.Down;
//If the low-order bit is 1, the key is toggled.
if ((retVal & 1) == 1)
state |= KeyStates.Toggled;
return state;
}
public static bool IsKeyDown(Keys key)
{
return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
}
public static bool IsKeyToggled(Keys key)
{
return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
}
}
private void Debugger_Load(object sender, EventArgs e)
{
}
private void autoloadToolStripMenuItem_Click(object sender, EventArgs e)
{
Global.Config.AutoloadGBDebugger ^= true;
}
private void settingsToolStripMenuItem_DropDownOpened(object sender, EventArgs e)
{
autoloadToolStripMenuItem.Checked = Global.Config.AutoloadGBDebugger;
}
}
}