Compare commits
2 Commits
0b674c7c64
...
527e72edd3
| Author | SHA1 | Date | |
|---|---|---|---|
|
527e72edd3
|
|||
|
81afd67e0b
|
@@ -1 +0,0 @@
|
|||||||
../src/bus.hh
|
|
||||||
21
include/bus.hh
Normal file
21
include/bus.hh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "memory.hh"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Bus {
|
||||||
|
public:
|
||||||
|
Bus(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:
|
||||||
|
std::shared_ptr<Memory> memory;
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../src/cpu/cpu.hh
|
|
||||||
55
include/cpu/cpu.hh
Normal file
55
include/cpu/cpu.hh
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "bus.hh"
|
||||||
|
#include "instruction.hh"
|
||||||
|
#include "psr.hh"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using std::size_t;
|
||||||
|
|
||||||
|
class Cpu {
|
||||||
|
public:
|
||||||
|
Cpu(Bus& bus);
|
||||||
|
void step();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t GPR_COUNT = 16;
|
||||||
|
|
||||||
|
static constexpr size_t GPR_FIQ_FIRST = 8;
|
||||||
|
static constexpr size_t GPR_SVC_FIRST = 13;
|
||||||
|
static constexpr size_t GPR_ABT_FIRST = 13;
|
||||||
|
static constexpr size_t GPR_IRQ_FIRST = 13;
|
||||||
|
static constexpr size_t GPR_UND_FIRST = 13;
|
||||||
|
static constexpr size_t GPR_SYS_USR_FIRST = 8;
|
||||||
|
|
||||||
|
std::shared_ptr<Bus> bus;
|
||||||
|
std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
|
||||||
|
|
||||||
|
Psr cpsr; // current program status register
|
||||||
|
Psr spsr; // status program status register
|
||||||
|
|
||||||
|
uint32_t& pc = gpr[15];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_ABT_FIRST - 1> abt;
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_IRQ_FIRST - 1> irq;
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_UND_FIRST - 1> und;
|
||||||
|
|
||||||
|
// visible registers before the mode switch
|
||||||
|
std::array<uint32_t, GPR_COUNT - GPR_SYS_USR_FIRST> old;
|
||||||
|
} gpr_banked; // banked general purpose registers
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Psr fiq;
|
||||||
|
Psr svc;
|
||||||
|
Psr abt;
|
||||||
|
Psr irq;
|
||||||
|
Psr und;
|
||||||
|
} spsr_banked; // banked saved program status registers
|
||||||
|
|
||||||
|
void chg_mode(const Mode to);
|
||||||
|
void exec_arm(const ArmInstruction instruction);
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../src/cpu/instruction.hh
|
|
||||||
132
include/cpu/instruction.hh
Normal file
132
include/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 +0,0 @@
|
|||||||
../../src/cpu/psr.hh
|
|
||||||
52
include/cpu/psr.hh
Normal file
52
include/cpu/psr.hh
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utility.hh"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class Psr {
|
||||||
|
public:
|
||||||
|
// clear the reserved bits i.e, [8:27]
|
||||||
|
Psr(uint32_t raw);
|
||||||
|
|
||||||
|
// Mode : [4:0]
|
||||||
|
Mode mode() const;
|
||||||
|
void set_mode(Mode mode);
|
||||||
|
|
||||||
|
// State : [5]
|
||||||
|
State state() const;
|
||||||
|
void set_state(State state);
|
||||||
|
|
||||||
|
#define GET_SET_NTH_BIT_FUNCTIONS(name) \
|
||||||
|
bool name() const; \
|
||||||
|
void set_##name(bool val);
|
||||||
|
|
||||||
|
// FIQ disable : [6]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled)
|
||||||
|
|
||||||
|
// IRQ disable : [7]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(irq_disabled)
|
||||||
|
|
||||||
|
// Reserved bits : [27:8]
|
||||||
|
|
||||||
|
// Overflow flag : [28]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(v)
|
||||||
|
|
||||||
|
// Carry flag : [29]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(c)
|
||||||
|
|
||||||
|
// Zero flag : [30]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(z)
|
||||||
|
|
||||||
|
// Negative flag : [30]
|
||||||
|
GET_SET_NTH_BIT_FUNCTIONS(n)
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
uint32_t psr;
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../src/cpu/utility.hh
|
|
||||||
93
include/cpu/utility.hh
Normal file
93
include/cpu/utility.hh
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#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
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, const ShiftType cond);
|
||||||
|
template<>
|
||||||
|
struct fmt::formatter<ShiftType> : ostream_formatter {};
|
||||||
@@ -1 +0,0 @@
|
|||||||
../src/header.hh
|
|
||||||
44
include/header.hh
Normal file
44
include/header.hh
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct Header {
|
||||||
|
enum class UniqueCode {
|
||||||
|
Old, // old games
|
||||||
|
New, // new games
|
||||||
|
Newer, // unused (newer games)
|
||||||
|
Famicom, // NES
|
||||||
|
YoshiKoro, // acceleration sensor
|
||||||
|
Ereader, // dot code scanner
|
||||||
|
Warioware, // rumble and z-axis gyro
|
||||||
|
Boktai, // RTC and solar sensor
|
||||||
|
DrillDozer, // rumble
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class I18n {
|
||||||
|
Japan,
|
||||||
|
Europe,
|
||||||
|
French,
|
||||||
|
Spanish,
|
||||||
|
Usa,
|
||||||
|
German,
|
||||||
|
Italian
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BootMode {
|
||||||
|
Joybus,
|
||||||
|
Normal,
|
||||||
|
Multiplay
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t entrypoint;
|
||||||
|
std::string title;
|
||||||
|
std::string title_code;
|
||||||
|
UniqueCode unique_code;
|
||||||
|
I18n i18n;
|
||||||
|
uint8_t version;
|
||||||
|
BootMode multiboot;
|
||||||
|
uint32_t multiboot_entrypoint;
|
||||||
|
uint8_t slave_id;
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
../src/memory.hh
|
|
||||||
64
include/memory.hh
Normal file
64
include/memory.hh
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "header.hh"
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Memory {
|
||||||
|
public:
|
||||||
|
static constexpr size_t BIOS_SIZE = 1024 * 16;
|
||||||
|
|
||||||
|
Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
||||||
|
std::vector<uint8_t>&& rom) noexcept;
|
||||||
|
|
||||||
|
uint8_t read(size_t address) const;
|
||||||
|
void write(size_t address, uint8_t byte);
|
||||||
|
|
||||||
|
uint16_t read_halfword(size_t address) const;
|
||||||
|
void write_halfword(size_t address, uint16_t halfword);
|
||||||
|
|
||||||
|
uint32_t read_word(size_t address) const;
|
||||||
|
void write_word(size_t address, uint32_t word);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#define MEMORY_REGION(name, start, end) \
|
||||||
|
static constexpr size_t name##_START = start; \
|
||||||
|
static constexpr size_t name##_END = end;
|
||||||
|
|
||||||
|
#define DECL_MEMORY(name, ident, start, end) \
|
||||||
|
MEMORY_REGION(name, start, end) \
|
||||||
|
std::array<uint8_t, name##_END - name##_START + 1> ident;
|
||||||
|
|
||||||
|
MEMORY_REGION(BIOS, 0x00000000, 0x00003FFF)
|
||||||
|
std::array<uint8_t, BIOS_SIZE> bios;
|
||||||
|
static_assert(BIOS_END - BIOS_START + 1 == BIOS_SIZE);
|
||||||
|
|
||||||
|
// board working RAM
|
||||||
|
DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF)
|
||||||
|
|
||||||
|
// chip working RAM
|
||||||
|
DECL_MEMORY(CHIP_WRAM, chip_wram, 0x03000000, 0x03007FFF)
|
||||||
|
|
||||||
|
// palette RAM
|
||||||
|
DECL_MEMORY(PALETTE_RAM, palette_ram, 0x05000000, 0x050003FF)
|
||||||
|
|
||||||
|
// video RAM
|
||||||
|
DECL_MEMORY(VRAM, vram, 0x06000000, 0x06017FFF)
|
||||||
|
|
||||||
|
// OAM OBJ attributes
|
||||||
|
DECL_MEMORY(OAM_OBJ_ATTR, oam_obj_attr, 0x07000000, 0x070003FF)
|
||||||
|
|
||||||
|
#undef DECL_MEMORY
|
||||||
|
|
||||||
|
MEMORY_REGION(ROM_0, 0x08000000, 0x09FFFFFF)
|
||||||
|
MEMORY_REGION(ROM_1, 0x0A000000, 0x0BFFFFFF)
|
||||||
|
MEMORY_REGION(ROM_2, 0x0C000000, 0x0DFFFFFF)
|
||||||
|
|
||||||
|
#undef MEMORY_REGION
|
||||||
|
|
||||||
|
std::vector<uint8_t> rom;
|
||||||
|
Header header;
|
||||||
|
void parse_header();
|
||||||
|
};
|
||||||
21
src/bus.hh
21
src/bus.hh
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "memory.hh"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class Bus {
|
|
||||||
public:
|
|
||||||
Bus(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:
|
|
||||||
std::shared_ptr<Memory> memory;
|
|
||||||
};
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "bus.hh"
|
|
||||||
#include "instruction.hh"
|
|
||||||
#include "psr.hh"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using std::size_t;
|
|
||||||
|
|
||||||
class Cpu {
|
|
||||||
public:
|
|
||||||
Cpu(Bus& bus);
|
|
||||||
void step();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr size_t GPR_COUNT = 16;
|
|
||||||
|
|
||||||
static constexpr size_t GPR_FIQ_FIRST = 8;
|
|
||||||
static constexpr size_t GPR_SVC_FIRST = 13;
|
|
||||||
static constexpr size_t GPR_ABT_FIRST = 13;
|
|
||||||
static constexpr size_t GPR_IRQ_FIRST = 13;
|
|
||||||
static constexpr size_t GPR_UND_FIRST = 13;
|
|
||||||
static constexpr size_t GPR_SYS_USR_FIRST = 8;
|
|
||||||
|
|
||||||
std::shared_ptr<Bus> bus;
|
|
||||||
std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
|
|
||||||
|
|
||||||
Psr cpsr; // current program status register
|
|
||||||
Psr spsr; // status program status register
|
|
||||||
|
|
||||||
uint32_t& pc = gpr[15];
|
|
||||||
|
|
||||||
struct {
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_ABT_FIRST - 1> abt;
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_IRQ_FIRST - 1> irq;
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_UND_FIRST - 1> und;
|
|
||||||
|
|
||||||
// visible registers before the mode switch
|
|
||||||
std::array<uint32_t, GPR_COUNT - GPR_SYS_USR_FIRST> old;
|
|
||||||
} gpr_banked; // banked general purpose registers
|
|
||||||
|
|
||||||
struct {
|
|
||||||
Psr fiq;
|
|
||||||
Psr svc;
|
|
||||||
Psr abt;
|
|
||||||
Psr irq;
|
|
||||||
Psr und;
|
|
||||||
} spsr_banked; // banked saved program status registers
|
|
||||||
|
|
||||||
void chg_mode(const Mode to);
|
|
||||||
void exec_arm(const ArmInstruction instruction);
|
|
||||||
};
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
#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,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utility.hh"
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
class Psr {
|
|
||||||
public:
|
|
||||||
// clear the reserved bits i.e, [8:27]
|
|
||||||
Psr(uint32_t raw);
|
|
||||||
|
|
||||||
// Mode : [4:0]
|
|
||||||
Mode mode() const;
|
|
||||||
void set_mode(Mode mode);
|
|
||||||
|
|
||||||
// State : [5]
|
|
||||||
State state() const;
|
|
||||||
void set_state(State state);
|
|
||||||
|
|
||||||
#define GET_SET_NTH_BIT_FUNCTIONS(name) \
|
|
||||||
bool name() const; \
|
|
||||||
void set_##name(bool val);
|
|
||||||
|
|
||||||
// FIQ disable : [6]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled)
|
|
||||||
|
|
||||||
// IRQ disable : [7]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(irq_disabled)
|
|
||||||
|
|
||||||
// Reserved bits : [27:8]
|
|
||||||
|
|
||||||
// Overflow flag : [28]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(v)
|
|
||||||
|
|
||||||
// Carry flag : [29]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(c)
|
|
||||||
|
|
||||||
// Zero flag : [30]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(z)
|
|
||||||
|
|
||||||
// Negative flag : [30]
|
|
||||||
GET_SET_NTH_BIT_FUNCTIONS(n)
|
|
||||||
|
|
||||||
#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;
|
|
||||||
|
|
||||||
uint32_t psr;
|
|
||||||
};
|
|
||||||
@@ -1,93 +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
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
std::ostream&
|
|
||||||
operator<<(std::ostream& os, const ShiftType cond);
|
|
||||||
template<>
|
|
||||||
struct fmt::formatter<ShiftType> : ostream_formatter {};
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct Header {
|
|
||||||
enum class UniqueCode {
|
|
||||||
Old, // old games
|
|
||||||
New, // new games
|
|
||||||
Newer, // unused (newer games)
|
|
||||||
Famicom, // NES
|
|
||||||
YoshiKoro, // acceleration sensor
|
|
||||||
Ereader, // dot code scanner
|
|
||||||
Warioware, // rumble and z-axis gyro
|
|
||||||
Boktai, // RTC and solar sensor
|
|
||||||
DrillDozer, // rumble
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class I18n {
|
|
||||||
Japan,
|
|
||||||
Europe,
|
|
||||||
French,
|
|
||||||
Spanish,
|
|
||||||
Usa,
|
|
||||||
German,
|
|
||||||
Italian
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class BootMode {
|
|
||||||
Joybus,
|
|
||||||
Normal,
|
|
||||||
Multiplay
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t entrypoint;
|
|
||||||
std::string title;
|
|
||||||
std::string title_code;
|
|
||||||
UniqueCode unique_code;
|
|
||||||
I18n i18n;
|
|
||||||
uint8_t version;
|
|
||||||
BootMode multiboot;
|
|
||||||
uint32_t multiboot_entrypoint;
|
|
||||||
uint8_t slave_id;
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "header.hh"
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class Memory {
|
|
||||||
public:
|
|
||||||
static constexpr size_t BIOS_SIZE = 1024 * 16;
|
|
||||||
|
|
||||||
Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
|
|
||||||
std::vector<uint8_t>&& rom) noexcept;
|
|
||||||
|
|
||||||
uint8_t read(size_t address) const;
|
|
||||||
void write(size_t address, uint8_t byte);
|
|
||||||
|
|
||||||
uint16_t read_halfword(size_t address) const;
|
|
||||||
void write_halfword(size_t address, uint16_t halfword);
|
|
||||||
|
|
||||||
uint32_t read_word(size_t address) const;
|
|
||||||
void write_word(size_t address, uint32_t word);
|
|
||||||
|
|
||||||
private:
|
|
||||||
#define MEMORY_REGION(name, start, end) \
|
|
||||||
static constexpr size_t name##_START = start; \
|
|
||||||
static constexpr size_t name##_END = end;
|
|
||||||
|
|
||||||
#define DECL_MEMORY(name, ident, start, end) \
|
|
||||||
MEMORY_REGION(name, start, end) \
|
|
||||||
std::array<uint8_t, name##_END - name##_START + 1> ident;
|
|
||||||
|
|
||||||
MEMORY_REGION(BIOS, 0x00000000, 0x00003FFF)
|
|
||||||
std::array<uint8_t, BIOS_SIZE> bios;
|
|
||||||
static_assert(BIOS_END - BIOS_START + 1 == BIOS_SIZE);
|
|
||||||
|
|
||||||
// board working RAM
|
|
||||||
DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF)
|
|
||||||
|
|
||||||
// chip working RAM
|
|
||||||
DECL_MEMORY(CHIP_WRAM, chip_wram, 0x03000000, 0x03007FFF)
|
|
||||||
|
|
||||||
// palette RAM
|
|
||||||
DECL_MEMORY(PALETTE_RAM, palette_ram, 0x05000000, 0x050003FF)
|
|
||||||
|
|
||||||
// video RAM
|
|
||||||
DECL_MEMORY(VRAM, vram, 0x06000000, 0x06017FFF)
|
|
||||||
|
|
||||||
// OAM OBJ attributes
|
|
||||||
DECL_MEMORY(OAM_OBJ_ATTR, oam_obj_attr, 0x07000000, 0x070003FF)
|
|
||||||
|
|
||||||
#undef DECL_MEMORY
|
|
||||||
|
|
||||||
MEMORY_REGION(ROM_0, 0x08000000, 0x09FFFFFF)
|
|
||||||
MEMORY_REGION(ROM_1, 0x0A000000, 0x0BFFFFFF)
|
|
||||||
MEMORY_REGION(ROM_2, 0x0C000000, 0x0DFFFFFF)
|
|
||||||
|
|
||||||
#undef MEMORY_REGION
|
|
||||||
|
|
||||||
std::vector<uint8_t> rom;
|
|
||||||
Header header;
|
|
||||||
void parse_header();
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user