aboutsummaryrefslogtreecommitdiff
path: root/qtmips_gui
diff options
context:
space:
mode:
authorPavel Pisa <pisa@cmp.felk.cvut.cz>2019-07-02 12:22:05 +0200
committerPavel Pisa <pisa@cmp.felk.cvut.cz>2019-07-02 12:22:05 +0200
commitcf97513e757cdd25e6b1dd1fd584e4ec13d93cb1 (patch)
tree2fa12b7e17d460e3b9f39f024fcab863653b6823 /qtmips_gui
parentb62686b24fbb65d0810475e24aba7a5c4ee3e05e (diff)
downloadqtmips-cf97513e757cdd25e6b1dd1fd584e4ec13d93cb1.tar.gz
qtmips-cf97513e757cdd25e6b1dd1fd584e4ec13d93cb1.tar.bz2
qtmips-cf97513e757cdd25e6b1dd1fd584e4ec13d93cb1.zip
Evaluation of symbolic expressions in assembler added.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
Diffstat (limited to 'qtmips_gui')
-rw-r--r--qtmips_gui/fixmatheval.cpp324
-rw-r--r--qtmips_gui/fixmatheval.h126
-rw-r--r--qtmips_gui/mainwindow.cpp64
-rw-r--r--qtmips_gui/qtmips_gui.pro6
4 files changed, 512 insertions, 8 deletions
diff --git a/qtmips_gui/fixmatheval.cpp b/qtmips_gui/fixmatheval.cpp
new file mode 100644
index 0000000..8672cbc
--- /dev/null
+++ b/qtmips_gui/fixmatheval.cpp
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*******************************************************************************
+ * QtMips - MIPS 32-bit Architecture Subset Simulator
+ *
+ * Implemented to support following courses:
+ *
+ * B35APO - Computer Architectures
+ * https://cw.fel.cvut.cz/wiki/courses/b35apo
+ *
+ * B4M35PAP - Advanced Computer Architectures
+ * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start
+ *
+ * Copyright (c) 2017-2019 Karel Koci<cynerd@email.cz>
+ * Copyright (c) 2019 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ ******************************************************************************/
+
+#include "fixmatheval.h"
+
+#include "limits.h"
+
+using namespace fixmatheval;
+
+FmeSymbolDb::~FmeSymbolDb() {
+}
+
+bool FmeSymbolDb::getValue(FmeValue &value, QString name) {
+ (void)value;
+ (void)name;
+ return false;
+}
+
+FmeNode::FmeNode(int priority) {
+ prio = priority;
+}
+
+FmeNode::~FmeNode() {
+}
+
+int FmeNode::priority() {
+ return prio;
+}
+
+bool FmeNode::insert(FmeNode *node) {
+ (void)node;
+ return false;
+}
+
+FmeNode *FmeNode::child() {
+ return nullptr;
+}
+
+
+FmeNodeConstant::FmeNodeConstant(FmeValue value) : FmeNode(INT_MAX) {
+ this->value = value;
+}
+
+FmeNodeConstant::~FmeNodeConstant() {
+
+}
+
+bool FmeNodeConstant::eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) {
+ (void)symdb;
+ (void)error;
+ value = this->value;
+ return true;
+}
+
+QString FmeNodeConstant::dump() {
+ return QString::number(value);
+}
+
+FmeNodeSymbol::FmeNodeSymbol(QString &name) : FmeNode(INT_MAX) {
+ this->name = name;
+}
+
+FmeNodeSymbol::~FmeNodeSymbol() {
+}
+
+bool FmeNodeSymbol::eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) {
+ if (!symdb) {
+ error = QString("no symbol table to find value for %1").arg(name);
+ return false;
+ }
+ bool ok = symdb->getValue(value, name);
+ if (!ok)
+ error = QString("value for symbol \"%1\" not found").arg(name);
+ return ok;
+}
+
+QString FmeNodeSymbol::dump() {
+ return name;
+}
+
+FmeNodeUnaryOp::FmeNodeUnaryOp(int priority, FmeValue (*op)(FmeValue &a)) :
+ FmeNode(priority) {
+ this->operand_a = nullptr;
+ this->op = op;
+}
+
+FmeNodeUnaryOp::~FmeNodeUnaryOp() {
+ delete operand_a;
+}
+
+bool FmeNodeUnaryOp::FmeNodeUnaryOp::eval(FmeValue &value, FmeSymbolDb *symdb,
+ QString &error) {
+ FmeValue value_a;
+ if (!operand_a)
+ return false;
+ if (!operand_a->eval(value_a, symdb, error))
+ return false;
+ value = op(value_a);
+ return true;
+}
+
+FmeNode *FmeNodeUnaryOp::child() {
+ return operand_a;
+}
+
+bool FmeNodeUnaryOp::insert(FmeNode *node) {
+ operand_a = node;
+ return true;
+}
+
+QString FmeNodeUnaryOp::dump() {
+ return "(OP " + (operand_a? operand_a->dump(): "nullptr") + ")";
+}
+
+FmeNodeBinaryOp::FmeNodeBinaryOp(int priority, FmeValue (*op)(FmeValue &a, FmeValue &b),
+ FmeNode *left) : FmeNode(priority) {
+ this->operand_a = left;
+ this->operand_b = nullptr;
+ this->op = op;
+}
+
+FmeNodeBinaryOp::~FmeNodeBinaryOp() {
+ delete operand_a;
+ delete operand_b;
+}
+
+bool FmeNodeBinaryOp::eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) {
+ FmeValue value_a;
+ FmeValue value_b;
+ if (!operand_a || !operand_b)
+ return false;
+ if (!operand_a->eval(value_a, symdb, error) ||
+ !operand_b->eval(value_b, symdb, error))
+ return false;
+ value = op(value_a, value_b);
+ return true;
+}
+
+FmeNode *FmeNodeBinaryOp::child() {
+ return operand_b;
+}
+
+bool FmeNodeBinaryOp::insert(FmeNode *node) {
+ operand_b = node;
+ return true;
+}
+
+QString FmeNodeBinaryOp::dump() {
+ return "(" + (operand_a? operand_a->dump(): "nullptr") +
+ " OP " + (operand_b? operand_b->dump(): "nullptr") + ")";
+}
+
+FmeExpression::FmeExpression() : FmeNode(0) {
+ root = nullptr;
+}
+
+bool FmeExpression::parse(QString &expression, QString &error) {
+ delete root;
+ int base_prio = 100;
+ root = nullptr;
+ bool ok = true;
+ int i;
+ int word_start = 0;
+ bool in_word = false;
+ bool is_unary = true;
+ for (i = 0; true; i++) {
+ QChar ch = 0;
+ if (i < expression.size())
+ ch = expression.at(i);
+ if(!ch.isLetterOrNumber()|| (i >= expression.size())) {
+ if (in_word) {
+ FmeNode *new_node = nullptr;
+ QString word = expression.mid(word_start, i - word_start);
+ if (word.at(0).isDigit()) {
+ new_node = new FmeNodeConstant(word.toLongLong(&ok, 0));
+ if (!ok) {
+ error = QString("cannot convert \"%1\" to number").arg(word);
+ break;
+ }
+ } else {
+ new_node = new FmeNodeSymbol(word);
+ }
+ FmeNode *node;
+ FmeNode *child;
+ for (node = this; (child = node->child()) != nullptr; node = child);
+ ok = node->insert(new_node);
+ if (!ok) {
+ error = QString("parse stuck at \"%1\"").arg(word);
+ break;
+ }
+
+ in_word = false;
+ is_unary = false;
+ if (i >= expression.size())
+ break;
+ }
+ if (ch.isSpace())
+ continue;
+ FmeValue (*binary_op)(FmeValue &a, FmeValue &b) = nullptr;
+ FmeValue (*unary_op)(FmeValue &a) = nullptr;
+ int prio = base_prio;
+ if (ch == '-') {
+ if (is_unary) {
+ prio += 90;
+ unary_op = [](FmeValue &a) -> FmeValue { return -a; };
+ } else {
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a - b; };
+ prio += 20;
+ }
+ } else if (ch == '+') {
+ if (is_unary)
+ continue;
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a + b; };
+ prio += 20;
+ } else if (ch == '*') {
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a * b; };
+ prio += 30;
+ } else if (ch == '/') {
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a / b; };
+ prio += 30;
+ } else if (ch == '|') {
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a | b; };
+ prio += 10;
+ } else if (ch == '&') {
+ binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a & b; };
+ prio += 15;
+ } else if (ch == '(') {
+ base_prio += 100;
+ } else if (ch == ')') {
+ base_prio -= 100;
+ if (base_prio <= 0) {
+ ok = false;
+ error = QString("Unbalanced brackets.");
+ break;
+ }
+ } else {
+ error = QString("Unknow character \"%1\" in expression.").arg(ch);
+ ok = false;
+ break;
+ }
+ if ((binary_op != nullptr) || (unary_op != nullptr)) {
+ FmeNode *node;
+ FmeNode *child;
+ for (node = this; (child = node->child()) != nullptr; node = child) {
+ if (child->priority() >= prio)
+ break;
+ }
+ if (binary_op != nullptr) {
+ ok = node->insert(new FmeNodeBinaryOp(prio, binary_op, child));
+ is_unary = true;
+ } else {
+ ok = node->insert(new FmeNodeUnaryOp(prio, unary_op));
+ }
+ if (!ok) {
+ error = QString("parse stuck at \"%1\"").arg(QString(ch));
+ break;
+ }
+ }
+ } else {
+ if (!in_word)
+ word_start = i;
+ in_word = true;
+ }
+ }
+
+ return ok;
+}
+
+FmeExpression::~FmeExpression() {
+ delete root;
+ root = nullptr;
+}
+
+bool FmeExpression::eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) {
+ if (!root)
+ return false;
+ return root->eval(value, symdb, error);
+}
+
+bool FmeExpression::insert(FmeNode *node) {
+ root = node;
+ return true;
+}
+
+FmeNode *FmeExpression::child() {
+ return root;
+}
+
+QString FmeExpression::dump() {
+ return "(" + (root? root->dump(): "nullptr") + ")";
+}
+
diff --git a/qtmips_gui/fixmatheval.h b/qtmips_gui/fixmatheval.h
new file mode 100644
index 0000000..7712530
--- /dev/null
+++ b/qtmips_gui/fixmatheval.h
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*******************************************************************************
+ * QtMips - MIPS 32-bit Architecture Subset Simulator
+ *
+ * Implemented to support following courses:
+ *
+ * B35APO - Computer Architectures
+ * https://cw.fel.cvut.cz/wiki/courses/b35apo
+ *
+ * B4M35PAP - Advanced Computer Architectures
+ * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start
+ *
+ * Copyright (c) 2017-2019 Karel Koci<cynerd@email.cz>
+ * Copyright (c) 2019 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ ******************************************************************************/
+
+#ifndef FIXMATHEVAL_H
+#define FIXMATHEVAL_H
+
+#include <QString>
+
+namespace fixmatheval {
+
+typedef std::int64_t FmeValue;
+
+class FmeSymbolDb {
+public:
+ virtual ~FmeSymbolDb();
+ virtual bool getValue(FmeValue &value, QString name) = 0;
+};
+
+class FmeNode {
+public:
+ FmeNode(int priority);
+ virtual ~FmeNode();
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) = 0;
+ virtual bool insert(FmeNode *node);
+ virtual FmeNode *child();
+ virtual QString dump() = 0;
+ int priority();
+private:
+ int prio;
+};
+
+class FmeNodeConstant : public FmeNode {
+public:
+ FmeNodeConstant(FmeValue value);
+ virtual ~FmeNodeConstant();
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) override;
+ virtual QString dump() override;
+private:
+ FmeValue value;
+};
+
+class FmeNodeSymbol : public FmeNode {
+public:
+ FmeNodeSymbol(QString &name);
+ virtual ~FmeNodeSymbol();
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) override;
+ virtual QString dump() override;
+private:
+ QString name;
+};
+
+class FmeNodeUnaryOp : public FmeNode {
+public:
+ FmeNodeUnaryOp(int priority, FmeValue (*op)(FmeValue &a));
+ virtual ~FmeNodeUnaryOp();
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) override;
+ virtual bool insert(FmeNode *node) override;
+ virtual FmeNode *child() override;
+ virtual QString dump() override;
+private:
+ FmeValue (*op)(FmeValue &a);
+ FmeNode *operand_a;
+};
+
+class FmeNodeBinaryOp : public FmeNode {
+public:
+ FmeNodeBinaryOp(int priority, FmeValue (*op)(FmeValue &a, FmeValue &b), FmeNode *left);
+ virtual ~FmeNodeBinaryOp();
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) override;
+ virtual bool insert(FmeNode *node) override;
+ virtual FmeNode *child() override;
+ virtual QString dump() override;
+private:
+ FmeValue (*op)(FmeValue &a, FmeValue &b);
+ FmeNode *operand_a;
+ FmeNode *operand_b;
+};
+
+class FmeExpression : public FmeNode {
+public:
+ FmeExpression();
+ virtual ~FmeExpression();
+ virtual bool parse(QString &expression, QString &error);
+ virtual bool eval(FmeValue &value, FmeSymbolDb *symdb, QString &error) override;
+ virtual bool insert(FmeNode *node) override;
+ virtual FmeNode *child() override;
+ virtual QString dump() override;
+private:
+ FmeNode *root;
+};
+
+}
+
+#endif /*FIXMATHEVAL*/
diff --git a/qtmips_gui/mainwindow.cpp b/qtmips_gui/mainwindow.cpp
index 8a7956f..c9d89d9 100644
--- a/qtmips_gui/mainwindow.cpp
+++ b/qtmips_gui/mainwindow.cpp
@@ -49,6 +49,7 @@
#include "ossyscall.h"
#include "fontsize.h"
#include "gotosymboldialog.h"
+#include "fixmatheval.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
machine = nullptr;
@@ -546,6 +547,26 @@ void MainWindow::close_source() {
delete editor;
}
+class SymbolTableDb : public fixmatheval::FmeSymbolDb {
+public:
+ SymbolTableDb(const machine::SymbolTable *symtab);
+ virtual bool getValue(fixmatheval::FmeValue &value, QString name) override;
+private:
+ const machine::SymbolTable *symtab;
+};
+
+SymbolTableDb::SymbolTableDb(const machine::SymbolTable *symtab) {
+ this->symtab = symtab;
+}
+
+bool SymbolTableDb::getValue(fixmatheval::FmeValue &value, QString name) {
+ std::uint32_t val;
+ if (!symtab->name_to_value(val, name))
+ return false;
+ value = val;
+ return true;
+}
+
void MainWindow::compile_source() {
if (current_srceditor == nullptr)
return;
@@ -565,7 +586,7 @@ void MainWindow::compile_source() {
machine::RelocExpressionList reloc;
int ln = 1;
- bool ok;
+ bool ok = true;
for ( QTextBlock block = doc->begin(); block.isValid(); block = block.next(), ln++) {
int pos;
QString label = "";
@@ -583,16 +604,47 @@ void MainWindow::compile_source() {
line = line.mid(pos + 1).trimmed();
machine->set_symbol(label, address, 4);
}
+ if (line.isEmpty())
+ continue;
machine::Instruction inst;
- inst = machine::Instruction::from_string(line, &ok, address, &reloc);
+ inst = machine::Instruction::from_string(line, &ok, address, &reloc, ln);
+ if (!ok) {
+ QMessageBox::critical(this, "QtMips Error",
+ tr("line %1 instruction %2 parse error.")
+ .arg(QString::number(ln), line));
+ break;
+ }
mem->write_word(address, inst.data());
address += 4;
}
+ SymbolTableDb symtab(machine->symbol_table());
foreach(machine::RelocExpression *r, reloc) {
- QString e = r->expression;
-
-
- delete r;
+ QString error;
+ fixmatheval::FmeExpression expression;
+ if (ok && !expression.parse(r->expression, error)) {
+ QMessageBox::critical(this, "QtMips Error",
+ tr("expression parse error %1 at line %2, expression %3.")
+ .arg(error, QString::number(r->line), expression.dump()));
+ ok = false;
+ } else {
+ fixmatheval::FmeValue value;
+ if (ok && !expression.eval(value, &symtab, error)) {
+ QMessageBox::critical(this, "QtMips Error",
+ tr("expression evalution error %1 at line %2 , expression %3.")
+ .arg(error, QString::number(r->line), expression.dump()));
+ ok = false;
+ } else {
+ if (false)
+ QMessageBox::information(this, "QtMips info",
+ expression.dump() + " -> " + QString::number(value));
+ machine::Instruction inst(mem->read_word(r->location, true));
+ inst.update(value, r);
+ mem->write_word(r->location, inst.data());
+ }
+ }
+ }
+ while (!reloc.isEmpty()) {
+ delete reloc.takeFirst();
}
emit mem->external_change_notify(mem, 0, 0xffffffff, true);
diff --git a/qtmips_gui/qtmips_gui.pro b/qtmips_gui/qtmips_gui.pro
index 109503b..1075f96 100644
--- a/qtmips_gui/qtmips_gui.pro
+++ b/qtmips_gui/qtmips_gui.pro
@@ -73,7 +73,8 @@ SOURCES += \
cop0dock.cpp \
hinttabledelegate.cpp \
coreview/minimux.cpp \
- srceditor.cpp
+ srceditor.cpp \
+ fixmatheval.cpp
HEADERS += \
mainwindow.h \
@@ -117,7 +118,8 @@ HEADERS += \
cop0dock.h \
hinttabledelegate.h \
coreview/minimux.h \
- srceditor.h
+ srceditor.h \
+ fixmatheval.h
wasm: SOURCES += \
qhtml5file_html5.cpp