thumb: add disassembler

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2023-09-27 17:31:00 +05:30
parent 208527b7f8
commit 5ec5e6dddc
13 changed files with 716 additions and 71 deletions

View File

@@ -1,8 +1,7 @@
#include "instruction.hh"
#include "util/bits.hh"
namespace matar {
namespace arm {
namespace matar::arm {
std::string
Instruction::disassemble() {
auto condition = stringify(this->condition);
@@ -232,4 +231,3 @@ Instruction::disassemble() {
data);
}
}
}

View File

@@ -2,8 +2,7 @@
#include "util/bits.hh"
#include <iterator>
namespace matar {
namespace arm {
namespace matar::arm {
Instruction::Instruction(uint32_t insn)
: condition(static_cast<Condition>(bit_range(insn, 28, 31))) {
// Branch and exhcange
@@ -275,4 +274,3 @@ Instruction::Instruction(uint32_t insn)
}
}
}
}

View File

@@ -5,8 +5,7 @@
#include <fmt/ostream.h>
#include <variant>
namespace matar {
namespace arm {
namespace matar::arm {
// https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts>
@@ -223,4 +222,3 @@ struct Instruction {
#endif
};
}
}

View File

@@ -3,6 +3,7 @@
#include "util/log.hh"
#include <algorithm>
#include <cstdio>
#include <type_traits>
namespace matar {
CpuImpl::CpuImpl(const Bus& bus) noexcept

View File

@@ -15,6 +15,7 @@ class CpuImpl {
void chg_mode(const Mode to);
void exec(const arm::Instruction instruction);
// TODO: get rid of this
#ifndef MATAR_CPU_TESTS
private:
#endif

View File

@@ -0,0 +1,150 @@
#include "instruction.hh"
#include "util/bits.hh"
namespace matar::thumb {
std::string
Instruction::disassemble() {
return std::visit(
overloaded{
[](MoveShiftedRegister& data) {
return fmt::format("{} R{:d},R{:d},#{:d}",
stringify(data.opcode),
data.rd,
data.rs,
data.offset);
},
[](AddSubtract& data) {
return fmt::format("{} R{:d},R{:d},{}{:d}",
stringify(data.opcode),
data.rd,
data.rs,
(data.imm ? '#' : 'R'),
data.offset);
},
[](MovCmpAddSubImmediate& data) {
return fmt::format(
"{} R{:d},#{:d}", stringify(data.opcode), data.rd, data.offset);
},
[](AluOperations& data) {
return fmt::format(
"{} R{:d},R{:d}", stringify(data.opcode), data.rd, data.rs);
},
[](HiRegisterOperations& data) {
if (data.opcode == HiRegisterOperations::OpCode::BX) {
return fmt::format("{} R{:d}", stringify(data.opcode), data.rs);
}
return fmt::format(
"{} R{:d},R{:d}", stringify(data.opcode), data.rd, data.rs);
},
[](PcRelativeLoad& data) {
return fmt::format("LDR R{:d},[PC,#{:d}]", data.rd, data.word);
},
[](LoadStoreRegisterOffset& data) {
return fmt::format("{}{} R{:d},[R{:d},R{:d}]",
(data.load ? "LDR" : "STR"),
(data.byte ? "B" : ""),
data.rd,
data.rb,
data.ro);
},
[](LoadStoreSignExtendedHalfword& data) {
if (!data.s && !data.h) {
return fmt::format(
"STRH R{:d},[R{:d},R{:d}]", data.rd, data.rb, data.ro);
}
return fmt::format("{}{} R{:d},[R{:d},R{:d}]",
(data.s ? "LDS" : "LDR"),
(data.h ? 'H' : 'B'),
data.rd,
data.rb,
data.ro);
},
[](LoadStoreImmediateOffset& data) {
return fmt::format("{}{} R{:d},[R{:d},#{:d}]",
(data.load ? "LDR" : "STR"),
(data.byte ? "B" : ""),
data.rd,
data.rb,
data.offset);
},
[](LoadStoreHalfword& data) {
return fmt::format("{} R{:d},[R{:d},#{:d}]",
(data.load ? "LDRH" : "STRH"),
data.rd,
data.rb,
data.offset);
},
[](SpRelativeLoad& data) {
return fmt::format("{} R{:d},[SP,#{:d}]",
(data.load ? "LDR" : "STR"),
data.rd,
data.word);
},
[](LoadAddress& data) {
return fmt::format("ADD R{:d},{},#{:d}",
data.rd,
(data.sp ? "SP" : "PC"),
data.word);
},
[](AddOffsetStackPointer& data) {
return fmt::format(
"ADD SP,#{}{:d}", (data.sign ? '-' : '+'), data.word);
},
[](PushPopRegister& data) {
std::string regs;
for (uint8_t i = 0; i < 16; i++) {
if (get_bit(data.regs, i))
fmt::format_to(std::back_inserter(regs), "R{:d},", i);
};
if (data.load) {
if (data.pclr)
regs += "PC";
else
regs.pop_back();
return fmt::format("POP {{{}}}", regs);
} else {
if (data.pclr)
regs += "LR";
else
regs.pop_back();
return fmt::format("PUSH {{{}}}", regs);
}
},
[](MultipleLoad& data) {
std::string regs;
for (uint8_t i = 0; i < 16; i++) {
if (get_bit(data.regs, i))
fmt::format_to(std::back_inserter(regs), "R{:d},", i);
};
regs.pop_back();
return fmt::format(
"{} R{}!,{{{}}}", (data.load ? "LDMIA" : "STMIA"), data.rb, regs);
},
[](SoftwareInterrupt) { return std::string("SWI"); },
[](ConditionalBranch& data) {
return fmt::format("B{} {:d}",
stringify(data.condition),
data.offset);
},
[](UnconditionalBranch& data) {
return fmt::format("B {:d}", data.offset);
},
[](LongBranchWithLink& data) {
// duh this manual be empty for H = 0
return fmt::format(
"BL{} {:d}", (data.high ? "H" : ""), data.offset);
},
[](auto) { return std::string("unknown instruction"); } },
data);
}
}

View File

@@ -1,24 +1,10 @@
#include "instruction.hh"
#include "util/bits.hh"
#include <iterator>
namespace matar {
namespace thumb {
namespace matar::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) {
// Format 2: Add/Subtract
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);
@@ -30,6 +16,17 @@ Instruction::Instruction(uint16_t insn) {
.rd = rd, .rs = rs, .offset = offset, .opcode = opcode, .imm = imm
};
// Format 1: Move Shifted Register
} else 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 3: Move/compare/add/subtract immediate
} else if ((insn & 0xE000) == 0x2000) {
uint8_t offset = bit_range(insn, 0, 7);
@@ -58,9 +55,10 @@ Instruction::Instruction(uint16_t insn) {
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
};
rd += (hi_1 ? LO_GPR_COUNT : 0);
rs += (hi_2 ? LO_GPR_COUNT : 0);
data = HiRegisterOperations{ .rd = rd, .rs = rs, .opcode = opcode };
// Format 6: PC-relative load
} else if ((insn & 0xF800) == 0x4800) {
uint8_t word = bit_range(insn, 0, 7);
@@ -168,24 +166,26 @@ Instruction::Instruction(uint16_t insn) {
// Format 16: Conditional branch
} else if ((insn & 0xF000) == 0xD000) {
uint8_t offset = bit_range(insn, 0, 7);
uint16_t offset = bit_range(insn, 0, 7);
Condition condition = static_cast<Condition>(bit_range(insn, 8, 11));
data = ConditionalBranch{ .offset = offset, .condition = condition };
data = ConditionalBranch{ .offset = static_cast<uint16_t>(offset << 1),
.condition = condition };
// Format 18: Unconditional branch
} else if ((insn & 0xF800) == 0xE000) {
uint16_t offset = bit_range(insn, 0, 10);
data = UnconditionalBranch{ .offset = offset };
data =
UnconditionalBranch{ .offset = static_cast<uint16_t>(offset << 1) };
// 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 };
data = LongBranchWithLink{ .offset = static_cast<uint16_t>(offset << 1),
.high = high };
}
}
}
}

View File

@@ -1,13 +1,14 @@
#pragma once
#include "cpu/alu.hh"
#include "cpu/psr.hh"
#include <cstdint>
#include <fmt/ostream.h>
#include <variant>
namespace matar {
namespace thumb {
namespace matar::thumb {
// https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
@@ -16,6 +17,7 @@ template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
static constexpr size_t INSTRUCTION_SIZE = 2;
static constexpr uint8_t LO_GPR_COUNT = 8;
struct MoveShiftedRegister {
uint8_t rd;
@@ -37,6 +39,21 @@ struct AddSubtract {
bool imm;
};
constexpr auto
stringify(AddSubtract::OpCode opcode) {
#define CASE(opcode) \
case AddSubtract::OpCode::opcode: \
return #opcode;
switch (opcode) {
CASE(ADD)
CASE(SUB)
}
#undef CASE
return "";
}
struct MovCmpAddSubImmediate {
enum class OpCode {
MOV = 0b00,
@@ -50,6 +67,23 @@ struct MovCmpAddSubImmediate {
OpCode opcode;
};
constexpr auto
stringify(MovCmpAddSubImmediate::OpCode opcode) {
#define CASE(opcode) \
case MovCmpAddSubImmediate::OpCode::opcode: \
return #opcode;
switch (opcode) {
CASE(MOV)
CASE(CMP)
CASE(ADD)
CASE(SUB)
}
#undef CASE
return "";
}
struct AluOperations {
enum class OpCode {
AND = 0b0000,
@@ -75,6 +109,36 @@ struct AluOperations {
OpCode opcode;
};
constexpr auto
stringify(AluOperations::OpCode opcode) {
#define CASE(opcode) \
case AluOperations::OpCode::opcode: \
return #opcode;
switch (opcode) {
CASE(AND)
CASE(EOR)
CASE(LSL)
CASE(LSR)
CASE(ASR)
CASE(ADC)
CASE(SBC)
CASE(ROR)
CASE(TST)
CASE(NEG)
CASE(CMP)
CASE(CMN)
CASE(ORR)
CASE(MUL)
CASE(BIC)
CASE(MVN)
}
#undef CASE
return "";
}
struct HiRegisterOperations {
enum class OpCode {
ADD = 0b00,
@@ -85,11 +149,26 @@ struct HiRegisterOperations {
uint8_t rd;
uint8_t rs;
bool hi_2;
bool hi_1;
OpCode opcode;
};
constexpr auto
stringify(HiRegisterOperations::OpCode opcode) {
#define CASE(opcode) \
case HiRegisterOperations::OpCode::opcode: \
return #opcode;
switch (opcode) {
CASE(ADD)
CASE(CMP)
CASE(MOV)
CASE(BX)
}
#undef CASE
return "";
}
struct PcRelativeLoad {
uint8_t word;
uint8_t rd;
@@ -156,7 +235,7 @@ struct MultipleLoad {
};
struct ConditionalBranch {
uint8_t offset;
uint16_t offset;
Condition condition;
};
@@ -196,35 +275,8 @@ struct Instruction {
Instruction(uint16_t insn);
#ifdef DISASSEMBLER
std::string disassemble();
#endif
};
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 {};
}

View File

@@ -1,3 +1,7 @@
lib_sources += files(
'instruction.cc'
)
)
if get_option('disassembler')
lib_sources += files('disassembler.cc')
endif

View File

@@ -6,7 +6,7 @@ lib_sources = files(
subdir('util')
subdir('cpu')
lib_cpp_args = [ ]
lib_cpp_args = []
fmt = dependency('fmt', version : '>=10.1.0', static: true)
if not fmt.found()