Update to v070r07 release.

byuu says:

I'm happy enough with the debugger now. Not 100% up to par with the old
one, but it also does some new things the old one didn't.
- step into / step over are disabled unless they can be done safely
- this means step over is usually grayed unless you hit step into first,
  due to bsnes not being opcode-based (you can't skip an opcode that is
  half-executed)
- you can now trace console output to disk
- stepping the CPU will print stepped SMP opcodes if the checkbox for it
  is on and vice versa
- button added to clear the console log
This commit is contained in:
Tim Allen 2010-11-03 00:08:00 +11:00
parent 7e8958b102
commit 26643a43de
16 changed files with 100 additions and 42 deletions

View File

@ -7,14 +7,16 @@ void CPUDebugger::op_step() {
usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0);
opcode_pc = regs.pc; opcode_pc = regs.pc;
opcode_edge = true;
if(debugger.step_cpu) { if(debugger.step_cpu) {
debugger.break_event = Debugger::BreakEvent::CPUStep; debugger.break_event = Debugger::BreakEvent::CPUStep;
scheduler.exit(Scheduler::ExitReason::DebuggerEvent); scheduler.exit(Scheduler::ExitReason::DebuggerEvent);
} else { } else {
debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00);
} }
if(step_event) step_event(); if(step_event) step_event();
opcode_edge = false;
CPU::op_step(); CPU::op_step();
synchronize_smp(); synchronize_smp();
} }
@ -36,6 +38,7 @@ void CPUDebugger::op_write(uint32 addr, uint8 data) {
CPUDebugger::CPUDebugger() { CPUDebugger::CPUDebugger() {
usage = new uint8[1 << 24](); usage = new uint8[1 << 24]();
opcode_pc = 0x8000; opcode_pc = 0x8000;
opcode_edge = false;
} }
CPUDebugger::~CPUDebugger() { CPUDebugger::~CPUDebugger() {

View File

@ -12,7 +12,8 @@ public:
UsageFlagX = 0x01, UsageFlagX = 0x01,
}; };
uint8 *usage; uint8 *usage;
uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints uint32 opcode_pc;
bool opcode_edge;
void op_step(); void op_step();
uint8 op_read(uint32 addr); uint8 op_read(uint32 addr);

View File

@ -7,14 +7,16 @@ void CPUDebugger::op_step() {
usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0);
opcode_pc = regs.pc; opcode_pc = regs.pc;
opcode_edge = true;
if(debugger.step_cpu) { if(debugger.step_cpu) {
debugger.break_event = Debugger::BreakEvent::CPUStep; debugger.break_event = Debugger::BreakEvent::CPUStep;
scheduler.exit(Scheduler::ExitReason::DebuggerEvent); scheduler.exit(Scheduler::ExitReason::DebuggerEvent);
} else { } else {
debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00);
} }
if(step_event) step_event(); if(step_event) step_event();
opcode_edge = false;
CPU::op_step(); CPU::op_step();
synchronize_smp(); synchronize_smp();
} }
@ -36,6 +38,7 @@ void CPUDebugger::op_write(uint32 addr, uint8 data) {
CPUDebugger::CPUDebugger() { CPUDebugger::CPUDebugger() {
usage = new uint8[1 << 24](); usage = new uint8[1 << 24]();
opcode_pc = 0x8000; opcode_pc = 0x8000;
opcode_edge = false;
} }
CPUDebugger::~CPUDebugger() { CPUDebugger::~CPUDebugger() {

View File

@ -12,7 +12,8 @@ public:
UsageFlagX = 0x01, UsageFlagX = 0x01,
}; };
uint8 *usage; uint8 *usage;
uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints uint24 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints
bool opcode_edge; //true right before an opcode execues, used to skip over opcodes
void op_step(); void op_step();
uint8 op_read(uint32 addr); uint8 op_read(uint32 addr);

View File

@ -6,14 +6,16 @@ void SMPDebugger::op_step() {
usage[regs.pc] |= UsageExec; usage[regs.pc] |= UsageExec;
opcode_pc = regs.pc; opcode_pc = regs.pc;
opcode_edge = true;
if(debugger.step_smp) { if(debugger.step_smp) {
debugger.break_event = Debugger::BreakEvent::SMPStep; debugger.break_event = Debugger::BreakEvent::SMPStep;
scheduler.exit(Scheduler::ExitReason::DebuggerEvent); scheduler.exit(Scheduler::ExitReason::DebuggerEvent);
} else { } else {
debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00);
} }
if(step_event) step_event(); if(step_event) step_event();
opcode_edge = false;
SMP::op_step(); SMP::op_step();
synchronize_cpu(); synchronize_cpu();
} }
@ -35,6 +37,7 @@ void SMPDebugger::op_write(uint16 addr, uint8 data) {
SMPDebugger::SMPDebugger() { SMPDebugger::SMPDebugger() {
usage = new uint8[1 << 16](); usage = new uint8[1 << 16]();
opcode_pc = 0xffc0; opcode_pc = 0xffc0;
opcode_edge = false;
} }
SMPDebugger::~SMPDebugger() { SMPDebugger::~SMPDebugger() {

View File

@ -11,6 +11,7 @@ public:
}; };
uint8 *usage; uint8 *usage;
uint16 opcode_pc; uint16 opcode_pc;
bool opcode_edge;
void op_step(); void op_step();
uint8 op_read(uint16 addr); uint8 op_read(uint16 addr);

View File

@ -1,7 +1,7 @@
namespace SNES { namespace SNES {
namespace Info { namespace Info {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "072.06"; static const char Version[] = "072.07";
static const unsigned SerializerVersion = 14; static const unsigned SerializerVersion = 14;
} }
} }

View File

@ -11,28 +11,54 @@ void Console::create() {
traceToConsole.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to console"); y += Style::CheckBoxHeight; traceToConsole.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to console"); y += Style::CheckBoxHeight;
traceToConsole.setChecked(true); traceToConsole.setChecked(true);
traceToDisk.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to disk"); y += Style::CheckBoxHeight; traceToFile.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to file"); y += Style::CheckBoxHeight;
traceToDisk.setEnabled(false);
traceCPU.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace S-CPU"); y += Style::CheckBoxHeight; traceCPU.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace S-CPU"); y += Style::CheckBoxHeight;
traceCPU.setChecked(true); traceCPU.setChecked(true);
traceSMP.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace S-SMP"); y += Style::CheckBoxHeight; traceSMP.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace S-SMP"); y += Style::CheckBoxHeight;
setGeometry(0, 0, 775, 338); clearConsole.create(*this, x, 338 - Style::ButtonHeight - 5, 120, Style::ButtonHeight, "Clear console");
setGeometry(0, 0, 710, 338);
onClose = []() { onClose = []() {
debugger.showConsole.setChecked(false); debugger.showConsole.setChecked(false);
return true; return true;
}; };
traceToFile.onTick = []() { console.tracerEnable(console.traceToFile.checked()); };
clearConsole.onTick = []() {
console.buffer = "";
console.output.setText(console.buffer);
};
}
void Console::write(const string &text) {
if(traceToConsole.checked()) {
if(buffer != "") buffer.append("\n");
buffer.append(text);
output.setText(buffer);
output.setCursorPosition(~0);
}
if(traceToFile.checked() && logfile.open()) {
logfile.print(string(text, "\n"));
}
}
void Console::tracerEnable(bool state) {
if(state == true) {
string filename = { cartridge.baseName, ".log" };
logfile.open(filename, file::mode::write);
} else {
logfile.close();
}
} }
void Console::eventBreakpoint() { void Console::eventBreakpoint() {
if(traceToConsole.checked() == false) return; if(traceToConsole.checked() == false) return;
unsigned n = SNES::debugger.breakpoint_hit; unsigned n = SNES::debugger.breakpoint_hit;
string text = { "Breakpoint ", n + 1, " hit." }; write({ "Breakpoint ", n + 1, " hit." });
buffer.append(string(buffer == "" ? "" : "\n", text));
output.setText(buffer);
output.setCursorPosition(~0);
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::CPUBus) { if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::CPUBus) {
eventTraceCPU(); eventTraceCPU();
@ -43,22 +69,16 @@ void Console::eventBreakpoint() {
void Console::eventTraceCPU() { void Console::eventTraceCPU() {
if(traceCPU.checked() == false) return; if(traceCPU.checked() == false) return;
if(traceToConsole.checked() == false) return;
char text[256]; char text[256];
SNES::cpu.disassemble_opcode(text, SNES::cpu.regs.pc); SNES::cpu.disassemble_opcode(text, SNES::cpu.regs.pc);
buffer.append(string(buffer == "" ? "" : "\n", text)); write(text);
output.setText(buffer);
output.setCursorPosition(~0);
} }
void Console::eventTraceSMP() { void Console::eventTraceSMP() {
if(traceSMP.checked() == false) return; if(traceSMP.checked() == false) return;
if(traceToConsole.checked() == false) return;
char text[256]; char text[256];
SNES::smp.disassemble_opcode(text, SNES::smp.regs.pc); SNES::smp.disassemble_opcode(text, SNES::smp.regs.pc);
buffer.append(string(buffer == "" ? "" : "\n", text)); write(text);
output.setText(buffer);
output.setCursorPosition(~0);
} }

View File

@ -1,13 +1,17 @@
struct Console : TopLevelWindow { struct Console : TopLevelWindow {
EditBox output; EditBox output;
CheckBox traceToConsole; CheckBox traceToConsole;
CheckBox traceToDisk; CheckBox traceToFile;
CheckBox traceCPU; CheckBox traceCPU;
CheckBox traceSMP; CheckBox traceSMP;
Button clearConsole;
string buffer; string buffer;
file logfile;
void create(); void create();
void write(const string &text);
void tracerEnable(bool state);
void eventBreakpoint(); void eventBreakpoint();
void eventTraceCPU(); void eventTraceCPU();
void eventTraceSMP(); void eventTraceSMP();

View File

@ -1,6 +1,6 @@
CPUdebugger cpuDebugger; CPUDebugger cpuDebugger;
void CPUdebugger::create() { void CPUDebugger::create() {
Window::create(0, 0, 256, 256, "CPU Debugger"); Window::create(0, 0, 256, 256, "CPU Debugger");
application.addWindow(this, "Debugger.CPUdebugger", "192,192"); application.addWindow(this, "Debugger.CPUdebugger", "192,192");
@ -22,14 +22,18 @@ void CPUdebugger::create() {
}; };
stepInto.onTick = []() { stepInto.onTick = []() {
SNES::debugger.step_cpu = true;
debugger.debugMode = Debugger::DebugMode::StepIntoCPU; debugger.debugMode = Debugger::DebugMode::StepIntoCPU;
}; };
stepOver.onTick = { &CPUdebugger::eventStepOver, this }; stepOver.onTick = { &CPUDebugger::eventStepOver, this };
} }
void CPUdebugger::refreshDisassembly() { void CPUDebugger::synchronize() {
stepInto.setEnabled(SNES::cartridge.loaded() && debugger.enableDebugger.checked());
stepOver.setEnabled(stepInto.enabled() && SNES::cpu.opcode_edge);
}
void CPUDebugger::refreshDisassembly() {
unsigned addr = SNES::cpu.regs.pc; unsigned addr = SNES::cpu.regs.pc;
uint8_t *usage = SNES::cpu.usage; uint8_t *usage = SNES::cpu.usage;
@ -86,18 +90,18 @@ void CPUdebugger::refreshDisassembly() {
output.setText(buffer); output.setText(buffer);
} }
void CPUdebugger::eventStepInto() { void CPUDebugger::eventStepInto() {
SNES::debugger.step_cpu = false;
refreshDisassembly(); refreshDisassembly();
} }
void CPUdebugger::eventStepOver() { void CPUDebugger::eventStepOver() {
uint8_t opcode = read(SNES::cpu.regs.pc); uint8_t opcode = read(SNES::cpu.regs.pc);
unsigned length = SNESCPU::getOpcodeLength(SNES::cpu.regs.p.m, SNES::cpu.regs.p.x, opcode); unsigned length = SNESCPU::getOpcodeLength(SNES::cpu.regs.p.m, SNES::cpu.regs.p.x, opcode);
SNES::cpu.regs.pc += length; SNES::cpu.regs.pc += length;
refreshDisassembly(); refreshDisassembly();
console.eventTraceCPU();
} }
uint8_t CPUdebugger::read(unsigned addr) { uint8_t CPUDebugger::read(unsigned addr) {
return SNES::debugger.read(SNES::Debugger::MemorySource::CPUBus, addr); return SNES::debugger.read(SNES::Debugger::MemorySource::CPUBus, addr);
} }

View File

@ -1,10 +1,11 @@
struct CPUdebugger : TopLevelWindow { struct CPUDebugger : TopLevelWindow {
EditBox output; EditBox output;
Button stepInto; Button stepInto;
Button stepOver; Button stepOver;
Button proceed; Button proceed;
void create(); void create();
void synchronize();
void refreshDisassembly(); void refreshDisassembly();
void eventStepInto(); void eventStepInto();
void eventStepOver(); void eventStepOver();
@ -12,4 +13,4 @@ struct CPUdebugger : TopLevelWindow {
uint8_t read(unsigned addr); uint8_t read(unsigned addr);
}; };
extern CPUdebugger cpuDebugger; extern CPUDebugger cpuDebugger;

View File

@ -65,6 +65,13 @@ void Debugger::create() {
debugger.enable(false); debugger.enable(false);
return true; return true;
}; };
synchronize();
}
void Debugger::synchronize() {
cpuDebugger.synchronize();
smpDebugger.synchronize();
} }
void Debugger::setVisible(bool visible) { void Debugger::setVisible(bool visible) {
@ -78,9 +85,13 @@ void Debugger::setVisible(bool visible) {
void Debugger::enable(bool state) { void Debugger::enable(bool state) {
enableDebugger.setChecked(state); enableDebugger.setChecked(state);
SNES::debugger.step_cpu = state;
SNES::debugger.step_smp = state;
} }
void Debugger::run() { void Debugger::run() {
synchronize();
if(enableDebugger.checked() == false) { if(enableDebugger.checked() == false) {
SNES::system.run(); SNES::system.run();
return; return;
@ -101,20 +112,20 @@ void Debugger::run() {
} }
} }
if(debugMode == DebugMode::StepIntoCPU) { if(SNES::debugger.break_event == SNES::Debugger::BreakEvent::CPUStep) {
if(SNES::debugger.break_event == SNES::Debugger::BreakEvent::CPUStep) { if(debugMode == DebugMode::StepIntoCPU) {
debugMode = DebugMode::None; debugMode = DebugMode::None;
console.eventTraceCPU();
cpuDebugger.eventStepInto(); cpuDebugger.eventStepInto();
} }
console.eventTraceCPU();
} }
if(debugMode == DebugMode::StepIntoSMP) { if(SNES::debugger.break_event == SNES::Debugger::BreakEvent::SMPStep) {
if(SNES::debugger.break_event == SNES::Debugger::BreakEvent::SMPStep) { if(debugMode == DebugMode::StepIntoSMP) {
debugMode = DebugMode::None; debugMode = DebugMode::None;
console.eventTraceSMP();
smpDebugger.eventStepInto(); smpDebugger.eventStepInto();
} }
console.eventTraceSMP();
} }
SNES::debugger.break_event = SNES::Debugger::BreakEvent::None; SNES::debugger.break_event = SNES::Debugger::BreakEvent::None;

View File

@ -20,6 +20,7 @@ struct Debugger : TopLevelWindow {
CheckBox showMemoryEditor; CheckBox showMemoryEditor;
void create(); void create();
void synchronize();
void setVisible(bool visible = true); void setVisible(bool visible = true);
void enable(bool state); void enable(bool state);
void run(); void run();

View File

@ -22,13 +22,17 @@ void SMPDebugger::create() {
}; };
stepInto.onTick = []() { stepInto.onTick = []() {
SNES::debugger.step_smp = true;
debugger.debugMode = Debugger::DebugMode::StepIntoSMP; debugger.debugMode = Debugger::DebugMode::StepIntoSMP;
}; };
stepOver.onTick = { &SMPDebugger::eventStepOver, this }; stepOver.onTick = { &SMPDebugger::eventStepOver, this };
} }
void SMPDebugger::synchronize() {
stepInto.setEnabled(SNES::cartridge.loaded() && debugger.enableDebugger.checked());
stepOver.setEnabled(stepInto.enabled() && SNES::smp.opcode_edge);
}
void SMPDebugger::refreshDisassembly() { void SMPDebugger::refreshDisassembly() {
uint16_t addr = SNES::smp.regs.pc; uint16_t addr = SNES::smp.regs.pc;
uint8_t *usage = SNES::smp.usage; uint8_t *usage = SNES::smp.usage;
@ -86,7 +90,6 @@ void SMPDebugger::refreshDisassembly() {
} }
void SMPDebugger::eventStepInto() { void SMPDebugger::eventStepInto() {
SNES::debugger.step_smp = false;
refreshDisassembly(); refreshDisassembly();
} }
@ -95,6 +98,7 @@ void SMPDebugger::eventStepOver() {
unsigned length = SNESSMP::getOpcodeLength(opcode); unsigned length = SNESSMP::getOpcodeLength(opcode);
SNES::smp.regs.pc += length; SNES::smp.regs.pc += length;
refreshDisassembly(); refreshDisassembly();
console.eventTraceSMP();
} }
uint8_t SMPDebugger::read(uint16_t addr) { uint8_t SMPDebugger::read(uint16_t addr) {

View File

@ -5,6 +5,7 @@ struct SMPDebugger : TopLevelWindow {
Button proceed; Button proceed;
void create(); void create();
void synchronize();
void refreshDisassembly(); void refreshDisassembly();
void eventStepInto(); void eventStepInto();
void eventStepOver(); void eventStepOver();

View File

@ -6,7 +6,7 @@ void BreakpointEditor::create() {
unsigned x = 5, y = 5; unsigned x = 5, y = 5;
runToBreakpoint.create(*this, x, y, 295, Style::CheckBoxHeight, "Run To Breakpoint"); runToBreakpoint.create(*this, x, y, 295, Style::CheckBoxHeight, "Run to breakpoint");
y += Style::CheckBoxHeight + 5; y += Style::CheckBoxHeight + 5;
for(unsigned n = 0; n < Breakpoints; n++) { for(unsigned n = 0; n < Breakpoints; n++) {