cpu/{arm|thumb}(chore): change how branch disassembly happens

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2024-06-11 23:03:44 +05:30
parent 0062ad424b
commit 15c4802838
10 changed files with 38 additions and 33 deletions

View File

@@ -26,7 +26,7 @@ struct BranchAndExchange {
struct Branch { struct Branch {
bool link; bool link;
uint32_t offset; int32_t offset;
}; };
struct Multiply { struct Multiply {

View File

@@ -282,7 +282,7 @@ struct Instruction {
void exec(Cpu& cpu); void exec(Cpu& cpu);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
std::string disassemble(uint32_t pc = 0); std::string disassemble();
#endif #endif
InstructionData data; InstructionData data;

View File

@@ -1,7 +1,6 @@
#include "cpu/arm/instruction.hh" #include "cpu/arm/instruction.hh"
#include "util/bits.hh" #include "util/bits.hh"
#include <format> #include <format>
#include <string>
namespace matar::arm { namespace matar::arm {
std::string std::string
@@ -15,7 +14,10 @@ Instruction::disassemble() {
}, },
[condition](Branch& data) { [condition](Branch& data) {
return std::format( return std::format(
"B{}{} 0x{:06X}", (data.link ? "L" : ""), condition, data.offset); "B{}{} {:#06x}",
(data.link ? "L" : ""),
condition,
static_cast<int32_t>(data.offset + 2 * INSTRUCTION_SIZE));
}, },
[condition](Multiply& data) { [condition](Multiply& data) {
if (data.acc) { if (data.acc) {

View File

@@ -40,17 +40,14 @@ Instruction::exec(Cpu& cpu) {
if (state == State::Arm) if (state == State::Arm)
rst_bit(cpu.pc, 1); rst_bit(cpu.pc, 1);
// pc is affected so flush the pipeline // PC is affected so flush the pipeline
cpu.is_flushed = true; cpu.is_flushed = true;
}, },
[&cpu](Branch& data) { [&cpu](Branch& data) {
if (data.link) if (data.link)
cpu.gpr[14] = cpu.pc - INSTRUCTION_SIZE; cpu.gpr[14] = cpu.pc - INSTRUCTION_SIZE;
// data.offset accounts for two instructions ahead when cpu.pc += data.offset;
// disassembling, so need to adjust
cpu.pc =
static_cast<int32_t>(cpu.pc) - 2 * INSTRUCTION_SIZE + data.offset;
// pc is affected so flush the pipeline // pc is affected so flush the pipeline
cpu.is_flushed = true; cpu.is_flushed = true;

View File

@@ -1,6 +1,5 @@
#include "cpu/arm/instruction.hh" #include "cpu/arm/instruction.hh"
#include "util/bits.hh" #include "util/bits.hh"
#include <iterator>
namespace matar::arm { namespace matar::arm {
Instruction::Instruction(uint32_t insn) Instruction::Instruction(uint32_t insn)
@@ -13,13 +12,11 @@ Instruction::Instruction(uint32_t insn)
// Branch // Branch
} else if ((insn & 0x0E000000) == 0x0A000000) { } else if ((insn & 0x0E000000) == 0x0A000000) {
bool link = get_bit(insn, 24); bool link = get_bit(insn, 24);
uint32_t offset = bit_range(insn, 0, 23); int32_t offset = static_cast<int32_t>(bit_range(insn, 0, 23));
// lsh 2 and sign extend the 26 bit offset to 32 bits // lsh 2 and sign extend the 26 bit offset to 32 bits
offset = (static_cast<int32_t>(offset) << 8) >> 6; offset = (offset << 8) >> 6;
offset += 2 * INSTRUCTION_SIZE;
data = Branch{ .link = link, .offset = offset }; data = Branch{ .link = link, .offset = offset };

View File

@@ -119,9 +119,8 @@ Cpu::chg_mode(const Mode to) {
void void
Cpu::step() { Cpu::step() {
// Current instruction is two instructions behind PC // Current instruction is two instructions behind PC
uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE;
if (cpsr.state() == State::Arm) { if (cpsr.state() == State::Arm) {
uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE;
arm::Instruction instruction(bus->read_word(cur_pc)); arm::Instruction instruction(bus->read_word(cur_pc));
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
@@ -129,12 +128,12 @@ Cpu::step() {
#endif #endif
instruction.exec(*this); instruction.exec(*this);
} else { } else {
uint32_t cur_pc = pc - 2 * thumb::INSTRUCTION_SIZE;
thumb::Instruction instruction(bus->read_halfword(cur_pc)); thumb::Instruction instruction(bus->read_halfword(cur_pc));
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble(cur_pc)); glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble());
#endif #endif
instruction.exec(*this); instruction.exec(*this);

View File

@@ -4,7 +4,7 @@
namespace matar::thumb { namespace matar::thumb {
std::string std::string
Instruction::disassemble(uint32_t pc) { Instruction::disassemble() {
return std::visit( return std::visit(
overloaded{ overloaded{
[](MoveShiftedRegister& data) { [](MoveShiftedRegister& data) {
@@ -133,16 +133,16 @@ Instruction::disassemble(uint32_t pc) {
[](SoftwareInterrupt& data) { [](SoftwareInterrupt& data) {
return std::format("SWI {:d}", data.vector); return std::format("SWI {:d}", data.vector);
}, },
[pc](ConditionalBranch& data) { [](ConditionalBranch& data) {
return std::format( return std::format(
"B{} #{:d}", "B{} #{:d}",
stringify(data.condition), stringify(data.condition),
static_cast<int32_t>(data.offset + pc + 2 * INSTRUCTION_SIZE)); static_cast<int32_t>(data.offset + 2 * INSTRUCTION_SIZE));
}, },
[pc](UnconditionalBranch& data) { [](UnconditionalBranch& data) {
return std::format( return std::format(
"B #{:d}", "B #{:d}",
static_cast<int32_t>(data.offset + pc + 2 * INSTRUCTION_SIZE)); static_cast<int32_t>(data.offset + 2 * INSTRUCTION_SIZE));
}, },
[](LongBranchWithLink& data) { [](LongBranchWithLink& data) {
// duh this manual be empty for H = 0 // duh this manual be empty for H = 0

View File

@@ -24,18 +24,25 @@ TEST_CASE_METHOD(CpuFixture, "Branch", TAG) {
InstructionData data = Branch{ .link = false, .offset = 3489748 }; InstructionData data = Branch{ .link = false, .offset = 3489748 };
Branch* branch = std::get_if<Branch>(&data); Branch* branch = std::get_if<Branch>(&data);
// set PC to 48
setr(15, 48);
exec(data); exec(data);
CHECK(getr(15) == 3489748); // 48 + offset
CHECK(getr(15) == 3489796);
CHECK(getr(14) == 0); CHECK(getr(14) == 0);
// with link // with link
reset(); reset();
setr(15, 48);
branch->link = true; branch->link = true;
exec(data); exec(data);
CHECK(getr(15) == 3489748); // 48 + offset
CHECK(getr(14) == 0 + INSTRUCTION_SIZE); CHECK(getr(15) == 3489796);
// pc was set to 48
CHECK(getr(14) == 48 - INSTRUCTION_SIZE);
} }
TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) {

View File

@@ -31,15 +31,16 @@ TEST_CASE("Branch", TAG) {
// last 24 bits = 8748995 // last 24 bits = 8748995
// (8748995 << 8) >> 6 sign extended = 0xFE15FF0C // (8748995 << 8) >> 6 sign extended = 0xFE15FF0C
// Also +8 since PC is two instructions ahead CHECK(b->offset == static_cast<int32_t>(0xfe15ff0c));
CHECK(b->offset == 0xFE15FF14);
CHECK(b->link == true); CHECK(b->link == true);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
CHECK(instruction.disassemble() == "BL 0xFE15FF14"); // take prefetch into account
// offset + 8 = 0xfe15ff0c + 8 = -0x1ea00e4 + 8
CHECK(instruction.disassemble() == "BL -0x1ea00ec");
b->link = false; b->link = false;
CHECK(instruction.disassemble() == "B 0xFE15FF14"); CHECK(instruction.disassemble() == "B -0x1ea00ec");
#endif #endif
} }

View File

@@ -412,7 +412,8 @@ TEST_CASE("Conditional Branch", TAG) {
CHECK(b->condition == Condition::LS); CHECK(b->condition == Condition::LS);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
// (-76 << 1) + PC (0) + 4 // take prefetch into account
// offset + 4 = -152 + 4
CHECK(instruction.disassemble() == "BLS #-148"); CHECK(instruction.disassemble() == "BLS #-148");
#endif #endif
} }
@@ -439,7 +440,8 @@ TEST_CASE("Unconditional Branch") {
REQUIRE(b->offset == -410); REQUIRE(b->offset == -410);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
// (2147483443 << 1) + PC(0) + 4 // take prefetch into account
// offset + 4 = -410 + 4
CHECK(instruction.disassemble() == "B #-406"); CHECK(instruction.disassemble() == "B #-406");
#endif #endif
} }