diff --git a/.clang-format b/.clang-format index c15ab69..9fd9583 100644 --- a/.clang-format +++ b/.clang-format @@ -4,4 +4,4 @@ BreakBeforeBraces: Attach AlwaysBreakTemplateDeclarations: Yes AlignConsecutiveAssignments: Consecutive BreakAfterAttributes: Always -AllowShortEnumsOnASingleLine: False \ No newline at end of file +AllowShortEnumsOnASingleLine: False diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/apps/target/main.cc b/apps/target/main.cc index 22a3981..76e7b5d 100644 --- a/apps/target/main.cc +++ b/apps/target/main.cc @@ -1,15 +1,74 @@ -#include "emulator.hh" +#include "memory.hh" +#include +#include +#include #include +#include int main(int argc, const char* argv[]) { - if (argc != 2) { - std::cerr << "Usage: " << argv[0] << " " << std::endl; - return 1; + auto usage = [argv]() { + std::cerr << "Usage: " << argv[0] << " [-b ]" << std::endl; + 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 { - emulator::run(argv[1]); + std::ifstream ifile(rom_file, std::ios::in | std::ios::binary); + std::vector rom; + std::array bios; + std::streampos bios_size; + + if (!ifile.is_open()) { + throw std::ios::failure("File not found", std::error_code()); + } + + rom.assign(std::istreambuf_iterator(ifile), + std::istreambuf_iterator()); + + 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(bios.data()), bios.size()); + + ifile.close(); + + Memory memory(std::move(bios), std::move(rom)); } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; return 1; diff --git a/include/emulator.hh b/include/emulator.hh deleted file mode 100644 index d6b8cb4..0000000 --- a/include/emulator.hh +++ /dev/null @@ -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 - -namespace emulator { -void -run(std::string filepath); - -void -run(std::ifstream& ifile); -} - -#endif /* EMULATOR_HH */ diff --git a/include/header.hh b/include/header.hh new file mode 120000 index 0000000..dfac418 --- /dev/null +++ b/include/header.hh @@ -0,0 +1 @@ +../src/header.hh \ No newline at end of file diff --git a/include/memory.hh b/include/memory.hh new file mode 120000 index 0000000..638573c --- /dev/null +++ b/include/memory.hh @@ -0,0 +1 @@ +../src/memory.hh \ No newline at end of file diff --git a/include/meson.build b/include/meson.build index f80dd7f..4f332a5 100644 --- a/include/meson.build +++ b/include/meson.build @@ -1,5 +1,6 @@ headers = files( - 'emulator.hh' + 'memory.hh', + 'header.hh' ) install_headers(headers, subdir: meson.project_name(), preserve_path: true) \ No newline at end of file diff --git a/src/bus.cc b/src/bus.cc new file mode 100644 index 0000000..24cc819 --- /dev/null +++ b/src/bus.cc @@ -0,0 +1,4 @@ +#include "bus.hh" + +Bus::Bus(Memory&& memory) + : memory(std::move(memory)) {} diff --git a/src/bus.hh b/src/bus.hh index 0890f51..e79f65d 100644 --- a/src/bus.hh +++ b/src/bus.hh @@ -1,14 +1,11 @@ #pragma once -#include -#include +#include "memory.hh" class Bus { - std::vector data; - public: - Bus() = default; - Bus(std::istream& ifile) - : data(std::istreambuf_iterator(ifile), - std::istreambuf_iterator()) {} + Bus(Memory&& memory); + + private: + Memory memory; }; diff --git a/src/cpu/arm/insruction.hh b/src/cpu/arm/insruction.hh index 0d84f6e..f4083ce 100644 --- a/src/cpu/arm/insruction.hh +++ b/src/cpu/arm/insruction.hh @@ -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 { EQ = 0b0000, NE = 0b0001, @@ -53,7 +35,7 @@ enum class OpCode { MVN = 0b1111 }; -enum class Shift { +enum class ShiftType { LSL = 0b00, LSR = 0b01, ASR = 0b10, diff --git a/src/cpu/cpu.cc b/src/cpu/cpu.cc index b92d78a..b14e74b 100644 --- a/src/cpu/cpu.cc +++ b/src/cpu/cpu.cc @@ -3,6 +3,19 @@ #include #include +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 */ void Cpu::chg_mode(Mode from, Mode to) { @@ -92,13 +105,13 @@ Cpu::chg_mode(Mode from, Mode to) { // set register inline uint32_t& -Cpu::operator[](size_t idx) { +Cpu::operator[](uint8_t idx) { // avoid unneeded complexity like index checks return gpr[idx]; } // get register inline const uint32_t& -Cpu::operator[](size_t idx) const { +Cpu::operator[](uint8_t idx) const { return gpr[idx]; } diff --git a/src/cpu/cpu.hh b/src/cpu/cpu.hh index 1d8d471..a93fd6f 100644 --- a/src/cpu/cpu.hh +++ b/src/cpu/cpu.hh @@ -7,71 +7,57 @@ using std::size_t; -static constexpr size_t GPR_VISIBLE_COUNT = 16; - -static constexpr size_t GPR_FIQ_BANKED_FIRST = 8; -static constexpr size_t GPR_FIQ_BANKED_COUNT = 7; - -static constexpr size_t GPR_SVC_BANKED_FIRST = 13; -static constexpr size_t GPR_SVC_BANKED_COUNT = 2; - -static constexpr size_t GPR_ABT_BANKED_FIRST = 13; -static constexpr size_t GPR_ABT_BANKED_COUNT = 2; - -static constexpr size_t GPR_IRQ_BANKED_FIRST = 13; -static constexpr size_t GPR_IRQ_BANKED_COUNT = 2; - -static constexpr size_t GPR_UND_BANKED_FIRST = 13; -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_COUNT = 7; - -struct _GprBanked { - uint32_t fiq[GPR_FIQ_BANKED_COUNT]; - uint32_t svc[GPR_SVC_BANKED_COUNT]; - uint32_t abt[GPR_ABT_BANKED_COUNT]; - uint32_t irq[GPR_IRQ_BANKED_COUNT]; - uint32_t und[GPR_UND_BANKED_COUNT]; - - /* visible registers before the mode switch */ - uint32_t old[GPR_SYS_USR_BANKED_COUNT]; -}; -typedef struct _GprBanked GprBanked; - -struct _SpsrBanked { - Psr fiq; - Psr svc; - Psr abt; - Psr irq; - Psr und; -}; -typedef struct _SpsrBanked SpsrBanked; - class Cpu { + public: + Cpu(Bus bus); + + private: + static constexpr size_t GPR_FIQ_BANKED_FIRST = 8; + static constexpr size_t GPR_FIQ_BANKED_COUNT = 7; + + static constexpr size_t GPR_SVC_BANKED_FIRST = 13; + static constexpr size_t GPR_SVC_BANKED_COUNT = 2; + + static constexpr size_t GPR_ABT_BANKED_FIRST = 13; + static constexpr size_t GPR_ABT_BANKED_COUNT = 2; + + static constexpr size_t GPR_IRQ_BANKED_FIRST = 13; + static constexpr size_t GPR_IRQ_BANKED_COUNT = 2; + + static constexpr size_t GPR_UND_BANKED_FIRST = 13; + 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_COUNT = 7; + + static constexpr size_t GPR_VISIBLE_COUNT = 16; + 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; + struct { + uint32_t fiq[GPR_FIQ_BANKED_COUNT]; + uint32_t svc[GPR_SVC_BANKED_COUNT]; + uint32_t abt[GPR_ABT_BANKED_COUNT]; + uint32_t irq[GPR_IRQ_BANKED_COUNT]; + uint32_t und[GPR_UND_BANKED_COUNT]; + + // visible registers before the mode switch + uint32_t old[GPR_SYS_USR_BANKED_COUNT]; + } 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(Mode from, Mode to); - uint32_t& operator[](size_t idx); - const uint32_t& operator[](size_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); - } + uint32_t& operator[](uint8_t idx); + const uint32_t& operator[](uint8_t idx) const; }; diff --git a/src/cpu/psr.cc b/src/cpu/psr.cc new file mode 100644 index 0000000..e9afec2 --- /dev/null +++ b/src/cpu/psr.cc @@ -0,0 +1,49 @@ +#include "psr.hh" +#include "bits.hh" + +Psr::Psr(uint32_t raw) { + psr = raw & PSR_CLEAR_RESERVED; +} + +Mode +Psr::mode() const { + return static_cast(psr & ~PSR_CLEAR_MODE); +} + +void +Psr::set_mode(Mode mode) { + psr &= PSR_CLEAR_MODE; + psr |= static_cast(mode); +} + +bool +Psr::state() const { + return get_nth_bit(psr, 5); +} + +void +Psr::set_state(State state) { + chg_nth_bit(psr, 5, static_cast(state)); +} + +#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \ + inline bool Psr::name() const { \ + return get_nth_bit(psr, n); \ + } \ + inline 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 diff --git a/src/cpu/psr.hh b/src/cpu/psr.hh index 1726f9d..adccee8 100644 --- a/src/cpu/psr.hh +++ b/src/cpu/psr.hh @@ -1,15 +1,10 @@ #pragma once -#include "bits.hh" +#include "util/bits.hh" #include "utility.hh" #include -static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff; -static constexpr uint32_t PSR_CLEAR_MODE = 0x0b00000; - class Psr { - uint32_t psr; - public: // clear the reserved bits i.e, [8:27] Psr(uint32_t raw) { psr = raw & PSR_CLEAR_RESERVED; } @@ -52,4 +47,10 @@ class Psr { GET_SET_NTH_BIT_FUNCTIONS(n, 31); #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; }; diff --git a/src/emulator.cc b/src/emulator.cc deleted file mode 100644 index fa50829..0000000 --- a/src/emulator.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include "emulator.hh" -#include "bus.hh" -#include "cpu/cpu.hh" -#include - -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(); -} -} diff --git a/src/header.hh b/src/header.hh new file mode 100644 index 0000000..11aae03 --- /dev/null +++ b/src/header.hh @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +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; diff --git a/src/memory.cc b/src/memory.cc new file mode 100644 index 0000000..c4b9b40 --- /dev/null +++ b/src/memory.cc @@ -0,0 +1,229 @@ +#include "memory.hh" +#include "header.hh" +#include "util/log.hh" +#include "util/utils.hh" + +Memory::Memory(std::array&& bios, + std::vector&& 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 +} diff --git a/src/memory.hh b/src/memory.hh new file mode 100644 index 0000000..98120c8 --- /dev/null +++ b/src/memory.hh @@ -0,0 +1,64 @@ +#pragma once + +#include "header.hh" +#include +#include +#include +#include + +class Memory { + public: + static constexpr size_t BIOS_SIZE = 1024 * 16; + + Memory(std::array&& bios, + std::vector&& 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 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 rom; + Header header; + void parse_header(); +}; diff --git a/src/meson.build b/src/meson.build index 59981c7..68d9c0f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,13 +1,14 @@ lib_sources = files( - 'emulator.cc' + 'memory.cc', + 'bus.cc' ) +subdir('util') subdir('cpu') lib = library( meson.project_name(), lib_sources, - include_directories: inc, install: true ) diff --git a/src/bits.hh b/src/util/bits.hh similarity index 100% rename from src/bits.hh rename to src/util/bits.hh diff --git a/src/util/log.cc b/src/util/log.cc new file mode 100644 index 0000000..bafd2c6 --- /dev/null +++ b/src/util/log.cc @@ -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; diff --git a/src/util/log.hh b/src/util/log.hh new file mode 100644 index 0000000..4ae43ec --- /dev/null +++ b/src/util/log.hh @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include + +namespace logging { +enum class Level { + Debug, + Info, + Warn, + Error, +}; + +class Logger { + public: + Logger(std::ostream& os = std::clog); + + template + 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) diff --git a/src/util/meson.build b/src/util/meson.build new file mode 100644 index 0000000..2a1fcbe --- /dev/null +++ b/src/util/meson.build @@ -0,0 +1,4 @@ +lib_sources += files( + 'log.cc', + 'utils.cc' +) \ No newline at end of file diff --git a/src/util/utils.cc b/src/util/utils.cc new file mode 100644 index 0000000..cf6731c --- /dev/null +++ b/src/util/utils.cc @@ -0,0 +1,113 @@ +#include "utils.hh" +#include +#include +#include +#include + +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(); +} +} diff --git a/src/util/utils.hh b/src/util/utils.hh new file mode 100644 index 0000000..4731768 --- /dev/null +++ b/src/util/utils.hh @@ -0,0 +1,10 @@ +#pragma once + +#include + +// 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); +}