add a basic structure for disassembler + executor
Instructions added Branch and Exchange (BX) Branch and Link (B) Multiply and Accumulate (MUL, MLA) Multiply Long and Accumulate (SMULL, SMLAL, UMULL, UMLAL) Single data swap (SWP) [WIP] Halfword Transfer (STRH, LDRH) Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
0
.gitmodules
vendored
0
.gitmodules
vendored
@@ -1,12 +1,18 @@
|
|||||||
|
#include "bus.hh"
|
||||||
|
#include "cpu/cpu.hh"
|
||||||
#include "memory.hh"
|
#include "memory.hh"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char* argv[]) {
|
main(int argc, const char* argv[]) {
|
||||||
|
std::vector<uint8_t> rom;
|
||||||
|
std::array<uint8_t, Memory::BIOS_SIZE> bios;
|
||||||
|
|
||||||
auto usage = [argv]() {
|
auto usage = [argv]() {
|
||||||
std::cerr << "Usage: " << argv[0] << " <file> [-b <bios>]" << std::endl;
|
std::cerr << "Usage: " << argv[0] << " <file> [-b <bios>]" << std::endl;
|
||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
@@ -35,8 +41,6 @@ main(int argc, const char* argv[]) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
std::ifstream ifile(rom_file, std::ios::in | std::ios::binary);
|
std::ifstream ifile(rom_file, std::ios::in | std::ios::binary);
|
||||||
std::vector<uint8_t> rom;
|
|
||||||
std::array<uint8_t, Memory::BIOS_SIZE> bios;
|
|
||||||
std::streampos bios_size;
|
std::streampos bios_size;
|
||||||
|
|
||||||
if (!ifile.is_open()) {
|
if (!ifile.is_open()) {
|
||||||
@@ -68,11 +72,16 @@ main(int argc, const char* argv[]) {
|
|||||||
|
|
||||||
ifile.close();
|
ifile.close();
|
||||||
|
|
||||||
Memory memory(std::move(bios), std::move(rom));
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
std::cerr << "Exception: " << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Memory memory(std::move(bios), std::move(rom));
|
||||||
|
Bus bus(std::make_shared<Memory>(memory));
|
||||||
|
Cpu cpu(std::make_shared<Bus>(bus));
|
||||||
|
cpu.step();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
1
include/bus.hh
Symbolic link
1
include/bus.hh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../src/bus.hh
|
1
include/cpu/cpu.hh
Symbolic link
1
include/cpu/cpu.hh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/cpu/cpu.hh
|
5
include/cpu/meson.build
Normal file
5
include/cpu/meson.build
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
headers += files(
|
||||||
|
'cpu.hh',
|
||||||
|
'psr.hh',
|
||||||
|
'utility.hh'
|
||||||
|
)
|
1
include/cpu/psr.hh
Symbolic link
1
include/cpu/psr.hh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/cpu/psr.hh
|
@@ -1,3 +1,27 @@
|
|||||||
|
#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 {
|
enum class Condition {
|
||||||
EQ = 0b0000,
|
EQ = 0b0000,
|
||||||
NE = 0b0001,
|
NE = 0b0001,
|
||||||
@@ -41,3 +65,9 @@ enum class ShiftType {
|
|||||||
ASR = 0b10,
|
ASR = 0b10,
|
||||||
ROR = 0b11
|
ROR = 0b11
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 {};
|
@@ -1,6 +1,9 @@
|
|||||||
headers = files(
|
headers = files(
|
||||||
'memory.hh',
|
'memory.hh',
|
||||||
'header.hh'
|
'bus.hh',
|
||||||
|
'header.hh',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
subdir('cpu')
|
||||||
|
|
||||||
install_headers(headers, subdir: meson.project_name(), preserve_path: true)
|
install_headers(headers, subdir: meson.project_name(), preserve_path: true)
|
35
src/bus.cc
35
src/bus.cc
@@ -1,4 +1,35 @@
|
|||||||
#include "bus.hh"
|
#include "bus.hh"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
Bus::Bus(Memory&& memory)
|
Bus::Bus(std::shared_ptr<Memory> memory)
|
||||||
: memory(std::move(memory)) {}
|
: memory(memory) {}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
Bus::read_byte(size_t address) {
|
||||||
|
return memory->read(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Bus::write_byte(size_t address, uint8_t byte) {
|
||||||
|
memory->write(address, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
Bus::read_halfword(size_t address) {
|
||||||
|
return memory->read_halfword(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Bus::write_halfword(size_t address, uint16_t halfword) {
|
||||||
|
memory->write_halfword(address, halfword);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
Bus::read_word(size_t address) {
|
||||||
|
return memory->read_word(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Bus::write_word(size_t address, uint32_t word) {
|
||||||
|
memory->write_halfword(address, word);
|
||||||
|
}
|
||||||
|
14
src/bus.hh
14
src/bus.hh
@@ -1,11 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "memory.hh"
|
#include "memory.hh"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class Bus {
|
class Bus {
|
||||||
public:
|
public:
|
||||||
Bus(Memory&& memory);
|
Bus(std::shared_ptr<Memory> memory);
|
||||||
|
|
||||||
|
uint8_t read_byte(size_t address);
|
||||||
|
void write_byte(size_t address, uint8_t byte);
|
||||||
|
|
||||||
|
uint16_t read_halfword(size_t address);
|
||||||
|
void write_halfword(size_t address, uint16_t halfword);
|
||||||
|
|
||||||
|
uint32_t read_word(size_t address);
|
||||||
|
void write_word(size_t address, uint32_t word);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Memory memory;
|
std::shared_ptr<Memory> memory;
|
||||||
};
|
};
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
lib_sources += files(
|
|
||||||
)
|
|
@@ -1,9 +1,12 @@
|
|||||||
#include "cpu.hh"
|
#include "cpu.hh"
|
||||||
#include "cpu/utility.hh"
|
#include "util/log.hh"
|
||||||
|
#include "utility.hh"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
Cpu::Cpu(Bus bus)
|
using namespace logger;
|
||||||
|
|
||||||
|
Cpu::Cpu(std::shared_ptr<Bus> bus)
|
||||||
: gpr(0)
|
: gpr(0)
|
||||||
, cpsr(0)
|
, cpsr(0)
|
||||||
, spsr(0)
|
, spsr(0)
|
||||||
@@ -14,6 +17,7 @@ Cpu::Cpu(Bus bus)
|
|||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* change modes */
|
/* change modes */
|
||||||
@@ -103,15 +107,13 @@ Cpu::chg_mode(Mode from, Mode to) {
|
|||||||
cpsr.set_mode(to);
|
cpsr.set_mode(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set register
|
void
|
||||||
inline uint32_t&
|
Cpu::step() {
|
||||||
Cpu::operator[](uint8_t idx) {
|
uint32_t insn = 0xffffffff;
|
||||||
// avoid unneeded complexity like index checks
|
|
||||||
return gpr[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
// get register
|
if (cpsr.state() == State::Arm) {
|
||||||
inline const uint32_t&
|
std::string disassembled = exec_arm(insn);
|
||||||
Cpu::operator[](uint8_t idx) const {
|
log_info("{:#010X} : {}", gpr[15], disassembled);
|
||||||
return gpr[idx];
|
gpr[15] += ARM_INSTRUCTION_SIZE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,8 @@ using std::size_t;
|
|||||||
|
|
||||||
class Cpu {
|
class Cpu {
|
||||||
public:
|
public:
|
||||||
Cpu(Bus bus);
|
Cpu(std::shared_ptr<Bus> bus);
|
||||||
|
void step();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
|
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
|
||||||
@@ -35,7 +36,7 @@ class Cpu {
|
|||||||
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
|
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
|
||||||
Psr cpsr; // current program status register
|
Psr cpsr; // current program status register
|
||||||
Psr spsr; // status program status register
|
Psr spsr; // status program status register
|
||||||
Bus bus;
|
std::shared_ptr<Bus> bus;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint32_t fiq[GPR_FIQ_BANKED_COUNT];
|
uint32_t fiq[GPR_FIQ_BANKED_COUNT];
|
||||||
@@ -57,7 +58,5 @@ class Cpu {
|
|||||||
} spsr_banked; // banked saved program status registers
|
} spsr_banked; // banked saved program status registers
|
||||||
|
|
||||||
void chg_mode(Mode from, Mode to);
|
void chg_mode(Mode from, Mode to);
|
||||||
|
std::string exec_arm(uint32_t insn);
|
||||||
uint32_t& operator[](uint8_t idx);
|
|
||||||
const uint32_t& operator[](uint8_t idx) const;
|
|
||||||
};
|
};
|
||||||
|
294
src/cpu/instruction.cc
Normal file
294
src/cpu/instruction.cc
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
#include "cpu.hh"
|
||||||
|
#include "cpu/utility.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
|
#include "util/log.hh"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using namespace logger;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Cpu::exec_arm(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) {
|
||||||
|
if (r == 15)
|
||||||
|
log_error("Using PC (R15) as operand in {}", syn);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pc_undefined = [](uint8_t r, const char* 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";
|
||||||
|
|
||||||
|
uint8_t rn = insn & 0b1111;
|
||||||
|
|
||||||
|
pc_undefined(rn, syn);
|
||||||
|
|
||||||
|
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
|
||||||
|
gpr[15] = gpr[rn];
|
||||||
|
|
||||||
|
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||||
|
rst_nth_bit(gpr[15], 0);
|
||||||
|
if (state == State::Arm)
|
||||||
|
rst_nth_bit(gpr[15], 1);
|
||||||
|
}
|
||||||
|
// Branch
|
||||||
|
} else if ((insn & 0x0e000000) == 0x0a000000) {
|
||||||
|
static constexpr char syn[] = "B";
|
||||||
|
|
||||||
|
bool link = get_nth_bit(insn, 24);
|
||||||
|
uint32_t offset = get_bit_range(insn, 0, 23);
|
||||||
|
|
||||||
|
disassembled =
|
||||||
|
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] = gpr[15] - ARM_INSTRUCTION_SIZE;
|
||||||
|
|
||||||
|
gpr[15] += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply
|
||||||
|
} else if ((insn & 0x0fc000f0) == 0x00000090) {
|
||||||
|
static constexpr char syn[2][4] = { "MUL", "MLA" };
|
||||||
|
|
||||||
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
|
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||||
|
uint8_t rn = get_bit_range(insn, 12, 15);
|
||||||
|
uint8_t rd = get_bit_range(insn, 16, 19);
|
||||||
|
bool s = get_nth_bit(insn, 20);
|
||||||
|
bool a = get_nth_bit(insn, 21);
|
||||||
|
|
||||||
|
if (rd == rm)
|
||||||
|
log_error("rd and rm are not distinct in {} : {:d}", syn[a], rd);
|
||||||
|
|
||||||
|
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 char syn[2][2][6] = { { "SMULL", "SMLAL" },
|
||||||
|
{ "UMULL", "UMLAL" } };
|
||||||
|
|
||||||
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
|
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||||
|
uint8_t rdlo = get_bit_range(insn, 12, 15);
|
||||||
|
uint8_t rdhi = get_bit_range(insn, 16, 19);
|
||||||
|
bool s = get_nth_bit(insn, 20);
|
||||||
|
bool a = get_nth_bit(insn, 21);
|
||||||
|
bool u = get_nth_bit(insn, 22);
|
||||||
|
|
||||||
|
if (rdhi == rdlo || rdhi == rm || rdlo == rm)
|
||||||
|
log_error("rdhi, rdlo and rm are not distinct in {}", syn[u][a]);
|
||||||
|
|
||||||
|
pc_error(rdhi, syn[u][a]);
|
||||||
|
pc_error(rdlo, syn[u][a]);
|
||||||
|
pc_error(rm, syn[u][a]);
|
||||||
|
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<uint64_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
|
||||||
|
} else if ((insn & 0x0fb00ff0) == 0x01000090) {
|
||||||
|
static constexpr char syn[] = "SWP";
|
||||||
|
|
||||||
|
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||||
|
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||||
|
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||||
|
bool b = get_nth_bit(insn, 22);
|
||||||
|
|
||||||
|
pc_undefined(rm, syn);
|
||||||
|
pc_undefined(rn, syn);
|
||||||
|
pc_undefined(rd, syn);
|
||||||
|
|
||||||
|
disassembled = fmt::format(
|
||||||
|
"{}{}{} R{:d},R{:d},[R{:d}]", syn, cond, (b ? "B" : ""), rd, rm, rn);
|
||||||
|
|
||||||
|
if (cpsr.condition(cond)) {
|
||||||
|
if (b) {
|
||||||
|
gpr[rd] = bus->read_byte(gpr[rn]);
|
||||||
|
bus->write_byte(gpr[rn], gpr[rm] & 0xFF);
|
||||||
|
} else {
|
||||||
|
gpr[rd] = bus->read_word(gpr[rn]);
|
||||||
|
bus->write_word(gpr[rn], gpr[rm]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halfword transfer
|
||||||
|
// 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" };
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!p && w)
|
||||||
|
log_error("Write-back enabled with post-indexing in {}", syn[l]);
|
||||||
|
|
||||||
|
if (s && !l)
|
||||||
|
log_error("Signed data found in {}", syn[l]);
|
||||||
|
|
||||||
|
if (w)
|
||||||
|
pc_error(rn, syn[l]);
|
||||||
|
pc_error(rm, syn[l]);
|
||||||
|
|
||||||
|
if (rd == 15 && !l && s && h)
|
||||||
|
;
|
||||||
|
|
||||||
|
{
|
||||||
|
offset = (imm ? get_bit_range(insn, 8, 11) << 4 | rm : gpr[rm]);
|
||||||
|
std::string offset_str = fmt::format("{}{}{:d}",
|
||||||
|
(u ? "" : "-"),
|
||||||
|
(imm ? '#' : 'R'),
|
||||||
|
(imm ? offset : rm));
|
||||||
|
|
||||||
|
disassembled = fmt::format(
|
||||||
|
"{}{}{}{} 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 = (u ? gpr[rn] + offset : gpr[rn] - 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disassembled;
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
lib_sources += files(
|
lib_sources += files(
|
||||||
'cpu.cc',
|
'cpu.cc',
|
||||||
'psr.cc'
|
'instruction.cc',
|
||||||
|
'psr.cc',
|
||||||
|
'utility.cc'
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('arm')
|
|
@@ -1,5 +1,6 @@
|
|||||||
#include "psr.hh"
|
#include "psr.hh"
|
||||||
#include "util/bits.hh"
|
#include "util/bits.hh"
|
||||||
|
#include "util/log.hh"
|
||||||
|
|
||||||
Psr::Psr(uint32_t raw) {
|
Psr::Psr(uint32_t raw) {
|
||||||
psr = raw & PSR_CLEAR_RESERVED;
|
psr = raw & PSR_CLEAR_RESERVED;
|
||||||
@@ -16,9 +17,9 @@ Psr::set_mode(Mode mode) {
|
|||||||
psr |= static_cast<uint32_t>(mode);
|
psr |= static_cast<uint32_t>(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
State
|
||||||
Psr::state() const {
|
Psr::state() const {
|
||||||
return get_nth_bit(psr, 5);
|
return static_cast<State>(get_nth_bit(psr, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -47,3 +48,39 @@ GET_SET_NTH_BIT_FUNCTIONS(z, 30);
|
|||||||
GET_SET_NTH_BIT_FUNCTIONS(n, 31);
|
GET_SET_NTH_BIT_FUNCTIONS(n, 31);
|
||||||
|
|
||||||
#undef GET_SET_NTH_BIT_FUNCTIONS
|
#undef GET_SET_NTH_BIT_FUNCTIONS
|
||||||
|
|
||||||
|
bool
|
||||||
|
Psr::condition(Condition cond) const {
|
||||||
|
switch (cond) {
|
||||||
|
case Condition::EQ:
|
||||||
|
return z();
|
||||||
|
case Condition::NE:
|
||||||
|
return !z();
|
||||||
|
case Condition::CS:
|
||||||
|
return c();
|
||||||
|
case Condition::CC:
|
||||||
|
return !c();
|
||||||
|
case Condition::MI:
|
||||||
|
return n();
|
||||||
|
case Condition::PL:
|
||||||
|
return !n();
|
||||||
|
case Condition::VS:
|
||||||
|
return v();
|
||||||
|
case Condition::VC:
|
||||||
|
return !v();
|
||||||
|
case Condition::HI:
|
||||||
|
return c() && !z();
|
||||||
|
case Condition::LS:
|
||||||
|
return !c() || z();
|
||||||
|
case Condition::GE:
|
||||||
|
return n() == v();
|
||||||
|
case Condition::LT:
|
||||||
|
return n() != v();
|
||||||
|
case Condition::GT:
|
||||||
|
return !z() && (n() == v());
|
||||||
|
case Condition::LE:
|
||||||
|
return z() || (n() != v());
|
||||||
|
case Condition::AL:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/bits.hh"
|
|
||||||
#include "utility.hh"
|
#include "utility.hh"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ class Psr {
|
|||||||
void set_mode(Mode mode);
|
void set_mode(Mode mode);
|
||||||
|
|
||||||
// State : [5]
|
// State : [5]
|
||||||
bool state() const;
|
State state() const;
|
||||||
void set_state(State state);
|
void set_state(State state);
|
||||||
|
|
||||||
#define GET_SET_NTH_BIT_FUNCTIONS(name) \
|
#define GET_SET_NTH_BIT_FUNCTIONS(name) \
|
||||||
@@ -43,6 +42,8 @@ class Psr {
|
|||||||
|
|
||||||
#undef GET_SET_NTH_BIT_FUNCTIONS
|
#undef GET_SET_NTH_BIT_FUNCTIONS
|
||||||
|
|
||||||
|
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 = 0x0b00000;
|
||||||
|
34
src/cpu/utility.cc
Normal file
34
src/cpu/utility.cc
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "utility.hh"
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, const Condition cond) {
|
||||||
|
|
||||||
|
#define CASE(cond) \
|
||||||
|
case Condition::cond: \
|
||||||
|
os << #cond; \
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (cond) {
|
||||||
|
CASE(EQ)
|
||||||
|
CASE(NE)
|
||||||
|
CASE(CS)
|
||||||
|
CASE(CC)
|
||||||
|
CASE(MI)
|
||||||
|
CASE(PL)
|
||||||
|
CASE(VS)
|
||||||
|
CASE(VC)
|
||||||
|
CASE(HI)
|
||||||
|
CASE(LS)
|
||||||
|
CASE(GE)
|
||||||
|
CASE(LT)
|
||||||
|
CASE(GT)
|
||||||
|
CASE(LE)
|
||||||
|
case Condition::AL: {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CASE
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
@@ -1,5 +1,11 @@
|
|||||||
#pragma once
|
#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 {
|
enum class Mode {
|
||||||
/* M[4:0] in PSR */
|
/* M[4:0] in PSR */
|
||||||
User = 0b10000,
|
User = 0b10000,
|
||||||
@@ -15,3 +21,53 @@ enum class State {
|
|||||||
Arm = 0,
|
Arm = 0,
|
||||||
Thumb = 1
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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 Condition cond);
|
||||||
|
template<>
|
||||||
|
struct fmt::formatter<Condition> : ostream_formatter {};
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
#include "memory.hh"
|
#include "memory.hh"
|
||||||
#include "header.hh"
|
#include "header.hh"
|
||||||
|
#include "util/bits.hh"
|
||||||
#include "util/log.hh"
|
#include "util/log.hh"
|
||||||
#include "util/utils.hh"
|
#include "util/utils.hh"
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
using namespace logger;
|
||||||
|
|
||||||
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
||||||
std::vector<uint8_t>&& rom) noexcept
|
std::vector<uint8_t>&& rom) noexcept
|
||||||
@@ -13,20 +17,21 @@ Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
|||||||
, oam_obj_attr(0)
|
, oam_obj_attr(0)
|
||||||
, rom(std::move(rom)) {
|
, rom(std::move(rom)) {
|
||||||
std::string bios_hash = crypto::sha256(bios.data(), bios.size());
|
std::string bios_hash = crypto::sha256(bios.data(), bios.size());
|
||||||
std::string expected_hash =
|
static constexpr char expected_hash[] =
|
||||||
"fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
|
"fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
|
||||||
|
|
||||||
if (bios_hash != expected_hash) {
|
if (bios_hash != expected_hash) {
|
||||||
log_warn("BIOS hash failed to match, run at your own risk",
|
log_warn("BIOS hash failed to match, run at your own risk"
|
||||||
"\nExpected : ",
|
"\nExpected : {} "
|
||||||
|
"\nGot : {}",
|
||||||
expected_hash,
|
expected_hash,
|
||||||
"\nGot : ",
|
|
||||||
bios_hash);
|
bios_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_header();
|
parse_header();
|
||||||
|
|
||||||
log("Memory successfully initialised");
|
log_info("Memory successfully initialised");
|
||||||
|
log_info("Cartridge Title: {}", header.title);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MATCHES(area) address >= area##_START&& address <= area##_END
|
#define MATCHES(area) address >= area##_START&& address <= area##_END
|
||||||
@@ -164,7 +169,7 @@ Memory::parse_header() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_error("HEADER: invalid unique code: ", rom[0xAC]);
|
log_error("HEADER: invalid unique code: {}", rom[0xAC]);
|
||||||
}
|
}
|
||||||
|
|
||||||
header.title_code[0] = rom[0xAD];
|
header.title_code[0] = rom[0xAD];
|
||||||
@@ -194,7 +199,7 @@ Memory::parse_header() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_error("HEADER: invalid destination/language - ", rom[0xAF]);
|
log_error("HEADER: invalid destination/language: {}", rom[0xAF]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rom[0xB2] != 0x96)
|
if (rom[0xB2] != 0x96)
|
||||||
|
@@ -6,10 +6,13 @@ lib_sources = files(
|
|||||||
subdir('util')
|
subdir('util')
|
||||||
subdir('cpu')
|
subdir('cpu')
|
||||||
|
|
||||||
|
fmt = dependency('fmt', version : '>=10.1.0')
|
||||||
lib = library(
|
lib = library(
|
||||||
meson.project_name(),
|
meson.project_name(),
|
||||||
lib_sources,
|
lib_sources,
|
||||||
install: true
|
dependencies: [fmt],
|
||||||
|
install: true,
|
||||||
|
cpp_args: '-DFMT_HEADER_ONLY'
|
||||||
)
|
)
|
||||||
|
|
||||||
import('pkgconfig').generate(lib)
|
import('pkgconfig').generate(lib)
|
@@ -8,7 +8,7 @@ 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_nth_bit(Int num, size_t n) {
|
||||||
return (1 && (num >> n));
|
return (num >> n) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
@@ -26,14 +26,15 @@ rst_nth_bit(Int& num, size_t n) {
|
|||||||
template<std::integral Int>
|
template<std::integral Int>
|
||||||
inline void
|
inline void
|
||||||
chg_nth_bit(Int& num, size_t n, bool x) {
|
chg_nth_bit(Int& num, size_t n, bool x) {
|
||||||
num ^= (num ^ -x) & 1 << 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::unsigned_integral Int>
|
template<std::integral Int>
|
||||||
inline Int
|
inline Int
|
||||||
get_bit_range(Int& num, size_t start, size_t end) {
|
get_bit_range(Int num, size_t start, size_t end) {
|
||||||
// NOTE: we do not require -1 if it is a signed integral (which it is not)
|
// NOTE: we do not require -1 if it is a signed integral
|
||||||
Int left = std::numeric_limits<Int>::digits - 1 - end;
|
Int left =
|
||||||
|
std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end;
|
||||||
return num << left >> (left + start);
|
return num << left >> (left + start);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user