[UNTESTED] refactor how instructions are parsed
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
@@ -83,7 +84,10 @@ main(int argc, const char* argv[]) {
|
|||||||
Memory memory(std::move(bios), std::move(rom));
|
Memory memory(std::move(bios), std::move(rom));
|
||||||
Bus bus(memory);
|
Bus bus(memory);
|
||||||
Cpu cpu(bus);
|
Cpu cpu(bus);
|
||||||
cpu.step();
|
while (true) {
|
||||||
|
cpu.step();
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
1
include/cpu/instruction.hh
Symbolic link
1
include/cpu/instruction.hh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/cpu/instruction.hh
|
@@ -1,5 +1,6 @@
|
|||||||
headers += files(
|
headers += files(
|
||||||
'cpu.hh',
|
'cpu.hh',
|
||||||
|
'instruction.hh',
|
||||||
'psr.hh',
|
'psr.hh',
|
||||||
'utility.hh'
|
'utility.hh'
|
||||||
)
|
)
|
@@ -1,79 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
static constexpr size_t ARM_INSTRUCTION_SIZE = 4;
|
|
||||||
static constexpr size_t THUMB_INSTRUCTION_SIZE = 2;
|
|
||||||
|
|
||||||
enum class Mode {
|
|
||||||
/* M[4:0] in PSR */
|
|
||||||
User = 0b10000,
|
|
||||||
Fiq = 0b10001,
|
|
||||||
Irq = 0b10010,
|
|
||||||
Supervisor = 0b10011,
|
|
||||||
Abort = 0b10111,
|
|
||||||
Undefined = 0b11011,
|
|
||||||
System = 0b11111,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class State {
|
|
||||||
Arm = 0,
|
|
||||||
Thumb = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Condition {
|
|
||||||
EQ = 0b0000,
|
|
||||||
NE = 0b0001,
|
|
||||||
CS = 0b0010,
|
|
||||||
CC = 0b0011,
|
|
||||||
MI = 0b0100,
|
|
||||||
PL = 0b0101,
|
|
||||||
VS = 0b0110,
|
|
||||||
VC = 0b0111,
|
|
||||||
HI = 0b1000,
|
|
||||||
LS = 0b1001,
|
|
||||||
GE = 0b1010,
|
|
||||||
LT = 0b1011,
|
|
||||||
GT = 0b1100,
|
|
||||||
LE = 0b1101,
|
|
||||||
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,
|
|
||||||
SUB = 0b0010,
|
|
||||||
RSB = 0b0011,
|
|
||||||
ADD = 0b0100,
|
|
||||||
ADC = 0b0101,
|
|
||||||
SBC = 0b0110,
|
|
||||||
RSC = 0b0111,
|
|
||||||
TST = 0b1000,
|
|
||||||
TEQ = 0b1001,
|
|
||||||
CMP = 0b1010,
|
|
||||||
CMN = 0b1011,
|
|
||||||
ORR = 0b1100,
|
|
||||||
MOV = 0b1101,
|
|
||||||
BIC = 0b1110,
|
|
||||||
MVN = 0b1111
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShiftType {
|
|
||||||
LSL = 0b00,
|
|
||||||
LSR = 0b01,
|
|
||||||
ASR = 0b10,
|
|
||||||
ROR = 0b11
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
|
||||||
std::ostream&
|
|
||||||
operator<<(std::ostream& os, const ShiftType cond);
|
|
||||||
template<>
|
|
||||||
struct fmt::formatter<ShiftType> : ostream_formatter {};
|
|
1
include/cpu/utility.hh
Symbolic link
1
include/cpu/utility.hh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/cpu/utility.hh
|
265
src/cpu/cpu.cc
265
src/cpu/cpu.cc
@@ -1,4 +1,5 @@
|
|||||||
#include "cpu.hh"
|
#include "cpu.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
#include "util/log.hh"
|
#include "util/log.hh"
|
||||||
#include "utility.hh"
|
#include "utility.hh"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -112,16 +113,276 @@ Cpu::chg_mode(const Mode to) {
|
|||||||
cpsr.set_mode(to);
|
cpsr.set_mode(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cpu::exec_arm(const ArmInstruction instruction) {
|
||||||
|
auto cond = instruction.get_condition();
|
||||||
|
auto data = instruction.get_data();
|
||||||
|
|
||||||
|
if (!cpsr.condition(cond)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pc_error = [](uint8_t r) {
|
||||||
|
if (r == 15)
|
||||||
|
log_error("Using PC (R15) as operand register");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pc_warn = [](uint8_t r) {
|
||||||
|
if (r == 15)
|
||||||
|
log_warn("Using PC (R15) as operand register");
|
||||||
|
};
|
||||||
|
|
||||||
|
std::visit(
|
||||||
|
overloaded{
|
||||||
|
[this, pc_warn](ArmInstruction::BranchAndExchange& data) {
|
||||||
|
State state = static_cast<State>(data.rn & 1);
|
||||||
|
|
||||||
|
pc_warn(data.rn);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
cpsr.set_state(state);
|
||||||
|
|
||||||
|
// copy to PC
|
||||||
|
pc = gpr[data.rn];
|
||||||
|
|
||||||
|
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||||
|
rst_nth_bit(pc, 0);
|
||||||
|
|
||||||
|
if (state == State::Arm)
|
||||||
|
rst_nth_bit(pc, 1);
|
||||||
|
},
|
||||||
|
[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)
|
||||||
|
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
|
pc += offset - ARM_INSTRUCTION_SIZE;
|
||||||
|
},
|
||||||
|
[this, pc_error](ArmInstruction::Multiply& data) {
|
||||||
|
if (data.rd == data.rm)
|
||||||
|
log_error("rd and rm are not distinct in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
|
||||||
|
pc_error(data.rd);
|
||||||
|
pc_error(data.rd);
|
||||||
|
pc_error(data.rd);
|
||||||
|
|
||||||
|
gpr[data.rd] =
|
||||||
|
gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0);
|
||||||
|
|
||||||
|
if (data.set) {
|
||||||
|
cpsr.set_z(!static_cast<bool>(gpr[data.rd]));
|
||||||
|
cpsr.set_n(get_nth_bit(gpr[data.rd], 31));
|
||||||
|
cpsr.set_c(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this, pc_error](ArmInstruction::MultiplyLong& data) {
|
||||||
|
if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
|
||||||
|
data.rdlo == data.rm)
|
||||||
|
log_error("rdhi, rdlo and rm are not distinct in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
|
||||||
|
pc_error(data.rdhi);
|
||||||
|
pc_error(data.rdlo);
|
||||||
|
pc_error(data.rm);
|
||||||
|
pc_error(data.rs);
|
||||||
|
if (data.uns) {
|
||||||
|
uint64_t eval =
|
||||||
|
static_cast<uint64_t>(gpr[data.rm]) *
|
||||||
|
static_cast<uint64_t>(gpr[data.rs]) +
|
||||||
|
(data.acc ? static_cast<uint64_t>(gpr[data.rdhi]) << 32 |
|
||||||
|
static_cast<uint64_t>(gpr[data.rdlo])
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
||||||
|
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int64_t eval =
|
||||||
|
static_cast<int64_t>(gpr[data.rm]) *
|
||||||
|
static_cast<int64_t>(gpr[data.rs]) +
|
||||||
|
(data.acc ? static_cast<int64_t>(gpr[data.rdhi]) << 32 |
|
||||||
|
static_cast<int64_t>(gpr[data.rdlo])
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
||||||
|
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.set) {
|
||||||
|
cpsr.set_z(!(static_cast<bool>(gpr[data.rdhi]) ||
|
||||||
|
static_cast<bool>(gpr[data.rdlo])));
|
||||||
|
cpsr.set_n(get_nth_bit(gpr[data.rdhi], 31));
|
||||||
|
cpsr.set_c(0);
|
||||||
|
cpsr.set_v(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
|
||||||
|
[this, pc_warn](ArmInstruction::SingleDataSwap& data) {
|
||||||
|
pc_warn(data.rm);
|
||||||
|
pc_warn(data.rn);
|
||||||
|
pc_warn(data.rd);
|
||||||
|
|
||||||
|
if (data.byte) {
|
||||||
|
gpr[data.rd] = bus->read_byte(gpr[data.rn]);
|
||||||
|
bus->write_byte(gpr[data.rn], gpr[data.rm] & 0xFF);
|
||||||
|
} else {
|
||||||
|
gpr[data.rd] = bus->read_word(gpr[data.rn]);
|
||||||
|
bus->write_word(gpr[data.rn], gpr[data.rm]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this, pc_warn, pc_error](ArmInstruction::SingleDataTransfer& data) {
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint32_t address = gpr[data.rn];
|
||||||
|
|
||||||
|
if (!data.pre && data.write)
|
||||||
|
log_warn("Write-back enabled with post-indexing in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
|
||||||
|
if (data.write)
|
||||||
|
pc_warn(data.rn);
|
||||||
|
|
||||||
|
// evaluate the offset
|
||||||
|
if (const uint16_t* immediate =
|
||||||
|
std::get_if<uint16_t>(&data.offset)) {
|
||||||
|
offset = *immediate;
|
||||||
|
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
|
||||||
|
|
||||||
|
cpsr.set_c(carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PC is always two instructions ahead
|
||||||
|
if (data.rn == 15)
|
||||||
|
address -= 2 * ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
|
if (data.pre)
|
||||||
|
address += (data.up ? offset : -offset);
|
||||||
|
|
||||||
|
// load
|
||||||
|
if (data.load) {
|
||||||
|
// byte
|
||||||
|
if (data.byte)
|
||||||
|
gpr[data.rd] = bus->read_byte(address);
|
||||||
|
// word
|
||||||
|
else
|
||||||
|
gpr[data.rd] = bus->read_word(address);
|
||||||
|
// store
|
||||||
|
} else {
|
||||||
|
// take PC into consideration
|
||||||
|
if (data.rd == 15)
|
||||||
|
address += ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
|
// byte
|
||||||
|
if (data.byte)
|
||||||
|
bus->write_byte(address, gpr[data.rd] & 0xFF);
|
||||||
|
// word
|
||||||
|
else
|
||||||
|
bus->write_word(address, gpr[data.rd]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.pre)
|
||||||
|
address += (data.up ? offset : -offset);
|
||||||
|
|
||||||
|
if (!data.pre || data.write)
|
||||||
|
gpr[data.rn] = address;
|
||||||
|
},
|
||||||
|
[this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
|
||||||
|
uint32_t address = gpr[data.rn];
|
||||||
|
|
||||||
|
if (!data.pre && data.write)
|
||||||
|
log_error("Write-back enabled with post-indexing in {}",
|
||||||
|
typeid(data).name());
|
||||||
|
|
||||||
|
if (data.sign && !data.load)
|
||||||
|
log_error("Signed data found in {}", typeid(data).name());
|
||||||
|
|
||||||
|
if (data.write)
|
||||||
|
pc_warn(data.rn);
|
||||||
|
|
||||||
|
// offset is register number (4 bits) when not an immediate
|
||||||
|
if (!data.imm)
|
||||||
|
pc_error(data.offset);
|
||||||
|
|
||||||
|
if (data.pre)
|
||||||
|
address += (data.up ? data.offset : -data.offset);
|
||||||
|
|
||||||
|
// load
|
||||||
|
if (data.load) {
|
||||||
|
// signed
|
||||||
|
if (data.sign) {
|
||||||
|
// halfword
|
||||||
|
if (data.half) {
|
||||||
|
gpr[data.rd] = bus->read_halfword(address);
|
||||||
|
|
||||||
|
// sign extend the halfword
|
||||||
|
if (get_nth_bit(gpr[data.rd], 15))
|
||||||
|
gpr[data.rd] |= 0xFFFF0000;
|
||||||
|
// byte
|
||||||
|
} else {
|
||||||
|
gpr[data.rd] = bus->read_byte(address);
|
||||||
|
|
||||||
|
// sign extend the byte
|
||||||
|
if (get_nth_bit(gpr[data.rd], 7))
|
||||||
|
gpr[data.rd] |= 0xFFFFFF00;
|
||||||
|
}
|
||||||
|
// unsigned halfword
|
||||||
|
} else if (data.half) {
|
||||||
|
gpr[data.rd] = bus->read_halfword(address);
|
||||||
|
}
|
||||||
|
// store
|
||||||
|
} else {
|
||||||
|
// take PC into consideration
|
||||||
|
if (data.rd == 15)
|
||||||
|
address += ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
|
// halfword
|
||||||
|
if (data.half)
|
||||||
|
bus->write_halfword(address, gpr[data.rd]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.pre)
|
||||||
|
address += (data.up ? data.offset : -data.offset);
|
||||||
|
|
||||||
|
if (!data.pre || data.write)
|
||||||
|
gpr[data.rn] = address;
|
||||||
|
},
|
||||||
|
[this](ArmInstruction::SoftwareInterrupt) {
|
||||||
|
chg_mode(Mode::Supervisor);
|
||||||
|
pc = 0x08;
|
||||||
|
spsr = cpsr;
|
||||||
|
},
|
||||||
|
[](auto& data) { log_error("{} instruction", typeid(data).name()); } },
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cpu::step() {
|
Cpu::step() {
|
||||||
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));
|
||||||
log_info("{:#034b}", bus->read_word(cur_pc));
|
log_info("{:#034b}", bus->read_word(cur_pc));
|
||||||
|
|
||||||
std::string disassembled = exec_arm(bus->read_word(cur_pc));
|
exec_arm(instruction);
|
||||||
|
|
||||||
log_info("{:#010X} : {}", cur_pc, disassembled);
|
log_info("{:#010X} : {}", cur_pc, instruction.disassemble());
|
||||||
|
|
||||||
pc += ARM_INSTRUCTION_SIZE;
|
pc += ARM_INSTRUCTION_SIZE;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "bus.hh"
|
#include "bus.hh"
|
||||||
|
#include "instruction.hh"
|
||||||
#include "psr.hh"
|
#include "psr.hh"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -50,5 +51,5 @@ class Cpu {
|
|||||||
} spsr_banked; // banked saved program status registers
|
} spsr_banked; // banked saved program status registers
|
||||||
|
|
||||||
void chg_mode(const Mode to);
|
void chg_mode(const Mode to);
|
||||||
std::string exec_arm(const uint32_t insn);
|
void exec_arm(const ArmInstruction instruction);
|
||||||
};
|
};
|
||||||
|
@@ -1,342 +1,181 @@
|
|||||||
#include "cpu.hh"
|
#include "instruction.hh"
|
||||||
#include "cpu/utility.hh"
|
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
#include "util/log.hh"
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace logger;
|
|
||||||
using std::array;
|
|
||||||
using stringv = std::string_view;
|
|
||||||
|
|
||||||
std::string
|
|
||||||
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, stringv syn) {
|
|
||||||
if (r == 15)
|
|
||||||
log_error("Using PC (R15) as operand in {}", syn);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto pc_undefined = [](uint8_t r, stringv syn) {
|
|
||||||
if (r == 15)
|
|
||||||
log_warn("Using PC (R15) as operand in {}", syn);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
ArmInstruction::ArmInstruction(uint32_t insn)
|
||||||
|
: condition(static_cast<Condition>(get_bit_range(insn, 28, 31))) {
|
||||||
// Branch and exhcange
|
// Branch and exhcange
|
||||||
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
||||||
static constexpr stringv syn = "BX";
|
|
||||||
|
|
||||||
uint8_t rn = insn & 0b1111;
|
uint8_t rn = insn & 0b1111;
|
||||||
|
|
||||||
pc_undefined(rn, syn);
|
data = BranchAndExchange{ rn };
|
||||||
|
|
||||||
disassembled = fmt::format("{}{} R{:d}", syn, cond, rn);
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
|
||||||
State state = static_cast<State>(rn & 1);
|
|
||||||
|
|
||||||
// set state
|
|
||||||
cpsr.set_state(state);
|
|
||||||
|
|
||||||
// copy to PC
|
|
||||||
pc = gpr[rn];
|
|
||||||
|
|
||||||
// ignore [1:0] bits for arm and 0 bit for thumb
|
|
||||||
rst_nth_bit(pc, 0);
|
|
||||||
if (state == State::Arm)
|
|
||||||
rst_nth_bit(pc, 1);
|
|
||||||
}
|
|
||||||
// Branch
|
// Branch
|
||||||
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
||||||
static constexpr stringv syn = "B";
|
|
||||||
|
|
||||||
bool link = get_nth_bit(insn, 24);
|
bool link = get_nth_bit(insn, 24);
|
||||||
uint32_t offset = get_bit_range(insn, 0, 23);
|
uint32_t offset = get_bit_range(insn, 0, 23);
|
||||||
|
|
||||||
disassembled =
|
data = Branch{ .link = link, .offset = offset };
|
||||||
fmt::format("{}{}{} {:#08X}", syn, (link ? "L" : ""), cond, offset);
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
|
||||||
// lsh 2 and sign extend the 26 bit offset to 32
|
|
||||||
// bits
|
|
||||||
offset <<= 2;
|
|
||||||
if (get_nth_bit(offset, 25))
|
|
||||||
offset |= 0xFC000000;
|
|
||||||
|
|
||||||
if (link)
|
|
||||||
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
|
||||||
|
|
||||||
pc += offset - ARM_INSTRUCTION_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply
|
// Multiply
|
||||||
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
||||||
static constexpr array<stringv, 2> syn = { "MUL", "MLA" };
|
|
||||||
|
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||||
uint8_t rn = get_bit_range(insn, 12, 15);
|
uint8_t rn = get_bit_range(insn, 12, 15);
|
||||||
uint8_t rd = get_bit_range(insn, 16, 19);
|
uint8_t rd = get_bit_range(insn, 16, 19);
|
||||||
bool s = get_nth_bit(insn, 20);
|
bool set = get_nth_bit(insn, 20);
|
||||||
bool a = get_nth_bit(insn, 21);
|
bool acc = get_nth_bit(insn, 21);
|
||||||
|
|
||||||
if (rd == rm)
|
data = Multiply{
|
||||||
log_error("rd and rm are not distinct in {} : {:d}", syn[a], rd);
|
.rm = rm, .rs = rs, .rn = rn, .rd = rd, .set = set, .acc = acc
|
||||||
|
|
||||||
pc_error(rd, syn[a]);
|
|
||||||
pc_error(rm, syn[a]);
|
|
||||||
pc_error(rs, syn[a]);
|
|
||||||
|
|
||||||
if (a) {
|
|
||||||
pc_error(rn, syn[a]);
|
|
||||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
|
||||||
syn[a],
|
|
||||||
cond,
|
|
||||||
(s ? "S" : ""),
|
|
||||||
rd,
|
|
||||||
rm,
|
|
||||||
rs,
|
|
||||||
rn);
|
|
||||||
} else {
|
|
||||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d}",
|
|
||||||
syn[a],
|
|
||||||
cond,
|
|
||||||
(s ? "S" : ""),
|
|
||||||
rd,
|
|
||||||
rm,
|
|
||||||
rs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
|
||||||
gpr[rd] = gpr[rm] * gpr[rs] + (a ? gpr[rn] : 0);
|
|
||||||
|
|
||||||
if (s) {
|
|
||||||
cpsr.set_z(!static_cast<bool>(gpr[rd]));
|
|
||||||
cpsr.set_n(get_nth_bit(gpr[rd], 31));
|
|
||||||
cpsr.set_c(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Multiply long
|
|
||||||
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
|
||||||
static constexpr array<array<stringv, 2>, 2> syn = {
|
|
||||||
{ { "SMULL", "SMLAL" }, { "UMULL", "UMLAL" } }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Multiply long
|
||||||
|
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||||
uint8_t rdlo = get_bit_range(insn, 12, 15);
|
uint8_t rdlo = get_bit_range(insn, 12, 15);
|
||||||
uint8_t rdhi = get_bit_range(insn, 16, 19);
|
uint8_t rdhi = get_bit_range(insn, 16, 19);
|
||||||
bool s = get_nth_bit(insn, 20);
|
bool set = get_nth_bit(insn, 20);
|
||||||
bool a = get_nth_bit(insn, 21);
|
bool acc = get_nth_bit(insn, 21);
|
||||||
bool u = get_nth_bit(insn, 22);
|
bool uns = get_nth_bit(insn, 22);
|
||||||
|
|
||||||
if (rdhi == rdlo || rdhi == rm || rdlo == rm)
|
data = MultiplyLong{ .rm = rm,
|
||||||
log_error("rdhi, rdlo and rm are not distinct in {}", syn[u][a]);
|
.rs = rs,
|
||||||
|
.rdlo = rdlo,
|
||||||
|
.rdhi = rdhi,
|
||||||
|
.set = set,
|
||||||
|
.acc = acc,
|
||||||
|
.uns = uns };
|
||||||
|
|
||||||
pc_error(rdhi, syn[u][a]);
|
// Undefined
|
||||||
pc_error(rdlo, syn[u][a]);
|
} else if ((insn & 0x0E000010) == 0x06000010) {
|
||||||
pc_error(rm, syn[u][a]);
|
data = Undefined{};
|
||||||
pc_error(rs, syn[u][a]);
|
|
||||||
|
|
||||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
|
||||||
syn[u][a],
|
|
||||||
cond,
|
|
||||||
(s ? "S" : ""),
|
|
||||||
rdlo,
|
|
||||||
rdhi,
|
|
||||||
rm,
|
|
||||||
rs);
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
|
||||||
if (u) {
|
|
||||||
uint64_t eval = static_cast<uint64_t>(gpr[rm]) *
|
|
||||||
static_cast<uint64_t>(gpr[rs]) +
|
|
||||||
(a ? static_cast<uint64_t>(gpr[rdhi]) << 32 |
|
|
||||||
static_cast<uint64_t>(gpr[rdlo])
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
gpr[rdlo] = get_bit_range(eval, 0, 31);
|
|
||||||
gpr[rdhi] = get_bit_range(eval, 32, 63);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
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])
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
gpr[rdlo] = get_bit_range(eval, 0, 31);
|
|
||||||
gpr[rdhi] = get_bit_range(eval, 32, 63);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s) {
|
|
||||||
cpsr.set_z(!(static_cast<bool>(gpr[rdhi]) ||
|
|
||||||
static_cast<bool>(gpr[rdlo])));
|
|
||||||
cpsr.set_n(get_nth_bit(gpr[rdhi], 31));
|
|
||||||
cpsr.set_c(0);
|
|
||||||
cpsr.set_v(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single data swap
|
// Single data swap
|
||||||
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
||||||
static constexpr stringv syn = "SWP";
|
|
||||||
|
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
bool b = get_nth_bit(insn, 22);
|
bool byte = get_nth_bit(insn, 22);
|
||||||
|
|
||||||
pc_undefined(rm, syn);
|
data = SingleDataSwap{ .rm = rm, .rd = rd, .rn = rn, .byte = byte };
|
||||||
pc_undefined(rn, syn);
|
|
||||||
pc_undefined(rd, syn);
|
|
||||||
|
|
||||||
disassembled = fmt::format(
|
// Single data transfer
|
||||||
"{}{}{} R{:d},R{:d},[R{:d}]", syn, cond, (b ? "B" : ""), rd, rm, rn);
|
} else if ((insn & 0x0C000000) == 0x04000000) {
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
std::variant<uint16_t, Shift> offset;
|
||||||
if (b) {
|
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||||
gpr[rd] = bus->read_byte(gpr[rn]);
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
bus->write_byte(gpr[rn], gpr[rm] & 0xFF);
|
bool load = get_nth_bit(insn, 20);
|
||||||
} else {
|
bool write = get_nth_bit(insn, 21);
|
||||||
gpr[rd] = bus->read_word(gpr[rn]);
|
bool byte = get_nth_bit(insn, 22);
|
||||||
bus->write_word(gpr[rn], gpr[rm]);
|
bool up = get_nth_bit(insn, 23);
|
||||||
}
|
bool pre = get_nth_bit(insn, 24);
|
||||||
|
bool imm = get_nth_bit(insn, 25);
|
||||||
|
|
||||||
|
if (imm) {
|
||||||
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
|
bool reg = get_nth_bit(insn, 4);
|
||||||
|
ShiftType shift_type =
|
||||||
|
static_cast<ShiftType>(get_bit_range(insn, 5, 6));
|
||||||
|
uint8_t operand = get_bit_range(insn, (reg ? 8 : 7), 11);
|
||||||
|
|
||||||
|
offset = Shift{ .rm = rm,
|
||||||
|
.data = ShiftData{ .type = shift_type,
|
||||||
|
.immediate = !reg,
|
||||||
|
.operand = operand } };
|
||||||
|
} else {
|
||||||
|
offset = static_cast<uint16_t>(get_bit_range(insn, 0, 11));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = SingleDataTransfer{ .offset = offset,
|
||||||
|
.rd = rd,
|
||||||
|
.rn = rn,
|
||||||
|
.load = load,
|
||||||
|
.write = write,
|
||||||
|
.byte = byte,
|
||||||
|
.up = up,
|
||||||
|
.pre = pre };
|
||||||
|
|
||||||
// Halfword transfer
|
// Halfword transfer
|
||||||
// TODO: create abstraction to reuse for block data and single data
|
|
||||||
// transfer
|
|
||||||
} else if ((insn & 0x0E000090) == 0x00000090) {
|
} else if ((insn & 0x0E000090) == 0x00000090) {
|
||||||
static constexpr array<stringv, 2> syn_ = { "STR", "LDR" };
|
uint8_t offset = get_bit_range(insn, 0, 3);
|
||||||
|
bool half = get_nth_bit(insn, 5);
|
||||||
|
bool sign = get_nth_bit(insn, 6);
|
||||||
|
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||||
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
|
bool load = get_nth_bit(insn, 20);
|
||||||
|
bool write = get_nth_bit(insn, 21);
|
||||||
|
bool imm = get_nth_bit(insn, 22);
|
||||||
|
bool up = get_nth_bit(insn, 23);
|
||||||
|
bool pre = get_nth_bit(insn, 24);
|
||||||
|
|
||||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
offset |= (imm ? get_bit_range(insn, 8, 11) << 2 : 0);
|
||||||
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 =
|
data = HalfwordTransfer{ .offset = offset,
|
||||||
fmt::format("{}{}{}", syn_[l], (s ? "S" : ""), (h ? 'H' : 'B'));
|
.half = half,
|
||||||
|
.sign = sign,
|
||||||
|
.rd = rd,
|
||||||
|
.rn = rn,
|
||||||
|
.load = load,
|
||||||
|
.write = write,
|
||||||
|
.imm = imm,
|
||||||
|
.up = up,
|
||||||
|
.pre = pre };
|
||||||
|
|
||||||
if (!p && w)
|
// Block data transfer
|
||||||
log_error("Write-back enabled with post-indexing in {}", syn);
|
} else if ((insn & 0x0E000000) == 0x08000000) {
|
||||||
|
/*static constexpr array<stringv, 2> syn = { "STM", "LDM" };
|
||||||
|
|
||||||
if (s && !l)
|
uint16_t regs = get_bit_range(insn, 0, 15);
|
||||||
log_error("Signed data found in {}", syn);
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
|
bool load = get_nth_bit(insn, 20);
|
||||||
if (w)
|
bool write = get_nth_bit(insn, 21);
|
||||||
pc_error(rn, syn);
|
bool s = get_nth_bit(insn, 22);
|
||||||
pc_error(rm, syn);
|
bool up = get_nth_bit(insn, 23);
|
||||||
|
bool pre = get_nth_bit(insn, 24);
|
||||||
|
|
||||||
|
// disassembly
|
||||||
{
|
{
|
||||||
offset = (imm ? get_bit_range(insn, 8, 11) << 4 | rm : gpr[rm]);
|
uint8_t lpu = load << 2 | pre << 1 | up;
|
||||||
std::string offset_str = fmt::format("{}{}{:d}",
|
std::string addr_mode;
|
||||||
(u ? "" : "-"),
|
|
||||||
(imm ? '#' : 'R'),
|
|
||||||
(imm ? offset : rm));
|
|
||||||
|
|
||||||
disassembled = fmt::format(
|
switch(lpu) {
|
||||||
"{}{}{}{} R{:d},{}",
|
|
||||||
syn_[l],
|
|
||||||
cond,
|
|
||||||
(s ? "S" : ""),
|
|
||||||
(h ? 'H' : 'B'),
|
|
||||||
rd,
|
|
||||||
(!offset ? fmt::format("[R{:d}]", rn)
|
|
||||||
: p
|
|
||||||
? fmt::format(",[R{:d},{}]{}", rn, offset_str, (w ? "!" : ""))
|
|
||||||
: fmt::format(",[R{:d}],{}", rn, offset_str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
|
||||||
uint32_t address = gpr[rn];
|
|
||||||
|
|
||||||
if (p)
|
|
||||||
address += (u ? offset : -offset);
|
|
||||||
|
|
||||||
// load
|
|
||||||
if (l) {
|
|
||||||
// signed
|
|
||||||
if (s) {
|
|
||||||
// halfword
|
|
||||||
if (h) {
|
|
||||||
gpr[rd] = bus->read_halfword(address);
|
|
||||||
// sign extend the halfword
|
|
||||||
if (get_nth_bit(gpr[rd], 15))
|
|
||||||
gpr[rd] |= 0xFFFF0000;
|
|
||||||
// byte
|
|
||||||
} else {
|
|
||||||
// sign extend the byte
|
|
||||||
gpr[rd] = bus->read_byte(address);
|
|
||||||
if (get_nth_bit(gpr[rd], 7))
|
|
||||||
gpr[rd] |= 0xFFFFFF00;
|
|
||||||
}
|
|
||||||
// unsigned halfword
|
|
||||||
} else if (h) {
|
|
||||||
gpr[rd] = bus->read_halfword(address);
|
|
||||||
}
|
|
||||||
// store
|
|
||||||
} else {
|
|
||||||
// halfword
|
|
||||||
if (h) {
|
|
||||||
// take PC into consideration
|
|
||||||
if (rd == 15)
|
|
||||||
address += 12;
|
|
||||||
bus->write_halfword(address, gpr[rd]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
if (!p)
|
data = Undefined{};
|
||||||
address += (u ? offset : -offset);
|
|
||||||
|
|
||||||
if (!p || w)
|
|
||||||
gpr[rn] = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Software Interrupt
|
// Software Interrupt
|
||||||
// What to do here?
|
// What to do here?
|
||||||
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
||||||
static constexpr stringv syn = "SWI";
|
|
||||||
|
|
||||||
if (cpsr.condition(cond)) {
|
data = SoftwareInterrupt{};
|
||||||
chg_mode(Mode::Supervisor);
|
|
||||||
pc = 0x08;
|
|
||||||
spsr = cpsr;
|
|
||||||
}
|
|
||||||
|
|
||||||
disassembled = fmt::format("{}{} 0", syn, cond);
|
|
||||||
|
|
||||||
// Coprocessor data transfer
|
// Coprocessor data transfer
|
||||||
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
||||||
static constexpr array<stringv, 2> syn = { "STC", "LDC" };
|
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);
|
||||||
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
|
bool load = get_nth_bit(insn, 20);
|
||||||
|
bool write = get_nth_bit(insn, 21);
|
||||||
|
bool len = get_nth_bit(insn, 22);
|
||||||
|
bool up = get_nth_bit(insn, 23);
|
||||||
|
bool pre = get_nth_bit(insn, 24);
|
||||||
|
|
||||||
[[maybe_unused]] uint8_t offset = get_bit_range(insn, 0, 7);
|
data = CoprocessorDataTransfer{ .offset = offset,
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
.cpn = cpn,
|
||||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
.crd = crd,
|
||||||
[[maybe_unused]] uint8_t rn = get_bit_range(insn, 16, 19);
|
.rn = rn,
|
||||||
bool l = get_nth_bit(insn, 20);
|
.load = load,
|
||||||
[[maybe_unused]] bool w = get_nth_bit(insn, 21);
|
.write = write,
|
||||||
bool n = get_nth_bit(insn, 22);
|
.len = len,
|
||||||
[[maybe_unused]] bool u = get_nth_bit(insn, 23);
|
.up = up,
|
||||||
[[maybe_unused]] bool p = get_nth_bit(insn, 24);
|
.pre = pre };
|
||||||
|
|
||||||
disassembled = fmt::format(
|
|
||||||
"{}{}{} p{},c{},{}", syn[l], cond, (n ? "L" : ""), cpn, crd, "a.");
|
|
||||||
|
|
||||||
log_error("Unimplemented instruction: {}", syn[l]);
|
|
||||||
|
|
||||||
// Coprocessor data operation
|
// Coprocessor data operation
|
||||||
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
||||||
static constexpr stringv syn = "CDP";
|
|
||||||
|
|
||||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||||
@@ -344,42 +183,177 @@ Cpu::exec_arm(const uint32_t insn) {
|
|||||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||||
uint8_t cp_opc = get_bit_range(insn, 20, 23);
|
uint8_t cp_opc = get_bit_range(insn, 20, 23);
|
||||||
|
|
||||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
data = CoprocessorDataOperation{ .crm = crm,
|
||||||
syn,
|
.cp = cp,
|
||||||
cond,
|
.cpn = cpn,
|
||||||
cpn,
|
.crd = crd,
|
||||||
cp_opc,
|
.crn = crn,
|
||||||
crd,
|
.cp_opc = cp_opc };
|
||||||
crn,
|
|
||||||
crm,
|
|
||||||
cp);
|
|
||||||
|
|
||||||
log_error("Unimplemented instruction: {}", syn);
|
|
||||||
|
|
||||||
// Coprocessor register transfer
|
// Coprocessor register transfer
|
||||||
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
||||||
static constexpr array<stringv, 2> syn = { "MCR", "MRC" };
|
|
||||||
|
|
||||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||||
bool l = get_nth_bit(insn, 20);
|
bool load = get_nth_bit(insn, 20);
|
||||||
uint8_t cp_opc = get_bit_range(insn, 21, 23);
|
uint8_t cp_opc = get_bit_range(insn, 21, 23);
|
||||||
|
|
||||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
data = CoprocessorRegisterTransfer{ .crm = crm,
|
||||||
syn[l],
|
.cp = cp,
|
||||||
cond,
|
.cpn = cpn,
|
||||||
cpn,
|
.rd = rd,
|
||||||
cp_opc,
|
.crn = crn,
|
||||||
rd,
|
.load = load,
|
||||||
crn,
|
.cp_opc = cp_opc };
|
||||||
crm,
|
} else {
|
||||||
cp);
|
data = Undefined{};
|
||||||
|
|
||||||
log_error("Unimplemented instruction: {}", syn[l]);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return disassembled;
|
|
||||||
|
std::string
|
||||||
|
ArmInstruction::disassemble() {
|
||||||
|
static const std::string undefined = "UNDEFINED";
|
||||||
|
// 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{}{} {:#08X}", (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 undefined; },
|
||||||
|
[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}", *offset);
|
||||||
|
}
|
||||||
|
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||||
|
expression = fmt::format(",{}R{:d},{} {}{:d}",
|
||||||
|
(data.up ? '+' : '-'),
|
||||||
|
shift->rm,
|
||||||
|
shift->data.type,
|
||||||
|
(shift->data.immediate ? '#' : 'R'),
|
||||||
|
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.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](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{},{},c{},c{},c{},{}",
|
||||||
|
(data.load ? "MRC" : "MCR"),
|
||||||
|
condition,
|
||||||
|
data.cpn,
|
||||||
|
data.cp_opc,
|
||||||
|
data.rd,
|
||||||
|
data.crn,
|
||||||
|
data.crm,
|
||||||
|
data.cp);
|
||||||
|
},
|
||||||
|
[](auto) { return undefined; } },
|
||||||
|
data);
|
||||||
}
|
}
|
||||||
|
132
src/cpu/instruction.hh
Normal file
132
src/cpu/instruction.hh
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include "cpu/utility.hh"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
struct overloaded : Ts... {
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
template<class... Ts>
|
||||||
|
overloaded(Ts...) -> overloaded<Ts...>;
|
||||||
|
|
||||||
|
class ArmInstruction {
|
||||||
|
public:
|
||||||
|
ArmInstruction() = delete;
|
||||||
|
ArmInstruction(uint32_t insn);
|
||||||
|
|
||||||
|
auto get_condition() const { return condition; }
|
||||||
|
auto get_data() const { return data; }
|
||||||
|
|
||||||
|
std::string disassemble();
|
||||||
|
|
||||||
|
struct BranchAndExchange {
|
||||||
|
uint8_t rn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Branch {
|
||||||
|
bool link;
|
||||||
|
uint32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Multiply {
|
||||||
|
uint8_t rm;
|
||||||
|
uint8_t rs;
|
||||||
|
uint8_t rn;
|
||||||
|
uint8_t rd;
|
||||||
|
bool set;
|
||||||
|
bool acc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MultiplyLong {
|
||||||
|
uint8_t rm;
|
||||||
|
uint8_t rs;
|
||||||
|
uint8_t rdlo;
|
||||||
|
uint8_t rdhi;
|
||||||
|
bool set;
|
||||||
|
bool acc;
|
||||||
|
bool uns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SingleDataSwap {
|
||||||
|
uint8_t rm;
|
||||||
|
uint8_t rd;
|
||||||
|
uint8_t rn;
|
||||||
|
bool byte;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SingleDataTransfer {
|
||||||
|
std::variant<uint16_t, Shift> offset;
|
||||||
|
uint8_t rd;
|
||||||
|
uint8_t rn;
|
||||||
|
bool load;
|
||||||
|
bool write;
|
||||||
|
bool byte;
|
||||||
|
bool up;
|
||||||
|
bool pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HalfwordTransfer {
|
||||||
|
uint8_t offset;
|
||||||
|
bool half;
|
||||||
|
bool sign;
|
||||||
|
uint8_t rd;
|
||||||
|
uint8_t rn;
|
||||||
|
bool load;
|
||||||
|
bool write;
|
||||||
|
bool byte;
|
||||||
|
bool imm;
|
||||||
|
bool up;
|
||||||
|
bool pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CoprocessorDataTransfer {
|
||||||
|
uint8_t offset;
|
||||||
|
uint8_t cpn;
|
||||||
|
uint8_t crd;
|
||||||
|
uint8_t rn;
|
||||||
|
bool load;
|
||||||
|
bool write;
|
||||||
|
bool len;
|
||||||
|
bool up;
|
||||||
|
bool pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CoprocessorDataOperation {
|
||||||
|
uint8_t crm;
|
||||||
|
uint8_t cp;
|
||||||
|
uint8_t cpn;
|
||||||
|
uint8_t crd;
|
||||||
|
uint8_t crn;
|
||||||
|
uint8_t cp_opc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CoprocessorRegisterTransfer {
|
||||||
|
uint8_t crm;
|
||||||
|
uint8_t cp;
|
||||||
|
uint8_t cpn;
|
||||||
|
uint8_t rd;
|
||||||
|
uint8_t crn;
|
||||||
|
bool load;
|
||||||
|
uint8_t cp_opc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Undefined {};
|
||||||
|
struct SoftwareInterrupt {};
|
||||||
|
|
||||||
|
using InstructionData = std::variant<BranchAndExchange,
|
||||||
|
Branch,
|
||||||
|
Multiply,
|
||||||
|
MultiplyLong,
|
||||||
|
SingleDataSwap,
|
||||||
|
SingleDataTransfer,
|
||||||
|
HalfwordTransfer,
|
||||||
|
CoprocessorDataTransfer,
|
||||||
|
CoprocessorDataOperation,
|
||||||
|
CoprocessorRegisterTransfer,
|
||||||
|
Undefined,
|
||||||
|
SoftwareInterrupt>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Condition condition;
|
||||||
|
InstructionData data;
|
||||||
|
};
|
@@ -1,4 +1,6 @@
|
|||||||
#include "utility.hh"
|
#include "utility.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os, const Condition cond) {
|
operator<<(std::ostream& os, const Condition cond) {
|
||||||
@@ -33,6 +35,47 @@ operator<<(std::ostream& os, const Condition cond) {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
||||||
|
switch (shift_type) {
|
||||||
|
case ShiftType::LSL:
|
||||||
|
|
||||||
|
if (amount > 0 && amount <= 32)
|
||||||
|
carry = get_nth_bit(value, 32 - amount);
|
||||||
|
else if (amount > 32)
|
||||||
|
carry = 0;
|
||||||
|
|
||||||
|
return value << amount;
|
||||||
|
case ShiftType::LSR:
|
||||||
|
|
||||||
|
if (amount > 0 && amount <= 32)
|
||||||
|
carry = get_nth_bit(value, amount - 1);
|
||||||
|
else if (amount > 32)
|
||||||
|
carry = 0;
|
||||||
|
else
|
||||||
|
carry = get_nth_bit(value, 31);
|
||||||
|
|
||||||
|
return value >> amount;
|
||||||
|
case ShiftType::ASR:
|
||||||
|
if (amount > 0 && amount <= 32)
|
||||||
|
carry = get_nth_bit(value, amount - 1);
|
||||||
|
else
|
||||||
|
carry = get_nth_bit(value, 31);
|
||||||
|
|
||||||
|
return static_cast<int32_t>(value) >> amount;
|
||||||
|
case ShiftType::ROR:
|
||||||
|
if (amount == 0) {
|
||||||
|
bool old_carry = carry;
|
||||||
|
|
||||||
|
carry = get_nth_bit(value, 0);
|
||||||
|
return (value >> 1) | (old_carry << 31);
|
||||||
|
} else {
|
||||||
|
carry = get_nth_bit(value, (amount % 32 + 31) % 32);
|
||||||
|
return std::rotr(value, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os, const ShiftType shift_type) {
|
operator<<(std::ostream& os, const ShiftType shift_type) {
|
||||||
|
|
||||||
|
@@ -72,6 +72,20 @@ enum class ShiftType {
|
|||||||
ROR = 0b11
|
ROR = 0b11
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ShiftData {
|
||||||
|
ShiftType type;
|
||||||
|
bool immediate;
|
||||||
|
uint8_t operand;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Shift {
|
||||||
|
uint8_t rm;
|
||||||
|
ShiftData data;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry);
|
||||||
|
|
||||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os, const ShiftType cond);
|
operator<<(std::ostream& os, const ShiftType cond);
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef struct {
|
struct Header {
|
||||||
enum class UniqueCode {
|
enum class UniqueCode {
|
||||||
Old, // old games
|
Old, // old games
|
||||||
New, // new games
|
New, // new games
|
||||||
@@ -42,4 +42,4 @@ typedef struct {
|
|||||||
BootMode multiboot;
|
BootMode multiboot;
|
||||||
uint32_t multiboot_entrypoint;
|
uint32_t multiboot_entrypoint;
|
||||||
uint8_t slave_id;
|
uint8_t slave_id;
|
||||||
} Header;
|
};
|
||||||
|
Reference in New Issue
Block a user