From 208527b7f85a3a414fd5182cd02c48f0567a7354 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Sat, 23 Sep 2023 22:05:20 +0530 Subject: [PATCH] thumb: initialise instruction formats Signed-off-by: Amneesh Singh --- src/cpu/meson.build | 3 +- src/cpu/thumb/instruction.cc | 191 +++++++++++++++++++++++++++++ src/cpu/thumb/instruction.hh | 230 +++++++++++++++++++++++++++++++++++ src/cpu/thumb/meson.build | 3 + 4 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 src/cpu/thumb/instruction.cc create mode 100644 src/cpu/thumb/instruction.hh create mode 100644 src/cpu/thumb/meson.build diff --git a/src/cpu/meson.build b/src/cpu/meson.build index 8d83065..64368a2 100644 --- a/src/cpu/meson.build +++ b/src/cpu/meson.build @@ -5,4 +5,5 @@ lib_sources += files( 'alu.cc' ) -subdir('arm') \ No newline at end of file +subdir('arm') +subdir('thumb') \ No newline at end of file diff --git a/src/cpu/thumb/instruction.cc b/src/cpu/thumb/instruction.cc new file mode 100644 index 0000000..d9b769b --- /dev/null +++ b/src/cpu/thumb/instruction.cc @@ -0,0 +1,191 @@ +#include "instruction.hh" +#include "util/bits.hh" +#include + +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(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(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(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(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(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(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 }; + } +} +} +} diff --git a/src/cpu/thumb/instruction.hh b/src/cpu/thumb/instruction.hh new file mode 100644 index 0000000..1f9cba7 --- /dev/null +++ b/src/cpu/thumb/instruction.hh @@ -0,0 +1,230 @@ +#pragma once +#include "cpu/alu.hh" +#include "cpu/psr.hh" +#include +#include +#include + +namespace matar { +namespace thumb { + +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...) -> overloaded; + +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; + +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 : ostream_formatter {}; + +template<> +struct formatter + : ostream_formatter {}; + +template<> +struct formatter : ostream_formatter {}; + +template<> +struct formatter + : ostream_formatter {}; +} diff --git a/src/cpu/thumb/meson.build b/src/cpu/thumb/meson.build new file mode 100644 index 0000000..f030302 --- /dev/null +++ b/src/cpu/thumb/meson.build @@ -0,0 +1,3 @@ +lib_sources += files( + 'instruction.cc' +) \ No newline at end of file