123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- #include "DisasmViewer.h"
- #include "OpenMSXConnection.h"
- #include "CommClient.h"
- #include "DebuggerData.h"
- #include "Settings.h"
- #include <QPaintEvent>
- #include <QPainter>
- #include <QStyleOptionFocusRect>
- #include <QScrollBar>
- #include <QWheelEvent>
- #include <QApplication>
- #include <QDesktopWidget>
- #include <algorithm>
- #include <cmath>
- #include <cassert>
- #include <regex>
- class CommMemoryRequest : public ReadDebugBlockCommand
- {
- public:
- CommMemoryRequest(unsigned offset_, unsigned size_, unsigned char* target,
- DisasmViewer& viewer_)
- : ReadDebugBlockCommand("memory", offset_, size_, target)
- , offset(offset_), size(size_)
- , viewer(viewer_)
- {
- }
- virtual void replyOk(const QString& message)
- {
- copyData(message);
- viewer.memoryUpdated(this);
- }
- virtual void cancel()
- {
- viewer.updateCancelled(this);
- }
- // TODO Refactor: public members are ugly
- unsigned offset;
- unsigned size;
- int address;
- int line;
- int method;
- private:
- DisasmViewer& viewer;
- };
- DisasmViewer::DisasmViewer(QWidget* parent)
- : QFrame(parent)
- {
- setFrameStyle(WinPanel | Sunken);
- setFocusPolicy(Qt::StrongFocus);
- setBackgroundRole(QPalette::Base);
- setSizePolicy(QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred));
- breakMarker = QPixmap(":/icons/breakpoint.png");
- watchMarker = QPixmap(":/icons/watchpoint.png");
- pcMarker = QPixmap(":/icons/pcarrow.png");
- memory = NULL;
- cursorAddr = 0;
- cursorLine = 0;
- visibleLines = 0;
- programAddr = 0xFFFF;
- waitingForData = false;
- nextRequest = NULL;
- scrollBar = new QScrollBar(Qt::Vertical, this);
- scrollBar->setMinimum(0);
- scrollBar->setMaximum(0xFFFF);
- scrollBar->setSingleStep(0);
- scrollBar->setPageStep(0);
- settingsChanged();
- // manual scrollbar handling routines (real size of the data is not known)
- connect(scrollBar, SIGNAL(actionTriggered(int)),
- this, SLOT(scrollBarAction(int)));
- connect(scrollBar, SIGNAL(valueChanged(int)),
- this, SLOT(scrollBarChanged(int)));
- }
- QSize DisasmViewer::sizeHint() const
- {
- return QSize(xMnemArg + xMCode[0], 20 * codeFontHeight);
- }
- void DisasmViewer::resizeEvent(QResizeEvent* e)
- {
- QFrame::resizeEvent(e);
- scrollBar->setGeometry(width() - frameR, frameT,
- scrollBar->sizeHint().width(),
- height() - frameT - frameB);
- // reset the address in order to trigger a check on the disasmLines
- if (!waitingForData) {
- setAddress(scrollBar->value());
- }
- }
- void DisasmViewer::settingsChanged()
- {
- frameL = frameT = frameB = frameWidth();
- frameR = frameL + scrollBar->sizeHint().width();
- setMinimumHeight(frameT + 5 * fontMetrics().height() + frameB);
- // set font sizes
- Settings& s = Settings::get();
- QFontMetrics lfm(s.font(Settings::LABEL_FONT));
- QFontMetrics cfm(s.font(Settings::CODE_FONT));
- labelFontHeight = lfm.height();
- labelFontAscent = lfm.ascent();
- codeFontHeight = cfm.height();
- codeFontAscent = cfm.ascent();
- // calculate layout locations
- int charWidth = cfm.width("0");
- xAddr = frameL + 40;
- xMCode[0] = xAddr + 6 * charWidth;
- xMCode[1] = xMCode[0] + 3 * charWidth;
- xMCode[2] = xMCode[1] + 3 * charWidth;
- xMCode[3] = xMCode[2] + 3 * charWidth;
- xMnem = xMCode[3] + 4 * charWidth;
- xMnemArg = xMnem + 7 * charWidth;
- setMinimumSize(xMCode[0], 2*codeFontHeight);
- setMaximumSize(QApplication::desktop()->width(),
- QApplication::desktop()->height());
- update();
- }
- void DisasmViewer::symbolsChanged()
- {
- int disasmStart = disasmLines.front().addr;
- int disasmEnd = disasmLines.back().addr + disasmLines.back().numBytes;
- CommMemoryRequest* req = new CommMemoryRequest(
- disasmStart, disasmEnd - disasmStart, &memory[disasmStart], *this);
- req->address = disasmLines[disasmTopLine].addr;
- req->line = disasmLines[disasmTopLine].infoLine;
- req->method = TopAlways;
- if (waitingForData) {
- delete nextRequest;
- nextRequest = req;
- } else {
- waitingForData = true;
- CommClient::instance().sendCommand(req);
- }
- }
- void DisasmViewer::paintEvent(QPaintEvent* e)
- {
- // call parent for drawing the actual frame
- QFrame::paintEvent(e);
- QPainter p(this);
- // calc and set drawing bounds
- QRect r(e->rect());
- if (r.left() < frameL) r.setLeft(frameL);
- if (r.top() < frameT) r.setTop(frameT);
- if (r.right() > width() - frameR - 1) r.setRight (width() - frameR - 1);
- if (r.bottom() > height() - frameB - 1) r.setBottom(height() - frameB - 1);
- p.setClipRect(r);
- // draw background
- p.fillRect(frameL + 32, frameT, width() - 32 - frameL - frameR,
- height() - frameT - frameB, palette().color(QPalette::Base));
- // calculate lines while drawing
- visibleLines = 0;
- int y = frameT;
- QString hexStr;
- const DisasmRow* row;
- bool displayDisasm = memory != NULL && isEnabled();
- Settings& s = Settings::get();
- p.setFont(s.font(Settings::CODE_FONT));
- while (y < height() - frameB) {
- // fetch the data for this line
- row = displayDisasm
- ? &disasmLines[disasmTopLine+visibleLines]
- : &DISABLED_ROW;
- int h, a;
- switch (row->rowType) {
- case DisasmRow::LABEL:
- h = labelFontHeight;
- a = labelFontAscent;
- break;
- case DisasmRow::INSTRUCTION:
- h = codeFontHeight;
- a = codeFontAscent;
- break;
- }
- // draw cursor line
- bool isCursorLine = cursorAddr >= row->addr && cursorAddr < row->addr+row->numBytes &&
- row->infoLine == cursorLine;
- if (isCursorLine) {
- cursorAddr = row->addr;
- p.fillRect(frameL + 32, y, width() - 32 - frameL - frameR, h,
- palette().color(QPalette::Highlight));
- if (hasFocus()) {
- QStyleOptionFocusRect so;
- so.backgroundColor = palette().color(QPalette::Highlight);
- so.rect = QRect(frameL + 32, y, width() - 32 - frameL - frameR, h);
- style()->drawPrimitive(QStyle::PE_FrameFocusRect, &so, &p, this);
- }
- p.setPen(palette().color(QPalette::HighlightedText));
- }
- // if there is a label here, draw the label, otherwise code
- if (row->rowType == DisasmRow::LABEL) {
- // draw label
- hexStr = QString("%1:").arg(row->instr.c_str());
- p.setFont(s.font(Settings::LABEL_FONT));
- if (!isCursorLine) {
- p.setPen(s.fontColor(Settings::LABEL_FONT));
- }
- p.drawText(xAddr, y + a, hexStr);
- p.setFont(s.font(Settings::CODE_FONT));
- } else {
- // draw code line
- // default to text pen
- if (!isCursorLine) {
- p.setPen(s.fontColor(Settings::CODE_FONT));
- }
- // draw breakpoint marker
- if (row->infoLine == 0) {
- if (breakpoints->isBreakpoint(row->addr)) {
- p.drawPixmap(frameL + 2, y + h / 2 -5, breakMarker);
- if (!isCursorLine) {
- p.fillRect(frameL + 32, y, width() - 32 - frameL - frameR, h,
- Qt::red);
- p.setPen(Qt::white);
- }
- } else if (breakpoints->isWatchpoint(row->addr)) {
- p.drawPixmap(frameL + 2, y + h / 2 -5, watchMarker);
- }
- }
- // draw PC marker
- if (row->addr == programAddr && row->infoLine == 0) {
- p.drawPixmap(frameL + 18, y + h / 2 - 5, pcMarker);
- }
- // print the address
- hexStr.sprintf("%04X", row->addr);
- p.drawText(xAddr, y + a, hexStr);
- // print 1 to 4 bytes
- for (int j = 0; j < row->numBytes; ++j) {
- hexStr.sprintf("%02X", displayDisasm ? memory[row->addr + j] : 0);
- p.drawText(xMCode[j], y + a, hexStr);
- }
- // print the instruction and arguments
- p.drawText(xMnem, y + a, row->instr.substr(0, 7).c_str());
- p.drawText(xMnemArg, y + a, row->instr.substr(7 ).c_str());
- }
- // next line
- y += h;
- ++visibleLines;
- // check for end of data
- if (disasmTopLine+visibleLines == int(disasmLines.size())) break;
- }
- partialBottomLine = y > height() - frameB;
- visibleLines -= partialBottomLine;
- }
- void DisasmViewer::setCursorAddress(quint16 addr, int infoLine, int method)
- {
- cursorAddr = addr;
- cursorLine = 0;
- setAddress(addr, infoLine, method);
- }
- void DisasmViewer::setAddress(quint16 addr, int infoLine, int method)
- {
- int line = findDisasmLine(addr, infoLine);
- if (line >= 0) {
- int dt, db;
- switch (method) {
- case Top:
- case Middle:
- case Bottom:
- case Closest:
- dt = db = 10;
- break;
- case TopAlways:
- dt = 10;
- db = visibleLines + 10;
- break;
- case MiddleAlways:
- dt = db = 10 + visibleLines / 2;
- break;
- case BottomAlways:
- dt = 10 + visibleLines;
- db = 10;
- break;
- default:
- assert(false);
- dt = db = 0; // avoid warning
- }
- if ((line > dt || disasmLines[0].addr == 0) &&
- (line < int(disasmLines.size()) - db ||
- disasmLines.back().addr+disasmLines.back().numBytes > 0xFFFF)) {
- // line is within existing disasm'd data. Find where to put it.
- if (method == Top || method == TopAlways ||
- (method == Closest && line < disasmTopLine)) {
- // Move line to top
- disasmTopLine = line;
- } else if (method == Bottom || method == BottomAlways ||
- (method == Closest && line >= disasmTopLine + visibleLines)) {
- // Move line to bottom
- disasmTopLine = line - visibleLines + 1;
- } else if (method == MiddleAlways ||
- (method == Middle && (line < disasmTopLine || line >= disasmTopLine + visibleLines))) {
- // Move line to middle
- disasmTopLine = line - visibleLines / 2;
- }
- disasmTopLine = std::min(
- disasmTopLine, int(disasmLines.size()) - visibleLines);
- disasmTopLine = std::max(disasmTopLine, 0);
- update();
- return;
- }
- }
- if (method == Closest) {
- if (addr < disasmLines[disasmTopLine].addr) {
- method = Top;
- } else {
- method = Bottom;
- }
- }
- // The requested address it outside the pre-disassembled bounds.
- // This means that a new block of memory must be transfered from
- // openMSX and disassembled.
- // determine disasm bounds
- int disasmStart;
- int disasmEnd;
- int extra = 4 * (visibleLines > 9 ? visibleLines+partialBottomLine : 10);
- switch (method) {
- case Middle:
- case MiddleAlways:
- disasmStart = addr - 3 * extra / 2;
- disasmEnd = addr + 3 * extra / 2;
- break;
- case Bottom:
- case BottomAlways:
- disasmStart = addr - 2 * extra;
- disasmEnd = addr + extra;
- break;
- default:
- disasmStart = addr - extra;
- disasmEnd = addr + 2 * extra;
- }
- disasmStart = std::max(disasmStart, 0);
- disasmEnd = std::min(disasmEnd, 0xFFFF);
- CommMemoryRequest* req = new CommMemoryRequest(
- disasmStart, disasmEnd - disasmStart + 1, &memory[disasmStart], *this);
- req->address = addr;
- req->line = infoLine;
- req->method = method;
- if (waitingForData) {
- delete nextRequest;
- nextRequest = req;
- } else {
- waitingForData = true;
- CommClient::instance().sendCommand(req);
- }
- }
- void DisasmViewer::memoryUpdated(CommMemoryRequest* req)
- {
- // disassemble the newly received memory
- dasm(memory, req->offset, req->offset + req->size - 1, disasmLines,
- memLayout, symTable, programAddr);
- // locate the requested line
- disasmTopLine = findDisasmLine(req->address, req->line);
- switch (req->method) {
- case Middle:
- case MiddleAlways:
- disasmTopLine -= visibleLines / 2;
- break;
- case Bottom:
- case BottomAlways:
- disasmTopLine -= visibleLines - 1;
- break;
- }
- disasmTopLine = std::max(disasmTopLine, 0);
- disasmTopLine = std::min(disasmTopLine,
- int(disasmLines.size()) - visibleLines);
- updateCancelled(req);
- // sync the scrollbar with the actual address reached
- if (!waitingForData) {
- // set the slider with without the signal
- disconnect(scrollBar, SIGNAL(valueChanged(int)),
- this, SLOT(scrollBarChanged(int)));
- scrollBar->setSliderPosition(disasmLines[disasmTopLine].addr);
- connect (scrollBar, SIGNAL(valueChanged(int)),
- this, SLOT(scrollBarChanged(int)));
- // set the line
- setAddress(disasmLines[disasmTopLine].addr,
- disasmLines[disasmTopLine].infoLine);
- update();
- }
- }
- void DisasmViewer::updateCancelled(CommMemoryRequest* req)
- {
- delete req;
- if (nextRequest) {
- CommClient::instance().sendCommand(nextRequest);
- nextRequest = NULL;
- } else {
- waitingForData = false;
- }
- }
- quint16 DisasmViewer::cursorAddress() const
- {
- return cursorAddr;
- }
- quint16 DisasmViewer::programCounter() const
- {
- return programAddr;
- }
- void DisasmViewer::setProgramCounter(quint16 pc)
- {
- cursorAddr = pc;
- programAddr = pc;
- setAddress(pc, 0, MiddleAlways);
- }
- int DisasmViewer::findDisasmLine(quint16 lineAddr, int infoLine)
- {
- int line = disasmLines.size() - 1;
- while( line >= 0 ) {
- if( lineAddr >= disasmLines[line].addr ) {
- if( infoLine == 0 )
- return line;
- if (infoLine == LAST_INFO_LINE)
- return line-1;
- while (disasmLines[line].infoLine != infoLine && disasmLines[line].addr == lineAddr) {
- line--;
- if (line < 0) return -1;
- }
- return line;
- }
- line--;
- }
- return -1;
- }
- void DisasmViewer::scrollBarAction(int action)
- {
- switch (action) {
- case QScrollBar::SliderSingleStepAdd:
- setAddress(disasmLines[disasmTopLine + 1].addr,
- disasmLines[disasmTopLine + 1].infoLine,
- TopAlways);
- break;
- case QScrollBar::SliderSingleStepSub:
- if (disasmTopLine > 0) {
- setAddress(disasmLines[disasmTopLine - 1].addr,
- disasmLines[disasmTopLine - 1].infoLine,
- TopAlways);
- }
- break;
- case QScrollBar::SliderPageStepAdd: {
- int line = disasmTopLine + visibleLines + partialBottomLine - 1;
- if (line < int(disasmLines.size())) {
- setAddress(disasmLines[line].addr,
- disasmLines[line].infoLine,
- TopAlways);
- }
- break;
- }
- case QScrollBar::SliderPageStepSub:
- if (disasmTopLine > 0) {
- setAddress(disasmLines[disasmTopLine - 1].addr,
- disasmLines[disasmTopLine - 1].infoLine,
- BottomAlways);
- }
- break;
- default:
- break;
- }
- }
- // moving the slider is handled separately because
- // the SliderMoved action won't catch the extreme values
- void DisasmViewer::scrollBarChanged(int value)
- {
- setAddress(value, FIRST_INFO_LINE, TopAlways);
- }
- void DisasmViewer::setMemory(unsigned char* memPtr)
- {
- memory = memPtr;
- // init disasmLines
- DisasmRow newRow;
- newRow.rowType = DisasmRow::INSTRUCTION;
- newRow.numBytes = 1;
- newRow.infoLine = 0;
- newRow.instr = "nop";
- newRow.instr.resize(8, ' ');
- for (int i = 0; i < 150; ++i) {
- newRow.addr = i;
- disasmLines.push_back(newRow);
- }
- disasmTopLine = 50;
- }
- void DisasmViewer::setBreakpoints(Breakpoints* bps)
- {
- breakpoints = bps;
- }
- void DisasmViewer::setMemoryLayout(MemoryLayout* ml)
- {
- memLayout = ml;
- }
- void DisasmViewer::setSymbolTable(SymbolTable* st)
- {
- symTable = st;
- }
- void DisasmViewer::keyPressEvent(QKeyEvent* e)
- {
- switch (e->key()) {
- case Qt::Key_Up: {
- int line = findDisasmLine(cursorAddr, cursorLine);
- if (line > 0) {
- cursorAddr = disasmLines[line - 1].addr;
- cursorLine = disasmLines[line - 1].infoLine;
- }
- setAddress(cursorAddr, cursorLine, Closest);
- e->accept();
- break;
- }
- case Qt::Key_Down: {
- int line = findDisasmLine(cursorAddr, cursorLine);
- if (line >= 0 && line < int(disasmLines.size()) - 1) {
- cursorAddr = disasmLines[line + 1].addr;
- cursorLine = disasmLines[line + 1].infoLine;
- }
- setAddress(cursorAddr, cursorLine, Closest);
- e->accept();
- break;
- }
- case Qt::Key_PageUp: {
- int line = findDisasmLine(cursorAddr, cursorLine);
- if( line >= disasmTopLine && line < disasmTopLine+visibleLines ) {
- line -= visibleLines;
- if(line >= 0) {
- cursorAddr = disasmLines[line].addr;
- cursorLine = disasmLines[line].infoLine;
- setAddress(disasmLines[disasmTopLine - 1].addr,
- disasmLines[disasmTopLine - 1].infoLine,
- BottomAlways);
- } else {
- setCursorAddress(disasmLines[0].addr,
- disasmLines[0].infoLine,
- Closest);
- }
- } else
- setAddress(cursorAddr, cursorLine, Closest);
- e->accept();
- break;
- }
- case Qt::Key_PageDown: {
- int line = findDisasmLine(cursorAddr, cursorLine);
- if( line >= disasmTopLine && line < disasmTopLine+visibleLines ) {
- line += visibleLines;
- if( line < int(disasmLines.size()) ) {
- cursorAddr = disasmLines[line].addr;
- cursorLine = disasmLines[line].infoLine;
- line = disasmTopLine + visibleLines + partialBottomLine - 1;
- line = std::min(line, int(disasmLines.size()));
- setAddress(disasmLines[line].addr,
- disasmLines[line].infoLine,
- TopAlways);
- } else {
- setCursorAddress(disasmLines.back().addr,
- disasmLines.back().infoLine,
- Closest);
- }
- } else
- setAddress(cursorAddr, cursorLine, Closest);
- e->accept();
- break;
- }
- case Qt::Key_Home: {
- setCursorAddress(0, 0, Middle);
- e->accept();
- break;
- }
- case Qt::Key_End: {
- setCursorAddress(0xffff, 0, Middle);
- e->accept();
- break;
- }
- case Qt::Key_Right:
- case Qt::Key_Return: {
- int line = findDisasmLine(cursorAddr, cursorLine);
- if (line >= 0 && line < int(disasmLines.size())) {
- int naddr = INT_MAX;
- const DisasmRow &row = disasmLines[line];
- std::string instr = row.instr;
- std::regex re_absolute("(call|jp)\\ .*");
- std::regex re_relative("(djnz|jr)\\ .*");
- if (std::regex_match(instr, re_absolute))
- naddr = memory[row.addr + 1] + memory[row.addr + 2] * 256;
- else if (std::regex_match(instr, re_relative))
- naddr = (row.addr + 2 + (signed char)memory[row.addr + 1]) & 0xFFFF;
- if(naddr != INT_MAX) {
- jumpStack.push_back(cursorAddr);
- setCursorAddress(naddr, 0, Middle);
- }
- }
- e->accept();
- break;
- }
- case Qt::Key_Left:
- case Qt::Key_Backspace: {
- if(!jumpStack.empty()){
- int addr = jumpStack.takeLast();
- setCursorAddress(addr, 0, Middle);
- }
- e->accept();
- break;
- }
- default:
- QFrame::keyReleaseEvent(e);
- }
- }
- void DisasmViewer::wheelEvent(QWheelEvent* e)
- {
- int line = std::max(0, disasmTopLine - e->delta() / 40);
- if (e->orientation() == Qt::Vertical) {
- setAddress(disasmLines[line].addr,
- disasmLines[line].infoLine,
- TopAlways);
- e->accept();
- }
- }
- void DisasmViewer::mousePressEvent(QMouseEvent* e)
- {
- if (!(e->button() == Qt::LeftButton)) {
- QFrame::mousePressEvent(e);
- return;
- }
- if (e->x() >= frameL && e->x() < width() - frameR &&
- e->y() >= frameT && e->y() < height() - frameB) {
- int line = lineAtPos(e->pos());
- if (e->x() > frameL + 32) {
- // check if the line exists
- // (bottom of memory could have an empty line)
- if (line + disasmTopLine < int(disasmLines.size())) {
- cursorAddr = disasmLines[disasmTopLine + line].addr;
- cursorLine = disasmLines[disasmTopLine + line].infoLine;
- } else {
- return;
- }
- // scroll if partial line
- if (line == visibleLines) {
- setAddress(disasmLines[disasmTopLine + 1].addr, TopAlways);
- } else {
- update();
- }
- } else if (e->x() < frameL + 16) {
- // clicked on the breakpoint area
- if (line + disasmTopLine < int(disasmLines.size())) {
- emit toggleBreakpoint(disasmLines[line + disasmTopLine].addr);
- }
- }
- }
- }
- int DisasmViewer::lineAtPos(const QPoint& pos)
- {
- int line = -1;
- int y = frameT;
- do {
- ++line;
- switch (disasmLines[disasmTopLine+line].rowType) {
- case DisasmRow::LABEL:
- y += labelFontHeight;
- break;
- case DisasmRow::INSTRUCTION:
- y += codeFontHeight;
- break;
- }
- } while (y < pos.y());
- return line;
- }
|