To allow multiple games to be “installed” in the 32MB flash memory at the same time, all games are built as position-independent code with these flags: -fPIC -mno-pic-data-is-text-relative -mno-single-pic-base. A relocation header is appended and the firmware relocates games while writing to flash.
This also requires custom builds of newlib/libstdc++ with the same flags, which are provided here.
The .blit format has three parts, there's a relocation header, the executable and the game metadata.
The relocation header is a list of offsets in the executable that get an offset added to them as the game is written to flash (through the serial port or using the launcher to “run” from SD).
For example, if a game is written to the second “block” of flash, 0x10000 will be added to each listed offset in the binary.
The format of this header is:
{ uint32_t magic; // "RELO" uint32_t num_relocs; // Number of relocation entries uint32_t relocs[]; // the addresses to relocate }
This header is generated by the relocs tool in 32blit-tools and is not written to flash when a game is installed.
The raw game binary generated with arm-none-eabi-objcopy -O binary .... It has a small header created by the linker:
struct BlitGameHeader { uint32_t magic; // "BLIT" BlitRenderFunction render; // pointer to the render function BlitTickFunction tick; // pointer to blit::tick, which calls update and updates timers/tweens BlitInitFunction init; // pointer to the init function uint32_t end; // the address of the end of the game binary (0x90000000 + the size of the binary) uint32_t start; // the address of the start of the game (always 0x90000000, not used) };
The end address is used to locate the metadata.
The metadata is appended to the end and has the info generated by the metadata tool from the metadata.yml file.
There are multiple parts to this as the format has been extended.
struct RawMetadata { char[8] magic; // "BLITMETA" uint16_t size; // size of entire metadata uint32_t crc32; // these two are generated by the tools char datetime[16]; // char title[25]; char description[129]; char version[17]; char author[17]; }; // Prefixed with "BLITTYPE". Extended metadata - older .blits may not have this struct RawTypeMetadata { char category[17]; // the "launcher" category has special meaning char url[129]; // can be anything, but points to the GitHub repo most of the time uint8_t num_filetypes; // used for launcher file associations char filetypes[][5]; // };
Two packed images usually follow these for the icon/splash. This is the same format as generated for images by the asset packer and starts with either “SPRITERW”, “SPRITEPK” or “SPRITERL”.
The firmware is installed to the “internal” handles all the hardware stuff, including flashing games and switching between them. It also handles the system menu.
At startup, the firmware scans through “external” flash to find installed games and builds a list of “games” with file associations. This can be used for interpreters (like 32blit-lua), or emulators (DaftBoy32).
The interface between the games/firmware is in 32blit/engine/api_private.hpp.
The launcher is actually mostly a regular game, with a category of “launcher”. The firmware switches to this whenever a game is not running.
It uses some “semi-private” firmware API to handle launching/flashing games. It's possible to replace the launcher entirely, like this extremely basic one.
| Section | Address | Size | Usage |
|---|---|---|---|
| ITCM | 00000000 | 64k | fast code, not currently used |
| Internal flash | 08000000 | 128k | firmware .text/.rodata |
| DTCM | 20000000 | 128k | .data, stack |
| D1 RAM | 24000000 | 512k | 362k game heap/.bss, 150k for LTDC (screen) |
| D2 RAM | 30000000 | 288k | firmware heap/.bss, 225k for framebuffer |
| D3 RAM | 38000000 | 64k | some used by firmware, mostly unused |
| External flash | 90000000 | 32M | game .text/.rodata/metadata, 4MB reserved |