More bug squashing.
This commit is contained in:
parent
c2be2e4b07
commit
b28916f930
214 changed files with 3161 additions and 2310 deletions
8
Makefile
8
Makefile
|
|
@ -41,7 +41,7 @@ include $(dir $(firstword $(MAKEFILE_LIST)))make/common.mk
|
|||
|
||||
ALL_TARGETS := $(JOEY_PORTS_INSTALLED)
|
||||
|
||||
.PHONY: all iigs iigs-disk iigs-verify amiga atarist ste dos x68000 dvx sdl3 web macos win32 win16 psp haiku haiku32 beos blank-check tools clean help status verify verify-audio verify-conform verify-serial docs
|
||||
.PHONY: all iigs iigs-disk iigs-game-disks iigs-verify amiga atarist ste dos x68000 dvx sdl3 web macos win32 win16 psp haiku haiku32 beos blank-check tools clean help status verify verify-audio verify-conform verify-serial docs
|
||||
|
||||
all: $(ALL_TARGETS) tools
|
||||
ifeq ($(ALL_TARGETS),)
|
||||
|
|
@ -58,6 +58,9 @@ iigs:
|
|||
iigs-disk:
|
||||
@$(MAKE) -f $(REPO_DIR)/make/iigs.mk iigs-disk
|
||||
|
||||
iigs-game-disks:
|
||||
@$(MAKE) -f $(REPO_DIR)/make/iigs.mk iigs-game-disks
|
||||
|
||||
iigs-verify:
|
||||
@$(MAKE) -f $(REPO_DIR)/make/iigs.mk iigs-verify
|
||||
|
||||
|
|
@ -169,7 +172,8 @@ status:
|
|||
|
||||
help:
|
||||
@echo "Targets: iigs amiga atarist ste dos x68000 dvx sdl3 web macos win32 win16 psp haiku haiku32 beos all clean status help"
|
||||
@echo " iigs-disk iigs-verify (IIgs MAME visual check)"
|
||||
@echo " iigs-disk (the first examples that fit one 800 KB disk) iigs-game-disks (a bootable disk for every example)"
|
||||
@echo " iigs-verify (IIgs MAME visual check)"
|
||||
@echo " blank-check (host-cc smoke test of the new-port template + gnu89 lint)"
|
||||
@echo " verify (build every port, then run every gate; PORTS= subset)"
|
||||
@echo " verify-audio (every port's audio gate; PORTS=\"atarist amiga\" for a subset)"
|
||||
|
|
|
|||
83
README.md
83
README.md
|
|
@ -71,15 +71,18 @@ hand, `source toolchains/env.sh` sets the same variables.
|
|||
the example programs (`hello`, `pattern`, `draw`, `keys`, `serial`,
|
||||
`sertest`, `save`, `tilebank`, `joy`, `sprite`, `scroll`, `audio`, `uber`,
|
||||
`adventure`, `adventure2`) for each. The IIgs is the exception:
|
||||
`make iigs` builds the library, and `make iigs-disk` builds its examples and
|
||||
packages them onto a disk image. DVX needs the `dvx` installer component,
|
||||
which unpacks DVX and its SDK from a DVX tree's distribution, `dist/dvx.zip`
|
||||
and `dist/sdk.zip` (`./install.sh --dvx-dir PATH install dvx`; the default
|
||||
is `../dvxgui`); its examples land in `build/dvx/bin/apps/kpunch/<name>/`,
|
||||
laid out as they install in DVX. SDL3 needs the `sdl3` component
|
||||
(`./install.sh install toolchain:sdl3`), which builds SDL 3 from its release
|
||||
source; its examples are native programs in `build/sdl3/bin/`, and running
|
||||
one takes seconds rather than an emulator's minutes. The web port needs the
|
||||
`make iigs` builds the library; `make iigs-disk` builds its examples and packs
|
||||
the first that fit onto one 800 KB disk image, and `make iigs-game-disks` gives
|
||||
every example a bootable disk of its own. DVX needs the `dvx` installer
|
||||
component, which unpacks DVX and its SDK from a DVX tree's distribution,
|
||||
`dist/dvx.zip` and `dist/sdk.zip` (`./install.sh --dvx-dir PATH install dvx`;
|
||||
the default is `../dvxgui`); its examples land in `build/dvx/bin/apps/kpunch/<name>/`,
|
||||
laid out as they install in DVX, and each is also a DVX add-on package,
|
||||
`build/dvx/bin/packages/<name>.dpk`, which DVX's Software program installs.
|
||||
SDL3 needs the `sdl3` component (`./install.sh install toolchain:sdl3`), which
|
||||
builds SDL 3 from its release source; its examples are native programs in
|
||||
`build/sdl3/bin/`, and running one takes seconds rather than an emulator's
|
||||
minutes. The web port needs the
|
||||
`web` components (`./install.sh install toolchain:web`: Emscripten through
|
||||
emsdk, and SDL 3 built with it); its examples are pages in
|
||||
`build/web/bin/<NAME>/`. The macOS port needs `toolchain:macos` (zig,
|
||||
|
|
@ -92,7 +95,7 @@ needs `toolchain:win16` (Open Watcom) and, to run its programs,
|
|||
your own install disc and S3 display driver). The PSP port
|
||||
needs `toolchain:psp` (pspdev's prebuilt release, and SDL 3 built with it)
|
||||
and, to run its programs, `emulator:psp` (PPSSPP's headless build, patched
|
||||
for the gates); its examples are folders `build/psp/bin/<NAME>/` holding
|
||||
for the tests); its examples are folders `build/psp/bin/<NAME>/` holding
|
||||
`EBOOT.PBP` and `DATA/`, ready for a memory stick's `PSP/GAME/`. The Haiku
|
||||
ports need `toolchain:haiku` or `toolchain:haiku32` (cross compilers built
|
||||
from Haiku R1/beta6) and, to run their programs, `emulator:haiku` or
|
||||
|
|
@ -170,7 +173,7 @@ make -f make/x68000.mk EXTRA_CFLAGS=-DJOEY_LOG_SERIAL_TEE
|
|||
```
|
||||
|
||||
|
||||
## Conformance gate
|
||||
## Conformance test
|
||||
|
||||
`examples/uber` is the conformance vehicle: it exercises every public op and
|
||||
logs a surface hash per op.
|
||||
|
|
@ -194,7 +197,7 @@ scripts/bench-web.sh && build/tools/diffUberHashes tests/goldens/uber/dos.txt bu
|
|||
scripts/bench-psp.sh && build/tools/diffUberHashes tests/goldens/uber/dos.txt build/psp/bin/UBER/joeylog.txt
|
||||
```
|
||||
|
||||
The X68000 has gates of its own:
|
||||
The X68000 has tests of its own:
|
||||
|
||||
```
|
||||
make -f make/x68000.mk x68000-verify-golden # ~1 min under the patched MAME
|
||||
|
|
@ -203,10 +206,10 @@ make -f make/x68000.mk x68000-verify-joy # both joysticks, every line, ~10
|
|||
make -f make/x68000.mk x68000-verify-serial-scb # RS-232C under per-line palettes, ~3 min
|
||||
```
|
||||
|
||||
These X68000 gates run under the patched MAME and the X68000 firmware that
|
||||
These X68000 tests run under the patched MAME and the X68000 firmware that
|
||||
`./install.sh install emulator:x68000` sets up.
|
||||
|
||||
DVX's display is gated as well: `scripts/verify-dvx-display.sh` runs UBER's
|
||||
DVX's display is tested as well: `scripts/verify-dvx-display.sh` runs UBER's
|
||||
probe build (`make dvx` puts it in `build/dvx/probe`) on nine desktop depths
|
||||
and scales and on the full screen, and checks every displayed pixel against
|
||||
the stage.
|
||||
|
|
@ -225,7 +228,7 @@ src/<machine>/ per-machine HAL + per-CPU sprite emitter
|
|||
src/hosted/ the hosted core (desktop OSes and browsers: RGB
|
||||
present, virtual frame clock, input events, software
|
||||
mixer) and its backends (src/hosted/sdl3, which the
|
||||
sdl3, web and psp ports all build; src/hosted/win32;
|
||||
sdl3, web, and psp ports all build; src/hosted/win32;
|
||||
src/hosted/win16)
|
||||
src/m68k/ 68k code shared across machines (planar and word-
|
||||
interleaved sprite emitters; surface68k.s, which is
|
||||
|
|
@ -258,9 +261,9 @@ tools/ the host tools, all C (make tools builds them into build/t
|
|||
examples/ example programs
|
||||
joey-run run a built program in its emulator
|
||||
scripts/ bench / verification scripts (scripts/lib: shared helpers)
|
||||
tests/gate/ the gates' C helpers (screenshot, audio, and serial checks)
|
||||
tests/gate/ the tests' C helpers (screenshot, audio, and serial checks)
|
||||
tests/goldens/uber/ frozen UBER conformance hashes
|
||||
install.sh installs toolchains, emulators and firmware
|
||||
install.sh installs toolchains, emulators, and firmware
|
||||
installer/ the installer's component recipes, patches, lists
|
||||
toolchains/ everything install.sh downloads and builds (not in
|
||||
git; delete it any time and re-run install.sh)
|
||||
|
|
@ -380,11 +383,11 @@ void jlFloodFillBounded (jlSurfaceT *s, int16_t x, int16_t y,
|
|||
|
||||
Colors are 12-bit `$0RGB`. Color 0 of every palette is forced to
|
||||
black on `jlPaletteSet`. Each scanline picks one of the 16 palettes
|
||||
via the SCB. Every port honours it. The X68000 presents to its text
|
||||
via the SCB. Every port honors it. The X68000 presents to its text
|
||||
plane, which is planar like the stage (so a present is a copy) but has
|
||||
one 16-colour palette: a raster interrupt reloads that palette wherever
|
||||
one 16-color palette: a raster interrupt reloads that palette wherever
|
||||
the SCB changes, which for a single-palette screen is never. A build
|
||||
switch (`X68K_PRESENT_TEXT=0`) uses the 256-colour graphics plane the
|
||||
switch (`X68K_PRESENT_TEXT=0`) uses the 256-color graphics plane the
|
||||
way the DOS port uses its DAC instead -- each pixel byte is
|
||||
`(scb[y] << 4) | nibble`, exact with no interrupt, but slower.
|
||||
|
||||
|
|
@ -426,7 +429,7 @@ void jlTilePasteMono (jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|||
const jlTileT *in, uint8_t fgColor, uint8_t bgColor);
|
||||
|
||||
// Load up to maxTiles tiles from a baked .tbk file (per-target planar
|
||||
// bytes from tools/assetbake --type tile --target ...).
|
||||
// bytes from assetbake --type tile --target ...).
|
||||
// Refuses files baked for the wrong target. outValid (optional) marks
|
||||
// each loaded slot; outPalette (optional) receives the embedded
|
||||
// 16-entry $0RGB palette if present.
|
||||
|
|
@ -464,7 +467,7 @@ jlSpriteT *jlSpriteCreateFromSurface (const jlSurfaceT *src, int16_t x, int16_t
|
|||
void jlSpriteDestroy (jlSpriteT *sp);
|
||||
|
||||
// Load up to maxCels cels from a baked .spr file (cross-target chunky
|
||||
// 4bpp blob from tools/assetbake --type sprite). Each
|
||||
// 4bpp blob from assetbake --type sprite). Each
|
||||
// cel becomes a freshly-allocated jlSpriteT (release with
|
||||
// jlSpriteDestroy). outPalette (optional) receives the embedded
|
||||
// 16-entry $0RGB palette if present.
|
||||
|
|
@ -472,7 +475,7 @@ uint16_t jlSpriteBankLoad(const char *path, jlSpriteT **outCels,
|
|||
uint16_t maxCels, uint16_t *outPalette);
|
||||
|
||||
// Same contract, but reading a `.spc` bank pre-compiled for THIS target by
|
||||
// the offline baker `tools/spritebake`: the native blit routines are copied
|
||||
// the offline baker `spritebake`: the native blit routines are copied
|
||||
// straight into the codegen arena, so there is no startup JIT. The runtime
|
||||
// twin writes one back out after a jlSpriteCompile pass.
|
||||
uint16_t jlSpriteBankLoadPrecompiled(const char *path, jlSpriteT **outCels,
|
||||
|
|
@ -510,7 +513,7 @@ uint32_t jlSpriteCodegenBytesTotal (void);
|
|||
### Assets (baked at build time)
|
||||
|
||||
Asset PNGs live in each example's `assets/` directory and are baked
|
||||
to native binary blobs at build time by `tools/assetbake` (C, built
|
||||
to native binary blobs at build time by `assetbake` (C, built
|
||||
into `build/tools/`). The runtime loads those blobs directly with no
|
||||
conversion -- there is no in-memory chunky-to-planar or palette
|
||||
re-indexing on the device.
|
||||
|
|
@ -529,8 +532,8 @@ Two blob formats:
|
|||
Both formats embed an optional 16-entry `$0RGB` palette.
|
||||
|
||||
```
|
||||
build/tools/assetbake --type tile --target {amiga|atarist|dos|iigs|x68000} in.png out.tbk
|
||||
build/tools/assetbake --type sprite --cell WxH in.png out.spr
|
||||
build/assetbake --type tile --target {amiga|atarist|dos|iigs|x68000} in.png out.tbk
|
||||
build/assetbake --type sprite --cell WxH in.png out.spr
|
||||
```
|
||||
|
||||
A program's description wires its bake rules once for every target
|
||||
|
|
@ -549,7 +552,7 @@ on-the-fly path stays alongside the baked load API.
|
|||
Every program gets an icon on each system that shows programs by one --
|
||||
the IIgs Finder, Workbench, the Program Managers of DVX and Windows 3.1,
|
||||
Explorer, Tracker, the PSP's menu, the SDL3 window, and the web page --
|
||||
baked by `tools/iconBake` from one picture: the program's `ID_ICON` (a
|
||||
baked by `iconBake` from one picture: the program's `ID_ICON` (a
|
||||
PNG), or JoeyLib's kangaroo (`assets/joeyIcon.png`). `docs/BuildIntegration.adoc`
|
||||
(Icons) says what each system gets, and `scripts/verify-all.sh icon` checks
|
||||
that each one shows it.
|
||||
|
|
@ -559,7 +562,7 @@ that each one shows it.
|
|||
`docs/Assets.adoc` (Content Creation Tools) recommends a tool for each kind
|
||||
of asset: GrafX2 or LibreSprite for tile and sprite sheets, Furnace for
|
||||
chip-tracker music (`./install.sh install furnace`; its VGM exports become
|
||||
`.song` files through `tools/vgm2song`), MilkyTracker or OpenMPT for
|
||||
`.song` files through `vgm2song`), MilkyTracker or OpenMPT for
|
||||
`.MOD` music, and sfxr, jfxr, Audacity, or SoX for sound effects.
|
||||
|
||||
|
||||
|
|
@ -585,8 +588,8 @@ pops the next typed character (printable ASCII 0x20..0x7E plus
|
|||
`JL_CHAR_BACKSPACE`/`TAB`/`RETURN`/`ESCAPE`) or -1 when empty, with
|
||||
shift, caps lock, and the machine's keyboard layout already applied by
|
||||
the backend -- so punctuation like `.` and `:` arrives correctly on
|
||||
every port. Refilled by the same `jlInputPoll`; see `claudeDocs/input.md`
|
||||
for per-port sources and limitations.
|
||||
every port. Refilled by the same `jlInputPoll`; see the manual's Input
|
||||
chapter (`docs/ApiInput.adoc`) for per-port sources and limitations.
|
||||
|
||||
```c
|
||||
typedef enum { /* JL_KEY_NONE, JL_KEY_A..JL_KEY_Z, JL_KEY_0..JL_KEY_9, JL_KEY_SPACE,
|
||||
|
|
@ -594,7 +597,7 @@ typedef enum { /* JL_KEY_NONE, JL_KEY_A..JL_KEY_Z, JL_KEY_0..JL_KEY_9, JL_KEY_SP
|
|||
JL_KEY_UP/DOWN/LEFT/RIGHT, JL_KEY_LSHIFT/RSHIFT/LCTRL/LALT,
|
||||
JL_KEY_F1..JL_KEY_F10, JL_KEY_COUNT */ } jlKeyE;
|
||||
typedef enum { JL_MOUSE_BUTTON_NONE, JL_MOUSE_BUTTON_LEFT, JL_MOUSE_BUTTON_RIGHT,
|
||||
JL_MOUSE_BUTTON_MIDDLE, JL_MOUSE_BUTTON_COUNT } jlMouseButtonE;
|
||||
JL_MOUSE_BUTTON_COUNT } jlMouseButtonE;
|
||||
typedef enum { JL_JOYSTICK_0, JL_JOYSTICK_1, JL_JOYSTICK_COUNT } jlJoystickE;
|
||||
typedef enum { JL_JOY_BUTTON_0, JL_JOY_BUTTON_1, JL_JOY_BUTTON_COUNT } jlJoyButtonE;
|
||||
|
||||
|
|
@ -657,9 +660,9 @@ void jlAudioPlaySfxStream (uint8_t slot, jlAudioStreamFillT fill, void *ctx,
|
|||
uint16_t rateHz);
|
||||
void jlAudioStopSfx (uint8_t slot);
|
||||
|
||||
// Portable chip-tracker music: a JYM1 event stream baked by tools/songbake
|
||||
// Portable chip-tracker music: a JYM1 event stream baked by songbake
|
||||
// (from text notation, or from a tracker: write it in Furnace for the
|
||||
// SN76489 or AY-3-8910, export VGM, convert with tools/vgm2song),
|
||||
// SN76489 or AY-3-8910, export VGM, convert with vgm2song),
|
||||
// played through the tone/noise layer at register-write cost on every port.
|
||||
// No CPU mixing, so it is viable where jlAudioPlayMod is not.
|
||||
bool jlMusicPlay (const uint8_t *data, uint32_t length, bool loop);
|
||||
|
|
@ -729,12 +732,13 @@ Per-port device mapping:
|
|||
|
||||
Only the IIgs has multiple built-in ports; elsewhere `MODEM`/`PRINTER` fall
|
||||
back to the natural port. The `serial` example is an echo terminal and
|
||||
loopback self-test. `scripts/check-serial.sh` unit-tests the portable gate
|
||||
layer against a loopback mock with no hardware. Full reference, per-port
|
||||
behavior, and porting notes: [`claudeDocs/serial.md`](claudeDocs/serial.md).
|
||||
loopback self-test. `scripts/check-serial.sh` unit-tests the portable layer
|
||||
against a loopback mock with no hardware. Full reference, per-port behavior,
|
||||
and porting notes: the manual's Serial chapter
|
||||
([`docs/ApiSerial.adoc`](docs/ApiSerial.adoc)).
|
||||
|
||||
|
||||
### Files, saves & disk space (`joey/file.h`) -- opt-in
|
||||
### Files, saves, and disk space (`joey/file.h`) -- opt-in
|
||||
|
||||
File access, not pulled in by `<joey/joey.h>` -- include `<joey/file.h>`. Two
|
||||
directories sit beside the executable: read-only `DATA/` (game content) and
|
||||
|
|
@ -762,7 +766,8 @@ Directory create / delete / free-space use a per-port HAL (POSIX `mkdir` +
|
|||
`remove` + `_dos_dskfre` on the X68000). `scripts/check-save.sh`
|
||||
round-trips the core against real files; `make iigs-verify-save` boots the
|
||||
IIgs save path under MAME. Full reference, per-port behavior, and porting
|
||||
notes: [`claudeDocs/save.md`](claudeDocs/save.md).
|
||||
notes: the manual's Files and Saves chapter
|
||||
([`docs/ApiFile.adoc`](docs/ApiFile.adoc)).
|
||||
|
||||
|
||||
### Debug logging (`joey/debug.h`)
|
||||
|
|
@ -785,7 +790,7 @@ Building with `EXTRA_CFLAGS=-DJOEY_LOG_SERIAL_TEE` additionally mirrors every
|
|||
unbounded, needs no disk, and arrives in real time, which is what makes a
|
||||
wedged run diagnosable. It is OFF by default and is currently implemented
|
||||
only for the X68000 (`jlpLogTee` in `src/x68000/hal.c`); the host end is
|
||||
the `x68kSerialPeer` gate tool (`tests/gate/x68kSerialPeer.c`, built by
|
||||
the `x68kSerialPeer` test tool (`tests/gate/x68kSerialPeer.c`, built by
|
||||
`make -f make/tools.mk x68kSerialPeer` into `build/tools/`), whose output is
|
||||
line-buffered, so a killed peer keeps everything it captured:
|
||||
|
||||
|
|
|
|||
|
|
@ -227,10 +227,12 @@ with no caller-side allocation.
|
|||
[IMPORTANT]
|
||||
====
|
||||
Every port feeds streams from `jlAudioFrameTick`, so *you must be calling
|
||||
`jlAudioFrameTick` every frame* or the stream stops. On the IIgs and Amiga the
|
||||
engine plays one chunk at a time (7 KB on the IIgs, 4 KB on the Amiga) and
|
||||
fires the next from it, so a chunk seam on those ports can carry up to one
|
||||
frame of jitter.
|
||||
`jlAudioFrameTick` every frame* or the stream stops. On the IIgs the engine
|
||||
plays one chunk at a time (7 KB) and fires the next from it, so a chunk seam
|
||||
there can carry up to one frame of jitter. The Amiga loops an 8 KB ring that
|
||||
`jlAudioFrameTick` tops up as it plays, with no seams; if the calls stop for
|
||||
longer than the ring lasts (a third of a second at 22 kHz), it plays the
|
||||
ring's old contents again.
|
||||
====
|
||||
|
||||
=== jlAudioSfxRawFormat / jlAudioPlaySfxStreamRaw
|
||||
|
|
@ -268,7 +270,7 @@ typedef enum {
|
|||
void jlAudioSetPan(uint8_t slot, jlAudioPanE pan);
|
||||
----
|
||||
|
||||
Places a sound effect slot in the stereo field. Slots start centred, and the
|
||||
Places a sound effect slot in the stereo field. Slots start centered, and the
|
||||
setting sticks to the *slot*, surviving retriggers -- so set it once for a
|
||||
sound that should come from one side.
|
||||
|
||||
|
|
@ -285,13 +287,13 @@ What each port can actually do:
|
|||
|
||||
[cols="1,4", options="header"]
|
||||
|===
|
||||
|Platform |Behaviour
|
||||
|Platform |Behavior
|
||||
|
||||
|IIgs |All three. The DOC pans per oscillator, so a centred sound plays on two
|
||||
|IIgs |All three. The DOC pans per oscillator, so a centered sound plays on two
|
||||
oscillators, one per side. A stock IIgs sums to mono, so this shows up only on
|
||||
a stereo card or an emulator.
|
||||
|Amiga |Left and right by choosing the Paula channel side; a Paula channel plays
|
||||
on one side only. A centred sound plays on a free channel each side when both
|
||||
on one side only. A centered sound plays on a free channel each side when both
|
||||
sides have one, so it reaches both ears; otherwise on the one channel
|
||||
PTPlayer picks.
|
||||
|X68000 |Ignored. Everything mixes into one ADPCM stream, so panning would move
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ whatever the memory held.
|
|||
bool jlInit(const jlConfigT *config);
|
||||
----
|
||||
|
||||
Initialises the library: takes over the display, allocates the stage and the
|
||||
Initializes 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
|
||||
|
|
@ -104,8 +104,7 @@ 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"`.
|
||||
`"MS-DOS"`, `"DVX"`, `"SDL3"`, `"Web"`, `"macOS"`, or `"Windows"`.
|
||||
Library-owned.
|
||||
|
||||
=== jlVersionString
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Declared in `<joey/debug.h>`.
|
|||
|
||||
A crash-tracing logger that writes `joeylog.txt` in the program's folder,
|
||||
beside `DATA/` and `SAVES/` (<<_files>>: on the ST, the current directory; on
|
||||
DVX, the program's settings folder, `CONFIG\APPS\VENDOR\NAME\JOEYLOG.TXT`;
|
||||
DVX, the program's user folder, `USER\APPS\VENDOR\NAME\JOEYLOG.TXT`;
|
||||
on macOS, its Application Support folder).
|
||||
Debugging a program on an emulated 1988 machine is awkward, and this is usually
|
||||
the fastest way to find out what it did before it stopped doing it. The
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ no-ops, never errors, and there are no unclipped variants in the public API --
|
|||
you cannot corrupt memory by drawing off the edge. Every primitive marks the
|
||||
rows it touched, so `jlStagePresent` knows what to copy.
|
||||
|
||||
Primitives work in colour indices, and use only the low 4 bits of one.
|
||||
Primitives work in color indices, and use only the low 4 bits of one.
|
||||
Per-scanline palette resolution happens only at display time, so drawing does
|
||||
not care which palette a line uses.
|
||||
|
||||
|
|
@ -18,14 +18,14 @@ not care which palette a line uses.
|
|||
void jlSurfaceClear(jlSurfaceT *s, uint8_t colorIndex);
|
||||
----
|
||||
|
||||
Fills the whole surface with one colour index. Clearing to colour 0 is a
|
||||
Fills the whole surface with one color index. Clearing to color 0 is a
|
||||
legitimate clear-to-black, not a skipped no-op.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|===
|
||||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -44,7 +44,7 @@ Plots one pixel. Off-surface coordinates are no-ops.
|
|||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x`, `y` |`int16_t` |Position. Valid range is 0..319 and 0..199.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -65,7 +65,7 @@ Reads one pixel.
|
|||
|`x`, `y` |`int16_t` |Position.
|
||||
|===
|
||||
|
||||
*Returns:* the colour index at that position, or 0 for an off-surface read.
|
||||
*Returns:* the color index at that position, or 0 for an off-surface read.
|
||||
|
||||
=== jlDrawLine
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ paths.
|
|||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x0`, `y0` |`int16_t` |Start point, inclusive.
|
||||
|`x1`, `y1` |`int16_t` |End point, inclusive.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -108,7 +108,7 @@ a line, and 1x1 to a single pixel. Zero dimensions are no-ops.
|
|||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x`, `y` |`int16_t` |Top-left corner.
|
||||
|`w`, `h` |`uint16_t` |Size in pixels. Zero is a no-op.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -129,7 +129,7 @@ Fills a solid rectangle. Zero dimensions are no-ops.
|
|||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x`, `y` |`int16_t` |Top-left corner.
|
||||
|`w`, `h` |`uint16_t` |Size in pixels.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -149,9 +149,9 @@ plots a single pixel.
|
|||
|===
|
||||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`cx`, `cy` |`int16_t` |Centre.
|
||||
|`cx`, `cy` |`int16_t` |Center.
|
||||
|`r` |`uint16_t` |Radius in pixels.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -171,9 +171,9 @@ of 0 plots a single pixel.
|
|||
|===
|
||||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`cx`, `cy` |`int16_t` |Centre.
|
||||
|`cx`, `cy` |`int16_t` |Center.
|
||||
|`r` |`uint16_t` |Radius in pixels.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -185,7 +185,7 @@ of 0 plots a single pixel.
|
|||
void jlFloodFill(jlSurfaceT *s, int16_t x, int16_t y, uint8_t newColor);
|
||||
----
|
||||
|
||||
Flood-fills a 4-connected region, replacing every pixel of the original colour
|
||||
Flood-fills a 4-connected region, replacing every pixel of the original color
|
||||
reachable by north, south, east, and west steps. A no-op if the start point is
|
||||
off-surface or already `newColor`. The fill keeps a 512-entry stack of spans
|
||||
still to visit, and a region complex enough to need more is left partly
|
||||
|
|
@ -196,7 +196,7 @@ filled, the same way on every port.
|
|||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x`, `y` |`int16_t` |Seed point.
|
||||
|`newColor` |`uint8_t` |Replacement colour index.
|
||||
|`newColor` |`uint8_t` |Replacement color index.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -210,13 +210,13 @@ void jlFloodFillBounded(jlSurfaceT *s, int16_t x, int16_t y,
|
|||
----
|
||||
|
||||
Flood-fills a 4-connected region, stopping at pixels of `boundaryColor` rather
|
||||
than at pixels differing from the seed colour. Every reachable pixel that is
|
||||
not the boundary colour becomes `newColor`. Pixels that are `newColor` already
|
||||
than at pixels differing from the seed color. Every reachable pixel that is
|
||||
not the boundary color becomes `newColor`. Pixels that are `newColor` already
|
||||
stop the fill too, so a region behind them is not reached, and a start on
|
||||
either colour does nothing. The 512-entry span stack of `jlFloodFill` applies.
|
||||
either color does nothing. The 512-entry span stack of `jlFloodFill` applies.
|
||||
|
||||
This is the primitive for vector-art playback, such as Sierra-style picture
|
||||
drawing: outline a closed region with `jlDrawLine` in the boundary colour, then
|
||||
drawing: outline a closed region with `jlDrawLine` in the boundary color, then
|
||||
fill it with this.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|
|
@ -224,8 +224,8 @@ fill it with this.
|
|||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`x`, `y` |`int16_t` |Seed point.
|
||||
|`newColor` |`uint8_t` |Replacement colour index.
|
||||
|`boundaryColor` |`uint8_t` |Colour that stops the fill.
|
||||
|`newColor` |`uint8_t` |Replacement color index.
|
||||
|`boundaryColor` |`uint8_t` |Color that stops the fill.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ goes there too):
|
|||
its `SAVES/` there.
|
||||
* *DVX* keeps the two apart, because its programs share one current
|
||||
directory: there `DATA/` is beside the program, in its folder under DVX's
|
||||
`APPS`, and `SAVES/` is in the program's own settings folder,
|
||||
`CONFIG\APPS\VENDOR\NAME` (`VENDOR` its maker's, <<_a_project>>), which
|
||||
DVX keeps apart from the program files so a reinstall never overwrites it.
|
||||
`APPS`, and `SAVES/` is in the program's own folder in DVX's user folder,
|
||||
`USER\APPS\VENDOR\NAME` (`VENDOR` its maker's, <<_a_project>>), which
|
||||
DVX keeps apart from the program files so an upgrade never overwrites it.
|
||||
* *The web* keeps them apart too: `DATA/` is packed into the program, and
|
||||
`SAVES/` and the log are in a folder named after the page
|
||||
(`games/hero.html` has `/games/hero/`), its `SAVES/` kept in the browser's
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ typedef enum {
|
|||
} jlKeyE;
|
||||
|
||||
typedef enum {
|
||||
JL_MOUSE_BUTTON_NONE = 0, JL_MOUSE_BUTTON_LEFT,
|
||||
JL_MOUSE_BUTTON_RIGHT, JL_MOUSE_BUTTON_MIDDLE, JL_MOUSE_BUTTON_COUNT
|
||||
JL_MOUSE_BUTTON_NONE = 0, JL_MOUSE_BUTTON_LEFT, JL_MOUSE_BUTTON_RIGHT,
|
||||
JL_MOUSE_BUTTON_COUNT
|
||||
} jlMouseButtonE;
|
||||
|
||||
typedef enum { JL_JOYSTICK_0 = 0, JL_JOYSTICK_1, JL_JOYSTICK_COUNT } jlJoystickE;
|
||||
|
|
@ -166,8 +166,8 @@ bool jlMousePresent(void);
|
|||
*Returns:* `true` if the platform confirmed a mouse at init. On DOS that means
|
||||
the INT 33h driver reset successfully; on the Amiga, that JoeyLib's input
|
||||
window opened; on Win32 and Win16, that Windows reports a mouse; on SDL3 and
|
||||
the web, that SDL reports one. The IIgs, ST, X68000, DVX, Haiku, and BeOS
|
||||
always report one, and the PSP never does.
|
||||
the web, that SDL reports one. The IIgs, ST, X68000, DVX, Haiku, BeOS, and
|
||||
PSP always report one; the PSP's is its analog nub.
|
||||
|
||||
=== jlMouseX / jlMouseY
|
||||
|
||||
|
|
@ -208,9 +208,14 @@ bool jlMouseReleased(jlMouseButtonE button);
|
|||
|
||||
*Returns:* held state, rising edge, and falling edge respectively.
|
||||
|
||||
The IIgs mouse has one button, `JL_MOUSE_BUTTON_LEFT`. The ST and X68000 have
|
||||
left and right. The other ports report left, right, and middle, as far as the
|
||||
mouse has them. The PSP has no mouse.
|
||||
Every port has two buttons, `JL_MOUSE_BUTTON_LEFT` and `JL_MOUSE_BUTTON_RIGHT`,
|
||||
whatever its mouse has. A left click made with Ctrl held is the right button,
|
||||
on every port: it is down from the press until the button is let go, even if
|
||||
Ctrl is let go first, and it is never the left button too (Ctrl pressed after
|
||||
the button changes nothing). So a one-button mouse, the IIgs's or many a
|
||||
Mac's, has both, and a program can tell its users "Ctrl-click" on any machine.
|
||||
A middle button, where a mouse has one, is not reported. The PSP's buttons are
|
||||
L (left) and R (right).
|
||||
|
||||
=== jlJoystickConnected
|
||||
|
||||
|
|
@ -253,9 +258,9 @@ int8_t jlJoystickY(jlJoystickE js);
|
|||
|
||||
*Returns:* axis value, -127 to +127. Digital sticks (the Amiga, ST, and
|
||||
X68000) snap to the extremes; analog sticks (the IIgs, DOS, DVX, and the
|
||||
hosted ports) return their offset from the centre, scaled to that range, with
|
||||
hosted ports) return their offset from the center, scaled to that range, with
|
||||
the dead zone applied. On the IIgs and DOS the axes read 0 until the stick's
|
||||
centre has been read (<<_jljoystickreset>>). Test against a threshold rather
|
||||
center has been read (<<_jljoystickreset>>). Test against a threshold rather
|
||||
than for exact values, so the same code works with both.
|
||||
|
||||
=== jlJoyDown / jlJoyPressed / jlJoyReleased
|
||||
|
|
@ -292,18 +297,18 @@ buttons are the stick's own, and a one-button stick never reports
|
|||
void jlJoystickReset(jlJoystickE js, uint8_t deadZone);
|
||||
----
|
||||
|
||||
Sets the stick's dead zone and, where the port can, re-centres it and looks
|
||||
Sets the stick's dead zone and, where the port can, re-centers it and looks
|
||||
for it again:
|
||||
|
||||
* *IIgs*: polling starts again if it had stopped for want of a stick, and the
|
||||
stick is re-centred at its next reading.
|
||||
* *DOS*: the game port is probed again, and the stick is re-centred at its
|
||||
stick is re-centered at its next reading.
|
||||
* *DOS*: the game port is probed again, and the stick is re-centered at its
|
||||
next reading.
|
||||
* *Amiga*: the port goes back to `lowlevel.library`'s own detection.
|
||||
* *Elsewhere* only the dead zone changes.
|
||||
|
||||
On the IIgs polling for a stick that is not there stops. If a user may plug a
|
||||
stick in while your program runs, call this, with the stick centred, to pick
|
||||
stick in while your program runs, call this, with the stick centered, to pick
|
||||
it up.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|
|
@ -321,6 +326,6 @@ it up.
|
|||
[IMPORTANT]
|
||||
====
|
||||
On the IIgs and DOS the next reading after this call is taken as the stick's
|
||||
centre, so the user must be holding the stick centred when you call it. The
|
||||
center, so the user must be holding the stick centered when you call it. The
|
||||
call is safe to make unconditionally.
|
||||
====
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
|
||||
Declared in `<joey/palette.h>`.
|
||||
|
||||
Colours are 12-bit `$0RGB`: bits 11..8 red, 7..4 green, 3..0 blue. Each surface
|
||||
holds 16 palettes of 16 colours, plus a 200-entry table (the SCB table) naming
|
||||
Colors are 12-bit `$0RGB`: bits 11..8 red, 7..4 green, 3..0 blue. Each surface
|
||||
holds 16 palettes of 16 colors, plus a 200-entry table (the SCB table) naming
|
||||
which palette each scanline uses. That is how a JoeyLib program shows more than
|
||||
16 colours at once.
|
||||
16 colors at once.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
Colour 0 of every palette is black, always. `jlPaletteSet` silently masks entry
|
||||
Color 0 of every palette is black, always. `jlPaletteSet` silently masks entry
|
||||
0 to `$000` whatever you pass, and `jlSurfaceLoadFile` does the same to every
|
||||
palette it loads. This is deliberate: the Amiga and ST hardware border follows
|
||||
colour 0 of the active palette, and colour 0 is the transparent colour for
|
||||
sprite drawing. You have 15 usable colours per palette.
|
||||
color 0 of the active palette, and color 0 is the transparent color for
|
||||
sprite drawing. You have 15 usable colors per palette.
|
||||
====
|
||||
|
||||
Palette and SCB changes to the stage reach the display at the next
|
||||
|
|
@ -22,6 +22,26 @@ the same way on every port: a palette index above 15 or a line above 199 makes
|
|||
the setters do nothing (`jlScbSetRange` stops its range at line 199), and
|
||||
`jlScbGet` returns 0 for a line off the surface.
|
||||
|
||||
=== The Default Palette
|
||||
|
||||
Every surface starts with all 16 of its palettes set to the standard 16 EGA
|
||||
colors, and every line of its SCB on palette 0: the stage at `jlInit`, and a
|
||||
surface at `jlSurfaceCreate`. A program that never calls `jlPaletteSet` draws
|
||||
in these colors:
|
||||
|
||||
[cols="1,2,1,1,2,1", options="header"]
|
||||
|===
|
||||
|Index |Color |`$0RGB` |Index |Color |`$0RGB`
|
||||
|0 |Black |`$000` |8 |Dark gray |`$555`
|
||||
|1 |Blue |`$00A` |9 |Light blue |`$55F`
|
||||
|2 |Green |`$0A0` |10 |Light green |`$5F5`
|
||||
|3 |Cyan |`$0AA` |11 |Light cyan |`$5FF`
|
||||
|4 |Red |`$A00` |12 |Light red |`$F55`
|
||||
|5 |Magenta |`$A0A` |13 |Light magenta |`$F5F`
|
||||
|6 |Brown |`$A50` |14 |Yellow |`$FF5`
|
||||
|7 |Light gray |`$AAA` |15 |White |`$FFF`
|
||||
|===
|
||||
|
||||
=== jlPaletteSet
|
||||
|
||||
[source,c]
|
||||
|
|
@ -29,7 +49,7 @@ the setters do nothing (`jlScbSetRange` stops its range at line 199), and
|
|||
void jlPaletteSet(jlSurfaceT *s, uint8_t paletteIndex, const uint16_t *colors16);
|
||||
----
|
||||
|
||||
Loads all 16 colours of one palette.
|
||||
Loads all 16 colors of one palette.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|===
|
||||
|
|
@ -49,7 +69,7 @@ Loads all 16 colours of one palette.
|
|||
void jlPaletteGet(const jlSurfaceT *s, uint8_t paletteIndex, uint16_t *out16);
|
||||
----
|
||||
|
||||
Reads all 16 colours of one palette, as they were set: the values passed to
|
||||
Reads all 16 colors of one palette, as they were set: the values passed to
|
||||
`jlPaletteSet` (with entry 0 forced to `$000`, and bits 12 to 15 kept as
|
||||
passed), not what the display can show of them.
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ and receives on its own SCC interrupt; DVX goes through DVX's serial library,
|
|||
which owns the COM ports for every program on the desktop (a port another
|
||||
program has open cannot be opened); Win32 and Win16 go through Windows' own
|
||||
communications calls, whose driver owns the ports the same way; SDL3 on Linux,
|
||||
Haiku, and BeOS open the port as a terminal device through termios. The web and
|
||||
macOS, Haiku, and BeOS open the port as a terminal device through termios. The web and
|
||||
the PSP have no serial port: `jlSerialOpen` fails there.
|
||||
|
||||
The model is *non-blocking and buffered*. Received bytes accumulate in a
|
||||
|
|
@ -137,7 +137,7 @@ Ways to live with them:
|
|||
=== Flow Control
|
||||
|
||||
`flow` in `jlSerialConfigT` picks the handshake, and every port with a serial
|
||||
port honours it:
|
||||
port honors it:
|
||||
|
||||
* `JL_SERIAL_FLOW_RTSCTS`: transmit waits while the peer holds CTS down, and
|
||||
the port drops RTS when its receive buffer is three quarters full, raising
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Declared in `<joey/sprite.h>`.
|
||||
|
||||
A sprite is a rectangle of 8x8 tiles drawn at an arbitrary pixel position with
|
||||
colour-0 transparency. Sprite pixel data is `widthTiles * heightTiles * 32`
|
||||
color-0 transparency. Sprite pixel data is `widthTiles * heightTiles * 32`
|
||||
bytes of 4bpp packed data, laid out as a flat blob of tiles in row-major order.
|
||||
|
||||
The distinguishing feature is that *sprite blitters are compiled at run time*.
|
||||
|
|
@ -55,26 +55,22 @@ It is the recorded region you must not overwrite before restoring.
|
|||
|
||||
[source,c]
|
||||
----
|
||||
#define jlSpriteBackupLength(_type, _wT, _hT) /* ... */
|
||||
#define jlSpriteBackupLength(_wT, _hT) /* ... */
|
||||
----
|
||||
|
||||
How many `_type` elements a backup buffer needs for a `widthTiles` by
|
||||
`heightTiles` sprite at any x position, on every port, rounded up. Use it to
|
||||
size your backing array; with `uint8_t` it is the size in bytes.
|
||||
How many `uint16_t` a backup buffer needs for a `widthTiles` by `heightTiles`
|
||||
sprite at any x position, on every port. A backup buffer *must be an array of
|
||||
`uint16_t`*:
|
||||
|
||||
[source,c]
|
||||
----
|
||||
static uint16_t gBacking[jlSpriteBackupLength(uint16_t, 4, 4)];
|
||||
static uint16_t gBacking[jlSpriteBackupLength(4, 4)];
|
||||
----
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Give the backing array *at least 2-byte alignment*. On the Amiga, ST, and
|
||||
X68000 an odd buffer address sends the save and restore calls down a much
|
||||
slower path, and it makes the trusted variants crash; the
|
||||
other ports do not mind. Declaring it `uint16_t`, as above, guarantees the
|
||||
alignment.
|
||||
====
|
||||
That gives it the 2-byte alignment the Amiga, ST, and X68000 need: an odd
|
||||
address sends the save and restore calls down a much slower path there, and
|
||||
makes the trusted variants crash. A buffer from `jlAlloc` is
|
||||
`jlSpriteBackupLength(w, h) * sizeof(uint16_t)` bytes.
|
||||
|
||||
=== jlSpriteBankLoad
|
||||
|
||||
|
|
@ -219,7 +215,7 @@ says so; raise `codegenBytes` or destroy sprites you no longer need), the
|
|||
sprite has no source tile data, the 64 KB the compiler works in cannot be
|
||||
allocated, no routine fits the compiler's limits (16 KB a routine), or the
|
||||
port has no code emitter (the Sprites row of
|
||||
<<_per_platform_behaviour_summary>>). Only the full arena sets `jlLastError`.
|
||||
<<_per_platform_behavior_summary>>). Only the full arena sets `jlLastError`.
|
||||
A `false` is never fatal: the sprite still draws, only slower.
|
||||
|
||||
`true` means at least part of the sprite compiled, not all of it. When the
|
||||
|
|
@ -254,7 +250,7 @@ variants. Returns 0 if the sprite was never compiled.
|
|||
void jlSpriteDraw(jlSurfaceT *s, jlSpriteT *sp, int16_t x, int16_t y);
|
||||
----
|
||||
|
||||
Draws the sprite at a pixel position. Source pixels of colour 0 are skipped.
|
||||
Draws the sprite at a pixel position. Source pixels of color 0 are skipped.
|
||||
Off-surface portions are clipped. Compiled routines do not clip, so a sprite
|
||||
partly off the surface draws through the interpreter, compiled or not.
|
||||
|
||||
|
|
@ -286,8 +282,8 @@ would write, so it can be restored later.
|
|||
|`sp` |`jlSpriteT *` |Sprite whose footprint to capture.
|
||||
|`x`, `y` |`int16_t` |Position the sprite will be drawn at.
|
||||
|`backup` |`jlSpriteBackupT *` |Receives the capture. You must set `bytes` to
|
||||
a buffer of at least `jlSpriteBackupLength(uint8_t, w, h)` bytes for fully
|
||||
in-bounds draws. For clipped draws only the visible bytes are stored. On the
|
||||
a `uint16_t` buffer of at least `jlSpriteBackupLength(w, h)` elements for
|
||||
fully in-bounds draws. For clipped draws only the visible bytes are stored. On the
|
||||
IIgs the buffer must not cross a 64 KB bank boundary; `jlAlloc`, the C heap,
|
||||
and static arrays never give one that does. With `bytes` `NULL` nothing is
|
||||
copied: the call only fills in the region a save would record.
|
||||
|
|
@ -344,16 +340,16 @@ void jlSpriteSaveAndDrawTrusted(jlSurfaceT *s, jlSpriteT *sp,
|
|||
|
||||
Expert inner-loop entry points. Each has the same effect as its plain
|
||||
counterpart but *skips the per-call geometry validation* -- roughly eleven
|
||||
range and alignment checks plus the NULL and sprite-slot gates.
|
||||
range and alignment checks plus the NULL and sprite-slot checks.
|
||||
|
||||
Use them only in a game that pre-compiles its sprites and already clamps
|
||||
positions on screen. Anything the fast path does not recognise falls back to
|
||||
positions on screen. Anything the fast path does not recognize falls back to
|
||||
the fully validated call, so a conservative caller degrades safely rather than
|
||||
corrupting -- but the guarantees below are still yours to keep.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Behaviour is *undefined* -- out-of-bounds writes, crashes -- unless all of the
|
||||
Behavior is *undefined* -- out-of-bounds writes, crashes -- unless all of the
|
||||
following hold:
|
||||
|
||||
* `s`, `backup`, `+backup->sprite+`, and `+backup->bytes+` are non-`NULL`;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Declared in `<joey/surface.h>` and `<joey/present.h>`.
|
||||
|
||||
A surface is a 320x200 16-colour image with its own 200-entry SCB table and its
|
||||
A surface is a 320x200 16-color image with its own 200-entry SCB table and its
|
||||
own 16x16 palette set. Storage is target-native -- chunky 4bpp on the IIgs,
|
||||
DOS, DVX, and the hosted ports, planar on the Amiga, ST, and X68000 -- but every
|
||||
entry point speaks in colour indices, so your code never sees the difference.
|
||||
A new surface starts with the 16-colour EGA palette in all 16 of its palettes.
|
||||
entry point speaks in color indices, so your code never sees the difference.
|
||||
A new surface starts with the 16-color EGA palette in all 16 of its palettes.
|
||||
|
||||
=== Constants
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ A new surface starts with the 16-colour EGA palette in all 16 of its palettes.
|
|||
|`JL_SURFACE_BYTES_PER_ROW` |160 |Bytes per row in chunky 4bpp form.
|
||||
|`JL_SURFACE_PIXELS_SIZE` |32000 |Total pixel bytes.
|
||||
|`JL_SURFACE_PALETTE_COUNT` |16 |Palettes per surface.
|
||||
|`JL_SURFACE_COLORS_PER_PALETTE` |16 |Colours per palette.
|
||||
|`JL_SURFACE_COLORS_PER_PALETTE` |16 |Colors per palette.
|
||||
|`JL_SURFACE_PALETTE_ENTRIES` |256 |Total palette entries per surface.
|
||||
|===
|
||||
|
||||
|
|
@ -146,9 +146,9 @@ save and restore in the same region.
|
|||
void jlSurfacePasteIndexed(jlSurfaceT *dst, int16_t x, int16_t y, uint16_t w, uint16_t h, const uint8_t *src, uint16_t srcStride, bool doubleWide);
|
||||
----
|
||||
|
||||
Writes a rectangle of colour indices from your memory onto a surface: one byte
|
||||
Writes a rectangle of color indices from your memory onto a surface: one byte
|
||||
a pixel, whatever the port stores pixels as. Row `r` of the source starts at
|
||||
`src + r * srcStride`. Each byte's low nibble is the colour index; the high
|
||||
`src + r * srcStride`. Each byte's low nibble is the color index; the high
|
||||
nibble is ignored, so a byte array of 0..15 with flags in the top bits pastes
|
||||
as it is.
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ marked for the next `jlStagePresent`.
|
|||
|`dst` |`jlSurfaceT *` |Destination surface.
|
||||
|`x`, `y` |`int16_t` |Top-left pixel on the surface.
|
||||
|`w`, `h` |`uint16_t` |Bytes a row and rows.
|
||||
|`src` |`const uint8_t *` |The colour indices, row by row.
|
||||
|`src` |`const uint8_t *` |The color indices, row by row.
|
||||
|`srcStride` |`uint16_t` |Bytes from one source row to the next.
|
||||
|`doubleWide` |`bool` |Each byte fills two pixels.
|
||||
|===
|
||||
|
|
@ -191,7 +191,7 @@ void jlSurfaceSnapIndexed(const jlSurfaceT *src, int16_t x, int16_t y, uint16_t
|
|||
----
|
||||
|
||||
The reverse of `jlSurfacePasteIndexed`: reads a rectangle of a surface into your
|
||||
memory as colour indices, one byte a pixel (0..15), row `r` at
|
||||
memory as color indices, one byte a pixel (0..15), row `r` at
|
||||
`dst + r * dstStride`. With `doubleWide` the rectangle is `2 * w` pixels wide
|
||||
and each byte takes the left pixel of its pair. A byte whose pixel is off the
|
||||
surface reads 0, as `jlSamplePixel` does. Bytes between `w` and `dstStride` are
|
||||
|
|
@ -203,7 +203,7 @@ left alone.
|
|||
|`src` |`const jlSurfaceT *` |Source surface.
|
||||
|`x`, `y` |`int16_t` |Top-left pixel on the surface.
|
||||
|`w`, `h` |`uint16_t` |Bytes a row and rows.
|
||||
|`dst` |`uint8_t *` |Where the colour indices go, row by row.
|
||||
|`dst` |`uint8_t *` |Where the color indices go, row by row.
|
||||
|`dstStride` |`uint16_t` |Bytes from one destination row to the next.
|
||||
|`doubleWide` |`bool` |Read every other pixel.
|
||||
|===
|
||||
|
|
@ -256,7 +256,7 @@ nothing is reallocated.
|
|||
|
||||
*Returns:* `true` on success; `false` if the file is missing, the wrong size,
|
||||
has an SCB byte above 15, or unreadable. A read that fails part way can leave
|
||||
the pixels partly overwritten; the SCBs and palettes are then untouched. Colour
|
||||
the pixels partly overwritten; the SCBs and palettes are then untouched. Color
|
||||
0 of every palette loaded is set to black, as `jlPaletteSet` does. Neither file
|
||||
call sets `jlLastError`.
|
||||
|
||||
|
|
@ -304,7 +304,7 @@ The Amiga, the ST, DOS, and the X68000 draw into a second screen page and flip
|
|||
to it at the next vertical blank, so the picture never tears. A present that comes before
|
||||
that blank, right after another one, waits for it: the page it would draw into
|
||||
is still on the screen. Present once a frame, after `jlWaitVBL`, and it never
|
||||
waits. On DOS, a present that changes colours also waits: VGA loads them in
|
||||
waits. On DOS, a present that changes colors also waits: VGA loads them in
|
||||
the blank that shows the new page, and the `jlWaitVBL` after it returns at
|
||||
once, so the loop keeps its pace.
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ one. Fonts, terrain sets, and spritesheets are all the same thing.
|
|||
|`JL_TILE_BLOCKS_PER_ROW` |40 |Tile columns on a surface.
|
||||
|`JL_TILE_BLOCKS_PER_COL` |25 |Tile rows on a surface.
|
||||
|`JL_TILE_NO_GLYPH` |0xFFFF |`jlDrawText` map entry meaning "skip this character".
|
||||
|`JL_TILE_MAP_SKIP` |0xFF |`jlTileMapPaste` entry meaning "leave this cell alone"; in a tile layer's map, colour 0.
|
||||
|`JL_TILE_MAP_SKIP` |0xFF |`jlTileMapPaste` entry meaning "leave this cell alone"; in a tile layer's map, color 0.
|
||||
|`JL_TILE_LAYER_MAX_SIDE` |4096 |Largest tile layer map side, in tiles.
|
||||
|===
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ any tileset with see-through pixels.
|
|||
|`dstBx`, `dstBy` |`uint8_t` |Destination block coordinates.
|
||||
|`src` |`const jlSurfaceT *` |Source surface.
|
||||
|`srcBx`, `srcBy` |`uint8_t` |Source block coordinates.
|
||||
|`transparentIndex` |`uint8_t` |Source colour index treated as transparent.
|
||||
|`transparentIndex` |`uint8_t` |Source color index treated as transparent.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -102,8 +102,8 @@ any tileset with see-through pixels.
|
|||
void jlTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex);
|
||||
----
|
||||
|
||||
Fills one block with a solid colour. Equivalent to
|
||||
`jlFillRect(s, bx*8, by*8, 8, 8, colorIndex)` but skips the clipping maths,
|
||||
Fills one block with a solid color. Equivalent to
|
||||
`jlFillRect(s, bx*8, by*8, 8, 8, colorIndex)` but skips the clipping math,
|
||||
since block coordinates are already known to be in range.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|
|
@ -111,7 +111,7 @@ since block coordinates are already known to be in range.
|
|||
|Parameter |Type |Description
|
||||
|`s` |`jlSurfaceT *` |Target surface.
|
||||
|`bx`, `by` |`uint8_t` |Block coordinates.
|
||||
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
||||
|`colorIndex` |`uint8_t` |Color index 0..15.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -218,8 +218,8 @@ void jlTilePasteMono(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|||
----
|
||||
|
||||
Pastes a tile as a silhouette: any non-zero source pixel draws `fgColor`, zero
|
||||
pixels draw `bgColor`. Useful when the bitmap is a fixed glyph but the colour
|
||||
comes from a separate map, as in C64-style colour RAM. Opaque.
|
||||
pixels draw `bgColor`. Useful when the bitmap is a fixed glyph but the color
|
||||
comes from a separate map, as in C64-style color RAM. Opaque.
|
||||
|
||||
[cols="1,1,4", options="header"]
|
||||
|===
|
||||
|
|
@ -227,8 +227,8 @@ comes from a separate map, as in C64-style colour RAM. Opaque.
|
|||
|`dst` |`jlSurfaceT *` |Destination surface.
|
||||
|`bx`, `by` |`uint8_t` |Block coordinates.
|
||||
|`in` |`const jlTileT *` |Source tile, read as a silhouette.
|
||||
|`fgColor` |`uint8_t` |Colour for non-zero pixels. Masked to 4 bits.
|
||||
|`bgColor` |`uint8_t` |Colour for zero pixels. Masked to 4 bits.
|
||||
|`fgColor` |`uint8_t` |Color for non-zero pixels. Masked to 4 bits.
|
||||
|`bgColor` |`uint8_t` |Color for zero pixels. Masked to 4 bits.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -263,8 +263,8 @@ charset, anything 1bpp -- so nothing needs expanding into a `jlTileT` first.
|
|||
|`dst` |`jlSurfaceT *` |Destination surface.
|
||||
|`bx`, `by` |`uint8_t` |Block coordinates.
|
||||
|`bits` |`const uint8_t *` |8 bytes, one per glyph row, MSB leftmost.
|
||||
|`fgColor` |`uint8_t` |Set-bit colour. Masked to 4 bits.
|
||||
|`bgColor` |`uint8_t` |Clear-bit colour. Masked to 4 bits.
|
||||
|`fgColor` |`uint8_t` |Set-bit color. Masked to 4 bits.
|
||||
|`bgColor` |`uint8_t` |Clear-bit color. Masked to 4 bits.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
@ -279,7 +279,7 @@ void jlDrawText(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|||
----
|
||||
|
||||
Draws a NUL-terminated ASCII string using glyphs from a font surface. Glyph
|
||||
colour 0 is transparent, so the background shows through. The cursor wraps at
|
||||
color 0 is transparent, so the background shows through. The cursor wraps at
|
||||
the right edge and truncates at the bottom. A start off the 40x25 grid uses up
|
||||
characters without drawing them as the cursor moves on: a start column past
|
||||
39 drops the first character and carries on at the start of the next row, and
|
||||
|
|
@ -404,7 +404,7 @@ Makes a layer over `map`. Nothing is drawn until `jlTileLayerDraw`.
|
|||
|Parameter |Type |Description
|
||||
|`tiles` |`const jlTileT *` |The tileset. Kept, not copied.
|
||||
|`map` |`uint8_t *` |`mapWidth * mapHeight` tileset indices, row-major, so
|
||||
`map[row * mapWidth + col]`. `JL_TILE_MAP_SKIP` draws colour 0. Kept, not
|
||||
`map[row * mapWidth + col]`. `JL_TILE_MAP_SKIP` draws color 0. Kept, not
|
||||
copied; `jlTileLayerSetTile` writes it.
|
||||
|`mapWidth`, `mapHeight` |`uint16_t` |The map's size in tiles, 1 to
|
||||
`JL_TILE_LAYER_MAX_SIDE`.
|
||||
|
|
@ -468,7 +468,7 @@ Sets map cell `(tx, ty)` to `tile`. The next draw shows it.
|
|||
|Parameter |Type |Description
|
||||
|`layer` |`jlTileLayerT *` |The layer.
|
||||
|`tx`, `ty` |`uint16_t` |The cell, in tiles. Out-of-range cells are ignored.
|
||||
|`tile` |`uint8_t` |The tileset index, or `JL_TILE_MAP_SKIP` for colour 0.
|
||||
|`tile` |`uint8_t` |The tileset index, or `JL_TILE_MAP_SKIP` for color 0.
|
||||
|===
|
||||
|
||||
*Returns:* nothing.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|`JL_SURFACE_BYTES_PER_ROW` |160 |Bytes per row, chunky 4bpp.
|
||||
|`JL_SURFACE_PIXELS_SIZE` |32000 |Total pixel bytes.
|
||||
|`JL_SURFACE_PALETTE_COUNT` |16 |Palettes per surface.
|
||||
|`JL_SURFACE_COLORS_PER_PALETTE` |16 |Colours per palette.
|
||||
|`JL_SURFACE_COLORS_PER_PALETTE` |16 |Colors per palette.
|
||||
|`JL_SURFACE_PALETTE_ENTRIES` |256 |Total palette entries.
|
||||
|===
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ run on Linux, and all but OpenMPT are native there.
|
|||
animation frames. *GIMP* (GPLv3), to bring existing art down to an indexed
|
||||
PNG.
|
||||
|In GrafX2, set the palette's RGB scale to 16 (right-click the palette
|
||||
button for the secondary palette menu): every colour you pick is then one a
|
||||
button for the secondary palette menu): every color you pick is then one a
|
||||
JoeyLib palette holds exactly, 4 bits a gun. The sheet rules are in
|
||||
<<_authoring_the_png>>.
|
||||
|
||||
|
|
@ -117,14 +117,14 @@ build uses. Only Furnace has an installer component, because
|
|||
Both tile and sprite sheets start as an *indexed* PNG with strict rules:
|
||||
|
||||
* Mode `P` (indexed / palette), *not* RGB or RGBA.
|
||||
* At most *16 colours*.
|
||||
* At most *16 colors*.
|
||||
* Width and height both *multiples of 8*.
|
||||
* Palette entry 0 is the transparent / background colour. Remember the library
|
||||
* Palette entry 0 is the transparent / background color. Remember the library
|
||||
forces it to black on screen.
|
||||
|
||||
Any decent pixel editor can produce this. In GIMP: *Image > Mode > Indexed*,
|
||||
"Generate optimum palette" with maximum 16 colours, and no dithering --
|
||||
dithering invents colours and will push you over the limit.
|
||||
"Generate optimum palette" with maximum 16 colors, and no dithering --
|
||||
dithering invents colors and will push you over the limit.
|
||||
|
||||
Lay the artwork out on a grid. A tile sheet is read as 8x8 cells row by row; a
|
||||
sprite sheet is read as cells of whatever size you declare with `--cell`, also
|
||||
|
|
@ -148,7 +148,7 @@ loader refuses a file baked for a different target rather than drawing garbage.
|
|||
|
||||
==== Maps from Tiled
|
||||
|
||||
`tools/tiled2joey` turns a map made in Tiled into what a JoeyLib program
|
||||
`tiled2joey` turns a map made in Tiled into what a JoeyLib program
|
||||
pastes: a tile sheet, which `assetbake` bakes into a tile bank for each
|
||||
port, and a map for each tile layer, which `jlTileMapPaste` takes as it is:
|
||||
|
||||
|
|
@ -156,16 +156,16 @@ port, and a map for each tile layer, which `jlTileMapPaste` takes as it is:
|
|||
----
|
||||
$ build/tools/tiled2joey --header build/level1.h level1.tmx \
|
||||
build/level1.png build/level1
|
||||
tiled2joey: 60x25 map, 87 tiles, 14 colours, layers: ground.map, detail.map
|
||||
tiled2joey: 60x25 map, 87 tiles, 14 colors, layers: ground.map, detail.map
|
||||
$ build/tools/assetbake --type tile --target iigs \
|
||||
build/level1.png build/iigs/bin/DATA/level1.tbk
|
||||
----
|
||||
|
||||
* *The map* is Tiled's own TMX or its JSON (TMJ), orthogonal and finite, with
|
||||
8x8 tiles. Its tilesets, in the map or in files of their own (TSX or TSJ),
|
||||
are single images: indexed PNGs whose colours make one palette of at most
|
||||
16 between them. The first tileset's colours keep their indices, so its
|
||||
colour 0 stays the background; another's are matched by colour or take a
|
||||
are single images: indexed PNGs whose colors make one palette of at most
|
||||
16 between them. The first tileset's colors keep their indices, so its
|
||||
color 0 stays the background; another's are matched by color or take a
|
||||
slot the first does not use.
|
||||
* *The sheet* holds the tiles the map's layers use, in the order they first
|
||||
appear, so the bank holds nothing the map does not place. A tile drawn
|
||||
|
|
@ -193,7 +193,7 @@ for (row = 0; row < 25u; row++) {
|
|||
|
||||
A level that scrolls is a tile layer over the whole map (see
|
||||
<<_scrolling_tile_layers>>). A layer is opaque and draws `JL_TILE_MAP_SKIP` as
|
||||
colour 0, so it scrolls one map layer; flatten the scenery into one layer in
|
||||
color 0, so it scrolls one map layer; flatten the scenery into one layer in
|
||||
Tiled.
|
||||
|
||||
[source,c]
|
||||
|
|
@ -217,7 +217,7 @@ because it holds chunky 4bpp tiles and each port converts a sprite to its own
|
|||
form when the sprite is created. Bake once, ship everywhere.
|
||||
|
||||
Both formats can embed the 16-entry `$0RGB` palette, and both loaders will hand
|
||||
it back through their `outPalette` parameter so artwork carries its own colours
|
||||
it back through their `outPalette` parameter so artwork carries its own colors
|
||||
rather than depending on whatever the stage happens to have set.
|
||||
|
||||
==== Pre-Compiling Sprites
|
||||
|
|
@ -316,7 +316,7 @@ same time; `songbake` warns when they overlap.
|
|||
|
||||
A tracker is easier than a text file for anything longer than a jingle. Write
|
||||
the song in Furnace, export it as VGM (a log of the chip's register writes as
|
||||
the song plays), and `tools/vgm2song` turns that into a `.song` file, which
|
||||
the song plays), and `vgm2song` turns that into a `.song` file, which
|
||||
is still plain text you can read and touch up:
|
||||
|
||||
. Start the song on a system with a *TI SN76489* (the Sega Master System, BBC
|
||||
|
|
@ -478,7 +478,7 @@ lists it as a prerequisite, so make builds it first.
|
|||
`examples/tilebank` is a working reference: one rule in `make/examples.mk`
|
||||
bakes the same PNG for each target into its `DATA/tilebank` (`TILEBANK_DATA`),
|
||||
and the program loads that bank both ways and shows the loader refusing the
|
||||
other targets' bakes. Its `scripts/verify-<port>-tilebank.sh` gates run it on
|
||||
other targets' bakes. Its `scripts/verify-<port>-tilebank.sh` tests run it on
|
||||
every port. Space Taxi, a game built on JoeyLib in a repository of its own,
|
||||
has the full-sized one in its `program.mk`: levels extracted from a C64 image,
|
||||
a cel bank generated by a host tool of its own and pre-compiled per target,
|
||||
|
|
@ -513,7 +513,7 @@ and the streaming loader needs only a small internal scratch buffer.
|
|||
|
||||
The JoeyLib logo lives in `assets/` as both the original 320x200 PNG and a
|
||||
traced SVG. If the source art ever changes, regenerate the vector with
|
||||
`tools/traceLogo`, which needs the potrace program (the distribution's
|
||||
`traceLogo`, which needs the potrace program (the distribution's
|
||||
`potrace` package, or set `POTRACE` to one installed elsewhere):
|
||||
|
||||
[source,console]
|
||||
|
|
@ -523,10 +523,10 @@ $ build/tools/traceLogo assets/joeylib_logo_-_320x200.png \
|
|||
assets/joeylibLogo.svg
|
||||
----
|
||||
|
||||
The art is four flat colours (ink, skin, the green shorts, and the beige
|
||||
The art is four flat colors (ink, skin, the green shorts, and the beige
|
||||
monitor), so the default of four clusters is the trace that looks best: each
|
||||
layer is filled with the exact colour most of its pixels have, and the
|
||||
clustering is done in CIELAB, where an antialiased grey edge joins the ink or
|
||||
the beige it lies between rather than the skin colour nearest it in RGB. A
|
||||
fifth cluster and beyond only carve those grey edges into layers of their
|
||||
layer is filled with the exact color most of its pixels have, and the
|
||||
clustering is done in CIELAB, where an antialiased gray edge joins the ink or
|
||||
the beige it lies between rather than the skin color nearest it in RGB. A
|
||||
fifth cluster and beyond only carve those gray edges into layers of their
|
||||
own, which render as speckled outlines rather than detail.
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ with `MYGAME_IIGS_GAME_DISK` the disks `build/iigs/bin/MYGAME.2mg` and
|
|||
|MS-DOS |`build/dos/bin/MYGAME.EXE`, with the DPMI server built in
|
||||
|Sharp X68000 |`build/x68000/bin/MYGAME.X`
|
||||
|DVX |`build/dvx/bin/apps/acme/mygame/`: `mygame.app`, `mygame.hlp`, and its
|
||||
`DATA/`
|
||||
`DATA/`; and its package, `build/dvx/bin/packages/mygame.dpk`
|
||||
|SDL3 |`build/sdl3/bin/MYGAME`, a native program with SDL 3 linked in
|
||||
|Web |`build/web/bin/MYGAME/`: `MYGAME.html`, `.js`, `.wasm`, and `.data`
|
||||
(its `DATA/`, packed in), and `icon.png`
|
||||
|
|
@ -142,8 +142,8 @@ otherwise. `PROGRAMS` lists the ids. A project may describe several programs.
|
|||
|Variable |Meaning
|
||||
|
||||
|`PROGRAMS` |The ids that `make` builds.
|
||||
|`TEST_PROGRAMS` |Ids with rules that are built only when asked for (test
|
||||
probes for gates).
|
||||
|`TEST_PROGRAMS` |Ids with rules that are built only when asked for (the
|
||||
tests' probes).
|
||||
|`ID_SRCS` |Its C sources, in link order.
|
||||
|`ID_CFLAGS` |Extra compiler flags: `-I` folders, `-D` defines.
|
||||
|`ID_LDFLAGS` |Extra link flags (`-s` to strip).
|
||||
|
|
@ -154,12 +154,15 @@ ST, DOS, X68000, SDL3, Win32, Win16; on the IIgs, DVX, the web, macOS, and
|
|||
the PSP every program is packaged apart anyway).
|
||||
|`ID_BINDIR` |Build it there instead of `bin/`.
|
||||
|`ID_DATA` |Its data files (<<_data_files>>).
|
||||
|`ID_TITLE` |DVX: its window title. PSP: its title in the PSP's menu.
|
||||
|`ID_TITLE` |DVX: its window title and its package's. PSP: its title in the PSP's menu.
|
||||
macOS: its app's display name (`CFBundleDisplayName`). By default its name.
|
||||
|`ID_ICON` |Its icon: a picture (a PNG, best square, transparent around the
|
||||
figure), baked for every target whose system shows programs by an icon
|
||||
(<<_icons>>). JoeyLib's kangaroo by default.
|
||||
|`ID_HELP` |DVX: the line its help file says about it.
|
||||
|`ID_HELP` |DVX: the line its help file says about it, and its package's
|
||||
summary (at most 127 characters, and no apostrophes).
|
||||
|`ID_VERSION` |Its version, `major.minor.patch` (its DVX package's). By
|
||||
default the project's `PROGRAM_VERSION`, else `1.0.0`.
|
||||
|`ID_IIGS_OVERFLOW_CAP` |IIgs: link large overflow segments (`0xF000`), which
|
||||
a big program needs to leave the Memory Manager room (see
|
||||
`make/support/iigs/clang-build.sh`).
|
||||
|
|
@ -178,8 +181,10 @@ MYGAME_IIGS_DATA := # nothing staged on the IIgs
|
|||
|
||||
DVX's help and resource texts call a program "a `$(DVX_KIND)` program"
|
||||
(`JoeyLib` by default) and name `$(DVX_AUTHOR)` as its author; `DVX_VENDOR`
|
||||
is the folder it installs in under `APPS\` (<<_a_project>>). Set them in the
|
||||
description.
|
||||
is the folder it installs in under `APPS\` (<<_a_project>>). Its DVX package
|
||||
also takes `DVX_PUBLISHER`, `DVX_LICENSE`, `DVX_HOMEPAGE`, and `DVX_PKG_KEY`,
|
||||
and each program's `ID_VERSION` (<<_disk_images_and_packaging>>). Set them in
|
||||
the description.
|
||||
|
||||
=== Data Files
|
||||
|
||||
|
|
@ -250,7 +255,7 @@ program's id:
|
|||
|Apple IIgs |Uppercase, 8 characters |`MYGAME`
|
||||
|Atari ST |Uppercase 8.3, `.PRG` |`MYGAME.PRG`
|
||||
|MS-DOS |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|
||||
|Amiga |Mixed case, no extension: the id Capitalised unless `ID_AMIGA_NAME`
|
||||
|Amiga |Mixed case, no extension: the id Capitalized unless `ID_AMIGA_NAME`
|
||||
says otherwise |`Mygame`
|
||||
|Sharp X68000 |Uppercase 8.3, `.X` |`MYGAME.X`
|
||||
|DVX |Lowercase, 8 characters, a folder of its own |`apps/acme/mygame/mygame.app`
|
||||
|
|
@ -296,10 +301,23 @@ needs to be on the disk.
|
|||
*DVX.* Every program is a folder laid out as it installs in DVX:
|
||||
`build/dvx/bin/apps/acme/mygame/` holds `mygame.app`, its help file
|
||||
`mygame.hlp` (DVX gives every program one; the rules write a one-topic help
|
||||
from `MYGAME_HELP`) and its `DATA/`. Copy that folder into a DVX installation's
|
||||
from `MYGAME_HELP`), and its `DATA/`. Copy that folder into a DVX installation's
|
||||
`APPS\ACME\` and start it from DVX, or let `joey-run` do it
|
||||
(<<_dvx_programs>>).
|
||||
|
||||
It is also a package, `build/dvx/bin/packages/mygame.dpk`, which is how a DVX
|
||||
program is handed out: anyone installs it with DVX's Software program (or
|
||||
`SYSTEM\DVXPKG.EXE install`), and DVX checks it was built for its binary
|
||||
interface. The package is `acme.mygame`; Software shows its title and
|
||||
summary from `MYGAME_TITLE` and `MYGAME_HELP` (at most 127 characters), its
|
||||
version from `MYGAME_VERSION` (else the project's `PROGRAM_VERSION`, else
|
||||
1.0.0), and its publisher, license, and web site from the project's
|
||||
`DVX_PUBLISHER` (`DVX_AUTHOR` by default), `DVX_LICENSE`, and
|
||||
`DVX_HOMEPAGE`. Set `DVX_PKG_KEY` to a secret key file to sign every package,
|
||||
so whoever installs one can tell it is yours: the SDK's tool makes the pair
|
||||
(`toolchains/opt/dvx/sdk/bin/dvxpkg keygen "Acme Games" acme` writes
|
||||
`acme.SEC`, which you keep, and `acme.KEY`, which you hand out).
|
||||
|
||||
*SDL3.* A program is one native executable with SDL 3 linked in statically,
|
||||
so the folder it is in, with its `DATA/`, is all it needs on a machine like
|
||||
the one that built it.
|
||||
|
|
@ -334,10 +352,10 @@ stick; the PSP shows it by its icon and its `ID_TITLE`. The unstripped ELF stays
|
|||
|
||||
A program has an icon on every system that shows programs by one. `ID_ICON`
|
||||
names a picture, a PNG, best square and transparent around the figure, and
|
||||
`make` bakes it for each target with `tools/iconBake`; a program without
|
||||
`make` bakes it for each target with `iconBake` (`build/tools/iconBake`); a program without
|
||||
one gets JoeyLib's kangaroo, `assets/joeyIcon.png`. A picture of 64 pixels or
|
||||
less is taken for pixel art and enlarged by whole pixels; a bigger one is
|
||||
resampled smoothly. Where an icon has few colours, each pixel takes the
|
||||
resampled smoothly. Where an icon has few colors, each pixel takes the
|
||||
nearest.
|
||||
|
||||
[cols="1,5", options="header"]
|
||||
|
|
@ -346,28 +364,28 @@ nearest.
|
|||
|
||||
|Apple IIgs |The Finder's: an icon file (type `$CA`) for each program, which
|
||||
the disk scripts put in the volume's `Icons` folder as `NAME.ICONS`, where the
|
||||
Finder reads icon files when it starts. 24x20 and 12x10 colour icons, which
|
||||
Finder reads icon files when it starts. 24x20 and 12x10 color icons, which
|
||||
the Finder shows as they are: in its 640 mode each icon pixel is two screen
|
||||
pixels, black, red, green, or white beside black, blue, yellow, or white, seen
|
||||
together as one of 16 colours (an icon drawn at an odd screen column would
|
||||
together as one of 16 colors (an icon drawn at an odd screen column would
|
||||
pair them with the other halves of the palette; the Finder drew every one at
|
||||
an even column). A data disk takes the icons
|
||||
last, while there is room; a game disk keeps its program's at its root. A
|
||||
program copied to another volume takes its icon file along, into that
|
||||
volume's `Icons` folder.
|
||||
|Amiga |Workbench's: `Name.info` beside the program, a tool's icon in
|
||||
Workbench's four colours, 48x24 hires pixels, which starts it with
|
||||
Workbench's four colors, 48x24 hires pixels, which starts it with
|
||||
`AMIGA_ICON_STACK` (32768) bytes of stack. Started from it, the program runs
|
||||
in its own folder, as it does from a shell.
|
||||
|DVX |Program Manager's: the program's `icon32` resource, 32x32 on DVX's grey.
|
||||
|Win16 |Program Manager's and the minimised window's: 32x32 in the 16 VGA
|
||||
colours, bound into the `.EXE`.
|
||||
|DVX |Program Manager's: the program's `icon32` resource, 32x32 on DVX's gray.
|
||||
|Win16 |Program Manager's and the minimized window's: 32x32 in the 16 VGA
|
||||
colors, bound into the `.EXE`.
|
||||
|Win32 |Explorer's and the window's: 16, 32, and 48 pixels, each in 256
|
||||
colours and in 32-bit, linked into the `.EXE`.
|
||||
colors and in 32-bit, linked into the `.EXE`.
|
||||
|Haiku, BeOS |Tracker's and the Deskbar's: 32x32 and 16x16 in the BeOS
|
||||
palette, in the program's resources, and on Haiku the same two as one vector
|
||||
icon, which draws each at its own size and the 32x32 cleanly at any larger
|
||||
one (an icon with too many colour patches for the vector format goes
|
||||
one (an icon with too many color patches for the vector format goes
|
||||
without). Tracker shows a file by the icons in its
|
||||
attributes, which a program built on another machine does not have; the
|
||||
program copies them across the first time it runs, so Tracker shows it by its
|
||||
|
|
@ -414,16 +432,18 @@ the JoeyLib checkout, everything lands in its `build/`:
|
|||
[source,console]
|
||||
----
|
||||
$ make # the library and the examples, every installed target
|
||||
$ make iigs-disk # the IIgs examples on build/iigs/bin/joey.2mg
|
||||
$ make iigs-disk # the first IIgs examples that fit on build/iigs/bin/joey.2mg
|
||||
$ make iigs-game-disks # every IIgs example on a bootable disk of its own
|
||||
$ make -f make/iigs.mk iigs-game-disk GAME=DRAW
|
||||
----
|
||||
|
||||
`make iigs` builds only the IIgs library; `make iigs-disk` builds the examples
|
||||
and packs them. Space Taxi and JoeyAGI, two complete games, are projects of
|
||||
`make iigs` builds only the IIgs library; `make iigs-disk` and
|
||||
`make iigs-game-disks` build the examples and pack them. Space Taxi and
|
||||
JoeyAGI, two complete games, are projects of
|
||||
their own built exactly as this chapter describes: each repository's Makefile
|
||||
includes its `program.mk` description between JoeyLib's two makefiles.
|
||||
|
||||
=== The Gates
|
||||
=== The Tests
|
||||
|
||||
The repository carries automated checks. They exist because several classes of
|
||||
bug on these machines are invisible until measured, and they are worth running
|
||||
|
|
@ -438,7 +458,7 @@ Human68k floppies, environment checks) takes paths.
|
|||
|Command |What it proves
|
||||
|
||||
|`make verify-audio`
|
||||
|Every port's audio gate: the IIgs DOC and X68000 chips are read directly,
|
||||
|Every port's audio test: the IIgs DOC and X68000 chips are read directly,
|
||||
the ST's YM2149 register writes are traced (and for the STe build the
|
||||
samples the DMA sound chip plays), the Amiga, DOS, and DVX output
|
||||
is recorded and graded, SDL3's mix is written to a file by SDL's disk
|
||||
|
|
@ -446,7 +466,7 @@ Human68k floppies, environment checks) takes paths.
|
|||
Chromium and graded, as is the Win32 program's under Wine, the Win16
|
||||
program's on Windows 3.11 in DOSBox, and what reaches the PSP's audio output
|
||||
under PPSSPP. Exits 3, not 0,
|
||||
if a gate had to be skipped.
|
||||
if a test had to be skipped.
|
||||
|
||||
|`make iigs-verify`
|
||||
|The IIgs actually reached the program and drew something.
|
||||
|
|
@ -464,7 +484,7 @@ if a gate had to be skipped.
|
|||
|Every program carries its icon where its system looks
|
||||
(`scripts/check-icons.sh`, no emulator), and each system shows it: the IIgs
|
||||
Finder's window, the Amiga's icon.library (and a Workbench start), DVX's
|
||||
Program Manager, Windows 3.11's desktop with the program minimised, the
|
||||
Program Manager, Windows 3.11's desktop with the program minimized, the
|
||||
Win32 and SDL3 windows' icons on X, and Tracker on Haiku and BeOS.
|
||||
|
||||
|`scripts/bench-<port>.sh`
|
||||
|
|
@ -484,7 +504,7 @@ different pixels from the others.
|
|||
|
||||
Debugging a program running on an emulated 1988 machine is awkward. JoeyLib
|
||||
provides a logger that writes to a file next to the executable (on DVX, in
|
||||
the program's settings folder):
|
||||
the program's user folder):
|
||||
|
||||
[source,c]
|
||||
----
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ repository.
|
|||
|
||||
| `list`
|
||||
| Every component, the groups it belongs to, and whether it is current,
|
||||
outdated or missing.
|
||||
outdated, or missing.
|
||||
|
||||
| `install TARGETS`
|
||||
| Installs or updates the targets and everything they depend on, and
|
||||
|
|
@ -118,7 +118,7 @@ programs), `win32` (llvm-mingw; its emulator is a portable Wine), `win16`
|
|||
(Open Watcom; its emulator is Windows for Workgroups 3.11 in DOSBox Staging,
|
||||
set up from your own install disc, see <<Firmware that is not downloaded>>),
|
||||
or `psp` (pspdev's prebuilt release, and SDL 3 built with it; its emulator
|
||||
is PPSSPP's headless build, patched for the gates).
|
||||
is PPSSPP's headless build, patched for the tests).
|
||||
|
||||
| `emulator:PORT`
|
||||
| Everything needed to run that port's programs: the emulator and its
|
||||
|
|
@ -231,14 +231,16 @@ produces a bootable disk image:
|
|||
|
||||
[source,console]
|
||||
----
|
||||
$ make iigs-disk # build/iigs/bin/joey.2mg
|
||||
$ make iigs-disk # build/iigs/bin/joey.2mg: the first examples that fit
|
||||
$ make iigs-game-disks # build/iigs/bin/NAME.2mg: every example, each bootable
|
||||
----
|
||||
|
||||
[TIP]
|
||||
====
|
||||
Never judge a retro build by its exit status alone. Check that the file you
|
||||
expect actually appeared and has a recent timestamp. `make iigs` builds the
|
||||
library but *no examples* -- `make iigs-disk` is what builds and packages them.
|
||||
library but *no examples* -- `make iigs-disk` and `make iigs-game-disks` are
|
||||
what build and package them.
|
||||
====
|
||||
|
||||
=== Hello, JoeyLib
|
||||
|
|
@ -348,9 +350,9 @@ $ ./install.sh install emulator:dos # or just one
|
|||
----
|
||||
|
||||
MAME (for the IIgs and X68000) is always built from source, because the test
|
||||
gates need JoeyLib's patches to it. DOSBox Staging is upstream's own Linux
|
||||
tests need JoeyLib's patches to it. DOSBox Staging is upstream's own Linux
|
||||
release. Hatari and FS-UAE are distribution packages on Debian and Ubuntu;
|
||||
Fedora does not ship the versions the gates expect, so there the installer
|
||||
Fedora does not ship the versions the tests expect, so there the installer
|
||||
builds both from source.
|
||||
|
||||
Point `joey-run` at a program, or name an example together with `--port`:
|
||||
|
|
@ -371,7 +373,7 @@ $ ./joey-run --port haiku draw
|
|||
$ ./joey-run --port beos draw
|
||||
----
|
||||
|
||||
It recognises the machine from the file itself. Interactive runs use the
|
||||
It recognizes the machine from the file itself. Interactive runs use the
|
||||
program's own folder or disk image, so saves and logs persist from one run to
|
||||
the next. Nothing is written to your home directory.
|
||||
|
||||
|
|
@ -405,7 +407,7 @@ ST-build program (`make atarist`) gets a plain ST and an STe-build one
|
|||
(`make ste`) an STe; the STe build runs only on `ste` and `megaste`.
|
||||
|
||||
| `--machine a1200`
|
||||
| Amiga: FS-UAE's machine, `a500` (the default), `a1200` (68EC020, AGA) or
|
||||
| Amiga: FS-UAE's machine, `a500` (the default), `a1200` (68EC020, AGA), or
|
||||
`a4000` (68040, AGA), each on its own Kickstart 3.1. The AGA ones need their
|
||||
Kickstarts (`./install.sh install emulator:aga`).
|
||||
|
||||
|
|
@ -439,8 +441,8 @@ installer took from a DVX tree (`./install.sh install dvx`), set up so DVX
|
|||
boots straight into the program: it is DVX's desktop program, in its own
|
||||
window on a 1024x768 desktop (640x480 with `--headless`), with a Sound
|
||||
Blaster 16. The DVX of interactive runs is kept in `build/dvx/run`, so what
|
||||
the program saves, in DVX's `CONFIG` folder, is there the next time; closing
|
||||
DOSBox ends the run. When the program ends, DVX stays up with an empty
|
||||
the program saves, in its folder in DVX's `USER` folder, is there the next
|
||||
time; closing DOSBox ends the run. When the program ends, DVX stays up with an empty
|
||||
desktop.
|
||||
|
||||
==== SDL3
|
||||
|
|
@ -449,8 +451,9 @@ An SDL3 program is a native program, so `joey-run` simply starts it, in a
|
|||
window; running the file yourself does the same. `--headless` runs it on a
|
||||
private virtual display at one window pixel per stage pixel, so a
|
||||
`--snapshot` is exactly the 320x200 stage. The window's size, shape, and full
|
||||
screen are set by environment variables (<<_sdl3_programs>>). The SDL3 port
|
||||
has no serial port.
|
||||
screen are set by environment variables (<<_sdl3_programs>>). `joey-run`
|
||||
connects no serial port to it: the program opens the computer's own
|
||||
(<<_serial_ports>>).
|
||||
|
||||
==== Web
|
||||
|
||||
|
|
@ -480,7 +483,7 @@ pixel, so a `--snapshot` is the stage.
|
|||
|
||||
A Windows 3.x program runs on Windows for Workgroups 3.11 in DOSBox Staging,
|
||||
on a fresh copy, each run, of the Windows the installer set up from your own
|
||||
disc (`./install.sh install emulator:win16`): 800x600 in 256 colours through
|
||||
disc (`./install.sh install emulator:win16`): 800x600 in 256 colors through
|
||||
the S3 Trio64V+ driver, with Windows' Sound Blaster driver and two drivers
|
||||
of JoeyLib's own: KPJOY.DRV, the joystick driver Windows 3.1 lacks (DOSBox's
|
||||
game port, two sticks), and KPCOMM.DRV in place of Windows' COMM.DRV (COM1
|
||||
|
|
@ -517,7 +520,7 @@ and powers off when the program ends; what the program wrote in its folder
|
|||
(saves, its log) is then copied back. The window's size, shape, and full
|
||||
screen are set by the same environment variables as SDL3's. Haiku's VM has
|
||||
sound; BeOS's has none, and is taken through R5's boot loader menu to its
|
||||
fail-safe video mode (1024x768, 16-bit colour), as R5 has no driver for any
|
||||
fail-safe video mode (1024x768, 16-bit color), as R5 has no driver for any
|
||||
of QEMU's video cards. With `--headless` it runs without a window at one
|
||||
window pixel per stage pixel; `--seconds` counts from when the program
|
||||
starts, after the boot, and a `--snapshot` is QEMU's picture of the whole
|
||||
|
|
@ -534,9 +537,9 @@ $ ./joey-run --headless --seconds 30 --snapshot draw.png --port dos draw
|
|||
|
||||
==== The Apple IIgs
|
||||
|
||||
The IIgs runs programs from disk images. `make -f make/iigs.mk iigs-game-disk
|
||||
GAME=DRAW` builds a bootable one, `build/iigs/bin/DRAW.2mg`, which is also how
|
||||
a game ships. Given a data disk instead, such as `joey.2mg`, `joey-run` boots
|
||||
The IIgs runs programs from disk images. `make iigs-game-disks` builds a
|
||||
bootable one for every example, such as `build/iigs/bin/DRAW.2mg`, which is
|
||||
also how a game ships. Given a data disk instead, such as `joey.2mg`, `joey-run` boots
|
||||
GS/OS beside it and you open the program in the Finder. Given a bare program,
|
||||
`build/iigs/bin/DRAW`, it builds both disks itself, which needs the IIgs
|
||||
toolchain.
|
||||
|
|
@ -599,9 +602,16 @@ arrives. To connect out, `socat` joins a pseudo-terminal to the network.
|
|||
| As on the ST: Wine's COM1 can only be a device, so `socat` joins a
|
||||
pseudo-terminal to the network both ways.
|
||||
|
||||
| SDL3, Web, PSP, Haiku, BeOS
|
||||
| SDL3, macOS, Haiku, BeOS
|
||||
| The computer's own
|
||||
| Not connected by `joey-run`: these programs run on a real computer (or
|
||||
Haiku's and BeOS's VMs), and open its serial device themselves, a USB
|
||||
adapter included. See <<_sdl3_programs>>, <<_macos_programs>>, and
|
||||
<<_haiku_and_beos_programs>> for which device and how to choose another.
|
||||
|
||||
| Web, PSP
|
||||
| None
|
||||
| These ports have no serial port; `jlSerialOpen` fails there.
|
||||
| These have no serial port; `jlSerialOpen` fails there.
|
||||
|===
|
||||
|
||||
`socat` comes from your distribution: `./install.sh deps host --install`
|
||||
|
|
@ -652,7 +662,7 @@ $ ./install.sh import ~/my-roms
|
|||
(point it at your Workbench 3.1 disk for `serial.device` and
|
||||
`lowlevel.library`, and at the S3
|
||||
driver's `.zip` for its three files), identifies each file by its checksum
|
||||
(the list is `installer/files.sha1`) and puts it in place.
|
||||
(the list is `installer/files.sha1`), and puts it in place.
|
||||
|
||||
A project that hands its setup to testers can host these files instead:
|
||||
`./install.sh bundle joeylib-private.tar.gz` packs the ones present on
|
||||
|
|
@ -662,25 +672,30 @@ in `installer/bundle.conf`, makes every `install` fetch it automatically.
|
|||
=== The Example Programs
|
||||
|
||||
The `examples/` directory is the best available reference, and every example is
|
||||
built for every platform:
|
||||
built for, and runs on, every platform. JoeyLib draws no text, so the examples
|
||||
that show words share a small 5x7 font, `examples/common/exampleFont.c`:
|
||||
|
||||
[cols="1,4", options="header"]
|
||||
|===
|
||||
|Example |What it shows
|
||||
|
||||
|`hello` |Minimal init, version query, shutdown.
|
||||
|`draw` |Every drawing primitive, laid out in four quadrants.
|
||||
|`pattern` |Palette and per-scanline palette (SCB) work.
|
||||
|`keys` |Typed-character input and the key predicates.
|
||||
|`joy` |Joystick axes, buttons, and recalibration.
|
||||
|`sprite` |Sprite loading, compiling, save-under, and restore.
|
||||
|`scroll` |A scrolling tile layer under a sprite, with a frame-rate bar.
|
||||
|`audio` |Tracker music, `.MOD` playback, digital sound effects, a streamed sound, and the noise generator.
|
||||
|`save` |Save files and the disk-space query.
|
||||
|`tilebank` |Loading a baked tile bank both ways, and a bank for another machine refused.
|
||||
|`serial` |The optional RS-232 add-on.
|
||||
|`adventure` |A small complete game.
|
||||
|`uber` |The validation and benchmark harness.
|
||||
|`hello` |The smallest program: its greeting, version, and machine on the screen in the default palette, and a key to end it.
|
||||
|`draw` |Every drawing primitive, laid out in four quadrants.
|
||||
|`pattern` |Palette and per-scanline palette (SCB) work.
|
||||
|`keys` |Typed-character input, the key predicates, and the mouse.
|
||||
|`joy` |Joystick axes, buttons, and recalibration.
|
||||
|`sprite` |Sprite loading, compiling, save-under, and restore.
|
||||
|`scroll` |A scrolling tile layer under a sprite, with a frame-rate bar.
|
||||
|`audio` |Tracker music, `.MOD` playback, digital sound effects, a streamed sound, and the noise generator.
|
||||
|`save` |Save files and the disk-space query.
|
||||
|`tilebank` |Loading a baked tile bank both ways, and a bank for another machine refused.
|
||||
|`serial` |A terminal on the optional RS-232 add-on.
|
||||
|`sertest` |A serial self-test: a banner, then an echo of every byte received.
|
||||
|`adventure` |A Sierra-style adventure demo: two rooms, a verb cursor, hotspots, an inventory, walk-mask pathfinding, and walking behind things.
|
||||
|`adventure2` |One room drawn the way Sierra's SCI engine does, with visual, priority, and control screens.
|
||||
|`uber` |The validation and benchmark harness.
|
||||
|`scb` |The per-scanline palette under the heaviest load: all 16 palettes, changing as often as every line.
|
||||
|`sermouse` |The serial port and the mouse together: a stream both ways while the mouse moves (the X68000 runs both on one chip).
|
||||
|===
|
||||
|
||||
Two complete games built on JoeyLib live in repositories of their own and
|
||||
|
|
@ -690,7 +705,8 @@ from its own directory against a JoeyLib checkout, as any program of yours
|
|||
would (<<_building_your_program>>).
|
||||
|
||||
On DVX every example is a folder of its own under `build/dvx/bin/apps/kpunch/`
|
||||
with its help file (<<_dvx_programs>>). On SDL3 every example is a native
|
||||
with its help file, and a package in `build/dvx/bin/packages/`
|
||||
(<<_dvx_programs>>). On SDL3 every example is a native
|
||||
program in `build/sdl3/bin/`, beside the shared `DATA/` (<<_sdl3_programs>>),
|
||||
on the web a page in a folder of its own, `build/web/bin/NAME/`
|
||||
(<<_web_programs>>), on macOS an app, `build/macos/bin/NAME.app`, with its
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ and runs on all of them:
|
|||
|Sharp X68000
|
||||
|68000 at 10 MHz
|
||||
|stock
|
||||
|Draws into the text plane, 320x200 centred in a 512x512 screen.
|
||||
|Draws into the text plane, 320x200 centered in a 512x512 screen.
|
||||
|
||||
|DVX
|
||||
|486 or later
|
||||
|
|
@ -74,7 +74,7 @@ and runs on all of them:
|
|||
|what Windows 3.1 in 386 enhanced mode needs
|
||||
|A Windows 3.x program: the hosted core with a backend written to the
|
||||
Windows 3.1 API, in 32-bit code through Open Watcom's Win386 extender; 8-bit
|
||||
indexed, so every palette is exact on a 256-colour display, and sprites
|
||||
indexed, so every palette is exact on a 256-color display, and sprites
|
||||
compiled as on DOS. See <<_windows_programs>>.
|
||||
|
||||
|PSP
|
||||
|
|
@ -82,8 +82,9 @@ and runs on all of them:
|
|||
|32 MB (PSP-1000)
|
||||
|PSP homebrew, for custom firmware and PPSSPP: the SDL3 port built with
|
||||
pspdev. The stage sits at 1:1 in the middle of the 480x272 screen; the
|
||||
d-pad, nub, and buttons are joystick 0; Start is Return, Select is Escape,
|
||||
and Square opens a keyboard on the screen for every other key. See
|
||||
d-pad, cross, and circle are joystick 0; the analog nub is the mouse, with
|
||||
L and R its buttons; Start is Return, Select is Escape, and Square opens a
|
||||
keyboard on the screen for every other key. See
|
||||
<<_psp_programs>>.
|
||||
|===
|
||||
|
||||
|
|
@ -95,19 +96,25 @@ something performs acceptably there, it performs acceptably everywhere.
|
|||
Every JoeyLib program gets exactly the same screen on every platform:
|
||||
|
||||
* *320 x 200 pixels*, always. There is no other resolution.
|
||||
* *16 colours on screen at once*, chosen per scanline from one of 16 palettes.
|
||||
* *Colour 0 of every palette is black*, always, and is forced to black by the
|
||||
* *16 colors per scanline*: each line shows one of 16 palettes of 16 colors,
|
||||
so up to 241 colors are on the screen at once (256, less the 15 extra
|
||||
blacks, since color 0 is black in every palette).
|
||||
* *Color 0 of every palette is black*, always, and is forced to black by the
|
||||
library even if you try to set it to something else. This is not negotiable:
|
||||
the Amiga and ST hardware border follows colour 0 of the active palette, and
|
||||
colour 0 is the transparent colour for sprite drawing. You have 15 usable
|
||||
colours per palette.
|
||||
* Colours are *12-bit `$0RGB`*: bits 11..8 red, 7..4 green, 3..0 blue.
|
||||
the Amiga and ST hardware border follows color 0 of the active palette, and
|
||||
color 0 is the transparent color for sprite drawing. You have 15 usable
|
||||
colors per palette.
|
||||
* Colors are *12-bit `$0RGB`*: bits 11..8 red, 7..4 green, 3..0 blue.
|
||||
* Origin `(0, 0)` is *top-left*. X increases right, Y increases down.
|
||||
Coordinates are signed `int16_t`, so off-screen positions are representable.
|
||||
|
||||
Every drawing primitive clips to the surface rectangle. An out-of-bounds draw
|
||||
is a silent no-op, never an error. There are no "fast unclipped" variants in
|
||||
the public API, so you cannot corrupt memory by drawing off the edge.
|
||||
is a silent no-op, never an error, so you cannot corrupt memory by drawing off
|
||||
the edge. The exceptions are the sprite calls whose names end in `Trusted`
|
||||
(`jlSpriteDrawTrusted`, `jlSpriteSaveUnderTrusted`,
|
||||
`jlSpriteRestoreUnderTrusted`, and `jlSpriteSaveAndDrawTrusted`): for speed
|
||||
they skip the checks, so the sprite must lie wholly on the surface, and one
|
||||
that does not corrupts memory (<<_the_trusted_variants>>).
|
||||
|
||||
=== What the Library Hides
|
||||
|
||||
|
|
@ -132,7 +139,7 @@ that out of your program:
|
|||
copied to is the text plane, four planes 128 KB apart.
|
||||
|===
|
||||
|
||||
Every public entry point speaks in *colour indices, 0 to 15*. Your code never
|
||||
Every public entry point speaks in *color indices, 0 to 15*. Your code never
|
||||
sees the storage format. The one place the difference leaks through is
|
||||
documented and flagged in this manual -- see <<_writing_portable_code>>.
|
||||
|
||||
|
|
@ -141,10 +148,10 @@ documented and flagged in this manual -- see <<_writing_portable_code>>.
|
|||
* Surfaces, palettes, and per-scanline palette selection
|
||||
* Drawing primitives: pixels, lines, rectangles, circles, flood fill
|
||||
* 8x8 tiles, tile banks, tile maps, and bitmap text
|
||||
* Sprites with colour-0 transparency and *runtime-compiled blitters* -- the
|
||||
* Sprites with color-0 transparency and *runtime-compiled blitters* -- the
|
||||
library emits native 65816, 68000, or x86 code for each sprite at run time
|
||||
(on the hosted ports other than Win16 every sprite is interpreted)
|
||||
* Keyboard (both game-style predicates and a typed-character queue), mouse
|
||||
* Keyboard (both game-style predicates and a typed-character queue), mouse,
|
||||
and joystick
|
||||
* Four-channel Protracker `.MOD` music, a portable chip-tracker music format,
|
||||
digital one-shot and streaming sound effects, and a PSG-style tone and
|
||||
|
|
@ -180,5 +187,5 @@ library can load.
|
|||
directory of its own, compiling for every target.
|
||||
|
||||
The API Reference documents every public function: its exact signature, each
|
||||
parameter, what it returns, and any per-platform behaviour you need to know
|
||||
parameter, what it returns, and any per-platform behavior you need to know
|
||||
about.
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ usually that target.
|
|||
|
||||
. Use `jlAlloc` for anything large, never `malloc`.
|
||||
. Never assume a frame rate. Ask `jlFrameHz()` or use `jlMillisElapsed()`.
|
||||
. Never assume colour 0 is anything but black.
|
||||
. Never assume color 0 is anything but black.
|
||||
. Compile your sprites before you draw them.
|
||||
. Keep large objects out of function-local variables on the IIgs.
|
||||
. Use `jlRandom`, not `rand`, when identical behaviour across ports matters.
|
||||
. Use `jlRandom`, not `rand`, when identical behavior across ports matters.
|
||||
. Keep file names short, uppercase-safe, and 8.3-compatible.
|
||||
. Do not mix save-under with clean-buffer erasing in the same screen region.
|
||||
. Pick one of the audio layers per scene, not two.
|
||||
|
|
@ -81,14 +81,14 @@ Scale by `jlFrameHz()` or work from `jlMillisElapsed()`.
|
|||
`jlFrameCount()` is a 16-bit counter that wraps roughly every 18 minutes at
|
||||
60 Hz, so compare two readings by subtracting them.
|
||||
|
||||
=== Colour and the Palette
|
||||
=== Color and the Palette
|
||||
|
||||
Colour 0 is black in every palette on every platform, and `jlPaletteSet`
|
||||
Color 0 is black in every palette on every platform, and `jlPaletteSet`
|
||||
silently rewrites entry 0 to `$000` no matter what you pass. Do not design
|
||||
artwork that needs colour 0 to be something else -- you have 15 free colours per
|
||||
artwork that needs color 0 to be something else -- you have 15 free colors per
|
||||
palette, and 16 palettes to switch between per scanline.
|
||||
|
||||
Colour 0 is also the transparent colour for sprites. A sprite pixel of colour 0
|
||||
Color 0 is also the transparent color for sprites. A sprite pixel of color 0
|
||||
is never drawn. If you need an opaque rectangle, use the tile operations, which
|
||||
have both opaque and masked forms.
|
||||
|
||||
|
|
@ -100,19 +100,11 @@ Two rules.
|
|||
uncompiled sprite runs through the interpreter, two to six times slower
|
||||
depending on the machine, and nothing warns you.
|
||||
|
||||
*Align your backup buffers.* Give any `jlSpriteBackupT` backing array 2-byte
|
||||
alignment, by declaring it `uint16_t` or with an alignment attribute. On the
|
||||
Amiga, ST, and X68000 an odd buffer address sends save and restore down a much
|
||||
slower path, and it crashes the trusted variants.
|
||||
|
||||
[source,c]
|
||||
----
|
||||
// Right: 2-byte aligned by construction.
|
||||
static uint16_t gBacking[jlSpriteBackupLength(uint16_t, 4, 4)];
|
||||
|
||||
// Risky: may land on an odd address.
|
||||
static uint8_t gBacking[jlSpriteBackupLength(uint8_t, 4, 4)];
|
||||
----
|
||||
*Backup buffers are `uint16_t` arrays.* The buffer of a `jlSpriteBackupT` is
|
||||
declared `uint16_t gBacking[jlSpriteBackupLength(w, h)]`, which gives it the
|
||||
2-byte alignment save and restore need: on the Amiga, ST, and X68000 an odd
|
||||
address sends them down a much slower path, and it crashes the trusted
|
||||
variants.
|
||||
|
||||
Note also that the region a save-under actually captures is often not the
|
||||
sprite rectangle: it can be the *16-pixel-aligned region* containing the
|
||||
|
|
@ -217,7 +209,7 @@ modules and sound effects become silent no-ops (the tone and noise voices
|
|||
still play on the Amiga, the ST, DOS, and the X68000), so a program need not
|
||||
treat missing sound as an error.
|
||||
|
||||
=== Per-Platform Behaviour Summary
|
||||
=== Per-Platform Behavior Summary
|
||||
|
||||
The API is uniform; these are the places where what you get differs.
|
||||
|
||||
|
|
@ -257,7 +249,7 @@ The API is uniform; these are the places where what you get differs.
|
|||
|
||||
|Stereo pan
|
||||
|all three
|
||||
|all three (centred on a free channel each side)
|
||||
|all three (centered on a free channel each side)
|
||||
|ignored (mono); all three in the STe build
|
||||
|all three on an SB16; ignored (mono) before it
|
||||
|ignored
|
||||
|
|
@ -357,8 +349,8 @@ is first pressed; a CD32 pad has both from the start.
|
|||
|
||||
On DOS the game port's counts depend on the stick and on the CPU's speed, so a
|
||||
stick's first reading -- after `jlInit`, after `jlJoystickReset`, or when it is
|
||||
plugged in -- is taken as its centre, and its axes are read in proportion to
|
||||
that: a count of 0 or twice the centre is a full push.
|
||||
plugged in -- is taken as its center, and its axes are read in proportion to
|
||||
that: a count of 0 or twice the center is a full push.
|
||||
|
||||
The X68000's display refreshes at 55.46 Hz. `jlFrameHz` times it when the
|
||||
program starts and rounds to the nearest whole number, so it reports 55 --
|
||||
|
|
@ -367,11 +359,15 @@ and 56 under MAME, whose X68000 display runs slightly fast (56.07 Hz).
|
|||
timer to the millisecond.
|
||||
|
||||
Per-scanline palettes cost CPU time on the ST and the X68000, whose displays
|
||||
have one 16-colour palette. Both reload it as the beam scans, and store each
|
||||
band's 16 colours in the horizontal blanking before the band's first line, so
|
||||
have one 16-color palette. Both reload it as the beam scans, and store each
|
||||
band's 16 colors in the horizontal blanking before the band's first line, so
|
||||
the line shows them from its first pixel. The ST takes a timer interrupt a
|
||||
line above every change of palette, every frame, and waits in it for that
|
||||
line's display to end: about one line of CPU time (64 microseconds) a change.
|
||||
line above a change of palette, every frame, and waits in it for that
|
||||
line's display to end; changes on the lines after it, one a line, it stores
|
||||
before it returns. That is about one line of CPU time (64 microseconds) a
|
||||
change, and a little more where a change is not on the line after another.
|
||||
A change on line 1 is timed from the vertical blank instead, and costs about
|
||||
three lines.
|
||||
A screen with one palette costs nothing on either and arms no interrupt. On
|
||||
the X68000 each change of palette between one
|
||||
line and the next costs about one line of CPU time (32 microseconds); a band
|
||||
|
|
@ -383,11 +379,11 @@ does the same for palettes one line apart, and spends those lines in the
|
|||
interrupt. On the Amiga the copper changes the palettes at no CPU cost: a
|
||||
present that changes palettes or the SCB writes a list of the bands' copper
|
||||
instructions, a few hundred bytes. On the Amiga, the ST, DOS, and the X68000
|
||||
a present's colours and pixels show from the same frame: the Amiga's VBL
|
||||
a present's colors and pixels show from the same frame: the Amiga's VBL
|
||||
switches the copper to the new list with the page it flips to, the ST and
|
||||
the X68000 hand their interrupts the new colours with the page they flip to,
|
||||
and DOS loads changed colours in the vertical blank that shows the new page.
|
||||
A present that changes colours right after another, before the vertical
|
||||
the X68000 hand their interrupts the new colors with the page they flip to,
|
||||
and DOS loads changed colors in the vertical blank that shows the new page.
|
||||
A present that changes colors right after another, before the vertical
|
||||
blank that shows the first, waits for it on the Amiga and DOS (and on DOS
|
||||
the `jlWaitVBL` after it returns at once).
|
||||
|
||||
|
|
@ -412,24 +408,24 @@ names the ports, says how they differ, and gives the reason.
|
|||
* *X68000: a small, wide picture.* The machine has no 320x200 mode, so the stage
|
||||
sits in the middle of a 512x512 screen, a picture about 2.1 times as wide as
|
||||
it is tall. That screen is a 31 kHz mode, so the monitor must take 31 kHz.
|
||||
* *ST: eight levels a channel.* The plain ST's video chip has 3 bits per colour
|
||||
* *ST: eight levels a channel.* The plain ST's video chip has 3 bits per color
|
||||
channel, so `$0RGB` values one apart can look the same. The STe build shows
|
||||
all 4 bits.
|
||||
* *X68000: one intensity bit for three channels.* Its colours have 5 bits per
|
||||
* *X68000: one intensity bit for three channels.* Its colors have 5 bits per
|
||||
channel and an intensity bit the three share. JoeyLib sets that bit for every
|
||||
colour but black, so white is full white, and a channel that is 0 in any
|
||||
other colour shows at 1/63 rather than 0: `$F00` carries a trace of green and
|
||||
color but black, so white is full white, and a channel that is 0 in any
|
||||
other color shows at 1/63 rather than 0: `$F00` carries a trace of green and
|
||||
blue.
|
||||
* *Windows and DVX on a 256-colour display.* The display has one palette for
|
||||
* *Windows and DVX on a 256-color display.* The display has one palette for
|
||||
everything on it. Windows keeps 20 of its entries for itself, and DVX shares
|
||||
it among all its programs, so Win32 and a DVX window show each colour as the
|
||||
it among all its programs, so Win32 and a DVX window show each color as the
|
||||
nearest one the display has, and Win16 shows them exactly only while the 236
|
||||
entries left are enough (<<_windows_programs>>).
|
||||
* *ST and X68000: per-scanline palettes cost time.* Both displays have one
|
||||
16-colour palette at a time, so the CPU loads each band's colours as the beam
|
||||
16-color palette at a time, so the CPU loads each band's colors as the beam
|
||||
reaches it: the ST in a timer interrupt at every change of palette, every
|
||||
frame (about a line of CPU time each), and the X68000 in a raster interrupt.
|
||||
A screen with one palette costs nothing (<<_per_platform_behaviour_summary>>).
|
||||
A screen with one palette costs nothing (<<_per_platform_behavior_summary>>).
|
||||
|
||||
[[portsDifferInput]]
|
||||
==== Input
|
||||
|
|
@ -461,8 +457,9 @@ names the ports, says how they differ, and gives the reason.
|
|||
* *IIgs joystick: one stick, sharing two keys.* The game port takes one stick,
|
||||
and its buttons are wired to the same lines as the Command and Option keys,
|
||||
so holding either key reads as a button.
|
||||
* *Mouse buttons.* The IIgs mouse has one button, and the ST and X68000 mice
|
||||
have two; the other ports report three where the mouse has them.
|
||||
* *Mouse buttons.* Every port has two, left and right, and a left click made
|
||||
with Ctrl held is the right button everywhere, so the IIgs's one-button
|
||||
mouse has both. A middle button is not reported on any port.
|
||||
* *ST and X68000: a stick is always connected.* An empty joystick port reads
|
||||
exactly as a stick at rest, so `jlJoystickConnected` is true for both.
|
||||
* *ST: the mouse port's stick.* The keyboard processor reports port 0 as the
|
||||
|
|
@ -471,9 +468,10 @@ names the ports, says how they differ, and gives the reason.
|
|||
* *Amiga: one button or two.* `lowlevel.library` reports a one-button and a
|
||||
two-button stick alike, so a stick counts as a one-button one until its
|
||||
second button is first pressed.
|
||||
* *PSP: no mouse, one key at a time.* The PSP has no mouse, and its on-screen
|
||||
keyboard has one cursor, so it presses one key at a time (Shift, Ctrl, and
|
||||
Alt stay down until the next key is let go).
|
||||
* *PSP: the nub for a mouse, one key at a time.* The PSP's mouse is its
|
||||
analog nub, which sets the pointer's speed rather than its place, and its
|
||||
on-screen keyboard has one cursor, so it presses one key at a time (Shift,
|
||||
Ctrl, and Alt stay down until the next key is let go).
|
||||
|
||||
[[portsDifferAudio]]
|
||||
==== Audio
|
||||
|
|
@ -498,7 +496,7 @@ names the ports, says how they differ, and gives the reason.
|
|||
on each side, shared by the module, the effects, the three tone voices, and
|
||||
the noise. At most four sounds play at once, an effect can take a channel a
|
||||
voice was using, and a module plays on all four. A channel plays on one side
|
||||
only, so a centred effect needs a free channel on each side, and without
|
||||
only, so a centered effect needs a free channel on each side, and without
|
||||
them it plays on one channel, from one side; and the tone voices, one
|
||||
channel each, are heard from one side: voice 0 (and `jlAudioTone`) on the
|
||||
left, voices 1 and 2 on the right.
|
||||
|
|
@ -633,7 +631,7 @@ names the ports, says how they differ, and gives the reason.
|
|||
is one Memory Manager block inside one bank, 32 KB to 48 KB, and a `jlAlloc`
|
||||
block is a Memory Manager handle, so `jlFree` and `free` must never be given
|
||||
each other's blocks.
|
||||
* *ST: a `<math.h>` of five functions.* The ST's C library has no maths
|
||||
* *ST: a `<math.h>` of five functions.* The ST's C library has no math
|
||||
library, so JoeyLib brings `ceil`, `floor`, `log`, `pow`, and `trunc` (and
|
||||
their `float` forms), good to a few hundred parts per million, and nothing
|
||||
else: a program that calls `sin` or `sqrt` does not link there. Every
|
||||
|
|
@ -721,7 +719,7 @@ Nothing in the API differs between them:
|
|||
|===
|
||||
|
||||
The pixels are identical: UBER's 51 surface hashes match the ST's golden for
|
||||
the STe build on an STe and a Mega STe, and the gates check that under Hatari.
|
||||
the STe build on an STe and a Mega STe, and the tests check that under Hatari.
|
||||
`./joey-run` gives an ST-build program a plain ST and an STe-build program an
|
||||
STe; `--machine st`, `megast`, `ste`, or `megaste` picks another. A project
|
||||
builds the STe variant with `make ste`, like any other port; it takes the ST's
|
||||
|
|
@ -729,7 +727,7 @@ baked assets.
|
|||
|
||||
What the STe build buys, measured with UBER under Hatari against the ST build
|
||||
on a plain ST: a large `jlFillRect` runs up to 1.76 times as fast (80x80), a
|
||||
vertical line 1.4 times and `jlSurfaceClear` 1.1 times; everything else runs
|
||||
vertical line 1.4 times, and `jlSurfaceClear` 1.1 times; everything else runs
|
||||
as on an ST. On a Mega STe at 16 MHz every operation runs about twice as fast.
|
||||
Hatari models that 16 MHz without the RAM wait states a real Mega STe has, so
|
||||
expect less on memory-bound operations there.
|
||||
|
|
@ -750,18 +748,18 @@ hashes are DOS's -- and the rest behaves like this:
|
|||
`JOEYLIB_ASPECT=square` gives the SDL3 window). In 4:3 that is 1x at
|
||||
640x480 and 2x at 800x600 and 1024x768; with square pixels 1x, 2x, and
|
||||
3x. The choice is kept with the full-screen one. The stage reaches the
|
||||
desktop through one colour table per palette, so SCB palettes are exact on
|
||||
15-, 16-, and 32-bit desktops. On a 256-colour desktop every colour is the
|
||||
desktop through one color table per palette, so SCB palettes are exact on
|
||||
15-, 16-, and 32-bit desktops. On a 256-color desktop every color is the
|
||||
nearest one in DVX's palette, which all programs share: gradients band.
|
||||
* *Full screen.* Game > Full Screen, or Alt+Enter, puts the program on the
|
||||
whole screen in DVX's full-screen mode, 320x200 in 256 colours (VGA mode
|
||||
whole screen in DVX's full-screen mode, 320x200 in 256 colors (VGA mode
|
||||
13h, or a VBE mode of that size), and back in its window. There it looks
|
||||
exactly as on MS-DOS: the screen's 256 colours hold all 16 palettes, so
|
||||
exactly as on MS-DOS: the screen's 256 colors hold all 16 palettes, so
|
||||
SCB palettes are exact at any desktop depth and palette changes are free.
|
||||
The other programs run on meanwhile. Alt+Tab, Ctrl+Esc or another window
|
||||
The other programs run on meanwhile. Alt+Tab, Ctrl+Esc, or another window
|
||||
coming to the front brings the desktop back, with the program still
|
||||
running and drawing in its window; giving its window the focus again puts
|
||||
it back on the whole screen. The choice is kept in the program's settings
|
||||
it back on the whole screen. The choice is kept in the program's user
|
||||
folder (`JOEYLIB.INI`), so it starts the way it was left. It is the user's
|
||||
choice: JoeyLib's API has no full-screen call. (Under DOSBox, Alt+Enter is
|
||||
DOSBox's own full-screen key; use Alt with the keypad's Enter, or the
|
||||
|
|
@ -774,12 +772,12 @@ hashes are DOS's -- and the rest behaves like this:
|
|||
does not tear. The frame count is the same clock in both, so switching
|
||||
never makes it jump.
|
||||
* *Keys DVX keeps.* While the window is active, or the program has the whole
|
||||
screen, every key goes to the program, F1, F10, and Alt included, except
|
||||
Alt+Tab (switch programs), Ctrl+Esc (Task Manager) and Alt+F4 (close the
|
||||
window), and Alt+Enter, which the port keeps for full screen. The window's
|
||||
Game menu has Full Screen, Square Pixels, Help (the program's help file),
|
||||
and Exit. Typed
|
||||
characters come through DVX's keyboard layout.
|
||||
screen, every key goes to the program, F1, F10, and Alt included. The
|
||||
exceptions are the three DVX keeps, Alt+Tab (switch programs), Ctrl+Esc
|
||||
(Task Manager), and Alt+F4 (close the window), and Alt+Enter, which the
|
||||
port keeps for full screen. The window's Game menu has Full Screen, Square
|
||||
Pixels, Help (the program's help file), and Exit. Typed characters come
|
||||
through DVX's keyboard layout.
|
||||
* *Closing ends the program.* Alt+F4, the close box, and Game > Exit end the
|
||||
program at its next `jlWaitVBL`, `jlInputPoll`, `jlStagePresent`, or
|
||||
`jlSerialPoll`, as `exit()` does anywhere: its `atexit` handlers run, then
|
||||
|
|
@ -791,9 +789,9 @@ hashes are DOS's -- and the rest behaves like this:
|
|||
it ends.
|
||||
* *Files.* DVX's programs share one current directory, so JoeyLib roots its
|
||||
files at run time: `DATA/` is found beside the program (its folder under
|
||||
`APPS`), while `SAVES/` and `joeylog.txt` go to the program's own settings
|
||||
folder, `CONFIG\APPS\VENDOR\NAME`, which DVX keeps apart from the program
|
||||
files so a reinstall never overwrites them.
|
||||
`APPS`), while `SAVES/` and `joeylog.txt` go to the program's own folder in
|
||||
DVX's user folder, `USER\APPS\VENDOR\NAME`, which DVX keeps apart from the
|
||||
program files so an upgrade never overwrites them.
|
||||
* *Sound* goes through DVX's sound and music libraries, mixed with every
|
||||
other program's: `.MOD` music, SFX in stereo, and the tone voices and the
|
||||
noise as the hosted ports make them, square waves and the YM2149's noise
|
||||
|
|
@ -812,6 +810,11 @@ hashes are DOS's -- and the rest behaves like this:
|
|||
every port, closing one leaves DTR as it is.
|
||||
* A program's `main` is compiled as `jlGameMain`, which the port calls with
|
||||
`argv[0]` the `.app`'s path.
|
||||
* *Packages.* Each program is also a DVX add-on package,
|
||||
`build/dvx/bin/packages/NAME.dpk`, which anyone installs with DVX's
|
||||
Software program (<<_disk_images_and_packaging>>). DVX refuses a program
|
||||
built for another release's binary interface, so build for the DVX it
|
||||
runs on: `./install.sh install dvx` takes the DVX tree's newest.
|
||||
|
||||
`make dvx` builds every example as a DVX program (the `dvx` installer
|
||||
component supplies DVX and its SDK, <<_setting_up_the_toolchains>>), and
|
||||
|
|
@ -954,7 +957,7 @@ on macOS 13 (Ventura) or newer, with SDL 3 built the same way and linked in
|
|||
(`scripts/check-macos-apps.sh`) -- but what an app does on a Mac has not
|
||||
been seen yet.
|
||||
|
||||
`make macos` builds every example as an app; `./joey-run` recognises one
|
||||
`make macos` builds every example as an app; `./joey-run` recognizes one
|
||||
and says it runs on a Mac.
|
||||
|
||||
=== PSP Programs
|
||||
|
|
@ -977,11 +980,15 @@ is the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
* *The screen.* The PSP's 480x272 screen shows the stage at 1:1 in its
|
||||
middle, black around it. The scale and aspect settings do not apply (a PSP
|
||||
has no environment), and there is no full screen to switch to.
|
||||
* *Its own controls, and a keyboard on the screen.* The PSP's controls are
|
||||
joystick 0, through SDL's gamepad mapping: the d-pad or the analog nub,
|
||||
* *Its own controls, a mouse, and a keyboard on the screen.* The PSP's
|
||||
controls come through SDL's gamepad mapping. The d-pad is joystick 0, with
|
||||
cross as `JL_JOY_BUTTON_0` and circle as `JL_JOY_BUTTON_1`; joystick 1 is
|
||||
never there. Start is also the Return key and Select the Escape key, held
|
||||
(`JL_KEY_RETURN`, `JL_KEY_ESCAPE`) and typed (`JL_CHAR_RETURN`,
|
||||
never there. The analog nub is the mouse: tilted past a small dead zone
|
||||
(where a worn nub may rest), it moves the pointer the way it leans, faster
|
||||
the further it leans, up to 320 stage pixels a second (across the stage in
|
||||
a second), and the pointer stays on the stage. L and R are the left and
|
||||
right mouse buttons. Start is also the Return key and Select the Escape
|
||||
key, held (`JL_KEY_RETURN`, `JL_KEY_ESCAPE`) and typed (`JL_CHAR_RETURN`,
|
||||
`JL_CHAR_ESCAPE` from `jlInputGetChar`) as those keys are, so
|
||||
`jlWaitForAnyKey`, Return to go on, and Escape to quit work at a press.
|
||||
Square opens a PC keyboard drawn over the bottom of the screen, US layout,
|
||||
|
|
@ -993,8 +1000,8 @@ is the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
letters on and off. Its keys go down and up and type as a desktop's do,
|
||||
so `jlKeyDown`, `jlKeyPressed`, and `jlInputGetChar` see the same as they
|
||||
would there, F1 to F10, the arrows, and Ctrl and Alt chords included.
|
||||
While it is open, joystick 0 rests: the d-pad and cross are the
|
||||
keyboard's. There is no pointer to move.
|
||||
While it is open, joystick 0 and the mouse buttons rest (the d-pad,
|
||||
cross, L, and R are the keyboard's), and the pointer stays where it is.
|
||||
* *HOME quits.* The HOME menu's Exit ends the program as closing a window
|
||||
does.
|
||||
* *Free space* (`jlDiskFree`) is that of the memory stick (or a PSP Go's
|
||||
|
|
@ -1005,12 +1012,12 @@ is the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
a 16- or 32-bit value at an address that is not a multiple of its size,
|
||||
where an x86 does not notice (a 68000 stops too, but only on odd
|
||||
addresses). Read a multi-byte value from a file buffer a byte at a time.
|
||||
The gates run every PSP program with PPSSPP checking every access the same
|
||||
The tests run every PSP program with PPSSPP checking every access the same
|
||||
way.
|
||||
|
||||
`make psp` builds every example, and `./joey-run --port psp --headless NAME`
|
||||
runs one under PPSSPP's headless build (the `ppsspp` installer component,
|
||||
patched for the gates); `--snapshot` saves the PSP's screen. There is no
|
||||
patched for the tests); `--snapshot` saves the PSP's screen. There is no
|
||||
window to play in here: to play, copy the folder to a PSP, or open its
|
||||
`EBOOT.PBP` in PPSSPP. None of it has run on a real PSP yet.
|
||||
|
||||
|
|
@ -1036,7 +1043,7 @@ the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
shape, 4:3 or square (the same `JOEYLIB_SCALE`, `JOEYLIB_ASPECT`, and
|
||||
`JOEYLIB_FULLSCREEN` settings), black around it; full screen is a
|
||||
borderless window over the whole monitor, which leaves the display mode
|
||||
alone. On a 256-colour desktop Windows maps every colour to the nearest
|
||||
alone. On a 256-color desktop Windows maps every color to the nearest
|
||||
one it has. Each frame draws only what changed since the last: a game
|
||||
that moves a few sprites pays for their rectangles, not for the whole
|
||||
window, which on a Windows 95 machine is most of a frame's time.
|
||||
|
|
@ -1049,7 +1056,7 @@ the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
|
||||
`make win32` builds every example as a Windows program in `build/win32/bin/`,
|
||||
and `./joey-run --port win32 NAME` runs one under Wine (the `wine` installer
|
||||
component, a portable Wine kept in `toolchains/`); the gates run there too,
|
||||
component, a portable Wine kept in `toolchains/`); the tests run there too,
|
||||
`scripts/verify-win32-display.sh` among them, though that checks Wine's GDI
|
||||
rather than Windows'. None of it has run on a real Windows 9x or NT 4 here.
|
||||
|
||||
|
|
@ -1064,12 +1071,12 @@ scan code, DTR dropped at close -- except:
|
|||
32-bit code bound to Watcom's supervisor, which turns each call into the
|
||||
16-bit one Windows takes, so it needs Windows in 386 enhanced mode. It
|
||||
calls only what Windows 3.1 has: USER, GDI, KERNEL, and MMSYSTEM.
|
||||
* *Exact colours.* The frame is 8-bit, each pixel one entry of a 256-entry
|
||||
colour table holding all 16 palettes, which on a 256-colour display is a
|
||||
logical palette too, so every colour is exact, SCB palettes included, as
|
||||
far as the display has room: Windows keeps 20 of a 256-colour display's
|
||||
* *Exact colors.* The frame is 8-bit, each pixel one entry of a 256-entry
|
||||
color table holding all 16 palettes, which on a 256-color display is a
|
||||
logical palette too, so every color is exact, SCB palettes included, as
|
||||
far as the display has room: Windows keeps 20 of a 256-color display's
|
||||
entries for itself, so with more than 14 palettes in use and more than 236
|
||||
different colours on screen, some show as the nearest it has.
|
||||
different colors on screen, some show as the nearest it has.
|
||||
`JOEYLIB_SCALE` defaults to 2, and the settings are read from the DOS
|
||||
environment Windows started from (`joey-run` puts its own there). Under
|
||||
DOSBox, Alt+Enter is DOSBox's own full-screen key; use Alt with the
|
||||
|
|
@ -1081,14 +1088,14 @@ scan code, DTR dropped at close -- except:
|
|||
the machine (its runtime in Windows' `SYSTEM` folder) the rows go straight
|
||||
into a WinG bitmap the size of the window, and drawing a rectangle is a
|
||||
plain copy to the screen; without it, each rectangle goes through a device
|
||||
bitmap on a 256-colour display, and only another kind of display pays for
|
||||
Windows matching every pixel's colour. `JOEYLIB_WING=0` leaves WinG alone.
|
||||
bitmap on a 256-color display, and only another kind of display pays for
|
||||
Windows matching every pixel's color. `JOEYLIB_WING=0` leaves WinG alone.
|
||||
JoeyLib does not ship WinG: a game that wants it on its players' machines
|
||||
ships the runtime under Microsoft's terms (see `LICENSES.md`). While the
|
||||
SCBs use 14 palettes or fewer, the frame's colours are the screen's own (an
|
||||
identity palette, as WinG recommends), so a palette change, colour cycling
|
||||
SCBs use 14 palettes or fewer, the frame's colors are the screen's own (an
|
||||
identity palette, as WinG recommends), so a palette change, color cycling
|
||||
included, reaches the screen at once with nothing redrawn; more palettes
|
||||
than that, or a display without a palette, redraw the window when colours
|
||||
than that, or a display without a palette, redraw the window when colors
|
||||
change. On a 386SX-16 (DOSBox's model of one), a game loop presenting
|
||||
every frame runs at, in frames a second, with an Apple IIgs (under MAME)
|
||||
for scale:
|
||||
|
|
@ -1096,7 +1103,7 @@ scan code, DTR dropped at close -- except:
|
|||
[cols="3,1,1,1,1", options="header"]
|
||||
|===
|
||||
| |2x with WinG |2x without |1:1 |IIgs
|
||||
|Nothing moving, or colour cycling |60 |60 |60 |60
|
||||
|Nothing moving, or color cycling |60 |60 |60 |60
|
||||
|One 16x16 sprite moving |60 |60 |60 |60
|
||||
|Eight sprites moving |26 |20 |30 |20
|
||||
|Eight sprites in a column |30 |20 |30 |20
|
||||
|
|
@ -1118,7 +1125,7 @@ screen every frame, so a game that redraws everything every frame wants
|
|||
driver, which Windows 3.1 itself does not have; without one no stick is
|
||||
seen. JoeyLib's Windows runs KPJOY.DRV (the `kpjoy` installer component,
|
||||
built from the kpjoy project): the PC game port's two sticks, each axis's
|
||||
rest taken from its first readings, so leave the stick centred as the
|
||||
rest taken from its first readings, so leave the stick centered as the
|
||||
program starts. A game shipped for Windows 3.x can ship KPJOY.DRV with it
|
||||
(`[drivers] joystick=kpjoy.drv` in SYSTEM.INI).
|
||||
* *Files.* The program's folder, drive included, is the current directory
|
||||
|
|
@ -1149,11 +1156,11 @@ which the installer puts beside the driver in `toolchains/opt/`.
|
|||
`make win16` builds every example as a Windows 3.x program in
|
||||
`build/win16/bin/`, and `./joey-run --port win16 NAME` runs one on Windows
|
||||
for Workgroups 3.11 in DOSBox Staging (the `win311` installer component, set
|
||||
up from your own install disc); the gates run there too, among them
|
||||
up from your own install disc); the tests run there too, among them
|
||||
`scripts/verify-win16-display.sh`, which checks every pixel the window shows
|
||||
against the stage after every present, with WinG and without (the optional
|
||||
`wing` installer component: `./install.sh import WING10.EXE`, Microsoft's
|
||||
WinG 1.0 SDK, then `./install.sh install wing`; the gates then install its
|
||||
WinG 1.0 SDK, then `./install.sh install wing`; the tests then install its
|
||||
runtime in their Windows). None of it has run on a real 386
|
||||
here; the speeds above are DOSBox's 386SX-16 model's, and the IIgs's are
|
||||
MAME's.
|
||||
|
|
@ -1195,8 +1202,8 @@ Their stage is the same chunky one, so UBER's hashes are DOS's. Everything in
|
|||
* *Serial* through termios: the first entry of `/dev/ports` (Haiku's
|
||||
`pc_serial0`, BeOS's `serial1`), or the device the `JOEYLIB_SERIAL`
|
||||
environment variable names, and the unit'th entry in name order for a
|
||||
slot; 7 or 8 data bits only. The gates run it on Haiku; BeOS R5 shares the
|
||||
code but not the test, since the gates' BeOS VM has no serial ports.
|
||||
slot; 7 or 8 data bits only. The tests run it on Haiku; BeOS R5 shares the
|
||||
code but not the test, since the tests' BeOS VM has no serial ports.
|
||||
* *BeOS R5's loader* refuses any program with a dynamic tag it does not
|
||||
know, so `make beos` links without combined relocations (`-z
|
||||
nocombreloc`). Build for R5 with `make beos`, not `make haiku32`.
|
||||
|
|
@ -1207,8 +1214,8 @@ port's VM under QEMU: Haiku R1/beta6 (the `haikuVm` and `haikuVm32` installer
|
|||
components) or BeOS R5 (`beosVm`, made from your CD), booted from a disk it
|
||||
never writes to, with the program's folder on a second disk. R5 drives none
|
||||
of QEMU's video cards, so its VM is taken through the boot loader's menu to
|
||||
the fail-safe (VESA) mode, 1024x768 in 16-bit colour; it has no sound card.
|
||||
The gates run in the same VMs (`scripts/verify-be-input.sh`,
|
||||
the fail-safe (VESA) mode, 1024x768 in 16-bit color; it has no sound card.
|
||||
The tests run in the same VMs (`scripts/verify-be-input.sh`,
|
||||
`scripts/verify-be-display.sh`, `scripts/verify-be-icon.sh`, and
|
||||
`scripts/bench-be.sh`). None of it has
|
||||
run on a real BeOS or Haiku machine here.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
== Anatomy of a JoeyLib Program
|
||||
|
||||
Every JoeyLib program has the same four-part shape: configure and initialise,
|
||||
set up the screen and load assets, run a frame loop, shut down. This chapter
|
||||
Every JoeyLib program has the same four-part shape: configure and initialize,
|
||||
set up the screen and load assets, run a frame loop, and shut down. This chapter
|
||||
walks through each part and ends with a complete annotated skeleton you can
|
||||
copy.
|
||||
|
||||
=== Part 1: Configure and Initialise
|
||||
=== Part 1: Configure and Initialize
|
||||
|
||||
[source,c]
|
||||
----
|
||||
|
|
@ -57,7 +57,9 @@ The *stage* is the library-owned back buffer. You never create or destroy it;
|
|||
it is valid from `jlInit` to `jlShutdown`. All drawing goes here, and
|
||||
`jlStagePresent()` copies it to the real display.
|
||||
|
||||
Set up colour before drawing:
|
||||
The stage starts black, every line on palette 0, and every palette set to the
|
||||
standard 16 EGA colors (<<_the_default_palette>>), so a program can draw
|
||||
straight away. To use colors of your own, set them up before drawing:
|
||||
|
||||
[source,c]
|
||||
----
|
||||
|
|
@ -67,11 +69,11 @@ jlPaletteSet(stage, 0, colors);
|
|||
jlScbSetRange(stage, 0, JL_SURFACE_HEIGHT - 1, 0);
|
||||
----
|
||||
|
||||
Each surface carries *16 palettes of 16 colours* plus a 200-entry table (one
|
||||
Each surface carries *16 palettes of 16 colors* plus a 200-entry table (one
|
||||
per scanline) selecting which palette that line uses. That table is the *SCB*,
|
||||
or scanline control byte table. Using one palette everywhere is the simple
|
||||
case; assigning different palettes to different bands is how a JoeyLib program
|
||||
shows more than 16 colours at once.
|
||||
shows more than 16 colors at once.
|
||||
|
||||
Load assets after init, because sprite loading needs the codegen arena:
|
||||
|
||||
|
|
@ -345,7 +347,7 @@ Before drawing a sprite, capture what is underneath; next frame, put it back.
|
|||
|
||||
[source,c]
|
||||
----
|
||||
uint16_t backing[jlSpriteBackupLength(uint16_t, 2, 2)];
|
||||
uint16_t backing[jlSpriteBackupLength(2, 2)];
|
||||
jlSpriteBackupT backup;
|
||||
|
||||
backup.bytes = (uint8_t *)backing;
|
||||
|
|
@ -356,10 +358,8 @@ jlSpriteRestoreUnder(stage, &backup); // erase last frame's sprite
|
|||
jlSpriteSaveAndDraw(stage, sprite, x, y, &backup);
|
||||
----
|
||||
|
||||
Size the buffer with `jlSpriteBackupLength`, and declare it as `uint16_t` (or
|
||||
otherwise 2-byte aligned): on the Amiga, ST, and X68000 an odd address sends
|
||||
save and restore down a much slower path, and it crashes the trusted
|
||||
variants.
|
||||
The buffer is an array of `uint16_t`, sized with `jlSpriteBackupLength`;
|
||||
that is what save and restore require (<<_jlspritebackuplength>>).
|
||||
|
||||
==== The Clean-Buffer Pattern
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@
|
|||
// Controls:
|
||||
// Mouse - left-click to act with the current verb on the
|
||||
// thing under the cursor (or walk if no hotspot)
|
||||
// Right-click - cycle verb (WALK / LOOK / TAKE / USE / TALK)
|
||||
// Space - cycle verb (one-button-mouse fallback for IIgs)
|
||||
// Right-click - cycle verb (WALK / LOOK / TAKE / USE / TALK); Ctrl-click
|
||||
// is a right-click on every machine
|
||||
// Space - cycle verb from the keyboard
|
||||
// 1..5 - jump straight to a verb
|
||||
// I - toggle "look at inventory" cursor mode
|
||||
// Click item - select item; cursor becomes the item icon. Next
|
||||
|
|
@ -34,7 +35,7 @@
|
|||
// ESC - quit
|
||||
//
|
||||
// This is intentionally one file: every system (art, engine, rooms,
|
||||
// pathfinding, UI) lives below as a labelled section so a reader can
|
||||
// pathfinding, UI) lives below as a labeled section so a reader can
|
||||
// follow the whole engine without chasing headers.
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
@ -42,6 +43,8 @@
|
|||
|
||||
#include <joey/joey.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
* Section 1 -- Layout, constants, types
|
||||
|
|
@ -217,7 +220,7 @@ static RoomT gRooms[ROOM_COUNT];
|
|||
static jlSpriteT *gEgo[DIR_COUNT][EGO_FRAMES];
|
||||
static uint8_t gEgoTiles[DIR_COUNT][EGO_FRAMES][EGO_TILE_BYTES];
|
||||
|
||||
static uint16_t gEgoBackup[jlSpriteBackupLength(uint16_t, EGO_W / 8, EGO_H / 8)];
|
||||
static uint16_t gEgoBackup[jlSpriteBackupLength(EGO_W / 8, EGO_H / 8)];
|
||||
static jlSpriteBackupT gEgoBak;
|
||||
|
||||
// Cursor sprite per verb (+ item-icon for "using" mode)
|
||||
|
|
@ -225,7 +228,7 @@ static jlSpriteT *gCursor[VERB_COUNT];
|
|||
static jlSpriteT *gItemSprite[ITEM_COUNT]; // index 0 = unused
|
||||
static jlSpriteT *gLampLitSprite; // lit-state inventory icon
|
||||
static uint8_t gLampLitTiles[2 * 2 * JL_TILE_BYTES];
|
||||
static uint16_t gCursorBackup[jlSpriteBackupLength(uint16_t, CURSOR_TILES_X, CURSOR_TILES_Y)];
|
||||
static uint16_t gCursorBackup[jlSpriteBackupLength(CURSOR_TILES_X, CURSOR_TILES_Y)];
|
||||
static jlSpriteBackupT gCursorBak;
|
||||
|
||||
// Ego state
|
||||
|
|
@ -293,7 +296,7 @@ static uint16_t rgb(uint8_t r, uint8_t g, uint8_t b) {
|
|||
* Every sprite is authored as an ASCII "row of pixel chars" grid;
|
||||
* a tiny helper converts that to JoeyLib's 4bpp packed tile-major
|
||||
* layout at startup. Same approach as examples/sprite/sprite.c but
|
||||
* generalised so we can author dozens of frames in one file.
|
||||
* generalized so we can author dozens of frames in one file.
|
||||
*
|
||||
* Char palette in the strings:
|
||||
* ' ' or '.' -> transparent (color 0)
|
||||
|
|
@ -1505,7 +1508,7 @@ static void cellToFoot(int16_t cx, int16_t cy, int16_t *px, int16_t *py) {
|
|||
|
||||
|
||||
/* BFS parents stored as a (row,col)-indexed grid. Each entry is a
|
||||
* direction byte (0=N,1=E,2=S,3=W) telling us which neighbour we
|
||||
* direction byte (0=N,1=E,2=S,3=W) telling us which neighbor we
|
||||
* came from; 0xFF means unvisited. */
|
||||
static uint8_t gBfsParent[WALK_GRID_H][WALK_GRID_W];
|
||||
static int16_t gBfsQueueX[WALK_GRID_H * WALK_GRID_W];
|
||||
|
|
@ -1536,7 +1539,7 @@ static __attribute__((noinline)) void bfsClear(void) {
|
|||
// gBfsParent. Returns true if the target cell was reached. Split out of
|
||||
// findPath to keep the hot loop's live-value count low enough for the
|
||||
// w65816 register allocator.
|
||||
// Visit one neighbour of (cx,cy) in direction d. If that neighbour is
|
||||
// Visit one neighbor of (cx,cy) in direction d. If that neighbor is
|
||||
// walkable and not yet visited, record its incoming direction and append
|
||||
// it to the BFS queue, advancing *qtail. Kept noinline so bfsSearch's
|
||||
// inner loop reduces to a single call and stays within the w65816
|
||||
|
|
@ -1868,134 +1871,13 @@ static int hotspotAt(int16_t mx, int16_t my) {
|
|||
|
||||
|
||||
/* ===================================================================
|
||||
* Section 11 -- Tiny inline 5x7 bitmap font
|
||||
* Section 11 -- Text
|
||||
*
|
||||
* Drawing text uses jlDrawPixel directly (set bits only). The
|
||||
* message bar updates rarely so per-pixel plot is fine.
|
||||
*
|
||||
* Each glyph is 5 wide x 7 tall, stored as 7 bytes (one per row),
|
||||
* with the high 5 bits as pixel pattern. The font covers space,
|
||||
* '0'..'9', 'A'..'Z', '.', ',', ':', '!', '?', '-', '\'', '"'.
|
||||
* Other chars render as space.
|
||||
* The bars' text is the examples' shared 5x7 font
|
||||
* (examples/common/exampleFont.c), drawn on the stage pixel by pixel:
|
||||
* the bars change rarely, so that is fast enough.
|
||||
* =================================================================== */
|
||||
|
||||
#define FONT_W 5
|
||||
#define FONT_H 7
|
||||
|
||||
static const uint8_t kFontGlyph[][FONT_H] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0 space
|
||||
{ 0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70 }, // 1 '0'
|
||||
{ 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 2 '1'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8 }, // 3 '2'
|
||||
{ 0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70 }, // 4 '3'
|
||||
{ 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10 }, // 5 '4'
|
||||
{ 0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70 }, // 6 '5'
|
||||
{ 0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70 }, // 7 '6'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40 }, // 8 '7'
|
||||
{ 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70 }, // 9 '8'
|
||||
{ 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60 }, // 10 '9'
|
||||
{ 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 11 'A'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0 }, // 12 'B'
|
||||
{ 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70 }, // 13 'C'
|
||||
{ 0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0 }, // 14 'D'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8 }, // 15 'E'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80 }, // 16 'F'
|
||||
{ 0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70 }, // 17 'G'
|
||||
{ 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 18 'H'
|
||||
{ 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 19 'I'
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x90, 0x90, 0x60 }, // 20 'J'
|
||||
{ 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88 }, // 21 'K'
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8 }, // 22 'L'
|
||||
{ 0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88 }, // 23 'M'
|
||||
{ 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x88 }, // 24 'N'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 25 'O'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80 }, // 26 'P'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68 }, // 27 'Q'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88 }, // 28 'R'
|
||||
{ 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70 }, // 29 'S'
|
||||
{ 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, // 30 'T'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 31 'U'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20 }, // 32 'V'
|
||||
{ 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88 }, // 33 'W'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88 }, // 34 'X'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20 }, // 35 'Y'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8 }, // 36 'Z'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60 }, // 37 '.'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x40 }, // 38 ','
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00 }, // 39 ':'
|
||||
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20 }, // 40 '!'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20 }, // 41 '?'
|
||||
{ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00 }, // 42 '-'
|
||||
{ 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 43 '\''
|
||||
{ 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 44 '"'
|
||||
};
|
||||
|
||||
static int glyphIdx(char c) {
|
||||
if (c == ' ') {
|
||||
return 0;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
return 1 + (c - '0');
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return 11 + (c - 'A');
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return 11 + (c - 'a'); // uppercase fallback
|
||||
}
|
||||
if (c == '.') {
|
||||
return 37;
|
||||
}
|
||||
if (c == ',') {
|
||||
return 38;
|
||||
}
|
||||
if (c == ':') {
|
||||
return 39;
|
||||
}
|
||||
if (c == '!') {
|
||||
return 40;
|
||||
}
|
||||
if (c == '?') {
|
||||
return 41;
|
||||
}
|
||||
if (c == '-') {
|
||||
return 42;
|
||||
}
|
||||
if (c == '\'') {
|
||||
return 43;
|
||||
}
|
||||
if (c == '"') {
|
||||
return 44;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drawTextLine(int16_t x, int16_t y, const char *text, uint8_t color) {
|
||||
int16_t cx;
|
||||
int16_t i;
|
||||
int idx;
|
||||
int16_t row;
|
||||
int16_t col;
|
||||
uint8_t bits;
|
||||
|
||||
cx = x;
|
||||
for (i = 0; text[i] != '\0'; i++) {
|
||||
idx = glyphIdx(text[i]);
|
||||
for (row = 0; row < FONT_H; row++) {
|
||||
bits = kFontGlyph[idx][row];
|
||||
for (col = 0; col < FONT_W; col++) {
|
||||
if (bits & (0x80 >> col)) {
|
||||
jlDrawPixel(jlgScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
cx = (int16_t)(cx + FONT_W + 1);
|
||||
if (cx > JL_SURFACE_WIDTH - FONT_W) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
* Section 12 -- UI bars (message + inventory)
|
||||
|
|
@ -2009,7 +1891,7 @@ static void drawMessageBar(void) {
|
|||
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
||||
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||
if (gMessage[0] != '\0') {
|
||||
drawTextLine(4, MSG_BAR_Y + 2, gMessage, UI_INK);
|
||||
exampleFontDraw(jlgScreen, 4, MSG_BAR_Y + 2, gMessage, UI_INK);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2024,7 +1906,7 @@ static void drawInventoryBar(void) {
|
|||
|
||||
// Verb readout at the left.
|
||||
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
||||
drawTextLine(4, INV_BAR_Y + 2, buf, UI_HILITE);
|
||||
exampleFontDraw(jlgScreen, 4, INV_BAR_Y + 2, buf, UI_HILITE);
|
||||
|
||||
// Item icons at the right.
|
||||
x = JL_SURFACE_WIDTH - (ITEM_ICON_W + 4);
|
||||
|
|
@ -2356,8 +2238,6 @@ static void enterRoom(RoomE room, bool fromEast, bool fromWest) {
|
|||
gEgoFrameIdx = 0;
|
||||
gPathLen = 0;
|
||||
gPathStep = 0;
|
||||
|
||||
programSkyScb(jlgScreen, room);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2378,6 +2258,12 @@ static bool buildBackgrounds(void) {
|
|||
}
|
||||
drawForestBackground(gBgScene[ROOM_FOREST]);
|
||||
drawCottageBackground(gBgScene[ROOM_COTTAGE]);
|
||||
// jlSurfaceCopy brings a background's palettes and SCB to the stage with
|
||||
// its pixels, so each carries the scene's colors.
|
||||
buildPalettes(gBgScene[ROOM_FOREST]);
|
||||
buildPalettes(gBgScene[ROOM_COTTAGE]);
|
||||
programSkyScb(gBgScene[ROOM_FOREST], ROOM_FOREST);
|
||||
programSkyScb(gBgScene[ROOM_COTTAGE], ROOM_COTTAGE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2420,9 +2306,6 @@ int main(void) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
buildPalettes(jlgScreen);
|
||||
programSkyScb(jlgScreen, ROOM_FOREST);
|
||||
|
||||
if (!buildAllSprites()) {
|
||||
fprintf(stderr, "buildAllSprites failed: %s\n", jlLastError());
|
||||
jlShutdown();
|
||||
|
|
@ -2473,9 +2356,8 @@ int main(void) {
|
|||
if (jlKeyPressed(JL_KEY_5)) {
|
||||
gCurVerb = VERB_TALK;
|
||||
}
|
||||
/* SPACE cycles verb -- one-button-mouse fallback for the
|
||||
* IIgs, where right-click does not exist. Mirrors the
|
||||
* right-click behaviour: drops any in-progress item-use
|
||||
/* SPACE cycles verb from the keyboard. Mirrors the
|
||||
* right-click behavior: drops any in-progress item-use
|
||||
* mode first, then advances the verb. */
|
||||
if (jlKeyPressed(JL_KEY_SPACE)) {
|
||||
if (gUsingItem != ITEM_NONE) {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
// Click on a tree trunk and the BFS still finds a path to the
|
||||
// nearest walkable pixel.
|
||||
//
|
||||
// *** Visualising the buffers ***
|
||||
// *** Visualizing the buffers ***
|
||||
// ------------------------------------------------------------------
|
||||
// Press P to swap the visible screen with the priority buffer; the
|
||||
// palette is replaced with a rainbow ramp so each priority band is
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
// Controls:
|
||||
// Mouse left walk to / act on whatever is under the cursor
|
||||
// Mouse right cycle verb (WALK / LOOK / TAKE)
|
||||
// SPACE cycle verb (one-button-mouse fallback)
|
||||
// SPACE cycle verb from the keyboard
|
||||
// 1 / 2 / 3 jump straight to a verb
|
||||
// P toggle priority-screen overlay
|
||||
// C toggle control-screen overlay
|
||||
|
|
@ -78,6 +78,8 @@
|
|||
|
||||
#include <joey/joey.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
* Section 1 -- Layout, priority bands, control codes
|
||||
|
|
@ -110,7 +112,7 @@
|
|||
#define CTRL_TRIGGER_LOOK 2 // "you smell the forest" trigger zone
|
||||
|
||||
/* Pathfinding grid -- one cell per 4x4 pixel block, queried against
|
||||
* the control surface (sampled at the cell centre). */
|
||||
* the control surface (sampled at the cell center). */
|
||||
#define WALK_CELL_PX 4
|
||||
#define WALK_GRID_W (JL_SURFACE_WIDTH / WALK_CELL_PX) // 80
|
||||
#define WALK_GRID_H (PLAY_AREA_H / WALK_CELL_PX) // 42
|
||||
|
|
@ -540,114 +542,15 @@ static const PropDef kProps[] = {
|
|||
|
||||
|
||||
/* ===================================================================
|
||||
* Section 6 -- Tiny 5x7 font (lifted from adventure.c for self-
|
||||
* containment).
|
||||
* Section 6 -- Text
|
||||
*
|
||||
* The bars' text is the examples' shared 5x7 font
|
||||
* (examples/common/exampleFont.c), drawn on the stage.
|
||||
* =================================================================== */
|
||||
|
||||
#define FONT_W 5
|
||||
#define FONT_H 7
|
||||
|
||||
static const uint8_t kFontGlyph[][FONT_H] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0 space
|
||||
{ 0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70 }, // 1 '0'
|
||||
{ 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 2 '1'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8 }, // 3 '2'
|
||||
{ 0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70 }, // 4 '3'
|
||||
{ 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10 }, // 5 '4'
|
||||
{ 0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70 }, // 6 '5'
|
||||
{ 0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70 }, // 7 '6'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40 }, // 8 '7'
|
||||
{ 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70 }, // 9 '8'
|
||||
{ 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60 }, // 10 '9'
|
||||
{ 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 11 'A'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0 }, // 12 'B'
|
||||
{ 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70 }, // 13 'C'
|
||||
{ 0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0 }, // 14 'D'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8 }, // 15 'E'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80 }, // 16 'F'
|
||||
{ 0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70 }, // 17 'G'
|
||||
{ 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 18 'H'
|
||||
{ 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 19 'I'
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x90, 0x90, 0x60 }, // 20 'J'
|
||||
{ 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88 }, // 21 'K'
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8 }, // 22 'L'
|
||||
{ 0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88 }, // 23 'M'
|
||||
{ 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x88 }, // 24 'N'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 25 'O'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80 }, // 26 'P'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68 }, // 27 'Q'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88 }, // 28 'R'
|
||||
{ 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70 }, // 29 'S'
|
||||
{ 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, // 30 'T'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 31 'U'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20 }, // 32 'V'
|
||||
{ 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88 }, // 33 'W'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88 }, // 34 'X'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20 }, // 35 'Y'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8 }, // 36 'Z'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60 }, // 37 '.'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x40 }, // 38 ','
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00 }, // 39 ':'
|
||||
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20 }, // 40 '!'
|
||||
};
|
||||
|
||||
static int glyphIdx(char c) {
|
||||
if (c == ' ') {
|
||||
return 0;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
return 1 + (c - '0');
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return 11 + (c - 'A');
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return 11 + (c - 'a');
|
||||
}
|
||||
if (c == '.') {
|
||||
return 37;
|
||||
}
|
||||
if (c == ',') {
|
||||
return 38;
|
||||
}
|
||||
if (c == ':') {
|
||||
return 39;
|
||||
}
|
||||
if (c == '!') {
|
||||
return 40;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drawText(int16_t x, int16_t y, const char *text, uint8_t color) {
|
||||
int16_t cx;
|
||||
int i;
|
||||
int idx;
|
||||
int16_t row;
|
||||
int16_t col;
|
||||
uint8_t bits;
|
||||
|
||||
cx = x;
|
||||
for (i = 0; text[i] != '\0'; i++) {
|
||||
idx = glyphIdx(text[i]);
|
||||
for (row = 0; row < FONT_H; row++) {
|
||||
bits = kFontGlyph[idx][row];
|
||||
for (col = 0; col < FONT_W; col++) {
|
||||
if (bits & (0x80 >> col)) {
|
||||
jlDrawPixel(jlgScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
cx = (int16_t)(cx + FONT_W + 1);
|
||||
if (cx > JL_SURFACE_WIDTH - FONT_W) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void setMessage(const char *text) {
|
||||
size_t n;
|
||||
|
||||
n = strlen(text);
|
||||
if (n >= sizeof(gMessage)) {
|
||||
n = sizeof(gMessage) - 1;
|
||||
|
|
@ -668,7 +571,7 @@ static uint16_t rgb(uint8_t r, uint8_t g, uint8_t b) {
|
|||
}
|
||||
|
||||
|
||||
static void buildPalettes(void) {
|
||||
static void buildPalettes(jlSurfaceT *s) {
|
||||
uint16_t scene[16];
|
||||
uint16_t pri[16];
|
||||
uint16_t ctl[16];
|
||||
|
|
@ -736,18 +639,19 @@ static void buildPalettes(void) {
|
|||
ui[UI_HILITE] = rgb(15, 13, 3);
|
||||
ui[UI_DIM] = rgb(7, 7, 8);
|
||||
|
||||
jlPaletteSet(jlgScreen, PAL_SCENE, scene);
|
||||
jlPaletteSet(jlgScreen, PAL_DEBUG_PRI, pri);
|
||||
jlPaletteSet(jlgScreen, PAL_DEBUG_CTRL, ctl);
|
||||
jlPaletteSet(jlgScreen, PAL_UI, ui);
|
||||
jlPaletteSet(s, PAL_SCENE, scene);
|
||||
jlPaletteSet(s, PAL_DEBUG_PRI, pri);
|
||||
jlPaletteSet(s, PAL_DEBUG_CTRL, ctl);
|
||||
jlPaletteSet(s, PAL_UI, ui);
|
||||
}
|
||||
|
||||
|
||||
/* The stage's SCB selects which of the 4 palettes is active on each
|
||||
/* A surface's SCB selects which of the 4 palettes is active on each
|
||||
* scanline. Normal view uses PAL_SCENE for the play area and PAL_UI
|
||||
* for the message/inventory bars. Debug views swap PAL_SCENE for
|
||||
* the matching debug palette. */
|
||||
static void programScbForView(DebugViewE view) {
|
||||
* the matching debug palette. Each of the three screens carries its
|
||||
* view's, which jlSurfaceCopy brings to the stage with its pixels. */
|
||||
static void programScbForView(jlSurfaceT *s, DebugViewE view) {
|
||||
int16_t line;
|
||||
uint8_t playPal;
|
||||
|
||||
|
|
@ -763,10 +667,10 @@ static void programScbForView(DebugViewE view) {
|
|||
break;
|
||||
}
|
||||
for (line = 0; line < PLAY_AREA_H; line++) {
|
||||
jlScbSet(jlgScreen, (uint16_t)line, playPal);
|
||||
jlScbSet(s, (uint16_t)line, playPal);
|
||||
}
|
||||
for (line = PLAY_AREA_H; line < JL_SURFACE_HEIGHT; line++) {
|
||||
jlScbSet(jlgScreen, (uint16_t)line, PAL_UI);
|
||||
jlScbSet(s, (uint16_t)line, PAL_UI);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1333,7 +1237,7 @@ static void drawMessageBar(void) {
|
|||
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
||||
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||
if (gMessage[0] != '\0') {
|
||||
drawText(4, (int16_t)(MSG_BAR_Y + 2), gMessage, UI_INK);
|
||||
exampleFontDraw(jlgScreen, 4, (int16_t)(MSG_BAR_Y + 2), gMessage, UI_INK);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1346,7 +1250,7 @@ static void drawInventoryBar(void) {
|
|||
jlFillRect(jlgScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||
|
||||
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
||||
drawText(4, (int16_t)(INV_BAR_Y + 2), buf, UI_HILITE);
|
||||
exampleFontDraw(jlgScreen, 4, (int16_t)(INV_BAR_Y + 2), buf, UI_HILITE);
|
||||
|
||||
switch (gDebugView) {
|
||||
case VIEW_PRIORITY:
|
||||
|
|
@ -1359,7 +1263,7 @@ static void drawInventoryBar(void) {
|
|||
vw = "VIEW: NORMAL (P pri, C ctrl)";
|
||||
break;
|
||||
}
|
||||
drawText(4, (int16_t)(INV_BAR_Y + 11), vw, UI_INK);
|
||||
exampleFontDraw(jlgScreen, 4, (int16_t)(INV_BAR_Y + 11), vw, UI_INK);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1525,9 +1429,15 @@ int main(void) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
buildPalettes();
|
||||
// jlSurfaceCopy brings a screen's palettes and SCB to the stage with
|
||||
// its pixels, so each carries them.
|
||||
buildPalettes(gVisual);
|
||||
buildPalettes(gPriority);
|
||||
buildPalettes(gControl);
|
||||
programScbForView(gVisual, VIEW_NORMAL);
|
||||
programScbForView(gPriority, VIEW_PRIORITY);
|
||||
programScbForView(gControl, VIEW_CONTROL);
|
||||
gDebugView = VIEW_NORMAL;
|
||||
programScbForView(gDebugView);
|
||||
|
||||
paintForestVisual();
|
||||
paintForestPriority();
|
||||
|
|
@ -1574,15 +1484,12 @@ int main(void) {
|
|||
* on every target's standard keyboard. */
|
||||
if (jlKeyPressed(JL_KEY_P)) {
|
||||
gDebugView = (gDebugView == VIEW_PRIORITY) ? VIEW_NORMAL : VIEW_PRIORITY;
|
||||
programScbForView(gDebugView);
|
||||
}
|
||||
if (jlKeyPressed(JL_KEY_C)) {
|
||||
gDebugView = (gDebugView == VIEW_CONTROL) ? VIEW_NORMAL : VIEW_CONTROL;
|
||||
programScbForView(gDebugView);
|
||||
}
|
||||
if (jlKeyPressed(JL_KEY_N)) {
|
||||
gDebugView = VIEW_NORMAL;
|
||||
programScbForView(gDebugView);
|
||||
}
|
||||
|
||||
mx = jlMouseX();
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ int main(void) {
|
|||
}
|
||||
if (keyPressed(JL_KEY_N, frame) && !noiseOn) {
|
||||
// The song's own drums would fight the sweep for the noise
|
||||
// generator, so it pauses and picks up again afterwards.
|
||||
// generator, so it pauses and picks up again afterward.
|
||||
noiseResumes = jlMusicIsPlaying() && !musicPaused;
|
||||
jlMusicPause();
|
||||
noiseOn = true;
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ static void loadMod(uint8_t **bytes, uint32_t *length) {
|
|||
|
||||
*bytes = NULL;
|
||||
*length = 0u;
|
||||
fp = jlDataOpen(TEST_MOD_NAME, "rb");
|
||||
fp = jlDataOpen(TEST_MOD_NAME, "rb");
|
||||
if (fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
122
examples/common/exampleFont.c
Normal file
122
examples/common/exampleFont.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||
// See LICENSE.md for license information.
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// exampleFont.c - the examples' 5x7 font (exampleFont.h). Each glyph is seven
|
||||
// rows, the left pixel in each row's top bit.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
#define GLYPH_SPACE 0
|
||||
#define GLYPH_DIGIT_BASE 1
|
||||
#define GLYPH_ALPHA_BASE 11
|
||||
#define GLYPH_PUNCT_BASE 37
|
||||
#define GLYPH_UNKNOWN 52
|
||||
#define GLYPH_TOP_BIT 0x80u
|
||||
|
||||
static uint8_t glyphIndex(char c);
|
||||
|
||||
// The punctuation the font draws, in the order of its glyphs from
|
||||
// GLYPH_PUNCT_BASE.
|
||||
static const char kPunctuation[] = ".,:!?-'\"/;=+_()";
|
||||
|
||||
static const uint8_t kGlyphs[][EXAMPLE_FONT_H] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0 space
|
||||
{ 0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70 }, // 1 '0'
|
||||
{ 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 2 '1'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8 }, // 3 '2'
|
||||
{ 0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70 }, // 4 '3'
|
||||
{ 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10 }, // 5 '4'
|
||||
{ 0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70 }, // 6 '5'
|
||||
{ 0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70 }, // 7 '6'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40 }, // 8 '7'
|
||||
{ 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70 }, // 9 '8'
|
||||
{ 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60 }, // 10 '9'
|
||||
{ 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 11 'A'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0 }, // 12 'B'
|
||||
{ 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70 }, // 13 'C'
|
||||
{ 0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0 }, // 14 'D'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8 }, // 15 'E'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80 }, // 16 'F'
|
||||
{ 0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70 }, // 17 'G'
|
||||
{ 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 18 'H'
|
||||
{ 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 19 'I'
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x90, 0x90, 0x60 }, // 20 'J'
|
||||
{ 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88 }, // 21 'K'
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8 }, // 22 'L'
|
||||
{ 0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88 }, // 23 'M'
|
||||
{ 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x88 }, // 24 'N'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 25 'O'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80 }, // 26 'P'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68 }, // 27 'Q'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88 }, // 28 'R'
|
||||
{ 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70 }, // 29 'S'
|
||||
{ 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, // 30 'T'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 31 'U'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20 }, // 32 'V'
|
||||
{ 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88 }, // 33 'W'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88 }, // 34 'X'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20 }, // 35 'Y'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8 }, // 36 'Z'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60 }, // 37 '.'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x40 }, // 38 ','
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00 }, // 39 ':'
|
||||
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20 }, // 40 '!'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20 }, // 41 '?'
|
||||
{ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00 }, // 42 '-'
|
||||
{ 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 43 '\''
|
||||
{ 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 44 '"'
|
||||
{ 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00 }, // 45 '/'
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x40 }, // 46 ';'
|
||||
{ 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00 }, // 47 '='
|
||||
{ 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00 }, // 48 '+'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8 }, // 49 '_'
|
||||
{ 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10 }, // 50 '('
|
||||
{ 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40 }, // 51 ')'
|
||||
{ 0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF8 } // 52 unknown box
|
||||
};
|
||||
|
||||
|
||||
// The glyph that draws c.
|
||||
static uint8_t glyphIndex(char c) {
|
||||
const char *punct;
|
||||
|
||||
if (c == ' ') {
|
||||
return GLYPH_SPACE;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
return (uint8_t)(GLYPH_DIGIT_BASE + (c - '0'));
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return (uint8_t)(GLYPH_ALPHA_BASE + (c - 'A'));
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return (uint8_t)(GLYPH_ALPHA_BASE + (c - 'a'));
|
||||
}
|
||||
punct = strchr(kPunctuation, c);
|
||||
if (c != '\0' && punct != NULL) {
|
||||
return (uint8_t)(GLYPH_PUNCT_BASE + (punct - kPunctuation));
|
||||
}
|
||||
return GLYPH_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
void exampleFontDraw(jlSurfaceT *s, int16_t x, int16_t y, const char *text, uint8_t color) {
|
||||
const uint8_t *glyph;
|
||||
int16_t row;
|
||||
int16_t col;
|
||||
|
||||
for (; *text != '\0' && x <= JL_SURFACE_WIDTH - EXAMPLE_FONT_W; text++) {
|
||||
glyph = kGlyphs[glyphIndex(*text)];
|
||||
for (row = 0; row < EXAMPLE_FONT_H; row++) {
|
||||
for (col = 0; col < EXAMPLE_FONT_W; col++) {
|
||||
if ((glyph[row] & (GLYPH_TOP_BIT >> col)) != 0u) {
|
||||
jlDrawPixel(s, (int16_t)(x + col), (int16_t)(y + row), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
x = (int16_t)(x + EXAMPLE_FONT_ADVANCE);
|
||||
}
|
||||
}
|
||||
23
examples/common/exampleFont.h
Normal file
23
examples/common/exampleFont.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||
// See LICENSE.md for license information.
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// exampleFont.h - the examples' 5x7 font. JoeyLib draws no text of its own,
|
||||
// so the examples that show words share this one: the digits, the letters
|
||||
// (lower case drawn as upper case), and . , : ! ? - ' " / ; = + _ ( ),
|
||||
// each on a 6-pixel advance. Any other character is drawn as a box.
|
||||
|
||||
#ifndef EXAMPLE_FONT_HEADER
|
||||
#define EXAMPLE_FONT_HEADER
|
||||
|
||||
#include <joey/joey.h>
|
||||
|
||||
#define EXAMPLE_FONT_W 5
|
||||
#define EXAMPLE_FONT_H 7
|
||||
#define EXAMPLE_FONT_ADVANCE (EXAMPLE_FONT_W + 1)
|
||||
|
||||
// Draw text on s with its top-left corner at (x, y), in color; it stops at
|
||||
// the surface's right edge.
|
||||
void exampleFontDraw(jlSurfaceT *s, int16_t x, int16_t y, const char *text, uint8_t color);
|
||||
|
||||
#endif
|
||||
|
|
@ -2,28 +2,57 @@
|
|||
// See LICENSE.md for license information.
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// JoeyLib hello-world example.
|
||||
//
|
||||
// Validates that the C API headers, library, and per-platform link
|
||||
// path all work end-to-end.
|
||||
// JoeyLib hello-world example: the smallest program that shows something. It
|
||||
// starts the library, writes a greeting, the library's version, and the
|
||||
// machine it runs on on the stage in the default palette (no jlPaletteSet:
|
||||
// every surface starts with the 16 EGA colors), presents it, says the same on
|
||||
// the console where there is one, and waits for a key.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <joey/joey.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
// No sprites, so the smallest compiled-sprite cache.
|
||||
#define CODEGEN_BYTES ((uint32_t)8 * 1024)
|
||||
|
||||
// The lines, each a label and its value, and where they go: the greeting
|
||||
// in yellow, the version and the platform in white below it, the prompt in
|
||||
// light gray.
|
||||
#define TEXT_X 64
|
||||
#define GREETING_Y 72
|
||||
#define VERSION_Y 96
|
||||
#define PLATFORM_Y 108
|
||||
#define PROMPT_Y 132
|
||||
#define VALUE_X (TEXT_X + 10 * EXAMPLE_FONT_ADVANCE)
|
||||
#define COLOR_YELLOW 14
|
||||
#define COLOR_WHITE 15
|
||||
#define COLOR_GRAY 7
|
||||
|
||||
|
||||
int main(void) {
|
||||
jlConfigT config;
|
||||
jlConfigT config;
|
||||
jlSurfaceT *stage;
|
||||
|
||||
config.codegenBytes = 8 * 1024;
|
||||
config.codegenBytes = CODEGEN_BYTES;
|
||||
config.noAudio = true;
|
||||
|
||||
if (!jlInit(&config)) {
|
||||
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("JoeyLib %s\n", jlVersionString());
|
||||
printf("Platform: %s\n", jlPlatformName());
|
||||
printf("Hello from JoeyLib.\n");
|
||||
stage = jlStageGet();
|
||||
exampleFontDraw(stage, TEXT_X, GREETING_Y, "Hello from JoeyLib!", COLOR_YELLOW);
|
||||
exampleFontDraw(stage, TEXT_X, VERSION_Y, "Version:", COLOR_WHITE);
|
||||
exampleFontDraw(stage, VALUE_X, VERSION_Y, jlVersionString(), COLOR_WHITE);
|
||||
exampleFontDraw(stage, TEXT_X, PLATFORM_Y, "Platform:", COLOR_WHITE);
|
||||
exampleFontDraw(stage, VALUE_X, PLATFORM_Y, jlPlatformName(), COLOR_WHITE);
|
||||
exampleFontDraw(stage, TEXT_X, PROMPT_Y, "Press any key.", COLOR_GRAY);
|
||||
jlStagePresent();
|
||||
// The same on the console, where there is one (scripts/verify-ste-machine.sh
|
||||
// reads it through Hatari).
|
||||
printf("JoeyLib %s\nPlatform: %s\n", jlVersionString(), jlPlatformName());
|
||||
jlWaitForAnyKey();
|
||||
|
||||
jlShutdown();
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include <joey/joey.h>
|
||||
#include <joey/debug.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
#define GRID_COLS 10
|
||||
#define GRID_ROWS 6
|
||||
#define CELL_W 28
|
||||
|
|
@ -51,8 +53,6 @@
|
|||
|
||||
// Typed-text line under the grid (the grid ends at y = 178 with the
|
||||
// 26-pixel cells above).
|
||||
#define FONT_W 5
|
||||
#define FONT_H 7
|
||||
#define TEXT_STRIP_Y 180
|
||||
#define TEXT_X 4
|
||||
#define TEXT_Y 182
|
||||
|
|
@ -75,8 +75,8 @@
|
|||
#define VERIFY_WORD_NIBBLES 4
|
||||
|
||||
// Mouse verification scanline (the row below the typed-character one):
|
||||
// its own 5A5A sentinel, then present/left/right/middle as one nibble
|
||||
// each, then jlMouseX and jlMouseY as four nibbles each.
|
||||
// its own 5A5A sentinel, then present/left/right as one nibble each and a
|
||||
// zero nibble, then jlMouseX and jlMouseY as four nibbles each.
|
||||
// Re-stamped only when the reported state changes.
|
||||
#define VERIFY_MOUSE_Y 199
|
||||
#define VERIFY_MOUSE_SENTINEL 0x5A5A
|
||||
|
|
@ -90,7 +90,6 @@
|
|||
#define MOUSE_FLAG_PRESENT 0x1000
|
||||
#define MOUSE_FLAG_LEFT 0x0100
|
||||
#define MOUSE_FLAG_RIGHT 0x0010
|
||||
#define MOUSE_FLAG_MIDDLE 0x0001
|
||||
|
||||
static void buildPalette(jlSurfaceT *screen);
|
||||
static void cellAtPoint(int16_t px, int16_t py, int16_t *outCol, int16_t *outRow);
|
||||
|
|
@ -98,9 +97,7 @@ static bool cellTargetLit(int16_t col, int16_t row, int16_t cursorCol, int16_t c
|
|||
static void drawCell(jlSurfaceT *screen, int16_t col, int16_t row, bool lit);
|
||||
static void drawCursor(jlSurfaceT *screen, int16_t x, int16_t y);
|
||||
static void drawMouseVerifyRow(jlSurfaceT *screen);
|
||||
static void drawTextLine(jlSurfaceT *screen, int16_t x, int16_t y, const char *text, uint8_t color);
|
||||
static void drawVerifyRow(jlSurfaceT *screen);
|
||||
static int glyphIdx(char c);
|
||||
static void initialPaint(jlSurfaceT *screen);
|
||||
static void logTypedHistory(void);
|
||||
static uint16_t mouseFlags(void);
|
||||
|
|
@ -142,86 +139,6 @@ static int16_t gLineLen = 0;
|
|||
static uint8_t gReceived[MAX_RECEIVED];
|
||||
static int16_t gReceivedCount = 0;
|
||||
|
||||
// Tiny 5x7 font (high 5 bits of each row byte), borrowed from the
|
||||
// adventure example and extended with a few typed-punctuation glyphs.
|
||||
// Coverage: space, 0-9, A-Z (lowercase renders uppercase), and
|
||||
// . , : ! ? - ' " / ; = + _ -- everything else renders as the hollow
|
||||
// box at the end of the table.
|
||||
//
|
||||
// Row indices used by glyphIdx. The table below must keep this order;
|
||||
// the trailing row comments carry the same numbers.
|
||||
#define GLYPH_SPACE 0
|
||||
#define GLYPH_DIGIT_BASE 1
|
||||
#define GLYPH_ALPHA_BASE 11
|
||||
#define GLYPH_PERIOD 37
|
||||
#define GLYPH_COMMA 38
|
||||
#define GLYPH_COLON 39
|
||||
#define GLYPH_BANG 40
|
||||
#define GLYPH_QUESTION 41
|
||||
#define GLYPH_MINUS 42
|
||||
#define GLYPH_APOSTROPHE 43
|
||||
#define GLYPH_QUOTE 44
|
||||
#define GLYPH_SLASH 45
|
||||
#define GLYPH_SEMICOLON 46
|
||||
#define GLYPH_EQUALS 47
|
||||
#define GLYPH_PLUS 48
|
||||
#define GLYPH_UNDERSCORE 49
|
||||
#define GLYPH_UNKNOWN 50
|
||||
|
||||
static const uint8_t kFontGlyph[][FONT_H] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0 space
|
||||
{ 0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70 }, // 1 '0'
|
||||
{ 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 2 '1'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8 }, // 3 '2'
|
||||
{ 0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70 }, // 4 '3'
|
||||
{ 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10 }, // 5 '4'
|
||||
{ 0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70 }, // 6 '5'
|
||||
{ 0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70 }, // 7 '6'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40 }, // 8 '7'
|
||||
{ 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70 }, // 9 '8'
|
||||
{ 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60 }, // 10 '9'
|
||||
{ 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 11 'A'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0 }, // 12 'B'
|
||||
{ 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70 }, // 13 'C'
|
||||
{ 0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0 }, // 14 'D'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8 }, // 15 'E'
|
||||
{ 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80 }, // 16 'F'
|
||||
{ 0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70 }, // 17 'G'
|
||||
{ 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88 }, // 18 'H'
|
||||
{ 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70 }, // 19 'I'
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x90, 0x90, 0x60 }, // 20 'J'
|
||||
{ 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88 }, // 21 'K'
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8 }, // 22 'L'
|
||||
{ 0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88 }, // 23 'M'
|
||||
{ 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x88 }, // 24 'N'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 25 'O'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80 }, // 26 'P'
|
||||
{ 0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68 }, // 27 'Q'
|
||||
{ 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88 }, // 28 'R'
|
||||
{ 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70 }, // 29 'S'
|
||||
{ 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, // 30 'T'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70 }, // 31 'U'
|
||||
{ 0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20 }, // 32 'V'
|
||||
{ 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88 }, // 33 'W'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88 }, // 34 'X'
|
||||
{ 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20 }, // 35 'Y'
|
||||
{ 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8 }, // 36 'Z'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60 }, // 37 '.'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x40 }, // 38 ','
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00 }, // 39 ':'
|
||||
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20 }, // 40 '!'
|
||||
{ 0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20 }, // 41 '?'
|
||||
{ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00 }, // 42 '-'
|
||||
{ 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 43 '\''
|
||||
{ 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 44 '"'
|
||||
{ 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00 }, // 45 '/'
|
||||
{ 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x40 }, // 46 ';'
|
||||
{ 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00 }, // 47 '='
|
||||
{ 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00 }, // 48 '+'
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8 }, // 49 '_'
|
||||
{ 0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF8 }, // 50 unknown box
|
||||
};
|
||||
|
||||
|
||||
static void buildPalette(jlSurfaceT *screen) {
|
||||
uint16_t colors[JL_SURFACE_COLORS_PER_PALETTE];
|
||||
|
|
@ -326,33 +243,6 @@ static void drawMouseVerifyRow(jlSurfaceT *screen) {
|
|||
}
|
||||
|
||||
|
||||
static void drawTextLine(jlSurfaceT *screen, int16_t x, int16_t y, const char *text, uint8_t color) {
|
||||
int16_t cx;
|
||||
int16_t i;
|
||||
int16_t row;
|
||||
int16_t col;
|
||||
int idx;
|
||||
uint8_t bits;
|
||||
|
||||
cx = x;
|
||||
for (i = 0; text[i] != '\0'; i++) {
|
||||
idx = glyphIdx(text[i]);
|
||||
for (row = 0; row < FONT_H; row++) {
|
||||
bits = kFontGlyph[idx][row];
|
||||
for (col = 0; col < FONT_W; col++) {
|
||||
if (bits & (0x80 >> col)) {
|
||||
jlDrawPixel(screen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
cx = (int16_t)(cx + FONT_W + 1);
|
||||
if (cx > JL_SURFACE_WIDTH - FONT_W) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Re-stamp the verification scanline: sentinel, received count, then
|
||||
// every received character as two nibble pixels. Only the count and
|
||||
// the newest characters actually change, but the row is cheap enough
|
||||
|
|
@ -371,62 +261,6 @@ static void drawVerifyRow(jlSurfaceT *screen) {
|
|||
}
|
||||
|
||||
|
||||
static int glyphIdx(char c) {
|
||||
if (c == ' ') {
|
||||
return GLYPH_SPACE;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
return GLYPH_DIGIT_BASE + (c - '0');
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return GLYPH_ALPHA_BASE + (c - 'A');
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return GLYPH_ALPHA_BASE + (c - 'a'); // uppercase fallback
|
||||
}
|
||||
if (c == '.') {
|
||||
return GLYPH_PERIOD;
|
||||
}
|
||||
if (c == ',') {
|
||||
return GLYPH_COMMA;
|
||||
}
|
||||
if (c == ':') {
|
||||
return GLYPH_COLON;
|
||||
}
|
||||
if (c == '!') {
|
||||
return GLYPH_BANG;
|
||||
}
|
||||
if (c == '?') {
|
||||
return GLYPH_QUESTION;
|
||||
}
|
||||
if (c == '-') {
|
||||
return GLYPH_MINUS;
|
||||
}
|
||||
if (c == '\'') {
|
||||
return GLYPH_APOSTROPHE;
|
||||
}
|
||||
if (c == '"') {
|
||||
return GLYPH_QUOTE;
|
||||
}
|
||||
if (c == '/') {
|
||||
return GLYPH_SLASH;
|
||||
}
|
||||
if (c == ';') {
|
||||
return GLYPH_SEMICOLON;
|
||||
}
|
||||
if (c == '=') {
|
||||
return GLYPH_EQUALS;
|
||||
}
|
||||
if (c == '+') {
|
||||
return GLYPH_PLUS;
|
||||
}
|
||||
if (c == '_') {
|
||||
return GLYPH_UNDERSCORE;
|
||||
}
|
||||
return GLYPH_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static void initialPaint(jlSurfaceT *screen) {
|
||||
int16_t col;
|
||||
int16_t row;
|
||||
|
|
@ -473,7 +307,7 @@ static void logTypedHistory(void) {
|
|||
// drawMouseVerifyRow stamps them. Sampled once per use so the drawn
|
||||
// row and updateMouseVerifyRow's change check can never disagree.
|
||||
static uint16_t mouseFlags(void) {
|
||||
return (uint16_t)((jlMousePresent() ? MOUSE_FLAG_PRESENT : 0) | (jlMouseDown(JL_MOUSE_BUTTON_LEFT) ? MOUSE_FLAG_LEFT : 0) | (jlMouseDown(JL_MOUSE_BUTTON_RIGHT) ? MOUSE_FLAG_RIGHT : 0) | (jlMouseDown(JL_MOUSE_BUTTON_MIDDLE) ? MOUSE_FLAG_MIDDLE : 0));
|
||||
return (uint16_t)((jlMousePresent() ? MOUSE_FLAG_PRESENT : 0) | (jlMouseDown(JL_MOUSE_BUTTON_LEFT) ? MOUSE_FLAG_LEFT : 0) | (jlMouseDown(JL_MOUSE_BUTTON_RIGHT) ? MOUSE_FLAG_RIGHT : 0));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -545,8 +379,8 @@ static void processTypedChars(jlSurfaceT *screen) {
|
|||
static void redrawTextStrip(jlSurfaceT *screen) {
|
||||
gLine[gLineLen] = '\0';
|
||||
jlFillRect(screen, 0, TEXT_STRIP_Y, JL_SURFACE_WIDTH, JL_SURFACE_HEIGHT - TEXT_STRIP_Y, COLOR_BACKGROUND);
|
||||
drawTextLine(screen, TEXT_X, TEXT_Y, "TYPE HERE:", COLOR_UNLIT);
|
||||
drawTextLine(screen, TEXT_INPUT_X, TEXT_Y, gLine, COLOR_CURSOR);
|
||||
exampleFontDraw(screen, TEXT_X, TEXT_Y, "TYPE HERE:", COLOR_UNLIT);
|
||||
exampleFontDraw(screen, TEXT_INPUT_X, TEXT_Y, gLine, COLOR_CURSOR);
|
||||
drawVerifyRow(screen);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
//
|
||||
// It times, over PROBE_FRAMES frames each, a palette change followed by a
|
||||
// present, and an SCB change followed by a present, on a screen of four SCB
|
||||
// bands, and logs the rates. Then it paints the bands (colour 1 of palettes
|
||||
// bands, and logs the rates. Then it paints the bands (color 1 of palettes
|
||||
// 0..3 red, green, blue, white), makes one palette change (palette 2's
|
||||
// colour 1 to yellow) and one SCB change that keeps every band where it was
|
||||
// color 1 to yellow) and one SCB change that keeps every band where it was
|
||||
// (band 3 to palette 0), logs "PALPROBE: ready" and holds the screen for
|
||||
// HOLD_FRAMES frames, so a gate can look at it: top to bottom red, green,
|
||||
// yellow, red. scripts/verify-amiga-palette.sh is that gate.
|
||||
|
|
|
|||
|
|
@ -3,19 +3,19 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// SCB stress screen: the per-scanline palette under the heaviest load a
|
||||
// program can reasonably put on it. Written for the ports that honour the SCB
|
||||
// program can reasonably put on it. Written for the ports that honor the SCB
|
||||
// by reloading ONE hardware palette as the beam scans (the X68000's text-plane
|
||||
// present, the Atari ST's Timer B), where every palette change is a deadline
|
||||
// an interrupt has to meet. Portable C only: nothing here knows which port it
|
||||
// runs on.
|
||||
//
|
||||
// All sixteen palettes are in use, and colour i of each palette differs from
|
||||
// colour i of every other palette, so a line shown in the wrong palette shows
|
||||
// the wrong colour everywhere but in colour 0 (black in every palette by
|
||||
// All sixteen palettes are in use, and color i of each palette differs from
|
||||
// color i of every other palette, so a line shown in the wrong palette shows
|
||||
// the wrong color everywhere but in color 0 (black in every palette by
|
||||
// library contract). Twenty 16-pixel vertical stripes run the full height,
|
||||
// stripe s in colour s mod 16, so every line uses every colour.
|
||||
// stripe s in color s mod 16, so every line uses every color.
|
||||
//
|
||||
// Colour i of palette p is $0RGB with red = p, green = i and
|
||||
// Color i of palette p is $0RGB with red = p, green = i and
|
||||
// blue = (p * 7 + i) mod 16.
|
||||
//
|
||||
// The SCB, in three zones:
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
//
|
||||
// Every pass of the loop redraws and presents the whole stage, so the palette
|
||||
// reloads land while the present is streaming into video memory, and sets all
|
||||
// sixteen palettes again -- to the same colours, so the picture never changes
|
||||
// sixteen palettes again -- to the same colors, so the picture never changes
|
||||
// -- so a port that keeps per-band state rebuilds it every pass while the
|
||||
// frame it describes is on screen. Typed characters are read and dropped, so a
|
||||
// gate can keep the keyboard interrupt busy during the check; ESC exits.
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@
|
|||
#define COLOR_GRASS 3
|
||||
#define COLOR_DIRT_DARK 4
|
||||
#define COLOR_BRICK 5
|
||||
#define COLOR_GREY 6
|
||||
#define COLOR_GRAY 6
|
||||
#define COLOR_WATER 7
|
||||
#define COLOR_FOAM 8
|
||||
#define COLOR_CLOUD 9
|
||||
#define COLOR_GREY_DARK 10
|
||||
#define COLOR_GRAY_DARK 10
|
||||
#define COLOR_PETAL 11
|
||||
#define COLOR_BALL 12
|
||||
#define COLOR_SHINE 13
|
||||
|
|
@ -81,7 +81,7 @@ static const uint16_t gPalette[16] = {
|
|||
0x8CF, 0xFFF, 0x555, 0xFC2, 0xFD0, 0xFFF, 0x113, 0x4E4
|
||||
};
|
||||
|
||||
// The ball, 16x16 at 4 bits a pixel, high nibble left; colour 0 is clear.
|
||||
// The ball, 16x16 at 4 bits a pixel, high nibble left; color 0 is clear.
|
||||
static const uint8_t gBallRows[BALL_SIDE * BALL_SIDE / 2] = {
|
||||
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00,
|
||||
0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00,
|
||||
|
|
@ -205,20 +205,20 @@ static void makeTiles(jlSurfaceT *screen) {
|
|||
jlDrawPixel(screen, 17, 2, COLOR_DIRT_DARK);
|
||||
jlDrawPixel(screen, 21, 5, COLOR_DIRT_DARK);
|
||||
jlFillRect(screen, 24, 0, 8, 8, COLOR_BRICK);
|
||||
jlDrawLine(screen, 24, 3, 31, 3, COLOR_GREY);
|
||||
jlDrawLine(screen, 24, 7, 31, 7, COLOR_GREY);
|
||||
jlDrawLine(screen, 24, 0, 24, 2, COLOR_GREY);
|
||||
jlDrawLine(screen, 28, 4, 28, 6, COLOR_GREY);
|
||||
jlDrawLine(screen, 24, 3, 31, 3, COLOR_GRAY);
|
||||
jlDrawLine(screen, 24, 7, 31, 7, COLOR_GRAY);
|
||||
jlDrawLine(screen, 24, 0, 24, 2, COLOR_GRAY);
|
||||
jlDrawLine(screen, 28, 4, 28, 6, COLOR_GRAY);
|
||||
jlFillRect(screen, 32, 0, 8, 8, COLOR_WATER);
|
||||
jlDrawLine(screen, 32, 1, 34, 1, COLOR_FOAM);
|
||||
jlDrawLine(screen, 36, 5, 38, 5, COLOR_FOAM);
|
||||
jlFillRect(screen, 40, 0, 8, 8, COLOR_SKY);
|
||||
jlFillRect(screen, 41, 3, 6, 3, COLOR_CLOUD);
|
||||
jlFillRect(screen, 43, 2, 3, 1, COLOR_CLOUD);
|
||||
jlFillRect(screen, 48, 0, 8, 8, COLOR_GREY);
|
||||
jlDrawPixel(screen, 50, 2, COLOR_GREY_DARK);
|
||||
jlDrawPixel(screen, 53, 5, COLOR_GREY_DARK);
|
||||
jlDrawPixel(screen, 49, 6, COLOR_GREY_DARK);
|
||||
jlFillRect(screen, 48, 0, 8, 8, COLOR_GRAY);
|
||||
jlDrawPixel(screen, 50, 2, COLOR_GRAY_DARK);
|
||||
jlDrawPixel(screen, 53, 5, COLOR_GRAY_DARK);
|
||||
jlDrawPixel(screen, 49, 6, COLOR_GRAY_DARK);
|
||||
jlFillRect(screen, 56, 0, 8, 8, COLOR_SKY);
|
||||
jlDrawLine(screen, 59, 3, 59, 7, COLOR_GRASS);
|
||||
jlFillRect(screen, 58, 1, 3, 2, COLOR_PETAL);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@
|
|||
//
|
||||
// Mouse: a small pointer follows it.
|
||||
//
|
||||
// A panel at the top says the same in words, for whoever runs it: the line,
|
||||
// the bytes each way, and the mouse, redrawn every STATUS_FRAMES frames.
|
||||
//
|
||||
// Built with -DDENSE_SCB_LINES=n, stage lines 0 .. n-1 alternate between two
|
||||
// palettes, a palette change on every one of them. A port that honours the SCB
|
||||
// palettes, a palette change on every one of them. A port that honors the SCB
|
||||
// by reloading its one hardware palette as the beam scans (the X68000's raster
|
||||
// interrupt) then holds that interrupt for the whole run of lines every frame,
|
||||
// and the serial checks show whether receive survives it. The default, 0, is
|
||||
|
|
@ -38,7 +41,7 @@
|
|||
// high nibble first, behind a sentinel, for a harness that reads the
|
||||
// framebuffer back:
|
||||
// row 198 serial: 3C3C, status, received, bad, sent, short writes, first bad
|
||||
// row 199 mouse: 5A5A, present/left/right/middle, x, y (examples/keys' row)
|
||||
// row 199 mouse: 5A5A, present/left/right/0, x, y (examples/keys' row)
|
||||
// The serial row is restamped at most every SERIAL_ROW_FRAMES frames, so a
|
||||
// reader that sees the same row on two consecutive frames knows it did not
|
||||
// catch a present half done. The mouse row changes only with the mouse.
|
||||
|
|
@ -46,9 +49,13 @@
|
|||
// Serial is an OPT-IN add-on: it is reached through <joey/serial.h>, which the
|
||||
// umbrella <joey/joey.h> deliberately does NOT pull in.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <joey/joey.h>
|
||||
#include <joey/serial.h>
|
||||
|
||||
#include "exampleFont.h"
|
||||
|
||||
#ifndef LINE_BAUD
|
||||
#define LINE_BAUD 9600u
|
||||
#endif
|
||||
|
|
@ -91,11 +98,11 @@
|
|||
#define MOUSE_XPOS_X (MOUSE_FLAGS_X + WORD_NIBBLES)
|
||||
#define MOUSE_YPOS_X (MOUSE_XPOS_X + WORD_NIBBLES)
|
||||
|
||||
// The mouse flags word, one nibble per predicate, in examples/keys' order.
|
||||
// The mouse flags word, one nibble per predicate (the last always 0), in
|
||||
// examples/keys' order.
|
||||
#define MOUSE_FLAG_PRESENT 0x1000u
|
||||
#define MOUSE_FLAG_LEFT 0x0100u
|
||||
#define MOUSE_FLAG_RIGHT 0x0010u
|
||||
#define MOUSE_FLAG_MIDDLE 0x0001u
|
||||
#define MOUSE_FLAGS_NONE 0xFFFFu // never a real flags word: forces the first stamp
|
||||
|
||||
#define POINTER_SIZE 4
|
||||
|
|
@ -103,9 +110,24 @@
|
|||
#define COLOR_POINTER 15
|
||||
#define PALETTE_RED_SHIFT 8
|
||||
#define PALETTE_GREEN_SHIFT 4
|
||||
#define GREY_PALETTE 0u
|
||||
#define GRAY_PALETTE 0u
|
||||
#define BLUE_PALETTE 1u // the dense SCB's other palette
|
||||
#define BLUE_FLOOR 0x00Fu // added to every colour but black
|
||||
#define BLUE_FLOOR 0x00Fu // added to every color but black
|
||||
|
||||
// The status panel: its place, its lines, and how often it is redrawn.
|
||||
#define PANEL_X 8
|
||||
#define PANEL_Y 8
|
||||
#define PANEL_W (JL_SURFACE_WIDTH - 2 * PANEL_X)
|
||||
#define PANEL_LINE_H 10
|
||||
#define PANEL_LINES 6
|
||||
#define PANEL_H (PANEL_LINES * PANEL_LINE_H)
|
||||
#define PANEL_LINE_SERIAL 2
|
||||
#define PANEL_LINE_COUNTS 3
|
||||
#define PANEL_LINE_MOUSE 4
|
||||
#define PANEL_LINE_KEYS 5
|
||||
#define PANEL_TEXT_BYTES 64
|
||||
#define STATUS_FRAMES 30u
|
||||
#define COLOR_TEXT 15
|
||||
|
||||
typedef struct StreamStatsT {
|
||||
uint32_t received; // peer bytes read so far = index of the next one expected
|
||||
|
|
@ -129,6 +151,7 @@ typedef struct PointerT {
|
|||
|
||||
static void buildPalette(jlSurfaceT *screen);
|
||||
static void buildScb(jlSurfaceT *screen);
|
||||
static void drawStatus(jlSurfaceT *screen, const StreamStatsT *stats, uint16_t status, PointerT *pointer);
|
||||
static void fillPointer(jlSurfaceT *screen, int16_t x, int16_t y, uint8_t color);
|
||||
static uint16_t mouseFlags(void);
|
||||
static void movePointer(jlSurfaceT *screen, PointerT *pointer);
|
||||
|
|
@ -140,7 +163,7 @@ static uint8_t streamByte(uint32_t index, uint8_t mask);
|
|||
static void updateMouseRow(jlSurfaceT *screen, MouseRowT *last);
|
||||
|
||||
|
||||
// A grey ramp, so every index the verification rows stamp is visible.
|
||||
// A gray ramp, so every index the verification rows stamp is visible.
|
||||
static void buildPalette(jlSurfaceT *screen) {
|
||||
uint16_t colors[JL_SURFACE_COLORS_PER_PALETTE];
|
||||
uint16_t i;
|
||||
|
|
@ -148,21 +171,21 @@ static void buildPalette(jlSurfaceT *screen) {
|
|||
for (i = 0u; i < JL_SURFACE_COLORS_PER_PALETTE; i++) {
|
||||
colors[i] = (uint16_t)((i << PALETTE_RED_SHIFT) | (i << PALETTE_GREEN_SHIFT) | i);
|
||||
}
|
||||
jlPaletteSet(screen, GREY_PALETTE, colors);
|
||||
jlPaletteSet(screen, GRAY_PALETTE, colors);
|
||||
}
|
||||
|
||||
|
||||
// The grey palette everywhere, or DENSE_SCB_LINES lines alternating between
|
||||
// The gray palette everywhere, or DENSE_SCB_LINES lines alternating between
|
||||
// it and the same ramp in blue.
|
||||
static void buildScb(jlSurfaceT *screen) {
|
||||
jlScbSetRange(screen, 0, JL_SURFACE_HEIGHT - 1, GREY_PALETTE);
|
||||
jlScbSetRange(screen, 0, JL_SURFACE_HEIGHT - 1, GRAY_PALETTE);
|
||||
#if DENSE_SCB_LINES > 0
|
||||
{
|
||||
uint16_t colors[JL_SURFACE_COLORS_PER_PALETTE];
|
||||
uint16_t i;
|
||||
uint16_t line;
|
||||
|
||||
jlPaletteGet(screen, GREY_PALETTE, colors);
|
||||
jlPaletteGet(screen, GRAY_PALETTE, colors);
|
||||
for (i = 1u; i < JL_SURFACE_COLORS_PER_PALETTE; i++) {
|
||||
colors[i] = (uint16_t)(colors[i] | BLUE_FLOOR);
|
||||
}
|
||||
|
|
@ -175,6 +198,30 @@ static void buildScb(jlSurfaceT *screen) {
|
|||
}
|
||||
|
||||
|
||||
// The panel: the line, the bytes each way, the mouse, and the keys, over the
|
||||
// panel cleared. A pointer it covered is drawn again on the next move.
|
||||
static void drawStatus(jlSurfaceT *screen, const StreamStatsT *stats, uint16_t status, PointerT *pointer) {
|
||||
char text[PANEL_TEXT_BYTES];
|
||||
|
||||
jlFillRect(screen, PANEL_X, PANEL_Y, PANEL_W, PANEL_H, COLOR_BACKGROUND);
|
||||
exampleFontDraw(screen, PANEL_X, PANEL_Y, "Serial and mouse together", COLOR_TEXT);
|
||||
if ((status & STATUS_OPEN) == 0u) {
|
||||
(void)snprintf(text, sizeof(text), "Serial: no port to open");
|
||||
} else {
|
||||
(void)snprintf(text, sizeof(text), "Serial: %u baud, 8N1%s", (unsigned)LINE_BAUD, (status & STATUS_PAUSED) != 0u ? ", paused" : "");
|
||||
}
|
||||
exampleFontDraw(screen, PANEL_X, PANEL_Y + PANEL_LINE_SERIAL * PANEL_LINE_H, text, COLOR_TEXT);
|
||||
(void)snprintf(text, sizeof(text), "Sent %lu, received %lu, bad %u", (unsigned long)stats->sent, (unsigned long)stats->received, (unsigned)stats->bad);
|
||||
exampleFontDraw(screen, PANEL_X, PANEL_Y + PANEL_LINE_COUNTS * PANEL_LINE_H, text, COLOR_TEXT);
|
||||
(void)snprintf(text, sizeof(text), "Mouse %d,%d", (int)jlMouseX(), (int)jlMouseY());
|
||||
exampleFontDraw(screen, PANEL_X, PANEL_Y + PANEL_LINE_MOUSE * PANEL_LINE_H, text, COLOR_TEXT);
|
||||
exampleFontDraw(screen, PANEL_X, PANEL_Y + PANEL_LINE_KEYS * PANEL_LINE_H, "Space pauses sending; Escape quits.", COLOR_TEXT);
|
||||
if (pointer->x < PANEL_X + PANEL_W && pointer->x + POINTER_SIZE > PANEL_X && pointer->y < PANEL_Y + PANEL_H && pointer->y + POINTER_SIZE > PANEL_Y) {
|
||||
pointer->drawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The pointer block at (x, y), clipped above the verification rows so it can
|
||||
// never draw over them: a harness samples those between frames.
|
||||
static void fillPointer(jlSurfaceT *screen, int16_t x, int16_t y, uint8_t color) {
|
||||
|
|
@ -197,7 +244,7 @@ static void fillPointer(jlSurfaceT *screen, int16_t x, int16_t y, uint8_t color)
|
|||
|
||||
|
||||
static uint16_t mouseFlags(void) {
|
||||
return (uint16_t)((jlMousePresent() ? MOUSE_FLAG_PRESENT : 0u) | (jlMouseDown(JL_MOUSE_BUTTON_LEFT) ? MOUSE_FLAG_LEFT : 0u) | (jlMouseDown(JL_MOUSE_BUTTON_RIGHT) ? MOUSE_FLAG_RIGHT : 0u) | (jlMouseDown(JL_MOUSE_BUTTON_MIDDLE) ? MOUSE_FLAG_MIDDLE : 0u));
|
||||
return (uint16_t)((jlMousePresent() ? MOUSE_FLAG_PRESENT : 0u) | (jlMouseDown(JL_MOUSE_BUTTON_LEFT) ? MOUSE_FLAG_LEFT : 0u) | (jlMouseDown(JL_MOUSE_BUTTON_RIGHT) ? MOUSE_FLAG_RIGHT : 0u));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -327,12 +374,14 @@ int main(void) {
|
|||
PointerT pointer;
|
||||
uint16_t status;
|
||||
uint16_t rowFrame;
|
||||
uint16_t statusFrame;
|
||||
uint16_t i;
|
||||
bool paused;
|
||||
|
||||
config.codegenBytes = 0u;
|
||||
config.noAudio = true;
|
||||
if (!jlInit(&config)) {
|
||||
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
|
||||
return 1;
|
||||
}
|
||||
screen = jlStageGet();
|
||||
|
|
@ -362,10 +411,12 @@ int main(void) {
|
|||
paused = false;
|
||||
|
||||
jlInputPoll();
|
||||
drawStatus(screen, &stats, status, &pointer);
|
||||
stampSerialRow(screen, &stats, status);
|
||||
updateMouseRow(screen, &mouseRow);
|
||||
jlStagePresent();
|
||||
rowFrame = jlFrameCount();
|
||||
rowFrame = jlFrameCount();
|
||||
statusFrame = rowFrame;
|
||||
|
||||
for (;;) {
|
||||
jlInputPoll();
|
||||
|
|
@ -382,6 +433,10 @@ int main(void) {
|
|||
for (i = 0u; i < DTR_WRITES_PER_LOOP; i++) {
|
||||
jlSerialSetDtr(true);
|
||||
}
|
||||
if ((uint16_t)(jlFrameCount() - statusFrame) >= STATUS_FRAMES) {
|
||||
statusFrame = jlFrameCount();
|
||||
drawStatus(screen, &stats, (uint16_t)(status | (paused ? STATUS_PAUSED : 0u)), &pointer);
|
||||
}
|
||||
movePointer(screen, &pointer);
|
||||
updateMouseRow(screen, &mouseRow);
|
||||
if ((uint16_t)(jlFrameCount() - rowFrame) >= SERIAL_ROW_FRAMES) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ static const uint8_t gBallAuthored[BALL_H * (BALL_W / 2)] = {
|
|||
};
|
||||
|
||||
static uint8_t gBallTiles[BALL_TILE_BYTES];
|
||||
static uint16_t gBallBackup[jlSpriteBackupLength(uint16_t, BALL_W / 8, BALL_H / 8)];
|
||||
static uint16_t gBallBackup[jlSpriteBackupLength(BALL_W / 8, BALL_H / 8)];
|
||||
|
||||
|
||||
// Repack from authored "wide row" layout to tile-major jlSpriteT
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
// refuse<n>.tbk n = 1, 2, ...: every other target's bake
|
||||
// expect.bin the PNG decoded by PIL, independent of assetbake's
|
||||
// encoders: tileCount LE16, palette[16] LE16 $0RGB, then
|
||||
// tileCount * 64 colour indices (tiles in bank order, each
|
||||
// tileCount * 64 color indices (tiles in bank order, each
|
||||
// row-major)
|
||||
//
|
||||
// The checks, all on the stage:
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
// Result: one jlLog line, flushed before anything is shown,
|
||||
// TILEBANK: PASS tiles=55 streamed=55 pasted=55 palette=ok streamHash=<hash> pasteHash=<hash> refused=4/4
|
||||
// which scripts/verify-<port>-tilebank.sh reads (and holds the hashes against
|
||||
// the host's), then the tiles in the bank's own colours over a lamp centred on
|
||||
// the host's), then the tiles in the bank's own colors over a lamp centered on
|
||||
// the stage: LAMP_PASS_COLOR or LAMP_FAIL_COLOR in palette LAMP_PALETTE, with
|
||||
// the rest of its rows left at FILL_COLOR. The MAME gates find the verdict
|
||||
// there before they read the log. Any key exits.
|
||||
|
|
@ -70,8 +70,8 @@
|
|||
// No sprites here, so the port's default codegen cache is plenty.
|
||||
#define CODEGEN_DEFAULT 0u
|
||||
|
||||
// The verdict lamp, centred on the stage. Its rows get their own palette so
|
||||
// the lamp colours cannot collide with the bank's sixteen.
|
||||
// The verdict lamp, centered on the stage. Its rows get their own palette so
|
||||
// the lamp colors cannot collide with the bank's sixteen.
|
||||
#define LAMP_W 64
|
||||
#define LAMP_H 64
|
||||
#define LAMP_X ((JL_SURFACE_WIDTH - LAMP_W) / 2)
|
||||
|
|
@ -101,7 +101,7 @@ static uint16_t gBankPalette[PALETTE_COLORS];
|
|||
static uint16_t gExpectCount = 0u;
|
||||
static uint16_t gExpectPalette[PALETTE_COLORS];
|
||||
static uint8_t *gExpectPixels = NULL;
|
||||
static jlSurfaceT *jlgScreen = NULL;
|
||||
static jlSurfaceT *jlgScreen = NULL;
|
||||
static jlTileT *gTiles = NULL;
|
||||
static bool *gValid = NULL;
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ static bool runChecks(void) {
|
|||
}
|
||||
|
||||
|
||||
// Show the bank's tiles in its own colours, and the lamp.
|
||||
// Show the bank's tiles in its own colors, and the lamp.
|
||||
static void showVerdict(bool pass) {
|
||||
uint16_t colors[PALETTE_COLORS];
|
||||
uint16_t i;
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ typedef void (*OpFn)(void);
|
|||
static jlSurfaceT *jlgStage = NULL;
|
||||
static jlSpriteT *gSprite = NULL;
|
||||
static jlSpriteBackupT gBackup;
|
||||
static unsigned char gBackupBytes[jlSpriteBackupLength(unsigned char, 2, 2)] __attribute__((aligned(2)));
|
||||
static uint16_t gBackupWords[jlSpriteBackupLength(2, 2)];
|
||||
|
||||
static jlTileT gTileScratch;
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ static jlTileT gTileMapB;
|
|||
#define UBER_FRAME_SPRITES 3
|
||||
|
||||
static jlSpriteBackupT gFrameBackups[UBER_FRAME_SPRITES];
|
||||
static unsigned char gFrameBackupBytes[UBER_FRAME_SPRITES][jlSpriteBackupLength(unsigned char, 2, 2)] __attribute__((aligned(2)));
|
||||
static uint16_t gFrameBackupWords[UBER_FRAME_SPRITES][jlSpriteBackupLength(2, 2)];
|
||||
|
||||
|
||||
// Current timed-op number (1-based); mirrored into the mailbox. Lives
|
||||
|
|
@ -719,7 +719,7 @@ static void PROBE_ATTR op_probePresentIdle(void) {
|
|||
// rows in this same log. Stage pixels are never written (saves read;
|
||||
// the restoreVal call fails validation), so these rows are hash-inert.
|
||||
static jlSpriteBackupT gProbeSpriteBackup;
|
||||
static unsigned char gProbeSpriteBackupBytes[jlSpriteBackupLength(unsigned char, 2, 2)] __attribute__((aligned(2)));
|
||||
static uint16_t gProbeSpriteBackupWords[jlSpriteBackupLength(2, 2)];
|
||||
|
||||
static void PROBE_ATTR op_probeSpriteSaveVal(void) {
|
||||
jlSpriteSaveUnder(jlgStage, gSprite, -100, -100, &gProbeSpriteBackup);
|
||||
|
|
@ -750,7 +750,7 @@ static void PROBE_ATTR op_probeSpriteSaveG16(void) {
|
|||
// and gBackup untouched, so the row is inert to the golden hashes.
|
||||
static jlSurfaceT *gProbeRestoreWork = NULL;
|
||||
static jlSpriteBackupT gProbeRestoreBackup;
|
||||
static unsigned char gProbeRestoreBackupBytes[jlSpriteBackupLength(unsigned char, 2, 2)] __attribute__((aligned(2)));
|
||||
static uint16_t gProbeRestoreBackupWords[jlSpriteBackupLength(2, 2)];
|
||||
|
||||
static void PROBE_ATTR op_probeRestoreWork(void) {
|
||||
jlSpriteRestoreUnder(gProbeRestoreWork, &gProbeRestoreBackup);
|
||||
|
|
@ -1259,7 +1259,7 @@ extern uint32_t jlHostBeProbeDisplayMismatch(const jlSurfaceT *src, uint32_t *co
|
|||
// counts the ones that differ. The hosted backends draw only the
|
||||
// rectangles that changed at each show; their probes count the frame's
|
||||
// pixels that differ from the stage and the window's that differ from the
|
||||
// whole frame drawn afresh. A strip of one-pixel stripes in all 16 colours stays on screen,
|
||||
// whole frame drawn afresh. A strip of one-pixel stripes in all 16 colors stays on screen,
|
||||
// so every table entry, and both pixels of a byte, are checked; the small
|
||||
// rect is on odd pixels. After P12's steps (tall band, small inner rect,
|
||||
// settle, idle) two more work the tables: the strip's rows moved to another
|
||||
|
|
@ -1304,7 +1304,7 @@ static void PROBE_ATTR op_probeWindowDisplayCheck(void) {
|
|||
jlPaletteGet(jlgStage, (uint8_t)PROBE_DVX_PALETTE, savedPalette);
|
||||
jlPaletteSet(jlgStage, (uint8_t)PROBE_DVX_PALETTE, probePalette);
|
||||
// Shown before the steps, so the palette's own full redraw (Win16
|
||||
// repaints its window when colours change) is behind them; and a frame
|
||||
// repaints its window when colors change) is behind them; and a frame
|
||||
// between B's presents, so each is a show of its own (Win16 shows at
|
||||
// most once a frame, everything changed since the last).
|
||||
jlStagePresent();
|
||||
|
|
@ -1968,7 +1968,7 @@ static void __attribute__((noinline)) checkTileMonoFloodExtra(void) {
|
|||
// across the map's seams (64x40 tiles = 512x320 pixels) both ways, a
|
||||
// jump of more than a view, an origin past the map's size, and cell
|
||||
// changes in and out of view. The layer is a band (rows 24-159) over a
|
||||
// stage of colour 6, so the rows outside it are in every hash. One hash
|
||||
// stage of color 6, so the rows outside it are in every hash. One hash
|
||||
// folds the stage's after each draw.
|
||||
#define UBER_LAYER_CHECK_MOVES 20
|
||||
|
||||
|
|
@ -2005,10 +2005,10 @@ static void __attribute__((noinline)) checkTileLayer(void) {
|
|||
}
|
||||
|
||||
|
||||
// jlTilePasteGlyph, which nothing else here calls: every colour on both
|
||||
// jlTilePasteGlyph, which nothing else here calls: every color on both
|
||||
// sides of a glyph whose rows hold every pixel pair, on the stage (each
|
||||
// port's own paste, mark included) and on another surface, with colours
|
||||
// above 15 (their low nibble is the colour) and the calls that must draw
|
||||
// port's own paste, mark included) and on another surface, with colors
|
||||
// above 15 (their low nibble is the color) and the calls that must draw
|
||||
// nothing -- a cell off the surface, no bits, no surface. One hash folds
|
||||
// both surfaces'.
|
||||
static void __attribute__((noinline)) checkTilePasteGlyph(void) {
|
||||
|
|
@ -2048,7 +2048,7 @@ static void __attribute__((noinline)) checkTilePasteGlyph(void) {
|
|||
|
||||
// jlFillRect one row high, the shape a planar picture is pushed in, which
|
||||
// UBER's timed fills never are: widths 1 to 41 at every pixel phase, all
|
||||
// sixteen colours, both edges of the surface, and rows that clip or are
|
||||
// sixteen colors, both edges of the surface, and rows that clip or are
|
||||
// empty -- on the stage and on another surface. One hash folds both.
|
||||
static void __attribute__((noinline)) checkFillRectRows(void) {
|
||||
jlSurfaceT *other;
|
||||
|
|
@ -2354,9 +2354,9 @@ static void __attribute__((noinline)) runAllTests(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
gFrameBackups[0].bytes = gFrameBackupBytes[0];
|
||||
gFrameBackups[1].bytes = gFrameBackupBytes[1];
|
||||
gFrameBackups[2].bytes = gFrameBackupBytes[2];
|
||||
gFrameBackups[0].bytes = (uint8_t *)gFrameBackupWords[0];
|
||||
gFrameBackups[1].bytes = (uint8_t *)gFrameBackupWords[1];
|
||||
gFrameBackups[2].bytes = (uint8_t *)gFrameBackupWords[2];
|
||||
|
||||
timeOp("jlTilePaste map 10x5", op_tileMapPaste);
|
||||
timeOp("gameFrame composite", op_gameFrame);
|
||||
|
|
@ -2453,7 +2453,7 @@ static void __attribute__((noinline)) runAllTests(void) {
|
|||
// before op_probeShrParity. timeOp (not probeOp) to match the
|
||||
// rest of this arm (see probeOp).
|
||||
gProbeRestoreBackup.sprite = gSprite;
|
||||
gProbeRestoreBackup.bytes = gProbeRestoreBackupBytes;
|
||||
gProbeRestoreBackup.bytes = (uint8_t *)gProbeRestoreBackupWords;
|
||||
jlSpriteSaveUnder(jlgStage, gSprite, 40, 30, &gProbeRestoreBackup);
|
||||
timeOp("probe restoreTrusted", op_probeRestoreTrusted);
|
||||
timeOp("probe saveTrusted", op_probeSaveTrusted);
|
||||
|
|
@ -2495,7 +2495,7 @@ static void __attribute__((noinline)) runAllTests(void) {
|
|||
// demote it to the metadata-only path); the odd-x fields AFTER the
|
||||
// saves (which overwrite the metadata), right before restoreVal.
|
||||
gProbeSpriteBackup.sprite = gSprite;
|
||||
gProbeSpriteBackup.bytes = gProbeSpriteBackupBytes;
|
||||
gProbeSpriteBackup.bytes = (uint8_t *)gProbeSpriteBackupWords;
|
||||
probeOp("probe spriteSaveVal", op_probeSpriteSaveVal);
|
||||
probeOp("probe spriteSave g16", op_probeSpriteSaveG16);
|
||||
gProbeSpriteBackup.x = 41;
|
||||
|
|
@ -2509,7 +2509,7 @@ static void __attribute__((noinline)) runAllTests(void) {
|
|||
// difference vs the golden jlSpriteRestoreUnder row = the mark.
|
||||
gProbeRestoreWork = jlSurfaceCreate();
|
||||
gProbeRestoreBackup.sprite = gSprite;
|
||||
gProbeRestoreBackup.bytes = gProbeRestoreBackupBytes;
|
||||
gProbeRestoreBackup.bytes = (uint8_t *)gProbeRestoreBackupWords;
|
||||
jlSpriteSaveUnder(jlgStage, gSprite, 40, 30, &gProbeRestoreBackup);
|
||||
if (gProbeRestoreWork != NULL) {
|
||||
probeOp("probe restoreWork", op_probeRestoreWork);
|
||||
|
|
@ -2577,7 +2577,7 @@ static void __attribute__((noinline)) setupPalette(void) {
|
|||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
pal[i] = (uint16_t)((i << 8) | (i << 4) | i); // grey ramp
|
||||
pal[i] = (uint16_t)((i << 8) | (i << 4) | i); // gray ramp
|
||||
}
|
||||
pal[ 0] = 0x000;
|
||||
pal[ 1] = 0x800; // dark red (running)
|
||||
|
|
@ -2625,7 +2625,7 @@ static bool __attribute__((noinline)) setupSprite(void) {
|
|||
// wait for next VBL edge
|
||||
}
|
||||
jlLogF("UBER: jlSpriteCompile: 1 call in <= 1 frame");
|
||||
gBackup.bytes = gBackupBytes;
|
||||
gBackup.bytes = (uint8_t *)gBackupWords;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
// GetDiskObject reads FOLDER/NAME.info; its type, stack and image are
|
||||
// logged, and the image is drawn at ICON_X,ICON_Y on a hires screen of
|
||||
// Workbench's four colours, with the mouse pointer hidden, held HOLD_TICKS
|
||||
// Workbench's four colors, with the mouse pointer hidden, held HOLD_TICKS
|
||||
// for a screenshot. Then
|
||||
// FOLDER/NAME is started as Workbench starts a tool -- LoadSeg, CreateProc
|
||||
// with the icon's stack, and a WBStartup message whose one argument is the
|
||||
|
|
@ -63,14 +63,14 @@ static bool drawIcon(struct DiskObject *icon);
|
|||
static bool launch(const char *folder, const char *name, ULONG stack);
|
||||
static void logLine(const char *format, ...);
|
||||
|
||||
// Workbench's four colours (2.0 on), 4 bits a gun: grey, black, white and
|
||||
// blue (tools/iconBake/iconBake.c's kAmigaColours).
|
||||
static const UWORD gWorkbenchColours[] = { 0xAAA, 0x000, 0xFFF, 0x68B };
|
||||
// Workbench's four colors (2.0 on), 4 bits a gun: gray, black, white and
|
||||
// blue (tools/iconBake/iconBake.c's kAmigaColors).
|
||||
static const UWORD gWorkbenchColors[] = { 0xAAA, 0x000, 0xFFF, 0x68B };
|
||||
|
||||
static FILE *gLog = NULL;
|
||||
|
||||
|
||||
// The icon's image on a screen of Workbench's colours, held for the gate's
|
||||
// The icon's image on a screen of Workbench's colors, held for the gate's
|
||||
// screenshot. A window over the whole screen, active, hides the pointer.
|
||||
static bool drawIcon(struct DiskObject *icon) {
|
||||
struct Screen *screen;
|
||||
|
|
@ -90,8 +90,8 @@ static bool drawIcon(struct DiskObject *icon) {
|
|||
logLine("FAIL cannot open a screen and a window");
|
||||
} else {
|
||||
SetPointer(window, blank, BLANK_POINTER_H, BLANK_POINTER_W, 0, 0);
|
||||
for (i = 0; i < sizeof(gWorkbenchColours) / sizeof(gWorkbenchColours[0]); i++) {
|
||||
SetRGB4(&screen->ViewPort, i, (gWorkbenchColours[i] >> RED_SHIFT) & GUN_MASK, (gWorkbenchColours[i] >> GREEN_SHIFT) & GUN_MASK, gWorkbenchColours[i] & GUN_MASK);
|
||||
for (i = 0; i < sizeof(gWorkbenchColors) / sizeof(gWorkbenchColors[0]); i++) {
|
||||
SetRGB4(&screen->ViewPort, i, (gWorkbenchColors[i] >> RED_SHIFT) & GUN_MASK, (gWorkbenchColors[i] >> GREEN_SHIFT) & GUN_MASK, gWorkbenchColors[i] & GUN_MASK);
|
||||
}
|
||||
SetRast(window->RPort, 0);
|
||||
DrawImage(window->RPort, (struct Image *)icon->do_Gadget.GadgetRender, ICON_X, ICON_Y);
|
||||
|
|
|
|||
|
|
@ -118,11 +118,14 @@ typedef uint32_t (*jlAudioStreamFillT)(void *ctx, int8_t *dst, uint32_t count);
|
|||
// rate. If the slot is currently playing, the new stream replaces
|
||||
// it. Implemented on every port: DOS/ST/X68000/hosted pull through the
|
||||
// shared SFX overlay mixer as jlAudioFrameTick refills their sound
|
||||
// buffers, while the IIgs (NTPstreamsound, 7 KB chunks) and Amiga
|
||||
// (PTPlayer mt_playfx, 4 KB chunks) play one chunk at a time and fire the
|
||||
// next from jlAudioFrameTick as the current one runs down -- so the host
|
||||
// must be calling jlAudioFrameTick for a stream to continue, and on those
|
||||
// two a seam can carry up to one frame of jitter.
|
||||
// buffers, the IIgs (NTPstreamsound, 7 KB chunks) plays one chunk at a
|
||||
// time and fires the next from jlAudioFrameTick as the current one runs
|
||||
// down, and the Amiga (PTPlayer mt_loopfx) loops an 8 KB ring that
|
||||
// jlAudioFrameTick tops up as Paula plays it -- so the host must be calling
|
||||
// jlAudioFrameTick for a stream to continue. On the IIgs a seam can carry
|
||||
// up to one frame of jitter; the Amiga's ring has none, and plays its old
|
||||
// contents again if the calls stop for longer than it lasts (a third of a
|
||||
// second at 22 kHz).
|
||||
void jlAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
|
||||
|
||||
// Raw-format streaming: jlAudioPlaySfxStream, except `fill` delivers
|
||||
|
|
@ -154,12 +157,12 @@ typedef enum {
|
|||
// What each port can actually do, because the hardware differs:
|
||||
// IIgs all three. The DOC pans per OSCILLATOR (control bits 7:4 are the
|
||||
// output channel, and per Apple TN #19 odd = left, even = right),
|
||||
// so a centred sound is played on two oscillators, one per side.
|
||||
// so a centered sound is played on two oscillators, one per side.
|
||||
// A stock IIgs sums to mono anyway; this only shows up on a stereo
|
||||
// card or an emulator.
|
||||
// Amiga left and right, by picking the Paula channel side (0 and 3 are
|
||||
// left, 1 and 2 right). A channel plays on one side only, so a
|
||||
// centred sound plays on a free channel each side when both sides
|
||||
// centered sound plays on a free channel each side when both sides
|
||||
// have one, and otherwise on the one channel PTPlayer picks.
|
||||
// X68000 ignored. Everything is mixed into ONE MSM6258 ADPCM stream, so
|
||||
// its pan bits would move all audio at once, not one slot.
|
||||
|
|
@ -308,7 +311,7 @@ void jlMusicResume(void);
|
|||
|
||||
// Global music attenuation, added to every authored voice/noise
|
||||
// attenuation (0 = as authored .. 15 = silent). Applies immediately
|
||||
// to sounding voices and to everything played afterwards; jlMusicPlay
|
||||
// to sounding voices and to everything played afterward; jlMusicPlay
|
||||
// does NOT reset it, so a fade-out can carry into the next song.
|
||||
// Step it over successive frames for fades.
|
||||
void jlMusicSetAtten(uint8_t atten);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@
|
|||
// jlMousePressed(b) -- rising edge since last poll
|
||||
// jlMouseReleased(b) -- falling edge since last poll
|
||||
//
|
||||
// Every port has two buttons, left and right, whatever its mouse has: a
|
||||
// left click made with Ctrl held is the right button (from the press until
|
||||
// the button is let go), so a one-button mouse has both.
|
||||
//
|
||||
// The joystick predicates return per-stick state:
|
||||
//
|
||||
// jlJoystickConnected(js) -- true if the platform reports a
|
||||
|
|
@ -39,7 +43,7 @@
|
|||
// jlJoystickX/Y(js) -- axis values, signed -127..+127.
|
||||
// Digital sticks snap to the
|
||||
// extremes; analog sticks return
|
||||
// their offset from centre, scaled,
|
||||
// their offset from center, scaled,
|
||||
// dead zone applied.
|
||||
// jlJoyDown(js, b) -- is button b held on stick js
|
||||
// jlJoyPressed(js, b) -- rising edge since last poll
|
||||
|
|
@ -80,7 +84,6 @@ typedef enum {
|
|||
JL_MOUSE_BUTTON_NONE = 0,
|
||||
JL_MOUSE_BUTTON_LEFT,
|
||||
JL_MOUSE_BUTTON_RIGHT,
|
||||
JL_MOUSE_BUTTON_MIDDLE,
|
||||
|
||||
JL_MOUSE_BUTTON_COUNT
|
||||
} jlMouseButtonE;
|
||||
|
|
@ -109,7 +112,7 @@ typedef enum {
|
|||
#define JL_CHAR_TAB 0x09
|
||||
#define JL_CHAR_RETURN 0x0D
|
||||
#define JL_CHAR_ESCAPE 0x1B
|
||||
// The FORWARD delete (the key labelled Del/Delete on a PC, ST or Amiga keyboard - not backspace). A text
|
||||
// The FORWARD delete (the key labeled Del/Delete on a PC, ST or Amiga keyboard - not backspace). A text
|
||||
// editor needs both: backspace removes the character BEFORE the caret, this one removes the character
|
||||
// UNDER it. 0x7F is ASCII DEL. The IIgs never delivers it: its Delete key is backspace.
|
||||
#define JL_CHAR_DELETE 0x7F
|
||||
|
|
@ -164,8 +167,7 @@ bool jlKeyReleased(jlKeyE key);
|
|||
|
||||
// True when the platform confirmed an attached mouse at init (DOS: the INT 33h driver reset;
|
||||
// Amiga: JoeyLib's input window opened; Win32/Win16: Windows reports one; SDL3/web: SDL reports one;
|
||||
// IIgs/ST/X68000/DVX/Haiku/BeOS: always; PSP: never). The IIgs mouse has one button, the ST's and
|
||||
// X68000's two.
|
||||
// IIgs/ST/X68000/DVX/Haiku/BeOS: always; PSP: always, its analog nub, with L and R its buttons).
|
||||
bool jlMousePresent(void);
|
||||
int16_t jlMouseX(void);
|
||||
int16_t jlMouseY(void);
|
||||
|
|
@ -189,8 +191,8 @@ bool jlJoyReleased(jlJoystickE js, jlJoyButtonE button);
|
|||
// Set the stick's dead zone (8 at jlInit): axis values within `deadZone` of 0 (on the
|
||||
// -127..127 scale) read 0 on the ports with analog sticks (IIgs, DOS,
|
||||
// DVX, hosted); use 0 to disable it. On the IIgs and DOS the call also
|
||||
// re-centres the stick: the next reading after it is taken as the
|
||||
// centre, so the user must hold the stick centred when calling. The
|
||||
// re-centers the stick: the next reading after it is taken as the
|
||||
// center, so the user must hold the stick centered when calling. The
|
||||
// IIgs port stops polling after about a second (60 polls) with no stick
|
||||
// seen (an absent stick costs ~1 ms a frame of busy-wait) and does NOT
|
||||
// re-probe by itself: this call resumes polling. DOS probes the game
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
// The stage is four separate 1bpp planes in main RAM (src/x68000/halPlanar.c,
|
||||
// the Amiga's layout), and a present copies its dirty rows into the text
|
||||
// plane at $E00000, which is four 1bpp planes too. The text plane has one
|
||||
// 16-colour palette, so per-line SCB palettes are reloaded as the beam scans
|
||||
// 16-color palette, so per-line SCB palettes are reloaded as the beam scans
|
||||
// by a CRTC raster interrupt (src/x68000/jlX68kRasterIsr.s).
|
||||
#define JL_CPU_68000
|
||||
#define JL_ENDIAN_BIG
|
||||
|
|
@ -139,6 +139,8 @@
|
|||
#define JL_PLATFORM_NAME "PSP"
|
||||
#elif defined(JOEYLIB_HOSTED_SDL3) && defined(__APPLE__)
|
||||
#define JL_PLATFORM_NAME "macOS"
|
||||
#elif defined(JOEYLIB_HOSTED_SDL3) && defined(__EMSCRIPTEN__)
|
||||
#define JL_PLATFORM_NAME "Web"
|
||||
#elif defined(JOEYLIB_HOSTED_SDL3)
|
||||
#define JL_PLATFORM_NAME "SDL3"
|
||||
#elif defined(JOEYLIB_HOSTED_WIN32)
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ void jlSerialSetDtr(bool on);
|
|||
// "my buffer is nearly full, stop sending" and later "go ahead", which is what a client with a small
|
||||
// ring and a peer that can be told to pause actually needs. RetroNet's 6551 clients (C64, Apple II)
|
||||
// have done exactly this for a long time and the 16-bit tier could not, so a JoeyLib target's only
|
||||
// defence against an overrun was the packet layer's retransmit.
|
||||
// defense against an overrun was the packet layer's retransmit.
|
||||
//
|
||||
// `on` = true asserts RTS (ready to receive). A port with no controllable RTS line no-ops, and a
|
||||
// closed port no-ops; neither is an error, so a caller can drive it unconditionally.
|
||||
|
|
|
|||
|
|
@ -146,24 +146,26 @@ void jlSpriteDraw(jlSurfaceT *s, jlSpriteT *sp, int16_t x, int16_t y);
|
|||
// sprites and on ports without a compiled fast path.
|
||||
void jlSpriteDrawTrusted(jlSurfaceT *s, jlSpriteT *sp, int16_t x, int16_t y);
|
||||
|
||||
// How many _type elements a backup buffer needs for a widthTiles x
|
||||
// heightTiles sprite at ANY x, on every port (rounded up), so that
|
||||
// How many uint16_t a backup buffer needs for a widthTiles x heightTiles
|
||||
// sprite at ANY x, on every port. A backup buffer is an array of uint16_t,
|
||||
// which gives it the 2-byte alignment it must have (on the Amiga, ST, and
|
||||
// X68000 an odd address sends save and restore down a much slower path,
|
||||
// and it crashes the trusted variants):
|
||||
//
|
||||
// static uint16_t gBacking[jlSpriteBackupLength(uint16_t, 4, 4)];
|
||||
// static uint16_t gBacking[jlSpriteBackupLength(4, 4)];
|
||||
//
|
||||
// is big enough, and jlSpriteBackupLength(uint8_t, w, h) is the size in
|
||||
// bytes (for jlAlloc, say). The worst case is the 16-pixel-group-aligned
|
||||
// region covering the sprite (x rounded DOWN to a 16-px boundary, width
|
||||
// UP): one extra 16-px group, (ceil(w/16) + 1) * 8 bytes per row. Give
|
||||
// the buffer 2-byte alignment (a uint16_t array has it): on the Amiga,
|
||||
// ST, and X68000 an odd address sends save and restore down a much
|
||||
// slower path, and it crashes the trusted variants.
|
||||
#define jlSpriteBackupLength(_type, _wT, _hT) \
|
||||
((uint16_t)((((((((uint16_t)(_wT) * 8u) + 15u) >> 4) + 1u) * 8u * ((uint16_t)(_hT) * 8u)) + sizeof(_type) - 1u) / sizeof(_type)))
|
||||
// One from jlAlloc is jlSpriteBackupLength(w, h) * sizeof(uint16_t) bytes
|
||||
// (on the Amiga, ST, and X68000 a jlAlloc block is malloc's, aligned for
|
||||
// any type). The worst case is the 16-pixel-group-aligned region covering
|
||||
// the sprite (x rounded DOWN to a 16-px boundary, width UP): one extra 16-px
|
||||
// group, (ceil(w/16) + 1) * 8 bytes per row, always a whole number of
|
||||
// uint16_t.
|
||||
#define jlSpriteBackupLength(_wT, _hT) \
|
||||
((uint16_t)((((((uint16_t)(_wT) * 8u + 15u) >> 4) + 1u) * 8u * ((uint16_t)(_hT) * 8u)) / sizeof(uint16_t)))
|
||||
|
||||
// Capture the destination region a subsequent jlSpriteDraw at the same
|
||||
// (x,y) would write to. backup->bytes must have room for
|
||||
// jlSpriteBackupLength(uint8_t, widthTiles, heightTiles) bytes for
|
||||
// jlSpriteBackupLength(widthTiles, heightTiles) uint16_t for
|
||||
// fully in-bounds draws; for clipped draws only the visible bytes are
|
||||
// stored. The captured region's exact geometry is reported in
|
||||
// backup->x/y/width/height/sizeBytes: x is rounded down to even on every
|
||||
|
|
@ -240,7 +242,7 @@ void jlSpriteSaveAndDrawTrusted(jlSurfaceT *s, jlSpriteT *sp, int16_t x, int16_t
|
|||
|
||||
// Snapshot an 8x8-aligned region of a jlSurfaceT into a new jlSpriteT.
|
||||
// The captured pixel data is copied into a sprite-owned buffer so
|
||||
// the source surface can be modified afterwards. Width and height
|
||||
// the source surface can be modified afterward. Width and height
|
||||
// are in TILES (each tile = 8x8 pixels). x and y are in pixels and
|
||||
// must be aligned to a tile boundary (multiple of 8) on the source
|
||||
// surface. Returns NULL if src is NULL, a dimension is 0, x or y is
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ bool jlSurfaceSaveFile(const jlSurfaceT *src, const char *path);
|
|||
// Read a surface from disk, overwriting dst. Returns false if the
|
||||
// file is missing, the wrong size, has an SCB byte above 15, or the
|
||||
// read fails; a read that fails part way can leave dst's pixels partly
|
||||
// overwritten, but its SCBs and palettes are then untouched. Colour 0
|
||||
// overwritten, but its SCBs and palettes are then untouched. Color 0
|
||||
// of every palette loaded is set to black, as jlPaletteSet does. dst
|
||||
// keeps its identity (no reallocation). Neither file call sets
|
||||
// jlLastError.
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uin
|
|||
// the band with the map as seen from a pixel origin, to the pixel both
|
||||
// ways, so moving the origin a little each frame scrolls it. The map
|
||||
// wraps at its edges (an origin past them, or a map smaller than the
|
||||
// view, repeats it). An index of JL_TILE_MAP_SKIP draws colour 0.
|
||||
// view, repeats it). An index of JL_TILE_MAP_SKIP draws color 0.
|
||||
//
|
||||
// The layer keeps the tiles around the view drawn, so a draw only draws
|
||||
// the edge tiles a move uncovered (a move of a whole view or more draws
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ cmdDoctor() {
|
|||
header "host"
|
||||
family="$(distroFamily)"
|
||||
if [ "$family" = "unknown" ]; then
|
||||
warn "unrecognised distribution: host packages must be installed by hand"
|
||||
warn "unrecognized distribution: host packages must be installed by hand"
|
||||
failures=$((failures + 1))
|
||||
else
|
||||
ok "package manager: $family"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
# The build clones its sub-projects by BRANCH name, which would make the
|
||||
# result drift; every sub-project is pre-cloned here at a pinned commit so
|
||||
# the Makefile finds it in place and skips its own clone. It also wget's
|
||||
# the NDK and ixemul SDK on every build; those are checked afterwards.
|
||||
# the NDK and ixemul SDK on every build; those are checked afterward.
|
||||
|
||||
compDescription="Amiga compiler: m68k-amigaos-gcc 6.5 + NDK 3.2 + vasm (source build)"
|
||||
compGroups="toolchain:amiga"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,32 @@ dvxSdkPin() {
|
|||
}
|
||||
|
||||
|
||||
compPin="dir:$dvxDir:dvx:$([ -f "$dvxRuntimeZip" ] && sha256Of "$dvxRuntimeZip"):sdk:$(dvxSdkPin)"
|
||||
compPin="dir:$dvxDir:dvx:$([ -f "$dvxRuntimeZip" ] && sha256Of "$dvxRuntimeZip"):sdk:$(dvxSdkPin):foldcase"
|
||||
|
||||
|
||||
# dvxFoldCase DIR -- DOS names are case-blind, and dvx.zip has folders that
|
||||
# differ only by case (system/ and SYSTEM/PACKAGES/): fold each into its
|
||||
# lower-case one, all the way down, so the tree is one DVX as DOS sees it.
|
||||
dvxFoldCase() {
|
||||
local dir="$1"
|
||||
local entry
|
||||
local name
|
||||
local lower
|
||||
for entry in "$dir"/*/; do
|
||||
[ -d "$entry" ] || continue
|
||||
name="$(basename "$entry")"
|
||||
lower="${name,,}"
|
||||
if [ "$name" != "$lower" ] && [ -d "$dir/$lower" ]; then
|
||||
cp -a "$dir/$name/." "$dir/$lower/"
|
||||
rm -rf "${dir:?}/${name:?}"
|
||||
fi
|
||||
done
|
||||
for entry in "$dir"/*/; do
|
||||
if [ -d "$entry" ]; then
|
||||
dvxFoldCase "${entry%/}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
compInstall() {
|
||||
|
|
@ -57,6 +82,7 @@ compInstall() {
|
|||
[ -f "$dvxRuntimeZip" ] || die "$dvxDir has no dist/dvx.zip; make DVX's distribution there, or point --dvx-dir at a DVX tree that has one"
|
||||
rm -rf "$compDir"
|
||||
extractArchive "$dvxRuntimeZip" "$compDir"
|
||||
dvxFoldCase "$compDir"
|
||||
if [ -f "$dvxSdkZip" ]; then
|
||||
tmp="$(freshBuildDir "$compId")"
|
||||
extractArchive "$dvxSdkZip" "$tmp"
|
||||
|
|
@ -73,7 +99,7 @@ compInstall() {
|
|||
|
||||
compInstalled() {
|
||||
[ -f "$compDir/dvx.exe" ] &&
|
||||
[ -f "$compDir/config/DVX.INI" ] &&
|
||||
[ -f "$compDir/system/DVX.INI" ] &&
|
||||
[ -f "$compDir/$dvxSdk/sdk.mk" ] &&
|
||||
[ -x "$compDir/$dvxSdk/bin/dvxhlpc" ] &&
|
||||
[ -x "$compDir/$dvxSdk/bin/dvxres" ]
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
# See LICENSE.md for license information.
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# llvm-mingw 20260922, msvcrt flavour: the cross compiler the win32 port
|
||||
# llvm-mingw 20260922, msvcrt flavor: the cross compiler the win32 port
|
||||
# (make win32) builds Windows programs with -- clang, lld and the mingw-w64
|
||||
# headers and libraries, prebuilt for Linux x86-64. The msvcrt flavour links
|
||||
# headers and libraries, prebuilt for Linux x86-64. The msvcrt flavor links
|
||||
# against MSVCRT.DLL, the C library every Windows since 95 OSR2 carries (the
|
||||
# ucrt one needs Windows 10's), so a program runs on the old Windows the
|
||||
# port aims at. Programs are 32-bit (i686).
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
# from the mirror osxcross users share (github.com/joseluisq/macosx-sdks),
|
||||
# pinned to the release's SHA-256; a copy of the same tarball put in
|
||||
# toolchains/cache/downloads first (MacOSX15.5.sdk.tar.xz) is used instead.
|
||||
# Its licence is Apple's (LICENSES.md). llvm-lipo joins each architecture's
|
||||
# Its license is Apple's (LICENSES.md). llvm-lipo joins each architecture's
|
||||
# build into one universal file and llvm-libtool-darwin archives universal
|
||||
# objects (make/support/macos/cc.sh, make/macos.mk).
|
||||
|
||||
|
|
|
|||
|
|
@ -17,23 +17,23 @@ compHostDeps="tar"
|
|||
pspdevRelease="v20261001"
|
||||
pspdevHostId="$( (. /etc/os-release 2>/dev/null && printf '%s' "${ID:-}") || true)"
|
||||
if [ "$(distroFamily)" = "dnf" ]; then
|
||||
pspdevFlavour="fedora-latest"
|
||||
pspdevFlavor="fedora-latest"
|
||||
compSha256="7874d4d696f1abe58d3d17fd8ae365e9b129c4bea16b383bf708655037c0e21c"
|
||||
elif [ "$pspdevHostId" = "debian" ]; then
|
||||
pspdevFlavour="debian-latest"
|
||||
pspdevFlavor="debian-latest"
|
||||
compSha256="39b20efc7c8492cdbd9929178f45afc801dabb0967b7c882573520f22536920c"
|
||||
else
|
||||
pspdevFlavour="ubuntu-latest-x86_64"
|
||||
pspdevFlavor="ubuntu-latest-x86_64"
|
||||
compSha256="a86efe624e770005290919617859e9df24bd8fcd3e364f4587f23ec7364b0acd"
|
||||
fi
|
||||
compUrl="https://github.com/pspdev/pspdev/releases/download/$pspdevRelease/pspdev-$pspdevFlavour.tar.gz"
|
||||
compUrl="https://github.com/pspdev/pspdev/releases/download/$pspdevRelease/pspdev-$pspdevFlavor.tar.gz"
|
||||
compPin="$compSha256"
|
||||
|
||||
|
||||
compInstall() {
|
||||
local archive
|
||||
# Every flavour's file has the same name: cache it under the release's.
|
||||
archive="$(fetchFile "$compUrl" "$compSha256" "pspdev-$pspdevRelease-$pspdevFlavour.tar.gz")"
|
||||
# Every flavor's file has the same name: cache it under the release's.
|
||||
archive="$(fetchFile "$compUrl" "$compSha256" "pspdev-$pspdevRelease-$pspdevFlavor.tar.gz")"
|
||||
rm -rf "$compDir"
|
||||
extractArchive "$archive" "$compDir" 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Windows for Workgroups 3.11, set up unattended on a DOSBox Staging PC with
|
||||
# the S3 Trio64V+ driver at 800x600 in 256 colours and Windows' own Sound
|
||||
# the S3 Trio64V+ driver at 800x600 in 256 colors and Windows' own Sound
|
||||
# Blaster driver (installer/lib/win16.sh): what the win16 port's programs
|
||||
# run on for joey-run and the gates (scripts/lib/win16Gate.sh). Made from
|
||||
# your own install disc and S3 driver disk -- private files, never
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
# BFS system volume it finds: a copy of the x86 track, on the machine's
|
||||
# first disk, with the job runner as its UserBootscript. The runner is the
|
||||
# Haiku VMs' (installer/lib/haiku.sh), so the gates drive both alike: a disk
|
||||
# labelled HAIKU_JOB_VOLUME holding job.sh (Tracker mounts it), job.sh run
|
||||
# labeled HAIKU_JOB_VOLUME holding job.sh (Tracker mounts it), job.sh run
|
||||
# there with its output in job.log, then the machine powered off (the
|
||||
# kernel's apm and enable_shutdown settings), so QEMU exits. R5 has no
|
||||
# serial port to say so on: its serial driver finds the ports through the
|
||||
|
|
@ -70,7 +70,7 @@ beosBfs() {
|
|||
beosJobRunner() {
|
||||
cat <<EOF
|
||||
# JoeyLib's job runner (installer/lib/beos.sh): when Tracker mounts a disk
|
||||
# labelled $HAIKU_JOB_VOLUME holding job.sh, job.sh is run there (its output in
|
||||
# labeled $HAIKU_JOB_VOLUME holding job.sh, job.sh is run there (its output in
|
||||
# job.log) and the machine powers off. Without one, an ordinary boot.
|
||||
tries=0
|
||||
while [ ! -f /$HAIKU_JOB_VOLUME/job.sh ]; do
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# for BeOS compatibility).
|
||||
#
|
||||
# The job runner: a VM image's UserBootscript (written by haikuVmPrepare)
|
||||
# looks for a disk labelled HAIKU_JOB_VOLUME, mounts it, waits for the
|
||||
# looks for a disk labeled HAIKU_JOB_VOLUME, mounts it, waits for the
|
||||
# desktop and the media services (the boot script runs before they are up,
|
||||
# and a program opening its sound before the media server has started gets
|
||||
# none), runs its job.sh there with the output going to job.log beside it,
|
||||
|
|
@ -171,7 +171,7 @@ haikuVmVerify() {
|
|||
haikuJobRunner() {
|
||||
cat <<EOF
|
||||
#!/bin/sh
|
||||
# JoeyLib's job runner (installer/lib/haiku.sh): a disk labelled
|
||||
# JoeyLib's job runner (installer/lib/haiku.sh): a disk labeled
|
||||
# $HAIKU_JOB_VOLUME holding job.sh is mounted, job.sh is run there (its output
|
||||
# in job.log) and the machine shuts down. Without one, an ordinary boot.
|
||||
serial=/dev/ports/pc_serial0
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# PS/2 mouse, nothing optional, Setup back to DOS at the end. Then the
|
||||
# Sound Blaster 1.5 driver and its virtual device are expanded from the
|
||||
# disc with its own EXPAND.EXE.
|
||||
# 2. The S3 Trio64V+ 1.70.04 driver is put in at 800x600 in 256 colours,
|
||||
# 2. The S3 Trio64V+ 1.70.04 driver is put in at 800x600 in 256 colors,
|
||||
# and the Sound Blaster driver turned on, editing SYSTEM.INI the way
|
||||
# the driver disk's OEMSETUP.INF (its D808S profile) and Control Panel
|
||||
# would. The swap file is off.
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@
|
|||
# change there makes the component out
|
||||
# of date
|
||||
# winDriverSources DIR the files a driver is made from,
|
||||
# and its licence
|
||||
# and its license
|
||||
|
||||
# The source files' names: C sources and headers, link and module
|
||||
# definition files, makefiles, the licence.
|
||||
# definition files, makefiles, the license.
|
||||
WIN_DRIVER_LICENSE=LICENSE.md
|
||||
WIN_DRIVER_SOURCES=(-name '*.c' -o -name '*.h' -o -name '*.lnk' -o -name '*.def' -o -name 'Makefile' -o -name '*.mk' -o -name "$WIN_DRIVER_LICENSE")
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ winDriverBuild() {
|
|||
local file="$3"
|
||||
local build
|
||||
[ -f "$dir/$makefile" ] || die "no $makefile in $dir: the driver's project must be beside this repository (or named by its *_DIR variable)"
|
||||
[ -f "$dir/$WIN_DRIVER_LICENSE" ] || die "no $WIN_DRIVER_LICENSE in $dir: the driver ships with its licence"
|
||||
[ -f "$dir/$WIN_DRIVER_LICENSE" ] || die "no $WIN_DRIVER_LICENSE in $dir: the driver ships with its license"
|
||||
build="$(freshBuildDir "$compId")"
|
||||
make -C "$dir" -f "$makefile" WATCOM="$JOEY_OPT/openWatcom" OBJ="$build/obj" BIN="$build/bin" > "$build/make.log" 2>&1 ||
|
||||
die "building $file failed; the log: $build/make.log"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ toolchain.
|
|||
|
||||
| Patch | What it fixes | Needed by |
|
||||
|---|---|---|
|
||||
| 0001-bbb-moveq-mask | A miscompile in Bebbo's post-pass optimizer (`gcc/bbb-opts.c`, on by default at `-O2`). The backend loads a data register's word or byte constant in -128..127 with `moveq`, which writes all 32 bits, sign-extended; the optimizer's register tracker took it for a word or byte write and kept the upper word's old "known clear" mask. After a negative one (a `moveq #-1,dN` for a compare with 0xFFFF, say), its `(e0)` step then deleted a later zero-extension's `moveq #0,dN` before `move.w x,dN` as superfluous, so a 16-bit offset carried 0xFFFF in its upper word into a 32-bit pointer add. It struck `jlSpriteRestoreUnder`'s fused stage restore, which wrote 64 KB below the planes once a neighbouring inline asm call changed the register allocation. Upstream (`amiga6.5`, 2026-09-26) has the same code. | every Amiga build |
|
||||
| 0001-bbb-moveq-mask | A miscompile in Bebbo's post-pass optimizer (`gcc/bbb-opts.c`, on by default at `-O2`). The backend loads a data register's word or byte constant in -128..127 with `moveq`, which writes all 32 bits, sign-extended; the optimizer's register tracker took it for a word or byte write and kept the upper word's old "known clear" mask. After a negative one (a `moveq #-1,dN` for a compare with 0xFFFF, say), its `(e0)` step then deleted a later zero-extension's `moveq #0,dN` before `move.w x,dN` as superfluous, so a 16-bit offset carried 0xFFFF in its upper word into a 32-bit pointer add. It struck `jlSpriteRestoreUnder`'s fused stage restore, which wrote 64 KB below the planes once a neighboring inline asm call changed the register allocation. Upstream (`amiga6.5`, 2026-09-26) has the same code. | every Amiga build |
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ drivers. The gates depend on every one of them.
|
|||
|---|---|---|
|
||||
| 0002-z80scc-rx-fifo | Makes the Z8530 receive FIFO depth selectable at run time (see below). By default it is 512 bytes, so a POLLED receiver (no RX interrupt) survives the emulator's deaf windows; `JOEY_Z80SCC_RX_FIFO=3` restores the real chip's depth. Originally written for RetroNet. | IIgs serial gates (512); `verify-iigs-serial-loss.sh` (3) |
|
||||
| 0003-x68k-crtc-timing | The X68000 CRTC: the graphic high-speed clear's timing (the main defect), and the text raster copy firing on both hsync edges instead of only the front porch | X68000 gates and timing |
|
||||
| 0004-x68k-waitstates-rs232-mouse | Three X68000 changes that edit the same files: VRAM wait states (MAME modelled none), the RS-232C port wired to a slot so `-rs232c null_modem` works, and a backport of upstream commit ab6b6fc9e797 (Patrick Mackinlay, "x68k: add serial mouse"), which provides the `:mouse_port:x68k:X/Y/BTN` inputs | `verify-x68000-serial.sh`, `verify-x68000-mouse.sh`, X68000 timing |
|
||||
| 0004-x68k-waitstates-rs232-mouse | Three X68000 changes that edit the same files: VRAM wait states (MAME modeled none), the RS-232C port wired to a slot so `-rs232c null_modem` works, and a backport of upstream commit ab6b6fc9e797 (Patrick Mackinlay, "x68k: add serial mouse"), which provides the `:mouse_port:x68k:X/Y/BTN` inputs | `verify-x68000-serial.sh`, `verify-x68000-mouse.sh`, X68000 timing |
|
||||
|
||||
There is no 0001 any more. It gave the IIgs SCC a 3.6864 MHz PCLK and no
|
||||
RTxC clock, to suit a HAL that ran the baud rate generator from PCLK. Stock
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ the Amiga's playback the same as theirs.
|
|||
|
||||
| Patch | What it fixes | Needed by |
|
||||
|---|---|---|
|
||||
| 0001-protracker-2.3-effects | Four places PTPlayer strays from Protracker 2.3, whose behaviour the module player keeps: its volume slide sets Paula's period back to the note's, which is right for `Axy` but undid `6xy`'s vibrato, `5xy`'s glissando, and a tick of either under `EAx`/`EBx`; its arpeggio counts from the note's index, not the period a slide has left (and never reads on into the next finetune's periods past B-3); and its tremolo is half Protracker's depth, with its saw ramping by its own position rather than the vibrato's. And `6xy` read its vibrato depth into a register whose high byte held whatever came before, so the vibrato read past its table (hidden while the period was set back each tick). | the Amiga's module playback; `tests/host/modPlayerHost.c` checks the module player's side |
|
||||
| 0001-protracker-2.3-effects | Four places PTPlayer strays from Protracker 2.3, whose behavior the module player keeps: its volume slide sets Paula's period back to the note's, which is right for `Axy` but undid `6xy`'s vibrato, `5xy`'s glissando, and a tick of either under `EAx`/`EBx`; its arpeggio counts from the note's index, not the period a slide has left (and never reads on into the next finetune's periods past B-3); and its tremolo is half Protracker's depth, with its saw ramping by its own position rather than the vibrato's. And `6xy` read its vibrato depth into a register whose high byte held whatever came before, so the vibrato read past its table (hidden while the period was set back each tick). | the Amiga's module playback; `tests/host/modPlayerHost.c` checks the module player's side |
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
26
joey-run
26
joey-run
|
|
@ -4,7 +4,7 @@
|
|||
# SDL3 port, natively, for the web port, in a browser, for the Win32 port,
|
||||
# under Wine, and for the Win16 port, on Windows 3.11 in DOSBox; the PSP
|
||||
# port's in headless PPSSPP; the Haiku and BeOS ports' in their QEMU VMs).
|
||||
# A macOS port's app is recognised but not run: nothing here runs Mac
|
||||
# A macOS port's app is recognized but not run: nothing here runs Mac
|
||||
# programs.
|
||||
#
|
||||
# Usage:
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
# an IIgs disk image build/iigs/bin/DRAW.2mg (make iigs-game-disk), or
|
||||
# a data disk such as joey.2mg (boots GS/OS with it)
|
||||
# an example name draw, audio, uber, ... together with --port
|
||||
# The machine is recognised from the file itself. A program built outside
|
||||
# The machine is recognized from the file itself. A program built outside
|
||||
# JoeyLib (make/project.mk) runs the same way: name its file, folder or disk
|
||||
# image in the project's build/, or give its name with --project and --port.
|
||||
#
|
||||
|
|
@ -117,8 +117,8 @@
|
|||
# with the keypad's Enter (DOSBox keeps Alt+Enter for its own full screen),
|
||||
# puts it on the whole screen. Interactive runs keep that DVX in
|
||||
# build/dvx/run (the project's with --project), so what the program saves
|
||||
# (in DVX's CONFIG folder), and whether it was left on the whole screen, is
|
||||
# still there next time.
|
||||
# (in its folder in DVX's USER folder), and whether it was left on the whole
|
||||
# screen, is still there next time.
|
||||
#
|
||||
# The serial port is the machine's usual one: the IIgs modem port (the
|
||||
# printer port and a Super Serial Card are not connected), the X68000's
|
||||
|
|
@ -1025,7 +1025,12 @@ runPsp() {
|
|||
# ends, and what it wrote in its folder (saves, its log) is copied back.
|
||||
# --headless: no window and no sound, at one window pixel per stage pixel,
|
||||
# and --snapshot is QEMU's picture of the whole screen. --seconds counts
|
||||
# from when the job starts, not from the boot.
|
||||
# from when the job starts, not from the boot. In a headless run the host
|
||||
# times them and stops the VM after them (and the snapshot), so the shot never
|
||||
# misses a VM whose clock strays (BeOS R5's ran twice as fast before it got
|
||||
# an instruction-counted TSC, BEOS_VM_ICOUNT; it still runs about 5% slow). A
|
||||
# run in a window brings its files back, so it ends by itself, in the VM's
|
||||
# seconds.
|
||||
runBe() {
|
||||
local name
|
||||
local dir
|
||||
|
|
@ -1052,8 +1057,11 @@ runBe() {
|
|||
done
|
||||
fi
|
||||
# The job outlasts --seconds a little, so the snapshot finds the
|
||||
# program still up.
|
||||
# program still up; a headless one runs until the host stops it.
|
||||
jobSeconds=$((${seconds:-$beForeverSeconds} + beSnapshotSeconds))
|
||||
if [ "$headless" = "1" ]; then
|
||||
jobSeconds=$beForeverSeconds
|
||||
fi
|
||||
beJobScript "$name" "$name" "" "$jobSeconds" "${settings[@]}" > "$work/job/job.sh"
|
||||
beJobDisk "$work/job.img" "$work/job" || fail "could not make the VM's job disk"
|
||||
echo "joey-run: running $name in the $detected VM (QEMU); it powers off when the program ends."
|
||||
|
|
@ -1071,7 +1079,11 @@ runBe() {
|
|||
if [ -n "$snapshot" ]; then
|
||||
beQmp "$work" screendump "$snapshot" || echo "joey-run: no screen to snapshot" >&2
|
||||
fi
|
||||
beWaitExit "$work" "$((beSnapshotSeconds + beShutdownSeconds))" || beStop
|
||||
if [ "$headless" = "1" ]; then
|
||||
beStop
|
||||
else
|
||||
beWaitExit "$work" "$((beSnapshotSeconds + beShutdownSeconds))" || beStop
|
||||
fi
|
||||
else
|
||||
wait "$emuPid" || true
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ $(call requireTool,$(AMIGA_PTPLAYER)/ptplayer.asm,toolchain:amiga)
|
|||
# independently. -I on $(SRC_DIR)/amiga lets ptplayer.h resolve
|
||||
# <SDI_compiler.h> from the machine-local shim alongside our code.
|
||||
PTPLAYER_DIR := $(AMIGA_PTPLAYER)
|
||||
# -fno-common: gcc 6 merges a program's uninitialised global into a library
|
||||
# -fno-common: gcc 6 merges a program's uninitialized global into a library
|
||||
# one of the same name; gcc 10 on (every other port) refuses the link.
|
||||
CFLAGS := $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer -fno-common -noixemul -D__OSCOMPAT -I$(SRC_DIR)/amiga -I$(SRC_68K) -I$(PTPLAYER_DIR) -MMD -MP
|
||||
# OSCOMPAT=1 selects PTPlayer's audio.device-friendly variant (uses
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ $(call requireTool,$(HAIKU32_ROOT)/cross/bin/i586-pc-haiku-g++,toolchain:beos)
|
|||
$(call requireTool,$(BEOS_R5_SYSROOT)/system-lib/libbe.so,toolchain:beos)
|
||||
|
||||
BEOS_GCC2 := BE_R5_SYSROOT=$(BEOS_R5_SYSROOT) $(HAIKU32_GCC2) $(HAIKU32_ROOT)
|
||||
# -fno-common: gcc 2.95 merges a program's uninitialised global into a
|
||||
# -fno-common: gcc 2.95 merges a program's uninitialized global into a
|
||||
# library one of the same name; every other port's compiler refuses the link.
|
||||
HOSTED_CC := $(BEOS_GCC2) gcc -fno-common
|
||||
HOSTED_CXX := $(BEOS_GCC2) g++
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ PROJECT_BUILD ?= $(PROJECT_DIR)/build
|
|||
export JOEY_BUILD
|
||||
|
||||
INCLUDE_DIR := $(REPO_DIR)/include
|
||||
# JoeyLib's version, from the one place it is set (joey/platform.h).
|
||||
JOEYLIB_VERSION := $(shell sed -n 's/^\#define JL_VERSION_STRING "\(.*\)"$$/\1/p' $(INCLUDE_DIR)/joey/platform.h)
|
||||
# Machine-specific code lives in src/<machine>/ (iigs, amiga, atarist, dos,
|
||||
# x68000, dvx); code shared across machines of one CPU lives in a processor
|
||||
# folder (src/m68k, src/x86); the hosted core and its backends in src/hosted
|
||||
|
|
@ -109,7 +111,7 @@ GENERIC_C_SRCS := $(filter-out %/spriteEmitStub.c, $(wildcard $(SRC_DIR)/generic
|
|||
# Common include flags. Per-port code can include port.h / surfaceInternal.h
|
||||
# directly because SRC_CORE is in the include path. EXTRA_CFLAGS is the one
|
||||
# command-line hook for extra flags on every port (make EXTRA_CFLAGS=-DX);
|
||||
# make/iigs.mk, which does not use COMMON_CFLAGS, honours it too.
|
||||
# make/iigs.mk, which does not use COMMON_CFLAGS, honors it too.
|
||||
COMMON_CFLAGS := -I$(INCLUDE_DIR) -I$(SRC_CORE) -Wall -Wextra -Werror -O2 $(EXTRA_CFLAGS)
|
||||
|
||||
ifdef PLATFORM
|
||||
|
|
@ -170,6 +172,10 @@ programTarget = $(call programBin,$(1))
|
|||
# DVX SDK) build/<port>/data/<name>/DATA.
|
||||
programDataDir = $(if $(PROGRAM_DATA_APART),$(PROGRAM_BUILD)/data/$(call programName,$(1))/DATA,$(call programDir,$(1))/DATA)
|
||||
|
||||
# $(call programVersion,ID): its version, major.minor.patch: its own
|
||||
# VERSION, else the project's PROGRAM_VERSION, else 1.0.0.
|
||||
programVersion = $(or $(call programVar,$(1),VERSION),$(PROGRAM_VERSION),1.0.0)
|
||||
|
||||
# $(call programOptionalDir,ID): where its optional DATA files are staged:
|
||||
# ones it runs without (a game that JIT-compiles a level's sprites when the
|
||||
# level's pre-compiled bank is missing). Only the IIgs disk packer treats
|
||||
|
|
|
|||
29
make/dvx.mk
29
make/dvx.mk
|
|
@ -114,13 +114,25 @@ $(LIB): $(LIB_OBJS)
|
|||
# DVX_KIND what the help and resource texts call a program: "<title> is a
|
||||
# <kind> program" (default JoeyLib)
|
||||
# The vendor and the author are the project's own: it must set them.
|
||||
#
|
||||
# Each program is also an add-on package, bin/packages/<name>.dpk, which
|
||||
# anyone installs with DVX's Software program: its id is vendor.name, its
|
||||
# title and summary the program's TITLE and HELP, its version the program's
|
||||
# (programVersion), and the rest the project's (no apostrophes in any):
|
||||
# DVX_PUBLISHER who publishes it (default DVX_AUTHOR)
|
||||
# DVX_LICENSE its license, as Software shows it (MIT, say)
|
||||
# DVX_HOMEPAGE its web site
|
||||
# DVX_PKG_KEY the secret key file (BASE.SEC, from the SDK's
|
||||
# `dvxpkg keygen "Your Name" BASE`) to sign it with, so
|
||||
# whoever has BASE.KEY can tell it is yours; unsigned
|
||||
# without it
|
||||
DVX_KIND := JoeyLib
|
||||
PROGRAM_EXT := .app
|
||||
PROGRAM_DATA_APART := yes
|
||||
|
||||
# What every program's help says after "<title> is a <kind> program.".
|
||||
DVX_HELP_WINDOW := It runs in its own window at a virtual 70 frames a second; while the window is active every key goes to it except Alt+Tab (switch programs), Ctrl+Esc (Task Manager) and Alt+F4 (close it). Game > Exit also closes it.
|
||||
DVX_HELP_SCREEN := Game > Full Screen, or Alt+Enter, puts it on the whole screen at 320x200 in 256 colours, and back in its window. It starts the way it was left. Alt+Tab or Ctrl+Esc brings the desktop back while it runs on; coming back to it gives it the whole screen again.
|
||||
DVX_HELP_SCREEN := Game > Full Screen, or Alt+Enter, puts it on the whole screen at 320x200 in 256 colors, and back in its window. It starts the way it was left. Alt+Tab or Ctrl+Esc brings the desktop back while it runs on; coming back to it gives it the whole screen again.
|
||||
programNameRule = $(call lc,$(1))
|
||||
programDir = $(or $(call programVar,$(1),BINDIR),$(BINDIR))/apps/$(DVX_VENDOR)/$(call programName,$(1))
|
||||
programTarget = app-$(call programName,$(1))
|
||||
|
|
@ -140,6 +152,7 @@ programRules = $(call programDvxRules,$(1),$(call programName,$(1)))
|
|||
# $(1) the id, $(2) the name. Everything that may hold a comma (the title,
|
||||
# the help line) is looked up here rather than passed in, and every line
|
||||
# written out is shellQuote'd, so a title or help line may hold quotes.
|
||||
# PKG_LICENCE is spelled the British way because sdk.mk names it so.
|
||||
define programDvxRules
|
||||
$(call requireSet,DVX_VENDOR,the project's own vendor folder$(comma) which its programs install in: 1 to 8 letters$(comma) digits or _)
|
||||
$(call requireSet,DVX_AUTHOR,the author DVX names for the project's programs)
|
||||
|
|
@ -153,8 +166,16 @@ app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(
|
|||
'KIND = app' \
|
||||
'NAME = $(2)' \
|
||||
'VENDOR = $(DVX_VENDOR)' \
|
||||
$(call shellQuote,PKG_TITLE = $(call programTitle,$(1))) \
|
||||
'PKG_VERSION = $(call programVersion,$(1))' \
|
||||
$(call shellQuote,PKG_SUMMARY = $(call programVar,$(1),HELP)) \
|
||||
$(call shellQuote,PKG_PUBLISHER = $(or $(DVX_PUBLISHER),$(DVX_AUTHOR))) \
|
||||
$(call shellQuote,PKG_AUTHOR = $(DVX_AUTHOR)) \
|
||||
$(call shellQuote,PKG_LICENCE = $(DVX_LICENSE)) \
|
||||
$(call shellQuote,PKG_HOMEPAGE = $(DVX_HOMEPAGE)) \
|
||||
'PKG_KEY = $(if $(DVX_PKG_KEY),$(abspath $(DVX_PKG_KEY)))' \
|
||||
'LIBS = $(LIB)' \
|
||||
'DATA = $(if $(call programVar,$(1),DATA),$(call programDataDir,$(1)))' \
|
||||
'DATA = $(if $(call programVar,$(1),DATA),$$$$(wildcard $(call programDataDir,$(1))/*))' \
|
||||
$(call shellQuote,CFLAGS = $(APP_CFLAGS) $(call programVar,$(1),CFLAGS) -I$(dir $(abspath $(firstword $(call programVar,$(1),SRCS)))) -DDVX_PORT_APP_NAME=$(call shellQuote,$(call cQuote,$(call programTitle,$(1))))) \
|
||||
'include $$$$(SDK_DIR)/sdk.mk' | $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/Makefile
|
||||
@printf '%s\n' \
|
||||
|
|
@ -182,9 +203,11 @@ app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(
|
|||
@mkdir -p $(or $(call programVar,$(1),BINDIR),$(BINDIR))
|
||||
rm -rf $(call programDir,$(1))
|
||||
cp -R $(APPSDIR)/$(2)/pkg/apps $(or $(call programVar,$(1),BINDIR),$(BINDIR))/
|
||||
@mkdir -p $(or $(call programVar,$(1),BINDIR),$(BINDIR))/packages
|
||||
cp $(APPSDIR)/$(2)/$(2).dpk $(or $(call programVar,$(1),BINDIR),$(BINDIR))/packages/
|
||||
|
||||
# Its icon, the one Program Manager shows: icon32 among its resources, a
|
||||
# 32x32 BMP on DVX's grey, which sdk.mk takes from the project's folder.
|
||||
# 32x32 BMP on DVX's gray, which sdk.mk takes from the project's folder.
|
||||
$(APPSDIR)/$(2)/icon32.bmp: $(call programIcon,$(1)) $(ICON_BAKE)
|
||||
@mkdir -p $$(dir $$@)
|
||||
$(ICON_BAKE) bmp $$< $$@
|
||||
|
|
|
|||
|
|
@ -49,26 +49,33 @@ PROGRAMS := HELLO PATTERN DRAW KEYS SERIAL SERTEST SERMOUSE SAVE TILEBANK S
|
|||
TEST_PROGRAMS := SCCPROBE SERLOSS SERLOSSS SERSLOT SERFAST DIRTYMRK UBERPROB AUDIOPRB AUDPAR PALPROBE FASTRAM WBLAUNCH
|
||||
|
||||
# Whose these are, as a project names its own: on DVX the vendor folder
|
||||
# they install in and their author (make/dvx.mk), on macOS the start of
|
||||
# their apps' identifiers (make/macos.mk). DVX's help and resource texts
|
||||
# call them JoeyLib examples.
|
||||
# they install in, their author, and their package's license (make/dvx.mk),
|
||||
# on macOS the start of their apps' identifiers (make/macos.mk). DVX's help
|
||||
# and resource texts call them JoeyLib examples. Their version is
|
||||
# JoeyLib's.
|
||||
DVX_VENDOR := kpunch
|
||||
DVX_AUTHOR := Scott Duensing
|
||||
DVX_LICENSE := MIT
|
||||
PROGRAM_VERSION := $(JOEYLIB_VERSION)
|
||||
DVX_KIND := JoeyLib example
|
||||
MACOS_ID_PREFIX := com.kangaroopunch.joeylib
|
||||
|
||||
# The IIgs build has never included HELLO.
|
||||
HELLO_SRCS := $(EXAMPLES)/hello/hello.c
|
||||
HELLO_PORTS := amiga atarist ste dos x68000 dvx
|
||||
# The examples that show text share one font (examples/common).
|
||||
EXAMPLE_FONT := $(EXAMPLES)/common/exampleFont.c
|
||||
EXAMPLE_FONT_CFLAGS := -I$(EXAMPLES)/common
|
||||
|
||||
HELLO_SRCS := $(EXAMPLES)/hello/hello.c $(EXAMPLE_FONT)
|
||||
HELLO_CFLAGS := $(EXAMPLE_FONT_CFLAGS)
|
||||
HELLO_TITLE := Hello
|
||||
HELLO_HELP := The smallest JoeyLib program: it clears the screen and waits for a key.
|
||||
HELLO_HELP := The smallest JoeyLib program: it says hello, its version, and the machine, and waits for a key.
|
||||
PATTERN_SRCS := $(EXAMPLES)/pattern/pattern.c
|
||||
PATTERN_TITLE := Pattern
|
||||
PATTERN_HELP := Eight bands of gradient palettes, one per band of scan lines.
|
||||
DRAW_SRCS := $(EXAMPLES)/draw/draw.c
|
||||
DRAW_TITLE := Draw
|
||||
DRAW_HELP := The drawing primitives: pixels, lines, circles, rectangles and flood fills.
|
||||
KEYS_SRCS := $(EXAMPLES)/keys/keys.c
|
||||
KEYS_SRCS := $(EXAMPLES)/keys/keys.c $(EXAMPLE_FONT)
|
||||
KEYS_CFLAGS := $(EXAMPLE_FONT_CFLAGS)
|
||||
KEYS_TITLE := Keys
|
||||
KEYS_HELP := Keyboard, typed characters and mouse: held keys light up and typed text appears. Escape ends it.
|
||||
SERIAL_SRCS := $(EXAMPLES)/serial/serial.c
|
||||
|
|
@ -78,17 +85,21 @@ SERTEST_SRCS := $(EXAMPLES)/sertest/sertest.c
|
|||
SERTEST_TITLE := SerTest
|
||||
SERTEST_HELP := The serial port test program.
|
||||
SERTEST_AMIGA_NAME := SerTest
|
||||
SERMOUSE_SRCS := $(EXAMPLES)/sermouse/sermouse.c
|
||||
SERMOUSE_PORTS := x68000
|
||||
SERMOUSE_SRCS := $(EXAMPLES)/sermouse/sermouse.c $(EXAMPLE_FONT)
|
||||
SERMOUSE_CFLAGS := $(EXAMPLE_FONT_CFLAGS)
|
||||
SERMOUSE_TITLE := SerMouse
|
||||
SERMOUSE_HELP := The serial port and the mouse together: an echo test on the line while the mouse is read and drawn.
|
||||
SERMOUSE_AMIGA_NAME := SerMouse
|
||||
SAVE_SRCS := $(EXAMPLES)/save/save.c
|
||||
SAVE_TITLE := Save
|
||||
SAVE_HELP := Saved files: counts its runs in a file in its settings folder.
|
||||
SAVE_HELP := Saved files: counts its runs in a file in its user folder.
|
||||
TILEBANK_SRCS := $(EXAMPLES)/tilebank/tilebank.c
|
||||
TILEBANK_TITLE := TileBank
|
||||
TILEBANK_HELP := The tile bank check: loads a baked tile bank two ways and checks every pixel.
|
||||
TILEBANK_AMIGA_NAME := TileBank
|
||||
SCB_SRCS := $(EXAMPLES)/scb/scb.c
|
||||
SCB_PORTS := x68000
|
||||
SCB_TITLE := SCB
|
||||
SCB_HELP := The per-scanline palette under its heaviest load: all sixteen palettes, changing as often as every line.
|
||||
JOY_SRCS := $(EXAMPLES)/joy/joy.c
|
||||
JOY_TITLE := Joy
|
||||
JOY_HELP := Joysticks: shows the position and buttons of each joystick.
|
||||
|
|
@ -103,17 +114,19 @@ AUDIO_TITLE := Audio
|
|||
AUDIO_HELP := Sound: tracker music, a MOD, sound effects, a streamed tone and a noise sweep.
|
||||
UBER_SRCS := $(EXAMPLES)/uber/uber.c
|
||||
UBER_TITLE := Uber
|
||||
UBER_HELP := The JoeyLib conformance and speed test: every drawing call, timed and checked. Its results go to joeylog.txt in its settings folder.
|
||||
ADV_SRCS := $(EXAMPLES)/adventure/adventure.c
|
||||
UBER_HELP := The JoeyLib conformance and speed test: every drawing call timed and checked, its results in joeylog.txt in its user folder.
|
||||
ADV_SRCS := $(EXAMPLES)/adventure/adventure.c $(EXAMPLE_FONT)
|
||||
ADV_CFLAGS := $(EXAMPLE_FONT_CFLAGS)
|
||||
ADV_TITLE := Adventure
|
||||
ADV_HELP := A small adventure game.
|
||||
ADV2_SRCS := $(EXAMPLES)/adventure2/adventure2.c
|
||||
ADV_HELP := A Sierra-style adventure demo: two rooms, a verb cursor, hotspots, and an inventory.
|
||||
ADV2_SRCS := $(EXAMPLES)/adventure2/adventure2.c $(EXAMPLE_FONT)
|
||||
ADV2_CFLAGS := $(EXAMPLE_FONT_CFLAGS)
|
||||
ADV2_TITLE := Adventure 2
|
||||
ADV2_HELP := A second small adventure game.
|
||||
ADV2_HELP := One room drawn as Sierra SCI games draw it, with priority and control screens.
|
||||
|
||||
# Every example that ships as its own bootable IIgs volume (make
|
||||
# iigs-game-disks).
|
||||
$(foreach p,DRAW PATTERN KEYS JOY SPRITE SCROLL SAVE AUDIO ADV ADV2 UBER SERIAL SERTEST,$(eval $(p)_IIGS_GAME_DISK := yes))
|
||||
# Every example also ships as its own bootable IIgs volume (make
|
||||
# iigs-game-disks): the shared disk (make iigs-disk) holds only the first few.
|
||||
$(foreach p,$(PROGRAMS),$(eval $(p)_IIGS_GAME_DISK := yes))
|
||||
|
||||
# IIgs serial probes for the gates: SCCPROBE (verify-iigs-serial.sh: does
|
||||
# opening the modem port leave the printer port, AppleTalk's channel, alive?)
|
||||
|
|
@ -174,7 +187,7 @@ UBERPROB_SRCS := $(UBER_SRCS)
|
|||
UBERPROB_PORTS := iigs dvx win16 win32 haiku haiku32 beos atarist ste amiga
|
||||
UBERPROB_CFLAGS := -DUBER_PROBE -DUBER_FRAMES=1
|
||||
UBERPROB_TITLE := Uber Probe
|
||||
UBERPROB_HELP := UBER with its probe rows: the present checked against the stage on screen. Its results go to joeylog.txt in its settings folder.
|
||||
UBERPROB_HELP := UBER with its probe rows: the present checked against the stage on screen, the results in joeylog.txt in its user folder.
|
||||
UBERPROB_IIGS_NAME := UBER
|
||||
UBERPROB_IIGS_BINDIR := $(PROGRAM_BUILD)/probe/bin
|
||||
UBERPROB_DVX_BINDIR := $(PROGRAM_BUILD)/probe
|
||||
|
|
@ -208,7 +221,7 @@ AUDPAR_IIGS_BINDIR := $(PROGRAM_BUILD)/probe/bin
|
|||
AUDPAR_DATA := $(call programDataDir,AUDPAR)/test.mod
|
||||
AUDPAR_IIGS_DATA := $(call programDataDir,AUDPAR)/TEST.NTP
|
||||
|
||||
$(call programDataDir,AUDPAR)/TEST.NTP: $(REPO_DIR)/assets/test.mod $(REPO_DIR)/tools/joeymod/joeymod $(MOD_TO_NTP)
|
||||
$(call programDataDir,AUDPAR)/TEST.NTP $(call programDataDir,AUDIO)/TEST.NTP: $(REPO_DIR)/assets/test.mod $(REPO_DIR)/tools/joeymod/joeymod $(MOD_TO_NTP)
|
||||
@mkdir -p $(dir $@)
|
||||
$(REPO_DIR)/tools/joeymod/joeymod $< $@
|
||||
|
||||
|
|
@ -216,10 +229,10 @@ $(call programDataDir,AUDPAR)/%: $(REPO_DIR)/assets/%
|
|||
@mkdir -p $(dir $@)
|
||||
cp $< $@
|
||||
|
||||
# AUDIO loads DATA/test.mod and DATA/test.sfx from beside the program. The
|
||||
# IIgs plays its chip music through NTP and stages no MOD.
|
||||
# AUDIO loads DATA/test.mod and DATA/test.sfx from beside the program; the
|
||||
# IIgs plays the module as NinjaTrackerPlus's TEST.NTP (AUDPAR's rule).
|
||||
AUDIO_DATA := $(addprefix $(call programDataDir,AUDIO)/,test.mod test.sfx)
|
||||
AUDIO_IIGS_DATA :=
|
||||
AUDIO_IIGS_DATA := $(addprefix $(call programDataDir,AUDIO)/,TEST.NTP test.sfx)
|
||||
|
||||
$(call programDataDir,AUDIO)/%: $(REPO_DIR)/assets/%
|
||||
@mkdir -p $(dir $@)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
|
|||
|
||||
$(call requireTool,$(HAIKU32_ROOT)/cross/bin/i586-pc-haiku-g++,toolchain:haiku32)
|
||||
|
||||
# -fno-common: gcc 2.95 merges a program's uninitialised global into a
|
||||
# -fno-common: gcc 2.95 merges a program's uninitialized global into a
|
||||
# library one of the same name; every other port's compiler refuses the link.
|
||||
HOSTED_CC := $(HAIKU32_GCC2) $(HAIKU32_ROOT) gcc -fno-common
|
||||
HOSTED_CXX := $(HAIKU32_GCC2) $(HAIKU32_ROOT) g++
|
||||
|
|
|
|||
|
|
@ -141,11 +141,11 @@ $(LIB): $(LIB_OBJS)
|
|||
PROGRAM_FOLDERS := yes
|
||||
|
||||
# The file one program, $(1) its id, is linked to: the program file itself
|
||||
# unless the port packs it afterwards.
|
||||
# unless the port packs it afterward.
|
||||
hostedLinkOut = $(or $(call HOSTED_LINK_OUT,$(1)),$(call programBin,$(1)))
|
||||
|
||||
# One program, $(1) its id: compiled and linked in one step with the library
|
||||
# and the backend's libraries (and the maths library the mixer's tables use), its DATA too where the port packs it in, and its icon where
|
||||
# and the backend's libraries (and the math library the mixer's tables use), its DATA too where the port packs it in, and its icon where
|
||||
# the link takes it, then packed where the port packs it. -MMD leaves the
|
||||
# dependency file beside what is linked, for header edits; it is named after
|
||||
# the program only while the link compiles one source, so an icon reaches
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
# ID_LDFLAGS extra link flags (-s)
|
||||
# ID_PORTS the ports it builds for; every port when unset
|
||||
# ID_NAME its file name without the extension; by default the ID,
|
||||
# on the Amiga Capitalised (Hello), on DVX lower case
|
||||
# on the Amiga Capitalized (Hello), on DVX lower case
|
||||
# ID_FOLDER non-empty: it ships in a folder of its own, bin/<name>/,
|
||||
# with its DATA beside it (the Amiga, ST, DOS and X68000;
|
||||
# on the IIgs, DVX and macOS every program is packaged
|
||||
|
|
@ -44,8 +44,11 @@
|
|||
# ID_TITLE its title: DVX's window title (default: its Amiga-style
|
||||
# name), the PSP menu's, and a Mac app's display name
|
||||
# (default: its name)
|
||||
# ID_HELP DVX: the line of its help file (no apostrophes: the help is
|
||||
# ID_HELP DVX: the line of its help file and its package's summary
|
||||
# (at most 127 characters, and no apostrophes: both are
|
||||
# written through the shell in quotes)
|
||||
# ID_VERSION its version, major.minor.patch (DVX's package); by
|
||||
# default the project's PROGRAM_VERSION, else 1.0.0
|
||||
# ID_IIGS_OVERFLOW_CAP IIgs: link large overflow segments (clang-build.sh's
|
||||
# JOEY_OVERFLOW_CAP)
|
||||
# ID_IIGS_GAME_DISK IIgs: non-empty: it ships as its own bootable volume
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ while [ $# -gt 0 ]; do
|
|||
-MP)
|
||||
;;
|
||||
-lm)
|
||||
# R5 keeps the maths in libroot and has no libm.
|
||||
# R5 keeps the math in libroot and has no libm.
|
||||
if [ -z "$r5" ]; then
|
||||
args+=(-lm)
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ JOEY_MIN_HEAP="${JOEY_MIN_HEAP:-0}"
|
|||
# so this stays below 0x10000.
|
||||
#
|
||||
# DEFAULT 0 = overflow segments use --segment-cap (the historical
|
||||
# behaviour); a program opts in with ID_IIGS_OVERFLOW_CAP in its description
|
||||
# behavior); a program opts in with ID_IIGS_OVERFLOW_CAP in its description
|
||||
# (make/programs.mk). AGI links 5 segments instead of 19, STAXI 4 instead of
|
||||
# 26 (both now separate projects built on JoeyLib).
|
||||
#
|
||||
|
|
@ -252,7 +252,7 @@ JOEY_MIN_HEAP="${JOEY_MIN_HEAP:-0}"
|
|||
# The real chain: 26 bank-aligned segments starved the Memory Manager, so
|
||||
# NTP's 34K NewHandle failed, gNTPReady stayed 0 and every SFX call
|
||||
# short-circuited. IIgs builds were simply SILENT. Freeing that memory let
|
||||
# audio initialise, and a latent bug fired the first time NTPstreamsound
|
||||
# audio initialize, and a latent bug fired the first time NTPstreamsound
|
||||
# was ever reached (fixed in src/iigs/audio_full.c: SFX now requires a
|
||||
# running replayer, as jlpAudioStopSfx already did).
|
||||
#
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@
|
|||
# owcc:
|
||||
# -O2 -O3 owcc's include -oa, which assumes no global is ever reached
|
||||
# through a pointer (JoeyLib's are), and -on; this is -ox
|
||||
# (branch, inline, loop and scheduling optimisation, no stack
|
||||
# (branch, inline, loop and scheduling optimization, no stack
|
||||
# checks) and -ot instead
|
||||
# -MMD -MP Watcom's dependency file names the object and its source as
|
||||
# given (-ad, -adt, -add); each header is then made a target of
|
||||
# its own (-MP), so a deleted header does not stop make
|
||||
# -Wno-error dropped (owcc misreads it); -w is owcc's -w0
|
||||
# -lm dropped: the maths is in Watcom's C library
|
||||
# -lm dropped: the math is in Watcom's C library
|
||||
# A program (no -c): each source is compiled as above, the objects and
|
||||
# libraries are linked into a 32-bit image (NAME.rex, its stack STACK_BYTES)
|
||||
# and that is bound to the supervisor (WIN386.EXT), which makes NAME.EXE, a
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
# tiled2joey -- a Tiled map (TMX or TMJ) -> a tile sheet and maps.
|
||||
# tileBankExpect -- the tile-bank gates' expected pixels
|
||||
# (tests/host/tileBankStage.sh).
|
||||
# traceLogo -- a flat-colour PNG logo -> a layered SVG, through the
|
||||
# traceLogo -- a flat-color PNG logo -> a layered SVG, through the
|
||||
# potrace program: remakes assets/joeylibLogo.svg.
|
||||
# uberPerfTable -- every port's UBER log -> one Markdown table of ops/sec
|
||||
# against a reference port.
|
||||
|
|
@ -45,6 +45,15 @@ include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
|
|||
BUILD_DIR := $(JOEY_BUILD)/tools
|
||||
TOOLS_DIR := $(REPO_DIR)/tools
|
||||
|
||||
# A tool lands under its name in one rename. The ports' makes, run side by
|
||||
# side (make -j all), each ask this file for the tools they bake with, so two
|
||||
# can build one tool at once, and one ran it while another was still writing
|
||||
# it ("Permission denied"). Each links to a name of its own (the shell's
|
||||
# process id) and renames it into place: whoever runs the tool finds the old
|
||||
# one or the new one, whole.
|
||||
TOOL_TMP = $@.$$$$.tmp
|
||||
TOOL_LAND = mv -f $(TOOL_TMP) $@
|
||||
|
||||
# The tools of one C file (and any headers beside it) over tools/common (PNG,
|
||||
# DEFLATE, whole files, byte buffers): tools/<name>/<name>.c, or a test's
|
||||
# helper in tests/host.
|
||||
|
|
@ -92,7 +101,7 @@ spritebake-x68000: $(BUILD_DIR)/spritebake-x68000
|
|||
define commonToolRule
|
||||
$(BUILD_DIR)/$(1): $(2) $(wildcard $(dir $(2))*.h) $(TOOL_COMMON_SRC) $(TOOL_COMMON_HDRS)
|
||||
@mkdir -p $$(dir $$@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common $$(filter %.c,$$^) -o $$@ -lm
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common $$(filter %.c,$$^) -o $$(TOOL_TMP) -lm && $$(TOOL_LAND)
|
||||
endef
|
||||
$(foreach t,$(COMMON_TOOLS),$(eval $(call commonToolRule,$(t),$(TOOLS_DIR)/$(t)/$(t).c)))
|
||||
$(eval $(call commonToolRule,tileBankExpect,$(REPO_DIR)/tests/host/tileBankExpect.c))
|
||||
|
|
@ -102,35 +111,35 @@ $(eval $(call commonToolRule,tileBankExpect,$(REPO_DIR)/tests/host/tileBankExpec
|
|||
define gateToolRule
|
||||
$(BUILD_DIR)/$(1): $(GATE_DIR)/$(1).c $(GATE_COMMON_SRC) $(GATE_COMMON_HDRS) $(TOOL_COMMON_SRC) $(TOOL_COMMON_HDRS)
|
||||
@mkdir -p $$(dir $$@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common -I$(GATE_DIR)/common $$(filter %.c,$$^) -o $$@ -lm
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common -I$(GATE_DIR)/common $$(filter %.c,$$^) -o $$(TOOL_TMP) -lm && $$(TOOL_LAND)
|
||||
endef
|
||||
$(foreach t,$(GATE_TOOLS),$(eval $(call gateToolRule,$(t))))
|
||||
|
||||
$(MODLOOPEND_BIN): $(MODLOOPEND_SRC) $(SRC_CORE)/modStopMarker.h
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(SRC_CORE) $(MODLOOPEND_SRC) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -I$(SRC_CORE) $(MODLOOPEND_SRC) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
$(BUILD_DIR)/spritebake-dos: $(SPRITEBAKE_SRC) $(SRC_X86)/spriteEmitX86.c $(SPRITEBAKE_HDRS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_DOS $(SPRITEBAKE_INC) -I$(SRC_X86) $(filter %.c,$^) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_DOS $(SPRITEBAKE_INC) -I$(SRC_X86) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
$(BUILD_DIR)/spritebake-amiga: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_AMIGA $(SPRITEBAKE_INC) -I$(SRC_DIR)/amiga -I$(SRC_68K) $(filter %.c,$^) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_AMIGA $(SPRITEBAKE_INC) -I$(SRC_DIR)/amiga -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
# The X68000 runs the Amiga's planar 68k emitter unchanged (see
|
||||
# make/x68000.mk), so its bake is the same source under its own target byte.
|
||||
$(BUILD_DIR)/spritebake-x68000: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_X68000 $(SPRITEBAKE_INC) -I$(SRC_DIR)/x68000 -I$(SRC_68K) $(filter %.c,$^) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_X68000 $(SPRITEBAKE_INC) -I$(SRC_DIR)/x68000 -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
$(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_ATARIST $(SPRITEBAKE_INC) -I$(SRC_DIR)/atarist -I$(SRC_68K) $(filter %.c,$^) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_ATARIST $(SPRITEBAKE_INC) -I$(SRC_DIR)/atarist -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
$(BUILD_DIR)/spritebake-iigs: $(SPRITEBAKE_SRC) $(SRC_DIR)/iigs/spriteEmitIigs.c $(SPRITEBAKE_HDRS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_IIGS $(SPRITEBAKE_INC) $(filter %.c,$^) -o $@
|
||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_IIGS $(SPRITEBAKE_INC) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND)
|
||||
|
||||
clean-tools:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# pointers -- bound to Watcom's supervisor, so a program is one Windows 3.x
|
||||
# NE executable NAME.EXE, in bin/ or in its own bin/NAME/ with its DATA
|
||||
# beside it, calling USER, GDI, KERNEL and MMSYSTEM through the supervisor.
|
||||
# The frame is 8-bit indexed (a 256-colour DIB holding all 16 palettes) and
|
||||
# The frame is 8-bit indexed (a 256-color DIB holding all 16 palettes) and
|
||||
# the sound 22.05 kHz mono, what Windows 3.1 and a 386 handle well. The port
|
||||
# makes the program's folder the current directory when the window opens.
|
||||
|
||||
|
|
@ -36,8 +36,8 @@ HOSTED_DEFINES := -DJOEYLIB_PLATFORM_HOSTED -DJOEYLIB_HOSTED_WIN16 -DHOSTED_F
|
|||
HOSTED_SDK_CFLAGS := -I$(WIN16_WATCOM)/h/win -I$(SRC_X86)
|
||||
HOSTED_LINK :=
|
||||
PROGRAM_EXT := .EXE
|
||||
# Its icon, the one Program Manager shows and the window shows minimised:
|
||||
# 32x32 in the 16 VGA colours, bound in from a resource script.
|
||||
# Its icon, the one Program Manager shows and the window shows minimized:
|
||||
# 32x32 in the 16 VGA colors, bound in from a resource script.
|
||||
HOSTED_ICON = $(call programIconDir,$(1))/icon.rc
|
||||
HOSTED_ICON_BAKE = $(call hostedIconScript,$(1),ico16)
|
||||
HOSTED_ICON_LINK = $(call HOSTED_ICON,$(1))
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ HOSTED_SDK_CFLAGS := -I$(SRC_X86)
|
|||
HOSTED_LINK := -mwindows -static -lwinmm -lgdi32 -Wl,--subsystem,windows:4.0 -Wl,--major-os-version,4 -Wl,--minor-os-version,0
|
||||
PROGRAM_EXT := .EXE
|
||||
# Its icon, the one Explorer and the window show: 16, 32 and 48 pixels, each
|
||||
# in 256 colours and in 32-bit, compiled into a resource object it links.
|
||||
# in 256 colors and in 32-bit, compiled into a resource object it links.
|
||||
WIN32_WINDRES := $(patsubst %-clang,%-windres,$(WIN32_CC))
|
||||
HOSTED_ICON = $(call programIconDir,$(1))/icon.o
|
||||
HOSTED_ICON_BAKE = $(call hostedIconScript,$(1),ico) && $(WIN32_WINDRES) $(call programIconDir,$(1))/icon.rc -o $(call HOSTED_ICON,$(1))
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
# log lands directly in build/<build>/bin/joeylog.txt (for the ST build
|
||||
# where build/tools/uberPerfTable expects it). A copy is kept as
|
||||
# joeylog-MACHINE.txt beside it, so runs on several machines can be
|
||||
# compared afterwards.
|
||||
# compared afterward.
|
||||
#
|
||||
# scripts/bench-atarist.sh # the ST build on a plain ST, default 90s timeout
|
||||
# scripts/bench-atarist.sh megast # the ST build on a Mega ST
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
#
|
||||
# Stages a scratch DVX (scripts/lib/dvxGate.sh) whose desktop is UBER, boots
|
||||
# it in DOSBox Staging under Xvfb, and waits for UBER's "press any key to
|
||||
# exit" line in its joeylog.txt -- which a DVX program writes to its settings
|
||||
# folder, CONFIG\APPS\<VENDOR>\UBER on the staged drive. The log is copied to
|
||||
# exit" line in its joeylog.txt -- which a DVX program writes to its user
|
||||
# folder, USER\APPS\<VENDOR>\UBER on the staged drive. The log is copied to
|
||||
# build/dvx/bin/joeylog.txt (build/tools/diffUberHashes and
|
||||
# build/tools/uberPerfTable read it from there) and DOSBox is stopped.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# Prints the UBER rows and leaves the full log in build/x68000/joeylog.txt.
|
||||
#
|
||||
# Takes a few minutes. The run EXITS ON COMPLETION, not on a frame budget: UBER
|
||||
# ends by clearing the whole stage to colour index 2 and presenting it before it
|
||||
# ends by clearing the whole stage to color index 2 and presenting it before it
|
||||
# blocks in jlWaitForAnyKey, so the Lua below watches GVRAM for that screen.
|
||||
# (It used to burn a flat 300,000 frames -- ~90 minutes of emulated time -- a
|
||||
# budget sized back when this port still drew through the slow generic renderer.
|
||||
|
|
@ -70,13 +70,13 @@ x68kDataDisk "$work/data.xdf" || exit 1
|
|||
x68kXdf add "$work/data.xdf" "$work/UBER.X" UBER.X >/dev/null || exit 1
|
||||
|
||||
# Completion-driven exit. UBER's last act before jlWaitForAnyKey is
|
||||
# jlSurfaceClear(jlgStage, 2) + jlStagePresent, so an all-colour-2 stage in GVRAM
|
||||
# jlSurfaceClear(jlgStage, 2) + jlStagePresent, so an all-color-2 stage in GVRAM
|
||||
# is the "hashes are computed and flushed" signal. The benchmark's own
|
||||
# jlSurfaceClear/jlStagePresent ops can paint that colour for an instant, so the
|
||||
# jlSurfaceClear/jlStagePresent ops can paint that color for an instant, so the
|
||||
# reading only counts once it has held for GREEN_STABLE frames -- the finished
|
||||
# screen never changes again, a mid-benchmark frame never lasts.
|
||||
#
|
||||
# jlLogFlush only fflush()es; the directory entry is finalised by the atexit
|
||||
# jlLogFlush only fflush()es; the directory entry is finalized by the atexit
|
||||
# fclose. So the keypress that releases jlWaitForAnyKey still has to be posted,
|
||||
# and MAME must not be killed until UBER has returned to Human68k -- hence the
|
||||
# repeated posts and the EXIT_SETTLE wait before exiting.
|
||||
|
|
@ -94,11 +94,11 @@ local EXIT_SETTLE = 900 -- keypress -> fclose -> back at the Human68k prompt
|
|||
-- Where the present puts pixels, which depends on the port's
|
||||
-- X68K_PRESENT_TEXT switch:
|
||||
-- text (default) -- crtmod 12, the four 1bpp TEXT planes at \$E00000, 128
|
||||
-- bytes per row, planes 128 KB apart. A pixel's colour is
|
||||
-- bytes per row, planes 128 KB apart. A pixel's color is
|
||||
-- one bit from each plane at the same offset.
|
||||
-- gfx -- crtmod 12, 256-colour GRAPHICS plane at \$C00000, one
|
||||
-- gfx -- crtmod 12, 256-color GRAPHICS plane at \$C00000, one
|
||||
-- 16-bit word per pixel, 512 words per row.
|
||||
-- The 320x200 stage is centred at origin (96, 156) either way.
|
||||
-- The 320x200 stage is centered at origin (96, 156) either way.
|
||||
local presentPlane = "$PRESENT_PLANE"
|
||||
-- The text plane holds two pages, and the port flips between them with the
|
||||
-- text Y scroll (CRTC R11, which reads back): rows are counted from the page
|
||||
|
|
|
|||
|
|
@ -45,4 +45,4 @@ if [ "$fails" != 0 ]; then
|
|||
echo "check-fileroots: FAIL" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "check-fileroots: PASS (roots honoured under JL_HAS_FILE_ROOTS; the flagless layout unchanged)"
|
||||
echo "check-fileroots: PASS (roots honored under JL_HAS_FILE_ROOTS; the flagless layout unchanged)"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# every port that mixes .MOD in software plays through (DOS, the ST, the
|
||||
# X68000, and the hosted ports). Builds tests/host/modPlayerHost.c, which
|
||||
# includes the player, with the host cc and the address and undefined-
|
||||
# behaviour sanitizers, and runs it: the Protracker behaviours the player
|
||||
# behavior sanitizers, and runs it: the Protracker behaviors the player
|
||||
# keeps, where a song ends, the 8-bit and 16-bit outputs, and mangled
|
||||
# modules played without reading outside the player's copy. When the
|
||||
# Amiga's PTPlayer is installed, its period and vibrato tables are handed
|
||||
|
|
@ -81,4 +81,4 @@ if [ "$status" -ne 0 ]; then
|
|||
echo "$label: FAIL - ${counts:-harness error} checks pass"
|
||||
exit 1
|
||||
fi
|
||||
echo "$label: PASS ($counts checks: Protracker's behaviours, song ends, both outputs, and mangled modules, under the sanitizers)"
|
||||
echo "$label: PASS ($counts checks: Protracker's behaviors, song ends, both outputs, and mangled modules, under the sanitizers)"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#
|
||||
# machine = svga_s3 an S3 Trio64, the card the S3 Trio64V+
|
||||
# Windows driver the image is set up with
|
||||
# drives (800x600, 256 colours)
|
||||
# drives (800x600, 256 colors)
|
||||
# memsize = 16 16 MB, plenty for Windows 3.11; the image
|
||||
# runs without a swap file
|
||||
# cputype = 386 the port's floor is a 386: an instruction a
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
# taken through its boot loader's menu to the
|
||||
# fail-safe (VESA) video mode BEOS_VIDEO_MODE:
|
||||
# its own drivers know none of QEMU's cards,
|
||||
# and without it the screen is 16 greys.
|
||||
# and without it the screen is 16 grays.
|
||||
# Serial port: WORK/serial.log, QMP:
|
||||
# WORK/qmp.sock
|
||||
# beStop stop QEMU
|
||||
|
|
@ -61,9 +61,16 @@ BE_AUDIODEV="${BE_AUDIODEV:-none,id=beSound}"
|
|||
# Haiku's machine: memory (MB) and processors.
|
||||
HAIKU_VM_MEMORY_MB=1024
|
||||
HAIKU_VM_CPUS=2
|
||||
# BeOS R5's machine: memory (MB) and processor.
|
||||
# BeOS R5's machine: memory (MB) and processor, and its time stamp counter
|
||||
# counted in instructions (icount): R5 times itself by the TSC, rated at
|
||||
# boot, and the host's (5 GHz here) overflowed the rating -- it said -842 MHz
|
||||
# one boot and -735 the next, and the clock ran about twice as fast as the
|
||||
# host's, a different amount each boot. With shift=auto the TSC rates 1000
|
||||
# MHz and the clock keeps the host's time to about 5%, for a boot about 3 s
|
||||
# longer.
|
||||
BEOS_VM_MEMORY_MB=256
|
||||
BEOS_VM_CPU="pentium2"
|
||||
BEOS_VM_ICOUNT="${BEOS_VM_ICOUNT-shift=auto}" # empty: no icount
|
||||
# Its boot loader's menu: open it (space, pressed while the loader starts),
|
||||
# go to "Select fail-safe video mode", choose the mode BEOS_VIDEO_ROWS rows
|
||||
# below Default in the mode list (the list QEMU 11.1.2's VGA BIOS gives:
|
||||
|
|
@ -221,7 +228,7 @@ beStartBeos() {
|
|||
local presses=()
|
||||
local rows=()
|
||||
shift 2
|
||||
"$BE_QEMU" -machine pc -cpu "$BEOS_VM_CPU" -m "$BEOS_VM_MEMORY_MB" \
|
||||
"$BE_QEMU" -machine pc -cpu "$BEOS_VM_CPU" -m "$BEOS_VM_MEMORY_MB" ${BEOS_VM_ICOUNT:+-icount "$BEOS_VM_ICOUNT"} \
|
||||
-drive "file=$BEOS_VM_DIR/floppy.img,format=raw,if=floppy,readonly=on" \
|
||||
-drive "file=$BEOS_VM_DIR/system.img,format=raw,index=0,media=disk,snapshot=on" \
|
||||
-drive "file=$disk,format=raw,index=1,media=disk" -boot a -vga std \
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ DVX_PROBE_DIR="${DVX_PROBE_DIR:-$JOEY_REPO/build/dvx/probe}"
|
|||
# log.
|
||||
DVX_VENDOR="${DVX_VENDOR:-$(make -s -f "$JOEY_REPO/make/dvx.mk" --eval 'dvxVendor: ; @echo $(DVX_VENDOR)' dvxVendor 2> /dev/null)}"
|
||||
DVX_LOG_NAME=joeylog.txt
|
||||
# The port's own settings file in a program's settings folder, and its
|
||||
# The port's own settings file in a program's user folder, and its
|
||||
# full-screen and square-pixels switches (src/dvx/hal.c).
|
||||
DVX_SETTINGS_FILE=JOEYLIB.INI
|
||||
DVX_SETTINGS_SECTION=display
|
||||
|
|
@ -33,9 +33,9 @@ DVX_FULL_SCREEN_LOG="Video: full screen 320x200 at 8 bpp"
|
|||
# DVX.LOG's line when a program cannot be loaded (a missing file, a name
|
||||
# that is not 8.3, an unresolved import).
|
||||
DVX_LOAD_FAILED_LOG="DXE load failed"
|
||||
# The runtime parts of toolchains/opt/dvx (all but the SDK).
|
||||
DVX_RUNTIME="dvx.exe config system libs widgets apps"
|
||||
# The desktop the gates run: 640x480 in 16-bit colour, DVX's own DOS test
|
||||
# The runtime parts of toolchains/opt/dvx (all but the SDK and SETUP.EXE).
|
||||
DVX_RUNTIME="dvx.exe system libs widgets apps shared"
|
||||
# The desktop the gates run: 640x480 in 16-bit color, DVX's own DOS test
|
||||
# mode, where a program's window shows the stage at 1x. A caller may set
|
||||
# others before dvxStage (joey-run shows 1024x768, the stage at 3x).
|
||||
DVX_VIDEO_W=${DVX_VIDEO_W:-640}
|
||||
|
|
@ -70,7 +70,8 @@ dvxNeedBuild() {
|
|||
# APPDIRs (each a folder holding NAME.app, from dvxAppDir or anywhere else),
|
||||
# installed as apps/VENDOR/NAME. The first one is the desktop. DIR may hold
|
||||
# an earlier staging: the runtime and the programs are copied over it, and
|
||||
# what DVX wrote there (its settings, the programs' CONFIG folders) stays.
|
||||
# what DVX wrote there (its user folder, USER\: its settings and log, and
|
||||
# each program's) stays.
|
||||
dvxStage() {
|
||||
local dir="$1"
|
||||
local part
|
||||
|
|
@ -87,21 +88,21 @@ dvxStage() {
|
|||
cp -a "$app" "$dir/DVX/apps/$DVX_VENDOR/"
|
||||
done
|
||||
name="$(basename "$1")"
|
||||
dvxIniSet "$dir/DVX/config/DVX.INI" "apps/$DVX_VENDOR/$name/$name.app"
|
||||
dvxIniSet "$(dvxIni "$dir")" "apps/$DVX_VENDOR/$name/$name.app"
|
||||
}
|
||||
|
||||
|
||||
# dvxSettings DIR PROGRAM SWITCH... -- have PROGRAM start with the port's
|
||||
# own display switches on (DVX_SETTINGS_FULL_SCREEN, DVX_SETTINGS_SQUARE) in
|
||||
# the DVX staged in DIR: its JOEYLIB.INI, [display], in the program's
|
||||
# settings folder, as the Game menu leaves it. DOS names are case-blind, so
|
||||
# an existing folder of any case is used.
|
||||
# the DVX staged in DIR: its JOEYLIB.INI, [display], in the program's user
|
||||
# folder, as the Game menu leaves it. DOS names are case-blind, so an
|
||||
# existing folder of any case is used.
|
||||
dvxSettings() {
|
||||
local conf
|
||||
local key
|
||||
conf="$(find "$1/DVX" -ipath "*/config/apps/$DVX_VENDOR/$2" -type d -print -quit 2>/dev/null)"
|
||||
conf="$(find "$1/DVX" -ipath "*/user/apps/$DVX_VENDOR/$2" -type d -print -quit 2>/dev/null)"
|
||||
if [ -z "$conf" ]; then
|
||||
conf="$1/DVX/config/apps/$DVX_VENDOR/$2"
|
||||
conf="$(dvxUserDir "$1")/apps/$DVX_VENDOR/$2"
|
||||
mkdir -p "$conf"
|
||||
fi
|
||||
printf '[%s]\r\n' "$DVX_SETTINGS_SECTION" > "$conf/$DVX_SETTINGS_FILE"
|
||||
|
|
@ -118,6 +119,37 @@ dvxFullScreen() {
|
|||
}
|
||||
|
||||
|
||||
# dvxUserDir DIR -- print the user folder (USER\) of the DVX staged in DIR,
|
||||
# where DVX writes everything (its DVX.INI and DVX.LOG, and each program's
|
||||
# settings and saves), making it when DVX has not yet. DOS names are
|
||||
# case-blind, so a folder of any case is DVX's.
|
||||
dvxUserDir() {
|
||||
local user
|
||||
user="$(find "$1/DVX" -mindepth 1 -maxdepth 1 -type d -iname user -print -quit 2>/dev/null)"
|
||||
if [ -z "$user" ]; then
|
||||
user="$1/DVX/user"
|
||||
mkdir -p "$user"
|
||||
fi
|
||||
printf '%s\n' "$user"
|
||||
}
|
||||
|
||||
|
||||
# dvxIni DIR -- print the DVX.INI the DVX staged in DIR starts with: the user
|
||||
# folder's, made from the one DVX ships (SYSTEM\DVX.INI) when it is not there
|
||||
# yet, as DVX's first start makes it.
|
||||
dvxIni() {
|
||||
local user
|
||||
local ini
|
||||
user="$(dvxUserDir "$1")"
|
||||
ini="$(find "$user" -maxdepth 1 -type f -iname dvx.ini -print -quit)"
|
||||
if [ -z "$ini" ]; then
|
||||
ini="$user/DVX.INI"
|
||||
cp "$(find "$1/DVX/system" -maxdepth 1 -type f -iname dvx.ini -print -quit)" "$ini"
|
||||
fi
|
||||
printf '%s\n' "$ini"
|
||||
}
|
||||
|
||||
|
||||
# dvxIniSet INI DESKTOP -- DVX.INI for a run: DESKTOP as the desktop program,
|
||||
# no first-run Welcome window, the gate video mode, and no desktop sounds (a
|
||||
# recording gate must hear only the program). DOS line ends are kept.
|
||||
|
|
@ -192,10 +224,10 @@ EOF
|
|||
|
||||
|
||||
# dvxProgramLog DIR PROGRAM -- print the path of PROGRAM's log in the staged
|
||||
# DVX: in its settings folder, CONFIG\ plus the folder the .app is in (DOS
|
||||
# DVX: in its user folder, USER\APPS\ plus the folder the .app is in (DOS
|
||||
# names are upper case on the host). Prints nothing while there is none.
|
||||
dvxProgramLog() {
|
||||
find "$1/DVX" -ipath "*/config/apps/$DVX_VENDOR/$2/$DVX_LOG_NAME" -print -quit 2>/dev/null
|
||||
find "$1/DVX" -ipath "*/user/apps/$DVX_VENDOR/$2/$DVX_LOG_NAME" -print -quit 2>/dev/null
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -235,7 +267,7 @@ dvxKeysTyped() {
|
|||
# place a program that failed to load is explained.
|
||||
dvxDvxLog() {
|
||||
local log
|
||||
log="$(find "$1/DVX" -maxdepth 1 -iname dvx.log -print -quit)"
|
||||
log="$(find "$1/DVX" -maxdepth 2 -ipath "*/user/dvx.log" -print -quit)"
|
||||
[ -n "$log" ] && tr -d '\r' < "$log" | tail -"${2:-15}"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -245,7 +277,7 @@ dvxDvxLog() {
|
|||
# ends); FILE is left out when there is none.
|
||||
dvxDvxLogCopy() {
|
||||
local log
|
||||
log="$(find "$1/DVX" -maxdepth 1 -iname dvx.log -print -quit)"
|
||||
log="$(find "$1/DVX" -maxdepth 2 -ipath "*/user/dvx.log" -print -quit)"
|
||||
[ -n "$log" ] && tr -d '\r' < "$log" > "$2"
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
# go first, which must leave Up down (src/core/inputInternal.h,
|
||||
# INPUT_KEY_SECOND). Each step holds its keys (letting go of every other),
|
||||
# waits KEYS_SETTLE_SECONDS, and reads KEYS's Return, Ctrl, Alt, Up, Down,
|
||||
# Left, and Right cells (examples/keys) as seven digits, keysCells's.
|
||||
# Left, and Right cells (examples/keys) as seven digits, keysCells's --
|
||||
# again every KEYS_SETTLE_SECONDS until they read right or KEYS_WAIT_SECONDS
|
||||
# have gone, so a machine whose frame lags the key passes and a wrong state
|
||||
# that stays still fails.
|
||||
#
|
||||
# The gate sets KEYS_LABEL (its name, for the lines it prints) and defines,
|
||||
# by the key names below:
|
||||
|
|
@ -25,6 +28,7 @@
|
|||
# ralt.
|
||||
|
||||
KEYS_SETTLE_SECONDS="${KEYS_SETTLE_SECONDS:-0.5}"
|
||||
KEYS_WAIT_SECONDS="${KEYS_WAIT_SECONDS:-5}"
|
||||
KEYS_FAILS=""
|
||||
KEYS_STEPS=0
|
||||
KEYS_HELD=()
|
||||
|
|
@ -37,6 +41,7 @@ keysHold() {
|
|||
local want="$2"
|
||||
local key
|
||||
local lit
|
||||
local deadline
|
||||
shift 2
|
||||
for key in "${KEYS_HELD[@]}"; do
|
||||
[[ " $* " == *" $key "* ]] || keysRelease "$key"
|
||||
|
|
@ -45,8 +50,14 @@ keysHold() {
|
|||
[[ " ${KEYS_HELD[*]} " == *" $key "* ]] || keysPress "$key"
|
||||
done
|
||||
KEYS_HELD=("$@")
|
||||
sleep "$KEYS_SETTLE_SECONDS"
|
||||
lit=$(keysRead)
|
||||
deadline=$((SECONDS + KEYS_WAIT_SECONDS))
|
||||
while true; do
|
||||
sleep "$KEYS_SETTLE_SECONDS"
|
||||
lit=$(keysRead)
|
||||
if [ "$lit" = "$want" ] || [ "$SECONDS" -ge "$deadline" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
KEYS_STEPS=$((KEYS_STEPS + 1))
|
||||
echo "$KEYS_LABEL: $name: Return/Ctrl/Alt/Up/Down/Left/Right $lit"
|
||||
[ "$lit" = "$want" ] || KEYS_FAILS="${KEYS_FAILS:+$KEYS_FAILS; }$name: lit $lit, want $want"
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ win16Stage() {
|
|||
# display the first time a program uses it and keeps the result in WIN.INI
|
||||
# (WIN16_WING_PROFILE), which on a copy made afresh each run would be every
|
||||
# run: about 165 s at 386SX-16 speed. Its result for this Windows (the S3
|
||||
# driver at 800x600 in 256 colours), taken at that speed, goes in instead,
|
||||
# driver at 800x600 in 256 colors), taken at that speed, goes in instead,
|
||||
# under WinG's key for the display: the driver, the mode, the driver's
|
||||
# version, the driver file's date as DOS keeps it (year, month and day in
|
||||
# one 16-bit word, in hex) and WinG's version. The copy is dated the day it
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ local sampler = coroutine.create(function()
|
|||
if pc >= base and pc < base + textSize then
|
||||
-- The EXACT offset, not a 64-byte bucket. Buckets straddle
|
||||
-- function boundaries, and a bucket credited to the symbol at
|
||||
-- its start hands one function's samples to its neighbour:
|
||||
-- its start hands one function's samples to its neighbor:
|
||||
-- that is how a vblank wait came out looking like an
|
||||
-- expensive input poll.
|
||||
key = pc - base
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
# pulseEnsureServer -- locate the audio server, or fail with a real reason.
|
||||
#
|
||||
# A sanitised environment (cron, CI, `env -i`, a systemd unit without the
|
||||
# A sanitized environment (cron, CI, `env -i`, a systemd unit without the
|
||||
# user session) has no XDG_RUNTIME_DIR, and pactl then reports "no server"
|
||||
# even though one is running -- measured: `env -i make verify-audio` skipped
|
||||
# both recording gates for exactly this. The socket lives at a predictable
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# See LICENSE.md for license information.
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# verify-all.sh - Run JoeyLib's gates, one kind or all of them, and summarise.
|
||||
# verify-all.sh - Run JoeyLib's gates, one kind or all of them, and summarize.
|
||||
#
|
||||
# Driven by `make verify` (all), `make verify-audio` and `make verify-serial`.
|
||||
#
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
# repainted, and the frame against the stage
|
||||
# x68000: the SCB -- every text-palette reload of a 147-change
|
||||
# screen lands in the blanking before its band, timed on the beam
|
||||
# atarist and ste: the palette -- every shifter colour the ST
|
||||
# atarist and ste: the palette -- every shifter color the ST
|
||||
# build writes is the 3-bit truncation, the STe build's (on an
|
||||
# STe and a Mega STe) the exact 4-bit $0RGB
|
||||
# amiga: the palette -- palette and SCB changes present one a
|
||||
|
|
@ -100,12 +100,15 @@
|
|||
# virtualPad; needs /dev/uinput), then unplugged;
|
||||
# sdl3 again with two joysticks SDL cannot map
|
||||
# web the page's Gamepad API, stood in for by the driver
|
||||
# psp: no keyboard; its d-pad, nub, cross and circle as joystick
|
||||
# 0 (read back from examples/joy's display, resting while the
|
||||
# keyboard on the screen is open), Start as Return (held, and
|
||||
# typed: examples/keys's received row), Select as Escape, and the
|
||||
# keyboard on the screen (F5 and Shift lit in examples/keys, a
|
||||
# string typed on it), pressed through PPSSPP's input script
|
||||
# psp: no keyboard or mouse; its d-pad, cross and circle as
|
||||
# joystick 0 (read back from examples/joy's display, resting while
|
||||
# the keyboard on the screen is open), the nub as the mouse and L
|
||||
# and R as its buttons (examples/keys's mouse row: the dead zone,
|
||||
# the edges, a half-tilt speed, still under the keyboard), Start
|
||||
# as Return (held, and typed: examples/keys's received row),
|
||||
# Select as Escape, and the keyboard on the screen (F5 and Shift
|
||||
# lit in examples/keys, a string typed on it), pressed through
|
||||
# PPSSPP's input script
|
||||
# audio twelve gates, two kinds (the IIgs, X68000 and PSP on AUDIOPRB,
|
||||
# AUDIO pressing its own keys; the rest drive AUDIO's keys):
|
||||
# iigs DOC wavetable + sound IRQ vector (register tap)
|
||||
|
|
@ -129,7 +132,7 @@
|
|||
# one-shot module and ticks; its sound is recorded and graded
|
||||
# against what it logged (scripts/verify-audio-conformance.sh,
|
||||
# tests/gate/audioGate.c): pitch, level per attenuation
|
||||
# step, noise rate, mix balance, centring and pan where the port
|
||||
# step, noise rate, mix balance, centering and pan where the port
|
||||
# is stereo, the module's end, the tick rate. Every port that
|
||||
# records sound: all but beos, whose VM has no sound card
|
||||
# serial the host check of the shared layer, then each port's loopback
|
||||
|
|
@ -151,6 +154,8 @@
|
|||
# R5's VM has no serial ports)
|
||||
# mouse x68000: deltas, clamps and buttons through the IOCS mouse
|
||||
# (and with serial streaming both ways on the same SCC)
|
||||
# iigs: the one-button ADB mouse has two: a click is left, and a
|
||||
# Ctrl-click right until it is let go (examples/keys's mouse row)
|
||||
# save iigs: a SAVES/ blob written and read back through GS/OS
|
||||
# sdl3: SAVE's launch count beside the program after two runs
|
||||
# web: SAVE's launch count back from IndexedDB after a reload
|
||||
|
|
@ -177,7 +182,7 @@
|
|||
# amiga icon.library reads Draw.info's kind (a tool, its
|
||||
# stack) and draws it, and a Workbench start runs the
|
||||
# program in its own folder
|
||||
# win16 the program minimised on Windows 3.11's desktop
|
||||
# win16 the program minimized on Windows 3.11's desktop
|
||||
# win32 the window's icon as Wine hands it to X
|
||||
# sdl3 the window's icon as SDL hands it to X
|
||||
# dvx Program Manager
|
||||
|
|
@ -368,6 +373,7 @@ serial haiku serial scripts/verify-be-serial.sh haiku
|
|||
serial haiku32 serial scripts/verify-be-serial.sh haiku32
|
||||
mouse x68000 mouse scripts/verify-x68000-mouse.sh
|
||||
mouse x68000 sermouse scripts/verify-x68000-serial-mouse.sh
|
||||
mouse iigs mouse scripts/verify-iigs-mouse.sh
|
||||
save iigs save make -s -f make/iigs.mk iigs-verify-save || exit 1
|
||||
save sdl3 save scripts/verify-sdl3-save.sh
|
||||
save web save scripts/verify-web-save.sh
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
#
|
||||
# Firing the SFX into a paused song is the point: mixed under the music an
|
||||
# 8-bit one-shot barely moves the total and "did the SFX play" becomes a
|
||||
# judgement call, while against silence it is unambiguous. Coexistence is
|
||||
# still covered, because the music has to come back afterwards on the same
|
||||
# judgment call, while against silence it is unambiguous. Coexistence is
|
||||
# still covered, because the music has to come back afterward on the same
|
||||
# hardware. Measured margins: music ~5700, paused ~240, sfx ~1170.
|
||||
#
|
||||
# Keys are HELD (keydown, pause, keyup), not tapped: the demo tests
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
# Amiga's own icon.library, and the program started from it.
|
||||
#
|
||||
# make bakes Name.info beside every Amiga program (tools/iconBake info:
|
||||
# a tool's icon in Workbench's four colours, with the stack it starts
|
||||
# a tool's icon in Workbench's four colors, with the stack it starts
|
||||
# with). WBLAUNCH (examples/wblaunch) runs under FS-UAE on an A500 with
|
||||
# PALPROBE and its icon in a folder of their own, Prog/. It reads the icon
|
||||
# with GetDiskObject -- it must be a tool's, of AMIGA_ICON_STACK bytes of
|
||||
# stack and the baked size -- and draws it; the screenshot must show every
|
||||
# pixel of the icon the file holds, in Workbench's colours. Then it starts
|
||||
# pixel of the icon the file holds, in Workbench's colors. Then it starts
|
||||
# PALPROBE as Workbench starts a tool, from RAM:, and its message must come
|
||||
# back with PALPROBE's log, which opens in the current directory, in Prog/:
|
||||
# libnix's start-up moves into the folder Workbench names.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ joeyNeed HATARI hatari
|
|||
joeyNeed EMUTOS_IMG emutos
|
||||
|
||||
build="${1:-atarist}"
|
||||
# The ST build writes 3-bit colours on any machine (joyScreen --st).
|
||||
# The ST build writes 3-bit colors on any machine (joyScreen --st).
|
||||
case "$build" in
|
||||
atarist) machine="${2:-st}"; JOY_SCREEN_ARGS=(--st) ;;
|
||||
ste) machine="${2:-ste}" ;;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
#
|
||||
# verify-atarist-palette.sh - The ST family's palette, read off the shifter.
|
||||
#
|
||||
# JoeyLib colours are 12-bit $0RGB. A plain ST's shifter has 3 bits per
|
||||
# JoeyLib colors are 12-bit $0RGB. A plain ST's shifter has 3 bits per
|
||||
# channel, so it keeps each channel's top three bits; an STe's has 4, with
|
||||
# the channel's LOW bit in bit 3 of the register nibble. The ST build (make
|
||||
# atarist) must write the 3-bit truncation -- on every machine, an STe
|
||||
# included, since it never uses STe hardware -- and the STe build (make
|
||||
# ste) the exact 4-bit colour.
|
||||
# ste) the exact 4-bit color.
|
||||
#
|
||||
# Subject: the PATTERN example -- eight bands, each its own palette, each
|
||||
# palette a gradient whose entry i has i in some of its channels. So every
|
||||
|
|
@ -56,7 +56,7 @@ for machine in $machines; do
|
|||
mkdir -p "$work/c"
|
||||
cp "$prog" "$work/c/"
|
||||
# The trace is filtered as it is produced: only palette register writes
|
||||
# from the application, not TOS's ($Exxxxx) or Hatari's other colour
|
||||
# from the application, not TOS's ($Exxxxx) or Hatari's other color
|
||||
# bookkeeping. --run-vbls ends the run; timeout is the backstop.
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout -s KILL "$wall" "$HATARI" \
|
||||
--machine "$machine" \
|
||||
|
|
@ -76,7 +76,7 @@ for machine in $machines; do
|
|||
function channel(v) {
|
||||
return build == "atarist" ? rshift(v, 1) : or(rshift(v, 1), lshift(and(v, 1), 3))
|
||||
}
|
||||
# A set of colour words as Python prints a sorted list of their hex.
|
||||
# A set of color words as Python prints a sorted list of their hex.
|
||||
function hexList(set, count, names, i, n, out) {
|
||||
n = 0
|
||||
for (i = 0; i < count; i++) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
# on the left).
|
||||
# Checks a port's hardware cannot pass are graded SKIP, by name: the
|
||||
# Amiga's tone and voices each play on one Paula channel, which feeds one
|
||||
# side only, so none of them can be centred (its centred sound effects
|
||||
# side only, so none of them can be centered (its centered sound effects
|
||||
# take a channel each side and are graded).
|
||||
# A port whose chip cannot land every 2 dB step gets a level tolerance of
|
||||
# its own: the YM2149's sixteen levels are about 3.5 dB apart, so
|
||||
|
|
@ -371,7 +371,7 @@ LUA
|
|||
|
||||
# A scratch DVX in DOSBox Staging with a Sound Blaster 16, AUDPAR its
|
||||
# desktop (scripts/lib/dvxGate.sh), recorded as the DVX audio gate records
|
||||
# it. DVX stays up when AUDPAR ends; the log is in AUDPAR's settings folder.
|
||||
# it. DVX stays up when AUDPAR ends; the log is in AUDPAR's user folder.
|
||||
runDvx() {
|
||||
local display=":$((90 + RANDOM % 9))"
|
||||
local dosPid
|
||||
|
|
@ -541,7 +541,7 @@ esac
|
|||
case "$port" in
|
||||
atarist) flags+=(--level-tolerance 1.75) ;;
|
||||
ste) flags+=(--level-tolerance 1.95) ;;
|
||||
amiga) flags+=(--skip "tone centred" --skip "voice 0 centred" --skip "voice 1 centred" --skip "voice 2 centred") ;;
|
||||
amiga) flags+=(--skip "tone centered" --skip "voice 0 centered" --skip "voice 1 centered" --skip "voice 2 centered") ;;
|
||||
esac
|
||||
"$grader" conformance "$port" "$work/log.txt" "$work/out.wav" "${flags[@]}" > "$work/grade.txt"
|
||||
status=$?
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# attribute is there (attributeSeconds at most), stops it, and opens a
|
||||
# Tracker window on its folder: a screendump must hold the baked icon, one
|
||||
# size or the other, every pixel it leaves opaque in the BeOS palette's
|
||||
# colour (give or take TOLERANCE: BeOS R5 runs in 16-bit colour).
|
||||
# color (give or take TOLERANCE: BeOS R5 runs in 16-bit color).
|
||||
#
|
||||
# Usage: scripts/verify-be-icon.sh [haiku|haiku32|beos] (default haiku)
|
||||
# Env: BE_ICON_KEEP keep the last screendump as build/PORT/be-icon.png
|
||||
|
|
@ -36,14 +36,17 @@ program=$(joeyExampleProgram draw "$port")
|
|||
iconDir=$repo/build/$port/icon/DRAW
|
||||
[ -x "$program" ] && [ -f "$iconDir/icon32.raw" ] && [ -f "$iconDir/icon16.raw" ] || { echo "$label: build DRAW first (make $port)" >&2; exit 2; }
|
||||
bootSeconds=120
|
||||
# Seconds the gate looks for the icon in Tracker's window, and the job keeps
|
||||
# the machine up after (longer: the screendumps must not outlast it).
|
||||
lookSeconds=90
|
||||
holdSeconds=150
|
||||
# Seconds DRAW has to give itself its icon attribute, and the attributes
|
||||
# then have to settle.
|
||||
attributeSeconds=60
|
||||
settleSeconds=5
|
||||
# Seconds the gate looks for the icon in Tracker's window -- past the job's
|
||||
# wait for the attribute and the settling, by the time Tracker may take to
|
||||
# open its window -- and the job keeps the machine up after (longer: the
|
||||
# screendumps must not outlast it).
|
||||
trackerSeconds=55
|
||||
lookSeconds=$((attributeSeconds + settleSeconds + trackerSeconds))
|
||||
holdSeconds=150
|
||||
case "$port" in
|
||||
beos) tracker=/boot/beos/system/Tracker ;;
|
||||
*) tracker=/system/Tracker ;;
|
||||
|
|
@ -67,25 +70,30 @@ iconShown() {
|
|||
|
||||
mkdir -p "$work/job/DRAW"
|
||||
cp "$program" "$work/job/DRAW/DRAW"
|
||||
# The job: DRAW runs until it has given itself its icon attribute, then
|
||||
# Tracker opens a window on its folder (Tracker runs already: a second start
|
||||
# hands it the folder) and the machine stays up a while.
|
||||
# The job: DRAW runs until it has given itself its icon attributes (the
|
||||
# 32x32, and the 16x16 Tracker's list view shows), then Tracker opens a
|
||||
# window on a fresh copy of it, data and attributes (Tracker runs already: a
|
||||
# second start hands it the folder), and the machine stays up a while. A
|
||||
# copy because R5's Tracker keeps the first icon it saw for a file, which may
|
||||
# be the generic one from before the registrar wrote DRAW's: it failed one
|
||||
# run in three on that.
|
||||
cat > "$work/job/job.sh" <<EOF
|
||||
cd DRAW
|
||||
./DRAW &
|
||||
pid=\$!
|
||||
waited=0
|
||||
while [ \$waited -lt $attributeSeconds ] && ! catattr BEOS:L:STD_ICON DRAW > /dev/null 2>&1; do
|
||||
while [ \$waited -lt $attributeSeconds ] && ! { catattr BEOS:L:STD_ICON DRAW && catattr BEOS:M:STD_ICON DRAW; } > /dev/null 2>&1; do
|
||||
sleep 1
|
||||
waited=\$((waited + 1))
|
||||
done
|
||||
# All of it: on BeOS R5 a plain kill ends only the main thread, and the
|
||||
# window stays. The registrar writes the other attributes after the first,
|
||||
# and R5's Tracker keeps the icon it first finds: a moment for them.
|
||||
# window stays. The registrar writes the other attributes after the icons:
|
||||
# a moment for them.
|
||||
kill -9 \$pid
|
||||
sleep $settleSeconds
|
||||
cd ..
|
||||
$tracker "\$(pwd)/DRAW" &
|
||||
mkdir shown
|
||||
copyattr -d DRAW shown/DRAW
|
||||
$tracker "\$(pwd)/shown" &
|
||||
sleep $holdSeconds
|
||||
EOF
|
||||
beJobDisk "$work/job.img" "$work/job"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# and PULSE_SINK sends this ONE process to a null sink created for the
|
||||
# run, so the host's default sink is never touched and the gate is safe on
|
||||
# a desktop that is playing something else. parec records that sink's
|
||||
# monitor. Note the WAV header is only finalised when parec exits, so it
|
||||
# monitor. Note the WAV header is only finalized when parec exits, so it
|
||||
# is stopped and waited for before the file is read.
|
||||
#
|
||||
# What is graded: the AUDIO example is driven through four phases whose
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#
|
||||
# The golden hashes gate the stage, never the display. UBER's probe build
|
||||
# (make dvx builds it: examples/uber with UBER_PROBE) ends with a row for DVX
|
||||
# (P13) that draws one-pixel stripes in all 16 colours, bands and a small rect, moves
|
||||
# (P13) that draws one-pixel stripes in all 16 colors, bands and a small rect, moves
|
||||
# rows to another palette and changes that palette, presenting after each,
|
||||
# and asks the port after every present how many pixels differ from the
|
||||
# stage (jlDvxPortProbeDisplayMismatch in src/dvx/hal.c): the window's content
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
# The default desktops cover each pixel size the present writes (1, 2 and
|
||||
# 4 bytes) at each window scale (1x, 2x, 3x: 3x is square pixels only, as 4:3
|
||||
# at 3x does not fit 1024x768), in both shapes, the 8-bit desktop's nearest
|
||||
# colours and both 16-bit packings (5-5-5 and 5-6-5), then the full screen
|
||||
# colors and both 16-bit packings (5-5-5 and 5-6-5), then the full screen
|
||||
# (320x200 at 8 bits, its palette the stage's 16).
|
||||
#
|
||||
# Usage: scripts/verify-dvx-display.sh [WxHxBPP:SCALE[:square] | full]...
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ if [ -z "$log" ]; then
|
|||
else
|
||||
dvxKeysTyped "$log" "$target" || fail "KEYS's typed log"
|
||||
fi
|
||||
setting=$(find "$work/keys/c/DVX" -ipath "*/config/apps/$DVX_VENDOR/keys/$DVX_SETTINGS_FILE" -print -quit)
|
||||
setting=$(find "$work/keys/c/DVX" -ipath "*/user/apps/$DVX_VENDOR/keys/$DVX_SETTINGS_FILE" -print -quit)
|
||||
tr -d '\r' < "$setting" 2> /dev/null | grep -qi "^$DVX_SETTINGS_FULL_SCREEN *= *true" || fail "the setting does not say full screen after switching back ($setting)"
|
||||
|
||||
if [ -n "${DVX_FULLSCREEN_SHOTS:-}" ]; then
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
# verify-dvx-icon.sh - a DVX program's icon as Program Manager shows it.
|
||||
#
|
||||
# make gives every DVX program its icon (make/dvx.mk: tools/iconBake bmp,
|
||||
# a 32x32 24-bit BMP on DVX's grey, appended by dvxres as the icon32
|
||||
# a 32x32 24-bit BMP on DVX's gray, appended by dvxres as the icon32
|
||||
# resource). DVX is staged with DRAW (examples/draw) installed and Program
|
||||
# Manager, DVX's own, as the desktop (scripts/lib/dvxGate.sh), and boots in
|
||||
# DOSBox Staging; Program Manager draws each program by its icon32, so
|
||||
# DOSBox's own screenshot must hold the baked icon: every pixel of it that
|
||||
# is not the grey, in its colour (give or take TOLERANCE: the desktop is
|
||||
# is not the gray, in its color (give or take TOLERANCE: the desktop is
|
||||
# 16-bit).
|
||||
#
|
||||
# Usage: scripts/verify-dvx-icon.sh
|
||||
|
|
@ -64,7 +64,7 @@ xvfb=$!
|
|||
sleep 1
|
||||
|
||||
dvxStage "$work/c" "$(dvxAppDir "$program")"
|
||||
dvxIniSet "$work/c/DVX/config/DVX.INI" "$progman"
|
||||
dvxIniSet "$(dvxIni "$work/c")" "$progman"
|
||||
dvxDosboxConf "$work/dosbox.conf" "$work/c"
|
||||
printf '\n[capture]\ncapture_dir = %s\ndefault_image_capture_formats = raw\n' "$work/shots" >> "$work/dosbox.conf"
|
||||
mkdir -p "$work/xdg" "$work/shots"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
# the typed bytes, every stage pixel a whole scale x scale block
|
||||
# (tests/gate/keysRow.c);
|
||||
# 3. the typed history: after Escape, KEYS logs every received character
|
||||
# as hex to its joeylog.txt (in its settings folder), which must equal
|
||||
# as hex to its joeylog.txt (in its user folder), which must equal
|
||||
# the typed ASCII.
|
||||
# Each desktop given (default 640x480 at 16 bits, where the window is 1x) is
|
||||
# a run of its own, the window 4:3 or (":square") with square pixels;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
# joystick nothing else moves the stick: it rests in the middle, buttons up.
|
||||
#
|
||||
# JOY (examples/joy) draws, for each stick, a dot (in the middle at rest,
|
||||
# grey; green when moved), two button squares and a patch that says whether
|
||||
# gray; green when moved), two button squares and a patch that says whether
|
||||
# the port reports a stick. It runs with square pixels (dvxSettings): the
|
||||
# 4:3 window repeats some rows, and the dots are read pixel for pixel.
|
||||
# tests/gate/joyScreen.c reads the WHOLE state of
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# gate's fixture. The program loads its bake through jlTileBankLoadToSurface
|
||||
# and through jlTileBankLoad + jlTilePaste, checks every pixel of both against
|
||||
# the PNG, hashes both, and tries every other target's bake; its TILEBANK line
|
||||
# in its joeylog.txt (in its settings folder) must say PASS with both hashes
|
||||
# in its joeylog.txt (in its user folder) must say PASS with both hashes
|
||||
# equal to the host gate's (scripts/check-tilebank.sh, run here for the value)
|
||||
# and every other target refused. DOSBox is stopped as soon as the line
|
||||
# appears.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# verify-iigs-audio.sh - Headless gate proving NTP is actually running on
|
||||
# the IIgs, modelled on verify-iigs.sh (same Finder launch timeline, same
|
||||
# the IIgs, modeled on verify-iigs.sh (same Finder launch timeline, same
|
||||
# "read emulated memory, not a snapshot" approach).
|
||||
#
|
||||
# Sound cannot be graded from a -sound none run, so this grades the two
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# opaque, as the two screen pixels QuickDraw draws a 4-bit icon pixel as in
|
||||
# 640 mode, the first its high two bits and the second its low two. The
|
||||
# screen's own 2-bit values are compared, so the check does not lean on the
|
||||
# palette; the icon's colours are the Finder's palette's (tools/iconBake).
|
||||
# palette; the icon's colors are the Finder's palette's (tools/iconBake).
|
||||
#
|
||||
# Usage: scripts/verify-iigs-icon.sh
|
||||
# Env: IIGS_ICON_KEEP keep the screen as build/iigs/iigs-icon.png
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue