diff --git a/include/helpers.hpp b/include/helpers.hpp
index fc3c0167..853e487b 100644
--- a/include/helpers.hpp
+++ b/include/helpers.hpp
@@ -1,6 +1,6 @@
 #pragma once
-#include <cstdarg>
 #include <climits>
+#include <cstdarg>
 #include <cstdint>
 #include <fstream>
 #include <iostream>
@@ -8,8 +8,16 @@
 #include <type_traits>
 #include <utility>
 #include <vector>
+
 #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 <bit>
+#endif
+
 using u8 = std::uint8_t;
 using u16 = std::uint16_t;
 using u32 = std::uint32_t;
@@ -23,78 +31,74 @@ 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);
+	[[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);
-    }
+		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 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 <u8> loadROM(std::string directory) {
-        std::ifstream file (directory, std::ios::binary);
-        if (file.fail())
-            panic("Couldn't read %s", directory.c_str());
+	static std::vector<u8> loadROM(std::string directory) {
+		std::ifstream file(directory, std::ios::binary);
+		if (file.fail()) panic("Couldn't read %s", directory.c_str());
 
-        std::vector<u8> ROM;
+		std::vector<u8> ROM;
 
-        file.unsetf(std::ios::skipws);
-        ROM.insert(ROM.begin(),
-                  std::istream_iterator<uint8_t>(file),
-                   std::istream_iterator<uint8_t>());
+		file.unsetf(std::ios::skipws);
+		ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
 
-        file.close();
+		file.close();
 
-        printf ("%s loaded successfully\n", directory.c_str());
-        return ROM;
-    }
+		printf("%s loaded successfully\n", directory.c_str());
+		return ROM;
+	}
 
-    static constexpr bool buildingInDebugMode() {
-        #ifdef NDEBUG
-            return false;
-         #endif
+	static constexpr bool buildingInDebugMode() {
+#ifdef NDEBUG
+		return false;
+#endif
+		return true;
+	}
 
-         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);
-        }
-    }
+	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 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);
-    }
+	/// 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<typename T, usize count>
-	static constexpr T ones () {
+	template <typename T, usize count>
+	static constexpr T ones() {
 		constexpr usize bitsize = CHAR_BIT * sizeof(T);
 		static_assert(count <= bitsize, "count larger than bitsize of T");
 
@@ -105,80 +109,74 @@ namespace Helpers {
 	}
 
 	/// Extract bits from an integer-type
-	template<usize offset, typename T>
-	static constexpr T getBit (T value)	{
-		return (value >> offset) & T(1); 
+	template <usize offset, typename T>
+	static constexpr T getBit(T value) {
+		return (value >> offset) & T(1);
 	}
 
 	/// Extract bits from an integer-type
-	template<usize offset, usize bits, typename T>
-	static constexpr T getBits (T value) {
-		return (value >> offset) & ones<T, bits>(); 
+	template <usize offset, usize bits, typename T>
+	static constexpr T getBits(T value) {
+		return (value >> offset) & ones<T, bits>();
 	}
 
-    /// Check if a bit "bit" of value is set
-    static constexpr bool isBitSet (u32 value, int bit) {
-        return (value >> bit) & 1;
-    }
+	/// 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 <typename T>
-    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 right
+	template <typename T>
+	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 <typename T>
-    static constexpr T rotl (T value, int bits) {
-        constexpr auto bitWidth = sizeof(T) * 8;
-        bits &= bitWidth - 1;
-        return (value << bits) | (value >> (bitWidth - bits));
-    }
+	// rotate number left
+	template <typename T>
+	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 <typename T, T Begin,  class Func, T ...Is>
-    static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
-    ( f( std::integral_constant<T, Begin + Is>{ } ),... );
-    }
+	/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
+	template <typename T, T Begin, class Func, T... Is>
+	static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
+		(f(std::integral_constant<T, Begin + Is>{}), ...);
+	}
 
-    template <typename T, T Begin, T End, class Func>
-    static constexpr void static_for(Func&& f) {
-        static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
-    }
+	template <typename T, T Begin, T End, class Func>
+	static constexpr void static_for(Func&& f) {
+		static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
+	}
 
-    // For values < 0x99
-    static constexpr inline u8 incBCDByte(u8 value) {
-        return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
-    }
-    // Use this helper for platforms that lack a working std::bit_cast implementation
-    // TODO: Replace this with C++20 version if available
-    template< class To, class From >
-    constexpr To bit_cast( const From& from ) noexcept{
-        return *reinterpret_cast<const To*>(&from);
-    }
-};
+	// For values < 0x99
+	static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; }
+
+#ifdef HELPERS_APPLE_CLANG
+	template <class To, class From>
+	constexpr To bit_cast(const From& from) noexcept {
+		return *reinterpret_cast<const To*>(&from);
+	}
+#else
+	template <class To, class From>
+	constexpr To bit_cast(const From& from) noexcept {
+		return std::bit_cast<To, From>(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;
-}
+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
\ No newline at end of file
+#define likely(x) __builtin_expect((x), 1)
+#define unlikely(x) __builtin_expect((x), 0)
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif