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

View File

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

View File

@ -12,7 +12,8 @@ public:
UsageFlagX = 0x01,
};
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();
uint8 op_read(uint32 addr);

View File

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

View File

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

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "072.06";
static const char Version[] = "072.07";
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.setChecked(true);
traceToDisk.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to disk"); y += Style::CheckBoxHeight;
traceToDisk.setEnabled(false);
traceToFile.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace to file"); y += Style::CheckBoxHeight;
traceCPU.create(*this, x, y, 120, Style::CheckBoxHeight, "Trace S-CPU"); y += Style::CheckBoxHeight;
traceCPU.setChecked(true);
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 = []() {
debugger.showConsole.setChecked(false);
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() {
if(traceToConsole.checked() == false) return;
unsigned n = SNES::debugger.breakpoint_hit;
string text = { "Breakpoint ", n + 1, " hit." };
buffer.append(string(buffer == "" ? "" : "\n", text));
output.setText(buffer);
output.setCursorPosition(~0);
write({ "Breakpoint ", n + 1, " hit." });
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::CPUBus) {
eventTraceCPU();
@ -43,22 +69,16 @@ void Console::eventBreakpoint() {
void Console::eventTraceCPU() {
if(traceCPU.checked() == false) return;
if(traceToConsole.checked() == false) return;
char text[256];
SNES::cpu.disassemble_opcode(text, SNES::cpu.regs.pc);
buffer.append(string(buffer == "" ? "" : "\n", text));
output.setText(buffer);
output.setCursorPosition(~0);
write(text);
}
void Console::eventTraceSMP() {
if(traceSMP.checked() == false) return;
if(traceToConsole.checked() == false) return;
char text[256];
SNES::smp.disassemble_opcode(text, SNES::smp.regs.pc);
buffer.append(string(buffer == "" ? "" : "\n", text));
output.setText(buffer);
output.setCursorPosition(~0);
write(text);
}

View File

@ -1,13 +1,17 @@
struct Console : TopLevelWindow {
EditBox output;
CheckBox traceToConsole;
CheckBox traceToDisk;
CheckBox traceToFile;
CheckBox traceCPU;
CheckBox traceSMP;
Button clearConsole;
string buffer;
file logfile;
void create();
void write(const string &text);
void tracerEnable(bool state);
void eventBreakpoint();
void eventTraceCPU();
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");
application.addWindow(this, "Debugger.CPUdebugger", "192,192");
@ -22,14 +22,18 @@ void CPUdebugger::create() {
};
stepInto.onTick = []() {
SNES::debugger.step_cpu = true;
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;
uint8_t *usage = SNES::cpu.usage;
@ -86,18 +90,18 @@ void CPUdebugger::refreshDisassembly() {
output.setText(buffer);
}
void CPUdebugger::eventStepInto() {
SNES::debugger.step_cpu = false;
void CPUDebugger::eventStepInto() {
refreshDisassembly();
}
void CPUdebugger::eventStepOver() {
void CPUDebugger::eventStepOver() {
uint8_t opcode = read(SNES::cpu.regs.pc);
unsigned length = SNESCPU::getOpcodeLength(SNES::cpu.regs.p.m, SNES::cpu.regs.p.x, opcode);
SNES::cpu.regs.pc += length;
refreshDisassembly();
console.eventTraceCPU();
}
uint8_t CPUdebugger::read(unsigned addr) {
uint8_t CPUDebugger::read(unsigned addr) {
return SNES::debugger.read(SNES::Debugger::MemorySource::CPUBus, addr);
}

View File

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

View File

@ -65,6 +65,13 @@ void Debugger::create() {
debugger.enable(false);
return true;
};
synchronize();
}
void Debugger::synchronize() {
cpuDebugger.synchronize();
smpDebugger.synchronize();
}
void Debugger::setVisible(bool visible) {
@ -78,9 +85,13 @@ void Debugger::setVisible(bool visible) {
void Debugger::enable(bool state) {
enableDebugger.setChecked(state);
SNES::debugger.step_cpu = state;
SNES::debugger.step_smp = state;
}
void Debugger::run() {
synchronize();
if(enableDebugger.checked() == false) {
SNES::system.run();
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;
console.eventTraceCPU();
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;
console.eventTraceSMP();
smpDebugger.eventStepInto();
}
console.eventTraceSMP();
}
SNES::debugger.break_event = SNES::Debugger::BreakEvent::None;

View File

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

View File

@ -22,13 +22,17 @@ void SMPDebugger::create() {
};
stepInto.onTick = []() {
SNES::debugger.step_smp = true;
debugger.debugMode = Debugger::DebugMode::StepIntoSMP;
};
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() {
uint16_t addr = SNES::smp.regs.pc;
uint8_t *usage = SNES::smp.usage;
@ -86,7 +90,6 @@ void SMPDebugger::refreshDisassembly() {
}
void SMPDebugger::eventStepInto() {
SNES::debugger.step_smp = false;
refreshDisassembly();
}
@ -95,6 +98,7 @@ void SMPDebugger::eventStepOver() {
unsigned length = SNESSMP::getOpcodeLength(opcode);
SNES::smp.regs.pc += length;
refreshDisassembly();
console.eventTraceSMP();
}
uint8_t SMPDebugger::read(uint16_t addr) {

View File

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

View File

@ -6,7 +6,7 @@ void BreakpointEditor::create() {
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;
for(unsigned n = 0; n < Breakpoints; n++) {