// Codegen arena: a pool of executable memory that holds compiled // sprite routines. Allocator is first-fit with adjacent-hole // coalescing on free; manual compaction (codegenArenaCompact) // memmoves live slots down to consolidate free space. // // jlSpriteT references slots via ArenaSlotT*, never raw function // pointers. jlSpriteDraw computes the call address each invocation as // `gArenaBase + slot->offset + routineOffsets[shift][op]` so a // compaction that moves a slot's bytes is transparent to callers. // // Allocations go through plain malloc; on every supported port // (IIgs, Amiga libnix, ST mintlib, DJGPP+CWSDPMI) the heap is // readable, writable, AND executable. We are not running in a W^X // environment. // // The arena is a process-singleton: codegenArenaInit creates it from // jlConfigT.codegenBytes during jlInit; codegenArenaShutdown // frees it on jlShutdown. Most callers should go through the // sprite API rather than touching the arena directly. #ifndef JOEYLIB_CODEGEN_ARENA_INTERNAL_H #define JOEYLIB_CODEGEN_ARENA_INTERNAL_H #include "joey/types.h" typedef struct ArenaSlotT { uint32_t offset; // byte offset within the arena base uint32_t size; // bytes occupied by this slot bool used; // true = held by a sprite; false = free struct ArenaSlotT *next; // linked-list, sorted by offset struct ArenaSlotT *prev; } ArenaSlotT; // Lifecycle. Returns false if totalBytes is 0 or the underlying // allocation fails. Idempotent: calling Init twice without an // intervening Shutdown is a no-op. bool codegenArenaInit(uint32_t totalBytes); void codegenArenaShutdown(void); // First-fit allocate `bytes` of executable memory. Returns NULL if // no free slot is large enough -- the caller should run // codegenArenaCompact and retry, or surface the failure. ArenaSlotT *codegenArenaAlloc(uint32_t bytes); // Mark the slot free and merge with adjacent free neighbors. The // caller must drop its ArenaSlotT* immediately; the struct may be // freed (if it coalesced into a neighbor). void codegenArenaFree(ArenaSlotT *slot); // Walk live slots in offset order, memmove each down to fill any // preceding hole, drop all free slots from the list, and finish with // one trailing free slot covering the remaining space. Per-slot // `offset` fields are updated atomically with the memmove so any // callers indexing through `gArenaBase + slot->offset` see the new // value on their next read. void codegenArenaCompact(void); // Used for jlSpriteDraw's address computation. The base pointer is // stable for the lifetime of the arena; only slot->offset moves. // // Direct extern access (instead of a getter function) so per-frame // hot paths in jlSpriteCompile.c skip the JSL/PHB/RTL/PLB the wrapper // would impose. Both globals are read-only after codegenArenaInit; // the function-form getters below are kept as a back-compat shim. extern uint8_t *gCodegenArenaBase; extern uint32_t gCodegenArenaBaseAddr; #define codegenArenaBase() ((uint8_t *)gCodegenArenaBase) #define codegenArenaBaseAddr() ((uint32_t)gCodegenArenaBaseAddr) // Public-API support: sum of live slot sizes, total arena size. // Difference is free space (which may be fragmented across holes // until codegenArenaCompact runs). uint32_t codegenArenaBytesUsed(void); uint32_t codegenArenaBytesTotal(void); #endif