tests: add execution tests

all but data processing

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2023-09-18 18:23:52 +05:30
parent dd9dd5f116
commit fa96a4d09f
31 changed files with 2076 additions and 1265 deletions

View File

@@ -5,7 +5,7 @@
class Bus {
public:
Bus(Memory& memory);
Bus(const Memory& memory);
uint8_t read_byte(size_t address);
void write_byte(size_t address, uint8_t byte);

View File

@@ -1,58 +1,19 @@
#pragma once
#include "bus.hh"
#include "instruction.hh"
#include "psr.hh"
#include <cstdint>
using std::size_t;
class CpuImpl;
class Cpu {
public:
Cpu(Bus& bus);
Cpu(const Bus& bus) noexcept;
Cpu(const Cpu&) = delete;
Cpu(Cpu&&) = delete;
Cpu& operator=(const Cpu&) = delete;
Cpu& operator=(Cpu&&) = delete;
~Cpu();
void step();
private:
static constexpr uint8_t GPR_COUNT = 16;
static constexpr uint8_t GPR_FIQ_FIRST = 8;
static constexpr uint8_t GPR_SVC_FIRST = 13;
static constexpr uint8_t GPR_ABT_FIRST = 13;
static constexpr uint8_t GPR_IRQ_FIRST = 13;
static constexpr uint8_t GPR_UND_FIRST = 13;
static constexpr uint8_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
static constexpr uint8_t PC_INDEX = 15;
uint32_t& pc = gpr[PC_INDEX];
bool is_flushed;
void chg_mode(const Mode to);
void exec_arm(const arm::Instruction instruction);
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
std::unique_ptr<CpuImpl> impl;
};

View File

@@ -1,161 +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...>;
namespace arm {
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 imm;
bool up;
bool pre;
};
struct BlockDataTransfer {
uint16_t regs;
uint8_t rn;
bool load;
bool write;
bool s;
bool up;
bool pre;
};
struct DataProcessing {
std::variant<Shift, uint32_t> operand;
uint8_t rd;
uint8_t rn;
bool set;
OpCode opcode;
};
struct PsrTransfer {
enum class Type {
Mrs,
Msr,
Msr_flg
};
uint32_t operand;
bool spsr;
Type type;
// ignored outside MSR_flg
bool imm;
};
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,
BlockDataTransfer,
DataProcessing,
PsrTransfer,
CoprocessorDataTransfer,
CoprocessorDataOperation,
CoprocessorRegisterTransfer,
Undefined,
SoftwareInterrupt>;
struct Instruction {
Condition condition;
InstructionData data;
Instruction(uint32_t insn);
std::string disassemble();
};
}

View File

@@ -1,6 +1,3 @@
headers += files(
'cpu.hh',
'instruction.hh',
'psr.hh',
'utility.hh'
)

View File

@@ -1,55 +0,0 @@
#pragma once
#include "utility.hh"
#include <cstdint>
class Psr {
public:
// clear the reserved bits i.e, [8:27]
Psr(uint32_t raw);
uint32_t raw() const;
void set_all(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 = 0xFFFFFFE0;
uint32_t psr;
};

View File

@@ -1,99 +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
};
// https://fmt.dev/dev/api.html#std-ostream-support
std::ostream&
operator<<(std::ostream& os, const OpCode cond);
template<>
struct fmt::formatter<OpCode> : ostream_formatter {};
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 {};

View File

@@ -4,6 +4,8 @@
#include <string>
struct Header {
static constexpr uint8_t HEADER_SIZE = 192;
enum class UniqueCode {
Old, // old games
New, // new games

View File

@@ -4,14 +4,14 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <unordered_map>
#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;
Memory(std::array<uint8_t, BIOS_SIZE>&& bios, std::vector<uint8_t>&& rom);
uint8_t read(size_t address) const;
void write(size_t address, uint8_t byte);
@@ -58,6 +58,7 @@ class Memory {
#undef MEMORY_REGION
std::unordered_map<size_t, uint8_t> invalid_mem;
std::vector<uint8_t> rom;
Header header;
void parse_header();

View File

@@ -4,6 +4,8 @@ headers = files(
'header.hh',
)
inc = include_directories('.')
subdir('cpu')
install_headers(headers, subdir: meson.project_name(), preserve_path: true)