From 32084b4f5dfbfa2f653f55cfa514317c4b065b1e Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Mon, 11 Feb 2019 15:09:10 +0100 Subject: Converted program listing to be QTableView based. Signed-off-by: Pavel Pisa --- qtmips_gui/hexlineedit.cpp | 4 +- qtmips_gui/hexlineedit.h | 4 +- qtmips_gui/mainwindow.cpp | 2 +- qtmips_gui/memorydock.cpp | 9 +- qtmips_gui/memorymodel.cpp | 5 + qtmips_gui/memorytableview.cpp | 10 +- qtmips_gui/memorytableview.h | 4 +- qtmips_gui/memoryview.cpp | 246 ---------------------------------------- qtmips_gui/memoryview.h | 113 ------------------ qtmips_gui/programdock.cpp | 132 ++++++--------------- qtmips_gui/programdock.h | 35 +----- qtmips_gui/programmodel.cpp | 185 ++++++++++++++++++++++++++++++ qtmips_gui/programmodel.h | 97 ++++++++++++++++ qtmips_gui/programtableview.cpp | 155 +++++++++++++++++++++++++ qtmips_gui/programtableview.h | 68 +++++++++++ qtmips_gui/qtmips_gui.pro | 10 +- 16 files changed, 575 insertions(+), 504 deletions(-) delete mode 100644 qtmips_gui/memoryview.cpp delete mode 100644 qtmips_gui/memoryview.h create mode 100644 qtmips_gui/programmodel.cpp create mode 100644 qtmips_gui/programmodel.h create mode 100644 qtmips_gui/programtableview.cpp create mode 100644 qtmips_gui/programtableview.h diff --git a/qtmips_gui/hexlineedit.cpp b/qtmips_gui/hexlineedit.cpp index c138591..761f153 100644 --- a/qtmips_gui/hexlineedit.cpp +++ b/qtmips_gui/hexlineedit.cpp @@ -75,13 +75,13 @@ HexLineEdit::HexLineEdit(QWidget *parent, int digits, int base, QString prefix): set_value(0); } -void HexLineEdit::set_value(std::int32_t value) { +void HexLineEdit::set_value(std::uint32_t value) { QString s, t = ""; last_set = value; s = QString::number(value, base); if (s.count() < digits) t.fill('0', digits - s.count()); - setText(prefix + t +s); + setText(prefix + t +s.toUpper()); } diff --git a/qtmips_gui/hexlineedit.h b/qtmips_gui/hexlineedit.h index 6b16edc..a00063b 100644 --- a/qtmips_gui/hexlineedit.h +++ b/qtmips_gui/hexlineedit.h @@ -50,10 +50,10 @@ public: QString prefix = "0x"); public slots: - void set_value(std::int32_t value); + void set_value(std::uint32_t value); signals: - void value_edit_finished(std::int32_t value); + void value_edit_finished(std::uint32_t value); private slots: void on_edit_finished(); diff --git a/qtmips_gui/mainwindow.cpp b/qtmips_gui/mainwindow.cpp index a520f9d..90d6911 100644 --- a/qtmips_gui/mainwindow.cpp +++ b/qtmips_gui/mainwindow.cpp @@ -145,7 +145,7 @@ void MainWindow::create_core(const machine::MachineConfig &config) { connect(corescene, SIGNAL(request_registers()), this, SLOT(show_registers())); connect(corescene, SIGNAL(request_program_memory()), this, SLOT(show_program())); connect(corescene, SIGNAL(request_data_memory()), this, SLOT(show_memory())); - connect(corescene, SIGNAL(request_jump_to_program_counter(std::uint32_t)), program, SLOT(jump_to_pc(std::uint32_t))); + connect(corescene, SIGNAL(request_jump_to_program_counter(std::uint32_t)), program, SIGNAL(jump_to_pc(std::uint32_t))); connect(corescene, SIGNAL(request_cache_program()), this, SLOT(show_cache_program())); connect(corescene, SIGNAL(request_cache_data()), this, SLOT(show_cache_data())); // Connect signal from break to machine pause diff --git a/qtmips_gui/memorydock.cpp b/qtmips_gui/memorydock.cpp index dfeb9c8..ccb3b6a 100644 --- a/qtmips_gui/memorydock.cpp +++ b/qtmips_gui/memorydock.cpp @@ -71,7 +71,6 @@ MemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) { layout->addWidget(cell_size); layout->addWidget(memory_content); layout->addWidget(go_edit); - layout->addWidget(go_edit); content->setLayout(layout); @@ -80,10 +79,10 @@ MemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) { connect(this, &MemoryDock::machine_setup, memory_model, &MemoryModel::setup); connect(cell_size, SIGNAL(currentIndexChanged(int)), memory_content, SLOT(set_cell_size(int))); - connect(go_edit, SIGNAL(value_edit_finished(std::int32_t)), - memory_content, SLOT(go_to_address(std::int32_t))); - connect(memory_content, SIGNAL(address_changed(std::int32_t)), - go_edit, SLOT(set_value(std::int32_t))); + connect(go_edit, SIGNAL(value_edit_finished(std::uint32_t)), + memory_content, SLOT(go_to_address(std::uint32_t))); + connect(memory_content, SIGNAL(address_changed(std::uint32_t)), + go_edit, SLOT(set_value(std::uint32_t))); } void MemoryDock::setup(machine::QtMipsMachine *machine) { diff --git a/qtmips_gui/memorymodel.cpp b/qtmips_gui/memorymodel.cpp index 09157bf..908f019 100644 --- a/qtmips_gui/memorymodel.cpp +++ b/qtmips_gui/memorymodel.cpp @@ -167,6 +167,11 @@ void MemoryModel::set_cell_size(int index) { } void MemoryModel::update_all() { + if (machine != nullptr && machine->memory() != nullptr) { + memory_change_counter = machine->memory()->get_change_counter(); + if (machine->cache_data() != nullptr) + cache_data_change_counter = machine->cache_data()->get_change_counter(); + } emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } diff --git a/qtmips_gui/memorytableview.cpp b/qtmips_gui/memorytableview.cpp index 9b5d0ba..18e1bb1 100644 --- a/qtmips_gui/memorytableview.cpp +++ b/qtmips_gui/memorytableview.cpp @@ -62,21 +62,21 @@ void MemoryTableView::adjustColumnCount() { QString t = ""; t.fill(QChar('0'), m->cellSizeBytes() * 2); /* t + = " C"; */ - int width1 = fm.width(t) + 1; + int width1 = fm.width(t); if (width1 < fm.width("+99")) width1 = fm.width("+99"); horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); horizontalHeader()->resizeSection(1, width1); - int w = verticalHeader()->width() + 8; + int w = verticalHeader()->width() + 4; unsigned int cells; width0 = columnWidth(0); width1 = columnWidth(1); w = width() - w - width0; - if (w < width1) { + if (w < width1 + 2) { cells = 1; } else { - cells = w / width1; + cells = w / (width1 + 2); } if (cells != m->cellsPerRow()) { m->setCellsPerRow(cells); @@ -167,7 +167,7 @@ void MemoryTableView::resizeEvent(QResizeEvent *event) { } } -void MemoryTableView:: go_to_address(std::int32_t address) { +void MemoryTableView:: go_to_address(std::uint32_t address) { MemoryModel *m = dynamic_cast(model()); int row; if (m == nullptr) diff --git a/qtmips_gui/memorytableview.h b/qtmips_gui/memorytableview.h index 9ade84f..f5bf832 100644 --- a/qtmips_gui/memorytableview.h +++ b/qtmips_gui/memorytableview.h @@ -52,10 +52,10 @@ public: void resizeEvent(QResizeEvent *event) override; signals: - void address_changed(std::int32_t address); + void address_changed(std::uint32_t address); public slots: void set_cell_size(int index); - void go_to_address(std::int32_t address); + void go_to_address(std::uint32_t address); private slots: void adjust_scroll_pos(); private: diff --git a/qtmips_gui/memoryview.cpp b/qtmips_gui/memoryview.cpp deleted file mode 100644 index c4d022a..0000000 --- a/qtmips_gui/memoryview.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/******************************************************************************* - * QtMips - MIPS 32-bit Architecture Subset Simulator - * - * Implemented to support following courses: - * - * B35APO - Computer Architectures - * https://cw.fel.cvut.cz/wiki/courses/b35apo - * - * B4M35PAP - Advanced Computer Architectures - * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start - * - * Copyright (c) 2017-2019 Karel Koci - * Copyright (c) 2019 Pavel Pisa - * - * Faculty of Electrical Engineering (http://www.fel.cvut.cz) - * Czech Technical University (http://www.cvut.cz/) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - ******************************************************************************/ - -#include "memoryview.h" - -/////////////////////////// -// Minimal reserved range in pixels of scroll area (otherwise 10% of height are used) -#define MIN_OFF 10 -// Focus point (this is multiplied with height of widget to know position where we want to focus) -#define FOCUS 0.25 -// How angle maps to pixels when and scroll is used -#define ANGLE_SCROLL 1 -/////////////////////////// - -MemoryView::MemoryView(QWidget *parent, std::uint32_t addr0) : QWidget(parent) { - memory = nullptr; - addr_0 = addr0; - change_counter = 0; - cache_data_change_counter = 0; - - layout = new QVBoxLayout(this); - - memf= new Frame(this); - layout->addWidget(memf); - - - ctl_widg = new QWidget(this); - layout->addWidget(ctl_widg); - - go_edit = new QLineEdit(ctl_widg); - go_edit->setText("0x00000000"); - go_edit->setInputMask("\\0\\xHHHHHHHH"); - layout->addWidget(go_edit); - connect(go_edit, SIGNAL(editingFinished()), this, SLOT(go_edit_finish())); -} - -void MemoryView::setup(machine::QtMipsMachine *machine) { - memory = (machine == nullptr) ? nullptr : machine->memory(); - cache_data = (machine == nullptr) ? nullptr : machine->cache_data(); - if (machine != nullptr) - connect(machine, SIGNAL(post_tick()), this, SLOT(check_for_updates())); - reload_content(); -} - -void MemoryView::set_focus(std::uint32_t address) { - if (address < addr_0 || (address - addr_0)/4 > (unsigned)memf->widg->count()) { - // This is outside of loaded area so just move it and reload everything - addr_0 = address - 4*memf->focussed(); - addr0_save_change(addr_0); - reload_content(); - } else { - memf->focus((address - addr_0) / 4); - } - edit_load_focus(); -} - -std::uint32_t MemoryView::focus() const { - return addr_0 + 4*memf->focussed(); -} - -std::uint32_t MemoryView::addr0() const { - return addr_0; -} - -void MemoryView::edit_load_focus() { - go_edit->setText(QString("0x%1").arg(focus(), 8, 16, QChar('0'))); -} - -void MemoryView::check_for_updates() { - bool need_update = false; - if (memory != nullptr) { - if (change_counter != memory->get_change_counter()) - need_update = true; - } - if (cache_data != nullptr) { - if (cache_data_change_counter != cache_data->get_change_counter()) - need_update = true; - } - if (need_update) - reload_content(); -} - -void MemoryView::reload_content() { - int count = memf->widg->count(); - memf->widg->clearRows(); - if (memory != nullptr) - change_counter = memory->get_change_counter(); - if (cache_data != nullptr) - cache_data_change_counter = cache_data->get_change_counter(); - update_content(count, 0); -} - -void MemoryView::update_content(int count, int shift) { - if (abs(shift) >= memf->widg->count()) { - // This shifts more than we have so just reload whole content - memf->widg->clearRows(); - addr_0 += 4*shift; - for (int i = 0; i <= count; i++) - memf->widg->addRow(row_widget(addr_0 + 4*i, memf->widg)); - return; - } - - int diff = count - memf->widg->count(); - int d_b = shift; - int d_e = diff - shift; - - if (d_b > 0) - for (int i = 0; i < d_b; i++) { - addr_0 -= 4; - memf->widg->insertRow(row_widget(addr_0, memf->widg), 0); - } - else - for (int i = 0; i > d_b; i--) { - addr_0 += 4; - memf->widg->removeRow(0); - } - if (d_e > 0) - for (int i = 0; i < d_e; i++) - memf->widg->addRow(row_widget(addr_0 + 4*memf->widg->count(), memf->widg)); - else - for (int i = 0; i > d_e; i--) - memf->widg->removeRow(memf->widg->count() - 1); - addr0_save_change(addr_0); -} - -void MemoryView::addr0_save_change(std::uint32_t val) { /* ignore */ } - -void MemoryView::go_edit_finish() { - QString hex = go_edit->text(); - hex.remove(0, 2); - - bool ok; - std::uint32_t nw = hex.toUInt(&ok, 16); - if (ok) { - set_focus(nw); - } else - edit_load_focus(); -} - -MemoryView::Frame::Frame(MemoryView *parent) : QAbstractScrollArea(parent) { - mv = parent; - content_y = -3*MIN_OFF/2; // When this is initialized the width is 0 so this uses just min to inialize it - - widg = new StaticTable(this); - setViewport(widg); - - setFrameShape(QFrame::NoFrame); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setContentsMargins(0, 0, 0, 0); -} - -void MemoryView::Frame::focus(unsigned i) { - content_y = (FOCUS*height()) - widg->row_size()*i/widg->columns() - widg->row_size()/2; - viewport()->move(0, content_y); - viewport()->repaint(0, content_y, width(), height()); - check_update(); -} - -// Calculate which row is in focus at the moment -unsigned MemoryView::Frame::focussed() { - int h = (FOCUS*height() - content_y) / widg->row_size() * widg->columns(); - return qMax(h, 0); -} - -// This verifies that we are not scrolled too far away down or up and that we have enought height -// We make 10% of height as buffer zone with fixed minimum in pixels -void MemoryView::Frame::check_update() { - int hpart = qMax(height()/10, MIN_OFF); - int req_height = height() + 2*hpart; - - if ((content_y < -hpart) && (content_y > -2*hpart) && (widg->height() >= req_height)) - return; - - int row_h = widg->row_size(); - // Calculate how many we need and how much we need to move and update content accordingly - int count = (req_height / row_h) + 1; - int shift = (content_y + hpart + hpart/2)/row_h; - mv->update_content(count * widg->columns(), shift * widg->columns()); - // Move and resize widget - content_y -= shift * row_h; - widg->setGeometry(0, content_y, width(), count * row_h); - - mv->edit_load_focus(); -} - -void MemoryView::Frame::resizeEvent(QResizeEvent *e) { - QAbstractScrollArea::resizeEvent(e); - widg->setGeometry(0, content_y, e->size().width(), widg->heightForWidth(e->size().width())); - check_update(); -} - -void MemoryView::Frame::wheelEvent(QWheelEvent *e) { - QPoint pix = e->pixelDelta(); - QPoint ang = e->angleDelta(); - - if (!pix.isNull()) - content_y += pix.ry(); - else if (!ang.isNull()) - content_y += ang.ry() * ANGLE_SCROLL; - - // TODO smooth scroll - viewport()->move(0, content_y); - viewport()->repaint(0, content_y, width(), height()); - - check_update(); -} - -bool MemoryView::Frame::viewportEvent(QEvent *e) { - bool p = QAbstractScrollArea::viewportEvent(e); - // Pass paint event to viewport widget - if (e->type() == QEvent::Paint) - p = false; - return p; -} diff --git a/qtmips_gui/memoryview.h b/qtmips_gui/memoryview.h deleted file mode 100644 index f60ac53..0000000 --- a/qtmips_gui/memoryview.h +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/******************************************************************************* - * QtMips - MIPS 32-bit Architecture Subset Simulator - * - * Implemented to support following courses: - * - * B35APO - Computer Architectures - * https://cw.fel.cvut.cz/wiki/courses/b35apo - * - * B4M35PAP - Advanced Computer Architectures - * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start - * - * Copyright (c) 2017-2019 Karel Koci - * Copyright (c) 2019 Pavel Pisa - * - * Faculty of Electrical Engineering (http://www.fel.cvut.cz) - * Czech Technical University (http://www.cvut.cz/) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - ******************************************************************************/ - -#ifndef MEMORYVIEW_H -#define MEMORYVIEW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qtmipsmachine.h" -#include "statictable.h" - -class MemoryView : public QWidget { - Q_OBJECT -public: - MemoryView(QWidget *parent = nullptr, std::uint32_t addr0 = 0); - - virtual void setup(machine::QtMipsMachine*); - - void set_focus(std::uint32_t address); - std::uint32_t focus() const; - std::uint32_t addr0() const; - - void edit_load_focus(); // Set current focus to edit field - -public slots: - void check_for_updates(); - -protected: - const machine::Memory *memory; - const machine::Cache *cache_data; - - virtual QList row_widget(std::uint32_t address, QWidget *parent) = 0; - - QVBoxLayout *layout; - - void reload_content(); // reload displayed data - void update_content(int count, int shift); // update content to match given count and shift - - virtual void addr0_save_change(std::uint32_t val); - -private slots: - void go_edit_finish(); - -private: - std::uint32_t change_counter; - std::uint32_t cache_data_change_counter; - unsigned count; - std::uint32_t addr_0; // First address in view - - class Frame : public QAbstractScrollArea { - public: - Frame(MemoryView *parent); - - StaticTable *widg; - void focus(unsigned i); // Focus on given item in widget - unsigned focussed(); // What item is in focus - void check_update(); // Update widget size and content if needed - - protected: - MemoryView *mv; - int content_y; - - bool viewportEvent(QEvent*); - void resizeEvent(QResizeEvent*); - void wheelEvent(QWheelEvent *event); - }; - Frame *memf; - - QWidget *ctl_widg; - QLineEdit *go_edit; -}; - -#endif // MEMORYVIEW_H diff --git a/qtmips_gui/programdock.cpp b/qtmips_gui/programdock.cpp index 4c379eb..39a3f77 100644 --- a/qtmips_gui/programdock.cpp +++ b/qtmips_gui/programdock.cpp @@ -33,114 +33,56 @@ * ******************************************************************************/ +#include +#include +#include +#include +#include +#include #include "programdock.h" -#include "qtmipsexception.h" +#include "programmodel.h" +#include "programtableview.h" +#include "hexlineedit.h" -ProgramView::ProgramView(QWidget *parent, QSettings *settings) : MemoryView(parent, settings->value("ProgramViewAddr0", 0x8001FF80).toULongLong()) { - this->settings = settings; - /* - cb_single = new QComboBox(this); - cb_single->addItems({ - "Don't follow", - "Follow executing instruction" - }); - cb_single->setCurrentIndex(1); - cb_single->hide(); - layout->addWidget(cb_single); - connect(cb_single, SIGNAL(currentIndexChanged(int)), this, SLOT(cb_single_changed(int))); - cb_pipelined = new QComboBox(this); - cb_pipelined->addItems({ - "Don't follow", - "Follow Instruction fetch stage", - "Follow Instruction decode stage", - "Follow Execution stage", - "Follow Memory access stage", - "Follow Registers write back stage", - }); - cb_pipelined->hide(); - cb_pipelined->setCurrentIndex(1); - layout->addWidget(cb_pipelined); - connect(cb_pipelined, SIGNAL(currentIndexChanged(int)), this, SLOT(cb_pipelined_changed(int))); - */ -} - -void ProgramView::setup(machine::QtMipsMachine *machine) { - MemoryView::setup(machine); - if (machine == nullptr) - return; - /* - bool pipelined = machine->config().pipelined(); - cb_single->setVisible(!pipelined); - cb_pipelined->setVisible(pipelined); - // Sync selection somewhat - if (pipelined) { - if (cb_single->currentIndex() == 0) - cb_pipelined->setCurrentIndex(0); - else if (cb_pipelined->currentIndex() == 0) - cb_pipelined->setCurrentIndex(1); - } else - cb_single->setCurrentIndex(cb_pipelined->currentIndex() == 0 ? 0 : 1); - // TODO connect to instructuion hooks - */ -} - -void ProgramView::jump_to_pc(std::uint32_t addr) { - set_focus(addr); -} - -QList ProgramView::row_widget(std::uint32_t address, QWidget *parent) { - QList widgs; - QLabel *l; - - QFont f; - f.setStyleHint(QFont::Monospace); - - l = new QLabel(" ", parent); - l->setFont(f); - widgs.append(l); +ProgramDock::ProgramDock(QWidget *parent, QSettings *settings) : Super(parent) { + setObjectName("Program"); + setWindowTitle("Program"); - l = new QLabel(QString("0x") + QString("%1").arg(address, 8, 16, QChar('0')).toUpper(), parent); - l->setTextInteractionFlags(Qt::TextSelectableByMouse); - l->setFont(f); - widgs.append(l); + QWidget *content = new QWidget(); - l = new QLabel(parent); - l->setTextInteractionFlags(Qt::TextSelectableByMouse); - l->setFont(f); - l->setMinimumWidth(60); - if (memory != nullptr) - l->setText(machine::Instruction(memory->read_word(address)).to_str(address)); - widgs.append(l); + QComboBox *follow_inst = new QComboBox(); + follow_inst->addItem("Follow - none"); + follow_inst->addItem("Follow - fetch"); - return widgs; -} + QTableView *program_content = new ProgramTableView(0, settings); + // program_content->setSizePolicy(); + ProgramModel *program_model = new ProgramModel(0); + program_content->setModel(program_model); + program_content->verticalHeader()->hide(); + //program_content->setHorizontalHeader(program_model->); -void ProgramView::addr0_save_change(std::uint32_t val) { - settings->setValue("ProgramViewAddr0", val); -} + QLineEdit *go_edit = new HexLineEdit(0, 8, 16, "0x"); -void ProgramView::cb_single_changed(int index) { - // TODO set memory view -} + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(follow_inst); + layout->addWidget(program_content); + layout->addWidget(go_edit); -void ProgramView::cb_pipelined_changed(int index) { - // TODO set memory view -} + content->setLayout(layout); -ProgramDock::ProgramDock(QWidget *parent, QSettings *settings) : QDockWidget(parent) { - view = new ProgramView(this, settings); - setWidget(view); + setWidget(content); - setObjectName("Program"); - setWindowTitle("Program"); + connect(this, &ProgramDock::machine_setup, program_model, &ProgramModel::setup); + connect(go_edit, SIGNAL(value_edit_finished(std::uint32_t)), + program_content, SLOT(go_to_address(std::uint32_t))); + connect(program_content, SIGNAL(address_changed(std::uint32_t)), + go_edit, SLOT(set_value(std::uint32_t))); + connect(this, SIGNAL(jump_to_pc(std::uint32_t)), + program_content, SLOT(go_to_address(std::uint32_t))); } void ProgramDock::setup(machine::QtMipsMachine *machine) { - view->setup(machine); -} - -void ProgramDock::jump_to_pc(std::uint32_t addr) { - view->jump_to_pc(addr); + emit machine_setup(machine); } diff --git a/qtmips_gui/programdock.h b/qtmips_gui/programdock.h index 8a9e4b1..10b9df1 100644 --- a/qtmips_gui/programdock.h +++ b/qtmips_gui/programdock.h @@ -37,49 +37,26 @@ #define PROGRAMDOCK_H #include -#include -#include #include -#include +#include #include "qtmipsmachine.h" -#include "memoryview.h" -class ProgramView : public MemoryView { +class ProgramDock : public QDockWidget { Q_OBJECT -public: - ProgramView(QWidget *parent, QSettings *settings); - - void setup(machine::QtMipsMachine*); - - void jump_to_pc(std::uint32_t); - -protected: - QList row_widget(std::uint32_t address, QWidget *parent); - void addr0_save_change(std::uint32_t val); + using Super = QDockWidget; -private slots: - void cb_single_changed(int index); - void cb_pipelined_changed(int index); - -private: - QComboBox *cb_single; - QComboBox *cb_pipelined; - QSettings *settings; -}; - -class ProgramDock : public QDockWidget { - Q_OBJECT public: ProgramDock(QWidget *parent, QSettings *settings); void setup(machine::QtMipsMachine *machine); -public slots: +signals: + void machine_setup(machine::QtMipsMachine *machine); void jump_to_pc(std::uint32_t); private: - ProgramView *view; + }; #endif // PROGRAMDOCK_H diff --git a/qtmips_gui/programmodel.cpp b/qtmips_gui/programmodel.cpp new file mode 100644 index 0000000..5387058 --- /dev/null +++ b/qtmips_gui/programmodel.cpp @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/******************************************************************************* + * QtMips - MIPS 32-bit Architecture Subset Simulator + * + * Implemented to support following courses: + * + * B35APO - Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b35apo + * + * B4M35PAP - Advanced Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start + * + * Copyright (c) 2017-2019 Karel Koci + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#include + +#include "programmodel.h" + +ProgramModel::ProgramModel(QObject *parent) + : Super(parent), data_font("Monospace") { + index0_offset = 0; + data_font.setStyleHint(QFont::TypeWriter); + machine = nullptr; + memory_change_counter = 0; + cache_program_change_counter = 0; +} + +int ProgramModel::rowCount(const QModelIndex & /*parent*/) const { + return 750; +} + +int ProgramModel::columnCount(const QModelIndex & /*parent*/) const { + return 4; +} + +QVariant ProgramModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) { + if(role == Qt::DisplayRole) { + switch (section) { + case 0: + return tr("Bp"); + case 1: + return tr("Address"); + case 2: + return tr("Code"); + case 3: + return tr("Instruction"); + default: + return tr(""); + } + } + } + return Super::headerData(section, orientation, role); +} + +QVariant ProgramModel::data(const QModelIndex &index, int role) const { + if (role == Qt::DisplayRole) + { + QString s, t; + std::uint32_t address; + std::uint32_t data; + if (!get_row_address(address, index.row())) + return QString(""); + + if (index.column() == 1) { + t = QString::number(address, 16); + s.fill('0', 8 - t.count()); + return "0x" + s + t.toUpper(); + } + + if (machine == nullptr) + return QString(" "); + if (machine->memory() == nullptr) + return QString(" "); + + machine::Instruction inst(machine->memory()->read_word(address)); + + switch (index.column()) { + case 0: + return QString(" "); + case 2: + t = QString::number(inst.data(), 16); + s.fill('0', 8 - t.count()); + return s + t.toUpper(); + case 3: return inst.to_str(address); + default: return tr(""); + } + } + if (role == Qt::BackgroundRole) { + std::uint32_t address; + if (!get_row_address(address, index.row()) || + machine == nullptr || index.column() != 2) + return QVariant(); + if (machine->cache_program() != nullptr) { + machine::LocationStatus loc_stat; + loc_stat = machine->cache_program()->location_status(address); + if (loc_stat & machine::LOCSTAT_CACHED) { + QBrush bgd(Qt::lightGray); + return bgd; + } + } + return QVariant(); + } + if (role==Qt::FontRole) + return data_font; + return QVariant(); +} + +void ProgramModel::setup(machine::QtMipsMachine *machine) { + this->machine = machine; + if (machine != nullptr) + connect(machine, SIGNAL(post_tick()), this, SLOT(check_for_updates())); + emit update_all(); +} + +void ProgramModel::update_all() { + if (machine != nullptr && machine->memory() != nullptr) { + memory_change_counter = machine->memory()->get_change_counter(); + if (machine->cache_program() != nullptr) + cache_program_change_counter = machine->cache_program()->get_change_counter(); + } + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); +} + +void ProgramModel::check_for_updates() { + bool need_update = false; + if (machine == nullptr) + return; + if (machine->memory() == nullptr) + return; + + if (memory_change_counter != machine->memory()->get_change_counter()) + need_update = true; + if (machine->cache_data() != nullptr) { + if (cache_program_change_counter != machine->cache_program()->get_change_counter()) + need_update = true; + } + if (!need_update) + return; + update_all(); +} + +bool ProgramModel::adjustRowAndOffset(int &row, int optimal_row, std::uint32_t address) { + if (optimal_row < rowCount() / 8) + optimal_row = rowCount() / 8; + if (optimal_row >= rowCount() - rowCount() / 8) + optimal_row = rowCount() - rowCount() / 8; + row = rowCount() / 2; + address -= address % cellSizeBytes(); + std::uint32_t row_bytes = cellSizeBytes(); + std::uint32_t diff = row * row_bytes; + if (diff > address) { + row = address / row_bytes; + if (row == 0) { + index0_offset = 0; + } else { + index0_offset = address - row * row_bytes; + } + } else { + index0_offset = address - diff; + } + return get_row_for_address(row, address); +} diff --git a/qtmips_gui/programmodel.h b/qtmips_gui/programmodel.h new file mode 100644 index 0000000..4a17e99 --- /dev/null +++ b/qtmips_gui/programmodel.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/******************************************************************************* + * QtMips - MIPS 32-bit Architecture Subset Simulator + * + * Implemented to support following courses: + * + * B35APO - Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b35apo + * + * B4M35PAP - Advanced Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start + * + * Copyright (c) 2017-2019 Karel Koci + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#ifndef PROGRAMMODEL_H +#define PROGRAMMODEL_H + +#include +#include +#include "qtmipsmachine.h" + +class ProgramModel : public QAbstractTableModel +{ + Q_OBJECT + + using Super = QAbstractTableModel; +public: + ProgramModel(QObject *parent); + int rowCount(const QModelIndex &parent = QModelIndex()) const override ; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool adjustRowAndOffset(int &row, int optimal_row, std::uint32_t address); + void update_all(); + + inline const QFont *getFont() const { + return &data_font; + } + + inline std::uint32_t getIndex0Offset() const { + return index0_offset; + } + + inline unsigned int cellSizeBytes() const { + return 4; + } + inline bool get_row_address(std::uint32_t &address, int row) const { + address = index0_offset + row * cellSizeBytes(); + return address >= index0_offset; + } + inline bool get_row_for_address(int &row, std::uint32_t address) const { + if (address < index0_offset) { + row = -1; + return false; + } + row = (address - index0_offset) / cellSizeBytes(); + if ((address - index0_offset > 0x80000000) || row > rowCount()) { + row = rowCount(); + return false; + } + return true; + } + +public slots: + void setup(machine::QtMipsMachine *machine); + void check_for_updates(); + +private: + std::uint32_t index0_offset; + QFont data_font; + machine::QtMipsMachine *machine; + std::uint32_t memory_change_counter; + std::uint32_t cache_program_change_counter; +}; + +#endif // PROGRAMMODEL_H diff --git a/qtmips_gui/programtableview.cpp b/qtmips_gui/programtableview.cpp new file mode 100644 index 0000000..34a119a --- /dev/null +++ b/qtmips_gui/programtableview.cpp @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/******************************************************************************* + * QtMips - MIPS 32-bit Architecture Subset Simulator + * + * Implemented to support following courses: + * + * B35APO - Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b35apo + * + * B4M35PAP - Advanced Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start + * + * Copyright (c) 2017-2019 Karel Koci + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#include +#include +#include +#include "programtableview.h" +#include "programmodel.h" + +ProgramTableView::ProgramTableView(QWidget *parent, QSettings *settings) : Super(parent) { + connect(verticalScrollBar() , SIGNAL(valueChanged(int)), + this, SLOT(adjust_scroll_pos())); + this->settings = settings; + initial_address = settings->value("ProgramViewAddr0", 0).toULongLong(); +} + +void ProgramTableView::addr0_save_change(std::uint32_t val) { + settings->setValue("ProgramViewAddr0", val); +} + +void ProgramTableView::adjustColumnCount() { + int cwidth; + int totwidth; + ProgramModel *m = dynamic_cast(model()); + + if (m == nullptr) + return; + + QFontMetrics fm(*m->getFont()); + cwidth = fm.width("Bp"); + totwidth = cwidth; + horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); + horizontalHeader()->resizeSection(0, cwidth); + cwidth = fm.width("0x00000000"); + totwidth += cwidth; + horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); + horizontalHeader()->resizeSection(1, cwidth); + totwidth += cwidth; + cwidth = fm.width("00000000"); + horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); + horizontalHeader()->resizeSection(2, cwidth); + horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); + totwidth += fm.width("BEQ $18, $17, 0x80020058"); + totwidth += verticalHeader()->width(); + setColumnHidden(2, totwidth > width()); + + if (initial_address != 0) { + go_to_address(initial_address); + initial_address = 0; + } +} + + +void ProgramTableView:: adjust_scroll_pos() { + std::uint32_t address; + ProgramModel *m = dynamic_cast(model()); + if (m == nullptr) + return; + + QModelIndex prev_index = currentIndex(); + std::uint32_t row_bytes = m->cellSizeBytes(); + std::uint32_t index0_offset = m->getIndex0Offset(); + + do { + int row = rowAt(0); + int prev_row = row; + if (row < m->rowCount() / 8) { + if ((row == 0) && (index0_offset < row_bytes) && (index0_offset != 0)) { + m->adjustRowAndOffset(row, 0, 0); + } else if (index0_offset > row_bytes) { + m->get_row_address(address, row); + m->adjustRowAndOffset(row, m->rowCount() / 7, address); + } else { + break; + } + } else if (row > m->rowCount() - m->rowCount() / 8) { + m->get_row_address(address, row); + m->adjustRowAndOffset(row, m->rowCount() - m->rowCount() / 7, address); + } else { + break; + } + scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop); + setCurrentIndex(m->index(prev_index.row() + row - prev_row, + prev_index.column())); + emit m->update_all(); + } while(0); + m->get_row_address(address, rowAt(0)); + addr0_save_change(address); + emit address_changed(address); +} + +void ProgramTableView::resizeEvent(QResizeEvent *event) { + ProgramModel *m = dynamic_cast(model()); + std::uint32_t address; + bool keep_row0 = false; + + if (m != nullptr) { + if (initial_address == 0) { + keep_row0 = m->get_row_address(address, rowAt(0)); + } else { + address = initial_address; + } + } + Super::resizeEvent(event); + adjustColumnCount(); + if (keep_row0) { + initial_address = 0; + go_to_address(address); + } +} + +void ProgramTableView:: go_to_address(std::uint32_t address) { + ProgramModel *m = dynamic_cast(model()); + int row; + if (m == nullptr) + return; + m->adjustRowAndOffset(row, m->rowCount() / 2, address); + scrollTo(m->index(row, 0), + QAbstractItemView::PositionAtTop); + setCurrentIndex(m->index(row, 1)); + addr0_save_change(address); + emit m->update_all(); +} diff --git a/qtmips_gui/programtableview.h b/qtmips_gui/programtableview.h new file mode 100644 index 0000000..b24c47d --- /dev/null +++ b/qtmips_gui/programtableview.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/******************************************************************************* + * QtMips - MIPS 32-bit Architecture Subset Simulator + * + * Implemented to support following courses: + * + * B35APO - Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b35apo + * + * B4M35PAP - Advanced Computer Architectures + * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start + * + * Copyright (c) 2017-2019 Karel Koci + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#ifndef PROGRAMTABLEVIEW_H +#define PROGRAMTABLEVIEW_H + +#include +#include +#include +#include + +class ProgramTableView : public QTableView +{ + Q_OBJECT + + using Super = QTableView; + +public: + ProgramTableView(QWidget *parent, QSettings *settings); + + void resizeEvent(QResizeEvent *event) override; +signals: + void address_changed(std::uint32_t address); +public slots: + void go_to_address(std::uint32_t address); +private slots: + void adjust_scroll_pos(); +private: + void addr0_save_change(std::uint32_t val); + void adjustColumnCount(); + QSettings *settings; + + std::uint32_t initial_address; +}; + +#endif // PROGRAMTABLEVIEW_H diff --git a/qtmips_gui/qtmips_gui.pro b/qtmips_gui/qtmips_gui.pro index 05aa784..e3a3890 100644 --- a/qtmips_gui/qtmips_gui.pro +++ b/qtmips_gui/qtmips_gui.pro @@ -28,7 +28,6 @@ SOURCES += \ registersdock.cpp \ programdock.cpp \ memorydock.cpp \ - memoryview.cpp \ coreview/programcounter.cpp \ coreview/multiplexer.cpp \ coreview/connection.cpp \ @@ -49,7 +48,9 @@ SOURCES += \ coreview/value.cpp \ memorymodel.cpp \ memorytableview.cpp \ - hexlineedit.cpp + hexlineedit.cpp \ + programmodel.cpp \ + programtableview.cpp HEADERS += \ mainwindow.h \ @@ -58,7 +59,6 @@ HEADERS += \ registersdock.h \ programdock.h \ memorydock.h \ - memoryview.h \ coreview/programcounter.h \ coreview/multiplexer.h \ coreview/connection.h \ @@ -79,7 +79,9 @@ HEADERS += \ coreview/value.h \ memorymodel.h \ memorytableview.h \ - hexlineedit.h + hexlineedit.h \ + programmodel.h \ + programtableview.h FORMS += \ NewDialog.ui \ -- cgit v1.2.3