From: slack Date: Sun, 26 Apr 2009 00:59:25 +0000 (+0200) Subject: Breakpoint support in the GUI X-Git-Url: http://slack.codemaniacs.com/git/?a=commitdiff_plain;h=49317bd0fae358698fdd7a82298cb640c3ee8c0b;p=wenboi.git Breakpoint support in the GUI --- diff --git a/core/GameBoy.h b/core/GameBoy.h index cef00d5..a330fe1 100644 --- a/core/GameBoy.h +++ b/core/GameBoy.h @@ -68,6 +68,17 @@ class GameBoy IRQ_JOYPAD = 0x10, }; + struct Breakpoint { + int addr; + bool enabled; + + Breakpoint(int a, bool e): addr(a), enabled(e) {} + Breakpoint(): addr(-1), enabled(false) {} + }; + + typedef std::map BreakpointMap; + + // Constructors / destructors GameBoy(GameBoyType type=GAMEBOY); ~GameBoy(); @@ -92,6 +103,7 @@ class GameBoy void delete_breakpoint (int id); void enable_breakpoint (int id); void disable_breakpoint(int id); + const BreakpointMap get_breakpoints() const { return breakpoints; } std::string status_string(); Instruction disassemble_opcode(u16 addr); @@ -178,17 +190,6 @@ class GameBoy // free ROM (used in destructor and load_rom) void free_ROM(); - // debug things - struct Breakpoint { - int addr; - bool enabled; - - Breakpoint(int a, bool e): addr(a), enabled(e) {} - Breakpoint(): addr(-1), enabled(false) {} - }; - - typedef std::map BreakpointMap; - BreakpointMap breakpoints; int last_breakpoint_id; diff --git a/qtboi/QtBoiDisassemblyWindow.cc b/qtboi/QtBoiDisassemblyWindow.cc index b0983da..9827e71 100644 --- a/qtboi/QtBoiDisassemblyWindow.cc +++ b/qtboi/QtBoiDisassemblyWindow.cc @@ -11,39 +11,39 @@ #include "../common/toString.h" QtBoiDisassemblyWindow::QtBoiDisassemblyWindow(QWidget *parent, GameBoy *gb, QHash *tags) - :QWidget(parent), gb(gb), tags(tags), history(), historyPosition(-1) +:QWidget(parent), gb(gb), tags(tags), history(), historyPosition(-1) { - setFocusPolicy(Qt::NoFocus); - - browser = new QTextBrowser(this); - browser->setOpenLinks(false); - browser->setFont(QFont("courier")); - browser->setFocusPolicy(Qt::NoFocus); - browser->setMinimumSize(500,500); - connect(browser, SIGNAL(anchorClicked(const QUrl&)), this, SIGNAL(anchorClicked(const QUrl &))); - - - backButton = new QPushButton("Back", this); - backButton->setEnabled(false); - backButton->setFocusPolicy(Qt::NoFocus); - forwardButton = new QPushButton("Forward", this); - forwardButton->setEnabled(false); - forwardButton->setFocusPolicy(Qt::NoFocus); - connect(backButton, SIGNAL(clicked()), this, SLOT(historyBack())); - connect(forwardButton, SIGNAL(clicked()), this, SLOT(historyForward())); - - //backButton->setIcon(QIcon("../icons/go-next.svg")); - //forwardButton->setIcon(QIcon("../icons/go-previous.svg")); - - QHBoxLayout *buttons = new QHBoxLayout(); - buttons->addWidget(backButton); - buttons->addWidget(forwardButton); - - QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(browser); - vbox->addLayout(buttons); - - setLayout(vbox); + setFocusPolicy(Qt::NoFocus); + + browser = new QTextBrowser(this); + browser->setOpenLinks(false); + browser->setFont(QFont("courier")); + browser->setFocusPolicy(Qt::NoFocus); + browser->setMinimumSize(500,500); + connect(browser, SIGNAL(anchorClicked(const QUrl&)), this, SIGNAL(anchorClicked(const QUrl &))); + + + backButton = new QPushButton("Back", this); + backButton->setEnabled(false); + backButton->setFocusPolicy(Qt::NoFocus); + forwardButton = new QPushButton("Forward", this); + forwardButton->setEnabled(false); + forwardButton->setFocusPolicy(Qt::NoFocus); + connect(backButton, SIGNAL(clicked()), this, SLOT(historyBack())); + connect(forwardButton, SIGNAL(clicked()), this, SLOT(historyForward())); + + //backButton->setIcon(QIcon("../icons/go-next.svg")); + //forwardButton->setIcon(QIcon("../icons/go-previous.svg")); + + QHBoxLayout *buttons = new QHBoxLayout(); + buttons->addWidget(backButton); + buttons->addWidget(forwardButton); + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(browser); + vbox->addLayout(buttons); + + setLayout(vbox); } QtBoiDisassemblyWindow::~QtBoiDisassemblyWindow() @@ -52,161 +52,181 @@ QtBoiDisassemblyWindow::~QtBoiDisassemblyWindow() std::string QtBoiDisassemblyWindow::htmlLinkMem(u32 addr) { - std::string result(""; - if (tags->value(addr) != "") - result += tags->value(addr).toStdString(); - else - result += toStringHex(addr, 4); - result += ""; - - return result; + std::string result(""; + if (tags->value(addr) != "") + result += tags->value(addr).toStdString(); + else + result += toStringHex(addr, 4); + result += ""; + + return result; } std::string QtBoiDisassemblyWindow::operandToHtml(const Instruction::Operand &op) { - std::string result; - switch(op.type) - { - case Instruction::MEM_DIRECT: - result += "("; - result += htmlLinkMem(op.val); - result += ")"; - break; - - case Instruction::INM16: - result += toStringHex(op.val,4); - break; - - default: - return op.str; - } - - return result; + std::string result; + switch(op.type) + { + case Instruction::MEM_DIRECT: + result += "("; + result += htmlLinkMem(op.val); + result += ")"; + break; + + case Instruction::INM16: + result += toStringHex(op.val,4); + break; + + default: + return op.str; + } + + return result; } std::string QtBoiDisassemblyWindow::insToHtml(const Instruction &ins) { - std::string result; - - if (ins.type == Instruction::RESET) - { - result += "RST "; - result += htmlLinkMem(strtol(ins.str.substr(4).c_str(), 0, 16)); - } - else - { - result += ins.str; - result += " "; - if (ins.str.substr(0,2)=="JR") - { - result += operandToHtml(ins.op1); - result += " ["; - result += htmlLinkMem(ins.op2.val); - result += "]"; - } - else if ((ins.str.substr(0,2)=="JP" && ins.op1.type == Instruction::INM16) - || ins.str.substr(0,4)=="CALL") - { - result += htmlLinkMem(ins.op1.val); - } - else - { - result += operandToHtml(ins.op1); - if (ins.op2.type != Instruction::NONE) - { - result += ", "; - result += operandToHtml(ins.op2); - } - } - } - return result; + std::string result; + + if (ins.type == Instruction::RESET) + { + result += "RST "; + result += htmlLinkMem(strtol(ins.str.substr(4).c_str(), 0, 16)); + } + else + { + result += ins.str; + result += " "; + if (ins.str.substr(0,2)=="JR") + { + result += operandToHtml(ins.op1); + result += " ["; + result += htmlLinkMem(ins.op2.val); + result += "]"; + } + else if ((ins.str.substr(0,2)=="JP" && ins.op1.type == Instruction::INM16) + || ins.str.substr(0,4)=="CALL") + { + result += htmlLinkMem(ins.op1.val); + } + else + { + result += operandToHtml(ins.op1); + if (ins.op2.type != Instruction::NONE) + { + result += ", "; + result += operandToHtml(ins.op2); + } + } + } + if (ins.str.substr(0,3)=="LDH") { + int port; + if (ins.op1.type == Instruction::MEM_DIRECT) + port = ins.op1.val - 0xFF00; + else + port = ins.op2.val - 0xFF00; + result += " ["+GameBoy::get_port_name(port)+"]"; + } + return result; } void QtBoiDisassemblyWindow::gotoAddress(u16 addr) { - int start = addr; - int end = start+200; - int pos = start; + GameBoy::BreakpointMap bpmap = gb->get_breakpoints(); - if (end > 0xFFFF) end = 0xFFFF; - std::ostringstream str; - - str << "Disassembly"; - str << ""; - str << ""; + int start = addr; + int end = start+200; + int pos = start; - bool hilightBG=true; + if (end > 0xFFFF) end = 0xFFFF; + std::ostringstream str; - while (pos < end) - { - Instruction ins(gb->disassemble_opcode(pos)); - str << "" << - "" << - "regs.PC ? "#ffc0c0" : (hilightBG ? "#d0d0d0" : "#ffffff"))) << ">" << + "" << + "" << + ""; + pos += ins.length; + + hilightBG = !hilightBG; + } + + str << "
LabelsAddrOpcodesInstruction
" << tags->value(pos).toStdString() << "0x" << std::hex << std::setw(4) << std::setfill('0') << - pos << ""; - for (int i=0; imemory.read(pos+i)) << " "; + str << "Disassembly"; + str << ""; + str << ""; - str << ""; - pos += ins.length; + while (pos < end) + { + bool isBP=false; - hilightBG = !hilightBG; + for (GameBoy::BreakpointMap::iterator i = bpmap.begin(); i != bpmap.end(); i++) { + if (i->second.addr == pos) { + isBP=true; + break; + } } - str << "
Labels BP AddrOpcodesInstruction
"; + bool hilightBG=true; - str << insToHtml(ins) << "
"; - - browser->setHtml(QString(str.str().c_str())); - currentAddress=addr; - - // If there are "forward" history elements - // - check if we are going forwards or backwards - // - else, delete forward history and insert the new address - if (historyPosition < history.size()-1 && history[historyPosition+1] == addr) - historyPosition++; - else if (historyPosition > 0 && history[historyPosition-1] == addr) - historyPosition--; - else - { - while (history.size() > historyPosition+1) - history.pop_back(); - history.push_back(addr); - historyPosition++; - } - - // enable/disable navigation buttons - if (historyPosition >= 1) backButton->setEnabled(true); - else backButton->setEnabled(false); - - if (historyPosition >= 0 && historyPosition < history.size()-1) forwardButton->setEnabled(true); - else forwardButton->setEnabled(false); + Instruction ins(gb->disassemble_opcode(pos)); + str << "
" << tags->value(pos).toStdString() << " "<< (isBP? "#" : " ") <<" 0x" << std::hex << std::setw(4) << std::setfill('0') << + pos << ""; + for (int i=0; imemory.read(pos+i)) << " "; + + str << ""; + + str << insToHtml(ins) << "
"; + + browser->setHtml(QString(str.str().c_str())); + currentAddress=addr; + + // If there are "forward" history elements + // - check if we are going forwards or backwards + // - else, delete forward history and insert the new address + if (historyPosition < history.size()-1 && history[historyPosition+1] == addr) + historyPosition++; + else if (historyPosition > 0 && history[historyPosition-1] == addr) + historyPosition--; + else + { + while (history.size() > historyPosition+1) + history.pop_back(); + history.push_back(addr); + historyPosition++; + } + + // enable/disable navigation buttons + if (historyPosition >= 1) backButton->setEnabled(true); + else backButton->setEnabled(false); + + if (historyPosition >= 0 && historyPosition < history.size()-1) forwardButton->setEnabled(true); + else forwardButton->setEnabled(false); } void QtBoiDisassemblyWindow::gotoPC() { - gotoAddress(gb->regs.PC); + gotoAddress(gb->regs.PC); } void QtBoiDisassemblyWindow::refresh() { - gotoAddress(currentAddress); + gotoAddress(currentAddress); } void QtBoiDisassemblyWindow::historyBack() { - if (historyPosition >= 1) - gotoAddress(history[historyPosition-1]); + if (historyPosition >= 1) + gotoAddress(history[historyPosition-1]); } void QtBoiDisassemblyWindow::historyForward() { - if (historyPosition < history.size()-1) - gotoAddress(history[historyPosition+1]); + if (historyPosition < history.size()-1) + gotoAddress(history[historyPosition+1]); } diff --git a/qtboi/QtBoiEmuThread.cc b/qtboi/QtBoiEmuThread.cc index 29b4673..edcfa57 100644 --- a/qtboi/QtBoiEmuThread.cc +++ b/qtboi/QtBoiEmuThread.cc @@ -139,9 +139,13 @@ void QtBoiEmuThread::run() { frameCount = gb.video.get_frames_rendered(); emit redraw(gb.video.get_screen_buffer()); - msleep(5); + //msleep(5); } } + if (status == GameBoy::BREAKPOINT) { + emit breakpointReached(gb.regs.PC); + isPaused=true; + } break; case STEP: do diff --git a/qtboi/QtBoiEmuThread.h b/qtboi/QtBoiEmuThread.h index e8e8b04..4108211 100644 --- a/qtboi/QtBoiEmuThread.h +++ b/qtboi/QtBoiEmuThread.h @@ -33,6 +33,7 @@ class QtBoiEmuThread: public QThread signals: void emulationPaused(); + void breakpointReached(uint addr); void redraw(const uchar *buffer); private: diff --git a/qtboi/QtBoiMainWindow.cc b/qtboi/QtBoiMainWindow.cc index d5d6425..1b66baf 100644 --- a/qtboi/QtBoiMainWindow.cc +++ b/qtboi/QtBoiMainWindow.cc @@ -220,6 +220,24 @@ void QtBoiMainWindow::onDisassemblyAnchorClicked(const QUrl& url) tags[addr] = tag; disassembly->refresh(); } + else if (url.scheme() == "togglebp") { + u32 addr = url.path().toUInt(); + bool bpFound = false; + GameBoy::BreakpointMap bpmap = emuThread->gb.get_breakpoints(); + for (GameBoy::BreakpointMap::iterator i=bpmap.begin(); i != bpmap.end(); i++) { + if (i->second.addr == addr) { + emuThread->gb.delete_breakpoint(i->first); + bpFound = true; + break; + } + } + if (!bpFound) { + emuThread->gb.set_breakpoint(addr); + std::cout << "bp set at 0x" << std::hex << addr << std::endl; + } + disassembly->refresh(); + status->update(); + } } void QtBoiMainWindow::onRedraw(const uchar *buffer) diff --git a/qtboi/QtBoiStatusWindow.cc b/qtboi/QtBoiStatusWindow.cc index 53f40c5..5d6ef5c 100644 --- a/qtboi/QtBoiStatusWindow.cc +++ b/qtboi/QtBoiStatusWindow.cc @@ -5,11 +5,11 @@ #include "QtBoiStatusWindow.h" QtBoiStatusWindow::QtBoiStatusWindow(QWidget *parent, GameBoy *gb) - :gb(gb) +:gb(gb) { - setFocusPolicy(Qt::NoFocus); - setMinimumSize(200,200); - setMaximumWidth(320); + setFocusPolicy(Qt::NoFocus); + setMinimumSize(200,200); + setMaximumWidth(320); } QtBoiStatusWindow::~QtBoiStatusWindow() @@ -18,64 +18,74 @@ QtBoiStatusWindow::~QtBoiStatusWindow() void QtBoiStatusWindow::update() { - std::ostringstream str; - - str << "Status"; - - str << ""; - str << "
"; - // 8-bit regs table - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << "
8-bit
A = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.A) << "
B = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.B) << "
C = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.C) << "
D = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.D) << "
E = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.E) << "
H = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.H) << "
L = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.L) << "
"; - str << "
"; - // 16-bit regs table - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << "
16-bit
BC = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.BC) << "
DE = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.DE) << "
HL = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.HL) << "
SP = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.SP) << "
PC = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.PC) << "
"; - str << "
"; - // Flags & state table (TODO: Interpret IE and IF registers) - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - str << ""; - int IE = int(gb->memory.read(0xFFFF, GBMemory::DONT_WATCH)); - str << ""; - int IF = int(gb->memory.read(0xFF0F, GBMemory::DONT_WATCH)); - str << ""; - str << "
Flags
Z
N
H
C
IME = " << int(gb->IME) << "
IE  ="; - if (IE & GameBoy::IRQ_VBLANK) str << " VBL"; - if (IE & GameBoy::IRQ_LCD_STAT) str << " LCD"; - if (IE & GameBoy::IRQ_TIMER) str << " TIM"; - if (IE & GameBoy::IRQ_SERIAL) str << " SER"; - if (IE & GameBoy::IRQ_JOYPAD) str << " JOYP"; - str << "
IF  ="; - if (IF & GameBoy::IRQ_VBLANK) str << " VBL"; - if (IF & GameBoy::IRQ_LCD_STAT) str << " LCD"; - if (IF & GameBoy::IRQ_TIMER) str << " TIM"; - if (IF & GameBoy::IRQ_SERIAL) str << " SER"; - if (IF & GameBoy::IRQ_JOYPAD) str << " JOYP"; - str << "
"; - str << "
"; + std::ostringstream str; - setHtml(QString(str.str().c_str())); + str << "Status"; + + str << ""; + str << "
"; + // 8-bit regs table + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << "
8-bit
A = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.A) << "
B = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.B) << "
C = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.C) << "
D = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.D) << "
E = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.E) << "
H = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.H) << "
L = 0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.L) << "
"; + str << "
"; + // 16-bit regs table + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << "
16-bit
BC = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.BC) << "
DE = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.DE) << "
HL = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.HL) << "
SP = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.SP) << "
PC = 0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.PC) << "
"; + str << "
"; + // Flags & state table (TODO: Interpret IE and IF registers) + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + str << ""; + int IE = int(gb->memory.read(0xFFFF, GBMemory::DONT_WATCH)); + str << ""; + int IF = int(gb->memory.read(0xFF0F, GBMemory::DONT_WATCH)); + str << ""; + str << "
Flags
Z
N
H
C
IME = " << int(gb->IME) << "
IE  ="; + if (IE & GameBoy::IRQ_VBLANK) str << " VBL"; + if (IE & GameBoy::IRQ_LCD_STAT) str << " LCD"; + if (IE & GameBoy::IRQ_TIMER) str << " TIM"; + if (IE & GameBoy::IRQ_SERIAL) str << " SER"; + if (IE & GameBoy::IRQ_JOYPAD) str << " JOYP"; + str << "
IF  ="; + if (IF & GameBoy::IRQ_VBLANK) str << " VBL"; + if (IF & GameBoy::IRQ_LCD_STAT) str << " LCD"; + if (IF & GameBoy::IRQ_TIMER) str << " TIM"; + if (IF & GameBoy::IRQ_SERIAL) str << " SER"; + if (IF & GameBoy::IRQ_JOYPAD) str << " JOYP"; + str << "
"; + str << "

"; + + str << ""; + str << ""; + GameBoy::BreakpointMap bpmap = gb->get_breakpoints(); + for (GameBoy::BreakpointMap::iterator i = bpmap.begin(); i != bpmap.end(); i++){ + str << ""; + str << ""; + } + + str <<"
Breakpoints
 " << i->first << " 0x" << std::hex << i->second.addr << (i->second.enabled? "":"(disabled)") << "
"; + + setHtml(QString(str.str().c_str())); }