cpu/{arm|thumb}(chore): change how branch disassembly happens
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -26,7 +26,7 @@ struct BranchAndExchange {
|
|||||||
|
|
||||||
struct Branch {
|
struct Branch {
|
||||||
bool link;
|
bool link;
|
||||||
uint32_t offset;
|
int32_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Multiply {
|
struct Multiply {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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 };
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user