BizHawk/BizHawk.Client.EmuHawk/tools/Atari2600/Atari2600Debugger.cs

280 lines
8.3 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Cores.Atari.Atari2600;
namespace BizHawk.Client.EmuHawk
{
public partial class Atari2600Debugger : Form, IToolForm
{
// TODO:
// Take control of mainform
// Consider how to handle trace logger (the two will compete with each other with the TakeContents() method)
// Step Into
// Step Out
// Step
// Advance 1 scanline?
// Settable registers, also implement in lua
// Breakpoints
private Atari2600 _core = Global.Emulator as Atari2600;
private readonly List<string> _instructions = new List<string>();
private int _defaultWidth;
private int _defaultHeight;
//the opsize table is used to quickly grab the instruction sizes (in bytes)
private readonly byte[] opsize = new byte[]
{
/*0x00*/ 1,2,0,0,0,2,2,0,1,2,1,0,0,3,3,0,
/*0x10*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0,
/*0x20*/ 3,2,0,0,2,2,2,0,1,2,1,0,3,3,3,0,
/*0x30*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0,
/*0x40*/ 1,2,0,0,0,2,2,0,1,2,1,0,3,3,3,0,
/*0x50*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0,
/*0x60*/ 1,2,0,0,0,2,2,0,1,2,1,0,3,3,3,0,
/*0x70*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0,
/*0x80*/ 0,2,0,0,2,2,2,0,1,0,1,0,3,3,3,0,
/*0x90*/ 2,2,0,0,2,2,2,0,1,3,1,0,0,3,0,0,
/*0xA0*/ 2,2,2,0,2,2,2,0,1,2,1,0,3,3,3,0,
/*0xB0*/ 2,2,0,0,2,2,2,0,1,3,1,0,3,3,3,0,
/*0xC0*/ 2,2,0,0,2,2,2,0,1,2,1,0,3,3,3,0,
/*0xD0*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0,
/*0xE0*/ 2,2,0,0,2,2,2,0,1,2,1,0,3,3,3,0,
/*0xF0*/ 2,2,0,0,0,2,2,0,1,3,0,0,0,3,3,0
};
/*the optype table is a quick way to grab the addressing mode for any 6502 opcode
//
// 0 = Implied\Accumulator\Immediate\Branch\NULL
// 1 = (Indirect,X)
// 2 = Zero Page
// 3 = Absolute
// 4 = (Indirect),Y
// 5 = Zero Page,X
// 6 = Absolute,Y
// 7 = Absolute,X
// 8 = Zero Page,Y
*/
private readonly byte[] optype = new byte[]
{
/*0x00*/ 0,1,0,0,0,2,2,0,0,0,0,0,0,3,3,0,
/*0x10*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0,
/*0x20*/ 0,1,0,0,2,2,2,0,0,0,0,0,3,3,3,0,
/*0x30*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0,
/*0x40*/ 0,1,0,0,0,2,2,0,0,0,0,0,0,3,3,0,
/*0x50*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0,
/*0x60*/ 0,1,0,0,0,2,2,0,0,0,0,0,3,3,3,0,
/*0x70*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0,
/*0x80*/ 0,1,0,0,2,2,2,0,0,0,0,0,3,3,3,0,
/*0x90*/ 0,4,0,0,5,5,8,0,0,6,0,0,0,7,0,0,
/*0xA0*/ 0,1,0,0,2,2,2,0,0,0,0,0,3,3,3,0,
/*0xB0*/ 0,4,0,0,5,5,8,0,0,6,0,0,7,7,6,0,
/*0xC0*/ 0,1,0,0,2,2,2,0,0,0,0,0,3,3,3,0,
/*0xD0*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0,
/*0xE0*/ 0,1,0,0,2,2,2,0,0,0,0,0,3,3,3,0,
/*0xF0*/ 0,4,0,0,0,5,5,0,0,6,0,0,0,7,7,0
};
public Atari2600Debugger()
{
InitializeComponent();
TraceView.QueryItemText += TraceView_QueryItemText;
TraceView.VirtualMode = true;
TopMost = Global.Config.Atari2600DebuggerSettings.TopMost;
Closing += (o, e) => Shutdown();
}
private void Atari2600Debugger_Load(object sender, EventArgs e)
{
_defaultWidth = Size.Width;
_defaultHeight = Size.Height;
// TODO: some kind of method like PauseAndRelinquishControl() which will set a flag preventing unpausing by the user, and then a ResumeControl() method that is done on close
//GlobalWin.MainForm.PauseEmulator();
Global.CoreComm.Tracer.Enabled = true;
if (Global.Config.Atari2600DebuggerSettings.UseWindowPosition)
{
Location = Global.Config.Atari2600DebuggerSettings.WindowPosition;
}
if (Global.Config.Atari2600DebuggerSettings.UseWindowSize)
{
Size = Global.Config.Atari2600DebuggerSettings.WindowSize;
}
}
private void Shutdown()
{
//TODO: add a Mainform.ResumeControl() call
Global.CoreComm.Tracer.TakeContents();
Global.CoreComm.Tracer.Enabled = false;
}
public void Restart()
{
// TODO
}
public bool AskSave()
{
return true;
}
public bool UpdateBefore
{
get { return false; } // TODO: think about this
}
public void UpdateValues()
{
var flags = _core.GetCpuFlagsAndRegisters();
PCRegisterBox.Text = flags["PC"].ToString();
SPRegisterBox.Text = flags["S"].ToString();
SPRegisterHexBox.Text = string.Format("{0:X2}", flags["S"]);
SPRegisterBinaryBox.Text = ToBinStr(flags["S"]);
ARegisterBox.Text = flags["A"].ToString();
ARegisterHexBox.Text = string.Format("{0:X2}", flags["A"]);
ARegisterBinaryBox.Text = ToBinStr(flags["A"]);
XRegisterBox.Text = flags["X"].ToString();
XRegisterHexBox.Text = string.Format("{0:X2}", flags["X"]);
XRegisterBinaryBox.Text = ToBinStr(flags["X"]);
YRegisterBox.Text = flags["Y"].ToString();
YRegisterHexBox.Text = string.Format("{0:X2}", flags["Y"]);
YRegisterBinaryBox.Text = ToBinStr(flags["Y"]);
NFlagCheckbox.Checked = flags["Flag N"] == 1;
VFlagCheckbox.Checked = flags["Flag V"] == 1;
TFlagCheckbox.Checked = flags["Flag T"] == 1;
BFlagCheckbox.Checked = flags["Flag B"] == 1;
DFlagCheckbox.Checked = flags["Flag D"] == 1;
IFlagCheckbox.Checked = flags["Flag I"] == 1;
ZFlagCheckbox.Checked = flags["Flag Z"] == 1;
CFlagCheckbox.Checked = flags["Flag C"] == 1;
FrameCountBox.Text = _core.Frame.ToString();
ScanlineBox.Text = _core.CurrentScanLine.ToString();
VSyncChexkbox.Checked = _core.IsVsync;
VBlankCheckbox.Checked = _core.IsVBlank;
UpdateTraceLog();
}
private void UpdateTraceLog()
{
var instructions = Global.CoreComm.Tracer.TakeContents().Split('\n');
if (!string.IsNullOrWhiteSpace(instructions[0]))
{
_instructions.AddRange(instructions.Where(str => !string.IsNullOrEmpty(str)));
}
if (_instructions.Count >= Global.Config.TraceLoggerMaxLines)
{
_instructions.RemoveRange(0, _instructions.Count - Global.Config.TraceLoggerMaxLines);
}
TraceView.ItemCount = _instructions.Count;
}
private string ToBinStr(int val)
{
return Convert.ToString((uint)val, 2).PadLeft(8, '0');
}
private void TraceView_QueryItemText(int index, int column, out string text)
{
text = index < _instructions.Count ? _instructions[index] : string.Empty;
}
#region Events
private void ExitMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void StepBtn_Click(object sender, EventArgs e)
{
_core.CycleAdvance();
UpdateValues();
}
private void ScanlineAdvanceBtn_Click(object sender, EventArgs e)
{
_core.ScanlineAdvance();
UpdateValues();
}
private void FrameAdvButton_Click(object sender, EventArgs e)
{
_core.FrameAdvance(true, true);
UpdateValues();
}
private void RefreshFloatingWindowControl()
{
Owner = Global.Config.RamSearchSettings.FloatingWindow ? null : GlobalWin.MainForm;
}
#endregion
private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e)
{
AutoloadMenuItem.Checked = Global.Config.Atari2600DebuggerAutoload;
SaveWindowPositionMenuItem.Checked = Global.Config.Atari2600DebuggerSettings.SaveWindowPosition;
TopmostMenuItem.Checked = Global.Config.Atari2600DebuggerSettings.TopMost;
FloatingWindowMenuItem.Checked = Global.Config.Atari2600DebuggerSettings.FloatingWindow;
}
private void AutoloadMenuItem_Click(object sender, EventArgs e)
{
Global.Config.Atari2600DebuggerAutoload ^= true;
}
private void SaveWindowPositionMenuItem_Click(object sender, EventArgs e)
{
Global.Config.Atari2600DebuggerSettings.SaveWindowPosition ^= true;
}
private void TopmostMenuItem_Click(object sender, EventArgs e)
{
TopMost = Global.Config.Atari2600DebuggerSettings.TopMost ^= true;
}
private void FloatingWindowMenuItem_Click(object sender, EventArgs e)
{
Global.Config.Atari2600DebuggerSettings.FloatingWindow ^= true;
RefreshFloatingWindowControl();
}
private void RestoreDefaultsMenuItem_Click(object sender, EventArgs e)
{
Size = new Size(_defaultWidth, _defaultHeight);
Global.Config.Atari2600DebuggerSettings = new ToolDialogSettings();
TopMost = Global.Config.Atari2600DebuggerSettings.TopMost;
RefreshFloatingWindowControl();
}
protected override void OnShown(EventArgs e)
{
RefreshFloatingWindowControl();
base.OnShown(e);
}
}
}