joeylib2/docs/ApiCore.adoc

316 lines
9 KiB
Text

== Core and Lifecycle
Declared in `<joey/core.h>`, included by `<joey/joey.h>`.
=== 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 a PAL Amiga and 60 on an NTSC one, whose screen is in the
machine's own standard; 50 or 60 on the ST, as the machine is set (71 with a
monochrome monitor); 55 on the X68000; 70 on DOS's VGA 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.