// JoeyLib lifecycle: init, shutdown, error reporting. // // jlInit stores configuration, allocates the library-owned screen // surface, and asks the port HAL to set up the display mode. // jlShutdown tears those down in reverse order. #include #include #include #include "joey/core.h" #include "codegenArenaInternal.h" #include "port.h" #include "spriteInternal.h" #include "surfaceInternal.h" // Default codegen arena size when jlConfigT.codegenBytes == 0. // Per-platform because emitter code density differs ~4-8x across CPUs // (PERF-AUDIT.md #39): // // IIgs (65816): ~3-4 KB per all-opaque 32x32 sprite, so 8 KB fits // the typical working set and keeps the attrNoCross Memory Manager // request small. Requests above one 64 KB bank are clamped in // codegenArenaInit (#8). // // Amiga/ST/DOS: the 68k/x86 emitters are far less dense -- an // all-opaque 32x32 costs ~6.4 KB on ST (both shifts) and a // substantially-mixed one ~8-16 KB by itself, so an 8 KB default // silently demoted sprites to the interpreter. 32 KB matches what // the sprite-heavy examples (uber, sprite, spacetaxi) already pass // explicitly, and these ports have real heaps. #if defined(JOEYLIB_PLATFORM_IIGS) #define DEFAULT_CODEGEN_BYTES (8UL * 1024UL) #else #define DEFAULT_CODEGEN_BYTES (32UL * 1024UL) #endif // ----- Prototypes ----- static void clearError(void); // coreSetError is the shared error setter (declared in surfaceInternal.h // and called by surface.c etc.); its definition lives here next to the // rest of the error-reporting state. // ----- Module state ----- static jlConfigT gConfig; static bool gInitialized = false; static const char *gLastError = NULL; // ----- Internal helpers (alphabetical) ----- static void clearError(void) { gLastError = NULL; } void coreSetError(const char *message) { gLastError = message; } // ----- Default large-allocation backing ----- // // jlAlloc / jlFree route through jlpBigAlloc / jlpBigFree. Most ports have a // normal heap, so the malloc/free generic default (src/generic/genericPort.c) // serves them; only ports whose C heap is too small or per-allocation-capped // (IIgs: bank-0 heap) define JL_HAS_BIG_ALLOC and override with their native // allocator (IIgs Memory Manager NewHandle / DisposeHandle). // ----- Public API (alphabetical) ----- void *jlAlloc(uint32_t bytes) { return jlpBigAlloc(bytes); } void jlFree(void *p) { jlpBigFree(p); } bool jlInit(const jlConfigT *config) { clearError(); if (gInitialized) { coreSetError("jlInit called while already initialized"); return false; } if (config == NULL) { coreSetError("jlInit called with NULL config"); return false; } memcpy(&gConfig, config, sizeof(gConfig)); // jlpInit must run before stageAlloc: on IIgs the stage's pixel // buffer comes from jlpStageAllocPixels, which depends on shadow / // SHR setup that jlpInit performs. if (!jlpInit(&gConfig)) { const char *halMsg = jlpLastError(); coreSetError(halMsg != NULL ? halMsg : "jlpInit failed"); return false; } if (!stageAlloc()) { coreSetError("failed to allocate stage surface"); jlpShutdown(); return false; } if (!codegenArenaInit(gConfig.codegenBytes != 0 ? gConfig.codegenBytes : DEFAULT_CODEGEN_BYTES)) { coreSetError("failed to allocate codegen arena"); stageFree(); jlpShutdown(); return false; } jlpInputInit(); gInitialized = true; return true; } const char *jlLastError(void) { return gLastError; } const char *jlPlatformName(void) { return JOEYLIB_PLATFORM_NAME; } void jlShutdown(void) { if (!gInitialized) { return; } jlpInputShutdown(); // Reset compiled-sprite state (slot pointers, routine offsets, patch // caches) in every registered sprite BEFORE the arena teardown frees // the slot structs -- see spriteSystemShutdown in spriteInternal.h // (PERF-AUDIT.md #34). spriteSystemShutdown(); codegenArenaShutdown(); stageFree(); jlpShutdown(); gInitialized = false; clearError(); } const char *jlVersionString(void) { return JOEYLIB_VERSION_STRING; } void jlWaitVBL(void) { jlpWaitVBL(); } uint16_t jlFrameCount(void) { return jlpFrameCount(); } uint16_t jlFrameHz(void) { return jlpFrameHz(); } uint32_t jlMillisElapsed(void) { return jlpMillisElapsed(); }