#pragma once #include #include #include #include #include #include #include #include #include #include "termcolor.hpp" // We have to detect and special-case AppleClang at the moment since its C++20 support is finicky and doesn't quite support std::bit_cast #if defined(__clang__) && defined(__apple_build_version__) #define HELPERS_APPLE_CLANG #else #include #endif using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; using usize = std::size_t; using uint = unsigned int; using s8 = std::int8_t; using s16 = std::int16_t; using s32 = std::int32_t; using s64 = std::int64_t; namespace Helpers { [[noreturn]] static void panic(const char* fmt, ...) { std::va_list args; va_start(args, fmt); std::cout << termcolor::on_red << "[FATAL] "; std::vprintf(fmt, args); std::cout << termcolor::reset << "\n"; va_end(args); exit(1); } static void warn(const char* fmt, ...) { std::va_list args; va_start(args, fmt); std::cout << termcolor::on_red << "[Warning] "; std::vprintf(fmt, args); std::cout << termcolor::reset << "\n"; va_end(args); } static std::vector loadROM(std::string directory) { std::ifstream file(directory, std::ios::binary); if (file.fail()) panic("Couldn't read %s", directory.c_str()); std::vector ROM; file.unsetf(std::ios::skipws); ROM.insert(ROM.begin(), std::istream_iterator(file), std::istream_iterator()); file.close(); printf("%s loaded successfully\n", directory.c_str()); return ROM; } static constexpr bool buildingInDebugMode() { #ifdef NDEBUG return false; #endif return true; } static void debug_printf(const char* fmt, ...) { if constexpr (buildingInDebugMode()) { std::va_list args; va_start(args, fmt); std::vprintf(fmt, args); va_end(args); } } /// Sign extend an arbitrary-size value to 32 bits static constexpr u32 inline signExtend32(u32 value, u32 startingSize) { auto temp = (s32)value; auto bitsToShift = 32 - startingSize; return (u32)(temp << bitsToShift >> bitsToShift); } /// Sign extend an arbitrary-size value to 16 bits static constexpr u16 signExtend16(u16 value, u32 startingSize) { auto temp = (s16)value; auto bitsToShift = 16 - startingSize; return (u16)(temp << bitsToShift >> bitsToShift); } /// Create a mask with `count` number of one bits. template static constexpr T ones() { constexpr usize bitsize = CHAR_BIT * sizeof(T); static_assert(count <= bitsize, "count larger than bitsize of T"); if (count == T(0)) { return T(0); } return static_cast(~static_cast(0)) >> (bitsize - count); } /// Extract bits from an integer-type template static constexpr T getBit(T value) { return (value >> offset) & T(1); } /// Extract bits from an integer-type template static constexpr T getBits(T value) { return (value >> offset) & ones(); } /// Check if a bit "bit" of value is set static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; } /// rotate number right template static constexpr T rotr(T value, int bits) { constexpr auto bitWidth = sizeof(T) * 8; bits &= bitWidth - 1; return (value >> bits) | (value << (bitWidth - bits)); } // rotate number left template static constexpr T rotl(T value, int bits) { constexpr auto bitWidth = sizeof(T) * 8; bits &= bitWidth - 1; return (value << bits) | (value >> (bitWidth - bits)); } /// Used to make the compiler evaluate beeg loops at compile time for the tablegen template static constexpr void static_for_impl(Func&& f, std::integer_sequence) { (f(std::integral_constant{}), ...); } template static constexpr void static_for(Func&& f) { static_for_impl(std::forward(f), std::make_integer_sequence{}); } // For values < 0x99 static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; } #ifdef HELPERS_APPLE_CLANG template constexpr To bit_cast(const From& from) noexcept { return *reinterpret_cast(&from); } #else template constexpr To bit_cast(const From& from) noexcept { return std::bit_cast(from); } #endif }; // namespace Helpers // UDLs for memory size values constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; } constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; } constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; } // useful macros // likely/unlikely #ifdef __GNUC__ #define likely(x) __builtin_expect((x), 1) #define unlikely(x) __builtin_expect((x), 0) #else #define likely(x) (x) #define unlikely(x) (x) #endif