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 <array>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
int
|
||||
main(int argc, const char* argv[]) {
|
||||
std::vector<uint8_t> rom;
|
||||
std::array<uint8_t, Memory::BIOS_SIZE> bios;
|
||||
|
||||
auto usage = [argv]() {
|
||||
std::cerr << "Usage: " << argv[0] << " <file> [-b <bios>]" << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
@@ -35,8 +41,6 @@ main(int argc, const char* argv[]) {
|
||||
|
||||
try {
|
||||
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;
|
||||
|
||||
if (!ifile.is_open()) {
|
||||
@@ -68,11 +72,16 @@ main(int argc, const char* argv[]) {
|
||||
|
||||
ifile.close();
|
||||
|
||||
Memory memory(std::move(bios), std::move(rom));
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
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;
|
||||
}
|
||||
|
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 {
|
||||
EQ = 0b0000,
|
||||
NE = 0b0001,
|
||||
@@ -41,3 +65,9 @@ enum class ShiftType {
|
||||
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,6 +1,9 @@
|
||||
headers = files(
|
||||
'memory.hh',
|
||||
'header.hh'
|
||||
'bus.hh',
|
||||
'header.hh',
|
||||
)
|
||||
|
||||
subdir('cpu')
|
||||
|
||||
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 <memory>
|
||||
|
||||
Bus::Bus(Memory&& memory)
|
||||
: memory(std::move(memory)) {}
|
||||
Bus::Bus(std::shared_ptr<Memory> 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
|
||||
|
||||
#include "memory.hh"
|
||||
#include <memory>
|
||||
|
||||
class Bus {
|
||||
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:
|
||||
Memory memory;
|
||||
std::shared_ptr<Memory> memory;
|
||||
};
|
||||
|
@@ -1,2 +0,0 @@
|
||||
lib_sources += files(
|
||||
)
|
@@ -1,9 +1,12 @@
|
||||
#include "cpu.hh"
|
||||
#include "cpu/utility.hh"
|
||||
#include "util/log.hh"
|
||||
#include "utility.hh"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
Cpu::Cpu(Bus bus)
|
||||
using namespace logger;
|
||||
|
||||
Cpu::Cpu(std::shared_ptr<Bus> bus)
|
||||
: gpr(0)
|
||||
, cpsr(0)
|
||||
, spsr(0)
|
||||
@@ -14,6 +17,7 @@ Cpu::Cpu(Bus bus)
|
||||
cpsr.set_irq_disabled(true);
|
||||
cpsr.set_fiq_disabled(true);
|
||||
cpsr.set_state(State::Arm);
|
||||
log_info("CPU successfully initialised");
|
||||
}
|
||||
|
||||
/* change modes */
|
||||
@@ -103,15 +107,13 @@ Cpu::chg_mode(Mode from, Mode to) {
|
||||
cpsr.set_mode(to);
|
||||
}
|
||||
|
||||
// set register
|
||||
inline uint32_t&
|
||||
Cpu::operator[](uint8_t idx) {
|
||||
// avoid unneeded complexity like index checks
|
||||
return gpr[idx];
|
||||
}
|
||||
void
|
||||
Cpu::step() {
|
||||
uint32_t insn = 0xffffffff;
|
||||
|
||||
// get register
|
||||
inline const uint32_t&
|
||||
Cpu::operator[](uint8_t idx) const {
|
||||
return gpr[idx];
|
||||
if (cpsr.state() == State::Arm) {
|
||||
std::string disassembled = exec_arm(insn);
|
||||
log_info("{:#010X} : {}", gpr[15], disassembled);
|
||||
gpr[15] += ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,8 @@ using std::size_t;
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(Bus bus);
|
||||
Cpu(std::shared_ptr<Bus> bus);
|
||||
void step();
|
||||
|
||||
private:
|
||||
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
|
||||
@@ -35,7 +36,7 @@ class Cpu {
|
||||
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
|
||||
Psr cpsr; // current program status register
|
||||
Psr spsr; // status program status register
|
||||
Bus bus;
|
||||
std::shared_ptr<Bus> bus;
|
||||
|
||||
struct {
|
||||
uint32_t fiq[GPR_FIQ_BANKED_COUNT];
|
||||
@@ -57,7 +58,5 @@ class Cpu {
|
||||
} spsr_banked; // banked saved program status registers
|
||||
|
||||
void chg_mode(Mode from, Mode to);
|
||||
|
||||
uint32_t& operator[](uint8_t idx);
|
||||
const uint32_t& operator[](uint8_t idx) const;
|
||||
std::string exec_arm(uint32_t insn);
|
||||
};
|
||||
|
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(
|
||||
'cpu.cc',
|
||||
'psr.cc'
|
||||
'instruction.cc',
|
||||
'psr.cc',
|
||||
'utility.cc'
|
||||
)
|
||||
|
||||
subdir('arm')
|
@@ -1,5 +1,6 @@
|
||||
#include "psr.hh"
|
||||
#include "util/bits.hh"
|
||||
#include "util/log.hh"
|
||||
|
||||
Psr::Psr(uint32_t raw) {
|
||||
psr = raw & PSR_CLEAR_RESERVED;
|
||||
@@ -16,9 +17,9 @@ Psr::set_mode(Mode mode) {
|
||||
psr |= static_cast<uint32_t>(mode);
|
||||
}
|
||||
|
||||
bool
|
||||
State
|
||||
Psr::state() const {
|
||||
return get_nth_bit(psr, 5);
|
||||
return static_cast<State>(get_nth_bit(psr, 5));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -47,3 +48,39 @@ GET_SET_NTH_BIT_FUNCTIONS(z, 30);
|
||||
GET_SET_NTH_BIT_FUNCTIONS(n, 31);
|
||||
|
||||
#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
|
||||
|
||||
#include "util/bits.hh"
|
||||
#include "utility.hh"
|
||||
#include <cstdint>
|
||||
|
||||
@@ -14,7 +13,7 @@ class Psr {
|
||||
void set_mode(Mode mode);
|
||||
|
||||
// State : [5]
|
||||
bool state() const;
|
||||
State state() const;
|
||||
void set_state(State state);
|
||||
|
||||
#define GET_SET_NTH_BIT_FUNCTIONS(name) \
|
||||
@@ -43,6 +42,8 @@ class Psr {
|
||||
|
||||
#undef GET_SET_NTH_BIT_FUNCTIONS
|
||||
|
||||
bool condition(Condition cond) const;
|
||||
|
||||
private:
|
||||
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
|
||||
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
|
||||
|
||||
#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,
|
||||
@@ -15,3 +21,53 @@ 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
|
||||
};
|
||||
|
||||
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 "header.hh"
|
||||
#include "util/bits.hh"
|
||||
#include "util/log.hh"
|
||||
#include "util/utils.hh"
|
||||
#include <bitset>
|
||||
|
||||
using namespace logger;
|
||||
|
||||
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
||||
std::vector<uint8_t>&& rom) noexcept
|
||||
@@ -13,20 +17,21 @@ Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
||||
, oam_obj_attr(0)
|
||||
, rom(std::move(rom)) {
|
||||
std::string bios_hash = crypto::sha256(bios.data(), bios.size());
|
||||
std::string expected_hash =
|
||||
static constexpr char expected_hash[] =
|
||||
"fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
|
||||
|
||||
if (bios_hash != expected_hash) {
|
||||
log_warn("BIOS hash failed to match, run at your own risk",
|
||||
"\nExpected : ",
|
||||
log_warn("BIOS hash failed to match, run at your own risk"
|
||||
"\nExpected : {} "
|
||||
"\nGot : {}",
|
||||
expected_hash,
|
||||
"\nGot : ",
|
||||
bios_hash);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -164,7 +169,7 @@ Memory::parse_header() {
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("HEADER: invalid unique code: ", rom[0xAC]);
|
||||
log_error("HEADER: invalid unique code: {}", rom[0xAC]);
|
||||
}
|
||||
|
||||
header.title_code[0] = rom[0xAD];
|
||||
@@ -194,7 +199,7 @@ Memory::parse_header() {
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("HEADER: invalid destination/language - ", rom[0xAF]);
|
||||
log_error("HEADER: invalid destination/language: {}", rom[0xAF]);
|
||||
}
|
||||
|
||||
if (rom[0xB2] != 0x96)
|
||||
|
@@ -6,10 +6,13 @@ lib_sources = files(
|
||||
subdir('util')
|
||||
subdir('cpu')
|
||||
|
||||
fmt = dependency('fmt', version : '>=10.1.0')
|
||||
lib = library(
|
||||
meson.project_name(),
|
||||
lib_sources,
|
||||
install: true
|
||||
dependencies: [fmt],
|
||||
install: true,
|
||||
cpp_args: '-DFMT_HEADER_ONLY'
|
||||
)
|
||||
|
||||
import('pkgconfig').generate(lib)
|
@@ -8,7 +8,7 @@ using std::size_t;
|
||||
template<std::integral Int>
|
||||
inline bool
|
||||
get_nth_bit(Int num, size_t n) {
|
||||
return (1 && (num >> n));
|
||||
return (num >> n) & 1;
|
||||
}
|
||||
|
||||
template<std::integral Int>
|
||||
@@ -26,14 +26,15 @@ rst_nth_bit(Int& num, size_t n) {
|
||||
template<std::integral Int>
|
||||
inline void
|
||||
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
|
||||
template<std::unsigned_integral Int>
|
||||
template<std::integral Int>
|
||||
inline Int
|
||||
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)
|
||||
Int left = std::numeric_limits<Int>::digits - 1 - end;
|
||||
get_bit_range(Int num, size_t start, size_t end) {
|
||||
// NOTE: we do not require -1 if it is a signed integral
|
||||
Int left =
|
||||
std::numeric_limits<Int>::digits - (std::is_unsigned<Int>::value) - end;
|
||||
return num << left >> (left + start);
|
||||
}
|
||||
|
Reference in New Issue
Block a user