A debugger appeared.
parent
66e5efff59
commit
449b6ebd0c
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.Diagnostics
|
||||
{
|
||||
public partial class Debugger
|
||||
{
|
||||
private struct Command
|
||||
{
|
||||
#region Properties
|
||||
public IEnumerable<string> Names { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public Action<string[]> Action { get; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public Command(IEnumerable<string> names, string description, Action<string[]> action)
|
||||
{
|
||||
Names = names;
|
||||
Description = description;
|
||||
Action = action;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace DotN64.Diagnostics
|
||||
{
|
||||
public partial class Debugger
|
||||
{
|
||||
public enum Status : byte
|
||||
{
|
||||
Stopped,
|
||||
Running,
|
||||
Debugging
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.Diagnostics
|
||||
{
|
||||
using CPU;
|
||||
|
||||
public partial class Debugger
|
||||
{
|
||||
#region Fields
|
||||
private readonly Nintendo64 nintendo64;
|
||||
private readonly IReadOnlyCollection<Command> commands;
|
||||
private readonly IDictionary<ulong, string> labels = new Dictionary<ulong, string>();
|
||||
private readonly IList<ulong> breakpoints = new List<ulong>();
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public Status DebuggerStatus { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public Debugger(Nintendo64 nintendo64)
|
||||
{
|
||||
this.nintendo64 = nintendo64;
|
||||
commands = new[]
|
||||
{
|
||||
new Command(new[] { "continue", "c" }, "Continues execution of the CPU.", args => DebuggerStatus = Status.Running),
|
||||
new Command(new[] { "step", "s" }, "Steps the CPU a specified amount of times.", args =>
|
||||
{
|
||||
var count = args.Length > 0 ? int.Parse(args.First()) : 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
Disassemble();
|
||||
nintendo64.CPU.Step();
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "goto", "g" }, "Sets the CPU's PC to the specified address.", args => nintendo64.CPU.PC = ulong.Parse(args.First(), NumberStyles.HexNumber)),
|
||||
new Command(new[] { "disassemble", "d" }, "Disassembles instructions from the current PC.", args =>
|
||||
{
|
||||
var count = args.Length > 0 ? ulong.Parse(args.First()) : 1;
|
||||
|
||||
for (var i = 0ul; i < count; i++)
|
||||
{
|
||||
Disassemble(nintendo64.CPU.PC + i * VR4300.Instruction.Size);
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "label", "labels", "l" }, "Shows, adds or removes a label attached to an address.", args =>
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
switch (args.Length == 0 ? "show" : args[index++])
|
||||
{
|
||||
case "add":
|
||||
case "a":
|
||||
labels[args.Length <= index + 1 ? nintendo64.CPU.PC : ulong.Parse(args[index++], NumberStyles.HexNumber)] = args[index++];
|
||||
break;
|
||||
case "remove":
|
||||
case "r":
|
||||
labels.Remove(args.Length <= index ? nintendo64.CPU.PC : ulong.Parse(args[index++], NumberStyles.HexNumber));
|
||||
break;
|
||||
case "clear":
|
||||
case "c":
|
||||
labels.Clear();
|
||||
break;
|
||||
case "show":
|
||||
case "s":
|
||||
{
|
||||
if (args.Length <= index)
|
||||
{
|
||||
foreach (var pair in labels)
|
||||
{
|
||||
Console.WriteLine($".{pair.Value}: {pair.Key:X16}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
var address = ulong.Parse(args[index++], NumberStyles.HexNumber);
|
||||
|
||||
Console.WriteLine($".{labels[address]}: {address:X16}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "breakpoint", "breakpoints", "b" }, "Shows, adds or removes breakpoints.", args =>
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
switch (args.Length == 0 ? "show" : args[index++])
|
||||
{
|
||||
case "add":
|
||||
case "a":
|
||||
breakpoints.Add(args.Length <= index ? nintendo64.CPU.PC : ulong.Parse(args[index++], NumberStyles.HexNumber));
|
||||
break;
|
||||
case "remove":
|
||||
case "r":
|
||||
breakpoints.Remove(args.Length <= index ? nintendo64.CPU.PC : ulong.Parse(args[index++], NumberStyles.HexNumber));
|
||||
break;
|
||||
case "clear":
|
||||
case "c":
|
||||
breakpoints.Clear();
|
||||
break;
|
||||
case "show":
|
||||
case "s":
|
||||
foreach (var breakpoint in breakpoints)
|
||||
{
|
||||
Console.WriteLine($"● {breakpoint:X16}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "exit", "quit", "q" }, "Exits the debugger.", args => DebuggerStatus = Status.Stopped),
|
||||
new Command(new[] { "help", "h" }, "Shows help for commands.", args =>
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
foreach (var command in commands)
|
||||
{
|
||||
Console.WriteLine($"- {string.Join(", ", command.Names)}: {command.Description}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var commandName = args.First();
|
||||
var command = commands.First(c => c.Names.Contains(commandName));
|
||||
|
||||
Console.WriteLine($"{string.Join(", ", command.Names)}: {command.Description}");
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "clear" }, "Clears the terminal.", args => Console.Clear())
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private string Disassemble(VR4300.Instruction instruction)
|
||||
{
|
||||
switch (instruction.OP)
|
||||
{
|
||||
case VR4300.OpCode.SPECIAL:
|
||||
return ((VR4300.SpecialOpCode)instruction.Funct).ToString();
|
||||
case VR4300.OpCode.REGIMM:
|
||||
return ((VR4300.RegImmOpCode)instruction.RT).ToString();
|
||||
default:
|
||||
return instruction.OP.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void Disassemble(ulong? address = null)
|
||||
{
|
||||
if (!address.HasValue)
|
||||
address = nintendo64.CPU.PC;
|
||||
|
||||
var physicalAddress = address.Value;
|
||||
var instruction = nintendo64.CPU.CP0.Map(ref physicalAddress).ReadWord(physicalAddress);
|
||||
|
||||
if (labels.ContainsKey(address.Value))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
|
||||
Console.WriteLine($".{labels[address.Value]}:");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
var hasBreakpoint = breakpoints.Contains(address.Value);
|
||||
|
||||
if (hasBreakpoint)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.DarkRed;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
|
||||
Console.WriteLine($"{(hasBreakpoint ? '●' : ' ')} {address.Value:X16}: {instruction:X8} {Disassemble(instruction)}");
|
||||
|
||||
if (hasBreakpoint)
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public void Debug()
|
||||
{
|
||||
DebuggerStatus = Status.Debugging;
|
||||
|
||||
while (DebuggerStatus == Status.Debugging)
|
||||
{
|
||||
Console.Write("DotN64-dbg>");
|
||||
|
||||
var input = Console.ReadLine().Trim();
|
||||
|
||||
if (input.Length == 0)
|
||||
continue;
|
||||
|
||||
var arguments = input.Split();
|
||||
var commandName = arguments.First();
|
||||
Command command;
|
||||
|
||||
try
|
||||
{
|
||||
command = commands.First(c => c.Names.Contains(commandName));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
|
||||
Console.WriteLine("Command not found.");
|
||||
Console.ResetColor();
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
command.Action(arguments.Skip(1).ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
|
||||
Console.WriteLine($"Exception: {e.Message}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Run(bool debug = false)
|
||||
{
|
||||
DebuggerStatus = debug ? Status.Debugging : Status.Running;
|
||||
|
||||
while (DebuggerStatus != Status.Stopped)
|
||||
{
|
||||
switch (DebuggerStatus)
|
||||
{
|
||||
case Status.Running:
|
||||
if (breakpoints.Contains(nintendo64.CPU.PC))
|
||||
{
|
||||
Console.WriteLine("Hit a breakpoint; entering debug prompt.");
|
||||
Debug();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
nintendo64.CPU.Step();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
|
||||
Console.WriteLine($"Exception during CPU execution: {e.Message}");
|
||||
Console.ResetColor();
|
||||
Debug();
|
||||
}
|
||||
break;
|
||||
case Status.Debugging:
|
||||
Debug();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -64,6 +64,9 @@
|
|||
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.Register.cs" />
|
||||
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.RegisterIndex.cs" />
|
||||
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.StatusRegister.cs" />
|
||||
<Compile Include="Diagnostics\Debugger.cs" />
|
||||
<Compile Include="Diagnostics\Debugger.Status.cs" />
|
||||
<Compile Include="Diagnostics\Debugger.Command.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CPU\" />
|
||||
|
@ -79,6 +82,7 @@
|
|||
<Folder Include="RCP\SP\" />
|
||||
<Folder Include="CPU\VR4300\" />
|
||||
<Folder Include="CPU\VR4300\CP0\" />
|
||||
<Folder Include="Diagnostics\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -99,7 +99,10 @@ namespace DotN64
|
|||
|
||||
if (RCP.PI.BootROM == null)
|
||||
RCP.PI.EmulateBootROM();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
CPU.Step();
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
namespace DotN64
|
||||
{
|
||||
using Diagnostics;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
var nintendo64 = new Nintendo64();
|
||||
var debugger = default(Debugger);
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
|
@ -17,6 +20,10 @@ namespace DotN64
|
|||
case "--pif-rom":
|
||||
nintendo64.RCP.PI.BootROM = File.ReadAllBytes(args[++i]);
|
||||
break;
|
||||
case "--debug":
|
||||
case "-d":
|
||||
debugger = new Debugger(nintendo64);
|
||||
break;
|
||||
default:
|
||||
nintendo64.Cartridge = Cartridge.FromFile(new FileInfo(arg));
|
||||
break;
|
||||
|
@ -24,6 +31,11 @@ namespace DotN64
|
|||
}
|
||||
|
||||
nintendo64.PowerOn();
|
||||
|
||||
if (debugger == null)
|
||||
nintendo64.Run();
|
||||
else
|
||||
debugger.Run(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue