diff --git a/native/lucore/CMakeLists.txt b/native/lucore/CMakeLists.txt index e69b939..d7f30d3 100644 --- a/native/lucore/CMakeLists.txt +++ b/native/lucore/CMakeLists.txt @@ -5,9 +5,11 @@ project(lucore LANGUAGES CXX ) -add_library(lucore INTERFACE) +add_library(lucore + src/Assert.cpp +) -target_compile_features(lucore INTERFACE cxx_std_20) -target_include_directories(lucore INTERFACE ${PROJECT_SOURCE_DIR}/include) +target_compile_features(lucore PUBLIC cxx_std_20) +target_include_directories(lucore PUBLIC ${PROJECT_SOURCE_DIR}/include) add_library(lucore::lucore ALIAS lucore) diff --git a/native/lucore/include/lucore/Assert.hpp b/native/lucore/include/lucore/Assert.hpp index a69269f..72b3ddc 100644 --- a/native/lucore/include/lucore/Assert.hpp +++ b/native/lucore/include/lucore/Assert.hpp @@ -1,16 +1,35 @@ //! Lucore Assert Wrappers -// this just plumbs everything to libc by default -// your project can choose to configure its own assertion handler by -// defining LUCORE_ASSERT() -// TODO: LUCORE_VERIFY/plumbing this into our own assertion code? -#ifndef LUCORE_ASSERT -#include +#pragma once -#ifdef NDEBUG - #define LUCORE_ASSERT(...) assert(__VA_ARGS__) +#include + +namespace lucore { + + // TODO: wrapper which uses source_location (we don't need no macros anymore!) + [[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message); + +} + +#ifndef NDEBUG + #define LUCORE_ASSERT(expr, format, ...) \ + if(!(expr)) [[unlikely]] { \ + char buffer[256]; \ + std::snprintf(&buffer[0], sizeof(buffer), \ + "[Lucore] Assertion \"%s\" failed with message: " format "\n", #expr, \ + ##__VA_ARGS__); \ + ::lucore::ExitMsg(__FILE__, __LINE__, &buffer[0]); \ + } #else - #define LUCORE_ASSERT(...) + #define LUCORE_ASSERT(expr, format, ...) #endif -#endif +// CHECK() is always active, even in release builds +#define LUCORE_CHECK(expr, format, ...) \ + if(!(expr)) [[unlikely]] { \ + char buffer[256]; \ + std::snprintf(&buffer[0], sizeof(buffer), \ + "[Lucore] Check \"%s\" failed with message: " format "\n", #expr, \ + ##__VA_ARGS__); \ + ::lucore::ExitMsg(__FILE__, __LINE__, &buffer[0]); \ + } diff --git a/native/lucore/include/lucore/OptionalRef.hpp b/native/lucore/include/lucore/OptionalRef.hpp index c825810..a1ae9a7 100644 --- a/native/lucore/include/lucore/OptionalRef.hpp +++ b/native/lucore/include/lucore/OptionalRef.hpp @@ -6,6 +6,9 @@ namespace lucore::detail { struct Nullref_t {}; + /// Sentinel value to explicitly not populate an OptionalRef. + inline static Nullref_t NullRef {}; + template struct OptionalRef; // sfinae on non-reference types @@ -35,7 +38,9 @@ namespace lucore::detail { } constexpr T& Value() const { - LUCORE_ASSERT(HasValue() && "Attempt to access OptionalRef without stored value!"); + // this is a CHECK() since allowing unchecked access in release builds is probably a + // very very bad idea + LUCORE_CHECK(HasValue(), "Attempt to access OptionalRef without stored value!"); return *ptr; } @@ -60,8 +65,6 @@ namespace lucore::detail { T* ptr {}; }; - /// Sentinel value to explicitly not populate an OptionalRef. - inline static Nullref_t NullRef {}; } // namespace lucore::detail namespace lucore { diff --git a/native/lucore/src/Assert.cpp b/native/lucore/src/Assert.cpp new file mode 100644 index 0000000..46f423e --- /dev/null +++ b/native/lucore/src/Assert.cpp @@ -0,0 +1,15 @@ +//! Implementation of lucore assertion facilities + +#include +#include + +namespace lucore { + + [[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) { + // TODO: move this to logger functionality of lucore (gmsv_lcpu will end up containing a + // Sink impl..) + std::puts(message); + std::quick_exit(0xAF); + } + +} // namespace lucore diff --git a/native/riscv/CMakeLists.txt b/native/riscv/CMakeLists.txt index 666daaa..47ced39 100644 --- a/native/riscv/CMakeLists.txt +++ b/native/riscv/CMakeLists.txt @@ -7,6 +7,7 @@ project(riscv_emu add_library(riscv src/Bus.cpp + src/MemoryDevice.cpp ) target_compile_features(riscv PUBLIC cxx_std_20) diff --git a/native/riscv/src/MemoryDevice.cpp b/native/riscv/src/MemoryDevice.cpp index c463f68..520caa6 100644 --- a/native/riscv/src/MemoryDevice.cpp +++ b/native/riscv/src/MemoryDevice.cpp @@ -1,21 +1,17 @@ #include -#include "riscv/Types.hpp" - #include +#include "riscv/Types.hpp" + namespace riscv { namespace { - - template + template struct BasicMemoryDevice : public Bus::Device { - BasicMemoryDevice(usize size) - : memorySize(size) { + BasicMemoryDevice(usize size) : memorySize(size) { memory = new u8[size]; - // TODO(feat): we should have a global panic system which is hooked in - // so that we don't just blindly crash everything - assert(memory && "Out of host memory"); + LUCORE_CHECK(memory, "Could not allocate buffer for memory device."); } virtual ~BasicMemoryDevice() { @@ -43,7 +39,7 @@ namespace riscv { } u32 PeekWord(AddressT offset) override { - return std::bit_cast(memory)[OffsetToIndex(offset)]; + return std::bit_cast(memory)[OffsetToIndex(offset)]; } void PokeByte(AddressT offset, u8 value) override { @@ -70,29 +66,26 @@ namespace riscv { } } - private: - - + private: /// helper used for implementing stuff - template + template constexpr usize OffsetToIndex(AddressT offset) { return (offset % memorySize) / sizeof(T); } // remember what we were attached to via "signal" - Bus* attachedBus{}; - AddressT baseAddress{}; + Bus* attachedBus {}; + AddressT baseAddress {}; - u8* memory{}; - usize memorySize{}; + u8* memory {}; + usize memorySize {}; }; using RamDevice = BasicMemoryDevice; using RomDevice = BasicMemoryDevice; - } + } // namespace + // Bus::Device* NewRam() - //Bus::Device* NewRam() - -} +} // namespace riscv