From 9cf92379d5fcf0076c25dae0935daab446c992cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Wed, 30 Aug 2017 21:37:53 +0200 Subject: Initial commit Adding work done so far. --- qtmips_machine/cache.cpp | 1 + qtmips_machine/cache.h | 18 ++++ qtmips_machine/core.cpp | 5 ++ qtmips_machine/core.h | 21 +++++ qtmips_machine/instruction.cpp | 81 +++++++++++++++++ qtmips_machine/instruction.h | 48 ++++++++++ qtmips_machine/instructions/arithmetic.cpp | 87 ++++++++++++++++++ qtmips_machine/instructions/arithmetic.h | 46 ++++++++++ qtmips_machine/instructions/jumpbranch.cpp | 30 +++++++ qtmips_machine/instructions/jumpbranch.h | 34 +++++++ qtmips_machine/instructions/loadstore.cpp | 67 ++++++++++++++ qtmips_machine/instructions/loadstore.h | 32 +++++++ qtmips_machine/instructions/nop.cpp | 7 ++ qtmips_machine/instructions/nop.h | 11 +++ qtmips_machine/instructions/shift.cpp | 51 +++++++++++ qtmips_machine/instructions/shift.h | 28 ++++++ qtmips_machine/memory.cpp | 137 +++++++++++++++++++++++++++++ qtmips_machine/memory.h | 60 +++++++++++++ qtmips_machine/programloader.cpp | 81 +++++++++++++++++ qtmips_machine/programloader.h | 28 ++++++ qtmips_machine/programmemory.cpp | 111 +++++++++++++++++++++++ qtmips_machine/programmemory.h | 22 +++++ qtmips_machine/qtmips_machine.pro | 46 ++++++++++ qtmips_machine/qtmips_machine_global.h | 12 +++ qtmips_machine/qtmipsexception.cpp | 60 +++++++++++++ qtmips_machine/qtmipsexception.h | 63 +++++++++++++ qtmips_machine/qtmipsmachine.cpp | 5 ++ qtmips_machine/qtmipsmachine.h | 25 ++++++ qtmips_machine/registers.cpp | 66 ++++++++++++++ qtmips_machine/registers.h | 31 +++++++ qtmips_machine/tests/testmemory.cpp | 61 +++++++++++++ qtmips_machine/tests/testregisters.cpp | 39 ++++++++ qtmips_machine/tests/tests.pro | 24 +++++ qtmips_machine/tests/tst_machine.cpp | 3 + qtmips_machine/tests/tst_machine.h | 21 +++++ qtmips_machine/utils.cpp | 30 +++++++ qtmips_machine/utils.h | 13 +++ 37 files changed, 1505 insertions(+) create mode 100644 qtmips_machine/cache.cpp create mode 100644 qtmips_machine/cache.h create mode 100644 qtmips_machine/core.cpp create mode 100644 qtmips_machine/core.h create mode 100644 qtmips_machine/instruction.cpp create mode 100644 qtmips_machine/instruction.h create mode 100644 qtmips_machine/instructions/arithmetic.cpp create mode 100644 qtmips_machine/instructions/arithmetic.h create mode 100644 qtmips_machine/instructions/jumpbranch.cpp create mode 100644 qtmips_machine/instructions/jumpbranch.h create mode 100644 qtmips_machine/instructions/loadstore.cpp create mode 100644 qtmips_machine/instructions/loadstore.h create mode 100644 qtmips_machine/instructions/nop.cpp create mode 100644 qtmips_machine/instructions/nop.h create mode 100644 qtmips_machine/instructions/shift.cpp create mode 100644 qtmips_machine/instructions/shift.h create mode 100644 qtmips_machine/memory.cpp create mode 100644 qtmips_machine/memory.h create mode 100644 qtmips_machine/programloader.cpp create mode 100644 qtmips_machine/programloader.h create mode 100644 qtmips_machine/programmemory.cpp create mode 100644 qtmips_machine/programmemory.h create mode 100644 qtmips_machine/qtmips_machine.pro create mode 100644 qtmips_machine/qtmips_machine_global.h create mode 100644 qtmips_machine/qtmipsexception.cpp create mode 100644 qtmips_machine/qtmipsexception.h create mode 100644 qtmips_machine/qtmipsmachine.cpp create mode 100644 qtmips_machine/qtmipsmachine.h create mode 100644 qtmips_machine/registers.cpp create mode 100644 qtmips_machine/registers.h create mode 100644 qtmips_machine/tests/testmemory.cpp create mode 100644 qtmips_machine/tests/testregisters.cpp create mode 100644 qtmips_machine/tests/tests.pro create mode 100644 qtmips_machine/tests/tst_machine.cpp create mode 100644 qtmips_machine/tests/tst_machine.h create mode 100644 qtmips_machine/utils.cpp create mode 100644 qtmips_machine/utils.h (limited to 'qtmips_machine') 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 +#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 +#include + +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 InstructionR::to_strs() { + std::vector 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 InstructionI::to_strs() { + std::vector 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 InstructionJ::to_strs() { + std::vector 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 +#include +#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 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 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 to_strs(); +protected: + std::uint8_t rs, rt; + std::uint16_t immediage; +}; + +class InstructionJ : public Instruction { +public: + InstructionJ(std::uint32_t address); + + std::vector 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 + +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 InstructionArithmetic::to_strs() { + std::vector 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 InstructionArithmeticImmediate::to_strs() { + std::vector 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 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 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 InstructionJump::to_strs() { + std::vector 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 InstructionJumpRegister::to_strs() { + std::vector 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 to_strs(); +private: + bool link; +}; + +class InstructionJumpRegister : InstructionR { +public: + InstructionJumpRegister(bool link, std::uint8_t rs); + std::vector to_strs(); +private: + bool link; +}; + +enum InstructionBranchT { + +}; + +class InstructionBranch : InstructionI { +public: + InstructionBranch(); + std::vector 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 InstructionLoad::to_strs() { + std::vector 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 InstructionStore::to_strs() { + std::vector 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 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 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 InstructionNop::to_strs() { + std::vector 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 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 InstructionShift::to_strs() { + std::vector 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 InstructionShiftVariable::to_strs() { + std::vector 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 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 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 +#include +#include +#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 +#include +#include +#include +#include +#include +#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 ProgramLoader::get_data(size_t sec) { + SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section"); + std::vector 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 +#include +#include +#include +#include + + +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 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 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 +#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 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 +#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 + +#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 +#include + +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 +#include + +#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 + +#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 +#include + +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("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("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 +#include + +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 + +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 + +#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 + +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 -- cgit v1.2.3