From b28916f930ce498e4c81100fb176302c0a2409d1 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 19 Oct 2026 15:46:47 -0500 Subject: [PATCH] More bug squashing. --- Makefile | 8 +- README.md | 83 +++--- docs/ApiAudio.adoc | 18 +- docs/ApiCore.adoc | 5 +- docs/ApiDebug.adoc | 2 +- docs/ApiDraw.adoc | 40 +-- docs/ApiFile.adoc | 6 +- docs/ApiInput.adoc | 33 ++- docs/ApiPalette.adoc | 36 ++- docs/ApiSerial.adoc | 4 +- docs/ApiSprite.adoc | 38 ++- docs/ApiSurface.adoc | 22 +- docs/ApiTile.adoc | 28 +- docs/Appendix.adoc | 2 +- docs/Assets.adoc | 40 +-- docs/BuildIntegration.adoc | 74 +++-- docs/GettingStarted.adoc | 86 +++--- docs/Overview.adoc | 39 +-- docs/Portability.adoc | 191 ++++++------- docs/ProgramLayout.adoc | 22 +- examples/adventure/adventure.c | 168 ++---------- examples/adventure2/adventure2.c | 157 +++-------- examples/audio/audio.c | 2 +- examples/audpar/audpar.c | 2 +- examples/common/exampleFont.c | 122 +++++++++ examples/common/exampleFont.h | 23 ++ examples/hello/hello.c | 49 +++- examples/keys/keys.c | 180 +------------ examples/palprobe/palprobe.c | 4 +- examples/scb/scb.c | 14 +- examples/scroll/scroll.c | 22 +- examples/sermouse/sermouse.c | 81 +++++- examples/sprite/sprite.c | 2 +- examples/tilebank/tilebank.c | 12 +- examples/uber/uber.c | 38 +-- examples/wblaunch/wblaunch.c | 14 +- include/joey/audio.h | 19 +- include/joey/input.h | 16 +- include/joey/platform.h | 4 +- include/joey/serial.h | 2 +- include/joey/sprite.h | 30 ++- include/joey/surface.h | 2 +- include/joey/tile.h | 2 +- install.sh | 2 +- installer/components/amigaGcc.sh | 2 +- installer/components/dvx.sh | 30 ++- installer/components/llvmMingw.sh | 4 +- installer/components/macosSdk.sh | 2 +- installer/components/pspdev.sh | 12 +- installer/components/win311.sh | 2 +- installer/lib/beos.sh | 4 +- installer/lib/haiku.sh | 4 +- installer/lib/win16.sh | 2 +- installer/lib/winDriver.sh | 6 +- installer/patches/amigaGcc/README.md | 2 +- installer/patches/mame/README.md | 2 +- installer/patches/ptplayer/README.md | 2 +- joey-run | 26 +- make/amiga.mk | 2 +- make/beos.mk | 2 +- make/common.mk | 8 +- make/dvx.mk | 29 +- make/examples.mk | 63 +++-- make/haiku32.mk | 2 +- make/hosted.mk | 4 +- make/programs.mk | 7 +- make/support/be/gcc2.sh | 2 +- make/support/iigs/clang-build.sh | 4 +- make/support/win16/owcc.sh | 4 +- make/tools.mk | 27 +- make/win16.mk | 6 +- make/win32.mk | 2 +- scripts/bench-atarist.sh | 2 +- scripts/bench-dvx.sh | 4 +- scripts/bench-x68000.sh | 14 +- scripts/check-fileroots.sh | 2 +- scripts/check-modplayer.sh | 4 +- scripts/dosbox-win16.conf | 2 +- scripts/lib/beGate.sh | 13 +- scripts/lib/dvxGate.sh | 62 +++-- scripts/lib/keysGate.sh | 17 +- scripts/lib/win16Gate.sh | 2 +- scripts/profile-x68000.sh | 2 +- scripts/pulseRecordLib.sh | 2 +- scripts/verify-all.sh | 26 +- scripts/verify-amiga-audio.sh | 4 +- scripts/verify-amiga-icon.sh | 4 +- scripts/verify-atarist-joy.sh | 2 +- scripts/verify-atarist-palette.sh | 8 +- scripts/verify-audio-conformance.sh | 6 +- scripts/verify-be-icon.sh | 34 ++- scripts/verify-dos-audio.sh | 2 +- scripts/verify-dvx-display.sh | 4 +- scripts/verify-dvx-fullscreen.sh | 2 +- scripts/verify-dvx-icon.sh | 6 +- scripts/verify-dvx-input.sh | 2 +- scripts/verify-dvx-joy.sh | 2 +- scripts/verify-dvx-tilebank.sh | 2 +- scripts/verify-iigs-audio.sh | 2 +- scripts/verify-iigs-icon.sh | 2 +- scripts/verify-iigs-mouse.sh | 161 +++++++++++ scripts/verify-iigs-tilebank.sh | 6 +- scripts/verify-psp-input.sh | 118 +++++++- scripts/verify-win16-display.sh | 4 +- scripts/verify-win16-icon.sh | 8 +- scripts/verify-win32-icon.sh | 2 +- scripts/verify-x68000-golden.sh | 14 +- scripts/verify-x68000-input.sh | 10 +- scripts/verify-x68000-joy.sh | 14 +- scripts/verify-x68000-mouse.sh | 14 +- scripts/verify-x68000-scb.sh | 12 +- scripts/verify-x68000-serial-mouse.sh | 12 +- scripts/verify-x68000-serial-scb.sh | 2 +- scripts/verify-x68000-tilebank.sh | 10 +- src/amiga/amigaCustom.h | 6 + src/amiga/audio.c | 372 ++++++++++++++++++-------- src/amiga/hal.c | 30 +-- src/amiga/input.c | 6 - src/atarist/audio.c | 2 +- src/atarist/copyRect.s | 2 +- src/atarist/hal.c | 285 ++++++++++++-------- src/atarist/input.c | 5 +- src/atarist/lineSpan.s | 6 +- src/atarist/rasterIsr.s | 72 ++++- src/atarist/serial.c | 2 +- src/atarist/stGlyph.s | 6 +- src/atarist/ste/steBlitter.h | 2 +- src/atarist/superExit.s | 2 +- src/core/draw.c | 4 +- src/core/init.c | 2 +- src/core/input.c | 61 ++++- src/core/modFormat.h | 2 +- src/core/modPlayer.c | 4 +- src/core/modPlayer.h | 2 +- src/core/psgSynth.c | 2 +- src/core/sprite.c | 4 +- src/core/surface.c | 8 +- src/core/tileLayer.c | 2 +- src/core/tileLayerInternal.h | 2 +- src/dos/audio.c | 4 +- src/dos/hal.c | 12 +- src/dos/input.c | 14 +- src/dvx/audio.c | 2 +- src/dvx/hal.c | 34 +-- src/dvx/input.c | 16 +- src/dvx/program.c | 12 +- src/dvx/save.c | 2 +- src/generic/genericTileLayer.c | 2 +- src/hosted/audio.c | 6 +- src/hosted/be/beBackend.cpp | 12 +- src/hosted/hal.c | 10 +- src/hosted/hosted.h | 20 +- src/hosted/psp/pspKeyboard.c | 2 +- src/hosted/sdl3/sdl3Backend.c | 122 +++++++-- src/hosted/win16/win16Backend.c | 53 ++-- src/hosted/win32/win32Backend.c | 7 +- src/iigs/audio_full.c | 14 +- src/iigs/hal.c | 6 +- src/iigs/iigsLayerBlit.s | 2 +- src/iigs/input.c | 76 ++++-- src/iigs/joeyDraw.s | 32 +-- src/iigs/serialIrq.s | 4 +- src/iigs/sfxCopy.s | 2 +- src/iigs/spriteEmitIigs.c | 2 +- src/m68k/planarFill.s | 86 +++--- src/m68k/planarIndexed.c | 2 +- src/m68k/planarPlot.s | 90 +++---- src/m68k/planarShape.s | 4 +- src/m68k/planarTile.h | 2 +- src/m68k/spriteEmitInterleaved68k.c | 2 +- src/m68k/spriteEmitPlanar68k.c | 2 +- src/x68000/hal.c | 81 +++--- src/x68000/halPlanar.c | 2 +- src/x68000/save.c | 2 +- src/x68000/x68kPlanar.h | 8 +- src/x68000/x68kPresent.s | 2 +- src/x68000/x68kRasterIsr.s | 8 +- src/x68000/x68kSccRxIsr.s | 4 +- src/x68000/x68kVdispIsr.s | 8 +- src/x86/scanSet1.h | 2 +- src/x86/x86Expand.h | 2 +- tests/gate/amigaCheck.c | 54 ++-- tests/gate/audioGate.c | 42 +-- tests/gate/common/gateImage.c | 54 ++-- tests/gate/common/gateImage.h | 18 +- tests/gate/dosRow.c | 117 +++++--- tests/gate/iconFind.c | 32 +-- tests/gate/iigsIcon.c | 4 +- tests/gate/joyScreen.c | 104 +++---- tests/gate/keysCells.c | 16 +- tests/gate/keysRow.c | 14 +- tests/gate/netWmIcon.c | 2 +- tests/gate/pngProbe.c | 2 +- tests/gate/pspKeyboardPath.c | 8 +- tests/gate/stScreenDump.c | 12 +- tests/gate/tiledCheck.c | 28 +- tests/gate/x68kSerialPeer.c | 2 +- tests/host/drawHost.c | 4 +- tests/host/inputHost.c | 79 +++++- tests/host/modPlayerHost.c | 2 +- tests/host/tileBankExpect.c | 2 +- tests/host/tileBankHost.c | 4 +- tests/host/tileLayerHost.c | 4 +- tools/assetbake/assetbake.c | 2 +- tools/common/toolPng.c | 88 +++--- tools/common/toolPng.h | 12 +- tools/common/toolXml.c | 2 +- tools/common/toolZlib.c | 8 +- tools/iconBake/iconBake.c | 254 +++++++++--------- tools/modToNtp/modToNtp.c | 12 +- tools/songbake/songbake.c | 2 +- tools/tiled2joey/tiled2joey.c | 62 ++--- tools/traceLogo/traceLogo.c | 118 ++++---- tools/vgm2song/vgm2song.c | 14 +- 214 files changed, 3161 insertions(+), 2310 deletions(-) create mode 100644 examples/common/exampleFont.c create mode 100644 examples/common/exampleFont.h create mode 100755 scripts/verify-iigs-mouse.sh diff --git a/Makefile b/Makefile index 6c39ad2..aa3c741 100644 --- a/Makefile +++ b/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)" diff --git a/README.md b/README.md index d1d97c5..3b207b4 100644 --- a/README.md +++ b/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//`, -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//`, +laid out as they install in DVX, and each is also a DVX add-on package, +`build/dvx/bin/packages/.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//`. 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//` holding +for the tests); its examples are folders `build/psp/bin//` 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// 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 `` -- include ``. 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: diff --git a/docs/ApiAudio.adoc b/docs/ApiAudio.adoc index e8917d9..7ca1498 100644 --- a/docs/ApiAudio.adoc +++ b/docs/ApiAudio.adoc @@ -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 diff --git a/docs/ApiCore.adoc b/docs/ApiCore.adoc index 342e8b1..e84f5db 100644 --- a/docs/ApiCore.adoc +++ b/docs/ApiCore.adoc @@ -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 diff --git a/docs/ApiDebug.adoc b/docs/ApiDebug.adoc index 8e5ac54..5f4e5bf 100644 --- a/docs/ApiDebug.adoc +++ b/docs/ApiDebug.adoc @@ -4,7 +4,7 @@ Declared in ``. 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 diff --git a/docs/ApiDraw.adoc b/docs/ApiDraw.adoc index 4168463..0204b2d 100644 --- a/docs/ApiDraw.adoc +++ b/docs/ApiDraw.adoc @@ -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. diff --git a/docs/ApiFile.adoc b/docs/ApiFile.adoc index db78a0b..386e916 100644 --- a/docs/ApiFile.adoc +++ b/docs/ApiFile.adoc @@ -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 diff --git a/docs/ApiInput.adoc b/docs/ApiInput.adoc index e32ddd6..ce28a31 100644 --- a/docs/ApiInput.adoc +++ b/docs/ApiInput.adoc @@ -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. ==== diff --git a/docs/ApiPalette.adoc b/docs/ApiPalette.adoc index 85ac728..579c697 100644 --- a/docs/ApiPalette.adoc +++ b/docs/ApiPalette.adoc @@ -2,18 +2,18 @@ Declared in ``. -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. diff --git a/docs/ApiSerial.adoc b/docs/ApiSerial.adoc index 21befae..8ce1136 100644 --- a/docs/ApiSerial.adoc +++ b/docs/ApiSerial.adoc @@ -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 diff --git a/docs/ApiSprite.adoc b/docs/ApiSprite.adoc index 4425fe8..4ded374 100644 --- a/docs/ApiSprite.adoc +++ b/docs/ApiSprite.adoc @@ -3,7 +3,7 @@ Declared in ``. 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`; diff --git a/docs/ApiSurface.adoc b/docs/ApiSurface.adoc index 48fac79..a3b5b57 100644 --- a/docs/ApiSurface.adoc +++ b/docs/ApiSurface.adoc @@ -2,11 +2,11 @@ Declared in `` and ``. -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. diff --git a/docs/ApiTile.adoc b/docs/ApiTile.adoc index feeb913..6c8c846 100644 --- a/docs/ApiTile.adoc +++ b/docs/ApiTile.adoc @@ -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. diff --git a/docs/Appendix.adoc b/docs/Appendix.adoc index 4fa552a..2059530 100644 --- a/docs/Appendix.adoc +++ b/docs/Appendix.adoc @@ -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. |=== diff --git a/docs/Assets.adoc b/docs/Assets.adoc index 1b191c0..a7f82ce 100644 --- a/docs/Assets.adoc +++ b/docs/Assets.adoc @@ -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--tilebank.sh` gates run it on +other targets' bakes. Its `scripts/verify--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. diff --git a/docs/BuildIntegration.adoc b/docs/BuildIntegration.adoc index 62c1854..498b553 100644 --- a/docs/BuildIntegration.adoc +++ b/docs/BuildIntegration.adoc @@ -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-.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] ---- diff --git a/docs/GettingStarted.adoc b/docs/GettingStarted.adoc index f6f3715..e2294b4 100644 --- a/docs/GettingStarted.adoc +++ b/docs/GettingStarted.adoc @@ -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 <>), 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 diff --git a/docs/Overview.adoc b/docs/Overview.adoc index a038123..d3e90c5 100644 --- a/docs/Overview.adoc +++ b/docs/Overview.adoc @@ -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. diff --git a/docs/Portability.adoc b/docs/Portability.adoc index 4b76bd8..f3a1d8b 100644 --- a/docs/Portability.adoc +++ b/docs/Portability.adoc @@ -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 `` of five functions.* The ST's C library has no maths +* *ST: a `` 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. diff --git a/docs/ProgramLayout.adoc b/docs/ProgramLayout.adoc index f317a7c..dcd97c5 100644 --- a/docs/ProgramLayout.adoc +++ b/docs/ProgramLayout.adoc @@ -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 diff --git a/examples/adventure/adventure.c b/examples/adventure/adventure.c index 31c18a8..a59eb5b 100644 --- a/examples/adventure/adventure.c +++ b/examples/adventure/adventure.c @@ -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 @@ -42,6 +43,8 @@ #include +#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) { diff --git a/examples/adventure2/adventure2.c b/examples/adventure2/adventure2.c index 57705ce..d0547ab 100644 --- a/examples/adventure2/adventure2.c +++ b/examples/adventure2/adventure2.c @@ -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 +#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(); diff --git a/examples/audio/audio.c b/examples/audio/audio.c index 3968654..7722384 100644 --- a/examples/audio/audio.c +++ b/examples/audio/audio.c @@ -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; diff --git a/examples/audpar/audpar.c b/examples/audpar/audpar.c index f93c07a..153bd59 100644 --- a/examples/audpar/audpar.c +++ b/examples/audpar/audpar.c @@ -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; } diff --git a/examples/common/exampleFont.c b/examples/common/exampleFont.c new file mode 100644 index 0000000..8e4d98e --- /dev/null +++ b/examples/common/exampleFont.c @@ -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 + +#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); + } +} diff --git a/examples/common/exampleFont.h b/examples/common/exampleFont.h new file mode 100644 index 0000000..95bfa2a --- /dev/null +++ b/examples/common/exampleFont.h @@ -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 + +#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 diff --git a/examples/hello/hello.c b/examples/hello/hello.c index 4646d40..7be2266 100644 --- a/examples/hello/hello.c +++ b/examples/hello/hello.c @@ -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 #include +#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; diff --git a/examples/keys/keys.c b/examples/keys/keys.c index a76bb0f..947e3f7 100644 --- a/examples/keys/keys.c +++ b/examples/keys/keys.c @@ -31,6 +31,8 @@ #include #include +#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); } diff --git a/examples/palprobe/palprobe.c b/examples/palprobe/palprobe.c index 765f5a8..71411a3 100644 --- a/examples/palprobe/palprobe.c +++ b/examples/palprobe/palprobe.c @@ -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. diff --git a/examples/scb/scb.c b/examples/scb/scb.c index b254eee..d9d7685 100644 --- a/examples/scb/scb.c +++ b/examples/scb/scb.c @@ -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. diff --git a/examples/scroll/scroll.c b/examples/scroll/scroll.c index 0bdff79..83a9465 100644 --- a/examples/scroll/scroll.c +++ b/examples/scroll/scroll.c @@ -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); diff --git a/examples/sermouse/sermouse.c b/examples/sermouse/sermouse.c index f171317..aa66f25 100644 --- a/examples/sermouse/sermouse.c +++ b/examples/sermouse/sermouse.c @@ -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 , which the // umbrella deliberately does NOT pull in. +#include + #include #include +#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) { diff --git a/examples/sprite/sprite.c b/examples/sprite/sprite.c index f5b09f6..2507e68 100644 --- a/examples/sprite/sprite.c +++ b/examples/sprite/sprite.c @@ -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 diff --git a/examples/tilebank/tilebank.c b/examples/tilebank/tilebank.c index 9df6c3a..62729ec 100644 --- a/examples/tilebank/tilebank.c +++ b/examples/tilebank/tilebank.c @@ -15,7 +15,7 @@ // refuse.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= pasteHash= refused=4/4 // which scripts/verify--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; diff --git a/examples/uber/uber.c b/examples/uber/uber.c index 1f9f7f8..2b59ce6 100644 --- a/examples/uber/uber.c +++ b/examples/uber/uber.c @@ -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; } diff --git a/examples/wblaunch/wblaunch.c b/examples/wblaunch/wblaunch.c index feb9c0e..941f0fb 100644 --- a/examples/wblaunch/wblaunch.c +++ b/examples/wblaunch/wblaunch.c @@ -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); diff --git a/include/joey/audio.h b/include/joey/audio.h index 47bd89b..b59d4cc 100644 --- a/include/joey/audio.h +++ b/include/joey/audio.h @@ -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); diff --git a/include/joey/input.h b/include/joey/input.h index 74b5efc..4e71e99 100644 --- a/include/joey/input.h +++ b/include/joey/input.h @@ -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 diff --git a/include/joey/platform.h b/include/joey/platform.h index 342177f..5fb1d9b 100644 --- a/include/joey/platform.h +++ b/include/joey/platform.h @@ -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) diff --git a/include/joey/serial.h b/include/joey/serial.h index 0375d83..82ae67f 100644 --- a/include/joey/serial.h +++ b/include/joey/serial.h @@ -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. diff --git a/include/joey/sprite.h b/include/joey/sprite.h index fb2cc17..5f73fed 100644 --- a/include/joey/sprite.h +++ b/include/joey/sprite.h @@ -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 diff --git a/include/joey/surface.h b/include/joey/surface.h index b220a36..6ac0b34 100644 --- a/include/joey/surface.h +++ b/include/joey/surface.h @@ -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. diff --git a/include/joey/tile.h b/include/joey/tile.h index 3890b51..7470d0d 100644 --- a/include/joey/tile.h +++ b/include/joey/tile.h @@ -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 diff --git a/install.sh b/install.sh index 5b2508f..a1ab287 100755 --- a/install.sh +++ b/install.sh @@ -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" diff --git a/installer/components/amigaGcc.sh b/installer/components/amigaGcc.sh index c7364e7..3daea79 100644 --- a/installer/components/amigaGcc.sh +++ b/installer/components/amigaGcc.sh @@ -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" diff --git a/installer/components/dvx.sh b/installer/components/dvx.sh index 598df3e..6e59c39 100644 --- a/installer/components/dvx.sh +++ b/installer/components/dvx.sh @@ -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" ] diff --git a/installer/components/llvmMingw.sh b/installer/components/llvmMingw.sh index 01ce8b6..89dba36 100644 --- a/installer/components/llvmMingw.sh +++ b/installer/components/llvmMingw.sh @@ -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). diff --git a/installer/components/macosSdk.sh b/installer/components/macosSdk.sh index 3b22f4e..15d07c4 100644 --- a/installer/components/macosSdk.sh +++ b/installer/components/macosSdk.sh @@ -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). diff --git a/installer/components/pspdev.sh b/installer/components/pspdev.sh index eb8e6f8..e78552f 100644 --- a/installer/components/pspdev.sh +++ b/installer/components/pspdev.sh @@ -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 } diff --git a/installer/components/win311.sh b/installer/components/win311.sh index ccb2471..dbf2840 100644 --- a/installer/components/win311.sh +++ b/installer/components/win311.sh @@ -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 diff --git a/installer/lib/beos.sh b/installer/lib/beos.sh index 08ca3bd..d60d4b3 100644 --- a/installer/lib/beos.sh +++ b/installer/lib/beos.sh @@ -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 < "$build/make.log" 2>&1 || die "building $file failed; the log: $build/make.log" diff --git a/installer/patches/amigaGcc/README.md b/installer/patches/amigaGcc/README.md index 6907a4d..95851d5 100644 --- a/installer/patches/amigaGcc/README.md +++ b/installer/patches/amigaGcc/README.md @@ -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 diff --git a/installer/patches/mame/README.md b/installer/patches/mame/README.md index 770c9a3..c3730ac 100644 --- a/installer/patches/mame/README.md +++ b/installer/patches/mame/README.md @@ -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 diff --git a/installer/patches/ptplayer/README.md b/installer/patches/ptplayer/README.md index 522232d..1ab6b02 100644 --- a/installer/patches/ptplayer/README.md +++ b/installer/patches/ptplayer/README.md @@ -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 diff --git a/joey-run b/joey-run index f090a00..d37c67a 100755 --- a/joey-run +++ b/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 diff --git a/make/amiga.mk b/make/amiga.mk index f41b9b1..b121996 100644 --- a/make/amiga.mk +++ b/make/amiga.mk @@ -20,7 +20,7 @@ $(call requireTool,$(AMIGA_PTPLAYER)/ptplayer.asm,toolchain:amiga) # independently. -I on $(SRC_DIR)/amiga lets ptplayer.h resolve # 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 diff --git a/make/beos.mk b/make/beos.mk index 569c960..e1fc332 100644 --- a/make/beos.mk +++ b/make/beos.mk @@ -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++ diff --git a/make/common.mk b/make/common.mk index dc0c71a..a5cde7b 100644 --- a/make/common.mk +++ b/make/common.mk @@ -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// (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//data//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 diff --git a/make/dvx.mk b/make/dvx.mk index 4bf081d..7791952 100644 --- a/make/dvx.mk +++ b/make/dvx.mk @@ -114,13 +114,25 @@ $(LIB): $(LIB_OBJS) # DVX_KIND what the help and resource texts call a program: " 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 $$< $$@ diff --git a/make/examples.mk b/make/examples.mk index 5a7ff4f..2fb01de 100644 --- a/make/examples.mk +++ b/make/examples.mk @@ -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 $@) diff --git a/make/haiku32.mk b/make/haiku32.mk index ba19b46..fc56c10 100644 --- a/make/haiku32.mk +++ b/make/haiku32.mk @@ -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++ diff --git a/make/hosted.mk b/make/hosted.mk index fd16ca5..d0a16d6 100644 --- a/make/hosted.mk +++ b/make/hosted.mk @@ -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 diff --git a/make/programs.mk b/make/programs.mk index 60d8e24..43be212 100644 --- a/make/programs.mk +++ b/make/programs.mk @@ -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 diff --git a/make/support/be/gcc2.sh b/make/support/be/gcc2.sh index a51ded1..1d402f8 100755 --- a/make/support/be/gcc2.sh +++ b/make/support/be/gcc2.sh @@ -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 diff --git a/make/support/iigs/clang-build.sh b/make/support/iigs/clang-build.sh index 1f831a1..19dd5f5 100755 --- a/make/support/iigs/clang-build.sh +++ b/make/support/iigs/clang-build.sh @@ -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). # diff --git a/make/support/win16/owcc.sh b/make/support/win16/owcc.sh index 1f04b28..5404e72 100755 --- a/make/support/win16/owcc.sh +++ b/make/support/win16/owcc.sh @@ -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 diff --git a/make/tools.mk b/make/tools.mk index 96b3336..0f5f227 100644 --- a/make/tools.mk +++ b/make/tools.mk @@ -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) diff --git a/make/win16.mk b/make/win16.mk index 519933a..8253a05 100644 --- a/make/win16.mk +++ b/make/win16.mk @@ -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)) diff --git a/make/win32.mk b/make/win32.mk index e04000c..657efbd 100644 --- a/make/win32.mk +++ b/make/win32.mk @@ -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)) diff --git a/scripts/bench-atarist.sh b/scripts/bench-atarist.sh index a0ed329..4b6b4e8 100755 --- a/scripts/bench-atarist.sh +++ b/scripts/bench-atarist.sh @@ -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 diff --git a/scripts/bench-dvx.sh b/scripts/bench-dvx.sh index ea1e23e..0560905 100755 --- a/scripts/bench-dvx.sh +++ b/scripts/bench-dvx.sh @@ -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. # diff --git a/scripts/bench-x68000.sh b/scripts/bench-x68000.sh index 2599fc3..693e6a3 100755 --- a/scripts/bench-x68000.sh +++ b/scripts/bench-x68000.sh @@ -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 diff --git a/scripts/check-fileroots.sh b/scripts/check-fileroots.sh index b0321f4..f789014 100755 --- a/scripts/check-fileroots.sh +++ b/scripts/check-fileroots.sh @@ -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)" diff --git a/scripts/check-modplayer.sh b/scripts/check-modplayer.sh index d4935c1..3531040 100755 --- a/scripts/check-modplayer.sh +++ b/scripts/check-modplayer.sh @@ -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)" diff --git a/scripts/dosbox-win16.conf b/scripts/dosbox-win16.conf index b02bfe9..3cded60 100644 --- a/scripts/dosbox-win16.conf +++ b/scripts/dosbox-win16.conf @@ -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 diff --git a/scripts/lib/beGate.sh b/scripts/lib/beGate.sh index e9e5809..81b085d 100644 --- a/scripts/lib/beGate.sh +++ b/scripts/lib/beGate.sh @@ -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 \ diff --git a/scripts/lib/dvxGate.sh b/scripts/lib/dvxGate.sh index 6e68a13..6753aba 100644 --- a/scripts/lib/dvxGate.sh +++ b/scripts/lib/dvxGate.sh @@ -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 } diff --git a/scripts/lib/keysGate.sh b/scripts/lib/keysGate.sh index 48c440e..253cb07 100644 --- a/scripts/lib/keysGate.sh +++ b/scripts/lib/keysGate.sh @@ -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" diff --git a/scripts/lib/win16Gate.sh b/scripts/lib/win16Gate.sh index af13656..8d79f12 100644 --- a/scripts/lib/win16Gate.sh +++ b/scripts/lib/win16Gate.sh @@ -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 diff --git a/scripts/profile-x68000.sh b/scripts/profile-x68000.sh index 5196b4a..1383851 100755 --- a/scripts/profile-x68000.sh +++ b/scripts/profile-x68000.sh @@ -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 diff --git a/scripts/pulseRecordLib.sh b/scripts/pulseRecordLib.sh index 95d139e..bbb13ad 100644 --- a/scripts/pulseRecordLib.sh +++ b/scripts/pulseRecordLib.sh @@ -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 diff --git a/scripts/verify-all.sh b/scripts/verify-all.sh index 2901b6a..fecfb88 100755 --- a/scripts/verify-all.sh +++ b/scripts/verify-all.sh @@ -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 diff --git a/scripts/verify-amiga-audio.sh b/scripts/verify-amiga-audio.sh index 5f5d298..5713b6f 100755 --- a/scripts/verify-amiga-audio.sh +++ b/scripts/verify-amiga-audio.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 diff --git a/scripts/verify-amiga-icon.sh b/scripts/verify-amiga-icon.sh index 23a2ff0..60cdc83 100755 --- a/scripts/verify-amiga-icon.sh +++ b/scripts/verify-amiga-icon.sh @@ -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. diff --git a/scripts/verify-atarist-joy.sh b/scripts/verify-atarist-joy.sh index d14a9c6..5e0ac88 100755 --- a/scripts/verify-atarist-joy.sh +++ b/scripts/verify-atarist-joy.sh @@ -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}" ;; diff --git a/scripts/verify-atarist-palette.sh b/scripts/verify-atarist-palette.sh index c89f6fd..4fd199d 100755 --- a/scripts/verify-atarist-palette.sh +++ b/scripts/verify-atarist-palette.sh @@ -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++) { diff --git a/scripts/verify-audio-conformance.sh b/scripts/verify-audio-conformance.sh index e76961e..1f0b09f 100755 --- a/scripts/verify-audio-conformance.sh +++ b/scripts/verify-audio-conformance.sh @@ -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=$? diff --git a/scripts/verify-be-icon.sh b/scripts/verify-be-icon.sh index 82fd435..53b7606 100755 --- a/scripts/verify-be-icon.sh +++ b/scripts/verify-be-icon.sh @@ -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" diff --git a/scripts/verify-dos-audio.sh b/scripts/verify-dos-audio.sh index 30e8429..7b2ad89 100755 --- a/scripts/verify-dos-audio.sh +++ b/scripts/verify-dos-audio.sh @@ -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 diff --git a/scripts/verify-dvx-display.sh b/scripts/verify-dvx-display.sh index 496f231..c379781 100755 --- a/scripts/verify-dvx-display.sh +++ b/scripts/verify-dvx-display.sh @@ -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]... diff --git a/scripts/verify-dvx-fullscreen.sh b/scripts/verify-dvx-fullscreen.sh index 7cfd782..9280c10 100755 --- a/scripts/verify-dvx-fullscreen.sh +++ b/scripts/verify-dvx-fullscreen.sh @@ -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 diff --git a/scripts/verify-dvx-icon.sh b/scripts/verify-dvx-icon.sh index 4b50f44..ed59d8b 100755 --- a/scripts/verify-dvx-icon.sh +++ b/scripts/verify-dvx-icon.sh @@ -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" diff --git a/scripts/verify-dvx-input.sh b/scripts/verify-dvx-input.sh index 70f824e..8e6129a 100755 --- a/scripts/verify-dvx-input.sh +++ b/scripts/verify-dvx-input.sh @@ -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; diff --git a/scripts/verify-dvx-joy.sh b/scripts/verify-dvx-joy.sh index be258a9..44a44e1 100755 --- a/scripts/verify-dvx-joy.sh +++ b/scripts/verify-dvx-joy.sh @@ -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 diff --git a/scripts/verify-dvx-tilebank.sh b/scripts/verify-dvx-tilebank.sh index f3e508a..1ddf565 100755 --- a/scripts/verify-dvx-tilebank.sh +++ b/scripts/verify-dvx-tilebank.sh @@ -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. diff --git a/scripts/verify-iigs-audio.sh b/scripts/verify-iigs-audio.sh index cd7fb53..acc2bcb 100755 --- a/scripts/verify-iigs-audio.sh +++ b/scripts/verify-iigs-audio.sh @@ -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 diff --git a/scripts/verify-iigs-icon.sh b/scripts/verify-iigs-icon.sh index 788cddb..717b3ff 100755 --- a/scripts/verify-iigs-icon.sh +++ b/scripts/verify-iigs-icon.sh @@ -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 diff --git a/scripts/verify-iigs-mouse.sh b/scripts/verify-iigs-mouse.sh new file mode 100755 index 0000000..bdd9aa4 --- /dev/null +++ b/scripts/verify-iigs-mouse.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License. +# See LICENSE.md for license information. +# SPDX-License-Identifier: MIT +# +# verify-iigs-mouse.sh - Headless gate for the IIgs's mouse buttons. Every +# port has two, and the IIgs's ADB mouse has one: a left click made with +# Ctrl held is the right button (src/core/input.c, mouseCtrlClick), read +# through the asm snapshot and predicates (joeyDraw.s). Boots GS/OS under +# MAME and launches the KEYS example off joey.2mg, which stamps the mouse's +# state on its stage row 199: the sentinel 5A5A, then present, left, +# right, and a zero nibble. Presses the emulated mouse button and Ctrl in +# turn and reads the row straight out of SHR memory after each: +# +# start present, no button +# click left +# click let go none +# Ctrl-click right, not left +# Ctrl let go first still right (until the button is let go) +# Ctrl-click let go none +# click, then Ctrl left: Ctrl after the press changes nothing +# both let go none +# +# Then Escape: KEYS must quit to the Finder. +# +# Usage: scripts/verify-iigs-mouse.sh +# Requires: ./install.sh install iigs; build/iigs/bin/joey.2mg built +# with the current KEYS (make iigs-disk). +set -euo pipefail + +repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +source "$repo/scripts/lib/joeyEnv.sh" +joeyNeed MAME mame +joeyNeed IIGS_ROMPATH iigsFirmware +joeyNeed GSOS_SYSTEM_DISK iigsFirmware + +label=verify-iigs-mouse +sys_disk=$GSOS_SYSTEM_DISK +data_disk=$repo/build/iigs/bin/joey.2mg + +for f in "$sys_disk" "$data_disk"; do + [ -f "$f" ] || { echo "$label: missing $f (run 'make iigs-disk')" >&2; exit 2; } +done + +work=$(mktemp -d -t joeylib-mouse-verify.XXXXXX) +trap 'rm -rf "$work"' EXIT +cp "$sys_disk" "$work/boot.po" +cp "$data_disk" "$work/joey.2mg" + +# Finder keystroke timeline as in verify-iigs-keys.sh. KEYS' mouse row is +# stage row 199, the SHR bytes from $E1/2000 + 199*160 = $E1/9C60, two +# nibble pixels a byte: 5A 5A, then the flags word (present 1000, left +# 0100, right 0010). SCB 0 is $E1/9D00. Each step waits 90 frames (1.5 s) +# before the row is read: KEYS restamps it at its next frame. +cat > "$work/verify.lua" <<'LUA' +local cpu = manager.machine.devices[":maincpu"] +local mem = cpu.spaces["program"] +local nat = manager.machine.natkeyboard +local frame = 0 +local idx = 1 + +local function field(port, name) + local p = manager.machine.ioport.ports[port] + if p == nil then return nil end + return p.fields[name] +end +local key_cmd = field(":macadb:KEY3", "Command / Open Apple") +local key_ctrl = field(":macadb:KEY3", "Control") +local key_esc = field(":macadb:KEY3", "Esc") +local button = field(":macadb:MOUSE0", "Mouse Button") +local function press(f) if f then f:set_value(1) end end +local function release(f) if f then f:set_value(0) end end + +local function report(tag) + local row = "" + for i = 0, 3 do + row = row .. string.format("%02X", mem:read_u8(0xE19C60 + i)) + end + io.write(string.format("VERIFY-IIGS-MOUSE %s scb0=%02X row=%s\n", tag, mem:read_u8(0xE19D00), row)) + io.flush() +end + +local steps = { + {3000, function() nat:post("J") end}, + {3120, function() press(key_cmd) end}, + {3126, function() nat:post("o") end}, + {3180, function() release(key_cmd) end}, + {3540, function() nat:post("KEYS") end}, + {3660, function() press(key_cmd) end}, + {3666, function() nat:post("o") end}, + {3720, function() release(key_cmd) end}, + {6600, function() report("start") end}, + {6610, function() press(button) end}, + {6700, function() report("click") end}, + {6710, function() release(button) end}, + {6800, function() report("clickLetGo") end}, + {6810, function() press(key_ctrl) end}, + {6840, function() press(button) end}, + {6930, function() report("ctrlClick") end}, + {6940, function() release(key_ctrl) end}, + {7030, function() report("ctrlLetGoFirst") end}, + {7040, function() release(button) end}, + {7130, function() report("ctrlClickLetGo") end}, + {7140, function() press(button) end}, + {7170, function() press(key_ctrl) end}, + {7260, function() report("clickThenCtrl") end}, + {7270, function() release(button) end}, + {7280, function() release(key_ctrl) end}, + {7370, function() report("bothLetGo") end}, + {7380, function() press(key_esc) end}, + {7400, function() release(key_esc) end}, + {9600, function() report("escape"); manager.machine:exit() end}, +} + +emu.register_frame_done(function() + frame = frame + 1 + while idx <= #steps and frame >= steps[idx][1] do + steps[idx][2]() + idx = idx + 1 + end +end) +LUA + +cd "$work" +out=$(QT_QPA_PLATFORM=offscreen SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \ + timeout 300 "$MAME" apple2gs \ + -rompath "$IIGS_ROMPATH" \ + -flop3 "$work/boot.po" -flop4 "$work/joey.2mg" \ + -video none -sound none -nothrottle \ + -autoboot_script "$work/verify.lua" </dev/null 2>&1) || true + +report=$(echo "$out" | grep -E '^VERIFY-IIGS-MOUSE ' || true) +echo "$report" +value() { + echo "$report" | grep " $1 " | sed -E "s/.*$2=([0-9A-Fa-f]+).*/\1/" +} +fails=() +[ "$(value start scb0)" = "00" ] || fails+=("KEYS never launched (SCB 0 $(value start scb0))") +# step, the row it must show, what that means +for check in "start 5A5A1000 present, no button" \ + "click 5A5A1100 the left button" \ + "clickLetGo 5A5A1000 no button" \ + "ctrlClick 5A5A1010 the right button" \ + "ctrlLetGoFirst 5A5A1010 the right button" \ + "ctrlClickLetGo 5A5A1000 no button" \ + "clickThenCtrl 5A5A1100 the left button" \ + "bothLetGo 5A5A1000 no button"; do + read -r step want meaning <<< "$check" + got=$(value "$step" row) + [ "$got" = "$want" ] || fails+=("$step: row ${got:-none}, want $want ($meaning)") +done +scb=$(value escape scb0) +[ -n "$scb" ] && [ $((16#$scb)) -ge 128 ] || fails+=("Escape did not quit KEYS (SCB 0 ${scb:-none})") +if [ ${#fails[@]} -eq 0 ]; then + echo "$label: PASS (the mouse button is left, and right when Ctrl is held as it goes down, until it is let go; Escape quit KEYS)" + exit 0 +fi +for f in "${fails[@]}"; do + echo "$label: FAIL - $f" >&2 +done +exit 1 diff --git a/scripts/verify-iigs-tilebank.sh b/scripts/verify-iigs-tilebank.sh index f105992..e51bbe9 100755 --- a/scripts/verify-iigs-tilebank.sh +++ b/scripts/verify-iigs-tilebank.sh @@ -20,11 +20,11 @@ # extracted from the work disk with cadius, 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, and the lamp must be the PASS -# colour. +# color. # # SHR geometry: 160 bytes per row at $E12000, high nibble = left pixel; the # stage is the whole 320x200 screen. The lamp is examples/tilebank's: 64x64 -# at (128, 68), colour 1 for PASS and 2 for FAIL, with FILL_COLOR (9) either +# at (128, 68), color 1 for PASS and 2 for FAIL, with FILL_COLOR (9) either # side of it on its rows. # # Usage: scripts/verify-iigs-tilebank.sh @@ -104,7 +104,7 @@ local function stagePixel(x, y) if (x & 1) == 0 then return (b >> 4) & 0x0F else return b & 0x0F end end --- The lamp colour when the whole lamp shows one verdict colour with +-- The lamp color when the whole lamp shows one verdict color with -- FILL_COLOR on its rows either side, else 0. local function lampColor() local left = LAMP_X + MARGIN diff --git a/scripts/verify-psp-input.sh b/scripts/verify-psp-input.sh index 6f9bad8..e86c27f 100755 --- a/scripts/verify-psp-input.sh +++ b/scripts/verify-psp-input.sh @@ -7,20 +7,22 @@ # JoeyLib, pressed through PPSSPPHeadless's input script and read back from # what the examples draw. # -# The PSP has no keyboard and one set of controls: SDL3's PSP joystick, which -# the SDL3 backend opens as gamepad 0 (JL_JOYSTICK_0: the d-pad or the analog -# nub, cross as button 0, circle as button 1), with Start as Return and -# Select as Escape, and Square opening a keyboard drawn over the screen +# The PSP has no keyboard or mouse and one set of controls: SDL3's PSP +# joystick, which the SDL3 backend opens as gamepad 0 (JL_JOYSTICK_0: the +# d-pad, cross as button 0, circle as button 1), with the analog nub as the +# mouse pointer and L and R as its buttons, Start as Return and Select as +# Escape, and Square opening a keyboard drawn over the screen # (src/hosted/psp/pspKeyboard.c) whose d-pad moves and cross presses are -# worked out from its own source by tests/gate/pspKeyboardPath.c. Three +# worked out from its own source by tests/gate/pspKeyboardPath.c. Four # runs under PPSSPPHeadless (scripts/lib/pspGate.sh), each from its folder # on a private memory stick with every memory access checked as a PSP # checks it, each driven by an --input script on PPSSPP's emulated clock # (installer/patches/ppsspp): # # JOY (examples/joy) each d-pad direction, two diagonals, each button -# and both together, the nub left and up, with a release after -# every press so a latched line shows up; then the keyboard opened, +# and both together, the nub left and up (the stick must rest: the +# nub is the mouse's), with a release after every press so a +# latched line shows up; then the keyboard opened, # the d-pad and cross pressed (the stick must rest: they are the # keyboard's), the keyboard closed and the d-pad pressed again (the # stick's once more). After each, a snapshot of the PSP's screen, @@ -31,6 +33,15 @@ # again on release, and Start has typed one Return, read off KEYS's # row of the characters it received (tests/gate/keysRow.c). Then # Select: KEYS must end by itself. +# KEYS again for the mouse, read off KEYS's mouse row (stage row 199, +# tests/gate/dosRow.c mouse-at): present, in the middle at first; L, +# R, and both; the nub a little off center (inside the dead zone: +# no move), then full tilt right, up, and down-left into the +# stage's edges and corner, half tilt right for a second (a speed +# between, short of the edge), and left again; then the keyboard +# opened and moved to the top with L (no click) and the nub tilted +# (no move), closed, and the nub moving the pointer again. Then +# Select. # KEYS again through the keyboard: Square shows it at the bottom of the # screen and L moves it to the top; F5 held lights KEYS's F5 cell # and let go puts it out; the RetroNet string typed on it (Shift @@ -62,9 +73,11 @@ runSeconds=60 startMs=1500 holdMs=400 stepMs=600 +# The input script's steps run on slices of emulated time this long. +sliceMs=100 # KEYS's Return cell (examples/keys: column 2, row 4 of 28x26 cells on a -# 32x30 pitch from (2, 2)), at its centre, on the PSP's screen, where the -# backend centres the 320x200 stage at 1:1 in 480x272. +# 32x30 pitch from (2, 2)), at its center, on the PSP's screen, where the +# backend centers the 320x200 stage at 1:1 in 480x272. screenSize=480x272 stageX=80 stageY=36 @@ -97,8 +110,8 @@ joySteps=( "cross|buttons cross|10010" "release|buttons none|$rest" "circle|buttons circle|10001" "release|buttons none|$rest" "cross+circle|buttons cross circle|10011" "release|buttons none|$rest" - "nub left|analog -1 0|1-000" "nub centred|analog 0 0|$rest" - "nub up|analog 0 1|10-00" "nub centred|analog 0 0|$rest" + "nub left|analog -1 0|$rest" "nub centered|analog 0 0|$rest" + "nub up|analog 0 1|$rest" "nub centered|analog 0 0|$rest" "keyboard open|buttons square|$covered|$covered" "release|buttons none|$covered|$covered" "keyboard left|buttons left|$covered|$covered" "release|buttons none|$covered|$covered" "keyboard cross|buttons cross|$covered|$covered" "release|buttons none|$covered|$covered" @@ -106,7 +119,34 @@ joySteps=( "left again|buttons left|1-000" "release|buttons none|$rest" ) -# The keyboard over KEYS: its border's colour (COLOR_BOARD, which the PSP's +# The mouse: name, input script action, emulated milliseconds until its +# snapshot (the next action comes a slice later), and KEYS's mouse row then +# ("FLAGS X Y": flags 1000 present, 0100 left, 0010 right; an X of LO-HI is +# a range), or - where the keyboard covers the row. Full tilt moves the +# pointer 320 stage pixels a second, so a second or more reaches any edge; +# half tilt some 130 a second. +mouseSteps=( + "start|buttons none|300|1000 160 100" + "L|buttons ltrigger|300|1100 160 100" + "R|buttons rtrigger|300|1010 160 100" + "L+R|buttons ltrigger rtrigger|300|1110 160 100" + "release|buttons none|300|1000 160 100" + "dead zone|analog 0.1 -0.1|1000|1000 160 100" + "right|analog 1 0|1500|1000 319 100" + "up|analog 0 1|1000|1000 319 0" + "down-left|analog -1 -1|2000|1000 0 199" + "half right|analog 0.5 0|1000|1000 90-170 199" + "left|analog -1 0|1000|1000 0 199" + "centered|analog 0 0|300|1000 0 199" + "keyboard open|buttons square|300|-" + "keyboard L|buttons ltrigger|300|1000 0 199" + "keyboard nub|analog 1 -1|500|1000 0 199" + "keyboard nub centered|analog 0 0|300|1000 0 199" + "keyboard closed|buttons square|300|1000 0 199" + "nub again|analog 1 0|1500|1000 319 199" +) + +# The keyboard over KEYS: its border's color (COLOR_BOARD, which the PSP's # 16-bit textures keep) at the screen's bottom (where Square opens it) and # top (where L puts it), black where it is not; how # long a d-pad press and a cross press last (short of the keyboard's repeat) @@ -210,6 +250,58 @@ for shot in "start|$returnLit" "release|$returnUnlit"; do echo "$label: KEYS $name: ${typed//$'\n'/; }" done +# mouseMatch GOT WANT -- KEYS's mouse row GOT ("FLAGS X Y") is WANT, whose X +# may be a range LO-HI. +mouseMatch() { + local flags + local x + local y + local wantFlags + local wantX + local wantY + read -r flags x y <<< "$1" + read -r wantFlags wantX wantY <<< "$2" + if [[ $wantX == *-* ]]; then + (( x >= ${wantX%-*} && x <= ${wantX#*-} )) || return 1 + elif [ "$x" != "$wantX" ]; then + return 1 + fi + [ "$flags $y" = "$wantFlags $wantY" ] +} + + +# KEYS for the mouse: each step's action, then its snapshot; Select last. +mkdir -p "$work/mouse/shots" +at=$startMs +i=0 +for step in "${mouseSteps[@]}"; do + IFS='|' read -r name action ms want <<< "$step" + printf '%d %s\n%d snapshot %s/mouse/shots/%02d.png\n' "$at" "$action" "$((at + ms))" "$work" "$i" >> "$work/mouse/input.txt" + at=$((at + ms + sliceMs)) + i=$((i + 1)) +done +printf '%d buttons select\n' "$at" >> "$work/mouse/input.txt" +runScript "$keysDir" mouse +i=0 +for step in "${mouseSteps[@]}"; do + IFS='|' read -r name action ms want <<< "$step" + png=$(printf '%s/mouse/shots/%02d.png' "$work" "$i") + i=$((i + 1)) + if [ "$want" = - ]; then + continue + fi + if [ ! -f "$png" ]; then + fail "MOUSE $name: no snapshot" + continue + fi + if ! got=$("$(joeyTool dosRow)" mouse-at "$png" "$stageX,$stageY"); then + fail "MOUSE $name: no mouse row on the screen" + continue + fi + echo "$label: MOUSE $name: $got" + mouseMatch "$got" "$want" || fail "MOUSE $name: read $got, want $want" +done + # KEYS through the keyboard: open, to the top, F5 held and let go, the # string typed, closed. mkdir -p "$kbdShots" @@ -287,4 +379,4 @@ if [ -n "$fails" ]; then echo "$label: FAIL - $fails" >&2 exit 1 fi -echo "$label: PASS (JOY: ${#joySteps[@]} d-pad, button and nub states read back from the screen, stick B unplugged, resting while the keyboard is open; Start held KEYS's Return and typed one; the keyboard shown, moved, F5 held and \"$kbdTarget\" typed on it; Select ended all three)" +echo "$label: PASS (JOY: ${#joySteps[@]} d-pad, button, and nub states read back from the screen, the nub leaving the stick alone, stick B unplugged, resting while the keyboard is open; Start held KEYS's Return and typed one; MOUSE: ${#mouseSteps[@]} nub and L/R states, the dead zone, the edges, a half-tilt speed, and the pointer still under the keyboard; the keyboard shown, moved, F5 held and \"$kbdTarget\" typed on it; Select ended all four)" diff --git a/scripts/verify-win16-display.sh b/scripts/verify-win16-display.sh index 4ed17e7..9f31bf7 100755 --- a/scripts/verify-win16-display.sh +++ b/scripts/verify-win16-display.sh @@ -17,8 +17,8 @@ # scale, that repaint against GDI's own stretch of the frame. In the # identity layout (a palette display, 14 palettes in use or fewer) it also # checks every window pixel's screen index against the frame's and every -# colour of the palettes in use against the system palette, which is where -# an animated colour change shows (its win16Layout line gives that count). +# color of the palettes in use against the system palette, which is where +# an animated color change shows (its win16Layout line gives that count). # Every count must be 0, and every pixel must have been compared: 64000 for # the frame, plus the window's pixels once, or twice at a whole-number # scale, plus the identity checks. diff --git a/scripts/verify-win16-icon.sh b/scripts/verify-win16-icon.sh index 7f90e7c..3ef8882 100755 --- a/scripts/verify-win16-icon.sh +++ b/scripts/verify-win16-icon.sh @@ -6,13 +6,13 @@ # verify-win16-icon.sh - a Win16 program's icon as Windows 3.1 shows it. # # make binds every Win16 program's icon into its executable (make/win16.mk: -# tools/iconBake ico16, 32x32 in the 16 VGA colours, resource +# tools/iconBake ico16, 32x32 in the 16 VGA colors, resource # HOSTED_ICON_ID), and the backend gives its window class that icon. Here # Program Manager is Windows' shell and WIN.INI's load= starts DRAW -# (examples/draw) minimised, so Windows draws the class's icon on the -# desktop -- and DRAW runs on, minimised, presenting to a window with no +# (examples/draw) minimized, so Windows draws the class's icon on the +# desktop -- and DRAW runs on, minimized, presenting to a window with no # room. DOSBox's own screenshot of the 800x600 screen must hold the baked -# icon: every pixel of it the icon's mask leaves opaque, in its colour. +# icon: every pixel of it the icon's mask leaves opaque, in its color. # # Usage: scripts/verify-win16-icon.sh # Env: WIN16_ICON_KEEP keep the screenshot as build/win16/win16-icon.png diff --git a/scripts/verify-win32-icon.sh b/scripts/verify-win32-icon.sh index b12b5b0..4566bca 100755 --- a/scripts/verify-win32-icon.sh +++ b/scripts/verify-win32-icon.sh @@ -6,7 +6,7 @@ # verify-win32-icon.sh - a Win32 program's icon on its window. # # make links every Win32 program's icon into it (make/win32.mk: -# tools/iconBake ico, 16, 32 and 48 pixels in 256 colours and in 32-bit, +# tools/iconBake ico, 16, 32 and 48 pixels in 256 colors and in 32-bit, # resource HOSTED_ICON_ID), and the backend gives its window class that # icon. DRAW (examples/draw) runs under Wine (scripts/lib/wineGate.sh), which # hands a window's class icon to X as its _NET_WM_ICON; that must be the diff --git a/scripts/verify-x68000-golden.sh b/scripts/verify-x68000-golden.sh index 5b952b4..82fe435 100755 --- a/scripts/verify-x68000-golden.sh +++ b/scripts/verify-x68000-golden.sh @@ -11,7 +11,7 @@ # image with xdftool, and diffs the hashes against the Apple IIgs reference. # # 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. @@ -61,13 +61,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. @@ -85,11 +85,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 diff --git a/scripts/verify-x68000-input.sh b/scripts/verify-x68000-input.sh index 3f21c69..eb84f51 100755 --- a/scripts/verify-x68000-input.sh +++ b/scripts/verify-x68000-input.sh @@ -18,7 +18,7 @@ # leave Up down. # # Text-plane geometry: four 1bpp planes 0x20000 apart at 0xE00000, 128 -# bytes per row, leftmost pixel in bit 7; the 320x200 stage is centred at +# bytes per row, leftmost pixel in bit 7; the 320x200 stage is centered at # origin (96, 156). Verification row = stage y 198 -> screen y 354. # # bash scripts/verify-x68000-input.sh @@ -53,8 +53,8 @@ local keysAt = 0 local done = false -- KEYS's row 4 cells (examples/keys: 28x26 cells 32 and 30 apart from --- (2, 2)) for Return, Alt, Up, Down, Left, and Right: their centres' x, and --- the row's centre y. A lit cell is colour 2. +-- (2, 2)) for Return, Alt, Up, Down, Left, and Right: their centers' x, and +-- the row's center y. A lit cell is color 2. local CELL_X = { 80, 144, 176, 208, 240, 272 } local CELL_Y = 135 local LIT = 2 @@ -78,10 +78,10 @@ local STEPS = { } local fields = {} --- Stage pixel (px, y) -> its 4-bit colour index. The port presents to the +-- Stage pixel (px, y) -> its 4-bit color index. The port presents to the -- TEXT plane (src/x68000/hal.c X68K_PRESENT_TEXT): four bit planes 128 KB -- apart at 0xE00000, 128 bytes per row, leftmost pixel in bit 7, the stage --- centred at (96, 156). (Before 2026-09-24 it drew the graphics plane at +-- centered at (96, 156). (Before 2026-09-24 it drew the graphics plane at -- 0xC00000, which this gate used to read.) -- 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 diff --git a/scripts/verify-x68000-joy.sh b/scripts/verify-x68000-joy.sh index 9408a13..e4c37d1 100755 --- a/scripts/verify-x68000-joy.sh +++ b/scripts/verify-x68000-joy.sh @@ -16,7 +16,7 @@ # diagonals are exact. # # JOY.X (examples/joy) redraws, for each stick, a dot at one of nine positions -# (the axis signs; green off-centre, grey at rest), two trigger squares, and a +# (the axis signs; green off-center, gray at rest), two trigger squares, and a # patch that says whether the port reports a stick. The Lua decodes all of it # from the text plane and compares the WHOLE state of BOTH sticks after every # step: each direction alone, two diagonals, each trigger alone and both @@ -25,7 +25,7 @@ # ports shows up too. # # Text-plane geometry: four 1bpp planes 0x20000 apart at 0xE00000, 128 bytes -# per row, leftmost pixel in bit 7; the 320x200 stage is centred at origin +# per row, leftmost pixel in bit 7; the 320x200 stage is centered at origin # (96, 156). # # Usage: scripts/verify-x68000-joy.sh @@ -77,7 +77,7 @@ local SETTLE = 20 -- frames between holding the lines and reading back local ORIGIN_X, ORIGIN_Y = 96, 156 local TVRAM, PLANE_STEP, TVRAM_STRIDE, PLANES = 0xE00000, 0x20000, 128, 4 --- examples/joy/joy.c: its layout and colours. +-- examples/joy/joy.c: its layout and colors. local STICK_LEFT = { 24, 216 } local STICK_TOP = 16 local FRAME_SIZE = 80 @@ -124,7 +124,7 @@ for stick = 1, 2 do end end --- Stage pixel (x, y) -> its colour index, one bit from each text plane. +-- Stage pixel (x, y) -> its color index, one bit from each text 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 -- on screen. @@ -159,7 +159,7 @@ local function readStick(stick) end end local dot = (#lit == 1) and lit[1] or ("dots[" .. table.concat(lit, ";") .. "]") - local centreX = left + FRAME_SIZE // 2 + local centerX = left + FRAME_SIZE // 2 local function button(x) local c = stagePixel(x + BUTTON_W // 2, BUTTON_TOP + BUTTON_H // 2) if c == COLOR_BUTTON_ON then return "1" end @@ -168,7 +168,7 @@ local function readStick(stick) end local statusColor = stagePixel(left + (FRAME_SIZE - STATUS_W) // 2 + STATUS_W // 2, STATUS_TOP + STATUS_H // 2) local status = (statusColor == COLOR_CONNECTED) and "1" or ((statusColor == COLOR_DISCONNECTED) and "0" or ("st" .. statusColor .. "?")) - return dot .. "," .. button(centreX - BUTTON_W - BUTTON_GAP // 2) .. "," .. button(centreX + BUTTON_GAP // 2) .. "," .. status + return dot .. "," .. button(centerX - BUTTON_W - BUTTON_GAP // 2) .. "," .. button(centerX + BUTTON_GAP // 2) .. "," .. status end -- The state a set of held lines must produce: positive wins a pair, as on the @@ -225,7 +225,7 @@ emu.register_frame_done(function() frame = frame + 1 if done then return end if readyAt == 0 then - -- JOY is up once both status patches are painted, in either colour. + -- JOY is up once both status patches are painted, in either color. if frame % SAMPLE == 0 then local one = stagePixel(STICK_LEFT[1] + STATUS_W // 2, STATUS_TOP + STATUS_H // 2) local two = stagePixel(STICK_LEFT[2] + STATUS_W // 2, STATUS_TOP + STATUS_H // 2) diff --git a/scripts/verify-x68000-mouse.sh b/scripts/verify-x68000-mouse.sh index daa2e08..334b78b 100755 --- a/scripts/verify-x68000-mouse.sh +++ b/scripts/verify-x68000-mouse.sh @@ -27,7 +27,7 @@ # one clamped 127-unit packet). # # Text-plane geometry: four 1bpp planes 0x20000 apart at 0xE00000, 128 -# bytes per row, leftmost pixel in bit 7; the 320x200 stage is centred at +# bytes per row, leftmost pixel in bit 7; the 320x200 stage is centered at # origin (96, 156). Mouse verification row = stage y 199 -> screen y 355. # # bash scripts/verify-x68000-mouse.sh @@ -60,10 +60,10 @@ local done = false local checks = 0 local fails = {} --- Stage pixel (px, y) -> its 4-bit colour index. The port presents to the +-- Stage pixel (px, y) -> its 4-bit color index. The port presents to the -- TEXT plane (src/x68000/hal.c X68K_PRESENT_TEXT): four bit planes 128 KB -- apart at 0xE00000, 128 bytes per row, leftmost pixel in bit 7, the stage --- centred at (96, 156). (Before 2026-09-24 it drew the graphics plane at +-- centered at (96, 156). (Before 2026-09-24 it drew the graphics plane at -- 0xC00000, which this gate used to read.) -- 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 @@ -88,7 +88,7 @@ local function pix(px, y) end -- Decode the mouse verification row (stage y 199): 5A5A sentinel, --- present/left/right/middle nibbles, then x and y as 4 nibbles each. +-- present/left/right nibbles and a 0 one, then x and y as 4 nibbles each. local function readMouse() local m = {} m.sentinel = pix(0, 199) == 0x5 and pix(1, 199) == 0xA and @@ -96,7 +96,7 @@ local function readMouse() m.present = pix(4, 199) m.left = pix(5, 199) m.right = pix(6, 199) - m.middle = pix(7, 199) + m.zero = pix(7, 199) m.x = (pix(8, 199) << 12) | (pix(9, 199) << 8) | (pix(10, 199) << 4) | pix(11, 199) m.y = (pix(12, 199) << 12) | (pix(13, 199) << 8) | (pix(14, 199) << 4) | pix(15, 199) return m @@ -139,7 +139,7 @@ local function expectRow(step, m, x, y, l, r) expect(step .. ":y", m.y, y) expect(step .. ":left", m.left, l) expect(step .. ":right", m.right, r) - expect(step .. ":middle", m.middle, 0) + expect(step .. ":zero", m.zero, 0) end -- Steps run at fixed offsets from t0 (the frame the sentinel landed @@ -198,7 +198,7 @@ emu.register_frame_done(function() if not biased and frame == 300 then -- Mid-boot counter bias for two-directional headroom. However -- the boot-time solicitation history consumes this, the slam - -- steps above make the position deterministic afterwards. + -- steps above make the position deterministic afterward. biased = true axisX:set_value(2000) axisY:set_value(2000) diff --git a/scripts/verify-x68000-scb.sh b/scripts/verify-x68000-scb.sh index 532ddf8..455b39d 100755 --- a/scripts/verify-x68000-scb.sh +++ b/scripts/verify-x68000-scb.sh @@ -8,17 +8,17 @@ # a screen that changes palette 147 times a frame. # # The X68000 port presents to the text plane, which has ONE 16-entry palette, -# so it honours the SCB by reloading that palette as the beam scans: a CRTC +# so it honors the SCB by reloading that palette as the beam scans: a CRTC # raster interrupt (src/x68000/jlX68kRasterIsr.s) stores a band's sixteen -# colours in the horizontal blanking before the band's first line. Each reload +# colors in the horizontal blanking before the band's first line. Each reload # has a window of about 400 dots (~18 us): after the last stage pixel of the # line above and before the first stage pixel of the band. Land early and the -# end of the line above shows the new colours; land late and the start of the +# end of the line above shows the new colors; land late and the start of the # band shows the old ones; miss and the whole band does. # # Boots Human68k headless under MAME with SCB.X (examples/scb): all sixteen -# palettes, every colour distinct per palette, a change on every line for 128 -# of the 200 lines, irregular runs for the rest, 20 stripes using every colour +# palettes, every color distinct per palette, a change on every line for 128 +# of the 200 lines, irregular runs for the rest, 20 stripes using every color # on every line, the whole stage redrawn and every palette set again on every # pass. Once the stripes are up and have settled, the Lua below taps every # write to the text palette ($E82200), to the raster compare (CRTC R09), to @@ -169,7 +169,7 @@ do end end --- Colour i of palette p as \$0RGB (colour 0 is black in every palette). +-- Color i of palette p as \$0RGB (color 0 is black in every palette). local function rgb12(p, i) if i == 0 then return 0 diff --git a/scripts/verify-x68000-serial-mouse.sh b/scripts/verify-x68000-serial-mouse.sh index ba74028..f252f39 100755 --- a/scripts/verify-x68000-serial-mouse.sh +++ b/scripts/verify-x68000-serial-mouse.sh @@ -61,7 +61,7 @@ # The guest reports through two verification rows at the bottom of the stage # (one nibble per pixel, high nibble first), read from the text plane: # row 198 3C3C, status, received(8), bad, sent(8), short writes, first bad(8) -# row 199 5A5A, present/left/right/middle, x, y (examples/keys' mouse row) +# row 199 5A5A, present/left/right/0, x, y (examples/keys' mouse row) # The serial row changes at most every 4 frames, so a read counts only when # two consecutive frames agree (a present caught half done cannot). # @@ -164,7 +164,7 @@ local axisValY = 0 -- Stage geometry on the text plane (src/x68000/hal.c X68K_PRESENT_TEXT): four -- bit planes 128 KB apart at 0xE00000, 128 bytes per row, leftmost pixel in --- bit 7, the stage centred at (96, 156). +-- bit 7, the stage centered at (96, 156). local TEXT_BASE = 0xE00000 local PLANES = 4 local PLANE_BYTES = 0x20000 @@ -281,7 +281,7 @@ local function parseSerial(t) return s end --- The flags word is one nibble per predicate: present, left, right, middle. +-- The flags word is one nibble per predicate: present, left, right, then 0. local function readMouse() local take = fields(rowNibbles(MOUSE_ROW, MOUSE_NIBBLES)) local m = {} @@ -289,7 +289,7 @@ local function readMouse() m.present = take(1) m.left = take(1) m.right = take(1) - m.middle = take(1) + m.zero = take(1) m.x = take(WORD) m.y = take(WORD) return m @@ -363,8 +363,8 @@ local function expectMouse(step, x, y, l, r) expect(step .. ":sentinel", m.sentinel) expect(step .. ":present", m.present == 1, tostring(m.present)) expect(step .. ":xy", m.x == x and m.y == y, string.format("%d,%d want %d,%d", m.x, m.y, x, y)) - expect(step .. ":buttons", m.left == l and m.right == r and m.middle == 0, - string.format("%d%d%d want %d%d0", m.left, m.right, m.middle, l, r)) + expect(step .. ":buttons", m.left == l and m.right == r and m.zero == 0, + string.format("%d%d%d want %d%d0", m.left, m.right, m.zero, l, r)) end -- A steady serial read: fn runs with the row once two consecutive frames diff --git a/scripts/verify-x68000-serial-scb.sh b/scripts/verify-x68000-serial-scb.sh index f88870b..b7fc8ee 100755 --- a/scripts/verify-x68000-serial-scb.sh +++ b/scripts/verify-x68000-serial-scb.sh @@ -6,7 +6,7 @@ # verify-x68000-serial-scb.sh - X68000 RS-232C receive (and the mouse) under a # per-line palette change: the SCB raster interrupt and the SCC together. # -# The X68000 port honours the SCB by reloading the text plane's one palette as +# The X68000 port honors the SCB by reloading the text plane's one palette as # the beam scans (src/x68000/jlX68kRasterIsr.s). A palette change on every line # keeps that interrupt at IPL 6 for the whole run, about one raster (31.8 us) # a change plus two rasters of lead, and the SCC is IPL 5, so every byte that diff --git a/scripts/verify-x68000-tilebank.sh b/scripts/verify-x68000-tilebank.sh index efcd44f..1f30414 100755 --- a/scripts/verify-x68000-tilebank.sh +++ b/scripts/verify-x68000-tilebank.sh @@ -16,16 +16,16 @@ # every pixel of both against the PNG, hashes both, tries every other # target's bake, logs one TILEBANK line and shows its verdict lamp. The Lua # below waits for the lamp in text VRAM, then presses a key so the program -# exits -- Human68k only finalises joeylog.txt's directory entry when the +# exits -- Human68k only finalizes joeylog.txt's directory entry when the # program's stdio closes it -- and stops MAME. The line, extracted from the # data disk, 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, and the lamp must be the PASS colour. +# refused, and the lamp must be the PASS color. # # Text-plane geometry (see verify-x68000-input.sh): four 1bpp planes 0x20000 # apart at 0xE00000, 128 bytes per row, leftmost pixel in bit 7, the 320x200 -# stage centred at (96, 156). The lamp is examples/tilebank's: 64x64 at stage -# (128, 68), colour 1 for PASS and 2 for FAIL, with FILL_COLOR (9) either side +# stage centered at (96, 156). The lamp is examples/tilebank's: 64x64 at stage +# (128, 68), color 1 for PASS and 2 for FAIL, with FILL_COLOR (9) either side # of it on its rows. # # Usage: scripts/verify-x68000-tilebank.sh @@ -104,7 +104,7 @@ local function stagePixel(px, py) return v end --- The lamp colour when the whole lamp shows one verdict colour with +-- The lamp color when the whole lamp shows one verdict color with -- FILL_COLOR on its rows either side, else 0. local function lampColor() local left = LAMP_X + MARGIN diff --git a/src/amiga/amigaCustom.h b/src/amiga/amigaCustom.h index e936698..78e542e 100644 --- a/src/amiga/amigaCustom.h +++ b/src/amiga/amigaCustom.h @@ -32,4 +32,10 @@ static volatile struct Custom *const gCust = &custom; #define AMIGA_BLT_MINTERM_SET 0xFAu // D = A | C #define AMIGA_BLT_MINTERM_CLEAR 0x0Au // D = ~A & C +// The beam: VHPOSR's high byte is the line's low 8 bits, VPOSR bit 0 its +// ninth; a line is 227 color clocks (227.5 on NTSC), Paula's period unit. +#define AMIGA_VPOSR_V8 0x0001u +#define AMIGA_VHPOSR_V_SHIFT 8 +#define AMIGA_LINE_COLOR_CLOCKS 227u + #endif diff --git a/src/amiga/audio.c b/src/amiga/audio.c index b736ada..c7df591 100644 --- a/src/amiga/audio.c +++ b/src/amiga/audio.c @@ -10,7 +10,8 @@ // with-Intuition graphics HAL. mt_install allocates all four Paula // channels via audio.device; mt_init loads a .MOD; mt_Enable=1 starts // playback; mt_playfx triggers a SFX on a free channel (or the worst -// channel if all are busy) with priority arbitration. +// channel if all are busy) with priority arbitration, and mt_loopfx loops +// a streaming slot's ring (jlpAudioPlaySfxStream). // // PTPlayer expects modules and SFX samples to live in Chip RAM. The // asset pipeline does not yet partition into Chip vs. Fast, so we @@ -43,12 +44,12 @@ extern UBYTE mt_E8Trigger; // ----- Constants ----- -// Paula plays a sample at a period (colour clocks a sample), not a rate: +// Paula plays a sample at a period (color clocks a sample), not a rate: // period = clock / rate, clamped to Paula's legal periods. The clock is the // machine's crystal, 3546895 Hz on a PAL Amiga and 3579545 Hz on an NTSC // one, whatever its display shows; it is five times the E-clock exec // measures at boot (paulaClock). -#define ECLOCKS_PER_COLOUR 5UL +#define COLOR_CLOCKS_PER_ECLOCK 5UL #define PAULA_PERIOD_MIN 124 // Paula HW limit #define PAULA_PERIOD_MAX 65535 #define PERIOD_DEFAULT 428 // ~middle C, for a rate of 0 @@ -110,40 +111,59 @@ extern UBYTE mt_E8Trigger; // ----- Module state ----- -// Streaming SFX chunk size, in bytes. PTPlayer's mt_playfx plays a -// one-shot sample and does not queue, so a stream is played one chunk at -// a time with the next one fired from jlpAudioFrameTick when the current -// is due to end. Bigger chunks mean fewer seams; 4 KB is ~0.8 s at 5 kHz, -// which covers a whole speech sample with no seam at all. -#define SFX_STREAM_CHUNK 4096u +// Streaming SFX: a ring in Chip RAM that PTPlayer's mt_loopfx loops on the +// slot's channels for as long as the stream plays, refilled from +// jlpAudioFrameTick wherever Paula has played -- which the E-clock says: +// an E-clock tick is COLOR_CLOCKS_PER_ECLOCK color clocks, and a sample +// lasts a period of them -- so the stream has no seams. (One-shot chunks had: each +// was fetched into the buffer Paula still played, a frame and more early.) +// 8 KB is 0.37 s at 22 kHz, about as long as a program may stall without a +// gap. mt_loopfx wants a zero word ahead of the loop, which it idles on once +// stopped. STREAM_GUARD samples stay unwritten ahead of Paula, for the +// estimate of when she started: PTPlayer takes a loop at its next tick (the +// CIA-B timer A count says how far off that is, in E-clock ticks) and turns +// the DMA on STREAM_DMA_DELAY ticks after it (its DMADELAY). +#define STREAM_RING_BYTES 8192u +#define STREAM_IDLE_BYTES 2u +#define STREAM_GUARD 256u +#define STREAM_DMA_DELAY 576u +#define CENTER_CHANNELS 2u // a centered sound plays on a channel each side +#define STREAM_NO_END 0xFFFFFFFFUL +#define CIAB_TIMER_A_LOW ((volatile UBYTE *)0xBFD400L) +#define CIAB_TIMER_A_HIGH ((volatile UBYTE *)0xBFD500L) +#define BITS_PER_BYTE 8 typedef struct { - UBYTE *samples; // Chip-RAM copy of one SFX sample - ULONG sampleBytes; // its allocation + UBYTE *samples; // Chip-RAM copy of one SFX sample + ULONG sampleBytes; // its allocation + BYTE cha; // Paula channel, CHANNEL_PICK = centered + SfxChanStatus *status; // what mt_playfx returned: the channel it took + SfxChanStatus *status2; // a centered sound's second channel, or NULL // ---- streaming (jlpAudioPlaySfxStream) ---- - UBYTE *chunk; // Chip-RAM chunk buffer, SFX_STREAM_CHUNK - BYTE cha; // Paula channel, CHANNEL_PICK = centred - SfxChanStatus *status; // what mt_playfx returned: the channel it took - SfxChanStatus *status2; // a centred sound's second channel, or NULL + UBYTE *ring; // Chip-RAM ring: the idle word, then STREAM_RING_BYTES + BYTE streamCha[CENTER_CHANNELS]; // the channels mt_loopfx loops it on, or CHANNEL_PICK jlAudioStreamFillT fill; void *ctx; - uint16_t rateHz; - uint16_t dueFrame; // the VBL count the next chunk goes out at - uint8_t decimate; // every decimate-th sample plays (jlAudioDecimate), at rateHz - uint8_t phase; // jlAudioDecimate's phase between chunks + struct EClockVal start; // when Paula started the ring + ULONG written; // samples written into the ring since then + ULONG endAt; // the samples in all once fill has ended, else STREAM_NO_END + UWORD period; // Paula's + uint8_t decimate; // every decimate-th sample plays (jlAudioDecimate) + uint8_t phase; // jlAudioDecimate's phase between fills bool active; } SfxSlotT; static UBYTE *gModuleChip = NULL; // Chip-RAM copy of the playing module static ULONG gModuleLength = 0; // bytes to FreeMem on swap / shutdown static SfxSlotT gSfxSlots[JL_AUDIO_SFX_SLOTS]; -// Which voices and the noise are sounding, so a centred sound can find a +// Which voices and the noise are sounding, so a centered sound can find a // channel on each side that nothing holds. static bool gVoiceOn[JL_AUDIO_VOICES]; -static bool gNoiseOn = false; +static bool gNoiseOn = false; +static UBYTE gNoiseQuarter = 0u; // the noise copy playing: the quarter swing's -// A centred sound's channels, by side, best first: Paula feeds 0 and 3 to +// A centered sound's channels, by side, best first: Paula feeds 0 and 3 to // the left and 1 and 2 to the right. Channel 3 first: PTPlayer's own picks // never take it (maskNoiseChannel), so it is most often free. static const BYTE kLeftChannels[SIDE_CHANNELS] = { NOISE_CHANNEL, 0 }; @@ -200,11 +220,15 @@ static BYTE channelFree(const BYTE *candidates); static bool channelHeld(BYTE cha); static void maskNoiseChannel(void); static ULONG paulaClock(void); +static void paulaIdleWait(UWORD period); static UWORD paulaMaxRate(void); static UWORD periodForRate(ULONG rateHz); static void sfxPlay(uint8_t slot, SfxStructure *sfx); static void slotSilence(uint8_t slot); -static bool streamChunk(uint8_t slot); +static ULONG streamPlayed(const SfxSlotT *s); +static bool streamRefill(uint8_t slot); +static void streamStartAt(struct EClockVal *at); +static void streamWrite(SfxSlotT *s, ULONG played); static bool tickOpen(void); static void tickQueue(void); static void tickSoftInt(void); @@ -242,11 +266,14 @@ static bool channelHeld(BYTE cha) { return true; } for (slot = 0u; slot < JL_AUDIO_SFX_SLOTS; slot++) { - for (i = 0u; i < 2u; i++) { + for (i = 0u; i < CENTER_CHANNELS; i++) { status = i == 0u ? gSfxSlots[slot].status : gSfxSlots[slot].status2; if (status != NULL && status->n_sfxpri != 0u && status->n_index == (UBYTE)cha) { return true; } + if (gSfxSlots[slot].active && gSfxSlots[slot].streamCha[i] == cha) { + return true; + } } } return false; @@ -265,10 +292,33 @@ static void maskNoiseChannel(void) { } -// Paula's clock (colour clocks a second), from the E-clock: exec reads it +// Paula's clock (color clocks a second), from the E-clock: exec reads it // at boot, 709379 Hz on a PAL machine and 715909 Hz on an NTSC one. static ULONG paulaClock(void) { - return SysBase->ex_EClockFrequency * ECLOCKS_PER_COLOUR; + return SysBase->ex_EClockFrequency * COLOR_CLOCKS_PER_ECLOCK; +} + + +// Paula restarts a channel from its location registers only once it has +// gone idle: with its DMA turned off it plays out the sample it is on. Two +// periods of that, in lines, and one more for the line the wait starts on. +#define PAULA_IDLE_PERIODS 2u +static void paulaIdleWait(UWORD period) { + UWORD lines; + UWORD seen; + UBYTE line; + UBYTE now; + + lines = (UWORD)((PAULA_IDLE_PERIODS * (ULONG)period + AMIGA_LINE_COLOR_CLOCKS - 1u) / AMIGA_LINE_COLOR_CLOCKS + 1u); + line = (UBYTE)(gCust->vhposr >> AMIGA_VHPOSR_V_SHIFT); + seen = 0u; + while (seen < lines) { + now = (UBYTE)(gCust->vhposr >> AMIGA_VHPOSR_V_SHIFT); + if (now != line) { + line = now; + seen++; + } + } } @@ -285,7 +335,9 @@ static UWORD periodForRate(ULONG rateHz) { if (rateHz == 0) { return PERIOD_DEFAULT; } - period = paulaClock() / rateHz; + // Rounded: truncated, every rate played sharp, by up to a period in + // 124 (0.5% at 22 kHz). + period = (paulaClock() + rateHz / 2u) / rateHz; if (period < PAULA_PERIOD_MIN) { period = PAULA_PERIOD_MIN; } @@ -315,13 +367,16 @@ bool jlpAudioInit(void) { gModuleChip = NULL; gModuleLength = 0; for (i = 0; i < JL_AUDIO_SFX_SLOTS; i++) { - gSfxSlots[i].samples = NULL; - gSfxSlots[i].sampleBytes = 0; - // Centred, not 0: zero is Paula channel 0, which would silently pin + gSfxSlots[i].samples = NULL; + gSfxSlots[i].sampleBytes = 0; + // Centered, not 0: zero is Paula channel 0, which would silently pin // every slot hard left. - gSfxSlots[i].cha = CHANNEL_PICK; - gSfxSlots[i].status = NULL; - gSfxSlots[i].status2 = NULL; + gSfxSlots[i].cha = CHANNEL_PICK; + gSfxSlots[i].status = NULL; + gSfxSlots[i].status2 = NULL; + gSfxSlots[i].streamCha[0] = CHANNEL_PICK; + gSfxSlots[i].streamCha[1] = CHANNEL_PICK; + gSfxSlots[i].active = false; } return true; } @@ -373,9 +428,9 @@ void jlpAudioShutdown(void) { gSfxSlots[i].sampleBytes = 0; } gSfxSlots[i].active = false; - if (gSfxSlots[i].chunk != NULL) { - FreeMem(gSfxSlots[i].chunk, (ULONG)SFX_STREAM_CHUNK); - gSfxSlots[i].chunk = NULL; + if (gSfxSlots[i].ring != NULL) { + FreeMem(gSfxSlots[i].ring, (ULONG)(STREAM_IDLE_BYTES + STREAM_RING_BYTES)); + gSfxSlots[i].ring = NULL; } } } @@ -430,7 +485,7 @@ void jlpAudioPlayMod(const uint8_t *data, uint32_t length, bool loop) { } -// Hand a slot's sound to PTPlayer: on the slot's channel, or for a centred +// Hand a slot's sound to PTPlayer: on the slot's channel, or for a centered // slot on a channel each side when both are free, so it reaches both ears; // failing that, on the one channel PTPlayer picks. static void sfxPlay(uint8_t slot, SfxStructure *sfx) { @@ -459,64 +514,127 @@ static void sfxPlay(uint8_t slot, SfxStructure *sfx) { // Stop the slot's own sound, and only it. mt_playfx hands back its // channel's status, which says which channel the sound took and, while it // plays, the priority it was given: the slot's own unless another slot's -// sound has the channel since, which is then left alone. +// sound has the channel since, which is then left alone. A stream's loops +// (mt_loopfx) the slot keeps the channels of, and stops them. static void slotSilence(uint8_t slot) { + SfxSlotT *s; SfxChanStatus *status; uint8_t i; - for (i = 0u; i < 2u; i++) { - status = i == 0u ? gSfxSlots[slot].status : gSfxSlots[slot].status2; + s = &gSfxSlots[slot]; + for (i = 0u; i < CENTER_CHANNELS; i++) { + status = i == 0u ? s->status : s->status2; if (status != NULL && status->n_sfxpri == (UBYTE)SLOT_PRIORITY(slot)) { mt_stopfx((void *)&custom, status->n_index); } + if (s->active && s->streamCha[i] != CHANNEL_PICK) { + mt_stopfx((void *)&custom, s->streamCha[i]); + } + s->streamCha[i] = CHANNEL_PICK; } - gSfxSlots[slot].status = NULL; - gSfxSlots[slot].status2 = NULL; + s->status = NULL; + s->status2 = NULL; + s->active = false; } -// Pull one chunk from a streaming slot and hand it to PTPlayer. -// Returns false when the stream has ended (fill returned 0). -static bool streamChunk(uint8_t slot) { - SfxSlotT *s = &gSfxSlots[slot]; - SfxStructure sfx; - uint32_t got; +// Samples Paula has played of the slot's stream: E-clock ticks since she +// started the ring, in color clocks, over the period (0 before she starts). +static ULONG streamPlayed(const SfxSlotT *s) { + struct EClockVal now; + unsigned long long ticks; - // The fill contract is SIGNED 8-bit, which is exactly what Paula - // wants, so the callback writes straight into Chip RAM. A stream faster - // than Paula keeps every decimate-th sample, packed to the front, and - // asks again if a short fill left too few. - do { - got = s->fill(s->ctx, (int8_t *)s->chunk, (uint32_t)SFX_STREAM_CHUNK); - if (got == 0u || got > (uint32_t)SFX_STREAM_CHUNK) { - return false; - } - if (s->decimate > 1u) { - got = jlAudioDecimate(s->chunk, s->chunk, got, s->decimate, &s->phase); - } - } while (got < 2u); - got &= ~1u; // Paula counts 16-bit words - - sfx.sfx_ptr = s->chunk; - sfx.sfx_len = (UWORD)(got >> 1); - sfx.sfx_per = periodForRate(s->rateHz); - sfx.sfx_vol = SFX_VOLUME_MAX; - sfx.sfx_pri = SLOT_PRIORITY(slot); - sfxPlay(slot, &sfx); - - // Fire the next chunk a frame early rather than leaving a gap. - { - uint32_t frames = (got * (uint32_t)jlpFrameHz()) / (uint32_t)s->rateHz; - - if (frames > 1u) { - frames--; - } - s->dueFrame = (uint16_t)(jlpFrameCount() + frames); + (void)ReadEClock(&now); + if (now.ev_hi < s->start.ev_hi || (now.ev_hi == s->start.ev_hi && now.ev_lo < s->start.ev_lo)) { + return 0u; } + ticks = (((unsigned long long)(now.ev_hi - s->start.ev_hi)) << 32) + now.ev_lo - s->start.ev_lo; + return (ULONG)(ticks * COLOR_CLOCKS_PER_ECLOCK / s->period); +} + + +// Top the slot's ring up to where Paula is, and stop the loop once she has +// played the last of the stream. Returns false when the stream has ended. +static bool streamRefill(uint8_t slot) { + SfxSlotT *s; + ULONG played; + + s = &gSfxSlots[slot]; + played = streamPlayed(s); + if (s->endAt != STREAM_NO_END && (LONG)(played - s->endAt) >= 0) { + slotSilence(slot); + return false; + } + streamWrite(s, played); return true; } +// When a loop just asked for starts: at PTPlayer's next tick, which the +// CIA-B timer A count says is that many E-clock ticks off, and +// STREAM_DMA_DELAY on. Called with interrupts off (Disable) since the +// asking, so the tick that takes the loop is the one counted to, or one +// already due, which starts it sooner -- never later. +static void streamStartAt(struct EClockVal *at) { + UBYTE high; + UBYTE low; + ULONG toTick; + + do { + high = *CIAB_TIMER_A_HIGH; + low = *CIAB_TIMER_A_LOW; + } while (high != *CIAB_TIMER_A_HIGH); + toTick = (((ULONG)high << BITS_PER_BYTE) | low) + STREAM_DMA_DELAY; + (void)ReadEClock(at); + at->ev_lo += toTick; + if (at->ev_lo < toTick) { + at->ev_hi++; + } +} + + +// Write the stream into the ring from where it left off up to +// STREAM_GUARD short of where Paula is, played: what fill gives (every +// decimate-th sample of it), then, once fill has ended, silence -- so a ring +// Paula goes on looping past the end plays nothing. A program that let her +// overtake what was written loses that part and goes on from where she is. +static void streamWrite(SfxSlotT *s, ULONG played) { + UBYTE *dst; + ULONG room; + ULONG at; + ULONG span; + ULONG got; + + if ((LONG)(played - s->written) > 0) { + s->written = played; + } + if (s->written - played >= STREAM_RING_BYTES - STREAM_GUARD) { + return; + } + room = STREAM_RING_BYTES - STREAM_GUARD - (s->written - played); + while (room > 0u) { + at = s->written % STREAM_RING_BYTES; + span = STREAM_RING_BYTES - at < room ? STREAM_RING_BYTES - at : room; + dst = s->ring + STREAM_IDLE_BYTES + at; + got = span; + if (s->endAt == STREAM_NO_END) { + got = s->fill(s->ctx, (int8_t *)dst, span); + if (got == 0u || got > span) { + s->endAt = s->written; + got = span; + } else if (s->decimate > 1u) { + got = jlAudioDecimate(dst, dst, got, s->decimate, &s->phase); + } + } + if (s->endAt != STREAM_NO_END) { + memset(dst, 0, span); + } + s->written += got; + room -= got; + } +} + + // timer.device's E-clock unit, opened once, with a reply port that raises // tickSoftInt. static bool tickOpen(void) { @@ -653,15 +771,16 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1 } -// Streaming SFX. PTPlayer's mt_playfx wants a complete sample at fire -// time, so the stream is chunked rather than refilled mid-playback: -// arm the slot, push the first chunk now, and let jlpAudioFrameTick -// push each following one as the previous is due to run out. The seam -// therefore quantises to a 50 Hz frame, which is the jitter the public -// contract documents. Each slot gets one SFX_STREAM_CHUNK Chip-RAM -// buffer, allocated on first use and freed in jlpAudioShutdown. +// Streaming SFX: the ring (STREAM_RING_BYTES) is filled, then looped by +// mt_loopfx on the slot's channel -- a channel each side for a centered +// slot -- and jlpAudioFrameTick tops it up as Paula plays it +// (streamRefill). Each slot's ring is allocated on first use and freed in +// jlpAudioShutdown. void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) { - SfxSlotT *s; + SfxSlotT *s; + SfxStructure sfx; + BYTE side; + uint8_t i; if (!gInstalled || slot >= JL_AUDIO_SFX_SLOTS) { return; @@ -669,10 +788,14 @@ void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uin if (fill == (jlAudioStreamFillT)0 || rateHz == 0) { return; } + if (!tickOpen()) { + return; + } s = &gSfxSlots[slot]; - if (s->chunk == NULL) { - s->chunk = (UBYTE *)AllocMem((ULONG)SFX_STREAM_CHUNK, MEMF_CHIP); - if (s->chunk == NULL) { + slotSilence(slot); + if (s->ring == NULL) { + s->ring = (UBYTE *)AllocMem((ULONG)(STREAM_IDLE_BYTES + STREAM_RING_BYTES), MEMF_CHIP | MEMF_CLEAR); + if (s->ring == NULL) { return; } } @@ -681,11 +804,37 @@ void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uin s->phase = 0u; s->fill = fill; s->ctx = ctx; - s->rateHz = (uint16_t)(rateHz / s->decimate); - s->active = true; - if (!streamChunk(slot)) { - s->active = false; + s->period = periodForRate((ULONG)(rateHz / s->decimate)); + s->written = 0u; + s->endAt = STREAM_NO_END; + streamWrite(s, 0u); + + s->streamCha[0] = s->cha; + if (s->cha == CHANNEL_PICK) { + side = channelFree(kLeftChannels); + s->streamCha[0] = side != CHANNEL_PICK ? side : kLeftChannels[0]; + side = channelFree(kRightChannels); + s->streamCha[1] = side != CHANNEL_PICK ? side : kRightChannels[0]; } + sfx.sfx_ptr = s->ring; + sfx.sfx_len = (UWORD)((STREAM_IDLE_BYTES + STREAM_RING_BYTES) / 2u); // Paula counts words + sfx.sfx_per = s->period; + sfx.sfx_vol = SFX_VOLUME_MAX; + sfx.sfx_pri = SLOT_PRIORITY(slot); + // The loops are asked for, then the count read, with interrupts off: a + // tick in between only starts them a tick before the estimate, which the + // ring allows (Paula ahead of the estimate plays what is written); one + // never starts them after it. + Disable(); + for (i = 0u; i < CENTER_CHANNELS; i++) { + if (s->streamCha[i] != CHANNEL_PICK) { + sfx.sfx_cha = s->streamCha[i]; + mt_loopfx((void *)&custom, &sfx); + } + } + streamStartAt(&s->start); + Enable(); + s->active = true; } @@ -694,6 +843,10 @@ void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uin // squares; while a MOD plays, PTPlayer's interrupt owns the channel // and noise writes lose -- the same exclusivity as the tone voices. void jlpAudioNoise(uint8_t pitch, uint8_t atten) { + bool wasOn; + UBYTE quarter; + + wasOn = gNoiseOn; gNoiseOn = atten < VOICE_ATTEN_OFF; if (!gNoiseOn) { gCust->dmacon = DMAF_AUD3; @@ -734,8 +887,17 @@ void jlpAudioNoise(uint8_t pitch, uint8_t atten) { // ptr/len writes latch at the next loop reload; per/vol take // effect immediately -- a live retune is click-free and the - // redundant DMA set is harmless. - gCust->aud[NOISE_CHANNEL].ac_ptr = (UWORD *)(gNoiseChip + (kAttenVolume[atten].quarter ? NOISE_BYTES : 0)); + // redundant DMA set is harmless. But a change of copy (the quarter + // swing's) waiting for the reload played the other copy at the new + // volume, 12 dB off, for up to a whole noise sample: the channel is + // restarted on the new one instead. + quarter = kAttenVolume[atten].quarter; + if (wasOn && quarter != gNoiseQuarter) { + gCust->dmacon = DMAF_AUD3; + paulaIdleWait(gNoisePeriod[NOISE_PITCH_MAX]); + } + gNoiseQuarter = quarter; + gCust->aud[NOISE_CHANNEL].ac_ptr = (UWORD *)(gNoiseChip + (quarter ? NOISE_BYTES : 0)); gCust->aud[NOISE_CHANNEL].ac_len = NOISE_BYTES / 2; // Paula length is in words gCust->aud[NOISE_CHANNEL].ac_per = gNoisePeriod[pitch]; gCust->aud[NOISE_CHANNEL].ac_vol = kAttenVolume[atten].vol; @@ -749,7 +911,7 @@ void jlpAudioNoise(uint8_t pitch, uint8_t atten) { // while a MOD is playing, PTPlayer rewrites all four channels' regs // from its CIA interrupt and voice writes lose -- same voices-vs-MOD // exclusivity as the ST's YM. A sound effect PTPlayer places itself can -// land on a voice's channel (a centred one looks for free channels +// land on a voice's channel (a centered one looks for free channels // first, sfxPlay); prefer the tracker/voice layer OR the MOD/SFX layer // per scene, not both. void jlpAudioVoice(uint8_t voice, uint16_t freqHz, uint8_t atten) { @@ -832,7 +994,7 @@ void jlpAudioStopMod(void) { // Paula pans per CHANNEL: 0 and 3 feed the left output, 1 and 2 the right. -// A panned slot pins its SFX to a channel on that side; a centred one plays +// A panned slot pins its SFX to a channel on that side; a centered one plays // on a channel each side when both are free (sfxPlay). void jlpAudioSetPan(uint8_t slot, uint8_t pan) { if (slot >= JL_AUDIO_SFX_SLOTS) { @@ -856,28 +1018,18 @@ void jlpAudioStopSfx(uint8_t slot) { if (!gInstalled || slot >= JL_AUDIO_SFX_SLOTS) { return; } - gSfxSlots[slot].active = false; slotSilence(slot); } void jlpAudioFrameTick(void) { - // Streaming slots: mt_playfx plays one chunk and stops, so the next - // chunk goes out here as the previous one runs down. + // Streaming slots: each ring is topped up to where Paula is. { uint8_t slot; for (slot = 0u; slot < JL_AUDIO_SFX_SLOTS; slot++) { - if (!gSfxSlots[slot].active) { - continue; - } - // Counted on the VBL, not in calls of this, so a program - // slower than the display still gets its chunks on time. - if ((int16_t)(jlpFrameCount() - gSfxSlots[slot].dueFrame) < 0) { - continue; - } - if (!streamChunk(slot)) { - gSfxSlots[slot].active = false; + if (gSfxSlots[slot].active) { + (void)streamRefill(slot); } } } diff --git a/src/amiga/hal.c b/src/amiga/hal.c index 6da809a..4168876 100644 --- a/src/amiga/hal.c +++ b/src/amiga/hal.c @@ -11,10 +11,10 @@ // // M2.5 scope (per-scanline palette / SCB emulation): // * Two band lists of our own in chip RAM WAIT for each SCB band's -// first line and MOVE its palette into the 16 colour registers. The +// first line and MOVE its palette into the 16 color registers. The // screen's user copper list, merged once, jumps to the one shown. // * A present that changes palettes or the SCB writes the other list, -// and the VBL server switches to it with the planes, so colours and +// and the VBL server switches to it with the planes, so colors and // pixels change on the same frame and a band that moves costs a list // write, not a MrgCop (see the band lists below). // @@ -134,13 +134,13 @@ static struct timerequest gClockRequest; // to COP2LC and COPJMP2; graphics.library's copinit sets COP2LC again every // frame before it uses it) to gTrampoline, which jumps to the list shown. A // band list holds what the merged list runs at the jump after it (its tail's -// head), band 0's colours, a WAIT and the colours of each later band, and +// head), band 0's colors, a WAIT and the colors of each later band, and // then the rest of the merged list's tail -- the end of the screen's display // and whatever is below it -- so the copper never has to jump back into a // list Intuition may have rebuilt. A present that changes palettes or the // SCB writes the list not shown and asks the VBL server to switch the // trampoline to it with the planes (FLIP_LIST). The server also loads band -// 0's colours into the merged list's own colour load above the screen, and +// 0's colors into the merged list's own color load above the screen, and // the planes into its bitplane pointer MOVEs, while that list is still the // one copperScan read; when Intuition rebuilds it (a screen moved, or // another brought to the front), the next present reads the new one. @@ -442,8 +442,8 @@ static uint8_t bandListUpdate(const jlSurfaceT *src) { // Write the stage's bands into band list idx, growing it when it is too // small (false when chip RAM runs out): the head of the merged list's tail -// (MOVEs it runs right after the jump), band 0's colours, a WAIT and the -// colours of each later band, then the rest of the tail. A band the beam +// (MOVEs it runs right after the jump), band 0's colors, a WAIT and the +// colors of each later band, then the rest of the tail. A band the beam // meets only at or past the tail's first WAIT, or past line 255, is left // out: the copper would never reach it before the tail. static bool bandListWrite(const jlSurfaceT *src, uint8_t idx) { @@ -551,7 +551,7 @@ static uint16_t bandsFromScb(const jlSurfaceT *src, uint8_t *lines, uint8_t *pal // WaitTOF). The trampoline leads to a one-instruction list, the end, until // band list 0 is written, from the merged list, before LoadView shows it; // if it cannot be, the jump becomes a no-op and the screen shows its own -// colours only. +// colors only. static void copperInstall(const jlSurfaceT *src) { struct UCopList *ucl; struct View *view; @@ -627,7 +627,7 @@ static bool copperListLive(void) { // Read the merged list the display runs: before the jump, the screen's own -// colour load (the first COLOR00..COLOR15 run) and its bitplane pointer +// color load (the first COLOR00..COLOR15 run) and its bitplane pointer // MOVEs; the jump and the line its WAIT waits for; after it, the tail to // the list's end, split at its first WAIT. gCopperReady says all were // found. A jump with no end after it is made a no-op, so the copper never @@ -747,8 +747,8 @@ static void patchBplCopperPokes(UBYTE *planes[AMIGA_BITPLANES]) { } -// Load colors into the merged list's own colour load above the screen, so -// the lines before the band list's first MOVEs land show band 0's colours. +// Load colors into the merged list's own color load above the screen, so +// the lines before the band list's first MOVEs land show band 0's colors. static void patchTopColors(const uint16_t *colors) { uint16_t col; @@ -1264,8 +1264,6 @@ static bool gVblInstalled = false; #define BEAM_FRAMES 2u #define FRAME_HZ_PAL 50u #define FRAME_HZ_NTSC 60u -#define VPOSR_V8 0x0001u -#define VHPOSR_V_SHIFT 8 static uint16_t gFrameHz = FRAME_HZ_PAL; @@ -1300,7 +1298,7 @@ static void frameHzMeasure(void) { highest = 0u; frames = 0u; while (frames < BEAM_FRAMES) { - line = (uint16_t)(((gCust->vposr & VPOSR_V8) << 8) | (gCust->vhposr >> VHPOSR_V_SHIFT)); + line = (uint16_t)(((gCust->vposr & AMIGA_VPOSR_V8) << AMIGA_VHPOSR_V_SHIFT) | (gCust->vhposr >> AMIGA_VHPOSR_V_SHIFT)); if (line < last) { frames++; } @@ -1323,8 +1321,8 @@ static void frameHzMeasure(void) { // // It also makes the flips a present asked for (flipRequest): the band list // through the trampoline, and, in the merged list while it is still the -// one copperScan read, the bitplane pointers and the screen's own colour -// load (band 0's colours for the lines above the band list's). The copper +// one copperScan read, the bitplane pointers and the screen's own color +// load (band 0's colors for the lines above the band list's). The copper // reaches the screen about 44 lines later, so they show from this frame. static ULONG vblServer(void) { uint8_t pending; @@ -1446,7 +1444,7 @@ void jlpShutdown(void) { // src/m68k/planarPlot.s the pixel walkers. extern void jlSurface68kFillCircle(uint8_t *const *planes, uint16_t cx, uint16_t cy, uint16_t r, uint16_t color); // src/m68k/planarPlot.s, shared with the X68000: Bresenham walkers with 16-way -// colour dispatch, so every plane write is one memory read-modify-write with +// color dispatch, so every plane write is one memory read-modify-write with // the set-or-clear choice already made. Both are bit-identical to draw.c's own // walkers. Straight lines never reach jlpDrawLine -- draw.c diverts those to // jlFillRect -- so the line body walks the general diagonal case only. diff --git a/src/amiga/input.c b/src/amiga/input.c index e373446..9e24692 100644 --- a/src/amiga/input.c +++ b/src/amiga/input.c @@ -303,12 +303,6 @@ static void drainMessages(void) { case MENUUP: mouseButtonEvent(JL_MOUSE_BUTTON_RIGHT, false); break; - case MIDDLEDOWN: - mouseButtonEvent(JL_MOUSE_BUTTON_MIDDLE, true); - break; - case MIDDLEUP: - mouseButtonEvent(JL_MOUSE_BUTTON_MIDDLE, false); - break; default: break; } diff --git a/src/atarist/audio.c b/src/atarist/audio.c index 43664c6..f453eb2 100644 --- a/src/atarist/audio.c +++ b/src/atarist/audio.c @@ -98,7 +98,7 @@ // start. #define SFX_START_MARGIN 64u -// The module player and the SFX overlay mix unsigned samples centred on $80. +// The module player and the SFX overlay mix unsigned samples centered on $80. #define MIX_SILENCE_UNSIGNED MOD_PLAYER_SILENCE_U8 // The 8-bit mix's unit (joey/audio.h's mix): a module channel at full volume // and a full-scale sound effect each swing half a byte (jlModPlayerSetU8Shift, diff --git a/src/atarist/copyRect.s b/src/atarist/copyRect.s index 2f3e04c..8b6f8b4 100644 --- a/src/atarist/copyRect.s +++ b/src/atarist/copyRect.s @@ -8,7 +8,7 @@ | erases three 16x16 rects from the clean surface, i.e. 8 bytes/row (2 | longs) x 16 rows x 3 = 48 memcpy calls where mintlib memcpy's | frame+length-dispatch dwarfs the two move.l. This is the ST (no-blitter) -| analogue of the Amiga W3 blitter copyRect that deleted the same +| analog of the Amiga W3 blitter copyRect that deleted the same | call-overhead class (+76% at its landing). Large rows keep memcpy in C | (its movem path beats a move.l loop past the crossover), so this only | ever sees short spans. diff --git a/src/atarist/hal.c b/src/atarist/hal.c index fb3f096..316dd48 100644 --- a/src/atarist/hal.c +++ b/src/atarist/hal.c @@ -13,24 +13,30 @@ // transitions table: each entry is (start_line, palette_index) // for a new palette region. For pattern.c's 8 uniform bands this // is 8 entries; in the worst case it is 200 (one per scanline). -// * VBL ISR pre-loads the first band's palette, then programs -// MFP Timer B (event-count mode) to fire at the end of the display -// of the line TWO above the next band (fireDelta). -// * Timer B's handler (rasterIsr.s) loads the band's palette into -// registers, waits for the video address counter to reach the end -// of the next line's display, and stores all 16 colours in the -// horizontal blank before the band, so its first line shows them -// from its first pixel; jlRasterBandNext then aims it at the next -// band. With 8 transitions per frame it runs 8 times instead of 313 -// -- well under the ~147-HBL-fires-per-frame cap Hatari's MFP -// emulation imposes on event-count mode -- and waits about a line -// each time, ~3% of the CPU. +// * VBL ISR programs MFP Timer B (event-count mode) to fire at the +// end of the display of the line TWO above the next band that heads +// a run (bands one line apart are a run, stored in the one +// interrupt), then pre-loads the first band's palette. A band on +// line 1 has no such line: Timer B is timed from the VBL instead +// (delay mode, RASTER_HEAD_TIMED), and the handler restarts the +// count after its run. +// * Timer B's handler (rasterIsr.s) stores each band of the run: it +// loads the band's palette into registers, waits for the video +// address counter to reach the end of the line above it, and stores +// all 16 colors in the horizontal blank before the band, so its +// first line shows them from its first pixel. Then it loads TBDR +// with the fire after next and steps to the next head itself, with +// no C, so runs two lines apart still keep up. With 8 transitions +// per frame it runs 8 times instead of 313 -- well under the +// ~147-HBL-fires-per-frame cap Hatari's MFP emulation imposes on +// event-count mode -- and waits about a line a band, ~3% of the CPU; +// a palette on every line costs every line. // * A table set (ScreenTablesT) holds each of the 16 palettes already // in shifter format and the band table; jlpPresent requantizes only // the palettes jlPaletteSet changed (jlgStagePaletteDirtyMask), and the // ISRs copy a band's row straight into the 16 shifter registers. // * Two table sets: jlpPresent writes the one not shown and hands it to -// the VBL ISR with the page it flips to, so colours and pixels change +// the VBL ISR with the page it flips to, so colors and pixels change // at the same frame. // // Two builds of this one port. The ST build (make atarist) is pure 68000 @@ -164,6 +170,7 @@ extern void jlSurface68kStLongFill(uint8_t *dst, uint16_t numGroups, uint32_t lo #define ST_TICK_RELOAD 192u #define ST_TICK_MS 5u #define MFP_TBCR_STOP 0x00 +#define MFP_TBCR_DELAY50 0x04 // delay mode, the MFP clock divided by 50 #define MFP_TBCR_EVENT 0x08 #define MFP_TB_CLEAR 0xFE // clear bit 0 of ISRA (Timer B) @@ -244,6 +251,8 @@ static inline __attribute__((always_inline)) uint16_t stFloodStopBits(const uint } return (uint16_t)(stGroupEqBits(gp, matchColor) | stGroupEqBits(gp, newColor)); } + + // One table set: the stage's 16 palettes in shifter format, ready to be // copied straight into the shifter registers by the VBL and Timer B ISRs // (one row per PALETTE, not per scanline: a palette change requantizes 16 @@ -251,21 +260,74 @@ static inline __attribute__((always_inline)) uint16_t stFloodStopBits(const uint // about 38 ms of 8 MHz 68000 per jlPaletteSet, which is what made Space // Taxi's palette-cycled warp drop frames on the ST); and the band table, // one entry per palette change down the screen: at display line -// bandStart[i], load palette bandPalIdx[i]. Timer B fires for band i -// fireDelta[i] display lines after it fired for band i - 1 (for band 1, -// after the frame starts): at the end of the line two above it, or as -// soon after the last fire as it can (buildTransitions). Built from the +// bandStart[i], load palette bandPalIdx[i]. A band (from 2) one line +// below the one before is in a run: the Timer B handler that stored the +// band before stays to store it, since an interrupt a line would cost more +// than the line. Every other band from 1 heads a run, and Timer B fires for +// it (buildTransitions); heads[] lists them in order, as the handler reads +// them, and bandRec[i] is band i as it reads it. Built from the // jlSurfaceT's palettes and SCB by jlpPresent. #define MAX_BANDS JL_SURFACE_HEIGHT // Timer B fires this many lines above a band: its handler then has a whole -// line to load the colours before the blank they go in (rasterIsr.s). +// line to load the colors before the blank they go in (rasterIsr.s). #define RASTER_LEAD_LINES 2 +// A band on line 1 would fire on line -1, which has no display end to +// count: line 0's comes too late, the handler taking about 400 cycles to +// its first store where the blank between two lines' displays is about +// 190. Its head is timed from the VBL instead -- Timer B in delay mode, +// prescaled by 50 (MFP_TBCR_DELAY50: 163 CPU cycles a tick), set to run +// out RASTER_TIMED_LEAD lines above the display (the VBL is +// RASTER_TOP_LINES_* lines above it) -- and its handler waits on the video +// counter, which stands at the page's start through the top border, so an +// early fire costs only the wait. The VBL handler arms it about 1.75 lines +// after the VBL, so it fires about two lines above line 0's display end: +// a VBL held off by another interrupt for up to about 1100 cycles still +// stores in time, for about 1400 cycles of waiting a frame. After its run +// the handler restarts the count (RasterHeadT.restart). +#define RASTER_HEAD_TIMED(tables) ((tables)->bandStart[1] < RASTER_LEAD_LINES) +#define RASTER_CYCLES_PER_TICK 163u +#define RASTER_TIMED_LEAD 4u +#define RASTER_TOP_LINES_50 63u +#define RASTER_TOP_LINES_60 34u +#define RASTER_LINE_CYCLES_50 512u +#define RASTER_LINE_CYCLES_60 508u +#define RASTER_TIMED_TICKS_50 ((RASTER_TOP_LINES_50 - RASTER_TIMED_LEAD) * RASTER_LINE_CYCLES_50 / RASTER_CYCLES_PER_TICK) +#define RASTER_TIMED_TICKS_60 ((RASTER_TOP_LINES_60 - RASTER_TIMED_LEAD) * RASTER_LINE_CYCLES_60 / RASTER_CYCLES_PER_TICK) +typedef char rasterTimedTicksFit[(RASTER_TIMED_TICKS_50 <= 255u && RASTER_TIMED_TICKS_60 <= 255u) ? 1 : -1]; +// One band as the Timer B handler stores it (rasterIsr.s reads it as six +// bytes): its 16 colors, and the video address counter's offset from the +// page's start when the display of the line above it ends. typedef struct { - uint16_t palettes [JL_SURFACE_PALETTE_COUNT][JL_SURFACE_COLORS_PER_PALETTE]; - uint16_t bandStart [MAX_BANDS]; - uint8_t bandPalIdx[MAX_BANDS]; - uint8_t fireDelta [MAX_BANDS]; - uint16_t bandCount; + const uint16_t *colors; + uint16_t offset; +} RasterBandT; +typedef char rasterBandIsSixBytes[(sizeof(RasterBandT) == 6) ? 1 : -1]; +// One run as the Timer B handler stores it (rasterIsr.s reads the first +// twelve bytes): its bands, from its head to past its last; what to load +// into TBDR once it is stored -- the fire delta of the head two on, the +// MFP's reload latching TBDR one fire ahead, or 0 when there is none (a +// count of 256, past the frame's end); for a timed head with a head after +// it, the display ends from the one its last store waited for to that +// head's fire, the count the handler restarts Timer B with (0 for any +// other); and its own fire delta, the display lines after the head +// before's fire (for the first, after the frame starts), which the VBL +// handler loads. +typedef struct { + const RasterBandT *run; + const RasterBandT *runEnd; + uint16_t reload; + uint16_t restart; + uint16_t fireDelta; +} RasterHeadT; +typedef char rasterHeadIsFourteenBytes[(sizeof(RasterHeadT) == 14) ? 1 : -1]; +typedef struct { + uint16_t palettes [JL_SURFACE_PALETTE_COUNT][JL_SURFACE_COLORS_PER_PALETTE]; + uint16_t bandStart [MAX_BANDS]; + uint8_t bandPalIdx[MAX_BANDS]; + RasterBandT bandRec [MAX_BANDS]; + RasterHeadT heads [MAX_BANDS]; + uint16_t bandCount; + uint16_t headCount; } ScreenTablesT; static void flipWait(void); @@ -284,8 +346,6 @@ static long writePrevPaletteRegs(void); static __attribute__((interrupt_handler)) void vblIsr(void); static void buildTransitions(ScreenTablesT *tables, const jlSurfaceT *src); -static void rasterAim(const ScreenTablesT *tables, uint16_t band); -void jlRasterBandNext(void); static void refreshPaletteStateIfNeeded(const jlSurfaceT *src); extern void jlStRasterIsr(void); @@ -353,8 +413,8 @@ static bool gPrevAnyDirty = true; // The two table sets. The ISRs show gTables[gTablesShown]. jlpPresent // writes the other (gTablesWriting while it does) and hands it over // (gTablesPending) with the page it flips to; the VBL ISR takes both at the -// frame they show, so new colours never land on the old page's pixels, nor -// old colours on the new page's. +// frame they show, so new colors never land on the old page's pixels, nor +// old colors on the new page's. #define TABLES_NONE 0xFFu #define TABLES_ALL_PALETTES ((uint16_t)((1u << JL_SURFACE_PALETTE_COUNT) - 1u)) static ScreenTablesT gTables[2]; @@ -368,18 +428,15 @@ static uint8_t gTablesWriting = TABLES_NONE; static uint16_t gTablesStalePalettes[2]; static bool gTablesStaleBands[2]; -// Index of the band whose palette is on the shifter. At VBL this is 0 -// (band 0 palette pre-loaded, Timer B scheduled for band 1); each Timer B -// fire loads the next band's and advances it (jlRasterBandNext). -static volatile uint16_t gCurrentBand = 0; - -// What the Timer B handler (rasterIsr.s) loads next: the band's 16 colours, -// and the video address counter's low 16 bits when the display of the line -// above the band ends (rasterAim), from gRasterBase, the counter at the -// frame's start (the shown page's address). -const uint16_t *jlgStRasterColors = gTables[0].palettes[0]; -uint16_t jlgStRasterTarget = 0u; -static uint16_t gRasterBase = 0u; +// What the Timer B handler (rasterIsr.s) stores next: the run of the head +// jlgStRasterHead, up to jlgStRasterHeadEnd (the VBL handler aims it at the +// shown table set's first, and the handler steps it), each band's colors at +// the video address counter's low 16 bits jlgStRasterBase + offset, +// jlgStRasterBase being the counter at the frame's start (the shown page's +// address). +const RasterHeadT *jlgStRasterHead = NULL; +const RasterHeadT *jlgStRasterHeadEnd = NULL; +uint16_t jlgStRasterBase = 0u; // The VBL count (jlpFrameCount). static volatile int16_t gFrameCount = 0; @@ -410,8 +467,10 @@ static void (*gOldTimerBVec)(void) = NULL; static void buildTransitions(ScreenTablesT *tables, const jlSurfaceT *src) { uint16_t line; uint16_t band; + uint16_t head; int16_t fire; - int16_t lastFire; + int16_t lastStore; + int16_t headFire; uint8_t idx; uint8_t prev; @@ -432,18 +491,36 @@ static void buildTransitions(ScreenTablesT *tables, const jlSurfaceT *src) { prev = idx; } } - // Timer B fires for band i at the end of display line bandStart[i] - 2 - // (RASTER_LEAD_LINES), the frame's line -1 being its start, or a line - // after the last fire when that is later: a band one line below the - // last has its colours by the line after it starts. - lastFire = -1; + // Timer B fires for head i at the end of display line bandStart[i] - 2 + // (RASTER_LEAD_LINES), the frame's line -1 being its start: always + // after the display end the run ahead's last store waits for + // (lastStore), a head being at least two lines below that run. A band + // on line 1 is timed instead (RASTER_HEAD_TIMED), and restarts the + // count for the head after it. + lastStore = -1; + headFire = -1; + tables->headCount = 0u; for (band = 1; band < tables->bandCount; band++) { - fire = (int16_t)(tables->bandStart[band] - RASTER_LEAD_LINES); - if (fire <= lastFire) { - fire = (int16_t)(lastFire + 1); + tables->bandRec[band].colors = tables->palettes[tables->bandPalIdx[band]]; + tables->bandRec[band].offset = (uint16_t)(tables->bandStart[band] * ST_BYTES_PER_ROW); + if (band >= 2u && tables->bandStart[band] == tables->bandStart[band - 1u] + 1u) { + tables->heads[tables->headCount - 1u].runEnd = &tables->bandRec[band + 1u]; + } else { + fire = (int16_t)(tables->bandStart[band] - RASTER_LEAD_LINES); + if (tables->headCount == 1u && RASTER_HEAD_TIMED(tables)) { + tables->heads[0].restart = (uint16_t)(fire - lastStore); + } + tables->heads[tables->headCount].run = &tables->bandRec[band]; + tables->heads[tables->headCount].runEnd = &tables->bandRec[band + 1u]; + tables->heads[tables->headCount].restart = 0u; + tables->heads[tables->headCount].fireDelta = (uint16_t)(fire - headFire); + tables->headCount++; + headFire = fire; } - tables->fireDelta[band] = (uint8_t)(fire - lastFire); - lastFire = fire; + lastStore = (int16_t)(tables->bandStart[band] - 1); + } + for (head = 0; head < tables->headCount; head++) { + tables->heads[head].reload = head + 2u < tables->headCount ? tables->heads[head + 2u].fireDelta : 0u; } } @@ -551,48 +628,10 @@ static uint8_t tablesToWrite(void) { } -// Aim the Timer B handler at band: its colours, and the counter's value when -// the display of the line above it ends. -static void rasterAim(const ScreenTablesT *tables, uint16_t band) { - jlgStRasterColors = tables->palettes[tables->bandPalIdx[band]]; - jlgStRasterTarget = (uint16_t)(gRasterBase + tables->bandStart[band] * ST_BYTES_PER_ROW); -} - -// Timer B's work after its handler (rasterIsr.s) has loaded the next band's -// colours: advance to it, aim at the band after it, and end the interrupt. -// Timer B's auto-reload keeps counting for the next fire. We deliberately -// do NOT stop/reload/restart the timer here: that sequence would cost 1-2 -// HBL edges each fire, and those losses compound across 7+ transitions into -// a visible "last band short" drift. -// -// MFP event-count auto-reload latches the CURRENT TBDR at each underflow, -// so a value written here does not take effect on the NEXT fire (that -// interval was already reloaded at the underflow that triggered this -// interrupt) but on the fire AFTER next: the band + 2 delta. -void jlRasterBandNext(void) { - const ScreenTablesT *tables; - uint16_t band; - - tables = &gTables[gTablesShown]; - band = (uint16_t)(gCurrentBand + 1u); - gCurrentBand = band; - if (band + 1u < tables->bandCount) { - rasterAim(tables, (uint16_t)(band + 1u)); - if (band + 2u < tables->bandCount) { - *ST_MFP_TBDR = tables->fireDelta[band + 2u]; - } - } else { - // No further transitions this frame. - *ST_MFP_TBCR = MFP_TBCR_STOP; - } - *ST_MFP_ISRA = MFP_TB_CLEAR; -} - - -// Vertical blank handler. Pre-loads band 0's palette so the first -// visible scanline is correct, then programs Timer B to fire for the next -// band transition (if any). The video address counter holds the shown +// Vertical blank handler. Programs Timer B to fire for the next band +// transition (if any), then pre-loads band 0's palette so the first +// visible scanline is correct. The video address counter holds the shown // page's address until the display starts: the band targets' base. static void vblIsr(void) { const ScreenTablesT *tables; @@ -600,7 +639,6 @@ static void vblIsr(void) { volatile uint16_t *dst; gFrameCount = gFrameCount + 1; - gCurrentBand = 0; gFlipPending = 0u; // The table set the last present handed over shows from this frame, as // does the page it flipped to (presentShow). @@ -614,6 +652,39 @@ static void vblIsr(void) { return; } + // Program Timer B for the first head, before anything else: a timed + // head counts from here (RASTER_HEAD_TIMED). We deliberately do NOT + // stop/reload/restart the timer at each fire (rasterIsr.s): that would + // cost 1-2 HBL edges each fire, and those losses compound into a + // visible "last band short" drift. + if (tables->headCount > 0u) { + jlgStRasterBase = (uint16_t)((*ST_VID_COUNT_MID << 8) | *ST_VID_COUNT_LOW); + jlgStRasterHead = &tables->heads[0]; + jlgStRasterHeadEnd = &tables->heads[tables->headCount]; + *ST_MFP_TBCR = MFP_TBCR_STOP; + *ST_MFP_ISRA = MFP_TB_CLEAR; + if (RASTER_HEAD_TIMED(tables)) { + *ST_MFP_TBDR = (uint8_t)((*ST_SYNC_MODE & ST_SYNC_50HZ_BIT) ? RASTER_TIMED_TICKS_50 : RASTER_TIMED_TICKS_60); + *ST_MFP_TBCR = MFP_TBCR_DELAY50; + } else { + *ST_MFP_TBDR = (uint8_t)tables->heads[0].fireDelta; + *ST_MFP_TBCR = MFP_TBCR_EVENT; + // The restart above loads the counter from TBDR (the first + // head's delta), so the first fire is correctly timed. Now + // pre-program TBDR with the second head's: the MFP's + // event-count auto-reload latches the CURRENT TBDR at each + // underflow, so a value written while counting is the reload + // one fire ahead (each head's reload, which the handler writes + // after its run, is the head two on). With no second head, 0: + // a count of 256, past the frame's end, where the first head's + // delta again could expire inside its own run and fire it twice. + *ST_MFP_TBDR = (uint8_t)((tables->headCount > 1u) ? tables->heads[1].fireDelta : 0u); + } + } else { + *ST_MFP_TBCR = MFP_TBCR_STOP; + *ST_MFP_ISRA = MFP_TB_CLEAR; + } + // Stage band 0's palette into the shifter registers. src = tables->palettes[tables->bandPalIdx[0]]; dst = ST_PALETTE_REGS; @@ -633,28 +704,6 @@ static void vblIsr(void) { dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; - - // Program Timer B for the next band transition. - if (tables->bandCount > 1) { - gRasterBase = (uint16_t)((*ST_VID_COUNT_MID << 8) | *ST_VID_COUNT_LOW); - rasterAim(tables, 1u); - *ST_MFP_TBCR = MFP_TBCR_STOP; - *ST_MFP_TBDR = tables->fireDelta[1]; - *ST_MFP_ISRA = MFP_TB_CLEAR; - *ST_MFP_TBCR = MFP_TBCR_EVENT; - // The restart above loads the counter from TBDR (band 1's - // delta), so the first fire is correctly timed. Now pre-program - // TBDR with band 2's so the band-1 underflow auto-reloads with - // the right interval (one fire ahead, matching jlRasterBandNext). - // Writing TBDR while counting only changes the next reload, not - // the in-flight count. - if (tables->bandCount > 2) { - *ST_MFP_TBDR = tables->fireDelta[2]; - } - } else { - *ST_MFP_TBCR = MFP_TBCR_STOP; - *ST_MFP_ISRA = MFP_TB_CLEAR; - } } @@ -736,10 +785,10 @@ bool jlpInit(const jlConfigT *config) { #endif // A monochrome monitor (SM124) takes only the high resolution, which - // Getrez reports as the shifter's own mode number: the 16-colour + // Getrez reports as the shifter's own mode number: the 16-color // 320x200 mode cannot be shown there. if (Getrez() == ST_RES_MONO) { - jlCoreSetError("a monochrome monitor cannot show the 16-colour 320x200 mode"); + jlCoreSetError("a monochrome monitor cannot show the 16-color 320x200 mode"); return false; } @@ -819,7 +868,7 @@ bool jlpInit(const jlConfigT *config) { // Install MFP Timer B's vector (jlStRasterIsr) stopped, TBDR=1 a // placeholder, then enable it in IMRA: the VBL ISR starts it in // event-count (HBL) mode each frame with bands, once it has aimed the - // handler at the first (rasterAim). + // handler at the first head (jlgStRasterHead). Xbtimer(1, MFP_TBCR_STOP, 1, jlStRasterIsr); Jenabint(INT_TIMER_B); @@ -1017,7 +1066,7 @@ uint16_t jlpFrameHz(void) { // The REAL refresh, not a hardcoded 50 (the old assumption under-reported // ops/sec ~17% under Hatari, which runs the ST at 60 Hz, and is wrong on any // NTSC or mono machine). The shifter resolution register selects mono vs -// colour; for colour the sync-mode register's 50/60 bit is authoritative. +// color; for color the sync-mode register's 50/60 bit is authoritative. // These $FFFF82xx reads need supervisor mode: jlpFrameHz calls this direct // while the program holds it (jlpInit's Super(0L)) and through Supexec // before jlInit and after jlShutdown, as jlpMillisElapsed does. diff --git a/src/atarist/input.c b/src/atarist/input.c index 7090f25..a99117d 100644 --- a/src/atarist/input.c +++ b/src/atarist/input.c @@ -752,9 +752,8 @@ void jlpInputPoll(void) { jlgMouseX = gMouseAbsX; jlgMouseY = gMouseAbsY; - jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_HDR_LEFT_BTN) != 0; - jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_HDR_RIGHT_BTN) != 0; - jlgMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = false; + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_HDR_LEFT_BTN) != 0; + jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_HDR_RIGHT_BTN) != 0; // Decode latest joystick byte for each stick. Direction is digital // on the ST, so axes snap to the JOYSTICK_AXIS_* extremes. diff --git a/src/atarist/lineSpan.s b/src/atarist/lineSpan.s index c83205e..a6e9301 100644 --- a/src/atarist/lineSpan.s +++ b/src/atarist/lineSpan.s @@ -923,7 +923,7 @@ _jlgStRowOffsetLut: | 320x200). Writes numGroups groups of 8 bytes (loLong, hiLong) | starting at dst. Register-pumps movem.l d2-d7/a1-a6 (12 longs = | 6 groups = 48 bytes per batch) so the per-batch lea/subq/bne loop -| overhead is amortised over twice the data of the old 24-byte batch. +| overhead is amortized over twice the data of the old 24-byte batch. | a1 is caller-saved scratch (free); a2-a6 and d2-d7 are callee-saved | and restored on exit. | @@ -1050,7 +1050,7 @@ _jlSurface68kStLongFill: .equ ST_GROUP_PIXELS, 16 .equ ST_BYTES_PER_GROUP, 8 | a group's four plane words .equ ST_GROUPS_PER_ROW, 20 - .equ ST_PAIR_MASK, 3 | a long's two planes of the colour + .equ ST_PAIR_MASK, 3 | a long's two planes of the color .equ ST_HI_PAIR_MASK, 12 | the high pair, already a long's offset .equ ST_RIGHT_LUT, _jlgStRightMaskLut - _jlgStLeftMaskLut | Where the STe's BLiTTER beats the CPU fills (measured 2026-10-06; hal.c @@ -1234,7 +1234,7 @@ _jlStFillRectBase: movem.l (%sp)+,%d2-%d5 bra.w _jlSurface68kStLongFill -| A long of two planes' words from two colour bits: the low bit's plane in +| A long of two planes' words from two color bits: the low bit's plane in | the high word (it comes first in memory). .Lfrb_pairs: .long 0x00000000, 0xFFFF0000, 0x0000FFFF, 0xFFFFFFFF diff --git a/src/atarist/rasterIsr.s b/src/atarist/rasterIsr.s index df2b9ea..3f0802f 100644 --- a/src/atarist/rasterIsr.s +++ b/src/atarist/rasterIsr.s @@ -3,23 +3,36 @@ | SPDX-License-Identifier: MIT | | The ST family's Timer B band handler (hal.c has the rest). A band's 16 -| colours are 16 words, and the shifter shows a line's pixels from about +| colors are 16 words, and the shifter shows a line's pixels from about | cycle 56 to 376 of its 512 (508 at 60 Hz), so they must all land in the | 190 or so cycles between two lines' displays. Taking the interrupt and | copying 16 words takes longer than that, so Timer B fires a line early, | at the end of the display of the line two above the band (hal.c, -| buildTransitions). This handler loads the colours (_jlgStRasterColors) into -| eight registers, waits for the video address counter to reach -| _jlgStRasterTarget -- its value at the end of the next line's display -- -| and stores all 16 with one movem.l, about 72 cycles. jlRasterBandNext (C) -| then sets up the next band and ends the interrupt. +| buildTransitions). This handler stores the run of the head +| _jlgStRasterHead (hal.c's RasterHeadT): the band Timer B fired for and the +| bands after it one line apart (RasterBandT, six bytes: the colors' +| address, then the video address counter's offset from _jlgStRasterBase +| when the display of the line above the band ends). For each it loads the +| colors into eight registers, waits for the counter to reach the band's +| value, and stores all 16 with one movem.l, about 72 cycles; the next band +| of the run is a line later, which is why it stays rather than take an +| interrupt a line, which would cost more than the line. Then it loads TBDR +| with the head's reload (the MFP's auto-reload latches TBDR one fire +| ahead, so this is the head two on's delta; 0, a count of 256, when there +| is none, so the count cannot run out inside a long last run and fire it +| again), steps _jlgStRasterHead to the next head or stops Timer B after +| the last, and ends the interrupt -- all without C, which cost two lines a +| head. A head timed from the VBL (a band on line 1: delay mode, see hal.c) +| first restarts Timer B counting display ends, from its restart count. +| Stopping or restarting clears Timer B's pending bit too: a delay-mode +| count can run out again inside the run. | | It waits with the interrupt mask at 5, so the plain ST's PWM pump (Timer | A, above Timer B in the MFP) is not held off past a sample, and raises it | to 6 for the last RASTER_NEAR bytes and the store, so nothing lands in | between. A counter more than RASTER_FAR bytes short of the target (a | frame whose start the VBL handler did not see) is not waited for: the -| colours go at once. +| colors go at once. | | GAS-syntax (binutils m68k); assembled by m68k-atari-mint-as via the | gcc driver. @@ -33,14 +46,27 @@ .equ SR_MFP_HELD, 0x2600 | supervisor, interrupt mask 6 .equ RASTER_NEAR, 32 | bytes of display: about 64 cycles .equ RASTER_FAR, 480 | bytes of display: three lines + .equ MFP_IPRA, 0xfffffa0b + .equ MFP_ISRA, 0xfffffa0f + .equ MFP_TBCR, 0xfffffa1b + .equ MFP_TBDR, 0xfffffa21 + .equ MFP_TBCR_STOP, 0x00 + .equ MFP_TBCR_EVENT, 0x08 + .equ MFP_TB_CLEAR, 0xfe | clears Timer B's pending or in-service bit _jlStRasterIsr: move.w #SR_ABOVE_TIMER_B,%sr - movem.l %d0-%d7/%a0-%a2,-(%sp) - move.l _jlgStRasterColors,%a0 - movem.l (%a0),%d1-%d7/%a2 - move.w _jlgStRasterTarget,%a0 + movem.l %d0-%d7/%a0-%a6,-(%sp) + move.l _jlgStRasterHead,%a6 + move.l (%a6)+,%a3 + move.l (%a6)+,%a4 + move.w _jlgStRasterBase,%a5 lea VIDEO_COUNTER_MID,%a1 +.Lband: + move.l (%a3)+,%a0 + movem.l (%a0),%d1-%d7/%a2 + move.w (%a3)+,%a0 + adda.w %a5,%a0 movep.w 0(%a1),%d0 sub.w %a0,%d0 cmp.w #-RASTER_FAR,%d0 @@ -63,6 +89,26 @@ _jlStRasterIsr: .Lstore: movem.l %d1-%d7/%a2,PALETTE move.w #SR_ABOVE_TIMER_B,%sr - jsr _jlRasterBandNext - movem.l (%sp)+,%d0-%d7/%a0-%a2 + cmpa.l %a4,%a3 + bcs.s .Lband + move.w (%a6)+,%d0 + move.w (%a6)+,%d1 + beq.s .Lcounting + move.b #MFP_TBCR_STOP,MFP_TBCR + move.b #MFP_TB_CLEAR,MFP_IPRA + move.b %d1,MFP_TBDR + move.b #MFP_TBCR_EVENT,MFP_TBCR +.Lcounting: + move.b %d0,MFP_TBDR + addq.l #2,%a6 + cmpa.l _jlgStRasterHeadEnd,%a6 + bcs.s .Lnext + move.b #MFP_TBCR_STOP,MFP_TBCR + move.b #MFP_TB_CLEAR,MFP_IPRA + bra.s .Ldone +.Lnext: + move.l %a6,_jlgStRasterHead +.Ldone: + move.b #MFP_TB_CLEAR,MFP_ISRA + movem.l (%sp)+,%d0-%d7/%a0-%a6 rte diff --git a/src/atarist/serial.c b/src/atarist/serial.c index 1c392cb..ad2a125 100644 --- a/src/atarist/serial.c +++ b/src/atarist/serial.c @@ -323,7 +323,7 @@ bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) { // Rsconf() argument list. mint/osbind.h's own change log says so: the // seven-word trap macro relaxed its operand constraints from "r" to "g" // and notes that is "ok since these args will never be expressions", so - // gcc may materialise an argument from a stack slot AFTER the macro has + // gcc may materialize an argument from a stack slot AFTER the macro has // moved sp out from under it. Passing serialBaudCode(...) inline was // exactly that, and it lost the baud code: MEASURED under EmuTOS/Hatari, // every requested rate -- 19200, 9600, 4800, 2400, 1200, 300 -- programmed diff --git a/src/atarist/stGlyph.s b/src/atarist/stGlyph.s index b64e768..5a6100f 100644 --- a/src/atarist/stGlyph.s +++ b/src/atarist/stGlyph.s @@ -22,7 +22,7 @@ | at its own displacement. | | cdecl, mint-gcc: every argument in a 4-byte stack slot, a uint8_t in its low -| byte; C symbols spelt with a leading underscore; d2-d7/a2-a6 callee-save. +| byte; C symbols spelled with a leading underscore; d2-d7/a2-a6 callee-save. .equ GLYPH_SAVED, 12 | d2-d3/a2, pushed first .equ GLYPH_DST, GLYPH_SAVED + 4 @@ -119,7 +119,7 @@ _jlTilePasteGlyph: .Lg_none: movem.l (%sp)+,%d2-%d3/%a2 rts -| No planes: the generic, with the same arguments, the colours masked. +| No planes: the generic, with the same arguments, the colors masked. .Lg_generic: andi.b #COLOR_MASK,GLYPH_FG(%sp) andi.b #COLOR_MASK,GLYPH_BG(%sp) @@ -132,7 +132,7 @@ _jlTilePasteGlyph: .irp by, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 .word \by * 8 * ROW_BYTES .endr -| A colour's planes as the bytes of a long, plane 0 high: 0xFF where set. +| A color's planes as the bytes of a long, plane 0 high: 0xFF where set. | (The bytes add, as | starts a comment here.) .Lg_planes: .set color, 0 diff --git a/src/atarist/ste/steBlitter.h b/src/atarist/ste/steBlitter.h index 228e7c7..07d14db 100644 --- a/src/atarist/ste/steBlitter.h +++ b/src/atarist/ste/steBlitter.h @@ -15,7 +15,7 @@ void jlStBlitCopyRows(uint8_t *dst, const uint8_t *src, uint16_t rowBytes, uint16_t h); // Fill the on-surface rect (x, y, w, h), w and h >= 1, of an interleaved -// planar buffer with colour index color. +// planar buffer with color index color. void jlStBlitFillRect(uint8_t *base, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color); // A tile layer's band (rows rows, from ring pixel row ringY and column ringX, diff --git a/src/atarist/superExit.s b/src/atarist/superExit.s index 5ae22f0..b9eb838 100644 --- a/src/atarist/superExit.s +++ b/src/atarist/superExit.s @@ -20,7 +20,7 @@ | it as a Panic in the caller of Super, one instruction past the rts. | | The cure is to note the stack position before the trap and to restore -| it afterwards. In user mode interrupts run on the supervisor stack, so +| it afterward. In user mode interrupts run on the supervisor stack, so | the bytes just below the restored SP stay ours to read. | | GAS-syntax (binutils m68k); assembled by m68k-atari-mint-as via the diff --git a/src/core/draw.c b/src/core/draw.c index c9b4657..6704a05 100644 --- a/src/core/draw.c +++ b/src/core/draw.c @@ -63,7 +63,7 @@ static void plotPixelWide(jlSurfaceT *s, int32_t x, int32_t y, uint8_t co // Whether a circle's whole bounding box is on the surface: the test for the // port fast paths, which do no per-pixel clip. In 32 bits: cx + r in a -// 16-bit int (the IIgs's) wraps for a centre near the int16_t limits and +// 16-bit int (the IIgs's) wraps for a center near the int16_t limits and // would pass a circle far off the surface. static bool circleOnSurface(int16_t cx, int16_t cy, uint16_t r) { return (int32_t)cx - (int32_t)r >= 0 && (int32_t)cx + (int32_t)r < JL_SURFACE_WIDTH && (int32_t)cy - (int32_t)r >= 0 && (int32_t)cy + (int32_t)r < JL_SURFACE_HEIGHT; @@ -1129,7 +1129,7 @@ uint8_t jlSamplePixel(const jlSurfaceT *s, int16_t x, int16_t y) { } #if !defined(JL_HAS_SAMPLE_PIXEL) // Chunky ports (no JL_HAS_SAMPLE_PIXEL override): inline the nibble - // read. The out-of-line jlpGenericSamplePixel hop re-marshalled + // read. The out-of-line jlpGenericSamplePixel hop re-marshaled // s/x/y just to run this same macro, and its body paid the y * 160 // software multiply on IIgs (PERF-AUDIT #14/#24). x and y are plain // locals here, so the macro's double x evaluation is side-effect diff --git a/src/core/init.c b/src/core/init.c index 7474da5..4abe167 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -157,7 +157,7 @@ bool jlInit(const jlConfigT *config) { } #endif - // Empty the typed-character queue and centre the mouse before the + // Empty the typed-character queue and center the mouse before the // port arms its input: a shutdown/re-init cycle must not hand the app // characters typed during the previous run. jlInputReset(); diff --git a/src/core/input.c b/src/core/input.c index 6a51327..1373525 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -19,6 +19,10 @@ // then until it is let go (joyTapOrHold). Ports mark such a stick in // jlgJoyOneButton. // +// Every mouse has two buttons, left and right: a left press made with Ctrl +// held is the right button until it is let go (mouseCtrlClick), on every +// port, so a one-button mouse has both. +// // The typed-character queue (jlgCharQueue) is a separate path for text // entry: ports push OS/firmware-translated ASCII via jlInputCharPush // during jlpInputPoll, and apps pop with jlInputGetChar. The queue is @@ -61,6 +65,8 @@ static void joyTapOrHold(void); #endif +static void mouseCtrlClick(void); +static void mousePortButtons(void); // See inputInternal.h for why these are uint8_t and not bool. @@ -73,6 +79,16 @@ int16_t jlgMouseY = 0; uint8_t jlgMouseButtonState[JL_MOUSE_BUTTON_COUNT]; uint8_t jlgMouseButtonPrev [JL_MOUSE_BUTTON_COUNT]; +// A left press made with Ctrl held is the right button until it is let go, +// on every port (mouseCtrlClick), so every port has two buttons whatever +// its mouse has. The port's own left and right as it last reported them, +// which mousePortButtons puts back before its next poll (a port may set +// them only as they change), and whether the left one held is a +// Ctrl-click. +static uint8_t gMouseLeftPort = 0u; +static uint8_t gMouseRightPort = 0u; +static bool gMouseCtrlClick = false; + uint8_t jlgJoyConnected [JL_JOYSTICK_COUNT]; int8_t jlgJoyAxisX [JL_JOYSTICK_COUNT]; int8_t jlgJoyAxisY [JL_JOYSTICK_COUNT]; @@ -113,7 +129,7 @@ extern void jlIigsInputSnapshot(void); // the wrong bytes. Either condition declares a zero-size array // below, which is a compile error. typedef int joey_keycount_check [(JL_KEY_COUNT == 60) ? 1 : -1]; -typedef int joey_mousebtn_check [(JL_MOUSE_BUTTON_COUNT == 4) ? 1 : -1]; +typedef int joey_mousebtn_check [(JL_MOUSE_BUTTON_COUNT == 3) ? 1 : -1]; typedef int joey_joybtn_check [(JL_JOYSTICK_COUNT * JL_JOY_BUTTON_COUNT == 4) ? 1 : -1]; typedef int joey_keystate_size_check [(sizeof(jlgKeyState) == JL_KEY_COUNT) ? 1 : -1]; typedef int joey_mousebtn_size_check [(sizeof(jlgMouseButtonState) == JL_MOUSE_BUTTON_COUNT) ? 1 : -1]; @@ -152,18 +168,20 @@ void jlInputCharReset(void) { // from a previous run, the default dead zone whatever a previous run set // (jlJoystickReset), no mouse until the port finds one, and the mouse in the // middle of the stage, where it reads on every port until a mouse reports -// (and stays, with none). +// (and stays, with none), its buttons up. void jlInputReset(void) { jlInputCharReset(); memset(jlgJoyDeadZone, JOY_DEAD_ZONE_DEFAULT, sizeof(jlgJoyDeadZone)); jlgMouseAttached = false; jlgMouseX = JL_SURFACE_WIDTH / 2; jlgMouseY = JL_SURFACE_HEIGHT / 2; + gMouseLeftPort = 0u; + gMouseCtrlClick = false; } // A raw stick axis, min to max, in JoeyLib's range, rounded to the nearest -// step: a centred stick reads 0 whichever middle value its driver reports +// step: a centered stick reads 0 whichever middle value its driver reports // (0..65535 has two: 32767 and 32768), and the ends read // JL_JOYSTICK_AXIS_MIN and MAX. int8_t jlInputAxisScale(int32_t pos, int32_t min, int32_t max) { @@ -192,7 +210,7 @@ void jlInputCharTyped(uint8_t ch, bool chord) { } -// An analog axis within its stick's dead zone (jlJoystickReset) of centre +// An analog axis within its stick's dead zone (jlJoystickReset) of center // reads 0: |value| <= zone, the same test on every port. int8_t jlInputDeadZone(int8_t value, uint8_t zone) { int16_t magnitude; @@ -281,6 +299,37 @@ static void joyTapOrHold(void) { #endif +// The port's left button, as its poll left it, made the right one from a +// press made with Ctrl held until it is let go (Ctrl may be let go first); +// the right button is down while either is. +static void mouseCtrlClick(void) { + uint8_t left; + + left = jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT]; + if (left == 0u) { + gMouseCtrlClick = false; + } else if (gMouseLeftPort == 0u) { + gMouseCtrlClick = jlgKeyState[JL_KEY_LCTRL] != 0u; + } + gMouseLeftPort = left; + gMouseRightPort = jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT]; + if (gMouseCtrlClick) { + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = 0u; + jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = 1u; + } +} + + +// The port's own buttons back where mouseCtrlClick changed them, for its +// poll to go on from. +static void mousePortButtons(void) { + if (gMouseCtrlClick) { + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = gMouseLeftPort; + jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = gMouseRightPort; + } +} + + void jlInputPoll(void) { #ifdef JOEYLIB_PLATFORM_IIGS // One asm pass for the key and the mouse/joy button snapshots, in @@ -291,7 +340,9 @@ void jlInputPoll(void) { memcpy(jlgMouseButtonPrev, jlgMouseButtonState, sizeof(jlgMouseButtonState)); memcpy(jlgJoyButtonPrev, jlgJoyButtonState, sizeof(jlgJoyButtonState)); #endif + mousePortButtons(); jlpInputPoll(); + mouseCtrlClick(); #ifdef JOY_TAP_OR_HOLD joyTapOrHold(); #endif @@ -352,7 +403,7 @@ void jlWaitForAnyKey(void) { * (JL_HAS_INPUT_PREDICATES): JoeyAGI asks 42 a poll, 2.2% of its IIgs * time, the compiled C there spending twice as long making its bool. */ #if defined(JL_HAS_INPUT_PREDICATES) -typedef char inputPredicatesProof[(JL_KEY_COUNT == 60 && JL_MOUSE_BUTTON_COUNT == 4) ? 1 : -1]; +typedef char inputPredicatesProof[(JL_KEY_COUNT == 60 && JL_MOUSE_BUTTON_COUNT == 3) ? 1 : -1]; #else bool jlKeyDown(jlKeyE key) { if ((uint16_t)key >= (uint16_t)JL_KEY_COUNT) { diff --git a/src/core/modFormat.h b/src/core/modFormat.h index 4565ac6..0dce7b8 100644 --- a/src/core/modFormat.h +++ b/src/core/modFormat.h @@ -48,7 +48,7 @@ static inline uint16_t modFormatWord(const uint8_t *bytes); // every JoeyLib port, plays. static inline bool modFormatIsFourChannel(const uint8_t *module) { static const char *const kMagic[] = { "M.K.", "M!K!", "FLT4", "4CHN" }; - uint16_t i; + uint16_t i; for (i = 0u; i < (uint16_t)(sizeof(kMagic) / sizeof(kMagic[0])); i++) { if (memcmp(module + MOD_MAGIC_OFFSET, kMagic[i], MOD_MAGIC_LEN) == 0) { diff --git a/src/core/modPlayer.c b/src/core/modPlayer.c index 73f9bad..66ca299 100644 --- a/src/core/modPlayer.c +++ b/src/core/modPlayer.c @@ -366,7 +366,7 @@ static PlayerT gPlay; static int8_t gVolTab[VOLUME_MAX + 1u][SAMPLE_VALUES]; // a sample (as its byte) times a volume, over 64 << gU8Shift static bool gVolTabBuilt = false; static uint8_t gU8Shift = U8_SHIFT_QUARTER; -// The sum of two 8-bit sides (each centred on U8_BIAS), clamped to a byte. +// The sum of two 8-bit sides (each centered on U8_BIAS), clamped to a byte. static uint8_t gU8Clamp[U8_SUMS]; static int32_t gMixLeft[MIX_CHUNK]; static int32_t gMixRight[MIX_CHUNK]; @@ -1238,7 +1238,7 @@ static uint32_t tickLength(void) { } -// mt_toneporta: 3xx slides towards the note at speed xx (00 the last speed). +// mt_toneporta: 3xx slides toward the note at speed xx (00 the last speed). static void tonePorta(ChannelT *c, PaulaT *a, uint8_t param) { if (param != 0u) { c->tonePortSpeed = param; diff --git a/src/core/modPlayer.h b/src/core/modPlayer.h index 39a41a1..b1920eb 100644 --- a/src/core/modPlayer.h +++ b/src/core/modPlayer.h @@ -20,7 +20,7 @@ // a song that does not loop, its end. bool jlModPlayerActive(void); -// Unsigned 8-bit, centred on MOD_PLAYER_SILENCE_U8: the DOS, ST and X68000 +// Unsigned 8-bit, centered on MOD_PLAYER_SILENCE_U8: the DOS, ST and X68000 // ports' format, mono when channels is 1, and interleaved when it is 2 // (Paula's left and right channels; each channel at the level // jlModPlayerSetU8Shift gives it, in mono or stereo alike). Returns diff --git a/src/core/psgSynth.c b/src/core/psgSynth.c index a1bed6d..4f1f6ca 100644 --- a/src/core/psgSynth.c +++ b/src/core/psgSynth.c @@ -5,7 +5,7 @@ // The software sound chip (psgSynth.h). Phase accumulators are 32-bit // fractions of a cycle, the top bit a square's half; the noise's is 32.32, // whole LFSR clocks above the point. Integer throughout, so it builds where -// there is no maths library. +// there is no math library. #include <string.h> diff --git a/src/core/sprite.c b/src/core/sprite.c index 89dc63e..09f9012 100644 --- a/src/core/sprite.c +++ b/src/core/sprite.c @@ -115,7 +115,7 @@ static jlSpriteT *gSpriteRegistry = NULL; // ----- The chunky interpreter's keep masks ----- // // For a source byte b (two 4bpp pixels), the destination bits it leaves -// alone: a nibble of ones where that pixel is transparent (colour 0), so +// alone: a nibble of ones where that pixel is transparent (color 0), so // *dst = (*dst & NIBBLE_KEEP(b)) | b merges both pixels with no tests. The // IIgs looks it up in jlgSpriteNibbleKeep; the other chunky ports bake it into // each sprite's phase images. @@ -1117,7 +1117,7 @@ void jlSpriteRestoreUnder(jlSurfaceT *s, const jlSpriteBackupT *backup) { // copy body is cheap) is skipped here. Only the class/route derivation // (needed to pick the compiled routine) and the NOT_COMPILED gate stay: // the gate both prevents jumping into garbage AND routes anything the -// trusted fast path does not recognise to the fully-validated fallback, +// trusted fast path does not recognize to the fully-validated fallback, // so a conservative caller degrades safely. Optimized on Amiga/ST (the // inline h==16 mark); on DOS / generic this is just the validated call. // The IIgs body is asm (joeyDraw.s, beside the stage sprite entries). diff --git a/src/core/surface.c b/src/core/surface.c index 3200984..b20fe88 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -355,7 +355,7 @@ uint32_t jlSurfaceHashFinish(uint16_t lo, uint16_t hi, const jlSurfaceT *s) { // The file is the port's pixels, then the SCBs, then the palettes. The SCBs // and palettes are read and checked first, so a file that names a palette -// that is not there is refused with the surface untouched; colour 0 is +// that is not there is refused with the surface untouched; color 0 is // made black, as jlPaletteSet makes it. bool jlSurfaceLoadFile(jlSurfaceT *dst, const char *path) { static uint8_t scb[JL_SURFACE_HEIGHT]; @@ -584,7 +584,11 @@ bool jlStageAlloc(void) { return false; } #endif - jlStageDirtyClearAll(); + // The stage starts black, and its first present shows all of it, + // whatever the display held before (the IIgs leaves GS/OS's screen in + // SHR memory): a program that draws a little and presents shows only + // that on black. + jlSurfaceMarkDirtyAll(jlgStage); jlPaletteInitDefault(jlgStage); // Fresh stage = fresh SCB/palette; force the first present after // (re-)init to upload them. The load-time initializers cover the diff --git a/src/core/tileLayer.c b/src/core/tileLayer.c index 9ab86d4..0a962e1 100644 --- a/src/core/tileLayer.c +++ b/src/core/tileLayer.c @@ -24,7 +24,7 @@ // Tileset indexing below is idx << 5; pin it to sizeof(jlTileT). typedef char tileLayerStrideProof[(sizeof(jlTileT) == 32) ? 1 : -1]; -// What a JL_TILE_MAP_SKIP cell draws: colour 0, all zero bits in every +// What a JL_TILE_MAP_SKIP cell draws: color 0, all zero bits in every // port's tile format. static const jlTileT gBlankTile; diff --git a/src/core/tileLayerInternal.h b/src/core/tileLayerInternal.h index 965d8de..0e3f93e 100644 --- a/src/core/tileLayerInternal.h +++ b/src/core/tileLayerInternal.h @@ -46,7 +46,7 @@ // left, so an odd ringX copies whole bytes from `odd` instead of joining // nibbles. A pixel row is TILE_LAYER_CHUNKY_BYTES bytes and a 2-byte guard // repeating its first two (a 16-bit read at the last byte sees the right -// neighbour). +// neighbor). #define TILE_LAYER_CHUNKY_BYTES (TILE_LAYER_RING_PX / 2) #define TILE_LAYER_CHUNKY_STRIDE (TILE_LAYER_CHUNKY_BYTES + 2) diff --git a/src/dos/audio.c b/src/dos/audio.c index 6c713ba..9b7e4e9 100644 --- a/src/dos/audio.c +++ b/src/dos/audio.c @@ -848,10 +848,10 @@ bool jlpAudioInit(void) { // effective playback rate from the same time constant the SB DAC will // receive, so the module player and the SFX resampler agree with the // hardware instead of using the nominal MIX_RATE. - gChannels = gDspMajor >= DSP_MAJOR_SB16 ? MIX_CHANNELS_MAX : 1u; + gChannels = gDspMajor >= DSP_MAJOR_SB16 ? MIX_CHANNELS_MAX : 1u; gHalfBytes = (uint16_t)(MIX_HALF_FRAMES * gChannels); gBlockBytes = (uint16_t)(MIX_BLOCK_FRAMES * gChannels); - gEffRate = gChannels == MIX_CHANNELS_MAX ? (uint16_t)MIX_RATE : (uint16_t)(SB_DSP_CLOCK_HZ / (256u - dspTimeConst(MIX_RATE))); + gEffRate = gChannels == MIX_CHANNELS_MAX ? (uint16_t)MIX_RATE : (uint16_t)(SB_DSP_CLOCK_HZ / (256u - dspTimeConst(MIX_RATE))); memset(gSfxPan, JL_AUDIO_PAN_CENTER, sizeof(gSfxPan)); jlModPlayerSetU8Shift(MIX_UNIT_SHIFT); jlAudioSfxSetShift(MIX_UNIT_SHIFT); diff --git a/src/dos/hal.c b/src/dos/hal.c index 4ccce36..6631656 100644 --- a/src/dos/hal.c +++ b/src/dos/hal.c @@ -114,9 +114,9 @@ static uint8_t gFrontPage = 0u; static bool gFlipPending = false; static uint64_t gFlipClocks = 0u; // The last retrace start retraceWait saw, on the PIT's clock. -static bool gRetraceSeen = false; +static bool gRetraceSeen = false; static uint64_t gRetraceClocks = 0u; -// A present that waited for a retrace to load its colours, at +// A present that waited for a retrace to load its colors, at // gBankedClocks: the next jlpWaitVBL within a frame of it has had its // retrace, so a loop that waits and then presents keeps the refresh rate. static bool gVblBanked = false; @@ -449,7 +449,7 @@ static void pitClockStart(void) { // Nothing to present: no band marked this frame or the last, the back page -// has every row's SCB, and the colours are as they were. +// has every row's SCB, and the colors are as they were. static void planeLutBuild(void) { uint16_t palette; uint16_t byte; @@ -614,7 +614,7 @@ bool jlpInit(const jlConfigT *config) { // PAGE FLIP (see the Mode Y constants): the rows that changed go into the // page not shown, a plane at a time, and the CRTC flips to it as the next // retrace begins. A palette change waits for that retrace and loads the DAC -// in its blank, so the new colours show from the frame the new pixels do. +// in its blank, so the new colors show from the frame the new pixels do. void jlpPresent(const jlSurfaceT *src) { uint8_t *page; uint32_t offset; @@ -668,7 +668,7 @@ void jlpPresent(const jlSurfaceT *src) { // P12 (perf-hunt-2 rank 4): display-side parity probe for the -// UBER_PROBE build -- DOS's analogue of the ST jlpProbeDisplayMismatch +// UBER_PROBE build -- DOS's analog of the ST jlpProbeDisplayMismatch // / IIgs shrParity gates. Re-derives every VGA byte from the stage // INDEPENDENTLY of the gExpandLut fast path (naive nibble split + scb // palette base) and counts mismatches against the live VGA @@ -713,7 +713,7 @@ uint32_t jlDosProbeVgaMismatch(const jlSurfaceT *src) { // first wait while we're already in retrace, then wait until we // re-enter retrace. void jlpWaitVBL(void) { - // A present that just waited for a retrace to load its colours has + // A present that just waited for a retrace to load its colors has // given this wait its retrace. if (gVblBanked && pitClocksNow() - gBankedClocks < VGA_FRAME_PIT_CLOCKS) { gVblBanked = false; diff --git a/src/dos/input.c b/src/dos/input.c index 554f67e..c6499b1 100644 --- a/src/dos/input.c +++ b/src/dos/input.c @@ -79,7 +79,7 @@ #define BIOS_KB_FLAG_NUM 0x20 // Numeric keypad scan range (set 1). With num-lock active these type -// digits and '.'; the grey arrow cluster shares the codes but arrives +// digits and '.'; the gray arrow cluster shares the codes but arrives // behind an 0xE0 prefix, which the ISR tracks to keep them apart. #define SCAN_KEYPAD_FIRST 0x47 #define SCAN_KEYPAD_LAST 0x53 @@ -132,7 +132,6 @@ #define MOUSE_MICKEYS_Y 16 #define MOUSE_BTN_LEFT 0x0001 #define MOUSE_BTN_RIGHT 0x0002 -#define MOUSE_BTN_MIDDLE 0x0004 // INT 15h game-port BIOS functions: // AH=84h, DX=0: read switches (AL bits 4-7 = buttons, 0=pressed) @@ -535,7 +534,7 @@ static bool joystickInit(void) { } -// No stick: nothing held, centred, not connected, its rest to be taken +// No stick: nothing held, centered, not connected, its rest to be taken // again from the next stick seen. static void joystickClear(jlJoystickE js) { jlgJoyConnected[js] = false; @@ -598,11 +597,10 @@ static void mousePoll(void) { btn = r.x.bx; } // X in the driver's 640-wide virtual screen (MOUSE_X_SHIFT, above). - jlgMouseX = (int16_t)(x >> MOUSE_X_SHIFT); - jlgMouseY = (int16_t)y; - jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_BTN_LEFT) != 0; - jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_BTN_RIGHT) != 0; - jlgMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = (btn & MOUSE_BTN_MIDDLE) != 0; + jlgMouseX = (int16_t)(x >> MOUSE_X_SHIFT); + jlgMouseY = (int16_t)y; + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_BTN_LEFT) != 0; + jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_BTN_RIGHT) != 0; } diff --git a/src/dvx/audio.c b/src/dvx/audio.c index 8159312..8be88a6 100644 --- a/src/dvx/audio.c +++ b/src/dvx/audio.c @@ -337,7 +337,7 @@ static uint32_t synthFill(AudioStreamT *stream, void *buf, uint32_t frames, void // The synth's stream at the device's rate, on its own voice at full volume -// in the centre. +// in the center. static void synthOpen(void) { AudioInfoT info; AudioFormatT fmt; diff --git a/src/dvx/hal.c b/src/dvx/hal.c index 02bbeec..94a9a2f 100644 --- a/src/dvx/hal.c +++ b/src/dvx/hal.c @@ -18,7 +18,7 @@ // 16 or 32 bits), so the present expands the stage's changed rows through // one table per palette: the desktop pixel for each of its 16 entries, the // $0RGB channels scaled to 8 bits and packed (packColor), or on a -// 256-colour desktop the nearest desktop colour (gamePalNearest: that +// 256-color desktop the nearest desktop color (gamePalNearest: that // palette belongs to the whole desktop). Each row takes the table its SCB // picks, so SCB palettes are exact at 15 bits and up. A palette change // rebuilds its table and expands every row using it again; an SCB change, @@ -26,15 +26,15 @@ // row. Runs of consecutive changed rows go to DVX as one rectangle each. // // Full screen (Game > Full Screen, or Alt+Enter; the choice is kept in the -// program's settings folder, so it starts the way it was left): DVX's +// program's user folder, so it starts the way it was left): DVX's // exclusive mode at 320x200 and 8 bits a pixel, which on DOS is VGA mode 13h // or a VBE mode of that size. The model is the DOS port's: the screen's 256 -// colours hold all 16 palettes (entry = palette * 16 + colour), so a pixel +// colors hold all 16 palettes (entry = palette * 16 + color), so a pixel // is the stage's palette index, (SCB << 4) | nibble, SCB palettes are exact, // and a palette change is only the screen's palette: no pixel is expanded // again. The same present writes the changed rows into the full-screen -// backbuffer (centred at the largest whole scale, should DVX give a larger -// mode or another depth, whose tables are then packed colours) and flushes +// backbuffer (centered at the largest whole scale, should DVX give a larger +// mode or another depth, whose tables are then packed colors) and flushes // each run to the screen. When the window loses the focus DVX gives the // desktop back (Suspended): the present goes to the window again, the // window redrawn first; with the focus back the screen is the program's @@ -104,7 +104,7 @@ #define CMD_GAME_EXIT 102 #define CMD_GAME_SQUARE 103 -// The port's own settings, a file in the program's settings folder. +// The port's own settings, a file in the program's user folder. #define SETTINGS_FILE "JOEYLIB.INI" #define SETTINGS_SECTION "display" #define SETTINGS_FULL_SCREEN "fullScreen" @@ -124,8 +124,8 @@ // What the tables hold: the pixel for each palette entry. typedef enum { - LUT_PACKED = 0, // the colour packed in the display's format (15 bits and up) - LUT_NEAREST, // the nearest colour of the 256-colour desktop's palette + LUT_PACKED = 0, // the color packed in the display's format (15 bits and up) + LUT_NEAREST, // the nearest color of the 256-color desktop's palette LUT_INDEXED // the palette index itself: the whole screen at 8 bits, its palette the stage's } LutKindE; @@ -202,7 +202,7 @@ static LutKindE gLutKind = LUT_PACKED; static int32_t gLutBits = 0; static uint8_t gLutDesktopPalette[PAL_ENTRIES * PAL_CHANNELS]; -// The full-screen palette (LUT_INDEXED): entry palette * 16 + colour. +// The full-screen palette (LUT_INDEXED): entry palette * 16 + color. static uint8_t gFullPalette[PAL_ENTRIES * PAL_CHANNELS]; // ----- Internal helpers (alphabetical) ----- @@ -341,7 +341,7 @@ static void fullSet(bool on) { // Rebuild the tables of the palettes whose bits are set, for where the stage -// goes now; on the whole screen at 8 bits (LUT_INDEXED) their colours go to +// goes now; on the whole screen at 8 bits (LUT_INDEXED) their colors go to // the screen's palette instead, and the tables are the indexes themselves. static void lutBuild(const jlSurfaceT *src, uint16_t palettes) { const DisplayT *d; @@ -402,7 +402,7 @@ static LutKindE lutKind(const DisplayT *d) { // Rebuild every table when they were not built for where the stage goes now // (full screen came or went, or the user changed the video mode, or the -// 256-colour desktop's palette, while the program runs). True when it did: +// 256-color desktop's palette, while the program runs). True when it did: // every row must be expanded again. static bool lutSync(const jlSurfaceT *src) { const DisplayT *d; @@ -606,7 +606,7 @@ static void redrawAll(void) { } -// Channel shift of a $0RGB colour, widened to 8 bits. +// Channel shift of a $0RGB color, widened to 8 bits. static uint8_t rgb12Channel(uint16_t rgb, uint8_t shift) { return (uint8_t)(((rgb >> shift) & RGB12_CHANNEL_MASK) * RGB12_TO_RGB24); } @@ -621,7 +621,7 @@ static bool settingLoad(const char *key) { PrefsHandleT *prefs; bool on; - shellConfigPath(jlgDvxPortProgram, SETTINGS_FILE, path, (int32_t)sizeof(path)); + shellUserPath(jlgDvxPortProgram, SETTINGS_FILE, path, (int32_t)sizeof(path)); prefs = prefsLoad(path); if (prefs == NULL) { return false; @@ -636,10 +636,10 @@ static void settingSave(const char *key, bool on) { char path[DVX_MAX_PATH]; PrefsHandleT *prefs; - if (shellEnsureConfigDir(jlgDvxPortProgram) != 0) { + if (shellEnsureUserDir(jlgDvxPortProgram) != 0) { return; } - shellConfigPath(jlgDvxPortProgram, SETTINGS_FILE, path, (int32_t)sizeof(path)); + shellUserPath(jlgDvxPortProgram, SETTINGS_FILE, path, (int32_t)sizeof(path)); prefs = prefsLoad(path); if (prefs == NULL) { return; @@ -686,7 +686,7 @@ static const DisplayT *targetDisplay(void) { } -// Where the stage goes now. On the whole screen, centred at the largest +// Where the stage goes now. On the whole screen, centered at the largest // whole scale that fits (as dvxExclusivePresent8 places a picture; DVX's // mode is at least the stage's size); in the window, at the window's scale, // clipped to the content area in whole pixel pairs. False without a window. @@ -987,7 +987,7 @@ void jlpPresent(const jlSurfaceT *src) { palettes = PALETTES_ALL; } else if (jlgStagePaletteDirty) { lutBuild(src, jlgStagePaletteDirtyMask); - // On the whole screen at 8 bits the new colours are the screen's + // On the whole screen at 8 bits the new colors are the screen's // palette: no pixel changes. palettes = gLutKind == LUT_INDEXED ? PALETTES_NONE : jlgStagePaletteDirtyMask; } diff --git a/src/dvx/input.c b/src/dvx/input.c index b98d2d1..a4b82dc 100644 --- a/src/dvx/input.c +++ b/src/dvx/input.c @@ -8,7 +8,7 @@ // KEY_RAW_EXT for the keys sent behind 0xE0) from its INT 9 chain. The poll // copies it once and maps it through the set-1 table shared with DOS // (src/x86/scanSet1.h); a key is down when either of its codes is, so the -// grey arrows are the arrows and right Ctrl is Ctrl, as on DOS. Every key +// gray arrows are the arrows and right Ctrl is Ctrl, as on DOS. Every key // reads up while the window does not have the focus. // // Typed characters come from the window's onKey, with Shift and the DVX @@ -159,14 +159,12 @@ void jlDvxPortOnKey(WindowT *win, int32_t key, int32_t mod) { void jlDvxPortOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) { (void)win; jlDvxPortStagePoint(&x, &y); - gNextMouseX = (int16_t)x; - gNextMouseY = (int16_t)y; - gMouseHeld[JL_MOUSE_BUTTON_LEFT] = (buttons & MOUSE_LEFT) != 0; - gMouseHeld[JL_MOUSE_BUTTON_RIGHT] = (buttons & MOUSE_RIGHT) != 0; - gMouseHeld[JL_MOUSE_BUTTON_MIDDLE] = (buttons & MOUSE_MIDDLE) != 0; - gMouseTapped[JL_MOUSE_BUTTON_LEFT] |= gMouseHeld[JL_MOUSE_BUTTON_LEFT]; - gMouseTapped[JL_MOUSE_BUTTON_RIGHT] |= gMouseHeld[JL_MOUSE_BUTTON_RIGHT]; - gMouseTapped[JL_MOUSE_BUTTON_MIDDLE] |= gMouseHeld[JL_MOUSE_BUTTON_MIDDLE]; + gNextMouseX = (int16_t)x; + gNextMouseY = (int16_t)y; + gMouseHeld[JL_MOUSE_BUTTON_LEFT] = (buttons & MOUSE_LEFT) != 0; + gMouseHeld[JL_MOUSE_BUTTON_RIGHT] = (buttons & MOUSE_RIGHT) != 0; + gMouseTapped[JL_MOUSE_BUTTON_LEFT] |= gMouseHeld[JL_MOUSE_BUTTON_LEFT]; + gMouseTapped[JL_MOUSE_BUTTON_RIGHT] |= gMouseHeld[JL_MOUSE_BUTTON_RIGHT]; } diff --git a/src/dvx/program.c b/src/dvx/program.c index eebfbe0..0aae5d4 100644 --- a/src/dvx/program.c +++ b/src/dvx/program.c @@ -18,9 +18,9 @@ // // DVX shares one current directory between all its programs, so the file // roots (fileRoot.h) come from the context instead: DATA/ beside the .app -// (appDir), SAVES/ and joeylog.txt in the program's own settings folder -// (configDir, CONFIG\ plus the .app's folder), which DVX keeps apart from the -// program files so a reinstall never overwrites them. +// (appDir), SAVES/ and joeylog.txt in the program's user folder (userDir, +// USER\APPS\ plus the program's path), which DVX keeps apart from the +// program files so an upgrade never overwrites them. #include "dvxSdk.h" @@ -140,7 +140,7 @@ int32_t jlDvxPortRun(DxeAppContextT *ctx) { gAtExitCount = 0; gWriteRootMade = false; rootSet(gReadRoot, sizeof(gReadRoot), ctx->appDir); - rootSet(gWriteRoot, sizeof(gWriteRoot), ctx->configDir); + rootSet(gWriteRoot, sizeof(gWriteRoot), ctx->userDir); (void)snprintf(gArgs, sizeof(gArgs), "%s", ctx->args); argv[0] = ctx->appPath; argc = 1 + argsSplit(gArgs, &argv[1], ARGV_MAX - 1); @@ -192,11 +192,11 @@ const char *jlpReadRoot(void) { } -// The settings folder is made on first use: a program that never writes +// The user folder is made on first use: a program that never writes // leaves no folder behind. const char *jlpWriteRoot(void) { if (!gWriteRootMade) { - gWriteRootMade = shellEnsureConfigDir(jlgDvxPortProgram) == 0; + gWriteRootMade = shellEnsureUserDir(jlgDvxPortProgram) == 0; } return gWriteRoot; } diff --git a/src/dvx/save.c b/src/dvx/save.c index 30ab61a..a6b831a 100644 --- a/src/dvx/save.c +++ b/src/dvx/save.c @@ -4,7 +4,7 @@ // // DVX save-file + disk-space HAL. // -// SAVES/ lives in the program's settings folder (program.c's write root); +// SAVES/ lives in the program's user folder (program.c's write root); // DVX's platform layer makes it, parents and all, and measures the disk it // is on. Delete is stdio remove(). diff --git a/src/generic/genericTileLayer.c b/src/generic/genericTileLayer.c index 8defbdf..1032f2d 100644 --- a/src/generic/genericTileLayer.c +++ b/src/generic/genericTileLayer.c @@ -7,7 +7,7 @@ // slotCol * 4 of pixel rows slotRow * 8 on in `even`, and Put keeps `odd` // in step: its byte j is even's low nibble of byte j and high nibble of // byte j + 1, so a slot reaches odd bytes 4c-1 .. 4c+3 (the first and last -// of those also wait on a neighbouring slot, which sets them in turn when +// of those also wait on a neighboring slot, which sets them in turn when // it is drawn). The blit copies whole bytes from whichever ring starts at // the band's first pixel, in two runs a row: up to the ring's right edge // and on from its left. diff --git a/src/hosted/audio.c b/src/hosted/audio.c index 493cfd2..f625905 100644 --- a/src/hosted/audio.c +++ b/src/hosted/audio.c @@ -10,7 +10,7 @@ // * MOD music through the module player (src/core/modPlayer.c, the DOS, ST // and X68000 ports' player too), // * the SFX slots through the shared overlay mixer (audioSfxMix.c), each -// placed left, centre or right (jlAudioSetPan; a mono mix has no sides), +// placed left, center or right (jlAudioSetPan; a mono mix has no sides), // * the three tone voices and the noise voice through the software sound // chip DVX uses too (psgSynth.c: squares, the YM2149's LFSR, 2 dB a // step), jlAudioTone as voice 0 at full volume (one-voice tone @@ -153,7 +153,7 @@ static void mixModInto(uint32_t frames) { // Each slot alone through the shared overlay mixer (its unsigned 8-bit -// output, centred at SFX_BIAS, is that slot's signal), then into the +// output, centered at SFX_BIAS, is that slot's signal), then into the // channels its pan reaches. static void mixSfxInto(uint32_t frames) { uint32_t i; @@ -183,7 +183,7 @@ static void mixSfxInto(uint32_t frames) { } -// Left is the first channel, right the last, centre all of them; a mono mix +// Left is the first channel, right the last, center all of them; a mono mix // has one channel, which every pan reaches. static bool panReaches(uint8_t pan, uint8_t channel) { return pan == JL_AUDIO_PAN_CENTER || channel == (pan == JL_AUDIO_PAN_LEFT ? 0u : HOSTED_AUDIO_CHANNELS - 1u); diff --git a/src/hosted/be/beBackend.cpp b/src/hosted/be/beBackend.cpp index 32fb896..115a033 100644 --- a/src/hosted/be/beBackend.cpp +++ b/src/hosted/be/beBackend.cpp @@ -128,13 +128,13 @@ extern "C" { #define SCALED_NEW 1 #define SCALED_NONE 2 -// The display probe reads the screen back as B_RGB32: three colour bytes +// The display probe reads the screen back as B_RGB32: three color bytes // and one the screen ignores. #define PROBE_BITS_PER_PIXEL 32u #define PROBE_SHOWN_BYTES 3u // A cursor in the Be API's 16x16, 1-bit format (size, depth, hot spot y and -// x, then the colour and mask bits), every pixel masked out: the pointer +// x, then the color and mask bits), every pixel masked out: the pointer // over the frame. #define CURSOR_DATA_BYTES 68 #define CURSOR_SIZE 16 @@ -293,8 +293,8 @@ static const KeyRowT kLetterRows[] = { { RAW_KEY_ROW_A, "ASDFGHJKL" }, { RAW_KEY_ROW_Z, "ZXCVBNM" } }; -static const uint32 kMouseButtons[] = { B_PRIMARY_MOUSE_BUTTON, B_SECONDARY_MOUSE_BUTTON, B_TERTIARY_MOUSE_BUTTON }; -static const jlMouseButtonE kMouseButtonIds[] = { JL_MOUSE_BUTTON_LEFT, JL_MOUSE_BUTTON_RIGHT, JL_MOUSE_BUTTON_MIDDLE }; +static const uint32 kMouseButtons[] = { B_PRIMARY_MOUSE_BUTTON, B_SECONDARY_MOUSE_BUTTON }; +static const jlMouseButtonE kMouseButtonIds[] = { JL_MOUSE_BUTTON_LEFT, JL_MOUSE_BUTTON_RIGHT }; static const uint8 kHiddenCursor[CURSOR_DATA_BYTES] = { CURSOR_SIZE, CURSOR_DEPTH }; // The program, the application and the window. Set up by jlHostBackendOpen. @@ -683,7 +683,7 @@ static void bitmapCopy(const uint32_t *pixels, const HostedRectT *rect) { // Where the frame goes in a view of these bounds: as large as fits at the -// stage's shape, centred. +// stage's shape, centered. static BRect frameRect(BRect bounds) { float outW; float outH; @@ -1110,7 +1110,7 @@ static void sticksPoll(void) { } -// The window's frame: the stage at JOEYLIB_SCALE and its shape, centred on +// The window's frame: the stage at JOEYLIB_SCALE and its shape, centered on // the screen, or the whole screen. static BRect windowFrame(void) { BRect screen; diff --git a/src/hosted/hal.c b/src/hosted/hal.c index 5109bcc..3e649be 100644 --- a/src/hosted/hal.c +++ b/src/hosted/hal.c @@ -13,7 +13,7 @@ // expands every row. A backend that keeps an indexed frame of its own // (HOSTED_FRAME_INDEXED, hosted.h) is handed each changed row as the stage // holds it, and expands it itself, each pixel its palette's base and its -// colour, so a palette change only hands it its new colours and the rows +// color, so a palette change only hands it its new colors and the rows // stay as they are. // // Timing: a window has no vertical blank, so the frame rate is a virtual @@ -63,7 +63,7 @@ #define STAGE_PIXELS_PER_WORD (JL_SURFACE_WIDTH / SURFACE_WORDS_PER_ROW) #define BITS_PER_BYTE 8u -// A 32-bit device pixel is three colour bytes and a padding byte that the +// A 32-bit device pixel is three color bytes and a padding byte that the // display ignores and drivers fill as they please (Wine's differs with how // the pixel was drawn): display probes compare the three. #define PIXEL32_BITS 32u @@ -107,8 +107,8 @@ static uint16_t gChangedTop = JL_SURFACE_HEIGHT; static uint16_t gChangedEnd = 0; static HostedRectT gRects[JL_SURFACE_HEIGHT]; static uint16_t gRectCount = 0; -// Each palette's colours, 0x00RRGGBB; palette after palette, so entry -// (p << 4) | c is an indexed frame's colour table. +// Each palette's colors, 0x00RRGGBB; palette after palette, so entry +// (p << 4) | c is an indexed frame's color table. static uint32_t gLut[JL_SURFACE_PALETTE_COUNT][JL_SURFACE_COLORS_PER_PALETTE]; #if defined(HOSTED_FRAME_INDEXED) // The palettes the SCBs used when the backend last laid its frame out for @@ -500,7 +500,7 @@ void jlpPresent(const jlSurfaceT *src) { if (jlgStagePaletteDirty) { lutBuild(src, jlgStagePaletteDirtyMask); #if !defined(HOSTED_FRAME_INDEXED) - // The rows hold colours: those in a changed palette are expanded again. + // The rows hold colors: those in a changed palette are expanded again. palettes = jlgStagePaletteDirtyMask; #endif } diff --git a/src/hosted/hosted.h b/src/hosted/hosted.h index aa6c0a7..69b1022 100644 --- a/src/hosted/hosted.h +++ b/src/hosted/hosted.h @@ -35,8 +35,8 @@ // HOSTED_FRAME_INDEXED (a system that shows 8-bit pictures best: Windows // 3.x on a 386) keeps a frame of its own instead: the core hands it each // changed row as the stage holds it (jlHostBackendFrameRow), and it expands -// them, each pixel base + colour -- the base it gives the row's SCB palette -// (jlHostBackendPaletteBases) -- shown through a colour table the core keeps it +// them, each pixel base + color -- the base it gives the row's SCB palette +// (jlHostBackendPaletteBases) -- shown through a color table the core keeps it // up to date with (jlHostBackendColors). Its palette changes then only touch // that table. typedef uint32_t HostedPixelT; @@ -73,7 +73,7 @@ void jlHostBackendClose(void); // Show one frame (HostedPixelT above; an indexed frame is the backend's // own). rects[0..rectCount-1] cover every pixel that changed since the last // show, rows top to bottom and apart; a backend may show more (the whole -// frame), and must when it has a reason of its own (its colours changed, +// frame), and must when it has a reason of its own (its colors changed, // its window was uncovered). #if defined(HOSTED_FRAME_INDEXED) void jlHostBackendShow(const HostedRectT *rects, uint16_t rectCount); @@ -85,14 +85,14 @@ void jlHostBackendShow(const HostedPixelT *pixels, const HostedRectT *rec // holds them from x0's byte, two a byte as the stage does, and scb is the // row's SCB. They stay the backend's to expand, now or when it shows them. void jlHostBackendFrameRow(const uint8_t *pixels, uint8_t scb, uint16_t y, uint16_t x0, uint16_t x1); -// The colour table changed: colors holds all 256 entries, 0x00RRGGBB, entry -// (palette << 4) | colour, and palettes has bit p set for each palette whose +// The color table changed: colors holds all 256 entries, 0x00RRGGBB, entry +// (palette << 4) | color, and palettes has bit p set for each palette whose // sixteen are new. colors stays valid, and current, until the core shuts // down. Called before the first frame with every bit set. void jlHostBackendColors(const uint32_t *colors, uint16_t palettes); // The palettes the stage's SCBs use (bit p for palette p) changed, or this -// is the first present: the backend gives each palette the base its colours -// go at in the frame from now on (a pixel is the base + colour). True when +// is the first present: the backend gives each palette the base its colors +// go at in the frame from now on (a pixel is the base + color). True when // any base moved: the core then hands it every row again. bool jlHostBackendPaletteBases(uint16_t used); #endif @@ -156,13 +156,13 @@ void jlHostedJoyButtonEvent(jlJoystickE js, jlJoyButtonE button, bool down); // Keep the audio queue full; the core calls it from every frequent library // call (present, the frame wait, the input poll and the frame tick). void jlHostedAudioService(void); -// For a display probe: colour c of the stage's palette p, worked out from -// the stage's own entry as the frame's colours are, 0x00RRGGBB. +// For a display probe: color c of the stage's palette p, worked out from +// the stage's own entry as the frame's colors are, 0x00RRGGBB. uint32_t jlHostedProbeStageRgb(const jlSurfaceT *src, uint8_t palette, uint8_t color); #if defined(HOSTED_FRAME_INDEXED) // Every row of the stage the last present showed into the backend's frame // again (jlHostBackendFrameRow), for a backend whose frame was remade; and, -// for its display probe, stage pixel (x, y) as (palette << 4) | colour. +// for its display probe, stage pixel (x, y) as (palette << 4) | color. void jlHostedFrameRefresh(void); uint8_t jlHostedProbeStagePixel(const jlSurfaceT *src, uint16_t x, uint16_t y); #else diff --git a/src/hosted/psp/pspKeyboard.c b/src/hosted/psp/pspKeyboard.c index 93e57e8..e61d4a9 100644 --- a/src/hosted/psp/pspKeyboard.c +++ b/src/hosted/psp/pspKeyboard.c @@ -93,7 +93,7 @@ // A key's legend made from its character, and the terminator. #define LEGEND_CHAR_BYTES 2 -// Colours, 0xRRGGBB, and how much of a lit key's colour covers it. The +// Colors, 0xRRGGBB, and how much of a lit key's color covers it. The // PSP's renderer keeps the keyboard's pictures in 16 bits (five a channel); // COLOR_BOARD is one it keeps exactly, for scripts/verify-psp-input.sh. #define COLOR_BOARD 0x212121u diff --git a/src/hosted/sdl3/sdl3Backend.c b/src/hosted/sdl3/sdl3Backend.c index 3717873..ac223fc 100644 --- a/src/hosted/sdl3/sdl3Backend.c +++ b/src/hosted/sdl3/sdl3Backend.c @@ -42,12 +42,14 @@ // // On the PSP (the psp port) the window is the 480x272 screen and the stage // sits in its middle at 1:1. The PSP's own controls are gamepad 0 through -// SDL's mapping (the d-pad or the analog nub, cross as button 0 and circle -// as button 1); having no keyboard, Start is the Return key and Select the -// Escape key, held and typed as those keys are, so "press any key", Return -// and Escape work, and Square opens a PC keyboard drawn over the screen for -// every other key (src/hosted/psp/pspKeyboard.c), which takes the d-pad and -// cross while it is open. SDL's text input, which would open the PSP's own +// SDL's mapping: the d-pad is joystick 0, cross its button 0 and circle its +// button 1; having no mouse, the analog nub moves the mouse pointer and L +// and R are its left and right buttons; having no keyboard, Start is the +// Return key and Select the Escape key, held and typed as those keys are, so +// "press any key", Return, and Escape work, and Square opens a PC keyboard +// drawn over the screen for every other key (src/hosted/psp/pspKeyboard.c), +// which takes the d-pad, cross, L, and R, and stills the pointer, while it +// is open. SDL's text input, which would open the PSP's own // on-screen keyboard, is never started. The HOME menu's Exit ends the // program as closing a window does. The program's // folder (PSP/GAME/NAME on the memory stick) is the current directory, and @@ -169,6 +171,16 @@ extern const uint8_t jlgHostedIconRgba[]; // A device name such as "ms0:", its colon and a terminator. #define PSP_DEVICE_BYTES 16 #define PSP_DEVICE_END ':' +// The analog nub is the mouse (pspPointerTick). SDL already bends the nub's +// travel (flat round its center, full well before its edge), so past a dead +// zone, where a worn nub may rest, its tilt is the pointer's speed, up to +// PSP_POINTER_SPEED stage pixels a second at full tilt (across the stage in +// a second). A pump that comes later than PSP_POINTER_STEP_S (the program +// was busy) moves the pointer only as far as one that long would. +#define PSP_NUB_DEAD_ZONE 0.15f +#define PSP_POINTER_SPEED 320.0f +#define PSP_POINTER_STEP_S 0.1f +#define PSP_NS_PER_S 1000000000.0f #endif // ----- Types ----- @@ -204,9 +216,10 @@ static void padUpdate(SDL_JoystickID id); static void pointerMove(float windowX, float windowY); static const char *programName(void); #if defined(__PSP__) -static void pspButtonKey(uint8_t button, bool down); +static void pspButton(uint8_t button, bool down); static int pspExitCallback(int arg1, int arg2, void *common); static int pspExitThread(SceSize args, void *argp); +static void pspPointerTick(void); #endif static void rawStickEvent(SDL_JoystickID id); static void reshow(void); @@ -240,6 +253,13 @@ static uint64_t gWebYieldUs = 0; // when the browser last had its tur #if defined(__APPLE__) static char *gAppleWriteFolder = NULL; // SDL's, NULL until made #endif +#if defined(__PSP__) +// The nub's pointer, in stage pixels to a fraction (it starts in the middle, +// as the core's does), and when the last pump moved it. +static float gPointerX = (float)JL_SURFACE_WIDTH / 2.0f; +static float gPointerY = (float)JL_SURFACE_HEIGHT / 2.0f; +static uint64_t gPointerNs = 0u; +#endif #if defined(__PSP__) // The PSP loads a program by its module information. Its main thread may @@ -299,7 +319,7 @@ static void charFromKey(SDL_Scancode code) { // Where the frame goes in the window: as large as fits at the stage's shape, -// centred (on the PSP's screen, at 1:1). +// centered (on the PSP's screen, at 1:1). static void frameRect(SDL_FRect *dst) { int outW; int outH; @@ -340,14 +360,17 @@ static void fullScreenToggle(void) { #if defined(__PSP__) -// The keyboard took a button: while it is open stick 0 rests (the d-pad and -// cross are the keyboard's), and once it has closed the stick is read again; -// either way the screen shows the keyboard as it is now. +// The keyboard took a button: while it is open stick 0 and the mouse buttons +// rest (the d-pad, cross, L, and R are the keyboard's), and once it has +// closed the stick is read again; either way the screen shows the keyboard +// as it is now. static void keyboardTook(SDL_JoystickID id) { if (jlPspKeyboardIsOpen()) { jlHostedJoystickMove(JL_JOYSTICK_0, 0, 0); jlHostedJoyButtonEvent(JL_JOYSTICK_0, JL_JOY_BUTTON_0, false); jlHostedJoyButtonEvent(JL_JOYSTICK_0, JL_JOY_BUTTON_1, false); + jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_LEFT, false); + jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_RIGHT, false); } else { padUpdate(id); } @@ -534,6 +557,8 @@ static void padUpdate(SDL_JoystickID id) { int axes; int8_t x; int8_t y; + int16_t rawX; + int16_t rawY; Uint8 hat; slot = padSlot(id); @@ -542,8 +567,16 @@ static void padUpdate(SDL_JoystickID id) { } pad = gPads[slot]; if (pad != NULL) { - x = stickAxis(SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTX), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_LEFT), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - y = stickAxis(SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTY), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_UP), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); +#if defined(__PSP__) + // The nub is the mouse (pspPointerTick): the stick is the d-pad. + rawX = 0; + rawY = 0; +#else + rawX = SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTX); + rawY = SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTY); +#endif + x = stickAxis(rawX, SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_LEFT), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + y = stickAxis(rawY, SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_UP), SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); jlHostedJoystickMove((jlJoystickE)slot, x, y); jlHostedJoyButtonEvent((jlJoystickE)slot, JL_JOY_BUTTON_0, SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_SOUTH)); jlHostedJoyButtonEvent((jlJoystickE)slot, JL_JOY_BUTTON_1, SDL_GetGamepadButton(pad, SDL_GAMEPAD_BUTTON_EAST)); @@ -600,12 +633,17 @@ static const char *programName(void) { #if defined(__PSP__) // Start is the Return key and Select the Escape key, at a press (the -// keyboard on the screen has every key). -static void pspButtonKey(uint8_t button, bool down) { +// keyboard on the screen has every key); L and R are the mouse's left and +// right buttons. +static void pspButton(uint8_t button, bool down) { if (button == SDL_GAMEPAD_BUTTON_START) { keyEvent(SDL_SCANCODE_RETURN, down, false); } else if (button == SDL_GAMEPAD_BUTTON_BACK) { keyEvent(SDL_SCANCODE_ESCAPE, down, false); + } else if (button == SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) { + jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_LEFT, down); + } else if (button == SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER) { + jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_RIGHT, down); } } @@ -633,6 +671,39 @@ static int pspExitThread(SceSize args, void *argp) { sceKernelSleepThreadCB(); return 0; } + + +// The pointer moved by the nub's tilt for the time since the last pump: in +// the tilt's direction, at a speed that grows from nothing at the dead zone +// to PSP_POINTER_SPEED at full tilt, held to the stage. It rests while the +// keyboard covers the stage. +static void pspPointerTick(void) { + uint64_t now; + float seconds; + float x; + float y; + float tilt; + float step; + + now = SDL_GetTicksNS(); + seconds = gPointerNs == 0u ? 0.0f : (float)(now - gPointerNs) / PSP_NS_PER_S; + gPointerNs = now; + if (gPads[JL_JOYSTICK_0] == NULL || jlPspKeyboardIsOpen()) { + return; + } + x = (float)SDL_GetGamepadAxis(gPads[JL_JOYSTICK_0], SDL_GAMEPAD_AXIS_LEFTX) / (float)INT16_MAX; + y = (float)SDL_GetGamepadAxis(gPads[JL_JOYSTICK_0], SDL_GAMEPAD_AXIS_LEFTY) / (float)INT16_MAX; + tilt = SDL_sqrtf(x * x + y * y); + if (tilt <= PSP_NUB_DEAD_ZONE) { + return; + } + // A diagonal reads past 1 (each axis is full well before the edge). + step = ((tilt < 1.0f ? tilt : 1.0f) - PSP_NUB_DEAD_ZONE) / (1.0f - PSP_NUB_DEAD_ZONE); + step = step * PSP_POINTER_SPEED * (seconds < PSP_POINTER_STEP_S ? seconds : PSP_POINTER_STEP_S) / tilt; + gPointerX = SDL_clamp(gPointerX + x * step, 0.0f, (float)(JL_SURFACE_WIDTH - 1)); + gPointerY = SDL_clamp(gPointerY + y * step, 0.0f, (float)(JL_SURFACE_HEIGHT - 1)); + jlHostedMouseMove((int16_t)gPointerX, (int16_t)gPointerY); +} #endif @@ -850,11 +921,10 @@ void jlHostBackendInputPoll(void) { } -// The PSP has none (its pointer is the on-screen keyboard's); elsewhere, what -// SDL finds. +// The PSP's is its nub (pspPointerTick); elsewhere, what SDL finds. bool jlHostBackendMousePresent(void) { #if defined(__PSP__) - return false; + return true; #else return SDL_HasMouse(); #endif @@ -990,8 +1060,6 @@ void jlHostBackendPump(void) { jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_LEFT, e.button.down); } else if (e.button.button == SDL_BUTTON_RIGHT) { jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_RIGHT, e.button.down); - } else if (e.button.button == SDL_BUTTON_MIDDLE) { - jlHostedMouseButtonEvent(JL_MOUSE_BUTTON_MIDDLE, e.button.down); } break; @@ -1025,13 +1093,10 @@ void jlHostBackendPump(void) { break; case SDL_EVENT_GAMEPAD_AXIS_MOTION: -#if defined(__PSP__) - // The stick rests while the keyboard is open. - if (jlPspKeyboardIsOpen()) { - break; - } -#endif +#if !defined(__PSP__) + // (The PSP's only axes are its nub's, read by pspPointerTick.) padUpdate(e.gaxis.which); +#endif break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: @@ -1044,7 +1109,7 @@ void jlHostBackendPump(void) { if (!jlPspKeyboardIsOpen()) { padUpdate(e.gbutton.which); } - pspButtonKey(e.gbutton.button, e.gbutton.down); + pspButton(e.gbutton.button, e.gbutton.down); #else padUpdate(e.gbutton.which); #endif @@ -1059,6 +1124,9 @@ void jlHostBackendPump(void) { break; } } +#if defined(__PSP__) + pspPointerTick(); +#endif } diff --git a/src/hosted/win16/win16Backend.c b/src/hosted/win16/win16Backend.c index f7daf7b..c89baa5 100644 --- a/src/hosted/win16/win16Backend.c +++ b/src/hosted/win16/win16Backend.c @@ -8,7 +8,7 @@ // KERNEL and MMSYSTEM -- called from 32-bit flat-model code through Watcom's // Win386 supervisor (make/win16.mk), which turns each pointer argument into // the 16:16 pointer Windows takes. Two kinds of pointer it cannot turn are -// made here: the band the window is drawn through and the colour tables, +// made here: the band the window is drawn through and the color tables, // given to GDI as 16:16 aliases made once (the supervisor's own conversion // of a DIB's bits on every show left Wine faulting at exit), and the waveOut // buffers, which the sound driver keeps using after the call returns -- @@ -27,10 +27,10 @@ // each presented row's changed pixels as the stage holds them // (jlHostBackendFrameRow), and this backend expands them, 8 bits a pixel, // scaled to the window as it goes. Each pixel is its SCB palette's base and -// its colour (the bases, below), through a colour +// its color (the bases, below), through a color // table holding all 16 palettes, so SCB palettes are exact and a palette -// change only touches the table; on a 256-colour display the table is a -// logical palette, kept in the window's own DC, so the colours are exact as +// change only touches the table; on a 256-color display the table is a +// logical palette, kept in the window's own DC, so the colors are exact as // far as the display has room. The window shows the frame at the stage's // shape, 4:3 by default or square pixels with JOEYLIB_ASPECT=square, black // around it. With Microsoft's WinG 1.0 installed (wing.dll; JOEYLIB_WING=0 @@ -45,7 +45,7 @@ // window rows as show it. A 386 takes a large part of a second to draw the // whole window, so a show draws only the rectangles the core says changed, // those close enough together as one, and the whole window only when it -// must: when it is uncovered or resized, or its colours changed outside the +// must: when it is uncovered or resized, or its colors changed outside the // identity layout (below). JOEYLIB_SCALE=N opens it N times 320 wide // (default 2), JOEYLIB_FULLSCREEN=1 on the whole screen (a borderless // window over it; the display mode is left alone), all in the DOS @@ -119,7 +119,7 @@ // rectangles closer than one draw's cost are drawn as one // (jlHostedRectsMerge). WinGBitBlt from the canvas, which the rows were // expanded into as they were presented; SetBitmapBits and BitBlt; and -// SetDIBitsToDevice, which matches every pixel's colour. +// SetDIBitsToDevice, which matches every pixel's color. #define DRAW_WING_US 1300u #define DRAW_WING_OUT_NS 140u #define DRAW_WING_STAGE_NS 0u @@ -171,16 +171,16 @@ #define DDB_ROW_ALIGN_BITS 16u #define BITS_PER_BYTE 8u -// The frame's colour table: every palette's sixteen, (palette << 4) | colour. +// The frame's color table: every palette's sixteen, (palette << 4) | color. #define FRAME_COLORS (JL_SURFACE_PALETTE_COUNT * JL_SURFACE_COLORS_PER_PALETTE) #define FRAME_BITS_PER_PIXEL 8 #define LOG_PALETTE_VERSION 0x300 -// The identity layout (WinG's): the system's 20 static colours keep 0..9 +// The identity layout (WinG's): the system's 20 static colors keep 0..9 // and 246..255, and each palette the SCBs use takes a 16-entry block of the // 236 between, reserved, so the frame's indices are the screen's own and a -// colour change is animated in place. Past IDENTITY_PALETTES palettes in -// use, the frame is laid out (palette << 4) | colour and a colour change +// color change is animated in place. Past IDENTITY_PALETTES palettes in +// use, the frame is laid out (palette << 4) | color and a color change // redraws the window. #define STATIC_LOW_COUNT 10u #define STATIC_HIGH_FIRST 246u @@ -230,9 +230,9 @@ typedef struct { RGBQUAD colors[FRAME_COLORS]; } FrameInfoT; -// The frame's header with its colour table as indices into the logical +// The frame's header with its color table as indices into the logical // palette (DIB_PAL_COLORS): on a palette display, GDI then matches no -// colours at each blit. +// colors at each blit. typedef struct { BITMAPINFOHEADER header; WORD indices[FRAME_COLORS]; @@ -600,7 +600,7 @@ static int32_t gCanvasStep = 0; static uint32_t gCanvasRow[JL_SURFACE_HEIGHT]; static LogPaletteT gLogPalette; static HPALETTE gPalette = NULL; -// The core's colour table (jlHostBackendColors), where each palette's sixteen +// The core's color table (jlHostBackendColors), where each palette's sixteen // are in the frame, the palettes the SCBs use, and whether the identity // layout is in force (jlHostBackendPaletteBases). static const uint32_t *gColors = NULL; @@ -860,7 +860,7 @@ static void ddbsFree(void) { // Where the frame goes in the window: as large as fits at the stage's shape, -// centred. +// centered. static void frameRect(RECT *dst) { RECT client; int32_t outW; @@ -997,7 +997,7 @@ static void paletteAnimate(uint16_t first) { } -// Entry index of the frame's colour tables and the logical palette. +// Entry index of the frame's color tables and the logical palette. static void paletteEntrySet(uint16_t index, uint32_t rgb, BYTE flags) { gFrameInfo.colors[index].rgbRed = (BYTE)(rgb >> RGB24_RED_SHIFT); gFrameInfo.colors[index].rgbGreen = (BYTE)(rgb >> RGB24_GREEN_SHIFT); @@ -1009,9 +1009,9 @@ static void paletteEntrySet(uint16_t index, uint32_t rgb, BYTE flags) { } -// Every entry of the colour tables and the logical palette for the layout in +// Every entry of the color tables and the logical palette for the layout in // force, realized afresh at the next show, which draws the whole window. The -// identity layout copies the system's static colours and, as WinG does, +// identity layout copies the system's static colors and, as WinG does, // clears the system palette first, so the reserved blocks land at their own // indices. static void paletteLoad(void) { @@ -1149,7 +1149,7 @@ static uint8_t probeFramePixel(uint16_t x, uint16_t y) { // The identity layout's checks: each window pixel of a raw 8-bit capture // (screen, rowBytes a row) that is not the frame's own index for the stage -// pixel it shows (gColMap, gRowMap), and each colour of a palette in use +// pixel it shows (gColMap, gRowMap), and each color of a palette in use // that the system palette does not hold, worked out from the stage's own // entries. *compared grows by what was compared. static uint32_t probeIdentity(const jlSurfaceT *src, HDC dc, const uint8_t *screen, uint16_t w, uint16_t h, uint16_t rowBytes, uint32_t *compared) { @@ -1435,11 +1435,6 @@ static LRESULT CALLBACK windowProc(HWND window, UINT message, WPARAM wParam, LPA mouseButton(window, JL_MOUSE_BUTTON_RIGHT, message == WM_RBUTTONDOWN); return 0; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - mouseButton(window, JL_MOUSE_BUTTON_MIDDLE, message == WM_MBUTTONDOWN); - return 0; - case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { SetCursor(NULL); @@ -1504,7 +1499,7 @@ static void wingClose(void) { } -// count entries of the frame's colour table from first into WinG's bitmap, +// count entries of the frame's color table from first into WinG's bitmap, // which must match the screen's palette for WinGBitBlt to copy straight. static void wingColors(uint16_t first, uint16_t count) { if (gWinGDc != NULL) { @@ -1703,7 +1698,7 @@ void jlHostBackendClose(void) { // In the identity layout the screen's pixels are the reserved entries // themselves: a palette in use is animated, nothing redrawn. Otherwise the -// tables change and the window is redrawn in the new colours. +// tables change and the window is redrawn in the new colors. void jlHostBackendColors(const uint32_t *colors, uint16_t palettes) { uint16_t p; uint16_t c; @@ -1733,7 +1728,7 @@ void jlHostBackendColors(const uint32_t *colors, uint16_t palettes) { SetPaletteEntries(gPalette, first, JL_SURFACE_COLORS_PER_PALETTE, &gLogPalette.entries[first]); } wingColors(first, JL_SURFACE_COLORS_PER_PALETTE); - // The pixels on screen were drawn in the old colours. + // The pixels on screen were drawn in the old colors. gRepaintAll = true; } } @@ -1808,7 +1803,7 @@ const char *jlHostBackendOpen(void) { return "cannot make the program's folder the current directory"; } // Every running copy has a class of its own: the window procedure is in - // its own 32-bit image. Its icon, shown while the window is minimised, + // its own 32-bit image. Its icon, shown while the window is minimized, // is the program's, which make binds in as resource HOSTED_ICON_ID // (make/hosted.mk). if (!gClassMade) { @@ -1843,7 +1838,7 @@ const char *jlHostBackendOpen(void) { gFrameInfo.header.biBitCount = FRAME_BITS_PER_PIXEL; gFrameInfo.header.biClrUsed = FRAME_COLORS; gFrameInfo16 = AllocAlias16(&gFrameInfo); - // A 256-colour display shows the frame's colours through a logical + // A 256-color display shows the frame's colors through a logical // palette of them. memset(&gLogPalette, 0, sizeof(gLogPalette)); gLogPalette.version = LOG_PALETTE_VERSION; @@ -2014,7 +2009,7 @@ bool jlpSaveDirEnsure(const char *dir) { // window after a full repaint; plus, at a whole-number scale, those that // differ from GDI's own stretch of the frame (this backend's scaler checked // against Windows'). Every comparison is of the screen's raw pixels, read -// back the same way, so no colour matching comes into it; the window is +// back the same way, so no color matching comes into it; the window is // left fully repainted. *compared: every pixel compared. uint32_t jlHostWin16ProbeDisplayMismatch(const jlSurfaceT *src, uint32_t *compared) { RECT dst; diff --git a/src/hosted/win32/win32Backend.c b/src/hosted/win32/win32Backend.c index f4227ee..32c2ce5 100644 --- a/src/hosted/win32/win32Backend.c +++ b/src/hosted/win32/win32Backend.c @@ -233,7 +233,7 @@ static void dibCopy(const HostedRectT *rect) { // Where the frame goes in the window: as large as fits at the stage's shape, -// centred. +// centered. static void frameRect(RECT *dst) { RECT client; LONG outW; @@ -487,11 +487,6 @@ static LRESULT CALLBACK windowProc(HWND window, UINT message, WPARAM wParam, LPA mouseButton(window, JL_MOUSE_BUTTON_RIGHT, message == WM_RBUTTONDOWN); return 0; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - mouseButton(window, JL_MOUSE_BUTTON_MIDDLE, message == WM_MBUTTONDOWN); - return 0; - case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { SetCursor(NULL); diff --git a/src/iigs/audio_full.c b/src/iigs/audio_full.c index d3c1568..edc2f6c 100644 --- a/src/iigs/audio_full.c +++ b/src/iigs/audio_full.c @@ -120,7 +120,7 @@ typedef void *Pointer; // the streaming buffer. #define SFX_BASE_OSC 14 // 1 streaming/interrupt oscillator + 2 playing oscillators per slot, so a -// sound can be CENTRED: the DOC pans per oscillator, with no way to put one +// sound can be CENTERED: the DOC pans per oscillator, with no way to put one // oscillator in both ears. 5 slots * 3 = oscs 14..28, clear of NTP's music // oscillators (0..13) and its interrupt timer (31). #define SFX_OSCS_PER_SLOT 3 @@ -133,7 +133,7 @@ typedef void *Pointer; // output channel. On the IIgs channel 0 is the RIGHT output and channel 1 is // the LEFT one -- MAME wires the DOC as add_route(0,"rspeaker"), // add_route(1,"lspeaker"), matching the hardware. This file used to define a -// single `SFX_CHANNEL_LEFT 0x00`, which is neither left nor centred: every +// single `SFX_CHANNEL_LEFT 0x00`, which is neither left nor centered: every // sound came out of the right ear only. It goes unnoticed on a stock IIgs // because its built-in output is mono and sums both channels. // Mirrors jlAudioPanE (joey/audio.h); the HAL boundary passes a plain byte. @@ -362,7 +362,7 @@ static const uint8_t gToneMarker = 0; static const uint8_t gNoiseMarker = 0; // Per-slot stereo placement (jlAudioSetPan). Index is AUDIO_PAN_*; the DOC -// pans per oscillator, so a centred slot sounds both of its playing +// pans per oscillator, so a centered slot sounds both of its playing // oscillators and a panned one mutes the wrong side. static uint8_t gSlotPan[JL_AUDIO_SFX_SLOTS] = { SFX_PAN_CENTER, SFX_PAN_CENTER, SFX_PAN_CENTER, SFX_PAN_CENTER, SFX_PAN_CENTER @@ -841,7 +841,7 @@ static void ntpSilentModule(uint32_t base) { } -// Write the volume pair for a slot's two playing oscillators, honouring its +// Write the volume pair for a slot's two playing oscillators, honoring its // pan. Oscillator 0 is on DOC channel 0 (right), oscillator 1 on channel 1 // (left); silencing one is how a single-oscillator-per-side chip pans. static void sfxApplyPan(unsigned char *sfx, uint8_t slot, uint8_t vol) { @@ -857,7 +857,7 @@ static void sfxApplyPan(unsigned char *sfx, uint8_t slot, uint8_t vol) { // unsigned format, then pad the tail out to NTP's 512-byte page. // // Two separate 0x00 hazards, both of which halt the oscillator dead -- -// 0x00 in DOC RAM is the ES5503's wave-end byte, and the chip honours it +// 0x00 in DOC RAM is the ES5503's wave-end byte, and the chip honors it // even in free-run mode: // - the sign flip alone turns a perfectly legal -128 sample into 0x00, // so it clamps to -127. One level out of 256, and every caller's @@ -939,7 +939,7 @@ static void sfxFillStruct(uint8_t slot, uint32_t slotBase, uint32_t length, uint // own DOC IRQ to keep refilling it -- the NTP source describes it as // playing SFX "alongside music". That IRQ exists only once the replayer // has been prepared, so the guard here is gNTPArmed. Calling in before -// then executes NTP entry code against uninitialised state and takes +// then executes NTP entry code against uninitialized state and takes // the machine down; guarding on gNTPPlaying instead would be safe but // would silence every SFX in an application that plays no music, which // is all of them today. @@ -1315,7 +1315,7 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1 // Streaming SFX. NTPstreamsound plays one resident buffer and stops, // so the stream is chunked: arm the slot, push the first chunk now, // and let jlpAudioFrameTick push each following one as the previous -// is due to run out. The seam therefore quantises to a frame, which +// is due to run out. The seam therefore quantizes to a frame, which // is the jitter the public contract documents. void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) { streamStart(slot, fill, ctx, rateHz, false); diff --git a/src/iigs/hal.c b/src/iigs/hal.c index 4dcb84e..77bc838 100644 --- a/src/iigs/hal.c +++ b/src/iigs/hal.c @@ -305,7 +305,7 @@ static void uploadScbAndPaletteIfNeeded(const jlSurfaceT *src) { jlgStageScbDirty = false; } if (jlgStagePaletteDirty) { - // Only the palettes that changed: a colour-cycling effect touches + // Only the palettes that changed: a color-cycling effect touches // one palette a frame, and 32 bytes beats 512 sixteen times over. uint16_t mask = jlgStagePaletteDirtyMask; uint8_t pal; @@ -434,7 +434,7 @@ void jlpPresent(const jlSurfaceT *src) { // SCB + palette upload via the C-side memcpy to bank $E1, then pixels. // (The asm's old SCB/palette MVN paths never wrote $E1:9E00 reliably and // were dead once this C path landed; PERF-AUDIT #52 removed them along - // with the per-present scbPtr/palettePtr/uploadFlags marshalling, so + // with the per-present scbPtr/palettePtr/uploadFlags marshaling, so // jlIigsBlitStageToShr is pixels-only and takes no args.) // The KNOWN RESIDUAL that used to be documented here -- "the PEI-slam // corrupts $E1:9E20+ (palettes 1-15) on a DRAW" -- described correct @@ -665,7 +665,7 @@ void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint // bytes (by IIGS_PASTE_*: a doubled pixel pair from one source byte, or a // byte joining two), and a pixel alone in a row's first byte's low nibble // (an odd x) or its last byte's high one is plotted here. Doubled pixels -// from an odd x always leave both: the bytes between join neighbours. +// from an odd x always leave both: the bytes between join neighbors. void jlpSurfacePasteIndexed(jlSurfaceT *dst, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *src, uint16_t srcStride, bool doubleWide) { uint8_t *row; uint16_t head; diff --git a/src/iigs/iigsLayerBlit.s b/src/iigs/iigsLayerBlit.s index 15772c3..1a0ba29 100644 --- a/src/iigs/iigsLayerBlit.s +++ b/src/iigs/iigsLayerBlit.s @@ -277,7 +277,7 @@ lbDone: ; even, W(k) at byte k, the odd word at k is, big-endian, the 24 bits ; e[k] e[k+1] e[k+2] shifted right a nibble: (xba W(k)) << 4, or'd with ; the high nibble of e[k+2], swapped back. Words 0, 2, and 4 are odd -; bytes 4 c - 1 .. 4 c + 4: the slot's own four and its neighbours' near +; bytes 4 c - 1 .. 4 c + 4: the slot's own four and its neighbors' near ; bytes, which come out as they were, from the same even bytes. Each W ; feeds two odd words, so a row reads four (the last one's high byte is ; past the slot's bytes, never used). diff --git a/src/iigs/input.c b/src/iigs/input.c index 8c51d73..d332457 100644 --- a/src/iigs/input.c +++ b/src/iigs/input.c @@ -36,12 +36,16 @@ // Mouse: $C024 (delta data) and $C027 (status). Each $C024 read // returns one signed 7-bit delta; $C027 bit 1 indicates whether the // next read will return X (0) or Y (1). On the Y read, $C024 bit 7 -// also encodes inverted button state (0 = pressed). We do exactly two -// $C024 reads per jlpInputPoll, accumulating the deltas onto an -// absolute position which is clamped to the surface rectangle. The -// IIgs ADB MCU autopolls the mouse and queues fifos behind these -// softswitches, so the per-frame two-read cadence keeps up with -// normal motion; bursts may lag by a frame. +// also encodes inverted button state (0 = pressed). We read $C024 only +// while $C027 bit 7 says it holds a pair not yet read, at most the two +// bytes of one pair per jlpInputPoll, accumulating the deltas onto an +// absolute position which is clamped to the surface rectangle. (Read +// with none waiting, it gives the last pair again, its deltas counted +// twice -- or, before the mouse has ever reported, zeros, whose Y byte +// says the button is down.) jlpInputInit drops what waits as the program +// starts (mouseSettle). The IIgs ADB MCU autopolls the mouse and queues +// fifos behind these softswitches, so the per-frame two-read cadence keeps +// up with normal motion; bursts may lag by a frame. #include <string.h> @@ -85,7 +89,7 @@ #define MOD_OPTION 0x40 // $C027 layout (IIgs Hardware Reference / ADB MCU): -#define KMSTATUS_MOUSE_DATA 0x80 // mouse data available +#define KMSTATUS_MOUSE_DATA 0x80 // a pair waits in $C024 (its Y read clears it) #define KMSTATUS_MOUSE_COORD 0x02 // 0 = next $C024 read is X, 1 = Y // $C024 mouse-data layout: bit 7 on Y reads encodes button (0=down). @@ -93,6 +97,11 @@ #define MOUSE_DELTA_MASK 0x7F #define MOUSE_DELTA_SIGN_BIT 0x40 #define MOUSE_BUTTON_INV 0x80 +#define MOUSE_BYTES_PER_PAIR 2 +// mouseSettle: the frames it waits for more of what the ADB controller had +// queued, and the most bytes it reads at a time (a stuck status bit). +#define MOUSE_SETTLE_FRAMES 3u +#define MOUSE_DRAIN_GUARD 16u #define ASCII_TABLE_SIZE 128 @@ -118,6 +127,7 @@ static const char kShiftedDigits[DIGIT_COUNT] = { ')', '!', '@', '#', '$', '%', static int8_t analogPaddle(uint8_t v, uint8_t center, uint8_t deadZone); static void buildAsciiTable(void); +static void mouseSettle(void); static void pollJoystickButtons(void); static uint32_t pollJoystickTight(void); static void pollMouse(void); @@ -247,7 +257,7 @@ static bool gJoyDisconnectLatched = false; // Analog calibration: gJoyCenterX/Y hold the raw paddle reading captured // at the first successful poll after init, and again after each -// jlJoystickReset, as every analog port centres its sticks. Until one is +// jlJoystickReset, as every analog port centers its sticks. Until one is // captured gJoyCenterValid is false and the axes read 0. gJoyRecalibrate // is set by jlpInputInit and jlpJoystickReset and cleared by the capture. // uint8_t (not bool) so the per-element stride is a known 1 byte. @@ -304,7 +314,7 @@ extern uint32_t jlIigsPollJoystickInner(void); // read's by a count. An axis that counts 0 timed out before // the count began: only a low pass can read it. // The last reading picks the first pass: a stick with both axes low or -// centred costs one low pass, one past the low pass's reach a high pass +// centered costs one low pass, one past the low pass's reach a high pass // first, and then, for an axis that was already out when the high pass // began counting (a corner), a low pass that stops at that axis. Each // case costs about what the classic read does. An axis the passes cannot @@ -335,7 +345,7 @@ extern uint16_t jlgIigsJoyStart; static uint8_t gJoyTightLast[JOY_AXES] = { JOY_PADDLE_CENTER, JOY_PADDLE_CENTER }; // Buttons are I/O reads -- always cheap, so every poll takes them. The -// paddles are timed a count at a time (~1.4 ms at centre), so they wait +// paddles are timed a count at a time (~1.4 ms at center), so they wait // for jlpJoystickAxesRead: the poll only marks them stale. static void pollJoystickButtons(void) { // Indexing through a (uint8_t *) cast collapses each @@ -505,11 +515,31 @@ static uint32_t pollJoystickTight(void) { } -// Drain one X+Y delta pair from the ADB mouse FIFO. $C027 bit 1 tells -// us which coordinate the next $C024 read will return; we honor that -// rather than assuming an order, so we stay in sync even if a stray -// $C024 read happened between frames. The Y read also carries the -// inverted button state in bit 7 (0 = pressed). +// Read and drop what the ADB controller has queued for the mouse as the +// program starts, and what follows it within MOUSE_SETTLE_FRAMES. Started +// off a game disk, with no Finder to have read them, a program found two +// reports waiting: the button down, then up a frame later -- a click +// nobody made, which sent the adventure's ego walking. +static void mouseSettle(void) { + uint16_t frame; + uint16_t guard; + + for (frame = 0; frame <= MOUSE_SETTLE_FRAMES; frame++) { + if (frame != 0u) { + jlpWaitVBL(); + } + for (guard = 0; guard < MOUSE_DRAIN_GUARD && (*IIGS_KMSTATUS & KMSTATUS_MOUSE_DATA) != 0u; guard++) { + (void)*IIGS_MOUSEDATA; + } + } +} + + +// Drain one X+Y delta pair from the ADB mouse FIFO, if one waits ($C027 +// bit 7). $C027 bit 1 tells us which coordinate the next $C024 read will +// return; we honor that rather than assuming an order, so we stay in sync +// even if a stray $C024 read happened between frames. The Y read also +// carries the inverted button state in bit 7 (0 = pressed). static void pollMouse(void) { uint8_t status; uint8_t data; @@ -518,8 +548,11 @@ static void pollMouse(void) { bool isYRead; uint16_t i; - for (i = 0; i < 2; i++) { - status = *IIGS_KMSTATUS; + for (i = 0; i < MOUSE_BYTES_PER_PAIR; i++) { + status = *IIGS_KMSTATUS; + if ((status & KMSTATUS_MOUSE_DATA) == 0u) { + break; + } isYRead = (status & KMSTATUS_MOUSE_COORD) != 0; data = *IIGS_MOUSEDATA; delta = signExtend7(data); @@ -534,7 +567,7 @@ static void pollMouse(void) { } gMouseAbsY = newPos; // Button bit only meaningful on Y reads. 0 = pressed. - jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (data & MOUSE_BUTTON_INV) == 0; + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (data & MOUSE_BUTTON_INV) == 0u; } else { newPos = (int16_t)(gMouseAbsX + delta); if (newPos < 0) { @@ -549,10 +582,8 @@ static void pollMouse(void) { jlgMouseX = gMouseAbsX; jlgMouseY = gMouseAbsY; - // The ADB mouse only reports the single physical button; right - // and middle stay false. - jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = false; - jlgMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = false; + // The ADB mouse has one button: the right one is a Ctrl-click + // (src/core/input.c), and this never sets it. } @@ -569,6 +600,7 @@ void jlpInputInit(void) { jlgMouseAttached = true; // the ADB mouse is part of every IIgs gMouseAbsX = JL_SURFACE_WIDTH / 2; gMouseAbsY = JL_SURFACE_HEIGHT / 2; + mouseSettle(); // The stick's resting position is captured at the first good read, and // a stick a previous run found missing is looked for again. diff --git a/src/iigs/joeyDraw.s b/src/iigs/joeyDraw.s index c8314e5..f22aa1c 100644 --- a/src/iigs/joeyDraw.s +++ b/src/iigs/joeyDraw.s @@ -34,7 +34,7 @@ ; void jlIigsInitRowLut(void) ; ; One-shot init for the y*160 lookup table at jlgRowOffsetLut. C calls -; this once from jlpInit; afterwards every primitive that needs y -> +; this once from jlpInit; afterward every primitive that needs y -> ; row byte offset can do `lda jlgRowOffsetLut,x` (X = y*2) in a few cyc ; instead of a shift-add chain. 200 entries x 2 bytes = 400 bytes. ; @@ -1285,7 +1285,7 @@ tpmCombo = 8 ; tile.c leaves its C body out): an 8x8 ONE-BIT-PER-PIXEL glyph pasted ; into the stage with the dirty mark fused, in one JSL. The C wrapper ; that used to stand in front of the paste spent ~250 cycles a glyph, a -; third of the paste, on its checks, its colour masks and a second JSL +; third of the paste, on its checks, its color masks and a second JSL ; with every argument pushed again. A glyph off the surface, or with no ; bits, is nothing, as in C; any surface but the stage goes on, checked, ; to jlpGenericTilePasteGlyph with the same arguments. @@ -1397,11 +1397,11 @@ tpgStage: adc #12 tcd ; [0] = bits -; The 16-word colour table depends only on (fg, bg), and consecutive -; cells almost always share them (a level is painted in colour runs), +; The 16-word color table depends only on (fg, bg), and consecutive +; cells almost always share them (a level is painted in color runs), ; so it is kept across calls: rebuild only when the pair changes. The ; key is fg << 4 | bg; a zeroed bss key with zeroed tables IS the -; fg = bg = 0 table, so no initialisation is needed. +; fg = bg = 0 table, so no initialization is needed. lda 16,s ; fg asl a asl a @@ -5013,7 +5013,7 @@ jlIigsFcFillHi: ; The old scbPtr/palettePtr/uploadFlags args and their flag-gated MVN ; upload blocks were dead (uploadFlags was hard-coded 0 at the only ; call site) and were deleted along with the per-present argument -; marshalling they required (PERF-AUDIT #52). +; marshaling they required (PERF-AUDIT #52). ; ; ABI: no args. Phase 3 (the slam) clobbers D freely via TCD to row ; bases; the final PLD restores the caller's D (saved by our PHD) and @@ -5136,7 +5136,7 @@ peiRowClean: ; so most of the 200 row visits were pure scan. The flag is only ever a ; HINT -- a set byte means "may be dirty" and the band test above stays ; the authority -- so a stale-set flag costs time, never correctness. -; The index maths runs in M=16 throughout: TAX/TAY there move a clean +; The index math runs in M=16 throughout: TAX/TAY there move a clean ; 16-bit value, whereas in M=8 they would carry whatever the hidden ; high byte B happens to hold. ; The flag is consulted only on a group's FIRST row: a clean row inside @@ -6083,7 +6083,7 @@ gjGotXY: gjNoJoy: ; Settle timed out (speed unchanged, so no `rol`). Report no stick -; (resolved=0) with centred axes (px=py=128). +; (resolved=0) with centered axes (px=py=128). pld ; restore caller DP plp ; back to caller M=I=16 .a16 @@ -6498,11 +6498,13 @@ jlgIigsJoyStart: ; jlIigsInputSnapshot(void) ; ; Snapshot jlgKeyState->jlgKeyPrev (JL_KEY_COUNT=60 bytes) plus -; jlgMouseButtonState/jlgJoyButtonState (4 bytes each). No args. +; jlgMouseButtonState (3 bytes) and jlgJoyButtonState (4 bytes). No args. ; The key state is copied only when jlgIigsKeysMoved says it may differ ; from jlgKeyPrev (src/iigs/input.c): an idle keyboard leaves both all -; zero. The copies are words, unrolled: 30 key words and 2 of each -; button set (input.c checks the sizes). +; zero. The copies are words, unrolled: 30 key words, the mouse's left +; and right buttons as one word from byte 1 (byte 0, JL_MOUSE_BUTTON_NONE, +; is never written, so it stays 0 in both), and 2 of the stick buttons +; (input.c checks the sizes). ; ; ABI: void; no register/stack args. DBR is the caller's data bank; ; all globals via 16-bit abs. @@ -6523,10 +6525,8 @@ jlIigsInputSnapshot: sta jlgKeyPrev+\off .endr isnButtons: - lda jlgMouseButtonState - sta jlgMouseButtonPrev - lda jlgMouseButtonState+2 - sta jlgMouseButtonPrev+2 + lda jlgMouseButtonState+1 + sta jlgMouseButtonPrev+1 lda jlgJoyButtonState sta jlgJoyButtonPrev lda jlgJoyButtonState+2 @@ -6550,7 +6550,7 @@ isnButtons: ; the shared false exit is a short branch from each. ; ==================================================================== .equ IPR_KEY_COUNT, 60 ; JL_KEY_COUNT (input.c checks it) -.equ IPR_MOUSE_COUNT, 4 ; JL_MOUSE_BUTTON_COUNT +.equ IPR_MOUSE_COUNT, 3 ; JL_MOUSE_BUTTON_COUNT .section .text.jlInputPredicates,"ax" .a16 diff --git a/src/iigs/serialIrq.s b/src/iigs/serialIrq.s index 1e8c9c6..dac53fd 100644 --- a/src/iigs/serialIrq.s +++ b/src/iigs/serialIrq.s @@ -16,12 +16,12 @@ ; fast path, for an SCC interrupt AppleTalk refused and whose RR3 bits ; are in SERFLAG ($E1/0104), with M=X=8, DBR=$E1, D=0 and ; A = RR3 & SERFLAG -- and BEFORE the ROM has switched to its own -; stack or normalised STATEREG, so this runs on the interrupted +; stack or normalized STATEREG, so this runs on the interrupted ; program's stack with whatever RAMRD/RAMWRT it had set (the ROM's ; own serial handler, serialdrvr SERPINT, saves and clears them). ; $0017 other ($E1/005C) -> jlIigsSscIsr. Called last, for an interrupt no ; built-in source claimed (a slot card), after the ROM has saved the -; machine and normalised it: M=X=8, DBR=0, D=0, main memory. +; machine and normalized it: M=X=8, DBR=0, D=0, main memory. ; ; Both return carry clear for "serviced" and M=X=8 (the ROM ROR/PLAs a byte ; right after the call). Anything that is not ours goes on to the vector's diff --git a/src/iigs/sfxCopy.s b/src/iigs/sfxCopy.s index 8cd22db..4de8603 100644 --- a/src/iigs/sfxCopy.s +++ b/src/iigs/sfxCopy.s @@ -20,7 +20,7 @@ ; counting the passes, and the 0x00 fix-ups off the straight line. ; Measured under MAME at 2.8 MHz: 7.0 us a byte, 57 ms for a full 8172- ; byte slot, against 90 us a byte for the compiled C loop, whose 32-bit -; index and bound became materialised compares on every byte. +; index and bound became materialized compares on every byte. ; ; ABI: arg0 dst in A:X (A = low16, X = high16). src (4-byte ptr), length ; and padTo on the stack: src @4,s, length @8,s, padTo @10,s on JSL diff --git a/src/iigs/spriteEmitIigs.c b/src/iigs/spriteEmitIigs.c index 1452e77..6696407 100644 --- a/src/iigs/spriteEmitIigs.c +++ b/src/iigs/spriteEmitIigs.c @@ -292,7 +292,7 @@ uint32_t jlSpriteEmitRestoreIigs(uint8_t *out, uint32_t cap, const jlSpriteT *sp // M=16 would clobber the adjacent byte. // // A store of the value A already holds skips its LDA #: a solid run of one -// colour costs 6c / 3 bytes a pair (M=16) or 5c / 3 bytes a byte (M=8). +// color costs 6c / 3 bytes a pair (M=16) or 5c / 3 bytes a byte (M=8). uint32_t jlSpriteEmitDrawIigs(uint8_t *out, uint32_t cap, const jlSpriteT *sp, uint8_t shift) { uint32_t cursor; uint16_t row; diff --git a/src/m68k/planarFill.s b/src/m68k/planarFill.s index 7b6abd5..81a5686 100644 --- a/src/m68k/planarFill.s +++ b/src/m68k/planarFill.s @@ -22,11 +22,11 @@ | built around them: | | * A PLANE'S FILL VALUE IS ONLY EVER ALL-ONES OR ALL-ZEROS. It is one bit -| of the colour smeared across a byte. So a masked edge is not a read, +| of the color smeared across a byte. So a masked edge is not a read, | two ANDs, an OR and a store; it is `or.b mask,(a0)` for a set plane and | `and.b ~mask,(a0)` for a clear one -- one instruction, one bus cycle. -| * THE COLOUR IS FIXED FOR THE WHOLE SHAPE, so which of those two it is -| need not be decided at run time. The body is emitted once per colour +| * THE COLOR IS FIXED FOR THE WHOLE SHAPE, so which of those two it is +| need not be decided at run time. The body is emitted once per color | value and dispatched to through a jump table, exactly as | src/x68000/circle.s does. Set planes then run as a group with d6 = -1 | and clear planes as a group with d6 = 0 and the masks complemented @@ -113,39 +113,39 @@ .endm -| ---- SPAN: one span in all four planes, for a known colour ---- +| ---- SPAN: one span in all four planes, for a known color ---- | Set planes first with d6 = -1, then the clear planes with d6 = 0 and the | masks complemented, so `and.b %d2` is a single instruction there too. - .macro SPAN colour - .if (\colour) != 0 + .macro SPAN color + .if (\color) != 0 moveq #-1,%d6 - .if ((\colour) & 1) + .if ((\color) & 1) PLANEFILL %a2, or .endif - .if ((\colour) & 2) + .if ((\color) & 2) PLANEFILL %a3, or .endif - .if ((\colour) & 4) + .if ((\color) & 4) PLANEFILL %a4, or .endif - .if ((\colour) & 8) + .if ((\color) & 8) PLANEFILL %a5, or .endif .endif - .if (\colour) != 15 + .if (\color) != 15 moveq #0,%d6 not.b %d2 not.b %d3 - .if !((\colour) & 1) + .if !((\color) & 1) PLANEFILL %a2, and .endif - .if !((\colour) & 2) + .if !((\color) & 2) PLANEFILL %a3, and .endif - .if !((\colour) & 4) + .if !((\color) & 4) PLANEFILL %a4, and .endif - .if !((\colour) & 8) + .if !((\color) & 8) PLANEFILL %a5, and .endif not.b %d2 | the row's second span wants @@ -154,7 +154,7 @@ .endm -| ---- CIRCLEBODY: the whole exact-disk walk for one colour ---- +| ---- CIRCLEBODY: the whole exact-disk walk for one color ---- | | Bit-identical to the chunky reference loop in src/core/draw.c jlFillCircle | and to the C walk this replaces: for each y in [0, r] find the largest x @@ -165,25 +165,25 @@ | r <= 181; the caller's contract (the bounding box is on-surface, and a | surface is 200 rows tall) caps r at 99, and halPlanar.c has a build-time | check on that. - .macro CIRCLEBODY colour -.Lrow\colour: + .macro CIRCLEBODY color +.Lrow\color: | walk x down until x*x + y*y <= r*r again move.w FC_XX(%sp),%d0 move.w FC_XLIMIT(%sp),%d1 cmp.w %d1,%d0 - bls.s .Lgeom\colour + bls.s .Lgeom\color move.w FC_X(%sp),%d4 -.Lshrink\colour: +.Lshrink\color: move.w %d4,%d5 add.w %d5,%d5 subq.w #1,%d5 | 2x - 1 sub.w %d5,%d0 | xx -= 2x - 1 subq.w #1,%d4 | x-- cmp.w %d1,%d0 - bhi.s .Lshrink\colour + bhi.s .Lshrink\color move.w %d0,FC_XX(%sp) move.w %d4,FC_X(%sp) -.Lgeom\colour: +.Lgeom\color: | span geometry, computed once for the row's two spans move.w FC_X(%sp),%d0 move.w %a6,%d1 @@ -199,7 +199,7 @@ and.w #7,%d0 move.b 8(%a1,%d0.w),%d3 | right mask sub.w %d4,%d1 - bne.s .Lmulti\colour + bne.s .Lmulti\color | Both ends in one byte. Combining the masks covers it, and | zeroing the right mask makes the right edge write the NEXT | byte back unchanged instead of needing its own code path. @@ -207,21 +207,21 @@ and.b %d3,%d2 moveq #0,%d3 moveq #0,%d4 - bra.s .Ldown\colour -.Lmulti\colour: + bra.s .Ldown\color +.Lmulti\color: subq.w #1,%d1 move.w %d1,%d4 | middle byte count -.Ldown\colour: +.Ldown\color: move.w FC_DOWN(%sp),%d7 add.w FC_FIRST(%sp),%d7 - SPAN \colour + SPAN \color | the mirrored row, unless y is zero and it IS this row move.w FC_UP(%sp),%d7 cmp.w FC_DOWN(%sp),%d7 - beq.w .Lstep\colour + beq.w .Lstep\color add.w FC_FIRST(%sp),%d7 - SPAN \colour -.Lstep\colour: + SPAN \color +.Lstep\color: addi.w #ROW_BYTES,FC_DOWN(%sp) subi.w #ROW_BYTES,FC_UP(%sp) | y*y grows by 2y+1, so the x*x limit shrinks by it @@ -230,7 +230,7 @@ addq.w #2,%d0 move.w %d0,FC_T(%sp) subq.w #1,FC_COUNT(%sp) - bne.w .Lrow\colour + bne.w .Lrow\color bra.w fcDone .endm @@ -387,7 +387,7 @@ fcDone: | ---- RECTPLANE: run plane \n's body ---- -| d0 = the first row's left edge offset, d1 = colour, d7 = rows - 1, a3/a4 = +| d0 = the first row's left edge offset, d1 = color, d7 = rows - 1, a3/a4 = | this rect's OR/AND bodies, a6 = the plane table. .macro RECTPLANE n move.l (\n * 4)(%a6),%a0 @@ -558,7 +558,7 @@ gFillWordLut: | goes on, unchanged, to jlFillRectClip, which clips it and fills it the | port's way (the Amiga's blitter, jlSurface68kFillRect on the X68000). A | planar picture is drawn as such rows -- JoeyAGI pushes a picture a run of -| one colour at a time -- and each paid the C wrapper, an eleven-register +| one color at a time -- and each paid the C wrapper, an eleven-register | fill entry, a multiply for the row and a call for the mark, several times | the few word stores the row needs; on the Amiga a blitter set-up and wait | besides. Here a row is its word masks, then each plane's run ORed into a @@ -580,12 +580,12 @@ gFillWordLut: .equ WORD_TO_BAND, 2 | x >> 2: a pixel's 16-bit stage word (its dirty band unit) .equ COLOR_MASK, 0x0F -| A row one word wide in colour \colour: each plane's word ORed with the -| mask (d2) where the colour has its bit, ANDed with its complement (d3) +| A row one word wide in color \color: each plane's word ORed with the +| mask (d2) where the color has its bit, ANDed with its complement (d3) | where not; a0-a3 = planes 0-3, d0 = the word's byte in a plane. - .macro ROWTHIN colour + .macro ROWTHIN color .irp plane, 0, 1, 2, 3 - .if (\colour >> \plane) & 1 + .if (\color >> \plane) & 1 or.w %d2,0(%a\plane,%d0.w) .else and.w %d3,0(%a\plane,%d0.w) @@ -597,7 +597,7 @@ gFillWordLut: | One plane of a row several words wide: the left edge (d2), d1 whole words | between, the right edge (d3); a clear plane ANDs the edges' complements and -| clears the words between. a2 walks the planes array, d4 = the colour +| clears the words between. a2 walks the planes array, d4 = the color | (shifted down a plane a time), d5 = scratch. .macro ROWPLANE_WIDE movea.l (%a2)+,%a1 @@ -694,12 +694,12 @@ _jlFillRect: movem.l (%a2),%a0-%a3 | the four planes jmp .Lr_thinTab(%pc,%d1.w) .Lr_thinTab: - .irp colour, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - bra.w .Lr_thin\colour + .irp color, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + bra.w .Lr_thin\color .endr - .irp colour, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -.Lr_thin\colour: - ROWTHIN \colour + .irp color, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +.Lr_thin\color: + ROWTHIN \color .endr .Lr_wide: movem.l %d4-%d5,-(%sp) diff --git a/src/m68k/planarIndexed.c b/src/m68k/planarIndexed.c index e0dc874..bd3f016 100644 --- a/src/m68k/planarIndexed.c +++ b/src/m68k/planarIndexed.c @@ -18,7 +18,7 @@ // of them, which is where scripts/check-indexed.sh proves the table and the // method. (The asm reads a first or last column's whole source window, up // to 7 bytes outside a source row; pasteRow reads only the row, the rest of -// its window colour 15.) +// its window color 15.) // // Snap: a column's 4 plane bytes are its 4 chunky bytes through // jlgPlanarSpread, as the hash reads them, whose nibbles are the diff --git a/src/m68k/planarPlot.s b/src/m68k/planarPlot.s index e8a6f82..6a83dc3 100644 --- a/src/m68k/planarPlot.s +++ b/src/m68k/planarPlot.s @@ -24,8 +24,8 @@ | | The two things that make this quick are both borrowed from the ST: | -| * 16-WAY COLOUR DISPATCH. The colour is fixed for the whole circle, so -| the plot is emitted once per colour value with the set-or-clear choice +| * 16-WAY COLOR DISPATCH. The color is fixed for the whole circle, so +| the plot is emitted once per color value with the set-or-clear choice | already made. Every plane write is then a single `or.b` or `and.b` | straight to memory -- no test, no branch, no read-back. | * PER-ITERATION PRECOMPUTE. One Bresenham step touches four distinct @@ -96,33 +96,33 @@ .endm -| ---- PLOT: one pixel, colour known at assembly time ---- +| ---- PLOT: one pixel, color known at assembly time ---- | slotY: row offset slot, slotB: xByte slot, slotM: mask slot. | Trashes d0, d1, d7. - .macro PLOT slotY, slotB, slotM, colour + .macro PLOT slotY, slotB, slotM, color move.w \slotY(%sp),%d0 add.w \slotB(%sp),%d0 | d0 = y*40 + (x >> 3) move.b \slotM(%sp),%d1 | d1 = bit - .if (\colour) != 15 + .if (\color) != 15 move.b %d1,%d7 not.b %d7 | d7 = ~bit, for the clears .endif - .if ((\colour) & 1) + .if ((\color) & 1) or.b %d1,(%a0,%d0.w) .else and.b %d7,(%a0,%d0.w) .endif - .if ((\colour) & 2) + .if ((\color) & 2) or.b %d1,(%a1,%d0.w) .else and.b %d7,(%a1,%d0.w) .endif - .if ((\colour) & 4) + .if ((\color) & 4) or.b %d1,(%a2,%d0.w) .else and.b %d7,(%a2,%d0.w) .endif - .if ((\colour) & 8) + .if ((\color) & 8) or.b %d1,(%a3,%d0.w) .else and.b %d7,(%a3,%d0.w) @@ -130,9 +130,9 @@ .endm -| ---- BODY: the whole Bresenham loop for one colour ---- - .macro BODY colour -.Lloop\colour: +| ---- BODY: the whole Bresenham loop for one color ---- + .macro BODY color +.Lloop\color: XREC 0, 8, add, %d2 | cx + bx XREC 2, 9, sub, %d2 | cx - bx XREC 4, 10, add, %d3 | cx + by @@ -142,35 +142,35 @@ YREC 16, add, %d2 | cy + bx YREC 18, sub, %d2 | cy - bx - PLOT 12, 0, 8, \colour | (cx+bx, cy+by) - PLOT 12, 2, 9, \colour | (cx-bx, cy+by) - PLOT 14, 0, 8, \colour | (cx+bx, cy-by) - PLOT 14, 2, 9, \colour | (cx-bx, cy-by) - PLOT 16, 4, 10, \colour | (cx+by, cy+bx) - PLOT 16, 6, 11, \colour | (cx-by, cy+bx) - PLOT 18, 4, 10, \colour | (cx+by, cy-bx) - PLOT 18, 6, 11, \colour | (cx-by, cy-bx) + PLOT 12, 0, 8, \color | (cx+bx, cy+by) + PLOT 12, 2, 9, \color | (cx-bx, cy+by) + PLOT 14, 0, 8, \color | (cx+bx, cy-by) + PLOT 14, 2, 9, \color | (cx-bx, cy-by) + PLOT 16, 4, 10, \color | (cx+by, cy+bx) + PLOT 16, 6, 11, \color | (cx-by, cy+bx) + PLOT 18, 4, 10, \color | (cx+by, cy-bx) + PLOT 18, 6, 11, \color | (cx-by, cy-bx) | err <= 0 must NOT shrink the major axis: the tie is what | keeps this pixel-identical to src/core/draw.c. tst.w %d4 - bgt.s .Lshrink\colour + bgt.s .Lshrink\color move.w %d2,%d0 add.w %d0,%d0 addq.w #3,%d0 add.w %d0,%d4 | err += 2*bx + 3 - bra.s .Lstep\colour -.Lshrink\colour: + bra.s .Lstep\color +.Lshrink\color: move.w %d2,%d0 sub.w %d3,%d0 add.w %d0,%d0 addq.w #5,%d0 add.w %d0,%d4 | err += 2*(bx - by) + 5 subq.w #1,%d3 | by-- -.Lstep\colour: +.Lstep\color: addq.w #1,%d2 | bx++ cmp.w %d3,%d2 - ble.w .Lloop\colour + ble.w .Lloop\color bra.w circleDone .endm @@ -189,7 +189,7 @@ _jlSurface68kPlanarCircle: move.w 82(%sp),%d6 | cy move.w %d6,%a6 move.w 86(%sp),%d3 | r -> by - move.w 90(%sp),%d0 | colour + move.w 90(%sp),%d0 | color lea gPlanarBitLut,%a5 moveq #0,%d2 | bx = 0 @@ -245,8 +245,8 @@ circleDone: | e2 >= dy / e2 <= dx tie handling that decides which pixels a 45-degree | line lands on. | -| Same two ideas as the circle above. The colour is fixed for the whole line, -| so the body is emitted once per colour value and every plane write is a bare +| Same two ideas as the circle above. The color is fixed for the whole line, +| so the body is emitted once per color value and every plane write is a bare | `or.b` or `and.b` to memory. And the row offset is CARRIED in a6 rather than | recomputed: y moves by one row at a time, so it is one add of +/-40 per step | instead of the two shifts and an add a multiply-free y*40 would cost per @@ -278,35 +278,35 @@ circleDone: .equ LN_COLOR, LN_ARG + 20 + 2 -| ---- LPLOT: one pixel at (d2, a6), colour known at assembly time ---- +| ---- LPLOT: one pixel at (d2, a6), color known at assembly time ---- | Trashes d0, d1, d7. - .macro LPLOT colour + .macro LPLOT color move.w %d2,%d0 lsr.w #3,%d0 add.w %a6,%d0 | y*40 + (x >> 3) move.w %d2,%d1 and.w #7,%d1 move.b (%a5,%d1.w),%d1 | 0x80 >> (x & 7) - .if (\colour) != 15 + .if (\color) != 15 move.b %d1,%d7 not.b %d7 .endif - .if ((\colour) & 1) + .if ((\color) & 1) or.b %d1,(%a0,%d0.w) .else and.b %d7,(%a0,%d0.w) .endif - .if ((\colour) & 2) + .if ((\color) & 2) or.b %d1,(%a1,%d0.w) .else and.b %d7,(%a1,%d0.w) .endif - .if ((\colour) & 4) + .if ((\color) & 4) or.b %d1,(%a2,%d0.w) .else and.b %d7,(%a2,%d0.w) .endif - .if ((\colour) & 8) + .if ((\color) & 8) or.b %d1,(%a3,%d0.w) .else and.b %d7,(%a3,%d0.w) @@ -314,29 +314,29 @@ circleDone: .endm -| ---- LINEBODY: the whole Bresenham walk for one colour ---- - .macro LINEBODY colour -.Lline\colour: - LPLOT \colour +| ---- LINEBODY: the whole Bresenham walk for one color ---- + .macro LINEBODY color +.Lline\color: + LPLOT \color | done when both coordinates have arrived cmp.w LN_X1(%sp),%d2 - bne.s .LlineStep\colour + bne.s .LlineStep\color cmp.w LN_Y1(%sp),%d3 beq.w lineDone -.LlineStep\colour: +.LlineStep\color: move.w %d4,%d0 add.w %d0,%d0 | e2 = 2 * err cmp.w %d6,%d0 - blt.s .LlineNoX\colour + blt.s .LlineNoX\color add.w %d6,%d4 | err += dy add.w LN_SX(%sp),%d2 | x += sx -.LlineNoX\colour: +.LlineNoX\color: cmp.w %d5,%d0 - bgt.w .Lline\colour + bgt.w .Lline\color add.w %d5,%d4 | err += dx add.w LN_SY(%sp),%d3 | y += sy adda.w LN_SY40(%sp),%a6 | and its row offset - bra.w .Lline\colour + bra.w .Lline\color .endm diff --git a/src/m68k/planarShape.s b/src/m68k/planarShape.s index 534b1d7..035487e 100644 --- a/src/m68k/planarShape.s +++ b/src/m68k/planarShape.s @@ -8,7 +8,7 @@ | the mono paste's folded silhouette) into an 8x8 tile of the four planes, | set bits taking the foreground's plane bit and clear ones the | background's. A plane's eight bytes are then only ever one of four -| things, by its two colour bits: all zeros (both 0), the shape rows +| things, by its two color bits: all zeros (both 0), the shape rows | inverted (fg 0, bg 1), the shape rows (fg 1, bg 0), or all ones (both 1). | So each plane picks its case once and stores eight unrolled bytes, each at | its row's displacement, against the C's and, xor and store a byte with a @@ -160,7 +160,7 @@ _jlTilePasteGlyph: .Lg_none: movem.l (%sp)+,%d2-%d4/%a2 rts -| No planes: the generic, with the same arguments, the colours masked. +| No planes: the generic, with the same arguments, the colors masked. .Lg_generic: andi.b #COLOR_MASK,GLYPH_FG(%sp) andi.b #COLOR_MASK,GLYPH_BG(%sp) diff --git a/src/m68k/planarTile.h b/src/m68k/planarTile.h index 79d4c61..02dd92f 100644 --- a/src/m68k/planarTile.h +++ b/src/m68k/planarTile.h @@ -468,7 +468,7 @@ static inline __attribute__((always_inline)) void planarTileMapPaste(jlSurfaceT // mono paste (which folds a chunky silhouette into these rows first) // and the glyph paste (whose input already IS these rows). A plane's eight // bytes are then all zeros, the rows inverted, the rows, or all ones, by -// its two colour bits, which src/m68k/planarShape.s picks once a plane and +// its two color bits, which src/m68k/planarShape.s picks once a plane and // stores unrolled; it is handed the rows, the plane pointers and the // tile's byte offset in registers. typedef char planarShapeGeometryProof[(PLANAR_TILE_BYTES_PER_ROW == 40 && PLANAR_TILE_PLANES == 4) ? 1 : -1]; diff --git a/src/m68k/spriteEmitInterleaved68k.c b/src/m68k/spriteEmitInterleaved68k.c index 7df570a..14d1f3a 100644 --- a/src/m68k/spriteEmitInterleaved68k.c +++ b/src/m68k/spriteEmitInterleaved68k.c @@ -31,7 +31,7 @@ // both columns' opacity, repeated) the same for both. The plane bits are // the ones the interpreter merges (both convert through spriteC2p.h's // c2pColumn), and a long's data bits always lie inside the drawn bits -// (colour 0 is transparent), so a long whose data is 0 only needs the AND +// (color 0 is transparent), so a long whose data is 0 only needs the AND // and one whose data fills every drawn bit only the OR. Group forms: // both columns opaque -> move.l D01,d16(a0); move.l D23,d16+4(a0) // both present, both longs need the AND and the OR diff --git a/src/m68k/spriteEmitPlanar68k.c b/src/m68k/spriteEmitPlanar68k.c index fa13184..8014891 100644 --- a/src/m68k/spriteEmitPlanar68k.c +++ b/src/m68k/spriteEmitPlanar68k.c @@ -31,7 +31,7 @@ // The emitter pre-shifts and OR-merges those contributions per dest // byte at emit time (destMask/destData accumulators), then emits one // write per plane of every non-empty dest byte, in the cheapest form the -// byte's mask and that plane's data allow (colour 0 is transparent, so a +// byte's mask and that plane's data allow (color 0 is transparent, so a // plane's data bits always lie inside the mask): // mask 0xFF: move.b data,off(a_i) store // data 0: and.b ~mask,off(a_i) clear the drawn bits diff --git a/src/x68000/hal.c b/src/x68000/hal.c index ce77731..19447ac 100644 --- a/src/x68000/hal.c +++ b/src/x68000/hal.c @@ -7,7 +7,7 @@ // The stage is native planar (src/x68000/halPlanar.c) and jlpPresent copies // its dirty rows into the four 1bpp TEXT planes at $E00000, which are planar // too (jlX68kPresentText, src/x68000/x68kPresent.s). The text plane has one -// 16-colour palette, so the SCB's per-line palettes are reloaded as the beam +// 16-color palette, so the SCB's per-line palettes are reloaded as the beam // scans by a CRTC raster interrupt (jlX68kRasterIsr.s). The graphics-plane // present (X68K_PRESENT_TEXT 0) is the alternative that needs no interrupt. // @@ -38,10 +38,10 @@ #define X68K_GVRAM_STRIDE 512u // _iocs_crtmod screen mode 12: 512x512, 31 kHz, PROGRESSIVE, ~55.5 Hz. It is -// the 65536-colour graphic mode, which matters to neither present path: the -// text plane has its own 16 colours whatever the graphic mode, and the graphics -// path forces 256 colours (X68K_VIDCTRL0 below). There is no 320x200 mode on -// this machine, so the stage is centred and letterboxed. +// the 65536-color graphic mode, which matters to neither present path: the +// text plane has its own 16 colors whatever the graphic mode, and the graphics +// path forces 256 colors (X68K_VIDCTRL0 below). There is no 320x200 mode on +// this machine, so the stage is centered and letterboxed. // // NOT mode 13, its 15 kHz sibling, which this port used from 2026-09-26: that // mode is INTERLACED (the CRTC reads back R20 = $0305 and R04 = 259, so a field @@ -138,14 +138,14 @@ typedef char x68kJoyPortsCoverSticks[(X68K_JOY_PORT_COUNT == JL_JOYSTICK_COUNT) // Which plane jlpPresent writes. 1 = the TEXT plane at $E00000, which is // PLANAR at 128 bytes per row -- the stage's own layout -- so a present is a -// per-row copy per plane with no pixel conversion at all. 0 = the 256-colour +// per-row copy per plane with no pixel conversion at all. 0 = the 256-color // GRAPHICS plane, where a pixel owns a whole 16-bit GVRAM word and every eight // pixels cost sixteen table reads to assemble. Measured on the text plane: // 0.5 bytes of traffic per pixel against GVRAM's 2, and no gather. // // The graphics path is kept because it is the one that needs no raster -// interrupt to honour the SCB: the 256-entry palette holds all sixteen -// 16-colour palettes at once, so (scb << 4) | nibble picks the band by value. +// interrupt to honor the SCB: the 256-entry palette holds all sixteen +// 16-color palettes at once, so (scb << 4) | nibble picks the band by value. // The text plane has ONE 16-entry palette, so per-band palettes need the // beam-chasing interrupt below. #ifndef X68K_PRESENT_TEXT @@ -187,17 +187,17 @@ typedef char x68kJoyPortsCoverSticks[(X68K_JOY_PORT_COUNT == JL_JOYSTICK_COUNT) // path does it the way the DOS port does (src/dos/hal.c:7): `pixel byte = // (scb[y] << 4) | nibble` into a 256-entry palette, so all 16 palettes are // resident at once and the PIXEL VALUE selects the band. No interrupt, no -// timing window, correct by construction. The 256-colour graphics plane has -// exactly that: a 256-entry palette at $E82000, 16 palettes x 16 colours. +// timing window, correct by construction. The 256-color graphics plane has +// exactly that: a 256-entry palette at $E82000, 16 palettes x 16 colors. // // The default text-plane present cannot (four 1bpp planes cap it at 16 -// colours, one 16-entry palette) and honours the SCB with the raster interrupt +// colors, one 16-entry palette) and honors the SCB with the raster interrupt // instead -- see "SCB per-band palette" further down. The text plane wins on // speed because it is planar like the stage, so presenting is a copy. // -// GVRAM layout in 256-colour mode, from MAME x68k_v.cpp:339-340: -// colour = gvram[lineoffset0 + x] & 0x000F; // low nibble -// colour |= gvram[lineoffset1 + x] & 0x00F0; // high nibble +// GVRAM layout in 256-color mode, from MAME x68k_v.cpp:339-340: +// color = gvram[lineoffset0 + x] & 0x000F; // low nibble +// color |= gvram[lineoffset1 + x] & 0x00F0; // high nibble // With both graphics scroll registers 0 the two line offsets are equal, so both // nibbles come from the SAME word: the palette index is that word's LOW BYTE. // One byte write per pixel, 512 words per line. @@ -215,11 +215,11 @@ typedef char x68kJoyPortsCoverSticks[(X68K_JOY_PORT_COUNT == JL_JOYSTICK_COUNT) #define X68K_VIDCTRL2 ((volatile uint16_t *)0xE82600L) #define X68K_VIDCTRL2_TEXT 0x0020u -// Video Controller register 0 ($E82400) low 2 bits select the graphic colour depth (MAME x68k_v.cpp -// switch(reg[0] & 3)): 0 = 16-colour, 1 = 256-colour palette-indexed, 3 = 65536-colour DIRECT. +// Video Controller register 0 ($E82400) low 2 bits select the graphic color depth (MAME x68k_v.cpp +// switch(reg[0] & 3)): 0 = 16-color, 1 = 256-color palette-indexed, 3 = 65536-color DIRECT. // _iocs_crtmod(13) sets the 512x512 CRTC but leaves this at 3 (direct), whereas this port draws 8-bit // PALETTE INDICES into GVRAM and uploads a 256-entry palette at $E82000 -- so the mode MUST be forced to -// 256-colour or every index is interpreted as a near-black direct colour (the "invisible/dim" bug). +// 256-color or every index is interpreted as a near-black direct color (the "invisible/dim" bug). #define X68K_VIDCTRL0 ((volatile uint16_t *)0xE82400L) #define X68K_VIDCTRL0_DEPTH 0x0003u #define X68K_VIDCTRL0_256 0x0001u @@ -354,13 +354,13 @@ static bool gVdispHooked = false; // _VDISPST took jlX68kVdispIsr #if X68K_PRESENT_TEXT // The page flip (jlX68kVdispIsr.s, or vdispPoll without it): a present hands // over the text Y scroll of the page it drew, and the palette a one-band -// screen's colours went into (NULL: none), and the next display start shows +// screen's colors went into (NULL: none), and the next display start shows // both together. jlgX68kFlipPending is set last and cleared there. volatile uint8_t jlgX68kFlipPending = 0u; volatile uint16_t jlgX68kFlipScroll = 0u; const uint16_t *volatile jlgX68kFlipPalette = NULL; static uint8_t gFrontPage = 0u; -// A one-band screen's colours, for the next flip; gPaletteQueued until then. +// A one-band screen's colors, for the next flip; gPaletteQueued until then. static uint16_t gFlipPaletteBuf[JL_SURFACE_COLORS_PER_PALETTE]; static bool gPaletteQueued = false; // The page drawn into holds the frame before last, so a present copies what @@ -376,10 +376,10 @@ static uint32_t gMillisBase = 0ul; // tickMillisNow() as jlpInit finished static uint16_t gFrameHz = X68K_FRAME_HZ_NOMINAL; // Palette words by lookup, built once from x68kColorFromRgb12 by colorLutBuild: -// a $0RGB colour's red and green nibbles index one table and its blue nibble +// a $0RGB color's red and green nibbles index one table and its blue nibble // the other, and the two entries OR into exactly x68kColorFromRgb12's word. -// A present converts every colour of each palette that changed, and the -// function's shifts cost ~250 cycles a colour (6.4 ms for all 256, measured on +// A present converts every color of each palette that changed, and the +// function's shifts cost ~250 cycles a color (6.4 ms for all 256, measured on // examples/scb), the lookup ~70. #define X68K_LUT_RED_GREEN_ENTRIES 256u #define X68K_LUT_RED_GREEN_SHIFT 4u @@ -392,7 +392,7 @@ static uint16_t gColorLutBlue[X68K_LUT_BLUE_ENTRIES]; #if X68K_PRESENT_TEXT // ----- SCB band tables (text-plane present) ----------------------------------- // -// The text plane has ONE 16-entry palette, so honouring the SCB means reloading +// The text plane has ONE 16-entry palette, so honoring the SCB means reloading // it as the beam scans: one reload per TRANSITION between lines with different // SCB values, worst case one per line. src/x68000/jlX68kRasterIsr.s does the // reloads from a table built here -- see "SCB per-band palette" below. @@ -405,7 +405,7 @@ typedef char x68kAllPalettesFit[(JL_SURFACE_PALETTE_COUNT == 16u) ? 1 : -1]; // One band as the raster interrupt reads it. jlX68kRasterIsr.s addresses the // fields by offset; the size checks below hold both sides to one layout. typedef struct { - const uint16_t *palette; // its 16 colours, already in hardware format + const uint16_t *palette; // its 16 colors, already in hardware format uint16_t raster; // the raster of the line above its first line uint16_t stay; // lines to the next band if the interrupt stays for it, else 0 } X68kBandT; @@ -520,8 +520,8 @@ static inline uint16_t colorLookup(uint16_t rgb12) { // Each entry is x68kColorFromRgb12 of its channels alone, the others zero. The // fields do not overlap, and each entry carries the shared intensity bit when -// its own channels are non-zero, so the OR has it exactly when the colour is -// not black -- the same word as converting the whole colour. +// its own channels are non-zero, so the OR has it exactly when the color is +// not black -- the same word as converting the whole color. static void colorLutBuild(void) { uint16_t i; @@ -547,7 +547,7 @@ bool jlpInit(const jlConfigT *config) { colorLutBuild(); gPrevCrtMode = _iocs_crtmod(-1); // -1 queries without changing #if X68K_PRESENT_TEXT - // 16 colours, because the present writes the four 1bpp TEXT planes directly + // 16 colors, because the present writes the four 1bpp TEXT planes directly // and that is what they are. _G_CLR_ON clears the GRAPHICS plane so it // cannot show through underneath, and then the text plane is wiped to get // rid of Human68k's desktop -- which the graphics-plane path could only @@ -581,8 +581,8 @@ bool jlpInit(const jlConfigT *config) { #else _iocs_crtmod(X68K_CRTMOD_512_31K); _iocs_g_clr_on(); // clear graphics + enable the plane - // The 65536-colour modes (12 and 13) leave the video controller in DIRECT mode (reg[0] & 3 == 3). This port - // draws 8-bit palette indices, so force 256-colour palette-indexed mode or every colour renders + // The 65536-color modes (12 and 13) leave the video controller in DIRECT mode (reg[0] & 3 == 3). This port + // draws 8-bit palette indices, so force 256-color palette-indexed mode or every color renders // near-black. Root-caused + proven live on MAME (reg0 0x0003 -> 0x0001 turned a black frame into a // correct red/yellow/blue test pattern). X68K_VIDCTRL0[0] = (uint16_t)((X68K_VIDCTRL0[0] & (uint16_t)~X68K_VIDCTRL0_DEPTH) | X68K_VIDCTRL0_256); @@ -906,7 +906,7 @@ static void vdispPoll(void) { // // Timing (scripts/verify-x68000-scb.sh measures it on every transition of a // screen with 147 of them): jlX68kRasterIsr.s waits out the display of the line -// ABOVE a band and stores the band's sixteen colours with one movem in the +// ABOVE a band and stores the band's sixteen colors with one movem in the // horizontal blanking after it, between the last stage pixel of one line and // the first of the next. A band that starts within X68K_RASTER_STAY_MAX lines // of the previous one is reached by staying in the interrupt, so a run of @@ -954,7 +954,7 @@ static uint16_t buildScbBands(X68kBandTableT *table, const jlSurfaceT *src) { } -// Quantise the palettes the table does not have current into the form the +// Quantize the palettes the table does not have current into the form the // interrupt stores. static void buildScbPalettes(X68kBandTableT *table, const jlSurfaceT *src) { uint16_t pal; @@ -1059,7 +1059,7 @@ static void installScb(const jlSurfaceT *src, uint16_t paletteMask, bool scbChan // ----- GRAPHICS-plane present support (only when X68K_PRESENT_TEXT is 0) ----- // Load all 16 palettes into the 256-entry graphics palette: entry p*16+c is -// palette p colour c, so a pixel byte of (p << 4) | c selects it directly. +// palette p color c, so a pixel byte of (p << 4) | c selects it directly. // Rebuilt only when a palette actually changes; the SCB flag is deliberately // NOT consumed here, because the per-line choice is applied in the pixel // expansion, which reads src->scb directly. Same contract as @@ -1087,7 +1087,7 @@ static void uploadGfxPalette(const jlSurfaceT *src) { // 16-bit WORD per pixel (index in the low byte), so the output bytes are two // apart. The fusion that works on this machine is the other way up -- write // WHOLE WORDS, because one 32-bit store then covers exactly two pixels. The -// high byte belongs to graphics pages 2/3, which the 256-colour index does not +// high byte belongs to graphics pages 2/3, which the 256-color index does not // read (x68k_v.cpp:339-340 masks $000F | $00F0), so writing 0 there is safe. // // So the tables are indexed by a PLANE and a NIBBLE (4 source pixels) and are @@ -1172,7 +1172,7 @@ static void spreadSetPalBase(uint8_t palBase) { // Dirty rows only, and only the marked band within a row. #if X68K_PRESENT_TEXT // Wipe the whole text plane, both pages, once at start-up. Human68k leaves -// its desktop there, and we only ever write the centred stage rectangle, so +// its desktop there, and we only ever write the centered stage rectangle, so // without this the surround keeps the desktop. Long stores; one-time cost. static void clearTextPlane(void) { uint16_t plane; @@ -1231,7 +1231,7 @@ static void flipApply(void) { // Hand a page over to the next display start, with a one-band screen's -// queued colours. The pending flag goes last: the interrupt reads the rest +// queued colors. The pending flag goes last: the interrupt reads the rest // once it sees it. static void flipRequest(uint8_t page) { jlgX68kFlipScroll = (uint16_t)(page * X68K_TEXT_PAGE_ROWS); @@ -1294,14 +1294,14 @@ uint32_t jlX68kProbeDisplayMismatch(const jlSurfaceT *src) { // Present to the TEXT plane. The stage and the text plane are both four 1bpp // planes, so this is a copy -- no pixel conversion, no table, 0.5 bytes of bus -// traffic per pixel. The SCB is honoured by the raster interrupt above rather +// traffic per pixel. The SCB is honored by the raster interrupt above rather // than by a per-pixel palette index. The copy, dirty-band walk included, is // jlX68kPresentText (src/x68000/x68kPresent.s), whose header has the numbers. // // PAGE FLIP: the copy goes into the page not shown, which holds the frame // before last, so it takes the union of this frame's dirty rows and the last // one's (dirtyUnion), and the next display start shows it (flipRequest), with -// the colours that came with it. The page on screen is never written. +// the colors that came with it. The page on screen is never written. void jlpPresent(const jlSurfaceT *src) { X68kPlanarT *pd; uint8_t back; @@ -1395,7 +1395,7 @@ void jlpPresent(const jlSurfaceT *src) { p1 = pd->planes[1] + ((uint32_t)y * X68K_BYTES_PER_ROW) + firstByte; p2 = pd->planes[2] + ((uint32_t)y * X68K_BYTES_PER_ROW) + firstByte; p3 = pd->planes[3] + ((uint32_t)y * X68K_BYTES_PER_ROW) + firstByte; - // Centred in the 512x512 screen. One 16-bit GVRAM word per pixel, and + // Centered in the 512x512 screen. One 16-bit GVRAM word per pixel, and // the palette index is that word's LOW byte -- odd address on this // big-endian bus. rowBase = X68K_GVRAM_BASE + (((uint32_t)(y + X68K_ORIGIN_Y) * X68K_GVRAM_WORDS_ROW) + X68K_ORIGIN_X + ((uint32_t)firstByte * 8u)) * 2u; @@ -1950,9 +1950,8 @@ void jlpInputPoll(void) { jlgMouseX = (int16_t)((mousePos >> 16) & 0xFFFFu); jlgMouseY = (int16_t)(mousePos & 0xFFFFu); - jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = leftDown; - jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = rightDown; - jlgMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = false; + jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = leftDown; + jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = rightDown; // The ports are live levels: nothing latches, so a released line reads // released on the very next poll. diff --git a/src/x68000/halPlanar.c b/src/x68000/halPlanar.c index dd31ac9..104a984 100644 --- a/src/x68000/halPlanar.c +++ b/src/x68000/halPlanar.c @@ -43,7 +43,7 @@ extern void jlSurface68kFillCircle(uint8_t *const *planes, uint16_t cx, uint16_t // circle.s // src/m68k/planarPlot.s, shared with the Amiga: Bresenham walkers with 16-way -// colour dispatch, so every plane write is one memory read-modify-write with +// color dispatch, so every plane write is one memory read-modify-write with // the set-or-clear choice already made. Both are bit-identical to draw.c's own // walkers. Straight lines never reach jlpDrawLine -- draw.c diverts those to // jlFillRect -- so the line body walks the general diagonal case only. diff --git a/src/x68000/save.c b/src/x68000/save.c index cc0750d..19cc780 100644 --- a/src/x68000/save.c +++ b/src/x68000/save.c @@ -93,7 +93,7 @@ bool jlpSaveDirEnsure(const char *dir) { int rc; // "Already exists" is the common case and is success for us, so accept - // _DOSE_EXISTDIR (-20) alongside a clean create. Note the neighbouring + // _DOSE_EXISTDIR (-20) alongside a clean create. Note the neighboring // _DOSE_EXISTFILE is -80 -- easy to transpose, and it means something // else entirely (a FILE of that name is in the way, which IS a failure). rc = _dos_mkdir(dir); diff --git a/src/x68000/x68kPlanar.h b/src/x68000/x68kPlanar.h index bed8c57..0f03e97 100644 --- a/src/x68000/x68kPlanar.h +++ b/src/x68000/x68kPlanar.h @@ -50,7 +50,7 @@ #define X68K_TVRAM_STRIDE 128u // 1024 px / 8 // The library surface is 320x200 and the machine has no such mode, so the -// closest is 512x512 (_iocs_crtmod 12) and the window is CENTRED in it rather +// closest is 512x512 (_iocs_crtmod 12) and the window is CENTERED in it rather // than parked in the corner. X must land on a byte boundary in a 1bpp plane; // 96/8 = 12 exactly, so no sub-byte shifting is needed. #define X68K_DISPLAY_W 512u @@ -70,9 +70,9 @@ typedef char x68kOriginXIsByteAligned[((X68K_ORIGIN_X % 8u) == 0u) ? 1 : -1]; // the 256-entry GRAPHIC palette -- the text plane reads the PCG block. #define X68K_TEXT_PALETTE ((volatile uint16_t *)0xE82200L) -// Colour word is GGGGG RRRRR BBBBB I -- green and red SWAPPED versus the usual +// Color word is GGGGG RRRRR BBBBB I -- green and red SWAPPED versus the usual // RGB555. Writing JoeyLib's $0RGB straight through puts red where green -// belongs, which reads as "greens turn blue, reds turn grey". Verified against +// belongs, which reads as "greens turn blue, reds turn gray". Verified against // MAME's own decoder (x68k_v.cpp GGGGGRRRRRBBBBBI): // i = raw & 1; r = (raw >> 5) & 0x3e | i; // g = (raw >> 10) & 0x3e | i; b = (raw >> 0) & 0x3e | i; @@ -86,7 +86,7 @@ static inline uint16_t x68kColorFromRgb12(uint16_t rgb12) { uint16_t g5 = (uint16_t)((g4 << 1) | (g4 >> 3)); uint16_t b5 = (uint16_t)((b4 << 1) | (b4 >> 3)); // Intensity is shared, so it cannot be per-channel: set it for any - // non-black colour, which keeps $000 pure black and lets $FFF reach full + // non-black color, which keeps $000 pure black and lets $FFF reach full // white (31<<1 | 1 == 63) instead of topping out one step short. uint16_t i = (uint16_t)(((r4 | g4 | b4) != 0u) ? 1u : 0u); return (uint16_t)((g5 << 11) | (r5 << 6) | (b5 << 1) | i); diff --git a/src/x68000/x68kPresent.s b/src/x68000/x68kPresent.s index 006dff0..255b092 100644 --- a/src/x68000/x68kPresent.s +++ b/src/x68000/x68kPresent.s @@ -23,7 +23,7 @@ | bytes wider than it was marked: those bytes are the surface's own and match | what is already on screen, and a band in whole longs never needs a byte or | word move. Its ends are even on both sides (the surface row is 40 bytes, the -| text row 128, the centring offset 12), which is all a 68000 long move asks. +| text row 128, the centering offset 12), which is all a 68000 long move asks. | A max past the last word of the row (STAGE_DIRTY_FULL_MAX) is clamped to | it, as jlpPresent's C did. | diff --git a/src/x68000/x68kRasterIsr.s b/src/x68000/x68kRasterIsr.s index 7884987..faba194 100644 --- a/src/x68000/x68kRasterIsr.s +++ b/src/x68000/x68kRasterIsr.s @@ -15,7 +15,7 @@ | handler -- the pointer goes straight into the vector. So this is a RAW | interrupt routine: it saves what it uses and returns with RTE. | -| TIMING. A band's sixteen colours are stored with one movem.l -- eight long +| TIMING. A band's sixteen colors are stored with one movem.l -- eight long | writes, 72 cycles -- in the horizontal blanking between the last stage pixel | of the line above the band and the first stage pixel of the band. Nothing is | counted in cycles; every step waits on a signal edge, so the landing point @@ -27,7 +27,7 @@ | (X68kBandT.raster) and waits for GPIP6 to go low, which it does as that | raster begins -- a fixed point however late the interrupt was taken. | 3. It waits for H-SYNC (GPIP7) to go high, then low: the blanking ahead of -| row D-1 is over and the row is being drawn. The colours are loaded into +| row D-1 is over and the row is being drawn. The colors are loaded into | d0-d7 while it is. | 4. It waits for H-SYNC to go high again -- row D-1's display is over -- and | stores. @@ -62,7 +62,7 @@ .equ MFP_ISRA, 0xE8800F | MFP reg 7, in-service A .equ CH14_CLEAR, 0xBF | clear bit 6 (channel 14), keep the rest .equ CRTC_R09, 0xE80012 | raster compare - .equ TEXT_PALETTE, 0xE82200 | the text plane's 16 colours + .equ TEXT_PALETTE, 0xE82200 | the text plane's 16 colors .equ ARM_LEAD, 1 | arm one raster before X68kBandT.raster .equ REF_WAIT_LINES, 3 | H-SYNC periods to wait for step 2's edge @@ -116,7 +116,7 @@ rasterDisplay: | the line above the band is be lea TEXT_PALETTE,%a3 rasterLoad: movea.l %a2@(BAND_PALETTE),%a0 - movem.l %a0@,%d0-%d7 | the colours, ready... + movem.l %a0@,%d0-%d7 | the colors, ready... rasterBlank: tst.b %a1@ bpl.s rasterBlank diff --git a/src/x68000/x68kSccRxIsr.s b/src/x68000/x68kSccRxIsr.s index fb30fb8..95c653b 100644 --- a/src/x68000/x68kSccRxIsr.s +++ b/src/x68000/x68kSccRxIsr.s @@ -15,7 +15,7 @@ | (WR2 base | %110x for channel A RX -- serial.c reads RR2 and computes it), so | these are raw interrupt handlers: every register touched is saved, and both | return with RTE. Unlike the tick hook there is NO chaining -- the byte is -| consumed here, so running RSDRV's handler afterwards would be wrong. +| consumed here, so running RSDRV's handler afterward would be wrong. | | ONE INTERRUPT, EVERY WAITING BYTE. Whatever holds interrupt level 5 off -- | the SCB raster interrupt staying through a run of per-line palette changes, @@ -44,7 +44,7 @@ | drop rather than corruption. | | Flow control (jlgX68kSerFlow, serial.c): under XON/XOFF the peer's XOFF and XON -| are taken out of the data into jlgX68kTxPaused, which jlpSerialWrite honours. +| are taken out of the data into jlgX68kTxPaused, which jlpSerialWrite honors. | Once a drain leaves the ring three quarters full the handler holds the peer | off, once, by setting jlgX68kRxThrottled: under RTS/CTS it drops RTS itself | (WR5, written with every interrupt masked like the RR1 read); under XON/XOFF diff --git a/src/x68000/x68kVdispIsr.s b/src/x68000/x68kVdispIsr.s index 6cbca86..63d8b95 100644 --- a/src/x68000/x68kVdispIsr.s +++ b/src/x68000/x68kVdispIsr.s @@ -17,7 +17,7 @@ | and an edge in that window is counted as the mask lifts. | | THE FLIP. jlpPresent draws into the text page not shown and hands it over -| (jlgX68kFlipPending, set last): its text Y scroll, and the colours of a +| (jlgX68kFlipPending, set last): its text Y scroll, and the colors of a | one-band screen (jlgX68kFlipPalette, NULL for none). Written here, at the | display's start, they apply to the whole frame: the stage starts 156 lines | down and both pages are blank above and below it, so the write lands well @@ -27,7 +27,7 @@ | GAS m68k syntax, ELF target -- symbols carry NO leading underscore. .equ CRTC_TEXT_Y, 0xE80016 | CRTC R11: the text plane's Y scroll - .equ TEXT_PALETTE, 0xE82200 | the 16 colours the text plane shows + .equ TEXT_PALETTE, 0xE82200 | the 16 colors the text plane shows .equ PALETTE_LONGS, 8 | 16 words .text @@ -45,9 +45,9 @@ jlX68kVdispIsr: movea.l jlgX68kFlipPalette,%a0 lea TEXT_PALETTE,%a1 moveq #PALETTE_LONGS - 1,%d0 -.Lcolour: +.Lcolor: move.l (%a0)+,(%a1)+ - dbf %d0,.Lcolour + dbf %d0,.Lcolor movem.l (%sp)+,%d0/%a0-%a1 .Lflipped: clr.b jlgX68kFlipPending diff --git a/src/x86/scanSet1.h b/src/x86/scanSet1.h index 7a7104d..c0d3ae5 100644 --- a/src/x86/scanSet1.h +++ b/src/x86/scanSet1.h @@ -6,7 +6,7 @@ // raw PC scan codes: DOS (its own INT 9 handler), DVX (the held-key table // DVX keeps from its INT 9 chain) and the hosted Win32 backend (Windows hands // every key message the key's set-1 make code). Indexed by the make code with the break -// bit and any 0xE0 prefix stripped, so the grey arrows land on the same +// bit and any 0xE0 prefix stripped, so the gray arrows land on the same // keys as the keypad ones, right Ctrl on JL_KEY_LCTRL and right Alt on // JL_KEY_LALT. Slots left 0 (JL_KEY_NONE) are keys JoeyLib does not name. // scanSet1Key makes a prefixed code its key's second (INPUT_KEY_SECOND), so diff --git a/src/x86/x86Expand.h b/src/x86/x86Expand.h index d385bd0..7c53a39 100644 --- a/src/x86/x86Expand.h +++ b/src/x86/x86Expand.h @@ -4,7 +4,7 @@ // // x86 stage-row expansion to 8-bit palette indexes, for the DVX port's full // screen at 8 bits a pixel (the DOS port's Mode Y screen has a planar one of -// its own in src/dos/hal.c). All 16 palettes x 16 colours fill the screen's +// its own in src/dos/hal.c). All 16 palettes x 16 colors fill the screen's // 256 entries, so a pixel is (SCB << 4) | nibble and no per-scanline palette // programming is needed at display time. Each including file gets its own // table. diff --git a/tests/gate/amigaCheck.c b/tests/gate/amigaCheck.c index 4c4ea4a..96ab016 100644 --- a/tests/gate/amigaCheck.c +++ b/tests/gate/amigaCheck.c @@ -12,17 +12,17 @@ // the screenshot (screen.png). Every line of the log with FAIL in it fails; // the log must say icon.library read the icon as a tool's (type WBTOOL) of // STACK bytes of stack and the file's image size and depth; and the -// screenshot must show the icon. The screen is Workbench's grey and the -// icon's coloured pixels lie inside it: the file's coloured pixels (index -// not 0) and the screenshot's (not grey) must have the same bounds, scaled, +// screenshot must show the icon. The screen is Workbench's gray and the +// icon's colored pixels lie inside it: the file's colored pixels (index +// not 0) and the screenshot's (not gray) must have the same bounds, scaled, // and each of the file's pixels is looked at in the middle of its cell. -// Only the grey's upper half is searched: FS-UAE prints its notices over +// Only the gray's upper half is searched: FS-UAE prints its notices over // the bottom. Then Palprobe's log must be in Prog/ with its last line, and // no log in WORK: it ran in its own folder. // // palette: LOG must have PALPROBE's two rates (palette and SCB changes // presented a second; the last of each the log has), each at least -// MINRATE, and the colour bands down the screenshot's middle column, top to +// MINRATE, and the color bands down the screenshot's middle column, top to // bottom, black (the border) dropped, must be red, green, yellow, and red. // // Prints LABEL: PASS or LABEL: FAIL - the reasons, as the gate's verdict; @@ -61,8 +61,8 @@ #define WORD_BYTES 2 #define BYTE_BITS 8 #define TOP_BIT 0x80u -#define AMIGA_COLOURS 4u -#define GREY_STEP 2 +#define AMIGA_COLORS 4u +#define GRAY_STEP 2 #define HALF 0.5 #define ICON_PATTERN "icon type=# stack=# gadget=#x# image=#x#x#" #define ICON_TYPE 0u @@ -97,7 +97,7 @@ typedef struct BoundsT { } BoundsT; // A Workbench icon's image: its size and depth as the file gives them, and -// each pixel's colour index (no pixels when the size is not positive). +// each pixel's color index (no pixels when the size is not positive). typedef struct IconT { long width; long height; @@ -127,13 +127,13 @@ static const GateModeT kModes[] = { { "palette", 4, checkPalette } }; -// Workbench's grey, black, white, and blue. -static const uint8_t kWorkbench[AMIGA_COLOURS][GATE_RGB_BYTES] = { { 170, 170, 170 }, { 0, 0, 0 }, { 255, 255, 255 }, { 102, 136, 187 } }; +// Workbench's gray, black, white, and blue. +static const uint8_t kWorkbench[AMIGA_COLORS][GATE_RGB_BYTES] = { { 170, 170, 170 }, { 0, 0, 0 }, { 255, 255, 255 }, { 102, 136, 187 } }; static const char *const kRateKinds[RATE_KINDS] = { "palette", "scb" }; static const char *const kRatePatterns[RATE_KINDS] = { "PALPROBE: palette+present: # in # ms = #/s", "PALPROBE: scb+present: # in # ms = #/s" }; -static const uint8_t kBandColours[BAND_NAMES][GATE_RGB_BYTES] = { { 255, 0, 0 }, { 0, 255, 0 }, { 255, 255, 0 } }; +static const uint8_t kBandColors[BAND_NAMES][GATE_RGB_BYTES] = { { 255, 0, 0 }, { 0, 255, 0 }, { 255, 255, 0 } }; static const char *const kBandNames[BAND_NAMES] = { "red", "green", "yellow" }; static const uint32_t kWantBands[BAND_COUNT] = { 0u, 1u, 2u, 0u }; static const uint8_t kBlack[GATE_RGB_BYTES] = { 0, 0, 0 }; @@ -276,7 +276,7 @@ static int checkPalette(char **args) { } // The bands down the middle column, top to bottom, black (the border) - // dropped: each run of one colour is a band. + // dropped: each run of one color is a band. gateImageLoad(args[0], &img); bandCount = 0u; for (y = 0; y < img.height; y++) { @@ -286,7 +286,7 @@ static int checkPalette(char **args) { } band = BAND_OTHER; for (k = 0; k < BAND_NAMES && band == BAND_OTHER; k++) { - band = (memcmp(rgb, kBandColours[k], GATE_RGB_BYTES) == 0) ? k : BAND_OTHER; + band = (memcmp(rgb, kBandColors[k], GATE_RGB_BYTES) == 0) ? k : BAND_OTHER; } if (band != BAND_OTHER) { snprintf(name, sizeof(name), "%s", kBandNames[band]); @@ -374,7 +374,7 @@ static bool findPattern(const uint8_t *text, size_t len, size_t *at, const char // The image of the icon file at path: its size and depth, and its pixels' -// colour indices, a bit of each from each plane. +// color indices, a bit of each from each plane. static void iconRead(const char *path, IconT *icon) { uint8_t *info; size_t infoLen; @@ -418,14 +418,14 @@ static void iconRead(const char *path, IconT *icon) { } -// Whether the screenshot at path shows the icon: the bounds of the grey -// (looked for at every other pixel), the non-grey inside its upper half, +// Whether the screenshot at path shows the icon: the bounds of the gray +// (looked for at every other pixel), the non-gray inside its upper half, // and each of the icon's pixels in the middle of its cell there. Prints // where the icon is and how many of its pixels are right; adds what is // wrong to fails. static void iconShown(const char *path, const IconT *icon, const char *label, FailsT *fails) { GateImageT shot; - BoundsT grey; + BoundsT gray; BoundsT shown; BoundsT held; const uint8_t *pixel; @@ -438,21 +438,21 @@ static void iconShown(const char *path, const IconT *icon, const char *label, Fa double scaleY; gateImageLoad(path, &shot); - memset(&grey, 0, sizeof(grey)); - for (y = 0; y < (long)shot.height; y += GREY_STEP) { - for (x = 0; x < (long)shot.width; x += GREY_STEP) { + memset(&gray, 0, sizeof(gray)); + for (y = 0; y < (long)shot.height; y += GRAY_STEP) { + for (x = 0; x < (long)shot.width; x += GRAY_STEP) { if (memcmp(gateImagePixel(&shot, (uint32_t)x, (uint32_t)y), kWorkbench[0], GATE_RGB_BYTES) == 0) { - boundsAdd(&grey, x, y); + boundsAdd(&gray, x, y); } } } - if (!grey.any) { - failAdd(fails, "no Workbench grey on the screenshot"); + if (!gray.any) { + failAdd(fails, "no Workbench gray on the screenshot"); return; } memset(&shown, 0, sizeof(shown)); - for (y = grey.y0; y <= (grey.y0 + grey.y1) / 2; y++) { - for (x = grey.x0; x <= grey.x1; x++) { + for (y = gray.y0; y <= (gray.y0 + gray.y1) / 2; y++) { + for (x = gray.x0; x <= gray.x1; x++) { if (memcmp(gateImagePixel(&shot, (uint32_t)x, (uint32_t)y), kWorkbench[0], GATE_RGB_BYTES) != 0) { boundsAdd(&shown, x, y); } @@ -476,8 +476,8 @@ static void iconShown(const char *path, const IconT *icon, const char *label, Fa for (y = held.y0; y <= held.y1; y++) { for (x = held.x0; x <= held.x1; x++) { index = icon->indices[y * icon->cols + x]; - if (index >= AMIGA_COLOURS) { - toolFail("the icon's pixel %ld,%ld is colour %u, past Workbench's four", x, y, (unsigned)index); + if (index >= AMIGA_COLORS) { + toolFail("the icon's pixel %ld,%ld is color %u, past Workbench's four", x, y, (unsigned)index); } pixel = gateImagePixel(&shot, (uint32_t)(shown.x0 + (x - held.x0 + HALF) * scaleX), (uint32_t)(shown.y0 + (y - held.y0 + HALF) * scaleY)); if (memcmp(pixel, kWorkbench[index], GATE_RGB_BYTES) != 0) { diff --git a/tests/gate/audioGate.c b/tests/gate/audioGate.c index 6c87085..75ad750 100644 --- a/tests/gate/audioGate.c +++ b/tests/gate/audioGate.c @@ -128,8 +128,8 @@ #define SILENCE_DBFS -55.0 #define SOUND_DBFS -45.0 #define PAN_APART_DB 12.0 -#define PAN_CENTRE_DB 3.0 -#define CENTRE_CHECK_HZ 440.0 +#define PAN_CENTER_DB 3.0 +#define CENTER_CHECK_HZ 440.0 #define NOISE_LOUDNESS_PITCH 31 #define MOD_END_SLACK_S 0.6 #define MOD_STOP_LOOK_S 0.2 @@ -297,7 +297,7 @@ typedef struct CommandT { static void *arrayRoom(void *items, size_t count, size_t *cap, size_t size); static void bandOf(const double *freqs, size_t bins, double low, double high, bool inclusive, size_t *lo, size_t *hi); static void confArgs(int argc, char **argv, ConfT *c); -static void confCentred(ConfT *c, double start, double end, const char *what); +static void confCentered(ConfT *c, double start, double end, const char *what); static double confCentroid(ConfT *c, const double *x, size_t n); static void confCheck(ConfT *c, bool ok, const char *fmt, ...) __attribute__((format(printf, 3, 4))); static SamplesT confCut(const ConfT *c, double start, double end, int channel, double shift); @@ -356,7 +356,7 @@ static int wavRun(int argc, char **argv); static const int32_t kSineCycle[] = { 0, 71, 100, 71, 0, -71, -100, -71 }; // The pans an effect is played at, by number. -static const char *const kPanNames[] = { "left", "centre", "right" }; +static const char *const kPanNames[] = { "left", "center", "right" }; static const CommandT kCommands[] = { { "phases", phasesRun }, @@ -452,15 +452,15 @@ static void confArgs(int argc, char **argv, ConfT *c) { } -// Each channel's level for a step: within PAN_CENTRE_DB of each other. -static void confCentred(ConfT *c, double start, double end, const char *what) { +// Each channel's level for a step: within PAN_CENTER_DB of each other. +static void confCentered(ConfT *c, double start, double end, const char *what) { double left; double right; left = confMeasure(c, start, end, CHANNEL_LEFT, 0.0, false); right = confMeasure(c, start, end, CHANNEL_RIGHT, 0.0, false); - confName(c, "%s centred", what); - confCheck(c, fabs(left - right) <= PAN_CENTRE_DB, "left %.1f dBFS, right %.1f dBFS", left, right); + confName(c, "%s centered", what); + confCheck(c, fabs(left - right) <= PAN_CENTER_DB, "left %.1f dBFS, right %.1f dBFS", left, right); } @@ -542,7 +542,7 @@ static SamplesT confCut(const ConfT *c, double start, double end, int channel, d } -// A level in dBFS without DC: a DAC resting off centre (the ST's YM) is +// A level in dBFS without DC: a DAC resting off center (the ST's YM) is // silence. static double confDbfs(const double *x, size_t n) { double *squares; @@ -1138,7 +1138,7 @@ static double confPurity(ConfT *c, const double *x, size_t n, double hz) { // (kSineCycle's) against voice 0 (VOICE_MATCH_DB), each by // its louder channel on a --stereo port (the Amiga's // voices play on one side), the sine's level the median -// of the BALANCE_RATE effects (centre, left, right): one +// of the BALANCE_RATE effects (center, left, right): one // sound an emulator spoils does not decide it (MAME 0.264 // clocks the X68000's MSM6258 at 4 MHz, not the 8 its CT1 // pin says, until the first write to the OPM's CT1, so the @@ -1366,7 +1366,7 @@ static void confSample(ConfT *c, double start, const EventT *e) { } else if (pan == PAN_RIGHT) { ok = -apart >= PAN_APART_DB; } else { - ok = fabs(apart) <= PAN_CENTRE_DB; + ok = fabs(apart) <= PAN_CENTER_DB; } if (c->stereo) { confName(c, "pan %s", panName); @@ -1461,7 +1461,7 @@ static void confSummary(ConfT *c) { } } // Full-swing noise and a full-swing square carry the same power, as - // on the PSG every port's voices are modelled on. + // on the PSG every port's voices are modeled on. entry = entriesFind(&c->noiseLevels, NOISE_LOUDNESS_PITCH, 0); if (entry != NULL) { confName(c, "noise as loud as a voice"); @@ -1601,8 +1601,8 @@ static void confTone(ConfT *c, double start, double end, const EventT *e) { confCheck(c, fabs(got / hz - 1.0) <= PITCH_TOLERANCE && level > SOUND_DBFS, "%.1f Hz at %.1f dBFS", got, level); if (strcmp(e->kind, "tone") == 0) { entriesAdd(&c->toneLevels, (long)truncated(hz), 0, level); - if (c->stereo && hz == CENTRE_CHECK_HZ) { - confCentred(c, start, end, "tone"); + if (c->stereo && hz == CENTER_CHECK_HZ) { + confCentered(c, start, end, "tone"); } } } @@ -1636,7 +1636,7 @@ static void confVoice(ConfT *c, double start, double end, const EventT *e) { level = confSteady(c, x.x, x.n); if (c->stereo && atten == 0) { snprintf(what, sizeof(what), "voice %ld", voice); - confCentred(c, start, end, what); + confCentered(c, start, end, what); } entriesSet(&c->voiceLevels, voice, atten, level); if (voice == 0 && atten == 0) { @@ -1657,7 +1657,7 @@ static void confVoice(ConfT *c, double start, double end, const EventT *e) { // densities averaged in turn. The bins' frequencies and densities, in // malloc'd arrays; how many. static size_t confWelch(ConfT *c, const double *x, size_t n, double **freqs, double **psd) { - double *centred; + double *centered; double *window; double *squares; double *segment; @@ -1680,7 +1680,7 @@ static size_t confWelch(ConfT *c, const double *x, size_t n, double **freqs, dou step = perSegment - perSegment / 2u; segments = (n - perSegment / 2u) / step; bins = perSegment / 2u + 1u; - centred = (double *)memoryFor(n, sizeof(double)); + centered = (double *)memoryFor(n, sizeof(double)); window = (double *)memoryFor(perSegment, sizeof(double)); squares = (double *)memoryFor(perSegment, sizeof(double)); segment = (double *)memoryFor(perSegment, sizeof(double)); @@ -1690,7 +1690,7 @@ static size_t confWelch(ConfT *c, const double *x, size_t n, double **freqs, dou *freqs = freqsOf(perSegment, c->rate); mean = gateMean(x, n); for (k = 0u; k < n; k++) { - centred[k] = x[k] - mean; + centered[k] = x[k] - mean; } confHann(window, perSegment); for (k = 0u; k < perSegment; k++) { @@ -1699,9 +1699,9 @@ static size_t confWelch(ConfT *c, const double *x, size_t n, double **freqs, dou scale = 1.0 / ((double)c->rate * gateSum(squares, perSegment)); fftFor(&c->welchFft, perSegment); for (s = 0u; s < segments; s++) { - mean = gateMean(centred + s * step, perSegment); + mean = gateMean(centered + s * step, perSegment); for (k = 0u; k < perSegment; k++) { - segment[k] = window[k] * (centred[s * step + k] - mean); + segment[k] = window[k] * (centered[s * step + k] - mean); } gateFftReal(&c->welchFft, segment, perSegment, re, im); for (k = 0u; k < bins; k++) { @@ -1717,7 +1717,7 @@ static size_t confWelch(ConfT *c, const double *x, size_t n, double **freqs, dou for (k = 0u; k < bins; k++) { (*psd)[k] /= (double)segments; } - free(centred); + free(centered); free(window); free(squares); free(segment); diff --git a/tests/gate/common/gateImage.c b/tests/gate/common/gateImage.c index af17ff4..9b51c21 100644 --- a/tests/gate/common/gateImage.c +++ b/tests/gate/common/gateImage.c @@ -6,13 +6,13 @@ // // The rules the pictures are read by (checked once against the Python // gates these tools replaced): -// - A PNG goes through toolPng to RGBA: grey spread to RGB, 16-bit +// - A PNG goes through toolPng to RGBA: gray spread to RGB, 16-bit // samples cut to their high byte, palette entries looked up. -// - A BMP is an uncompressed DIB: 1, 4, or 8 bits through its colour +// - A BMP is an uncompressed DIB: 1, 4, or 8 bits through its color // table (an index past the table is black), or 24 or 32 bits of BGR; it // is opaque. // - An ICO's directory is sorted largest picture first and, among equals, -// fewest colours first (a stable sort); its default picture is the first. +// fewest colors first (a stable sort); its default picture is the first. // A picture that is a PNG is decoded as one. A DIB picture is its XOR // bitmap (half the height its header gives), its alpha the 32-bit // picture's fourth bytes or else its AND mask (a set bit transparent), @@ -38,7 +38,7 @@ #define NIBBLE_WIDEN 17u #define PIXELS_INITIAL 256u -// KEYS's palette 0: a grey ramp (n in each gun), then black, dark grey, +// KEYS's palette 0: a gray ramp (n in each gun), then black, dark gray, // green, and white at 0 to 3 and red at 15 (examples/keys buildPalette). #define KEYS_BLACK 0x000u #define KEYS_UNLIT 0x333u @@ -62,13 +62,13 @@ #define BMP_FILE_HEADER 14u #define BMP_PIXELS_AT 10u -// A DIB's BITMAPINFOHEADER and colour table. +// A DIB's BITMAPINFOHEADER and color table. #define DIB_HEADER_MIN 40u #define DIB_WIDTH_AT 4u #define DIB_HEIGHT_AT 8u #define DIB_BITS_AT 14u #define DIB_COMPRESSION_AT 16u -#define DIB_COLOURS_AT 32u +#define DIB_COLORS_AT 32u #define DIB_RGB 0u #define DIB_ENTRY_BYTES 4u #define DIB_ROW_BITS 32u @@ -89,7 +89,7 @@ #define ICO_ENTRY_BYTES 16u #define ICO_ENTRY_W 0u #define ICO_ENTRY_H 1u -#define ICO_ENTRY_COLOURS 2u +#define ICO_ENTRY_COLORS 2u #define ICO_ENTRY_BPP 6u #define ICO_ENTRY_SIZE 8u #define ICO_ENTRY_OFFSET 12u @@ -101,7 +101,7 @@ #define ICO_ANY_BPP 0u // A DIB's layout: the picture's size (the XOR half of an icon's), its bits -// a pixel, row order, and where its rows and its colour table are. +// a pixel, row order, and where its rows and its color table are. typedef struct DibT { uint32_t width; uint32_t height; @@ -117,7 +117,7 @@ typedef struct DibT { typedef struct IcoEntryT { uint32_t width; uint32_t height; - uint32_t colourDepth; + uint32_t colorDepth; uint32_t bpp; uint32_t size; uint32_t offset; @@ -128,7 +128,7 @@ static void dibDecode(const uint8_t *data, size_t len, const DibT *dib, cons static void dibRead(const uint8_t *data, size_t len, size_t headerAt, size_t pixelsAt, bool icon, const char *path, DibT *dib); static void icoAndMask(const uint8_t *data, size_t len, const IcoEntryT *entry, const char *path, GateImageT *img); static void icoDecode(const uint8_t *data, size_t len, uint32_t side, uint32_t bpp, const char *path, GateImageT *img); -static uint32_t icoDepth(uint32_t bpp, uint32_t colours); +static uint32_t icoDepth(uint32_t bpp, uint32_t colors); static bool icoEntryBefore(const IcoEntryT *a, const IcoEntryT *b); static uint32_t iconAnchor(const GateImageT *screen, const GateIconT *icon, uint32_t tolerance); static bool iconAt(const GateImageT *screen, const GateIconT *icon, uint32_t tolerance, uint32_t left, uint32_t top); @@ -188,13 +188,13 @@ static void dibDecode(const uint8_t *data, size_t len, const DibT *dib, const ch // A DIB's BITMAPINFOHEADER at headerAt: an icon's picture is the upper -// half of the height it gives, and its rows follow its colour table +// half of the height it gives, and its rows follow its color table // (pixelsAt 0); a BMP file says where its rows are. static void dibRead(const uint8_t *data, size_t len, size_t headerAt, size_t pixelsAt, bool icon, const char *path, DibT *dib) { const uint8_t *h; uint32_t headerBytes; int32_t height; - uint32_t colours; + uint32_t colors; if (headerAt > len || len - headerAt < DIB_HEADER_MIN) { toolFail("%s: truncated bitmap header", path); @@ -204,7 +204,7 @@ static void dibRead(const uint8_t *data, size_t len, size_t headerAt, size_t pix height = (int32_t)gateLe32(h + DIB_HEIGHT_AT); dib->width = gateLe32(h + DIB_WIDTH_AT); dib->bits = gateLe16(h + DIB_BITS_AT); - colours = gateLe32(h + DIB_COLOURS_AT); + colors = gateLe32(h + DIB_COLORS_AT); if (headerBytes < DIB_HEADER_MIN || gateLe32(h + DIB_COMPRESSION_AT) != DIB_RGB || (int32_t)dib->width <= 0 || height == 0) { toolFail("%s: not an uncompressed bitmap", path); } @@ -216,9 +216,9 @@ static void dibRead(const uint8_t *data, size_t len, size_t headerAt, size_t pix dib->paletteAt = headerAt + headerBytes; dib->paletteCount = 0u; if (dib->bits <= BITS_8) { - dib->paletteCount = (colours != 0u) ? colours : (1u << dib->bits); + dib->paletteCount = (colors != 0u) ? colors : (1u << dib->bits); if (dib->paletteCount > DIB_PALETTE_MAX || dib->paletteAt > len || (size_t)dib->paletteCount * DIB_ENTRY_BYTES > len - dib->paletteAt) { - toolFail("%s: bad colour table", path); + toolFail("%s: bad color table", path); } } if (icon) { @@ -364,10 +364,10 @@ void gateKeysExpect(const char *target, ToolBufT *expect) { } -void gateKeysPalette(uint16_t words[GATE_KEYS_COLOURS]) { +void gateKeysPalette(uint16_t words[GATE_KEYS_COLORS]) { uint32_t i; - for (i = 0; i < GATE_KEYS_COLOURS; i++) { + for (i = 0; i < GATE_KEYS_COLORS; i++) { words[i] = (uint16_t)((i << (2u * NIBBLE_BITS)) | (i << NIBBLE_BITS) | i); } words[0] = KEYS_BLACK; @@ -471,7 +471,7 @@ static void icoDecode(const uint8_t *data, size_t len, uint32_t side, uint32_t b entries[i].width = (e[ICO_ENTRY_W] != 0u) ? e[ICO_ENTRY_W] : ICO_SIDE_ZERO; entries[i].height = (e[ICO_ENTRY_H] != 0u) ? e[ICO_ENTRY_H] : ICO_SIDE_ZERO; entries[i].bpp = gateLe16(e + ICO_ENTRY_BPP); - entries[i].colourDepth = icoDepth(entries[i].bpp, e[ICO_ENTRY_COLOURS]); + entries[i].colorDepth = icoDepth(entries[i].bpp, e[ICO_ENTRY_COLORS]); entries[i].size = gateLe32(e + ICO_ENTRY_SIZE); entries[i].offset = gateLe32(e + ICO_ENTRY_OFFSET); // An insertion sort, which keeps equals in the directory's order. @@ -483,7 +483,7 @@ static void icoDecode(const uint8_t *data, size_t len, uint32_t side, uint32_t b } chosen = 0u; for (i = 0; i < count && side != ICO_DEFAULT_SIDE; i++) { - if (entries[i].width == side && entries[i].height == side && (bpp == ICO_ANY_BPP || bpp == entries[i].colourDepth)) { + if (entries[i].width == side && entries[i].height == side && (bpp == ICO_ANY_BPP || bpp == entries[i].colorDepth)) { chosen = i; break; } @@ -511,23 +511,23 @@ static void icoDecode(const uint8_t *data, size_t len, uint32_t side, uint32_t b } -// An ICO entry's colour depth: its bits a pixel, or else the bits its -// colour count needs, or else ICO_DEPTH_NONE. -static uint32_t icoDepth(uint32_t bpp, uint32_t colours) { +// An ICO entry's color depth: its bits a pixel, or else the bits its +// color count needs, or else ICO_DEPTH_NONE. +static uint32_t icoDepth(uint32_t bpp, uint32_t colors) { uint32_t depth; if (bpp != 0u) { return bpp; } depth = 0u; - while (depth < BITS_32 && (1u << depth) < colours) { + while (depth < BITS_32 && (1u << depth) < colors) { depth++; } return (depth != 0u) ? depth : ICO_DEPTH_NONE; } -// Whether entry a goes before b: bigger first, then fewer colours. +// Whether entry a goes before b: bigger first, then fewer colors. static bool icoEntryBefore(const IcoEntryT *a, const IcoEntryT *b) { uint32_t squareA; uint32_t squareB; @@ -537,12 +537,12 @@ static bool icoEntryBefore(const IcoEntryT *a, const IcoEntryT *b) { if (squareA != squareB) { return squareA > squareB; } - return a->colourDepth < b->colourDepth; + return a->colorDepth < b->colorDepth; } -// The icon's opaque pixel whose colour (within tolerance) the screen holds -// least, the first of equals: the screen's colours are counted once, sorted. +// The icon's opaque pixel whose color (within tolerance) the screen holds +// least, the first of equals: the screen's colors are counted once, sorted. static uint32_t iconAnchor(const GateImageT *screen, const GateIconT *icon, uint32_t tolerance) { uint32_t *packed; uint32_t *counts; diff --git a/tests/gate/common/gateImage.h b/tests/gate/common/gateImage.h index db53a8b..ef45936 100644 --- a/tests/gate/common/gateImage.h +++ b/tests/gate/common/gateImage.h @@ -4,10 +4,10 @@ // // gateImage.h - screenshots and icons for the gate tools. // -// A gate's screenshot (a PNG of any colour type and bit depth) or an icon +// A gate's screenshot (a PNG of any color type and bit depth) or an icon // (a BMP, or an ICO's picture) is read as RGBA, four bytes a pixel. Beside // that: the tolerant search for an icon on a screen, the nearest of a -// palette's colours, and KEYS's palette and the row it stamps +// palette's colors, and KEYS's palette and the row it stamps // (examples/keys). #ifndef GATE_IMAGE_H @@ -22,7 +22,7 @@ #define GATE_RGB_BYTES 3u #define GATE_RGBA_BYTES 4u #define GATE_ALPHA 3u -#define GATE_KEYS_COLOURS 16u +#define GATE_KEYS_COLORS 16u // A picture: RGBA, four bytes a pixel, row after row. typedef struct GateImageT { @@ -31,7 +31,7 @@ typedef struct GateImageT { uint8_t *rgba; } GateImageT; -// One opaque pixel of an icon: where it is in the icon, and its colour. +// One opaque pixel of an icon: where it is in the icon, and its color. typedef struct GateIconPixelT { uint32_t x; uint32_t y; @@ -48,7 +48,7 @@ typedef struct GateIconT { GateIconPixelT *pixels; } GateIconT; -// The squared distance between two RGB colours. +// The squared distance between two RGB colors. uint32_t gateDistance(const uint8_t *a, const uint8_t *b); // Print bytes as upper-case hex, two digits a byte, to stdout. @@ -58,9 +58,9 @@ void gateHexPrint(const uint8_t *data, size_t len); void gateIconAdd(GateIconT *icon, uint32_t x, uint32_t y, const uint8_t *rgb); // Where the screen shows the icon: every opaque pixel within tolerance a -// gun of its colour. The search anchors on the opaque pixel whose colour +// gun of its color. The search anchors on the opaque pixel whose color // the screen holds least (the first of equals) and tries the icon at each -// place that colour is, top to bottom, left to right; the first place the +// place that color is, top to bottom, left to right; the first place the // whole icon fits and matches is the answer. An icon with no opaque pixel // is fatal. bool gateIconFind(const GateImageT *screen, const GateIconT *icon, uint32_t tolerance, uint32_t *left, uint32_t *top); @@ -75,7 +75,7 @@ void gateImageFree(GateImageT *img); bool gateImageHas(const GateImageT *img, long x, long y); // Load a PNG, BMP, or ICO (its default picture: the largest, and of those -// the one of fewest colours). Anything unreadable is fatal. +// the one of fewest colors). Anything unreadable is fatal. void gateImageLoad(const char *path, GateImageT *img); // As gateImageLoad, but an ICO gives its side x side picture of bpp bits @@ -91,7 +91,7 @@ const uint8_t *gateImagePixel(const GateImageT *img, uint32_t x, uint32_t y); void gateKeysExpect(const char *target, ToolBufT *expect); // KEYS's palette 0 (examples/keys buildPalette) as 12-bit $0RGB words. -void gateKeysPalette(uint16_t words[GATE_KEYS_COLOURS]); +void gateKeysPalette(uint16_t words[GATE_KEYS_COLORS]); // Little-endian 16- and 32-bit numbers at p. uint32_t gateLe16(const uint8_t *p); diff --git a/tests/gate/dosRow.c b/tests/gate/dosRow.c index 4332c5c..517bd1e 100644 --- a/tests/gate/dosRow.c +++ b/tests/gate/dosRow.c @@ -3,17 +3,20 @@ // SPDX-License-Identifier: MIT // // dosRow.c - KEYS's verification rows off a DOSBox raw screenshot -// (scripts/verify-dos-input.sh and verify-dos-mouse.sh). +// (scripts/verify-dos-input.sh and verify-dos-mouse.sh), or off a screen that +// shows the stage at 1:1 (scripts/verify-psp-input.sh). // // dosRow input PNG TARGET // dosRow mouse PNG +// dosRow mouse-at PNG X,Y // // DOSBox's raw capture is the 320x200 stage, or a whole multiple of it. // KEYS (examples/keys) stamps its rows as nibble pixels in its palette 0, // which the port's 6-bit DAC shows as each 4-bit gun widened to 6 bits and // then to 8 (bit replication); a pixel is the nearest entry, since // emulators differ in how they scale the DAC (replication or rounding) but -// the entries sit some 17 apart a gun. +// the entries sit some 17 apart a gun. A screen that widens each gun x17 +// (the PSP's) is at most 3 off them, so the nearest is exact there too. // // input: row 198's first 40 bytes (two pixels a byte, high nibble first), // printed as "VERIFY-DOS-INPUT row=<hex>", must start with the sentinel A5 @@ -25,6 +28,9 @@ // sentinel 5A5A; prints "<flags hex> <x> <y>", or nothing and exits 1 when // the row is not there (or the screenshot is another size). // +// mouse-at: the same, from the stage at 1:1 with its corner at (X, Y) on a +// screenshot of any size it fits in. +// // A bad command line exits 2. #include <stdio.h> @@ -56,29 +62,36 @@ #define DAC_LOW_BITS 2u #define MAX_DIST (3u * 8u * 8u) -// The screenshot, its scale, and KEYS's palette as the DAC shows it. +// The screenshot, the stage's scale and corner on it, and KEYS's palette as +// the DAC shows it. typedef struct StageT { GateImageT img; uint32_t sx; uint32_t sy; - uint8_t palette[GATE_KEYS_COLOURS][GATE_RGB_BYTES]; + uint32_t ox; + uint32_t oy; + uint8_t palette[GATE_KEYS_COLORS][GATE_RGB_BYTES]; } StageT; static uint8_t dac8(uint32_t c4); +static int mousePrint(StageT *stage); static uint32_t pixel(const StageT *stage, uint32_t x, uint32_t y, uint32_t *distance, const uint8_t **rgb); static int readInput(char **args); static int readMouse(char **args); -static bool stageLoad(const char *path, StageT *stage); +static int readMouseAt(char **args); +static bool stageLoad(const char *path, const char *at, StageT *stage); static const GateModeT kModes[] = { { "input", 2, readInput }, - { "mouse", 1, readMouse } + { "mouse", 1, readMouse }, + { "mouse-at", 2, readMouseAt } }; static const char *const kUsage[] = { "usage:", " dosRow input PNG TARGET KEYS's row 198 must hold TARGET", - " dosRow mouse PNG KEYS's row 199: prints \"<flags hex> <x> <y>\"" + " dosRow mouse PNG KEYS's row 199: prints \"<flags hex> <x> <y>\"", + " dosRow mouse-at PNG X,Y the same, the stage at 1:1 with its corner at (X, Y)" }; @@ -91,10 +104,34 @@ static uint8_t dac8(uint32_t c4) { } +// KEYS's mouse row off the stage, printed as "<flags hex> <x> <y>"; 1 when +// the row is not there. +static int mousePrint(StageT *stage) { + const uint8_t *rgb; + uint32_t words[MOUSE_WORDS]; + uint32_t distance; + uint32_t w; + uint32_t n; + + for (w = 0; w < MOUSE_WORDS; w++) { + words[w] = 0u; + for (n = 0; n < WORD_NIBBLES; n++) { + words[w] = (words[w] << NIBBLE_BITS) | pixel(stage, w * WORD_NIBBLES + n, MOUSE_Y, &distance, &rgb); + } + } + gateImageFree(&stage->img); + if (words[0] != MOUSE_SENTINEL) { + return FAIL_EXIT; + } + printf("%04X %u %u\n", (unsigned)words[MOUSE_FLAGS], (unsigned)words[MOUSE_X], (unsigned)words[MOUSE_Y_WORD]); + return 0; +} + + // The palette entry nearest stage pixel (x, y), and how near. static uint32_t pixel(const StageT *stage, uint32_t x, uint32_t y, uint32_t *distance, const uint8_t **rgb) { - *rgb = gateImagePixel(&stage->img, x * stage->sx, y * stage->sy); - return gateNearest(*rgb, (const uint8_t (*)[GATE_RGB_BYTES])stage->palette, GATE_KEYS_COLOURS, distance); + *rgb = gateImagePixel(&stage->img, stage->ox + x * stage->sx, stage->oy + y * stage->sy); + return gateNearest(*rgb, (const uint8_t (*)[GATE_RGB_BYTES])stage->palette, GATE_KEYS_COLORS, distance); } @@ -108,7 +145,7 @@ static int readInput(char **args) { uint32_t p; size_t gotLen; - if (!stageLoad(args[0], &stage)) { + if (!stageLoad(args[0], NULL, &stage)) { printf("verify-dos-input: FAIL - unexpected screenshot size %ux%u\n", (unsigned)stage.img.width, (unsigned)stage.img.height); return FAIL_EXIT; } @@ -142,46 +179,50 @@ static int readInput(char **args) { static int readMouse(char **args) { - StageT stage; - const uint8_t *rgb; - uint32_t words[MOUSE_WORDS]; - uint32_t distance; - uint32_t w; - uint32_t n; + StageT stage; - if (!stageLoad(args[0], &stage)) { - return FAIL_EXIT; - } - for (w = 0; w < MOUSE_WORDS; w++) { - words[w] = 0u; - for (n = 0; n < WORD_NIBBLES; n++) { - words[w] = (words[w] << NIBBLE_BITS) | pixel(&stage, w * WORD_NIBBLES + n, MOUSE_Y, &distance, &rgb); - } - } - gateImageFree(&stage.img); - if (words[0] != MOUSE_SENTINEL) { - return FAIL_EXIT; - } - printf("%04X %u %u\n", (unsigned)words[MOUSE_FLAGS], (unsigned)words[MOUSE_X], (unsigned)words[MOUSE_Y_WORD]); - return 0; + return stageLoad(args[0], NULL, &stage) ? mousePrint(&stage) : FAIL_EXIT; } -// The screenshot and its scale (false when it is not a whole multiple of -// the stage), and the palette. -static bool stageLoad(const char *path, StageT *stage) { - uint16_t words[GATE_KEYS_COLOURS]; +static int readMouseAt(char **args) { + StageT stage; + + return stageLoad(args[0], args[1], &stage) ? mousePrint(&stage) : FAIL_EXIT; +} + + +// The screenshot, the palette, and where the stage is: the whole screenshot, +// at a whole multiple of its size (at NULL), or at 1:1 with its corner at +// at's "X,Y". False when the stage is not there. +static bool stageLoad(const char *path, const char *at, StageT *stage) { + uint16_t words[GATE_KEYS_COLORS]; uint32_t i; + long x; + long y; gateImageLoad(path, &stage->img); - stage->sx = stage->img.width / STAGE_W; - stage->sy = stage->img.height / STAGE_H; gateKeysPalette(words); - for (i = 0; i < GATE_KEYS_COLOURS; i++) { + for (i = 0; i < GATE_KEYS_COLORS; i++) { stage->palette[i][0] = dac8((words[i] >> (2u * NIBBLE_BITS)) & NIBBLE_MASK); stage->palette[i][1] = dac8((words[i] >> NIBBLE_BITS) & NIBBLE_MASK); stage->palette[i][2] = dac8(words[i] & NIBBLE_MASK); } + if (at != NULL) { + gateArgPair("corner", at, ',', &x, &y); + if (x < 0 || y < 0 || (unsigned long)x + STAGE_W > stage->img.width || (unsigned long)y + STAGE_H > stage->img.height) { + return false; + } + stage->sx = 1u; + stage->sy = 1u; + stage->ox = (uint32_t)x; + stage->oy = (uint32_t)y; + return true; + } + stage->sx = stage->img.width / STAGE_W; + stage->sy = stage->img.height / STAGE_H; + stage->ox = 0u; + stage->oy = 0u; return stage->sx >= 1u && stage->sy >= 1u && stage->img.width % STAGE_W == 0u && stage->img.height % STAGE_H == 0u; } diff --git a/tests/gate/iconFind.c b/tests/gate/iconFind.c index b8e372c..9418f82 100644 --- a/tests/gate/iconFind.c +++ b/tests/gate/iconFind.c @@ -11,16 +11,16 @@ // iconFind win16 PNG ICON.ico LABEL // // The screenshot must hold every opaque pixel of the icon, each in its -// colour give or take TOLERANCE a gun: BeOS R5's and DVX's 16-bit screens +// color give or take TOLERANCE a gun: BeOS R5's and DVX's 16-bit screens // keep 5 or 6 bits a gun, and the 6-bit DAC behind DOSBox's capture moves -// a colour by a few levels. The search anchors on the opaque pixel whose -// colour the screen holds least and tries the icon at every place that -// colour is (gateIconFind). +// a color by a few levels. The search anchors on the opaque pixel whose +// color the screen holds least and tries the icon at every place that +// color is (gateIconFind). // // be: iconBake's raw icons in ICONDIR, icon32.raw then icon16.raw (32x32 // and 16x16 indices into the BeOS palette, whose numbers are read from // PALETTE.h's kBePalette table; index 255 is transparent); either will do. -// dvx: a BMP whose DVX grey (192, 192, 192) is the ground. win16: an icon +// dvx: a BMP whose DVX gray (192, 192, 192) is the ground. win16: an icon // whose picture is opaque where its alpha is at least half. // // Prints LABEL: PASS or LABEL: FAIL as the gate's verdict; exits 0 or 1, @@ -60,7 +60,7 @@ static int findDvx(char **args); static int findWin16(char **args); static bool halfOpaque(const uint8_t *pixel); static void iconFromImage(const GateImageT *img, OpaqueFnT opaque, GateIconT *icon); -static bool notGrey(const uint8_t *pixel); +static bool notGray(const uint8_t *pixel); static const GateModeT kModes[] = { { "be", 4, findBe }, @@ -70,21 +70,21 @@ static const GateModeT kModes[] = { static const BeIconT kBeIcons[BE_ICON_COUNT] = { { 32u, "icon32.raw" }, { 16u, "icon16.raw" } }; -static const uint8_t kDvxGrey[GATE_RGB_BYTES] = { 192, 192, 192 }; +static const uint8_t kDvxGray[GATE_RGB_BYTES] = { 192, 192, 192 }; static const char *const kUsage[] = { "usage:", " iconFind be PNG ICONDIR PALETTE.h LABEL Tracker shows icon32.raw or", " icon16.raw (BeOS palette)", " iconFind dvx PNG ICON.bmp LABEL Program Manager shows the BMP", - " (all but DVX's grey)", + " (all but DVX's gray)", " iconFind win16 PNG ICON.ico LABEL Windows 3.1 shows the icon", " (its opaque pixels)" }; // The BeOS palette iconBake bakes with: the numbers in the table that -// follows kBePalette in its header, three a colour. +// follows kBePalette in its header, three a color. static void bePalette(const char *path, uint8_t palette[BE_PALETTE_SIZE][GATE_RGB_BYTES]) { uint8_t *text; size_t len; @@ -161,7 +161,7 @@ static int findBe(char **args) { } -// Program Manager shows DRAW by its icon: every pixel not the grey. +// Program Manager shows DRAW by its icon: every pixel not the gray. static int findDvx(char **args) { GateImageT bmp; GateImageT screen; @@ -170,18 +170,18 @@ static int findDvx(char **args) { uint32_t top; gateImageLoad(args[1], &bmp); - iconFromImage(&bmp, notGrey, &icon); + iconFromImage(&bmp, notGray, &icon); gateImageLoad(args[0], &screen); if (!gateIconFind(&screen, &icon, TOLERANCE, &left, &top)) { printf("%s: FAIL - DRAW's icon is not on the screen (%u pixels looked for)\n", args[2], (unsigned)icon.count); return FAIL_EXIT; } - printf("%s: PASS (Program Manager shows DRAW by its icon at %u,%u: all %u pixels of the baked %ux%u that are not the grey)\n", args[2], (unsigned)left, (unsigned)top, (unsigned)icon.count, (unsigned)icon.width, (unsigned)icon.height); + printf("%s: PASS (Program Manager shows DRAW by its icon at %u,%u: all %u pixels of the baked %ux%u that are not the gray)\n", args[2], (unsigned)left, (unsigned)top, (unsigned)icon.count, (unsigned)icon.width, (unsigned)icon.height); return 0; } -// DRAW minimised: Windows 3.1 shows the icon bound into it. +// DRAW minimized: Windows 3.1 shows the icon bound into it. static int findWin16(char **args) { GateImageT ico; GateImageT screen; @@ -200,7 +200,7 @@ static int findWin16(char **args) { printf("%s: FAIL - the program's icon is not on the screen (%u opaque pixels looked for)\n", args[2], (unsigned)icon.count); return FAIL_EXIT; } - printf("%s: PASS (DRAW minimised shows its bound icon at %u,%u: all %u opaque pixels of the baked 32x32 in place)\n", args[2], (unsigned)left, (unsigned)top, (unsigned)icon.count); + printf("%s: PASS (DRAW minimized shows its bound icon at %u,%u: all %u opaque pixels of the baked 32x32 in place)\n", args[2], (unsigned)left, (unsigned)top, (unsigned)icon.count); return 0; } @@ -230,8 +230,8 @@ static void iconFromImage(const GateImageT *img, OpaqueFnT opaque, GateIconT *ic } -static bool notGrey(const uint8_t *pixel) { - return memcmp(pixel, kDvxGrey, GATE_RGB_BYTES) != 0; +static bool notGray(const uint8_t *pixel) { + return memcmp(pixel, kDvxGray, GATE_RGB_BYTES) != 0; } diff --git a/tests/gate/iigsIcon.c b/tests/gate/iigsIcon.c index 8163627..f9d0c71 100644 --- a/tests/gate/iigsIcon.c +++ b/tests/gate/iigsIcon.c @@ -10,7 +10,7 @@ // SHR.bin is the Super Hi-Res block MAME's memory held ($E1/2000 to // $E1/9FFF: pixels, scan line control bytes, palettes); ICON is iconBake's // Finder icon file. The screen is read at 640 pixels a line: a 640-mode -// line's pixel takes its colour from the palette quarter its place in the +// line's pixel takes its color from the palette quarter its place in the // byte gives it (bits 7-6 from entries 8-11, 5-4 from 12-15, 3-2 from 0-3, // 1-0 from 4-7); a 320-mode line's pixel is shown twice. Each 640-mode // screen pixel's own 2-bit value is kept (none on a 320-mode line), and @@ -255,7 +255,7 @@ int main(int argc, char **argv) { printf("%s: FAIL - no program's icon is in the JOEYLIB window (%u opaque pixels looked for)\n", label, (unsigned)(halfCount / HALVES)); return FAIL_EXIT; } - printf("%s: PASS (the Finder shows %u programs by the baked %ux%u colour icon, the first at %u,%u: all %u opaque pixels, each its two screen pixels' values)\n", label, (unsigned)found, (unsigned)w, (unsigned)h, (unsigned)firstX, (unsigned)firstY, (unsigned)(halfCount / HALVES)); + printf("%s: PASS (the Finder shows %u programs by the baked %ux%u color icon, the first at %u,%u: all %u opaque pixels, each its two screen pixels' values)\n", label, (unsigned)found, (unsigned)w, (unsigned)h, (unsigned)firstX, (unsigned)firstY, (unsigned)(halfCount / HALVES)); free(halves); free(icon); free(values); diff --git a/tests/gate/joyScreen.c b/tests/gate/joyScreen.c index 960e504..ff1fbe0 100644 --- a/tests/gate/joyScreen.c +++ b/tests/gate/joyScreen.c @@ -8,7 +8,7 @@ // // JOY (examples/joy) shows each stick in a frame: a dot that sits in the // middle at rest and moves DOT_TRAVEL stage pixels per full axis -// (jlJoystickX/Y * DOT_TRAVEL / 127, grey at rest, green away from it), two +// (jlJoystickX/Y * DOT_TRAVEL / 127, gray at rest, green away from it), two // button squares (yellow while down) and a patch under them, green when the // port reports a joystick and red when it does not. This finds the stage in // a screenshot (on a desktop the window is wherever DVX put it, at the @@ -16,7 +16,7 @@ // characters: connected (1/0), X and Y (- 0 +: the dot at least half its // travel left or up, in the middle, at least half its travel right or // down), buttons 0 and 1 (1/0); '?' for anything else (a dot part way from -// the middle, a colour that is not JOY's). Half way, as DVX's own joystick +// the middle, a color that is not JOY's). Half way, as DVX's own joystick // tests judge an axis: an uncalibrated DVX axis ranges itself as it moves, // so an emulated stick's full push reads a little short of the end. // @@ -59,8 +59,8 @@ #define STATUS_TOP (BUTTON_TOP + BUTTON_H + ROW_GAP) // A 15-bit desktop is off by at most 8 a channel, the 6-bit VGA DAC by at -// most 3; JOY's colours sit much further apart. (All are exact in DVX's -// 256-colour desktop palette.) +// most 3; JOY's colors sit much further apart. (All are exact in DVX's +// 256-color desktop palette.) #define MAX_DIST (3u * 9u * 9u) #define SCALE_COUNT 3u #define UNKNOWN '?' @@ -75,23 +75,23 @@ #define STATE_CHARS 5u #define OFFSET_CHARS 24u -// JOY's colours, in the order the nearest is chosen among equals. -typedef enum JoyColourE { - COLOUR_BACKGROUND, - COLOUR_FRAME, - COLOUR_DOT_OFF, - COLOUR_DOT_ON, - COLOUR_BUTTON_OFF, - COLOUR_BUTTON_ON, - COLOUR_DISCONNECTED, - COLOUR_COUNT, - COLOUR_NONE = -1 -} JoyColourE; +// JOY's colors, in the order the nearest is chosen among equals. +typedef enum JoyColorE { + COLOR_BACKGROUND, + COLOR_FRAME, + COLOR_DOT_OFF, + COLOR_DOT_ON, + COLOR_BUTTON_OFF, + COLOR_BUTTON_ON, + COLOR_DISCONNECTED, + COLOR_COUNT, + COLOR_NONE = -1 +} JoyColorE; // The patch's green is the dot's (COLOR_CONNECTED = COLOR_DOT_ON). -#define COLOUR_CONNECTED COLOUR_DOT_ON +#define COLOR_CONNECTED COLOR_DOT_ON -// The screenshot as JOY's colour names, and the stage's place and scale. +// The screenshot as JOY's color names, and the stage's place and scale. typedef struct ScreenT { uint32_t w; uint32_t h; @@ -109,12 +109,12 @@ typedef struct StickT { static char axisChar(long offset); static bool findStage(ScreenT *scr); -static JoyColourE name(const ScreenT *scr, long x, long y); -static bool patch(JoyColourE colour); -static JoyColourE stagePixel(const ScreenT *scr, long x, long y); +static JoyColorE name(const ScreenT *scr, long x, long y); +static bool patch(JoyColorE color); +static JoyColorE stagePixel(const ScreenT *scr, long x, long y); static void stick(const ScreenT *scr, long left, StickT *out); -static const uint16_t kColours[COLOUR_COUNT] = { 0x000, 0x444, 0x666, 0x0F0, 0x333, 0xFF0, 0xF00 }; +static const uint16_t kColors[COLOR_COUNT] = { 0x000, 0x444, 0x666, 0x0F0, 0x333, 0xFF0, 0xF00 }; static const long kStickLeft[STICK_COUNT] = { 24, 216 }; static const long kScales[SCALE_COUNT] = { 1, 2, 3 }; @@ -149,7 +149,7 @@ static char axisChar(long offset) { // The screen position of stage (0, 0) at the screen's scale, found by the -// two status patches: runs of STATUS_W stage pixels in either patch colour +// two status patches: runs of STATUS_W stage pixels in either patch color // on one row, as far apart as the two frames. static bool findStage(ScreenT *scr) { long run; @@ -195,22 +195,22 @@ static bool findStage(ScreenT *scr) { } -// JOY's colour name at screen (x, y), or none. -static JoyColourE name(const ScreenT *scr, long x, long y) { +// JOY's color name at screen (x, y), or none. +static JoyColorE name(const ScreenT *scr, long x, long y) { if (x < 0 || y < 0 || x >= (long)scr->w || y >= (long)scr->h) { - return COLOUR_NONE; + return COLOR_NONE; } - return (JoyColourE)scr->names[(size_t)y * scr->w + (size_t)x]; + return (JoyColorE)scr->names[(size_t)y * scr->w + (size_t)x]; } -static bool patch(JoyColourE colour) { - return colour == COLOUR_CONNECTED || colour == COLOUR_DISCONNECTED; +static bool patch(JoyColorE color) { + return color == COLOR_CONNECTED || color == COLOR_DISCONNECTED; } -// JOY's colour name of stage pixel (x, y): its block's middle. -static JoyColourE stagePixel(const ScreenT *scr, long x, long y) { +// JOY's color name of stage pixel (x, y): its block's middle. +static JoyColorE stagePixel(const ScreenT *scr, long x, long y) { return name(scr, scr->originX + x * scr->scale + scr->scale / 2, scr->originY + y * scr->scale + scr->scale / 2); } @@ -218,9 +218,9 @@ static JoyColourE stagePixel(const ScreenT *scr, long x, long y) { // The five-character state of the stick whose frame starts at left, and // its dot's offset from the middle ("x,y", "?" when there is no dot). static void stick(const ScreenT *scr, long left, StickT *out) { - JoyColourE colour; + JoyColorE color; uint32_t dots; - uint32_t colours; + uint32_t colors; uint32_t b; long x; long y; @@ -230,34 +230,34 @@ static void stick(const ScreenT *scr, long left, StickT *out) { long y1; long dx; long dy; - long centre; + long center; long bx; bool atRest; - colour = stagePixel(scr, left + STATUS_W / 2, STATUS_TOP + STATUS_H / 2); - out->state[STATE_CONNECTED] = (colour == COLOUR_CONNECTED) ? '1' : ((colour == COLOUR_DISCONNECTED) ? '0' : UNKNOWN); + color = stagePixel(scr, left + STATUS_W / 2, STATUS_TOP + STATUS_H / 2); + out->state[STATE_CONNECTED] = (color == COLOR_CONNECTED) ? '1' : ((color == COLOR_DISCONNECTED) ? '0' : UNKNOWN); out->state[STATE_X] = UNKNOWN; out->state[STATE_Y] = UNKNOWN; out->state[STATE_CHARS] = '\0'; snprintf(out->offset, sizeof(out->offset), "%c", UNKNOWN); dots = 0u; - colours = 0u; + colors = 0u; x0 = 0; x1 = 0; y0 = 0; y1 = 0; for (y = STICK_TOP; y < STICK_TOP + FRAME_SIZE; y++) { for (x = left; x < left + FRAME_SIZE; x++) { - colour = stagePixel(scr, x, y); - if (colour != COLOUR_DOT_ON && colour != COLOUR_DOT_OFF) { + color = stagePixel(scr, x, y); + if (color != COLOR_DOT_ON && color != COLOR_DOT_OFF) { continue; } x0 = (dots == 0u || x < x0) ? x : x0; x1 = (dots == 0u || x > x1) ? x : x1; y0 = (dots == 0u || y < y0) ? y : y0; y1 = (dots == 0u || y > y1) ? y : y1; - colours |= 1u << colour; + colors |= 1u << color; dots++; } } @@ -265,23 +265,23 @@ static void stick(const ScreenT *scr, long left, StickT *out) { dx = x0 - (left + FRAME_SIZE / 2 - DOT_SIZE / 2); dy = y0 - (STICK_TOP + FRAME_SIZE / 2 - DOT_SIZE / 2); snprintf(out->offset, sizeof(out->offset), "%+ld,%+ld", dx, dy); - if (dots == DOT_SIZE * DOT_SIZE && x1 == x0 + DOT_SIZE - 1 && y1 == y0 + DOT_SIZE - 1 && (colours & (colours - 1u)) == 0u) { + if (dots == DOT_SIZE * DOT_SIZE && x1 == x0 + DOT_SIZE - 1 && y1 == y0 + DOT_SIZE - 1 && (colors & (colors - 1u)) == 0u) { out->state[STATE_X] = axisChar(dx); out->state[STATE_Y] = axisChar(dy); - // Green away from the middle, grey in it. + // Green away from the middle, gray in it. atRest = out->state[STATE_X] == '0' && out->state[STATE_Y] == '0'; - if (colours != (1u << (atRest ? COLOUR_DOT_OFF : COLOUR_DOT_ON))) { + if (colors != (1u << (atRest ? COLOR_DOT_OFF : COLOR_DOT_ON))) { out->state[STATE_X] = UNKNOWN; out->state[STATE_Y] = UNKNOWN; } } } - centre = left + FRAME_SIZE / 2; + center = left + FRAME_SIZE / 2; for (b = 0; b < BUTTON_COUNT; b++) { - bx = (b == 0u) ? centre - BUTTON_W - BUTTON_GAP / 2 : centre + BUTTON_GAP / 2; - colour = stagePixel(scr, bx + BUTTON_W / 2, BUTTON_TOP + BUTTON_H / 2); - out->state[STATE_BUTTONS + b] = (colour == COLOUR_BUTTON_ON) ? '1' : ((colour == COLOUR_BUTTON_OFF) ? '0' : UNKNOWN); + bx = (b == 0u) ? center - BUTTON_W - BUTTON_GAP / 2 : center + BUTTON_GAP / 2; + color = stagePixel(scr, bx + BUTTON_W / 2, BUTTON_TOP + BUTTON_H / 2); + out->state[STATE_BUTTONS + b] = (color == COLOR_BUTTON_ON) ? '1' : ((color == COLOR_BUTTON_OFF) ? '0' : UNKNOWN); } } @@ -290,7 +290,7 @@ int main(int argc, char **argv) { GateImageT img; ScreenT scr; StickT read[STICK_COUNT]; - uint8_t palette[COLOUR_COUNT][GATE_RGB_BYTES]; + uint8_t palette[COLOR_COUNT][GATE_RGB_BYTES]; const long *scales; uint32_t scaleCount; uint32_t mask; @@ -335,8 +335,8 @@ int main(int argc, char **argv) { } gateImageLoad(argv[1], &img); - for (i = 0; i < COLOUR_COUNT; i++) { - gateRgb12(kColours[i] & mask, palette[i]); + for (i = 0; i < COLOR_COUNT; i++) { + gateRgb12(kColors[i] & mask, palette[i]); } scr.w = img.width; scr.h = img.height; @@ -345,8 +345,8 @@ int main(int argc, char **argv) { toolFail("out of memory"); } for (p = 0; p < (size_t)img.width * img.height; p++) { - best = gateNearest(img.rgba + p * GATE_RGBA_BYTES, (const uint8_t (*)[GATE_RGB_BYTES])palette, COLOUR_COUNT, &distance); - scr.names[p] = (distance <= MAX_DIST) ? (int8_t)best : (int8_t)COLOUR_NONE; + best = gateNearest(img.rgba + p * GATE_RGBA_BYTES, (const uint8_t (*)[GATE_RGB_BYTES])palette, COLOR_COUNT, &distance); + scr.names[p] = (distance <= MAX_DIST) ? (int8_t)best : (int8_t)COLOR_NONE; } found = false; for (i = 0; i < scaleCount && !found; i++) { diff --git a/tests/gate/keysCells.c b/tests/gate/keysCells.c index 2aa442f..59473de 100644 --- a/tests/gate/keysCells.c +++ b/tests/gate/keysCells.c @@ -7,12 +7,12 @@ // // keysCells PNG [--at X,Y] [--scale S] // -// KEYS (examples/keys) draws a cell for each named key, lit (colour 2, -// green) while jlKeyDown says it is down and dark grey (colour 1) +// KEYS (examples/keys) draws a cell for each named key, lit (color 2, +// green) while jlKeyDown says it is down and dark gray (color 1) // otherwise. This reads the cells of its fourth row that a port gives a // second physical key for -- Return, Ctrl, Alt, Up, Down, Left, and Right // -- at the middle of each, and prints one digit a cell, in that order: 1 -// lit, 0 not, ? neither colour. +// lit, 0 not, ? neither color. // // --at: where the stage's top left is in the screenshot (default 0,0). // --scale: screen pixels a stage pixel (default 1). A point off the @@ -35,7 +35,7 @@ #define MARGIN_Y 2 #define ROW 4 #define COLUMN_COUNT 7u -// KEYS's colours 1 and 2, and how near a pixel must be: a 15- or 16-bit +// KEYS's colors 1 and 2, and how near a pixel must be: a 15- or 16-bit // screen is off by at most 8 a gun. #define UNLIT_INDEX 1u #define LIT_INDEX 2u @@ -48,12 +48,12 @@ static const long kColumns[COLUMN_COUNT] = { 2, 3, 4, 5, 6, 7, 8 }; static const char *const kUsage[] = { "keysCells - read which of KEYS's named-key cells are lit off a screenshot.", "", - "KEYS (examples/keys) draws a cell for each named key, lit (colour 2, green)", - "while jlKeyDown says it is down and dark grey (colour 1) otherwise. This", + "KEYS (examples/keys) draws a cell for each named key, lit (color 2, green)", + "while jlKeyDown says it is down and dark gray (color 1) otherwise. This", "reads the cells of its fourth row that a port gives a second physical key", "for -- Return, Ctrl, Alt, Up, Down, Left, and Right -- at the middle of", "each, and prints one digit a cell, in that order: 1 lit, 0 not, ? neither", - "colour.", + "color.", "", " keysCells PNG [--at X,Y] [--scale S]", "", @@ -66,7 +66,7 @@ int main(int argc, char **argv) { GateImageT img; const char *png; const uint8_t *pixel; - uint16_t words[GATE_KEYS_COLOURS]; + uint16_t words[GATE_KEYS_COLORS]; uint8_t lit[GATE_RGB_BYTES]; uint8_t unlit[GATE_RGB_BYTES]; char digits[COLUMN_COUNT + 1u]; diff --git a/tests/gate/keysRow.c b/tests/gate/keysRow.c index 61a322b..51df98b 100644 --- a/tests/gate/keysRow.c +++ b/tests/gate/keysRow.c @@ -12,7 +12,7 @@ // in a screenshot (on a desktop the window is wherever DVX put it, at the // window's scale: every stage pixel a SCALE x SCALE block) and checks it // holds exactly TARGET, and that every block of it is whole: each of its -// screen pixels the same colour. +// screen pixels the same color. // // --size: the screenshot must be that size (the full-screen mode, not the // desktop). --at: the row must start exactly there (the stage's own row @@ -24,8 +24,8 @@ // widened x17, as the DVX port's tables do), or no entry when none is // within MAX_DIST: a 15- or 16-bit desktop is off by at most 8 a gun after // the round trip, the 6-bit VGA DAC by at most 3, while the entries sit 17 -// apart, so nearest is exact. (Every KEYS colour is also one of DVX's -// 256-colour desktop palette, so 8 bits is exact too.) +// apart, so nearest is exact. (Every KEYS color is also one of DVX's +// 256-color desktop palette, so 8 bits is exact too.) #include <stdio.h> #include <stdlib.h> @@ -103,19 +103,19 @@ static bool findRow(const int8_t *nibs, uint32_t w, uint32_t h, long scale, cons // Every pixel's KEYS palette index, NO_NIBBLE where no entry is near. static void nibbleMap(const GateImageT *img, int8_t *nibs) { - uint16_t words[GATE_KEYS_COLOURS]; - uint8_t palette[GATE_KEYS_COLOURS][GATE_RGB_BYTES]; + uint16_t words[GATE_KEYS_COLORS]; + uint8_t palette[GATE_KEYS_COLORS][GATE_RGB_BYTES]; uint32_t i; uint32_t best; uint32_t distance; size_t p; gateKeysPalette(words); - for (i = 0; i < GATE_KEYS_COLOURS; i++) { + for (i = 0; i < GATE_KEYS_COLORS; i++) { gateRgb12(words[i], palette[i]); } for (p = 0; p < (size_t)img->width * img->height; p++) { - best = gateNearest(img->rgba + p * GATE_RGBA_BYTES, (const uint8_t (*)[GATE_RGB_BYTES])palette, GATE_KEYS_COLOURS, &distance); + best = gateNearest(img->rgba + p * GATE_RGBA_BYTES, (const uint8_t (*)[GATE_RGB_BYTES])palette, GATE_KEYS_COLORS, &distance); nibs[p] = (distance <= MAX_DIST) ? (int8_t)best : (int8_t)NO_NIBBLE; } } diff --git a/tests/gate/netWmIcon.c b/tests/gate/netWmIcon.c index 4a2029d..4e8e969 100644 --- a/tests/gate/netWmIcon.c +++ b/tests/gate/netWmIcon.c @@ -15,7 +15,7 @@ // // and this compares the picture of the wanted size in that against WANT (a // PNG; an .ico's 32-bit picture of that size). A pixel at least half -// opaque must be so in both, and then of the same colour, give or take +// opaque must be so in both, and then of the same color, give or take // TOLERANCE a gun (a system may round its own way). // // The numbers are those after the first '=', separated by commas and white diff --git a/tests/gate/pngProbe.c b/tests/gate/pngProbe.c index 6a594a0..91d40f0 100644 --- a/tests/gate/pngProbe.c +++ b/tests/gate/pngProbe.c @@ -22,7 +22,7 @@ #define TOOL_NAME "pngProbe" #define SAMPLE_MAX 255 // The arguments after a mode's name: the screenshot, a point, a box's -// size, and a colour. +// size, and a color. #define ARG_PNG 0u #define ARG_X 1u #define ARG_Y 2u diff --git a/tests/gate/pspKeyboardPath.c b/tests/gate/pspKeyboardPath.c index 64694e3..04e1381 100644 --- a/tests/gate/pspKeyboardPath.c +++ b/tests/gate/pspKeyboardPath.c @@ -68,7 +68,7 @@ typedef struct BoardT { static const char kCast[] = "(uint8_t)("; static uint32_t argsSplit(const char *call, size_t len, char out[ARGS_MAX][ARG_MAX]); -static uint32_t centre(const RowT *row, uint32_t col); +static uint32_t center(const RowT *row, uint32_t col); static int charOf(const char *text); static uint32_t defineOf(const char *text, const char *name); static void find(const BoardT *board, const char *want, bool byLegend, uint32_t *row, uint32_t *col, bool *shifted); @@ -130,7 +130,7 @@ static uint32_t argsSplit(const char *call, size_t len, char out[ARGS_MAX][ARG_M // Twice the middle of a key, in half keys. -static uint32_t centre(const RowT *row, uint32_t col) { +static uint32_t center(const RowT *row, uint32_t col) { uint32_t start; uint32_t i; @@ -335,12 +335,12 @@ static void path(BoardT *board, uint32_t toRow, uint32_t toCol) { col = board->col; n = (int)board->count; while (row != toRow) { - middle = centre(&board->rows[row], col); + middle = center(&board->rows[row], col); down = mod((int)toRow - (int)row, n) <= mod((int)row - (int)toRow, n); row = (uint32_t)mod((int)row + (down ? 1 : -1), n); best = UINT32_MAX; for (c = 0; c < board->rows[row].count; c++) { - distance = centre(&board->rows[row], c); + distance = center(&board->rows[row], c); distance = distance > middle ? distance - middle : middle - distance; if (distance < best || (down && distance == best)) { best = distance; diff --git a/tests/gate/stScreenDump.c b/tests/gate/stScreenDump.c index ad643e1..1eca05a 100644 --- a/tests/gate/stScreenDump.c +++ b/tests/gate/stScreenDump.c @@ -11,7 +11,7 @@ // LOG holds the output of "hatari-debug m $ADDRESS 160": lines of // "00XXXXXX: bb bb ..." (up to 16 bytes each; the last dump of an address // wins). The 160 bytes from ADDRESS are one line of the screen: 16-pixel -// groups of four big-endian plane words, a pixel's colour bit n from plane +// groups of four big-endian plane words, a pixel's color bit n from plane // n. // // keys: KEYS's verification row (examples/keys): pixels 0-3 the sentinel @@ -22,7 +22,7 @@ // wrong, saying so). // // cells: the row through KEYS's cells: the first pixel of groups 5, 7, ... -// 17, printed as one digit each (colour 2 "1", colour 1 "0", else "?"). +// 17, printed as one digit each (color 2 "1", color 1 "0", else "?"). #include <stdio.h> #include <stdlib.h> @@ -73,18 +73,18 @@ static int usage(void); static int cells(const RowT *row) { uint32_t group; uint32_t plane; - uint32_t colour; + uint32_t color; uint32_t word; size_t at; for (group = CELL_GROUP_FIRST; group <= CELL_GROUP_LAST; group += CELL_GROUP_STEP) { - colour = 0; + color = 0; for (plane = 0; plane < PLANES; plane++) { at = group * GROUP_BYTES + plane * 2u; word = ((uint32_t)row->bytes[at] << 8) | row->bytes[at + 1u]; - colour |= ((word >> TOP_BIT) & 1u) << plane; + color |= ((word >> TOP_BIT) & 1u) << plane; } - putchar(colour == CELL_ON ? '1' : (colour == CELL_OFF ? '0' : '?')); + putchar(color == CELL_ON ? '1' : (color == CELL_OFF ? '0' : '?')); } putchar('\n'); return 0; diff --git a/tests/gate/tiledCheck.c b/tests/gate/tiledCheck.c index f79a192..37d7cc2 100644 --- a/tests/gate/tiledCheck.c +++ b/tests/gate/tiledCheck.c @@ -16,10 +16,10 @@ // place. tiled2joey, run from JOEY_BUILD's tools/ (REPO's build/ when it is // not set), must turn both maps into the same sheet and maps, and those // must be what is worked out here by hand: the tiles in the order they -// first appear, flips baked, A's colours at their own indices and B's new +// first appear, flips baked, A's colors at their own indices and B's new // one in the first free slot, empty cells 0xFF. assetbake must bake the // sheet as a tile bank, and tiled2joey must refuse a map with 16x16 tiles -// and one whose tileset is not indexed (A's made truecolour). +// and one whose tileset is not indexed (A's made truecolor). // // Prints a "TILED:" line a check. Exit 0 all pass, 1 any fails (or a file // the checks need cannot be read or written, which stops them there), 2 a @@ -90,10 +90,10 @@ #define RUN_ARGS_MAX 16u #define PATH_BYTES 4096u #define DIR_MODE 0777 -// A PNG's bit depth and colour type, in its IHDR (after the signature, the +// A PNG's bit depth and color type, in its IHDR (after the signature, the // chunk's length and type, and the width and height). #define PNG_DEPTH_AT 24u -#define PNG_COLOUR_AT 25u +#define PNG_COLOR_AT 25u // A zlib stream is a 2-byte header, raw DEFLATE, and a 4-byte Adler-32. #define ZLIB_HEADER_BYTES 2u #define ZLIB_TRAILER_BYTES 4u @@ -137,7 +137,7 @@ typedef struct TilesetT { const char *name; const char *image; const RgbT *palette; - size_t colours; + size_t colors; const TileT *tiles; size_t count; uint32_t margin; @@ -168,11 +168,11 @@ typedef struct OutputT { size_t topLen; } OutputT; -// A PNG's kind by its bit depth and colour type: P palette, L grey (1 when -// 1-bit, I when 16-bit), LA grey and alpha, RGB, and RGBA. +// A PNG's kind by its bit depth and color type: P palette, L gray (1 when +// 1-bit, I when 16-bit), LA gray and alpha, RGB, and RGBA. typedef struct PngModeT { uint8_t depth; - uint8_t colourType; + uint8_t colorType; const char *mode; } PngModeT; @@ -212,7 +212,7 @@ static const uint32_t gTop[] = { }; // What the sheet must hold, worked out by hand: each tile's markers as -// sheet palette indices, in first-appearance order. A's colours keep their +// sheet palette indices, in first-appearance order. A's colors keep their // indices (red 1, green 2, blue 3); B's yellow takes slot 4. static const TileT gExpectTiles[] = { {1u, {{1u, 2u, 1u}}}, // A0 @@ -416,7 +416,7 @@ static char *hexText(const char *data, size_t len) { // A tileset's image: an 8-bit palette PNG of 256 entries (black past its -// colours), its tiles' markers set. +// colors), its tiles' markers set. static void indexedImage(const char *path, const TilesetT *tileset) { ToolImageT image; uint32_t width; @@ -433,7 +433,7 @@ static void indexedImage(const char *path, const TilesetT *tileset) { for (i = 0; i < TOOL_PNG_PALETTE_MAX; i++) { image.palette[i][3] = OPAQUE; } - for (i = 0; i < tileset->colours; i++) { + for (i = 0; i < tileset->colors; i++) { image.palette[i][0] = tileset->palette[i].r; image.palette[i][1] = tileset->palette[i].g; image.palette[i][2] = tileset->palette[i].b; @@ -576,7 +576,7 @@ static void sheetLoad(const char *path, SheetT *sheet) { } sheet->mode = "?"; for (i = 0; i < GATE_COUNT_OF(gPngModes); i++) { - if ((uint8_t)data[PNG_DEPTH_AT] == gPngModes[i].depth && (uint8_t)data[PNG_COLOUR_AT] == gPngModes[i].colourType) { + if ((uint8_t)data[PNG_DEPTH_AT] == gPngModes[i].depth && (uint8_t)data[PNG_COLOR_AT] == gPngModes[i].colorType) { sheet->mode = gPngModes[i].mode; } } @@ -585,7 +585,7 @@ static void sheetLoad(const char *path, SheetT *sheet) { // The sheet is what was worked out by hand: a palette image of the expected -// tiles in a row, its first colours the expected ones, every pixel the +// tiles in a row, its first colors the expected ones, every pixel the // expected marker index or 0. The detail says what differs first. static bool sheetMatches(const SheetT *sheet, char *detail, size_t detailSize) { const ToolImageT *image; @@ -905,7 +905,7 @@ int main(int argc, char **argv) { check("16x16 tiles refused", result.status != 0 && contains((const char *)result.err.data, result.err.len, "8x8"), spanTrimmed(&result.err)); runFree(&result); - // Tileset A made truecolour. + // Tileset A made truecolor. pathMake(path, work, gTilesetA.image); if (!toolPngLoad(path, &image, err)) { toolFail("%s: %s", path, err); diff --git a/tests/gate/x68kSerialPeer.c b/tests/gate/x68kSerialPeer.c index f8ca7a9..5642a64 100644 --- a/tests/gate/x68kSerialPeer.c +++ b/tests/gate/x68kSerialPeer.c @@ -13,7 +13,7 @@ // and reading it back proves both directions of the link. // // The guest takes a while to appear: Human68k boots, then COMMAND.X runs -// AUTOEXEC.BAT, then SERIAL.X initialises. Rather than guess, this +// AUTOEXEC.BAT, then SERIAL.X initializes. Rather than guess, this // retransmits the probe every RESEND_SECONDS until the echo comes back or // the deadline (--timeout, default 420 seconds) passes. // diff --git a/tests/host/drawHost.c b/tests/host/drawHost.c index aef03a5..cbbed64 100644 --- a/tests/host/drawHost.c +++ b/tests/host/drawHost.c @@ -10,9 +10,9 @@ // long flat line (-20000,50)-(20000,60) crosses the stage on row 55 // long tall line (10,-20000)-(11,20000) puts one pixel on every row, // in column 10 or 11 -// huge circle r 20000 centred 20000 left of x 160: every pixel on +// huge circle r 20000 centered 20000 left of x 160: every pixel on // the stage within a pixel of the circle, one on every row -// far circle centred near the int16_t limit: nothing on the stage +// far circle centered near the int16_t limit: nothing on the stage // An alarm fails a case that does not finish. // // Built by scripts/check-draw.sh with -DJOEYLIB_PLATFORM_BLANK. diff --git a/tests/host/inputHost.c b/tests/host/inputHost.c index cbacb3a..c6fc957 100644 --- a/tests/host/inputHost.c +++ b/tests/host/inputHost.c @@ -37,6 +37,14 @@ static int gMockCount = 0; static uint8_t gMockButtons[JL_JOY_BUTTON_COUNT]; static uint32_t gMockMillis = 0; +// What the mocked jlpInputPoll reports for the mouse's buttons and Ctrl: +// every poll, or (gMockMouseEvents) a button only as it changes, as the +// Amiga's IDCMP and the hosted ports' events do; and what it last reported. +static uint8_t gMockMouse [JL_MOUSE_BUTTON_COUNT]; +static uint8_t gMockMouseSent[JL_MOUSE_BUTTON_COUNT]; +static bool gMockCtrl = false; +static bool gMockMouseEvents = false; + // A script for jlWaitForAnyKey: each poll applies its next step, the keys // held and the characters typed. Past its end the poll holds ESCAPE, which // ends any wait, and says so in gWaitOverrun. @@ -62,10 +70,12 @@ static bool gWaitOverrun = false; // Prototypes (harness-local, alphabetical). static bool checkEmpty(const char *name); static bool pollJoy(const char *name, uint32_t millis, bool fire, bool down0, bool down1); +static bool pollMouse(const char *name, bool left, bool right, bool ctrl, bool wantLeft, bool wantRight); static bool popExpect(const char *name, const uint8_t *expect, int count); static void pushAll(const uint8_t *chars, int count); static bool runAxisScale(void); static bool runChord(void); +static bool runCtrlClick(void); static bool runDeadZone(void); static bool runFilter(void); static bool runInterleave(void); @@ -106,6 +116,13 @@ void jlpInputPoll(void) { gMockCount = 0; jlgJoyButtonState[JL_JOYSTICK_0][JL_JOY_BUTTON_0] = gMockButtons[JL_JOY_BUTTON_0]; jlgJoyButtonState[JL_JOYSTICK_0][JL_JOY_BUTTON_1] = gMockButtons[JL_JOY_BUTTON_1]; + jlgKeyState[JL_KEY_LCTRL] = gMockCtrl; + for (i = JL_MOUSE_BUTTON_LEFT; i < JL_MOUSE_BUTTON_COUNT; i++) { + if (!gMockMouseEvents || gMockMouse[i] != gMockMouseSent[i]) { + jlgMouseButtonState[i] = gMockMouse[i]; + gMockMouseSent[i] = gMockMouse[i]; + } + } } @@ -150,6 +167,26 @@ static bool pollJoy(const char *name, uint32_t millis, bool fire, bool down0, bo } +// One poll with the mouse's left and right buttons and Ctrl as given; true +// when the left and right buttons then read wantLeft and wantRight. +static bool pollMouse(const char *name, bool left, bool right, bool ctrl, bool wantLeft, bool wantRight) { + bool gotLeft; + bool gotRight; + + gMockMouse[JL_MOUSE_BUTTON_LEFT] = left; + gMockMouse[JL_MOUSE_BUTTON_RIGHT] = right; + gMockCtrl = ctrl; + jlInputPoll(); + gotLeft = jlMouseDown(JL_MOUSE_BUTTON_LEFT); + gotRight = jlMouseDown(JL_MOUSE_BUTTON_RIGHT); + if (gotLeft != wantLeft || gotRight != wantRight) { + printf(" %s (%s): left %d, right %d, Ctrl %d: read %d %d, want %d %d\n", name, gMockMouseEvents ? "events" : "levels", left, right, ctrl, gotLeft, gotRight, wantLeft, wantRight); + return false; + } + return true; +} + + static bool popExpect(const char *name, const uint8_t *expect, int count) { int got; int i; @@ -175,7 +212,7 @@ static void pushAll(const uint8_t *chars, int count) { // A raw axis reads -127..127 rounded to the nearest: the ends at the ends, -// and a centred stick 0 from either middle value of an even span (DVX's +// and a centered stick 0 from either middle value of an even span (DVX's // -32768..32767 read -1 at rest before it was rounded). static bool runAxisScale(void) { static const int32_t kPos[] = { INT16_MIN, INT16_MAX, 0, -1, 32767, 32768, 0, 65535, -100 }; @@ -215,8 +252,45 @@ static bool runChord(void) { } -// A reading within the dead zone of centre is 0, at its edge too; one past +// A reading within the dead zone of center is 0, at its edge too; one past // it is the reading; a zone of 0 leaves every reading but 0 alone. +// A left press made with Ctrl held is the right button until it is let go, +// Ctrl let go first or not; Ctrl pressed after the press changes nothing; +// a real right button is down alongside. The same whether the port reports +// its buttons every poll or only as they change. +static bool runCtrlClick(void) { + bool ok = true; + int mode; + + for (mode = 0; mode < 2; mode++) { + gMockMouseEvents = mode != 0; + ok &= pollMouse("click", true, false, false, true, false); + ok &= jlMousePressed(JL_MOUSE_BUTTON_LEFT); + ok &= pollMouse("click let go", false, false, false, false, false); + ok &= pollMouse("Ctrl down", false, false, true, false, false); + ok &= pollMouse("Ctrl-click", true, false, true, false, true); + ok &= jlMousePressed(JL_MOUSE_BUTTON_RIGHT) && !jlMousePressed(JL_MOUSE_BUTTON_LEFT); + ok &= pollMouse("Ctrl-click held", true, false, true, false, true); + ok &= pollMouse("Ctrl let go first", true, false, false, false, true); + ok &= pollMouse("Ctrl-click let go", false, false, false, false, false); + ok &= jlMouseReleased(JL_MOUSE_BUTTON_RIGHT) && !jlMouseReleased(JL_MOUSE_BUTTON_LEFT); + ok &= pollMouse("click, then Ctrl", true, false, false, true, false); + ok &= pollMouse("Ctrl after the click", true, false, true, true, false); + ok &= pollMouse("let go with Ctrl", false, false, true, false, false); + ok &= pollMouse("Ctrl-click again", true, false, true, false, true); + ok &= pollMouse("and the right button", true, true, true, false, true); + ok &= pollMouse("Ctrl-click let go, right held", false, true, false, false, true); + ok &= pollMouse("right let go", false, false, false, false, false); + ok &= pollMouse("right, then Ctrl-click", true, true, true, false, true); + ok &= pollMouse("right let go, Ctrl-click held", true, false, false, false, true); + ok &= pollMouse("all let go", false, false, false, false, false); + } + gMockMouseEvents = false; + printf(" Ctrl-click: %s\n", ok ? "PASS" : "FAIL"); + return ok; +} + + static bool runDeadZone(void) { static const int8_t kIn[] = { 10, 11, -10, -11, 0, 1, -1, 127, -127 }; static const uint8_t kZone[] = { 10, 10, 10, 10, 0, 0, 0, 10, 10 }; @@ -492,6 +566,7 @@ int main(void) { ok &= runDeadZone(); ok &= runAxisScale(); ok &= runChord(); + ok &= runCtrlClick(); ok &= runSecondKey(); ok &= runMouse(); ok &= runWait(); diff --git a/tests/host/modPlayerHost.c b/tests/host/modPlayerHost.c index 14114da..ed8b09a 100644 --- a/tests/host/modPlayerHost.c +++ b/tests/host/modPlayerHost.c @@ -5,7 +5,7 @@ // Host harness for src/core/modPlayer.c (scripts/check-modplayer.sh). It // includes the player, so it can tick the replay and read the emulated Paula // directly, and builds its own small modules in memory: -// protracker the Protracker 2.3 behaviours the player keeps: a sample +// protracker the Protracker 2.3 behaviors the player keeps: a sample // offset applied twice, 6xy's vibrato, an arpeggio past B-3, // a tremolo twice a vibrato's depth, glissando, pattern loops // and delays, a sample number without a note taking effect diff --git a/tests/host/tileBankExpect.c b/tests/host/tileBankExpect.c index 604b343..bb1bbb2 100644 --- a/tests/host/tileBankExpect.c +++ b/tests/host/tileBankExpect.c @@ -6,7 +6,7 @@ // must show, decoded straight from its indexed PNG (independent of // assetbake's encoders), for tests/host/tileBankStage.sh: // tileCount LE16, the first 16 palette entries as LE16 $0RGB, then each -// tile's 64 colour indices (tiles row-major across the sheet, each tile +// tile's 64 color indices (tiles row-major across the sheet, each tile // row-major) // // Usage: tileBankExpect PNG OUT diff --git a/tests/host/tileBankHost.c b/tests/host/tileBankHost.c index 199b46a..bc9f298 100644 --- a/tests/host/tileBankHost.c +++ b/tests/host/tileBankHost.c @@ -19,7 +19,7 @@ // refuse<n>.tbk one bake per other target; their names arrive as argv[1..] // 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 malformed banks (bad magic, short header, truncated payload, no // palette, more tiles than a surface holds) are cut from own.tbk here, so @@ -67,7 +67,7 @@ #define TILE_CAPACITY BIG_BANK_TILES #define BAD_MAGIC_BYTE 'X' -#define FILL_COLOR 9u // pre-paste colour; blocks past a bank must keep it (examples/tilebank clears to the same, so its hash can match ours) +#define FILL_COLOR 9u // pre-paste color; blocks past a bank must keep it (examples/tilebank clears to the same, so its hash can match ours) #define PALETTE_SENTINEL 0xBEEFu #define SHORT_HEADER_BYTES (TBK_HEADER_BYTES / 2) #define SMALL_MAX_TILES 5u diff --git a/tests/host/tileLayerHost.c b/tests/host/tileLayerHost.c index 04e35c7..3e0ff4c 100644 --- a/tests/host/tileLayerHost.c +++ b/tests/host/tileLayerHost.c @@ -12,7 +12,7 @@ // both ways), maps smaller than the view, random bands, and cells changed // with jlTileLayerSetTile, in and out of view. Also: rows outside the band // keep their pixels, the band's rows are marked dirty full width, a -// JL_TILE_MAP_SKIP cell draws colour 0, and jlTileLayerCreate refuses bad +// JL_TILE_MAP_SKIP cell draws color 0, and jlTileLayerCreate refuses bad // arguments. // // Built by scripts/check-tilelayer.sh with -DJOEYLIB_PLATFORM_BLANK. @@ -98,7 +98,7 @@ static bool checkCreateRefusals(void) { } -// The colour of map pixel (mx, my), straight from the map and the tiles. +// The color of map pixel (mx, my), straight from the map and the tiles. static uint8_t expectedPixel(uint16_t mapW, uint16_t mapH, uint32_t mx, uint32_t my) { uint8_t idx; uint8_t b; diff --git a/tools/assetbake/assetbake.c b/tools/assetbake/assetbake.c index ff8f72a..32e1f55 100644 --- a/tools/assetbake/assetbake.c +++ b/tools/assetbake/assetbake.c @@ -132,7 +132,7 @@ static void headerWrite(uint8_t *header, const char *magic, const ToolImageT *im memset(header, 0, HEADER_BYTES); memcpy(header, magic, MAGIC_BYTES); for (i = 0; i < PALETTE_ENTRIES && i < img->paletteCount; i++) { - word = (uint16_t)((img->palette[i][0] >> NIBBLE_SHIFT) << 8 | (img->palette[i][1] >> NIBBLE_SHIFT) << 4 | (img->palette[i][2] >> NIBBLE_SHIFT)); + word = (uint16_t)((img->palette[i][0] >> NIBBLE_SHIFT) << 8 | (img->palette[i][1] >> NIBBLE_SHIFT) << 4 | (img->palette[i][2] >> NIBBLE_SHIFT)); header[HEADER_PALETTE_AT + i * 2u] = (uint8_t)word; header[HEADER_PALETTE_AT + i * 2u + 1u] = (uint8_t)(word >> 8); } diff --git a/tools/common/toolPng.c b/tools/common/toolPng.c index 1ee2dd4..f0731b3 100644 --- a/tools/common/toolPng.c +++ b/tools/common/toolPng.c @@ -21,10 +21,10 @@ #define PNG_SIGNATURE_BYTES 8u #define CHUNK_OVERHEAD 12u #define IHDR_BYTES 13u -#define COLOR_GREY 0u +#define COLOR_GRAY 0u #define COLOR_RGB 2u #define COLOR_PALETTE 3u -#define COLOR_GREY_ALPHA 4u +#define COLOR_GRAY_ALPHA 4u #define COLOR_RGBA 6u #define FILTER_NONE 0u #define FILTER_SUB 1u @@ -48,10 +48,10 @@ typedef struct DecodeT { } DecodeT; static const uint8_t kSignature[PNG_SIGNATURE_BYTES] = { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n' }; -static const uint8_t kAdam7X0[ADAM7_PASSES] = { 0, 4, 0, 2, 0, 1, 0 }; -static const uint8_t kAdam7Y0[ADAM7_PASSES] = { 0, 0, 4, 0, 2, 0, 1 }; -static const uint8_t kAdam7Dx[ADAM7_PASSES] = { 8, 8, 4, 4, 2, 2, 1 }; -static const uint8_t kAdam7Dy[ADAM7_PASSES] = { 8, 8, 8, 4, 4, 2, 2 }; +static const uint8_t kAdam7X0[ADAM7_PASSES] = { 0, 4, 0, 2, 0, 1, 0 }; +static const uint8_t kAdam7Y0[ADAM7_PASSES] = { 0, 0, 4, 0, 2, 0, 1 }; +static const uint8_t kAdam7Dx[ADAM7_PASSES] = { 8, 8, 4, 4, 2, 2, 1 }; +static const uint8_t kAdam7Dy[ADAM7_PASSES] = { 8, 8, 8, 4, 4, 2, 2 }; static bool appendBytes(uint8_t **buf, size_t *len, size_t *cap, const uint8_t *data, size_t count); static bool appendChunk(uint8_t **buf, size_t *len, size_t *cap, const char *type, const uint8_t *data, size_t count); @@ -115,11 +115,11 @@ static void decodePixel(const DecodeT *d, const uint8_t *row, uint32_t i, uint32 } out = d->img->pixels + ((size_t)y * d->width + x) * 4u; maxValue = (uint16_t)((1u << d->depth) - 1u); - if (d->color == COLOR_GREY || d->color == COLOR_GREY_ALPHA) { + if (d->color == COLOR_GRAY || d->color == COLOR_GRAY_ALPHA) { out[0] = (uint8_t)(d->depth == 16u ? v[0] >> 8 : (uint32_t)v[0] * 255u / maxValue); out[1] = out[0]; out[2] = out[0]; - if (d->color == COLOR_GREY_ALPHA) { + if (d->color == COLOR_GRAY_ALPHA) { out[3] = (uint8_t)(d->depth == 16u ? v[1] >> 8 : v[1]); } else { out[3] = (d->hasKey && v[0] == d->key[0]) ? 0u : OPAQUE; @@ -239,10 +239,10 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO memset(palette, OPAQUE, sizeof(palette)); paletteCount = 0u; memset(img, 0, sizeof(*img)); - idat = NULL; - idatLen = 0; - idatCap = 0; - seenEnd = false; + idat = NULL; + idatLen = 0; + idatCap = 0; + seenEnd = false; interlace = 0u; if (len < PNG_SIGNATURE_BYTES || memcmp(data, kSignature, PNG_SIGNATURE_BYTES) != 0) { snprintf(err, TOOL_PNG_ERROR_BYTES, "not a PNG"); @@ -285,7 +285,7 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO for (i = 0; i < chunkLen && i < TOOL_PNG_PALETTE_MAX; i++) { palette[i][3] = data[pos + 8u + i]; } - } else if (d.color == COLOR_GREY && chunkLen >= 2u) { + } else if (d.color == COLOR_GRAY && chunkLen >= 2u) { d.hasKey = true; d.key[0] = (uint16_t)((uint16_t)data[pos + 8u] << 8 | data[pos + 9u]); } else if (d.color == COLOR_RGB && chunkLen >= 6u) { @@ -306,7 +306,7 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO pos += CHUNK_OVERHEAD + chunkLen; } switch (d.color) { - case COLOR_GREY: + case COLOR_GRAY: d.channels = 1u; break; case COLOR_RGB: @@ -315,7 +315,7 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO case COLOR_PALETTE: d.channels = 1u; break; - case COLOR_GREY_ALPHA: + case COLOR_GRAY_ALPHA: d.channels = 2u; break; case COLOR_RGBA: @@ -326,7 +326,7 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO break; } if (d.channels == 0u || d.width == 0u || d.height == 0u || d.width > MAX_DIMENSION || d.height > MAX_DIMENSION || (d.depth != 1u && d.depth != 2u && d.depth != 4u && d.depth != 8u && d.depth != 16u) || interlace > 1u) { - snprintf(err, TOOL_PNG_ERROR_BYTES, "unsupported PNG header (%ux%u, depth %u, colour type %u)", d.width, d.height, d.depth, d.color); + snprintf(err, TOOL_PNG_ERROR_BYTES, "unsupported PNG header (%ux%u, depth %u, color type %u)", d.width, d.height, d.depth, d.color); free(idat); return false; } @@ -400,30 +400,30 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO bool toolPngEncode(const ToolImageT *img, uint8_t **out, size_t *outLen) { - uint8_t *raw; - uint8_t *z; - uint8_t *buf; - uint8_t *trial; - uint8_t ihdr[IHDR_BYTES]; - uint8_t plte[TOOL_PNG_PALETTE_MAX * 3u]; - uint8_t trns[TOOL_PNG_PALETTE_MAX]; - size_t len; - size_t cap; - size_t zLen; - size_t bpp; - size_t rowBytes; - size_t i; - uint32_t y; - uint32_t cost; - uint32_t best; - uint16_t trnsCount; - uint8_t f; - uint8_t bestFilter; - uint8_t left; - uint8_t up; - uint8_t upLeft; - uint8_t v; - bool ok; + uint8_t *raw; + uint8_t *z; + uint8_t *buf; + uint8_t *trial; + uint8_t ihdr[IHDR_BYTES]; + uint8_t plte[TOOL_PNG_PALETTE_MAX * 3u]; + uint8_t trns[TOOL_PNG_PALETTE_MAX]; + size_t len; + size_t cap; + size_t zLen; + size_t bpp; + size_t rowBytes; + size_t i; + uint32_t y; + uint32_t cost; + uint32_t best; + uint16_t trnsCount; + uint8_t f; + uint8_t bestFilter; + uint8_t left; + uint8_t up; + uint8_t upLeft; + uint8_t v; + bool ok; const uint8_t *row; const uint8_t *prior; @@ -488,10 +488,10 @@ bool toolPngEncode(const ToolImageT *img, uint8_t **out, size_t *outLen) { ihdr[10] = 0u; ihdr[11] = 0u; ihdr[12] = 0u; - buf = NULL; - len = 0; - cap = 0; - ok = appendBytes(&buf, &len, &cap, kSignature, PNG_SIGNATURE_BYTES) && appendChunk(&buf, &len, &cap, "IHDR", ihdr, IHDR_BYTES); + buf = NULL; + len = 0; + cap = 0; + ok = appendBytes(&buf, &len, &cap, kSignature, PNG_SIGNATURE_BYTES) && appendChunk(&buf, &len, &cap, "IHDR", ihdr, IHDR_BYTES); if (ok && img->indexed) { trnsCount = 0u; for (i = 0; i < img->paletteCount; i++) { diff --git a/tools/common/toolPng.h b/tools/common/toolPng.h index 5d08e82..ab95a76 100644 --- a/tools/common/toolPng.h +++ b/tools/common/toolPng.h @@ -2,7 +2,7 @@ // See LICENSE.md for license information. // SPDX-License-Identifier: MIT // -// toolPng.h - PNG for the JoeyLib host tools: decode any PNG (every colour +// toolPng.h - PNG for the JoeyLib host tools: decode any PNG (every color // type and bit depth, Adam7, tRNS) to an indexed or RGBA image, and encode // an indexed or RGBA image. Self-contained C99 over toolZlib. @@ -16,10 +16,10 @@ #define TOOL_PNG_PALETTE_MAX 256u #define TOOL_PNG_ERROR_BYTES 160u -// An image: indexed (a palette-colour PNG, one byte a pixel, palette entries +// An image: indexed (a palette-color PNG, one byte a pixel, palette entries // RGBA with tRNS's alpha) or RGBA (four bytes a pixel, everything else, -// converted: grey spread to RGB, 16-bit samples cut to their high byte, a -// tRNS colour made transparent). +// converted: gray spread to RGB, 16-bit samples cut to their high byte, a +// tRNS color made transparent). typedef struct ToolImageT { uint32_t width; uint32_t height; @@ -43,8 +43,8 @@ bool toolPngDecode(const uint8_t *data, size_t len, ToolImageT *img, char err[TO bool toolPngLoad(const char *path, ToolImageT *img, char err[TOOL_PNG_ERROR_BYTES]); // Encode an image as PNG into a malloc'd buffer, or write it to a file: -// indexed as colour type 3 (8-bit, a tRNS when any entry is not opaque), -// RGBA as colour type 6. +// indexed as color type 3 (8-bit, a tRNS when any entry is not opaque), +// RGBA as color type 6. bool toolPngEncode(const ToolImageT *img, uint8_t **out, size_t *outLen); bool toolPngSave(const char *path, const ToolImageT *img); diff --git a/tools/common/toolXml.c b/tools/common/toolXml.c index c685031..ed590a0 100644 --- a/tools/common/toolXml.c +++ b/tools/common/toolXml.c @@ -159,7 +159,7 @@ static ToolXmlT *element(ParserT *p) { p->pos++; continue; } else { - // Attribute-value normalisation: a literal tab or line end + // Attribute-value normalization: a literal tab or line end // is a space. toolBufByte(&value, (c == '\t' || c == '\n' || c == '\r') ? ' ' : (uint8_t)c); } diff --git a/tools/common/toolZlib.c b/tools/common/toolZlib.c index e6bb179..ff5f7d8 100644 --- a/tools/common/toolZlib.c +++ b/tools/common/toolZlib.c @@ -74,10 +74,10 @@ typedef struct DeflateT { bool error; } DeflateT; -static const uint16_t kLengthBase[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; -static const uint8_t kLengthExtra[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; -static const uint16_t kDistBase[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; -static const uint8_t kDistExtra[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; +static const uint16_t kLengthBase[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; +static const uint8_t kLengthExtra[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; +static const uint16_t kDistBase[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; +static const uint8_t kDistExtra[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const uint8_t kCodeLenOrder[MAX_CODE_LEN_CODES] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void deflateBits(DeflateT *d, uint32_t value, uint32_t count); diff --git a/tools/iconBake/iconBake.c b/tools/iconBake/iconBake.c index 9799c36..8c114ed 100644 --- a/tools/iconBake/iconBake.c +++ b/tools/iconBake/iconBake.c @@ -6,10 +6,10 @@ // shows programs by an icon. // // The picture is a PNG, best square, with transparency; it is fitted into -// each icon's size keeping its shape, and centred. A picture of 64 pixels or +// each icon's size keeping its shape, and centered. A picture of 64 pixels or // less is taken for pixel art and enlarged by whole pixels; anything else is -// resampled smoothly (Lanczos, three lobes, on colour weighted by its -// opacity, in fixed point as Pillow does it). Where an icon has few colours +// resampled smoothly (Lanczos, three lobes, on color weighted by its +// opacity, in fixed point as Pillow does it). Where an icon has few colors // it takes the nearest (the 8-bit Windows icon dithers to its own 256, cut // from the picture's by median cut), and a pixel at least half transparent // is transparent. The formats each system takes are told at their bake @@ -56,9 +56,9 @@ #define BLEND_HALF 0x80u #define DIV255_SHIFT 8u -// The nearest colour is looked up by the RGB cell a colour falls in, 6 bits +// The nearest color is looked up by the RGB cell a color falls in, 6 bits // a channel, measured from the cell's low corner (as Pillow's palette cache -// does it, so the same colours are chosen). +// does it, so the same colors are chosen). #define CELL_SHIFT 2u #define CELL_SIDE 64u #define CELL_LOW_MASK 0xFCu @@ -69,42 +69,42 @@ // 1/16 below right. #define FS_DIVISOR 16 -// Median cut: the 8-bit Windows icon's 256 colours are black (behind the +// Median cut: the 8-bit Windows icon's 256 colors are black (behind the // AND mask) and up to 255 of the picture's own. -#define MEDIAN_COLOURS 255u +#define MEDIAN_COLORS 255u #define RGB_RED_SHIFT 16u #define RGB_GREEN_SHIFT 8u #define KEY_SHIFT 24u -// Windows: the sizes Win32 icons come in, and the 16 colours of a Windows +// Windows: the sizes Win32 icons come in, and the 16 colors of a Windows // 3.x icon (the VGA's, in Windows' order). #define ICO_SIZE_COUNT 3u #define ICO16_SIZE 32u -#define ICO16_COLOURS 16u +#define ICO16_COLORS 16u #define ICO_TYPE 1u #define DIB_HEADER_BYTES 40u #define ICONDIR_BYTES 6u #define ICONDIRENTRY_BYTES 16u -#define ICO_TRUE_COLOUR_BITS 32u +#define ICO_TRUE_COLOR_BITS 32u #define ICO_256_BITS 8u #define ICO_16_BITS 4u #define DIB_ROW_ALIGN 4u #define ENTRY_SIZE_WRAP 256u -// DVX: its icons' size and the grey they sit on (its own apps'); the +// DVX: its icons' size and the gray they sit on (its own apps'); the // 24-bit BMP DVX reads, at 96 dots an inch. #define DVX_SIZE 32u #define BMP_FILE_HEADER_BYTES 14u #define BMP_BITS 24u -#define BMP_PIXELS_PER_METRE 3780u +#define BMP_PIXELS_PER_METER 3780u // The Amiga: a Workbench tool icon (struct DiskObject, workbench.h) of // AMIGA_W x AMIGA_H Workbench pixels, which are twice as tall as wide, in -// the four colours Workbench 2.0 on has (grey, black, white, blue). +// the four colors Workbench 2.0 on has (gray, black, white, blue). #define AMIGA_W 48u #define AMIGA_H 24u #define AMIGA_DEPTH 2u -#define AMIGA_COLOURS 4u +#define AMIGA_COLORS 4u #define AMIGA_WORD_PIXELS 16u #define AMIGA_WORD_LEFT_BIT 0x8000u #define WB_DISKMAGIC 0xE310u @@ -125,7 +125,7 @@ #define BE_TRANSPARENT 255u // Haiku's vector icon (HVIF, src/libs/icon/flat_icon in Haiku): the 16x16 -// and 32x32 icons' pixels as outlines on its 64-unit grid, a shape a colour, +// and 32x32 icons' pixels as outlines on its 64-unit grid, a shape a color, // hinted so every edge falls on a pixel. The 16x16 shapes show below 32 // pixels and the 32x32 ones from there up (scale 0.5 is 32 pixels; a level // of detail byte is 63.75 to 1), so each size draws exactly its own pixels. @@ -169,13 +169,13 @@ // (Toolbox Reference volume 2, QuickDraw II Auxiliary): iconType, iconSize // (the image's bytes), iconHeight, iconWidth, the image at 4 bits a pixel // (rows of whole bytes, the even pixel in the high nibble), then a mask of -// the same shape, non-zero where the icon is opaque. They are colour icons -// (iconType bit 15), whose colours the Finder leaves alone. The Finder runs +// the same shape, non-zero where the icon is opaque. They are color icons +// (iconType bit 15), whose colors the Finder leaves alone. The Finder runs // in 640 mode, where QuickDraw draws each 4-bit icon pixel as two screen // pixels, its high two bits then its low two, and in the Finder's palette // the first of the pair is black, red, green, or white and the second // black, blue, yellow, or white (kIigsFirst, kIigsSecond: measured in MAME, -// an icon at an even screen x), so an icon pixel is one of 16 colours a +// an icon at an even screen x), so an icon pixel is one of 16 colors a // pair makes, seen as their mean. An icon pixel is some 1.2 times as tall // as wide; the big icon is the size of the Finder's own (its folder is 24 // pixels wide). @@ -191,7 +191,7 @@ #define IIGS_BLACK 0x0u #define IIGS_WHITE 0xFu #define IIGS_OPAQUE 0xFu -#define IIGS_ICON_TYPE_COLOUR 0x8000u +#define IIGS_ICON_TYPE_COLOR 0x8000u #define IIGS_ICON_FILE_ID 0x0001u #define IIGS_APPLICATION_TYPE 0x00B3u #define IIGS_FILE_NAME_BYTES 16u @@ -199,12 +199,12 @@ #define IIGS_NAME_BYTES 16u #define IIGS_LENGTH_WORD_BYTES 2u #define ASCII_MAX 0x7Fu -// A pixel with less colour than this (CIELAB chroma) may be shown in a -// grey; a more colourful one keeps a colour, or black or white: brown fur -// nearest a grey by lightness reads better in the nearest colour. -#define IIGS_GREY_CHROMA 12.0 +// A pixel with less color than this (CIELAB chroma) may be shown in a +// gray; a more colorful one keeps a color, or black or white: brown fur +// nearest a gray by lightness reads better in the nearest color. +#define IIGS_GRAY_CHROMA 12.0 // CIE94 (graphic arts): how much a difference in chroma and in hue counts, -// against the colour's own chroma. +// against the color's own chroma. #define CIE94_CHROMA 0.045 #define CIE94_HUE 0.015 // sRGB's transfer curve, and CIELAB's white point (D65) and curve. @@ -233,13 +233,13 @@ typedef struct TapsT { int32_t *weights; } TapsT; -// A palette of 256 colours and, by RGB cell, the nearest entry found yet. +// A palette of 256 colors and, by RGB cell, the nearest entry found yet. typedef struct PaletteT { uint8_t rgb[PALETTE_SIZE][RGB_BYTES]; int16_t *cache; } PaletteT; -// One of a picture's colours (0xRRGGBB), how many pixels have it, and what +// One of a picture's colors (0xRRGGBB), how many pixels have it, and what // a median cut box sorts it by. typedef struct ShadeT { uint32_t key; @@ -256,7 +256,7 @@ typedef struct BoxT { uint32_t range; } BoxT; -// A colour in CIELAB. +// A color in CIELAB. typedef struct LabT { double l; double a; @@ -297,7 +297,7 @@ static void bakeIigs(const ToolImageT *picture, char **args); static void bakeInfo(const ToolImageT *picture, char **args); static void bakePng(const ToolImageT *picture, char **args); static void boxMeasure(BoxT *box, const ShadeT *shades); -static double chroma(LabT colour); +static double chroma(LabT color); static double cie94(LabT reference, LabT sample); static uint8_t clampSample(int32_t value); static void ditherFloydSteinberg(const ToolImageT *flat, PaletteT *palette, uint8_t *indices); @@ -305,10 +305,10 @@ static void enlargeWhole(const ToolImageT *src, uint32_t scale, ToolImageT * static void fit(const ToolImageT *picture, uint32_t w, uint32_t h, ToolImageT *out); static void flatten(const ToolImageT *image, const uint8_t background[RGB_BYTES], ToolImageT *flat); static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out); -static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t colour, uint32_t units, ToolBufT *paths, uint32_t *pathCount); -static void icoFile(const uint32_t *sides, const uint32_t *colourCounts, const uint32_t *bitCounts, const ToolBufT *images, uint32_t count, const char *path); -static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t (*colours)[RGB_BYTES], uint32_t colourCount, ToolBufT *out); -static void iigsColours(LabT colours[IIGS_VALUES], uint32_t keeps[IIGS_VALUES], uint32_t *keepCount); +static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t color, uint32_t units, ToolBufT *paths, uint32_t *pathCount); +static void icoFile(const uint32_t *sides, const uint32_t *colorCounts, const uint32_t *bitCounts, const ToolBufT *images, uint32_t count, const char *path); +static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t (*colors)[RGB_BYTES], uint32_t colorCount, ToolBufT *out); +static void iigsColors(LabT colors[IIGS_VALUES], uint32_t keeps[IIGS_VALUES], uint32_t *keepCount); static void iigsIcon(const ToolImageT *picture, uint32_t w, uint32_t h, ToolBufT *out); static void imageCopy(const ToolImageT *src, ToolImageT *dst); static void imageNew(ToolImageT *img, uint32_t w, uint32_t h); @@ -316,11 +316,11 @@ static LabT lab(const double rgb[RGB_BYTES]); static double lanczos(double x); static double linear(double value); static void medianCut(const ToolImageT *image, uint8_t palette[PALETTE_SIZE][RGB_BYTES]); -static uint8_t nearestColour(PaletteT *palette, uint32_t r, uint32_t g, uint32_t b); +static uint8_t nearestColor(PaletteT *palette, uint32_t r, uint32_t g, uint32_t b); static bool opaque(const ToolImageT *image, size_t pixel); static int packedCompare(const void *a, const void *b); static void paletteFree(PaletteT *palette); -static void paletteInit(PaletteT *palette, const uint8_t (*colours)[RGB_BYTES], uint32_t count); +static void paletteInit(PaletteT *palette, const uint8_t (*colors)[RGB_BYTES], uint32_t count); static uint32_t parseSide(const char *text, size_t len, const char *what); static void pascal(ToolBufT *out, const char *text, uint32_t size); static void premultiply(const ToolImageT *src, ToolImageT *dst); @@ -330,7 +330,7 @@ static int shadeCompare(const void *a, const void *b); static double sinc(double x); static void tapsBuild(TapsT *taps, uint32_t inSize, uint32_t outSize); static void tapsFree(TapsT *taps); -static void toColours(const ToolImageT *image, const uint8_t (*colours)[RGB_BYTES], uint32_t count, bool dither, const uint8_t background[RGB_BYTES], uint8_t *indices); +static void toColors(const ToolImageT *image, const uint8_t (*colors)[RGB_BYTES], uint32_t count, bool dither, const uint8_t background[RGB_BYTES], uint8_t *indices); static void unpremultiply(const ToolImageT *src, ToolImageT *dst); static void usage(void); static void writeOut(const char *path, const uint8_t *data, size_t len); @@ -349,7 +349,7 @@ static const BakeT kBakes[] = { static const uint32_t kIcoSizes[ICO_SIZE_COUNT] = { 16u, 32u, 48u }; -static const uint8_t kVga16[ICO16_COLOURS][RGB_BYTES] = { +static const uint8_t kVga16[ICO16_COLORS][RGB_BYTES] = { { 0, 0, 0 }, { 128, 0, 0 }, { 0, 128, 0 }, { 128, 128, 0 }, { 0, 0, 128 }, { 128, 0, 128 }, { 0, 128, 128 }, { 192, 192, 192 }, { 128, 128, 128 }, { 255, 0, 0 }, { 0, 255, 0 }, { 255, 255, 0 }, @@ -358,9 +358,9 @@ static const uint8_t kVga16[ICO16_COLOURS][RGB_BYTES] = { static const uint8_t kBlack[RGB_BYTES] = { 0, 0, 0 }; static const uint8_t kWhite[RGB_BYTES] = { 255, 255, 255 }; -static const uint8_t kDvxGrey[RGB_BYTES] = { 192, 192, 192 }; +static const uint8_t kDvxGray[RGB_BYTES] = { 192, 192, 192 }; -static const uint8_t kAmigaColours[AMIGA_COLOURS][RGB_BYTES] = { +static const uint8_t kAmigaColors[AMIGA_COLORS][RGB_BYTES] = { { 170, 170, 170 }, { 0, 0, 0 }, { 255, 255, 255 }, { 102, 136, 187 } }; @@ -379,15 +379,15 @@ static const double kLabWhite[RGB_BYTES] = { 0.95047, 1.0, 1.08883 static const char *const kUsage[] = { "usage:", " iconBake ico PICTURE OUT.ico Windows 95 on (Win32): 16, 32 and", - " 48 pixels, each in 256 colours and", + " 48 pixels, each in 256 colors and", " in 32-bit", " iconBake ico16 PICTURE OUT.ico Windows 3.x (Win16): 32x32 in the 16", - " VGA colours", - " iconBake bmp PICTURE OUT.bmp DVX: 32x32, 24-bit, on DVX's grey", + " VGA colors", + " iconBake bmp PICTURE OUT.bmp DVX: 32x32, 24-bit, on DVX's gray", " iconBake png PICTURE OUT.png WxH a picture WxH (the PSP's 144x80", " ICON0, a web page's icon)", " iconBake info PICTURE OUT.info STACK the Amiga's Workbench: a tool's icon", - " in its four colours, STACK bytes of", + " in its four colors, STACK bytes of", " stack when started from it", " iconBake be PICTURE OUT32 OUT16 OUT.hvif", " BeOS and Haiku: 32x32 and 16x16 in", @@ -401,7 +401,7 @@ static const char *const kUsage[] = { " twice that for Retina screens", " iconBake iigs PICTURE OUT NAME the Apple IIgs Finder: an icon file", " ($CA) giving the program NAME its", - " icons, in the Finder's 16 colours", + " icons, in the Finder's 16 colors", "", "PICTURE is a PNG, best square, with transparency. One of 64 pixels or less", "is pixel art, enlarged by whole pixels; anything else is resampled smoothly." @@ -490,7 +490,7 @@ static void bakeBe(const ToolImageT *picture, char **args) { if (pixels[s] == NULL) { toolFail("out of memory"); } - toColours(&image, kBePalette, BE_TRANSPARENT, false, kBlack, pixels[s]); + toColors(&image, kBePalette, BE_TRANSPARENT, false, kBlack, pixels[s]); for (i = 0; i < count; i++) { if (!opaque(&image, i)) { pixels[s][i] = BE_TRANSPARENT; @@ -515,7 +515,7 @@ static void bakeBe(const ToolImageT *picture, char **args) { } -// DVX: 32x32, 24-bit, on DVX's grey, as a BMP. +// DVX: 32x32, 24-bit, on DVX's gray, as a BMP. static void bakeBmp(const ToolImageT *picture, char **args) { ToolImageT image; ToolImageT flat; @@ -526,7 +526,7 @@ static void bakeBmp(const ToolImageT *picture, char **args) { const uint8_t *p; fit(picture, DVX_SIZE, DVX_SIZE, &image); - flatten(&image, kDvxGrey, &flat); + flatten(&image, kDvxGray, &flat); stride = (DVX_SIZE * RGB_BYTES + DIB_ROW_ALIGN - 1u) / DIB_ROW_ALIGN * DIB_ROW_ALIGN; memset(&out, 0, sizeof(out)); toolBufAdd(&out, "BM", 2u); @@ -540,8 +540,8 @@ static void bakeBmp(const ToolImageT *picture, char **args) { toolBufLe16(&out, BMP_BITS); toolBufLe32(&out, 0u); toolBufLe32(&out, stride * DVX_SIZE); - toolBufLe32(&out, BMP_PIXELS_PER_METRE); - toolBufLe32(&out, BMP_PIXELS_PER_METRE); + toolBufLe32(&out, BMP_PIXELS_PER_METER); + toolBufLe32(&out, BMP_PIXELS_PER_METER); toolBufLe32(&out, 0u); toolBufLe32(&out, 0u); for (y = DVX_SIZE; y-- > 0u;) { @@ -647,13 +647,13 @@ static void bakeIcns(const ToolImageT *picture, char **args) { } -// Windows 95 on: 16, 32 and 48 pixels, each in 256 colours (its own, by +// Windows 95 on: 16, 32 and 48 pixels, each in 256 colors (its own, by // median cut) and in 32-bit. static void bakeIco(const ToolImageT *picture, char **args) { ToolImageT image; ToolBufT images[ICO_SIZE_COUNT * 2u]; uint32_t sides[ICO_SIZE_COUNT * 2u]; - uint32_t colourCounts[ICO_SIZE_COUNT * 2u]; + uint32_t colorCounts[ICO_SIZE_COUNT * 2u]; uint32_t bitCounts[ICO_SIZE_COUNT * 2u]; uint8_t palette[PALETTE_SIZE][RGB_BYTES]; uint32_t s; @@ -665,33 +665,33 @@ static void bakeIco(const ToolImageT *picture, char **args) { medianCut(&image, palette); for (i = s * 2u; i < s * 2u + 2u; i++) { sides[i] = kIcoSizes[s]; - colourCounts[i] = 0u; + colorCounts[i] = 0u; } bitCounts[s * 2u] = ICO_256_BITS; - bitCounts[s * 2u + 1u] = ICO_TRUE_COLOUR_BITS; + bitCounts[s * 2u + 1u] = ICO_TRUE_COLOR_BITS; icoImage(&image, ICO_256_BITS, (const uint8_t (*)[RGB_BYTES])palette, PALETTE_SIZE, &images[s * 2u]); - icoImage(&image, ICO_TRUE_COLOUR_BITS, NULL, 0u, &images[s * 2u + 1u]); + icoImage(&image, ICO_TRUE_COLOR_BITS, NULL, 0u, &images[s * 2u + 1u]); toolImageFree(&image); } - icoFile(sides, colourCounts, bitCounts, images, ICO_SIZE_COUNT * 2u, args[0]); + icoFile(sides, colorCounts, bitCounts, images, ICO_SIZE_COUNT * 2u, args[0]); for (i = 0; i < ICO_SIZE_COUNT * 2u; i++) { toolBufFree(&images[i]); } } -// Windows 3.x: 32x32 in the 16 VGA colours. +// Windows 3.x: 32x32 in the 16 VGA colors. static void bakeIco16(const ToolImageT *picture, char **args) { ToolImageT image; ToolBufT data; const uint32_t side = ICO16_SIZE; - const uint32_t colourCount = ICO16_COLOURS; + const uint32_t colorCount = ICO16_COLORS; const uint32_t bitCount = ICO_16_BITS; fit(picture, ICO16_SIZE, ICO16_SIZE, &image); memset(&data, 0, sizeof(data)); - icoImage(&image, ICO_16_BITS, kVga16, ICO16_COLOURS, &data); - icoFile(&side, &colourCount, &bitCount, &data, 1u, args[0]); + icoImage(&image, ICO_16_BITS, kVga16, ICO16_COLORS, &data); + icoFile(&side, &colorCount, &bitCount, &data, 1u, args[0]); toolBufFree(&data); toolImageFree(&image); } @@ -734,8 +734,8 @@ static void bakeIigs(const ToolImageT *picture, char **args) { // The Amiga: a WBTOOL DiskObject: the header, its gadget, the image (struct -// Image and its planes, colour 0 where transparent) and nothing more. -// Workbench's grey is the background alone: the picture is drawn in the +// Image and its planes, color 0 where transparent) and nothing more. +// Workbench's gray is the background alone: the picture is drawn in the // other three. static void bakeInfo(const ToolImageT *picture, char **args) { ToolImageT tall; @@ -756,7 +756,7 @@ static void bakeInfo(const ToolImageT *picture, char **args) { } fit(picture, AMIGA_W, AMIGA_H * 2u, &tall); resizeLanczos(&tall, AMIGA_W, AMIGA_H, &image); - toColours(&image, kAmigaColours + 1, AMIGA_COLOURS - 1u, false, kWhite, indices); + toColors(&image, kAmigaColors + 1, AMIGA_COLORS - 1u, false, kWhite, indices); for (i = 0; i < (size_t)AMIGA_W * AMIGA_H; i++) { indices[i] = opaque(&image, i) ? (uint8_t)(indices[i] + 1u) : 0u; } @@ -884,12 +884,12 @@ static void boxMeasure(BoxT *box, const ShadeT *shades) { } -static double chroma(LabT colour) { - return hypot(colour.a, colour.b); +static double chroma(LabT color) { + return hypot(color.a, color.b); } -// CIE94's colour difference of sample from reference. +// CIE94's color difference of sample from reference. static double cie94(LabT reference, LabT sample) { double c1; double dc; @@ -965,7 +965,7 @@ static void ditherFloydSteinberg(const ToolImageT *flat, PaletteT *palette, uint value[c] = (int32_t)SAMPLE_MAX; } } - index = nearestColour(palette, (uint32_t)value[0], (uint32_t)value[1], (uint32_t)value[2]); + index = nearestColor(palette, (uint32_t)value[0], (uint32_t)value[1], (uint32_t)value[2]); indices[(size_t)y * flat->width + x] = index; for (c = 0; c < RGB_BYTES; c++) { self = value[c] - (int32_t)palette->rgb[index][c]; @@ -999,9 +999,9 @@ static void enlargeWhole(const ToolImageT *src, uint32_t scale, ToolImageT *dst) } -// picture fitted into w x h, its shape kept, centred on transparency. Its +// picture fitted into w x h, its shape kept, centered on transparency. Its // sized copy never outgrows w x h (the scale is the smaller ratio, rounded -// to the nearest pixel), so the centring offsets are not negative. +// to the nearest pixel), so the centering offsets are not negative. static void fit(const ToolImageT *picture, uint32_t w, uint32_t h, ToolImageT *out) { double scale; double sw; @@ -1031,7 +1031,7 @@ static void fit(const ToolImageT *picture, uint32_t w, uint32_t h, ToolImageT *o } -// image over an opaque background colour. +// image over an opaque background color. static void flatten(const ToolImageT *image, const uint8_t background[RGB_BYTES], ToolImageT *flat) { size_t i; size_t pixels; @@ -1046,8 +1046,8 @@ static void flatten(const ToolImageT *image, const uint8_t background[RGB_BYTES] } -// An HVIF of the palette pictures in sizes: each colour's outlines a closed -// path each, a shape a colour and size, hinted, and shown only at the +// An HVIF of the palette pictures in sizes: each color's outlines a closed +// path each, a shape a color and size, hinted, and shown only at the // scales [lodMin, lodMax]. False, out untouched, when that is more of // anything than HVIF holds. static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out) { @@ -1059,7 +1059,7 @@ static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out) { uint32_t first; uint32_t rgb; uint32_t s; - uint32_t colour; + uint32_t color; uint32_t i; bool present[PALETTE_SIZE]; bool ok; @@ -1077,11 +1077,11 @@ static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out) { for (i = 0; i < sizes[s].size * sizes[s].size; i++) { present[sizes[s].pixels[i]] = true; } - for (colour = 0; colour < BE_TRANSPARENT && ok; colour++) { - if (!present[colour]) { + for (color = 0; color < BE_TRANSPARENT && ok; color++) { + if (!present[color]) { continue; } - rgb = ((uint32_t)kBePalette[colour][0] << RGB_RED_SHIFT) | ((uint32_t)kBePalette[colour][1] << RGB_GREEN_SHIFT) | kBePalette[colour][2]; + rgb = ((uint32_t)kBePalette[color][0] << RGB_RED_SHIFT) | ((uint32_t)kBePalette[color][1] << RGB_GREEN_SHIFT) | kBePalette[color][2]; for (style = 0; style < styleCount && styles[style] != rgb; style++) { } if (style == styleCount) { @@ -1092,7 +1092,7 @@ static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out) { styles[styleCount++] = rgb; } first = pathCount; - ok = hvifContours(sizes[s].pixels, sizes[s].size, (uint8_t)colour, HVIF_GRID / sizes[s].size, &paths, &pathCount) && pathCount <= HVIF_MAX_ITEMS && shapeCount < HVIF_MAX_ITEMS; + ok = hvifContours(sizes[s].pixels, sizes[s].size, (uint8_t)color, HVIF_GRID / sizes[s].size, &paths, &pathCount) && pathCount <= HVIF_MAX_ITEMS && shapeCount < HVIF_MAX_ITEMS; if (!ok) { break; } @@ -1129,15 +1129,15 @@ static bool hvif(const HvifSizeT *sizes, uint32_t sizeCount, ToolBufT *out) { } -// The outlines of colour's pixels in the size x size palette pixels, each +// The outlines of color's pixels in the size x size palette pixels, each // appended to paths as a closed HVIF path of its corner points: every pixel -// side with another colour (or the edge) beyond it, directed so the colour +// side with another color (or the edge) beyond it, directed so the color // is on its right, chained head to tail. Outer edges come out clockwise and // holes the other way, so a fill under either rule leaves the holes empty. // Loops start at the first corner an edge was found from, and where two // edges leave a corner the later found is taken first. False when a loop // has more corners than a path holds. -static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t colour, uint32_t units, ToolBufT *paths, uint32_t *pathCount) { +static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t color, uint32_t units, ToolBufT *paths, uint32_t *pathCount) { static const int32_t dx[HVIF_SIDES_PER_PIXEL][2] = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 0, 0 } }; static const int32_t dy[HVIF_SIDES_PER_PIXEL][2] = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } }; static const int32_t nx[HVIF_SIDES_PER_PIXEL] = { 0, 1, 0, -1 }; @@ -1178,13 +1178,13 @@ static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t colour, u orderCount = 0u; for (y = 0; y < (int32_t)size; y++) { for (x = 0; x < (int32_t)size; x++) { - if (pixels[(uint32_t)y * size + (uint32_t)x] != colour) { + if (pixels[(uint32_t)y * size + (uint32_t)x] != color) { continue; } for (k = 0; k < HVIF_SIDES_PER_PIXEL; k++) { ax = x + nx[k]; ay = y + ny[k]; - if (ax >= 0 && ay >= 0 && ax < (int32_t)size && ay < (int32_t)size && pixels[(uint32_t)ay * size + (uint32_t)ax] == colour) { + if (ax >= 0 && ay >= 0 && ax < (int32_t)size && ay < (int32_t)size && pixels[(uint32_t)ay * size + (uint32_t)ax] == color) { continue; } from = (uint32_t)(y + dy[k][0]) * side + (uint32_t)(x + dx[k][0]); @@ -1251,8 +1251,8 @@ static bool hvifContours(const uint8_t *pixels, uint32_t size, uint8_t colour, u } -// images (side, colour count, bit count, data) into a .ico. -static void icoFile(const uint32_t *sides, const uint32_t *colourCounts, const uint32_t *bitCounts, const ToolBufT *images, uint32_t count, const char *path) { +// images (side, color count, bit count, data) into a .ico. +static void icoFile(const uint32_t *sides, const uint32_t *colorCounts, const uint32_t *bitCounts, const ToolBufT *images, uint32_t count, const char *path) { ToolBufT out; uint32_t offset; uint32_t i; @@ -1265,7 +1265,7 @@ static void icoFile(const uint32_t *sides, const uint32_t *colourCounts, const u for (i = 0; i < count; i++) { toolBufByte(&out, sides[i] % ENTRY_SIZE_WRAP); toolBufByte(&out, sides[i] % ENTRY_SIZE_WRAP); - toolBufByte(&out, colourCounts[i] % ENTRY_SIZE_WRAP); + toolBufByte(&out, colorCounts[i] % ENTRY_SIZE_WRAP); toolBufByte(&out, 0u); toolBufLe16(&out, 1u); toolBufLe16(&out, bitCounts[i]); @@ -1281,11 +1281,11 @@ static void icoFile(const uint32_t *sides, const uint32_t *colourCounts, const u } -// One icon image: its BITMAPINFOHEADER, colour table, XOR bits and AND +// One icon image: its BITMAPINFOHEADER, color table, XOR bits and AND // mask. bitCount 32: BGRA, black where wholly transparent (a system that -// draws through the AND mask XORs this in); 4 or 8: indices into colours, +// draws through the AND mask XORs this in); 4 or 8: indices into colors, // index 0 (black) where transparent. -static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t (*colours)[RGB_BYTES], uint32_t colourCount, ToolBufT *out) { +static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t (*colors)[RGB_BYTES], uint32_t colorCount, ToolBufT *out) { ToolBufT xorBits; ToolBufT andBits; uint8_t *indices; @@ -1298,7 +1298,7 @@ static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t ( memset(&xorBits, 0, sizeof(xorBits)); memset(&andBits, 0, sizeof(andBits)); - if (bitCount == ICO_TRUE_COLOUR_BITS) { + if (bitCount == ICO_TRUE_COLOR_BITS) { for (y = image->height; y-- > 0u;) { for (x = 0; x < image->width; x++) { p = image->pixels + ((size_t)y * image->width + x) * RGBA_BYTES; @@ -1317,7 +1317,7 @@ static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t ( if (indices == NULL) { toolFail("out of memory"); } - toColours(image, colours, colourCount, bitCount == ICO_256_BITS, kBlack, indices); + toColors(image, colors, colorCount, bitCount == ICO_256_BITS, kBlack, indices); for (y = image->height; y-- > 0u;) { rowStart = xorBits.len; for (x = 0; x < image->width; x += (bitCount == ICO_16_BITS) ? 2u : 1u) { @@ -1345,12 +1345,12 @@ static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t ( toolBufLe32(out, (uint32_t)(xorBits.len + andBits.len)); toolBufLe32(out, 0u); toolBufLe32(out, 0u); - toolBufLe32(out, bitCount <= ICO_256_BITS ? colourCount : 0u); + toolBufLe32(out, bitCount <= ICO_256_BITS ? colorCount : 0u); toolBufLe32(out, 0u); - for (c = 0; c < colourCount && bitCount <= ICO_256_BITS; c++) { - toolBufByte(out, colours[c][2]); - toolBufByte(out, colours[c][1]); - toolBufByte(out, colours[c][0]); + for (c = 0; c < colorCount && bitCount <= ICO_256_BITS; c++) { + toolBufByte(out, colors[c][2]); + toolBufByte(out, colors[c][1]); + toolBufByte(out, colors[c][0]); toolBufByte(out, 0u); } toolBufAdd(out, xorBits.data, xorBits.len); @@ -1360,9 +1360,9 @@ static void icoImage(const ToolImageT *image, uint32_t bitCount, const uint8_t ( } -// The 16 colours of a Finder icon pixel, each the mean of its pair, in -// CIELAB; and which of them a colourful pixel may take. -static void iigsColours(LabT colours[IIGS_VALUES], uint32_t keeps[IIGS_VALUES], uint32_t *keepCount) { +// The 16 colors of a Finder icon pixel, each the mean of its pair, in +// CIELAB; and which of them a colorful pixel may take. +static void iigsColors(LabT colors[IIGS_VALUES], uint32_t keeps[IIGS_VALUES], uint32_t *keepCount) { double mean[RGB_BYTES]; uint32_t value; uint32_t c; @@ -1372,21 +1372,21 @@ static void iigsColours(LabT colours[IIGS_VALUES], uint32_t keeps[IIGS_VALUES], for (c = 0; c < RGB_BYTES; c++) { mean[c] = ((double)kIigsFirst[value >> IIGS_HALF_BITS][c] + (double)kIigsSecond[value & IIGS_HALF_MASK][c]) / 2.0; } - colours[value] = lab(mean); - if (value == IIGS_BLACK || value == IIGS_WHITE || chroma(colours[value]) >= IIGS_GREY_CHROMA) { + colors[value] = lab(mean); + if (value == IIGS_BLACK || value == IIGS_WHITE || chroma(colors[value]) >= IIGS_GRAY_CHROMA) { keeps[(*keepCount)++] = value; } } } -// A QuickDraw II colour icon of the picture at w x h icon pixels: black on -// its edge, within it the nearest of the Finder's 16 colours. +// A QuickDraw II color icon of the picture at w x h icon pixels: black on +// its edge, within it the nearest of the Finder's 16 colors. static void iigsIcon(const ToolImageT *picture, uint32_t w, uint32_t h, ToolBufT *out) { static const int32_t nx[] = { 1, -1, 0, 0 }; static const int32_t ny[] = { 0, 0, 1, -1 }; - LabT colours[IIGS_VALUES]; - LabT colour; + LabT colors[IIGS_VALUES]; + LabT color; uint32_t keeps[IIGS_VALUES]; uint32_t all[IIGS_VALUES]; uint32_t keepCount; @@ -1414,7 +1414,7 @@ static void iigsIcon(const ToolImageT *picture, uint32_t w, uint32_t h, ToolBufT fit(picture, w, (uint32_t)nearbyint((double)h * IIGS_PIXEL_ASPECT), &tall); resizeLanczos(&tall, w, h, &image); - iigsColours(colours, keeps, &keepCount); + iigsColors(colors, keeps, &keepCount); for (v = 0; v < IIGS_VALUES; v++) { all[v] = v; } @@ -1444,13 +1444,13 @@ static void iigsIcon(const ToolImageT *picture, uint32_t w, uint32_t h, ToolBufT for (v = 0; v < RGB_BYTES; v++) { rgb[v] = p[v]; } - colour = lab(rgb); - pool = chroma(colour) < IIGS_GREY_CHROMA ? all : keeps; - poolCount = chroma(colour) < IIGS_GREY_CHROMA ? IIGS_VALUES : keepCount; + color = lab(rgb); + pool = chroma(color) < IIGS_GRAY_CHROMA ? all : keeps; + poolCount = chroma(color) < IIGS_GRAY_CHROMA ? IIGS_VALUES : keepCount; value = pool[0]; - best = cie94(colour, colours[pool[0]]); + best = cie94(color, colors[pool[0]]); for (v = 1; v < poolCount; v++) { - d = cie94(colour, colours[pool[v]]); + d = cie94(color, colors[pool[v]]); if (d < best) { best = d; value = pool[v]; @@ -1463,7 +1463,7 @@ static void iigsIcon(const ToolImageT *picture, uint32_t w, uint32_t h, ToolBufT shape[at] |= (uint8_t)(IIGS_OPAQUE << shift); } } - toolBufLe16(out, IIGS_ICON_TYPE_COLOUR); + toolBufLe16(out, IIGS_ICON_TYPE_COLOR); toolBufLe16(out, rowBytes * h); toolBufLe16(out, h); toolBufLe16(out, w); @@ -1490,7 +1490,7 @@ static void imageNew(ToolImageT *img, uint32_t w, uint32_t h) { } -// An sRGB colour (channels 0-255) in CIELAB. +// An sRGB color (channels 0-255) in CIELAB. static LabT lab(const double rgb[RGB_BYTES]) { double channels[RGB_BYTES]; double f[RGB_BYTES]; @@ -1534,16 +1534,16 @@ static double linear(double value) { } -// 256 colours for an 8-bit icon of image: black (where transparent: the +// 256 colors for an 8-bit icon of image: black (where transparent: the // AND mask's partner), then up to 255 of its own (flattened on black), cut // down by median cut: the box with the widest channel (the most pixels of // equals) is split at its pixels' median along it, until there are enough -// boxes or each holds one colour; a box's colour is its pixels' mean. +// boxes or each holds one color; a box's color is its pixels' mean. static void medianCut(const ToolImageT *image, uint8_t palette[PALETTE_SIZE][RGB_BYTES]) { ToolImageT flat; uint32_t *packed; ShadeT *shades; - BoxT boxes[MEDIAN_COLOURS]; + BoxT boxes[MEDIAN_COLORS]; uint32_t boxCount; uint32_t shadeCount; uint32_t pixels; @@ -1583,7 +1583,7 @@ static void medianCut(const ToolImageT *image, uint8_t palette[PALETTE_SIZE][RGB boxes[0].count = shadeCount; boxMeasure(&boxes[0], shades); boxCount = 1u; - while (boxCount < MEDIAN_COLOURS) { + while (boxCount < MEDIAN_COLORS) { best = boxCount; for (b = 0; b < boxCount; b++) { if (boxes[b].range > 0u && (best == boxCount || boxes[b].range > boxes[best].range || (boxes[b].range == boxes[best].range && boxes[b].pixels > boxes[best].pixels))) { @@ -1633,9 +1633,9 @@ static void medianCut(const ToolImageT *image, uint8_t palette[PALETTE_SIZE][RGB // The palette entry nearest (r, g, b) by squared distance, the first of -// equals, measured from the low corner of the colour's cell, as Pillow +// equals, measured from the low corner of the color's cell, as Pillow // measures it. -static uint8_t nearestColour(PaletteT *palette, uint32_t r, uint32_t g, uint32_t b) { +static uint8_t nearestColor(PaletteT *palette, uint32_t r, uint32_t g, uint32_t b) { uint32_t cell; uint32_t i; int32_t dr; @@ -1684,12 +1684,12 @@ static void paletteFree(PaletteT *palette) { } -// count colours, then the first again to 256, and an empty cache. -static void paletteInit(PaletteT *palette, const uint8_t (*colours)[RGB_BYTES], uint32_t count) { +// count colors, then the first again to 256, and an empty cache. +static void paletteInit(PaletteT *palette, const uint8_t (*colors)[RGB_BYTES], uint32_t count) { uint32_t i; for (i = 0; i < PALETTE_SIZE; i++) { - memcpy(palette->rgb[i], colours[i < count ? i : 0u], RGB_BYTES); + memcpy(palette->rgb[i], colors[i < count ? i : 0u], RGB_BYTES); } palette->cache = (int16_t *)malloc(CELL_COUNT * sizeof(int16_t)); if (palette->cache == NULL) { @@ -1726,7 +1726,7 @@ static void pascal(ToolBufT *out, const char *text, uint32_t size) { } -// src with its colour weighted by its opacity (Pillow's RGBa). +// src with its color weighted by its opacity (Pillow's RGBa). static void premultiply(const ToolImageT *src, ToolImageT *dst) { size_t i; size_t pixels; @@ -1839,7 +1839,7 @@ static double sinc(double x) { // The taps resampling inSize samples to outSize takes, as Pillow computes // them: the window widened by the scale when shrinking, each output's -// weights normalised to sum to one, then rounded to fixed point. +// weights normalized to sum to one, then rounded to fixed point. static void tapsBuild(TapsT *taps, uint32_t inSize, uint32_t outSize) { double scale; double filterScale; @@ -1884,7 +1884,7 @@ static void tapsBuild(TapsT *taps, uint32_t inSize, uint32_t outSize) { total += weights[t]; } for (t = 0; t < taps->count[o]; t++) { - v = total != 0.0 ? weights[t] / total : weights[t]; + v = total != 0.0 ? weights[t] / total : weights[t]; taps->weights[(size_t)o * taps->size + t] = (int32_t)(v < 0.0 ? -0.5 + v * TAP_ONE : 0.5 + v * TAP_ONE); } } @@ -1899,10 +1899,10 @@ static void tapsFree(TapsT *taps) { } -// The image's pixels as indices into colours (count of them, padded to 256 +// The image's pixels as indices into colors (count of them, padded to 256 // with the first): over background where transparent, dithered // (Floyd-Steinberg) or the nearest. -static void toColours(const ToolImageT *image, const uint8_t (*colours)[RGB_BYTES], uint32_t count, bool dither, const uint8_t background[RGB_BYTES], uint8_t *indices) { +static void toColors(const ToolImageT *image, const uint8_t (*colors)[RGB_BYTES], uint32_t count, bool dither, const uint8_t background[RGB_BYTES], uint8_t *indices) { ToolImageT flat; PaletteT palette; size_t i; @@ -1910,14 +1910,14 @@ static void toColours(const ToolImageT *image, const uint8_t (*colours)[RGB_BYTE const uint8_t *p; flatten(image, background, &flat); - paletteInit(&palette, colours, count); + paletteInit(&palette, colors, count); if (dither) { ditherFloydSteinberg(&flat, &palette, indices); } else { pixels = (size_t)flat.width * flat.height; for (i = 0; i < pixels; i++) { p = flat.pixels + i * RGBA_BYTES; - indices[i] = nearestColour(&palette, p[0], p[1], p[2]); + indices[i] = nearestColor(&palette, p[0], p[1], p[2]); } } paletteFree(&palette); @@ -1925,7 +1925,7 @@ static void toColours(const ToolImageT *image, const uint8_t (*colours)[RGB_BYTE } -// Premultiplied colour divided back by its opacity (Pillow's RGBa to RGBA); +// Premultiplied color divided back by its opacity (Pillow's RGBa to RGBA); // clear and opaque pixels are left as they are. static void unpremultiply(const ToolImageT *src, ToolImageT *dst) { size_t i; diff --git a/tools/modToNtp/modToNtp.c b/tools/modToNtp/modToNtp.c index c1485d7..0896b2a 100644 --- a/tools/modToNtp/modToNtp.c +++ b/tools/modToNtp/modToNtp.c @@ -26,7 +26,7 @@ // play long enough: a loop under 256 bytes is repeated, to 512 when its // length is a power of two (ideal), else as many times as fit in 504; // - starting later is a header (the sample up to the loop's quietest -// byte, the first nearest the DOC's centre) and a loop instrument (the +// byte, the first nearest the DOC's center) and a loop instrument (the // loop from that byte round to it, repeated as above), played by two // oscillators in turn. // An instrument past 32760 bytes, one played with a sample offset (effect @@ -107,7 +107,7 @@ #define DOC_BYTES 65536u #define PAGE_BYTES 256u #define DOC_PAGES (DOC_BYTES / PAGE_BYTES) -#define DOC_CENTRE 128u +#define DOC_CENTER 128u #define DOC_STOP 0u #define DOC_STOP_FIX 1u #define STOPPER_BYTES 8u @@ -274,7 +274,7 @@ static bool claimAfter(const ClaimT *c, const uint32_t *o, uint32_t a, uint32_t static uint8_t docByte(const SampleT *s, uint32_t at) { uint8_t v; - v = (uint8_t)((at < s->have ? s->data[at] : 0u) ^ DOC_CENTRE); + v = (uint8_t)((at < s->have ? s->data[at] : 0u) ^ DOC_CENTER); return v == DOC_STOP ? DOC_STOP_FIX : v; } @@ -812,7 +812,7 @@ static void sortInsert(const ClaimT *c, uint32_t *o, uint32_t n) { // The first byte of the loop (loopLen bytes from start) nearest the DOC's -// centre: where a header ends and its loop begins. +// center: where a header ends and its loop begins. static uint32_t splitAt(const SampleT *s, uint32_t start, uint32_t loopLen) { uint32_t best; uint32_t bestDistance; @@ -821,10 +821,10 @@ static uint32_t splitAt(const SampleT *s, uint32_t start, uint32_t loopLen) { uint8_t v; best = start; - bestDistance = DOC_CENTRE + 1u; + bestDistance = DOC_CENTER + 1u; for (i = start; i < start + loopLen; i++) { v = docByte(s, i); - distance = v >= DOC_CENTRE ? v - DOC_CENTRE : DOC_CENTRE - v; + distance = v >= DOC_CENTER ? v - DOC_CENTER : DOC_CENTER - v; if (distance < bestDistance) { best = i; bestDistance = distance; diff --git a/tools/songbake/songbake.c b/tools/songbake/songbake.c index f3e6dc1..0a3e720 100644 --- a/tools/songbake/songbake.c +++ b/tools/songbake/songbake.c @@ -435,7 +435,7 @@ static size_t linesRead(char *text, size_t len, LineT **lines) { for (at = 0; at < len; at = next) { for (end = at; end < len && text[end] != '\n' && text[end] != '\r'; end++) { } - next = end + ((end + 1u < len && text[end] == '\r' && text[end + 1u] == '\n') ? 2u : 1u); + next = end + ((end + 1u < len && text[end] == '\r' && text[end + 1u] == '\n') ? 2u : 1u); text[end] = '\0'; lineNum++; comment = strchr(text + at, ';'); diff --git a/tools/tiled2joey/tiled2joey.c b/tools/tiled2joey/tiled2joey.c index e2e9954..cf7d185 100644 --- a/tools/tiled2joey/tiled2joey.c +++ b/tools/tiled2joey/tiled2joey.c @@ -8,9 +8,9 @@ // // MAP is a Tiled map, TMX (XML) or TMJ (JSON): orthogonal, finite, with 8x8 // tiles. Its tilesets, embedded or external (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 index 0 stays the -// background), and the others' are matched by colour or given a slot the +// indexed PNGs whose colors make one palette of at most 16 between them: +// the first tileset's colors keep their indices (so its index 0 stays the +// background), and the others' are matched by color or given a slot the // first does not use. // // SHEET.png gets the tiles the map's tile layers use, in a row, in the @@ -51,7 +51,7 @@ #define TILE_PIXELS (TILE_SIDE * TILE_SIDE) #define MAX_TILES 255u #define SKIP 0xFFu -#define MAX_COLOURS 16u +#define MAX_COLORS 16u #define FLIP_H 0x80000000u #define FLIP_V 0x40000000u #define FLIP_D 0x20000000u @@ -148,7 +148,7 @@ static void pyUpperIdent(const char *s, size_t len, FILE *f); static uint8_t *readAll(const char *path, size_t *len); static char *stringCopy(const char *s); static void tilesetAdd(MapT *map, long firstGid, TilesetT *tileset); -static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLOURS][3], bool used[MAX_COLOURS], bool first); +static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLORS][3], bool used[MAX_COLORS], bool first); static void usage(void); static void writeHeader(const char *path, const char *mapPath, long width, long height, size_t count, const MapT *map, char **files); static void xmlLayers(const ToolXmlT *element, MapT *map); @@ -747,8 +747,8 @@ static const char *pyRepr(const char *s) { if (s == NULL) { return "None"; } - quote = (strchr(s, '\'') != NULL && strchr(s, '"') == NULL) ? '"' : '\''; - n = 0u; + quote = (strchr(s, '\'') != NULL && strchr(s, '"') == NULL) ? '"' : '\''; + n = 0u; out[n++] = quote; for (; *s != '\0' && n + 5u < sizeof(out); s++) { c = *s; @@ -825,11 +825,11 @@ static void tilesetAdd(MapT *map, long firstGid, TilesetT *tileset) { } -// The tileset's tiles as 8x8 grids of indices into palette (16 colours, -// used marks the slots taken). The first tileset's colours take their own -// indices (a colour it holds twice reads as its first); another's are -// matched by colour, or take the first free slot. -static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLOURS][3], bool used[MAX_COLOURS], bool first) { +// The tileset's tiles as 8x8 grids of indices into palette (16 colors, +// used marks the slots taken). The first tileset's colors take their own +// indices (a color it holds twice reads as its first); another's are +// matched by color, or take the first free slot. +static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLORS][3], bool used[MAX_COLORS], bool first) { ToolImageT image; char err[TOOL_PNG_ERROR_BYTES]; bool inImage[TOOL_PNG_PALETTE_MAX]; @@ -868,19 +868,19 @@ static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLOURS][3], bo continue; } if (first) { - if (index >= MAX_COLOURS) { - toolFail("%s uses palette index %u; tiles have %u colours", tileset->image, index, MAX_COLOURS); + if (index >= MAX_COLORS) { + toolFail("%s uses palette index %u; tiles have %u colors", tileset->image, index, MAX_COLORS); } memcpy(palette[index], image.palette[index], 3u); used[index] = true; } else { - for (slot = 0; slot < MAX_COLOURS && !(used[slot] && memcmp(palette[slot], image.palette[index], 3u) == 0); slot++) { + for (slot = 0; slot < MAX_COLORS && !(used[slot] && memcmp(palette[slot], image.palette[index], 3u) == 0); slot++) { } - if (slot == MAX_COLOURS) { - for (slot = 0; slot < MAX_COLOURS && used[slot]; slot++) { + if (slot == MAX_COLORS) { + for (slot = 0; slot < MAX_COLORS && used[slot]; slot++) { } - if (slot == MAX_COLOURS) { - toolFail("the tilesets' colours make more than %u", MAX_COLOURS); + if (slot == MAX_COLORS) { + toolFail("the tilesets' colors make more than %u", MAX_COLORS); } memcpy(palette[slot], image.palette[index], 3u); used[slot] = true; @@ -894,11 +894,11 @@ static void tilesetPixels(TilesetT *tileset, uint8_t palette[MAX_COLOURS][3], bo // no columns or rows. columns = tileset->columns; if (columns == 0) { - x = (long)image.width - 2 * tileset->margin + tileset->spacing; + x = (long)image.width - 2 * tileset->margin + tileset->spacing; columns = x >= 0 ? x / (TILE_SIDE + tileset->spacing) : -((-x + TILE_SIDE + tileset->spacing - 1) / (TILE_SIDE + tileset->spacing)); } - y = (long)image.height - 2 * tileset->margin + tileset->spacing; - rows = y >= 0 ? y / (TILE_SIDE + tileset->spacing) : -((-y + TILE_SIDE + tileset->spacing - 1) / (TILE_SIDE + tileset->spacing)); + y = (long)image.height - 2 * tileset->margin + tileset->spacing; + rows = y >= 0 ? y / (TILE_SIDE + tileset->spacing) : -((-y + TILE_SIDE + tileset->spacing - 1) / (TILE_SIDE + tileset->spacing)); tileset->tileCount = (columns > 0 && rows > 0) ? (size_t)(columns * rows) : 0u; tileset->tiles = (uint8_t *)malloc(tileset->tileCount * TILE_PIXELS + 1u); if (tileset->tiles == NULL) { @@ -1046,15 +1046,15 @@ int main(int argc, char **argv) { MapT map; TileKeyT keys[MAX_TILES]; uint8_t tiles[MAX_TILES][TILE_PIXELS]; - uint8_t palette[MAX_COLOURS][3]; - bool used[MAX_COLOURS]; + uint8_t palette[MAX_COLORS][3]; + bool used[MAX_COLORS]; uint8_t **cells; char **taken; char **files; char fileName[PATH_MAX_BYTES]; char path[PATH_MAX_BYTES]; size_t tileCount; - size_t colours; + size_t colors; size_t l; size_t c; size_t s; @@ -1160,8 +1160,8 @@ int main(int argc, char **argv) { if (!toolImageCreate(&sheet, (uint32_t)(TILE_SIDE * tileCount), TILE_SIDE, true)) { toolFail("out of memory"); } - sheet.paletteCount = MAX_COLOURS; - for (c = 0; c < MAX_COLOURS; c++) { + sheet.paletteCount = MAX_COLORS; + for (c = 0; c < MAX_COLORS; c++) { memcpy(sheet.palette[c], palette[c], 3u); sheet.palette[c][3] = 0xFFu; } @@ -1193,11 +1193,11 @@ int main(int argc, char **argv) { if (header != NULL) { writeHeader(header, mapPath, map.width.value, map.height.value, tileCount, &map, files); } - colours = 0u; - for (c = 0; c < MAX_COLOURS; c++) { - colours += used[c] ? 1u : 0u; + colors = 0u; + for (c = 0; c < MAX_COLORS; c++) { + colors += used[c] ? 1u : 0u; } - printf("%s: %ldx%ld map, %zu tile%s, %zu colour%s, layers: ", TOOL_NAME, map.width.value, map.height.value, tileCount, tileCount == 1u ? "" : "s", colours, colours == 1u ? "" : "s"); + printf("%s: %ldx%ld map, %zu tile%s, %zu color%s, layers: ", TOOL_NAME, map.width.value, map.height.value, tileCount, tileCount == 1u ? "" : "s", colors, colors == 1u ? "" : "s"); for (l = 0; l < map.layerCount; l++) { printf("%s%s", l == 0u ? "" : ", ", files[l]); } diff --git a/tools/traceLogo/traceLogo.c b/tools/traceLogo/traceLogo.c index d6b29a7..be51942 100644 --- a/tools/traceLogo/traceLogo.c +++ b/tools/traceLogo/traceLogo.c @@ -2,30 +2,30 @@ // See LICENSE.md for license information. // SPDX-License-Identifier: MIT // -// traceLogo.c - trace a flat-colour PNG logo into a layered SVG. +// traceLogo.c - trace a flat-color PNG logo into a layered SVG. // // The JoeyLib logo ships as a 320x200 PNG, which is fine on screen and poor // everywhere else: the manual's cover wants it large, and a bitmap that small // either pixelates or blurs when scaled up. This makes a resolution-free SVG // of the same artwork, so it can be set at any size. // -// How it works. The art is flat cartoon colour with antialiased edges, so: +// How it works. The art is flat cartoon color with antialiased edges, so: // // 1. k-means the opaque pixels down to --colors clusters, which recovers -// the handful of real colours and throws away the antialiasing gradient -// (in CIELAB, so a grey edge pixel joins the ink or the paper rather -// than whatever colour is nearest it in RGB; the centres are seeded +// the handful of real colors and throws away the antialiasing gradient +// (in CIELAB, so a gray edge pixel joins the ink or the paper rather +// than whatever color is nearest it in RGB; the centers are seeded // k-means++ style from a fixed seed, so a run is repeatable), -// 2. make a one-bit mask (a PBM) for each colour, +// 2. make a one-bit mask (a PBM) for each color, // 3. run each mask through potrace, which fits Bezier curves to the region // outlines, -// 4. write one SVG <path> for each colour, filled with the exact colour +// 4. write one SVG <path> for each color, filled with the exact color // most of its pixels have (the art's own; the mean would be dragged // toward whatever its antialiased edges blend with), fill-rule evenodd. // // Layer order matters: the paths are written largest area first so big -// shapes sit behind small ones, EXCEPT the darkest colour, which is written -// last. In cartoon art the darkest colour is the ink outline, and it has to +// shapes sit behind small ones, EXCEPT the darkest color, which is written +// last. In cartoon art the darkest color is the ink outline, and it has to // be on top or the fills eat it. // // Transparent pixels are in no mask, so the SVG keeps the PNG's transparent @@ -99,8 +99,8 @@ #define LAB_A_SCALE 500.0 #define LAB_B_SCALE 200.0 -// k-means: at most this many rounds of assigning and re-centring, from -// centres seeded by a splitmix64 stream that starts here. +// k-means: at most this many rounds of assigning and re-centering, from +// centers seeded by a splitmix64 stream that starts here. #define KMEANS_ITERATIONS 24u #define KMEANS_SEED 1u #define SPLITMIX_GAMMA UINT64_C(0x9E3779B97F4A7C15) @@ -113,12 +113,12 @@ #define UNIT_SHIFT 11u #define UNIT_SCALE (1.0 / 9007199254740992.0) -// Rec. 601 luma, which picks the darkest colour (the ink). +// Rec. 601 luma, which picks the darkest color (the ink). #define LUMA_R 0.299 #define LUMA_G 0.587 #define LUMA_B 0.114 -// A pixel in no colour's mask. +// A pixel in no color's mask. #define LABEL_CLEAR (-1) // A raw PBM: one bit a pixel, set where traced, rows of whole bytes. @@ -187,7 +187,7 @@ typedef struct ArgsT { long values[OPTION_COUNT]; } ArgsT; -// One colour's layer: its k-means cluster, its pixels, its colour, and its +// One color's layer: its k-means cluster, its pixels, its color, and its // outline as SVG path data. typedef struct LayerT { uint32_t cluster; @@ -214,13 +214,13 @@ static void *allocate(size_t count, size_t size); static void bufEscaped(ToolBufT *buf, const char *text); static void bufPrintf(ToolBufT *buf, const char *fmt, ...) __attribute__((format(printf, 2, 3))); static double distance2(const double *a, const double *b); -static void kmeans(const double *samples, size_t count, uint32_t k, double *centres, uint32_t *labels); -static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t *random, double *centres); +static void kmeans(const double *samples, size_t count, uint32_t k, double *centers, uint32_t *labels); +static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t *random, double *centers); static double labCurve(double t); static void labOf(const double *rgb, double *lab); static int layerCompare(const void *a, const void *b); -static void modeColour(const double *samples, const uint32_t *labels, size_t count, uint32_t cluster, uint32_t *rgb); -static uint32_t nearestCentre(const double *sample, const double *centres, uint32_t k); +static void modeColor(const double *samples, const uint32_t *labels, size_t count, uint32_t cluster, uint32_t *rgb); +static uint32_t nearestCenter(const double *sample, const double *centers, uint32_t k); static int packedCompare(const void *a, const void *b); static void parseArgs(int argc, char **argv, ArgsT *args); static const char *pathConvert(const char *d, const TransformT *t, ToolBufT *out); @@ -261,9 +261,9 @@ static const double kWhite[CHANNELS] = { WHITE_X, WHITE_Y, WHITE_Z }; static const char *const kUsage[] = { "usage: traceLogo INPUT.png OUTPUT.svg [--colors N] [--alpha N] [--turdsize N]", "", - "Trace a flat-colour PNG into a layered SVG, one path a colour.", + "Trace a flat-color PNG into a layered SVG, one path a color.", "", - " --colors N number of flat colours to recover (default " VALUE_OF(COLORS_DEFAULT) ")", + " --colors N number of flat colors to recover (default " VALUE_OF(COLORS_DEFAULT) ")", " --alpha N alpha above which a pixel counts as opaque (default " VALUE_OF(ALPHA_DEFAULT) ")", " --turdsize N drop traced specks of up to this many pixels (default " VALUE_OF(TURDSIZE_DEFAULT) ")", "", @@ -325,7 +325,7 @@ static void bufPrintf(ToolBufT *buf, const char *fmt, ...) { } -// The squared distance between two colours. +// The squared distance between two colors. static double distance2(const double *a, const double *b) { double sum; double d; @@ -340,11 +340,11 @@ static double distance2(const double *a, const double *b) { } -// k-means over count samples (CHANNELS doubles each): k centres, and each -// sample's nearest. Rounds of assigning every sample its nearest centre and -// moving each centre to the mean of its samples (a centre with none stays -// put) run until no sample changes centre, at most KMEANS_ITERATIONS times. -static void kmeans(const double *samples, size_t count, uint32_t k, double *centres, uint32_t *labels) { +// k-means over count samples (CHANNELS doubles each): k centers, and each +// sample's nearest. Rounds of assigning every sample its nearest center and +// moving each center to the mean of its samples (a center with none stays +// put) run until no sample changes center, at most KMEANS_ITERATIONS times. +static void kmeans(const double *samples, size_t count, uint32_t k, double *centers, uint32_t *labels) { double *sums; size_t *members; size_t i; @@ -356,13 +356,13 @@ static void kmeans(const double *samples, size_t count, uint32_t k, double *cent bool changed; random = KMEANS_SEED; - kmeansSeed(samples, count, k, &random, centres); + kmeansSeed(samples, count, k, &random, centers); sums = allocate((size_t)k * CHANNELS, sizeof(*sums)); members = allocate(k, sizeof(*members)); for (iteration = 0; iteration < KMEANS_ITERATIONS; iteration++) { changed = false; for (i = 0; i < count; i++) { - label = nearestCentre(samples + i * CHANNELS, centres, k); + label = nearestCenter(samples + i * CHANNELS, centers, k); changed = changed || label != labels[i]; labels[i] = label; } @@ -382,7 +382,7 @@ static void kmeans(const double *samples, size_t count, uint32_t k, double *cent continue; } for (c = 0; c < CHANNELS; c++) { - centres[j * CHANNELS + c] = sums[j * CHANNELS + c] / (double)members[j]; + centers[j * CHANNELS + c] = sums[j * CHANNELS + c] / (double)members[j]; } } } @@ -391,11 +391,11 @@ static void kmeans(const double *samples, size_t count, uint32_t k, double *cent } -// k-means++ seeding, simplified: the first centre is a sample at random; +// k-means++ seeding, simplified: the first center is a sample at random; // each next one a sample drawn with odds by its squared distance to the -// nearest centre so far (so far-apart colours are favoured), or at random -// when every sample already sits on a centre. -static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t *random, double *centres) { +// nearest center so far (so far-apart colors are favored), or at random +// when every sample already sits on a center. +static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t *random, double *centers) { double *nearest; double total; double target; @@ -410,11 +410,11 @@ static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t nearest[i] = DBL_MAX; } pick = (size_t)randomBelow(random, count); - memcpy(centres, samples + pick * CHANNELS, CHANNELS * sizeof(*centres)); + memcpy(centers, samples + pick * CHANNELS, CHANNELS * sizeof(*centers)); for (c = 1; c < k; c++) { total = 0.0; for (i = 0; i < count; i++) { - d = distance2(samples + i * CHANNELS, centres + (c - 1u) * CHANNELS); + d = distance2(samples + i * CHANNELS, centers + (c - 1u) * CHANNELS); if (d < nearest[i]) { nearest[i] = d; } @@ -436,7 +436,7 @@ static void kmeansSeed(const double *samples, size_t count, uint32_t k, uint64_t } } } - memcpy(centres + c * CHANNELS, samples + pick * CHANNELS, CHANNELS * sizeof(*centres)); + memcpy(centers + c * CHANNELS, samples + pick * CHANNELS, CHANNELS * sizeof(*centers)); } free(nearest); } @@ -448,8 +448,8 @@ static double labCurve(double t) { } -// An sRGB colour (0-255 a channel) as CIELAB, where distance follows what the -// eye sees: a mid-grey antialiasing pixel is nearer the ink or the paper +// An sRGB color (0-255 a channel) as CIELAB, where distance follows what the +// eye sees: a mid-gray antialiasing pixel is nearer the ink or the paper // than an orange it happens to sit close to in RGB. static void labOf(const double *rgb, double *lab) { double linear[CHANNELS]; @@ -487,11 +487,11 @@ static int layerCompare(const void *a, const void *b) { } -// The most common exact colour among a cluster's samples: the art's own flat -// colour, which the cluster's mean misses (its antialiased edge pixels drag -// the mean toward the colours they blend with). Of equal counts, the +// The most common exact color among a cluster's samples: the art's own flat +// color, which the cluster's mean misses (its antialiased edge pixels drag +// the mean toward the colors they blend with). Of equal counts, the // lowest packed value. -static void modeColour(const double *samples, const uint32_t *labels, size_t count, uint32_t cluster, uint32_t *rgb) { +static void modeColor(const double *samples, const uint32_t *labels, size_t count, uint32_t cluster, uint32_t *rgb) { uint32_t *packed; uint32_t best; uint32_t c; @@ -530,23 +530,23 @@ static void modeColour(const double *samples, const uint32_t *labels, size_t cou } -// The centre nearest a sample (the first, on a tie). -static uint32_t nearestCentre(const double *sample, const double *centres, uint32_t k) { +// The center nearest a sample (the first, on a tie). +static uint32_t nearestCenter(const double *sample, const double *centers, uint32_t k) { double best; double d; - uint32_t bestCentre; + uint32_t bestCenter; uint32_t j; - best = distance2(sample, centres); - bestCentre = 0; + best = distance2(sample, centers); + bestCenter = 0; for (j = 1; j < k; j++) { - d = distance2(sample, centres + j * CHANNELS); + d = distance2(sample, centers + j * CHANNELS); if (d < best) { best = d; - bestCentre = j; + bestCenter = j; } } - return bestCentre; + return bestCenter; } @@ -915,7 +915,7 @@ int main(int argc, char **argv) { LayerT *layers; double *samples; double *lab; - double *centres; + double *centers; int32_t *labelImage; uint32_t *labels; uint32_t *areas; @@ -948,7 +948,7 @@ int main(int argc, char **argv) { picture = loaded; } - // The opaque pixels' colours, in order, and where each pixel's goes. + // The opaque pixels' colors, in order, and where each pixel's goes. pixels = (size_t)picture.width * picture.height; samples = allocate(pixels, CHANNELS * sizeof(*samples)); labelImage = allocate(pixels, sizeof(*labelImage)); @@ -967,15 +967,15 @@ int main(int argc, char **argv) { toolFail("%s is fully transparent", input); } - centres = allocate(colors, CHANNELS * sizeof(*centres)); + centers = allocate(colors, CHANNELS * sizeof(*centers)); labels = allocate(count, sizeof(*labels)); areas = allocate(colors, sizeof(*areas)); - // Clustered in CIELAB; each layer still takes its colour from the RGB. + // Clustered in CIELAB; each layer still takes its color from the RGB. lab = allocate(count, CHANNELS * sizeof(*lab)); for (p = 0; p < count; p++) { labOf(samples + p * CHANNELS, lab + p * CHANNELS); } - kmeans(lab, count, colors, centres, labels); + kmeans(lab, count, colors, centers, labels); free(lab); for (p = 0; p < pixels; p++) { if (labelImage[p] != LABEL_CLEAR) { @@ -984,7 +984,7 @@ int main(int argc, char **argv) { } } - // A layer for each colour some pixel has, traced. + // A layer for each color some pixel has, traced. tempCreate(); snprintf(turdsize, sizeof(turdsize), "%ld", args.values[OPTION_TURDSIZE]); layers = allocate(colors, sizeof(*layers)); @@ -993,7 +993,7 @@ int main(int argc, char **argv) { if (areas[j] == 0u) { continue; } - modeColour(samples, labels, count, j, rgb); + modeColor(samples, labels, count, j, rgb); layers[layerCount].cluster = j; layers[layerCount].area = areas[j]; layers[layerCount].luma = LUMA_R * rgb[0] + LUMA_G * rgb[1] + LUMA_B * rgb[2]; @@ -1012,7 +1012,7 @@ int main(int argc, char **argv) { } writeSvg(args.paths[PATH_OUTPUT], input, picture.width, picture.height, layers, layerCount, ink); - printf("%s: %s -- %" PRIu32 " colour layers, %" PRIu32 "x%" PRIu32 " viewBox\n", TOOL_NAME, args.paths[PATH_OUTPUT], layerCount, picture.width, picture.height); + printf("%s: %s -- %" PRIu32 " color layers, %" PRIu32 "x%" PRIu32 " viewBox\n", TOOL_NAME, args.paths[PATH_OUTPUT], layerCount, picture.width, picture.height); for (j = 0; j < layerCount; j++) { printf(" %s %6" PRIu32 " px\n", layers[j].color, layers[j].area); toolBufFree(&layers[j].path); @@ -1020,7 +1020,7 @@ int main(int argc, char **argv) { free(layers); free(areas); free(labels); - free(centres); + free(centers); free(labelImage); free(samples); toolImageFree(&picture); diff --git a/tools/vgm2song/vgm2song.c b/tools/vgm2song/vgm2song.c index 0010ea7..3909c90 100644 --- a/tools/vgm2song/vgm2song.c +++ b/tools/vgm2song/vgm2song.c @@ -538,8 +538,8 @@ static void gd3Strings(const uint8_t *data, size_t len, size_t at, VgmT *vgm) { if (end > len) { end = len; } - n = 0u; - vgm->titleCount = 1u; + n = 0u; + vgm->titleCount = 1u; vgm->title[0][0] = '\0'; for (i = at + GD3_TEXT_AT; i < end;) { if (i + 1u >= end) { @@ -671,11 +671,11 @@ static void readVgm(const char *path, ChipE chip, VgmT *vgm) { if (version >= VGM_DATA_OFFSET_V150 && u32(data, len, VGM_DATA_OFFSET_FIELD) != 0u) { start = VGM_DATA_OFFSET_FIELD + u32(data, len, VGM_DATA_OFFSET_FIELD); } - snClock = u32(data, len, VGM_SN_CLOCK_FIELD); - ayClock = (version >= VGM_AY_CLOCK_V151 && start > VGM_AY_CLOCK_FIELD) ? u32(data, len, VGM_AY_CLOCK_FIELD) : 0u; - hasLoopAt = u32(data, len, VGM_LOOP_FIELD) != 0u; - loopAt = VGM_LOOP_FIELD + u32(data, len, VGM_LOOP_FIELD); - vgm->rate = u32(data, len, VGM_RATE_FIELD); + snClock = u32(data, len, VGM_SN_CLOCK_FIELD); + ayClock = (version >= VGM_AY_CLOCK_V151 && start > VGM_AY_CLOCK_FIELD) ? u32(data, len, VGM_AY_CLOCK_FIELD) : 0u; + hasLoopAt = u32(data, len, VGM_LOOP_FIELD) != 0u; + loopAt = VGM_LOOP_FIELD + u32(data, len, VGM_LOOP_FIELD); + vgm->rate = u32(data, len, VGM_RATE_FIELD); vgm->titleCount = 0u; if (u32(data, len, VGM_GD3_FIELD) != 0u) { gd3Strings(data, len, VGM_GD3_FIELD + u32(data, len, VGM_GD3_FIELD), vgm);