aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md6
-rwxr-xr-xbuild.sh10
-rw-r--r--qtmips_cli/main.cpp48
-rw-r--r--qtmips_cli/qtmips_cli.pro8
-rw-r--r--qtmips_cli/tracer.cpp14
-rw-r--r--qtmips_cli/tracer.h18
-rw-r--r--qtmips_gui/CacheContent.ui22
-rw-r--r--qtmips_gui/CacheStatistics.ui10
-rw-r--r--qtmips_gui/MainWindow.ui99
-rw-r--r--qtmips_gui/NewDialog.ui344
-rw-r--r--qtmips_gui/cachecontent.cpp10
-rw-r--r--qtmips_gui/cachecontent.h17
-rw-r--r--qtmips_gui/cachestatistics.cpp8
-rw-r--r--qtmips_gui/cachestatistics.h15
-rw-r--r--qtmips_gui/coreview.cpp33
-rw-r--r--qtmips_gui/coreview.h43
-rw-r--r--qtmips_gui/icons.qrc16
-rw-r--r--qtmips_gui/icons/application-exit.pngbin2633 -> 2525 bytes
-rw-r--r--qtmips_gui/main.cpp15
-rw-r--r--qtmips_gui/mainwindow.cpp93
-rw-r--r--qtmips_gui/mainwindow.h44
-rw-r--r--qtmips_gui/newdialog.cpp153
-rw-r--r--qtmips_gui/newdialog.h33
-rw-r--r--qtmips_gui/qtmips_gui.pro23
-rw-r--r--qtmips_gui/registersdock.cpp10
-rw-r--r--qtmips_gui/registersdock.h18
-rw-r--r--qtmips_gui/registersdock.ui18
-rw-r--r--qtmips_machine/alu.cpp51
-rw-r--r--qtmips_machine/alu.h38
-rw-r--r--qtmips_machine/core.cpp161
-rw-r--r--qtmips_machine/core.h62
-rw-r--r--qtmips_machine/instruction.cpp148
-rw-r--r--qtmips_machine/instruction.h66
-rw-r--r--qtmips_machine/instructions/arithmetic.cpp174
-rw-r--r--qtmips_machine/instructions/arithmetic.h60
-rw-r--r--qtmips_machine/instructions/jumpbranch.cpp30
-rw-r--r--qtmips_machine/instructions/jumpbranch.h34
-rw-r--r--qtmips_machine/instructions/loadstore.cpp67
-rw-r--r--qtmips_machine/instructions/loadstore.h32
-rw-r--r--qtmips_machine/instructions/nop.cpp7
-rw-r--r--qtmips_machine/instructions/nop.h11
-rw-r--r--qtmips_machine/instructions/shift.cpp51
-rw-r--r--qtmips_machine/instructions/shift.h28
-rw-r--r--qtmips_machine/machineconfig.cpp43
-rw-r--r--qtmips_machine/machineconfig.h35
-rw-r--r--qtmips_machine/programloader.cpp41
-rw-r--r--qtmips_machine/programloader.h10
-rw-r--r--qtmips_machine/programmemory.h23
-rw-r--r--qtmips_machine/qtmips_machine.pro15
-rw-r--r--qtmips_machine/qtmipsexception.cpp12
-rw-r--r--qtmips_machine/qtmipsexception.h14
-rw-r--r--qtmips_machine/qtmipsmachine.cpp69
-rw-r--r--qtmips_machine/qtmipsmachine.h35
-rw-r--r--qtmips_machine/registers.cpp29
-rw-r--r--qtmips_machine/registers.h10
-rw-r--r--qtmips_machine/tests/testalu.cpp91
-rw-r--r--qtmips_machine/tests/testcore.cpp35
-rw-r--r--qtmips_machine/tests/testinstruction.cpp25
-rw-r--r--qtmips_machine/tests/testmemory.cpp58
-rw-r--r--qtmips_machine/tests/testprogramloader.cpp20
-rw-r--r--qtmips_machine/tests/tests.pro6
-rw-r--r--qtmips_machine/tests/tst_machine.h21
-rw-r--r--tests/machine-unit-tests/Makefile5
-rw-r--r--tests/machine-unit-tests/data.S6
-rwxr-xr-xtests/machine-unit-tests/test.sh2
-rw-r--r--tests/t-registers/Makefile5
-rw-r--r--tests/test.sh5
68 files changed, 1869 insertions, 896 deletions
diff --git a/.gitignore b/.gitignore
index e14702f..37f455d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@
# Common test directory
test_dir
+# Build directory
+build
diff --git a/README.md b/README.md
index ea96aa5..717c50e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ MIPS CPU simulator for education purposes.
Dependencies
------------
-* Qt 5 (version 4 is not tested but it might work)
+* Qt 5 (qtmips\_gui can be compiled with Qt4 but not qtmips\_cli)
* elfutils (libelf works too but you might have problems with it)
Compilation
@@ -43,3 +43,7 @@ being so.
* Privileged instructions and all features dependent on it
* Coprocessors (so no floating point unit nor any other type)
* Peripherals
+* Memory access stall (stalling execution because of cache miss would be pretty
+ annoying for users so difference between cache and memory is just in collected
+ statistics)
+* Interrupts and exceptions (if exception occurs then machine execution is halted)
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..aafe93b
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+mkdir -p build
+
+ROOT="$(dirname "$(readlink -f "$0")")"
+
+cd build
+/usr/lib/qt5/bin/qmake "$ROOT"
+
+make sub-qtmips_cli sub-qtmips_gui
diff --git a/qtmips_cli/main.cpp b/qtmips_cli/main.cpp
index 780f782..6240414 100644
--- a/qtmips_cli/main.cpp
+++ b/qtmips_cli/main.cpp
@@ -1,12 +1,50 @@
#include <QCoreApplication>
+#include <QCommandLineParser>
#include <iostream>
-#include "machineapp.h"
+#include "tracer.h"
-#include "instructions/arithmetic.h"
+void create_parser(QCommandLineParser &p) {
+ p.setApplicationDescription("QtMips CLI machine simulator");
+ p.addHelpOption();
+ p.addVersionOption();
-int main(int argc, char *argv[])
-{
- MachineApp app(argc, argv);
+ p.addPositionalArgument("FILE", "Input ELF executable file");
+}
+
+void configure_machine(QCommandLineParser &p, MachineConfig &cc) {
+ QStringList pa = p.positionalArguments();
+ if (pa.size() != 1) {
+ std::cerr << "Single ELF file has to be specified" << std::endl;
+ exit(1);
+ }
+ cc.set_elf(pa[0]);
+
+ // TODO
+}
+
+void configure_tracer(QCommandLineParser &p, Tracer &tr) {
+ // TODO
+ tr.reg_pc();
+}
+
+int main(int argc, char *argv[]) {
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("qtmips_cli");
+ app.setApplicationVersion("0.1");
+
+ QCommandLineParser p;
+ create_parser(p);
+ p.process(app);
+
+ MachineConfig cc;
+ configure_machine(p, cc);
+ QtMipsMachine machine(cc);
+
+ app.connect(&machine, SIGNAL(program_exit()), &app, SLOT(quit()));
+
+ Tracer tr(&machine);
+ configure_tracer(p, tr);
+ machine.play(); // Run machine
return app.exec();
}
diff --git a/qtmips_cli/qtmips_cli.pro b/qtmips_cli/qtmips_cli.pro
index a541451..088a015 100644
--- a/qtmips_cli/qtmips_cli.pro
+++ b/qtmips_cli/qtmips_cli.pro
@@ -12,11 +12,13 @@ LIBS += -L$$OUT_PWD/../qtmips_machine/ -lqtmips_machine
INCLUDEPATH += $$PWD/../qtmips_machine
DEPENDPATH += $$PWD/../qtmips_machine
QMAKE_CXXFLAGS += -std=c++0x
+QMAKE_CXXFLAGS += -ggdb
DEFINES += QT_DEPRECATED_WARNINGS
-SOURCES += main.cpp \
- machineapp.cpp
+SOURCES += \
+ main.cpp \
+ tracer.cpp
HEADERS += \
- machineapp.h
+ tracer.h
diff --git a/qtmips_cli/tracer.cpp b/qtmips_cli/tracer.cpp
index 63d00d1..5cbf339 100644
--- a/qtmips_cli/tracer.cpp
+++ b/qtmips_cli/tracer.cpp
@@ -1,6 +1,16 @@
#include "tracer.h"
+#include <iostream>
-Tracer::Tracer()
-{
+using namespace std;
+Tracer::Tracer(QtMipsMachine *machine) {
+ this->machine = machine;
+}
+
+void Tracer::reg_pc() {
+ connect(machine->registers(), SIGNAL(pc_update(std::uint32_t)), this, SLOT(regs_pc_update(std::uint32_t)));
+}
+
+void Tracer::regs_pc_update(std::uint32_t val) {
+ cout << "PC:" << hex << val << endl;
}
diff --git a/qtmips_cli/tracer.h b/qtmips_cli/tracer.h
index 5c99970..7055cd8 100644
--- a/qtmips_cli/tracer.h
+++ b/qtmips_cli/tracer.h
@@ -2,11 +2,21 @@
#define TRACER_H
#include <QObject>
+#include "qtmipsmachine.h"
-class Tracer
-{
+class Tracer : public QObject {
+ Q_OBJECT
public:
- Tracer();
+ Tracer(QtMipsMachine *machine);
+
+ // Trace registers
+ void reg_pc();
+
+private slots:
+ void regs_pc_update(std::uint32_t val);
+
+private:
+ QtMipsMachine *machine;
};
-#endif // TRACER_H \ No newline at end of file
+#endif // TRACER_H
diff --git a/qtmips_gui/CacheContent.ui b/qtmips_gui/CacheContent.ui
index ac2f263..372dee1 100644
--- a/qtmips_gui/CacheContent.ui
+++ b/qtmips_gui/CacheContent.ui
@@ -1,7 +1,7 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>DockWidget</class>
- <widget class="QDockWidget" name="DockWidget">
+ <class>CacheContent</class>
+ <widget class="QDockWidget" name="CacheContent">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,8 +11,20 @@
</rect>
</property>
<property name="windowTitle">
- <string>DockWidget</string>
+ <string>Cache content</string>
</property>
- <widget class="QWidget" name="dockWidgetContents"/>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>TODO</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
+ <resources/>
+ <connections/>
</ui>
diff --git a/qtmips_gui/CacheStatistics.ui b/qtmips_gui/CacheStatistics.ui
index ac2f263..5bb8b72 100644
--- a/qtmips_gui/CacheStatistics.ui
+++ b/qtmips_gui/CacheStatistics.ui
@@ -1,7 +1,7 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>DockWidget</class>
- <widget class="QDockWidget" name="DockWidget">
+ <class>CacheStatistics</class>
+ <widget class="QDockWidget" name="CacheStatistics">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,8 +11,10 @@
</rect>
</property>
<property name="windowTitle">
- <string>DockWidget</string>
+ <string>Cache statistics</string>
</property>
<widget class="QWidget" name="dockWidgetContents"/>
</widget>
+ <resources/>
+ <connections/>
</ui>
diff --git a/qtmips_gui/MainWindow.ui b/qtmips_gui/MainWindow.ui
index 0f69613..56f5319 100644
--- a/qtmips_gui/MainWindow.ui
+++ b/qtmips_gui/MainWindow.ui
@@ -6,20 +6,47 @@
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>300</height>
+ <width>957</width>
+ <height>651</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
- <widget class="QWidget" name="centralWidget"/>
+ <property name="dockNestingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="unifiedTitleAndToolBarOnMac">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget" native="true"/>
+ </item>
+ </layout>
+ </widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
+ <width>957</width>
<height>20</height>
</rect>
</property>
@@ -27,7 +54,8 @@
<property name="title">
<string>File</string>
</property>
- <addaction name="actionLoad_binary"/>
+ <addaction name="actionNew"/>
+ <addaction name="actionReload"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
@@ -85,16 +113,39 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
+ <addaction name="actionNew"/>
+ <addaction name="actionReload"/>
+ <addaction name="separator"/>
+ <addaction name="actionRun"/>
+ <addaction name="actionPause"/>
+ <addaction name="actionStep"/>
</widget>
- <action name="actionLoad_binary">
+ <action name="actionNew">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/document-import.png</normaloff>:/icons/document-import.png</iconset>
+ </property>
<property name="text">
- <string>Load binary...</string>
+ <string>New simulation...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O, Ctrl+N</string>
</property>
</action>
<action name="actionExit">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/application-exit.png</normaloff>:/icons/application-exit.png</iconset>
+ </property>
<property name="text">
<string>Exit</string>
</property>
+ <property name="toolTip">
+ <string>Exit program</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Q</string>
+ </property>
</action>
<action name="actionRestart">
<property name="text">
@@ -102,19 +153,40 @@
</property>
</action>
<action name="actionRun">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset>
+ </property>
<property name="text">
<string>Run</string>
</property>
+ <property name="shortcut">
+ <string>Ctrl+R</string>
+ </property>
</action>
<action name="actionStep">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>
+ </property>
<property name="text">
<string>Step</string>
</property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
</action>
<action name="actionPause">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/pause.png</normaloff>:/icons/pause.png</iconset>
+ </property>
<property name="text">
<string>Pause</string>
</property>
+ <property name="shortcut">
+ <string>Ctrl+P</string>
+ </property>
</action>
<action name="action1_ips">
<property name="text">
@@ -184,8 +256,19 @@
<string>Assembler</string>
</property>
</action>
+ <action name="actionReload">
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/reload.png</normaloff>:/icons/reload.png</iconset>
+ </property>
+ <property name="text">
+ <string>Reload existing</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
- <resources/>
+ <resources>
+ <include location="icons.qrc"/>
+ </resources>
<connections/>
</ui>
diff --git a/qtmips_gui/NewDialog.ui b/qtmips_gui/NewDialog.ui
index 18d31ab..e39edbf 100644
--- a/qtmips_gui/NewDialog.ui
+++ b/qtmips_gui/NewDialog.ui
@@ -1,71 +1,299 @@
-<ui version="4.0" >
- <author></author>
- <comment></comment>
- <exportmacro></exportmacro>
- <class>Dialog</class>
- <widget class="QDialog" name="Dialog" >
- <property name="geometry" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewDialog</class>
+ <widget class="QDialog" name="NewDialog">
+ <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>300</height>
+ <width>558</width>
+ <height>306</height>
</rect>
</property>
- <property name="windowTitle" >
+ <property name="windowTitle">
<string>Dialog</string>
</property>
- <widget class="QDialogButtonBox" name="buttonBox" >
- <property name="geometry" >
- <rect>
- <x>30</x>
- <y>240</y>
- <width>341</width>
- <height>32</height>
- </rect>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
</property>
- <property name="orientation" >
- <enum>Qt::Horizontal</enum>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <property name="standardButtons" >
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- </widget>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Basic</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Preset</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QRadioButton" name="preset_no_pipeline">
+ <property name="text">
+ <string>No pipeline no cache</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="preset_pipelined">
+ <property name="text">
+ <string>Pipelined and cache</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="preset_custom">
+ <property name="text">
+ <string>Custom</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Elf executable: </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="elf_file"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton_browse">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_core">
+ <attribute name="title">
+ <string>Core</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="pipelined">
+ <property name="text">
+ <string>Pipelined</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="hazard">
+ <property name="text">
+ <string>Hazard unit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="flush_jump">
+ <property name="text">
+ <string>Flush pipeline on jump</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="prediction">
+ <property name="title">
+ <string>Jump prediction</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QRadioButton" name="prediction_static">
+ <property name="text">
+ <string>Static</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="prediction_one_dynamic">
+ <property name="text">
+ <string>One level dynamic</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_memory">
+ <attribute name="title">
+ <string>Memory</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="mem_protec_write">
+ <property name="text">
+ <string>Program memory write protection</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="mem_protec_exec">
+ <property name="text">
+ <string>Data memory executable protection</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="cache">
+ <property name="title">
+ <string>Cache (for both program and data)</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QRadioButton" name="cache_associative">
+ <property name="text">
+ <string>Associative</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton_load">
+ <property name="text">
+ <string>Load machine</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton_cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
</widget>
- <pixmapfunction></pixmapfunction>
<resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>Dialog</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel" >
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel" >
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>Dialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel" >
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel" >
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/qtmips_gui/cachecontent.cpp b/qtmips_gui/cachecontent.cpp
index c651b7b..1dd4693 100644
--- a/qtmips_gui/cachecontent.cpp
+++ b/qtmips_gui/cachecontent.cpp
@@ -1,6 +1,10 @@
-#include "cache_content.h"
+#include "cachecontent.h"
-cache_content::cache_content()
-{
+CacheContentDock::CacheContentDock(QWidget *parent) : QDockWidget(parent) {
+ ui = new Ui::CacheContent();
+ ui->setupUi(this);
+}
+CacheContentDock::~CacheContentDock() {
+ delete ui;
}
diff --git a/qtmips_gui/cachecontent.h b/qtmips_gui/cachecontent.h
index 09b8734..3b028b5 100644
--- a/qtmips_gui/cachecontent.h
+++ b/qtmips_gui/cachecontent.h
@@ -1,11 +1,16 @@
-#ifndef CACHE_CONTENT_H
-#define CACHE_CONTENT_H
+#ifndef CACHECONTENT_H
+#define CACHECONTENT_H
+#include <QDockWidget>
+#include "ui_CacheContent.h"
-class cache_content
-{
+class CacheContentDock : public QDockWidget {
public:
- cache_content();
+ CacheContentDock(QWidget *parent);
+ ~CacheContentDock();
+
+private:
+ Ui::CacheContent *ui;
};
-#endif // CACHE_CONTENT_H \ No newline at end of file
+#endif // CACHECONTENT_H
diff --git a/qtmips_gui/cachestatistics.cpp b/qtmips_gui/cachestatistics.cpp
index ade92c6..dd78a8c 100644
--- a/qtmips_gui/cachestatistics.cpp
+++ b/qtmips_gui/cachestatistics.cpp
@@ -1,6 +1,10 @@
#include "cachestatistics.h"
-CacheStatistics::CacheStatistics()
-{
+CacheStatisticsDock::CacheStatisticsDock(QWidget *parent) : QDockWidget(parent) {
+ ui = new Ui::CacheStatistics();
+ ui->setupUi(this);
+}
+CacheStatisticsDock::~CacheStatisticsDock() {
+ delete ui;
}
diff --git a/qtmips_gui/cachestatistics.h b/qtmips_gui/cachestatistics.h
index 4fa77a6..a92d125 100644
--- a/qtmips_gui/cachestatistics.h
+++ b/qtmips_gui/cachestatistics.h
@@ -1,11 +1,18 @@
#ifndef CACHESTATISTICS_H
#define CACHESTATISTICS_H
+#include <QDockWidget>
+#include "ui_CacheStatistics.h"
-class CacheStatistics
-{
+class CacheStatisticsDock : public QDockWidget {
+ Q_OBJECT
public:
- CacheStatistics();
+ CacheStatisticsDock(QWidget *parent);
+ ~CacheStatisticsDock();
+
+private:
+ Ui::CacheStatistics *ui;
+
};
-#endif // CACHESTATISTICS_H \ No newline at end of file
+#endif // CACHESTATISTICS_H
diff --git a/qtmips_gui/coreview.cpp b/qtmips_gui/coreview.cpp
index de8a637..4d23bd8 100644
--- a/qtmips_gui/coreview.cpp
+++ b/qtmips_gui/coreview.cpp
@@ -1,6 +1,35 @@
#include "coreview.h"
-CoreView::CoreView()
-{
+CoreView::CoreView(QWidget *parent) : QGraphicsView(parent) {
}
+
+/*
+CoreViewBlock::CoreViewBlock() {
+}
+
+CoreViewLine::CoreViewLine() {
+
+}
+
+CoreViewLine::CoreViewLine(struct point start, struct point end, QList<struct point> axis) {
+
+}
+
+CoreViewLine::~CoreViewLine() {
+
+}
+
+void CoreViewLine::set_start(struct point p) {
+
+}
+
+void CoreViewLine::set_end(struct point p) {
+
+}
+
+void CoreViewLine::set_axis(QList<struct point> axs) {
+
+}
+
+*/
diff --git a/qtmips_gui/coreview.h b/qtmips_gui/coreview.h
index 6e7407f..a2772fb 100644
--- a/qtmips_gui/coreview.h
+++ b/qtmips_gui/coreview.h
@@ -1,11 +1,46 @@
#ifndef COREVIEW_H
#define COREVIEW_H
+#include <QGraphicsView>
+#include <QGraphicsItem>
+#include <QList>
+#include "machineconfig.h"
-class CoreView
-{
+class CoreView : public QGraphicsView {
+ Q_OBJECT
public:
- CoreView();
+ CoreView(QWidget *parent);
+
+private:
+
+};
+
+/*
+class CoreViewBlock : public QGraphicsItem {
+ Q_OBJECT
+public:
+ CoreViewBlock();
+};
+
+class CoreViewLine : public QGraphicsItem {
+ Q_OBJECT
+public:
+ struct point {
+ int x1, y1, x2, y2;
+ };
+
+ CoreViewLine();
+ CoreViewLine(struct point start, struct point end, QList<struct point> axis);
+ ~CoreViewLine();
+
+ void set_start(struct point);
+ void set_end(struct point);
+ void set_axis(QList<struct point>);
+
+protected:
+ struct point start, end;
+ QList<struct point> axis;
};
+*/
-#endif // COREVIEW_H \ No newline at end of file
+#endif // COREVIEW_H
diff --git a/qtmips_gui/icons.qrc b/qtmips_gui/icons.qrc
index 90f4a83..08542ee 100644
--- a/qtmips_gui/icons.qrc
+++ b/qtmips_gui/icons.qrc
@@ -1,2 +1,14 @@
-<!DOCTYPE RCC>
-<RCC version="1.0"/>
+<RCC>
+ <qresource prefix="/">
+ <file>icons/application-exit.png</file>
+ <file>icons/reload.png</file>
+ <file>icons/document-import.png</file>
+ <file>icons/finish.png</file>
+ <file>icons/forward.png</file>
+ <file>icons/next.png</file>
+ <file>icons/pause.png</file>
+ <file>icons/refresh.png</file>
+ <file>icons/play.png</file>
+ <file>icons/stop.png</file>
+ </qresource>
+</RCC>
diff --git a/qtmips_gui/icons/application-exit.png b/qtmips_gui/icons/application-exit.png
index 2d0cd61..79ee79c 100644
--- a/qtmips_gui/icons/application-exit.png
+++ b/qtmips_gui/icons/application-exit.png
Binary files differ
diff --git a/qtmips_gui/main.cpp b/qtmips_gui/main.cpp
index b48f94e..1c03f3f 100644
--- a/qtmips_gui/main.cpp
+++ b/qtmips_gui/main.cpp
@@ -1,11 +1,14 @@
-#include "mainwindow.h"
#include <QApplication>
+#include <QCommandLineParser>
+#include "mainwindow.h"
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ app.setApplicationName("qtmips_gui");
+ app.setApplicationVersion("0.1");
-int main(int argc, char *argv[])
-{
- QApplication a(argc, argv);
MainWindow w;
- w.show();
+ w.start();
- return a.exec();
+ return app.exec();
}
diff --git a/qtmips_gui/mainwindow.cpp b/qtmips_gui/mainwindow.cpp
index 49d64fc..ea48736 100644
--- a/qtmips_gui/mainwindow.cpp
+++ b/qtmips_gui/mainwindow.cpp
@@ -1,14 +1,93 @@
#include "mainwindow.h"
-#include "ui_mainwindow.h"
-MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
-{
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
+ settings = new QSettings("CTU", "QtMips");
+ coreview = nullptr;
+
+ ui = new Ui::MainWindow();
ui->setupUi(this);
+ setWindowTitle("QtMips");
+
+ // Create/prepare other widgets
+ ndialog = new NewDialog(this, settings);
+ cache_content = new CacheContentDock(this);
+ cache_content->hide();
+ cache_statictics = new CacheStatisticsDock(this);
+ cache_statictics->hide();
+ registers = new RegistersDock(this);
+ registers->hide();
+
+ // Connect signals from menu
+ QObject::connect(ui->actionExit, SIGNAL(triggered(bool)), this, SLOT(close()));
+ QObject::connect(ui->actionNew, SIGNAL(triggered(bool)), this, SLOT(new_machine()));
+ QObject::connect(ui->actionCache, SIGNAL(triggered(bool)), this, SLOT(show_cache_content()));
+ QObject::connect(ui->actionCache_statistics, SIGNAL(triggered(bool)), this, SLOT(show_cache_statictics()));
+ QObject::connect(ui->actionRegisters, SIGNAL(triggered(bool)), this, SLOT(show_registers()));
+
+ // Restore application state from settings
+ restoreState(settings->value("windowState").toByteArray());
+ restoreGeometry(settings->value("windowGeometry").toByteArray());
}
-MainWindow::~MainWindow()
-{
+MainWindow::~MainWindow() {
+ settings->sync();
+ delete settings;
+ if (coreview != nullptr)
+ delete coreview;
+ delete ndialog;
+ delete cache_content;
+ delete cache_statictics;
+ delete registers;
delete ui;
}
+
+void MainWindow::start() {
+ this->show();
+ ndialog->show();
+}
+
+void MainWindow::create_core(MachineConfig *config) {
+ // Create machine
+ machine = new QtMipsMachine(config);
+ // Create machine view
+ coreview = new CoreView(this);
+ this->setCentralWidget(coreview);
+ // TODO connect signals
+}
+
+void MainWindow::new_machine() {
+ ndialog->show();
+}
+
+void MainWindow::show_cache_content() {
+ show_dockwidget(cache_content);
+}
+
+void MainWindow::show_cache_statictics() {
+ show_dockwidget(cache_statictics);
+}
+
+void MainWindow::show_registers() {
+ show_dockwidget(registers);
+}
+
+bool MainWindow::configured() {
+ return (machine != nullptr);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event) {
+ settings->setValue("windowGeometry", saveGeometry());
+ settings->setValue("windowState", saveState());
+ settings->sync();
+ QMainWindow::closeEvent(event);
+}
+
+void MainWindow::show_dockwidget(QDockWidget *dw) {
+ if (dw->isHidden()) {
+ dw->show();
+ addDockWidget(Qt::RightDockWidgetArea, dw);
+ } else {
+ dw->raise();
+ dw->setFocus();
+ }
+}
diff --git a/qtmips_gui/mainwindow.h b/qtmips_gui/mainwindow.h
index a3948a9..bba1cb7 100644
--- a/qtmips_gui/mainwindow.h
+++ b/qtmips_gui/mainwindow.h
@@ -2,21 +2,55 @@
#define MAINWINDOW_H
#include <QMainWindow>
+#include <QSettings>
+#include "ui_MainWindow.h"
+#include "newdialog.h"
+#include "coreview.h"
+#include "cachecontent.h"
+#include "cachestatistics.h"
+#include "registersdock.h"
-namespace Ui {
-class MainWindow;
-}
+#include "qtmipsmachine.h"
+#include "machineconfig.h"
-class MainWindow : public QMainWindow
-{
+class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
+ void start();
+ void create_core(MachineConfig *config);
+
+public slots:
+ void new_machine();
+
+ void show_cache_content();
+ void show_cache_statictics();
+ void show_registers();
+
+ bool configured();
+
+protected:
+ void closeEvent(QCloseEvent *event);
+
private:
Ui::MainWindow *ui;
+
+ NewDialog *ndialog;
+
+ CoreView *coreview;
+
+ CacheContentDock *cache_content;
+ CacheStatisticsDock *cache_statictics;
+ RegistersDock *registers;
+
+ QSettings *settings;
+
+ QtMipsMachine *machine; // Current simulated machine
+
+ void show_dockwidget(QDockWidget *w);
};
#endif // MAINWINDOW_H
diff --git a/qtmips_gui/newdialog.cpp b/qtmips_gui/newdialog.cpp
index b2d24a8..31f4bb8 100644
--- a/qtmips_gui/newdialog.cpp
+++ b/qtmips_gui/newdialog.cpp
@@ -1,6 +1,155 @@
#include "newdialog.h"
+#include "mainwindow.h"
-newdialog::newdialog()
-{
+NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) {
+ ui = new Ui::NewDialog();
+ ui->setupUi(this);
+ setWindowTitle("New machine");
+ this->settings = settings;
+
+ QObject::connect(ui->pushButton_load, SIGNAL(clicked(bool)), this, SLOT(create()));
+ QObject::connect(ui->pushButton_cancel, SIGNAL(clicked(bool)), this, SLOT(cancel()));
+ // Signals on Base tab
+ QObject::connect(ui->preset_no_pipeline, SIGNAL(toggled(bool)), this, SLOT(preset(bool)));
+ QObject::connect(ui->preset_pipelined, SIGNAL(toggled(bool)), this, SLOT(preset(bool)));
+ QObject::connect(ui->pushButton_browse, SIGNAL(clicked(bool)), this, SLOT(browse_elf()));
+#define CUSTOM_PRESET(UI) QObject::connect(UI, SIGNAL(clicked(bool)), this, SLOT(set_custom_preset()))
+ // Signals on Core tab
+ CUSTOM_PRESET(ui->pipelined);
+ CUSTOM_PRESET(ui->hazard);
+ CUSTOM_PRESET(ui->flush_jump);
+ CUSTOM_PRESET(ui->prediction);
+ CUSTOM_PRESET(ui->prediction_static);
+ CUSTOM_PRESET(ui->prediction_one_dynamic);
+ // Signals on Memory tab
+ CUSTOM_PRESET(ui->mem_protec_write);
+ CUSTOM_PRESET(ui->mem_protec_exec);
+ CUSTOM_PRESET(ui->cache);
+ CUSTOM_PRESET(ui->cache_associative);
+#undef CUSTOM_PRESET
+
+ // Load setting after signals are configured so that we can have correct settings
+ load_settings();
+}
+
+NewDialog::~NewDialog() {
+ delete ui;
+ // Settings is freed by parent
+ delete elf_dialog;
+}
+
+void NewDialog::cancel() {
+ this->close();
+}
+
+void NewDialog::create() {
+ MainWindow *prnt = (MainWindow*)parent();
+
+ MachineConfig *mc = new MachineConfig();
+ mc->set_elf(ui->elf_file->text());
+ mc->set_pipelined(ui->pipelined->isChecked());
+ // TODO other settings
+
+ try {
+ prnt->create_core(mc);
+ } catch (const QtMipsExceptionInput &e) {
+ QMessageBox msg(this);
+ msg.setText(e.msg(false));
+ msg.setIcon(QMessageBox::Critical);
+ msg.setToolTip("Please check that ELF executable realy exists and is in correct format.");
+ msg.setDetailedText(e.msg(true));
+ msg.setWindowTitle("Error while initializing new machine");
+ msg.exec();
+ goto cleanup;
+ }
+
+ store_settings(); // Save to settings
+ this->close();
+
+cleanup:
+ delete mc;
+}
+
+void NewDialog::browse_elf() {
+ QFileDialog elf_dialog(this);
+ elf_dialog.setFileMode(QFileDialog::ExistingFile);
+ if (elf_dialog.exec())
+ ui->elf_file->setText(elf_dialog.selectedFiles()[0]);
+}
+
+void NewDialog::preset(bool value) {
+ if (value) {
+ bool pip = ui->preset_pipelined->isChecked();
+ // Core settings
+ ui->pipelined->setChecked(pip);
+ ui->hazard->setChecked(pip);
+ ui->flush_jump->setChecked(pip);
+ ui->prediction->setChecked(pip);
+ ui->prediction_one_dynamic->setChecked(pip);
+ // Memory settings
+ ui->mem_protec_write->setChecked(true);
+ ui->mem_protec_exec->setChecked(true);
+ ui->cache->setChecked(pip);
+ ui->cache_associative->setChecked(true);
+ } // Else custom so do no changes
+}
+
+void NewDialog::set_custom_preset() {
+ ui->preset_custom->setChecked(true);
+}
+
+void NewDialog::closeEvent(QCloseEvent *) {
+ load_settings(); // Reset from settings
+ // Close main window if not already configured
+ MainWindow *prnt = (MainWindow*)parent();
+ if (!prnt->configured())
+ prnt->close();
+}
+
+#define LOAD_BUTTON(NAME, DEF) ui->NAME->setChecked(settings->value(#NAME, DEF).toBool())
+#define LOAD_LINE(NAME, DEF) ui->NAME->setText(settings->value(#NAME, DEF).toString())
+
+#define STORE_BUTTON(NAME) settings->setValue(#NAME, ui->NAME->isChecked())
+#define STORE_LINE(NAME) settings->setValue(#NAME, ui->NAME->text())
+
+void NewDialog::load_settings() {
+ // Core tab
+ LOAD_BUTTON(pipelined, false);
+ LOAD_BUTTON(hazard, false);
+ LOAD_BUTTON(flush_jump, false);
+ LOAD_BUTTON(prediction, false);
+ LOAD_BUTTON(prediction_static, true);
+ LOAD_BUTTON(prediction_one_dynamic, false);
+ // Memory tab
+ LOAD_BUTTON(mem_protec_write, true);
+ LOAD_BUTTON(mem_protec_exec, true);
+ LOAD_BUTTON(cache, false);
+ LOAD_BUTTON(cache_associative, true);
+ // Base tab
+ // We are doing this last so presets can reset previous configuration to somethin valid
+ LOAD_BUTTON(preset_no_pipeline, true);
+ LOAD_BUTTON(preset_pipelined, false);
+ LOAD_BUTTON(preset_custom, false);
+ LOAD_LINE(elf_file, "");
+}
+
+void NewDialog::store_settings() {
+ // Core tab
+ STORE_BUTTON(pipelined);
+ STORE_BUTTON(hazard);
+ STORE_BUTTON(flush_jump);
+ STORE_BUTTON(prediction);
+ STORE_BUTTON(prediction_static);
+ STORE_BUTTON(prediction_one_dynamic);
+ // Memory tab
+ STORE_BUTTON(mem_protec_write);
+ STORE_BUTTON(mem_protec_exec);
+ STORE_BUTTON(cache);
+ STORE_BUTTON(cache_associative);
+ // Base tab
+ STORE_BUTTON(preset_no_pipeline);
+ STORE_BUTTON(preset_pipelined);
+ STORE_BUTTON(preset_custom);
+ STORE_LINE(elf_file);
}
diff --git a/qtmips_gui/newdialog.h b/qtmips_gui/newdialog.h
index d37c9c0..d9e850e 100644
--- a/qtmips_gui/newdialog.h
+++ b/qtmips_gui/newdialog.h
@@ -1,11 +1,36 @@
#ifndef NEWDIALOG_H
#define NEWDIALOG_H
+#include <QDialog>
+#include <QSettings>
+#include <QFileDialog>
+#include <QMessageBox>
+#include "ui_NewDialog.h"
+#include "qtmipsexception.h"
-class newdialog
-{
+class NewDialog : public QDialog {
+ Q_OBJECT
public:
- newdialog();
+ NewDialog(QWidget *parent, QSettings *settings);
+ ~NewDialog();
+
+public slots:
+ void cancel();
+ void create();
+ void browse_elf();
+ void preset(bool);
+ void set_custom_preset();
+
+protected:
+ void closeEvent(QCloseEvent *);
+
+private:
+ Ui::NewDialog *ui;
+ QSettings *settings;
+ QFileDialog *elf_dialog;
+
+ void load_settings();
+ void store_settings();
};
-#endif // NEWDIALOG_H \ No newline at end of file
+#endif // NEWDIALOG_H
diff --git a/qtmips_gui/qtmips_gui.pro b/qtmips_gui/qtmips_gui.pro
index 7dfb53c..ee2bd6e 100644
--- a/qtmips_gui/qtmips_gui.pro
+++ b/qtmips_gui/qtmips_gui.pro
@@ -14,10 +14,27 @@ DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
- mainwindow.cpp
+ mainwindow.cpp \
+ newdialog.cpp \
+ coreview.cpp \
+ registersdock.cpp \
+ cachestatistics.cpp \
+ cachecontent.cpp
HEADERS += \
- mainwindow.h
+ mainwindow.h \
+ newdialog.h \
+ coreview.h \
+ registersdock.h \
+ cachestatistics.h \
+ cachecontent.h
FORMS += \
- mainwindow.ui
+ NewDialog.ui \
+ MainWindow.ui \
+ CacheContent.ui \
+ registersdock.ui \
+ CacheStatistics.ui
+
+RESOURCES += \
+ icons.qrc
diff --git a/qtmips_gui/registersdock.cpp b/qtmips_gui/registersdock.cpp
index bf82725..e8a5907 100644
--- a/qtmips_gui/registersdock.cpp
+++ b/qtmips_gui/registersdock.cpp
@@ -1,6 +1,10 @@
-#include "registers.h"
+#include "registersdock.h"
-Registers::Registers()
-{
+RegistersDock::RegistersDock(QWidget *parent) : QDockWidget(parent) {
+ ui = new Ui::RegistersDock();
+ ui->setupUi(this);
+}
+RegistersDock::~RegistersDock() {
+ delete ui;
}
diff --git a/qtmips_gui/registersdock.h b/qtmips_gui/registersdock.h
index 0fb60eb..338edc3 100644
--- a/qtmips_gui/registersdock.h
+++ b/qtmips_gui/registersdock.h
@@ -1,11 +1,17 @@
-#ifndef REGISTERS_H
-#define REGISTERS_H
+#ifndef REGISTERSDOCK_H
+#define REGISTERSDOCK_H
+#include <QDockWidget>
+#include "ui_registersdock.h"
-class Registers
-{
+class RegistersDock : public QDockWidget {
public:
- Registers();
+ RegistersDock(QWidget *parent);
+ ~RegistersDock();
+
+private:
+ Ui::RegistersDock *ui;
+
};
-#endif // REGISTERS_H \ No newline at end of file
+#endif // REGISTERSDOCK_H
diff --git a/qtmips_gui/registersdock.ui b/qtmips_gui/registersdock.ui
index ac2f263..9b51505 100644
--- a/qtmips_gui/registersdock.ui
+++ b/qtmips_gui/registersdock.ui
@@ -1,7 +1,7 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>DockWidget</class>
- <widget class="QDockWidget" name="DockWidget">
+ <class>RegistersDock</class>
+ <widget class="QDockWidget" name="RegistersDock">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,8 +11,16 @@
</rect>
</property>
<property name="windowTitle">
- <string>DockWidget</string>
+ <string>Registers</string>
</property>
- <widget class="QWidget" name="dockWidgetContents"/>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTableWidget" name="tableWidget"/>
+ </item>
+ </layout>
+ </widget>
</widget>
+ <resources/>
+ <connections/>
</ui>
diff --git a/qtmips_machine/alu.cpp b/qtmips_machine/alu.cpp
index 5c74a5d..85bc804 100644
--- a/qtmips_machine/alu.cpp
+++ b/qtmips_machine/alu.cpp
@@ -1,6 +1,53 @@
#include "alu.h"
+#include "qtmipsexception.h"
-Alu::Alu()
-{
+std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) {
+ switch(operation) {
+ case ALU_OP_SLL:
+ return t << sa;
+ case ALU_OP_SRL:
+ return t >> sa;
+ case ALU_OP_SRA:
+ // TODO is this correct implementation? (Shouldn't we be masking top most bit?)
+ return (t >> sa) | (t & 0x80000000);
+ case ALU_OP_SLLV:
+ return t << s;
+ case ALU_OP_SRLV:
+ return t >> s;
+ case ALU_OP_SRAV:
+ // TODO is this correct implementation? (Shouldn't we be masking top most bit?)
+ return (t >> s) | (t & 0x80000000);
+ case ALU_OP_ADD:
+ if (s > (0xFFFFFFFF - t))
+ throw QTMIPS_EXCEPTION(Overflow, "ADD operation overflow/underflow", QString::number(s) + QString(" + ") + QString::number(t));
+ // Intentional falltrough
+ case ALU_OP_ADDU:
+ return s + t;
+ case ALU_OP_SUB:
+ if (s < t)
+ throw QTMIPS_EXCEPTION(Overflow, "SUB operation overflow/underflow", QString::number(s) + QString(" - ") + QString::number(t));
+ // Intentional falltrough
+ case ALU_OP_SUBU:
+ return s - t;
+ case ALU_OP_AND:
+ return s & t;
+ case ALU_OP_OR:
+ return s | t;
+ case ALU_OP_XOR:
+ return s ^ t;
+ case ALU_OP_NOR:
+ return ~(s | t);
+ case ALU_OP_SLTU:
+ // TODO is this correct implementation? (this is two's complement signed representation so do we care?)
+ // Intentional falltrough
+ case ALU_OP_SLT:
+ return (s < t) ? 1 : 0;
+ default:
+ throw QTMIPS_EXCEPTION(UnsupportedAluOperation, "Unknown ALU operation", QString::number(operation, 16));
+ }
+}
+QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) {
+ // TODO
+ return QString("");
}
diff --git a/qtmips_machine/alu.h b/qtmips_machine/alu.h
index e913b0c..974b462 100644
--- a/qtmips_machine/alu.h
+++ b/qtmips_machine/alu.h
@@ -1,11 +1,39 @@
#ifndef ALU_H
#define ALU_H
+#include <cstdint>
+#include <QString>
-class Alu
-{
-public:
- Alu();
+// TODO Any other operations? We seems to be missing a lot of them.
+enum AluOp : std::uint8_t {
+ ALU_OP_SLL = 0,
+ ALU_OP_SRL = 2,
+ ALU_OP_SRA,
+ ALU_OP_SLLV,
+ ALU_OP_SRLV = 6,
+ ALU_OP_SRAV,
+ ALU_OP_ADD = 32,
+ ALU_OP_ADDU,
+ ALU_OP_SUB,
+ ALU_OP_SUBU,
+ ALU_OP_AND,
+ ALU_OP_OR,
+ ALU_OP_XOR,
+ ALU_OP_NOR,
+ ALU_OP_SLT = 42,
+ ALU_OP_SLTU,
+ ALU_OP_LAST = 64 // First impossible operation (just to be sure that we don't overflow)
};
-#endif // ALU_H \ No newline at end of file
+// Do ALU operation.
+// operation: This is function field from instruction or shifted opcode for immediate instructions
+// s: Loaded from rs. Also calles as source.
+// t: Loaded from rt or immediate field from instruction it self. Also called as target.
+// sa: This is value directly from instruction it self (sa section) used for shift operations
+// Returned value is commonly saved to rt/rd or any other way passed trough core
+std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa);
+
+// Returns string representation of ALU instruction (internally used by Instruction::to_str)
+QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa);
+
+#endif // ALU_H
diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp
index a6c92b5..1edcc97 100644
--- a/qtmips_machine/core.cpp
+++ b/qtmips_machine/core.cpp
@@ -1,5 +1,164 @@
#include "core.h"
+#include "programloader.h"
-Core::Core() {
+ struct DecodeMap {
+ bool supported, mem2reg, memwrite, alubimm, regd, regwrite, branch;
+};
+
+// This is temporally operation place holder
+#define NOPE { .supported = false }
+
+// This is map from opcode to signals.
+static const struct DecodeMap dmap[] = {
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = true, .branch = false }, // Alu operations and more
+ // TODO These are just copies of first one
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = false, .branch = true }, // Branch on alu operations
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // J
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // JAL
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BEQ
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BNE
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BLEZ
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BGTZ
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDI
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDIU
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTI
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTIU
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ANDI
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ORI
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // XORI
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LUI
+ NOPE, // 16
+ NOPE, // 17
+ NOPE, // 18
+ NOPE, // 19
+ NOPE, // 20
+ NOPE, // 21
+ NOPE, // 22
+ NOPE, // 23
+ NOPE, // 24
+ NOPE, // 25
+ NOPE, // 26
+ NOPE, // 27
+ NOPE, // 28
+ NOPE, // 29
+ NOPE, // 30
+ NOPE, // 31
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LB
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LH
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWL
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LW
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LBU
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LHU
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWR
+ NOPE, // 39
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SB
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SH
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SWL
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SW
+ NOPE, // 44
+ NOPE, // 45
+ { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SWR
+ NOPE, // 47
+ NOPE, // 48
+ NOPE, // 49
+ NOPE, // 50
+ NOPE, // 51
+ NOPE, // 52
+ NOPE, // 53
+ NOPE, // 54
+ NOPE, // 55
+ NOPE, // 56
+ NOPE, // 57
+ NOPE, // 58
+ NOPE, // 59
+ NOPE, // 60
+ NOPE, // 61
+ NOPE, // 62
+ NOPE // 63
+};
+
+Core::Core(Registers *regs, MemoryAccess *mem) {
+ this->regs = regs;
+ this->mem = mem;
+}
+
+struct Core::dtFetch Core::fetch() {
+ // TODO signals
+ Instruction inst(mem->read_word(regs->read_pc()));
+ regs->pc_inc();
+ return {
+ .inst = inst
+ };
+}
+
+struct Core::dtDecode Core::decode(struct dtFetch dt) {
+ struct DecodeMap dec = dmap[dt.inst.opcode()];
+ if (!dec.supported)
+ // TODO message
+ throw QTMIPS_EXCEPTION(UnsupportedInstruction, "", "");
+ enum AluOp d_alu = ALU_OP_SLL; // TODO decode for real
+ return {
+ .mem2reg = dec.mem2reg,
+ .memwrite = dec.memwrite,
+ .alubimm = dec.alubimm,
+ .regd = dec.regd,
+ .regwrite = dec.regwrite,
+ .branch = dec.branch,
+ .aluop = d_alu,
+ .val_rs = regs->read_gp(dt.inst.rs()),
+ .val_rt = regs->read_gp(dt.inst.rt()),
+ .val_sa = dt.inst.shamt(),
+ .val_immediate = dt.inst.immediate(),
+ };
+ // TODO on jump there should be delay slot. Does processor addes it or compiler. And do we care?
+}
+
+struct Core::dtExecute Core::execute(struct dtDecode dt) {
+ // TODO signals
+ return {
+ .mem2reg = dt.mem2reg,
+ .val = alu_operate(dt.aluop, dt.val_rs, dt.val_rt, dt.val_sa)
+ };
+}
+
+struct Core::dtMemory Core::memory(struct dtExecute dt) {
+ // TODO signals
+ return {
+ .mem2reg = dt.mem2reg,
+ .val = dt.val,
+ };
+}
+
+void Core::writeback(struct dtMemory dt) {
+ if (dt.mem2reg) {
+
+ }
+}
+
+CoreSingle::CoreSingle(Registers *regs, MemoryAccess *mem) : \
+ Core(regs, mem) {
+ // Nothing to do
+}
+
+void CoreSingle::step() {
+ struct dtFetch f = fetch();
+ struct dtDecode d = decode(f);
+ struct dtExecute e = execute(d);
+ struct dtMemory m = memory(e);
+ writeback(m);
+}
+
+CorePipelined::CorePipelined(Registers *regs, MemoryAccess *mem) : \
+ Core(regs, mem) {
+ // Nothing to do
+}
+
+void CorePipelined::step() {
+ // TODO implement pipelined
+ struct dtFetch f = fetch();
+ struct dtDecode d = decode(f);
+ struct dtExecute e = execute(d);
+ struct dtMemory m =memory(e);
+ writeback(m);
}
diff --git a/qtmips_machine/core.h b/qtmips_machine/core.h
index 2fd0a40..d4523c3 100644
--- a/qtmips_machine/core.h
+++ b/qtmips_machine/core.h
@@ -2,20 +2,72 @@
#define CORE_H
#include <QObject>
-#include "instruction.h"
+#include "qtmipsexception.h"
#include "registers.h"
#include "memory.h"
-#include "programloader.h"
-#include "programmemory.h"
+#include "instruction.h"
+#include "alu.h"
class Core : public QObject {
Q_OBJECT
public:
- Core();
+ Core(Registers *regs, MemoryAccess *mem);
+
+ virtual void step() = 0; // Do single step
signals:
-public slots:
+protected:
+ Registers *regs;
+ MemoryAccess *mem;
+
+ struct dtFetch {
+ Instruction inst; // Loaded instruction
+ };
+ struct dtDecode {
+ bool mem2reg; // Write memory output to register (instead alu output)
+ bool memwrite; // If memory should write input
+ bool alubimm; // If b value to alu is immediate value (rt used otherwise)
+ bool regd; // If rd is used (otherwise rt is used for write target)
+ bool regwrite; // If output should be written back to register (which one depends on regd)
+ bool branch; // If this is branch instruction
+ enum AluOp aluop; // Decoded ALU operation
+ std::uint32_t val_rs; // Value from register rs
+ std::uint32_t val_rt; // Value from register rt
+ std::uint8_t val_sa; // Value of sa in instruction it self
+ std::uint16_t val_immediate; // Value of immediate in instruction it self
+ };
+ struct dtExecute {
+ bool mem2reg;
+ std::uint32_t val;
+ // TODO
+ };
+ struct dtMemory {
+ bool mem2reg;
+ // TODO
+ std::uint32_t val;
+ };
+
+ struct dtFetch fetch();
+ struct dtDecode decode(struct dtFetch);
+ struct dtExecute execute(struct dtDecode);
+ struct dtMemory memory(struct dtExecute);
+ void writeback(struct dtMemory);
+
+};
+
+class CoreSingle : public Core {
+public:
+ CoreSingle(Registers *regs, MemoryAccess *mem);
+
+ void step();
+};
+
+class CorePipelined : public Core {
+public:
+ CorePipelined(Registers *regs, MemoryAccess *mem);
+
+ void step();
};
#endif // CORE_H
diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp
index 1771afb..8591d93 100644
--- a/qtmips_machine/instruction.cpp
+++ b/qtmips_machine/instruction.cpp
@@ -1,102 +1,106 @@
#include "instruction.h"
#include "qtmipsexception.h"
-Instruction::Instruction() {
- this->st = IS_FETCH;
+struct InstructionMap {
+ const char *name;
+};
+
+#define IM_UNKNOWN {"UNKNOWN"}
+const struct InstructionMap instruction_map[] = {
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ {"ADDI"},
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN,
+ IM_UNKNOWN
+};
+
+Instruction::Instruction(std::uint32_t inst) {
+ this->dt = inst;
+}
+
+Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct) {
+ this->dt = 0;
+ this->dt |= opcode << 26;
+ this->dt |= rs << 21;
+ this->dt |= rt << 16;
+ this->dt |= rd << 11;
+ this->dt |= shamt << 6;
+ this->dt |= funct;
}
-void Instruction::decode(Registers *regs) {
- if (this->st != IS_FETCH)
- // TODO other exception
- throw std::exception();
- this->st = IS_DECODE;
+Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) {
+ this->dt = 0;
+ this->dt |= opcode << 26;
+ this->dt |= rs << 21;
+ this->dt |= rt << 16;
+ this->dt |= immediate;
}
-void Instruction::execute() {
- if (this->st != IS_DECODE)
- // TODO other exception
- throw std::exception();
- this->st = IS_EXECUTE;
+Instruction::Instruction(std::uint8_t opcode, std::uint32_t address) {
+ this->dt = 0;
+ this->dt |= opcode << 26;
+ this->dt |= address;
}
-void Instruction::memory(Memory *mem) {
- if (this->st != IS_EXECUTE)
- // TODO other exception
- throw std::exception();
- this->st = IS_MEMORY;
+QString Instruction::to_str() {
+ if (this->opcode() >= sizeof(instruction_map))
+ return QString("UNKNOWN");
+ return QString(instruction_map[this->opcode()].name);
}
-void Instruction::write_back(Registers *regs) {
- if (this->st != IS_MEMORY)
- // TODO other exception
- throw std::exception();
- this->st = IS_WRITE_BACK;
+#define MASK(LEN,OFF) ((this->dt >> (OFF)) & ((1 << (LEN)) - 1))
+
+std::uint8_t Instruction::opcode() const {
+ return (std::uint8_t) MASK(6, 26);
}
-enum InstructionState Instruction::state() {
- return this->st;
+std::uint8_t Instruction::rs() const {
+ return (std::uint8_t) MASK(5, 21);
}
-bool Instruction::running() {
- return this->st > IS_FETCH && this->st < IS_WRITE_BACK;
+std::uint8_t Instruction::rt() const {
+ return (std::uint8_t) MASK(5, 16);
}
-bool Instruction::done() {
- return this->st >= IS_WRITE_BACK;
+std::uint8_t Instruction::rd() const {
+ return (std::uint8_t) MASK(5, 11);
}
-InstructionR::InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa) : Instruction() {
- this->rs = rs;
- this->rd = rd;
- this->rt = rt;
- this->sa = sa;
+std::uint8_t Instruction::shamt() const {
+ return (std::uint8_t) MASK(5, 6);
+
}
-// TODO for registers output as register ($0)!
-
-QVector<QString> InstructionR::to_strs() {
- QVector<QString> str;
- // Instruction name
- str << "unknown"; // unknown instruction, should be replaced by child
- // Source register
- str << QString::number((unsigned)this->rs, 10);
- // Target register
- str << QString::number((unsigned)this->rt, 10);
- // Destination register
- str << QString::number((unsigned)this->rd, 10);
- // Shift amount
- str << QString::number((unsigned)this->sa, 10);
- return str;
+std::uint8_t Instruction::funct() const {
+ return (std::uint8_t) MASK(6, 0);
}
-InstructionI::InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) : Instruction() {
- this->rs = rs;
- this->rt = rt;
- this->immediate = immediate;
+std::uint16_t Instruction::immediate() const {
+ return (std::uint16_t) MASK(16, 0);
}
-QVector<QString> InstructionI::to_strs() {
- QVector<QString> str;
- // Instruction name
- str << "unknown"; // unknown instruction, should be replaced by child
- // Source register
- str << QString::number((unsigned)this->rs, 10);
- // Target register
- str << QString::number((unsigned)this->rt, 10);
- // Immediate value
- str << QString::number((unsigned)this->immediate, 16);
- return str;
+std::uint32_t Instruction::address() const {
+ return (std::uint32_t) MASK(26, 0);
}
-InstructionJ::InstructionJ(std::uint32_t address) : Instruction() {
- this->address = address;
+std::uint32_t Instruction::data() const {
+ return this->dt;
}
-QVector<QString> InstructionJ::to_strs() {
- QVector<QString> str;
- // Instruction name
- str << "unknown"; // unknown instruction, should be replaced by child
- // Source register
- str << QString::number((unsigned)this->address, 16);
- return str;
+bool Instruction::operator ==(const Instruction &c) const {
+ return (this->data() == c.data());
}
diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h
index 8c5ede9..3b76fba 100644
--- a/qtmips_machine/instruction.h
+++ b/qtmips_machine/instruction.h
@@ -2,64 +2,30 @@
#define INSTRUCTION_H
#include <qstring.h>
-#include <qvector.h>
-#include "registers.h"
-#include "memory.h"
-
-enum InstructionState {
- IS_FETCH,
- IS_DECODE,
- IS_EXECUTE,
- IS_MEMORY,
- IS_WRITE_BACK,
-};
class Instruction {
public:
- Instruction();
+ Instruction(std::uint32_t inst);
+ Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct); // Type R
+ Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); // Type I
+ Instruction(std::uint8_t opcode, std::uint32_t address); // Type J
- // TODO return some info for forwarding, stall, flush
- virtual void decode(Registers *regs); // Read and prepare instructions
- virtual void execute(); // ALU operations
- virtual void memory(Memory *mem); // Read or write to memory
- virtual void write_back(Registers *regs); // Write results to registers
+ QString to_str();
- enum InstructionState state();
- bool running();
- bool done();
+ std::uint8_t opcode() const;
+ std::uint8_t rs() const;
+ std::uint8_t rt() const;
+ std::uint8_t rd() const;
+ std::uint8_t shamt() const;
+ std::uint8_t funct() const;
+ std::uint16_t immediate() const;
+ std::uint32_t address() const;
+ std::uint32_t data() const;
- virtual QVector<QString> to_strs() = 0; // Returns all fields of instructions in string
+ bool operator ==(const Instruction &c) const;
private:
- enum InstructionState st;
-};
-
-class InstructionR : public Instruction {
-public:
- InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa);
-
- QVector<QString> to_strs();
-protected:
- std::uint8_t rs, rd, rt, sa;
-};
-
-class InstructionI : public Instruction {
-public:
- InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate);
-
- QVector<QString> to_strs();
-protected:
- std::uint8_t rs, rt;
- std::uint16_t immediate;
-};
-
-class InstructionJ : public Instruction {
-public:
- InstructionJ(std::uint32_t address);
-
- QVector<QString> to_strs();
-protected:
- std::uint32_t address;
+ std::uint32_t dt;
};
#endif // INSTRUCTION_H
diff --git a/qtmips_machine/instructions/arithmetic.cpp b/qtmips_machine/instructions/arithmetic.cpp
deleted file mode 100644
index f3cad86..0000000
--- a/qtmips_machine/instructions/arithmetic.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#include "instructions/arithmetic.h"
-
-InstructionArithmetic::InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt)
- : InstructionR(rs, rd, rt, 0) {
- this->type = type;
-}
-
-void InstructionArithmetic::decode(Registers *regs) {
- Instruction::decode(regs);
- this->rs_d = regs->read_gp(this->rs);
- this->rd_d = regs->read_gp(this->rd);
-}
-
-void InstructionArithmetic::execute() {
- Instruction::execute();
- switch (this->type) {
- case IAT_ADD:
- this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->rd_d);
- break;
- case IAT_ADDU:
- this->rt_d = this->rs_d + this->rd_d;
- break;
- case IAT_SUB:
- this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->rd_d);
- break;
- case IAT_SUBU:
- this->rt_d = this->rs_d - this->rd_d;
- break;
- case IAT_AND:
- this->rt_d = this->rs_d & this->rd_d;
- break;
- case IAT_OR:
- this->rt_d = this->rs_d | this->rd_d;
- break;
- case IAT_XOR:
- this->rt_d = this->rs_d ^ this->rd_d;
- break;
- case IAT_NOR:
- // TODO
- break;
- case IAT_SLT:
- // TODO
- break;
- case IAT_SLTU:
- // TODO
- break;
- }
-}
-
-void InstructionArithmetic::memory(Memory *mem) {
- Instruction::memory(mem);
- // pass
-}
-
-void InstructionArithmetic::write_back(Registers *regs) {
- Instruction::write_back(regs);
- regs->write_gp(this->rt, this->rt_d);
-}
-
-QVector<QString> InstructionArithmetic::to_strs() {
- QVector<QString> str = this->InstructionR::to_strs();
- str.erase(str.begin() + 4); // Drop sa field
- switch (this->type) {
- case IAT_ADD:
- str[0] = "add";
- break;
- case IAT_ADDU:
- str[0] = "addu";
- break;
- case IAT_SUB:
- str[0] = "sub";
- break;
- case IAT_SUBU:
- str[0] = "subu";
- break;
- case IAT_AND:
- str[0] = "and";
- break;
- case IAT_OR:
- str[0] = "or";
- break;
- case IAT_XOR:
- str[0] = "xor";
- break;
- case IAT_NOR:
- str[0] = "nor";
- break;
- case IAT_SLT:
- str[0] = "slt";
- break;
- case IAT_SLTU:
- str[0] = "sltu";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
-
-InstructionArithmeticImmediate::InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value)
- : InstructionI(rs, rt, value) {
- this->type = type;
-}
-
-void InstructionArithmeticImmediate::decode(Registers *regs) {
- Instruction::decode(regs);
- this->rs_d = regs->read_gp(this->rs);
-}
-
-void InstructionArithmeticImmediate::execute() {
- Instruction::execute();
- switch (this->type) {
- case IAT_ADDI:
- this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->immediate);
- break;
- case IAT_ADDIU:
- this->rt_d = this->rs_d + this->immediate;
- break;
- case IAT_ANDI:
- this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->immediate);
- break;
- case IAT_ORI:
- this->rt_d = this->rs_d - this->immediate;
- break;
- case IAT_XORI:
- this->rt_d = this->rs_d & this->immediate;
- break;
- }
-}
-
-void InstructionArithmeticImmediate::memory(Memory *mem) {
- Instruction::memory(mem);
- // pass
-}
-
-void InstructionArithmeticImmediate::write_back(Registers *regs) {
- Instruction::write_back(regs);
- regs->write_gp(this->rt, this->rt_d);
-}
-
-QVector<QString> InstructionArithmeticImmediate::to_strs() {
- QVector<QString> str = this->InstructionI::to_strs();
- switch (this->type) {
- case IAT_ADDI:
- str[0] = "addi";
- break;
- case IAT_ADDIU:
- str[0] = "addiu";
- break;
- case IAT_ANDI:
- str[0] = "andi";
- break;
- case IAT_ORI:
- str[0] = "ori";
- break;
- case IAT_XORI:
- str[0] = "xori";
- break;
- case IAT_SLTI:
- str[0] = "slti";
- break;
- case IAT_SLTIU:
- str[0] = "sltiu";
- break;
- case IAT_LUI:
- str[0] = "lui";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
diff --git a/qtmips_machine/instructions/arithmetic.h b/qtmips_machine/instructions/arithmetic.h
deleted file mode 100644
index 185ed95..0000000
--- a/qtmips_machine/instructions/arithmetic.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef ARITHMETIC_H
-#define ARITHMETIC_H
-
-#include "instruction.h"
-
-enum InstructionArithmeticT {
- IAT_ADD, // Add
- IAT_ADDU, // Add unsigned
- IAT_SUB, // Subtract
- IAT_SUBU, // Subtract unsigned
- IAT_AND,
- IAT_OR,
- IAT_XOR,
- IAT_NOR,
- IAT_SLT, // set on less than
- IAT_SLTU, // set on less than unsigned
-};
-
-class InstructionArithmetic : public InstructionR {
-public:
- InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt);
-
- void decode(Registers *regs);
- void execute();
- void memory(Memory *mem);
- void write_back(Registers *regs);
-
- QVector<QString> to_strs();
-private:
- enum InstructionArithmeticT type;
- std::uint32_t rs_d, rd_d, rt_d;
-};
-
-enum InstructionArithmeticImmediateT {
- IAT_ADDI,
- IAT_ADDIU,
- IAT_ANDI,
- IAT_ORI,
- IAT_XORI,
- IAT_SLTI,
- IAT_SLTIU,
- IAT_LUI
-};
-
-class InstructionArithmeticImmediate : public InstructionI {
-public:
- InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value);
-
- void decode(Registers *regs);
- void execute();
- void memory(Memory *mem);
- void write_back(Registers *regs);
-
- QVector<QString> to_strs();
-private:
- enum InstructionArithmeticImmediateT type;
- std::uint32_t rs_d, rt_d;
-};
-
-#endif // ARITHMETIC_H
diff --git a/qtmips_machine/instructions/jumpbranch.cpp b/qtmips_machine/instructions/jumpbranch.cpp
deleted file mode 100644
index 6579c2b..0000000
--- a/qtmips_machine/instructions/jumpbranch.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "jumpbranch.h"
-
-InstructionJump::InstructionJump(bool link, std::uint32_t address)
- : InstructionJ(address) {
- this->link = link;
-}
-
-QVector<QString> InstructionJump::to_strs() {
- QVector<QString> str = this->InstructionJ::to_strs();
- if (link)
- str[0] = "j";
- else
- str[0] = "jal";
- return str;
-}
-
-InstructionJumpRegister::InstructionJumpRegister(bool link, std::uint8_t rs)
- : InstructionR(rs, 0, 0, 0) {
- this->link = link;
-}
-
-QVector<QString> InstructionJumpRegister::to_strs() {
- QVector<QString> str = this->InstructionR::to_strs();
- str.erase(str.begin() + 2, str.end()); // Drop every field after rs
- if (link)
- str[0] = "j";
- else
- str[0] = "jal";
- return str;
-}
diff --git a/qtmips_machine/instructions/jumpbranch.h b/qtmips_machine/instructions/jumpbranch.h
deleted file mode 100644
index 762ad95..0000000
--- a/qtmips_machine/instructions/jumpbranch.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef JUMPBRANCH_H
-#define JUMPBRANCH_H
-
-#include "instruction.h"
-
-class InstructionJump : InstructionJ {
-public:
- InstructionJump(bool link, std::uint32_t address);
- QVector<QString> to_strs();
-private:
- bool link;
-};
-
-class InstructionJumpRegister : InstructionR {
-public:
- InstructionJumpRegister(bool link, std::uint8_t rs);
- QVector<QString> to_strs();
-private:
- bool link;
-};
-
-enum InstructionBranchT {
-
-};
-
-class InstructionBranch : InstructionI {
-public:
- InstructionBranch();
- QVector<QString> to_strs();
-private:
- // TODO
-};
-
-#endif // JUMPBRANCH_H
diff --git a/qtmips_machine/instructions/loadstore.cpp b/qtmips_machine/instructions/loadstore.cpp
deleted file mode 100644
index 27c6402..0000000
--- a/qtmips_machine/instructions/loadstore.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "loadstore.h"
-
-InstructionLoad::InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset)
- : InstructionI(rs, rt, offset) {
- this->type = type;
-}
-
-QVector<QString> InstructionLoad::to_strs() {
- QVector<QString> str = this->InstructionI::to_strs();
- switch (this->type) {
- case ILST_B:
- str[0] = "lb";
- break;
- case ILST_HW:
- str[0] = "lh";
- break;
- case ILST_WL:
- str[0] = "lwl";
- break;
- case ILST_W:
- str[0] = "lw";
- break;
- case ILST_BU:
- str[0] = "lbu";
- break;
- case ILST_HU:
- str[0] = "lhu";
- break;
- case ILST_WR:
- str[0] = "lwr";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
-
-InstructionStore::InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset)
- : InstructionI(rs, rt, offset) {
- this->type = type;
-}
-
-QVector<QString> InstructionStore::to_strs() {
- QVector<QString> str = this->InstructionI::to_strs();
- switch (this->type) {
- case ILST_B:
- str[0] = "sb";
- break;
- case ILST_HW:
- str[0] = "sh";
- break;
- case ILST_WL:
- str[0] = "swl";
- break;
- case ILST_W:
- str[0] = "sw";
- break;
- case ILST_WR:
- str[0] = "swr";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
diff --git a/qtmips_machine/instructions/loadstore.h b/qtmips_machine/instructions/loadstore.h
deleted file mode 100644
index 6f028fd..0000000
--- a/qtmips_machine/instructions/loadstore.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef LOADSTORE_H
-#define LOADSTORE_H
-
-#include "instruction.h"
-
-enum InstructionLoadStoreT {
- ILST_B, // Byte
- ILST_HW, // Half word
- ILST_WL, // Word left
- ILST_W, // Word
- ILST_BU, // Byte unsigned
- ILST_HU, // Half word unsigned
- ILST_WR // Word right
-};
-
-class InstructionLoad : public InstructionI {
-public:
- InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset);
- QVector<QString> to_strs();
-private:
- enum InstructionLoadStoreT type;
-};
-
-class InstructionStore : public InstructionI {
-public:
- InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset);
- QVector<QString> to_strs();
-private:
- enum InstructionLoadStoreT type;
-};
-
-#endif // LOADSTORE_H
diff --git a/qtmips_machine/instructions/nop.cpp b/qtmips_machine/instructions/nop.cpp
deleted file mode 100644
index 7623dff..0000000
--- a/qtmips_machine/instructions/nop.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "nop.h"
-
-QVector<QString> InstructionNop::to_strs() {
- QVector<QString> str;
- str << QString("nop");
- return str;
-}
diff --git a/qtmips_machine/instructions/nop.h b/qtmips_machine/instructions/nop.h
deleted file mode 100644
index 5c019fd..0000000
--- a/qtmips_machine/instructions/nop.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef NOP_H
-#define NOP_H
-
-#include "instruction.h"
-
-class InstructionNop : public Instruction {
-public:
- QVector<QString> to_strs();
-};
-
-#endif // NOP_H
diff --git a/qtmips_machine/instructions/shift.cpp b/qtmips_machine/instructions/shift.cpp
deleted file mode 100644
index 34bc1c9..0000000
--- a/qtmips_machine/instructions/shift.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "shift.h"
-
-InstructionShift::InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa)
- : InstructionR(0, rt, rd, sa) {
- this->type = type;
-}
-
-QVector<QString> InstructionShift::to_strs() {
- QVector<QString> str = this->InstructionR::to_strs();
- str.erase(str.begin() + 1); // Drop rs field
- switch (this->type) {
- case IST_LL:
- str[0] = "sll";
- break;
- case IST_RL:
- str[0] = "srl";
- break;
- case IST_RA:
- str[0] = "sra";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
-
-InstructionShiftVariable::InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd)
- : InstructionR(rs, rt, rd, 0) {
- this->type = type;
-}
-
-QVector<QString> InstructionShiftVariable::to_strs() {
- QVector<QString> str = this->InstructionR::to_strs();
- str.erase(str.begin() + 4); // Drop sa field
- switch (this->type) {
- case IST_LL:
- str[0] = "sllv";
- break;
- case IST_RL:
- str[0] = "srlv";
- break;
- case IST_RA:
- str[0] = "srav";
- break;
- default:
- // TODO different exception
- throw std::exception();
- }
- return str;
-}
diff --git a/qtmips_machine/instructions/shift.h b/qtmips_machine/instructions/shift.h
deleted file mode 100644
index 69e2e1e..0000000
--- a/qtmips_machine/instructions/shift.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef SHIFT_H
-#define SHIFT_H
-
-#include "instruction.h"
-
-enum InstructionShiftT {
- IST_LL, // Left logical
- IST_RL, // Right logical
- IST_RA // Right arithmetic
-};
-
-class InstructionShift : public InstructionR {
-public:
- InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa);
- QVector<QString> to_strs();
-private:
- enum InstructionShiftT type;
-};
-
-class InstructionShiftVariable : public InstructionR {
-public:
- InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd);
- QVector<QString> to_strs();
-private:
- enum InstructionShiftT type;
-};
-
-#endif // SHIFT_H
diff --git a/qtmips_machine/machineconfig.cpp b/qtmips_machine/machineconfig.cpp
index 8562ab9..f23140f 100644
--- a/qtmips_machine/machineconfig.cpp
+++ b/qtmips_machine/machineconfig.cpp
@@ -1,6 +1,45 @@
#include "machineconfig.h"
-MachineConfig::MachineConfig()
-{
+MachineConfig::MachineConfig() {
+ pipeline = false;
+ jumppred = false;
+}
+
+MachineConfig::MachineConfig(MachineConfig *cc) {
+ pipeline = cc->pipelined();
+ jumppred = cc->jump_prediction();
+}
+
+void MachineConfig::set_pipelined(bool v) {
+ pipeline = v;
+}
+
+void MachineConfig::set_jump_prediction(bool v) {
+ jumppred = v;
+ if (jumppred)
+ pipeline = true;
+}
+
+void MachineConfig::set_cache(enum CacheType cc) {
+ cache_type = cc;
+}
+
+void MachineConfig::set_elf(QString path) {
+ elf_path = path;
+}
+
+bool MachineConfig::pipelined() const {
+ return pipeline;
+}
+
+bool MachineConfig::jump_prediction() const {
+ return jumppred;
+}
+
+enum MachineConfig::CacheType MachineConfig::cache() const {
+ return cache_type;
+}
+QString MachineConfig::elf() const {
+ return elf_path;
}
diff --git a/qtmips_machine/machineconfig.h b/qtmips_machine/machineconfig.h
index 58c2fc2..352e62b 100644
--- a/qtmips_machine/machineconfig.h
+++ b/qtmips_machine/machineconfig.h
@@ -1,11 +1,40 @@
#ifndef MACHINECONFIG_H
#define MACHINECONFIG_H
+#include <QString>
-class MachineConfig
-{
+class MachineConfig {
public:
MachineConfig();
+ MachineConfig(MachineConfig *cc);
+
+ enum CacheType {
+ CCT_NONE,
+ CCT_ASSOCIATIVE,
+ // TODO
+ };
+
+ // Configure if CPU is pipelined
+ // In default disabled.
+ void set_pipelined(bool);
+ // Configure if we want to do jump prediction
+ // In default disabled. When enabled it also automatically enables pipelining
+ void set_jump_prediction(bool);
+ // Configure cache type
+ // In default CCT_NONE is used.
+ void set_cache(enum CacheType);
+ // Set path to source elf file. This has to be set before core is initialized.
+ void set_elf(QString path);
+
+ bool pipelined() const;
+ bool jump_prediction() const;
+ enum CacheType cache() const;
+ QString elf() const;
+
+private:
+ bool pipeline, jumppred;
+ enum CacheType cache_type;
+ QString elf_path;
};
-#endif // MACHINECONFIG_H \ No newline at end of file
+#endif // MACHINECONFIG_H
diff --git a/qtmips_machine/programloader.cpp b/qtmips_machine/programloader.cpp
index 6eefa6c..f49fa3c 100644
--- a/qtmips_machine/programloader.cpp
+++ b/qtmips_machine/programloader.cpp
@@ -2,12 +2,11 @@
#include <exception>
#include <unistd.h>
#include <fcntl.h>
-#include <iostream>
#include <errno.h>
#include <cstring>
#include "qtmipsexception.h"
-ProgramLoader::ProgramLoader(char *file) {
+ProgramLoader::ProgramLoader(const char *file) {
// Initialize elf library
if (elf_version(EV_CURRENT) == EV_NONE)
throw QTMIPS_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1));
@@ -53,29 +52,35 @@ ProgramLoader::ProgramLoader(char *file) {
// TODO instead of direct access should we be using sections and elf_data? And if so how to link program header and section?
}
+ProgramLoader::ProgramLoader(QString file) : ProgramLoader(file.toStdString().c_str()) { }
+
ProgramLoader::~ProgramLoader() {
// Close elf
- elf_end(this->elf);
+ // TODO fix (this results to segfault, there is probably somethig passed to it on stack or something)
+ //elf_end(this->elf);
// Close file
close(this->fd);
}
-size_t ProgramLoader::get_nsec() {
- return this->map.size();
-}
-
-std::uint32_t ProgramLoader::get_address(size_t sec) {
- SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section");
- return this->phdrs[this->map[sec]].p_vaddr;
+void ProgramLoader::to_memory(Memory *mem) {
+ // Load program to memory (just dump it byte by byte)
+ for (int i = 0; i < this->map.size(); i++) {
+ std::uint32_t base_address = this->phdrs[this->map[i]].p_vaddr;
+ char *f = elf_rawfile(this->elf, NULL);
+ size_t phdrs_i = this->map[i];
+ for (unsigned y = 0; y < this->phdrs[phdrs_i].p_filesz; y++) {
+ mem->write_byte(base_address + y, (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + y]);
+ }
+ }
}
-QVector<std::uint8_t> ProgramLoader::get_data(size_t sec) {
- SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section");
- QVector<std::uint8_t> d;
- char *f = elf_rawfile(this->elf, NULL);
- size_t phdrs_i = this->map[sec];
- for (unsigned i = 0; i < this->phdrs[phdrs_i].p_filesz; i++) {
- d << (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + i];
+std::uint32_t ProgramLoader::end() {
+ std::uint32_t last = 0;
+ // Go trough all sections and found out last one
+ for (int i = 0; i < this->map.size(); i++) {
+ Elf32_Phdr *phdr = &(this->phdrs[this->map[i]]);
+ if ((phdr->p_vaddr + phdr->p_filesz) > last)
+ last = phdr->p_vaddr + phdr->p_filesz;
}
- return d;
+ return last + 0x10; // We add offset so we are sure that also pipeline is empty
}
diff --git a/qtmips_machine/programloader.h b/qtmips_machine/programloader.h
index 4d722d2..7da241c 100644
--- a/qtmips_machine/programloader.h
+++ b/qtmips_machine/programloader.h
@@ -6,16 +6,18 @@
#include <gelf.h>
#include <cstdint>
#include <qvector.h>
+#include <qstring.h>
+#include "memory.h"
class ProgramLoader {
public:
- ProgramLoader(char *file);
+ ProgramLoader(const char *file);
+ ProgramLoader(QString file);
~ProgramLoader();
- size_t get_nsec(); // Returns number of loadable sections
- std::uint32_t get_address(size_t sec); // Get target address for given section
- QVector<std::uint8_t> get_data(size_t sec); // Returns bytes of given section
+ void to_memory(Memory *mem); // Writes all loaded sections to memory
+ std::uint32_t end(); // Return address after which there is no more code for sure
private:
int fd;
Elf *elf;
diff --git a/qtmips_machine/programmemory.h b/qtmips_machine/programmemory.h
deleted file mode 100644
index 14187c5..0000000
--- a/qtmips_machine/programmemory.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef PROGRAMMEMORY_H
-#define PROGRAMMEMORY_H
-
-#include <vector>
-#include "programloader.h"
-#include "memory.h"
-#include "instruction.h"
-
-class ProgramMemory {
-public:
- ProgramMemory(MemoryAccess *memory);
-
- void load(ProgramLoader *l);
- Instruction *at(std::uint32_t address); // return instruction isntance for given address
-
-private:
- MemoryAccess *memory;
- Instruction *decode_r(std::uint32_t dt);
- Instruction *decode_j(std::uint32_t dt, std::uint8_t opcode);
- Instruction *decode_i(std::uint32_t dt, std::uint8_t opcode);
-};
-
-#endif // PROGRAMMEMORY_H
diff --git a/qtmips_machine/qtmips_machine.pro b/qtmips_machine/qtmips_machine.pro
index 213727d..a1edcaa 100644
--- a/qtmips_machine/qtmips_machine.pro
+++ b/qtmips_machine/qtmips_machine.pro
@@ -7,6 +7,7 @@ TEMPLATE = lib
LIBS += -lelf
QMAKE_CXXFLAGS += -std=c++0x
+QMAKE_CXXFLAGS += -ggdb
DEFINES += QTMIPS_MACHINE_LIBRARY
DEFINES += QT_DEPRECATED_WARNINGS
@@ -19,13 +20,9 @@ SOURCES += \
instruction.cpp \
registers.cpp \
programloader.cpp \
- programmemory.cpp \
- instructions/arithmetic.cpp \
- instructions/loadstore.cpp \
- instructions/shift.cpp \
- instructions/nop.cpp \
- instructions/jumpbranch.cpp \
- cache.cpp
+ cache.cpp \
+ alu.cpp \
+ machineconfig.cpp
HEADERS += \
qtmipsmachine.h \
@@ -41,4 +38,6 @@ HEADERS += \
instructions/shift.h \
instructions/nop.h \
instructions/jumpbranch.h \
- cache.h
+ cache.h \
+ alu.h \
+ machineconfig.h
diff --git a/qtmips_machine/qtmipsexception.cpp b/qtmips_machine/qtmipsexception.cpp
index 4cec6b0..02193cb 100644
--- a/qtmips_machine/qtmipsexception.cpp
+++ b/qtmips_machine/qtmipsexception.cpp
@@ -19,7 +19,7 @@ const char *QtMipsException::what() const throw() {
QString QtMipsException::msg(bool pos) const {
QString message;
if (pos)
- message += QString("(") + QString(this->file) + QString(":") + QString(this->line) + QString(") ");
+ message += QString("(") + QString(this->file) + QString(":") + QString::number(this->line) + QString(") ");
message += this->reason;
if (!this->ext.isEmpty()) {
message += QString(": ");
@@ -44,6 +44,16 @@ QtMipsExceptionUnsupportedInstruction::QtMipsExceptionUnsupportedInstruction(QTM
return;
}
+QtMipsExceptionUnsupportedAluOperation::QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON)
+ : QtMipsExceptionRuntime(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionOverflow::QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON)
+ : QtMipsExceptionRuntime(reason, ext, file, line) {
+ return;
+}
+
QtMipsExceptionUnalignedJump::QtMipsExceptionUnalignedJump(QTMIPS_ARGS_COMMON)
: QtMipsExceptionRuntime(reason, ext, file, line) {
return;
diff --git a/qtmips_machine/qtmipsexception.h b/qtmips_machine/qtmipsexception.h
index 81895d6..b81f748 100644
--- a/qtmips_machine/qtmipsexception.h
+++ b/qtmips_machine/qtmipsexception.h
@@ -37,6 +37,20 @@ public:
QtMipsExceptionUnsupportedInstruction(QTMIPS_ARGS_COMMON);
};
+// Decoded ALU operation is not supported
+// This is basically same exception as QtMipsExceptionUnsupportedInstruction but it is emmited from ALU when executed and not before that.
+class QtMipsExceptionUnsupportedAluOperation : public QtMipsExceptionRuntime {
+public:
+ QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON);
+};
+
+// Integer operation resulted to overflow (or underflow as we are working with unsigned values)
+// This is for sure caused by program it self.
+class QtMipsExceptionOverflow : public QtMipsExceptionRuntime {
+public:
+ QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON);
+};
+
// Instruction is jumping to unaligned address (ADDR%4!=0)
// This can be caused by bug or by user program as it can be jumping relative to register
// This shouldn't be happening with non-register jumps as those should be verified by compiler
diff --git a/qtmips_machine/qtmipsmachine.cpp b/qtmips_machine/qtmipsmachine.cpp
index 0fc207c..3d5ce98 100644
--- a/qtmips_machine/qtmipsmachine.cpp
+++ b/qtmips_machine/qtmipsmachine.cpp
@@ -1,5 +1,70 @@
#include "qtmipsmachine.h"
+#include "programloader.h"
-QtMipsMachine::QtMipsMachine(char *file) {
- this->loader = new ProgramLoader(file);
+QtMipsMachine::QtMipsMachine(const MachineConfig &cc) {
+ ProgramLoader program(cc.elf());
+
+ regs = new Registers();
+ mem = new Memory();
+
+ program.to_memory(mem);
+ program_end = program.end();
+
+ MemoryAccess *coremem;
+ switch (cc.cache()) {
+ case MachineConfig::CCT_NONE:
+ cch = nullptr;
+ coremem = mem;
+ break;
+ case MachineConfig::CCT_ASSOCIATIVE:
+ // TODO
+ coremem = mem;
+ //coremem = cch = new CacheAssociative();
+ break;
+ }
+
+ // TODO pipelined
+ cr = new CoreSingle(regs, coremem);
+
+ run_speed = 1;
+ run_t = new QTimer(this);
+ connect(run_t, SIGNAL(timeout()), this, SLOT(step()));
+}
+
+void QtMipsMachine::set_speed(unsigned val) {
+ run_speed = val;
+}
+
+const Registers *QtMipsMachine::registers() {
+ return regs;
+}
+
+const Memory *QtMipsMachine::memory() {
+ return mem;
+}
+
+const Cache *QtMipsMachine::cache() {
+ return cch;
+}
+
+const Core *QtMipsMachine::core() {
+ return cr;
+}
+
+void QtMipsMachine::play() {
+ run_t->start(run_speed);
+}
+
+void QtMipsMachine::pause() {
+ run_t->stop();
+}
+
+void QtMipsMachine::step() {
+ cr->step();
+ if (regs->read_pc() >= program_end)
+ emit program_exit();
+}
+
+void QtMipsMachine::restart() {
+ // TODO
}
diff --git a/qtmips_machine/qtmipsmachine.h b/qtmips_machine/qtmipsmachine.h
index af981b3..829e571 100644
--- a/qtmips_machine/qtmipsmachine.h
+++ b/qtmips_machine/qtmipsmachine.h
@@ -2,24 +2,47 @@
#define QTMIPSMACHINE_H
#include <QObject>
-
+#include <QTimer>
+#include <cstdint>
#include "qtmipsexception.h"
-#include "programloader.h"
+#include "machineconfig.h"
+#include "registers.h"
+#include "memory.h"
#include "core.h"
-// TODO piplined core
+#include "cache.h"
-class QtMipsMachine : QObject {
+class QtMipsMachine : public QObject {
Q_OBJECT
public:
- QtMipsMachine(char *file);
+ QtMipsMachine(const MachineConfig &cc);
+
+ void set_speed(unsigned);
+ const Registers *registers();
+ const Memory *memory();
+ const Cache *cache();
+ const Core *core();
+
+public slots:
// TODO handle speed
void play();
void pause();
void step();
void restart();
+
+signals:
+ void program_exit();
+
private:
- ProgramLoader *loader;
+ Registers *regs;
+ Memory *mem;
+ Cache *cch;
+ Core *cr;
+
+ unsigned run_speed;
+ QTimer *run_t;
+
+ std::uint32_t program_end;
};
#endif // QTMIPSMACHINE_H
diff --git a/qtmips_machine/registers.cpp b/qtmips_machine/registers.cpp
index 837f403..fa984fb 100644
--- a/qtmips_machine/registers.cpp
+++ b/qtmips_machine/registers.cpp
@@ -14,12 +14,21 @@ Registers::Registers() {
this->hi = this->lo = 0;
}
-std::uint32_t Registers::read_pc() {
+Registers::Registers(const Registers *orig) : Registers() {
+ this->pc = orig->read_pc();
+ for (int i = 0; i < 31; i++)
+ this->gp[i] = orig->read_gp(i);
+ this->lo = orig->read_hi_lo(false);
+ this->hi = orig->read_hi_lo(true);
+}
+
+std::uint32_t Registers::read_pc() const {
return this->pc;
}
std::uint32_t Registers::pc_inc() {
this->pc += 4;
+ emit pc_update(this->pc);
return this->pc;
}
@@ -27,6 +36,7 @@ std::uint32_t Registers::pc_jmp(std::int32_t offset) {
if (offset % 4)
throw QTMIPS_EXCEPTION(UnalignedJump, "Trying to jump by unaligned offset", QString::number(offset, 16));
this->pc += offset;
+ emit pc_update(this->pc);
return this->pc;
}
@@ -36,7 +46,7 @@ void Registers::pc_abs_jmp(std::uint32_t address) {
this->pc = address;
}
-std::uint32_t Registers::read_gp(std::uint8_t i) {
+std::uint32_t Registers::read_gp(std::uint8_t i) const {
SANITY_ASSERT(i < 32, QString("Trying to read from register ") + QString(i));
if (!i) // $0 always reads as 0
return 0;
@@ -50,7 +60,7 @@ void Registers::write_gp(std::uint8_t i, std::uint32_t value) {
this->gp[i - 1] = value;
}
-std::uint32_t Registers::read_hi_lo(bool hi) {
+std::uint32_t Registers::read_hi_lo(bool hi) const {
if (hi)
return this->hi;
else
@@ -63,3 +73,16 @@ void Registers::write_hi_lo(bool hi, std::uint32_t value) {
else
this->lo = value;
}
+
+bool Registers::operator ==(const Registers &c) const {
+ if (read_pc() != c.read_pc())
+ return false;
+ for (int i = 0; i < 31; i++)
+ if (read_gp(i) != c.read_gp(i))
+ return false;
+ if (read_hi_lo(false) != c.read_hi_lo(false))
+ return false;
+ if (read_hi_lo(true) != c.read_hi_lo(true))
+ return false;
+ return true;
+}
diff --git a/qtmips_machine/registers.h b/qtmips_machine/registers.h
index a550f4a..905a212 100644
--- a/qtmips_machine/registers.h
+++ b/qtmips_machine/registers.h
@@ -8,18 +8,22 @@ class Registers : public QObject {
Q_OBJECT
public:
Registers();
+ Registers(const Registers*);
- std::uint32_t read_pc(); // Return current value of program counter
+ std::uint32_t read_pc() const; // Return current value of program counter
std::uint32_t pc_inc(); // Increment program counter by four bytes
std::uint32_t pc_jmp(std::int32_t offset); // Relative jump from current location in program counter
void pc_abs_jmp(std::uint32_t address); // Absolute jump in program counter (write to pc)
- std::uint32_t read_gp(std::uint8_t i); // Read general-purpose register
+ std::uint32_t read_gp(std::uint8_t i) const; // Read general-purpose register
void write_gp(std::uint8_t i, std::uint32_t value); // Write general-purpose register
- std::uint32_t read_hi_lo(bool hi); // true - read HI / false - read LO
+ std::uint32_t read_hi_lo(bool hi) const; // true - read HI / false - read LO
void write_hi_lo(bool hi, std::uint32_t value);
+ bool operator ==(const Registers &c) const;
+
signals:
+ void pc_update(std::uint32_t val);
// TODO signals
private:
diff --git a/qtmips_machine/tests/testalu.cpp b/qtmips_machine/tests/testalu.cpp
index e69de29..37accdf 100644
--- a/qtmips_machine/tests/testalu.cpp
+++ b/qtmips_machine/tests/testalu.cpp
@@ -0,0 +1,91 @@
+#include "tst_machine.h"
+#include "alu.h"
+#include "qtmipsexception.h"
+
+void MachineTests::alu_data() {
+ QTest::addColumn<std::uint8_t>("op");
+ QTest::addColumn<std::uint32_t>("s");
+ QTest::addColumn<std::uint32_t>("t");
+ QTest::addColumn<std::uint8_t>("sa");
+ QTest::addColumn<std::uint32_t>("res");
+
+ // TODO SLL-SRAV
+ QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \
+ << (std::uint32_t)24 \
+ << (std::uint32_t)66 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)90;
+ QTest::newRow("ADDU") << (std::uint8_t)ALU_OP_ADDU \
+ << (std::uint32_t)24 \
+ << (std::uint32_t)66 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)90;
+ QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \
+ << (std::uint32_t)66 \
+ << (std::uint32_t)24 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)42;
+ QTest::newRow("SUBU") << (std::uint8_t)ALU_OP_SUBU \
+ << (std::uint32_t)24 \
+ << (std::uint32_t)66 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)-42;
+ QTest::newRow("AND") << (std::uint8_t)ALU_OP_AND \
+ << (std::uint32_t)0xA81 \
+ << (std::uint32_t)0x603 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)0x201;
+ QTest::newRow("OR") << (std::uint8_t)ALU_OP_OR \
+ << (std::uint32_t)0xA81 \
+ << (std::uint32_t)0x603 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)0xE83;
+ QTest::newRow("XOR") << (std::uint8_t)ALU_OP_XOR \
+ << (std::uint32_t)0xA81 \
+ << (std::uint32_t)0x603 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)0xC82;
+ QTest::newRow("NOR") << (std::uint8_t)ALU_OP_NOR \
+ << (std::uint32_t)0xA81 \
+ << (std::uint32_t)0x603 \
+ << (std::uint8_t)0 \
+ << (std::uint32_t)0xFFFFF17C;
+ // TODO SLT-SLTU
+}
+
+void MachineTests::alu() {
+ QFETCH(std::uint8_t, op);
+ QFETCH(std::uint32_t, s);
+ QFETCH(std::uint32_t, t);
+ QFETCH(std::uint8_t, sa);
+ QFETCH(std::uint32_t, res);
+
+ QCOMPARE(alu_operate((enum AluOp)op, s , t, sa), res);
+}
+
+void MachineTests::alu_except_data() {
+ QTest::addColumn<std::uint8_t>("op");
+ QTest::addColumn<std::uint32_t>("s");
+ QTest::addColumn<std::uint32_t>("t");
+ // Note no sa as shift unstruction has no exceptions
+
+ QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \
+ << (std::uint32_t)0x8fffffff \
+ << (std::uint32_t)0x90000000;
+ QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \
+ << (std::uint32_t)3 \
+ << (std::uint32_t)4;
+ // Just test that we can throw unsupported ALU operation
+ QTest::newRow("?") << (std::uint8_t)ALU_OP_LAST \
+ << (std::uint32_t)0 \
+ << (std::uint32_t)0;
+}
+
+void MachineTests::alu_except() {
+ QFETCH(std::uint8_t, op);
+ QFETCH(std::uint32_t, s);
+ QFETCH(std::uint32_t, t);
+
+ // Only runtime exception is expected as any other exception is a bug
+ QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0), QtMipsExceptionRuntime);
+}
diff --git a/qtmips_machine/tests/testcore.cpp b/qtmips_machine/tests/testcore.cpp
index e69de29..bbf8086 100644
--- a/qtmips_machine/tests/testcore.cpp
+++ b/qtmips_machine/tests/testcore.cpp
@@ -0,0 +1,35 @@
+#include "tst_machine.h"
+#include "core.h"
+
+void MachineTests::core_regs_data() {
+ /*
+ QTest::addColumn<Instruction>("i");
+ QTest::addColumn<Registers>("init");
+ QTest::addColumn<Registers>("res");
+
+ // Test arithmetic instructions
+ {
+ Registers regs_init();
+ regs_init.write_gp(24, 12);
+ regs_init.write_gp(25, 24);
+ Registers regs_res(&regs_init);
+ regs_res.write_gp(26, 36);
+ QTest::newRow("ADD") << Instruction(0, 24, 25, 26, 0, 32) \
+ << regs_init \
+ << regs_res;
+ }
+ */
+ // TODO test other operations
+}
+
+void MachineTests::core_regs() {
+
+}
+
+void MachineTests::core_mem_data() {
+
+}
+
+void MachineTests::core_mem() {
+
+}
diff --git a/qtmips_machine/tests/testinstruction.cpp b/qtmips_machine/tests/testinstruction.cpp
index e69de29..4efedac 100644
--- a/qtmips_machine/tests/testinstruction.cpp
+++ b/qtmips_machine/tests/testinstruction.cpp
@@ -0,0 +1,25 @@
+#include "tst_machine.h"
+#include "instruction.h"
+
+// Test that we are correctly encoding instructions in constructor
+void MachineTests::instruction() {
+ QCOMPARE(Instruction(0x00), Instruction(0,0));
+ QCOMPARE(Instruction(0x4000002), Instruction(1, 2));
+ // QCOMPARE(Instruction(0x4000002), Instruction(1, 2, 3, 4));
+ // TODO other combinations
+}
+
+// Test that we are correctly decoding instruction fields
+void MachineTests::instruction_access() {
+ Instruction i(0xffffffff);
+
+ QCOMPARE(i.data(), (std::uint32_t) 0xffffffff);
+ QCOMPARE(i.opcode(), (std::uint8_t) 0x3f);
+ QCOMPARE(i.rs(), (std::uint8_t) 0x1f);
+ QCOMPARE(i.rt(), (std::uint8_t) 0x1f);
+ QCOMPARE(i.rd(), (std::uint8_t) 0x1f);
+ QCOMPARE(i.shamt(), (std::uint8_t) 0x1f);
+ QCOMPARE(i.funct(), (std::uint8_t) 0x3f);
+ QCOMPARE(i.immediate(), (std::uint16_t) 0xffff);
+ QCOMPARE(i.address(), (std::uint32_t) 0x3ffffff);
+}
diff --git a/qtmips_machine/tests/testmemory.cpp b/qtmips_machine/tests/testmemory.cpp
index eac7dd6..e450231 100644
--- a/qtmips_machine/tests/testmemory.cpp
+++ b/qtmips_machine/tests/testmemory.cpp
@@ -13,21 +13,21 @@ void MachineTests::memory_data() {
void MachineTests::memory() {
Memory m;
- QFETCH(std::uint32_t, address);
+ QFETCH(std::uint32_t, address);
- // Uninitialize memory should read as zero
- QCOMPARE(m.read_byte(address), (std::uint8_t)0);
- QCOMPARE(m.read_hword(address), (std::uint16_t)0);
- QCOMPARE(m.read_word(address), (std::uint32_t)0);
- // Just a byte
- m.write_byte(address, 0x42);
- QCOMPARE(m.read_byte(address), (std::uint8_t)0x42);
- // Half word
- m.write_hword(address, 0x4243);
- QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243);
- // Word
- m.write_word(address, 0x42434445);
- QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445);
+ // Uninitialize memory should read as zero
+ QCOMPARE(m.read_byte(address), (std::uint8_t)0);
+ QCOMPARE(m.read_hword(address), (std::uint16_t)0);
+ QCOMPARE(m.read_word(address), (std::uint32_t)0);
+ // Just a byte
+ m.write_byte(address, 0x42);
+ QCOMPARE(m.read_byte(address), (std::uint8_t)0x42);
+ // Half word
+ m.write_hword(address, 0x4243);
+ QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243);
+ // Word
+ m.write_word(address, 0x42434445);
+ QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445);
}
void MachineTests::memory_section_data() {
@@ -40,24 +40,24 @@ void MachineTests::memory_section_data() {
}
void MachineTests::memory_section() {
- Memory m;
+ Memory m;
- QFETCH(std::uint32_t, address);
+ QFETCH(std::uint32_t, address);
- // First section shouldn't exists
- QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr);
- // Create section
- MemorySection *s = m.get_section(address, true);
- QVERIFY(s != nullptr);
+ // First section shouldn't exists
+ QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr);
+ // Create section
+ MemorySection *s = m.get_section(address, true);
+ QVERIFY(s != nullptr);
- // Write some data to memory
- m.write_byte(address, 0x42);
- // Read it trough section (mask bits outside of the memory section)
- QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42);
- // Write some other data trough section
- s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66);
- // Read trough memory
- QCOMPARE(m.read_byte(address), (std::uint8_t)0x66);
+ // Write some data to memory
+ m.write_byte(address, 0x42);
+ // Read it trough section (mask bits outside of the memory section)
+ QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42);
+ // Write some other data trough section
+ s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66);
+ // Read trough memory
+ QCOMPARE(m.read_byte(address), (std::uint8_t)0x66);
}
void MachineTests::memory_endian() {
diff --git a/qtmips_machine/tests/testprogramloader.cpp b/qtmips_machine/tests/testprogramloader.cpp
index e69de29..7ff1c54 100644
--- a/qtmips_machine/tests/testprogramloader.cpp
+++ b/qtmips_machine/tests/testprogramloader.cpp
@@ -0,0 +1,20 @@
+#include <iostream>
+#include "tst_machine.h"
+#include "programloader.h"
+#include "instruction.h"
+
+// This is common program start (initial value of program counter)
+#define PC_INIT 0x80020000
+
+void MachineTests::program_loader() {
+ ProgramLoader pl("data");
+ Memory m;
+ pl.to_memory(&m);
+
+ // addi $1, $0, 6
+ QCOMPARE(Instruction(m.read_word(PC_INIT)), Instruction(8, 0, 1, 6));
+ // j 80020000
+ // TODO wtf to je relativni skok asi tady
+ //QCOMPARE(Instruction(m.read_word(PC_INIT + 4)), Instruction(2, PC_INIT));
+ // TODO add some more code to data and do more compares (for example more sections)
+}
diff --git a/qtmips_machine/tests/tests.pro b/qtmips_machine/tests/tests.pro
index 287d1e8..ffe75b7 100644
--- a/qtmips_machine/tests/tests.pro
+++ b/qtmips_machine/tests/tests.pro
@@ -18,8 +18,10 @@ DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += tst_machine.cpp \
testmemory.cpp \
testregisters.cpp \
- testprogrammemory.cpp \
- testinstruction.cpp
+ testprogramloader.cpp \
+ testinstruction.cpp \
+ testalu.cpp \
+ testcore.cpp
HEADERS += tst_machine.h
diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h
index c05e8ad..da8082a 100644
--- a/qtmips_machine/tests/tst_machine.h
+++ b/qtmips_machine/tests/tst_machine.h
@@ -17,14 +17,21 @@ private Q_SLOTS:
void memory_section();
void memory_section_data();
void memory_endian();
- // ProgramMemory
- void program_memory();
- void program_memory_data();
+ // Program loader
+ void program_loader();
// Instruction
- void instruction_arithmetic();
- void instruction_arithmetic_data();
- void instruction_arithmetic_immediate();
- void instruction_arithmetic_immediate_data();
+ void instruction();
+ void instruction_access();
+ // Alu
+ void alu();
+ void alu_data();
+ void alu_except();
+ void alu_except_data();
+ // Core
+ void core_regs();
+ void core_regs_data();
+ void core_mem();
+ void core_mem_data();
};
#endif // TST_MACHINE_H
diff --git a/tests/machine-unit-tests/Makefile b/tests/machine-unit-tests/Makefile
new file mode 100644
index 0000000..18f7932
--- /dev/null
+++ b/tests/machine-unit-tests/Makefile
@@ -0,0 +1,5 @@
+include ../test.mk
+
+SRC_data = data.S
+
+$(eval $(call MIPS_ELF,data))
diff --git a/tests/machine-unit-tests/data.S b/tests/machine-unit-tests/data.S
new file mode 100644
index 0000000..d61d81d
--- /dev/null
+++ b/tests/machine-unit-tests/data.S
@@ -0,0 +1,6 @@
+.text
+.globl _start
+
+_start:
+ addi $1, $0, 6
+ j _start
diff --git a/tests/machine-unit-tests/test.sh b/tests/machine-unit-tests/test.sh
index 2f06965..8378eff 100755
--- a/tests/machine-unit-tests/test.sh
+++ b/tests/machine-unit-tests/test.sh
@@ -9,5 +9,7 @@ qtmips_make sub-qtmips_machine-tests
# Build test data
mips_make_test
+cd "$TEST_DIR"
+
# Run unit tests
qtmips_run qtmips_machine/tests/tst_machine || echo_fail "Test $TEST_NAME failed!"
diff --git a/tests/t-registers/Makefile b/tests/t-registers/Makefile
new file mode 100644
index 0000000..b8278d1
--- /dev/null
+++ b/tests/t-registers/Makefile
@@ -0,0 +1,5 @@
+MAKEFLAGS += --no-builtin-rules
+
+SRC = registers.S
+
+include ../test.mk
diff --git a/tests/test.sh b/tests/test.sh
index aad0f5a..34b42e3 100644
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -66,10 +66,11 @@ TEST_DIR="$TEST_DIR_ROOT/$TEST_NAME"
qtmips_make() {
mkdir -p "$BUILD_DIR"
- pushd "$BUILD_DIR" >/dev/null
+ local ORIG="$(pwd)"
+ cd "$BUILD_DIR"
/usr/lib64/qt5/bin/qmake "$PROJECT_ROOT" || echo_fail "QtMips qmake failed!"
make "$@" || echo_fail "QtMips build failed! (target: $@)"
- popd >/dev/null
+ cd "$ORIG"
}
qtmips_run() {