3 Commits

Author SHA1 Message Date
492c06cc45 thumb: initialise instruction formats
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-23 22:05:20 +05:30
9cdfa90acc memory: remove unused functions
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-23 21:58:41 +05:30
91a82eec7c log: encapsulate logger
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
2023-09-23 21:09:44 +05:30
20 changed files with 602 additions and 141 deletions

View File

@@ -6,4 +6,5 @@ Checks: '
, -cppcoreguidelines-macro-usage , -cppcoreguidelines-macro-usage
, -cppcoreguidelines-avoid-const-or-ref-data-members , -cppcoreguidelines-avoid-const-or-ref-data-members
, -cppcoreguidelines-non-private-member-variables-in-classes , -cppcoreguidelines-non-private-member-variables-in-classes
, -cppcoreguidelines-avoid-non-const-global-variables
' '

View File

@@ -1,6 +1,7 @@
#include "bus.hh" #include "bus.hh"
#include "cpu/cpu.hh" #include "cpu/cpu.hh"
#include "memory.hh" #include "memory.hh"
#include "util/loglevel.hh"
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
@@ -84,6 +85,8 @@ main(int argc, const char* argv[]) {
std::flush(std::cout); std::flush(std::cout);
std::flush(std::cout); std::flush(std::cout);
matar::set_log_level(matar::LogLevel::Debug);
try { try {
matar::Memory memory(std::move(bios), std::move(rom)); matar::Memory memory(std::move(bios), std::move(rom));
matar::Bus bus(memory); matar::Bus bus(memory);

View File

@@ -17,12 +17,6 @@ class Memory {
uint8_t read(size_t address) const; uint8_t read(size_t address) const;
void write(size_t address, uint8_t byte); 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: private:
#define MEMORY_REGION(name, start, end) \ #define MEMORY_REGION(name, start, end) \
static constexpr size_t name##_START = start; \ static constexpr size_t name##_START = start; \

View File

@@ -7,5 +7,6 @@ headers = files(
inc = include_directories('.') inc = include_directories('.')
subdir('cpu') subdir('cpu')
subdir('util')
install_headers(headers, subdir: meson.project_name(), preserve_path: true) install_headers(headers, subdir: meson.project_name(), preserve_path: true)

14
include/util/loglevel.hh Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
namespace matar {
enum class LogLevel {
Off = 1 << 0,
Error = 1 << 1,
Warn = 1 << 2,
Info = 1 << 3,
Debug = 1 << 4
};
void
set_log_level(LogLevel level);
}

3
include/util/meson.build Normal file
View File

@@ -0,0 +1,3 @@
headers += files(
'loglevel.hh'
)

View File

@@ -1,4 +1,5 @@
#include "bus.hh" #include "bus.hh"
#include "util/log.hh"
#include <memory> #include <memory>
namespace matar { namespace matar {
@@ -17,21 +18,38 @@ Bus::write_byte(size_t address, uint8_t byte) {
uint16_t uint16_t
Bus::read_halfword(size_t address) { Bus::read_halfword(size_t address) {
return memory->read_halfword(address); if (address & 0b01)
glogger.warn("Reading a non aligned halfword address");
return memory->read(address) | memory->read(address + 1) << 8;
} }
void void
Bus::write_halfword(size_t address, uint16_t halfword) { Bus::write_halfword(size_t address, uint16_t halfword) {
memory->write_halfword(address, halfword); if (address & 0b01)
glogger.warn("Writing to a non aligned halfword address");
memory->write(address, halfword & 0xFF);
memory->write(address + 1, halfword >> 8 & 0xFF);
} }
uint32_t uint32_t
Bus::read_word(size_t address) { Bus::read_word(size_t address) {
return memory->read_word(address); if (address & 0b11)
glogger.warn("Reading a non aligned word address");
return memory->read(address) | memory->read(address + 1) << 8 |
memory->read(address + 2) << 16 | memory->read(address + 3) << 24;
} }
void void
Bus::write_word(size_t address, uint32_t word) { Bus::write_word(size_t address, uint32_t word) {
memory->write_word(address, word); if (address & 0b11)
glogger.warn("Writing to a non aligned word address");
memory->write(address, word & 0xFF);
memory->write(address + 1, word >> 8 & 0xFF);
memory->write(address + 2, word >> 16 & 0xFF);
memory->write(address + 3, word >> 24 & 0xFF);
} }
} }

View File

@@ -2,27 +2,24 @@
#include "util/bits.hh" #include "util/bits.hh"
#include "util/log.hh" #include "util/log.hh"
using namespace logger;
namespace matar { namespace matar {
void void
CpuImpl::exec_arm(const arm::Instruction instruction) { CpuImpl::exec_arm(const arm::Instruction instruction) {
Condition cond = instruction.condition; Condition cond = instruction.condition;
arm::InstructionData data = instruction.data; arm::InstructionData data = instruction.data;
debug(cpsr.condition(cond));
if (!cpsr.condition(cond)) { if (!cpsr.condition(cond)) {
return; return;
} }
auto pc_error = [](uint8_t r) { auto pc_error = [](uint8_t r) {
if (r == PC_INDEX) if (r == PC_INDEX)
log_error("Using PC (R15) as operand register"); glogger.error("Using PC (R15) as operand register");
}; };
auto pc_warn = [](uint8_t r) { auto pc_warn = [](uint8_t r) {
if (r == PC_INDEX) if (r == PC_INDEX)
log_warn("Using PC (R15) as operand register"); glogger.warn("Using PC (R15) as operand register");
}; };
using namespace arm; using namespace arm;
@@ -62,8 +59,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
}, },
[this, pc_error](Multiply& data) { [this, pc_error](Multiply& data) {
if (data.rd == data.rm) if (data.rd == data.rm)
log_error("rd and rm are not distinct in {}", glogger.error("rd and rm are not distinct in {}",
typeid(data).name()); typeid(data).name());
pc_error(data.rd); pc_error(data.rd);
pc_error(data.rd); pc_error(data.rd);
@@ -81,8 +78,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
[this, pc_error](MultiplyLong& data) { [this, pc_error](MultiplyLong& data) {
if (data.rdhi == data.rdlo || data.rdhi == data.rm || if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
data.rdlo == data.rm) data.rdlo == data.rm)
log_error("rdhi, rdlo and rm are not distinct in {}", glogger.error("rdhi, rdlo and rm are not distinct in {}",
typeid(data).name()); typeid(data).name());
pc_error(data.rdhi); pc_error(data.rdhi);
pc_error(data.rdlo); pc_error(data.rdlo);
@@ -123,7 +120,7 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
cpsr.set_v(0); cpsr.set_v(0);
} }
}, },
[](Undefined) { log_warn("Undefined instruction"); }, [](Undefined) { glogger.warn("Undefined instruction"); },
[this, pc_error](SingleDataSwap& data) { [this, pc_error](SingleDataSwap& data) {
pc_error(data.rm); pc_error(data.rm);
pc_error(data.rn); pc_error(data.rn);
@@ -142,12 +139,12 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
uint32_t address = gpr[data.rn]; uint32_t address = gpr[data.rn];
if (!data.pre && data.write) if (!data.pre && data.write)
log_warn("Write-back enabled with post-indexing in {}", glogger.warn("Write-back enabled with post-indexing in {}",
typeid(data).name()); typeid(data).name());
if (data.rn == PC_INDEX && data.write) if (data.rn == PC_INDEX && data.write)
log_warn("Write-back enabled with base register as PC {}", glogger.warn("Write-back enabled with base register as PC {}",
typeid(data).name()); typeid(data).name());
if (data.write) if (data.write)
pc_warn(data.rn); pc_warn(data.rn);
@@ -216,11 +213,11 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
uint32_t offset = 0; uint32_t offset = 0;
if (!data.pre && data.write) if (!data.pre && data.write)
log_error("Write-back enabled with post-indexing in {}", glogger.error("Write-back enabled with post-indexing in {}",
typeid(data).name()); typeid(data).name());
if (data.sign && !data.load) if (data.sign && !data.load)
log_error("Signed data found in {}", typeid(data).name()); glogger.error("Signed data found in {}", typeid(data).name());
if (data.write) if (data.write)
pc_warn(data.rn); pc_warn(data.rn);
@@ -294,8 +291,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
pc_error(data.rn); pc_error(data.rn);
if (cpsr.mode() == Mode::User && data.s) { if (cpsr.mode() == Mode::User && data.s) {
log_error("Bit S is set outside priviliged modes in {}", glogger.error("Bit S is set outside priviliged modes in {}",
typeid(data).name()); typeid(data).name());
} }
// we just change modes to load user registers // we just change modes to load user registers
@@ -304,8 +301,9 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
chg_mode(Mode::User); chg_mode(Mode::User);
if (data.write) { if (data.write) {
log_error("Write-back enable for user bank registers in {}", glogger.error(
typeid(data).name()); "Write-back enable for user bank registers in {}",
typeid(data).name());
} }
} }
@@ -358,8 +356,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
}, },
[this, pc_error](PsrTransfer& data) { [this, pc_error](PsrTransfer& data) {
if (data.spsr && cpsr.mode() == Mode::User) { if (data.spsr && cpsr.mode() == Mode::User) {
log_error("Accessing SPSR in User mode in {}", glogger.error("Accessing SPSR in User mode in {}",
typeid(data).name()); typeid(data).name());
} }
Psr& psr = data.spsr ? spsr : cpsr; Psr& psr = data.spsr ? spsr : cpsr;
@@ -513,8 +511,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
if (data.set) { if (data.set) {
if (data.rd == PC_INDEX) { if (data.rd == PC_INDEX) {
if (cpsr.mode() == Mode::User) if (cpsr.mode() == Mode::User)
log_error("Running {} in User mode", glogger.error("Running {} in User mode",
typeid(data).name()); typeid(data).name());
spsr = cpsr; spsr = cpsr;
} else { } else {
set_conditions(); set_conditions();
@@ -536,7 +534,7 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
spsr = cpsr; spsr = cpsr;
}, },
[](auto& data) { [](auto& data) {
log_error("Unimplemented {} instruction", typeid(data).name()); glogger.error("Unimplemented {} instruction", typeid(data).name());
} }, } },
data); data);
} }

View File

@@ -4,8 +4,6 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
using namespace logger;
namespace matar { namespace matar {
CpuImpl::CpuImpl(const Bus& bus) noexcept CpuImpl::CpuImpl(const Bus& bus) noexcept
: bus(std::make_shared<Bus>(bus)) : bus(std::make_shared<Bus>(bus))
@@ -19,7 +17,7 @@ CpuImpl::CpuImpl(const Bus& bus) noexcept
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"); glogger.info("CPU successfully initialised");
// PC always points to two instructions ahead // PC always points to two instructions ahead
// PC - 2 is the instruction being executed // PC - 2 is the instruction being executed
@@ -121,14 +119,13 @@ CpuImpl::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) {
debug(cur_pc);
uint32_t x = bus->read_word(cur_pc); uint32_t x = bus->read_word(cur_pc);
arm::Instruction instruction(x); arm::Instruction instruction(x);
log_info("{:#034b}", x); glogger.info("{:#034b}", x);
exec_arm(instruction); exec_arm(instruction);
log_info("0x{:08X} : {}", cur_pc, instruction.disassemble()); glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble());
if (is_flushed) { if (is_flushed) {
// if flushed, do not increment the PC, instead set it to two // if flushed, do not increment the PC, instead set it to two

View File

@@ -6,3 +6,4 @@ lib_sources += files(
) )
subdir('arm') subdir('arm')
subdir('thumb')

View File

@@ -0,0 +1,191 @@
#include "instruction.hh"
#include "util/bits.hh"
#include <iterator>
namespace matar {
namespace thumb {
Instruction::Instruction(uint16_t insn) {
// Format 1: Move Shifted Register
if ((insn & 0xE000) == 0x0000) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rs = bit_range(insn, 3, 5);
uint8_t offset = bit_range(insn, 6, 10);
ShiftType opcode = static_cast<ShiftType>(bit_range(insn, 11, 12));
data = MoveShiftedRegister{
.rd = rd, .rs = rs, .offset = offset, .opcode = opcode
};
// Format 2: Add/Subtract
} else if ((insn & 0xF800) == 0x1800) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rs = bit_range(insn, 3, 5);
uint8_t offset = bit_range(insn, 6, 8);
AddSubtract::OpCode opcode =
static_cast<AddSubtract::OpCode>(get_bit(insn, 9));
bool imm = get_bit(insn, 10);
data = AddSubtract{
.rd = rd, .rs = rs, .offset = offset, .opcode = opcode, .imm = imm
};
// Format 3: Move/compare/add/subtract immediate
} else if ((insn & 0xE000) == 0x2000) {
uint8_t offset = bit_range(insn, 0, 7);
uint8_t rd = bit_range(insn, 8, 10);
MovCmpAddSubImmediate::OpCode opcode =
static_cast<MovCmpAddSubImmediate::OpCode>(bit_range(insn, 11, 12));
data =
MovCmpAddSubImmediate{ .offset = offset, .rd = rd, .opcode = opcode };
// Format 4: ALU operations
} else if ((insn & 0xFC00) == 0x4000) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rs = bit_range(insn, 3, 5);
AluOperations::OpCode opcode =
static_cast<AluOperations::OpCode>(bit_range(insn, 6, 9));
data = AluOperations{ .rd = rd, .rs = rs, .opcode = opcode };
// Format 5: Hi register operations/branch exchange
} else if ((insn & 0xFC00) == 0x4400) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rs = bit_range(insn, 3, 5);
bool hi_2 = get_bit(insn, 6);
bool hi_1 = get_bit(insn, 7);
HiRegisterOperations::OpCode opcode =
static_cast<HiRegisterOperations::OpCode>(bit_range(insn, 8, 9));
data = HiRegisterOperations{
.rd = rd, .rs = rs, .hi_2 = hi_2, .hi_1 = hi_1, .opcode = opcode
};
// Format 6: PC-relative load
} else if ((insn & 0xF800) == 0x4800) {
uint8_t word = bit_range(insn, 0, 7);
uint8_t rd = bit_range(insn, 8, 10);
data = PcRelativeLoad{ .word = word, .rd = rd };
// Format 7: Load/store with register offset
} else if ((insn & 0xF200) == 0x5000) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rb = bit_range(insn, 3, 5);
uint8_t ro = bit_range(insn, 6, 8);
bool byte = get_bit(insn, 10);
bool load = get_bit(insn, 11);
data = LoadStoreRegisterOffset{
.rd = rd, .rb = rb, .ro = ro, .byte = byte, .load = load
};
// Format 8: Load/store sign-extended byte/halfword
} else if ((insn & 0xF200) == 0x5200) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rb = bit_range(insn, 3, 5);
uint8_t ro = bit_range(insn, 6, 8);
bool s = get_bit(insn, 10);
bool h = get_bit(insn, 11);
data = LoadStoreSignExtendedHalfword{
.rd = rd, .rb = rb, .ro = ro, .s = s, .h = h
};
// Format 9: Load/store with immediate offset
} else if ((insn & 0xF000) == 0x6000) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rb = bit_range(insn, 3, 5);
uint8_t offset = bit_range(insn, 6, 10);
bool load = get_bit(insn, 11);
bool byte = get_bit(insn, 12);
data = LoadStoreImmediateOffset{
.rd = rd, .rb = rb, .offset = offset, .load = load, .byte = byte
};
// Format 10: Load/store halfword
} else if ((insn & 0xF000) == 0x8000) {
uint8_t rd = bit_range(insn, 0, 2);
uint8_t rb = bit_range(insn, 3, 5);
uint8_t offset = bit_range(insn, 6, 10);
bool load = get_bit(insn, 11);
data = LoadStoreHalfword{
.rd = rd, .rb = rb, .offset = offset, .load = load
};
// Format 11: SP-relative load/store
} else if ((insn & 0xF000) == 0x9000) {
uint8_t word = bit_range(insn, 0, 7);
uint8_t rd = bit_range(insn, 8, 10);
bool load = get_bit(insn, 11);
data = SpRelativeLoad{ .word = word, .rd = rd, .load = load };
// Format 12: Load address
} else if ((insn & 0xF000) == 0xA000) {
uint8_t word = bit_range(insn, 0, 7);
uint8_t rd = bit_range(insn, 8, 10);
bool sp = get_bit(insn, 11);
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
// Format 12: Load address
} else if ((insn & 0xF000) == 0xA000) {
uint8_t word = bit_range(insn, 0, 7);
uint8_t rd = bit_range(insn, 8, 10);
bool sp = get_bit(insn, 11);
data = LoadAddress{ .word = word, .rd = rd, .sp = sp };
// Format 13: Add offset to stack pointer
} else if ((insn & 0xFF00) == 0xB000) {
uint8_t word = bit_range(insn, 0, 6);
bool sign = get_bit(insn, 7);
data = AddOffsetStackPointer{ .word = word, .sign = sign };
// Format 14: Push/pop registers
} else if ((insn & 0xF600) == 0xB400) {
uint8_t regs = bit_range(insn, 0, 7);
bool pclr = get_bit(insn, 8);
bool load = get_bit(insn, 11);
data = PushPopRegister{ .regs = regs, .pclr = pclr, .load = load };
// Format 15: Multiple load/store
} else if ((insn & 0xF000) == 0xC000) {
uint8_t regs = bit_range(insn, 0, 7);
uint8_t rb = bit_range(insn, 8, 10);
bool load = get_bit(insn, 11);
data = MultipleLoad{ .regs = regs, .rb = rb, .load = load };
// Format 17: Software interrupt
} else if ((insn & 0xFF00) == 0xDF00) {
data = SoftwareInterrupt{};
// Format 16: Conditional branch
} else if ((insn & 0xF000) == 0xD000) {
uint8_t offset = bit_range(insn, 0, 7);
Condition condition = static_cast<Condition>(bit_range(insn, 8, 11));
data = ConditionalBranch{ .offset = offset, .condition = condition };
// Format 18: Unconditional branch
} else if ((insn & 0xF800) == 0xE000) {
uint16_t offset = bit_range(insn, 0, 10);
data = UnconditionalBranch{ .offset = offset };
// Format 19: Long branch with link
} else if ((insn & 0xF000) == 0xF000) {
uint16_t offset = bit_range(insn, 0, 10);
bool high = get_bit(insn, 11);
data = LongBranchWithLink{ .offset = offset, .high = high };
}
}
}
}

View File

@@ -0,0 +1,230 @@
#pragma once
#include "cpu/alu.hh"
#include "cpu/psr.hh"
#include <cstdint>
#include <fmt/ostream.h>
#include <variant>
namespace matar {
namespace thumb {
template<class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
static constexpr size_t INSTRUCTION_SIZE = 2;
struct MoveShiftedRegister {
uint8_t rd;
uint8_t rs;
uint8_t offset;
ShiftType opcode;
};
struct AddSubtract {
enum class OpCode {
ADD = 0,
SUB = 1
};
uint8_t rd;
uint8_t rs;
uint8_t offset;
OpCode opcode;
bool imm;
};
struct MovCmpAddSubImmediate {
enum class OpCode {
MOV = 0b00,
CMP = 0b01,
ADD = 0b10,
SUB = 0b11
};
uint8_t offset;
uint8_t rd;
OpCode opcode;
};
struct AluOperations {
enum class OpCode {
AND = 0b0000,
EOR = 0b0001,
LSL = 0b0010,
LSR = 0b0011,
ASR = 0b0100,
ADC = 0b0101,
SBC = 0b0110,
ROR = 0b0111,
TST = 0b1000,
NEG = 0b1001,
CMP = 0b1010,
CMN = 0b1011,
ORR = 0b1100,
MUL = 0b1101,
BIC = 0b1110,
MVN = 0b1111
};
uint8_t rd;
uint8_t rs;
OpCode opcode;
};
struct HiRegisterOperations {
enum class OpCode {
ADD = 0b00,
CMP = 0b01,
MOV = 0b10,
BX = 0b11
};
uint8_t rd;
uint8_t rs;
bool hi_2;
bool hi_1;
OpCode opcode;
};
struct PcRelativeLoad {
uint8_t word;
uint8_t rd;
};
struct LoadStoreRegisterOffset {
uint8_t rd;
uint8_t rb;
uint8_t ro;
bool byte;
bool load;
};
struct LoadStoreSignExtendedHalfword {
uint8_t rd;
uint8_t rb;
uint8_t ro;
bool s;
bool h;
};
struct LoadStoreImmediateOffset {
uint8_t rd;
uint8_t rb;
uint8_t offset;
bool load;
bool byte;
};
struct LoadStoreHalfword {
uint8_t rd;
uint8_t rb;
uint8_t offset;
bool load;
};
struct SpRelativeLoad {
uint8_t word;
uint8_t rd;
bool load;
};
struct LoadAddress {
uint8_t word;
uint8_t rd;
bool sp;
};
struct AddOffsetStackPointer {
uint8_t word;
bool sign;
};
struct PushPopRegister {
uint8_t regs;
bool pclr;
bool load;
};
struct MultipleLoad {
uint8_t regs;
uint8_t rb;
bool load;
};
struct ConditionalBranch {
uint8_t offset;
Condition condition;
};
struct SoftwareInterrupt {};
struct UnconditionalBranch {
uint16_t offset;
};
struct LongBranchWithLink {
uint16_t offset;
bool high;
};
using InstructionData = std::variant<MoveShiftedRegister,
AddSubtract,
MovCmpAddSubImmediate,
AluOperations,
HiRegisterOperations,
PcRelativeLoad,
LoadStoreRegisterOffset,
LoadStoreSignExtendedHalfword,
LoadStoreImmediateOffset,
LoadStoreHalfword,
SpRelativeLoad,
LoadAddress,
AddOffsetStackPointer,
PushPopRegister,
MultipleLoad,
ConditionalBranch,
SoftwareInterrupt,
UnconditionalBranch,
LongBranchWithLink>;
struct Instruction {
InstructionData data;
Instruction(uint16_t insn);
std::string disassemble();
};
std::ostream&
operator<<(std::ostream& os, const AddSubtract::OpCode cond);
std::ostream&
operator<<(std::ostream& os, const MovCmpAddSubImmediate::OpCode cond);
std::ostream&
operator<<(std::ostream& os, const AluOperations::OpCode cond);
std::ostream&
operator<<(std::ostream& os, const HiRegisterOperations::OpCode cond);
}
}
namespace fmt {
template<>
struct formatter<matar::thumb::AddSubtract::OpCode> : ostream_formatter {};
template<>
struct formatter<matar::thumb::MovCmpAddSubImmediate::OpCode>
: ostream_formatter {};
template<>
struct formatter<matar::thumb::AluOperations::OpCode> : ostream_formatter {};
template<>
struct formatter<matar::thumb::HiRegisterOperations::OpCode>
: ostream_formatter {};
}

View File

@@ -0,0 +1,3 @@
lib_sources += files(
'instruction.cc'
)

View File

@@ -6,8 +6,6 @@
#include <bitset> #include <bitset>
#include <stdexcept> #include <stdexcept>
using namespace logger;
namespace matar { namespace matar {
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios, Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
std::vector<uint8_t>&& rom) std::vector<uint8_t>&& rom)
@@ -23,17 +21,17 @@ Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
"fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"; "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
if (bios_hash != expected_hash) { if (bios_hash != expected_hash) {
log_warn("BIOS hash failed to match, run at your own risk" glogger.warn("BIOS hash failed to match, run at your own risk"
"\nExpected : {} " "\nExpected : {} "
"\nGot : {}", "\nGot : {}",
expected_hash, expected_hash,
bios_hash); bios_hash);
} }
parse_header(); parse_header();
log_info("Memory successfully initialised"); glogger.info("Memory successfully initialised");
log_info("Cartridge Title: {}", header.title); glogger.info("Cartridge Title: {}", header.title);
}; };
#define MATCHES(area) address >= area##_START&& address <= area##_END #define MATCHES(area) address >= area##_START&& address <= area##_END
@@ -59,7 +57,7 @@ Memory::read(size_t address) const {
} else if (MATCHES(ROM_2)) { } else if (MATCHES(ROM_2)) {
return rom[address - ROM_2_START]; return rom[address - ROM_2_START];
} else { } else {
log_error("Invalid memory region accessed"); glogger.error("Invalid memory region accessed");
return 0xFF; return 0xFF;
} }
} }
@@ -85,49 +83,12 @@ Memory::write(size_t address, uint8_t byte) {
} else if (MATCHES(ROM_2)) { } else if (MATCHES(ROM_2)) {
rom[address - ROM_2_START] = byte; rom[address - ROM_2_START] = byte;
} else { } else {
log_error("Invalid memory region accessed"); glogger.error("Invalid memory region accessed");
} }
} }
#undef MATCHES #undef MATCHES
uint16_t
Memory::read_halfword(size_t address) const {
if (address & 0b01)
log_warn("Reading a non aligned halfword address");
return read(address) | read(address + 1) << 8;
}
void
Memory::write_halfword(size_t address, uint16_t halfword) {
if (address & 0b01)
log_warn("Writing to a non aligned halfword address");
write(address, halfword & 0xFF);
write(address + 1, halfword >> 8 & 0xFF);
}
uint32_t
Memory::read_word(size_t address) const {
if (address & 0b11)
log_warn("Reading a non aligned word address");
return read(address) | read(address + 1) << 8 | read(address + 2) << 16 |
read(address + 3) << 24;
}
void
Memory::write_word(size_t address, uint32_t word) {
if (address & 0b11)
log_warn("Writing to a non aligned word address");
write(address, word & 0xFF);
write(address + 1, word >> 8 & 0xFF);
write(address + 2, word >> 16 & 0xFF);
write(address + 3, word >> 24 & 0xFF);
}
void void
Memory::parse_header() { Memory::parse_header() {
@@ -142,7 +103,7 @@ Memory::parse_header() {
// nintendo logo // nintendo logo
if (rom[0x9C] != 0x21) if (rom[0x9C] != 0x21)
log_info("HEADER: BIOS debugger bits not set to 0"); glogger.info("HEADER: BIOS debugger bits not set to 0");
// game info // game info
header.title = std::string(&rom[0xA0], &rom[0xA0 + 12]); header.title = std::string(&rom[0xA0], &rom[0xA0 + 12]);
@@ -177,7 +138,7 @@ Memory::parse_header() {
break; break;
default: default:
log_error("HEADER: invalid unique code: {}", rom[0xAC]); glogger.error("HEADER: invalid unique code: {}", rom[0xAC]);
} }
header.title_code = std::string(&rom[0xAD], &rom[0xAE]); header.title_code = std::string(&rom[0xAD], &rom[0xAE]);
@@ -206,15 +167,16 @@ Memory::parse_header() {
break; break;
default: default:
log_error("HEADER: invalid destination/language: {}", rom[0xAF]); glogger.error("HEADER: invalid destination/language: {}",
rom[0xAF]);
} }
if (rom[0xB2] != 0x96) if (rom[0xB2] != 0x96)
log_error("HEADER: invalid fixed byte at 0xB2"); glogger.error("HEADER: invalid fixed byte at 0xB2");
for (size_t i = 0xB5; i < 0xBC; i++) { for (size_t i = 0xB5; i < 0xBC; i++) {
if (rom[i] != 0x00) if (rom[i] != 0x00)
log_error("HEADER: invalid fixed bytes at 0xB5"); glogger.error("HEADER: invalid fixed bytes at 0xB5");
} }
header.version = rom[0xBC]; header.version = rom[0xBC];
@@ -228,7 +190,7 @@ Memory::parse_header() {
chk &= 0xFF; chk &= 0xFF;
if (chk != rom[0xBD]) if (chk != rom[0xBD])
log_error("HEADER: checksum does not match"); glogger.error("HEADER: checksum does not match");
} }
// multiboot not required right now // multiboot not required right now

View File

@@ -3,9 +3,9 @@ lib_sources = files(
'bus.cc' 'bus.cc'
) )
subdir('util')
subdir('cpu') subdir('cpu')
lib_cpp_args = [ ] lib_cpp_args = [ ]
fmt = dependency('fmt', version : '>=10.1.0', static: true) fmt = dependency('fmt', version : '>=10.1.0', static: true)

8
src/util/log.cc Normal file
View File

@@ -0,0 +1,8 @@
#include "log.hh"
logging::Logger glogger = logging::Logger();
void
matar::set_log_level(LogLevel level) {
glogger.set_level(level);
}

View File

@@ -1,12 +1,10 @@
#pragma once #pragma once
#include "util/loglevel.hh"
#include <fmt/ostream.h> #include <fmt/ostream.h>
#include <iostream> #include <iostream>
using fmt::print; namespace logging {
using std::clog;
namespace logger {
namespace ansi { namespace ansi {
static constexpr std::string_view RED = "\033[31m"; static constexpr std::string_view RED = "\033[31m";
static constexpr std::string_view YELLOW = "\033[33m"; static constexpr std::string_view YELLOW = "\033[33m";
@@ -16,43 +14,69 @@ static constexpr std::string_view BOLD = "\033[1m";
static constexpr std::string_view RESET = "\033[0m"; static constexpr std::string_view RESET = "\033[0m";
} }
template<typename... Args> using fmt::print;
inline void
log_raw(const fmt::format_string<Args...>& fmt, Args&&... args) { class Logger {
fmt::println(clog, fmt, std::forward<Args>(args)...); using LogLevel = matar::LogLevel;
public:
Logger(LogLevel level = LogLevel::Debug)
: level(0) {
set_level(level);
}
template<typename... Args>
void log(const fmt::format_string<Args...>& fmt, Args&&... args) {
fmt::println(stream, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void debug(const fmt::format_string<Args...>& fmt, Args&&... args) {
if (level & static_cast<uint8_t>(LogLevel::Debug)) {
print(stream, "{}{}[DEBUG] ", ansi::MAGENTA, ansi::BOLD);
log(fmt, std::forward<Args>(args)...);
print(stream, ansi::RESET);
}
}
template<typename... Args>
void info(const fmt::format_string<Args...>& fmt, Args&&... args) {
if (level & static_cast<uint8_t>(LogLevel::Info)) {
print(stream, "{}[INFO] ", ansi::WHITE);
log(fmt, std::forward<Args>(args)...);
print(stream, ansi::RESET);
}
}
template<typename... Args>
void warn(const fmt::format_string<Args...>& fmt, Args&&... args) {
if (level & static_cast<uint8_t>(LogLevel::Warn)) {
print(stream, "{}[WARN] ", ansi::YELLOW);
log(fmt, std::forward<Args>(args)...);
print(stream, ansi::RESET);
}
}
template<typename... Args>
void error(const fmt::format_string<Args...>& fmt, Args&&... args) {
if (level & static_cast<uint8_t>(LogLevel::Error)) {
print(stream, "{}{}[ERROR] ", ansi::RED, ansi::BOLD);
log(fmt, std::forward<Args>(args)...);
print(stream, ansi::RESET);
}
}
void set_level(LogLevel level) {
this->level = (static_cast<uint8_t>(level) << 1) - 1;
}
void set_stream(std::ostream& stream) { this->stream = stream; }
private:
uint8_t level;
std::reference_wrapper<std::ostream> stream = std::clog;
};
} }
template<typename... Args> extern logging::Logger glogger;
inline void
log_debug(const fmt::format_string<Args...>& fmt, Args&&... args) {
print(clog, "{}{}[DEBUG] ", ansi::MAGENTA, ansi::BOLD);
log_raw(fmt, std::forward<Args>(args)...);
print(clog, ansi::RESET);
}
template<typename... Args> #define debug(x) logger.debug("{} = {}", #x, x);
inline void
log_info(const fmt::format_string<Args...>& fmt, Args&&... args) {
print(clog, "{}[INFO] ", ansi::WHITE);
log_raw(fmt, std::forward<Args>(args)...);
print(clog, ansi::RESET);
}
template<typename... Args>
inline void
log_warn(const fmt::format_string<Args...>& fmt, Args&&... args) {
print(clog, "{}[WARN] ", ansi::YELLOW);
log_raw(fmt, std::forward<Args>(args)...);
print(clog, ansi::RESET);
}
template<typename... Args>
inline void
log_error(const fmt::format_string<Args...>& fmt, Args&&... args) {
print(clog, "{}{}[ERROR] ", ansi::RED, ansi::BOLD);
log_raw(fmt, std::forward<Args>(args)...);
print(clog, ansi::RESET);
}
}
#define debug(value) logger::log_debug("{} = {}", #value, value)

3
src/util/meson.build Normal file
View File

@@ -0,0 +1,3 @@
lib_sources += files(
'log.cc'
)

8
tests/main.cc Normal file
View File

@@ -0,0 +1,8 @@
#include "util/loglevel.hh"
#include <catch2/catch_session.hpp>
int
main(int argc, char* argv[]) {
matar::set_log_level(matar::LogLevel::Off);
return Catch::Session().run(argc, argv);
}

View File

@@ -4,11 +4,13 @@ tests_deps = [
src = include_directories('../src') src = include_directories('../src')
tests_sources = files() tests_sources = files(
'main.cc'
)
subdir('cpu') subdir('cpu')
catch2 = dependency('catch2-with-main', version: '>=3.4.0', static: true) catch2 = dependency('catch2', version: '>=3.4.0', static: true)
catch2_tests = executable( catch2_tests = executable(
'matar_tests', 'matar_tests',
tests_sources, tests_sources,