Finalize syntax checking & label validation
MODIFY
src/addressdialog.ui
src/addressdialog.ui
</property>
</widget>
</item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>0x</string>
+ </property>
+ </widget>
+ </item>
<item>
<widget class="QLineEdit" name="address"/>
</item>
</layout>
</item>
+ </layout>
+ </item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

MODIFY
src/asmhighlighter.cpp
src/asmhighlighter.cpp
#include "defines.h"
-FieldType::FieldType(Type type, int lowerBound, int upperBound) {
+FieldType::FieldType(Type type, int lowerBound, int upperBound, AsmHighlighter* highlighter) {
m_type = type;
m_lowerBound = lowerBound;
m_upperBound = upperBound;
+ m_highlighter = highlighter;
}
QString FieldType::validateField(const QString& field) const {
- const static auto registerRegex = QRegularExpression("\\b[(a|s|t|x)][0-9]{1,2}");
-
switch (m_type) {
case Type::Immediate: {
// Check that immediate can be converted
@@ -36,15 +35,20 @@ QString FieldType::validateField(const QString& field) const {
}
}
case Type::Register: {
- if (!ABInames.contains(field) &&
- !registerRegex.match(field, 0, QRegularExpression::NormalMatch).hasMatch()) {
- return QString("Register %1 is unrecognized").arg(field);
- } else {
+ if (ABInames.contains(field) || RegNames.contains(field)) {
return QString();
+ } else {
+ return QString("Register %1 is unrecognized").arg(field);
}
}
case Type::Offset: {
+ // Check if label is defined in highlighter
+ Q_ASSERT(m_highlighter != nullptr);
+ if (m_highlighter->m_labelPosMap.contains(field)) {
return QString();
+ } else {
+ return QString("label \"%1\" is undefined").arg(field);
+ }
}
}
return QString("Validation error");
@@ -207,9 +211,10 @@ void AsmHighlighter::createSyntaxRules() {
// call
types.clear();
- types << FieldType(Type::Offset);
+ types << FieldType(Type::Offset, 0, 0, this);
rule.instr = "call";
rule.fields = 2;
+ rule.inputs = types;
m_syntaxRules.insert(rule.instr, rule);
// jr
@@ -217,13 +222,37 @@ void AsmHighlighter::createSyntaxRules() {
types << FieldType(Type::Register);
rule.instr = "jr";
rule.fields = 2;
+ rule.inputs = types;
+ m_syntaxRules.insert(rule.instr, rule);
+
+ // jalr -- pseudo and regular op.
+ types.clear();
+ types << FieldType(Type::Register) << FieldType(Type::Register) << FieldType(Type::Offset, 0, 0, this);
+ rule.instr = "jalr";
+ rule.fields = 4;
+ rule.fields2 = 2;
+ rule.inputs = types;
m_syntaxRules.insert(rule.instr, rule);
+ rule.fields2 = -1;
+
+ // jal -- pseudo and regular op
+ types.clear();
+ types << FieldType(Type::Register) << FieldType(Type::Offset, 0, 0, this);
+ rule.instr = "jal";
+ rule.fields = 3;
+ rule.fields2 = 2;
+ rule.inputs = types;
+ rule.skipFirstField = true; // jal can be written as "jal offset" - allow skipping of first field
+ m_syntaxRules.insert(rule.instr, rule);
+ rule.skipFirstField = false; // revert
+ rule.fields2 = -1;
// j
types.clear();
- types << FieldType(Type::Register);
+ types << FieldType(Type::Offset, 0, 0, this);
rule.instr = "j";
rule.fields = 2;
+ rule.inputs = types;
m_syntaxRules.insert(rule.instr, rule);
// li
@@ -257,7 +286,7 @@ void AsmHighlighter::createSyntaxRules() {
// Branch instructions
types.clear();
names.clear();
- types << FieldType(Type::Register) << FieldType(Type::Register) << FieldType(Type::Offset);
+ types << FieldType(Type::Register) << FieldType(Type::Register) << FieldType(Type::Offset, 0, 0, this);
names << "beg"
<< "bne"
<< "blt"
@@ -276,7 +305,7 @@ void AsmHighlighter::createSyntaxRules() {
// Branch pseudo-instructions
types.clear();
names.clear();
- types << FieldType(Type::Register) << FieldType(Type::Offset);
+ types << FieldType(Type::Register) << FieldType(Type::Offset, 0, 0, this);
names << "begz"
<< "bnez"
<< "blez"
@@ -384,7 +413,6 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
// duplicate label found
if (pos < m_labelPosMap[string]) {
// Label is redefined before previous use of label
- int prevPos = m_labelPosMap[string];
m_posLabelMap.remove(m_labelPosMap[string]);
m_labelPosMap[string] = pos;
m_posLabelMap[pos] = string;
@@ -399,14 +427,6 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
}
return QString();
} else {
- // remove label at given position
- if (m_posLabelMap.contains(pos)) {
- /*
- m_labelPosMap.remove(m_posLabelMap[pos]);
- m_posLabelMap.remove(pos);
-*/
- }
-
// Check for assembler directives
if (string[0] == '.') {
string = string.remove('.');
@@ -421,17 +441,27 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
// Validate remaining fields
if (fields.size() > 0) {
- auto rule = m_syntaxRules.find(fields[0]);
- if (rule != m_syntaxRules.end()) {
+ auto ruleIter = m_syntaxRules.find(fields[0]);
+ if (ruleIter != m_syntaxRules.end()) {
// A rule for the instruction has been found
- if (fields.size() == (*rule).fields) {
+ const SyntaxRule& rule = *ruleIter;
+ if (fields.size() == rule.fields || (rule.fields2 != -1 && fields.size() == rule.fields2)) {
// fields size is correct, check each instruction
- int index = 1;
- for (const auto& input : (*rule).inputs) {
- QString res = input.validateField(fields[index]);
+ int nFields = fields.size();
+ for (int index = 1; index < nFields; index++) {
+ // Get input rule - skipFirstField is a special case handler for jal pseudo-op when only 2 fields
+ // are available
+ auto inputRule =
+ rule.skipFirstField && nFields == rule.fields2 ? rule.inputs[index] : rule.inputs[index - 1];
+ // If an offset is used, store line number, to re-update after all variable declarations have been
+ // mapped (after syntax checking the entire document)
+ if (inputRule.m_type == Type::Offset) {
+ m_rowsUsingLabels << pos;
+ }
+ QString res = inputRule.validateField(fields[index]);
if (res == QString()) {
// continue
- index++;
+ // index++;
} else {
return res;
}
@@ -447,10 +477,19 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
return QString();
}
-void AsmHighlighter::clearAndRehighlight() {
+void AsmHighlighter::reset() {
m_labelPosMap.clear();
m_posLabelMap.clear();
+ m_rowsUsingLabels.clear();
+}
+
+void AsmHighlighter::clearAndRehighlight() {
+ reset();
rehighlight();
+ for (const auto& row : m_rowsUsingLabels) {
+ // Rehighlight rows which use labels, now that all labels have been recorded
+ rehighlightBlock(document()->findBlockByLineNumber(row));
+ }
}
void AsmHighlighter::invalidateLabels(const QTextCursor& cursor) {

MODIFY
src/asmhighlighter.h
src/asmhighlighter.h
@@ -13,23 +13,29 @@ namespace {
enum class Type { Immediate, Register, Offset };
}
+class AsmHighlighter;
class FieldType {
// Class for defining field-specific rules, such as immediate range checking,
// and whether a register is recognized
public:
- explicit FieldType(Type type, int lowerBound = 0, int upperBound = 0);
+ explicit FieldType(Type type, int lowerBound = 0, int upperBound = 0, AsmHighlighter* highlighter = nullptr);
QString validateField(const QString& field) const;
Type m_type;
int m_lowerBound;
int m_upperBound;
+
+ AsmHighlighter* m_highlighter;
};
class AsmHighlighter : public QSyntaxHighlighter {
Q_OBJECT
+ friend class FieldType;
+
public:
explicit AsmHighlighter(QTextDocument* parent = nullptr);
void highlightBlock(const QString& text);
+ void reset();
QString checkSyntax(const QString& line);
@@ -46,6 +52,8 @@ private:
struct SyntaxRule {
QString instr;
int fields; // n instruction fields, including the instruction
+ int fields2; // jal and jalr can be 2 different ops (pseudo- and base op)
+ bool skipFirstField = false; // jal needs to skip its first operand for its pseudo-op
QList<FieldType> inputs; // list of each accepted input for the instruction, in order
};
@@ -62,9 +70,10 @@ private:
QTextCharFormat commentFormat;
QTextCharFormat errorFormat;
- QMap<int, QString> m_errors;
QMap<QString, int> m_labelPosMap;
QMap<int, QString> m_posLabelMap;
+ QSet<int> m_rowsUsingLabels; // List of lines that are rehighlighted once the main syntax checking is done -
+ // needed for supporting labels that are declared after usage
public slots:
void invalidateLabels(const QTextCursor&);

MODIFY
src/codeeditor.cpp
src/codeeditor.cpp
@@ -21,11 +21,6 @@ CodeEditor::CodeEditor(QWidget* parent) : QPlainTextEdit(parent) {
connect(this, SIGNAL(updateRequest(QRect, int)), this, SLOT(updateSidebar(QRect, int)));
- // Connect runner PC to line hightlighting
- // connect(this, SIGNAL(cursorPositionChanged()), this,
- // SLOT(highlightCurrentLine()));
- // highlightCurrentLine();
-
updateSidebarWidth(0);
// Set font for the entire widget. calls to fontMetrics() will get the
@@ -39,20 +34,10 @@ CodeEditor::CodeEditor(QWidget* parent) : QPlainTextEdit(parent) {
// set event filter for catching scroll events
installEventFilter(this);
- // Set syntax highlighter
- m_highlighter = new AsmHighlighter(document());
-
// needed for instant tooltip displaying
setMouseTracking(true);
setWordWrapMode(QTextOption::NoWrap);
-
- // connect tooltip changes from asm highlighter
- connect(m_highlighter, &AsmHighlighter::setTooltip, this, &CodeEditor::updateTooltip);
-
- // The highlighting is reset upon line count changes, to detect label invalidation
- connect(this->document(), &QTextDocument::cursorPositionChanged, m_highlighter, &AsmHighlighter::invalidateLabels);
- connect(this->document(), &QTextDocument::blockCountChanged, m_highlighter, &AsmHighlighter::clearAndRehighlight);
}
int CodeEditor::lineNumberAreaWidth() {
@@ -100,8 +85,13 @@ bool CodeEditor::eventFilter(QObject* /*observed*/, QEvent* event) {
void CodeEditor::updateTooltip(int line, QString tip) {
// Connects to AsmHighlighter::setTooltip
+ if (tip == QString()) {
+ // unset tooltip - accepted syntax at line
+ m_tooltipForLine.remove(line);
+ } else {
m_tooltipForLine[line] = tip;
}
+}
bool CodeEditor::event(QEvent* event) {
// Override event handler for receiving tool tips
@@ -121,6 +111,12 @@ bool CodeEditor::event(QEvent* event) {
return QPlainTextEdit::event(event);
}
+void CodeEditor::enableBreakpointArea() {
+ // Enables breakpoint area, and sets cursor for the breakpoint area to a "clickable" cursor
+ m_breakpointAreaEnabled = true;
+ m_breakpointArea->setCursor(Qt::PointingHandCursor);
+}
+
void CodeEditor::updateSidebar(const QRect& rect, int dy) {
if (dy) {
m_lineNumberArea->scroll(0, dy);
@@ -198,8 +194,13 @@ void CodeEditor::breakpointAreaPaintEvent(QPaintEvent* event) {
// redrawing the visible breakpoint area
auto area = m_breakpointArea->rect();
QLinearGradient gradient = QLinearGradient(area.topLeft(), area.bottomRight());
+ if (m_breakpointAreaEnabled) {
gradient.setColorAt(0, QColor(Colors::FoundersRock).lighter(120));
gradient.setColorAt(1, QColor(Colors::FoundersRock));
+ } else {
+ gradient.setColorAt(0, QColor(Qt::lightGray).lighter(120));
+ gradient.setColorAt(1, QColor(Qt::lightGray).lighter(120));
+ }
painter.fillRect(area, gradient);
QTextBlock block = firstVisibleBlock();
@@ -222,7 +223,19 @@ void CodeEditor::breakpointAreaPaintEvent(QPaintEvent* event) {
}
}
+void CodeEditor::setupSyntaxHighlighter() {
+ // Creates AsmHighlighter object and connects it to the current document
+ m_highlighter = new AsmHighlighter(document());
+ // connect tooltip changes from asm highlighter
+ connect(m_highlighter, &AsmHighlighter::setTooltip, this, &CodeEditor::updateTooltip);
+
+ // The highlighting is reset upon line count changes, to detect label invalidation
+ connect(this->document(), &QTextDocument::cursorPositionChanged, m_highlighter, &AsmHighlighter::invalidateLabels);
+ connect(this->document(), &QTextDocument::blockCountChanged, m_highlighter, &AsmHighlighter::clearAndRehighlight);
+}
+
void CodeEditor::breakpointClick(QMouseEvent* event, int forceState) {
+ if (m_breakpointAreaEnabled) {
// Get line height
QTextBlock block = firstVisibleBlock();
auto height = blockBoundingRect(block).height();
@@ -259,12 +272,12 @@ void CodeEditor::breakpointClick(QMouseEvent* event, int forceState) {
repaint();
}
}
+}
// -------------- breakpoint area ----------------------------------
BreakpointArea::BreakpointArea(CodeEditor* editor) : QWidget(editor) {
codeEditor = editor;
- setCursor(Qt::PointingHandCursor);
// Create and connect actions for removing and setting breakpoints
m_removeAction = new QAction("Remove breakpoint", this);

MODIFY
src/codeeditor.h
src/codeeditor.h
@@ -26,6 +26,11 @@ public:
void breakpointClick(QMouseEvent* event, int forceState = 0);
void clearBreakpoints() { m_breakpoints.clear(); }
int lineNumberAreaWidth();
+ void setupSyntaxHighlighter();
+ void enableBreakpointArea();
+ void reset() { m_highlighter->reset(); }
+signals:
+ void readyToTranslate(); // Emitted when syntax has been accepted, and the input text can be translated to
protected:
void resizeEvent(QResizeEvent* event) override;
@@ -43,6 +48,9 @@ private:
BreakpointArea* m_breakpointArea;
int m_sidebarWidth;
+ bool m_syntaxChecking = false;
+ bool m_breakpointAreaEnabled = false;
+
std::set<int> m_breakpoints;
QMap<int, QString> m_tooltipForLine;

MODIFY
src/defines.h
src/defines.h
@@ -95,8 +95,7 @@ const static QStringList ABInames = QStringList() << "zero"
<< "t0"
<< "t1"
<< "t2"
- << "s0/fp"
- << "s1"
+ << "s0"
<< "a0"
<< "a1"
<< "a2"
@@ -105,6 +104,7 @@ const static QStringList ABInames = QStringList() << "zero"
<< "a5"
<< "a6"
<< "a7"
+ << "s1"
<< "s2"
<< "s3"
<< "s4"
@@ -120,6 +120,39 @@ const static QStringList ABInames = QStringList() << "zero"
<< "t5"
<< "t6";
+const static QStringList RegNames = QStringList() << "x0"
+ << "x1"
+ << "x2"
+ << "x3"
+ << "x4"
+ << "x5"
+ << "x6"
+ << "x7"
+ << "x8"
+ << "x9"
+ << "x10"
+ << "x11"
+ << "x12"
+ << "x13"
+ << "x14"
+ << "x15"
+ << "x16"
+ << "x17"
+ << "x18"
+ << "x19"
+ << "x20"
+ << "x21"
+ << "x22"
+ << "x23"
+ << "x24"
+ << "x25"
+ << "x26"
+ << "x27"
+ << "x28"
+ << "x29"
+ << "x30"
+ << "x31";
+
const static std::map<int, QString> cacheSizes = {{32, QString("32 Bytes")}, {64, QString("64 Bytes")},
{128, QString("128 Bytes")}, {256, {QString("256 Bytes")}},
{512, QString("512 Bytes")}, {1024, QString("1024 Bytes")}};

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -22,7 +22,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
showMaximized();
// Setup tab bar
- m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/binary-code.svg")), "Code");
+ m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/binary-code.svg")), "Editor");
m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/cpu.svg")), "Processor");
m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/ram-memory.svg")), "Memory");
// m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/server.svg")), "Cache");
@@ -109,7 +109,7 @@ void MainWindow::on_actionLoadAssemblyFile_triggered() {
void MainWindow::loadBinaryFile(QString filename) {
const QString& output = Parser::getParser()->loadBinaryFile(filename);
- m_ui->programfiletab->setBinaryText(output);
+ m_ui->programfiletab->setDisassemblerText(output);
emit update();
}

MODIFY
src/memorytab.ui
src/memorytab.ui
<item>
<widget class="QLabel" name="label_2">
<property name="text">
- <string>Display type</string>
+ <string>Display type:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
- <string>Go to</string>
+ <string>Go to:</string>
</property>
</widget>
</item>

MODIFY
src/parser.cpp
src/parser.cpp
@@ -68,10 +68,13 @@ const QString& Parser::loadBinaryFile(QString fileName) {
for (int i = 0; i < length; i += 4) {
in.readRawData(buffer, 4);
for (int j = 0; j < 4; j++) {
- output.append(QString().setNum((uint8_t)buffer[j], 2).rightJustified(8, '0'));
+ // output.append(QString().setNum((uint8_t)buffer[j], 2).rightJustified(8, '0'));
memPtr->write(pc, buffer[j], 1);
pc++;
}
+ uint32_t instr =
+ (buffer[3] & 0xff) << 24 | (buffer[2] & 0xff) << 16 | (buffer[1] & 0xff) << 8 | (buffer[0] & 0xff);
+ output.append(genStringRepr(instr));
output.append("\n");
}
m_binaryRepr = output;

MODIFY
src/programfiletab.cpp
src/programfiletab.cpp
ProgramfileTab::ProgramfileTab(QWidget* parent) : QWidget(parent), m_ui(new Ui::ProgramfileTab) {
m_ui->setupUi(this);
+
+ // Only add syntax highlighter for code edit view - not for translated code. This is assumed to be correct after a
+ // translation is complete
+ m_ui->assemblyedit->setupSyntaxHighlighter();
+ m_ui->binaryedit->setReadOnly(true);
+ // enable breakpoint area for the translated code only
+ m_ui->binaryedit->enableBreakpointArea();
}
ProgramfileTab::~ProgramfileTab() {
@@ -19,9 +26,11 @@ void ProgramfileTab::on_pushButton_clicked() {
}
void ProgramfileTab::setAssemblyText(const QString& text) {
+ m_ui->assemblyedit->reset();
m_ui->assemblyedit->setPlainText(text);
}
-void ProgramfileTab::setBinaryText(const QString& text) {
+void ProgramfileTab::setDisassemblerText(const QString& text) {
+ m_ui->binaryedit->clearBreakpoints();
m_ui->binaryedit->setPlainText(text);
}

MODIFY
src/programfiletab.h
src/programfiletab.h
@@ -16,7 +16,7 @@ public:
~ProgramfileTab();
void setAssemblyText(const QString& text);
- void setBinaryText(const QString& text);
+ void setDisassemblerText(const QString& text);
signals:
void loadBinaryFile();

MODIFY
src/registercontainerwidget.ui
src/registercontainerwidget.ui
<item>
<widget class="QLabel" name="label">
<property name="text">
- <string>Display type</string>
+ <string>Display type:</string>
</property>
</widget>
</item>

Finalize syntax checking & label validation