joeylib2/src/core/port.h

1072 lines
46 KiB
C

// Internal platform-override registry (the new joeylib's joey.h equivalent).
//
// Every overridable operation is a jlp<Op>() resolved at COMPILE TIME: a
// portable-C default lives in src/generic/ and a machine supplies its own
// version when it defines JL_HAS_<OP> in include/joey/platform.h. There is no
// runtime dispatch. This header wires the two together (the dispatch tail at
// the bottom) and holds the IIgs asm-macro overrides.
//
// Two flavors of op (both use the same jlp + JL_HAS mechanism):
// (A) ops with a genuine portable generic -- draw/tile/sprite primitives,
// surface readers, allocation, millisElapsed. A machine overrides only
// where it needs native speed/behavior; otherwise it links the generic.
// (B) platform SERVICES with no portable behavior -- init/shutdown/present,
// input, vbl/frame timing, the audio engine. The "generic" is a no-op/
// zero stub so a blank/template port still links; every real port
// overrides via JL_HAS_<OP>.
//
// Included by src/core/*.c, src/<machine>/*.c, and src/generic/*.c. NOT public.
#ifndef JOEYLIB_PORT_H
#define JOEYLIB_PORT_H
#include <stdio.h>
#include "joey/audio.h"
#include "joey/core.h"
#include "joey/input.h"
#include "joey/serial.h"
#include "joey/sprite.h"
#include "joey/surface.h"
// Every op's declaration + its generic/override dispatch lives in the "Op
// dispatch" section at the bottom of this header. The block immediately below
// holds the IIgs's asm-macro overrides: it #define-s jlp<Op> directly to the
// iigs*Inner asm entry points, so IIgs primitives never pay a function call.
#ifdef JOEYLIB_PLATFORM_IIGS
// Root the save tree at the APPLICATION directory via the GS/OS "1/" prefix
// designator (the Loader sets prefix 1 to the app's directory at launch).
// Unrooted paths resolve against prefix 0, which a Finder launch leaves
// pointing away from (or nowhere near) the app's volume - measured: every
// plain "SAVES/x" open failed under MAME GS/OS 6 while the same file opened
// fine through "1/SAVES/x". libc's fopen passes the string verbatim to
// GS/OS Open, which accepts prefix-designator syntax.
#define JL_SAVE_DIR "1/SAVES"
#define JL_SAVE_DIR_PREFIX "1/SAVES/"
// =====================================================================
// IIgs direct-dispatch macros.
//
// IIgs overrides the draw/tile/flood primitives by #define-ing jlp<Op>
// directly to its asm entry points (iigs*Inner), so each primitive inlines
// the asm call at the call site with no function-call plumbing. A C wrapper
// would cost ~60-80 cyc/call of pure overhead here (prologue PHB/PHD/TCD,
// redundant arg re-push for the inner JSL, then epilogue); the macro form
// eliminates it. Cross-platform code in src/core/*.c calls jlp<Op>(...)
// unchanged and the preprocessor swaps in the asm before compile.
//
// Ops the IIgs does not accelerate (or only accelerates for the stage)
// fall through to the portable-C generic jlpGeneric<Op>, like every other
// machine. Some macros use comma-expression form to evaluate to a `bool`
// (e.g. fillCircle: stage-only, returns false off-stage so the core falls
// back to its C span fill).
// =====================================================================
extern void iigsDrawPixelInner (uint8_t *pixels, uint16_t x, uint16_t y, uint16_t nibble);
extern void iigsDrawPixelMark (uint16_t x, uint16_t y, uint16_t nibble);
extern void iigsDrawLineInner (uint8_t *pixels, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t nibble);
extern void iigsDrawCircleInner (uint8_t *pixels, uint16_t cx, uint16_t cy, uint16_t r, uint16_t nibble);
extern void iigsFillCircleInner (uint8_t *pixels, uint16_t cx, uint16_t cy, uint16_t r, uint16_t nibble);
extern void iigsSurfaceClearInner(uint8_t *pixels, uint16_t fillWord);
extern void iigsSurfaceClearFastInner(uint8_t *pixels, uint16_t fillWord);
extern void iigsTileFillInner (uint8_t *dstRow0, uint16_t nibble);
extern void iigsTileFillMark (uint16_t nibble, uint16_t bx, uint16_t by);
extern void iigsTileCopyInner (uint8_t *dstRow0, const uint8_t *srcRow0);
extern void iigsTileCopyMark (const uint8_t *srcRow0, uint16_t dstBx, uint16_t dstBy);
extern void iigsTileCopyMaskedInner(uint8_t *dstRow0, const uint8_t *srcRow0, uint16_t transparent);
extern void iigsTilePasteInner (uint8_t *dstRow0, const uint8_t *srcTilePixels);
extern void iigsTilePasteMark (const uint8_t *srcTilePixels, uint16_t bx, uint16_t by);
extern void iigsTileMapPasteMark (uint16_t bx, uint16_t by, uint16_t wTiles, uint16_t hTiles, const uint8_t *tiles, const uint8_t *map);
extern void iigsTilePasteMonoInner(uint8_t *dstRow0, const uint8_t *monoTile, uint16_t fgColor, uint16_t bgColor);
extern void iigsTileSnapInner (uint8_t *dstTilePixels, const uint8_t *srcRow0);
extern void iigsCopyRectRows (uint8_t *dst, uint16_t srcLo, uint16_t srcBank, uint16_t rowBytes, uint16_t rows);
extern void iigsQuitGS (void);
extern void iigsFillRectMark (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t nibble);
extern void iigsFillRectNoMark (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t nibble);
extern void iigsFloodWalkAndScansInner(uint8_t *pixels, uint16_t x, uint16_t y, uint16_t matchColor, uint16_t newColor, uint16_t matchEqual, int16_t *stackX, int16_t *stackY, uint16_t *spInOut, uint16_t maxSp);
extern uint16_t gFloodSeedMatch;
extern uint16_t gFloodLeftX;
extern uint16_t gFloodRightX;
// Stage identity for the stage-gated macros below: a direct extern
// read of surface.c's gStage (also declared in surfaceInternal.h --
// duplicated here because port.h is included by TUs that never pull in
// surfaceInternal.h) instead of the jlStageGet() accessor, which costs
// a cross-segment JSL per primitive/span (PERF-AUDIT #10/#17). Same
// direct-extern precedent as codegenArenaInternal.h's arena globals.
// Read-only here.
extern jlSurfaceT *gStage;
// drawPixel: migrated to jlpDrawPixel. The asm plots into s->pixels for any
// IIgs (chunky) surface; no generic defer needed.
#define jlpDrawPixel(_s, _x, _y, _c) \
iigsDrawPixelInner((_s)->pixels, (uint16_t)(_x), (uint16_t)(_y), \
(uint16_t)((_c) & 0x0F))
// drawLine / drawCircle migrated to jlp<Op>: asm on any IIgs (chunky) surface,
// callers gate the on-surface fast path on JL_HAS_DRAW_LINE / _CIRCLE.
#define jlpDrawLine(_s, _x0, _y0, _x1, _y1, _c) \
iigsDrawLineInner((_s)->pixels, (uint16_t)(_x0), (uint16_t)(_y0), \
(uint16_t)(_x1), (uint16_t)(_y1), \
(uint16_t)((_c) & 0x0F))
#define jlpDrawCircle(_s, _cx, _cy, _r, _c) \
iigsDrawCircleInner((_s)->pixels, (uint16_t)(_cx), (uint16_t)(_cy), \
(_r), (uint16_t)((_c) & 0x0F))
// fillCircle migrated to jlpFillCircle: stage-only asm, returns bool so the core
// falls to the C span fill for non-stage / off-surface. JL_HAS_FILL_CIRCLE-gated.
// The asm derives its fill bytes from the raw nibble (PERF-AUDIT #41): the
// macro passes (c & 0x0F), never the old * 0x1111 fill word, which lowered
// to a software-multiply helper call for a runtime color (the only call
// site, draw.c, always passes a variable). The jlp layer owns the & 0x0F.
#define jlpFillCircle(_s, _cx, _cy, _r, _c) \
((_s) == gStage \
? (iigsFillCircleInner((_s)->pixels, (uint16_t)(_cx), (uint16_t)(_cy), \
(_r), (uint16_t)((_c) & 0x0F)), \
true) \
: false)
// surfaceClear: migrated to the jlp<Op> override model. The asm clears the
// stage surface; non-stage surfaces defer to the portable-C generic default.
#define jlpSurfaceClear(_s, _d) \
((_s) == gStage \
? iigsSurfaceClearFastInner((_s)->pixels, \
(uint16_t)((uint16_t)(_d) | ((uint16_t)(_d) << 8))) \
: jlpGenericSurfaceClear((_s), (_d)))
// fillRect: STAGE-GATED onto the FUSED Mark entry (NATIVE-PERF Phase 6
// W1, the tile Step-D pattern): ONE JSL derives the destination in asm
// from the pinned stage base + gRowOffsetLut, fills W x H with the same
// leading/middle/trailing nibble handling as the old Inner, and widens
// the H-row dirty band via stageRectMark -- replacing the fill JSL plus
// the C-side surfaceMarkDirtyRect -> iigsMarkDirtyRowsInner JSL pair
// (draw.c compiles that mark out under JL_FILL_RECT_MARK_FUSED below).
// Non-stage surfaces defer to the chunky generic and need no mark. The
// asm derives the fill word from the raw nibble (PERF-AUDIT #41); the
// jlp layer owns the & 0x0F.
#define jlpFillRect(_s, _x, _y, _w, _h, _c) \
((_s) == gStage \
? iigsFillRectMark((uint16_t)(_x), (uint16_t)(_y), \
(uint16_t)(_w), (uint16_t)(_h), \
(uint16_t)((_c) & 0x0F)) \
: jlpGenericFillRect((_s), (_x), (_y), (_w), (_h), (_c)))
// Unfused twin for callers that batch their own mark (jlDrawRect's
// on-surface fast path keeps its single whole-outline mark instead of
// paying two (h-2)-row fused walks on the vertical edges; jlFillCircle
// batches one bounding-square mark over its spans). Same body in asm;
// a flag skips the stageRectMark tail.
#define jlpFillRectNoMark(_s, _x, _y, _w, _h, _c) \
((_s) == gStage \
? iigsFillRectNoMark((uint16_t)(_x), (uint16_t)(_y), \
(uint16_t)(_w), (uint16_t)(_h), \
(uint16_t)((_c) & 0x0F)) \
: jlpGenericFillRect((_s), (_x), (_y), (_w), (_h), (_c)))
// The stage arm of jlpFillRect fuses its dirty mark into the asm.
#define JL_FILL_RECT_MARK_FUSED 1
// Tile primitives operate on caller-computed row pointers; just
// forward the args. by/bx are tile coords -> bx*4 + by*8*160 byte
// offset within the surface. Use SURFACE_ROW_OFFSET (LUT lookup) to
// dodge a software multiply helper for the *160 multiply.
// tileFill / tileCopy / tilePaste are STAGE-GATED onto the FUSED Mark
// entries (NATIVE-PERF Phase 2 Step D / R2): for the stage, ONE JSL
// derives the destination in asm from the pinned stage base + the
// gRowOffsetLut, runs the op, and widens the dirty band -- replacing
// C-side pointer math plus two JSLs (op inner + iigsMarkDirtyRowsInner
// via tile.c's tileMarkDirty, which is compiled out for these three
// ops on IIgs). Non-stage surfaces keep the pointer-based inners.
// tileFill: the macro passes the raw nibble (the jlp layer owns the & 0x0F);
// the asm derives the $NNNN fill word (PERF-AUDIT #41 -- the old C-side
// * 0x1111 lowered to a software-multiply helper for a runtime color).
#define jlpTileFill(_s, _bx, _by, _c) \
((_s) == gStage \
? iigsTileFillMark((uint16_t)((_c) & 0x0F), (uint16_t)(_bx), (uint16_t)(_by)) \
: iigsTileFillInner(&(_s)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_by) << 3) \
+ ((uint16_t)(_bx) << 2)], \
(uint16_t)((_c) & 0x0F)))
// tileCopy / tileCopyMasked: take surface+tile-coords; the asm macro derives
// row-0 pointers (row = by*8 -> SURFACE_ROW_OFFSET(by<<3); col byte = bx*4 -> bx<<2).
// tileCopy's src may be ANY surface, so its row-0 pointer stays C-derived
// even on the fused stage-dst arm.
#define jlpTileCopy(_d, _dbx, _dby, _s, _sbx, _sby) \
((_d) == gStage \
? iigsTileCopyMark(&(_s)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_sby) << 3) + ((uint16_t)(_sbx) << 2)], (uint16_t)(_dbx), (uint16_t)(_dby)) \
: iigsTileCopyInner(&(_d)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_dby) << 3) + ((uint16_t)(_dbx) << 2)], \
&(_s)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_sby) << 3) + ((uint16_t)(_sbx) << 2)]))
#define jlpTileCopyMasked(_d, _dbx, _dby, _s, _sbx, _sby, _t) \
iigsTileCopyMaskedInner(&(_d)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_dby) << 3) + ((uint16_t)(_dbx) << 2)], \
&(_s)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_sby) << 3) + ((uint16_t)(_sbx) << 2)], \
(uint16_t)(_t))
// tilePaste / tileSnap: take surface+coords + the chunky tile buffer; the asm
// macros derive row-0.
#define jlpTilePaste(_d, _bx, _by, _ct) \
((_d) == gStage \
? iigsTilePasteMark((_ct), (uint16_t)(_bx), (uint16_t)(_by)) \
: iigsTilePasteInner(&(_d)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_by) << 3) + ((uint16_t)(_bx) << 2)], (_ct)))
#define jlpTileSnap(_s, _bx, _by, _out) \
iigsTileSnapInner((_out), &(_s)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_by) << 3) + ((uint16_t)(_bx) << 2)])
// tilePasteMono: fused mono colorize + paste in one asm pass (PERF-AUDIT #3)
// -- no intermediate jlTileT and no second paste call. Stage-only like the
// other stage-gated macros; non-stage surfaces defer to the portable-C
// generic (colorize + jlpTilePaste). fg/bg arrive PRE-MASKED to 4 bits (the
// public jlTilePasteMono wrapper owns the & 0x0F, PERF-AUDIT #69), so they
// are passed through un-re-masked per the dispatch-stanza contract below.
#define jlpTilePasteMono(_d, _bx, _by, _mt, _fg, _bg) \
((_d) == gStage \
? iigsTilePasteMonoInner(&(_d)->pixels[SURFACE_ROW_OFFSET((uint16_t)(_by) << 3) + ((uint16_t)(_bx) << 2)], \
(_mt), (uint16_t)(_fg), (uint16_t)(_bg)) \
: jlpGenericTilePasteMono((_d), (_bx), (_by), (_mt), (_fg), (_bg)))
// Flood: IIgs overrides only the all-in-one tier (JL_HAS_FLOOD_WALK_AND_SCANS).
// The dispatch tail leaves jlpFloodWalkAndScans defined here; the planar flood
// hooks (jlpFloodWalkPlanes / jlpFloodScanRowPlanes) belong to the planar 68k
// ports (Amiga + Atari ST) and IIgs never declares them (their call sites are
// #if'd out on ports without the JL_HAS_FLOOD_*_PLANES flags).
//
// Tier-1 flood: multi-output. Asm sets gFloodSeedMatch / gFloodLeftX /
// gFloodRightX; macro reads those into the caller's out-ptrs.
#define jlpFloodWalkAndScans(_pix, _x, _y, _mc, _nc, _me, _sx, _sy, _sp, _ms, _smOut, _lxOut, _rxOut) \
(iigsFloodWalkAndScansInner((_pix), (uint16_t)(_x), (uint16_t)(_y), \
(uint16_t)((_mc) & 0x0F), \
(uint16_t)((_nc) & 0x0F), \
(uint16_t)((_me) ? 1 : 0), \
(_sx), (_sy), \
(uint16_t *)(_sp), \
(uint16_t)(_ms)), \
*(_smOut) = (gFloodSeedMatch != 0), \
*(_lxOut) = (int16_t)gFloodLeftX, \
*(_rxOut) = (int16_t)gFloodRightX, \
true)
// =====================================================================
// IIgs planar-hook elision.
//
// The planar dual-write hooks (surfaceCopyPlanes, the sprite *Planes ops)
// let Amiga/ST mirror draws into their bitplanes. IIgs is chunky-native, so
// it #define-s them to ((void)0): the call site is deleted at preprocess time
// rather than paying a cross-segment JSL/RTL for a no-op. Arg lists match each
// op's arity so the preprocessor parses and discards the exact call; all args
// are plain lvalues / field reads, so dropping them changes no behavior. (The
// other chunky ports get the same elision from the shared chunky block after
// the IIgs section -- PERF-AUDIT #42. The tile/fillRect planar work is folded
// into each jlp<Op> op, so there are no separate planar hooks left to elide
// for those.)
// =====================================================================
#define jlpSurfaceCopyPlanes(_dst, _src) ((void)0)
// sprite planes mirror: no-op on IIgs (chunky -> codegen does the work).
#define jlpSpriteDrawPlanes(_s, _sp, _x, _y) ((void)0)
#define jlpSpriteSavePlanes(_s, _x, _y, _w, _h, _dst) ((void)0)
#define jlpSpriteRestorePlanes(_s, _x, _y, _w, _h, _src) ((void)0)
// frameCount: straight to the GetTick asm wrapper (peislam.s). The hal.c
// C wrapper this replaces was one of two pure forwarding layers between
// jlFrameCount and the Misc Toolset GetTick call -- ~60-80 cycles each on
// a clock that game loops (and the UBER bench loop) poll constantly.
extern uint16_t iigsGetTickWord(void);
#define jlpFrameCount() iigsGetTickWord()
// =====================================================================
// JL_HAS registry consistency checks (PERF-AUDIT #26).
//
// The dispatch stanzas below short-circuit on #if !defined(jlp<Op>)
// before the JL_HAS_<OP> flag is ever tested, and the core fast-path
// gates (JL_HAS_DRAW_LINE / DRAW_CIRCLE / FILL_CIRCLE /
// FLOOD_WALK_AND_SCANS in core/draw.c) consult only the flag. So a
// macro override above whose flag is missing from the
// include/joey/platform.h registry either turns that registry into
// false documentation or silently drops a core fast path back to
// generic C on the perf-reference machine. These checks make a
// missing/removed flag a compile failure instead: one #error per
// macro override defined above. (The reverse drift -- flag present,
// macro removed -- already fails loudly: the dispatch stanza then
// declares a function override that nothing defines.) Extend this
// list whenever a new IIgs macro override is added.
//
// Deliberately unchecked: jlpFrameCount (registry documents it as
// macro-only, no JL_HAS_FRAME_COUNT on IIgs) and jlpSurfaceCopyPlanes
// (JL_HAS_SURFACE_COPY_PLANES means a real planar function override;
// the IIgs elision above pairs with the JL_HAS_SPRITE_* trio instead).
// =====================================================================
#if !defined(JL_HAS_DRAW_PIXEL)
#error "IIgs jlpDrawPixel macro without JL_HAS_DRAW_PIXEL in platform.h"
#endif
#if !defined(JL_HAS_DRAW_LINE)
#error "IIgs jlpDrawLine macro without JL_HAS_DRAW_LINE in platform.h"
#endif
#if !defined(JL_HAS_DRAW_CIRCLE)
#error "IIgs jlpDrawCircle macro without JL_HAS_DRAW_CIRCLE in platform.h"
#endif
#if !defined(JL_HAS_FILL_CIRCLE)
#error "IIgs jlpFillCircle macro without JL_HAS_FILL_CIRCLE in platform.h"
#endif
#if !defined(JL_HAS_SURFACE_CLEAR)
#error "IIgs jlpSurfaceClear macro without JL_HAS_SURFACE_CLEAR in platform.h"
#endif
#if !defined(JL_HAS_FILL_RECT)
#error "IIgs jlpFillRect macro without JL_HAS_FILL_RECT in platform.h"
#endif
#if !defined(JL_HAS_TILE_FILL)
#error "IIgs jlpTileFill macro without JL_HAS_TILE_FILL in platform.h"
#endif
#if !defined(JL_HAS_TILE_COPY)
#error "IIgs jlpTileCopy macro without JL_HAS_TILE_COPY in platform.h"
#endif
#if !defined(JL_HAS_TILE_COPY_MASKED)
#error "IIgs jlpTileCopyMasked macro without JL_HAS_TILE_COPY_MASKED in platform.h"
#endif
#if !defined(JL_HAS_TILE_PASTE)
#error "IIgs jlpTilePaste macro without JL_HAS_TILE_PASTE in platform.h"
#endif
#if !defined(JL_HAS_TILE_PASTE_MONO)
#error "IIgs jlpTilePasteMono macro without JL_HAS_TILE_PASTE_MONO in platform.h"
#endif
#if !defined(JL_HAS_TILE_SNAP)
#error "IIgs jlpTileSnap macro without JL_HAS_TILE_SNAP in platform.h"
#endif
#if !defined(JL_HAS_SPRITE_DRAW)
#error "IIgs jlpSpriteDrawPlanes elision without JL_HAS_SPRITE_DRAW in platform.h"
#endif
#if !defined(JL_HAS_SPRITE_SAVE)
#error "IIgs jlpSpriteSavePlanes elision without JL_HAS_SPRITE_SAVE in platform.h"
#endif
#if !defined(JL_HAS_SPRITE_RESTORE)
#error "IIgs jlpSpriteRestorePlanes elision without JL_HAS_SPRITE_RESTORE in platform.h"
#endif
#if !defined(JL_HAS_FLOOD_WALK_AND_SCANS)
#error "IIgs jlpFloodWalkAndScans macro without JL_HAS_FLOOD_WALK_AND_SCANS in platform.h"
#endif
#endif /* JOEYLIB_PLATFORM_IIGS */
// =====================================================================
// 68k tile ops as always-inline headers (NATIVE-PERF Phase 2 R4a).
//
// The ST/Amiga tile overrides live in stTile.h / amigaTile.h as
// static inline functions named stTile* / amigaTile*. Aliasing the
// jlpTile* dispatch names to them HERE (before the dispatch tail)
// makes the tail's #if !defined(jlpTile*) guards skip their extern
// prototypes -- same mechanism as the IIgs macro block above -- and
// every caller (tile.c wrappers, genericTile.c fallback routing)
// inlines the op instead of paying a second cdecl call. The headers
// themselves are #included at the END of this file so their
// portData == NULL fallbacks can see the jlpGenericTile* prototypes
// declared in the dispatch tail.
// =====================================================================
#if defined(JOEYLIB_PLATFORM_ATARIST)
#define jlpTileFill stTileFill
#define jlpTileCopy stTileCopy
#define jlpTileCopyMasked stTileCopyMasked
#define jlpTilePaste stTilePaste
#define jlpTilePasteMono stTilePasteMono
#define jlpTileSnap stTileSnap
// Whole-map engine alias (W3 seam): tile.c's jlTileMapPaste
// dispatches through this spelling when a port defines it, so the
// union-mark stanza lives exactly twice in tile.c (shared arm +
// generic) no matter how many ports bring engines.
#define jlpTileMapPaste stTileMapPaste
#elif defined(JOEYLIB_PLATFORM_AMIGA)
#define jlpTileFill amigaTileFill
#define jlpTileCopy amigaTileCopy
#define jlpTileCopyMasked amigaTileCopyMasked
#define jlpTilePaste amigaTilePaste
#define jlpTilePasteMono amigaTilePasteMono
#define jlpTileSnap amigaTileSnap
#define jlpTileMapPaste amigaTileMapPaste
#elif defined(JOEYLIB_PLATFORM_X68000)
#define jlpTileFill x68kTileFill
#define jlpTileCopy x68kTileCopy
#define jlpTileCopyMasked x68kTileCopyMasked
#define jlpTilePaste x68kTilePaste
#define jlpTilePasteMono x68kTilePasteMono
#define jlpTileSnap x68kTileSnap
#define jlpTileMapPaste x68kTileMapPaste
#elif defined(JOEYLIB_PLATFORM_DOS)
// DOS overrides only the whole-map walker (dosTile.h, W3); the
// single-tile ops stay on the chunky generics.
#define jlpTileMapPaste dosTileMapPaste
#endif
// =====================================================================
// Chunky planar-hook elision for the other chunky ports (PERF-AUDIT #42).
//
// DOS and the blank template are chunky-native like the IIgs: the planar
// dual-write hooks have no bitplanes to mirror into. Elide them to
// ((void)0) exactly like the IIgs block above instead of paying a real
// call into the empty generic bodies (genericSprite.c and
// jlpGenericSurfaceCopyPlanes) on every sprite draw/save/restore and
// surface copy. Those generic no-op bodies stay in the build only as the
// link-time safety net for a new planar port that has not implemented
// its hooks yet.
// =====================================================================
#if defined(JOEYLIB_NATIVE_CHUNKY) && !defined(JOEYLIB_PLATFORM_IIGS)
#define jlpSurfaceCopyPlanes(_dst, _src) ((void)0)
#define jlpSpriteDrawPlanes(_s, _sp, _x, _y) ((void)0)
#define jlpSpriteSavePlanes(_s, _x, _y, _w, _h, _dst) ((void)0)
#define jlpSpriteRestorePlanes(_s, _x, _y, _w, _h, _src) ((void)0)
#endif /* JOEYLIB_NATIVE_CHUNKY && !JOEYLIB_PLATFORM_IIGS */
// =====================================================================
// Migrated-op dispatch (generic default unless a machine overrides).
//
// jlpGenericSurfaceClear is the portable-C reference (src/generic/genericDraw.c),
// always available. Machine overrides are selected at compile time via
// JL_HAS_<OP> (include/joey/platform.h): a macro override (IIgs, above) already
// #define-d jlp<Op>; a function override (Amiga/ST) just declares its prototype
// here; otherwise jlp<Op> aliases the generic default. No runtime dispatch.
// =====================================================================
void jlpGenericSurfaceClear(jlSurfaceT *s, uint8_t doubled);
#if !defined(jlpSurfaceClear)
#if defined(JL_HAS_SURFACE_CLEAR)
void jlpSurfaceClear(jlSurfaceT *s, uint8_t doubled); /* function override */
#else
#define jlpSurfaceClear(_s, _d) jlpGenericSurfaceClear((_s), (_d))
#endif
#endif
void jlpGenericDrawPixel(jlSurfaceT *s, uint16_t x, uint16_t y, uint8_t colorIndex);
#if !defined(jlpDrawPixel)
#if defined(JL_HAS_DRAW_PIXEL)
void jlpDrawPixel(jlSurfaceT *s, uint16_t x, uint16_t y, uint8_t colorIndex); /* function override */
#else
#define jlpDrawPixel(_s, _x, _y, _c) jlpGenericDrawPixel((_s), (_x), (_y), (_c))
#endif
#endif
// drawLine / drawCircle have no generic default: the portable fallback is the
// clipping Bresenham in core/draw.c, and the on-surface fast path is gated by
// `#if defined(JL_HAS_DRAW_LINE/_CIRCLE)`. So here we only declare the function
// override prototype for non-macro overriders (Amiga/ST).
#if defined(JL_HAS_DRAW_LINE) && !defined(jlpDrawLine)
void jlpDrawLine(jlSurfaceT *s, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t colorIndex);
#endif
#if defined(JL_HAS_DRAW_CIRCLE) && !defined(jlpDrawCircle)
void jlpDrawCircle(jlSurfaceT *s, int16_t cx, int16_t cy, uint16_t r, uint8_t colorIndex);
#endif
// fillCircle returns bool (stage/portData check is inherent); the platform
// dispatch is compile-time via JL_HAS_FILL_CIRCLE.
#if defined(JL_HAS_FILL_CIRCLE) && !defined(jlpFillCircle)
bool jlpFillCircle(jlSurfaceT *s, int16_t cx, int16_t cy, uint16_t r, uint8_t colorIndex);
#endif
void jlpGenericFillRect(jlSurfaceT *s, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t colorIndex);
#if !defined(jlpFillRect)
#if defined(JL_HAS_FILL_RECT)
void jlpFillRect(jlSurfaceT *s, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t colorIndex); /* function override (planar) */
#else
#define jlpFillRect(_s, _x, _y, _w, _h, _c) jlpGenericFillRect((_s), (_x), (_y), (_w), (_h), (_c))
#endif
#endif
// jlpFillRectNoMark: fillRect guaranteed not to touch dirty state. On
// every port but the IIgs the ops never mark, so it IS jlpFillRect;
// the IIgs macro block above overrides it with the unfused asm twin.
#if !defined(jlpFillRectNoMark)
#define jlpFillRectNoMark(_s, _x, _y, _w, _h, _c) jlpFillRect((_s), (_x), (_y), (_w), (_h), (_c))
#endif
void jlpGenericTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex);
#if !defined(jlpTileFill)
#if defined(JL_HAS_TILE_FILL)
void jlpTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex); /* function override (planar) */
#else
#define jlpTileFill(_s, _bx, _by, _c) jlpGenericTileFill((_s), (_bx), (_by), (_c))
#endif
#endif
void jlpGenericTileCopy(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy, const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy);
#if !defined(jlpTileCopy)
#if defined(JL_HAS_TILE_COPY)
void jlpTileCopy(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy, const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy);
#else
#define jlpTileCopy(_d, _dbx, _dby, _s, _sbx, _sby) jlpGenericTileCopy((_d), (_dbx), (_dby), (_s), (_sbx), (_sby))
#endif
#endif
void jlpGenericTileCopyMasked(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy, const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy, uint8_t transparent);
#if !defined(jlpTileCopyMasked)
#if defined(JL_HAS_TILE_COPY_MASKED)
void jlpTileCopyMasked(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy, const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy, uint8_t transparent);
#else
#define jlpTileCopyMasked(_d, _dbx, _dby, _s, _sbx, _sby, _t) jlpGenericTileCopyMasked((_d), (_dbx), (_dby), (_s), (_sbx), (_sby), (_t))
#endif
#endif
void jlpGenericTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const uint8_t *chunkyTile);
#if !defined(jlpTilePaste)
#if defined(JL_HAS_TILE_PASTE)
void jlpTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const uint8_t *chunkyTile);
#else
#define jlpTilePaste(_d, _bx, _by, _ct) jlpGenericTilePaste((_d), (_bx), (_by), (_ct))
#endif
#endif
void jlpGenericTileSnap(const jlSurfaceT *src, uint8_t bx, uint8_t by, uint8_t *chunkyOut);
#if !defined(jlpTileSnap)
#if defined(JL_HAS_TILE_SNAP)
void jlpTileSnap(const jlSurfaceT *src, uint8_t bx, uint8_t by, uint8_t *chunkyOut);
#else
#define jlpTileSnap(_s, _bx, _by, _out) jlpGenericTileSnap((_s), (_bx), (_by), (_out))
#endif
#endif
// tilePasteMono color-masking contract (PERF-AUDIT #69): fgColor/bgColor
// arrive already masked to 4 bits -- the public jlTilePasteMono wrapper owns
// the & 0x0F. Neither the generic nor a machine override may re-mask.
// (tileFill is the opposite: its wrapper does NOT mask, so the jlp layer owns
// the & 0x0F there -- see the IIgs macro above and the planar overrides.)
void jlpGenericTilePasteMono(jlSurfaceT *dst, uint8_t bx, uint8_t by, const uint8_t *monoTile, uint8_t fgColor, uint8_t bgColor);
#if !defined(jlpTilePasteMono)
#if defined(JL_HAS_TILE_PASTE_MONO)
void jlpTilePasteMono(jlSurfaceT *dst, uint8_t bx, uint8_t by, const uint8_t *monoTile, uint8_t fgColor, uint8_t bgColor);
#else
#define jlpTilePasteMono(_d, _bx, _by, _mt, _fg, _bg) jlpGenericTilePasteMono((_d), (_bx), (_by), (_mt), (_fg), (_bg))
#endif
#endif
void jlpGenericSpriteDrawPlanes(jlSurfaceT *s, const jlSpriteT *sp, int16_t x, int16_t y);
#if !defined(jlpSpriteDrawPlanes)
#if defined(JL_HAS_SPRITE_DRAW)
void jlpSpriteDrawPlanes(jlSurfaceT *s, const jlSpriteT *sp, int16_t x, int16_t y);
#else
#define jlpSpriteDrawPlanes(_s, _sp, _x, _y) jlpGenericSpriteDrawPlanes((_s), (_sp), (_x), (_y))
#endif
#endif
void jlpGenericSpriteSavePlanes(const jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t *dstPlaneBytes);
#if !defined(jlpSpriteSavePlanes)
#if defined(JL_HAS_SPRITE_SAVE)
void jlpSpriteSavePlanes(const jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t *dstPlaneBytes);
#else
#define jlpSpriteSavePlanes(_s, _x, _y, _w, _h, _dst) jlpGenericSpriteSavePlanes((_s), (_x), (_y), (_w), (_h), (_dst))
#endif
#endif
void jlpGenericSpriteRestorePlanes(jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, const uint8_t *srcPlaneBytes);
#if !defined(jlpSpriteRestorePlanes)
#if defined(JL_HAS_SPRITE_RESTORE)
void jlpSpriteRestorePlanes(jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, const uint8_t *srcPlaneBytes);
#else
#define jlpSpriteRestorePlanes(_s, _x, _y, _w, _h, _src) jlpGenericSpriteRestorePlanes((_s), (_x), (_y), (_w), (_h), (_src))
#endif
#endif
// Flood ops (PATTERN B: no extractable generic -- the portable default is the
// inline C scanline fallback in floodFillInternal, gated per-tier by
// `#if defined(JL_HAS_FLOOD_*)` at the call site). Each real override is just a
// prototype here for the claiming machine. jlpFloodWalkAndScans is a macro on
// IIgs (defined above), so its `!defined` guard skips the proto there.
#if defined(JL_HAS_FLOOD_WALK_AND_SCANS) && !defined(jlpFloodWalkAndScans)
bool jlpFloodWalkAndScans(uint8_t *pixels, int16_t x, int16_t y, uint8_t matchColor, uint8_t newColor, bool matchEqual, int16_t *stackX, int16_t *stackY, int16_t *spInOut, int16_t maxSp, bool *seedMatched, int16_t *leftXOut, int16_t *rightXOut);
#endif
#if defined(JL_HAS_FLOOD_WALK_PLANES) && !defined(jlpFloodWalkPlanes)
bool jlpFloodWalkPlanes(const jlSurfaceT *s, int16_t startX, int16_t y, uint8_t matchColor, uint8_t newColor, bool matchEqual, bool *seedMatched, int16_t *leftXOut, int16_t *rightXOut);
#endif
#if defined(JL_HAS_FLOOD_SCAN_ROW_PLANES) && !defined(jlpFloodScanRowPlanes)
bool jlpFloodScanRowPlanes(const jlSurfaceT *s, int16_t leftX, int16_t rightX, int16_t scanY, uint8_t matchColor, uint8_t newColor, bool matchEqual, uint8_t *markBuf);
#endif
// =====================================================================
// Category B: structural port services (generic default + JL_HAS override).
//
// Unlike Category A accelerators these are resolved purely at link/compile
// time (no runtime dispatch ever existed). Each op has a portable-C default in
// src/generic/ -- a real implementation where one exists, or a no-op/zero stub
// for inherently platform-specific services so a blank/template port still
// links. A port that supplies its own version defines JL_HAS_<OP> in
// include/joey/platform.h. Same jlp<Op> stanza as Category A.
// =====================================================================
// --- Generic-only surface services (no port overrides them today) ---
void jlpGenericSurfaceCopyChunky(jlSurfaceT *dst, const jlSurfaceT *src);
#if !defined(jlpSurfaceCopyChunky)
#if defined(JL_HAS_SURFACE_COPY_CHUNKY)
void jlpSurfaceCopyChunky(jlSurfaceT *dst, const jlSurfaceT *src);
#else
#define jlpSurfaceCopyChunky(_d, _s) jlpGenericSurfaceCopyChunky((_d), (_s))
#endif
#endif
// Same-position group-snapped rect copy (jlSurfaceCopyRect payload).
// The generic body serves the chunky ports (incl. the IIgs stage --
// its pixels pointer reaches bank $01 through ordinary large-model
// stores); the planar ports override with their plane-layout walks.
void jlpGenericSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
#if !defined(jlpSurfaceCopyRect)
#if defined(JL_HAS_SURFACE_COPY_RECT)
void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
#else
#define jlpSurfaceCopyRect(_d, _s, _x, _y, _w, _h) jlpGenericSurfaceCopyRect((_d), (_s), (_x), (_y), (_w), (_h))
#endif
#endif
void jlpGenericSurfaceFreePixels(uint8_t *pixels);
#if !defined(jlpSurfaceFreePixels)
#if defined(JL_HAS_SURFACE_FREE_PIXELS)
void jlpSurfaceFreePixels(uint8_t *pixels);
#else
#define jlpSurfaceFreePixels(_p) jlpGenericSurfaceFreePixels((_p))
#endif
#endif
const char *jlpGenericLastError(void);
#if !defined(jlpLastError)
#if defined(JL_HAS_LAST_ERROR)
const char *jlpLastError(void);
#else
#define jlpLastError() jlpGenericLastError()
#endif
#endif
// --- Planar dual-write hook (no-op generic default; planar ports override) ---
void jlpGenericSurfaceCopyPlanes(jlSurfaceT *dst, const jlSurfaceT *src);
#if !defined(jlpSurfaceCopyPlanes)
#if defined(JL_HAS_SURFACE_COPY_PLANES)
void jlpSurfaceCopyPlanes(jlSurfaceT *dst, const jlSurfaceT *src);
#else
#define jlpSurfaceCopyPlanes(_d, _s) jlpGenericSurfaceCopyPlanes((_d), (_s))
#endif
#endif
// --- Surface readers (chunky generic default; planar ports decode portData) ---
uint8_t jlpGenericSamplePixel(const jlSurfaceT *s, int16_t x, int16_t y);
#if !defined(jlpSamplePixel)
#if defined(JL_HAS_SAMPLE_PIXEL)
uint8_t jlpSamplePixel(const jlSurfaceT *s, int16_t x, int16_t y);
#else
#define jlpSamplePixel(_s, _x, _y) jlpGenericSamplePixel((_s), (_x), (_y))
#endif
#endif
uint32_t jlpGenericSurfaceHash(const jlSurfaceT *s);
#if !defined(jlpSurfaceHash)
#if defined(JL_HAS_SURFACE_HASH)
uint32_t jlpSurfaceHash(const jlSurfaceT *s);
#else
#define jlpSurfaceHash(_s) jlpGenericSurfaceHash((_s))
#endif
#endif
bool jlpGenericSurfaceLoadFile(jlSurfaceT *dst, FILE *fp);
#if !defined(jlpSurfaceLoadFile)
#if defined(JL_HAS_SURFACE_LOAD_FILE)
bool jlpSurfaceLoadFile(jlSurfaceT *dst, FILE *fp);
#else
#define jlpSurfaceLoadFile(_d, _fp) jlpGenericSurfaceLoadFile((_d), (_fp))
#endif
#endif
bool jlpGenericSurfaceSaveFile(const jlSurfaceT *src, FILE *fp);
#if !defined(jlpSurfaceSaveFile)
#if defined(JL_HAS_SURFACE_SAVE_FILE)
bool jlpSurfaceSaveFile(const jlSurfaceT *src, FILE *fp);
#else
#define jlpSurfaceSaveFile(_s, _fp) jlpGenericSurfaceSaveFile((_s), (_fp))
#endif
#endif
// --- Allocation ops (chunky generic defaults; planar/pinned overrides) ---
uint8_t *jlpGenericStageAllocPixels(void);
#if !defined(jlpStageAllocPixels)
#if defined(JL_HAS_STAGE_ALLOC_PIXELS)
uint8_t *jlpStageAllocPixels(void);
#else
#define jlpStageAllocPixels() jlpGenericStageAllocPixels()
#endif
#endif
void jlpGenericStageFreePixels(uint8_t *pixels);
#if !defined(jlpStageFreePixels)
#if defined(JL_HAS_STAGE_FREE_PIXELS)
void jlpStageFreePixels(uint8_t *pixels);
#else
#define jlpStageFreePixels(_p) jlpGenericStageFreePixels((_p))
#endif
#endif
uint8_t *jlpGenericSurfaceAllocPixels(void);
#if !defined(jlpSurfaceAllocPixels)
#if defined(JL_HAS_SURFACE_ALLOC_PIXELS)
uint8_t *jlpSurfaceAllocPixels(void);
#else
#define jlpSurfaceAllocPixels() jlpGenericSurfaceAllocPixels()
#endif
#endif
void *jlpGenericSurfaceAllocPortData(jlSurfaceT *s, bool isStage);
#if !defined(jlpSurfaceAllocPortData)
#if defined(JL_HAS_SURFACE_ALLOC_PORT_DATA)
void *jlpSurfaceAllocPortData(jlSurfaceT *s, bool isStage);
#else
#define jlpSurfaceAllocPortData(_s, _st) jlpGenericSurfaceAllocPortData((_s), (_st))
#endif
#endif
void jlpGenericSurfaceFreePortData(jlSurfaceT *s, bool isStage, void *portData);
#if !defined(jlpSurfaceFreePortData)
#if defined(JL_HAS_SURFACE_FREE_PORT_DATA)
void jlpSurfaceFreePortData(jlSurfaceT *s, bool isStage, void *portData);
#else
#define jlpSurfaceFreePortData(_s, _st, _pd) jlpGenericSurfaceFreePortData((_s), (_st), (_pd))
#endif
#endif
// (jlpSurfacePlanePtr was retired in NATIVE-PERF Phase 2: its only
// callers were spriteCompile.c's 68k dispatchers, which now read
// portData directly via the inline accessors in amigaPlanar.h /
// stPlanar.h instead of paying a cross-TU call per plane.)
// --- Timing services (platform-only stub generics; millisElapsed has a real default) ---
uint16_t jlpGenericFrameHz(void);
#if !defined(jlpFrameHz)
#if defined(JL_HAS_FRAME_HZ)
uint16_t jlpFrameHz(void);
#else
#define jlpFrameHz() jlpGenericFrameHz()
#endif
#endif
uint16_t jlpGenericFrameCount(void);
#if !defined(jlpFrameCount)
#if defined(JL_HAS_FRAME_COUNT)
uint16_t jlpFrameCount(void);
#else
#define jlpFrameCount() jlpGenericFrameCount()
#endif
#endif
void jlpGenericWaitVBL(void);
#if !defined(jlpWaitVBL)
#if defined(JL_HAS_WAIT_VBL)
void jlpWaitVBL(void);
#else
#define jlpWaitVBL() jlpGenericWaitVBL()
#endif
#endif
uint32_t jlpGenericMillisElapsed(void);
#if !defined(jlpMillisElapsed)
#if defined(JL_HAS_MILLIS_ELAPSED)
uint32_t jlpMillisElapsed(void);
#else
#define jlpMillisElapsed() jlpGenericMillisElapsed()
#endif
#endif
// --- Audio services (platform-only; blank-port stub generic + JL_HAS override) ---
bool jlpGenericAudioInit(void);
#if !defined(jlpAudioInit)
#if defined(JL_HAS_AUDIO_INIT)
bool jlpAudioInit(void);
#else
#define jlpAudioInit() jlpGenericAudioInit()
#endif
#endif
void jlpGenericAudioShutdown(void);
#if !defined(jlpAudioShutdown)
#if defined(JL_HAS_AUDIO_SHUTDOWN)
void jlpAudioShutdown(void);
#else
#define jlpAudioShutdown() jlpGenericAudioShutdown()
#endif
#endif
void jlpGenericAudioPlayMod(const uint8_t *data, uint32_t length, bool loop);
#if !defined(jlpAudioPlayMod)
#if defined(JL_HAS_AUDIO_PLAY_MOD)
void jlpAudioPlayMod(const uint8_t *data, uint32_t length, bool loop);
#else
#define jlpAudioPlayMod(_a, _b, _c) jlpGenericAudioPlayMod((_a), (_b), (_c))
#endif
#endif
void jlpGenericAudioStopMod(void);
#if !defined(jlpAudioStopMod)
#if defined(JL_HAS_AUDIO_STOP_MOD)
void jlpAudioStopMod(void);
#else
#define jlpAudioStopMod() jlpGenericAudioStopMod()
#endif
#endif
bool jlpGenericAudioIsPlayingMod(void);
#if !defined(jlpAudioIsPlayingMod)
#if defined(JL_HAS_AUDIO_IS_PLAYING_MOD)
bool jlpAudioIsPlayingMod(void);
#else
#define jlpAudioIsPlayingMod() jlpGenericAudioIsPlayingMod()
#endif
#endif
void jlpGenericAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz);
#if !defined(jlpAudioPlaySfx)
#if defined(JL_HAS_AUDIO_PLAY_SFX)
void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz);
#else
#define jlpAudioPlaySfx(_a, _b, _c, _d) jlpGenericAudioPlaySfx((_a), (_b), (_c), (_d))
#endif
#endif
void jlpGenericAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
#if !defined(jlpAudioPlaySfxStream)
#if defined(JL_HAS_AUDIO_PLAY_SFX_STREAM)
void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
#else
#define jlpAudioPlaySfxStream(_a, _b, _c, _d) jlpGenericAudioPlaySfxStream((_a), (_b), (_c), (_d))
#endif
#endif
void jlpGenericAudioStopSfx(uint8_t slot);
#if !defined(jlpAudioStopSfx)
#if defined(JL_HAS_AUDIO_STOP_SFX)
void jlpAudioStopSfx(uint8_t slot);
#else
#define jlpAudioStopSfx(_a) jlpGenericAudioStopSfx((_a))
#endif
#endif
void jlpGenericAudioTone(uint16_t freqHz);
#if !defined(jlpAudioTone)
#if defined(JL_HAS_AUDIO_TONE)
void jlpAudioTone(uint16_t freqHz);
#else
#define jlpAudioTone(_a) jlpGenericAudioTone((_a))
#endif
#endif
void jlpGenericAudioVoice(uint8_t voice, uint16_t freqHz, uint8_t atten);
#if !defined(jlpAudioVoice)
#if defined(JL_HAS_AUDIO_VOICE)
void jlpAudioVoice(uint8_t voice, uint16_t freqHz, uint8_t atten);
#else
#define jlpAudioVoice(_a, _b, _c) jlpGenericAudioVoice((_a), (_b), (_c))
#endif
#endif
void jlpGenericAudioNoise(uint8_t pitch, uint8_t atten);
#if !defined(jlpAudioNoise)
#if defined(JL_HAS_AUDIO_NOISE)
void jlpAudioNoise(uint8_t pitch, uint8_t atten);
#else
#define jlpAudioNoise(_a, _b) jlpGenericAudioNoise((_a), (_b))
#endif
#endif
bool jlpGenericAudioTickRegister(jlAudioTickFnT fn, uint16_t hz);
#if !defined(jlpAudioTickRegister)
#if defined(JL_HAS_AUDIO_TICK_REGISTER)
bool jlpAudioTickRegister(jlAudioTickFnT fn, uint16_t hz);
#else
#define jlpAudioTickRegister(_a, _b) jlpGenericAudioTickRegister((_a), (_b))
#endif
#endif
void jlpGenericAudioCriticalEnter(void);
#if !defined(jlpAudioCriticalEnter)
#if defined(JL_HAS_AUDIO_CRITICAL_ENTER)
void jlpAudioCriticalEnter(void);
#else
#define jlpAudioCriticalEnter() jlpGenericAudioCriticalEnter()
#endif
#endif
void jlpGenericAudioCriticalExit(void);
#if !defined(jlpAudioCriticalExit)
#if defined(JL_HAS_AUDIO_CRITICAL_EXIT)
void jlpAudioCriticalExit(void);
#else
#define jlpAudioCriticalExit() jlpGenericAudioCriticalExit()
#endif
#endif
void jlpGenericAudioFrameTick(void);
#if !defined(jlpAudioFrameTick)
#if defined(JL_HAS_AUDIO_FRAME_TICK)
void jlpAudioFrameTick(void);
#else
#define jlpAudioFrameTick() jlpGenericAudioFrameTick()
#endif
#endif
// --- Lifecycle / present / input (platform-only; blank stub generic + JL_HAS) ---
bool jlpGenericInit(const jlConfigT *config);
#if !defined(jlpInit)
#if defined(JL_HAS_INIT)
bool jlpInit(const jlConfigT *config);
#else
#define jlpInit(_a) jlpGenericInit((_a))
#endif
#endif
void jlpGenericShutdown(void);
#if !defined(jlpShutdown)
#if defined(JL_HAS_SHUTDOWN)
void jlpShutdown(void);
#else
#define jlpShutdown() jlpGenericShutdown()
#endif
#endif
void jlpGenericPresent(const jlSurfaceT *src);
#if !defined(jlpPresent)
#if defined(JL_HAS_PRESENT)
void jlpPresent(const jlSurfaceT *src);
#else
#define jlpPresent(_a) jlpGenericPresent((_a))
#endif
#endif
void jlpGenericInputInit(void);
#if !defined(jlpInputInit)
#if defined(JL_HAS_INPUT_INIT)
void jlpInputInit(void);
#else
#define jlpInputInit() jlpGenericInputInit()
#endif
#endif
void jlpGenericInputShutdown(void);
#if !defined(jlpInputShutdown)
#if defined(JL_HAS_INPUT_SHUTDOWN)
void jlpInputShutdown(void);
#else
#define jlpInputShutdown() jlpGenericInputShutdown()
#endif
#endif
void jlpGenericInputPoll(void);
#if !defined(jlpInputPoll)
#if defined(JL_HAS_INPUT_POLL)
void jlpInputPoll(void);
#else
#define jlpInputPoll() jlpGenericInputPoll()
#endif
#endif
void jlpGenericJoystickReset(jlJoystickE js);
#if !defined(jlpJoystickReset)
#if defined(JL_HAS_JOYSTICK_RESET)
void jlpJoystickReset(jlJoystickE js);
#else
#define jlpJoystickReset(_a) jlpGenericJoystickReset((_a))
#endif
#endif
// --- Serial add-on (platform-only; blank-port stub generic + JL_HAS override) ---
// Function overrides (not IIgs asm macros), so no entry in the macro-consistency
// #error block above. Types come from joey/serial.h (included at the top).
bool jlpGenericSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config);
#if !defined(jlpSerialOpen)
#if defined(JL_HAS_SERIAL_OPEN)
bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config);
#else
#define jlpSerialOpen(_d, _c) jlpGenericSerialOpen((_d), (_c))
#endif
#endif
void jlpGenericSerialClose(void);
#if !defined(jlpSerialClose)
#if defined(JL_HAS_SERIAL_CLOSE)
void jlpSerialClose(void);
#else
#define jlpSerialClose() jlpGenericSerialClose()
#endif
#endif
void jlpGenericSerialPoll(void);
#if !defined(jlpSerialPoll)
#if defined(JL_HAS_SERIAL_POLL)
void jlpSerialPoll(void);
#else
#define jlpSerialPoll() jlpGenericSerialPoll()
#endif
#endif
uint16_t jlpGenericSerialAvailable(void);
#if !defined(jlpSerialAvailable)
#if defined(JL_HAS_SERIAL_AVAILABLE)
uint16_t jlpSerialAvailable(void);
#else
#define jlpSerialAvailable() jlpGenericSerialAvailable()
#endif
#endif
uint16_t jlpGenericSerialRead(uint8_t *buf, uint16_t max);
#if !defined(jlpSerialRead)
#if defined(JL_HAS_SERIAL_READ)
uint16_t jlpSerialRead(uint8_t *buf, uint16_t max);
#else
#define jlpSerialRead(_b, _m) jlpGenericSerialRead((_b), (_m))
#endif
#endif
uint16_t jlpGenericSerialWrite(const uint8_t *buf, uint16_t len);
#if !defined(jlpSerialWrite)
#if defined(JL_HAS_SERIAL_WRITE)
uint16_t jlpSerialWrite(const uint8_t *buf, uint16_t len);
#else
#define jlpSerialWrite(_b, _l) jlpGenericSerialWrite((_b), (_l))
#endif
#endif
void jlpGenericSerialFlush(void);
#if !defined(jlpSerialFlush)
#if defined(JL_HAS_SERIAL_FLUSH)
void jlpSerialFlush(void);
#else
#define jlpSerialFlush() jlpGenericSerialFlush()
#endif
#endif
// --- Save files + disk space (platform-only; blank-port stub generic + JL_HAS override) ---
// Function overrides (not IIgs asm macros), so no entry in the macro-consistency
// #error block above. Core layer is src/core/save.c; publics in joey/file.h.
bool jlpGenericSaveDirEnsure(const char *dir);
#if !defined(jlpSaveDirEnsure)
#if defined(JL_HAS_SAVE_DIR_ENSURE)
bool jlpSaveDirEnsure(const char *dir);
#else
#define jlpSaveDirEnsure(_d) jlpGenericSaveDirEnsure((_d))
#endif
#endif
bool jlpGenericSaveDelete(const char *path);
#if !defined(jlpSaveDelete)
#if defined(JL_HAS_SAVE_DELETE)
bool jlpSaveDelete(const char *path);
#else
#define jlpSaveDelete(_p) jlpGenericSaveDelete((_p))
#endif
#endif
uint32_t jlpGenericDiskFree(void);
#if !defined(jlpDiskFree)
#if defined(JL_HAS_DISK_FREE)
uint32_t jlpDiskFree(void);
#else
#define jlpDiskFree() jlpGenericDiskFree()
#endif
#endif
// --- Large allocation (malloc/free generic default; IIgs Memory Manager override) ---
void *jlpGenericBigAlloc(uint32_t bytes);
void jlpGenericBigFree(void *p);
#if !defined(jlpBigAlloc)
#if defined(JL_HAS_BIG_ALLOC)
void *jlpBigAlloc(uint32_t bytes);
void jlpBigFree(void *p);
#else
#define jlpBigAlloc(_b) jlpGenericBigAlloc((_b))
#define jlpBigFree(_p) jlpGenericBigFree((_p))
#endif
#endif
// Per-port tile-op inline bodies (see the alias block above the
// dispatch tail). The 68k headers are included LAST so their
// portData == NULL fallbacks can call the jlpGenericTile* prototypes
// declared above; the DOS header (whole-map engine only, W3) needs
// nothing from the tail but sits here for the same one-include-point.
#if defined(JOEYLIB_PLATFORM_ATARIST)
#include "stTile.h"
#elif defined(JOEYLIB_PLATFORM_AMIGA)
#include "amigaTile.h"
#elif defined(JOEYLIB_PLATFORM_DOS)
#include "dosTile.h"
#elif defined(JOEYLIB_PLATFORM_X68000)
#include "x68kTile.h"
#endif
#endif