thumb: initialise instruction formats
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -6,3 +6,4 @@ lib_sources += files(
|
||||
)
|
||||
|
||||
subdir('arm')
|
||||
subdir('thumb')
|
191
src/cpu/thumb/instruction.cc
Normal file
191
src/cpu/thumb/instruction.cc
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "instruction.hh"
|
||||
#include "util/bits.hh"
|
||||
#include <iterator>
|
||||
|
||||
namespace matar {
|
||||
namespace thumb {
|
||||
|
||||
Instruction::Instruction(uint16_t insn) {
|
||||
// Format 1: Move Shifted Register
|
||||
if ((insn & 0xE000) == 0x0000) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rs = bit_range(insn, 3, 5);
|
||||
uint8_t offset = bit_range(insn, 6, 10);
|
||||
ShiftType opcode = static_cast<ShiftType>(bit_range(insn, 11, 12));
|
||||
|
||||
data = MoveShiftedRegister{
|
||||
.rd = rd, .rs = rs, .offset = offset, .opcode = opcode
|
||||
};
|
||||
|
||||
// Format 2: Add/Subtract
|
||||
} else if ((insn & 0xF800) == 0x1800) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rs = bit_range(insn, 3, 5);
|
||||
uint8_t offset = bit_range(insn, 6, 8);
|
||||
AddSubtract::OpCode opcode =
|
||||
static_cast<AddSubtract::OpCode>(get_bit(insn, 9));
|
||||
bool imm = get_bit(insn, 10);
|
||||
|
||||
data = AddSubtract{
|
||||
.rd = rd, .rs = rs, .offset = offset, .opcode = opcode, .imm = imm
|
||||
};
|
||||
|
||||
// Format 3: Move/compare/add/subtract immediate
|
||||
} else if ((insn & 0xE000) == 0x2000) {
|
||||
uint8_t offset = bit_range(insn, 0, 7);
|
||||
uint8_t rd = bit_range(insn, 8, 10);
|
||||
MovCmpAddSubImmediate::OpCode opcode =
|
||||
static_cast<MovCmpAddSubImmediate::OpCode>(bit_range(insn, 11, 12));
|
||||
|
||||
data =
|
||||
MovCmpAddSubImmediate{ .offset = offset, .rd = rd, .opcode = opcode };
|
||||
|
||||
// Format 4: ALU operations
|
||||
} else if ((insn & 0xFC00) == 0x4000) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rs = bit_range(insn, 3, 5);
|
||||
AluOperations::OpCode opcode =
|
||||
static_cast<AluOperations::OpCode>(bit_range(insn, 6, 9));
|
||||
|
||||
data = AluOperations{ .rd = rd, .rs = rs, .opcode = opcode };
|
||||
|
||||
// Format 5: Hi register operations/branch exchange
|
||||
} else if ((insn & 0xFC00) == 0x4400) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rs = bit_range(insn, 3, 5);
|
||||
bool hi_2 = get_bit(insn, 6);
|
||||
bool hi_1 = get_bit(insn, 7);
|
||||
HiRegisterOperations::OpCode opcode =
|
||||
static_cast<HiRegisterOperations::OpCode>(bit_range(insn, 8, 9));
|
||||
|
||||
data = HiRegisterOperations{
|
||||
.rd = rd, .rs = rs, .hi_2 = hi_2, .hi_1 = hi_1, .opcode = opcode
|
||||
};
|
||||
// Format 6: PC-relative load
|
||||
} else if ((insn & 0xF800) == 0x4800) {
|
||||
uint8_t word = bit_range(insn, 0, 7);
|
||||
uint8_t rd = bit_range(insn, 8, 10);
|
||||
|
||||
data = PcRelativeLoad{ .word = word, .rd = rd };
|
||||
|
||||
// Format 7: Load/store with register offset
|
||||
} else if ((insn & 0xF200) == 0x5000) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rb = bit_range(insn, 3, 5);
|
||||
uint8_t ro = bit_range(insn, 6, 8);
|
||||
bool byte = get_bit(insn, 10);
|
||||
bool load = get_bit(insn, 11);
|
||||
|
||||
data = LoadStoreRegisterOffset{
|
||||
.rd = rd, .rb = rb, .ro = ro, .byte = byte, .load = load
|
||||
};
|
||||
|
||||
// Format 8: Load/store sign-extended byte/halfword
|
||||
} else if ((insn & 0xF200) == 0x5200) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rb = bit_range(insn, 3, 5);
|
||||
uint8_t ro = bit_range(insn, 6, 8);
|
||||
bool s = get_bit(insn, 10);
|
||||
bool h = get_bit(insn, 11);
|
||||
|
||||
data = LoadStoreSignExtendedHalfword{
|
||||
.rd = rd, .rb = rb, .ro = ro, .s = s, .h = h
|
||||
};
|
||||
|
||||
// Format 9: Load/store with immediate offset
|
||||
} else if ((insn & 0xF000) == 0x6000) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rb = bit_range(insn, 3, 5);
|
||||
uint8_t offset = bit_range(insn, 6, 10);
|
||||
bool load = get_bit(insn, 11);
|
||||
bool byte = get_bit(insn, 12);
|
||||
|
||||
data = LoadStoreImmediateOffset{
|
||||
.rd = rd, .rb = rb, .offset = offset, .load = load, .byte = byte
|
||||
};
|
||||
|
||||
// Format 10: Load/store halfword
|
||||
} else if ((insn & 0xF000) == 0x8000) {
|
||||
uint8_t rd = bit_range(insn, 0, 2);
|
||||
uint8_t rb = bit_range(insn, 3, 5);
|
||||
uint8_t offset = bit_range(insn, 6, 10);
|
||||
bool load = get_bit(insn, 11);
|
||||
|
||||
data = LoadStoreHalfword{
|
||||
.rd = rd, .rb = rb, .offset = offset, .load = load
|
||||
};
|
||||
|
||||
// Format 11: SP-relative load/store
|
||||
} else if ((insn & 0xF000) == 0x9000) {
|
||||
uint8_t word = bit_range(insn, 0, 7);
|
||||
uint8_t rd = bit_range(insn, 8, 10);
|
||||
bool load = get_bit(insn, 11);
|
||||
|
||||
data = SpRelativeLoad{ .word = word, .rd = rd, .load = load };
|
||||
|
||||
// Format 12: Load address
|
||||
} else if ((insn & 0xF000) == 0xA000) {
|
||||
uint8_t word = bit_range(insn, 0, 7);
|
||||
uint8_t rd = bit_range(insn, 8, 10);
|
||||
bool sp = get_bit(insn, 11);
|
||||
|
||||
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
|
||||
|
||||
// Format 12: Load address
|
||||
} else if ((insn & 0xF000) == 0xA000) {
|
||||
uint8_t word = bit_range(insn, 0, 7);
|
||||
uint8_t rd = bit_range(insn, 8, 10);
|
||||
bool sp = get_bit(insn, 11);
|
||||
|
||||
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
|
||||
|
||||
// Format 13: Add offset to stack pointer
|
||||
} else if ((insn & 0xFF00) == 0xB000) {
|
||||
uint8_t word = bit_range(insn, 0, 6);
|
||||
bool sign = get_bit(insn, 7);
|
||||
|
||||
data = AddOffsetStackPointer{ .word = word, .sign = sign };
|
||||
|
||||
// Format 14: Push/pop registers
|
||||
} else if ((insn & 0xF600) == 0xB400) {
|
||||
uint8_t regs = bit_range(insn, 0, 7);
|
||||
bool pclr = get_bit(insn, 8);
|
||||
bool load = get_bit(insn, 11);
|
||||
|
||||
data = PushPopRegister{ .regs = regs, .pclr = pclr, .load = load };
|
||||
|
||||
// Format 15: Multiple load/store
|
||||
} else if ((insn & 0xF000) == 0xC000) {
|
||||
uint8_t regs = bit_range(insn, 0, 7);
|
||||
uint8_t rb = bit_range(insn, 8, 10);
|
||||
bool load = get_bit(insn, 11);
|
||||
|
||||
data = MultipleLoad{ .regs = regs, .rb = rb, .load = load };
|
||||
|
||||
// Format 17: Software interrupt
|
||||
} else if ((insn & 0xFF00) == 0xDF00) {
|
||||
data = SoftwareInterrupt{};
|
||||
|
||||
// Format 16: Conditional branch
|
||||
} else if ((insn & 0xF000) == 0xD000) {
|
||||
uint8_t offset = bit_range(insn, 0, 7);
|
||||
Condition condition = static_cast<Condition>(bit_range(insn, 8, 11));
|
||||
|
||||
data = ConditionalBranch{ .offset = offset, .condition = condition };
|
||||
|
||||
// Format 18: Unconditional branch
|
||||
} else if ((insn & 0xF800) == 0xE000) {
|
||||
uint16_t offset = bit_range(insn, 0, 10);
|
||||
|
||||
data = UnconditionalBranch{ .offset = offset };
|
||||
|
||||
// Format 19: Long branch with link
|
||||
} else if ((insn & 0xF000) == 0xF000) {
|
||||
uint16_t offset = bit_range(insn, 0, 10);
|
||||
bool high = get_bit(insn, 11);
|
||||
|
||||
data = LongBranchWithLink{ .offset = offset, .high = high };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
230
src/cpu/thumb/instruction.hh
Normal file
230
src/cpu/thumb/instruction.hh
Normal file
@@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
#include "cpu/alu.hh"
|
||||
#include "cpu/psr.hh"
|
||||
#include <cstdint>
|
||||
#include <fmt/ostream.h>
|
||||
#include <variant>
|
||||
|
||||
namespace matar {
|
||||
namespace thumb {
|
||||
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
static constexpr size_t INSTRUCTION_SIZE = 2;
|
||||
|
||||
struct MoveShiftedRegister {
|
||||
uint8_t rd;
|
||||
uint8_t rs;
|
||||
uint8_t offset;
|
||||
ShiftType opcode;
|
||||
};
|
||||
|
||||
struct AddSubtract {
|
||||
enum class OpCode {
|
||||
ADD = 0,
|
||||
SUB = 1
|
||||
};
|
||||
|
||||
uint8_t rd;
|
||||
uint8_t rs;
|
||||
uint8_t offset;
|
||||
OpCode opcode;
|
||||
bool imm;
|
||||
};
|
||||
|
||||
struct MovCmpAddSubImmediate {
|
||||
enum class OpCode {
|
||||
MOV = 0b00,
|
||||
CMP = 0b01,
|
||||
ADD = 0b10,
|
||||
SUB = 0b11
|
||||
};
|
||||
|
||||
uint8_t offset;
|
||||
uint8_t rd;
|
||||
OpCode opcode;
|
||||
};
|
||||
|
||||
struct AluOperations {
|
||||
enum class OpCode {
|
||||
AND = 0b0000,
|
||||
EOR = 0b0001,
|
||||
LSL = 0b0010,
|
||||
LSR = 0b0011,
|
||||
ASR = 0b0100,
|
||||
ADC = 0b0101,
|
||||
SBC = 0b0110,
|
||||
ROR = 0b0111,
|
||||
TST = 0b1000,
|
||||
NEG = 0b1001,
|
||||
CMP = 0b1010,
|
||||
CMN = 0b1011,
|
||||
ORR = 0b1100,
|
||||
MUL = 0b1101,
|
||||
BIC = 0b1110,
|
||||
MVN = 0b1111
|
||||
};
|
||||
|
||||
uint8_t rd;
|
||||
uint8_t rs;
|
||||
OpCode opcode;
|
||||
};
|
||||
|
||||
struct HiRegisterOperations {
|
||||
enum class OpCode {
|
||||
ADD = 0b00,
|
||||
CMP = 0b01,
|
||||
MOV = 0b10,
|
||||
BX = 0b11
|
||||
};
|
||||
|
||||
uint8_t rd;
|
||||
uint8_t rs;
|
||||
bool hi_2;
|
||||
bool hi_1;
|
||||
OpCode opcode;
|
||||
};
|
||||
|
||||
struct PcRelativeLoad {
|
||||
uint8_t word;
|
||||
uint8_t rd;
|
||||
};
|
||||
|
||||
struct LoadStoreRegisterOffset {
|
||||
uint8_t rd;
|
||||
uint8_t rb;
|
||||
uint8_t ro;
|
||||
bool byte;
|
||||
bool load;
|
||||
};
|
||||
|
||||
struct LoadStoreSignExtendedHalfword {
|
||||
uint8_t rd;
|
||||
uint8_t rb;
|
||||
uint8_t ro;
|
||||
bool s;
|
||||
bool h;
|
||||
};
|
||||
|
||||
struct LoadStoreImmediateOffset {
|
||||
uint8_t rd;
|
||||
uint8_t rb;
|
||||
uint8_t offset;
|
||||
bool load;
|
||||
bool byte;
|
||||
};
|
||||
|
||||
struct LoadStoreHalfword {
|
||||
uint8_t rd;
|
||||
uint8_t rb;
|
||||
uint8_t offset;
|
||||
bool load;
|
||||
};
|
||||
|
||||
struct SpRelativeLoad {
|
||||
uint8_t word;
|
||||
uint8_t rd;
|
||||
bool load;
|
||||
};
|
||||
|
||||
struct LoadAddress {
|
||||
uint8_t word;
|
||||
uint8_t rd;
|
||||
bool sp;
|
||||
};
|
||||
|
||||
struct AddOffsetStackPointer {
|
||||
uint8_t word;
|
||||
bool sign;
|
||||
};
|
||||
|
||||
struct PushPopRegister {
|
||||
uint8_t regs;
|
||||
bool pclr;
|
||||
bool load;
|
||||
};
|
||||
|
||||
struct MultipleLoad {
|
||||
uint8_t regs;
|
||||
uint8_t rb;
|
||||
bool load;
|
||||
};
|
||||
|
||||
struct ConditionalBranch {
|
||||
uint8_t offset;
|
||||
Condition condition;
|
||||
};
|
||||
|
||||
struct SoftwareInterrupt {};
|
||||
|
||||
struct UnconditionalBranch {
|
||||
uint16_t offset;
|
||||
};
|
||||
|
||||
struct LongBranchWithLink {
|
||||
uint16_t offset;
|
||||
bool high;
|
||||
};
|
||||
|
||||
using InstructionData = std::variant<MoveShiftedRegister,
|
||||
AddSubtract,
|
||||
MovCmpAddSubImmediate,
|
||||
AluOperations,
|
||||
HiRegisterOperations,
|
||||
PcRelativeLoad,
|
||||
LoadStoreRegisterOffset,
|
||||
LoadStoreSignExtendedHalfword,
|
||||
LoadStoreImmediateOffset,
|
||||
LoadStoreHalfword,
|
||||
SpRelativeLoad,
|
||||
LoadAddress,
|
||||
AddOffsetStackPointer,
|
||||
PushPopRegister,
|
||||
MultipleLoad,
|
||||
ConditionalBranch,
|
||||
SoftwareInterrupt,
|
||||
UnconditionalBranch,
|
||||
LongBranchWithLink>;
|
||||
|
||||
struct Instruction {
|
||||
InstructionData data;
|
||||
|
||||
Instruction(uint16_t insn);
|
||||
|
||||
std::string disassemble();
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const AddSubtract::OpCode cond);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const MovCmpAddSubImmediate::OpCode cond);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const AluOperations::OpCode cond);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const HiRegisterOperations::OpCode cond);
|
||||
}
|
||||
}
|
||||
|
||||
namespace fmt {
|
||||
template<>
|
||||
struct formatter<matar::thumb::AddSubtract::OpCode> : ostream_formatter {};
|
||||
|
||||
template<>
|
||||
struct formatter<matar::thumb::MovCmpAddSubImmediate::OpCode>
|
||||
: ostream_formatter {};
|
||||
|
||||
template<>
|
||||
struct formatter<matar::thumb::AluOperations::OpCode> : ostream_formatter {};
|
||||
|
||||
template<>
|
||||
struct formatter<matar::thumb::HiRegisterOperations::OpCode>
|
||||
: ostream_formatter {};
|
||||
}
|
3
src/cpu/thumb/meson.build
Normal file
3
src/cpu/thumb/meson.build
Normal file
@@ -0,0 +1,3 @@
|
||||
lib_sources += files(
|
||||
'instruction.cc'
|
||||
)
|
Reference in New Issue
Block a user