From 162bb983abf6901958db83336a24f5afc406df06 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 13 Dec 2025 12:12:24 +1100 Subject: [PATCH] GBA: Optimise framebuffer clearing Measuring in mgBA, time taken reduced from ~52 ms to ~3ms --- misc/gba/Makefile | 6 +--- src/Graphics_SoftMin.c | 6 ++++ src/gba/AsmFuncs.S | 79 ++++++++++++++++++++++++++++++++++++++++++ src/gba/NocashLog.S | 22 ------------ src/gba/Platform_GBA.c | 13 ++++--- src/gba/Window_GBA.c | 16 +++++++-- 6 files changed, 109 insertions(+), 33 deletions(-) create mode 100644 src/gba/AsmFuncs.S delete mode 100644 src/gba/NocashLog.S diff --git a/misc/gba/Makefile b/misc/gba/Makefile index 408c742ba..ec03e0807 100644 --- a/misc/gba/Makefile +++ b/misc/gba/Makefile @@ -36,15 +36,11 @@ DEPFILES := $(OBJS:%.o=%.d) # Code generation #--------------------------------------------------------------------------------- ARCH = -mthumb -mthumb-interwork -CFLAGS = -g -Wall -O2 -DPLAT_GBA -ffunction-sections -fdata-sections -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH) +CFLAGS = -g -gdwarf-4 -Wall -O2 -DPLAT_GBA -ffunction-sections -fdata-sections -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH) ASFLAGS = -g $(ARCH) LDFLAGS = -specs=gba.specs -g $(ARCH) -LIBGBA := $(DEVKITPRO)/libgba -INCLUDES += $(foreach dir, $(LIBGBA), -I$(dir)/include) -LDFLAGS += $(foreach dir, $(LIBGBA), -L$(dir)/lib) - #--------------------------------------------------------------------------------- # Compiler tools diff --git a/src/Graphics_SoftMin.c b/src/Graphics_SoftMin.c index aabb306ee..df8095015 100644 --- a/src/Graphics_SoftMin.c +++ b/src/Graphics_SoftMin.c @@ -129,6 +129,11 @@ void Gfx_SetAlphaArgBlend(cc_bool enabled) { } static void ClearColorBuffer(void) { int i, x, y, size = fb_width * fb_height; +#ifdef CC_BUILD_GBA + /* in mGBA, fast clear takes ~2ms compared to ~52ms of code below */ + extern void VRAM_FastClear(BitmapCol color); + VRAM_FastClear(clearColor); +#else if (cb_stride == fb_width) { for (i = 0; i < size; i++) colorBuffer[i] = clearColor; } else { @@ -140,6 +145,7 @@ static void ClearColorBuffer(void) { } } } +#endif } void Gfx_ClearBuffers(GfxBuffers buffers) { diff --git a/src/gba/AsmFuncs.S b/src/gba/AsmFuncs.S new file mode 100644 index 000000000..1423e0fd6 --- /dev/null +++ b/src/gba/AsmFuncs.S @@ -0,0 +1,79 @@ + +#define BEG_THUMB_FUNC(name_) \ + .global name_; \ + .thumb_func; \ + .align 2; \ + name_: + +#define BEG_ARM_FUNC(name_) \ + .global name_; \ + .arm; \ + .align 4; \ + name_: + +#define END_FUNC(name_) \ + .size name_, . - name_; \ + .type name_, %function; + + +// =============================== +// FUNCTIONS IN TEXT SECTION +// =============================== +.section text + + +// =============================== +// FUNCTIONS IN IWRAM SECTION +// =============================== +.section .iwram,"ax",%progbits + +#define R_CUR r0 // r0 = beg address (incremented in function) +#define R_END r1 // r1 = end address +#define R_VAL r2 // r2 = value to fill + +BEG_ARM_FUNC(fastset_256_bytes) + // Spill callee saved registers + LR onto stack + stmfd sp!, {r4-r9, lr} + + // Clone 'value' for 'store multi CPU registers' loop + mov r3, R_VAL + mov r4, R_VAL + mov r5, R_VAL + mov r6, R_VAL + mov r7, R_VAL + mov r8, R_VAL + mov r9, R_VAL + +fastset_loop: + cmp R_CUR, R_END + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + stmltia R_CUR!, {r2-r9} + blt fastset_loop + + // Restore saved CPU registers + set PC to LR + ldmfd sp!, {r4-r9, pc} +END_FUNC(fastset_256_bytes) + + +// =============================== +// FUNCTIONS IN EWRAM SECTION +// =============================== +.section .ewram,"ax",%progbits +BEG_THUMB_FUNC(nocash_log) + // nocash looks for this specific pattern + mov r12, r12 + b nocash_log_return + .short 0x6464, 0x0000 +.global nocash_msg // Platform_GBA writes directly to this +nocash_msg: + .space 82 + +nocash_log_return: + bx lr +END_FUNC(nocash_log) diff --git a/src/gba/NocashLog.S b/src/gba/NocashLog.S deleted file mode 100644 index b3a2724b6..000000000 --- a/src/gba/NocashLog.S +++ /dev/null @@ -1,22 +0,0 @@ -.global nocash_log -.global nocash_msg - -// BEG nocash_log -.section .ewram,"ax",%progbits -.thumb_func -.align 2 - -// nocash looks for this specific pattern -nocash_log: - mov r12, r12 - b nocash_log_return - .short 0x6464, 0x0000 -nocash_msg: - .space 82 - -nocash_log_return: - bx lr - -.size nocash_log, .-nocash_log -.type nocash_log, %function -// END nocash_log diff --git a/src/gba/Platform_GBA.c b/src/gba/Platform_GBA.c index 7aaa66311..934e94b89 100644 --- a/src/gba/Platform_GBA.c +++ b/src/gba/Platform_GBA.c @@ -13,9 +13,8 @@ #include "../Options.h" #include "../Animations.h" -#include +#include #include -#include #include "gbadefs.h" #include "../../third_party/tinyalloc/tinyalloc.c" @@ -203,9 +202,10 @@ void DateTime_CurrentLocal(struct cc_datetime* t) { void CrashHandler_Install(void) { } + void Process_Abort2(cc_result result, const char* raw_msg) { Platform_LogConst(raw_msg); - _exit(0); + Process_Exit(0); } @@ -333,5 +333,10 @@ cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { return 0; } -void Process_Exit(cc_result code) { _exit(code); } +extern void bios_soft_reset(void); +void Process_Exit(cc_result code) { + //*(vu8*)0x03007FFA = 0x00; // controls reset address + // TODO jump to start_vector instead? + for (;;) { __asm__ volatile(""); } +} diff --git a/src/gba/Window_GBA.c b/src/gba/Window_GBA.c index 44381f1d4..8729a3acc 100644 --- a/src/gba/Window_GBA.c +++ b/src/gba/Window_GBA.c @@ -108,17 +108,20 @@ void Gamepads_Init(void) { Input.Sources |= INPUT_SOURCE_GAMEPAD; } +#define PAUSE_MASK (KEY_A|KEY_B|KEY_START|KEY_SELECT) void Gamepads_Process(float delta) { int port = Gamepad_Connect(0x5BA, pad_defaults); int mods = ~REG_KEYINPUT; + + // TODO see comment in Platform_GBA.c, doesn't work anyways + //if ((mods & PAUSE_MASK) == PAUSE_MASK) + // Process_Exit(0); Gamepad_SetButton(port, CCPAD_L, mods & KEY_L); Gamepad_SetButton(port, CCPAD_R, mods & KEY_R); Gamepad_SetButton(port, CCPAD_1, mods & KEY_A); Gamepad_SetButton(port, CCPAD_2, mods & KEY_B); - //Gamepad_SetButton(port, CCPAD_3, mods & KEY_X); - //Gamepad_SetButton(port, CCPAD_4, mods & KEY_Y); Gamepad_SetButton(port, CCPAD_START, mods & KEY_START); Gamepad_SetButton(port, CCPAD_SELECT, mods & KEY_SELECT); @@ -133,6 +136,15 @@ void Gamepads_Process(float delta) { /*########################################################################################################################* *------------------------------------------------------Framebuffer--------------------------------------------------------* *#########################################################################################################################*/ +extern void fastset_256_bytes(char* beg, char* end, int value); + +void VRAM_FastClear(BitmapCol col) { + int value = (col << 16) | col; + + char* vram = (char*)MEM_VRAM; + fastset_256_bytes(vram, vram + SCREEN_WIDTH * SCREEN_HEIGHT * 2, value); +} + void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) { bmp->scan0 = (BitmapCol*)MEM_VRAM; bmp->width = width;