refactor: make disassembler optional
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
235
src/cpu/arm/disassembler.cc
Normal file
235
src/cpu/arm/disassembler.cc
Normal file
@@ -0,0 +1,235 @@
|
||||
#include "instruction.hh"
|
||||
#include "util/bits.hh"
|
||||
|
||||
namespace matar {
|
||||
namespace arm {
|
||||
std::string
|
||||
Instruction::disassemble() {
|
||||
auto condition = stringify(this->condition);
|
||||
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[condition](BranchAndExchange& data) {
|
||||
return fmt::format("BX{} R{:d}", condition, data.rn);
|
||||
},
|
||||
[condition](Branch& data) {
|
||||
return fmt::format(
|
||||
"B{}{} 0x{:06X}", (data.link ? "L" : ""), condition, data.offset);
|
||||
},
|
||||
[condition](Multiply& data) {
|
||||
if (data.acc) {
|
||||
return fmt::format("MLA{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs,
|
||||
data.rn);
|
||||
} else {
|
||||
return fmt::format("MUL{}{} R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs);
|
||||
}
|
||||
},
|
||||
[condition](MultiplyLong& data) {
|
||||
return fmt::format("{}{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
(data.uns ? 'U' : 'S'),
|
||||
(data.acc ? "MLAL" : "MULL"),
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rdlo,
|
||||
data.rdhi,
|
||||
data.rm,
|
||||
data.rs);
|
||||
},
|
||||
[](Undefined) { return std::string("UND"); },
|
||||
[condition](SingleDataSwap& data) {
|
||||
return fmt::format("SWP{}{} R{:d},R{:d},[R{:d}]",
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rn);
|
||||
},
|
||||
[condition](SingleDataTransfer& data) {
|
||||
std::string expression;
|
||||
std::string address;
|
||||
|
||||
if (const uint16_t* offset = std::get_if<uint16_t>(&data.offset)) {
|
||||
if (*offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression =
|
||||
fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset);
|
||||
}
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||
// Shifts are always immediate in single data transfer
|
||||
expression = fmt::format(",{}R{:d},{} #{:d}",
|
||||
(data.up ? '+' : '-'),
|
||||
shift->rm,
|
||||
stringify(shift->data.type),
|
||||
shift->data.operand);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
(!data.pre && data.write ? "T" : ""),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[condition](HalfwordTransfer& data) {
|
||||
std::string expression;
|
||||
|
||||
if (data.imm) {
|
||||
if (data.offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression = fmt::format(
|
||||
",{}#{:d}", (data.up ? '+' : '-'), data.offset);
|
||||
}
|
||||
} else {
|
||||
expression =
|
||||
fmt::format(",{}R{:d}", (data.up ? '+' : '-'), data.offset);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.sign ? "S" : ""),
|
||||
(data.half ? 'H' : 'B'),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[condition](BlockDataTransfer& 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{:d}{},{{{}}}{}",
|
||||
(data.load ? "LDM" : "STM"),
|
||||
condition,
|
||||
(data.up ? 'I' : 'D'),
|
||||
(data.pre ? 'B' : 'A'),
|
||||
data.rn,
|
||||
(data.write ? "!" : ""),
|
||||
regs,
|
||||
(data.s ? "^" : ""));
|
||||
},
|
||||
[condition](PsrTransfer& data) {
|
||||
if (data.type == PsrTransfer::Type::Mrs) {
|
||||
return fmt::format("MRS{} R{:d},{}",
|
||||
condition,
|
||||
data.operand,
|
||||
(data.spsr ? "SPSR_all" : "CPSR_all"));
|
||||
} else {
|
||||
return fmt::format(
|
||||
"MSR{} {}_{},{}{}",
|
||||
condition,
|
||||
(data.spsr ? "SPSR" : "CPSR"),
|
||||
(data.type == PsrTransfer::Type::Msr_flg ? "flg" : "all"),
|
||||
(data.imm ? '#' : 'R'),
|
||||
data.operand);
|
||||
}
|
||||
},
|
||||
[condition](DataProcessing& data) {
|
||||
using OpCode = DataProcessing::OpCode;
|
||||
|
||||
std::string op_2;
|
||||
|
||||
if (const uint32_t* operand =
|
||||
std::get_if<uint32_t>(&data.operand)) {
|
||||
op_2 = fmt::format("#{:d}", *operand);
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
|
||||
op_2 = fmt::format("R{:d},{} {}{:d}",
|
||||
shift->rm,
|
||||
stringify(shift->data.type),
|
||||
(shift->data.immediate ? '#' : 'R'),
|
||||
shift->data.operand);
|
||||
}
|
||||
|
||||
switch (data.opcode) {
|
||||
case OpCode::MOV:
|
||||
case OpCode::MVN:
|
||||
return fmt::format("{}{}{} R{:d},{}",
|
||||
stringify(data.opcode),
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
op_2);
|
||||
case OpCode::TST:
|
||||
case OpCode::TEQ:
|
||||
case OpCode::CMP:
|
||||
case OpCode::CMN:
|
||||
return fmt::format("{}{} R{:d},{}",
|
||||
stringify(data.opcode),
|
||||
condition,
|
||||
data.rn,
|
||||
op_2);
|
||||
default:
|
||||
return fmt::format("{}{}{} R{:d},R{:d},{}",
|
||||
stringify(data.opcode),
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rn,
|
||||
op_2);
|
||||
}
|
||||
},
|
||||
[condition](SoftwareInterrupt) {
|
||||
return fmt::format("SWI{}", condition);
|
||||
},
|
||||
[condition](CoprocessorDataTransfer& data) {
|
||||
std::string expression = fmt::format(",#{:d}", data.offset);
|
||||
return fmt::format(
|
||||
"{}{}{} p{:d},c{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDC" : "STC"),
|
||||
condition,
|
||||
(data.len ? "L" : ""),
|
||||
data.cpn,
|
||||
data.crd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[condition](CoprocessorDataOperation& data) {
|
||||
return fmt::format("CDP{} p{},{},c{},c{},c{},{}",
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.crd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[condition](CoprocessorRegisterTransfer& data) {
|
||||
return fmt::format("{}{} p{},{},R{},c{},c{},{}",
|
||||
(data.load ? "MRC" : "MCR"),
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.rd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[](auto) { return std::string("unknown instruction"); } },
|
||||
data);
|
||||
}
|
||||
}
|
||||
}
|
@@ -274,230 +274,5 @@ Instruction::Instruction(uint32_t insn)
|
||||
data = Undefined{};
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Instruction::disassemble() {
|
||||
// goddamn this is gore
|
||||
// TODO: make this less ugly
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[this](BranchAndExchange& data) {
|
||||
return fmt::format("BX{} R{:d}", condition, data.rn);
|
||||
},
|
||||
[this](Branch& data) {
|
||||
return fmt::format(
|
||||
"B{}{} 0x{:06X}", (data.link ? "L" : ""), condition, data.offset);
|
||||
},
|
||||
[this](Multiply& data) {
|
||||
if (data.acc) {
|
||||
return fmt::format("MLA{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs,
|
||||
data.rn);
|
||||
} else {
|
||||
return fmt::format("MUL{}{} R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs);
|
||||
}
|
||||
},
|
||||
[this](MultiplyLong& data) {
|
||||
return fmt::format("{}{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
(data.uns ? 'U' : 'S'),
|
||||
(data.acc ? "MLAL" : "MULL"),
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rdlo,
|
||||
data.rdhi,
|
||||
data.rm,
|
||||
data.rs);
|
||||
},
|
||||
[](Undefined) { return std::string("UND"); },
|
||||
[this](SingleDataSwap& data) {
|
||||
return fmt::format("SWP{}{} R{:d},R{:d},[R{:d}]",
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rn);
|
||||
},
|
||||
[this](SingleDataTransfer& data) {
|
||||
std::string expression;
|
||||
std::string address;
|
||||
|
||||
if (const uint16_t* offset = std::get_if<uint16_t>(&data.offset)) {
|
||||
if (*offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression =
|
||||
fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset);
|
||||
}
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||
// Shifts are always immediate in single data transfer
|
||||
expression = fmt::format(",{}R{:d},{} #{:d}",
|
||||
(data.up ? '+' : '-'),
|
||||
shift->rm,
|
||||
shift->data.type,
|
||||
shift->data.operand);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
(!data.pre && data.write ? "T" : ""),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](HalfwordTransfer& data) {
|
||||
std::string expression;
|
||||
|
||||
if (data.imm) {
|
||||
if (data.offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression = fmt::format(
|
||||
",{}#{:d}", (data.up ? '+' : '-'), data.offset);
|
||||
}
|
||||
} else {
|
||||
expression =
|
||||
fmt::format(",{}R{:d}", (data.up ? '+' : '-'), data.offset);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.sign ? "S" : ""),
|
||||
(data.half ? 'H' : 'B'),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](BlockDataTransfer& 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{:d}{},{{{}}}{}",
|
||||
(data.load ? "LDM" : "STM"),
|
||||
condition,
|
||||
(data.up ? 'I' : 'D'),
|
||||
(data.pre ? 'B' : 'A'),
|
||||
data.rn,
|
||||
(data.write ? "!" : ""),
|
||||
regs,
|
||||
(data.s ? "^" : ""));
|
||||
},
|
||||
[this](PsrTransfer& data) {
|
||||
if (data.type == PsrTransfer::Type::Mrs) {
|
||||
return fmt::format("MRS{} R{:d},{}",
|
||||
condition,
|
||||
data.operand,
|
||||
(data.spsr ? "SPSR_all" : "CPSR_all"));
|
||||
} else {
|
||||
return fmt::format(
|
||||
"MSR{} {}_{},{}{}",
|
||||
condition,
|
||||
(data.spsr ? "SPSR" : "CPSR"),
|
||||
(data.type == PsrTransfer::Type::Msr_flg ? "flg" : "all"),
|
||||
(data.imm ? '#' : 'R'),
|
||||
data.operand);
|
||||
}
|
||||
},
|
||||
[this](DataProcessing& data) {
|
||||
using OpCode = DataProcessing::OpCode;
|
||||
|
||||
std::string op_2;
|
||||
|
||||
if (const uint32_t* operand =
|
||||
std::get_if<uint32_t>(&data.operand)) {
|
||||
op_2 = fmt::format("#{:d}", *operand);
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
|
||||
op_2 = fmt::format("R{:d},{} {}{:d}",
|
||||
shift->rm,
|
||||
shift->data.type,
|
||||
(shift->data.immediate ? '#' : 'R'),
|
||||
shift->data.operand);
|
||||
}
|
||||
|
||||
switch (data.opcode) {
|
||||
case OpCode::MOV:
|
||||
case OpCode::MVN:
|
||||
return fmt::format("{}{}{} R{:d},{}",
|
||||
data.opcode,
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
op_2);
|
||||
case OpCode::TST:
|
||||
case OpCode::TEQ:
|
||||
case OpCode::CMP:
|
||||
case OpCode::CMN:
|
||||
return fmt::format(
|
||||
"{}{} R{:d},{}", data.opcode, condition, data.rn, op_2);
|
||||
default:
|
||||
return fmt::format("{}{}{} R{:d},R{:d},{}",
|
||||
data.opcode,
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rn,
|
||||
op_2);
|
||||
}
|
||||
},
|
||||
[this](SoftwareInterrupt) { return fmt::format("SWI{}", condition); },
|
||||
[this](CoprocessorDataTransfer& data) {
|
||||
std::string expression = fmt::format(",#{:d}", data.offset);
|
||||
return fmt::format(
|
||||
"{}{}{} p{:d},c{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDC" : "STC"),
|
||||
condition,
|
||||
(data.len ? "L" : ""),
|
||||
data.cpn,
|
||||
data.crd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](CoprocessorDataOperation& data) {
|
||||
return fmt::format("CDP{} p{},{},c{},c{},c{},{}",
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.crd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[this](CoprocessorRegisterTransfer& data) {
|
||||
return fmt::format("{}{} p{},{},R{},c{},c{},{}",
|
||||
(data.load ? "MRC" : "MCR"),
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.rd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[](auto) { return std::string("unknown instruction"); } },
|
||||
data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -218,7 +218,9 @@ struct Instruction {
|
||||
: condition(condition)
|
||||
, data(data){};
|
||||
|
||||
#ifdef DISASSEMBLER
|
||||
std::string disassemble();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,8 @@
|
||||
lib_sources += files(
|
||||
'instruction.cc',
|
||||
'exec.cc'
|
||||
)
|
||||
)
|
||||
|
||||
if get_option('disassembler')
|
||||
lib_sources += files('disassembler.cc')
|
||||
endif
|
@@ -121,11 +121,13 @@ CpuImpl::step() {
|
||||
if (cpsr.state() == State::Arm) {
|
||||
uint32_t x = bus->read_word(cur_pc);
|
||||
arm::Instruction instruction(x);
|
||||
|
||||
exec(instruction);
|
||||
|
||||
#ifdef DISASSEMBLER
|
||||
glogger.info("{:#034b}", x);
|
||||
|
||||
arm(instruction);
|
||||
|
||||
glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble());
|
||||
#endif
|
||||
|
||||
if (is_flushed) {
|
||||
// if flushed, do not increment the PC, instead set it to two
|
||||
|
@@ -11,7 +11,11 @@ lib_cpp_args = [ ]
|
||||
fmt = dependency('fmt', version : '>=10.1.0', static: true)
|
||||
if not fmt.found()
|
||||
fmt = dependency('fmt', version : '>=10.1.0', static: false)
|
||||
lib_cpp_args += 'DFMT_HEADER_ONLY'
|
||||
lib_cpp_args += '-DFMT_HEADER_ONLY'
|
||||
endif
|
||||
|
||||
if get_option('disassembler')
|
||||
lib_cpp_args += '-DDISASSEMBLER'
|
||||
endif
|
||||
|
||||
lib = library(
|
||||
|
Reference in New Issue
Block a user