[UNTESTED] complete initial disassembler structure for ARM
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -14,14 +14,14 @@ class Cpu {
|
|||||||
void step();
|
void step();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t GPR_COUNT = 16;
|
static constexpr uint8_t GPR_COUNT = 16;
|
||||||
|
|
||||||
static constexpr size_t GPR_FIQ_FIRST = 8;
|
static constexpr uint8_t GPR_FIQ_FIRST = 8;
|
||||||
static constexpr size_t GPR_SVC_FIRST = 13;
|
static constexpr uint8_t GPR_SVC_FIRST = 13;
|
||||||
static constexpr size_t GPR_ABT_FIRST = 13;
|
static constexpr uint8_t GPR_ABT_FIRST = 13;
|
||||||
static constexpr size_t GPR_IRQ_FIRST = 13;
|
static constexpr uint8_t GPR_IRQ_FIRST = 13;
|
||||||
static constexpr size_t GPR_UND_FIRST = 13;
|
static constexpr uint8_t GPR_UND_FIRST = 13;
|
||||||
static constexpr size_t GPR_SYS_USR_FIRST = 8;
|
static constexpr uint8_t GPR_SYS_USR_FIRST = 8;
|
||||||
|
|
||||||
std::shared_ptr<Bus> bus;
|
std::shared_ptr<Bus> bus;
|
||||||
std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
|
std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
|
||||||
@@ -29,7 +29,13 @@ class Cpu {
|
|||||||
Psr cpsr; // current program status register
|
Psr cpsr; // current program status register
|
||||||
Psr spsr; // status program status register
|
Psr spsr; // status program status register
|
||||||
|
|
||||||
uint32_t& pc = gpr[15];
|
static constexpr uint8_t PC_INDEX = 15;
|
||||||
|
uint32_t& pc = gpr[PC_INDEX];
|
||||||
|
|
||||||
|
bool is_flushed;
|
||||||
|
|
||||||
|
void chg_mode(const Mode to);
|
||||||
|
void exec_arm(const ArmInstruction instruction);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
||||||
@@ -49,7 +55,4 @@ class Cpu {
|
|||||||
Psr irq;
|
Psr irq;
|
||||||
Psr und;
|
Psr und;
|
||||||
} spsr_banked; // banked saved program status registers
|
} spsr_banked; // banked saved program status registers
|
||||||
|
|
||||||
void chg_mode(const Mode to);
|
|
||||||
void exec_arm(const ArmInstruction instruction);
|
|
||||||
};
|
};
|
||||||
|
@@ -79,6 +79,38 @@ class ArmInstruction {
|
|||||||
bool pre;
|
bool pre;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BlockDataTransfer {
|
||||||
|
uint16_t regs;
|
||||||
|
uint8_t rn;
|
||||||
|
bool load;
|
||||||
|
bool write;
|
||||||
|
bool s;
|
||||||
|
bool up;
|
||||||
|
bool pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataProcessing {
|
||||||
|
std::variant<Shift, uint32_t> operand;
|
||||||
|
uint8_t rd;
|
||||||
|
uint8_t rn;
|
||||||
|
bool set;
|
||||||
|
OpCode opcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PsrTransfer {
|
||||||
|
enum class Type {
|
||||||
|
Mrs,
|
||||||
|
Msr,
|
||||||
|
Msr_flg
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t operand;
|
||||||
|
bool spsr;
|
||||||
|
Type type;
|
||||||
|
// ignored outside MSR_flg
|
||||||
|
bool imm;
|
||||||
|
};
|
||||||
|
|
||||||
struct CoprocessorDataTransfer {
|
struct CoprocessorDataTransfer {
|
||||||
uint8_t offset;
|
uint8_t offset;
|
||||||
uint8_t cpn;
|
uint8_t cpn;
|
||||||
@@ -120,6 +152,9 @@ class ArmInstruction {
|
|||||||
SingleDataSwap,
|
SingleDataSwap,
|
||||||
SingleDataTransfer,
|
SingleDataTransfer,
|
||||||
HalfwordTransfer,
|
HalfwordTransfer,
|
||||||
|
BlockDataTransfer,
|
||||||
|
DataProcessing,
|
||||||
|
PsrTransfer,
|
||||||
CoprocessorDataTransfer,
|
CoprocessorDataTransfer,
|
||||||
CoprocessorDataOperation,
|
CoprocessorDataOperation,
|
||||||
CoprocessorRegisterTransfer,
|
CoprocessorRegisterTransfer,
|
||||||
|
@@ -8,6 +8,9 @@ class Psr {
|
|||||||
// clear the reserved bits i.e, [8:27]
|
// clear the reserved bits i.e, [8:27]
|
||||||
Psr(uint32_t raw);
|
Psr(uint32_t raw);
|
||||||
|
|
||||||
|
uint32_t raw() const;
|
||||||
|
void set_all(uint32_t raw);
|
||||||
|
|
||||||
// Mode : [4:0]
|
// Mode : [4:0]
|
||||||
Mode mode() const;
|
Mode mode() const;
|
||||||
void set_mode(Mode mode);
|
void set_mode(Mode mode);
|
||||||
@@ -45,8 +48,8 @@ class Psr {
|
|||||||
bool condition(Condition cond) const;
|
bool condition(Condition cond) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
|
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xF00000FF;
|
||||||
static constexpr uint32_t PSR_CLEAR_MODE = 0x0b00000;
|
static constexpr uint32_t PSR_CLEAR_MODE = 0xFFFFFFE0;
|
||||||
|
|
||||||
uint32_t psr;
|
uint32_t psr;
|
||||||
};
|
};
|
||||||
|
@@ -65,6 +65,12 @@ enum class OpCode {
|
|||||||
MVN = 0b1111
|
MVN = 0b1111
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, const OpCode cond);
|
||||||
|
template<>
|
||||||
|
struct fmt::formatter<OpCode> : ostream_formatter {};
|
||||||
|
|
||||||
enum class ShiftType {
|
enum class ShiftType {
|
||||||
LSL = 0b00,
|
LSL = 0b00,
|
||||||
LSR = 0b01,
|
LSR = 0b01,
|
||||||
|
393
src/cpu/cpu.cc
393
src/cpu/cpu.cc
@@ -1,4 +1,5 @@
|
|||||||
#include "cpu/cpu.hh"
|
#include "cpu/cpu.hh"
|
||||||
|
#include "cpu/utility.hh"
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
#include "util/log.hh"
|
#include "util/log.hh"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -11,15 +12,17 @@ Cpu::Cpu(Bus& bus)
|
|||||||
, gpr({ 0 })
|
, gpr({ 0 })
|
||||||
, cpsr(0)
|
, cpsr(0)
|
||||||
, spsr(0)
|
, spsr(0)
|
||||||
|
, is_flushed(false)
|
||||||
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
||||||
, spsr_banked({ 0, 0, 0, 0, 0 }) {
|
, spsr_banked({ 0, 0, 0, 0, 0 }) {
|
||||||
cpsr.set_mode(Mode::System);
|
cpsr.set_mode(Mode::Supervisor);
|
||||||
cpsr.set_irq_disabled(true);
|
cpsr.set_irq_disabled(true);
|
||||||
cpsr.set_fiq_disabled(true);
|
cpsr.set_fiq_disabled(true);
|
||||||
cpsr.set_state(State::Arm);
|
cpsr.set_state(State::Arm);
|
||||||
log_info("CPU successfully initialised");
|
log_info("CPU successfully initialised");
|
||||||
|
|
||||||
// PC is always two instructions ahead in the pipeline
|
// PC always points to two instructions ahead
|
||||||
|
// PC - 2 is the instruction being executed
|
||||||
pc += 2 * ARM_INSTRUCTION_SIZE;
|
pc += 2 * ARM_INSTRUCTION_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ Cpu::chg_mode(const Mode to) {
|
|||||||
* concatenate views */
|
* concatenate views */
|
||||||
#define STORE_BANKED(mode, MODE) \
|
#define STORE_BANKED(mode, MODE) \
|
||||||
std::copy(gpr.begin() + GPR_##MODE##_FIRST, \
|
std::copy(gpr.begin() + GPR_##MODE##_FIRST, \
|
||||||
gpr.begin() + GPR_COUNT - 1, \
|
gpr.begin() + gpr.size() - 1, \
|
||||||
gpr_banked.mode.begin())
|
gpr_banked.mode.begin())
|
||||||
|
|
||||||
switch (from) {
|
switch (from) {
|
||||||
@@ -145,23 +148,25 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
pc = gpr[data.rn];
|
pc = gpr[data.rn];
|
||||||
|
|
||||||
// ignore [1:0] bits for arm and 0 bit for thumb
|
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||||
rst_nth_bit(pc, 0);
|
rst_bit(pc, 0);
|
||||||
|
|
||||||
if (state == State::Arm)
|
if (state == State::Arm)
|
||||||
rst_nth_bit(pc, 1);
|
rst_bit(pc, 1);
|
||||||
|
|
||||||
|
// pc is affected so flush the pipeline
|
||||||
|
is_flushed = true;
|
||||||
},
|
},
|
||||||
[this](ArmInstruction::Branch& data) {
|
[this](ArmInstruction::Branch& data) {
|
||||||
auto offset = data.offset;
|
|
||||||
// lsh 2 and sign extend the 26 bit offset to 32 bits
|
|
||||||
offset <<= 2;
|
|
||||||
|
|
||||||
if (get_nth_bit(offset, 25))
|
|
||||||
offset |= 0xFC000000;
|
|
||||||
|
|
||||||
if (data.link)
|
if (data.link)
|
||||||
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
pc += offset - ARM_INSTRUCTION_SIZE;
|
// data.offset accounts for two instructions ahead when
|
||||||
|
// disassembling, so need to adjust
|
||||||
|
pc =
|
||||||
|
static_cast<int32_t>(pc) - 2 * ARM_INSTRUCTION_SIZE + data.offset;
|
||||||
|
|
||||||
|
// pc is affected so flush the pipeline
|
||||||
|
is_flushed = true;
|
||||||
},
|
},
|
||||||
[this, pc_error](ArmInstruction::Multiply& data) {
|
[this, pc_error](ArmInstruction::Multiply& data) {
|
||||||
if (data.rd == data.rm)
|
if (data.rd == data.rm)
|
||||||
@@ -176,8 +181,8 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0);
|
gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0);
|
||||||
|
|
||||||
if (data.set) {
|
if (data.set) {
|
||||||
cpsr.set_z(!static_cast<bool>(gpr[data.rd]));
|
cpsr.set_z(gpr[data.rd] == 0);
|
||||||
cpsr.set_n(get_nth_bit(gpr[data.rd], 31));
|
cpsr.set_n(get_bit(gpr[data.rd], 31));
|
||||||
cpsr.set_c(0);
|
cpsr.set_c(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -199,8 +204,8 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
static_cast<uint64_t>(gpr[data.rdlo])
|
static_cast<uint64_t>(gpr[data.rdlo])
|
||||||
: 0);
|
: 0);
|
||||||
|
|
||||||
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
gpr[data.rdlo] = bit_range(eval, 0, 31);
|
||||||
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
gpr[data.rdhi] = bit_range(eval, 32, 63);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
int64_t eval =
|
int64_t eval =
|
||||||
@@ -210,23 +215,22 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
static_cast<int64_t>(gpr[data.rdlo])
|
static_cast<int64_t>(gpr[data.rdlo])
|
||||||
: 0);
|
: 0);
|
||||||
|
|
||||||
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
gpr[data.rdlo] = bit_range(eval, 0, 31);
|
||||||
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
gpr[data.rdhi] = bit_range(eval, 32, 63);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.set) {
|
if (data.set) {
|
||||||
cpsr.set_z(!(static_cast<bool>(gpr[data.rdhi]) ||
|
cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0);
|
||||||
static_cast<bool>(gpr[data.rdlo])));
|
cpsr.set_n(get_bit(gpr[data.rdhi], 31));
|
||||||
cpsr.set_n(get_nth_bit(gpr[data.rdhi], 31));
|
|
||||||
cpsr.set_c(0);
|
cpsr.set_c(0);
|
||||||
cpsr.set_v(0);
|
cpsr.set_v(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
|
[](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
|
||||||
[this, pc_warn](ArmInstruction::SingleDataSwap& data) {
|
[this, pc_error](ArmInstruction::SingleDataSwap& data) {
|
||||||
pc_warn(data.rm);
|
pc_error(data.rm);
|
||||||
pc_warn(data.rn);
|
pc_error(data.rn);
|
||||||
pc_warn(data.rd);
|
pc_error(data.rd);
|
||||||
|
|
||||||
if (data.byte) {
|
if (data.byte) {
|
||||||
gpr[data.rd] = bus->read_byte(gpr[data.rn]);
|
gpr[data.rd] = bus->read_byte(gpr[data.rn]);
|
||||||
@@ -244,6 +248,10 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
log_warn("Write-back enabled with post-indexing in {}",
|
log_warn("Write-back enabled with post-indexing in {}",
|
||||||
typeid(data).name());
|
typeid(data).name());
|
||||||
|
|
||||||
|
if (data.rn == PC_INDEX && data.write)
|
||||||
|
log_warn("Write-back enabled with base register as PC {}",
|
||||||
|
typeid(data).name());
|
||||||
|
|
||||||
if (data.write)
|
if (data.write)
|
||||||
pc_warn(data.rn);
|
pc_warn(data.rn);
|
||||||
|
|
||||||
@@ -262,18 +270,21 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
pc_error(shift->data.operand);
|
pc_error(shift->data.operand);
|
||||||
pc_error(shift->rm);
|
pc_error(shift->rm);
|
||||||
|
|
||||||
|
offset =
|
||||||
eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
|
eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
|
||||||
|
|
||||||
cpsr.set_c(carry);
|
cpsr.set_c(carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PC is always two instructions ahead
|
// PC is always two instructions ahead
|
||||||
if (data.rn == 15)
|
if (data.rn == PC_INDEX)
|
||||||
address -= 2 * ARM_INSTRUCTION_SIZE;
|
address -= 2 * ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
if (data.pre)
|
if (data.pre)
|
||||||
address += (data.up ? offset : -offset);
|
address += (data.up ? offset : -offset);
|
||||||
|
|
||||||
|
debug(address);
|
||||||
|
|
||||||
// load
|
// load
|
||||||
if (data.load) {
|
if (data.load) {
|
||||||
// byte
|
// byte
|
||||||
@@ -285,7 +296,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
// store
|
// store
|
||||||
} else {
|
} else {
|
||||||
// take PC into consideration
|
// take PC into consideration
|
||||||
if (data.rd == 15)
|
if (data.rd == PC_INDEX)
|
||||||
address += ARM_INSTRUCTION_SIZE;
|
address += ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
// byte
|
// byte
|
||||||
@@ -301,6 +312,9 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
|
|
||||||
if (!data.pre || data.write)
|
if (!data.pre || data.write)
|
||||||
gpr[data.rn] = address;
|
gpr[data.rn] = address;
|
||||||
|
|
||||||
|
if (data.rd == PC_INDEX && data.load)
|
||||||
|
is_flushed = true;
|
||||||
},
|
},
|
||||||
[this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
|
[this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
|
||||||
uint32_t address = gpr[data.rn];
|
uint32_t address = gpr[data.rn];
|
||||||
@@ -331,14 +345,14 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
gpr[data.rd] = bus->read_halfword(address);
|
gpr[data.rd] = bus->read_halfword(address);
|
||||||
|
|
||||||
// sign extend the halfword
|
// sign extend the halfword
|
||||||
if (get_nth_bit(gpr[data.rd], 15))
|
if (get_bit(gpr[data.rd], PC_INDEX))
|
||||||
gpr[data.rd] |= 0xFFFF0000;
|
gpr[data.rd] |= 0xFFFF0000;
|
||||||
// byte
|
// byte
|
||||||
} else {
|
} else {
|
||||||
gpr[data.rd] = bus->read_byte(address);
|
gpr[data.rd] = bus->read_byte(address);
|
||||||
|
|
||||||
// sign extend the byte
|
// sign extend the byte
|
||||||
if (get_nth_bit(gpr[data.rd], 7))
|
if (get_bit(gpr[data.rd], 7))
|
||||||
gpr[data.rd] |= 0xFFFFFF00;
|
gpr[data.rd] |= 0xFFFFFF00;
|
||||||
}
|
}
|
||||||
// unsigned halfword
|
// unsigned halfword
|
||||||
@@ -348,7 +362,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
// store
|
// store
|
||||||
} else {
|
} else {
|
||||||
// take PC into consideration
|
// take PC into consideration
|
||||||
if (data.rd == 15)
|
if (data.rd == PC_INDEX)
|
||||||
address += ARM_INSTRUCTION_SIZE;
|
address += ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
// halfword
|
// halfword
|
||||||
@@ -361,28 +375,337 @@ Cpu::exec_arm(const ArmInstruction instruction) {
|
|||||||
|
|
||||||
if (!data.pre || data.write)
|
if (!data.pre || data.write)
|
||||||
gpr[data.rn] = address;
|
gpr[data.rn] = address;
|
||||||
|
|
||||||
|
if (data.rd == PC_INDEX && data.load)
|
||||||
|
is_flushed = true;
|
||||||
|
},
|
||||||
|
[this, pc_error](ArmInstruction::BlockDataTransfer& data) {
|
||||||
|
uint32_t address = gpr[data.rn];
|
||||||
|
Mode mode = cpsr.mode();
|
||||||
|
uint8_t alignment = 4; // word
|
||||||
|
uint8_t i = 0;
|
||||||
|
uint8_t n_regs = std::popcount(data.regs);
|
||||||
|
|
||||||
|
pc_error(data.rn);
|
||||||
|
|
||||||
|
if (cpsr.mode() == Mode::User && data.s) {
|
||||||
|
log_error("Bit S is set outside priviliged modes in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we just change modes to load user registers
|
||||||
|
if ((!get_bit(data.regs, PC_INDEX) && data.s) ||
|
||||||
|
(!data.load && data.s)) {
|
||||||
|
chg_mode(Mode::User);
|
||||||
|
|
||||||
|
if (data.write) {
|
||||||
|
log_error("Write-back enable for user bank registers in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// account for decrement
|
||||||
|
if (!data.up)
|
||||||
|
address -= (n_regs - 1) * alignment;
|
||||||
|
|
||||||
|
if (data.pre)
|
||||||
|
address += (data.up ? alignment : -alignment);
|
||||||
|
|
||||||
|
if (data.load) {
|
||||||
|
if (get_bit(data.regs, PC_INDEX) && data.s && data.load) {
|
||||||
|
// current mode's spsr is already loaded when it was
|
||||||
|
// switched
|
||||||
|
spsr = cpsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < GPR_COUNT; i++) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
gpr[i] = bus->read_word(address);
|
||||||
|
address += alignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < GPR_COUNT; i++) {
|
||||||
|
if (get_bit(data.regs, i)) {
|
||||||
|
bus->write_word(address, gpr[i]);
|
||||||
|
address += alignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.pre)
|
||||||
|
address += (data.up ? alignment : -alignment);
|
||||||
|
|
||||||
|
// reset back to original address + offset if incremented earlier
|
||||||
|
if (data.up)
|
||||||
|
address -= n_regs * alignment;
|
||||||
|
|
||||||
|
if (!data.pre || data.write)
|
||||||
|
gpr[data.rn] = address;
|
||||||
|
|
||||||
|
if (data.load && get_bit(data.regs, PC_INDEX))
|
||||||
|
is_flushed = true;
|
||||||
|
|
||||||
|
// load back the original mode registers
|
||||||
|
chg_mode(mode);
|
||||||
|
},
|
||||||
|
[this, pc_error](ArmInstruction::PsrTransfer& data) {
|
||||||
|
if (data.spsr && cpsr.mode() == Mode::User) {
|
||||||
|
log_error("Accessing SPSR in User mode in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
}
|
||||||
|
|
||||||
|
Psr& psr = data.spsr ? spsr : cpsr;
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case ArmInstruction::PsrTransfer::Type::Mrs:
|
||||||
|
pc_error(data.operand);
|
||||||
|
gpr[data.operand] = psr.raw();
|
||||||
|
break;
|
||||||
|
case ArmInstruction::PsrTransfer::Type::Msr:
|
||||||
|
pc_error(data.operand);
|
||||||
|
|
||||||
|
if (cpsr.mode() != Mode::User) {
|
||||||
|
psr.set_all(gpr[data.operand]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArmInstruction::PsrTransfer::Type::Msr_flg:
|
||||||
|
psr.set_n(get_bit(data.operand, 31));
|
||||||
|
psr.set_z(get_bit(data.operand, 30));
|
||||||
|
psr.set_c(get_bit(data.operand, 29));
|
||||||
|
psr.set_v(get_bit(data.operand, 28));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this, pc_error](ArmInstruction::DataProcessing& data) {
|
||||||
|
uint32_t op_1 = gpr[data.rn];
|
||||||
|
uint32_t op_2 = 0;
|
||||||
|
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
bool overflow = cpsr.v();
|
||||||
|
bool carry = cpsr.c();
|
||||||
|
bool negative = cpsr.n();
|
||||||
|
bool zero = cpsr.z();
|
||||||
|
|
||||||
|
if (const uint32_t* immediate =
|
||||||
|
std::get_if<uint32_t>(&data.operand)) {
|
||||||
|
op_2 = *immediate;
|
||||||
|
} else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
|
||||||
|
uint8_t amount =
|
||||||
|
(shift->data.immediate ? shift->data.operand
|
||||||
|
: gpr[shift->data.operand] & 0xFF);
|
||||||
|
|
||||||
|
bool carry = cpsr.c();
|
||||||
|
|
||||||
|
if (!shift->data.immediate)
|
||||||
|
pc_error(shift->data.operand);
|
||||||
|
pc_error(shift->rm);
|
||||||
|
|
||||||
|
op_2 =
|
||||||
|
eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
|
||||||
|
|
||||||
|
cpsr.set_c(carry);
|
||||||
|
|
||||||
|
// PC is 12 bytes ahead when shifting
|
||||||
|
if (data.rn == PC_INDEX)
|
||||||
|
op_1 += ARM_INSTRUCTION_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.opcode) {
|
||||||
|
case OpCode::AND: {
|
||||||
|
result = op_1 & op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::EOR: {
|
||||||
|
result = op_1 ^ op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::SUB: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
result = op_1 - op_2;
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = op_1 < op_2;
|
||||||
|
overflow = s1 != s2 && s2 == negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::RSB: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
result = op_2 - op_1;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = op_2 < op_1;
|
||||||
|
overflow = s1 != s2 && s1 == negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::ADD: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
// result_ is 33 bits
|
||||||
|
uint64_t result_ = op_2 + op_1;
|
||||||
|
result = result_ & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = get_bit(result_, 32);
|
||||||
|
overflow = s1 == s2 && s1 != negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::ADC: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
uint64_t result_ = op_2 + op_1 + carry;
|
||||||
|
result = result_ & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = get_bit(result_, 32);
|
||||||
|
overflow = s1 == s2 && s1 != negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::SBC: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
uint64_t result_ = op_1 - op_2 + carry - 1;
|
||||||
|
result = result_ & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = get_bit(result_, 32);
|
||||||
|
overflow = s1 != s2 && s2 == negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::RSC: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
uint64_t result_ = op_1 - op_2 + carry - 1;
|
||||||
|
result = result_ & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = get_bit(result_, 32);
|
||||||
|
overflow = s1 != s2 && s1 == negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::TST: {
|
||||||
|
result = op_1 & op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::TEQ: {
|
||||||
|
result = op_1 ^ op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::CMP: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
result = op_1 - op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = op_1 < op_2;
|
||||||
|
overflow = s1 != s2 && s2 == negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::CMN: {
|
||||||
|
bool s1 = get_bit(op_1, 31);
|
||||||
|
bool s2 = get_bit(op_2, 31);
|
||||||
|
|
||||||
|
uint64_t result_ = op_2 + op_1;
|
||||||
|
result = result_ & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
carry = get_bit(result_, 32);
|
||||||
|
overflow = s1 == s2 && s1 != negative;
|
||||||
|
} break;
|
||||||
|
case OpCode::ORR: {
|
||||||
|
result = op_1 | op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::MOV: {
|
||||||
|
result = op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::BIC: {
|
||||||
|
result = op_1 & ~op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
case OpCode::MVN: {
|
||||||
|
result = ~op_2;
|
||||||
|
|
||||||
|
negative = get_bit(result, 31);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
zero = result == 0;
|
||||||
|
|
||||||
|
debug(carry);
|
||||||
|
debug(overflow);
|
||||||
|
debug(zero);
|
||||||
|
debug(negative);
|
||||||
|
|
||||||
|
auto set_conditions = [=]() {
|
||||||
|
cpsr.set_c(carry);
|
||||||
|
cpsr.set_v(overflow);
|
||||||
|
cpsr.set_n(negative);
|
||||||
|
cpsr.set_z(zero);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.set) {
|
||||||
|
if (data.rd == 15) {
|
||||||
|
if (cpsr.mode() == Mode::User)
|
||||||
|
log_error("Running {} in User mode",
|
||||||
|
typeid(data).name());
|
||||||
|
} else {
|
||||||
|
set_conditions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.opcode == OpCode::TST || data.opcode == OpCode::TEQ ||
|
||||||
|
data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) {
|
||||||
|
set_conditions();
|
||||||
|
} else {
|
||||||
|
gpr[data.rd] = result;
|
||||||
|
if (data.rd == 15 || data.opcode == OpCode::MVN)
|
||||||
|
is_flushed = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[this](ArmInstruction::SoftwareInterrupt) {
|
[this](ArmInstruction::SoftwareInterrupt) {
|
||||||
chg_mode(Mode::Supervisor);
|
chg_mode(Mode::Supervisor);
|
||||||
pc = 0x08;
|
pc = 0x08;
|
||||||
spsr = cpsr;
|
spsr = cpsr;
|
||||||
},
|
},
|
||||||
[](auto& data) { log_error("{} instruction", typeid(data).name()); } },
|
[](auto& data) {
|
||||||
|
log_error("Unimplemented {} instruction", typeid(data).name());
|
||||||
|
} },
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cpu::step() {
|
Cpu::step() {
|
||||||
|
// Current instruction is two instructions behind PC
|
||||||
uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
|
uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
if (cpsr.state() == State::Arm) {
|
if (cpsr.state() == State::Arm) {
|
||||||
ArmInstruction instruction(bus->read_word(cur_pc));
|
debug(cur_pc);
|
||||||
log_info("{:#034b}", bus->read_word(cur_pc));
|
uint32_t x = bus->read_word(cur_pc);
|
||||||
|
ArmInstruction instruction(x);
|
||||||
|
log_info("{:#034b}", x);
|
||||||
|
|
||||||
exec_arm(instruction);
|
exec_arm(instruction);
|
||||||
|
|
||||||
log_info("{:#010X} : {}", cur_pc, instruction.disassemble());
|
log_info("0x{:08X} : {}", cur_pc, instruction.disassemble());
|
||||||
|
|
||||||
|
if (is_flushed) {
|
||||||
|
// if flushed, do not increment the PC, instead set it to two
|
||||||
|
// instructions ahead to account for flushed "fetch" and "decode"
|
||||||
|
// instructions
|
||||||
|
pc += 2 * ARM_INSTRUCTION_SIZE;
|
||||||
|
is_flushed = false;
|
||||||
|
} else {
|
||||||
|
// if not flushed continue like normal
|
||||||
pc += ARM_INSTRUCTION_SIZE;
|
pc += ARM_INSTRUCTION_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
#include "cpu/instruction.hh"
|
#include "cpu/instruction.hh"
|
||||||
|
#include "cpu/utility.hh"
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
ArmInstruction::ArmInstruction(uint32_t insn)
|
ArmInstruction::ArmInstruction(uint32_t insn)
|
||||||
: condition(static_cast<Condition>(get_bit_range(insn, 28, 31))) {
|
: condition(static_cast<Condition>(bit_range(insn, 28, 31))) {
|
||||||
// Branch and exhcange
|
// Branch and exhcange
|
||||||
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
||||||
uint8_t rn = insn & 0b1111;
|
uint8_t rn = insn & 0b1111;
|
||||||
@@ -11,19 +13,27 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Branch
|
// Branch
|
||||||
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
||||||
bool link = get_nth_bit(insn, 24);
|
bool link = get_bit(insn, 24);
|
||||||
uint32_t offset = get_bit_range(insn, 0, 23);
|
uint32_t offset = bit_range(insn, 0, 23);
|
||||||
|
|
||||||
|
// lsh 2 and sign extend the 26 bit offset to 32 bits
|
||||||
|
offset <<= 2;
|
||||||
|
|
||||||
|
if (get_bit(offset, 25))
|
||||||
|
offset |= 0xFC000000;
|
||||||
|
|
||||||
|
offset += 2 * ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
data = Branch{ .link = link, .offset = offset };
|
data = Branch{ .link = link, .offset = offset };
|
||||||
|
|
||||||
// Multiply
|
// Multiply
|
||||||
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = bit_range(insn, 0, 3);
|
||||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
uint8_t rs = bit_range(insn, 8, 11);
|
||||||
uint8_t rn = get_bit_range(insn, 12, 15);
|
uint8_t rn = bit_range(insn, 12, 15);
|
||||||
uint8_t rd = get_bit_range(insn, 16, 19);
|
uint8_t rd = bit_range(insn, 16, 19);
|
||||||
bool set = get_nth_bit(insn, 20);
|
bool set = get_bit(insn, 20);
|
||||||
bool acc = get_nth_bit(insn, 21);
|
bool acc = get_bit(insn, 21);
|
||||||
|
|
||||||
data = Multiply{
|
data = Multiply{
|
||||||
.rm = rm, .rs = rs, .rn = rn, .rd = rd, .set = set, .acc = acc
|
.rm = rm, .rs = rs, .rn = rn, .rd = rd, .set = set, .acc = acc
|
||||||
@@ -31,13 +41,13 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Multiply long
|
// Multiply long
|
||||||
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = bit_range(insn, 0, 3);
|
||||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
uint8_t rs = bit_range(insn, 8, 11);
|
||||||
uint8_t rdlo = get_bit_range(insn, 12, 15);
|
uint8_t rdlo = bit_range(insn, 12, 15);
|
||||||
uint8_t rdhi = get_bit_range(insn, 16, 19);
|
uint8_t rdhi = bit_range(insn, 16, 19);
|
||||||
bool set = get_nth_bit(insn, 20);
|
bool set = get_bit(insn, 20);
|
||||||
bool acc = get_nth_bit(insn, 21);
|
bool acc = get_bit(insn, 21);
|
||||||
bool uns = get_nth_bit(insn, 22);
|
bool uns = get_bit(insn, 22);
|
||||||
|
|
||||||
data = MultiplyLong{ .rm = rm,
|
data = MultiplyLong{ .rm = rm,
|
||||||
.rs = rs,
|
.rs = rs,
|
||||||
@@ -53,10 +63,10 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Single data swap
|
// Single data swap
|
||||||
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = bit_range(insn, 0, 3);
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = bit_range(insn, 12, 15);
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
bool byte = get_nth_bit(insn, 22);
|
bool byte = get_bit(insn, 22);
|
||||||
|
|
||||||
data = SingleDataSwap{ .rm = rm, .rd = rd, .rn = rn, .byte = byte };
|
data = SingleDataSwap{ .rm = rm, .rd = rd, .rn = rn, .byte = byte };
|
||||||
|
|
||||||
@@ -64,28 +74,28 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
} else if ((insn & 0x0C000000) == 0x04000000) {
|
} else if ((insn & 0x0C000000) == 0x04000000) {
|
||||||
|
|
||||||
std::variant<uint16_t, Shift> offset;
|
std::variant<uint16_t, Shift> offset;
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = bit_range(insn, 12, 15);
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
bool load = get_nth_bit(insn, 20);
|
bool load = get_bit(insn, 20);
|
||||||
bool write = get_nth_bit(insn, 21);
|
bool write = get_bit(insn, 21);
|
||||||
bool byte = get_nth_bit(insn, 22);
|
bool byte = get_bit(insn, 22);
|
||||||
bool up = get_nth_bit(insn, 23);
|
bool up = get_bit(insn, 23);
|
||||||
bool pre = get_nth_bit(insn, 24);
|
bool pre = get_bit(insn, 24);
|
||||||
bool imm = get_nth_bit(insn, 25);
|
bool imm = get_bit(insn, 25);
|
||||||
|
|
||||||
if (imm) {
|
if (imm) {
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = bit_range(insn, 0, 3);
|
||||||
bool reg = get_nth_bit(insn, 4);
|
bool reg = get_bit(insn, 4);
|
||||||
ShiftType shift_type =
|
ShiftType shift_type =
|
||||||
static_cast<ShiftType>(get_bit_range(insn, 5, 6));
|
static_cast<ShiftType>(bit_range(insn, 5, 6));
|
||||||
uint8_t operand = get_bit_range(insn, (reg ? 8 : 7), 11);
|
uint8_t operand = bit_range(insn, (reg ? 8 : 7), 11);
|
||||||
|
|
||||||
offset = Shift{ .rm = rm,
|
offset = Shift{ .rm = rm,
|
||||||
.data = ShiftData{ .type = shift_type,
|
.data = ShiftData{ .type = shift_type,
|
||||||
.immediate = !reg,
|
.immediate = !reg,
|
||||||
.operand = operand } };
|
.operand = operand } };
|
||||||
} else {
|
} else {
|
||||||
offset = static_cast<uint16_t>(get_bit_range(insn, 0, 11));
|
offset = static_cast<uint16_t>(bit_range(insn, 0, 11));
|
||||||
}
|
}
|
||||||
|
|
||||||
data = SingleDataTransfer{ .offset = offset,
|
data = SingleDataTransfer{ .offset = offset,
|
||||||
@@ -99,18 +109,18 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Halfword transfer
|
// Halfword transfer
|
||||||
} else if ((insn & 0x0E000090) == 0x00000090) {
|
} else if ((insn & 0x0E000090) == 0x00000090) {
|
||||||
uint8_t offset = get_bit_range(insn, 0, 3);
|
uint8_t offset = bit_range(insn, 0, 3);
|
||||||
bool half = get_nth_bit(insn, 5);
|
bool half = get_bit(insn, 5);
|
||||||
bool sign = get_nth_bit(insn, 6);
|
bool sign = get_bit(insn, 6);
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = bit_range(insn, 12, 15);
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
bool load = get_nth_bit(insn, 20);
|
bool load = get_bit(insn, 20);
|
||||||
bool write = get_nth_bit(insn, 21);
|
bool write = get_bit(insn, 21);
|
||||||
bool imm = get_nth_bit(insn, 22);
|
bool imm = get_bit(insn, 22);
|
||||||
bool up = get_nth_bit(insn, 23);
|
bool up = get_bit(insn, 23);
|
||||||
bool pre = get_nth_bit(insn, 24);
|
bool pre = get_bit(insn, 24);
|
||||||
|
|
||||||
offset |= (imm ? get_bit_range(insn, 8, 11) << 2 : 0);
|
offset |= (imm ? bit_range(insn, 8, 11) << 2 : 0);
|
||||||
|
|
||||||
data = HalfwordTransfer{ .offset = offset,
|
data = HalfwordTransfer{ .offset = offset,
|
||||||
.half = half,
|
.half = half,
|
||||||
@@ -125,44 +135,99 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Block data transfer
|
// Block data transfer
|
||||||
} else if ((insn & 0x0E000000) == 0x08000000) {
|
} else if ((insn & 0x0E000000) == 0x08000000) {
|
||||||
/*static constexpr array<stringv, 2> syn = { "STM", "LDM" };
|
uint16_t regs = bit_range(insn, 0, 15);
|
||||||
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
|
bool load = get_bit(insn, 20);
|
||||||
|
bool write = get_bit(insn, 21);
|
||||||
|
bool s = get_bit(insn, 22);
|
||||||
|
bool up = get_bit(insn, 23);
|
||||||
|
bool pre = get_bit(insn, 24);
|
||||||
|
|
||||||
uint16_t regs = get_bit_range(insn, 0, 15);
|
data = BlockDataTransfer{ .regs = regs,
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
.rn = rn,
|
||||||
bool load = get_nth_bit(insn, 20);
|
.load = load,
|
||||||
bool write = get_nth_bit(insn, 21);
|
.write = write,
|
||||||
bool s = get_nth_bit(insn, 22);
|
.s = s,
|
||||||
bool up = get_nth_bit(insn, 23);
|
.up = up,
|
||||||
bool pre = get_nth_bit(insn, 24);
|
.pre = pre };
|
||||||
|
|
||||||
// disassembly
|
// Data Processing
|
||||||
{
|
} else if ((insn & 0x0C000000) == 0x00000000) {
|
||||||
uint8_t lpu = load << 2 | pre << 1 | up;
|
uint8_t rd = bit_range(insn, 12, 15);
|
||||||
std::string addr_mode;
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
|
bool set = get_bit(insn, 20);
|
||||||
|
OpCode opcode = static_cast<OpCode>(bit_range(insn, 21, 24));
|
||||||
|
bool imm = get_bit(insn, 25);
|
||||||
|
|
||||||
switch(lpu) {
|
if ((opcode == OpCode::TST || opcode == OpCode::CMP) && !set) {
|
||||||
|
data = PsrTransfer{ .operand = rd,
|
||||||
|
.spsr = get_bit(insn, 22),
|
||||||
|
.type = PsrTransfer::Type::Mrs,
|
||||||
|
.imm = 0 };
|
||||||
|
} else if ((opcode == OpCode::TEQ || opcode == OpCode::CMN) && !set) {
|
||||||
|
bool imm = get_bit(insn, 25);
|
||||||
|
uint32_t operand = 0;
|
||||||
|
|
||||||
|
if (imm) {
|
||||||
|
operand = bit_range(insn, 0, 3);
|
||||||
|
} else {
|
||||||
|
uint32_t immediate = bit_range(insn, 0, 7);
|
||||||
|
uint8_t rotate = bit_range(insn, 8, 11);
|
||||||
|
|
||||||
|
operand = std::rotr(immediate, rotate * 2);
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
data = Undefined{};
|
data = PsrTransfer{ .operand = operand,
|
||||||
|
.spsr = get_bit(insn, 22),
|
||||||
|
.type = (get_bit(insn, 16)
|
||||||
|
? PsrTransfer::Type::Msr
|
||||||
|
: PsrTransfer::Type::Msr_flg),
|
||||||
|
.imm = imm };
|
||||||
|
} else {
|
||||||
|
std::variant<Shift, uint32_t> operand;
|
||||||
|
|
||||||
// Software Interrupt
|
if (imm) {
|
||||||
// What to do here?
|
uint32_t immediate = bit_range(insn, 0, 7);
|
||||||
|
uint8_t rotate = bit_range(insn, 8, 11);
|
||||||
|
|
||||||
|
operand = std::rotr(immediate, rotate * 2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
uint8_t rm = bit_range(insn, 0, 3);
|
||||||
|
bool reg = get_bit(insn, 4);
|
||||||
|
ShiftType shift_type =
|
||||||
|
static_cast<ShiftType>(bit_range(insn, 5, 6));
|
||||||
|
uint8_t sh_operand = bit_range(insn, (reg ? 8 : 7), 11);
|
||||||
|
|
||||||
|
operand = Shift{ .rm = rm,
|
||||||
|
.data = ShiftData{ .type = shift_type,
|
||||||
|
.immediate = !reg,
|
||||||
|
.operand = sh_operand } };
|
||||||
|
}
|
||||||
|
|
||||||
|
data = DataProcessing{ .operand = operand,
|
||||||
|
.rd = rd,
|
||||||
|
.rn = rn,
|
||||||
|
.set = set,
|
||||||
|
.opcode = opcode };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Software interrupt
|
||||||
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
||||||
|
|
||||||
data = SoftwareInterrupt{};
|
data = SoftwareInterrupt{};
|
||||||
|
|
||||||
// Coprocessor data transfer
|
// Coprocessor data transfer
|
||||||
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
||||||
uint8_t offset = get_bit_range(insn, 0, 7);
|
uint8_t offset = bit_range(insn, 0, 7);
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
uint8_t cpn = bit_range(insn, 8, 11);
|
||||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
uint8_t crd = bit_range(insn, 12, 15);
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
uint8_t rn = bit_range(insn, 16, 19);
|
||||||
bool load = get_nth_bit(insn, 20);
|
bool load = get_bit(insn, 20);
|
||||||
bool write = get_nth_bit(insn, 21);
|
bool write = get_bit(insn, 21);
|
||||||
bool len = get_nth_bit(insn, 22);
|
bool len = get_bit(insn, 22);
|
||||||
bool up = get_nth_bit(insn, 23);
|
bool up = get_bit(insn, 23);
|
||||||
bool pre = get_nth_bit(insn, 24);
|
bool pre = get_bit(insn, 24);
|
||||||
|
|
||||||
data = CoprocessorDataTransfer{ .offset = offset,
|
data = CoprocessorDataTransfer{ .offset = offset,
|
||||||
.cpn = cpn,
|
.cpn = cpn,
|
||||||
@@ -176,12 +241,12 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Coprocessor data operation
|
// Coprocessor data operation
|
||||||
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
||||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
uint8_t crm = bit_range(insn, 0, 4);
|
||||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
uint8_t cp = bit_range(insn, 5, 7);
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
uint8_t cpn = bit_range(insn, 8, 11);
|
||||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
uint8_t crd = bit_range(insn, 12, 15);
|
||||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
uint8_t crn = bit_range(insn, 16, 19);
|
||||||
uint8_t cp_opc = get_bit_range(insn, 20, 23);
|
uint8_t cp_opc = bit_range(insn, 20, 23);
|
||||||
|
|
||||||
data = CoprocessorDataOperation{ .crm = crm,
|
data = CoprocessorDataOperation{ .crm = crm,
|
||||||
.cp = cp,
|
.cp = cp,
|
||||||
@@ -192,13 +257,13 @@ ArmInstruction::ArmInstruction(uint32_t insn)
|
|||||||
|
|
||||||
// Coprocessor register transfer
|
// Coprocessor register transfer
|
||||||
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
||||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
uint8_t crm = bit_range(insn, 0, 4);
|
||||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
uint8_t cp = bit_range(insn, 5, 7);
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
uint8_t cpn = bit_range(insn, 8, 11);
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = bit_range(insn, 12, 15);
|
||||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
uint8_t crn = bit_range(insn, 16, 19);
|
||||||
bool load = get_nth_bit(insn, 20);
|
bool load = get_bit(insn, 20);
|
||||||
uint8_t cp_opc = get_bit_range(insn, 21, 23);
|
uint8_t cp_opc = bit_range(insn, 21, 23);
|
||||||
|
|
||||||
data = CoprocessorRegisterTransfer{ .crm = crm,
|
data = CoprocessorRegisterTransfer{ .crm = crm,
|
||||||
.cp = cp,
|
.cp = cp,
|
||||||
@@ -319,6 +384,81 @@ ArmInstruction::disassemble() {
|
|||||||
(data.pre ? expression : ""),
|
(data.pre ? expression : ""),
|
||||||
(data.pre ? (data.write ? "!" : "") : 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) {
|
||||||
|
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](SoftwareInterrupt) { return fmt::format("SWI{}", condition); },
|
||||||
[this](CoprocessorDataTransfer& data) {
|
[this](CoprocessorDataTransfer& data) {
|
||||||
std::string expression = fmt::format(",#{:d}", data.offset);
|
std::string expression = fmt::format(",#{:d}", data.offset);
|
||||||
|
@@ -5,6 +5,16 @@
|
|||||||
Psr::Psr(uint32_t raw)
|
Psr::Psr(uint32_t raw)
|
||||||
: psr(raw & PSR_CLEAR_RESERVED) {}
|
: psr(raw & PSR_CLEAR_RESERVED) {}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
Psr::raw() const {
|
||||||
|
return psr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Psr::set_all(uint32_t raw) {
|
||||||
|
psr = raw & ~PSR_CLEAR_RESERVED;
|
||||||
|
}
|
||||||
|
|
||||||
Mode
|
Mode
|
||||||
Psr::mode() const {
|
Psr::mode() const {
|
||||||
return static_cast<Mode>(psr & ~PSR_CLEAR_MODE);
|
return static_cast<Mode>(psr & ~PSR_CLEAR_MODE);
|
||||||
@@ -18,20 +28,20 @@ Psr::set_mode(Mode mode) {
|
|||||||
|
|
||||||
State
|
State
|
||||||
Psr::state() const {
|
Psr::state() const {
|
||||||
return static_cast<State>(get_nth_bit(psr, 5));
|
return static_cast<State>(get_bit(psr, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Psr::set_state(State state) {
|
Psr::set_state(State state) {
|
||||||
chg_nth_bit(psr, 5, static_cast<bool>(state));
|
chg_bit(psr, 5, static_cast<bool>(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \
|
#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \
|
||||||
bool Psr::name() const { \
|
bool Psr::name() const { \
|
||||||
return get_nth_bit(psr, n); \
|
return get_bit(psr, n); \
|
||||||
} \
|
} \
|
||||||
void Psr::set_##name(bool val) { \
|
void Psr::set_##name(bool val) { \
|
||||||
chg_nth_bit(psr, n, val); \
|
chg_bit(psr, n, val); \
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6)
|
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6)
|
||||||
|
@@ -35,13 +35,45 @@ operator<<(std::ostream& os, const Condition cond) {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, const OpCode opcode) {
|
||||||
|
|
||||||
|
#define CASE(opcode) \
|
||||||
|
case OpCode::opcode: \
|
||||||
|
os << #opcode; \
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
CASE(AND)
|
||||||
|
CASE(EOR)
|
||||||
|
CASE(SUB)
|
||||||
|
CASE(RSB)
|
||||||
|
CASE(ADD)
|
||||||
|
CASE(ADC)
|
||||||
|
CASE(SBC)
|
||||||
|
CASE(RSC)
|
||||||
|
CASE(TST)
|
||||||
|
CASE(TEQ)
|
||||||
|
CASE(CMP)
|
||||||
|
CASE(CMN)
|
||||||
|
CASE(ORR)
|
||||||
|
CASE(MOV)
|
||||||
|
CASE(BIC)
|
||||||
|
CASE(MVN)
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CASE
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
||||||
switch (shift_type) {
|
switch (shift_type) {
|
||||||
case ShiftType::LSL:
|
case ShiftType::LSL:
|
||||||
|
|
||||||
if (amount > 0 && amount <= 32)
|
if (amount > 0 && amount <= 32)
|
||||||
carry = get_nth_bit(value, 32 - amount);
|
carry = get_bit(value, 32 - amount);
|
||||||
else if (amount > 32)
|
else if (amount > 32)
|
||||||
carry = 0;
|
carry = 0;
|
||||||
|
|
||||||
@@ -49,28 +81,28 @@ eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
|||||||
case ShiftType::LSR:
|
case ShiftType::LSR:
|
||||||
|
|
||||||
if (amount > 0 && amount <= 32)
|
if (amount > 0 && amount <= 32)
|
||||||
carry = get_nth_bit(value, amount - 1);
|
carry = get_bit(value, amount - 1);
|
||||||
else if (amount > 32)
|
else if (amount > 32)
|
||||||
carry = 0;
|
carry = 0;
|
||||||
else
|
else
|
||||||
carry = get_nth_bit(value, 31);
|
carry = get_bit(value, 31);
|
||||||
|
|
||||||
return value >> amount;
|
return value >> amount;
|
||||||
case ShiftType::ASR:
|
case ShiftType::ASR:
|
||||||
if (amount > 0 && amount <= 32)
|
if (amount > 0 && amount <= 32)
|
||||||
carry = get_nth_bit(value, amount - 1);
|
carry = get_bit(value, amount - 1);
|
||||||
else
|
else
|
||||||
carry = get_nth_bit(value, 31);
|
carry = get_bit(value, 31);
|
||||||
|
|
||||||
return static_cast<int32_t>(value) >> amount;
|
return static_cast<int32_t>(value) >> amount;
|
||||||
case ShiftType::ROR:
|
case ShiftType::ROR:
|
||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
bool old_carry = carry;
|
bool old_carry = carry;
|
||||||
|
|
||||||
carry = get_nth_bit(value, 0);
|
carry = get_bit(value, 0);
|
||||||
return (value >> 1) | (old_carry << 31);
|
return (value >> 1) | (old_carry << 31);
|
||||||
} else {
|
} else {
|
||||||
carry = get_nth_bit(value, (amount % 32 + 31) % 32);
|
carry = get_bit(value, (amount % 32 + 31) % 32);
|
||||||
return std::rotr(value, amount);
|
return std::rotr(value, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,32 +7,32 @@ using std::size_t;
|
|||||||
|
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline bool
|
inline bool
|
||||||
get_nth_bit(Int num, size_t n) {
|
get_bit(Int num, size_t n) {
|
||||||
return (num >> n) & 1;
|
return (num >> n) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline void
|
inline void
|
||||||
set_nth_bit(Int& num, size_t n) {
|
set_bit(Int& num, size_t n) {
|
||||||
num |= (1 << n);
|
num |= (1 << n);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline void
|
inline void
|
||||||
rst_nth_bit(Int& num, size_t n) {
|
rst_bit(Int& num, size_t n) {
|
||||||
num &= ~(1 << n);
|
num &= ~(1 << n);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline void
|
inline void
|
||||||
chg_nth_bit(Int& num, size_t n, bool x) {
|
chg_bit(Int& num, size_t n, bool x) {
|
||||||
num = (num & ~(1 << n)) | (x << n);
|
num = (num & ~(1 << n)) | (x << n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read range of bits from start to end inclusive
|
/// read range of bits from start to end inclusive
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline Int
|
inline Int
|
||||||
get_bit_range(Int num, size_t start, size_t end) {
|
bit_range(Int num, size_t start, size_t end) {
|
||||||
// NOTE: we do not require -1 if it is a signed integral
|
// NOTE: we do not require -1 if it is a signed integral
|
||||||
Int left =
|
Int left =
|
||||||
std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end;
|
std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end;
|
||||||
|
Reference in New Issue
Block a user