mirror of https://github.com/bsnes-emu/bsnes.git
Update to v085r07 release.
byuu says: Changelog: - stuff [Editor's note - pretty much just more debugger implementation]
This commit is contained in:
parent
4bc5f66aa5
commit
ad3eafd735
|
@ -1,7 +1,7 @@
|
|||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
const char Version[] = "085.06";
|
||||
const char Version[] = "085.07";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -433,51 +433,4 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr) {
|
|||
strcat(s, t);
|
||||
}
|
||||
|
||||
//opcode_length() retrieves the length of the next opcode
|
||||
//to be executed. It is used by the debugger to step over,
|
||||
//disable and proceed cpu opcodes.
|
||||
//
|
||||
//5 and 6 are special cases, 5 is used for #consts based on
|
||||
//the A register size, 6 for the X/Y register size. the
|
||||
//rest are literal sizes. There's no need to test for
|
||||
//emulation mode, as regs.p.m/regs.p.x should *always* be
|
||||
//set in emulation mode.
|
||||
|
||||
uint8 CPUcore::opcode_length() {
|
||||
uint8 op, len;
|
||||
static uint8 op_len_tbl[256] = {
|
||||
//0,1,2,3, 4,5,6,7, 8,9,a,b, c,d,e,f
|
||||
|
||||
2,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x0n
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x1n
|
||||
3,2,4,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x2n
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x3n
|
||||
|
||||
1,2,2,2, 3,2,2,2, 1,5,1,1, 3,3,3,4, //0x4n
|
||||
2,2,2,2, 3,2,2,2, 1,3,1,1, 4,3,3,4, //0x5n
|
||||
1,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x6n
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x7n
|
||||
|
||||
2,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x8n
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x9n
|
||||
6,2,6,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xan
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xbn
|
||||
|
||||
6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xcn
|
||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xdn
|
||||
6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xen
|
||||
2,2,2,2, 3,2,2,2, 1,3,1,1, 3,3,3,4 //0xfn
|
||||
};
|
||||
|
||||
if(false /* in_opcode() == true */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
op = dreadb(regs.pc.d);
|
||||
len = op_len_tbl[op];
|
||||
if(len == 5) return (regs.e || regs.p.m) ? 2 : 3;
|
||||
if(len == 6) return (regs.e || regs.p.x) ? 2 : 3;
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,4 +27,3 @@ uint8 dreadb(uint32 addr);
|
|||
uint16 dreadw(uint32 addr);
|
||||
uint32 dreadl(uint32 addr);
|
||||
uint32 decode(uint8 offset_type, uint32 addr);
|
||||
uint8 opcode_length();
|
||||
|
|
|
@ -4,7 +4,7 @@ include $(snes)/Makefile
|
|||
name := laevateinn
|
||||
|
||||
ui_objects := ui-main ui-interface ui-debugger ui-console
|
||||
ui_objects += ui-video ui-memory ui-breakpoint
|
||||
ui_objects += ui-video ui-cpu ui-memory ui-breakpoint
|
||||
ui_objects += phoenix ruby
|
||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||
|
||||
|
@ -33,6 +33,7 @@ obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
|
|||
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-console.o: $(ui)/console/console.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-cpu.o: $(ui)/cpu/cpu.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-video.o: $(ui)/video/video.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-memory.o: $(ui)/memory/memory.cpp $(call rwildcard,$(ui)/*)
|
||||
obj/ui-breakpoint.o: $(ui)/breakpoint/breakpoint.cpp $(call rwildcard,$(ui)/*)
|
||||
|
|
|
@ -22,6 +22,7 @@ using namespace ruby;
|
|||
#include "debugger/debugger.hpp"
|
||||
#include "console/console.hpp"
|
||||
#include "video/video.hpp"
|
||||
#include "cpu/cpu.hpp"
|
||||
#include "memory/memory.hpp"
|
||||
#include "breakpoint/breakpoint.hpp"
|
||||
extern uint8_t laevateinnLogo[121905];
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
BreakpointEditor *breakpointEditor = nullptr;
|
||||
|
||||
BreakpointEntry::BreakpointEntry() {
|
||||
enable.setText("Enable");
|
||||
static unsigned id = 1;
|
||||
enable.setText({ id++, ". Enable" });
|
||||
addr.setFont(application->monospaceFont);
|
||||
data.setFont(application->monospaceFont);
|
||||
type.append("Read", "Write", "Exec");
|
||||
|
@ -13,14 +14,83 @@ BreakpointEntry::BreakpointEntry() {
|
|||
append(data, {25, 0}, 5);
|
||||
append(type, {0, 0}, 5);
|
||||
append(source, {0, 0});
|
||||
|
||||
enable.onToggle = [&] {
|
||||
bool flag = !enable.checked();
|
||||
addr.setEnabled(flag);
|
||||
data.setEnabled(flag);
|
||||
type.setEnabled(flag);
|
||||
source.setEnabled(flag);
|
||||
breakpointEditor->synchronize();
|
||||
};
|
||||
}
|
||||
|
||||
BreakpointEditor::BreakpointEditor() {
|
||||
setTitle("Breakpoint Editor");
|
||||
|
||||
layout.setMargin(5);
|
||||
for(auto &bp : breakpoint) layout.append(bp, {0, 0}, 5);
|
||||
for(auto &bp : breakpointEntry) layout.append(bp, {0, 0}, 5);
|
||||
append(layout);
|
||||
|
||||
setGeometry({800, 600, layout.minimumGeometry().width, layout.minimumGeometry().height - 5});
|
||||
synchronize();
|
||||
}
|
||||
|
||||
//enable checkbox toggled on one of the five BreakpointEntry items:
|
||||
//cache settings to decrease testing overhead whilst debugging
|
||||
void BreakpointEditor::synchronize() {
|
||||
breakpoint.reset();
|
||||
unsigned id = 0;
|
||||
for(auto &entry : breakpointEntry) {
|
||||
id++;
|
||||
if(entry.enable.checked() == false) continue;
|
||||
Breakpoint bp;
|
||||
bp.id = id;
|
||||
bp.compare = !entry.data.text().empty();
|
||||
bp.addr = hex(entry.addr.text());
|
||||
bp.data = hex(entry.data.text());
|
||||
bp.type = entry.type.selection();
|
||||
bp.source = entry.source.selection();
|
||||
breakpoint.append(bp);
|
||||
}
|
||||
|
||||
breakpointReadCPU.reset();
|
||||
breakpointWriteCPU.reset();
|
||||
breakpointExecCPU.reset();
|
||||
|
||||
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::CPU) breakpointReadCPU.append(bp);
|
||||
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::CPU) breakpointWriteCPU.append(bp);
|
||||
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Exec && bp.source == Breakpoint::CPU) breakpointExecCPU.append(bp);
|
||||
}
|
||||
|
||||
bool BreakpointEditor::testReadCPU(uint24 addr) {
|
||||
for(auto &bp : breakpointReadCPU) {
|
||||
if(bp.addr == addr) {
|
||||
if(bp.compare && bp.data != SNES::bus.read(addr)) continue;
|
||||
debugger->print("Breakpoint #", bp.id, " hit\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BreakpointEditor::testWriteCPU(uint24 addr, uint8 data) {
|
||||
for(auto &bp : breakpointWriteCPU) {
|
||||
if(bp.addr == addr) {
|
||||
if(bp.compare && bp.data != data) continue;
|
||||
debugger->print("Breakpoint #", bp.id, " hit\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BreakpointEditor::testExecCPU(uint24 addr) {
|
||||
for(auto &bp : breakpointExecCPU) {
|
||||
if(bp.addr == addr) {
|
||||
debugger->print("Breapoint #", bp.id, " hit\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,27 @@ struct BreakpointEntry : HorizontalLayout {
|
|||
|
||||
struct BreakpointEditor : Window {
|
||||
VerticalLayout layout;
|
||||
BreakpointEntry breakpoint[5];
|
||||
BreakpointEntry breakpointEntry[8];
|
||||
|
||||
struct Breakpoint {
|
||||
enum : unsigned { Read, Write, Exec };
|
||||
enum : unsigned { CPU, APU, VRAM, OAM, CGRAM };
|
||||
unsigned id;
|
||||
bool compare;
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
unsigned type;
|
||||
unsigned source;
|
||||
};
|
||||
vector<Breakpoint> breakpoint;
|
||||
vector<Breakpoint> breakpointReadCPU;
|
||||
vector<Breakpoint> breakpointWriteCPU;
|
||||
vector<Breakpoint> breakpointExecCPU;
|
||||
void synchronize();
|
||||
|
||||
bool testReadCPU(uint24 addr);
|
||||
bool testWriteCPU(uint24 addr, uint8 data);
|
||||
bool testExecCPU(uint24 addr);
|
||||
|
||||
BreakpointEditor();
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ AboutWindow::AboutWindow() {
|
|||
setResizable(false);
|
||||
|
||||
layout.setMargin(10);
|
||||
layout.setAlignment(0.5);
|
||||
canvas.setSize({288, 360});
|
||||
title.setFont("Sans, 16, Bold");
|
||||
title.setText("Laevateinn");
|
||||
|
@ -13,14 +14,8 @@ AboutWindow::AboutWindow() {
|
|||
version.setText({"bsnes/debugger v", Version});
|
||||
|
||||
layout.append(canvas, {288, 360});
|
||||
layout.append(titleLayout, {~0, 0});
|
||||
titleLayout.append(titleL, {~0, 0});
|
||||
titleLayout.append(title, {0, 0});
|
||||
titleLayout.append(titleR, {~0, 0});
|
||||
layout.append(versionLayout, {~0, 0});
|
||||
versionLayout.append(versionL, {~0, 0});
|
||||
versionLayout.append(version, {0, 0});
|
||||
versionLayout.append(versionR, {~0, 0});
|
||||
layout.append(title, {0, 0});
|
||||
layout.append(version, {0, 0});
|
||||
append(layout);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ ConsoleWindow::ConsoleWindow() {
|
|||
|
||||
menuDebug.setText("&Debug");
|
||||
menuDebugCPU.setText("CPU");
|
||||
menuDebugCPU.setChecked(debugger->flags.debugCPU);
|
||||
menuDebugCPU.setChecked(debugger->debug.cpu);
|
||||
menuDebugSMP.setText("SMP");
|
||||
menuDebugSMP.setChecked(debugger->flags.debugSMP);
|
||||
menuDebugSMP.setChecked(debugger->debug.smp);
|
||||
menuDebugSMP.setEnabled(false);
|
||||
menuDebug.append(menuDebugCPU, menuDebugSMP);
|
||||
append(menuDebug);
|
||||
|
@ -39,9 +39,11 @@ ConsoleWindow::ConsoleWindow() {
|
|||
|
||||
menuWindows.setText("&Windows");
|
||||
menuWindowsVideoWindow.setText("Video");
|
||||
menuWindowsCPUDebugger.setText("CPU Debugger");
|
||||
menuWindowsMemoryEditor.setText("Memory Editor");
|
||||
menuWindowsBreakpointEditor.setText("Breakpoint Editor");
|
||||
menuWindows.append(menuWindowsVideoWindow, menuWindowsMemoryEditor, menuWindowsBreakpointEditor);
|
||||
menuWindows.append(menuWindowsVideoWindow, menuWindowsCPUDebugger, menuWindowsMemoryEditor,
|
||||
menuWindowsBreakpointEditor);
|
||||
append(menuWindows);
|
||||
|
||||
menuHelp.setText("&Help");
|
||||
|
@ -83,8 +85,8 @@ ConsoleWindow::ConsoleWindow() {
|
|||
audio.set(Audio::Synchronize, menuEmulationSynchronizeAudio.checked());
|
||||
};
|
||||
|
||||
menuDebugCPU.onToggle = [&] { debugger->flags.debugCPU = menuDebugCPU.checked(); };
|
||||
menuDebugSMP.onToggle = [&] { debugger->flags.debugSMP = menuDebugSMP.checked(); };
|
||||
menuDebugCPU.onToggle = [&] { debugger->debug.cpu = menuDebugCPU.checked(); };
|
||||
menuDebugSMP.onToggle = [&] { debugger->debug.smp = menuDebugSMP.checked(); };
|
||||
|
||||
menuTracerEnable.onToggle = [&] { debugger->tracerEnable(menuTracerEnable.checked()); };
|
||||
|
||||
|
@ -93,6 +95,11 @@ ConsoleWindow::ConsoleWindow() {
|
|||
videoWindow->setFocused();
|
||||
};
|
||||
|
||||
menuWindowsCPUDebugger.onActivate = [&] {
|
||||
cpuDebugger->setVisible();
|
||||
cpuDebugger->setFocused();
|
||||
};
|
||||
|
||||
menuWindowsMemoryEditor.onActivate = [&] {
|
||||
memoryEditor->update();
|
||||
memoryEditor->setVisible();
|
||||
|
@ -107,7 +114,7 @@ ConsoleWindow::ConsoleWindow() {
|
|||
menuHelpAbout.onActivate = [&] { aboutWindow->show(); };
|
||||
|
||||
runButton.onActivate = [&] {
|
||||
if(debugger->flags.paused == true) debugger->resume();
|
||||
if(debugger->paused) debugger->resume();
|
||||
else debugger->suspend();
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ struct ConsoleWindow : Window {
|
|||
|
||||
Menu menuWindows;
|
||||
Item menuWindowsVideoWindow;
|
||||
Item menuWindowsCPUDebugger;
|
||||
Item menuWindowsMemoryEditor;
|
||||
Item menuWindowsBreakpointEditor;
|
||||
|
||||
|
@ -40,14 +41,8 @@ struct ConsoleWindow : Window {
|
|||
struct AboutWindow : Window {
|
||||
VerticalLayout layout;
|
||||
Canvas canvas;
|
||||
HorizontalLayout titleLayout;
|
||||
Widget titleL;
|
||||
Label title;
|
||||
Widget titleR;
|
||||
HorizontalLayout versionLayout;
|
||||
Widget versionL;
|
||||
Label version;
|
||||
Widget versionR;
|
||||
Label title;
|
||||
Label version;
|
||||
|
||||
void show();
|
||||
AboutWindow();
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
#include "../base.hpp"
|
||||
CPUDebugger *cpuDebugger = nullptr;
|
||||
|
||||
unsigned CPUDebugger::opcodeLength(uint24 addr) const {
|
||||
#define M 5
|
||||
#define X 6
|
||||
static unsigned lengthTable[256] = {
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
3, 2, 4, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
|
||||
1, 2, 2, 2, 3, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 3, 2, 2, 2, 1, 3, 1, 1, 4, 3, 3, 4,
|
||||
1, 2, 3, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
|
||||
2, 2, 3, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
X, 2, X, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
|
||||
X, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
X, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||
2, 2, 2, 2, 3, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||
};
|
||||
|
||||
unsigned length = lengthTable[SNES::bus.read(addr)];
|
||||
if(length == M) return 3 - (SNES::cpu.regs.e | SNES::cpu.regs.p.m);
|
||||
if(length == X) return 3 - (SNES::cpu.regs.e | SNES::cpu.regs.p.x);
|
||||
return length;
|
||||
#undef M
|
||||
#undef X
|
||||
}
|
||||
|
||||
void CPUDebugger::updateDisassembly() {
|
||||
string line[15];
|
||||
char text[512];
|
||||
|
||||
SNES::cpu.disassemble_opcode(text, opcodePC);
|
||||
text[29] = 0;
|
||||
line[7] = { "> ", text };
|
||||
|
||||
signed addr = opcodePC;
|
||||
for(signed o = 6; o >= 0; o--) {
|
||||
for(signed b = 1; b <= 4; b++) {
|
||||
if(addr - b >= 0 && (debugger->cpuUsage.data[addr - b] & Usage::Exec)) {
|
||||
addr -= b;
|
||||
SNES::cpu.disassemble_opcode(text, addr);
|
||||
text[29] = 0;
|
||||
line[o] = { " ", text };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addr = opcodePC;
|
||||
for(signed o = 8; o <= 14; o++) {
|
||||
for(signed b = 1; b <= 4; b++) {
|
||||
if(addr + b <= 0xffffff && (debugger->cpuUsage.data[addr + b] & Usage::Exec)) {
|
||||
addr += b;
|
||||
SNES::cpu.disassemble_opcode(text, addr);
|
||||
text[29] = 0;
|
||||
line[o] = { " ", text };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string output;
|
||||
for(auto &n : line) {
|
||||
if(n.empty()) output.append(" ...\n");
|
||||
else output.append(n, "\n");
|
||||
}
|
||||
output.rtrim<1>("\n");
|
||||
|
||||
disassembly.setText(output);
|
||||
registers.setText({
|
||||
"A:", hex<4>(SNES::cpu.regs.a), " X:", hex<4>(SNES::cpu.regs.x), " Y:", hex<4>(SNES::cpu.regs.y),
|
||||
" S:", hex<4>(SNES::cpu.regs.s), " D:", hex<4>(SNES::cpu.regs.d), " DB:", hex<2>(SNES::cpu.regs.db), " ",
|
||||
SNES::cpu.regs.e ? "E" : "R", ":",
|
||||
SNES::cpu.regs.p.n ? "N" : "n", SNES::cpu.regs.p.v ? "V" : "v",
|
||||
SNES::cpu.regs.p.m ? "M" : "m", SNES::cpu.regs.p.x ? "X" : "x",
|
||||
SNES::cpu.regs.p.d ? "D" : "d", SNES::cpu.regs.p.i ? "I" : "i",
|
||||
SNES::cpu.regs.p.z ? "Z" : "z", SNES::cpu.regs.p.c ? "C" : "c",
|
||||
});
|
||||
}
|
||||
|
||||
CPUDebugger::CPUDebugger() {
|
||||
opcodePC = 0x008000;
|
||||
|
||||
setTitle("CPU Debugger");
|
||||
setGeometry({800, 64, 500, 255});
|
||||
|
||||
layout.setMargin(5);
|
||||
stepInto.setText("Step Into");
|
||||
stepOver.setText("Step Over");
|
||||
stepOver.setEnabled(false);
|
||||
stepOut.setText("Step Out");
|
||||
stepOut.setEnabled(false);
|
||||
skipOver.setText("Skip Over");
|
||||
skipOver.setEnabled(false);
|
||||
autoRefresh.setText("Auto");
|
||||
update.setText("Update");
|
||||
disassembly.setFont(application->monospaceFont);
|
||||
registers.setFont(application->monospaceFont);
|
||||
registers.setText(" ");
|
||||
|
||||
layout.append(controlLayout, {~0, 0}, 5);
|
||||
controlLayout.append(stepInto, {80, 0}, 5);
|
||||
controlLayout.append(stepOver, {80, 0}, 5);
|
||||
controlLayout.append(stepOut, {80, 0}, 5);
|
||||
controlLayout.append(skipOver, {80, 0});
|
||||
controlLayout.append(spacer, {~0, 0});
|
||||
controlLayout.append(autoRefresh, {0, 0}, 5);
|
||||
controlLayout.append(update, {80, 0});
|
||||
layout.append(disassembly, {~0, ~0}, 5);
|
||||
layout.append(registers, {~0, 0});
|
||||
append(layout);
|
||||
|
||||
stepInto.onActivate = [&] {
|
||||
debugger->flags.cpu.stepInto = true;
|
||||
debugger->resume();
|
||||
};
|
||||
|
||||
update.onActivate = { &CPUDebugger::updateDisassembly, this };
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
struct CPUDebugger : Window {
|
||||
uint24 opcodePC;
|
||||
|
||||
VerticalLayout layout;
|
||||
HorizontalLayout controlLayout;
|
||||
Button stepInto;
|
||||
Button stepOver;
|
||||
Button stepOut;
|
||||
Button skipOver;
|
||||
Widget spacer;
|
||||
CheckBox autoRefresh;
|
||||
Button update;
|
||||
TextEdit disassembly;
|
||||
Label registers;
|
||||
|
||||
unsigned opcodeLength(uint24 addr) const;
|
||||
void updateDisassembly();
|
||||
CPUDebugger();
|
||||
};
|
||||
|
||||
extern CPUDebugger *cpuDebugger;
|
|
@ -2,15 +2,17 @@
|
|||
Debugger *debugger = nullptr;
|
||||
|
||||
#include "hook.cpp"
|
||||
#include "usage.cpp"
|
||||
|
||||
void Debugger::run() {
|
||||
if(flags.paused == true) {
|
||||
if(paused == true) {
|
||||
usleep(2000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(memoryEditor->autoRefresh.checked()) memoryEditor->update();
|
||||
SNES::system.run();
|
||||
if(cpuDebugger->autoRefresh.checked()) cpuDebugger->updateDisassembly();
|
||||
if(memoryEditor->autoRefresh.checked()) memoryEditor->update();
|
||||
}
|
||||
|
||||
void Debugger::echo(const string &text) {
|
||||
|
@ -18,15 +20,17 @@ void Debugger::echo(const string &text) {
|
|||
}
|
||||
|
||||
void Debugger::resume() {
|
||||
flags.paused = false;
|
||||
if(paused == false) return;
|
||||
paused = false;
|
||||
consoleWindow->runButton.setText("Stop");
|
||||
consoleWindow->stepButton.setEnabled(false);
|
||||
}
|
||||
|
||||
void Debugger::suspend() {
|
||||
flags.paused = true;
|
||||
if(paused == true) return;
|
||||
paused = true;
|
||||
flags.step = false;
|
||||
flags.cpu.stepInto = false;
|
||||
consoleWindow->runButton.setText("Run");
|
||||
consoleWindow->stepButton.setEnabled(true);
|
||||
}
|
||||
|
||||
void Debugger::tracerEnable(bool state) {
|
||||
|
@ -40,22 +44,31 @@ void Debugger::tracerEnable(bool state) {
|
|||
//if all files exist, use 000, even if it overwrites another log.
|
||||
unsigned n = 1;
|
||||
do {
|
||||
if(file::exists({ interface->baseName, "-", decimal<3, '0'>(n), ".log" }) == false) break;
|
||||
if(file::exists({ interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }) == false) break;
|
||||
} while(++n <= 999);
|
||||
|
||||
string filename = { interface->baseName, "-", decimal<3, '0'>(n), ".log" };
|
||||
string filename = { interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" };
|
||||
if(fpTracer.open(filename, file::mode::write) == false) return;
|
||||
print("Tracing to ", filename, "\n");
|
||||
}
|
||||
|
||||
Debugger::Debugger() {
|
||||
flags.paused = true;
|
||||
flags.step = false;
|
||||
paused = true;
|
||||
|
||||
flags.debugCPU = true;
|
||||
flags.debugSMP = false;
|
||||
flags.step = false;
|
||||
flags.cpu.stepInto = false;
|
||||
|
||||
debug.cpu = true;
|
||||
debug.smp = false;
|
||||
|
||||
cpuUsage.allocate(16 * 1024 * 1024);
|
||||
apuUsage.allocate(64 * 1024);
|
||||
|
||||
SNES::cpu.debugger.op_exec = { &Debugger::cpu_op_exec, this };
|
||||
SNES::cpu.debugger.op_read = { &Debugger::cpu_op_read, this };
|
||||
SNES::cpu.debugger.op_write = { &Debugger::cpu_op_write, this };
|
||||
|
||||
SNES::smp.debugger.op_exec = { &Debugger::smp_op_exec, this };
|
||||
SNES::smp.debugger.op_read = { &Debugger::smp_op_read, this };
|
||||
SNES::smp.debugger.op_write = { &Debugger::smp_op_write, this };
|
||||
}
|
||||
|
|
|
@ -1,12 +1,35 @@
|
|||
struct Debugger {
|
||||
struct Flags {
|
||||
bool paused; //do not run emulation when true; set when breakpoint is hit
|
||||
bool step; //break on the next instruction from any processor
|
||||
struct Usage {
|
||||
enum : unsigned { Read = 4, Write = 2, Exec = 1 };
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
bool debugCPU;
|
||||
bool debugSMP;
|
||||
void allocate(unsigned size);
|
||||
void reset();
|
||||
Usage();
|
||||
~Usage();
|
||||
};
|
||||
|
||||
struct Debugger {
|
||||
bool paused;
|
||||
|
||||
struct Flags {
|
||||
bool step;
|
||||
struct CPU {
|
||||
bool stepInto;
|
||||
} cpu;
|
||||
} flags;
|
||||
|
||||
struct Debug {
|
||||
bool cpu;
|
||||
bool smp;
|
||||
} debug;
|
||||
|
||||
Usage cpuUsage;
|
||||
Usage apuUsage;
|
||||
void loadUsage();
|
||||
void saveUsage();
|
||||
void resetUsage();
|
||||
|
||||
void run();
|
||||
void echo(const string &text);
|
||||
void resume(); //start running until breakpoint is reached
|
||||
|
@ -18,6 +41,11 @@ struct Debugger {
|
|||
void cpu_op_read(uint24 addr);
|
||||
void cpu_op_write(uint24 addr, uint8 data);
|
||||
|
||||
//S-SMP
|
||||
void smp_op_exec(uint16 addr);
|
||||
void smp_op_read(uint16 addr);
|
||||
void smp_op_write(uint16 addr, uint8 data);
|
||||
|
||||
Debugger();
|
||||
|
||||
file fpTracer;
|
||||
|
|
|
@ -1,26 +1,70 @@
|
|||
//S-CPU
|
||||
//=====
|
||||
|
||||
void Debugger::cpu_op_exec(uint24 addr) {
|
||||
if(flags.debugCPU == false) return;
|
||||
cpuUsage.data[addr] |= Usage::Exec;
|
||||
cpuDebugger->opcodePC = addr;
|
||||
bool breakpointHit = breakpointEditor->testExecCPU(addr);
|
||||
|
||||
char text[512];
|
||||
if(fpTracer.open() || flags.step) {
|
||||
if(fpTracer.open() || (debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) {
|
||||
char text[512];
|
||||
SNES::cpu.disassemble_opcode(text, addr);
|
||||
}
|
||||
|
||||
if(fpTracer.open()) {
|
||||
fpTracer.print(text, "\n");
|
||||
}
|
||||
|
||||
if(flags.step) {
|
||||
flags.step = false;
|
||||
print(text, "\n");
|
||||
suspend();
|
||||
consoleWindow->stepButton.setFocused();
|
||||
SNES::scheduler.debug();
|
||||
if(fpTracer.open()) fpTracer.print(text, "\n");
|
||||
if((debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) {
|
||||
print(text, "\n");
|
||||
if(debug.cpu && flags.step) {
|
||||
consoleWindow->stepButton.setFocused();
|
||||
}
|
||||
if(flags.cpu.stepInto) {
|
||||
cpuDebugger->stepInto.setFocused();
|
||||
cpuDebugger->updateDisassembly();
|
||||
}
|
||||
suspend();
|
||||
SNES::scheduler.debug();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::cpu_op_read(uint24 addr) {
|
||||
cpuUsage.data[addr] |= Usage::Read;
|
||||
bool breakpointHit = breakpointEditor->testReadCPU(addr);
|
||||
|
||||
if(breakpointHit) {
|
||||
char text[512];
|
||||
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC);
|
||||
print(text, "\n");
|
||||
|
||||
suspend();
|
||||
SNES::scheduler.debug();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::cpu_op_write(uint24 addr, uint8 data) {
|
||||
cpuUsage.data[addr] |= Usage::Write;
|
||||
bool breakpointHit = breakpointEditor->testWriteCPU(addr, data);
|
||||
|
||||
if(breakpointHit) {
|
||||
char text[512];
|
||||
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC);
|
||||
print(text, "\n");
|
||||
|
||||
suspend();
|
||||
SNES::scheduler.debug();
|
||||
}
|
||||
}
|
||||
|
||||
//S-SMP
|
||||
//=====
|
||||
|
||||
void Debugger::smp_op_exec(uint16 addr) {
|
||||
apuUsage.data[addr] |= Usage::Exec;
|
||||
}
|
||||
|
||||
void Debugger::smp_op_read(uint16 addr) {
|
||||
apuUsage.data[addr] |= Usage::Read;
|
||||
}
|
||||
|
||||
void Debugger::smp_op_write(uint16 addr, uint8 data) {
|
||||
apuUsage.data[addr] |= Usage::Write;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
void Usage::allocate(unsigned size_) {
|
||||
if(data) delete[] data;
|
||||
size = size_;
|
||||
data = new uint8_t[size]();
|
||||
}
|
||||
|
||||
Usage::Usage() {
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
Usage::~Usage() {
|
||||
if(data) delete[] data;
|
||||
}
|
||||
|
||||
void Debugger::loadUsage() {
|
||||
file fp;
|
||||
if(fp.open({ interface->pathName, "debug/usage.cpu" }, file::mode::read)) {
|
||||
fp.read(cpuUsage.data, min(cpuUsage.size, fp.size()));
|
||||
fp.close();
|
||||
}
|
||||
if(fp.open({ interface->pathName, "debug/usage.apu" }, file::mode::read)) {
|
||||
fp.read(apuUsage.data, min(apuUsage.size, fp.size()));
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::saveUsage() {
|
||||
string filename;
|
||||
filename = { interface->pathName, "debug/usage.cpu" };
|
||||
file::write(filename, cpuUsage.data, cpuUsage.size);
|
||||
filename = { interface->pathName, "debug/usage.apu" };
|
||||
file::write(filename, apuUsage.data, apuUsage.size);
|
||||
}
|
||||
|
||||
void Debugger::resetUsage() {
|
||||
if(cpuUsage.data) memset(cpuUsage.data, 0, cpuUsage.size);
|
||||
if(apuUsage.data) memset(apuUsage.data, 0, apuUsage.size);
|
||||
}
|
|
@ -14,6 +14,8 @@ bool Interface::loadCartridge(const string &filename) {
|
|||
|
||||
fileName = filename;
|
||||
baseName = nall::basename(fileName);
|
||||
pathName = dir(baseName);
|
||||
mkdir(string{pathName, "debug/"}, 0755);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ baseName, ".xml" });
|
||||
|
@ -44,6 +46,8 @@ void Interface::loadMemory() {
|
|||
delete[] data;
|
||||
}
|
||||
}
|
||||
|
||||
debugger->loadUsage();
|
||||
}
|
||||
|
||||
void Interface::saveMemory() {
|
||||
|
@ -54,6 +58,8 @@ void Interface::saveMemory() {
|
|||
debugger->print("Saved ", filename, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
debugger->saveUsage();
|
||||
}
|
||||
|
||||
//hires is always true for accuracy core
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct Interface : SNES::Interface {
|
||||
string fileName;
|
||||
string baseName;
|
||||
string pathName;
|
||||
|
||||
bool loadCartridge(const string &filename);
|
||||
void loadMemory();
|
||||
|
|
|
@ -41,11 +41,13 @@ Application::Application(int argc, char **argv) {
|
|||
consoleWindow = new ConsoleWindow;
|
||||
aboutWindow = new AboutWindow;
|
||||
videoWindow = new VideoWindow;
|
||||
cpuDebugger = new CPUDebugger;
|
||||
memoryEditor = new MemoryEditor;
|
||||
breakpointEditor = new BreakpointEditor;
|
||||
|
||||
videoWindow->setVisible();
|
||||
consoleWindow->setVisible();
|
||||
videoWindow->setVisible();
|
||||
consoleWindow->setFocused();
|
||||
|
||||
if(audio.init() == false) {
|
||||
audio.driver("None");
|
||||
|
@ -55,6 +57,8 @@ Application::Application(int argc, char **argv) {
|
|||
audio.set(Audio::Frequency, 32000u);
|
||||
|
||||
if(interface->loadCartridge(filename) == false) return;
|
||||
cpuDebugger->updateDisassembly();
|
||||
memoryEditor->selectSource();
|
||||
|
||||
while(quit == false) {
|
||||
OS::processEvents();
|
||||
|
@ -65,8 +69,10 @@ Application::Application(int argc, char **argv) {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
exit(0);
|
||||
delete breakpointEditor;
|
||||
delete memoryEditor;
|
||||
delete cpuDebugger;
|
||||
delete videoWindow;
|
||||
delete aboutWindow;
|
||||
delete consoleWindow;
|
||||
|
|
|
@ -35,8 +35,6 @@ MemoryEditor::MemoryEditor() {
|
|||
source.onChange = { &MemoryEditor::selectSource, this };
|
||||
editor.onRead = { &MemoryEditor::read, this };
|
||||
editor.onWrite = { &MemoryEditor::write, this };
|
||||
|
||||
selectSource();
|
||||
}
|
||||
|
||||
uint8_t MemoryEditor::read(unsigned addr) {
|
||||
|
|
Loading…
Reference in New Issue