/** * @file check.h * * Tactility crash and check functions. * * The main problem with crashing is that you can't do anything without disturbing registers, * and if you disturb registers, you won't be able to see the correct register values in the debugger. * * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function. * r0-r10 are stored in the ram2 on crash routine start and restored at the end. * The only register that is going to be lost is r11. * */ #pragma once #include "CoreDefines.h" #include "Logger.h" #include #define TT_NORETURN [[noreturn]] /** Crash system */ namespace tt { /** * Don't call this directly. Use tt_crash() as it will trace info. */ TT_NORETURN void _crash(); } // TODO: Move crash logic to kernel namespace and consider refactoring to C++ /** Crash system with message. */ #define tt_crash(...) TT_ARG_CAT(_tt_crash,TT_ARGCOUNT(__VA_ARGS__))(__VA_ARGS__) #define _tt_crash0() do { \ tt::Logger("Kernel").error("Crash at {}:{}", __FILE__, __LINE__); \ tt::_crash(); \ } while (0) #define _tt_crash1(message) do { \ tt::Logger("Kernel").error("Crash: {}\n\tat {}:{}", message, __FILE__, __LINE__); \ tt::_crash(); \ } while (0) /** Halt the system * @param[in] optional message (const char*) */ #define tt_halt(...) M_APPLY(__tt_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) /** Check condition and crash if check failed */ #define tt_check_internal(__e, __m) \ do { \ if (!(__e)) { \ tt::Logger("Kernel").error("Check failed: {}", #__e); \ if (__m) { \ tt_crash(__m); \ } else { \ tt_crash(""); \ } \ } \ } while (0) /** Check condition and crash if failed * * @param[in] condition to check * @param[in] optional message (const char*) */ #define tt_check(x, ...) if (!(x)) { tt::Logger("Kernel").error("Check failed: {}", #x); tt::_crash(); }