From d7414157a5099ac8da87bfe9ef4430acd766f56f Mon Sep 17 00:00:00 2001 From: Rickey Date: Thu, 21 May 2026 20:07:34 -0700 Subject: [PATCH] Add logging (#492) * Add logger implementation for mgba debugging * Add logging details to contributors guide * Clang-format * update docs real quick * Cleanup for PR * Update for PR * Fix bad rebase * Fix docs --- CONTRIBUTING.md | 17 ++++++++++ Makefile | 4 +++ include/mgba_logger.h | 73 +++++++++++++++++++++++++++++++++++++++++++ source/main.c | 6 +++- source/mgba_logger.c | 53 +++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 include/mgba_logger.h create mode 100644 source/mgba_logger.c diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4016173..6a86708 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -143,6 +143,23 @@ In the repo we use custom scripts located in the [`scripts`](https://github.com/ ## Debugging It's recommended to use [mGBA](https://mgba.io/) for ROM testing and debugging. As it provides a [`gdbserver`](https://en.wikipedia.org/wiki/Gdbserver) via the `-g` flag `mgba -g build/balatro-gba.gba`. You can connect via `gdb` or here is a [great guide for vscode](https://felixjones.co.uk/mgba_gdb/vscode.html). +### Logging +Specifically for mgba, logging can be enabled. To do this set `MGBA_LOGGING=1` when running `make`: + +```sh +MGBA_LOGGING=1 make +``` + +🟡 **Note**: If you don't see any logs, try running `make clean` before rebuilding + +Then, you can enable logging via the `-l` or `--log-level` option: + +```sh +mgba -l 7 build/balatro-gba.gba +``` + +See [`mgba_logger.h`](include/mgba_logger.h) for details on log levels. + ## **Build Instructions**
diff --git a/Makefile b/Makefile index 07c0df1..9be1dc3 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,10 @@ CFLAGS += $(GIT_C_FLAGS) CFLAGS += $(INCLUDE) +ifeq ($(MGBA_LOGGING),1) +CFLAGS += -DMGBA_LOGGING +endif + CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) diff --git a/include/mgba_logger.h b/include/mgba_logger.h new file mode 100644 index 0000000..7e10194 --- /dev/null +++ b/include/mgba_logger.h @@ -0,0 +1,73 @@ +/** + * @file mgba_logger.h + * + * @brief Interface to interact with the mgba logger. + * + * Use with mgba in the command line. + * + * You can pass which logs you'd like to print with a flag passed to mgba + * with `-l` or `--log-level`. + * + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * |---|---|---|-------|------|------|-------|-------| + * | / | / | / | DEBUG | INFO | WARN | ERROR | FATAL | + * + * ```sh + * mgba -l 3 game.rom # ERROR and FATAL + * mgba -l 14 game.rom # INFO, WARN, and ERROR + * ``` + * + * @note You have to fight with other logs in mgba and DEBUG can get messy. + * + * @note FATAL does kill the game. Use with care. + */ +#ifndef MGBA_LOGGER_H +#define MGBA_LOGGER_H + +#include + +typedef enum +{ + MGBA_LOG_FATAL, + MGBA_LOG_ERROR, + MGBA_LOG_WARN, + MGBA_LOG_INFO, + MGBA_LOG_DEBUG, +} MgbaLogLevel; + +/** + * @brief Initialize mgba logger + * + * Checks that the logger is available by checking the expected registers + * magic number + */ +bool mgba_logger_init(void); + +/** + * @brief Print to mgba log with a format string + * + * @param level + * @param fmt Format string + * @param ... variadic arguments + * + * @note for all logs, it's cutoff at the hard mgba limit of 0x100 + */ +void mgba_printf(MgbaLogLevel level, const char* fmt, ...); + +// clang-format off +#ifdef MGBA_LOGGING +#define MGBA_FATAL(...) mgba_printf(MGBA_LOG_FATAL, __VA_ARGS__) +#define MGBA_ERROR(...) mgba_printf(MGBA_LOG_ERROR, __VA_ARGS__) +#define MGBA_WARN(...) mgba_printf(MGBA_LOG_WARN, __VA_ARGS__) +#define MGBA_INFO(...) mgba_printf(MGBA_LOG_INFO, __VA_ARGS__) +#define MGBA_DEBUG(...) mgba_printf(MGBA_LOG_DEBUG, __VA_ARGS__) +#else +#define MGBA_FATAL(...) ((void)0) +#define MGBA_ERROR(...) ((void)0) +#define MGBA_WARN(...) ((void)0) +#define MGBA_INFO(...) ((void)0) +#define MGBA_DEBUG(...) ((void)0) +#endif +// clang-format on + +#endif diff --git a/source/main.c b/source/main.c index 4e8b34c..ce53736 100644 --- a/source/main.c +++ b/source/main.c @@ -20,6 +20,7 @@ // Audio #include "audio_utils.h" +#include "mgba_logger.h" #include "soundbank.h" #include "soundbank_bin.h" @@ -31,6 +32,10 @@ void init() irq_add(II_VBLANK, mmVBlank); irq_add(II_HBLANK, affine_background_hblank); +#ifdef MGBA_LOGGING + mgba_logger_init(); +#endif + // Initialize text engine tte_init_se( 0, @@ -86,7 +91,6 @@ void init() REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 | DCNT_WIN1; - // Initialize subsystems mmInitDefault((mm_addr)soundbank_bin, GBAL_MM_NUM_CHANNELS); load_options(); diff --git a/source/mgba_logger.c b/source/mgba_logger.c new file mode 100644 index 0000000..572cb37 --- /dev/null +++ b/source/mgba_logger.c @@ -0,0 +1,53 @@ +#include "mgba_logger.h" + +#ifdef MGBA_LOGGING + +#include +#include +#include +#include +#include + +#define MGBA_REG_DEBUG_ENABLE ((vu16*)0x4FFF780) +#define MGBA_REG_DEBUG_FLAGS ((vu16*)0x4FFF700) +#define MGBA_REG_DEBUG_STRING ((char*)0x4FFF600) + +static const u32 MGBA_ENABLE_MAGIC = 0xC0DE; +static const u32 MGBA_ENABLE_OK = 0x1DEA; +static const u32 MGBA_LOG_SEND = 0x100; +static const u32 MGBA_LOG_BUFFER_SIZE = 0x100; + +static bool mgba_logger_available = false; + +bool mgba_logger_init(void) +{ + *MGBA_REG_DEBUG_ENABLE = MGBA_ENABLE_MAGIC; + mgba_logger_available = (*MGBA_REG_DEBUG_ENABLE == MGBA_ENABLE_OK); + return mgba_logger_available; +} + +void mgba_printf(MgbaLogLevel level, const char* fmt, ...) +{ + if (!mgba_logger_available || fmt == NULL) + return; + + va_list args; + va_start(args, fmt); + vsnprintf(MGBA_REG_DEBUG_STRING, MGBA_LOG_BUFFER_SIZE, fmt, args); + va_end(args); + + *MGBA_REG_DEBUG_FLAGS = ((uint16_t)level & 0x7) | MGBA_LOG_SEND; +} +#else + +// Noop stubs +bool mgba_logger_init(void) +{ + return false; +} + +void mgba_printf(MgbaLogLevel level, const char* fmt, ...) +{ +} + +#endif