// SPDX-License-Identifier: GPL-2.0+ /******************************************************************************* * QtMips - MIPS 32-bit Architecture Subset Simulator * * Implemented to support following courses: * * B35APO - Computer Architectures * https://cw.fel.cvut.cz/wiki/courses/b35apo * * B4M35PAP - Advanced Computer Architectures * https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start * * Copyright (c) 2017-2019 Karel Koci * Copyright (c) 2019 Pavel Pisa * * Faculty of Electrical Engineering (http://www.fel.cvut.cz) * Czech Technical University (http://www.cvut.cz/) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * ******************************************************************************/ #include "instruction.h" #include "alu.h" #include "memory.h" #include "qtmipsexception.h" using namespace machine; #define FLAGS_ALU_I (IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE) #define FLAGS_ALU_I_ZE (IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_ZERO_EXTEND) #define FLAGS_ALU_T_R (IMF_SUPPORTED | IMF_REGD | IMF_REGWRITE) #define FLAGS_ALU_T_R_STD (IMF_SUPPORTED | IMF_REGD | IMF_REGWRITE \ | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT) #define FLAGS_ALU_T_R_ST (IMF_SUPPORTED | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT) #define FLAGS_ALU_T_R_S (IMF_SUPPORTED | IMF_ALU_REQ_RS) #define FLAGS_ALU_T_R_D (IMF_SUPPORTED | IMF_REGD | IMF_REGWRITE ) #define FLAGS_ALU_T_R_SD (IMF_SUPPORTED | IMF_REGD | IMF_REGWRITE | IMF_ALU_REQ_RS) #define NOALU .alu = ALU_OP_SLL #define NOMEM .mem_ctl = AC_NONE #define IM_UNKNOWN {"UNKNOWN", Instruction::T_UNKNOWN, NOALU, NOMEM, .flags = 0} struct InstructionMap { const char *name; enum Instruction::Type type; enum AluOp alu; enum AccessControl mem_ctl; unsigned int flags; }; #define IT_R Instruction::T_R #define IT_I Instruction::T_I #define IT_J Instruction::T_J // This table is indexed by opcode static const struct InstructionMap instruction_map[] = { {"ALU", IT_R, NOALU, NOMEM, // Alu operations .flags = 0}, {"REGIMM", IT_I, NOALU, NOMEM, // REGIMM (BLTZ, BGEZ) .flags = IMF_SUPPORTED | IMF_BJR_REQ_RS}, {"J", IT_J, NOALU, NOMEM, // J .flags = IMF_SUPPORTED}, {"JAL", IT_J, ALU_OP_PASS_S, NOMEM, // JAL .flags = IMF_SUPPORTED | IMF_PC_TO_R31 | IMF_REGWRITE}, {"BEQ", IT_I, NOALU, NOMEM, // BEQ .flags = IMF_SUPPORTED | IMF_BJR_REQ_RS | IMF_BJR_REQ_RT}, {"BNE", IT_I, NOALU, NOMEM, // BNE .flags = IMF_SUPPORTED | IMF_BJR_REQ_RS | IMF_BJR_REQ_RT}, {"BLEZ", IT_I, NOALU, NOMEM, // BLEZ .flags = IMF_SUPPORTED | IMF_BJR_REQ_RS}, {"BGTZ", IT_I, NOALU, NOMEM, // BGTZ .flags = IMF_SUPPORTED | IMF_BJR_REQ_RS}, {"ADDI", IT_I, ALU_OP_ADD, NOMEM, // ADDI .flags = FLAGS_ALU_I}, {"ADDIU", IT_I, ALU_OP_ADDU, NOMEM, // ADDIU .flags = FLAGS_ALU_I}, {"SLTI", IT_I, ALU_OP_SLT, NOMEM, // SLTI .flags = FLAGS_ALU_I}, {"SLTIU", IT_I, ALU_OP_SLTU, NOMEM, // SLTIU .flags = FLAGS_ALU_I}, {"ANDI", IT_I, ALU_OP_AND, NOMEM, // ANDI .flags = FLAGS_ALU_I_ZE}, {"ORI", IT_I, ALU_OP_OR, NOMEM, // ORI .flags = FLAGS_ALU_I_ZE}, {"XORI", IT_I, ALU_OP_XOR, NOMEM, // XORI .flags = FLAGS_ALU_I_ZE}, {"LUI", IT_I, ALU_OP_LUI, NOMEM, // LUI .flags = FLAGS_ALU_I}, IM_UNKNOWN, // 16 IM_UNKNOWN, // 17 IM_UNKNOWN, // 18 IM_UNKNOWN, // 19 IM_UNKNOWN, // 20 IM_UNKNOWN, // 21 IM_UNKNOWN, // 22 IM_UNKNOWN, // 23 IM_UNKNOWN, // 24 IM_UNKNOWN, // 25 IM_UNKNOWN, // 26 IM_UNKNOWN, // 27 IM_UNKNOWN, // 28 IM_UNKNOWN, // 29 IM_UNKNOWN, // 30 IM_UNKNOWN, // 31 {"LB", IT_I, ALU_OP_ADDU, AC_BYTE, // LB .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM}, {"LH", IT_I, ALU_OP_ADDU, AC_HALFWORD, // LH .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM}, {"LWL", IT_I, ALU_OP_ADDU, NOMEM, // LWL - unsupported .flags = IMF_MEM}, {"LW", IT_I, ALU_OP_ADDU, AC_WORD, // LW .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM}, {"LBU", IT_I, ALU_OP_ADDU, AC_BYTE_UNSIGNED, // LBU .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM }, {"LHU", IT_I, ALU_OP_ADDU, AC_HALFWORD_UNSIGNED, // LHU .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM,}, {"LWR", IT_I, ALU_OP_ADDU, NOMEM, // LWR - unsupported .flags = IMF_MEM}, IM_UNKNOWN, // 39 {"SB", IT_I, ALU_OP_ADDU, AC_BYTE, // SB .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_MEMWRITE | IMF_MEM | IMF_MEM_STORE}, {"SH", IT_I, ALU_OP_ADDU, AC_HALFWORD, // SH .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_MEMWRITE | IMF_MEM | IMF_MEM_STORE}, {"SWL", IT_I, ALU_OP_ADDU, NOMEM, // SWL .flags = IMF_MEM | IMF_MEM_STORE}, {"SW", IT_I, ALU_OP_ADDU, AC_WORD, // SW .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_MEMWRITE | IMF_MEM | IMF_MEM_STORE}, IM_UNKNOWN, // 44,NOPE, // 44 IM_UNKNOWN, // 45,NOPE, // 45 {"SWR", IT_I, ALU_OP_ADDU, NOMEM, // SWR .flags = IMF_MEM | IMF_MEM_STORE}, {"CACHE", IT_I, ALU_OP_ADDU, AC_CACHE_OP, // CACHE .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_MEM| IMF_MEM_STORE}, IM_UNKNOWN, // 48 IM_UNKNOWN, // 49 IM_UNKNOWN, // 50 IM_UNKNOWN, // 51 IM_UNKNOWN, // 52 IM_UNKNOWN, // 53 IM_UNKNOWN, // 54 IM_UNKNOWN, // 55 IM_UNKNOWN, // 56 IM_UNKNOWN, // 57 IM_UNKNOWN, // 58 IM_UNKNOWN, // 59 IM_UNKNOWN, // 60 IM_UNKNOWN, // 61 IM_UNKNOWN, // 62 IM_UNKNOWN, // 63 }; #undef IM_UNKNOWN struct AluInstructionMap { const char *name; unsigned int flags; }; #define AIM_UNKNOWN {"UNKNOWN"} // This table is indexed by funct static const struct AluInstructionMap alu_instruction_map[] = { {"SLL", FLAGS_ALU_T_R_STD}, AIM_UNKNOWN, {"SRL", FLAGS_ALU_T_R_SD}, {"SRA", FLAGS_ALU_T_R_SD}, {"SLLV", FLAGS_ALU_T_R_STD}, AIM_UNKNOWN, {"SRLV", FLAGS_ALU_T_R_STD}, {"SRAV", FLAGS_ALU_T_R_STD}, {"JR", FLAGS_ALU_T_R_S}, {"JALR", FLAGS_ALU_T_R_SD}, {"MOVZ", FLAGS_ALU_T_R_STD}, {"MOVN", FLAGS_ALU_T_R_STD}, AIM_UNKNOWN, {"BREAK", IMF_SUPPORTED}, AIM_UNKNOWN, AIM_UNKNOWN, {"MFHI", FLAGS_ALU_T_R_D | IMF_READ_HILO}, {"MTHI", FLAGS_ALU_T_R_S | IMF_WRITE_HILO}, {"MFLO", FLAGS_ALU_T_R_D | IMF_READ_HILO}, {"MTLO", FLAGS_ALU_T_R_S | IMF_WRITE_HILO}, AIM_UNKNOWN, AIM_UNKNOWN, AIM_UNKNOWN, AIM_UNKNOWN, {"MULT", FLAGS_ALU_T_R_ST | IMF_WRITE_HILO}, // 24 {"MULTU", FLAGS_ALU_T_R_ST | IMF_WRITE_HILO}, // 25 {"DIV", FLAGS_ALU_T_R_ST | IMF_WRITE_HILO}, // 26 {"DIVU", FLAGS_ALU_T_R_ST | IMF_WRITE_HILO}, // 27 AIM_UNKNOWN, // 28 AIM_UNKNOWN, // 29 AIM_UNKNOWN, // 30 AIM_UNKNOWN, // 31 {"ADD", FLAGS_ALU_T_R_STD}, // 32 {"ADDU", FLAGS_ALU_T_R_STD}, {"SUB", FLAGS_ALU_T_R_STD}, {"SUBU", FLAGS_ALU_T_R_STD}, {"AND", FLAGS_ALU_T_R_STD}, {"OR", FLAGS_ALU_T_R_STD}, {"XOR", FLAGS_ALU_T_R_STD}, {"NOR", FLAGS_ALU_T_R_STD}, AIM_UNKNOWN, AIM_UNKNOWN, {"SLT", FLAGS_ALU_T_R_STD}, {"SLTU", FLAGS_ALU_T_R_STD} }; #undef AIM_UNKNOWN Instruction::Instruction() { this->dt = 0; } Instruction::Instruction(std::uint32_t inst) { this->dt = inst; } Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct) { this->dt = 0; this->dt |= opcode << 26; this->dt |= rs << 21; this->dt |= rt << 16; this->dt |= rd << 11; this->dt |= shamt << 6; this->dt |= funct; } Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) { this->dt = 0; this->dt |= opcode << 26; this->dt |= rs << 21; this->dt |= rt << 16; this->dt |= immediate; } Instruction::Instruction(std::uint8_t opcode, std::uint32_t address) { this->dt = 0; this->dt |= opcode << 26; this->dt |= address; } Instruction::Instruction(const Instruction &i) { this->dt = i.data(); } #define MASK(LEN,OFF) ((this->dt >> (OFF)) & ((1 << (LEN)) - 1)) std::uint8_t Instruction::opcode() const { return (std::uint8_t) MASK(6, 26); } std::uint8_t Instruction::rs() const { return (std::uint8_t) MASK(5, 21); } std::uint8_t Instruction::rt() const { return (std::uint8_t) MASK(5, 16); } std::uint8_t Instruction::rd() const { return (std::uint8_t) MASK(5, 11); } std::uint8_t Instruction::shamt() const { return (std::uint8_t) MASK(5, 6); } std::uint8_t Instruction::funct() const { return (std::uint8_t) MASK(6, 0); } std::uint16_t Instruction::immediate() const { return (std::uint16_t) MASK(16, 0); } std::uint32_t Instruction::address() const { return (std::uint32_t) MASK(26, 0); } std::uint32_t Instruction::data() const { return this->dt; } enum Instruction::Type Instruction::type() const { if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return T_UNKNOWN; const struct InstructionMap &im = instruction_map[opcode()]; return im.type; } enum InstructionFlags Instruction::flags() const { if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return (enum InstructionFlags)0; if (opcode() == 0) { return (enum InstructionFlags)alu_instruction_map[funct()].flags; } const struct InstructionMap &im = instruction_map[opcode()]; return (enum InstructionFlags)im.flags; } enum AluOp Instruction::alu_op() const { if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return ALU_OP_UNKNOWN; const struct InstructionMap &im = instruction_map[opcode()]; return im.alu; } enum AccessControl Instruction::mem_ctl() const { if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return AC_NONE; const struct InstructionMap &im = instruction_map[opcode()]; return im.mem_ctl; } bool Instruction::is_store() const { if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return false; const struct InstructionMap &im = instruction_map[opcode()]; return im.flags & IMF_MEM_STORE; } bool Instruction::is_break() const { return opcode() == 0 && funct() == ALU_OP_BREAK; } bool Instruction::operator==(const Instruction &c) const { return (this->data() == c.data()); } bool Instruction::operator!=(const Instruction &c) const { return ! this->operator==(c); } Instruction &Instruction::operator=(const Instruction &c) { if (this != &c) this->dt = c.data(); return *this; } QString Instruction::to_str() const { // TODO there are exception where some fields are zero and such so we should not print them in such case if (opcode() >= (sizeof(instruction_map) / sizeof(struct InstructionMap))) return QString("UNKNOWN"); if (dt == 0) return QString("NOP"); const struct InstructionMap &im = instruction_map[opcode()]; QString res; switch (im.type) { case T_I: res += im.name; if (im.flags & IMF_MEM) { res += " $" + QString::number(rt()); res += ", 0x" + QString::number(immediate(), 16).toUpper() + "(" + QString::number(rs()) + ")"; } else { res += " $" + QString::number(rt()); if (!(im.flags & IMF_NO_RS)) res += ", $" + QString::number(rs()); res += ", 0x" + QString::number(immediate(), 16).toUpper(); } break; case T_J: res += im.name; // TODO we need to know instruction address to expand address section by it res += " 0x" + QString::number(address(), 16).toUpper(); break; case T_R: { // Note that all R instructions we support has opcode == 0 and so they are processed by alu table if (funct() >= (sizeof(alu_instruction_map) / sizeof(struct AluInstructionMap))) return QString("UNKNOWN"); const struct AluInstructionMap &am = alu_instruction_map[funct()]; res += am.name; res += " $" + QString::number(rd()) + ", $" + QString::number(rs()) + ", $" + QString::number(rt()); break; } case T_UNKNOWN: return QString("UNKNOWN"); } return res; }