From cf97513e757cdd25e6b1dd1fd584e4ec13d93cb1 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Tue, 2 Jul 2019 12:22:05 +0200 Subject: Evaluation of symbolic expressions in assembler added. Signed-off-by: Pavel Pisa --- qtmips_gui/fixmatheval.cpp | 324 +++++++++++++++++++++++++++++++++++++++++ qtmips_gui/fixmatheval.h | 126 ++++++++++++++++ qtmips_gui/mainwindow.cpp | 64 +++++++- qtmips_gui/qtmips_gui.pro | 6 +- qtmips_machine/instruction.cpp | 24 +-- qtmips_machine/instruction.h | 7 +- 6 files changed, 532 insertions(+), 19 deletions(-) create mode 100644 qtmips_gui/fixmatheval.cpp create mode 100644 qtmips_gui/fixmatheval.h 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 + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#include "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 + * Copyright (c) 2019 Pavel Pisa + * + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************************/ + +#ifndef FIXMATHEVAL_H +#define FIXMATHEVAL_H + +#include + +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 diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp index d7ff37a..99ca26b 100644 --- a/qtmips_machine/instruction.cpp +++ b/qtmips_machine/instruction.cpp @@ -1040,7 +1040,8 @@ static int parse_reg_from_string(QString str, uint *chars_taken = nullptr) } static void reloc_append(RelocExpressionList *reloc, QString fl, uint32_t inst_addr, - std::int64_t offset, const ArgumentDesc *adesc, uint *chars_taken = nullptr) { + std::int64_t offset, const ArgumentDesc *adesc, uint *chars_taken = nullptr, + int line = 0) { uint bits = IMF_SUB_GET_BITS(adesc->loc); uint shift = IMF_SUB_GET_SHIFT(adesc->loc); QString expression = ""; @@ -1059,22 +1060,20 @@ static void reloc_append(RelocExpressionList *reloc, QString fl, uint32_t inst_a } reloc->append(new RelocExpression(inst_addr, expression, offset, - adesc->min, adesc->max, shift, bits, adesc->shift)); + adesc->min, adesc->max, shift, bits, adesc->shift, line)); if (chars_taken != nullptr) { *chars_taken = i; } } Instruction Instruction::from_string(QString str, bool *pok, uint32_t inst_addr, - RelocExpressionList *reloc) { + RelocExpressionList *reloc, int line) { std::uint32_t code; bool ok = false; if (str_to_instruction_code_map.isEmpty()) instruction_from_string_build_base(); - str = str.toUpper(); - QString inst_base = ""; QVector inst_fields(0); bool prev_white = true; @@ -1104,7 +1103,7 @@ Instruction Instruction::from_string(QString str, bool *pok, uint32_t inst_addr, error = true; break; } - inst_base = str.mid(l, k - l); + inst_base = str.mid(l, k - l).toUpper(); } else { if ((field && !comma) || (!field && comma)) { error = true; @@ -1195,7 +1194,7 @@ Instruction Instruction::from_string(QString str, bool *pok, uint32_t inst_addr, val += std::strtoull(p, &r, 0); chars_taken = r - p; } else { - reloc_append(reloc, fl, val, inst_addr, adesc, &chars_taken); + reloc_append(reloc, fl, inst_addr, val, adesc, &chars_taken, line); val = 0; } break; @@ -1212,7 +1211,7 @@ Instruction Instruction::from_string(QString str, bool *pok, uint32_t inst_addr, chars_taken = r - p; break; } else { - reloc_append(reloc, fl, val, inst_addr, adesc, &chars_taken); + reloc_append(reloc, fl, val, inst_addr, adesc, &chars_taken, line); val = 0; } } @@ -1257,9 +1256,16 @@ Instruction Instruction::from_string(QString str, bool *pok, uint32_t inst_addr, return Instruction(code); } - if (str == "NOP") + if (str.toUpper() == "NOP") ok = true; if (pok != nullptr) *pok = ok; return Instruction(0); } + +bool Instruction::update(std::int64_t val, RelocExpression *relocexp) { + std::int64_t mask = ((1 << relocexp->bits) - 1) << relocexp->lsb_bit; + dt &= ~ mask; + dt |= (((val + relocexp->offset) >> relocexp->shift) << relocexp->lsb_bit) & mask; + return true; +} diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h index f721835..d55b079 100644 --- a/qtmips_machine/instruction.h +++ b/qtmips_machine/instruction.h @@ -77,7 +77,7 @@ enum InstructionFlags { struct RelocExpression { inline RelocExpression(std::int32_t location, QString expression, std::int64_t offset, std::int64_t min, - std::int64_t max, unsigned lsb_bit, unsigned bits, unsigned shift) { + std::int64_t max, unsigned lsb_bit, unsigned bits, unsigned shift, int line) { this->location = location; this->expression = expression; this->offset = offset; @@ -86,6 +86,7 @@ struct RelocExpression { this->lsb_bit = lsb_bit; this->bits = bits; this->shift = shift; + this->line = line; } std::int32_t location; QString expression; @@ -95,6 +96,7 @@ struct RelocExpression { unsigned lsb_bit; unsigned bits; unsigned shift; + int line; }; typedef QVector RelocExpressionList; @@ -143,7 +145,8 @@ public: QString to_str(std::int32_t inst_addr = 0) const; static Instruction from_string(QString str, bool *pok = nullptr, - std::uint32_t inst_addr = 0, RelocExpressionList *reloc = nullptr); + std::uint32_t inst_addr = 0, RelocExpressionList *reloc = nullptr, int line = 0); + bool update(std::int64_t val, RelocExpression *relocexp); private: std::uint32_t dt; }; -- cgit v1.2.3