initialise a memory structure or smth

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2023-09-11 10:23:46 +05:30
parent 84c68a4e00
commit 332f0b87d6
26 changed files with 763 additions and 163 deletions

0
.gitmodules vendored Normal file
View File

View File

@@ -1,15 +1,74 @@
#include "emulator.hh" #include "memory.hh"
#include <array>
#include <cstdlib>
#include <fstream>
#include <iostream> #include <iostream>
#include <vector>
int int
main(int argc, const char* argv[]) { main(int argc, const char* argv[]) {
if (argc != 2) { auto usage = [argv]() {
std::cerr << "Usage: " << argv[0] << " <file>" << std::endl; std::cerr << "Usage: " << argv[0] << " <file> [-b <bios>]" << std::endl;
return 1; std::exit(EXIT_FAILURE);
};
if (argc < 2)
usage();
std::string rom_file, bios_file = "gba_bios.bin";
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-b") {
if (++i < argc)
bios_file = argv[i];
else
usage();
} else {
rom_file = arg;
}
} }
if (rom_file.empty())
usage();
try { try {
emulator::run(argv[1]); 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()) {
throw std::ios::failure("File not found", std::error_code());
}
rom.assign(std::istreambuf_iterator<char>(ifile),
std::istreambuf_iterator<char>());
ifile.close();
ifile.open(bios_file, std::ios::in | std::ios::binary);
if (!ifile.is_open()) {
throw std::ios::failure("BIOS file not found", std::error_code());
}
ifile.seekg(0, std::ios::end);
bios_size = ifile.tellg();
if (bios_size != Memory::BIOS_SIZE) {
throw std::ios::failure("BIOS file has invalid size",
std::error_code());
}
ifile.seekg(0, std::ios::beg);
ifile.read(reinterpret_cast<char*>(bios.data()), bios.size());
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;

View File

@@ -1,16 +0,0 @@
#ifndef EMULATOR_HH
#define EMULATOR_HH
// Why do I have a public API? We will know that in the future
#include <string>
namespace emulator {
void
run(std::string filepath);
void
run(std::ifstream& ifile);
}
#endif /* EMULATOR_HH */

1
include/header.hh Symbolic link
View File

@@ -0,0 +1 @@
../src/header.hh

1
include/memory.hh Symbolic link
View File

@@ -0,0 +1 @@
../src/memory.hh

View File

@@ -1,5 +1,6 @@
headers = files( headers = files(
'emulator.hh' 'memory.hh',
'header.hh'
) )
install_headers(headers, subdir: meson.project_name(), preserve_path: true) install_headers(headers, subdir: meson.project_name(), preserve_path: true)

4
src/bus.cc Normal file
View File

@@ -0,0 +1,4 @@
#include "bus.hh"
Bus::Bus(Memory&& memory)
: memory(std::move(memory)) {}

View File

@@ -1,14 +1,11 @@
#pragma once #pragma once
#include <fstream> #include "memory.hh"
#include <vector>
class Bus { class Bus {
std::vector<uint8_t> data;
public: public:
Bus() = default; Bus(Memory&& memory);
Bus(std::istream& ifile)
: data(std::istreambuf_iterator<char>(ifile), private:
std::istreambuf_iterator<char>()) {} Memory memory;
}; };

View File

@@ -1,21 +1,3 @@
enum class ArmInstructionFormat {
DataProcessingAndFsrTransfer,
Multiply,
MultiplyLong,
SingleDataSwap,
BranchAndExchange,
HalfwordDataTransferRegisterOffset,
HalfwordDataTransferImmediateOffset,
SingleDataTransfer,
Undefined,
BlockDataTransfer,
Branch,
CoprocessorDataTransfer,
CoprocessorDataOperation,
CoprocessorRegisterTransfer,
SoftwareInterrupt
};
enum class Condition { enum class Condition {
EQ = 0b0000, EQ = 0b0000,
NE = 0b0001, NE = 0b0001,
@@ -53,7 +35,7 @@ enum class OpCode {
MVN = 0b1111 MVN = 0b1111
}; };
enum class Shift { enum class ShiftType {
LSL = 0b00, LSL = 0b00,
LSR = 0b01, LSR = 0b01,
ASR = 0b10, ASR = 0b10,

View File

@@ -3,6 +3,19 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
Cpu::Cpu(Bus bus)
: gpr(0)
, cpsr(0)
, spsr(0)
, bus(bus)
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
, spsr_banked({ 0, 0, 0, 0, 0 }) {
cpsr.set_mode(Mode::System);
cpsr.set_irq_disabled(true);
cpsr.set_fiq_disabled(true);
cpsr.set_state(State::Arm);
}
/* change modes */ /* change modes */
void void
Cpu::chg_mode(Mode from, Mode to) { Cpu::chg_mode(Mode from, Mode to) {
@@ -92,13 +105,13 @@ Cpu::chg_mode(Mode from, Mode to) {
// set register // set register
inline uint32_t& inline uint32_t&
Cpu::operator[](size_t idx) { Cpu::operator[](uint8_t idx) {
// avoid unneeded complexity like index checks // avoid unneeded complexity like index checks
return gpr[idx]; return gpr[idx];
} }
// get register // get register
inline const uint32_t& inline const uint32_t&
Cpu::operator[](size_t idx) const { Cpu::operator[](uint8_t idx) const {
return gpr[idx]; return gpr[idx];
} }

View File

@@ -7,8 +7,11 @@
using std::size_t; using std::size_t;
static constexpr size_t GPR_VISIBLE_COUNT = 16; class Cpu {
public:
Cpu(Bus bus);
private:
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8; static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
static constexpr size_t GPR_FIQ_BANKED_COUNT = 7; static constexpr size_t GPR_FIQ_BANKED_COUNT = 7;
@@ -27,51 +30,34 @@ static constexpr size_t GPR_UND_BANKED_COUNT = 2;
static constexpr size_t GPR_SYS_USR_BANKED_FIRST = 8; static constexpr size_t GPR_SYS_USR_BANKED_FIRST = 8;
static constexpr size_t GPR_SYS_USR_BANKED_COUNT = 7; static constexpr size_t GPR_SYS_USR_BANKED_COUNT = 7;
struct _GprBanked { static constexpr size_t GPR_VISIBLE_COUNT = 16;
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
Psr cpsr; // current program status register
Psr spsr; // status program status register
Bus bus;
struct {
uint32_t fiq[GPR_FIQ_BANKED_COUNT]; uint32_t fiq[GPR_FIQ_BANKED_COUNT];
uint32_t svc[GPR_SVC_BANKED_COUNT]; uint32_t svc[GPR_SVC_BANKED_COUNT];
uint32_t abt[GPR_ABT_BANKED_COUNT]; uint32_t abt[GPR_ABT_BANKED_COUNT];
uint32_t irq[GPR_IRQ_BANKED_COUNT]; uint32_t irq[GPR_IRQ_BANKED_COUNT];
uint32_t und[GPR_UND_BANKED_COUNT]; uint32_t und[GPR_UND_BANKED_COUNT];
/* visible registers before the mode switch */ // visible registers before the mode switch
uint32_t old[GPR_SYS_USR_BANKED_COUNT]; uint32_t old[GPR_SYS_USR_BANKED_COUNT];
}; } gpr_banked; // banked general purpose registers
typedef struct _GprBanked GprBanked;
struct _SpsrBanked { struct {
Psr fiq; Psr fiq;
Psr svc; Psr svc;
Psr abt; Psr abt;
Psr irq; Psr irq;
Psr und; Psr und;
}; } spsr_banked; // banked saved program status registers
typedef struct _SpsrBanked SpsrBanked;
class Cpu {
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
GprBanked gpr_banked; // banked general purpose registers
SpsrBanked spsr_banked; // banked saved program status registers
Psr cpsr; // current program status register
Psr spsr; // status program status register
Bus bus;
void chg_mode(Mode from, Mode to); void chg_mode(Mode from, Mode to);
uint32_t& operator[](size_t idx); uint32_t& operator[](uint8_t idx);
const uint32_t& operator[](size_t idx) const; const uint32_t& operator[](uint8_t idx) const;
public:
Cpu(Bus bus)
: gpr(0)
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
, spsr_banked({ 0, 0, 0, 0, 0 })
, cpsr(0)
, spsr(0)
, bus(bus) {
cpsr.set_mode(Mode::System);
cpsr.set_irq_disabled(true);
cpsr.set_fiq_disabled(true);
cpsr.set_state(State::Arm);
}
}; };

View File

@@ -1,5 +1,6 @@
lib_sources += files( lib_sources += files(
'cpu.cc' 'cpu.cc',
'psr.cc'
) )
subdir('arm') subdir('arm')

49
src/cpu/psr.cc Normal file
View File

@@ -0,0 +1,49 @@
#include "psr.hh"
#include "util/bits.hh"
Psr::Psr(uint32_t raw) {
psr = raw & PSR_CLEAR_RESERVED;
}
Mode
Psr::mode() const {
return static_cast<Mode>(psr & ~PSR_CLEAR_MODE);
}
void
Psr::set_mode(Mode mode) {
psr &= PSR_CLEAR_MODE;
psr |= static_cast<uint32_t>(mode);
}
bool
Psr::state() const {
return get_nth_bit(psr, 5);
}
void
Psr::set_state(State state) {
chg_nth_bit(psr, 5, static_cast<bool>(state));
}
#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \
bool Psr::name() const { \
return get_nth_bit(psr, n); \
} \
void Psr::set_##name(bool val) { \
chg_nth_bit(psr, n, val); \
}
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6)
GET_SET_NTH_BIT_FUNCTIONS(irq_disabled, 7)
GET_SET_NTH_BIT_FUNCTIONS(v, 28);
GET_SET_NTH_BIT_FUNCTIONS(c, 29);
GET_SET_NTH_BIT_FUNCTIONS(z, 30);
GET_SET_NTH_BIT_FUNCTIONS(n, 31);
#undef GET_SET_NTH_BIT_FUNCTIONS

View File

@@ -1,55 +1,51 @@
#pragma once #pragma once
#include "bits.hh" #include "util/bits.hh"
#include "utility.hh" #include "utility.hh"
#include <cstdint> #include <cstdint>
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
static constexpr uint32_t PSR_CLEAR_MODE = 0x0b00000;
class Psr { class Psr {
uint32_t psr;
public: public:
// clear the reserved bits i.e, [8:27] // clear the reserved bits i.e, [8:27]
Psr(uint32_t raw) { psr = raw & PSR_CLEAR_RESERVED; } Psr(uint32_t raw);
// Mode : [4:0] // Mode : [4:0]
Mode mode() const { return static_cast<Mode>(psr & ~PSR_CLEAR_MODE); } Mode mode() const;
void set_mode(Mode mode) { void set_mode(Mode mode);
psr &= PSR_CLEAR_MODE;
psr |= static_cast<uint32_t>(mode);
}
// State : [5] // State : [5]
bool state() const { return get_nth_bit(psr, 5); } bool state() const;
void set_state(State state) { void set_state(State state);
chg_nth_bit(psr, 5, static_cast<bool>(state));
}
#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \ #define GET_SET_NTH_BIT_FUNCTIONS(name) \
bool name() const { return get_nth_bit(psr, n); } \ bool name() const; \
void set_##name(bool val) { chg_nth_bit(psr, n, val); } void set_##name(bool val);
// FIQ disable : [6] // FIQ disable : [6]
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6) GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled)
// IRQ disable : [7] // IRQ disable : [7]
GET_SET_NTH_BIT_FUNCTIONS(irq_disabled, 7) GET_SET_NTH_BIT_FUNCTIONS(irq_disabled)
// Reserved bits : [27:8] // Reserved bits : [27:8]
// Overflow flag : [28] // Overflow flag : [28]
GET_SET_NTH_BIT_FUNCTIONS(v, 28); GET_SET_NTH_BIT_FUNCTIONS(v)
// Carry flag : [29] // Carry flag : [29]
GET_SET_NTH_BIT_FUNCTIONS(c, 29); GET_SET_NTH_BIT_FUNCTIONS(c)
// Zero flag : [30] // Zero flag : [30]
GET_SET_NTH_BIT_FUNCTIONS(z, 30); GET_SET_NTH_BIT_FUNCTIONS(z)
// Negative flag : [30] // Negative flag : [30]
GET_SET_NTH_BIT_FUNCTIONS(n, 31); GET_SET_NTH_BIT_FUNCTIONS(n)
#undef GET_SET_NTH_BIT_FUNCTIONS #undef GET_SET_NTH_BIT_FUNCTIONS
private:
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
static constexpr uint32_t PSR_CLEAR_MODE = 0x0b00000;
uint32_t psr;
}; };

View File

@@ -1,24 +0,0 @@
#include "emulator.hh"
#include "bus.hh"
#include "cpu/cpu.hh"
#include <fstream>
namespace emulator {
void
run(std::ifstream& ifile) {
Bus bus(ifile);
Cpu cpu(bus);
}
void
run(std::string filepath) {
std::ifstream ifile(filepath, std::ios::in | std::ios::binary);
if (!ifile.is_open()) {
throw std::ios::failure("No such file exists", std::error_code());
}
run(ifile);
ifile.close();
}
}

44
src/header.hh Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <cstdint>
#include <vector>
typedef struct {
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;
char title[12];
UniqueCode unique_code;
char title_code[2];
I18n i18n;
uint8_t version;
BootMode multiboot;
uint32_t multiboot_entrypoint;
uint8_t slave_id;
} Header;

223
src/memory.cc Normal file
View File

@@ -0,0 +1,223 @@
#include "memory.hh"
#include "header.hh"
#include "util/log.hh"
#include "util/utils.hh"
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
std::vector<uint8_t>&& rom) noexcept
: bios(std::move(bios))
, board_wram(0)
, chip_wram(0)
, palette_ram(0)
, vram(0)
, oam_obj_attr(0)
, rom(std::move(rom)) {
std::string bios_hash = crypto::sha256(bios.data(), bios.size());
std::string expected_hash =
"fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
if (bios_hash != expected_hash) {
log_warn("BIOS hash failed to match, run at your own risk",
"\nExpected : ",
expected_hash,
"\nGot : ",
bios_hash);
}
parse_header();
log("Memory successfully initialised");
};
#define MATCHES(area) address >= area##_START&& address <= area##_END
uint8_t
Memory::read(size_t address) const {
if (MATCHES(BIOS)) {
return bios[address];
} else if (MATCHES(BOARD_WRAM)) {
return board_wram[address - BOARD_WRAM_START];
} else if (MATCHES(CHIP_WRAM)) {
return chip_wram[address - CHIP_WRAM_START];
} else if (MATCHES(PALETTE_RAM)) {
return palette_ram[address - PALETTE_RAM_START];
} else if (MATCHES(VRAM)) {
return vram[address - VRAM_START];
} else if (MATCHES(OAM_OBJ_ATTR)) {
return oam_obj_attr[address - OAM_OBJ_ATTR_START];
} else if (MATCHES(ROM_0)) {
return rom[address - ROM_0_START];
} else if (MATCHES(ROM_1)) {
return rom[address - ROM_1_START];
} else if (MATCHES(ROM_2)) {
return rom[address - ROM_2_START];
} else {
log_error("Invalid memory region accessed");
return 0;
}
}
void
Memory::write(size_t address, uint8_t byte) {
if (MATCHES(BIOS)) {
bios[address] = byte;
} else if (MATCHES(BOARD_WRAM)) {
board_wram[address - BOARD_WRAM_START] = byte;
} else if (MATCHES(CHIP_WRAM)) {
chip_wram[address - CHIP_WRAM_START] = byte;
} else if (MATCHES(PALETTE_RAM)) {
palette_ram[address - PALETTE_RAM_START] = byte;
} else if (MATCHES(VRAM)) {
vram[address - VRAM_START] = byte;
} else if (MATCHES(OAM_OBJ_ATTR)) {
oam_obj_attr[address - OAM_OBJ_ATTR_START] = byte;
} else if (MATCHES(ROM_0)) {
rom[address - ROM_0_START] = byte;
} else if (MATCHES(ROM_1)) {
rom[address - ROM_1_START] = byte;
} else if (MATCHES(ROM_2)) {
rom[address - ROM_2_START] = byte;
} else {
log_error("Invalid memory region accessed");
}
}
#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 halfword) {
if (address & 0b11)
log_warn("Writing to a non aligned word address");
write(address, halfword & 0xFF);
write(address + 1, halfword >> 8 & 0xFF);
write(address + 2, halfword >> 16 & 0xFF);
write(address + 3, halfword >> 24 & 0xFF);
}
void
Memory::parse_header() {
// entrypoint
header.entrypoint =
rom[0x00] | rom[0x01] << 8 | rom[0x02] << 16 | rom[0x03] << 24;
// nintendo logo
if (rom[0x9C] != 0x21)
log_info("HEADER: BIOS debugger bits not set to 0");
// game info
std::copy(&rom[0xA0], &rom[0xA0 + 12], header.title);
switch (rom[0xAC]) {
case 'A':
header.unique_code = Header::UniqueCode::Old;
break;
case 'B':
header.unique_code = Header::UniqueCode::New;
break;
case 'C':
header.unique_code = Header::UniqueCode::Newer;
break;
case 'F':
header.unique_code = Header::UniqueCode::Famicom;
break;
case 'K':
header.unique_code = Header::UniqueCode::YoshiKoro;
break;
case 'P':
header.unique_code = Header::UniqueCode::Ereader;
break;
case 'R':
header.unique_code = Header::UniqueCode::Warioware;
break;
case 'U':
header.unique_code = Header::UniqueCode::Boktai;
break;
case 'V':
header.unique_code = Header::UniqueCode::DrillDozer;
break;
default:
log_error("HEADER: invalid unique code: ", rom[0xAC]);
}
header.title_code[0] = rom[0xAD];
header.title_code[1] = rom[0xAE];
switch (rom[0xAF]) {
case 'J':
header.i18n = Header::I18n::Japan;
break;
case 'P':
header.i18n = Header::I18n::Europe;
break;
case 'F':
header.i18n = Header::I18n::French;
break;
case 'S':
header.i18n = Header::I18n::Spanish;
break;
case 'E':
header.i18n = Header::I18n::Usa;
break;
case 'D':
header.i18n = Header::I18n::German;
break;
case 'I':
header.i18n = Header::I18n::Italian;
break;
default:
log_error("HEADER: invalid destination/language - ", rom[0xAF]);
}
if (rom[0xB2] != 0x96)
log_error("HEADER: invalid fixed byte at 0xB2");
for (size_t i = 0xB5; i < 0xBC; i++) {
if (rom[i] != 0x00)
log_error("HEADER: invalid fixed bytes at 0xB5");
}
header.version = rom[0xBC];
// checksum
{
size_t i = 0xA0, chk = 0;
while (i <= 0xBC)
chk -= rom[i++];
chk -= 0x19;
chk &= 0xFF;
if (chk != rom[0xBD])
log_error("HEADER: checksum does not match");
}
// multiboot not required right now
}

64
src/memory.hh Normal file
View 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) \
uint8_t ident[name##_END - name##_START + 1];
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();
};

View File

@@ -1,13 +1,14 @@
lib_sources = files( lib_sources = files(
'emulator.cc' 'memory.cc',
'bus.cc'
) )
subdir('util')
subdir('cpu') subdir('cpu')
lib = library( lib = library(
meson.project_name(), meson.project_name(),
lib_sources, lib_sources,
include_directories: inc,
install: true install: true
) )

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

@@ -0,0 +1,49 @@
#include "log.hh"
namespace logging {
namespace ansi {
static const char* RED = "\033[31m";
static const char* YELLOW = "\033[33m";
static const char* MAGENTA = "\033[35m";
static const char* WHITE = "\033[37m";
static const char* BOLD = "\033[1m";
static const char* RESET = "\033[0m";
}
Logger::Logger(std::ostream& os)
: os(os){};
void
Logger::set_level(Level level) {
switch (level) {
case Level::Debug:
os << ansi::MAGENTA << ansi::BOLD << "[DEBUG] ";
break;
case Level::Info:
os << ansi::WHITE << "[INFO] ";
break;
case Level::Warn:
os << ansi::YELLOW << "[WARN] ";
break;
case Level::Error:
os << ansi::RED << ansi::BOLD << "[ERROR] ";
break;
// unreachable
default: {
}
}
}
void
Logger::reset_level() {
os << ansi::RESET;
}
}
// Global logger
logging::Logger logger;

42
src/util/log.hh Normal file
View File

@@ -0,0 +1,42 @@
#pragma once
#include <iomanip>
#include <iostream>
#include <source_location>
#include <streambuf>
namespace logging {
enum class Level {
Debug,
Info,
Warn,
Error,
};
class Logger {
public:
Logger(std::ostream& os = std::clog);
template<typename... Args>
void print(Level level, Args&&... args) {
set_level(level);
(os << ... << args);
reset_level();
os << std::endl;
}
private:
std::ostream& os;
void set_level(Level level);
void reset_level();
};
}
extern logging::Logger logger;
#define log_debug(...) logger.print(logging::Level::Debug, __VA_ARGS__)
#define log_info(...) logger.print(logging::Level::Info, __VA_ARGS__)
#define log_warn(...) logger.print(logging::Level::Warn, __VA_ARGS__)
#define log_error(...) logger.print(logging::Level::Error, __VA_ARGS__)
#define log(...) log_info(__VA_ARGS__)
#define debug(value) log_debug(#value, " = ", value)

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

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

113
src/util/utils.cc Normal file
View File

@@ -0,0 +1,113 @@
#include "utils.hh"
#include <bit>
#include <iomanip>
#include <sstream>
#include <vector>
namespace crypto {
using std::rotr;
std::string
sha256(const uint8_t* data, const size_t size) {
// Assuming 1 byte = 8 bits
std::stringstream string;
size_t k = 512 - (size * 8 + 65) % 512;
size_t L = size + (65 + k) / 8;
size_t i, j;
bool c = 0;
static constexpr uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
uint32_t h[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
for (i = 0; i < L; i += 64) {
size_t n = (size > i ? size - i : 0);
uint32_t h0[8];
uint32_t w[64] = { 0 };
if (n == 64)
c = 1;
if (n >= 64) {
for (j = 0; j < 16; j++) {
w[j] = data[i + j * 4] << 24 | data[i + j * 4 + 1] << 16 |
data[i + j * 4 + 2] << 8 | data[i + j * 4 + 3];
}
} else {
uint8_t cur[64] = { 0 };
if (n) {
std::copy(data + i, data + size, cur);
cur[n] = 0x80;
} else if (c) {
cur[n] = 0x80;
}
if (n < 54) {
for (j = 56; j < 64; j++) {
cur[j] = (size * 8 >> ((63 - j) * 8)) & 0xFF;
}
}
for (j = 0; j < 16; j++) {
w[j] = cur[j * 4] << 24 | cur[j * 4 + 1] << 16 |
cur[j * 4 + 2] << 8 | cur[j * 4 + 3];
}
}
for (j = 16; j < 64; j++) {
uint32_t s0 =
rotr(w[j - 15], 7) ^ rotr(w[j - 15], 18) ^ (w[j - 15] >> 3);
uint32_t s1 =
rotr(w[j - 2], 17) ^ rotr(w[j - 2], 19) ^ (w[j - 2] >> 10);
w[j] = w[j - 16] + w[j - 7] + s0 + s1;
}
std::copy(h, h + 8, h0);
for (j = 0; j < 64; j++) {
uint32_t s1 = rotr(h0[4], 6) ^ rotr(h0[4], 11) ^ rotr(h0[4], 25);
uint32_t ch = (h0[4] & h0[5]) ^ (~h0[4] & h0[6]);
uint32_t t1 = h0[7] + s1 + ch + K[j] + w[j];
uint32_t s0 = rotr(h0[0], 2) ^ rotr(h0[0], 13) ^ rotr(h0[0], 22);
uint32_t maj = (h0[0] & h0[1]) ^ (h0[0] & h0[2]) ^ (h0[1] & h0[2]);
uint32_t t2 = s0 + maj;
h0[7] = h0[6];
h0[6] = h0[5];
h0[5] = h0[4];
h0[4] = h0[3] + t1;
h0[3] = h0[2];
h0[2] = h0[1];
h0[1] = h0[0];
h0[0] = t1 + t2;
}
for (j = 0; j < 8; j++)
h[j] += h0[j];
}
string << std::hex << std::setfill('0');
for (j = 0; j < 8; j++)
for (i = 0; i < 4; i++)
string << std::setw(2) << ((h[j] >> (24 - i * 8)) & 0xFF);
return string.str();
}
}

10
src/util/utils.hh Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
// Why I wrote this myself? I do not know
// I will off myself 😹😹😹😹
namespace crypto {
std::string
sha256(const uint8_t* data, const size_t size);
}