aboutsummaryrefslogtreecommitdiff
path: root/qtmips_machine
diff options
context:
space:
mode:
authorKarel Kočí <cynerd@email.cz>2017-08-30 21:37:53 +0200
committerKarel Kočí <cynerd@email.cz>2017-08-30 21:42:02 +0200
commit9cf92379d5fcf0076c25dae0935daab446c992cd (patch)
treedd09a2e996db1e5a8117f01bec76f1e93eaca6e1 /qtmips_machine
downloadqtmips-9cf92379d5fcf0076c25dae0935daab446c992cd.tar.gz
qtmips-9cf92379d5fcf0076c25dae0935daab446c992cd.tar.bz2
qtmips-9cf92379d5fcf0076c25dae0935daab446c992cd.zip
Initial commit
Adding work done so far.
Diffstat (limited to 'qtmips_machine')
-rw-r--r--qtmips_machine/cache.cpp1
-rw-r--r--qtmips_machine/cache.h18
-rw-r--r--qtmips_machine/core.cpp5
-rw-r--r--qtmips_machine/core.h21
-rw-r--r--qtmips_machine/instruction.cpp81
-rw-r--r--qtmips_machine/instruction.h48
-rw-r--r--qtmips_machine/instructions/arithmetic.cpp87
-rw-r--r--qtmips_machine/instructions/arithmetic.h46
-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/memory.cpp137
-rw-r--r--qtmips_machine/memory.h60
-rw-r--r--qtmips_machine/programloader.cpp81
-rw-r--r--qtmips_machine/programloader.h28
-rw-r--r--qtmips_machine/programmemory.cpp111
-rw-r--r--qtmips_machine/programmemory.h22
-rw-r--r--qtmips_machine/qtmips_machine.pro46
-rw-r--r--qtmips_machine/qtmips_machine_global.h12
-rw-r--r--qtmips_machine/qtmipsexception.cpp60
-rw-r--r--qtmips_machine/qtmipsexception.h63
-rw-r--r--qtmips_machine/qtmipsmachine.cpp5
-rw-r--r--qtmips_machine/qtmipsmachine.h25
-rw-r--r--qtmips_machine/registers.cpp66
-rw-r--r--qtmips_machine/registers.h31
-rw-r--r--qtmips_machine/tests/testmemory.cpp61
-rw-r--r--qtmips_machine/tests/testregisters.cpp39
-rw-r--r--qtmips_machine/tests/tests.pro24
-rw-r--r--qtmips_machine/tests/tst_machine.cpp3
-rw-r--r--qtmips_machine/tests/tst_machine.h21
-rw-r--r--qtmips_machine/utils.cpp30
-rw-r--r--qtmips_machine/utils.h13
37 files changed, 1505 insertions, 0 deletions
diff --git a/qtmips_machine/cache.cpp b/qtmips_machine/cache.cpp
new file mode 100644
index 0000000..05b26b0
--- /dev/null
+++ b/qtmips_machine/cache.cpp
@@ -0,0 +1 @@
+#include "cache.h"
diff --git a/qtmips_machine/cache.h b/qtmips_machine/cache.h
new file mode 100644
index 0000000..280ac05
--- /dev/null
+++ b/qtmips_machine/cache.h
@@ -0,0 +1,18 @@
+#ifndef CACHE_H
+#define CACHE_H
+
+#include "memory.h"
+
+class Cache : public MemoryAccess {
+public:
+ Cache(Memory *m);
+};
+
+class CacheAssociative : public Cache {
+public:
+ CacheAssociative(Memory *m);
+};
+
+// TODO other chaches
+
+#endif // CACHE_H
diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp
new file mode 100644
index 0000000..a6c92b5
--- /dev/null
+++ b/qtmips_machine/core.cpp
@@ -0,0 +1,5 @@
+#include "core.h"
+
+Core::Core() {
+
+}
diff --git a/qtmips_machine/core.h b/qtmips_machine/core.h
new file mode 100644
index 0000000..2fd0a40
--- /dev/null
+++ b/qtmips_machine/core.h
@@ -0,0 +1,21 @@
+#ifndef CORE_H
+#define CORE_H
+
+#include <QObject>
+#include "instruction.h"
+#include "registers.h"
+#include "memory.h"
+#include "programloader.h"
+#include "programmemory.h"
+
+class Core : public QObject {
+ Q_OBJECT
+public:
+ Core();
+
+signals:
+
+public slots:
+};
+
+#endif // CORE_H
diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp
new file mode 100644
index 0000000..159d443
--- /dev/null
+++ b/qtmips_machine/instruction.cpp
@@ -0,0 +1,81 @@
+#include "instruction.h"
+#include <sstream>
+#include <iostream>
+
+InstructionR::InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa) {
+ this->rs = rs;
+ this->rd = rd;
+ this->rt = rt;
+ this->sa = sa;
+}
+
+// TODO for registers output as register ($0)!
+
+std::vector<std::string> InstructionR::to_strs() {
+ std::vector<std::string> str;
+ // Instruction name
+ str.push_back("unknown"); // unknown instruction, should be replaced by child
+
+ std::stringstream ss;
+ // Source register
+ ss << std::hex << (unsigned) this->rs;
+ str.push_back(ss.str());
+ ss.str("");
+ // Target register
+ ss << std::hex << (unsigned) this->rt;
+ str.push_back(ss.str());
+ ss.str("");
+ // Destination register
+ ss << std::hex << (unsigned) this->rd;
+ str.push_back(ss.str());
+ ss.str("");
+ // Shift amount
+ ss << std::hex << (unsigned) this->sa;
+ str.push_back(ss.str());
+
+ return str;
+}
+
+InstructionI::InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) {
+ this->rs = rs;
+ this->rt = rt;
+ this->immediage = immediate;
+}
+
+std::vector<std::string> InstructionI::to_strs() {
+ std::vector<std::string> str;
+ // Instruction name
+ str.push_back("unknown"); // unknown instruction, should be replaced by child
+
+ std::stringstream ss;
+ // Source register
+ ss << std::hex << (unsigned) this->rs;
+ str.push_back(ss.str());
+ ss.str("");
+ // Destination register
+ ss << std::hex << (unsigned) this->rt;
+ str.push_back(ss.str());
+ ss.str("");
+ // Immediate value
+ ss << std::hex << (unsigned) this->immediage;
+ str.push_back(ss.str());
+
+ return str;
+}
+
+InstructionJ::InstructionJ(std::uint32_t address) {
+ this->address = address;
+}
+
+std::vector<std::string> InstructionJ::to_strs() {
+ std::vector<std::string> str;
+ // Instruction name
+ str.push_back("unknown"); // unknown instruction, should be replaced by child
+
+ std::stringstream ss;
+ // Source register
+ ss << std::hex << (unsigned) this->address;
+ str.push_back(ss.str());
+
+ return str;
+}
diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h
new file mode 100644
index 0000000..6ab7015
--- /dev/null
+++ b/qtmips_machine/instruction.h
@@ -0,0 +1,48 @@
+#ifndef INSTRUCTION_H
+#define INSTRUCTION_H
+
+#include <vector>
+#include <string>
+#include "registers.h"
+#include "memory.h"
+
+class Instruction {
+public:
+ // TODO return types should be according to what instruction can pass from this stage
+ //virtual void decode(Registers *regs) = 0; // Read and prepare instructions
+ //virtual void execute() = 0; // ALU operations
+ //virtual void memory(Memory *mem) = 0; // Read or write to memory
+ //virtual void write_back(Registers *regs) = 0; // Write results to registers
+
+ virtual std::vector<std::string> to_strs() = 0; // Returns all fields of instructions in string
+};
+
+class InstructionR : public Instruction {
+public:
+ InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa);
+
+ std::vector<std::string> 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);
+
+ std::vector<std::string> to_strs();
+protected:
+ std::uint8_t rs, rt;
+ std::uint16_t immediage;
+};
+
+class InstructionJ : public Instruction {
+public:
+ InstructionJ(std::uint32_t address);
+
+ std::vector<std::string> to_strs();
+protected:
+ std::uint32_t address;
+};
+
+#endif // INSTRUCTION_H
diff --git a/qtmips_machine/instructions/arithmetic.cpp b/qtmips_machine/instructions/arithmetic.cpp
new file mode 100644
index 0000000..8c8f40e
--- /dev/null
+++ b/qtmips_machine/instructions/arithmetic.cpp
@@ -0,0 +1,87 @@
+#include "instructions/arithmetic.h"
+#include <iostream>
+
+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;
+}
+
+std::vector<std::string> InstructionArithmetic::to_strs() {
+ std::vector<std::string> 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;
+}
+
+std::vector<std::string> InstructionArithmeticImmediate::to_strs() {
+ std::vector<std::string> 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] = "lu";
+ break;
+ default:
+ // TODO different exception
+ throw std::exception();
+ }
+ return str;
+}
diff --git a/qtmips_machine/instructions/arithmetic.h b/qtmips_machine/instructions/arithmetic.h
new file mode 100644
index 0000000..29f89d2
--- /dev/null
+++ b/qtmips_machine/instructions/arithmetic.h
@@ -0,0 +1,46 @@
+#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);
+ std::vector<std::string> to_strs();
+private:
+ enum InstructionArithmeticT type;
+};
+
+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);
+ std::vector<std::string> to_strs();
+private:
+ enum InstructionArithmeticImmediateT type;
+};
+
+#endif // ARITHMETIC_H
diff --git a/qtmips_machine/instructions/jumpbranch.cpp b/qtmips_machine/instructions/jumpbranch.cpp
new file mode 100644
index 0000000..2ede399
--- /dev/null
+++ b/qtmips_machine/instructions/jumpbranch.cpp
@@ -0,0 +1,30 @@
+#include "jumpbranch.h"
+
+InstructionJump::InstructionJump(bool link, std::uint32_t address)
+ : InstructionJ(address) {
+ this->link = link;
+}
+
+std::vector<std::string> InstructionJump::to_strs() {
+ std::vector<std::string> 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;
+}
+
+std::vector<std::string> InstructionJumpRegister::to_strs() {
+ std::vector<std::string> 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
new file mode 100644
index 0000000..b8dee5c
--- /dev/null
+++ b/qtmips_machine/instructions/jumpbranch.h
@@ -0,0 +1,34 @@
+#ifndef JUMPBRANCH_H
+#define JUMPBRANCH_H
+
+#include "instruction.h"
+
+class InstructionJump : InstructionJ {
+public:
+ InstructionJump(bool link, std::uint32_t address);
+ std::vector<std::string> to_strs();
+private:
+ bool link;
+};
+
+class InstructionJumpRegister : InstructionR {
+public:
+ InstructionJumpRegister(bool link, std::uint8_t rs);
+ std::vector<std::string> to_strs();
+private:
+ bool link;
+};
+
+enum InstructionBranchT {
+
+};
+
+class InstructionBranch : InstructionI {
+public:
+ InstructionBranch();
+ std::vector<std::string> to_strs();
+private:
+ // TODO
+};
+
+#endif // JUMPBRANCH_H
diff --git a/qtmips_machine/instructions/loadstore.cpp b/qtmips_machine/instructions/loadstore.cpp
new file mode 100644
index 0000000..c83eae4
--- /dev/null
+++ b/qtmips_machine/instructions/loadstore.cpp
@@ -0,0 +1,67 @@
+#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;
+}
+
+std::vector<std::string> InstructionLoad::to_strs() {
+ std::vector<std::string> 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;
+}
+
+std::vector<std::string> InstructionStore::to_strs() {
+ std::vector<std::string> 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
new file mode 100644
index 0000000..9741bd7
--- /dev/null
+++ b/qtmips_machine/instructions/loadstore.h
@@ -0,0 +1,32 @@
+#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);
+ std::vector<std::string> 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);
+ std::vector<std::string> to_strs();
+private:
+ enum InstructionLoadStoreT type;
+};
+
+#endif // LOADSTORE_H
diff --git a/qtmips_machine/instructions/nop.cpp b/qtmips_machine/instructions/nop.cpp
new file mode 100644
index 0000000..5fd5e47
--- /dev/null
+++ b/qtmips_machine/instructions/nop.cpp
@@ -0,0 +1,7 @@
+#include "nop.h"
+
+std::vector<std::string> InstructionNop::to_strs() {
+ std::vector<std::string> str;
+ str.push_back("nop");
+ return str;
+}
diff --git a/qtmips_machine/instructions/nop.h b/qtmips_machine/instructions/nop.h
new file mode 100644
index 0000000..b098b11
--- /dev/null
+++ b/qtmips_machine/instructions/nop.h
@@ -0,0 +1,11 @@
+#ifndef NOP_H
+#define NOP_H
+
+#include "instruction.h"
+
+class InstructionNop : public Instruction {
+public:
+ std::vector<std::string> to_strs();
+};
+
+#endif // NOP_H
diff --git a/qtmips_machine/instructions/shift.cpp b/qtmips_machine/instructions/shift.cpp
new file mode 100644
index 0000000..a8c6e41
--- /dev/null
+++ b/qtmips_machine/instructions/shift.cpp
@@ -0,0 +1,51 @@
+#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;
+}
+
+std::vector<std::string> InstructionShift::to_strs() {
+ std::vector<std::string> 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;
+}
+
+std::vector<std::string> InstructionShiftVariable::to_strs() {
+ std::vector<std::string> 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
new file mode 100644
index 0000000..9ce29e0
--- /dev/null
+++ b/qtmips_machine/instructions/shift.h
@@ -0,0 +1,28 @@
+#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);
+ std::vector<std::string> 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);
+ std::vector<std::string> to_strs();
+private:
+ enum InstructionShiftT type;
+};
+
+#endif // SHIFT_H
diff --git a/qtmips_machine/memory.cpp b/qtmips_machine/memory.cpp
new file mode 100644
index 0000000..25deab0
--- /dev/null
+++ b/qtmips_machine/memory.cpp
@@ -0,0 +1,137 @@
+#include "memory.h"
+
+void MemoryAccess::write_hword(std::uint32_t offset, std::uint16_t value) {
+ this->write_byte(offset++, (std::uint8_t)value);
+ this->write_byte(offset, (std::uint8_t)(value >> 8));
+}
+
+void MemoryAccess::write_word(std::uint32_t offset, std::uint32_t value) {
+ this->write_byte(offset++, (std::uint8_t)value);
+ this->write_byte(offset++, (std::uint8_t)(value >> 8));
+ this->write_byte(offset++, (std::uint8_t)(value >> 16));
+ this->write_byte(offset++, (std::uint8_t)(value >> 24));
+}
+
+std::uint16_t MemoryAccess::read_hword(std::uint32_t offset) {
+ std::uint16_t dt = 0;
+ dt |= this->read_byte(offset++);
+ dt |= (this->read_byte(offset) << 8);
+ return dt;
+}
+
+std::uint32_t MemoryAccess::read_word(std::uint32_t offset) {
+ std::uint32_t dt = 0;
+ dt |= this->read_byte(offset++);
+ dt |= (this->read_byte(offset++) << 8);
+ dt |= (this->read_byte(offset++) << 16);
+ dt |= (this->read_byte(offset) << 24);
+ return dt;
+}
+
+MemorySection::MemorySection(std::uint32_t length) {
+ this->length = length;
+ this->dt = new std::uint8_t[length];
+}
+
+MemorySection::~MemorySection() {
+ delete this->dt;
+}
+
+using namespace std;
+
+void MemorySection::write_byte(std::uint32_t offset, std::uint8_t value) {
+ if (offset >= this->length)
+ throw QTMIPS_EXCEPTION(OutOfMemoryAccess, "Trying to write outside of the memory section", std::string("Accessing using offset: ") + std::to_string(offset));
+ this->dt[offset] = value;
+}
+
+std::uint8_t MemorySection::read_byte(std::uint32_t offset) {
+ if (offset >= this->length)
+ throw QTMIPS_EXCEPTION(OutOfMemoryAccess, "Trying to read outside of the memory section", std::string("Accessing using offset: ") + std::to_string(offset));
+ return this->dt[offset];
+}
+
+// Number of bites per row on lookup tree
+#define MEMORY_TREE_ROW ((32 - MEMORY_SECTION_BITS) / MEMORY_TREE_H)
+// Size of row in memory lookup tree
+#define MEMORY_TREE_LEN (1 << MEMORY_TREE_ROW)
+// Just do some sanity checks
+#if (MEMORY_TREE_LEN == 0)
+#error Nonzero memory tree row size
+#endif
+#if (((32 - MEMORY_SECTION_BITS) % MEMORY_TREE_H) != 0)
+#error Memory tree is not fully divisible by memory tree height
+#endif
+#if (MEMORY_TREE_H < 2)
+#error Memory tree have to be higher or in limit equal to two
+#endif
+
+union MemoryTree {
+ union MemoryTree *mt;
+ MemorySection *sec;
+};
+
+Memory::Memory() {
+ this->mt_root = allocate_section_tree();
+}
+
+Memory::~Memory() {
+ // Free up memory tree
+ // TODO
+}
+
+union MemoryTree *Memory::allocate_section_tree() {
+ union MemoryTree *mt = new union MemoryTree[MEMORY_TREE_LEN];
+ for (size_t i = 0; i < MEMORY_TREE_LEN; i++)
+ // Note that this also nulls sec pointer as those are both pointers and so they have same size
+ mt[i].mt = nullptr;
+ return mt;
+}
+
+// Create address mask with section length
+#define ADDRESS_MASK(LEN) ((1 << LEN) - 1)
+
+// Get index in tree node from address, length of row and tree depth
+// ADDR is expected to be and address with lowest bites removed (MEMORY_SECTION_BITS)
+#define ADDRESS_TREE_INDEX(DEPTH, ADDR) ((ADDR >> (DEPTH * MEMORY_TREE_ROW)) & ADDRESS_MASK(MEMORY_TREE_ROW))
+
+
+MemorySection *Memory::get_section(std::uint32_t address, bool create) {
+ std::uint32_t addr = address >> MEMORY_SECTION_BITS; // drop all bits for addressing inside of the section
+ union MemoryTree *w = this->mt_root;
+ size_t ii;
+ for (int i = 0; i < (MEMORY_TREE_H - 1); i++) {
+ ii = ADDRESS_TREE_INDEX(i, addr);
+ if (w[ii].mt == nullptr) { // We don't have this tree so allocate it
+ if (!create) // If we shouldn't be creating it than just return null
+ return nullptr;
+ w[ii].mt = allocate_section_tree();
+ }
+ w = w[ii].mt;
+ }
+ // Now expand last level
+ ii = ADDRESS_TREE_INDEX((MEMORY_TREE_H - 1), addr);
+ if (w[ii].sec == nullptr) {
+ if (!create)
+ return nullptr;
+ w[ii].sec = new MemorySection(1 << MEMORY_SECTION_BITS);
+ }
+ return w[ii].sec;
+}
+
+// Note about this address magic: we want to mask upper bits in address as those were used
+// for section lookup. We do it using (2^BITS - 1).
+#define SECTION_ADDRESS(ADDR) (ADDR & ADDRESS_MASK(MEMORY_SECTION_BITS))
+
+void Memory::write_byte(std::uint32_t address, std::uint8_t value) {
+ MemorySection *section = this->get_section(address, true);
+ section->write_byte(SECTION_ADDRESS(address), value);
+}
+
+std::uint8_t Memory::read_byte(std::uint32_t address) {
+ MemorySection *section = this->get_section(address, true);
+ if (section == nullptr)
+ return 0;
+ else
+ return section->read_byte(SECTION_ADDRESS(address));
+}
diff --git a/qtmips_machine/memory.h b/qtmips_machine/memory.h
new file mode 100644
index 0000000..1df23ae
--- /dev/null
+++ b/qtmips_machine/memory.h
@@ -0,0 +1,60 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <QObject>
+#include <vector>
+#include <cstdint>
+#include "qtmipsexception.h"
+
+// Virtual class for common memory access
+class MemoryAccess : public QObject {
+ Q_OBJECT
+public:
+ virtual void write_byte(std::uint32_t offset, std::uint8_t value) = 0;
+ void write_hword(std::uint32_t offset, std::uint16_t value);
+ void write_word(std::uint32_t offset, std::uint32_t value);
+
+ virtual std::uint8_t read_byte(std::uint32_t offset) = 0;
+ std::uint16_t read_hword(std::uint32_t offset);
+ std::uint32_t read_word(std::uint32_t offset);
+
+signals:
+ // TODO trigger
+ void byte_change(std::uint32_t address, std::uint32_t value);
+};
+
+class MemorySection : public MemoryAccess {
+public:
+ MemorySection(std::uint32_t length);
+ ~MemorySection();
+ void write_byte(std::uint32_t offset, std::uint8_t value);
+ std::uint8_t read_byte(std::uint32_t offset);
+private:
+ std::uint32_t length;
+ std::uint8_t *dt;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+/// Some optimalization options
+// How big memory sections will be (2^8=256)
+#define MEMORY_SECTION_BITS 8
+// How deep memory lookup tree will be
+#define MEMORY_TREE_H 4
+//////////////////////////////////////////////////////////////////////////////
+
+union MemoryTree;
+
+class Memory : public MemoryAccess {
+ Q_OBJECT
+public:
+ Memory();
+ ~Memory();
+ MemorySection *get_section(std::uint32_t address, bool create); // returns section containing given address
+ void write_byte(std::uint32_t address, std::uint8_t value);
+ std::uint8_t read_byte(std::uint32_t address);
+private:
+ union MemoryTree *mt_root;
+ static union MemoryTree *allocate_section_tree();
+};
+
+#endif // MEMORY_H
diff --git a/qtmips_machine/programloader.cpp b/qtmips_machine/programloader.cpp
new file mode 100644
index 0000000..0b0e294
--- /dev/null
+++ b/qtmips_machine/programloader.cpp
@@ -0,0 +1,81 @@
+#include "programloader.h"
+#include <exception>
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include <errno.h>
+#include <cstring>
+#include "qtmipsexception.h"
+
+ProgramLoader::ProgramLoader(char *file) {
+ // Initialize elf library
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ throw QTMIPS_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1));
+ // Open source file
+ if ((this->fd = open(file, O_RDONLY, 0)) < 0)
+ throw QTMIPS_EXCEPTION(Input, "Can't open input elf file for reading", std::strerror(errno));
+ // Initialize elf
+ if (!(this->elf = elf_begin(this->fd, ELF_C_READ, NULL)))
+ throw QTMIPS_EXCEPTION(Input, "Elf read begin failed", elf_errmsg(-1));
+ // Check elf kind
+ if (elf_kind(this->elf) != ELF_K_ELF)
+ throw QTMIPS_EXCEPTION(Input, "Invalid input file elf format, plain elf file expected", "");
+
+ if (!gelf_getehdr(this->elf, &this->hdr))
+ throw QTMIPS_EXCEPTION(Input, "Getting elf file header failed", elf_errmsg(-1));
+ // Check elf file format, executable expected, nothing else.
+ if (this->hdr.e_type != ET_EXEC)
+ throw QTMIPS_EXCEPTION(Input, "Invalid input file type", "");
+ // Check elf file architecture, of course only mips is supported.
+ if (this->hdr.e_machine != EM_MIPS)
+ throw QTMIPS_EXCEPTION(Input, "Invalid input file architecture", "");
+ // Check elf file class, only 32bit architecture is supported.
+ int elf_class;
+ if ((elf_class = gelf_getclass(this->elf)) == ELFCLASSNONE)
+ throw QTMIPS_EXCEPTION(Input, "Getting elf class failed", elf_errmsg(-1));
+ if (elf_class != ELFCLASS32)
+ throw QTMIPS_EXCEPTION(Input, "Only supported architecture is 32bit", "");
+ // TODO check endianity!
+
+ // Get number of program sections in elf file
+ if (elf_getphdrnum(this->elf, &this->n_secs))
+ throw QTMIPS_EXCEPTION(Input, "Elf program sections count query failed", elf_errmsg(-1));
+ // Get program sections headers
+ if (!(this->phdrs = elf32_getphdr(this->elf)))
+ throw QTMIPS_EXCEPTION(Input, "Elf program sections get failed", elf_errmsg(-1));
+ // We want only LOAD sections so we create map of those sections
+ for (unsigned i = 1; i < this->n_secs; i++) {
+ // TODO handle endianity
+ if (this->phdrs[i].p_type != PT_LOAD)
+ continue;
+ this->map.push_back(i);
+ }
+ // 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() {
+ // Close elf
+ 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;
+}
+
+std::vector<std::uint8_t> ProgramLoader::get_data(size_t sec) {
+ SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section");
+ std::vector<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.push_back((std::uint8_t) f[this->phdrs[phdrs_i].p_offset + i]);
+ }
+ return d;
+}
diff --git a/qtmips_machine/programloader.h b/qtmips_machine/programloader.h
new file mode 100644
index 0000000..058e5c1
--- /dev/null
+++ b/qtmips_machine/programloader.h
@@ -0,0 +1,28 @@
+#ifndef PROGRAM_H
+#define PROGRAM_H
+
+#include <unistd.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <cstdint>
+#include <vector>
+
+
+class ProgramLoader {
+public:
+ ProgramLoader(char *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
+ std::vector<std::uint8_t> get_data(size_t sec); // Returns bytes of given section
+private:
+ int fd;
+ Elf *elf;
+ GElf_Ehdr hdr; // elf file header
+ size_t n_secs; // number of sections in elf program header
+ Elf32_Phdr *phdrs; // program section headers
+ std::vector<size_t> map; // external index to phdrs index
+};
+
+#endif // PROGRAM_H
diff --git a/qtmips_machine/programmemory.cpp b/qtmips_machine/programmemory.cpp
new file mode 100644
index 0000000..c5ef53a
--- /dev/null
+++ b/qtmips_machine/programmemory.cpp
@@ -0,0 +1,111 @@
+#include "programmemory.h"
+#include <sstream>
+#include "qtmipsexception.h"
+#include "instructions/arithmetic.h"
+#include "instructions/loadstore.h"
+#include "instructions/nop.h"
+#include "instructions/shift.h"
+
+ProgramMemory::ProgramMemory(ProgramLoader *loader, MemoryAccess *memory) {
+ this->memory = memory;
+ // Load program to memory (just dump it byte by byte, decode is done on demand)
+ for (int i = 0; i < loader->get_nsec(); i++) {
+ std::uint32_t base_address = loader->get_address(i);
+ std::vector<std::uint8_t> data = loader->get_data(i);
+ for (auto it = data.begin(); it < data.end(); it++) {
+ memory->write_byte(base_address + i, *it);
+ }
+ }
+}
+
+#define MASKSUB(VAR, LEN, OFFSET) ((VAR & ~((1 << (LEN+OFFSET+1)) - 1)) >> OFFSET)
+
+Instruction *ProgramMemory::at(std::uint32_t address) {
+ if (address % 4)
+ // TODO different exception (unaligned address)
+ throw std::exception();
+ // Read instruction from memory
+ std::uint32_t dt = this->memory->read_word(address);
+
+ // Decode instruction
+ Instruction *inst;
+ std::uint8_t opcode = dt >> 26; // upper 6 bits
+ if (opcode == 0) { // Arithmetic and shift instructions
+ return this->decode_r(dt);
+ } else if (opcode == 2 || opcode == 3) { // Jump instructions
+ return decode_j(dt);
+ } else {
+ return decode_i(dt);
+ }
+}
+
+// TODO implement
+#define I_UNKNOWN(DATA) do { std::stringstream ss; ss << std::hex << DATA; throw QTMIPS_EXCEPTION(UnsupportedInstruction, "Unknown instruction, can't decode", ss.str()); } while(false);
+#define I_UNSUPPORTED(INST) throw QTMIPS_EXCEPTION(UnsupportedInstruction, "Decoded unsupported unstruction", #INST)
+
+Instruction *ProgramMemory::decode_r(std::uint32_t dt) {
+ std::uint8_t func = MASKSUB(dt, 6, 0);
+ if (!func)
+ return new InstructionNop();
+
+ std::uint8_t rs, rt, rd, sa;
+ rs = MASKSUB(dt, 5, 21);
+ rt = MASKSUB(dt, 5, 16);
+ rd = MASKSUB(dt, 5, 11);
+ sa = MASKSUB(dt, 5, 6);
+
+ if (func < 8) { // Shift instructions
+ bool variable = false;
+ enum InstructionShiftT t;
+ switch (func) {
+ case 0:
+ t = IST_LL;
+ break;
+ case 2:
+ t = IST_RL;
+ break;
+ case 3:
+ t = IST_RA;
+ break;
+ case 4:
+ t = IST_LL;
+ variable = true;
+ break;
+ case 6:
+ t = IST_RL;
+ variable = true;
+ break;
+ case 7:
+ t = IST_RA;
+ variable = true;
+ break;
+ default:
+ I_UNKNOWN(dt);
+ }
+ if (variable)
+ return new InstructionShiftVariable(t, rs, rt, rd);
+ else
+ return new InstructionShift(t, rt, rd, sa);
+ } else if (func < 10) { // Jump instructions
+ // TODO
+ I_UNKNOWN(dt);
+ } else { // TODO filter rest
+ I_UNKNOWN(dt);
+ }
+}
+
+Instruction *ProgramMemory::decode_j(std::uint32_t dt) {
+ std::uint32_t address = MASKSUB(dt, 26, 0);
+ // TODO
+ I_UNKNOWN(dt);
+}
+
+Instruction *ProgramMemory::decode_i(std::uint32_t dt) {
+ // InstructionI
+ std::uint8_t rs, rt;
+ rs = MASKSUB(dt, 5, 21);
+ rt = MASKSUB(dt, 5, 16);
+ std::uint16_t immediate = MASKSUB(dt, 16, 0);
+ // TODO
+ I_UNKNOWN(dt);
+}
diff --git a/qtmips_machine/programmemory.h b/qtmips_machine/programmemory.h
new file mode 100644
index 0000000..84b2f31
--- /dev/null
+++ b/qtmips_machine/programmemory.h
@@ -0,0 +1,22 @@
+#ifndef PROGRAMMEMORY_H
+#define PROGRAMMEMORY_H
+
+#include <vector>
+#include "programloader.h"
+#include "memory.h"
+#include "instruction.h"
+
+class ProgramMemory {
+public:
+ ProgramMemory(ProgramLoader *loader, MemoryAccess *memory);
+
+ 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);
+ Instruction *decode_i(std::uint32_t dt);
+};
+
+#endif // PROGRAMMEMORY_H
diff --git a/qtmips_machine/qtmips_machine.pro b/qtmips_machine/qtmips_machine.pro
new file mode 100644
index 0000000..66cb37c
--- /dev/null
+++ b/qtmips_machine/qtmips_machine.pro
@@ -0,0 +1,46 @@
+QT -= gui
+
+TARGET = qtmips_machine
+CONFIG += c++11
+
+TEMPLATE = lib
+
+LIBS += -lelf
+QMAKE_CXXFLAGS += -std=c++0x
+
+DEFINES += QTMIPS_MACHINE_LIBRARY
+DEFINES += QT_DEPRECATED_WARNINGS
+
+SOURCES += \
+ qtmipsmachine.cpp \
+ qtmipsexception.cpp \
+ core.cpp \
+ memory.cpp \
+ instruction.cpp \
+ registers.cpp \
+ programloader.cpp \
+ programmemory.cpp \
+ instructions/arithmetic.cpp \
+ instructions/loadstore.cpp \
+ instructions/shift.cpp \
+ instructions/nop.cpp \
+ instructions/jumpbranch.cpp \
+ utils.cpp \
+ cache.cpp
+
+HEADERS += \
+ qtmipsmachine.h \
+ qtmipsexception.h \
+ core.h \
+ memory.h \
+ instruction.h \
+ registers.h \
+ programloader.h \
+ programmemory.h \
+ instructions/arithmetic.h \
+ instructions/loadstore.h \
+ instructions/shift.h \
+ instructions/nop.h \
+ instructions/jumpbranch.h \
+ utils.h \
+ cache.h
diff --git a/qtmips_machine/qtmips_machine_global.h b/qtmips_machine/qtmips_machine_global.h
new file mode 100644
index 0000000..14cb0ac
--- /dev/null
+++ b/qtmips_machine/qtmips_machine_global.h
@@ -0,0 +1,12 @@
+#ifndef QTMIPS_MACHINE_GLOBAL_H
+#define QTMIPS_MACHINE_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(QTMIPS_MACHINE_LIBRARY)
+# define QTMIPS_MACHINESHARED_EXPORT Q_DECL_EXPORT
+#else
+# define QTMIPS_MACHINESHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // QTMIPS_MACHINE_GLOBAL_H
diff --git a/qtmips_machine/qtmipsexception.cpp b/qtmips_machine/qtmipsexception.cpp
new file mode 100644
index 0000000..2ce7ae5
--- /dev/null
+++ b/qtmips_machine/qtmipsexception.cpp
@@ -0,0 +1,60 @@
+#include "qtmipsexception.h"
+#include <iostream>
+#include <cstring>
+
+QtMipsException::QtMipsException(QTMIPS_ARGS_COMMON) {
+ this->reason = reason;
+ this->ext = ext;
+ this->file = file;
+ this->line = line;
+}
+
+const char *QtMipsException::what() const throw() {
+ std::string message = this->msg(true);
+ char * cstr = new char [message.length()+1];
+ std::strcpy (cstr, message.c_str());
+ return cstr;
+}
+
+std::string QtMipsException::msg(bool pos) const {
+ std::string message;
+ if (pos)
+ message += std::string("(") + std::string(this->file) + std::string(":") + std::to_string(this->line) + std::string(") ");
+ message += this->reason;
+ if (!this->ext.empty()) {
+ message += std::string(": ");
+ message += this->ext;
+ }
+ return message;
+}
+
+
+QtMipsExceptionInput::QtMipsExceptionInput(QTMIPS_ARGS_COMMON)
+ : QtMipsException(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionRuntime::QtMipsExceptionRuntime(QTMIPS_ARGS_COMMON)
+ : QtMipsException(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionUnsupportedInstruction::QtMipsExceptionUnsupportedInstruction(QTMIPS_ARGS_COMMON)
+ : QtMipsExceptionRuntime(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionUnalignedJump::QtMipsExceptionUnalignedJump(QTMIPS_ARGS_COMMON)
+ : QtMipsExceptionRuntime(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionOutOfMemoryAccess::QtMipsExceptionOutOfMemoryAccess(QTMIPS_ARGS_COMMON)
+ : QtMipsExceptionRuntime(reason, ext, file, line) {
+ return;
+}
+
+QtMipsExceptionSanity::QtMipsExceptionSanity(QTMIPS_ARGS_COMMON)
+ : QtMipsException(reason, ext, file, line) {
+ return;
+}
diff --git a/qtmips_machine/qtmipsexception.h b/qtmips_machine/qtmipsexception.h
new file mode 100644
index 0000000..bfaa788
--- /dev/null
+++ b/qtmips_machine/qtmipsexception.h
@@ -0,0 +1,63 @@
+#ifndef QTMIPSEXCEPTION_H
+#define QTMIPSEXCEPTION_H
+
+#include <exception>
+#include <string>
+
+#define QTMIPS_EXCEPTION(TYPE, REASON, EXT) (QtMipsException ## TYPE (std::string(REASON), std::string(EXT), std::string(__FILE__), __LINE__))
+#define QTMIPS_ARGS_COMMON std::string reason, std::string ext, std::string file, int line
+
+// Base exception for all machine ones
+class QtMipsException : public std::exception {
+public:
+ QtMipsException(QTMIPS_ARGS_COMMON);
+ const char *what() const throw();
+ std::string msg(bool pos) const;
+protected:
+ std::string reason, ext, file;
+ int line;
+};
+
+// Exception durring input loading
+class QtMipsExceptionInput : public QtMipsException {
+public:
+ QtMipsExceptionInput(QTMIPS_ARGS_COMMON);
+};
+
+// Exceptions caused by machine invalid input or unsupported action
+class QtMipsExceptionRuntime : public QtMipsException {
+public:
+ QtMipsExceptionRuntime(QTMIPS_ARGS_COMMON);
+};
+
+// Decoded instruction is not supported.
+// This can be cause by really using some unimplemented instruction or because of problems in instruction decode.
+class QtMipsExceptionUnsupportedInstruction : public QtMipsExceptionRuntime {
+public:
+ QtMipsExceptionUnsupportedInstruction(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
+class QtMipsExceptionUnalignedJump : public QtMipsExceptionRuntime {
+public:
+ QtMipsExceptionUnalignedJump(QTMIPS_ARGS_COMMON);
+};
+
+// Trying to access address outside of the memory
+// As we are simulating whole 32bit memory address space then this is most probably QtMips bug if raised not program.
+class QtMipsExceptionOutOfMemoryAccess : public QtMipsExceptionRuntime {
+public:
+ QtMipsExceptionOutOfMemoryAccess(QTMIPS_ARGS_COMMON);
+};
+
+// This is sanity check exception
+class QtMipsExceptionSanity : public QtMipsException {
+public:
+ QtMipsExceptionSanity(QTMIPS_ARGS_COMMON);
+};
+
+#define SANITY_ASSERT(COND, MSG) do { if (!(COND)) throw QTMIPS_EXCEPTION(Sanity, "Sanity check failed (" #COND ")", MSG); } while (false)
+
+#endif // QTMIPSEXCEPTION_H
diff --git a/qtmips_machine/qtmipsmachine.cpp b/qtmips_machine/qtmipsmachine.cpp
new file mode 100644
index 0000000..0fc207c
--- /dev/null
+++ b/qtmips_machine/qtmipsmachine.cpp
@@ -0,0 +1,5 @@
+#include "qtmipsmachine.h"
+
+QtMipsMachine::QtMipsMachine(char *file) {
+ this->loader = new ProgramLoader(file);
+}
diff --git a/qtmips_machine/qtmipsmachine.h b/qtmips_machine/qtmipsmachine.h
new file mode 100644
index 0000000..af981b3
--- /dev/null
+++ b/qtmips_machine/qtmipsmachine.h
@@ -0,0 +1,25 @@
+#ifndef QTMIPSMACHINE_H
+#define QTMIPSMACHINE_H
+
+#include <QObject>
+
+#include "qtmipsexception.h"
+#include "programloader.h"
+#include "core.h"
+// TODO piplined core
+
+class QtMipsMachine : QObject {
+ Q_OBJECT
+public:
+ QtMipsMachine(char *file);
+
+ // TODO handle speed
+ void play();
+ void pause();
+ void step();
+ void restart();
+private:
+ ProgramLoader *loader;
+};
+
+#endif // QTMIPSMACHINE_H
diff --git a/qtmips_machine/registers.cpp b/qtmips_machine/registers.cpp
new file mode 100644
index 0000000..514987a
--- /dev/null
+++ b/qtmips_machine/registers.cpp
@@ -0,0 +1,66 @@
+#include "registers.h"
+#include "qtmipsexception.h"
+#include "utils.h"
+
+// TODO should this be configurable?
+//////////////////////////////////////////////////////////////////////////////
+/// Program counter initial value
+#define PC_INIT 0x80020000
+//////////////////////////////////////////////////////////////////////////////
+
+Registers::Registers() {
+ this->pc = PC_INIT; // Initialize to beginning program section
+ for (int i = 0; i < 31; i++)
+ this->gp[i] = 0;
+ this->hi = this->lo = 0;
+}
+
+std::uint32_t Registers::read_pc() {
+ return this->pc;
+}
+
+std::uint32_t Registers::pc_inc() {
+ this->pc += 4;
+ return this->pc;
+}
+
+std::uint32_t Registers::pc_jmp(std::int32_t offset) {
+ if (offset % 4)
+ throw QTMIPS_EXCEPTION(UnalignedJump, "Trying to jump by unaligned offset", to_string_hex(offset));
+ this->pc += offset;
+ return this->pc;
+}
+
+void Registers::pc_abs_jmp(std::uint32_t address) {
+ if (address % 4)
+ throw QTMIPS_EXCEPTION(UnalignedJump, "Trying to jump to unaligned address", to_string_hex(address));
+ this->pc = address;
+}
+
+std::uint32_t Registers::read_gp(std::uint8_t i) {
+ SANITY_ASSERT(i < 32, std::string("Trying to read from register ") + std::to_string(i));
+ if (!i) // $0 always reads as 0
+ return 0;
+ return this->gp[i - 1];
+}
+
+void Registers::write_gp(std::uint8_t i, std::uint32_t value) {
+ SANITY_ASSERT(i < 32, std::string("Trying to write to register ") + std::to_string(i));
+ if (i == 0) // Skip write to $0
+ return;
+ this->gp[i - 1] = value;
+}
+
+std::uint32_t Registers::read_hi_lo(bool hi) {
+ if (hi)
+ return this->hi;
+ else
+ return this->lo;
+}
+
+void Registers::write_hi_lo(bool hi, std::uint32_t value) {
+ if (hi)
+ this->hi = value;
+ else
+ this->lo = value;
+}
diff --git a/qtmips_machine/registers.h b/qtmips_machine/registers.h
new file mode 100644
index 0000000..a550f4a
--- /dev/null
+++ b/qtmips_machine/registers.h
@@ -0,0 +1,31 @@
+#ifndef REGISTERS_H
+#define REGISTERS_H
+
+#include <QObject>
+#include <cstdint>
+
+class Registers : public QObject {
+ Q_OBJECT
+public:
+ Registers();
+
+ std::uint32_t read_pc(); // 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
+ 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
+ void write_hi_lo(bool hi, std::uint32_t value);
+
+signals:
+ // TODO signals
+
+private:
+ std::uint32_t gp[31]; // general-purpose registers ($0 is intentionally skipped)
+ std::uint32_t hi, lo;
+ std::uint32_t pc; // program counter
+};
+
+#endif // REGISTERS_H
diff --git a/qtmips_machine/tests/testmemory.cpp b/qtmips_machine/tests/testmemory.cpp
new file mode 100644
index 0000000..991e1f1
--- /dev/null
+++ b/qtmips_machine/tests/testmemory.cpp
@@ -0,0 +1,61 @@
+#include "tst_machine.h"
+#include "memory.h"
+
+void MachineTests::memory_data() {
+ QTest::addColumn<std::uint32_t>("address");
+
+ QTest::newRow("memory begin") << (std::uint32_t)0x00;
+ QTest::newRow("memory end") << (std::uint32_t)0xFFFFFFFC;
+ QTest::newRow("memory midle start") << (std::uint32_t)0xFFFF00;
+ QTest::newRow("memory midle end") << (std::uint32_t)0xFFFFFF;
+}
+
+void MachineTests::memory() {
+ Memory m;
+
+ 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);
+}
+
+void MachineTests::memory_section_data() {
+ QTest::addColumn<std::uint32_t>("address");
+
+ QTest::newRow("memory begin") << (std::uint32_t)0x00;
+ QTest::newRow("memory end") << (std::uint32_t)0xFFFFFFFF;
+ QTest::newRow("memory midle start") << (std::uint32_t)0xFFFF00;
+ QTest::newRow("memory midle end") << (std::uint32_t)0xFFFFFF;
+}
+
+void MachineTests::memory_section() {
+ Memory m;
+
+ 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);
+
+ // 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);
+}
diff --git a/qtmips_machine/tests/testregisters.cpp b/qtmips_machine/tests/testregisters.cpp
new file mode 100644
index 0000000..4430beb
--- /dev/null
+++ b/qtmips_machine/tests/testregisters.cpp
@@ -0,0 +1,39 @@
+#include "tst_machine.h"
+#include <qtmipsexception.h>
+#include <registers.h>
+
+void MachineTests::registers_gp0() {
+ Registers r;
+ QCOMPARE(r.read_gp(0), (unsigned)0);
+ r.write_gp(0, 0xff);
+ QCOMPARE(r.read_gp(0), (unsigned)0);
+}
+
+void MachineTests::registers_rw_gp() {
+ Registers r;
+ for (int i = 1; i < 32; i++) {
+ r.write_gp(i, 0xf00 + i);
+ QCOMPARE(r.read_gp(i), (unsigned)(0xf00 + i));
+ }
+}
+
+void MachineTests::registers_rw_hi_lo() {
+ Registers r;
+ r.write_hi_lo(false, 0xee);
+ r.write_hi_lo(true, 0xaa);
+ QCOMPARE(r.read_hi_lo(false), (unsigned)0xee);
+ QCOMPARE(r.read_hi_lo(true), (unsigned)0xaa);
+}
+
+void MachineTests::registers_pc() {
+ Registers r;
+ QCOMPARE(r.read_pc(), (unsigned)0x80020000); // Check initial pc address
+ QCOMPARE(r.pc_inc(), (unsigned)0x80020004);
+ QCOMPARE(r.pc_inc(), (unsigned)0x80020008);
+ QCOMPARE(r.pc_jmp(-0x8), (unsigned)0x80020000);
+ QCOMPARE(r.pc_jmp(0xC), (unsigned)0x8002000C);
+ r.pc_abs_jmp(0x80020100);
+ QCOMPARE(r.read_pc(), (unsigned)0x80020100);
+ QVERIFY_EXCEPTION_THROWN(r.pc_jmp(0x1), QtMipsExceptionUnalignedJump);
+ QVERIFY_EXCEPTION_THROWN(r.pc_abs_jmp(0x80020101), QtMipsExceptionUnalignedJump);
+}
diff --git a/qtmips_machine/tests/tests.pro b/qtmips_machine/tests/tests.pro
new file mode 100644
index 0000000..9477e08
--- /dev/null
+++ b/qtmips_machine/tests/tests.pro
@@ -0,0 +1,24 @@
+QT += testlib
+QT -= gui
+
+TARGET = tst_machine
+CONFIG += console
+CONFIG -= app_bundle
+CONFIG += c++11
+
+TEMPLATE = app
+
+LIBS += -L$$OUT_PWD/../ -lqtmips_machine
+INCLUDEPATH += $$PWD/..
+DEPENDPATH += $$PWD/..
+QMAKE_CXXFLAGS += -std=c++0x
+
+DEFINES += QT_DEPRECATED_WARNINGS
+
+SOURCES += tst_machine.cpp \
+ testmemory.cpp \
+ testregisters.cpp
+
+HEADERS += tst_machine.h
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
diff --git a/qtmips_machine/tests/tst_machine.cpp b/qtmips_machine/tests/tst_machine.cpp
new file mode 100644
index 0000000..d5fc354
--- /dev/null
+++ b/qtmips_machine/tests/tst_machine.cpp
@@ -0,0 +1,3 @@
+#include "tst_machine.h"
+
+QTEST_GUILESS_MAIN(MachineTests)
diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h
new file mode 100644
index 0000000..214ab88
--- /dev/null
+++ b/qtmips_machine/tests/tst_machine.h
@@ -0,0 +1,21 @@
+#ifndef TST_MACHINE_H
+#define TST_MACHINE_H
+
+#include <QtTest>
+
+class MachineTests : public QObject {
+ Q_OBJECT
+private Q_SLOTS:
+ // Registers
+ void registers_gp0();
+ void registers_rw_gp();
+ void registers_rw_hi_lo();
+ void registers_pc();
+ // Memory
+ void memory();
+ void memory_data();
+ void memory_section();
+ void memory_section_data();
+};
+
+#endif // TST_MACHINE_H
diff --git a/qtmips_machine/utils.cpp b/qtmips_machine/utils.cpp
new file mode 100644
index 0000000..dfe8c2a
--- /dev/null
+++ b/qtmips_machine/utils.cpp
@@ -0,0 +1,30 @@
+#include "utils.h"
+#include <sstream>
+
+#define TO_STR_HEX do { std::stringstream ss; ss << std::hex << v; return std::string(ss.str()); } while (false)
+
+std::string to_string_hex(int v) {
+ TO_STR_HEX;
+}
+
+std::string to_string_hex(unsigned v) {
+ TO_STR_HEX;
+}
+
+std::string to_string_hex(long v) {
+ TO_STR_HEX;
+}
+
+std::string to_string_hex(unsigned long v) {
+ TO_STR_HEX;
+}
+
+std::string to_string_hex(long long v) {
+ TO_STR_HEX;
+}
+
+std::string to_string_hex(unsigned long long v) {
+ TO_STR_HEX;
+}
+
+#undef TO_STR_HEX
diff --git a/qtmips_machine/utils.h b/qtmips_machine/utils.h
new file mode 100644
index 0000000..151e1d0
--- /dev/null
+++ b/qtmips_machine/utils.h
@@ -0,0 +1,13 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <string>
+
+std::string to_string_hex(int);
+std::string to_string_hex(unsigned);
+std::string to_string_hex(long);
+std::string to_string_hex(unsigned long);
+std::string to_string_hex(long long);
+std::string to_string_hex(unsigned long long);
+
+#endif // UTILS_H