mirror of https://github.com/bsnes-emu/bsnes.git
250 lines
8.3 KiB
C++
Executable File
250 lines
8.3 KiB
C++
Executable File
#include "../ui-base.hpp"
|
|
|
|
#if defined(DEBUGGER)
|
|
|
|
#include "debugger.moc"
|
|
Debugger *debugger;
|
|
|
|
#include "tracer.cpp"
|
|
|
|
#include "tools/disassembler.cpp"
|
|
#include "tools/breakpoint.cpp"
|
|
#include "tools/memory.cpp"
|
|
#include "tools/properties.cpp"
|
|
|
|
#include "ppu/layer-toggle.cpp"
|
|
#include "ppu/vram-viewer.cpp"
|
|
#include "ppu/oam-viewer.cpp"
|
|
#include "ppu/cgram-viewer.cpp"
|
|
|
|
#include "misc/debugger-options.cpp"
|
|
|
|
Debugger::Debugger() {
|
|
setObjectName("debugger");
|
|
setWindowTitle("Debugger");
|
|
setGeometryString(&config().geometry.debugger);
|
|
application.windowList.append(this);
|
|
|
|
layout = new QHBoxLayout;
|
|
layout->setMargin(Style::WindowMargin);
|
|
layout->setSpacing(Style::WidgetSpacing);
|
|
setLayout(layout);
|
|
|
|
menu = new QMenuBar;
|
|
layout->setMenuBar(menu);
|
|
|
|
menu_tools = menu->addMenu("Tools");
|
|
menu_tools_disassembler = menu_tools->addAction("Disassembler ...");
|
|
menu_tools_breakpoint = menu_tools->addAction("Breakpoint Editor ...");
|
|
menu_tools_memory = menu_tools->addAction("Memory Editor ...");
|
|
menu_tools_propertiesViewer = menu_tools->addAction("Properties Viewer ...");
|
|
|
|
menu_ppu = menu->addMenu("S-PPU");
|
|
menu_ppu_layerToggle = menu_ppu->addAction("Layer Toggle ...");
|
|
menu_ppu_vramViewer = menu_ppu->addAction("Video RAM Viewer ...");
|
|
menu_ppu_oamViewer = menu_ppu->addAction("Sprite Viewer ...");
|
|
menu_ppu_cgramViewer = menu_ppu->addAction("Palette Viewer ...");
|
|
|
|
menu_misc = menu->addMenu("Misc");
|
|
menu_misc_clear = menu_misc->addAction("Clear Console");
|
|
menu_misc_options = menu_misc->addAction("Options ...");
|
|
|
|
console = new QTextEdit;
|
|
console->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
console->setReadOnly(true);
|
|
console->setFont(QFont(Style::Monospace));
|
|
console->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
console->setMinimumWidth((92 + 4) * console->fontMetrics().width(' '));
|
|
console->setMinimumHeight((25 + 1) * console->fontMetrics().height());
|
|
layout->addWidget(console);
|
|
|
|
controlLayout = new QVBoxLayout;
|
|
controlLayout->setSpacing(0);
|
|
layout->addLayout(controlLayout);
|
|
|
|
commandLayout = new QHBoxLayout;
|
|
controlLayout->addLayout(commandLayout);
|
|
|
|
runBreak = new QPushButton("Break");
|
|
commandLayout->addWidget(runBreak);
|
|
commandLayout->addSpacing(Style::WidgetSpacing);
|
|
|
|
stepInstruction = new QPushButton("Step");
|
|
commandLayout->addWidget(stepInstruction);
|
|
|
|
controlLayout->addSpacing(Style::WidgetSpacing);
|
|
|
|
stepCPU = new QCheckBox("Step S-CPU");
|
|
controlLayout->addWidget(stepCPU);
|
|
|
|
stepSMP = new QCheckBox("Step S-SMP");
|
|
controlLayout->addWidget(stepSMP);
|
|
|
|
traceCPU = new QCheckBox("Trace S-CPU opcodes");
|
|
controlLayout->addWidget(traceCPU);
|
|
|
|
traceSMP = new QCheckBox("Trace S-SMP opcodes");
|
|
controlLayout->addWidget(traceSMP);
|
|
|
|
traceMask = new QCheckBox("Enable trace mask");
|
|
controlLayout->addWidget(traceMask);
|
|
|
|
spacer = new QWidget;
|
|
spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
controlLayout->addWidget(spacer);
|
|
|
|
tracer = new Tracer;
|
|
disassembler = new Disassembler;
|
|
breakpointEditor = new BreakpointEditor;
|
|
memoryEditor = new MemoryEditor;
|
|
propertiesViewer = new PropertiesViewer;
|
|
layerToggle = new LayerToggle;
|
|
vramViewer = new VramViewer;
|
|
oamViewer = new OamViewer;
|
|
cgramViewer = new CgramViewer;
|
|
debuggerOptions = new DebuggerOptions;
|
|
|
|
connect(menu_tools_disassembler, SIGNAL(triggered()), disassembler, SLOT(show()));
|
|
connect(menu_tools_breakpoint, SIGNAL(triggered()), breakpointEditor, SLOT(show()));
|
|
connect(menu_tools_memory, SIGNAL(triggered()), memoryEditor, SLOT(show()));
|
|
connect(menu_tools_propertiesViewer, SIGNAL(triggered()), propertiesViewer, SLOT(show()));
|
|
|
|
connect(menu_ppu_layerToggle, SIGNAL(triggered()), layerToggle, SLOT(show()));
|
|
connect(menu_ppu_vramViewer, SIGNAL(triggered()), vramViewer, SLOT(show()));
|
|
connect(menu_ppu_oamViewer, SIGNAL(triggered()), oamViewer, SLOT(show()));
|
|
connect(menu_ppu_cgramViewer, SIGNAL(triggered()), cgramViewer, SLOT(show()));
|
|
|
|
connect(menu_misc_clear, SIGNAL(triggered()), this, SLOT(clear()));
|
|
connect(menu_misc_options, SIGNAL(triggered()), debuggerOptions, SLOT(show()));
|
|
|
|
connect(runBreak, SIGNAL(released()), this, SLOT(toggleRunStatus()));
|
|
connect(stepInstruction, SIGNAL(released()), this, SLOT(stepAction()));
|
|
connect(stepCPU, SIGNAL(released()), this, SLOT(synchronize()));
|
|
connect(stepSMP, SIGNAL(released()), this, SLOT(synchronize()));
|
|
connect(traceCPU, SIGNAL(stateChanged(int)), tracer, SLOT(setCpuTraceState(int)));
|
|
connect(traceSMP, SIGNAL(stateChanged(int)), tracer, SLOT(setSmpTraceState(int)));
|
|
connect(traceMask, SIGNAL(stateChanged(int)), tracer, SLOT(setTraceMaskState(int)));
|
|
|
|
frameCounter = 0;
|
|
synchronize();
|
|
resize(855, 425);
|
|
}
|
|
|
|
void Debugger::modifySystemState(unsigned state) {
|
|
string usagefile = filepath(nall::basename(cartridge.fileName), config().path.data);
|
|
usagefile << "-usage.bin";
|
|
file fp;
|
|
|
|
if(state == Utility::LoadCartridge) {
|
|
if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_read)) {
|
|
fp.read(SNES::cpu.usage, 1 << 24);
|
|
fp.read(SNES::smp.usage, 1 << 16);
|
|
fp.close();
|
|
} else {
|
|
memset(SNES::cpu.usage, 0x00, 1 << 24);
|
|
memset(SNES::smp.usage, 0x00, 1 << 16);
|
|
}
|
|
}
|
|
|
|
if(state == Utility::UnloadCartridge) {
|
|
if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_write)) {
|
|
fp.write(SNES::cpu.usage, 1 << 24);
|
|
fp.write(SNES::smp.usage, 1 << 16);
|
|
fp.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Debugger::synchronize() {
|
|
runBreak->setText(application.debug ? "Run" : "Break");
|
|
stepInstruction->setEnabled(SNES::cartridge.loaded() && application.debug && (stepCPU->isChecked() || stepSMP->isChecked()));
|
|
SNES::debugger.step_cpu = application.debug && stepCPU->isChecked();
|
|
SNES::debugger.step_smp = application.debug && stepSMP->isChecked();
|
|
|
|
memoryEditor->synchronize();
|
|
}
|
|
|
|
void Debugger::echo(const char *message) {
|
|
console->moveCursor(QTextCursor::End);
|
|
console->insertHtml(message);
|
|
}
|
|
|
|
void Debugger::clear() {
|
|
console->setHtml("");
|
|
}
|
|
|
|
void Debugger::toggleRunStatus() {
|
|
application.debug = !application.debug;
|
|
if(!application.debug) application.debugrun = false;
|
|
synchronize();
|
|
}
|
|
|
|
void Debugger::stepAction() {
|
|
application.debugrun = true;
|
|
}
|
|
|
|
void Debugger::event() {
|
|
char t[256];
|
|
|
|
switch(SNES::debugger.break_event) {
|
|
case SNES::Debugger::BreakEvent::BreakpointHit: {
|
|
unsigned n = SNES::debugger.breakpoint_hit;
|
|
echo(string() << "Breakpoint " << n << " hit (" << SNES::debugger.breakpoint[n].counter << ").<br>");
|
|
|
|
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::CPUBus) {
|
|
SNES::debugger.step_cpu = true;
|
|
SNES::cpu.disassemble_opcode(t, SNES::cpu.opcode_pc);
|
|
string s = t;
|
|
s.replace(" ", " ");
|
|
echo(string() << "<font color='#a00000'>" << s << "</font><br>");
|
|
disassembler->refresh(Disassembler::CPU, SNES::cpu.opcode_pc);
|
|
}
|
|
|
|
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::APURAM) {
|
|
SNES::debugger.step_smp = true;
|
|
SNES::smp.disassemble_opcode(t, SNES::smp.opcode_pc);
|
|
string s = t;
|
|
s.replace(" ", " ");
|
|
echo(string() << "<font color='#a00000'>" << t << "</font><br>");
|
|
disassembler->refresh(Disassembler::SMP, SNES::smp.opcode_pc);
|
|
}
|
|
} break;
|
|
|
|
case SNES::Debugger::BreakEvent::CPUStep: {
|
|
SNES::cpu.disassemble_opcode(t, SNES::cpu.regs.pc);
|
|
string s = t;
|
|
s.replace(" ", " ");
|
|
echo(string() << "<font color='#0000a0'>" << s << "</font><br>");
|
|
disassembler->refresh(Disassembler::CPU, SNES::cpu.regs.pc);
|
|
} break;
|
|
|
|
case SNES::Debugger::BreakEvent::SMPStep: {
|
|
SNES::smp.disassemble_opcode(t, SNES::smp.regs.pc);
|
|
string s = t;
|
|
s.replace(" ", " ");
|
|
echo(string() << "<font color='#a00000'>" << s << "</font><br>");
|
|
disassembler->refresh(Disassembler::SMP, SNES::smp.regs.pc);
|
|
} break;
|
|
}
|
|
|
|
autoUpdate();
|
|
}
|
|
|
|
//called once every time a video frame is rendered, used to update "auto refresh" tool windows
|
|
void Debugger::frameTick() {
|
|
if(++frameCounter >= (SNES::system.region() == SNES::System::Region::NTSC ? 60 : 50)) {
|
|
frameCounter = 0;
|
|
autoUpdate();
|
|
}
|
|
}
|
|
|
|
void Debugger::autoUpdate() {
|
|
memoryEditor->autoUpdate();
|
|
propertiesViewer->autoUpdate();
|
|
vramViewer->autoUpdate();
|
|
oamViewer->autoUpdate();
|
|
cgramViewer->autoUpdate();
|
|
}
|
|
|
|
#endif
|