== Core and Lifecycle Declared in ``, included by ``. === jlConfigT [source,c] ---- typedef struct { uint32_t codegenBytes; bool noAudio; } jlConfigT; ---- [cols="1,1,4", options="header"] |=== |Field |Type |Description |`codegenBytes` |`uint32_t` |Size of the runtime compiled-sprite cache. `0` selects a per-platform default: 8 KB on the IIgs, 64 KB on the Amiga, ST, and X68000, and 32 KB on DOS, DVX, and Win16. The other hosted ports interpret every sprite and have no cache: they allocate none, whatever is asked. Every port takes as much of the request as the machine has, and `jlInit` fails only if it can have none of it; `jlSpriteCodegenBytesTotal()` says what it got. On the IIgs the cache is up to four Memory Manager blocks of at most 64 KB, each inside one 64 KB bank, so at most 256 KB (a bank that is refused is tried again at 60 KB); elsewhere it is one block, halved until the heap has room, down to 4 KB. Size it against `jlSpriteCompiledSize()`. |`noAudio` |`bool` |`true` leaves audio off: every audio call is silent, as on a machine without sound, and the program pays nothing for audio (on the IIgs no replayer read from disk, on DOS and the Amiga no interrupt hooked). `false` has `jlInit` start audio and `jlShutdown` stop it. |=== Set every field: a `jlConfigT` is read whole, so a field left unset is whatever the memory held. === jlInit [source,c] ---- bool jlInit(const jlConfigT *config); ---- Initialises the library: takes over the display, allocates the stage and the codegen arena, brings up input, and last starts audio (unless `noAudio`). Must be called before any other JoeyLib function. Sound that does not start is not a failure: modules and sound effects are then silent, and the log says so (<<_debug_logging>>). The tone and noise voices still play on the ports whose sound chip needs no engine: the Amiga, the ST, DOS, and the X68000. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`config` |`const jlConfigT *` |Configuration. Must not be `NULL`. |=== *Returns:* `true` on success. On failure returns `false` and `jlLastError()` describes the reason, on every port: for example, the plain ST refuses a monochrome monitor, and the Amiga says when chip RAM cannot hold the two screen pages. === jlShutdown [source,c] ---- void jlShutdown(void); ---- Shuts the library down: stops audio first, then restores the display mode, releases the library's own resources, and un-hooks any interrupt vectors the port installed. Surfaces and sprites the program created are not released: destroy them with `jlSurfaceDestroy` and `jlSpriteDestroy` first. A program that ends without calling it, by returning from `main` or calling `exit`, has it called then: the first successful `jlInit` registers it with `atexit`. Nothing can run it after a crash or an `abort`. *Parameters:* none. *Returns:* nothing. === jlLastError [source,c] ---- const char *jlLastError(void); ---- *Parameters:* none. *Returns:* the most recent error message, or `NULL` if there has been none. The string is library-owned; do not free it. === jlPlatformName [source,c] ---- const char *jlPlatformName(void); ---- *Parameters:* none. *Returns:* a human-readable platform identifier, for example `"Apple IIgs"`, `"MS-DOS"`, `"DVX"`, `"SDL3"` (the web port is the SDL3 port in a browser), `"macOS"`, or `"Windows"`. Library-owned. === jlVersionString [source,c] ---- const char *jlVersionString(void); ---- *Parameters:* none. *Returns:* the library version, for example `"1.0.0"`. Library-owned. === jlAlloc [source,c] ---- void *jlAlloc(uint32_t bytes); ---- Allocates a large or persistent buffer from the platform's native allocator rather than the small C heap. *Use this instead of `malloc` for anything substantial.* On the IIgs the whole C heap is one Memory Manager block of 32 KB to 48 KB (and, if that block cannot be had, only what the program's link left in its bank, which can be a few hundred bytes), so multi-kilobyte caches and frame buffers must come from the Memory Manager instead. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`bytes` |`uint32_t` |Number of bytes required. |=== *Returns:* a pointer to the block, or `NULL` on failure. The memory is *not* zero-filled. === jlFree [source,c] ---- void jlFree(void *p); ---- Releases a buffer from `jlAlloc`. Never mix `jlFree` and `free`: on most ports they are the same thing, but on the IIgs a `jlAlloc` block is a Memory Manager handle, and either call given the other's block corrupts memory. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`p` |`void *` |Block to release. `NULL` is accepted and ignored. |=== *Returns:* nothing. === jlMemCopy [source,c] ---- void jlMemCopy(void *dst, const void *src, uint32_t bytes); ---- Copies a block. Same contract as `memcpy`, including that the regions must not overlap, with a 32-bit byte count on every port. It is the port's libc `memcpy`; on the IIgs that is `MVN` block moves at 7 cycles per byte, split correctly at bank boundaries. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`dst` |`void *` |Destination. |`src` |`const void *` |Source. Must not overlap `dst`. |`bytes` |`uint32_t` |Byte count. |=== *Returns:* nothing. === jlWaitVBL [source,c] ---- void jlWaitVBL(void); ---- Blocks until the next display vertical blank, pacing a game loop to the display's native refresh rate. On the machine ports it waits for the display's own vertical blank. The hosted ports, and DVX in a window, have no vertical blank to wait for: there it waits for the next tick of a virtual frame clock, 60 Hz on the hosted ports and 70 Hz on DVX. A hosted window shows one frame a tick, as a display shows one a refresh (a present in a tick already shown waits for the next); a desktop that composites its windows, as every current one does, puts only whole frames on the screen, but on one that does not (Windows 3.1, a bare X11 desktop) and in a DVX window nothing stops a present from tearing. *Parameters:* none. *Returns:* nothing. === jlFrameCount [source,c] ---- uint16_t jlFrameCount(void); ---- *Parameters:* none. *Returns:* a monotonic 16-bit frame counter. An interrupt or a clock keeps it, so it counts every frame however seldom you read it -- except on an X68000 whose system refuses JoeyLib's frame interrupt, where it counts only while `jlWaitVBL`, `jlFrameCount`, or `jlStagePresent` is being called. [WARNING] ==== The counter wraps roughly every 18 minutes at 60 Hz, so compare two readings by subtracting them. For elapsed-time work prefer `jlMillisElapsed`. ==== === jlFrameHz [source,c] ---- uint16_t jlFrameHz(void); ---- *Parameters:* none. *Returns:* the nominal display frame rate in Hz -- 60 on the IIgs, or 50 on a PAL machine; 50 on the Amiga, whose screen is PAL, or 60 on an NTSC Amiga that cannot show PAL; 50 or 60 on the ST, as the machine is set; 55 on the X68000; 70 on VGA mode 13h and on DVX, whose window has a virtual 70 Hz (and whose full screen is mode 13h's, or a VBE mode's, with the same frame count); and 60 on every hosted port, whose window has a virtual 60 Hz. The X68000 times its display at `jlInit` and rounds to the nearest whole number: 55.46 Hz on the machine, 56.07 Hz under MAME, which therefore reports 56. The real cadence may drift slightly; this is the figure to divide by when converting frames to seconds. === jlMillisElapsed [source,c] ---- uint32_t jlMillisElapsed(void); ---- Monotonic millisecond counter since `jlInit`, read from a hardware timer to the millisecond on every port: the IIgs from the frame count and where the beam is, the ST and the X68000 from their system tick and the MFP timer counting down to the next, the Amiga from the E-clock, and DOS from the PIT. A machine whose timer is taken (an X68000 whose Timer C a resident program has set to another rate, an Amiga with no timer.device) falls back to the frame count and advances a frame at a time. Two calls separated by a long render report the true elapsed time, not the number of vertical blanks that fired. *Parameters:* none. *Returns:* milliseconds since `jlInit`. [NOTE] ==== Call it at least once every 15 minutes, or the time between two calls can be lost. ==== === jlRandom [source,c] ---- uint32_t jlRandom(void); ---- Deterministic pseudo-random generator, xorshift32. Produces *bit-identical* output on every JoeyLib port for a given seed, which `rand()` does not. Period 2^32^-1. Suitable for games, not for cryptography. *Parameters:* none. *Returns:* the next 32-bit value. === jlRandomRange [source,c] ---- uint16_t jlRandomRange(uint16_t bound); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`bound` |`uint16_t` |Exclusive upper bound. |=== *Returns:* a value in `[0, bound)`. A `bound` of 0 returns 0. === jlRandomSeed [source,c] ---- void jlRandomSeed(uint32_t seed); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`seed` |`uint32_t` |Any 32-bit value. A seed of 0 is remapped to 1, because xorshift cannot escape the all-zero state. |=== *Returns:* nothing.