// 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
