refactor: make linter happy
also add a few unused coprocessor instructions Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -7,10 +7,10 @@
|
||||
using namespace logger;
|
||||
|
||||
Cpu::Cpu(Bus& bus)
|
||||
: gpr(0)
|
||||
: bus(std::make_shared<Bus>(bus))
|
||||
, gpr({ 0 })
|
||||
, cpsr(0)
|
||||
, spsr(0)
|
||||
, bus(std::make_shared<Bus>(bus))
|
||||
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
||||
, spsr_banked({ 0, 0, 0, 0, 0 }) {
|
||||
cpsr.set_mode(Mode::System);
|
||||
@@ -18,20 +18,25 @@ Cpu::Cpu(Bus& bus)
|
||||
cpsr.set_fiq_disabled(true);
|
||||
cpsr.set_state(State::Arm);
|
||||
log_info("CPU successfully initialised");
|
||||
|
||||
// PC is always two instructions ahead in the pipeline
|
||||
pc += 2 * ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
|
||||
/* change modes */
|
||||
void
|
||||
Cpu::chg_mode(Mode from, Mode to) {
|
||||
Cpu::chg_mode(const Mode to) {
|
||||
Mode from = cpsr.mode();
|
||||
|
||||
if (from == to)
|
||||
return;
|
||||
|
||||
/* TODO: replace visible registers with view once I understand how to
|
||||
* concatenate views */
|
||||
#define STORE_BANKED(mode, MODE) \
|
||||
std::copy(gpr + GPR_##MODE##_BANKED_FIRST, \
|
||||
gpr + GPR_##MODE##_BANKED_FIRST + GPR_##MODE##_BANKED_COUNT, \
|
||||
gpr_banked.mode)
|
||||
std::copy(gpr.begin() + GPR_##MODE##_FIRST, \
|
||||
gpr.begin() + GPR_COUNT - 1, \
|
||||
gpr_banked.mode.begin())
|
||||
|
||||
switch (from) {
|
||||
case Mode::Fiq:
|
||||
@@ -66,9 +71,9 @@ Cpu::chg_mode(Mode from, Mode to) {
|
||||
}
|
||||
|
||||
#define RESTORE_BANKED(mode, MODE) \
|
||||
std::copy(gpr_banked.mode, \
|
||||
gpr_banked.mode + GPR_##MODE##_BANKED_COUNT, \
|
||||
gpr + GPR_##MODE##_BANKED_FIRST)
|
||||
std::copy(gpr_banked.mode.begin(), \
|
||||
gpr_banked.mode.end(), \
|
||||
gpr.begin() + GPR_##MODE##_FIRST)
|
||||
|
||||
switch (to) {
|
||||
case Mode::Fiq:
|
||||
@@ -109,11 +114,15 @@ Cpu::chg_mode(Mode from, Mode to) {
|
||||
|
||||
void
|
||||
Cpu::step() {
|
||||
uint32_t insn = 0xffffffff;
|
||||
uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
|
||||
|
||||
if (cpsr.state() == State::Arm) {
|
||||
std::string disassembled = exec_arm(insn);
|
||||
log_info("{:#010X} : {}", gpr[15], disassembled);
|
||||
gpr[15] += ARM_INSTRUCTION_SIZE;
|
||||
log_info("{:#034b}", bus->read_word(cur_pc));
|
||||
|
||||
std::string disassembled = exec_arm(bus->read_word(cur_pc));
|
||||
|
||||
log_info("{:#010X} : {}", cur_pc, disassembled);
|
||||
|
||||
pc += ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
}
|
||||
|
@@ -13,40 +13,32 @@ class Cpu {
|
||||
void step();
|
||||
|
||||
private:
|
||||
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
|
||||
static constexpr size_t GPR_FIQ_BANKED_COUNT = 7;
|
||||
static constexpr size_t GPR_COUNT = 16;
|
||||
|
||||
static constexpr size_t GPR_SVC_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_SVC_BANKED_COUNT = 2;
|
||||
static constexpr size_t GPR_FIQ_FIRST = 8;
|
||||
static constexpr size_t GPR_SVC_FIRST = 13;
|
||||
static constexpr size_t GPR_ABT_FIRST = 13;
|
||||
static constexpr size_t GPR_IRQ_FIRST = 13;
|
||||
static constexpr size_t GPR_UND_FIRST = 13;
|
||||
static constexpr size_t GPR_SYS_USR_FIRST = 8;
|
||||
|
||||
static constexpr size_t GPR_ABT_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_ABT_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_IRQ_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_IRQ_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_UND_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_UND_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_SYS_USR_BANKED_FIRST = 8;
|
||||
static constexpr size_t GPR_SYS_USR_BANKED_COUNT = 7;
|
||||
|
||||
static constexpr size_t GPR_VISIBLE_COUNT = 16;
|
||||
|
||||
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
|
||||
Psr cpsr; // current program status register
|
||||
Psr spsr; // status program status register
|
||||
std::shared_ptr<Bus> bus;
|
||||
std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
|
||||
|
||||
Psr cpsr; // current program status register
|
||||
Psr spsr; // status program status register
|
||||
|
||||
uint32_t& pc = gpr[15];
|
||||
|
||||
struct {
|
||||
uint32_t fiq[GPR_FIQ_BANKED_COUNT];
|
||||
uint32_t svc[GPR_SVC_BANKED_COUNT];
|
||||
uint32_t abt[GPR_ABT_BANKED_COUNT];
|
||||
uint32_t irq[GPR_IRQ_BANKED_COUNT];
|
||||
uint32_t und[GPR_UND_BANKED_COUNT];
|
||||
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
||||
std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
|
||||
std::array<uint32_t, GPR_COUNT - GPR_ABT_FIRST - 1> abt;
|
||||
std::array<uint32_t, GPR_COUNT - GPR_IRQ_FIRST - 1> irq;
|
||||
std::array<uint32_t, GPR_COUNT - GPR_UND_FIRST - 1> und;
|
||||
|
||||
// visible registers before the mode switch
|
||||
uint32_t old[GPR_SYS_USR_BANKED_COUNT];
|
||||
std::array<uint32_t, GPR_COUNT - GPR_SYS_USR_FIRST> old;
|
||||
} gpr_banked; // banked general purpose registers
|
||||
|
||||
struct {
|
||||
@@ -57,6 +49,6 @@ class Cpu {
|
||||
Psr und;
|
||||
} spsr_banked; // banked saved program status registers
|
||||
|
||||
void chg_mode(Mode from, Mode to);
|
||||
std::string exec_arm(uint32_t insn);
|
||||
void chg_mode(const Mode to);
|
||||
std::string exec_arm(const uint32_t insn);
|
||||
};
|
||||
|
@@ -5,25 +5,27 @@
|
||||
#include <cstdint>
|
||||
|
||||
using namespace logger;
|
||||
using std::array;
|
||||
using stringv = std::string_view;
|
||||
|
||||
std::string
|
||||
Cpu::exec_arm(uint32_t insn) {
|
||||
Cpu::exec_arm(const uint32_t insn) {
|
||||
Condition cond = static_cast<Condition>(get_bit_range(insn, 28, 31));
|
||||
std::string disassembled;
|
||||
|
||||
auto pc_error = [](uint8_t r, const char* syn) {
|
||||
auto pc_error = [](uint8_t r, stringv syn) {
|
||||
if (r == 15)
|
||||
log_error("Using PC (R15) as operand in {}", syn);
|
||||
};
|
||||
|
||||
auto pc_undefined = [](uint8_t r, const char* syn) {
|
||||
auto pc_undefined = [](uint8_t r, stringv syn) {
|
||||
if (r == 15)
|
||||
log_warn("Using PC (R15) as operand in {}", syn);
|
||||
};
|
||||
|
||||
// Branch and exhcange
|
||||
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
||||
static constexpr char syn[] = "BX";
|
||||
static constexpr stringv syn = "BX";
|
||||
|
||||
uint8_t rn = insn & 0b1111;
|
||||
|
||||
@@ -38,16 +40,16 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
cpsr.set_state(state);
|
||||
|
||||
// copy to PC
|
||||
gpr[15] = gpr[rn];
|
||||
pc = gpr[rn];
|
||||
|
||||
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||
rst_nth_bit(gpr[15], 0);
|
||||
rst_nth_bit(pc, 0);
|
||||
if (state == State::Arm)
|
||||
rst_nth_bit(gpr[15], 1);
|
||||
rst_nth_bit(pc, 1);
|
||||
}
|
||||
// Branch
|
||||
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
||||
static constexpr char syn[] = "B";
|
||||
static constexpr stringv syn = "B";
|
||||
|
||||
bool link = get_nth_bit(insn, 24);
|
||||
uint32_t offset = get_bit_range(insn, 0, 23);
|
||||
@@ -63,14 +65,14 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
offset |= 0xFC000000;
|
||||
|
||||
if (link)
|
||||
gpr[14] = gpr[15] - ARM_INSTRUCTION_SIZE;
|
||||
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
||||
|
||||
gpr[15] += offset;
|
||||
pc += offset - ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
|
||||
// Multiply
|
||||
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
||||
static constexpr char syn[2][4] = { "MUL", "MLA" };
|
||||
static constexpr array<stringv, 2> syn = { "MUL", "MLA" };
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||
@@ -117,8 +119,9 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
}
|
||||
// Multiply long
|
||||
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
||||
static constexpr char syn[2][2][6] = { { "SMULL", "SMLAL" },
|
||||
{ "UMULL", "UMLAL" } };
|
||||
static constexpr array<array<stringv, 2>, 2> syn = {
|
||||
{ { "SMULL", "SMLAL" }, { "UMULL", "UMLAL" } }
|
||||
};
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||
@@ -157,7 +160,7 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
gpr[rdhi] = get_bit_range(eval, 32, 63);
|
||||
|
||||
} else {
|
||||
int64_t eval = static_cast<uint64_t>(gpr[rm]) *
|
||||
int64_t eval = static_cast<int64_t>(gpr[rm]) *
|
||||
static_cast<int64_t>(gpr[rs]) +
|
||||
(a ? static_cast<int64_t>(gpr[rdhi]) << 32 |
|
||||
static_cast<int64_t>(gpr[rdlo])
|
||||
@@ -178,7 +181,7 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
|
||||
// Single data swap
|
||||
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
||||
static constexpr char syn[] = "SWP";
|
||||
static constexpr stringv syn = "SWP";
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
@@ -206,32 +209,32 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
// TODO: create abstraction to reuse for block data and single data
|
||||
// transfer
|
||||
} else if ((insn & 0x0E000090) == 0x00000090) {
|
||||
static constexpr char syn[2][4] = { "STR", "LDR" };
|
||||
static constexpr array<stringv, 2> syn_ = { "STR", "LDR" };
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t h = get_nth_bit(insn, 5);
|
||||
uint8_t s = get_nth_bit(insn, 6);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
bool w = get_nth_bit(insn, 21);
|
||||
bool imm = get_nth_bit(insn, 22);
|
||||
bool u = get_nth_bit(insn, 23);
|
||||
bool p = get_nth_bit(insn, 24);
|
||||
uint32_t offset;
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t h = get_nth_bit(insn, 5);
|
||||
uint8_t s = get_nth_bit(insn, 6);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
bool w = get_nth_bit(insn, 21);
|
||||
bool imm = get_nth_bit(insn, 22);
|
||||
bool u = get_nth_bit(insn, 23);
|
||||
bool p = get_nth_bit(insn, 24);
|
||||
uint32_t offset = 0;
|
||||
|
||||
std::string syn =
|
||||
fmt::format("{}{}{}", syn_[l], (s ? "S" : ""), (h ? 'H' : 'B'));
|
||||
|
||||
if (!p && w)
|
||||
log_error("Write-back enabled with post-indexing in {}", syn[l]);
|
||||
log_error("Write-back enabled with post-indexing in {}", syn);
|
||||
|
||||
if (s && !l)
|
||||
log_error("Signed data found in {}", syn[l]);
|
||||
log_error("Signed data found in {}", syn);
|
||||
|
||||
if (w)
|
||||
pc_error(rn, syn[l]);
|
||||
pc_error(rm, syn[l]);
|
||||
|
||||
if (rd == 15 && !l && s && h)
|
||||
;
|
||||
pc_error(rn, syn);
|
||||
pc_error(rm, syn);
|
||||
|
||||
{
|
||||
offset = (imm ? get_bit_range(insn, 8, 11) << 4 | rm : gpr[rm]);
|
||||
@@ -241,8 +244,8 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
(imm ? offset : rm));
|
||||
|
||||
disassembled = fmt::format(
|
||||
"{}{}{}{} R{:d}{}",
|
||||
syn[l],
|
||||
"{}{}{}{} R{:d},{}",
|
||||
syn_[l],
|
||||
cond,
|
||||
(s ? "S" : ""),
|
||||
(h ? 'H' : 'B'),
|
||||
@@ -254,7 +257,10 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
}
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
uint32_t address = (u ? gpr[rn] + offset : gpr[rn] - offset);
|
||||
uint32_t address = gpr[rn];
|
||||
|
||||
if (p)
|
||||
address += (u ? offset : -offset);
|
||||
|
||||
// load
|
||||
if (l) {
|
||||
@@ -287,7 +293,92 @@ Cpu::exec_arm(uint32_t insn) {
|
||||
bus->write_halfword(address, gpr[rd]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!p)
|
||||
address += (u ? offset : -offset);
|
||||
|
||||
if (!p || w)
|
||||
gpr[rn] = address;
|
||||
}
|
||||
|
||||
// Software Interrupt
|
||||
// What to do here?
|
||||
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
||||
static constexpr stringv syn = "SWI";
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
chg_mode(Mode::Supervisor);
|
||||
pc = 0x08;
|
||||
spsr = cpsr;
|
||||
}
|
||||
|
||||
disassembled = fmt::format("{}{} 0", syn, cond);
|
||||
|
||||
// Coprocessor data transfer
|
||||
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
||||
static constexpr array<stringv, 2> syn = { "STC", "LDC" };
|
||||
|
||||
[[maybe_unused]] uint8_t offset = get_bit_range(insn, 0, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
||||
[[maybe_unused]] uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
[[maybe_unused]] bool w = get_nth_bit(insn, 21);
|
||||
bool n = get_nth_bit(insn, 22);
|
||||
[[maybe_unused]] bool u = get_nth_bit(insn, 23);
|
||||
[[maybe_unused]] bool p = get_nth_bit(insn, 24);
|
||||
|
||||
disassembled = fmt::format(
|
||||
"{}{}{} p{},c{},{}", syn[l], cond, (n ? "L" : ""), cpn, crd, "a.");
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn[l]);
|
||||
|
||||
// Coprocessor data operation
|
||||
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
||||
static constexpr stringv syn = "CDP";
|
||||
|
||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||
uint8_t cp_opc = get_bit_range(insn, 20, 23);
|
||||
|
||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
||||
syn,
|
||||
cond,
|
||||
cpn,
|
||||
cp_opc,
|
||||
crd,
|
||||
crn,
|
||||
crm,
|
||||
cp);
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn);
|
||||
|
||||
// Coprocessor register transfer
|
||||
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
||||
static constexpr array<stringv, 2> syn = { "MCR", "MRC" };
|
||||
|
||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
uint8_t cp_opc = get_bit_range(insn, 21, 23);
|
||||
|
||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
||||
syn[l],
|
||||
cond,
|
||||
cpn,
|
||||
cp_opc,
|
||||
rd,
|
||||
crn,
|
||||
crm,
|
||||
cp);
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn[l]);
|
||||
}
|
||||
|
||||
return disassembled;
|
||||
|
@@ -2,9 +2,8 @@
|
||||
#include "util/bits.hh"
|
||||
#include "util/log.hh"
|
||||
|
||||
Psr::Psr(uint32_t raw) {
|
||||
psr = raw & PSR_CLEAR_RESERVED;
|
||||
}
|
||||
Psr::Psr(uint32_t raw)
|
||||
: psr(raw & PSR_CLEAR_RESERVED) {}
|
||||
|
||||
Mode
|
||||
Psr::mode() const {
|
||||
|
@@ -32,3 +32,23 @@ operator<<(std::ostream& os, const Condition cond) {
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const ShiftType shift_type) {
|
||||
|
||||
#define CASE(type) \
|
||||
case ShiftType::type: \
|
||||
os << #type; \
|
||||
break;
|
||||
|
||||
switch (shift_type) {
|
||||
CASE(LSL)
|
||||
CASE(LSR)
|
||||
CASE(ASR)
|
||||
CASE(ROR)
|
||||
}
|
||||
|
||||
#undef CASE
|
||||
|
||||
return os;
|
||||
}
|
||||
|
@@ -40,6 +40,12 @@ enum class Condition {
|
||||
AL = 0b1110
|
||||
};
|
||||
|
||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Condition cond);
|
||||
template<>
|
||||
struct fmt::formatter<Condition> : ostream_formatter {};
|
||||
|
||||
enum class OpCode {
|
||||
AND = 0b0000,
|
||||
EOR = 0b0001,
|
||||
@@ -68,6 +74,6 @@ enum class ShiftType {
|
||||
|
||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Condition cond);
|
||||
operator<<(std::ostream& os, const ShiftType cond);
|
||||
template<>
|
||||
struct fmt::formatter<Condition> : ostream_formatter {};
|
||||
struct fmt::formatter<ShiftType> : ostream_formatter {};
|
||||
|
Reference in New Issue
Block a user