Skip to content

Troubleshooting & Limits

This guide covers advanced topics that you might encounter as your projects grow in complexity. Here, you’ll find solutions to common limitations and techniques to push what’s possible on the Classpad.  As new techniques are discovered, they will be added here, so be sure to check it regularly. If you encounter some issue that isn’t yet known or listed here, feel free to 💬 Join our Discord community or report it as issue in our GitHub bug tracker.

As your application grows, you might run into a strict 64 kilobyte (kb) size limit for your compiled ELF binary that prevent it loading properly. This is a fundamental constraint of the standard memory space allocated for applications.

When your application’s compiled code exceeds 64kb, the calculator’s loader cannot place it in the default memory region. This typically results in your app crashing or freezing immediately upon launch, without any clear error message. The standard memory space allocated for an app is simply not large enough for bigger projects.

To bypass the 64kb limit, you can instruct the linker to place your application’s code in a larger memory area known as vbak or vram. These regions, normally used for system operations like video memory backup, each offer approximately 330kb of space.

To do this, you need to add a specific linker flag to your project’s Makefile.

  1. Open your Makefile.
  2. Locate the LD_FLAGS variable.
  3. Add the following flag: -Wl,-Ttext-segment,0x8C052800

Your LD_FLAGS line should look something like this:

LD_FLAGS = -Wl,-Ttext-segment,0x8C052800

This flag tells the YAL (HH3/v3) loader to place the executable code segment at the memory address 0x8C052800, which corresponds to the vbak area.

Important Side Effect: Managing the Screen Buffer

Section titled “Important Side Effect: Managing the Screen Buffer”

Placing your application code in the vbak region comes with a critical responsibility: you must now manually restore the screen’s contents restore after run. Because your code occupies the memory space the operating system would use to back up the screen (vram), you must save and restore the screen state yourself upon starting and exiting your app.

Doing it is hopefully trivial, you need to override the default calcInit and calcExit functions. The following code allocates a temporary buffer on the heap, copies the current screen content into it, and restores it when the app closes.

#include <type_traits>
#include <sdk/os/file.h>
#include <sdk/os/debug.h>
#include <sdk/os/lcd.h>
#include <sdk/os/string.h>
#include <sdk/calc/calc.h>
#include <cstring>
#include <cstdlib>
// A backup pointer for the original vram content
std::remove_pointer_t<decltype(vram)> (*vram_bak)[width * height];
// This function is called when your app starts
void calcInit(void)
{
// Allocate memory on the heap to store the screen backup
vram_bak = (decltype(vram_bak))malloc(sizeof(*vram_bak));
// If allocation fails, we can't proceed safely
if (!vram_bak)
{
return;
}
// Copy the current screen content to our backup buffer
memcpy(vram_bak, vram, sizeof(*vram_bak));
// Clear the screen for the app
memset(vram, 0, sizeof(*vram_bak));
}
// This function is called when your app exits
void calcExit(void)
{
// Copy the backed-up screen content back to vram
memcpy(vram, vram_bak, sizeof(*vram_bak));
// Refresh the LCD to show the restored content
LCD_Refresh();
// Free the memory we allocated for the backup
free(vram_bak);
}

For a complete implementation of this technique, you can look at the source code for CPDoom. The project uses the vbak memory region and provides a clear example of overriding calcInit and calcExit in its bootstrap.cpp file.

A common cause of crash is linked to a message simmilar to the following:

ADDRESS(R) ERROR!
TARGET=BDF7BE03
PC=8CFF3B48

Here’s common causes of crash to investigate :

This mean the program tried to write at an address that’s not valid or not allowed. Check the target value. Here’s a concrete example and its fix:

ADDRESS(W) ERROR!
TARGET=8CFFF275
PC=8007D452

From the CPapp.elf.map (generated at compile time) that target is in .bss.event (0x8cfff275) in that case, the error indicate “address write error”. The crash location 0x8007D452 (0x80XXXXXX = ROM) looks close to the GetInput (0x8007CE2C) call (use the addresses mapping from the sdk with your rom version to determine them). If the crash is in GetInput or related to it, and the target is .bss.event, the event variable is likely in .bss (uninitialized data). If GetInput writes to &event, and event is at 0x8cfff275, but 0x8cfff275 is an odd address (ends in 5, so not 4-byte aligned), GetInput expects an aligned pointer, and event is unaligned, writing to it might cause an exception.

In that case, the fix is to eother align event, or reorder variables to ensure alignment: Change uint8_t to uint32_t or add alignas(4).

The easiest solution is to run sh4aeb-elf-addr2line -ipfe dist/CPapp.elf 8CFF3B48 (change with your elf file and target/pc address) It would output something similar to : _ZN8Renderer12draw_MinimapEb at /workspaces/CP-3D-CarGoWroom/src/Renderer.cpp:303 that give you hint on where the issue is.

Sometimes the line isn’t available, and you’d need to troubleshoot the cause of the issue by looking at the CPapp.elf.map file that’s generated at compile time.

Here’s another alignment issue:

ADDRESS(R) ERROR!
TARGET=8CF791FF
PC=8005A578

The PC is close the file methods : { "FunctionName": "File_FindFirst", "startAddress": "0x8005A2AC" ... }, in that case File_FindFirst crash at 0x8CF791FF (unaligned address) is due to misaligned pointers passed to it (g_wpath, fileName, or findInfoBuf). The fix was to align globals in src/internal.hpp and locals in src/commands/ls.cpp to 4 bytes using __attribute__((aligned(4))) to prevent unaligned access crash in File_FindFirst.