More bugs fixed. Performance improvements. More tilemap features.
This commit is contained in:
parent
484d22691b
commit
423f992583
255 changed files with 16289 additions and 8839 deletions
45
LICENSES.md
45
LICENSES.md
|
|
@ -23,16 +23,17 @@ most.
|
||||||
|
|
||||||
### SDL 3.4.18
|
### SDL 3.4.18
|
||||||
|
|
||||||
- **Used on:** SDL3, PSP, web
|
- **Used on:** SDL3, PSP, web, macOS
|
||||||
- **URL:** <https://www.libsdl.org/>
|
- **URL:** <https://www.libsdl.org/>
|
||||||
- **License:** zlib
|
- **License:** zlib
|
||||||
- **Role:** window, input, and audio for the hosted SDL3 code. Built as a
|
- **Role:** window, input, and audio for the hosted SDL3 code. Built as a
|
||||||
static library for the host, for the PSP with pspdev, and for Emscripten
|
static library for the host, for the PSP with pspdev, for Emscripten
|
||||||
(with JoeyLib's patches, `installer/patches/sdl3Web/`, which are under
|
(with JoeyLib's patches, `installer/patches/sdl3Web/`, which are under
|
||||||
SDL's zlib license), and linked into each program.
|
SDL's zlib license), and for macOS with zig (universal), and linked into
|
||||||
|
each program.
|
||||||
- **Installed at:** `toolchains/opt/sdl3/`, `toolchains/opt/sdl3Psp/`,
|
- **Installed at:** `toolchains/opt/sdl3/`, `toolchains/opt/sdl3Psp/`,
|
||||||
`toolchains/opt/sdl3Web/` (installer components `sdl3`, `sdl3Psp`,
|
`toolchains/opt/sdl3Web/`, `toolchains/opt/sdl3Macos/` (installer
|
||||||
`sdl3Web`)
|
components `sdl3`, `sdl3Psp`, `sdl3Web`, `sdl3Macos`)
|
||||||
|
|
||||||
### NinjaTrackerPlus (NTP)
|
### NinjaTrackerPlus (NTP)
|
||||||
|
|
||||||
|
|
@ -52,7 +53,7 @@ most.
|
||||||
### PTPlayer 6.4
|
### PTPlayer 6.4
|
||||||
|
|
||||||
- **Used on:** Amiga; and, translated to C, DOS, Atari ST, X68000, SDL3,
|
- **Used on:** Amiga; and, translated to C, DOS, Atari ST, X68000, SDL3,
|
||||||
PSP, web, Win16, Win32, Haiku, BeOS
|
PSP, web, macOS, Win16, Win32, Haiku, BeOS
|
||||||
- **URL:** <http://aminet.net/mus/play/ptplayer.lha>
|
- **URL:** <http://aminet.net/mus/play/ptplayer.lha>
|
||||||
(author Frank Wille, <http://aminet.net/package/mus/play/ptplayer>)
|
(author Frank Wille, <http://aminet.net/package/mus/play/ptplayer>)
|
||||||
- **License:** Public Domain (the archive's `license` file: the Unlicense)
|
- **License:** Public Domain (the archive's `license` file: the Unlicense)
|
||||||
|
|
@ -109,6 +110,8 @@ them, in brief (the entries below have the detail):
|
||||||
terms Unknown; see the BeOS entry under the never-downloaded media
|
terms Unknown; see the BeOS entry under the never-downloaded media
|
||||||
below).
|
below).
|
||||||
- **Haiku:** the system's own shared libraries, at run time (MIT).
|
- **Haiku:** the system's own shared libraries, at run time (MIT).
|
||||||
|
- **macOS:** the system's own libraries and frameworks, at run time
|
||||||
|
(Apple's); zig's compiler runtime, linked in (MIT).
|
||||||
- **Every gcc port:** libgcc, under the GCC Runtime Library Exception,
|
- **Every gcc port:** libgcc, under the GCC Runtime Library Exception,
|
||||||
which puts no conditions on the programs it is linked into.
|
which puts no conditions on the programs it is linked into.
|
||||||
|
|
||||||
|
|
@ -303,7 +306,7 @@ above.
|
||||||
every Win16 program -- must, by section 2.2(d), include a prominent
|
every Win16 program -- must, by section 2.2(d), include a prominent
|
||||||
notice, in the program and its documentation, that the source is
|
notice, in the program and its documentation, that the source is
|
||||||
available under the license and where to get it. Every Win16 program
|
available under the license and where to get it. Every Win16 program
|
||||||
carries it (`hostWin16OwplNotice`, src/hosted/win16/win16Backend.c), as
|
carries it (`jlHostWin16OwplNotice`, src/hosted/win16/win16Backend.c), as
|
||||||
do KPJOY.DRV and KPCOMM.DRV, as a string never shown; docs/Portability.adoc
|
do KPJOY.DRV and KPCOMM.DRV, as a string never shown; docs/Portability.adoc
|
||||||
("Shipping") gives the same line for a program's documentation.
|
("Shipping") gives the same line for a program's documentation.
|
||||||
- **Role:** the Win386 target compiles the win16 port's 32-bit programs
|
- **Role:** the Win386 target compiles the win16 port's 32-bit programs
|
||||||
|
|
@ -324,6 +327,34 @@ above.
|
||||||
- **Installed at:** `toolchains/opt/llvmMingw/` (installer component
|
- **Installed at:** `toolchains/opt/llvmMingw/` (installer component
|
||||||
`llvmMingw`)
|
`llvmMingw`)
|
||||||
|
|
||||||
|
### zig 0.15.2
|
||||||
|
|
||||||
|
- **Used on:** macOS
|
||||||
|
- **URL:** <https://ziglang.org/>
|
||||||
|
- **License:** MIT. The clang, LLVM, and lld it is built from: Apache
|
||||||
|
License 2.0 with LLVM Exceptions. The macOS C library headers and
|
||||||
|
libSystem stubs it carries: Apple's, from Apple's open-source releases
|
||||||
|
(the Apple Public Source License 2.0, among others).
|
||||||
|
- **Role:** the macos port's C compiler and Mach-O linker; it signs the
|
||||||
|
Apple silicon programs ad hoc as it links them.
|
||||||
|
- **Installed at:** `toolchains/opt/zig/` (installer component `zig`)
|
||||||
|
|
||||||
|
### Apple MacOSX 15.5 SDK
|
||||||
|
|
||||||
|
- **Used on:** macOS
|
||||||
|
- **URL:** Apple ships it only inside Xcode; the installer takes it from
|
||||||
|
the mirror osxcross users share,
|
||||||
|
<https://github.com/joseluisq/macosx-sdks>, pinned to its SHA-256
|
||||||
|
- **License:** Apple's Xcode and Apple SDKs Agreement. None of it is
|
||||||
|
shipped: it gives the build the frameworks' headers and stub libraries to
|
||||||
|
link against, and a program uses the Mac's own frameworks when it runs.
|
||||||
|
- **Role:** the frameworks (Cocoa, CoreAudio, IOKit, and the rest) the
|
||||||
|
macos port's programs and its SDL 3 link against. The host's LLVM tools
|
||||||
|
(`llvm-lipo`, `llvm-libtool-darwin`; Apache License 2.0 with LLVM
|
||||||
|
Exceptions) join and archive what is built for both Mac processors.
|
||||||
|
- **Installed at:** `toolchains/opt/macosSdk/` (installer component
|
||||||
|
`macosSdk`)
|
||||||
|
|
||||||
### pspdev v20261001
|
### pspdev v20261001
|
||||||
|
|
||||||
- **Used on:** PSP
|
- **Used on:** PSP
|
||||||
|
|
|
||||||
9
Makefile
9
Makefile
|
|
@ -15,6 +15,7 @@
|
||||||
# make dvx builds DVX (JoeyLib programs as DVX desktop programs)
|
# make dvx builds DVX (JoeyLib programs as DVX desktop programs)
|
||||||
# make sdl3 builds SDL3 (JoeyLib programs as native windowed programs)
|
# make sdl3 builds SDL3 (JoeyLib programs as native windowed programs)
|
||||||
# make web builds the web port (JoeyLib programs as web pages)
|
# make web builds the web port (JoeyLib programs as web pages)
|
||||||
|
# make macos builds macOS (JoeyLib programs as universal Mac apps)
|
||||||
# make win32 builds Win32 (Windows 95 through today, as .EXE programs)
|
# make win32 builds Win32 (Windows 95 through today, as .EXE programs)
|
||||||
# make win16 builds Win16 (Windows 3.1 on a 386, as .EXE programs)
|
# make win16 builds Win16 (Windows 3.1 on a 386, as .EXE programs)
|
||||||
# make psp builds the PSP port (JoeyLib programs as PSP homebrew EBOOT.PBPs)
|
# make psp builds the PSP port (JoeyLib programs as PSP homebrew EBOOT.PBPs)
|
||||||
|
|
@ -39,7 +40,7 @@ include $(dir $(firstword $(MAKEFILE_LIST)))make/common.mk
|
||||||
|
|
||||||
ALL_TARGETS := $(JOEY_PORTS_INSTALLED)
|
ALL_TARGETS := $(JOEY_PORTS_INSTALLED)
|
||||||
|
|
||||||
.PHONY: all iigs iigs-disk iigs-verify amiga atarist ste dos x68000 dvx sdl3 web 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-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
|
all: $(ALL_TARGETS) tools
|
||||||
ifeq ($(ALL_TARGETS),)
|
ifeq ($(ALL_TARGETS),)
|
||||||
|
|
@ -83,6 +84,9 @@ sdl3:
|
||||||
web:
|
web:
|
||||||
@$(MAKE) -f $(REPO_DIR)/make/web.mk
|
@$(MAKE) -f $(REPO_DIR)/make/web.mk
|
||||||
|
|
||||||
|
macos:
|
||||||
|
@$(MAKE) -f $(REPO_DIR)/make/macos.mk
|
||||||
|
|
||||||
win32:
|
win32:
|
||||||
@$(MAKE) -f $(REPO_DIR)/make/win32.mk
|
@$(MAKE) -f $(REPO_DIR)/make/win32.mk
|
||||||
|
|
||||||
|
|
@ -154,6 +158,7 @@ status:
|
||||||
@echo " dvx: $(if $(HAVE_DVX),yes,NO)"
|
@echo " dvx: $(if $(HAVE_DVX),yes,NO)"
|
||||||
@echo " sdl3: $(if $(HAVE_SDL3),yes,NO)"
|
@echo " sdl3: $(if $(HAVE_SDL3),yes,NO)"
|
||||||
@echo " web: $(if $(HAVE_WEB),yes,NO)"
|
@echo " web: $(if $(HAVE_WEB),yes,NO)"
|
||||||
|
@echo " macos: $(if $(HAVE_MACOS),yes,NO)"
|
||||||
@echo " win32: $(if $(HAVE_WIN32),yes,NO)"
|
@echo " win32: $(if $(HAVE_WIN32),yes,NO)"
|
||||||
@echo " win16: $(if $(HAVE_WIN16),yes,NO)"
|
@echo " win16: $(if $(HAVE_WIN16),yes,NO)"
|
||||||
@echo " psp: $(if $(HAVE_PSP),yes,NO)"
|
@echo " psp: $(if $(HAVE_PSP),yes,NO)"
|
||||||
|
|
@ -162,7 +167,7 @@ status:
|
||||||
@echo " beos: $(if $(HAVE_BEOS),yes,NO)"
|
@echo " beos: $(if $(HAVE_BEOS),yes,NO)"
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Targets: iigs amiga atarist ste dos x68000 dvx sdl3 web win32 win16 psp haiku haiku32 beos all clean 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 iigs-verify (IIgs MAME visual check)"
|
||||||
@echo " blank-check (host-cc smoke test of the new-port template + gnu89 lint)"
|
@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 (build every port, then run every gate; PORTS= subset)"
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -17,6 +17,9 @@ and the PSP, from a single codebase:
|
||||||
statically; sprites are interpreted)
|
statically; sprites are interpreted)
|
||||||
- Web (the SDL3 port compiled to WebAssembly with Emscripten: each program
|
- Web (the SDL3 port compiled to WebAssembly with Emscripten: each program
|
||||||
a page, its DATA packed in, saves kept in the browser)
|
a page, its DATA packed in, saves kept in the browser)
|
||||||
|
- macOS (the SDL3 port cross-built on Linux with zig against Apple's SDK:
|
||||||
|
each program a universal app for Apple silicon and Intel Macs, macOS 13
|
||||||
|
or newer; built and checked here, but run only on a Mac)
|
||||||
- Win32 (Windows 95 through today: the hosted core with a backend written to
|
- Win32 (Windows 95 through today: the hosted core with a backend written to
|
||||||
the Win32 API, built with llvm-mingw against MSVCRT.DLL, tested under Wine)
|
the Win32 API, built with llvm-mingw against MSVCRT.DLL, tested under Wine)
|
||||||
- Win16 (Windows 3.1 on a 386: the hosted core with a backend written to the
|
- Win16 (Windows 3.1 on a 386: the hosted core with a backend written to the
|
||||||
|
|
@ -66,20 +69,23 @@ hand, `source toolchains/env.sh` sets the same variables.
|
||||||
|
|
||||||
`make` builds `libjoey.a` for every port whose toolchain is installed, plus
|
`make` builds `libjoey.a` for every port whose toolchain is installed, plus
|
||||||
the example programs (`hello`, `pattern`, `draw`, `keys`, `serial`,
|
the example programs (`hello`, `pattern`, `draw`, `keys`, `serial`,
|
||||||
`sertest`, `save`, `tilebank`, `joy`, `sprite`, `audio`, `uber`, `adventure`,
|
`sertest`, `save`, `tilebank`, `joy`, `sprite`, `scroll`, `audio`, `uber`,
|
||||||
`adventure2`) for each. The IIgs is the exception:
|
`adventure`, `adventure2`) for each. The IIgs is the exception:
|
||||||
`make iigs` builds the library, and `make iigs-disk` builds its examples and
|
`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,
|
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`
|
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
|
and `dist/sdk.zip` (`./install.sh --dvx-dir PATH install dvx`; the default
|
||||||
is `../dvxgui`); its examples land in `build/dvx/bin/apps/joeylib/<name>/`,
|
is `../dvxgui`); its examples land in `build/dvx/bin/apps/kpunch/<name>/`,
|
||||||
laid out as they install in DVX. SDL3 needs the `sdl3` component
|
laid out as they install in DVX. SDL3 needs the `sdl3` component
|
||||||
(`./install.sh install toolchain:sdl3`), which builds SDL 3 from its release
|
(`./install.sh install toolchain:sdl3`), which builds SDL 3 from its release
|
||||||
source; its examples are native programs in `build/sdl3/bin/`, and running
|
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
|
one takes seconds rather than an emulator's minutes. The web port needs the
|
||||||
`web` components (`./install.sh install toolchain:web`: Emscripten through
|
`web` components (`./install.sh install toolchain:web`: Emscripten through
|
||||||
emsdk, and SDL 3 built with it); its examples are pages in
|
emsdk, and SDL 3 built with it); its examples are pages in
|
||||||
`build/web/bin/<NAME>/`. The Win32 port needs `toolchain:win32` (llvm-mingw)
|
`build/web/bin/<NAME>/`. The macOS port needs `toolchain:macos` (zig,
|
||||||
|
Apple's macOS SDK, and SDL 3 built with them); its examples are apps,
|
||||||
|
`build/macos/bin/<NAME>.app`, which nothing here runs: copy one to a Mac.
|
||||||
|
The Win32 port needs `toolchain:win32` (llvm-mingw)
|
||||||
and, to run its programs, `emulator:win32` (a portable Wine). The Win16 port
|
and, to run its programs, `emulator:win32` (a portable Wine). The Win16 port
|
||||||
needs `toolchain:win16` (Open Watcom) and, to run its programs,
|
needs `toolchain:win16` (Open Watcom) and, to run its programs,
|
||||||
`emulator:win16` (Windows for Workgroups 3.11 in DOSBox Staging, set up from
|
`emulator:win16` (Windows for Workgroups 3.11 in DOSBox Staging, set up from
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ const char *jlPlatformName(void);
|
||||||
*Parameters:* none.
|
*Parameters:* none.
|
||||||
|
|
||||||
*Returns:* a human-readable platform identifier, for example `"Apple IIgs"`,
|
*Returns:* a human-readable platform identifier, for example `"Apple IIgs"`,
|
||||||
`"MS-DOS"`, `"DVX"`, `"SDL3"` (the web port is the SDL3 port in a browser) or
|
`"MS-DOS"`, `"DVX"`, `"SDL3"` (the web port is the SDL3 port in a browser),
|
||||||
`"Windows"`.
|
`"macOS"`, or `"Windows"`.
|
||||||
Library-owned.
|
Library-owned.
|
||||||
|
|
||||||
=== jlVersionString
|
=== jlVersionString
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ Declared in `<joey/debug.h>`.
|
||||||
|
|
||||||
A crash-tracing logger that writes `joeylog.txt` in the program's folder,
|
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
|
beside `DATA/` and `SAVES/` (<<_files>>: on the ST, the current directory; on
|
||||||
DVX, the program's settings folder, `CONFIG\APPS\JOEYLIB\NAME\JOEYLOG.TXT`).
|
DVX, the program's settings folder, `CONFIG\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
|
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
|
the fastest way to find out what it did before it stopped doing it. The
|
||||||
headless run scripts retrieve the log automatically. The library itself writes
|
headless run scripts retrieve the log automatically. The library itself writes
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ goes there too):
|
||||||
* *DVX* keeps the two apart, because its programs share one current
|
* *DVX* keeps the two apart, because its programs share one current
|
||||||
directory: there `DATA/` is beside the program, in its folder under DVX's
|
directory: there `DATA/` is beside the program, in its folder under DVX's
|
||||||
`APPS`, and `SAVES/` is in the program's own settings folder,
|
`APPS`, and `SAVES/` is in the program's own settings folder,
|
||||||
`CONFIG\APPS\JOEYLIB\NAME`, which DVX keeps apart from the program files so
|
`CONFIG\APPS\VENDOR\NAME` (`VENDOR` its maker's, <<_a_project>>), which
|
||||||
a reinstall never overwrites it.
|
DVX keeps apart from the program files so a reinstall never overwrites it.
|
||||||
* *The web* keeps them apart too: `DATA/` is packed into the program, and
|
* *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
|
`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
|
(`games/hero.html` has `/games/hero/`), its `SAVES/` kept in the browser's
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,12 @@ That is often not the sprite rectangle:
|
||||||
|
|
||||||
* `x` is rounded down to an even pixel, and `width` grows to match, on every
|
* `x` is rounded down to an even pixel, and `width` grows to match, on every
|
||||||
port.
|
port.
|
||||||
* A compiled save on the Amiga, ST, and X68000 captures the *16-pixel-aligned
|
* On the Amiga, ST, and X68000 a save captures the *16-pixel-aligned region*
|
||||||
region* containing the sprite.
|
containing the sprite (or its visible part).
|
||||||
* A compiled save on the IIgs captures only the rows that hold an opaque
|
* A compiled save on the IIgs captures only the rows that hold an opaque
|
||||||
pixel, so `y` can be lower and `height` shorter than the sprite's.
|
pixel, so `y` can be lower and `height` shorter than the sprite's.
|
||||||
* A save of a sprite partly off the surface captures only the visible part.
|
* A save of a sprite partly off the surface captures only the visible part
|
||||||
|
(on the Amiga, ST, and X68000, the 16-pixel-aligned region holding it).
|
||||||
|
|
||||||
It is the recorded region you must not overwrite before restoring.
|
It is the recorded region you must not overwrite before restoring.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ 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_ROW` |40 |Tile columns on a surface.
|
||||||
|`JL_TILE_BLOCKS_PER_COL` |25 |Tile rows 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_NO_GLYPH` |0xFFFF |`jlDrawText` map entry meaning "skip this character".
|
||||||
|`JL_TILE_MAP_SKIP` |0xFF |`jlTileMapPaste` entry meaning "leave this cell alone".
|
|`JL_TILE_MAP_SKIP` |0xFF |`jlTileMapPaste` entry meaning "leave this cell alone"; in a tile layer's map, colour 0.
|
||||||
|
|`JL_TILE_LAYER_MAX_SIDE` |4096 |Largest tile layer map side, in tiles.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
[source,c]
|
[source,c]
|
||||||
|
|
@ -333,3 +334,141 @@ Any map index other than `JL_TILE_MAP_SKIP` must be a valid tileset index. The
|
||||||
batch does not know the tileset length and does *not* range-check it -- an
|
batch does not know the tileset length and does *not* range-check it -- an
|
||||||
out-of-range index reads past the end of your array.
|
out-of-range index reads past the end of your array.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
=== Scrolling Tile Layers
|
||||||
|
|
||||||
|
A tile layer is a map of tile indices, up to `JL_TILE_LAYER_MAX_SIDE` tiles a
|
||||||
|
side, seen through a band of the stage: whole tile rows, the full width (rows
|
||||||
|
outside the band are yours, for a status bar or a score). `jlTileLayerDraw`
|
||||||
|
fills 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.
|
||||||
|
|
||||||
|
The layer keeps the tiles around the view drawn, so a draw only draws the edge
|
||||||
|
tiles the move uncovered (a move of a whole view or more draws them all), then
|
||||||
|
copies the band. A draw covers the band and never reads what was there, so a
|
||||||
|
scrolling frame is: draw the layer, draw the sprites with `jlSpriteDraw` (no
|
||||||
|
save-under, since the next draw covers them), present.
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
static jlTileT tiles[64]; // from jlTileBankLoad
|
||||||
|
static uint8_t map[256 * 25]; // 256 x 25 tile indices
|
||||||
|
jlSpriteT *ship; // from jlSpriteCreate, compiled
|
||||||
|
jlTileLayerT *layer;
|
||||||
|
uint16_t x = 0;
|
||||||
|
|
||||||
|
layer = jlTileLayerCreate(tiles, map, 256, 25, 0, JL_TILE_BLOCKS_PER_COL);
|
||||||
|
for (;;) {
|
||||||
|
jlTileLayerDraw(jlStageGet(), layer, x, 0);
|
||||||
|
jlSpriteDraw(jlStageGet(), ship, 40, 96);
|
||||||
|
jlStagePresent();
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
The layer keeps a pointer to your tileset and map rather than copying them, so
|
||||||
|
both must outlive it. Change a cell with `jlTileLayerSetTile`, not by writing
|
||||||
|
the map: the layer would not see the write until that cell scrolled out of the
|
||||||
|
view and back. A tile whose pixels you change in the tileset is likewise only
|
||||||
|
seen where a cell showing it is set again.
|
||||||
|
|
||||||
|
What a layer costs in memory is its view: about 1.3 KB per tile row of the band,
|
||||||
|
plus one, on the Amiga, ST, and X68000 (35 KB for a full-height band), twice that on the
|
||||||
|
others, which keep a second copy of the view shifted by a pixel so an odd
|
||||||
|
origin copies whole bytes.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
Every whole-band scroll redraws the band and presents it, so a scrolling game
|
||||||
|
runs at the rate of a full-screen present plus a full-band copy. On the
|
||||||
|
slowest machines JoeyLib targets that is a few frames a second (the IIgs about
|
||||||
|
5.8, the plain ST about 8, a 386SX-16 under DOS about 6.5 in the UBER
|
||||||
|
benchmark; the Amiga and STe copy the band with their blitters, about 20), so
|
||||||
|
plan scrolling games around a band no taller than the game needs.
|
||||||
|
====
|
||||||
|
|
||||||
|
=== jlTileLayerCreate
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
jlTileLayerT *jlTileLayerCreate(const jlTileT *tiles, uint8_t *map,
|
||||||
|
uint16_t mapWidth, uint16_t mapHeight,
|
||||||
|
uint8_t viewBy, uint8_t viewHeight);
|
||||||
|
----
|
||||||
|
|
||||||
|
Makes a layer over `map`. Nothing is drawn until `jlTileLayerDraw`.
|
||||||
|
|
||||||
|
[cols="1,1,4", options="header"]
|
||||||
|
|===
|
||||||
|
|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
|
||||||
|
copied; `jlTileLayerSetTile` writes it.
|
||||||
|
|`mapWidth`, `mapHeight` |`uint16_t` |The map's size in tiles, 1 to
|
||||||
|
`JL_TILE_LAYER_MAX_SIDE`.
|
||||||
|
|`viewBy` |`uint8_t` |The band's first tile row on the stage.
|
||||||
|
|`viewHeight` |`uint8_t` |The band's height in tile rows, at least 1;
|
||||||
|
`viewBy + viewHeight` at most `JL_TILE_BLOCKS_PER_COL`.
|
||||||
|
|===
|
||||||
|
|
||||||
|
*Returns:* the layer, or `NULL` if an argument is `NULL` or out of range, or
|
||||||
|
memory runs out.
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
====
|
||||||
|
Like `jlTileMapPaste`, a layer does not range-check map indices against the
|
||||||
|
tileset: any index other than `JL_TILE_MAP_SKIP` must be a valid one.
|
||||||
|
====
|
||||||
|
|
||||||
|
=== jlTileLayerDestroy
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
void jlTileLayerDestroy(jlTileLayerT *layer);
|
||||||
|
----
|
||||||
|
|
||||||
|
Frees a layer, not its tileset or map. `NULL` is a no-op.
|
||||||
|
|
||||||
|
=== jlTileLayerDraw
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
void jlTileLayerDraw(jlSurfaceT *dst, jlTileLayerT *layer,
|
||||||
|
uint16_t x, uint16_t y);
|
||||||
|
----
|
||||||
|
|
||||||
|
Fills the layer's band of `dst` with the map seen from pixel `(x, y)`: the
|
||||||
|
band's top-left pixel shows map pixel `(x, y)`, each taken modulo the map's
|
||||||
|
size in pixels. Any pixel origin, with no alignment.
|
||||||
|
|
||||||
|
[cols="1,1,4", options="header"]
|
||||||
|
|===
|
||||||
|
|Parameter |Type |Description
|
||||||
|
|`dst` |`jlSurfaceT *` |Destination surface, normally the stage.
|
||||||
|
|`layer` |`jlTileLayerT *` |The layer.
|
||||||
|
|`x`, `y` |`uint16_t` |The origin, in map pixels.
|
||||||
|
|===
|
||||||
|
|
||||||
|
*Returns:* nothing.
|
||||||
|
|
||||||
|
=== jlTileLayerSetTile
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
void jlTileLayerSetTile(jlTileLayerT *layer, uint16_t tx, uint16_t ty,
|
||||||
|
uint8_t tile);
|
||||||
|
----
|
||||||
|
|
||||||
|
Sets map cell `(tx, ty)` to `tile`. The next draw shows it.
|
||||||
|
|
||||||
|
[cols="1,1,4", options="header"]
|
||||||
|
|===
|
||||||
|
|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.
|
||||||
|
|===
|
||||||
|
|
||||||
|
*Returns:* nothing.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@ type: what you author, which tool bakes it, and which API call loads it.
|
||||||
|`.tbk`
|
|`.tbk`
|
||||||
|`jlTileBankLoad`, `jlTileBankLoadToSurface`
|
|`jlTileBankLoad`, `jlTileBankLoadToSurface`
|
||||||
|
|
||||||
|
|A Tiled map (TMX or TMJ) with indexed tilesets
|
||||||
|
|`tiled2joey.py`, then `assetbake.py --type tile` for its sheet
|
||||||
|
|a `.map` per layer, and a `.tbk`
|
||||||
|
|`jlTileMapPaste` (the map read with `jlDataOpen`)
|
||||||
|
|
||||||
|Indexed PNG sprite sheet
|
|Indexed PNG sprite sheet
|
||||||
|`assetbake.py --type sprite`
|
|`assetbake.py --type sprite`
|
||||||
|`.spr`
|
|`.spr`
|
||||||
|
|
@ -69,6 +74,12 @@ run on Linux, and all but OpenMPT are native there.
|
||||||
JoeyLib palette holds exactly, 4 bits a gun. The sheet rules are in
|
JoeyLib palette holds exactly, 4 bits a gun. The sheet rules are in
|
||||||
<<_authoring_the_png>>.
|
<<_authoring_the_png>>.
|
||||||
|
|
||||||
|
|Tile maps
|
||||||
|
|*Tiled* (GPLv2; what you make with it is yours), a map editor that places
|
||||||
|
tiles from tilesets in layers.
|
||||||
|
|Orthogonal maps of 8x8 tiles from indexed tilesets, then `tiled2joey.py`
|
||||||
|
(<<_maps_from_tiled>>).
|
||||||
|
|
||||||
|Program icons
|
|Program icons
|
||||||
|Any of the above, saving an RGBA PNG with transparency.
|
|Any of the above, saving an RGBA PNG with transparency.
|
||||||
|One picture for every system (<<_icons>>).
|
|One picture for every system (<<_icons>>).
|
||||||
|
|
@ -132,6 +143,61 @@ loader refuses a file baked for a different target rather than drawing garbage.
|
||||||
`--target` is required for tiles, and accepts `amiga`, `atarist`, `dos`,
|
`--target` is required for tiles, and accepts `amiga`, `atarist`, `dos`,
|
||||||
`iigs`, or `x68000`.
|
`iigs`, or `x68000`.
|
||||||
|
|
||||||
|
==== Maps from Tiled
|
||||||
|
|
||||||
|
`tools/tiled2joey.py` turns a map made in Tiled into what a JoeyLib program
|
||||||
|
pastes: a tile sheet, which `assetbake.py` bakes into a tile bank for each
|
||||||
|
port, and a map for each tile layer, which `jlTileMapPaste` takes as it is:
|
||||||
|
|
||||||
|
[source,console]
|
||||||
|
----
|
||||||
|
$ python3 tools/tiled2joey.py --header build/level1.h level1.tmx \
|
||||||
|
build/level1.png build/level1
|
||||||
|
tiled2joey: 60x25 map, 87 tiles, 14 colours, layers: ground.map, detail.map
|
||||||
|
$ python3 tools/assetbake/assetbake.py --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
|
||||||
|
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
|
||||||
|
flipped or rotated is baked as a tile of its own (JoeyLib draws tiles only
|
||||||
|
as they are), so a map can use up to 255 distinct tiles and orientations
|
||||||
|
between its layers.
|
||||||
|
* *Each layer* becomes `NAME.map` in the folder given: width x height bytes,
|
||||||
|
row after row, each a tile's index in the bank or `JL_TILE_MAP_SKIP` where
|
||||||
|
the layer is empty, so pasting a layer over another leaves the one below
|
||||||
|
showing through. The files are the same on every port.
|
||||||
|
* `--header` writes the map's size and tile count as C constants, and lists
|
||||||
|
each layer's file. Object layers, image layers, tile animations, and tile
|
||||||
|
properties are not carried over.
|
||||||
|
|
||||||
|
A map no bigger than the screen pastes in one call. A bigger one -- a level
|
||||||
|
the program shows a screen of at a time -- pastes a row of tiles a call,
|
||||||
|
starting each row at its place in the whole map:
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
for (row = 0; row < 25u; row++) {
|
||||||
|
jlTileMapPaste(jlStageGet(), 0, (uint8_t)row, 40, 1, tiles, &level[(top + row) * LEVEL1_WIDTH + left]);
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
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
|
||||||
|
Tiled.
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
layer = jlTileLayerCreate(tiles, level, LEVEL1_WIDTH, LEVEL1_HEIGHT, 0, 25);
|
||||||
|
----
|
||||||
|
|
||||||
==== Baking Sprites
|
==== Baking Sprites
|
||||||
|
|
||||||
[source,console]
|
[source,console]
|
||||||
|
|
@ -399,11 +465,12 @@ $(call programDataDir,MYGAME)/font.tbk: assets/font.png
|
||||||
----
|
----
|
||||||
|
|
||||||
Tile banks and `.spc` files differ per platform, while `.spr`, `.jym`, and
|
Tile banks and `.spc` files differ per platform, while `.spr`, `.jym`, and
|
||||||
`.sfx` files are shared: `ASSET_PLATFORM` names the target a bake is for (DVX
|
`.sfx` files are shared, as are the `.map` files `tiled2joey.py` writes:
|
||||||
and every hosted port -- SDL3, the web, Win32, Win16, the PSP, Haiku, and
|
`ASSET_PLATFORM` names the target a bake is for (DVX and every hosted port --
|
||||||
BeOS -- take the DOS bakes; DVX and Win16 run the x86 code of a `.spc`, the
|
SDL3, the web, macOS, Win32, Win16, the PSP, Haiku, and BeOS -- take the DOS
|
||||||
others use only its cels; the STe build takes the ST's) and `SPRITEBAKE` the
|
bakes; DVX and Win16 run the x86 code of a `.spc`, the others use only its
|
||||||
matching host `spritebake`.
|
cels; the STe build takes the ST's) and `SPRITEBAKE` the matching host
|
||||||
|
`spritebake`.
|
||||||
|
|
||||||
`examples/tilebank` is a working reference: one rule in `make/examples.mk`
|
`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`),
|
bakes the same PNG for each target into its `DATA/tilebank` (`TILEBANK_DATA`),
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ The whole Makefile for a program with no data files:
|
||||||
JOEYLIB ?= ../joeylib
|
JOEYLIB ?= ../joeylib
|
||||||
include $(JOEYLIB)/make/project.mk
|
include $(JOEYLIB)/make/project.mk
|
||||||
|
|
||||||
|
DVX_VENDOR := acme
|
||||||
|
DVX_AUTHOR := Acme Games
|
||||||
|
MACOS_ID_PREFIX := com.example.acme
|
||||||
|
|
||||||
PROGRAMS := MYGAME
|
PROGRAMS := MYGAME
|
||||||
MYGAME_SRCS := mygame.c world.c
|
MYGAME_SRCS := mygame.c world.c
|
||||||
MYGAME_TITLE := My Game
|
MYGAME_TITLE := My Game
|
||||||
|
|
@ -39,7 +43,16 @@ include $(JOEYLIB)/make/programs.mk
|
||||||
|
|
||||||
`make/project.mk` comes first and sets up the target being built; the
|
`make/project.mk` comes first and sets up the target being built; the
|
||||||
description comes next and may use what it set up (see <<_data_files>>);
|
description comes next and may use what it set up (see <<_data_files>>);
|
||||||
`make/programs.mk` comes last and makes the rules. Run make in the project's
|
`make/programs.mk` comes last and makes the rules.
|
||||||
|
|
||||||
|
The first three lines say whose the programs are, which only you can say:
|
||||||
|
JoeyLib never makes it up, and building for DVX or macOS stops until they
|
||||||
|
are set. `DVX_VENDOR` is the folder your programs install in under DVX's
|
||||||
|
`APPS\` (1 to 8 letters, digits, or `_`; DVX names a package `vendor.name`)
|
||||||
|
and `DVX_AUTHOR` the author DVX shows; `MACOS_ID_PREFIX`, a reverse-DNS name
|
||||||
|
of yours, starts each Mac app's identifier, which ends in the program's name
|
||||||
|
(`com.example.acme.MYGAME`). JoeyLib's own examples carry their author's
|
||||||
|
(`kpunch`, `com.kangaroopunch.joeylib`). Run make in the project's
|
||||||
directory -- the paths in the Makefile are relative to it -- or with `make -C`:
|
directory -- the paths in the Makefile are relative to it -- or with `make -C`:
|
||||||
|
|
||||||
[source,console]
|
[source,console]
|
||||||
|
|
@ -71,11 +84,14 @@ with `MYGAME_IIGS_GAME_DISK` the disks `build/iigs/bin/MYGAME.2mg` and
|
||||||
(<<_the_atari_st_family>>); same toolchain and baked data as the ST
|
(<<_the_atari_st_family>>); same toolchain and baked data as the ST
|
||||||
|MS-DOS |`build/dos/bin/MYGAME.EXE`, with the DPMI server built in
|
|MS-DOS |`build/dos/bin/MYGAME.EXE`, with the DPMI server built in
|
||||||
|Sharp X68000 |`build/x68000/bin/MYGAME.X`
|
|Sharp X68000 |`build/x68000/bin/MYGAME.X`
|
||||||
|DVX |`build/dvx/bin/apps/joeylib/mygame/`: `mygame.app`, `mygame.hlp`, and its
|
|DVX |`build/dvx/bin/apps/acme/mygame/`: `mygame.app`, `mygame.hlp`, and its
|
||||||
`DATA/`
|
`DATA/`
|
||||||
|SDL3 |`build/sdl3/bin/MYGAME`, a native program with SDL 3 linked in
|
|SDL3 |`build/sdl3/bin/MYGAME`, a native program with SDL 3 linked in
|
||||||
|Web |`build/web/bin/MYGAME/`: `MYGAME.html`, `.js`, `.wasm`, and `.data`
|
|Web |`build/web/bin/MYGAME/`: `MYGAME.html`, `.js`, `.wasm`, and `.data`
|
||||||
(its `DATA/`, packed in), and `icon.png`
|
(its `DATA/`, packed in), and `icon.png`
|
||||||
|
|macOS |`build/macos/bin/MYGAME.app`, a universal app: the program in
|
||||||
|
`Contents/MacOS/`, its `DATA/` and icon in `Contents/Resources/`; the program
|
||||||
|
it is packed from in `build/macos/link/MYGAME`
|
||||||
|Win32 |`build/win32/bin/MYGAME.EXE`, a 32-bit Windows program
|
|Win32 |`build/win32/bin/MYGAME.EXE`, a 32-bit Windows program
|
||||||
|Win16 |`build/win16/bin/MYGAME.EXE`, a Windows 3.x program
|
|Win16 |`build/win16/bin/MYGAME.EXE`, a Windows 3.x program
|
||||||
|PSP |`build/psp/bin/MYGAME/`: `EBOOT.PBP` and its `DATA/`, a folder for the
|
|PSP |`build/psp/bin/MYGAME/`: `EBOOT.PBP` and its `DATA/`, a folder for the
|
||||||
|
|
@ -134,12 +150,12 @@ probes for gates).
|
||||||
|`ID_PORTS` |The targets it builds for. Every target when unset.
|
|`ID_PORTS` |The targets it builds for. Every target when unset.
|
||||||
|`ID_NAME` |Its file name without the extension (see <<_naming>>).
|
|`ID_NAME` |Its file name without the extension (see <<_naming>>).
|
||||||
|`ID_FOLDER` |Set: it ships in a folder of its own with its `DATA/` (Amiga,
|
|`ID_FOLDER` |Set: it ships in a folder of its own with its `DATA/` (Amiga,
|
||||||
ST, DOS, X68000, SDL3, Win32, Win16; on the IIgs, DVX, the web, and the PSP
|
ST, DOS, X68000, SDL3, Win32, Win16; on the IIgs, DVX, the web, macOS, and
|
||||||
every program is packaged apart anyway).
|
the PSP every program is packaged apart anyway).
|
||||||
|`ID_BINDIR` |Build it there instead of `bin/`.
|
|`ID_BINDIR` |Build it there instead of `bin/`.
|
||||||
|`ID_DATA` |Its data files (<<_data_files>>).
|
|`ID_DATA` |Its data files (<<_data_files>>).
|
||||||
|`ID_TITLE` |DVX: its window title. PSP: its title in the PSP's menu (by
|
|`ID_TITLE` |DVX: its window title. PSP: its title in the PSP's menu.
|
||||||
default its name).
|
macOS: its app's display name (`CFBundleDisplayName`). By default its name.
|
||||||
|`ID_ICON` |Its icon: a picture (a PNG, best square, transparent around the
|
|`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
|
figure), baked for every target whose system shows programs by an icon
|
||||||
(<<_icons>>). JoeyLib's kangaroo by default.
|
(<<_icons>>). JoeyLib's kangaroo by default.
|
||||||
|
|
@ -160,9 +176,9 @@ MYGAME_X68000_LDFLAGS := -s
|
||||||
MYGAME_IIGS_DATA := # nothing staged on the IIgs
|
MYGAME_IIGS_DATA := # nothing staged on the IIgs
|
||||||
----
|
----
|
||||||
|
|
||||||
DVX's help and resource texts call a program "a `$(DVX_KIND)` program" and
|
DVX's help and resource texts call a program "a `$(DVX_KIND)` program"
|
||||||
name `$(DVX_AUTHOR)` as its author (both `JoeyLib` by default); `DVX_VENDOR`
|
(`JoeyLib` by default) and name `$(DVX_AUTHOR)` as its author; `DVX_VENDOR`
|
||||||
(`joeylib`) is the folder it installs in under `APPS\`. Set them in the
|
is the folder it installs in under `APPS\` (<<_a_project>>). Set them in the
|
||||||
description.
|
description.
|
||||||
|
|
||||||
=== Data Files
|
=== Data Files
|
||||||
|
|
@ -179,10 +195,10 @@ set up:
|
||||||
|`$(call programDataDir,ID)` |Where its `DATA/` files go on this target.
|
|`$(call programDataDir,ID)` |Where its `DATA/` files go on this target.
|
||||||
|`$(call programOptionalDir,ID)` |Where the files go that it can run without
|
|`$(call programOptionalDir,ID)` |Where the files go that it can run without
|
||||||
(<<_disk_images_and_packaging>>).
|
(<<_disk_images_and_packaging>>).
|
||||||
|`PLATFORM` |The target: `iigs`, `amiga`, `atarist`, `ste`, `dos`, `x68000`, `dvx`, `sdl3`, `web`, `win32`, `win16`, `psp`, `haiku`, `haiku32`, `beos`.
|
|`PLATFORM` |The target: `iigs`, `amiga`, `atarist`, `ste`, `dos`, `x68000`, `dvx`, `sdl3`, `web`, `macos`, `win32`, `win16`, `psp`, `haiku`, `haiku32`, `beos`.
|
||||||
|`ASSET_PLATFORM` |The target its bakes are for. DVX shows the DOS stage and
|
|`ASSET_PLATFORM` |The target its bakes are for. DVX shows the DOS stage and
|
||||||
runs x86 code, so it takes the DOS bakes; the hosted ports' stage (SDL3, the
|
runs x86 code, so it takes the DOS bakes; the hosted ports' stage (SDL3, the
|
||||||
web, Win32, Win16, the PSP, Haiku, BeOS) is the same chunky one, so they take
|
web, macOS, Win32, Win16, the PSP, Haiku, BeOS) is the same chunky one, so they take
|
||||||
them too: Win16 runs a pre-compiled sprite bank's x86 code as DVX does, and
|
them too: Win16 runs a pre-compiled sprite bank's x86 code as DVX does, and
|
||||||
the others take its cels without their code. The STe build takes the ST's.
|
the others take its cels without their code. The STe build takes the ST's.
|
||||||
|`SPRITEBAKE` |The host `spritebake` for this target (its rule is
|
|`SPRITEBAKE` |The host `spritebake` for this target (its rule is
|
||||||
|
|
@ -236,9 +252,10 @@ program's id:
|
||||||
|Amiga |Mixed case, no extension: the id Capitalised unless `ID_AMIGA_NAME`
|
|Amiga |Mixed case, no extension: the id Capitalised unless `ID_AMIGA_NAME`
|
||||||
says otherwise |`Mygame`
|
says otherwise |`Mygame`
|
||||||
|Sharp X68000 |Uppercase 8.3, `.X` |`MYGAME.X`
|
|Sharp X68000 |Uppercase 8.3, `.X` |`MYGAME.X`
|
||||||
|DVX |Lowercase, 8 characters, a folder of its own |`apps/joeylib/mygame/mygame.app`
|
|DVX |Lowercase, 8 characters, a folder of its own |`apps/acme/mygame/mygame.app`
|
||||||
|SDL3 |The id, no extension |`MYGAME`
|
|SDL3 |The id, no extension |`MYGAME`
|
||||||
|Web |The id, a folder of its own |`MYGAME/MYGAME.html`
|
|Web |The id, a folder of its own |`MYGAME/MYGAME.html`
|
||||||
|
|macOS |The id, an app |`MYGAME.app`
|
||||||
|Win32 |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|
|Win32 |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|
||||||
|Win16 |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|
|Win16 |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|
||||||
|PSP |The id, a folder of its own holding `EBOOT.PBP` |`MYGAME/EBOOT.PBP`
|
|PSP |The id, a folder of its own holding `EBOOT.PBP` |`MYGAME/EBOOT.PBP`
|
||||||
|
|
@ -276,10 +293,10 @@ needs to be on the disk.
|
||||||
`Name.info` beside it is its Workbench icon, from which it starts too.
|
`Name.info` beside it is its Workbench icon, from which it starts too.
|
||||||
|
|
||||||
*DVX.* Every program is a folder laid out as it installs in DVX:
|
*DVX.* Every program is a folder laid out as it installs in DVX:
|
||||||
`build/dvx/bin/apps/joeylib/mygame/` holds `mygame.app`, its help file
|
`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
|
`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\JOEYLIB\` and start it from DVX, or let `joey-run` do it
|
`APPS\ACME\` and start it from DVX, or let `joey-run` do it
|
||||||
(<<_dvx_programs>>).
|
(<<_dvx_programs>>).
|
||||||
|
|
||||||
*SDL3.* A program is one native executable with SDL 3 linked in statically,
|
*SDL3.* A program is one native executable with SDL 3 linked in statically,
|
||||||
|
|
@ -299,6 +316,13 @@ Win386 supervisor and calling only what Windows 3.1 has (USER, GDI, KERNEL,
|
||||||
and MMSYSTEM), so its folder, with its `DATA/`, is all it needs; it ships the
|
and MMSYSTEM), so its folder, with its `DATA/`, is all it needs; it ships the
|
||||||
way a DOS program does.
|
way a DOS program does.
|
||||||
|
|
||||||
|
*macOS.* A program is its app, `build/macos/bin/MYGAME.app`, which holds
|
||||||
|
everything it needs: the program (SDL 3 linked in, for Apple silicon and
|
||||||
|
Intel Macs both), its `DATA/`, its icon, and the `Info.plist` that names
|
||||||
|
them. Copy the app to a Mac, or zip it to send it; a Mac opens it from
|
||||||
|
anywhere. Its identifier is `MACOS_ID_PREFIX` and its name
|
||||||
|
(`com.example.acme.MYGAME`, <<_a_project>>).
|
||||||
|
|
||||||
*PSP.* A program is its folder, `build/psp/bin/MYGAME/`: `EBOOT.PBP` (the
|
*PSP.* A program is its folder, `build/psp/bin/MYGAME/`: `EBOOT.PBP` (the
|
||||||
program, SDL 3 linked in, packed as the PSPSDK packs it, with its icon and no
|
program, SDL 3 linked in, packed as the PSPSDK packs it, with its icon and no
|
||||||
other pictures) and its `DATA/`. Copy the folder into `PSP/GAME/` on a memory
|
other pictures) and its `DATA/`. Copy the folder into `PSP/GAME/` on a memory
|
||||||
|
|
@ -340,13 +364,19 @@ colours, bound into the `.EXE`.
|
||||||
|Win32 |Explorer's and the window's: 16, 32, and 48 pixels, each in 256
|
|Win32 |Explorer's and the window's: 16, 32, and 48 pixels, each in 256
|
||||||
colours and in 32-bit, linked into the `.EXE`.
|
colours and in 32-bit, linked into the `.EXE`.
|
||||||
|Haiku, BeOS |Tracker's and the Deskbar's: 32x32 and 16x16 in the BeOS
|
|Haiku, BeOS |Tracker's and the Deskbar's: 32x32 and 16x16 in the BeOS
|
||||||
palette, in the program's resources. Tracker shows a file by the icons in its
|
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
|
||||||
|
without). Tracker shows a file by the icons in its
|
||||||
attributes, which a program built on another machine does not have; the
|
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
|
program copies them across the first time it runs, so Tracker shows it by its
|
||||||
icon from then on (or at once, after `mimeset -F NAME`).
|
icon from then on (or at once, after `mimeset -F NAME`).
|
||||||
|PSP |The menu's: `ICON0` in the EBOOT, 144x80.
|
|PSP |The menu's: `ICON0` in the EBOOT, 144x80.
|
||||||
|SDL3 |The window's, 64x64, which the desktop shows where it shows one (title
|
|SDL3 |The window's, 64x64, which the desktop shows where it shows one (title
|
||||||
bar, task bar, dock).
|
bar, task bar, dock).
|
||||||
|
|macOS |Finder's and the Dock's: `icon.icns` in the app's `Contents/Resources/`,
|
||||||
|
16 to 128 pixels and twice that for Retina screens (up to 512, a picture that
|
||||||
|
big or pixel art).
|
||||||
|Web |The page's: `icon.png`, 32x32, beside it.
|
|Web |The page's: `icon.png`, 32x32, beside it.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
JoeyLib cross-compiles for five machines and for DVX, a DOS desktop whose
|
JoeyLib cross-compiles for five machines and for DVX, a DOS desktop whose
|
||||||
programs run in windows (see <<_dvx_programs>>), builds native programs for
|
programs run in windows (see <<_dvx_programs>>), builds native programs for
|
||||||
the machine you work on through SDL3 (see <<_sdl3_programs>>), builds web
|
the machine you work on through SDL3 (see <<_sdl3_programs>>), builds web
|
||||||
pages through Emscripten (see <<_web_programs>>), and builds PSP homebrew
|
pages through Emscripten (see <<_web_programs>>), builds Mac apps through
|
||||||
|
zig and Apple's SDK (see <<_macos_programs>>), and builds PSP homebrew
|
||||||
through pspdev (see <<_psp_programs>>). It builds on Linux x86-64,
|
through pspdev (see <<_psp_programs>>). It builds on Linux x86-64,
|
||||||
and on Windows inside WSL2. One script installs every cross toolchain, pinned
|
and on Windows inside WSL2. One script installs every cross toolchain, pinned
|
||||||
to a known-good version and checked against a published checksum, into
|
to a known-good version and checked against a published checksum, into
|
||||||
|
|
@ -111,12 +112,13 @@ A target is a component name from `list`, or a group:
|
||||||
| Everything needed to build for one port: `iigs`, `amiga`, `atarist`, `dos`,
|
| Everything needed to build for one port: `iigs`, `amiga`, `atarist`, `dos`,
|
||||||
`x68000`, `dvx`, `sdl3` (SDL 3, built as a static library that every SDL3
|
`x68000`, `dvx`, `sdl3` (SDL 3, built as a static library that every SDL3
|
||||||
program links in, so it runs without SDL installed), `web` (Emscripten,
|
program links in, so it runs without SDL installed), `web` (Emscripten,
|
||||||
through emsdk, and SDL 3 built with it), `win32` (llvm-mingw; its
|
through emsdk, and SDL 3 built with it), `macos` (zig, Apple's macOS SDK,
|
||||||
emulator is a portable Wine), `win16` (Open Watcom; its emulator is Windows
|
and SDL 3 built with them; there is no emulator: nothing here runs Mac
|
||||||
for Workgroups 3.11 in DOSBox Staging, set up from your own install disc, see
|
programs), `win32` (llvm-mingw; its emulator is a portable Wine), `win16`
|
||||||
<<Firmware that is not downloaded>>) or `psp` (pspdev's prebuilt release,
|
(Open Watcom; its emulator is Windows for Workgroups 3.11 in DOSBox Staging,
|
||||||
and SDL 3 built with it; its emulator is PPSSPP's headless build, patched for
|
set up from your own install disc, see <<Firmware that is not downloaded>>),
|
||||||
the gates).
|
or `psp` (pspdev's prebuilt release, and SDL 3 built with it; its emulator
|
||||||
|
is PPSSPP's headless build, patched for the gates).
|
||||||
|
|
||||||
| `emulator:PORT`
|
| `emulator:PORT`
|
||||||
| Everything needed to run that port's programs: the emulator and its
|
| Everything needed to run that port's programs: the emulator and its
|
||||||
|
|
@ -214,6 +216,7 @@ $ make x68000 # Sharp X68000 only
|
||||||
$ make dvx # DVX only
|
$ make dvx # DVX only
|
||||||
$ make sdl3 # native programs through SDL3 only
|
$ make sdl3 # native programs through SDL3 only
|
||||||
$ make web # web pages through Emscripten only
|
$ make web # web pages through Emscripten only
|
||||||
|
$ make macos # universal Mac apps only
|
||||||
$ make win32 # Windows programs only
|
$ make win32 # Windows programs only
|
||||||
$ make win16 # Windows 3.x programs only
|
$ make win16 # Windows 3.x programs only
|
||||||
$ make psp # PSP homebrew (EBOOT.PBP) only
|
$ make psp # PSP homebrew (EBOOT.PBP) only
|
||||||
|
|
@ -457,6 +460,12 @@ server. With `--headless` it runs in headless Chromium instead, at one canvas
|
||||||
pixel per stage pixel, so a `--snapshot` is the stage. Its `DATA/` is packed
|
pixel per stage pixel, so a `--snapshot` is the stage. Its `DATA/` is packed
|
||||||
in when it is built, so `--data` does not apply.
|
in when it is built, so `--data` does not apply.
|
||||||
|
|
||||||
|
==== macOS
|
||||||
|
|
||||||
|
Nothing here runs a Mac program: copy its app, `build/macos/bin/NAME.app`, to
|
||||||
|
a Mac and open it there (<<_macos_programs>>). `joey-run` knows one when it
|
||||||
|
sees it and says so.
|
||||||
|
|
||||||
==== Win32
|
==== Win32
|
||||||
|
|
||||||
A Windows program runs under Wine: in a window on your display, with its
|
A Windows program runs under Wine: in a window on your display, with its
|
||||||
|
|
@ -665,6 +674,7 @@ built for every platform:
|
||||||
|`keys` |Typed-character input and the key predicates.
|
|`keys` |Typed-character input and the key predicates.
|
||||||
|`joy` |Joystick axes, buttons, and recalibration.
|
|`joy` |Joystick axes, buttons, and recalibration.
|
||||||
|`sprite` |Sprite loading, compiling, save-under, and restore.
|
|`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.
|
|`audio` |Tracker music, `.MOD` playback, digital sound effects, a streamed sound, and the noise generator.
|
||||||
|`save` |Save files and the disk-space query.
|
|`save` |Save files and the disk-space query.
|
||||||
|`tilebank` |Loading a baked tile bank both ways, and a bank for another machine refused.
|
|`tilebank` |Loading a baked tile bank both ways, and a bank for another machine refused.
|
||||||
|
|
@ -679,9 +689,10 @@ game, and JoeyAGI, an interpreter for Sierra's AGI adventures. Each builds
|
||||||
from its own directory against a JoeyLib checkout, as any program of yours
|
from its own directory against a JoeyLib checkout, as any program of yours
|
||||||
would (<<_building_your_program>>).
|
would (<<_building_your_program>>).
|
||||||
|
|
||||||
On DVX every example is a folder of its own under `build/dvx/bin/apps/joeylib/`
|
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 (<<_dvx_programs>>). On SDL3 every example is a native
|
||||||
program in `build/sdl3/bin/`, beside the shared `DATA/` (<<_sdl3_programs>>),
|
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/`
|
on the web a page in a folder of its own, `build/web/bin/NAME/`
|
||||||
(<<_web_programs>>), and on the PSP a folder for `PSP/GAME/`,
|
(<<_web_programs>>), on macOS an app, `build/macos/bin/NAME.app`, with its
|
||||||
|
`DATA/` inside (<<_macos_programs>>), and on the PSP a folder for `PSP/GAME/`,
|
||||||
`build/psp/bin/NAME/` with `EBOOT.PBP` and its `DATA/` (<<_psp_programs>>).
|
`build/psp/bin/NAME/` with `EBOOT.PBP` and its `DATA/` (<<_psp_programs>>).
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ The API is uniform; these are the places where what you get differs.
|
||||||
|
|
||||||
[cols="2,2,2,2,2,2,2,2,2", options="header"]
|
[cols="2,2,2,2,2,2,2,2,2", options="header"]
|
||||||
|===
|
|===
|
||||||
|Feature |IIgs |Amiga |Atari ST |MS-DOS |X68000 |DVX |SDL3, Web, Win32, PSP, Haiku, BeOS |Win16
|
|Feature |IIgs |Amiga |Atari ST |MS-DOS |X68000 |DVX |SDL3, Web, macOS, Win32, PSP, Haiku, BeOS |Win16
|
||||||
|
|
||||||
|Frame rate
|
|Frame rate
|
||||||
|60 Hz (50 on a PAL machine)
|
|60 Hz (50 on a PAL machine)
|
||||||
|
|
@ -271,8 +271,8 @@ The API is uniform; these are the places where what you get differs.
|
||||||
|none
|
|none
|
||||||
|none
|
|none
|
||||||
|Program Manager's
|
|Program Manager's
|
||||||
|the window's (SDL3), the page's (web), Explorer's (Win32), the menu's
|
|the window's (SDL3), the page's (web), Finder's and the Dock's (macOS),
|
||||||
(PSP), Tracker's (Haiku, BeOS)
|
Explorer's (Win32), the menu's (PSP), Tracker's (Haiku, BeOS)
|
||||||
|Program Manager's
|
|Program Manager's
|
||||||
|
|
||||||
|Tone voices (`jlAudioTone` is voice 0)
|
|Tone voices (`jlAudioTone` is voice 0)
|
||||||
|
|
@ -292,7 +292,7 @@ The API is uniform; these are the places where what you get differs.
|
||||||
|analog
|
|analog
|
||||||
|digital
|
|digital
|
||||||
|analog (DVX calibrates)
|
|analog (DVX calibrates)
|
||||||
|SDL's gamepads and joysticks (SDL3, web); Windows' joysticks (Win32); the system's joysticks
|
|SDL's gamepads and joysticks (SDL3, web, macOS); Windows' joysticks (Win32); the system's joysticks
|
||||||
(Haiku, BeOS); the PSP's own controls
|
(Haiku, BeOS); the PSP's own controls
|
||||||
|Windows' joysticks (Control Panel calibrates)
|
|Windows' joysticks (Control Panel calibrates)
|
||||||
|
|
||||||
|
|
@ -313,7 +313,7 @@ The API is uniform; these are the places where what you get differs.
|
||||||
|COM1..COM4
|
|COM1..COM4
|
||||||
|RS-232C (needs RSDRV.SYS)
|
|RS-232C (needs RSDRV.SYS)
|
||||||
|COM1..COM4 (DVX's library)
|
|COM1..COM4 (DVX's library)
|
||||||
|Win32: COM1 and up; SDL3 (Linux), Haiku, BeOS: terminal devices; web, PSP: none
|
|Win32: COM1 and up; SDL3 (Linux), macOS, Haiku, BeOS: terminal devices; web, PSP: none
|
||||||
|COM1..COM9
|
|COM1..COM9
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
@ -612,6 +612,9 @@ names the ports, says how they differ, and gives the reason.
|
||||||
ports that interpret sprites use only its pixels.
|
ports that interpret sprites use only its pixels.
|
||||||
* *Web saves are the browser's.* A browser may clear a site's stored data to
|
* *Web saves are the browser's.* A browser may clear a site's stored data to
|
||||||
make room, and a private window keeps nothing once it closes.
|
make room, and a private window keeps nothing once it closes.
|
||||||
|
* *macOS: saves in Application Support.* An app's own folder may not be
|
||||||
|
writable, so `SAVES/` and the log go in the user's
|
||||||
|
`~/Library/Application Support/NAME/` (<<_macos_programs>>).
|
||||||
|
|
||||||
[[portsDifferMemory]]
|
[[portsDifferMemory]]
|
||||||
==== Sprites and Memory
|
==== Sprites and Memory
|
||||||
|
|
@ -705,9 +708,9 @@ Nothing in the API differs between them:
|
||||||
|
|
||||||
|Fills and copies
|
|Fills and copies
|
||||||
|The CPU
|
|The CPU
|
||||||
|The BLiTTER, for fills of 8 or more rows (24 for one 16-pixel column) and
|
|The BLiTTER, for fills of 8 or more rows (24 for one 16-pixel column),
|
||||||
copies of 4 or more rows; smaller work stays on the CPU, which is faster
|
copies of 4 or more rows, and a tile layer's band; smaller work stays on
|
||||||
there
|
the CPU, which is faster there
|
||||||
|
|
||||||
|CPU clock
|
|CPU clock
|
||||||
|As the machine is (8 MHz; TOS's setting on a Mega STe)
|
|As the machine is (8 MHz; TOS's setting on a Mega STe)
|
||||||
|
|
@ -788,7 +791,7 @@ hashes are DOS's -- and the rest behaves like this:
|
||||||
* *Files.* DVX's programs share one current directory, so JoeyLib roots its
|
* *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
|
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
|
`APPS`), while `SAVES/` and `joeylog.txt` go to the program's own settings
|
||||||
folder, `CONFIG\APPS\JOEYLIB\NAME`, which DVX keeps apart from the program
|
folder, `CONFIG\APPS\VENDOR\NAME`, which DVX keeps apart from the program
|
||||||
files so a reinstall never overwrites them.
|
files so a reinstall never overwrites them.
|
||||||
* *Sound* goes through DVX's sound and music libraries, mixed with every
|
* *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
|
other program's: `.MOD` music, SFX in stereo, and the tone voices and the
|
||||||
|
|
@ -918,6 +921,41 @@ are DOS's here too. Everything in <<_sdl3_programs>> holds, except:
|
||||||
`make web` builds every example as a page, and `./joey-run --port web NAME`
|
`make web` builds every example as a page, and `./joey-run --port web NAME`
|
||||||
serves its folder on this machine and opens it in your browser.
|
serves its folder on this machine and opens it in your browser.
|
||||||
|
|
||||||
|
=== macOS Programs
|
||||||
|
|
||||||
|
The macOS port is the SDL3 port built as Mac apps, on Linux: the same hosted
|
||||||
|
core, the same backend, the same stage (UBER's hashes are DOS's). It is
|
||||||
|
compiled with zig against Apple's macOS SDK, once for Apple silicon and once
|
||||||
|
for Intel, the two joined into one universal program that runs on either,
|
||||||
|
on macOS 13 (Ventura) or newer, with SDL 3 built the same way and linked in
|
||||||
|
(the `macos` installer components: zig, the SDK, and SDL 3). Everything in
|
||||||
|
<<_sdl3_programs>> holds, except:
|
||||||
|
|
||||||
|
* *An app.* A program is `build/macos/bin/NAME.app`: the program in
|
||||||
|
`Contents/MacOS/`, its `DATA/` and its icon in `Contents/Resources/`, which
|
||||||
|
is its folder and the current directory, and the `Info.plist` that names
|
||||||
|
them. Copy the app to a Mac, or zip it to send it; it opens from anywhere.
|
||||||
|
* *Saves in Application Support.* `SAVES/` and `joeylog.txt` go in
|
||||||
|
`~/Library/Application Support/NAME/`, not beside the program: an app in
|
||||||
|
`/Applications` may not be writable by its user, and macOS runs a
|
||||||
|
downloaded app from a read-only copy until it is moved. `jlDiskFree` is
|
||||||
|
the free space there.
|
||||||
|
* *Retina screens.* The window draws at the screen's own resolution, so the
|
||||||
|
stage is scaled up once, sharply, and not blurred again by the system.
|
||||||
|
* *Serial* through termios, as on Linux: a Mac has no serial port of its
|
||||||
|
own, so the ports are USB adapters' call-out devices (`/dev/cu.*`, the
|
||||||
|
Mac's own Bluetooth and debug devices left out), in name order, or the
|
||||||
|
device `JOEYLIB_SERIAL` names.
|
||||||
|
* *Untested on a Mac.* Nothing here runs Mac programs: `make verify` builds
|
||||||
|
every example and checks every app is one macOS opens -- both processors'
|
||||||
|
programs, the Apple silicon one signed, for macOS 13 on, linking only the
|
||||||
|
system's own libraries, with its `Info.plist` and icon
|
||||||
|
(`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
|
||||||
|
and says it runs on a Mac.
|
||||||
|
|
||||||
=== PSP Programs
|
=== PSP Programs
|
||||||
|
|
||||||
The PSP port is the SDL3 port built as PSP homebrew: the same hosted core
|
The PSP port is the SDL3 port built as PSP homebrew: the same hosted core
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ This compiles and runs on every target as-is.
|
||||||
#define SUBPIXEL_SHIFT 8 // positions are kept in 1/256 of a pixel
|
#define SUBPIXEL_SHIFT 8 // positions are kept in 1/256 of a pixel
|
||||||
#define MAX_STEP_MILLIS 100 // after a stall, move no further than this
|
#define MAX_STEP_MILLIS 100 // after a stall, move no further than this
|
||||||
|
|
||||||
static jlSurfaceT *gStage;
|
static jlSurfaceT *jlgStage;
|
||||||
static bool gRunning = true;
|
static bool gRunning = true;
|
||||||
static int32_t gPlayerX = 160L << SUBPIXEL_SHIFT;
|
static int32_t gPlayerX = 160L << SUBPIXEL_SHIFT;
|
||||||
static int32_t gPlayerY = 100L << SUBPIXEL_SHIFT;
|
static int32_t gPlayerY = 100L << SUBPIXEL_SHIFT;
|
||||||
|
|
@ -223,8 +223,8 @@ static void buildPalette(void) {
|
||||||
colors[2] = 0x0C0; // green
|
colors[2] = 0x0C0; // green
|
||||||
colors[3] = 0x00C; // blue
|
colors[3] = 0x00C; // blue
|
||||||
|
|
||||||
jlPaletteSet(gStage, 0, colors);
|
jlPaletteSet(jlgStage, 0, colors);
|
||||||
jlScbSetRange(gStage, 0, JL_SURFACE_HEIGHT - 1, 0);
|
jlScbSetRange(jlgStage, 0, JL_SURFACE_HEIGHT - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -278,9 +278,9 @@ static void handleInput(int32_t step) {
|
||||||
|
|
||||||
|
|
||||||
static void renderFrame(void) {
|
static void renderFrame(void) {
|
||||||
jlSurfaceClear(gStage, 0);
|
jlSurfaceClear(jlgStage, 0);
|
||||||
jlDrawRect(gStage, 0, 0, JL_SURFACE_WIDTH, JL_SURFACE_HEIGHT, 1);
|
jlDrawRect(jlgStage, 0, 0, JL_SURFACE_WIDTH, JL_SURFACE_HEIGHT, 1);
|
||||||
jlFillRect(gStage, (int16_t)(gPlayerX >> SUBPIXEL_SHIFT), (int16_t)(gPlayerY >> SUBPIXEL_SHIFT), PLAYER_SIZE, PLAYER_SIZE, 2);
|
jlFillRect(jlgStage, (int16_t)(gPlayerX >> SUBPIXEL_SHIFT), (int16_t)(gPlayerY >> SUBPIXEL_SHIFT), PLAYER_SIZE, PLAYER_SIZE, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -308,8 +308,8 @@ int main(void) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gStage = jlStageGet();
|
jlgStage = jlStageGet();
|
||||||
if (gStage == NULL) {
|
if (jlgStage == NULL) {
|
||||||
jlShutdown();
|
jlShutdown();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ typedef struct {
|
||||||
* Section 2 -- Globals (game state)
|
* Section 2 -- Globals (game state)
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
static jlSurfaceT *gScreen;
|
static jlSurfaceT *jlgScreen;
|
||||||
static jlSurfaceT *gBgScene[ROOM_COUNT]; // pristine background per room
|
static jlSurfaceT *gBgScene[ROOM_COUNT]; // pristine background per room
|
||||||
|
|
||||||
static RoomE gCurRoom;
|
static RoomE gCurRoom;
|
||||||
|
|
@ -1985,7 +1985,7 @@ static void drawTextLine(int16_t x, int16_t y, const char *text, uint8_t color)
|
||||||
bits = kFontGlyph[idx][row];
|
bits = kFontGlyph[idx][row];
|
||||||
for (col = 0; col < FONT_W; col++) {
|
for (col = 0; col < FONT_W; col++) {
|
||||||
if (bits & (0x80 >> col)) {
|
if (bits & (0x80 >> col)) {
|
||||||
jlDrawPixel(gScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
jlDrawPixel(jlgScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2006,8 +2006,8 @@ static void drawTextLine(int16_t x, int16_t y, const char *text, uint8_t color)
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
static void drawMessageBar(void) {
|
static void drawMessageBar(void) {
|
||||||
jlFillRect(gScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
||||||
jlFillRect(gScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||||
if (gMessage[0] != '\0') {
|
if (gMessage[0] != '\0') {
|
||||||
drawTextLine(4, MSG_BAR_Y + 2, gMessage, UI_INK);
|
drawTextLine(4, MSG_BAR_Y + 2, gMessage, UI_INK);
|
||||||
}
|
}
|
||||||
|
|
@ -2019,8 +2019,8 @@ static void drawInventoryBar(void) {
|
||||||
int n;
|
int n;
|
||||||
int16_t x;
|
int16_t x;
|
||||||
|
|
||||||
jlFillRect(gScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, INV_BAR_H, UI_BG);
|
jlFillRect(jlgScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, INV_BAR_H, UI_BG);
|
||||||
jlFillRect(gScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
jlFillRect(jlgScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||||
|
|
||||||
// Verb readout at the left.
|
// Verb readout at the left.
|
||||||
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
||||||
|
|
@ -2032,12 +2032,12 @@ static void drawInventoryBar(void) {
|
||||||
if (gHaveItem[ITEM_LAMP]) {
|
if (gHaveItem[ITEM_LAMP]) {
|
||||||
jlSpriteT *sp;
|
jlSpriteT *sp;
|
||||||
sp = gLampIsLit ? gLampLitSprite : gItemSprite[ITEM_LAMP];
|
sp = gLampIsLit ? gLampLitSprite : gItemSprite[ITEM_LAMP];
|
||||||
jlSpriteDraw(gScreen, sp, x, INV_BAR_Y + 2);
|
jlSpriteDraw(jlgScreen, sp, x, INV_BAR_Y + 2);
|
||||||
x = (int16_t)(x - (ITEM_ICON_W + 2));
|
x = (int16_t)(x - (ITEM_ICON_W + 2));
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
if (gHaveItem[ITEM_KEY]) {
|
if (gHaveItem[ITEM_KEY]) {
|
||||||
jlSpriteDraw(gScreen, gItemSprite[ITEM_KEY], x, INV_BAR_Y + 2);
|
jlSpriteDraw(jlgScreen, gItemSprite[ITEM_KEY], x, INV_BAR_Y + 2);
|
||||||
x = (int16_t)(x - (ITEM_ICON_W + 2));
|
x = (int16_t)(x - (ITEM_ICON_W + 2));
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
@ -2063,10 +2063,10 @@ static void composeFrame(int16_t cursorX, int16_t cursorY) {
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
// 1. background
|
// 1. background
|
||||||
jlSurfaceCopy(gScreen, gBgScene[gCurRoom]);
|
jlSurfaceCopy(jlgScreen, gBgScene[gCurRoom]);
|
||||||
|
|
||||||
// 2. ego
|
// 2. ego
|
||||||
jlSpriteDraw(gScreen, gEgo[gEgoDir][gEgoFrameIdx], gEgoX, gEgoY);
|
jlSpriteDraw(jlgScreen, gEgo[gEgoDir][gEgoFrameIdx], gEgoX, gEgoY);
|
||||||
|
|
||||||
// 3. prop priority redraw
|
// 3. prop priority redraw
|
||||||
r = &gRooms[gCurRoom];
|
r = &gRooms[gCurRoom];
|
||||||
|
|
@ -2078,7 +2078,7 @@ static void composeFrame(int16_t cursorX, int16_t cursorY) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((p->y + p->h) > egoBottom) {
|
if ((p->y + p->h) > egoBottom) {
|
||||||
jlSpriteDraw(gScreen, p->sp, p->x, p->y);
|
jlSpriteDraw(jlgScreen, p->sp, p->x, p->y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2100,7 +2100,7 @@ static void composeFrame(int16_t cursorX, int16_t cursorY) {
|
||||||
cs = gCursor[gCurVerb];
|
cs = gCursor[gCurVerb];
|
||||||
}
|
}
|
||||||
if (cs != NULL) {
|
if (cs != NULL) {
|
||||||
jlSpriteDraw(gScreen, cs, cx, cy);
|
jlSpriteDraw(jlgScreen, cs, cx, cy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2357,7 +2357,7 @@ static void enterRoom(RoomE room, bool fromEast, bool fromWest) {
|
||||||
gPathLen = 0;
|
gPathLen = 0;
|
||||||
gPathStep = 0;
|
gPathStep = 0;
|
||||||
|
|
||||||
programSkyScb(gScreen, room);
|
programSkyScb(jlgScreen, room);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2413,15 +2413,15 @@ int main(void) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gScreen = jlStageGet();
|
jlgScreen = jlStageGet();
|
||||||
if (gScreen == NULL) {
|
if (jlgScreen == NULL) {
|
||||||
fprintf(stderr, "jlStageGet returned NULL\n");
|
fprintf(stderr, "jlStageGet returned NULL\n");
|
||||||
jlShutdown();
|
jlShutdown();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPalettes(gScreen);
|
buildPalettes(jlgScreen);
|
||||||
programSkyScb(gScreen, ROOM_FOREST);
|
programSkyScb(jlgScreen, ROOM_FOREST);
|
||||||
|
|
||||||
if (!buildAllSprites()) {
|
if (!buildAllSprites()) {
|
||||||
fprintf(stderr, "buildAllSprites failed: %s\n", jlLastError());
|
fprintf(stderr, "buildAllSprites failed: %s\n", jlLastError());
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ typedef enum {
|
||||||
* Section 2 -- Globals
|
* Section 2 -- Globals
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
static jlSurfaceT *gScreen;
|
static jlSurfaceT *jlgScreen;
|
||||||
static jlSurfaceT *gVisual; // room background -- "picture" buffer
|
static jlSurfaceT *gVisual; // room background -- "picture" buffer
|
||||||
static jlSurfaceT *gPriority; // per-pixel priority codes
|
static jlSurfaceT *gPriority; // per-pixel priority codes
|
||||||
static jlSurfaceT *gControl; // per-pixel control codes
|
static jlSurfaceT *gControl; // per-pixel control codes
|
||||||
|
|
@ -634,7 +634,7 @@ static void drawText(int16_t x, int16_t y, const char *text, uint8_t color) {
|
||||||
bits = kFontGlyph[idx][row];
|
bits = kFontGlyph[idx][row];
|
||||||
for (col = 0; col < FONT_W; col++) {
|
for (col = 0; col < FONT_W; col++) {
|
||||||
if (bits & (0x80 >> col)) {
|
if (bits & (0x80 >> col)) {
|
||||||
jlDrawPixel(gScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
jlDrawPixel(jlgScreen, (int16_t)(cx + col), (int16_t)(y + row), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -736,10 +736,10 @@ static void buildPalettes(void) {
|
||||||
ui[UI_HILITE] = rgb(15, 13, 3);
|
ui[UI_HILITE] = rgb(15, 13, 3);
|
||||||
ui[UI_DIM] = rgb(7, 7, 8);
|
ui[UI_DIM] = rgb(7, 7, 8);
|
||||||
|
|
||||||
jlPaletteSet(gScreen, PAL_SCENE, scene);
|
jlPaletteSet(jlgScreen, PAL_SCENE, scene);
|
||||||
jlPaletteSet(gScreen, PAL_DEBUG_PRI, pri);
|
jlPaletteSet(jlgScreen, PAL_DEBUG_PRI, pri);
|
||||||
jlPaletteSet(gScreen, PAL_DEBUG_CTRL, ctl);
|
jlPaletteSet(jlgScreen, PAL_DEBUG_CTRL, ctl);
|
||||||
jlPaletteSet(gScreen, PAL_UI, ui);
|
jlPaletteSet(jlgScreen, PAL_UI, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -763,10 +763,10 @@ static void programScbForView(DebugViewE view) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (line = 0; line < PLAY_AREA_H; line++) {
|
for (line = 0; line < PLAY_AREA_H; line++) {
|
||||||
jlScbSet(gScreen, (uint16_t)line, playPal);
|
jlScbSet(jlgScreen, (uint16_t)line, playPal);
|
||||||
}
|
}
|
||||||
for (line = PLAY_AREA_H; line < JL_SURFACE_HEIGHT; line++) {
|
for (line = PLAY_AREA_H; line < JL_SURFACE_HEIGHT; line++) {
|
||||||
jlScbSet(gScreen, (uint16_t)line, PAL_UI);
|
jlScbSet(jlgScreen, (uint16_t)line, PAL_UI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -981,7 +981,7 @@ static void drawEgoMasked(int16_t x, int16_t y, DirE dir, uint8_t frame) {
|
||||||
// Picture pixel is in front. Skip the actor pixel.
|
// Picture pixel is in front. Skip the actor pixel.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
jlDrawPixel(gScreen, px, py, color);
|
jlDrawPixel(jlgScreen, px, py, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1323,15 +1323,15 @@ static void drawCursor(int16_t mx, int16_t my, VerbE v) {
|
||||||
if (color == C_TRANS) {
|
if (color == C_TRANS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
jlDrawPixel(gScreen, px, py, color);
|
jlDrawPixel(jlgScreen, px, py, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void drawMessageBar(void) {
|
static void drawMessageBar(void) {
|
||||||
jlFillRect(gScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, MSG_BAR_H, UI_BG);
|
||||||
jlFillRect(gScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
jlFillRect(jlgScreen, 0, MSG_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||||
if (gMessage[0] != '\0') {
|
if (gMessage[0] != '\0') {
|
||||||
drawText(4, (int16_t)(MSG_BAR_Y + 2), gMessage, UI_INK);
|
drawText(4, (int16_t)(MSG_BAR_Y + 2), gMessage, UI_INK);
|
||||||
}
|
}
|
||||||
|
|
@ -1342,8 +1342,8 @@ static void drawInventoryBar(void) {
|
||||||
char buf[40];
|
char buf[40];
|
||||||
const char *vw;
|
const char *vw;
|
||||||
|
|
||||||
jlFillRect(gScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, INV_BAR_H, UI_BG);
|
jlFillRect(jlgScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, INV_BAR_H, UI_BG);
|
||||||
jlFillRect(gScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
jlFillRect(jlgScreen, 0, INV_BAR_Y, JL_SURFACE_WIDTH, 1, UI_DIM);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
snprintf(buf, sizeof(buf), "VERB: %s", verbName(gCurVerb));
|
||||||
drawText(4, (int16_t)(INV_BAR_Y + 2), buf, UI_HILITE);
|
drawText(4, (int16_t)(INV_BAR_Y + 2), buf, UI_HILITE);
|
||||||
|
|
@ -1368,7 +1368,7 @@ static void drawInventoryBar(void) {
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
static void composeFrameNormal(int16_t mx, int16_t my) {
|
static void composeFrameNormal(int16_t mx, int16_t my) {
|
||||||
jlSurfaceCopy(gScreen, gVisual);
|
jlSurfaceCopy(jlgScreen, gVisual);
|
||||||
drawEgoMasked(gEgoX, gEgoY, gEgoDir, gEgoFrameIdx);
|
drawEgoMasked(gEgoX, gEgoY, gEgoDir, gEgoFrameIdx);
|
||||||
drawMessageBar();
|
drawMessageBar();
|
||||||
drawInventoryBar();
|
drawInventoryBar();
|
||||||
|
|
@ -1381,7 +1381,7 @@ static void composeFrameDebug(int16_t mx, int16_t my) {
|
||||||
/* The debug overlays blit the raw priority/control buffer as the
|
/* The debug overlays blit the raw priority/control buffer as the
|
||||||
* play-area picture; the palette swap (PAL_DEBUG_PRI or
|
* play-area picture; the palette swap (PAL_DEBUG_PRI or
|
||||||
* PAL_DEBUG_CTRL) makes the codes visible. */
|
* PAL_DEBUG_CTRL) makes the codes visible. */
|
||||||
jlSurfaceCopy(gScreen, (gDebugView == VIEW_PRIORITY) ? gPriority : gControl);
|
jlSurfaceCopy(jlgScreen, (gDebugView == VIEW_PRIORITY) ? gPriority : gControl);
|
||||||
/* Show where the actor is by overlaying ego pixels in pure white
|
/* Show where the actor is by overlaying ego pixels in pure white
|
||||||
* with priority always-on. */
|
* with priority always-on. */
|
||||||
{
|
{
|
||||||
|
|
@ -1404,7 +1404,7 @@ static void composeFrameDebug(int16_t mx, int16_t my) {
|
||||||
if (pixCharToColor(rows[r][c]) == C_TRANS) {
|
if (pixCharToColor(rows[r][c]) == C_TRANS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
jlDrawPixel(gScreen, px, py, 15);
|
jlDrawPixel(jlgScreen, px, py, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1509,8 +1509,8 @@ int main(void) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gScreen = jlStageGet();
|
jlgScreen = jlStageGet();
|
||||||
if (gScreen == NULL) {
|
if (jlgScreen == NULL) {
|
||||||
fprintf(stderr, "jlStageGet returned NULL\n");
|
fprintf(stderr, "jlStageGet returned NULL\n");
|
||||||
jlShutdown();
|
jlShutdown();
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
344
examples/scroll/scroll.c
Normal file
344
examples/scroll/scroll.c
Normal file
|
|
@ -0,0 +1,344 @@
|
||||||
|
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
// See LICENSE.md for license information.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Scroll demo: a tile layer (jlTileLayer*) over a landscape three screens
|
||||||
|
// wide and two tall, with a ball drawn over it every frame -- no
|
||||||
|
// save-under, since the next layer draw covers it. The arrow keys or a
|
||||||
|
// joystick move the view; left alone it drifts. The two tile rows under
|
||||||
|
// the layer are a status bar whose bar grows with the frames drawn a
|
||||||
|
// second, so the demo shows how fast each port scrolls. Escape ends it.
|
||||||
|
//
|
||||||
|
// The tiles are painted with the drawing calls and snapped, since a
|
||||||
|
// jlTileT is in each port's own format; the map is generated.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <joey/joey.h>
|
||||||
|
|
||||||
|
#define MAP_W 96
|
||||||
|
#define MAP_H 40
|
||||||
|
#define VIEW_ROWS 23 // tile rows of the layer
|
||||||
|
#define BAR_TOP (VIEW_ROWS * JL_TILE_PIXELS_PER_SIDE)
|
||||||
|
#define BAR_H (JL_SURFACE_HEIGHT - BAR_TOP)
|
||||||
|
#define BAR_PIXELS_PER_FPS 5 // the bar's growth for each frame a second
|
||||||
|
#define BAR_INSET 4
|
||||||
|
|
||||||
|
#define TILE_SKY 0
|
||||||
|
#define TILE_GRASS 1
|
||||||
|
#define TILE_DIRT 2
|
||||||
|
#define TILE_BRICK 3
|
||||||
|
#define TILE_WATER 4
|
||||||
|
#define TILE_CLOUD 5
|
||||||
|
#define TILE_STONE 6
|
||||||
|
#define TILE_FLOWER 7
|
||||||
|
#define TILE_COUNT 8
|
||||||
|
|
||||||
|
#define COLOR_SKY 1
|
||||||
|
#define COLOR_DIRT 2
|
||||||
|
#define COLOR_GRASS 3
|
||||||
|
#define COLOR_DIRT_DARK 4
|
||||||
|
#define COLOR_BRICK 5
|
||||||
|
#define COLOR_GREY 6
|
||||||
|
#define COLOR_WATER 7
|
||||||
|
#define COLOR_FOAM 8
|
||||||
|
#define COLOR_CLOUD 9
|
||||||
|
#define COLOR_GREY_DARK 10
|
||||||
|
#define COLOR_PETAL 11
|
||||||
|
#define COLOR_BALL 12
|
||||||
|
#define COLOR_SHINE 13
|
||||||
|
#define COLOR_BAR_BACK 14
|
||||||
|
#define COLOR_BAR 15
|
||||||
|
|
||||||
|
#define GROUND_LOW 34 // the ground's highest and lowest rows
|
||||||
|
#define GROUND_HIGH 22
|
||||||
|
#define WATER_LINE 31 // ground below this row floods
|
||||||
|
#define STONE_DEPTH 4 // dirt rows above the stone
|
||||||
|
#define LEDGES 30 // brick ledges and clouds, turn about
|
||||||
|
#define LEDGE_MIN 2 // their length in tiles
|
||||||
|
#define LEDGE_MAX 6
|
||||||
|
#define LEDGE_TOP 4 // their highest row
|
||||||
|
#define LEDGE_GAP 4 // rows kept clear above the highest ground
|
||||||
|
|
||||||
|
#define BALL_TILES 2
|
||||||
|
#define BALL_SIDE (BALL_TILES * JL_TILE_PIXELS_PER_SIDE)
|
||||||
|
#define BALL_X ((JL_SURFACE_WIDTH - BALL_SIDE) / 2)
|
||||||
|
#define BALL_Y ((BAR_TOP - BALL_SIDE) / 2)
|
||||||
|
|
||||||
|
#define SPEED 90 // pixels per second, the same on every port
|
||||||
|
#define DRIFT_X 40
|
||||||
|
#define DRIFT_Y 12
|
||||||
|
#define STICK_THRESHOLD 32
|
||||||
|
#define SUBPIXEL_SHIFT 8 // positions are kept in 1/256 of a pixel
|
||||||
|
#define MAX_STEP_MILLIS 100 // after a stall, move no further than this
|
||||||
|
#define MILLIS_PER_SECOND 1000
|
||||||
|
#define MAP_PX_W ((int32_t)MAP_W * JL_TILE_PIXELS_PER_SIDE << SUBPIXEL_SHIFT)
|
||||||
|
#define MAP_PX_H ((int32_t)MAP_H * JL_TILE_PIXELS_PER_SIDE << SUBPIXEL_SHIFT)
|
||||||
|
#define CODEGEN_BYTES (32UL * 1024)
|
||||||
|
|
||||||
|
static const uint16_t gPalette[16] = {
|
||||||
|
0x000, 0x6AF, 0x852, 0x3B3, 0x531, 0xB33, 0x999, 0x24C,
|
||||||
|
0x8CF, 0xFFF, 0x555, 0xFC2, 0xFD0, 0xFFF, 0x113, 0x4E4
|
||||||
|
};
|
||||||
|
|
||||||
|
// The ball, 16x16 at 4 bits a pixel, high nibble left; colour 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,
|
||||||
|
0x0C, 0xCC, 0xDC, 0xCC, 0xCC, 0xCC, 0xCC, 0xC0,
|
||||||
|
0x0C, 0xCD, 0xDC, 0xCC, 0xCC, 0xCC, 0xCC, 0xC0,
|
||||||
|
0xCC, 0xDD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||||||
|
0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xC0,
|
||||||
|
0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xC0,
|
||||||
|
0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00,
|
||||||
|
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
static jlTileT gTiles[TILE_COUNT];
|
||||||
|
static uint8_t gMap[MAP_W * MAP_H];
|
||||||
|
static uint8_t gBallTiles[BALL_TILES * BALL_TILES * JL_TILE_BYTES];
|
||||||
|
static uint32_t gSeed = 2026u;
|
||||||
|
|
||||||
|
static void drawBar(jlSurfaceT *screen, uint16_t framesPerSecond);
|
||||||
|
static void makeBall(void);
|
||||||
|
static void makeMap(void);
|
||||||
|
static void makeTiles(jlSurfaceT *screen);
|
||||||
|
static uint16_t nextRandom(uint16_t below);
|
||||||
|
static int32_t stickAxis(int8_t stick, bool minus, bool plus, int32_t drift);
|
||||||
|
static int32_t wrap(int32_t v, int32_t size);
|
||||||
|
|
||||||
|
|
||||||
|
// The status bar under the layer: its bar is BAR_PIXELS_PER_FPS a frame a second.
|
||||||
|
static void drawBar(jlSurfaceT *screen, uint16_t framesPerSecond) {
|
||||||
|
uint16_t w;
|
||||||
|
|
||||||
|
w = (uint16_t)(framesPerSecond * BAR_PIXELS_PER_FPS);
|
||||||
|
if (w > JL_SURFACE_WIDTH - 2 * BAR_INSET) {
|
||||||
|
w = JL_SURFACE_WIDTH - 2 * BAR_INSET;
|
||||||
|
}
|
||||||
|
jlFillRect(screen, 0, BAR_TOP, JL_SURFACE_WIDTH, BAR_H, COLOR_BAR_BACK);
|
||||||
|
if (w > 0) {
|
||||||
|
jlFillRect(screen, BAR_INSET, BAR_TOP + BAR_INSET, w, BAR_H - 2 * BAR_INSET, COLOR_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The ball's rows, repacked into the tile-major layout jlSpriteCreate takes.
|
||||||
|
static void makeBall(void) {
|
||||||
|
uint16_t tx;
|
||||||
|
uint16_t ty;
|
||||||
|
uint16_t row;
|
||||||
|
uint16_t b;
|
||||||
|
|
||||||
|
for (ty = 0; ty < BALL_TILES; ty++) {
|
||||||
|
for (tx = 0; tx < BALL_TILES; tx++) {
|
||||||
|
for (row = 0; row < JL_TILE_PIXELS_PER_SIDE; row++) {
|
||||||
|
for (b = 0; b < JL_TILE_BYTES_PER_ROW; b++) {
|
||||||
|
gBallTiles[(ty * BALL_TILES + tx) * JL_TILE_BYTES + row * JL_TILE_BYTES_PER_ROW + b] = gBallRows[(ty * JL_TILE_PIXELS_PER_SIDE + row) * (BALL_SIDE / 2) + tx * JL_TILE_BYTES_PER_ROW + b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rolling ground with flooded hollows and flowers, and brick ledges and
|
||||||
|
// clouds above it.
|
||||||
|
static void makeMap(void) {
|
||||||
|
uint16_t tx;
|
||||||
|
uint16_t ty;
|
||||||
|
uint16_t ground;
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t i;
|
||||||
|
uint16_t k;
|
||||||
|
uint8_t t;
|
||||||
|
|
||||||
|
ground = GROUND_HIGH + 4;
|
||||||
|
for (tx = 0; tx < MAP_W; tx++) {
|
||||||
|
if (nextRandom(3) == 0 && ground < GROUND_LOW) {
|
||||||
|
ground++;
|
||||||
|
} else if (nextRandom(3) == 0 && ground > GROUND_HIGH) {
|
||||||
|
ground--;
|
||||||
|
}
|
||||||
|
for (ty = 0; ty < MAP_H; ty++) {
|
||||||
|
if (ty < ground) {
|
||||||
|
t = (ty >= WATER_LINE) ? TILE_WATER : TILE_SKY;
|
||||||
|
} else if (ty == ground) {
|
||||||
|
t = (ty >= WATER_LINE) ? TILE_STONE : TILE_GRASS;
|
||||||
|
} else if (ty < ground + STONE_DEPTH) {
|
||||||
|
t = TILE_DIRT;
|
||||||
|
} else {
|
||||||
|
t = TILE_STONE;
|
||||||
|
}
|
||||||
|
gMap[ty * MAP_W + tx] = t;
|
||||||
|
}
|
||||||
|
if (ground < WATER_LINE && nextRandom(5) == 0) {
|
||||||
|
gMap[(ground - 1) * MAP_W + tx] = TILE_FLOWER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < LEDGES; i++) {
|
||||||
|
tx = nextRandom(MAP_W - LEDGE_MAX);
|
||||||
|
ty = (uint16_t)(LEDGE_TOP + nextRandom(GROUND_HIGH - LEDGE_TOP - LEDGE_GAP));
|
||||||
|
len = (uint16_t)(LEDGE_MIN + nextRandom(LEDGE_MAX - LEDGE_MIN));
|
||||||
|
for (k = 0; k < len; k++) {
|
||||||
|
gMap[ty * MAP_W + tx + k] = ((i & 1u) != 0u) ? TILE_BRICK : TILE_CLOUD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Paints the eight tiles in block row 0 of the screen and snaps them.
|
||||||
|
static void makeTiles(jlSurfaceT *screen) {
|
||||||
|
jlFillRect(screen, 0, 0, 8, 8, COLOR_SKY);
|
||||||
|
jlFillRect(screen, 8, 0, 8, 8, COLOR_DIRT);
|
||||||
|
jlFillRect(screen, 8, 0, 8, 3, COLOR_GRASS);
|
||||||
|
jlDrawPixel(screen, 10, 3, COLOR_GRASS);
|
||||||
|
jlDrawPixel(screen, 13, 3, COLOR_GRASS);
|
||||||
|
jlFillRect(screen, 16, 0, 8, 8, COLOR_DIRT);
|
||||||
|
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);
|
||||||
|
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, 56, 0, 8, 8, COLOR_SKY);
|
||||||
|
jlDrawLine(screen, 59, 3, 59, 7, COLOR_GRASS);
|
||||||
|
jlFillRect(screen, 58, 1, 3, 2, COLOR_PETAL);
|
||||||
|
jlTileSnap(screen, TILE_SKY, 0, &gTiles[TILE_SKY]);
|
||||||
|
jlTileSnap(screen, TILE_GRASS, 0, &gTiles[TILE_GRASS]);
|
||||||
|
jlTileSnap(screen, TILE_DIRT, 0, &gTiles[TILE_DIRT]);
|
||||||
|
jlTileSnap(screen, TILE_BRICK, 0, &gTiles[TILE_BRICK]);
|
||||||
|
jlTileSnap(screen, TILE_WATER, 0, &gTiles[TILE_WATER]);
|
||||||
|
jlTileSnap(screen, TILE_CLOUD, 0, &gTiles[TILE_CLOUD]);
|
||||||
|
jlTileSnap(screen, TILE_STONE, 0, &gTiles[TILE_STONE]);
|
||||||
|
jlTileSnap(screen, TILE_FLOWER, 0, &gTiles[TILE_FLOWER]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A number in [0, below), from a fixed sequence so every port builds the
|
||||||
|
// same landscape.
|
||||||
|
static uint16_t nextRandom(uint16_t below) {
|
||||||
|
gSeed = gSeed * 1103515245u + 12345u;
|
||||||
|
return (uint16_t)((gSeed >> 16) % below);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The view's speed along one axis, pixels per second: the stick or the two
|
||||||
|
// keys, else the drift.
|
||||||
|
static int32_t stickAxis(int8_t stick, bool minus, bool plus, int32_t drift) {
|
||||||
|
if (minus || stick < -STICK_THRESHOLD) {
|
||||||
|
return -SPEED;
|
||||||
|
}
|
||||||
|
if (plus || stick > STICK_THRESHOLD) {
|
||||||
|
return SPEED;
|
||||||
|
}
|
||||||
|
return drift;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// v moved into [0, size).
|
||||||
|
static int32_t wrap(int32_t v, int32_t size) {
|
||||||
|
while (v < 0) {
|
||||||
|
v += size;
|
||||||
|
}
|
||||||
|
while (v >= size) {
|
||||||
|
v -= size;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
jlConfigT config;
|
||||||
|
jlSurfaceT *screen;
|
||||||
|
jlSpriteT *ball;
|
||||||
|
jlTileLayerT *layer;
|
||||||
|
int32_t x; // the view's origin, 1/256 of a pixel
|
||||||
|
int32_t y;
|
||||||
|
int32_t vx; // pixels per second
|
||||||
|
int32_t vy;
|
||||||
|
uint32_t lastMillis;
|
||||||
|
uint32_t barMillis;
|
||||||
|
uint32_t now;
|
||||||
|
uint32_t elapsed;
|
||||||
|
uint16_t frames;
|
||||||
|
|
||||||
|
config.codegenBytes = CODEGEN_BYTES;
|
||||||
|
config.noAudio = true;
|
||||||
|
if (!jlInit(&config)) {
|
||||||
|
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
screen = jlStageGet();
|
||||||
|
jlPaletteSet(screen, 0, gPalette);
|
||||||
|
jlScbSetRange(screen, 0, JL_SURFACE_HEIGHT - 1, 0);
|
||||||
|
makeTiles(screen);
|
||||||
|
makeMap();
|
||||||
|
makeBall();
|
||||||
|
ball = jlSpriteCreate(gBallTiles, BALL_TILES, BALL_TILES);
|
||||||
|
layer = jlTileLayerCreate(gTiles, gMap, MAP_W, MAP_H, 0, VIEW_ROWS);
|
||||||
|
if (ball == NULL || layer == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
jlShutdown();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
(void)jlSpriteCompile(ball);
|
||||||
|
jlSurfaceClear(screen, 0);
|
||||||
|
drawBar(screen, 0);
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
y = (int32_t)(GROUND_HIGH - VIEW_ROWS / 2) * JL_TILE_PIXELS_PER_SIDE << SUBPIXEL_SHIFT;
|
||||||
|
frames = 0;
|
||||||
|
lastMillis = jlMillisElapsed();
|
||||||
|
barMillis = lastMillis;
|
||||||
|
for (;;) {
|
||||||
|
jlInputPoll();
|
||||||
|
if (jlKeyPressed(JL_KEY_ESCAPE)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vx = stickAxis(jlJoystickX(JL_JOYSTICK_1), jlKeyDown(JL_KEY_LEFT), jlKeyDown(JL_KEY_RIGHT), DRIFT_X);
|
||||||
|
vy = stickAxis(jlJoystickY(JL_JOYSTICK_1), jlKeyDown(JL_KEY_UP), jlKeyDown(JL_KEY_DOWN), DRIFT_Y);
|
||||||
|
|
||||||
|
now = jlMillisElapsed();
|
||||||
|
elapsed = now - lastMillis;
|
||||||
|
lastMillis = now;
|
||||||
|
if (elapsed > MAX_STEP_MILLIS) {
|
||||||
|
elapsed = MAX_STEP_MILLIS;
|
||||||
|
}
|
||||||
|
x = wrap(x + (vx * (int32_t)elapsed << SUBPIXEL_SHIFT) / MILLIS_PER_SECOND, MAP_PX_W);
|
||||||
|
y = wrap(y + (vy * (int32_t)elapsed << SUBPIXEL_SHIFT) / MILLIS_PER_SECOND, MAP_PX_H);
|
||||||
|
|
||||||
|
jlTileLayerDraw(screen, layer, (uint16_t)(x >> SUBPIXEL_SHIFT), (uint16_t)(y >> SUBPIXEL_SHIFT));
|
||||||
|
jlSpriteDraw(screen, ball, BALL_X, BALL_Y);
|
||||||
|
frames++;
|
||||||
|
if (now - barMillis >= MILLIS_PER_SECOND) {
|
||||||
|
drawBar(screen, (uint16_t)((uint32_t)frames * MILLIS_PER_SECOND / (now - barMillis)));
|
||||||
|
frames = 0;
|
||||||
|
barMillis = now;
|
||||||
|
}
|
||||||
|
jlStagePresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
jlTileLayerDestroy(layer);
|
||||||
|
jlSpriteDestroy(ball);
|
||||||
|
jlShutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -100,10 +100,10 @@
|
||||||
// shadows a card's 6551 at $C0A8).
|
// shadows a card's 6551 at $C0A8).
|
||||||
#define SLTROMSEL ((volatile uint8_t *)0x00C02DL)
|
#define SLTROMSEL ((volatile uint8_t *)0x00C02DL)
|
||||||
|
|
||||||
extern uint16_t gIigsRxOverruns;
|
extern uint16_t jlgIigsRxOverruns;
|
||||||
extern uint16_t gIigsRxDropped;
|
extern uint16_t jlgIigsRxDropped;
|
||||||
#ifdef SERLOSS_LOOSE
|
#ifdef SERLOSS_LOOSE
|
||||||
extern void iigsIrqLatencyRelax(void);
|
extern void jlIigsIrqLatencyRelax(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SERLOSS_JOY
|
#ifdef SERLOSS_JOY
|
||||||
|
|
@ -195,8 +195,8 @@ __attribute__((noinline)) static void publish(uint16_t received, uint16_t inSequ
|
||||||
mailbox16(MB_BREAKS, breaks);
|
mailbox16(MB_BREAKS, breaks);
|
||||||
mailbox16(MB_PRESENTS, presents);
|
mailbox16(MB_PRESENTS, presents);
|
||||||
mailbox16(MB_TICKS, ticks);
|
mailbox16(MB_TICKS, ticks);
|
||||||
mailbox16(MB_OVERRUNS, gIigsRxOverruns);
|
mailbox16(MB_OVERRUNS, jlgIigsRxOverruns);
|
||||||
mailbox16(MB_DROPPED, gIigsRxDropped);
|
mailbox16(MB_DROPPED, jlgIigsRxDropped);
|
||||||
mailbox16(MB_SUM, sum);
|
mailbox16(MB_SUM, sum);
|
||||||
mailbox16(MB_NOW, now);
|
mailbox16(MB_NOW, now);
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +315,7 @@ int main(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef SERLOSS_LOOSE
|
#ifdef SERLOSS_LOOSE
|
||||||
iigsIrqLatencyRelax();
|
jlIigsIrqLatencyRelax();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SERLOSS_JOY
|
#ifdef SERLOSS_JOY
|
||||||
// Calibrate on the first poll, in the latency mode the rest will run in, so a still stick
|
// Calibrate on the first poll, in the latency mode the rest will run in, so a still stick
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
// Minimal headless serial self-test -- NO jlInit (no video/graphics), so it
|
// Minimal headless serial self-test -- NO jlInit (no video/graphics), so it
|
||||||
// runs even where the graphics HAL can't (e.g. AROS on the Amiga). Opens the
|
// runs even where the graphics HAL can't (e.g. AROS on the Amiga). Opens the
|
||||||
// DEFAULT port (or SERTEST_DEVICE) at 9600 8N1, sends a banner (TX
|
// DEFAULT port (or SERTEST_DEVICE) at 9600 8N1 (or SERTEST_BAUD), sends a banner (TX
|
||||||
// heartbeat), then echoes every received byte forever. A host serial peer
|
// heartbeat), then echoes every received byte forever. A host serial peer
|
||||||
// verifies banner (TX) + echo (RX).
|
// verifies banner (TX) + echo (RX).
|
||||||
//
|
//
|
||||||
|
|
@ -27,6 +27,11 @@
|
||||||
#ifndef SERTEST_UNIT
|
#ifndef SERTEST_UNIT
|
||||||
#define SERTEST_UNIT 0u
|
#define SERTEST_UNIT 0u
|
||||||
#endif
|
#endif
|
||||||
|
// The line rate, overridable the same way (-DSERTEST_BAUD=115200u: SERFAST,
|
||||||
|
// the Win16 throughput gate's build).
|
||||||
|
#ifndef SERTEST_BAUD
|
||||||
|
#define SERTEST_BAUD 9600u
|
||||||
|
#endif
|
||||||
|
|
||||||
static void dtrCheck(const jlSerialConfigT *cfg);
|
static void dtrCheck(const jlSerialConfigT *cfg);
|
||||||
static void sendMark(const char *text);
|
static void sendMark(const char *text);
|
||||||
|
|
@ -79,7 +84,7 @@ int main(void) {
|
||||||
jlSerialConfigT cfg;
|
jlSerialConfigT cfg;
|
||||||
static const char banner[] = "JLSERTEST-READY\r\n";
|
static const char banner[] = "JLSERTEST-READY\r\n";
|
||||||
|
|
||||||
cfg.baud = 9600u;
|
cfg.baud = SERTEST_BAUD;
|
||||||
cfg.dataBits = 8u;
|
cfg.dataBits = 8u;
|
||||||
cfg.stopBits = 1u;
|
cfg.stopBits = 1u;
|
||||||
cfg.parity = JL_SERIAL_PARITY_NONE;
|
cfg.parity = JL_SERIAL_PARITY_NONE;
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ static uint16_t gBankPalette[PALETTE_COLORS];
|
||||||
static uint16_t gExpectCount = 0u;
|
static uint16_t gExpectCount = 0u;
|
||||||
static uint16_t gExpectPalette[PALETTE_COLORS];
|
static uint16_t gExpectPalette[PALETTE_COLORS];
|
||||||
static uint8_t *gExpectPixels = NULL;
|
static uint8_t *gExpectPixels = NULL;
|
||||||
static jlSurfaceT *gScreen = NULL;
|
static jlSurfaceT *jlgScreen = NULL;
|
||||||
static jlTileT *gTiles = NULL;
|
static jlTileT *gTiles = NULL;
|
||||||
static bool *gValid = NULL;
|
static bool *gValid = NULL;
|
||||||
|
|
||||||
|
|
@ -143,9 +143,9 @@ static bool loadExpected(void) {
|
||||||
static void pasteTiles(uint16_t count) {
|
static void pasteTiles(uint16_t count) {
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
jlSurfaceClear(gScreen, FILL_COLOR);
|
jlSurfaceClear(jlgScreen, FILL_COLOR);
|
||||||
for (i = 0u; i < count; i++) {
|
for (i = 0u; i < count; i++) {
|
||||||
jlTilePaste(gScreen, (uint8_t)(i % JL_TILE_BLOCKS_PER_ROW), (uint8_t)(i / JL_TILE_BLOCKS_PER_ROW), &gTiles[i]);
|
jlTilePaste(jlgScreen, (uint8_t)(i % JL_TILE_BLOCKS_PER_ROW), (uint8_t)(i / JL_TILE_BLOCKS_PER_ROW), &gTiles[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,16 +198,16 @@ static uint16_t refuseCheck(uint16_t *banks) {
|
||||||
uint16_t loaded;
|
uint16_t loaded;
|
||||||
uint16_t streamed;
|
uint16_t streamed;
|
||||||
|
|
||||||
jlSurfaceClear(gScreen, FILL_COLOR);
|
jlSurfaceClear(jlgScreen, FILL_COLOR);
|
||||||
fillHash = jlSurfaceHash(gScreen);
|
fillHash = jlSurfaceHash(jlgScreen);
|
||||||
refused = 0u;
|
refused = 0u;
|
||||||
*banks = 0u;
|
*banks = 0u;
|
||||||
while (refuseBankFound(name, (uint16_t)(*banks + 1u))) {
|
while (refuseBankFound(name, (uint16_t)(*banks + 1u))) {
|
||||||
(*banks)++;
|
(*banks)++;
|
||||||
jlSurfaceClear(gScreen, FILL_COLOR);
|
jlSurfaceClear(jlgScreen, FILL_COLOR);
|
||||||
loaded = jlTileBankLoad(name, gTiles, gExpectCount, gValid, NULL);
|
loaded = jlTileBankLoad(name, gTiles, gExpectCount, gValid, NULL);
|
||||||
streamed = jlTileBankLoadToSurface(name, gScreen, NULL);
|
streamed = jlTileBankLoadToSurface(name, jlgScreen, NULL);
|
||||||
if (loaded == 0u && !gValid[0] && streamed == 0u && jlSurfaceHash(gScreen) == fillHash) {
|
if (loaded == 0u && !gValid[0] && streamed == 0u && jlSurfaceHash(jlgScreen) == fillHash) {
|
||||||
refused++;
|
refused++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,16 +231,16 @@ static bool runChecks(void) {
|
||||||
|
|
||||||
refused = refuseCheck(&banks);
|
refused = refuseCheck(&banks);
|
||||||
|
|
||||||
jlSurfaceClear(gScreen, FILL_COLOR);
|
jlSurfaceClear(jlgScreen, FILL_COLOR);
|
||||||
streamed = jlTileBankLoadToSurface(OWN_BANK, gScreen, gBankPalette);
|
streamed = jlTileBankLoadToSurface(OWN_BANK, jlgScreen, gBankPalette);
|
||||||
streamOk = streamed == gExpectCount && stageMatches(streamed);
|
streamOk = streamed == gExpectCount && stageMatches(streamed);
|
||||||
streamHash = jlSurfaceHash(gScreen);
|
streamHash = jlSurfaceHash(jlgScreen);
|
||||||
paletteOk = memcmp(gBankPalette, gExpectPalette, sizeof(gBankPalette)) == 0;
|
paletteOk = memcmp(gBankPalette, gExpectPalette, sizeof(gBankPalette)) == 0;
|
||||||
|
|
||||||
pasted = jlTileBankLoad(OWN_BANK, gTiles, gExpectCount, gValid, NULL);
|
pasted = jlTileBankLoad(OWN_BANK, gTiles, gExpectCount, gValid, NULL);
|
||||||
pasteTiles(pasted);
|
pasteTiles(pasted);
|
||||||
pasteOk = pasted == gExpectCount && stageMatches(pasted);
|
pasteOk = pasted == gExpectCount && stageMatches(pasted);
|
||||||
pasteHash = jlSurfaceHash(gScreen);
|
pasteHash = jlSurfaceHash(jlgScreen);
|
||||||
|
|
||||||
pass = streamOk && pasteOk && paletteOk && streamHash == pasteHash && banks > 0u && refused == banks;
|
pass = streamOk && pasteOk && paletteOk && streamHash == pasteHash && banks > 0u && refused == banks;
|
||||||
jlLogF("TILEBANK: %s tiles=%u streamed=%u pasted=%u palette=%s streamHash=%08lX pasteHash=%08lX refused=%u/%u", pass ? "PASS" : "FAIL", (unsigned)gExpectCount, (unsigned)streamed, (unsigned)pasted, paletteOk ? "ok" : "wrong", (unsigned long)streamHash, (unsigned long)pasteHash, (unsigned)refused, (unsigned)banks);
|
jlLogF("TILEBANK: %s tiles=%u streamed=%u pasted=%u palette=%s streamHash=%08lX pasteHash=%08lX refused=%u/%u", pass ? "PASS" : "FAIL", (unsigned)gExpectCount, (unsigned)streamed, (unsigned)pasted, paletteOk ? "ok" : "wrong", (unsigned long)streamHash, (unsigned long)pasteHash, (unsigned)refused, (unsigned)banks);
|
||||||
|
|
@ -258,10 +258,10 @@ static void showVerdict(bool pass) {
|
||||||
}
|
}
|
||||||
colors[LAMP_PASS_COLOR] = LAMP_PASS_RGB;
|
colors[LAMP_PASS_COLOR] = LAMP_PASS_RGB;
|
||||||
colors[LAMP_FAIL_COLOR] = LAMP_FAIL_RGB;
|
colors[LAMP_FAIL_COLOR] = LAMP_FAIL_RGB;
|
||||||
jlPaletteSet(gScreen, 0u, gBankPalette);
|
jlPaletteSet(jlgScreen, 0u, gBankPalette);
|
||||||
jlPaletteSet(gScreen, LAMP_PALETTE, colors);
|
jlPaletteSet(jlgScreen, LAMP_PALETTE, colors);
|
||||||
jlScbSetRange(gScreen, LAMP_Y, LAMP_Y + LAMP_H - 1, LAMP_PALETTE);
|
jlScbSetRange(jlgScreen, LAMP_Y, LAMP_Y + LAMP_H - 1, LAMP_PALETTE);
|
||||||
jlFillRect(gScreen, LAMP_X, LAMP_Y, LAMP_W, LAMP_H, pass ? LAMP_PASS_COLOR : LAMP_FAIL_COLOR);
|
jlFillRect(jlgScreen, LAMP_X, LAMP_Y, LAMP_W, LAMP_H, pass ? LAMP_PASS_COLOR : LAMP_FAIL_COLOR);
|
||||||
jlStagePresent();
|
jlStagePresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +282,7 @@ static bool stageMatches(uint16_t tilesDrawn) {
|
||||||
if (block < tilesDrawn) {
|
if (block < tilesDrawn) {
|
||||||
want = gExpectPixels[(uint32_t)block * TILE_PIXELS + (y % JL_TILE_PIXELS_PER_SIDE) * JL_TILE_PIXELS_PER_SIDE + x % JL_TILE_PIXELS_PER_SIDE];
|
want = gExpectPixels[(uint32_t)block * TILE_PIXELS + (y % JL_TILE_PIXELS_PER_SIDE) * JL_TILE_PIXELS_PER_SIDE + x % JL_TILE_PIXELS_PER_SIDE];
|
||||||
}
|
}
|
||||||
if (jlSamplePixel(gScreen, x, y) != want) {
|
if (jlSamplePixel(jlgScreen, x, y) != want) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +301,7 @@ int main(void) {
|
||||||
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
|
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
gScreen = jlStageGet();
|
jlgScreen = jlStageGet();
|
||||||
|
|
||||||
pass = loadExpected();
|
pass = loadExpected();
|
||||||
if (pass) {
|
if (pass) {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -104,7 +104,7 @@
|
||||||
// the Amiga's layout), and a present copies its dirty rows into the text
|
// 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
|
// 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-colour palette, so per-line SCB palettes are reloaded as the beam scans
|
||||||
// by a CRTC raster interrupt (src/x68000/x68kRasterIsr.s).
|
// by a CRTC raster interrupt (src/x68000/jlX68kRasterIsr.s).
|
||||||
#define JL_CPU_68000
|
#define JL_CPU_68000
|
||||||
#define JL_ENDIAN_BIG
|
#define JL_ENDIAN_BIG
|
||||||
#define JL_NATIVE_PLANAR
|
#define JL_NATIVE_PLANAR
|
||||||
|
|
@ -137,6 +137,8 @@
|
||||||
#endif
|
#endif
|
||||||
#if defined(JOEYLIB_HOSTED_SDL3) && defined(__PSP__)
|
#if defined(JOEYLIB_HOSTED_SDL3) && defined(__PSP__)
|
||||||
#define JL_PLATFORM_NAME "PSP"
|
#define JL_PLATFORM_NAME "PSP"
|
||||||
|
#elif defined(JOEYLIB_HOSTED_SDL3) && defined(__APPLE__)
|
||||||
|
#define JL_PLATFORM_NAME "macOS"
|
||||||
#elif defined(JOEYLIB_HOSTED_SDL3)
|
#elif defined(JOEYLIB_HOSTED_SDL3)
|
||||||
#define JL_PLATFORM_NAME "SDL3"
|
#define JL_PLATFORM_NAME "SDL3"
|
||||||
#elif defined(JOEYLIB_HOSTED_WIN32)
|
#elif defined(JOEYLIB_HOSTED_WIN32)
|
||||||
|
|
@ -175,32 +177,34 @@
|
||||||
// original joeylib's joey.h JL_HAS_* table.
|
// original joeylib's joey.h JL_HAS_* table.
|
||||||
|
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
#define JL_HAS_SURFACE_CLEAR // iigsSurfaceClearFastInner (stage), macro
|
#define JL_HAS_SURFACE_CLEAR // jlIigsSurfaceClearFastInner (stage), macro
|
||||||
#define JL_HAS_DRAW_PIXEL // iigsDrawPixelInner, macro
|
#define JL_HAS_DRAW_PIXEL // jlIigsDrawPixelInner, macro
|
||||||
#define JL_HAS_DRAW_LINE // iigsDrawLineInner, macro
|
#define JL_HAS_DRAW_LINE // jlIigsDrawLineInner, macro
|
||||||
#define JL_HAS_DRAW_CIRCLE // iigsDrawCircleInner, macro
|
#define JL_HAS_DRAW_CIRCLE // jlIigsDrawCircleInner, macro
|
||||||
#define JL_HAS_FILL_CIRCLE // iigsFillCircleInner (stage), macro -- returns bool
|
#define JL_HAS_FILL_CIRCLE // jlIigsFillCircleInner (stage), macro -- returns bool
|
||||||
#define JL_HAS_FILL_RECT // iigsFillRectMark (stage, fused mark), macro -> generic else
|
#define JL_HAS_FILL_RECT // jlIigsFillRectMark (stage, fused mark), macro -> generic else
|
||||||
#define JL_HAS_TILE_FILL // iigsTileFillInner, macro
|
#define JL_HAS_TILE_FILL // jlIigsTileFillInner, macro
|
||||||
#define JL_HAS_TILE_COPY // iigsTileCopyInner, macro
|
#define JL_HAS_TILE_COPY // jlIigsTileCopyInner, macro
|
||||||
#define JL_HAS_TILE_COPY_MASKED // iigsTileCopyMaskedInner, macro
|
#define JL_HAS_TILE_COPY_MASKED // jlIigsTileCopyMaskedInner, macro
|
||||||
#define JL_HAS_TILE_PASTE // iigsTilePasteInner, macro
|
#define JL_HAS_TILE_PASTE // jlIigsTilePasteInner, macro
|
||||||
#define JL_HAS_TILE_PASTE_MONO // iigsTilePasteMonoInner (stage), macro -> generic else
|
#define JL_HAS_TILE_PASTE_MONO // jlIigsTilePasteMonoInner (stage), macro -> generic else
|
||||||
#define JL_HAS_TILE_PASTE_GLYPH // iigsTilePasteGlyphMark (stage, mark fused), macro -> generic else
|
#define JL_HAS_TILE_PASTE_GLYPH_ENTRY // jlTilePasteGlyph itself, asm (joeyDraw.s): stage, mark fused; generic else
|
||||||
#define JL_HAS_TILE_SNAP // iigsTileSnapInner, macro
|
#define JL_HAS_TILE_SNAP // jlIigsTileSnapInner, macro
|
||||||
#define JL_HAS_SURFACE_COPY_RECT // jlpSurfaceCopyRect: iigsCopyRectRows
|
#define JL_HAS_SURFACE_COPY_RECT // jlpSurfaceCopyRect: jlIigsCopyRectRows
|
||||||
// [dp],y row copies (short rows lose to
|
// [dp],y row copies (short rows lose to
|
||||||
// memcpy's per-call setup), function
|
// memcpy's per-call setup), function
|
||||||
|
#define JL_HAS_TILE_LAYER_BLIT // the generic rings, an unrolled stage blit (iigsTileLayer.c)
|
||||||
|
#define JL_HAS_TILE_LAYER_PUT // the generic rings, an asm put (iigsTileLayer.c)
|
||||||
#define JL_HAS_SPRITE_DRAW // chunky -> codegen; planes mirror is noop macro
|
#define JL_HAS_SPRITE_DRAW // chunky -> codegen; planes mirror is noop macro
|
||||||
#define JL_HAS_SPRITE_SAVE // noop macro (codegen path saves)
|
#define JL_HAS_SPRITE_SAVE // noop macro (codegen path saves)
|
||||||
#define JL_HAS_SPRITE_RESTORE // noop macro
|
#define JL_HAS_SPRITE_RESTORE // noop macro
|
||||||
#define JL_HAS_FLOOD_WALK_AND_SCANS // iigsFloodWalkAndScansInner (all-in-one), macro
|
#define JL_HAS_FLOOD_WALK_AND_SCANS // jlIigsFloodWalkAndScansInner (all-in-one), macro
|
||||||
#define JL_HAS_BIG_ALLOC // Memory Manager NewHandle/DisposeHandle, function
|
#define JL_HAS_BIG_ALLOC // Memory Manager NewHandle/DisposeHandle, function
|
||||||
#define JL_HAS_STAGE_ALLOC_PIXELS // pinned $01:2000 display memory, function
|
#define JL_HAS_STAGE_ALLOC_PIXELS // pinned $01:2000 display memory, function
|
||||||
#define JL_HAS_STAGE_FREE_PIXELS // no-op (pinned), function
|
#define JL_HAS_STAGE_FREE_PIXELS // no-op (pinned), function
|
||||||
#define JL_HAS_FRAME_HZ // runtime 50/60 from battery RAM, function
|
#define JL_HAS_FRAME_HZ // runtime 50/60 from battery RAM, function
|
||||||
#define JL_HAS_MILLIS_ELAPSED // heartbeat frames plus the beam's line
|
#define JL_HAS_MILLIS_ELAPSED // heartbeat frames plus the beam's line
|
||||||
// (frameCount: port.h #defines jlpFrameCount straight to iigsGetTickWord,
|
// (frameCount: port.h #defines jlpFrameCount straight to jlIigsGetTickWord,
|
||||||
// the VBL heartbeat tick read -- no function override to register here.)
|
// the VBL heartbeat tick read -- no function override to register here.)
|
||||||
#define JL_HAS_WAIT_VBL // the VBL heartbeat count advancing, function
|
#define JL_HAS_WAIT_VBL // the VBL heartbeat count advancing, function
|
||||||
// audio: every real port implements the full engine
|
// audio: every real port implements the full engine
|
||||||
|
|
@ -227,7 +231,9 @@
|
||||||
#define JL_HAS_INPUT_INIT
|
#define JL_HAS_INPUT_INIT
|
||||||
#define JL_HAS_INPUT_SHUTDOWN
|
#define JL_HAS_INPUT_SHUTDOWN
|
||||||
#define JL_HAS_INPUT_POLL
|
#define JL_HAS_INPUT_POLL
|
||||||
|
#define JL_HAS_INPUT_PREDICATES // jlKey/jlMouse Down/Pressed/Released themselves, asm (joeyDraw.s)
|
||||||
#define JL_HAS_JOYSTICK_RESET
|
#define JL_HAS_JOYSTICK_RESET
|
||||||
|
#define JL_HAS_JOYSTICK_AXES_READ // the paddles when asked for, not every poll (input.c)
|
||||||
// serial add-on (optional; SCC modem/printer + 6551 Super Serial Card)
|
// serial add-on (optional; SCC modem/printer + 6551 Super Serial Card)
|
||||||
#define JL_HAS_SERIAL_OPEN
|
#define JL_HAS_SERIAL_OPEN
|
||||||
#define JL_HAS_SERIAL_CLOSE
|
#define JL_HAS_SERIAL_CLOSE
|
||||||
|
|
@ -252,7 +258,7 @@
|
||||||
// millisElapsed: generic frameCount*1000/frameHz (no override)
|
// millisElapsed: generic frameCount*1000/frameHz (no override)
|
||||||
#elif defined(JOEYLIB_PLATFORM_AMIGA)
|
#elif defined(JOEYLIB_PLATFORM_AMIGA)
|
||||||
#define JL_HAS_SURFACE_CLEAR // planar clear, function
|
#define JL_HAS_SURFACE_CLEAR // planar clear, function
|
||||||
#define JL_HAS_DRAW_PIXEL // amigaPlanarSetPixel, function
|
#define JL_HAS_DRAW_PIXEL // planarDrawPixel, inline (amigaTile.h)
|
||||||
#define JL_HAS_DRAW_LINE // amigaPlanarLine, function
|
#define JL_HAS_DRAW_LINE // amigaPlanarLine, function
|
||||||
#define JL_HAS_DRAW_CIRCLE // amiga planar circle, function
|
#define JL_HAS_DRAW_CIRCLE // amiga planar circle, function
|
||||||
#define JL_HAS_FILL_CIRCLE // amiga planar fill circle, function -- returns bool
|
#define JL_HAS_FILL_CIRCLE // amiga planar fill circle, function -- returns bool
|
||||||
|
|
@ -263,15 +269,16 @@
|
||||||
#define JL_HAS_TILE_PASTE // amiga planar tile paste, inline (amigaTile.h)
|
#define JL_HAS_TILE_PASTE // amiga planar tile paste, inline (amigaTile.h)
|
||||||
#define JL_HAS_TILE_SNAP // amiga planar tile snap, inline (amigaTile.h)
|
#define JL_HAS_TILE_SNAP // amiga planar tile snap, inline (amigaTile.h)
|
||||||
#define JL_HAS_TILE_PASTE_MONO // amiga planar mono colorize+paste, inline (amigaTile.h)
|
#define JL_HAS_TILE_PASTE_MONO // amiga planar mono colorize+paste, inline (amigaTile.h)
|
||||||
#define JL_HAS_TILE_PASTE_GLYPH // amiga 1bpp glyph -> planes, inline (amigaTile.h)
|
#define JL_HAS_TILE_PASTE_GLYPH_ENTRY // jlTilePasteGlyph itself, asm (src/m68k/planarShape.s): planes, mark fused
|
||||||
|
#define JL_HAS_FILL_RECT_ENTRY // jlFillRect itself, asm (src/m68k/planarFill.s): a row; draw.c's is jlFillRectClip
|
||||||
#define JL_HAS_SPRITE_DRAW // amiga planar sprite draw, function
|
#define JL_HAS_SPRITE_DRAW // amiga planar sprite draw, function
|
||||||
#define JL_HAS_SPRITE_SAVE // amiga planar sprite save, function
|
#define JL_HAS_SPRITE_SAVE // planarSpriteSavePlanes, inline (amigaTile.h)
|
||||||
#define JL_HAS_SPRITE_RESTORE // amiga planar sprite restore, function
|
#define JL_HAS_SPRITE_RESTORE // planarSpriteRestorePlanes, inline (amigaTile.h)
|
||||||
#define JL_HAS_FLOOD_WALK_PLANES // amiga planar flood walk-out, function
|
#define JL_HAS_FLOOD_WALK_PLANES // amiga planar flood walk-out, function
|
||||||
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // amiga planar flood scan-row, function
|
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // amiga planar flood scan-row, function
|
||||||
#define JL_HAS_SURFACE_COPY_PLANES // amiga blitter plane copy, function
|
#define JL_HAS_SURFACE_COPY_PLANES // amiga blitter plane copy, function
|
||||||
#define JL_HAS_SURFACE_COPY_RECT // amiga per-plane rect row copy, function
|
#define JL_HAS_SURFACE_COPY_RECT // amiga per-plane rect row copy, function
|
||||||
#define JL_HAS_SAMPLE_PIXEL // amiga planar nibble decode, function
|
#define JL_HAS_SAMPLE_PIXEL // planarSamplePixel, inline (amigaTile.h)
|
||||||
#define JL_HAS_SURFACE_HASH // amiga planar hash, function
|
#define JL_HAS_SURFACE_HASH // amiga planar hash, function
|
||||||
#define JL_HAS_SURFACE_LOAD_FILE // amiga planar load, function
|
#define JL_HAS_SURFACE_LOAD_FILE // amiga planar load, function
|
||||||
#define JL_HAS_SURFACE_SAVE_FILE // amiga planar save, function
|
#define JL_HAS_SURFACE_SAVE_FILE // amiga planar save, function
|
||||||
|
|
@ -279,6 +286,7 @@
|
||||||
#define JL_HAS_SURFACE_ALLOC_PIXELS // amiga: NULL (planar storage), function
|
#define JL_HAS_SURFACE_ALLOC_PIXELS // amiga: NULL (planar storage), function
|
||||||
#define JL_HAS_SURFACE_ALLOC_PORT_DATA // amiga AmigaPlanarT + chip planes, function
|
#define JL_HAS_SURFACE_ALLOC_PORT_DATA // amiga AmigaPlanarT + chip planes, function
|
||||||
#define JL_HAS_SURFACE_FREE_PORT_DATA // amiga FreeMem planes, function
|
#define JL_HAS_SURFACE_FREE_PORT_DATA // amiga FreeMem planes, function
|
||||||
|
#define JL_HAS_TILE_LAYER // the shared separate-plane ring + blit (src/m68k/planarTileLayer.c)
|
||||||
#define JL_HAS_FRAME_HZ // 50 PAL / 60 NTSC, measured at init, function
|
#define JL_HAS_FRAME_HZ // 50 PAL / 60 NTSC, measured at init, function
|
||||||
#define JL_HAS_FRAME_COUNT // VBL-server counter, function
|
#define JL_HAS_FRAME_COUNT // VBL-server counter, function
|
||||||
#define JL_HAS_WAIT_VBL // WaitTOF, function
|
#define JL_HAS_WAIT_VBL // WaitTOF, function
|
||||||
|
|
@ -334,26 +342,27 @@
|
||||||
// millisElapsed: generic frameCount*1000/frameHz (no override)
|
// millisElapsed: generic frameCount*1000/frameHz (no override)
|
||||||
#elif defined(JOEYLIB_PLATFORM_ATARIST)
|
#elif defined(JOEYLIB_PLATFORM_ATARIST)
|
||||||
#define JL_HAS_SURFACE_CLEAR // planar clear, function
|
#define JL_HAS_SURFACE_CLEAR // planar clear, function
|
||||||
#define JL_HAS_DRAW_PIXEL // stPlanarSetPixel, function
|
#define JL_HAS_DRAW_PIXEL // stDrawPixel, inline (stTile.h)
|
||||||
#define JL_HAS_DRAW_LINE // stPlanarLine, function
|
#define JL_HAS_DRAW_LINE // stPlanarLine, function
|
||||||
#define JL_HAS_DRAW_CIRCLE // st planar circle, function
|
#define JL_HAS_DRAW_CIRCLE // st planar circle, function
|
||||||
#define JL_HAS_FILL_CIRCLE // st planar fill circle, function -- returns bool
|
#define JL_HAS_FILL_CIRCLE // st planar fill circle, function -- returns bool
|
||||||
#define JL_HAS_FILL_RECT // st planar fill rect, function
|
#define JL_HAS_FILL_RECT // stFillRect, inline (stTile.h)
|
||||||
|
#define JL_HAS_FILL_RECT_ENTRY // jlFillRect itself, asm (lineSpan.s); draw.c's is jlFillRectClip
|
||||||
#define JL_HAS_TILE_FILL // st planar tile fill, inline (stTile.h)
|
#define JL_HAS_TILE_FILL // st planar tile fill, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_COPY // st planar tile copy, inline (stTile.h)
|
#define JL_HAS_TILE_COPY // st planar tile copy, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_COPY_MASKED // st planar masked copy, inline (stTile.h)
|
#define JL_HAS_TILE_COPY_MASKED // st planar masked copy, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_PASTE // st planar tile paste, inline (stTile.h)
|
#define JL_HAS_TILE_PASTE // st planar tile paste, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_SNAP // st planar tile snap, inline (stTile.h)
|
#define JL_HAS_TILE_SNAP // st planar tile snap, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_PASTE_MONO // st planar mono colorize+paste, inline (stTile.h)
|
#define JL_HAS_TILE_PASTE_MONO // st planar mono colorize+paste, inline (stTile.h)
|
||||||
#define JL_HAS_TILE_PASTE_GLYPH // st 1bpp glyph -> planes, inline (stTile.h)
|
#define JL_HAS_TILE_PASTE_GLYPH_ENTRY // jlTilePasteGlyph itself, asm (stGlyph.s): planes, mark fused
|
||||||
#define JL_HAS_SPRITE_DRAW // st planar sprite draw, function
|
#define JL_HAS_SPRITE_DRAW // st planar sprite draw, function
|
||||||
#define JL_HAS_SPRITE_SAVE // st planar sprite save, function
|
#define JL_HAS_SPRITE_SAVE // stSpriteSavePlanes, inline (stTile.h)
|
||||||
#define JL_HAS_SPRITE_RESTORE // st planar sprite restore, function
|
#define JL_HAS_SPRITE_RESTORE // stSpriteRestorePlanes, inline (stTile.h)
|
||||||
#define JL_HAS_FLOOD_WALK_PLANES // st planar flood walk-out, function
|
#define JL_HAS_FLOOD_WALK_PLANES // st planar flood walk-out, function
|
||||||
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // st planar flood scan-row, function
|
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // st planar flood scan-row, function
|
||||||
#define JL_HAS_SURFACE_COPY_PLANES // st planar buffer memcpy, function
|
#define JL_HAS_SURFACE_COPY_PLANES // st planar buffer memcpy, function
|
||||||
#define JL_HAS_SURFACE_COPY_RECT // st interleaved rect row memcpy, function
|
#define JL_HAS_SURFACE_COPY_RECT // st interleaved rect row memcpy, function
|
||||||
#define JL_HAS_SAMPLE_PIXEL // st planar nibble decode, function
|
#define JL_HAS_SAMPLE_PIXEL // stSamplePixel, inline (stTile.h)
|
||||||
#define JL_HAS_SURFACE_HASH // st planar hash, function
|
#define JL_HAS_SURFACE_HASH // st planar hash, function
|
||||||
#define JL_HAS_SURFACE_LOAD_FILE // st planar load, function
|
#define JL_HAS_SURFACE_LOAD_FILE // st planar load, function
|
||||||
#define JL_HAS_SURFACE_SAVE_FILE // st planar save, function
|
#define JL_HAS_SURFACE_SAVE_FILE // st planar save, function
|
||||||
|
|
@ -361,6 +370,7 @@
|
||||||
#define JL_HAS_SURFACE_ALLOC_PIXELS // st: NULL (planar storage), function
|
#define JL_HAS_SURFACE_ALLOC_PIXELS // st: NULL (planar storage), function
|
||||||
#define JL_HAS_SURFACE_ALLOC_PORT_DATA // st StPlanarT + interleaved buffer, function
|
#define JL_HAS_SURFACE_ALLOC_PORT_DATA // st StPlanarT + interleaved buffer, function
|
||||||
#define JL_HAS_SURFACE_FREE_PORT_DATA // st free buffer, function
|
#define JL_HAS_SURFACE_FREE_PORT_DATA // st free buffer, function
|
||||||
|
#define JL_HAS_TILE_LAYER // st plane-separated ring + shifting blit (stTileLayer.c)
|
||||||
#define JL_HAS_FRAME_HZ // the machine's 50 / 60, function
|
#define JL_HAS_FRAME_HZ // the machine's 50 / 60, function
|
||||||
#define JL_HAS_FRAME_COUNT // VBL-ISR counter, function
|
#define JL_HAS_FRAME_COUNT // VBL-ISR counter, function
|
||||||
#define JL_HAS_WAIT_VBL // frame-counter wait, function
|
#define JL_HAS_WAIT_VBL // frame-counter wait, function
|
||||||
|
|
@ -448,6 +458,7 @@
|
||||||
#define JL_HAS_INPUT_SHUTDOWN
|
#define JL_HAS_INPUT_SHUTDOWN
|
||||||
#define JL_HAS_INPUT_POLL
|
#define JL_HAS_INPUT_POLL
|
||||||
#define JL_HAS_JOYSTICK_RESET
|
#define JL_HAS_JOYSTICK_RESET
|
||||||
|
#define JL_HAS_JOYSTICK_AXES_READ // the BIOS's axis read when asked for, not every poll (input.c)
|
||||||
// serial add-on (optional; 8250/16550 UART on a COM port, IRQ RX ring)
|
// serial add-on (optional; 8250/16550 UART on a COM port, IRQ RX ring)
|
||||||
#define JL_HAS_SERIAL_OPEN
|
#define JL_HAS_SERIAL_OPEN
|
||||||
#define JL_HAS_SERIAL_CLOSE
|
#define JL_HAS_SERIAL_CLOSE
|
||||||
|
|
@ -483,7 +494,8 @@
|
||||||
#define JL_HAS_TILE_COPY_MASKED
|
#define JL_HAS_TILE_COPY_MASKED
|
||||||
#define JL_HAS_TILE_PASTE
|
#define JL_HAS_TILE_PASTE
|
||||||
#define JL_HAS_TILE_SNAP
|
#define JL_HAS_TILE_SNAP
|
||||||
#define JL_HAS_TILE_PASTE_GLYPH // x68k 1bpp glyph -> planes, inline (x68kTile.h)
|
#define JL_HAS_TILE_PASTE_GLYPH_ENTRY // jlTilePasteGlyph itself, asm (src/m68k/planarShape.s): planes, mark fused
|
||||||
|
#define JL_HAS_FILL_RECT_ENTRY // jlFillRect itself, asm (src/m68k/planarFill.s): a row; draw.c's is jlFillRectClip
|
||||||
#define JL_HAS_TILE_PASTE_MONO
|
#define JL_HAS_TILE_PASTE_MONO
|
||||||
#define JL_HAS_SPRITE_DRAW
|
#define JL_HAS_SPRITE_DRAW
|
||||||
#define JL_HAS_SPRITE_SAVE
|
#define JL_HAS_SPRITE_SAVE
|
||||||
|
|
@ -500,6 +512,7 @@
|
||||||
#define JL_HAS_SURFACE_ALLOC_PIXELS
|
#define JL_HAS_SURFACE_ALLOC_PIXELS
|
||||||
#define JL_HAS_SURFACE_ALLOC_PORT_DATA
|
#define JL_HAS_SURFACE_ALLOC_PORT_DATA
|
||||||
#define JL_HAS_SURFACE_FREE_PORT_DATA
|
#define JL_HAS_SURFACE_FREE_PORT_DATA
|
||||||
|
#define JL_HAS_TILE_LAYER
|
||||||
#define JL_HAS_INIT // _iocs_crtmod display bring-up
|
#define JL_HAS_INIT // _iocs_crtmod display bring-up
|
||||||
#define JL_HAS_SHUTDOWN // restore the previous CRT mode
|
#define JL_HAS_SHUTDOWN // restore the previous CRT mode
|
||||||
#define JL_HAS_PRESENT // copy the stage's dirty plane rows into the text plane
|
#define JL_HAS_PRESENT // copy the stage's dirty plane rows into the text plane
|
||||||
|
|
@ -521,7 +534,7 @@
|
||||||
// block (fileRoot.h).
|
// block (fileRoot.h).
|
||||||
#define JL_HAS_FILE_ROOTS
|
#define JL_HAS_FILE_ROOTS
|
||||||
// Serial add-on: RSDRV.SYS transmit and line setup, receive on the
|
// Serial add-on: RSDRV.SYS transmit and line setup, receive on the
|
||||||
// port's own SCC interrupt (src/x68000/x68kSccRxIsr.s).
|
// port's own SCC interrupt (src/x68000/jlX68kSccRxIsr.s).
|
||||||
#define JL_HAS_SERIAL_OPEN
|
#define JL_HAS_SERIAL_OPEN
|
||||||
#define JL_HAS_SERIAL_CLOSE
|
#define JL_HAS_SERIAL_CLOSE
|
||||||
#define JL_HAS_SERIAL_POLL
|
#define JL_HAS_SERIAL_POLL
|
||||||
|
|
@ -629,7 +642,9 @@
|
||||||
#define JL_HAS_SAVE_DELETE
|
#define JL_HAS_SAVE_DELETE
|
||||||
#define JL_HAS_DISK_FREE
|
#define JL_HAS_DISK_FREE
|
||||||
// DATA/, SAVES/ and joeylog.txt in the program's folder, from the
|
// DATA/, SAVES/ and joeylog.txt in the program's folder, from the
|
||||||
// executable's path (hostBackendProgramFolder, fileRoot.h).
|
// executable's path (jlHostBackendProgramFolder, fileRoot.h); in a browser
|
||||||
|
// SAVES/ and the log in the page's folder, and on macOS in the program's
|
||||||
|
// Application Support folder (jlHostBackendWriteFolder).
|
||||||
#define JL_HAS_FILE_ROOTS
|
#define JL_HAS_FILE_ROOTS
|
||||||
// audio: a software mixer (MOD through the module player, the SFX slots, three
|
// audio: a software mixer (MOD through the module player, the SFX slots, three
|
||||||
// square-wave voices and YM-style noise) run on the program's thread, so
|
// square-wave voices and YM-style noise) run on the program's thread, so
|
||||||
|
|
@ -687,7 +702,7 @@
|
||||||
#define JL_HAS_PRESENT // blit the chunky stage to your framebuffer
|
#define JL_HAS_PRESENT // blit the chunky stage to your framebuffer
|
||||||
#define JL_HAS_INPUT_INIT
|
#define JL_HAS_INPUT_INIT
|
||||||
#define JL_HAS_INPUT_SHUTDOWN
|
#define JL_HAS_INPUT_SHUTDOWN
|
||||||
#define JL_HAS_INPUT_POLL // read your keyboard into gKeyState[]
|
#define JL_HAS_INPUT_POLL // read your keyboard into jlgKeyState[]
|
||||||
#define JL_HAS_JOYSTICK_RESET
|
#define JL_HAS_JOYSTICK_RESET
|
||||||
#define JL_HAS_WAIT_VBL // pace to the display
|
#define JL_HAS_WAIT_VBL // pace to the display
|
||||||
#define JL_HAS_FRAME_COUNT // a monotonic frame/refresh counter
|
#define JL_HAS_FRAME_COUNT // a monotonic frame/refresh counter
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,10 @@ void jlSpriteDrawTrusted(jlSurfaceT *s, jlSpriteT *sp, int16_t x, int16_t y);
|
||||||
// fully in-bounds draws; for clipped draws only the visible bytes are
|
// fully in-bounds draws; for clipped draws only the visible bytes are
|
||||||
// stored. The captured region's exact geometry is reported in
|
// stored. The captured region's exact geometry is reported in
|
||||||
// backup->x/y/width/height/sizeBytes: x is rounded down to even on every
|
// backup->x/y/width/height/sizeBytes: x is rounded down to even on every
|
||||||
// port, a compiled save on the Amiga, ST, and X68000 takes the 16-px-
|
// port, a save on the Amiga, ST, and X68000 takes the 16-px-aligned region
|
||||||
// aligned region containing the sprite, a compiled save on the IIgs only
|
// containing the sprite (or its visible part), a compiled save on the IIgs
|
||||||
// the rows holding an opaque pixel, and a clipped save the visible part.
|
// only the rows holding an opaque pixel, and a clipped save the visible
|
||||||
|
// part.
|
||||||
// So the region a caller must not invalidate before restoring is the
|
// So the region a caller must not invalidate before restoring is the
|
||||||
// RECORDED region, not just the sprite rect. On the IIgs the buffer must
|
// RECORDED region, not just the sprite rect. On the IIgs the buffer must
|
||||||
// not cross a 64 KB bank boundary. With backup->bytes NULL nothing is
|
// not cross a 64 KB bank boundary. With backup->bytes NULL nothing is
|
||||||
|
|
|
||||||
|
|
@ -172,4 +172,47 @@ void jlDrawText(jlSurfaceT *dst, uint8_t bx, uint8_t by, const jlSurfaceT *fontS
|
||||||
// convention. wTiles or hTiles of 0 is a no-op.
|
// convention. wTiles or hTiles of 0 is a no-op.
|
||||||
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map);
|
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map);
|
||||||
|
|
||||||
|
|
||||||
|
// ----- Tile layers (scrolling) -----
|
||||||
|
//
|
||||||
|
// A tile layer is a map of tile indices, up to JL_TILE_LAYER_MAX_SIDE
|
||||||
|
// tiles a side, seen through a band of a surface: whole tile rows
|
||||||
|
// [viewBy, viewBy + viewHeight), the full width. jlTileLayerDraw fills
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
// them all) before copying the band. A draw covers the band and never
|
||||||
|
// reads what was there: draw the layer, then the sprites (no save-under
|
||||||
|
// needed), then present.
|
||||||
|
//
|
||||||
|
// The layer keeps the tileset and the map you give it rather than
|
||||||
|
// copying them, so both must outlive it. Change a cell with
|
||||||
|
// jlTileLayerSetTile, not by writing the map: the layer would not see
|
||||||
|
// the write until the cell scrolled out of the view and back.
|
||||||
|
typedef struct jlTileLayerT jlTileLayerT;
|
||||||
|
|
||||||
|
#define JL_TILE_LAYER_MAX_SIDE 4096u
|
||||||
|
|
||||||
|
// Make a layer over `map` (mapWidth x mapHeight tile indices into `tiles`,
|
||||||
|
// row-major: map[row * mapWidth + col]) shown in tile rows [viewBy,
|
||||||
|
// viewBy + viewHeight). Returns NULL if an argument is NULL or out of
|
||||||
|
// range, or memory runs out. Nothing is drawn until jlTileLayerDraw.
|
||||||
|
jlTileLayerT *jlTileLayerCreate(const jlTileT *tiles, uint8_t *map, uint16_t mapWidth, uint16_t mapHeight, uint8_t viewBy, uint8_t viewHeight);
|
||||||
|
|
||||||
|
// Free a layer (not its tileset or map). NULL is a no-op.
|
||||||
|
void jlTileLayerDestroy(jlTileLayerT *layer);
|
||||||
|
|
||||||
|
// Fill the layer's band of dst with the map seen from pixel (x, y): the
|
||||||
|
// band's top-left pixel shows map pixel (x, y), taken modulo the map's
|
||||||
|
// size in pixels.
|
||||||
|
void jlTileLayerDraw(jlSurfaceT *dst, jlTileLayerT *layer, uint16_t x, uint16_t y);
|
||||||
|
|
||||||
|
// Set map cell (tx, ty) to `tile`; the next draw shows it. Out-of-range
|
||||||
|
// cells are ignored.
|
||||||
|
void jlTileLayerSetTile(jlTileLayerT *layer, uint16_t tx, uint16_t ty, uint8_t tile);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
#
|
#
|
||||||
# Targets are component names (see `list`) or groups:
|
# Targets are component names (see `list`) or groups:
|
||||||
# toolchain:PORT emulator:PORT PORT toolchains emulators all
|
# toolchain:PORT emulator:PORT PORT toolchains emulators all
|
||||||
# where PORT is iigs, amiga, atarist, dos, x68000, dvx, sdl3, web, win32, win16, psp, haiku, haiku32 or beos. `all`, `toolchains`
|
# where PORT is iigs, amiga, atarist, dos, x68000, dvx, sdl3, web, macos, win32, win16, psp, haiku, haiku32 or beos. `all`, `toolchains`
|
||||||
# and `emulators` leave out optional components (dvx, which unpacks a DVX
|
# and `emulators` leave out optional components (dvx, which unpacks a DVX
|
||||||
# tree's dist/ zips you have); name those explicitly.
|
# tree's dist/ zips you have); name those explicitly.
|
||||||
#
|
#
|
||||||
|
|
@ -62,7 +62,7 @@ source "$JOEY_INSTALLER/lib/config.sh"
|
||||||
# shellcheck source=installer/lib/bundle.sh
|
# shellcheck source=installer/lib/bundle.sh
|
||||||
source "$JOEY_INSTALLER/lib/bundle.sh"
|
source "$JOEY_INSTALLER/lib/bundle.sh"
|
||||||
|
|
||||||
JOEY_PORTS="iigs amiga atarist dos x68000 dvx sdl3 web win32 win16 psp haiku haiku32 beos"
|
JOEY_PORTS="iigs amiga atarist dos x68000 dvx sdl3 web macos win32 win16 psp haiku haiku32 beos"
|
||||||
depsInstall=0
|
depsInstall=0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ groupMembers() {
|
||||||
printf '%s\n' "$id"
|
printf '%s\n' "$id"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
iigs|amiga|atarist|dos|x68000|dvx|sdl3|web|win32|win16|psp)
|
iigs|amiga|atarist|dos|x68000|dvx|sdl3|web|macos|win32|win16|psp|haiku|haiku32|beos)
|
||||||
if [[ "$groups" == *" toolchain:$group "* || "$groups" == *" emulator:$group "* ]]; then
|
if [[ "$groups" == *" toolchain:$group "* || "$groups" == *" emulator:$group "* ]]; then
|
||||||
printf '%s\n' "$id"
|
printf '%s\n' "$id"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@
|
||||||
compDescription="Amiga compiler: m68k-amigaos-gcc 6.5 + NDK 3.2 + vasm (source build)"
|
compDescription="Amiga compiler: m68k-amigaos-gcc 6.5 + NDK 3.2 + vasm (source build)"
|
||||||
compGroups="toolchain:amiga"
|
compGroups="toolchain:amiga"
|
||||||
compRequires="host"
|
compRequires="host"
|
||||||
compHostDeps="build-tools git which bison flex m4 texinfo autoconf automake gettext rsync wget lhasa gmp-dev mpfr-dev mpc-dev ncurses-dev readline-dev"
|
compHostDeps="build-tools git patch which bison flex m4 texinfo autoconf automake gettext rsync wget lhasa gmp-dev mpfr-dev mpc-dev ncurses-dev readline-dev"
|
||||||
compUrl="https://github.com/AmigaPorts/m68k-amigaos-gcc.git"
|
compUrl="https://github.com/AmigaPorts/m68k-amigaos-gcc.git"
|
||||||
compCommit="7d3e35f58e61bdf325d84b912b139f973d9b7452"
|
compCommit="7d3e35f58e61bdf325d84b912b139f973d9b7452"
|
||||||
compPin="$compCommit"
|
patchDir="$JOEY_PATCHES/amigaGcc"
|
||||||
|
# Editing a patch rebuilds the toolchain, so the patches are part of the pin.
|
||||||
|
compPin="$compCommit $(cat "$patchDir"/*.patch | sha256sum | cut -d' ' -f1)"
|
||||||
|
|
||||||
# name commit (the URL comes from the checkout's default-repos file)
|
# name commit (the URL comes from the checkout's default-repos file)
|
||||||
subProjects=(
|
subProjects=(
|
||||||
|
|
@ -51,6 +53,7 @@ compInstall() {
|
||||||
local sha
|
local sha
|
||||||
local url
|
local url
|
||||||
local dir
|
local dir
|
||||||
|
local p
|
||||||
src="$(freshBuildDir "$compId")"
|
src="$(freshBuildDir "$compId")"
|
||||||
gitFetchCommit "$compUrl" "$compCommit" "$src"
|
gitFetchCommit "$compUrl" "$compCommit" "$src"
|
||||||
for entry in "${subProjects[@]}"; do
|
for entry in "${subProjects[@]}"; do
|
||||||
|
|
@ -59,6 +62,10 @@ compInstall() {
|
||||||
[ -n "$url" ] || die "amiga-gcc: no URL for sub-project $name in default-repos"
|
[ -n "$url" ] || die "amiga-gcc: no URL for sub-project $name in default-repos"
|
||||||
gitFetchCommit "$url" "$sha" "$src/projects/$name"
|
gitFetchCommit "$url" "$sha" "$src/projects/$name"
|
||||||
done
|
done
|
||||||
|
for p in "$patchDir"/*.patch; do
|
||||||
|
info "applying $(basename "$p")"
|
||||||
|
patch -d "$src/projects/gcc" -p1 -s -F0 --no-backup-if-mismatch < "$p"
|
||||||
|
done
|
||||||
rm -rf "$compDir"
|
rm -rf "$compDir"
|
||||||
make -C "$src" all PREFIX="$compDir" -j"$JOEY_JOBS"
|
make -C "$src" all PREFIX="$compDir" -j"$JOEY_JOBS"
|
||||||
# Anything cloned by the build itself is unpinned; say so.
|
# Anything cloned by the build itself is unpinned; say so.
|
||||||
|
|
|
||||||
57
installer/components/macosSdk.sh
Normal file
57
installer/components/macosSdk.sh
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
# See LICENSE.md for license information.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# The Apple SDK for macOS 15.5, and the Mach-O tools of the host's LLVM: what
|
||||||
|
# the macOS port (make macos) links its programs against and joins them
|
||||||
|
# with. The SDK holds the frameworks (Cocoa, CoreAudio, IOKit and the rest)
|
||||||
|
# and their stub libraries; zig (component zig) brings the C library. Apple
|
||||||
|
# ships the SDK only inside Xcode and offers no download of it, so it comes
|
||||||
|
# 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
|
||||||
|
# build into one universal file and llvm-libtool-darwin archives universal
|
||||||
|
# objects (make/support/macos/cc.sh, make/macos.mk).
|
||||||
|
|
||||||
|
compDescription="macos: Apple MacOSX 15.5 SDK + LLVM's lipo and libtool"
|
||||||
|
compGroups="toolchain:macos"
|
||||||
|
compRequires="host"
|
||||||
|
compHostDeps="tar xz llvm"
|
||||||
|
compUrl="https://github.com/joseluisq/macosx-sdks/releases/download/15.5/MacOSX15.5.sdk.tar.xz"
|
||||||
|
compSha256="c15cf0f3f17d714d1aa5a642da8e118db53d79429eb015771ba816aa7c6c1cbd"
|
||||||
|
compPin="$compSha256"
|
||||||
|
|
||||||
|
|
||||||
|
# llvmTool NAME -- the path of LLVM's NAME: on the PATH, else the newest
|
||||||
|
# versioned LLVM's (Debian and Ubuntu keep llvm-lipo in /usr/lib/llvm-18/bin
|
||||||
|
# and the like, with only some tools linked into /usr/bin).
|
||||||
|
llvmTool() {
|
||||||
|
command -v "$1" 2>/dev/null || find /usr/lib -maxdepth 3 -path "/usr/lib/llvm-*/bin/$1" 2>/dev/null | sort -V | tail -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compInstall() {
|
||||||
|
local archive
|
||||||
|
archive="$(fetchFile "$compUrl" "$compSha256")"
|
||||||
|
rm -rf "$compDir"
|
||||||
|
extractArchive "$archive" "$compDir" 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compInstalled() {
|
||||||
|
[ -d "$compDir/System/Library/Frameworks/Cocoa.framework" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compVerify() {
|
||||||
|
compInstalled && [ -x "$(llvmTool llvm-lipo)" ] && [ -x "$(llvmTool llvm-libtool-darwin)" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compEnv() {
|
||||||
|
envVar MACOS_SDK "$compDir"
|
||||||
|
envVar MACOS_LIPO "$(llvmTool llvm-lipo)"
|
||||||
|
envVar MACOS_LIBTOOL "$(llvmTool llvm-libtool-darwin)"
|
||||||
|
}
|
||||||
116
installer/components/sdl3Macos.sh
Normal file
116
installer/components/sdl3Macos.sh
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
# See LICENSE.md for license information.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# SDL 3 for macOS, static and universal, for the macOS port (make macos):
|
||||||
|
# the release source the sdl3 component builds for the host (its pin is read
|
||||||
|
# from sdl3.sh, so there is one), built once for Apple silicon and once for
|
||||||
|
# Intel with the port's own compiler (make/support/macos/cc.sh, zig and the
|
||||||
|
# Apple SDK), and the two libraries joined with llvm-lipo into
|
||||||
|
# toolchains/opt/sdl3Macos.
|
||||||
|
|
||||||
|
compDescription="macos: SDL 3 for macOS, static, universal (macos port)"
|
||||||
|
compGroups="toolchain:macos"
|
||||||
|
compRequires="host zig macosSdk"
|
||||||
|
compHostDeps="cmake pkg-config llvm"
|
||||||
|
sdl3Url="$(compQuery sdl3 compUrl)"
|
||||||
|
sdl3Sha256="$(compQuery sdl3 compSha256)"
|
||||||
|
macosCc="$JOEY_REPO/make/support/macos/cc.sh"
|
||||||
|
# How the compiler is driven is part of what is built, so the wrapper is part
|
||||||
|
# of the pin.
|
||||||
|
compPin="$sdl3Sha256:$(compQuery zig compPin):$(compQuery macosSdk compPin):$(sha256sum "$macosCc" | cut -d' ' -f1)"
|
||||||
|
|
||||||
|
|
||||||
|
# The tools the compiler wrapper reads from the environment, as make's
|
||||||
|
# recipes have them from toolchains/config.mk: zig's and the SDK's.
|
||||||
|
macosToolsExport() {
|
||||||
|
local kind
|
||||||
|
local name
|
||||||
|
local value
|
||||||
|
while IFS=$'\t' read -r kind name value; do
|
||||||
|
if [ "$kind" = var ]; then
|
||||||
|
export "$name=$value"
|
||||||
|
fi
|
||||||
|
done < <(compRun zig compEnv; compRun macosSdk compEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compInstall() {
|
||||||
|
local archive
|
||||||
|
local src
|
||||||
|
local arch
|
||||||
|
local processor
|
||||||
|
local libs=()
|
||||||
|
archive="$(fetchFile "$sdl3Url" "$sdl3Sha256")"
|
||||||
|
src="$(freshBuildDir "$compId")"
|
||||||
|
extractArchive "$archive" "$src" 1
|
||||||
|
macosToolsExport
|
||||||
|
for arch in aarch64 x86_64; do
|
||||||
|
export MACOS_ARCHS="$arch"
|
||||||
|
# Apple calls aarch64 arm64.
|
||||||
|
processor="${arch/aarch64/arm64}"
|
||||||
|
# Nothing here links dynamically, but CMake's Darwin support wants an
|
||||||
|
# install_name_tool all the same (LLVM's, beside its lipo); and no
|
||||||
|
# Xcode sysroot or -arch flags, since the wrapper names the SDK and
|
||||||
|
# the target itself.
|
||||||
|
cmake -S "$src" -B "$src/build-$arch" \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Darwin \
|
||||||
|
-DCMAKE_SYSTEM_PROCESSOR="$processor" \
|
||||||
|
-DCMAKE_C_COMPILER="$macosCc" \
|
||||||
|
-DCMAKE_OBJC_COMPILER="$macosCc" \
|
||||||
|
-DCMAKE_AR="$(dirname "$MACOS_LIPO")/llvm-ar" \
|
||||||
|
-DCMAKE_RANLIB="$(dirname "$MACOS_LIPO")/llvm-ranlib" \
|
||||||
|
-DCMAKE_INSTALL_NAME_TOOL="$(dirname "$MACOS_LIPO")/llvm-install-name-tool" \
|
||||||
|
-DCMAKE_OSX_SYSROOT= \
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES= \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH="$MACOS_SDK" \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="$compDir" \
|
||||||
|
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||||
|
-DSDL_SHARED=OFF \
|
||||||
|
-DSDL_STATIC=ON \
|
||||||
|
-DSDL_TESTS=OFF \
|
||||||
|
-DSDL_EXAMPLES=OFF
|
||||||
|
cmake --build "$src/build-$arch" -j"$JOEY_JOBS"
|
||||||
|
libs+=("$src/build-$arch/libSDL3.a")
|
||||||
|
done
|
||||||
|
rm -rf "$compDir"
|
||||||
|
# The headers, pkg-config file and CMake package are the same for both;
|
||||||
|
# the library is replaced by the universal one.
|
||||||
|
cmake --install "$src/build-aarch64"
|
||||||
|
"$MACOS_LIPO" -create "${libs[@]}" -output "$compDir/lib/libSDL3.a"
|
||||||
|
dropBuildDir "$compId"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compInstalled() {
|
||||||
|
[ -f "$compDir/lib/libSDL3.a" ] && [ -f "$compDir/lib/pkgconfig/sdl3.pc" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Link a program against the library the way make/macos.mk does: it must
|
||||||
|
# come out universal.
|
||||||
|
compVerify() {
|
||||||
|
local tmp
|
||||||
|
local status=1
|
||||||
|
macosToolsExport
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
printf '#include <SDL3/SDL.h>\nint main(void) { return SDL_GetVersion() == 0; }\n' > "$tmp/t.c"
|
||||||
|
# shellcheck disable=SC2046
|
||||||
|
if "$macosCc" "$tmp/t.c" -o "$tmp/t" $(PKG_CONFIG_LIBDIR="$compDir/lib/pkgconfig" pkg-config --cflags --static --libs sdl3) \
|
||||||
|
&& "$MACOS_LIPO" "$tmp/t" -verify_arch arm64 x86_64; then
|
||||||
|
status=0
|
||||||
|
fi
|
||||||
|
rm -rf "$tmp"
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compEnv() {
|
||||||
|
envVar SDL3_MACOS_DIR "$compDir"
|
||||||
|
}
|
||||||
44
installer/components/zig.sh
Normal file
44
installer/components/zig.sh
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
# See LICENSE.md for license information.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# zig 0.15.2: the C compiler the macOS port (make macos) builds with. zig is
|
||||||
|
# clang with its own Mach-O linker, the macOS C library's headers and
|
||||||
|
# libSystem's stubs, so it builds Mac programs on Linux and signs the Apple
|
||||||
|
# silicon ones (ad hoc) as it links them; the frameworks come from the Apple
|
||||||
|
# SDK (component macosSdk). Prebuilt for Linux x86-64. Its cache stays inside
|
||||||
|
# toolchains/opt/zig, not in the user's home directory.
|
||||||
|
|
||||||
|
compDescription="macos: zig 0.15.2 (clang with a Mach-O linker)"
|
||||||
|
compGroups="toolchain:macos"
|
||||||
|
compRequires="host"
|
||||||
|
compHostDeps="tar xz"
|
||||||
|
compUrl="https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz"
|
||||||
|
compSha256="02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239"
|
||||||
|
compPin="$compSha256"
|
||||||
|
|
||||||
|
|
||||||
|
compInstall() {
|
||||||
|
local archive
|
||||||
|
archive="$(fetchFile "$compUrl" "$compSha256")"
|
||||||
|
rm -rf "$compDir"
|
||||||
|
extractArchive "$archive" "$compDir" 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compInstalled() {
|
||||||
|
[ -x "$compDir/zig" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compVerify() {
|
||||||
|
ZIG_GLOBAL_CACHE_DIR="$compDir/cache" ZIG_LOCAL_CACHE_DIR="$compDir/cache" verifyCompiles "$compDir/zig" cc -target aarch64-macos
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compEnv() {
|
||||||
|
envVar ZIG "$compDir/zig"
|
||||||
|
envVar ZIG_GLOBAL_CACHE_DIR "$compDir/cache"
|
||||||
|
envVar ZIG_LOCAL_CACHE_DIR "$compDir/cache"
|
||||||
|
}
|
||||||
|
|
@ -91,6 +91,10 @@ gbm-dev libgbm-dev mesa-libgbm-devel
|
||||||
egl-dev libegl-dev mesa-libEGL-devel
|
egl-dev libegl-dev mesa-libEGL-devel
|
||||||
gles-dev libgles-dev mesa-libGLES-devel
|
gles-dev libgles-dev mesa-libGLES-devel
|
||||||
|
|
||||||
|
# ---- the macOS port: LLVM's Mach-O tools (llvm-lipo, llvm-libtool-darwin,
|
||||||
|
# llvm-install-name-tool), which zig does not carry
|
||||||
|
llvm llvm llvm
|
||||||
|
|
||||||
# ---- emulators packaged by the distribution
|
# ---- emulators packaged by the distribution
|
||||||
hatari hatari hatari
|
hatari hatari hatari
|
||||||
fs-uae fs-uae -
|
fs-uae fs-uae -
|
||||||
|
|
|
||||||
21
installer/patches/amigaGcc/0001-bbb-moveq-mask.patch
Normal file
21
installer/patches/amigaGcc/0001-bbb-moveq-mask.patch
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
--- a/gcc/bbb-opts.c
|
||||||
|
+++ b/gcc/bbb-opts.c
|
||||||
|
@@ -325,7 +325,17 @@
|
||||||
|
if (GET_CODE(src) == CONST_INT && (mode == HImode || mode == QImode))
|
||||||
|
{
|
||||||
|
unsigned iv = UINTVAL(src);
|
||||||
|
- setMask(regno, iv, mode);
|
||||||
|
+ /* output_move_himode and output_move_qimode load a data register's
|
||||||
|
+ word or byte constant in -128..127 with moveq, which writes all 32
|
||||||
|
+ bits sign-extended, where a strict_low_part move keeps the upper
|
||||||
|
+ ones. A negative one (but a byte's -1, which is st) may so leave
|
||||||
|
+ the upper bits set: keeping their old mask let opt_elim_dead_assign2
|
||||||
|
+ delete a later zero-extension's clear as superfluous. */
|
||||||
|
+ if (regno < 8 && INTVAL(src) >= -128 && INTVAL(src) < 0
|
||||||
|
+ && !(mode == QImode && INTVAL(src) == -1))
|
||||||
|
+ setMask(regno, iv, SImode);
|
||||||
|
+ else
|
||||||
|
+ setMask(regno, iv, mode);
|
||||||
|
value[regno] = gen_rtx_CONST_INT(mode, 0x100000000000000LL | ((long long int ) (regno) << 32) | index);
|
||||||
|
used[regno] = 1 << FIRST_PSEUDO_REGISTER;
|
||||||
|
clearRefsByMask(1<<regno, index);
|
||||||
14
installer/patches/amigaGcc/README.md
Normal file
14
installer/patches/amigaGcc/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# m68k-amigaos-gcc 6.5 patches
|
||||||
|
|
||||||
|
install.sh (component `amigaGcc`) applies these to the pinned `gcc`
|
||||||
|
sub-project (`projects/gcc`) in filename order before it builds the
|
||||||
|
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 |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The patch modifies GCC and contains its code, so it is under GCC's license,
|
||||||
|
the GNU GPL v3 or later, not JoeyLib's MIT License.
|
||||||
20
joey-run
20
joey-run
|
|
@ -4,6 +4,8 @@
|
||||||
# SDL3 port, natively, for the web port, in a browser, for the Win32 port,
|
# 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
|
# 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).
|
# 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
|
||||||
|
# programs.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./joey-run [options] PROGRAM [-- ARGS...]
|
# ./joey-run [options] PROGRAM [-- ARGS...]
|
||||||
|
|
@ -11,12 +13,12 @@
|
||||||
# PROGRAM is any of:
|
# PROGRAM is any of:
|
||||||
# a program file build/dos/bin/DRAW.EXE, build/atarist/bin/DRAW.PRG (or build/ste),
|
# a program file build/dos/bin/DRAW.EXE, build/atarist/bin/DRAW.PRG (or build/ste),
|
||||||
# build/amiga/bin/Draw, build/x68000/bin/DRAW.X,
|
# build/amiga/bin/Draw, build/x68000/bin/DRAW.X,
|
||||||
# build/iigs/bin/DRAW, build/dvx/bin/apps/joeylib/draw/draw.app,
|
# build/iigs/bin/DRAW, build/dvx/bin/apps/kpunch/draw/draw.app,
|
||||||
# build/sdl3/bin/DRAW, build/web/bin/DRAW/DRAW.html,
|
# build/sdl3/bin/DRAW, build/web/bin/DRAW/DRAW.html,
|
||||||
# build/psp/bin/DRAW/EBOOT.PBP, build/haiku/bin/DRAW
|
# build/psp/bin/DRAW/EBOOT.PBP, build/haiku/bin/DRAW
|
||||||
# (or build/haiku32, build/beos)
|
# (or build/haiku32, build/beos), build/macos/bin/DRAW.app
|
||||||
# an app folder a program in a folder of its own with its DATA:
|
# an app folder a program in a folder of its own with its DATA:
|
||||||
# build/dvx/bin/apps/joeylib/audio
|
# build/dvx/bin/apps/kpunch/audio
|
||||||
# an IIgs disk image build/iigs/bin/DRAW.2mg (make iigs-game-disk), or
|
# 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)
|
# a data disk such as joey.2mg (boots GS/OS with it)
|
||||||
# an example name draw, audio, uber, ... together with --port
|
# an example name draw, audio, uber, ... together with --port
|
||||||
|
|
@ -25,8 +27,8 @@
|
||||||
# image in the project's build/, or give its name with --project and --port.
|
# image in the project's build/, or give its name with --project and --port.
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# --port PORT iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, win32, win16,
|
# --port PORT iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, macos, win32,
|
||||||
# psp, haiku, haiku32 or beos
|
# win16, psp, haiku, haiku32 or beos
|
||||||
# --project DIR look a program name up in the project at DIR (its
|
# --project DIR look a program name up in the project at DIR (its
|
||||||
# build/PORT/bin) instead of JoeyLib's examples; DVX runs
|
# build/PORT/bin) instead of JoeyLib's examples; DVX runs
|
||||||
# keep their DVX in DIR's build/dvx/run
|
# keep their DVX in DIR's build/dvx/run
|
||||||
|
|
@ -273,6 +275,7 @@ magicPort() {
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
3c21646f*) printf 'web\n' ;; # "<!do(ctype html>": a web program's page
|
3c21646f*) printf 'web\n' ;; # "<!do(ctype html>": a web program's page
|
||||||
|
cafebabe*|cffaedfe*) printf 'macos\n' ;; # a Mach-O program, universal or 64-bit
|
||||||
00504250*) printf 'psp\n' ;; # "\0PBP": a PSP program's EBOOT.PBP
|
00504250*) printf 'psp\n' ;; # "\0PBP": a PSP program's EBOOT.PBP
|
||||||
*)
|
*)
|
||||||
# Apple IIgs OMF: segment header byte 14 (NUMLEN) is 4 and
|
# Apple IIgs OMF: segment header byte 14 (NUMLEN) is 4 and
|
||||||
|
|
@ -1108,7 +1111,7 @@ runBe() {
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--port) port="${2:?--port needs iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, win32, win16, psp, haiku, haiku32 or beos}"; shift ;;
|
--port) port="${2:?--port needs iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, macos, win32, win16, psp, haiku, haiku32 or beos}"; shift ;;
|
||||||
--project)
|
--project)
|
||||||
[ -d "${2:-}" ] || fail "--project needs a project folder (one built on make/project.mk)"
|
[ -d "${2:-}" ] || fail "--project needs a project folder (one built on make/project.mk)"
|
||||||
projectRoot="$(cd "$2" && pwd)"
|
projectRoot="$(cd "$2" && pwd)"
|
||||||
|
|
@ -1178,7 +1181,9 @@ if [ "$serialMode" = "listen" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Resolve PROGRAM to a file and its machine.
|
# Resolve PROGRAM to a file and its machine.
|
||||||
if [ -d "$program" ]; then
|
if [ -d "$program/Contents/MacOS" ]; then
|
||||||
|
program="$(folderProgram "$program/Contents/MacOS")"
|
||||||
|
elif [ -d "$program" ]; then
|
||||||
program="$(folderProgram "$program")"
|
program="$(folderProgram "$program")"
|
||||||
elif [ ! -e "$program" ]; then
|
elif [ ! -e "$program" ]; then
|
||||||
[ -n "$port" ] || fail "$program is not a file; for a program name add --port"
|
[ -n "$port" ] || fail "$program is not a file; for a program name add --port"
|
||||||
|
|
@ -1191,6 +1196,7 @@ detected="$(magicPort "$program")"
|
||||||
if [ -n "$port" ] && [ "$port" != "$detected" ]; then
|
if [ -n "$port" ] && [ "$port" != "$detected" ]; then
|
||||||
fail "$program is a $detected program, not $port"
|
fail "$program is a $detected program, not $port"
|
||||||
fi
|
fi
|
||||||
|
[ "$detected" != macos ] || fail "$program is a Mac app's: copy its .app to a Mac and open it there (nothing here runs macOS programs)"
|
||||||
if [ ${#progArgs[@]} -gt 0 ] && [ "$detected" != "dos" ] && [ "$detected" != "sdl3" ] && [ "$detected" != "win32" ] && [ "$detected" != "win16" ]; then
|
if [ ${#progArgs[@]} -gt 0 ] && [ "$detected" != "dos" ] && [ "$detected" != "sdl3" ] && [ "$detected" != "win32" ] && [ "$detected" != "win16" ]; then
|
||||||
fail "program arguments only work on DOS, SDL3, Win32 and Win16"
|
fail "program arguments only work on DOS, SDL3, Win32 and Win16"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ $(call requireTool,$(AMIGA_PTPLAYER)/ptplayer.asm,toolchain:amiga)
|
||||||
# independently. -I on $(SRC_DIR)/amiga lets ptplayer.h resolve
|
# independently. -I on $(SRC_DIR)/amiga lets ptplayer.h resolve
|
||||||
# <SDI_compiler.h> from the machine-local shim alongside our code.
|
# <SDI_compiler.h> from the machine-local shim alongside our code.
|
||||||
PTPLAYER_DIR := $(AMIGA_PTPLAYER)
|
PTPLAYER_DIR := $(AMIGA_PTPLAYER)
|
||||||
CFLAGS := $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer -noixemul -D__OSCOMPAT -I$(SRC_DIR)/amiga -I$(SRC_68K) -I$(PTPLAYER_DIR) -MMD -MP
|
# -fno-common: gcc 6 merges a program's uninitialised 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
|
# OSCOMPAT=1 selects PTPlayer's audio.device-friendly variant (uses
|
||||||
# CIA-B + audio.device interrupts via the OS rather than taking over
|
# CIA-B + audio.device interrupts via the OS rather than taking over
|
||||||
# Paula directly), matching the way our HAL cooperates with Intuition.
|
# Paula directly), matching the way our HAL cooperates with Intuition.
|
||||||
|
|
@ -33,11 +35,7 @@ CFLAGS := $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer -noixemul -D__OSCOMPAT
|
||||||
# "file format not recognized" failure mode is silent at archive time
|
# "file format not recognized" failure mode is silent at archive time
|
||||||
# and surfaces as undefined references at every binary's link step).
|
# and surfaces as undefined references at every binary's link step).
|
||||||
PTPLAYER_ASFLAGS := -Fhunk -m68000 -quiet -DOSCOMPAT=1
|
PTPLAYER_ASFLAGS := -Fhunk -m68000 -quiet -DOSCOMPAT=1
|
||||||
# --allow-multiple-definition lets our user-space tzset stub
|
LDFLAGS := -noixemul
|
||||||
# (src/amiga/libinit.c) win over libnix's version in
|
|
||||||
# __gmtoffset.o. libnix's tzset dereferences a possibly-NULL
|
|
||||||
# LocaleBase; our no-op skips the deref.
|
|
||||||
LDFLAGS := -noixemul -Wl,--allow-multiple-definition
|
|
||||||
|
|
||||||
PORT_C_SRCS := $(wildcard $(SRC_DIR)/amiga/*.c)
|
PORT_C_SRCS := $(wildcard $(SRC_DIR)/amiga/*.c)
|
||||||
PORT_S_SRCS := $(wildcard $(SRC_DIR)/amiga/*.s)
|
PORT_S_SRCS := $(wildcard $(SRC_DIR)/amiga/*.s)
|
||||||
|
|
@ -56,7 +54,10 @@ LIB_OBJS := \
|
||||||
$(patsubst $(SRC_68K)/%.s,$(BUILD)/obj/68k/%.o,$(SHARED_S)) \
|
$(patsubst $(SRC_68K)/%.s,$(BUILD)/obj/68k/%.o,$(SHARED_S)) \
|
||||||
$(BUILD)/obj/port/ptplayer.o \
|
$(BUILD)/obj/port/ptplayer.o \
|
||||||
$(BUILD)/obj/68k/spriteEmitPlanar68k.o \
|
$(BUILD)/obj/68k/spriteEmitPlanar68k.o \
|
||||||
|
$(BUILD)/obj/68k/planarHash.o \
|
||||||
$(BUILD)/obj/68k/planarSprite.o \
|
$(BUILD)/obj/68k/planarSprite.o \
|
||||||
|
$(BUILD)/obj/68k/spriteC2p.o \
|
||||||
|
$(BUILD)/obj/68k/planarTileLayer.o \
|
||||||
$(BUILD)/obj/codegen/spriteCompile.o \
|
$(BUILD)/obj/codegen/spriteCompile.o \
|
||||||
$(BUILD)/obj/codegen/spriteStage.o
|
$(BUILD)/obj/codegen/spriteStage.o
|
||||||
|
|
||||||
|
|
@ -75,10 +76,19 @@ $(BUILD)/obj/port/%.o: $(SRC_DIR)/amiga/%.c
|
||||||
$(AMIGA_CC) $(CFLAGS) -c $< -o $@
|
$(AMIGA_CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# Hand-written 68k assembly: GAS syntax, fed through the gcc driver
|
# Hand-written 68k assembly: GAS syntax, fed through the gcc driver
|
||||||
# so the bundled m68k-amigaos-as (binutils) does the work.
|
# so the bundled m68k-amigaos-as (binutils) does the work. It exports every
|
||||||
|
# .equ and .set as a global absolute symbol (in a hunk object), where every
|
||||||
|
# other port's assembler keeps them local; they would be in every program's
|
||||||
|
# link, sfxMix.s's loop counter "sum" among them, so each object's are made
|
||||||
|
# local again.
|
||||||
|
AMIGA_NM := $(dir $(AMIGA_CC))m68k-amigaos-nm
|
||||||
|
AMIGA_OBJCOPY := $(dir $(AMIGA_CC))m68k-amigaos-objcopy
|
||||||
|
amigaAbsLocal = $(AMIGA_OBJCOPY) $$($(AMIGA_NM) $(1) | awk '$$2 == "A" { print "-L", $$3 }') $(1)
|
||||||
|
|
||||||
$(BUILD)/obj/port/%.o: $(SRC_DIR)/amiga/%.s
|
$(BUILD)/obj/port/%.o: $(SRC_DIR)/amiga/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(AMIGA_CC) $(CFLAGS) -c $< -o $@
|
$(AMIGA_CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(call amigaAbsLocal,$@)
|
||||||
|
|
||||||
# PTPlayer is third-party Devpac/vasm-syntax assembly with no
|
# PTPlayer is third-party Devpac/vasm-syntax assembly with no
|
||||||
# maintained GAS port; vasm is invoked only here.
|
# maintained GAS port; vasm is invoked only here.
|
||||||
|
|
@ -90,9 +100,13 @@ $(BUILD)/obj/codegen/%.o: $(CODEGEN_DIR)/%.c
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(AMIGA_CC) $(CFLAGS) -I$(CODEGEN_DIR) -c $< -o $@
|
$(AMIGA_CC) $(CFLAGS) -I$(CODEGEN_DIR) -c $< -o $@
|
||||||
|
|
||||||
|
# gcc-amigaos spells a C symbol with a leading underscore; the shared asm
|
||||||
|
# that names one says it through C_OP (src/m68k/m68kCSym.inc, which the
|
||||||
|
# assembler finds through -I: the gcc driver hands it no include path).
|
||||||
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(AMIGA_CC) $(CFLAGS) -c $< -o $@
|
$(AMIGA_CC) $(CFLAGS) -Wa,--defsym,JL_ASM_USCORE=1 -Wa,-I$(SRC_68K) -c $< -o $@
|
||||||
|
$(call amigaAbsLocal,$@)
|
||||||
|
|
||||||
# 68k sprite emitters (spriteEmitPlanar68k.c) live in src/m68k/;
|
# 68k sprite emitters (spriteEmitPlanar68k.c) live in src/m68k/;
|
||||||
# -I$(CODEGEN_DIR) lets them resolve spriteEmitter.h.
|
# -I$(CODEGEN_DIR) lets them resolve spriteEmitter.h.
|
||||||
|
|
@ -158,3 +172,7 @@ clean-amiga:
|
||||||
clean: clean-amiga
|
clean: clean-amiga
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# UBER with its probe rows, for the display gate (scripts/verify-amiga-display.sh).
|
||||||
|
.PHONY: amiga-uberprob
|
||||||
|
amiga-uberprob: $(call programTarget,UBERPROB)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ ST_SHIM := $(REPO_DIR)/make/support/atarist/include-shim
|
||||||
ifeq ($(PLATFORM),ste)
|
ifeq ($(PLATFORM),ste)
|
||||||
STE_SRC_DIR := $(SRC_DIR)/atarist/ste
|
STE_SRC_DIR := $(SRC_DIR)/atarist/ste
|
||||||
STE_CFLAGS := -DJOEYLIB_ATARI_STE -I$(STE_SRC_DIR)
|
STE_CFLAGS := -DJOEYLIB_ATARI_STE -I$(STE_SRC_DIR)
|
||||||
|
STE_ASFLAGS := -Wa,--defsym,JOEYLIB_ATARI_STE=1
|
||||||
STE_C_SRCS := $(wildcard $(STE_SRC_DIR)/*.c)
|
STE_C_SRCS := $(wildcard $(STE_SRC_DIR)/*.c)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -33,7 +34,10 @@ LDFLAGS :=
|
||||||
|
|
||||||
PORT_C_SRCS := $(wildcard $(SRC_DIR)/atarist/*.c)
|
PORT_C_SRCS := $(wildcard $(SRC_DIR)/atarist/*.c)
|
||||||
PORT_S_SRCS := $(wildcard $(SRC_DIR)/atarist/*.s)
|
PORT_S_SRCS := $(wildcard $(SRC_DIR)/atarist/*.s)
|
||||||
SHARED_S := $(wildcard $(SRC_68K)/*.s)
|
# planarFill.s and planarShape.s are the separate-plane ports' (the Amiga,
|
||||||
|
# the X68000): the ST's planes interleave, and its jlFillRect and
|
||||||
|
# jlTilePasteGlyph are its own (lineSpan.s, stGlyph.s).
|
||||||
|
SHARED_S := $(filter-out $(SRC_68K)/planarFill.s $(SRC_68K)/planarShape.s,$(wildcard $(SRC_68K)/*.s))
|
||||||
|
|
||||||
# Sprite codegen: the ST-interleaved emitter (spriteEmitInterleaved68k.c)
|
# Sprite codegen: the ST-interleaved emitter (spriteEmitInterleaved68k.c)
|
||||||
# and the planar sprite interpreter (planarSprite.c) live in src/m68k/ and
|
# and the planar sprite interpreter (planarSprite.c) live in src/m68k/ and
|
||||||
|
|
@ -49,7 +53,9 @@ LIB_OBJS := \
|
||||||
$(patsubst $(STE_SRC_DIR)/%.c,$(BUILD)/obj/ste/%.o,$(STE_C_SRCS)) \
|
$(patsubst $(STE_SRC_DIR)/%.c,$(BUILD)/obj/ste/%.o,$(STE_C_SRCS)) \
|
||||||
$(patsubst $(SRC_68K)/%.s,$(BUILD)/obj/68k/%.o,$(SHARED_S)) \
|
$(patsubst $(SRC_68K)/%.s,$(BUILD)/obj/68k/%.o,$(SHARED_S)) \
|
||||||
$(BUILD)/obj/68k/spriteEmitInterleaved68k.o \
|
$(BUILD)/obj/68k/spriteEmitInterleaved68k.o \
|
||||||
|
$(BUILD)/obj/68k/planarHash.o \
|
||||||
$(BUILD)/obj/68k/planarSprite.o \
|
$(BUILD)/obj/68k/planarSprite.o \
|
||||||
|
$(BUILD)/obj/68k/spriteC2p.o \
|
||||||
$(BUILD)/obj/codegen/spriteCompile.o \
|
$(BUILD)/obj/codegen/spriteCompile.o \
|
||||||
$(BUILD)/obj/codegen/spriteStage.o \
|
$(BUILD)/obj/codegen/spriteStage.o \
|
||||||
$(BUILD)/obj/shim/math_shim.o
|
$(BUILD)/obj/shim/math_shim.o
|
||||||
|
|
@ -76,10 +82,11 @@ endif
|
||||||
|
|
||||||
# Hand-written 68k assembly: GAS syntax, fed through the gcc driver
|
# Hand-written 68k assembly: GAS syntax, fed through the gcc driver
|
||||||
# so the bundled m68k-atari-mint-as (binutils) does the work. No vasm
|
# so the bundled m68k-atari-mint-as (binutils) does the work. No vasm
|
||||||
# dependency on the ST path.
|
# dependency on the ST path. The STe build defines JOEYLIB_ATARI_STE for
|
||||||
|
# the port's .ifdef as well (the .s files see no preprocessor).
|
||||||
$(BUILD)/obj/port/%.o: $(SRC_DIR)/atarist/%.s
|
$(BUILD)/obj/port/%.o: $(SRC_DIR)/atarist/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(ST_CC) $(CFLAGS) -c $< -o $@
|
$(ST_CC) $(CFLAGS) $(STE_ASFLAGS) -c $< -o $@
|
||||||
|
|
||||||
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
|
|
@ -147,6 +154,10 @@ clean-$(PLATFORM):
|
||||||
# looks like success while leaving stale objects the next build links.
|
# looks like success while leaving stale objects the next build links.
|
||||||
clean: clean-$(PLATFORM)
|
clean: clean-$(PLATFORM)
|
||||||
|
|
||||||
|
# UBER with its probe rows, for the display gate (scripts/verify-atarist-display.sh).
|
||||||
|
.PHONY: $(PLATFORM)-uberprob
|
||||||
|
$(PLATFORM)-uberprob: $(call programTarget,UBERPROB)
|
||||||
|
|
||||||
# The dirty-mark check is the ST build's (DIRTYMRK_PORTS).
|
# The dirty-mark check is the ST build's (DIRTYMRK_PORTS).
|
||||||
ifeq ($(PLATFORM),atarist)
|
ifeq ($(PLATFORM),atarist)
|
||||||
.PHONY: atarist-dirtymark atarist-verify-dirtymark
|
.PHONY: atarist-dirtymark atarist-verify-dirtymark
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,16 @@ PROGRAM_EXT :=
|
||||||
# Its icons, the BeOS kind Haiku shows too: 32x32 and 16x16 in the BeOS
|
# Its icons, the BeOS kind Haiku shows too: 32x32 and 16x16 in the BeOS
|
||||||
# palette, raw (tools/iconBake.py be), put into the linked program's
|
# palette, raw (tools/iconBake.py be), put into the linked program's
|
||||||
# resources by xres (component haikuTools) as BEOS:L:STD_ICON and
|
# resources by xres (component haikuTools) as BEOS:L:STD_ICON and
|
||||||
# BEOS:M:STD_ICON, BE_ICON_ID each.
|
# BEOS:M:STD_ICON, BE_ICON_ID each; and the 32x32 one as Haiku's vector
|
||||||
|
# icon, BEOS:ICON, which Haiku draws at any size (BeOS R5 ignores it). An
|
||||||
|
# icon too busy for one bakes an empty file and goes without.
|
||||||
BE_XRES := $(HAIKU_TOOLS)/xres
|
BE_XRES := $(HAIKU_TOOLS)/xres
|
||||||
$(call requireTool,$(BE_XRES),toolchain:$(PLATFORM))
|
$(call requireTool,$(BE_XRES),toolchain:$(PLATFORM))
|
||||||
BE_ICON_ID := 101
|
BE_ICON_ID := 101
|
||||||
HOSTED_ICON = $(call programIconDir,$(1))/icon32.raw
|
HOSTED_ICON = $(call programIconDir,$(1))/icon32.raw
|
||||||
HOSTED_ICON_BAKE = $(ICON_BAKE) be $(call programIcon,$(1)) $(call HOSTED_ICON,$(1)) $(call programIconDir,$(1))/icon16.raw
|
BE_ICON_VECTOR = $(call programIconDir,$(1))/icon.hvif
|
||||||
HOSTED_PACK = $(BE_XRES) -o $(call programBin,$(1)) -a ICON:$(BE_ICON_ID):BEOS:L:STD_ICON $(call HOSTED_ICON,$(1)) -a MICN:$(BE_ICON_ID):BEOS:M:STD_ICON $(call programIconDir,$(1))/icon16.raw
|
HOSTED_ICON_BAKE = $(ICON_BAKE) be $(call programIcon,$(1)) $(call HOSTED_ICON,$(1)) $(call programIconDir,$(1))/icon16.raw $(call BE_ICON_VECTOR,$(1))
|
||||||
|
HOSTED_PACK = $(BE_XRES) -o $(call programBin,$(1)) -a ICON:$(BE_ICON_ID):BEOS:L:STD_ICON $(call HOSTED_ICON,$(1)) -a MICN:$(BE_ICON_ID):BEOS:M:STD_ICON $(call programIconDir,$(1))/icon16.raw $$$$([ -s $(call BE_ICON_VECTOR,$(1)) ] && echo "-a VICN:$(BE_ICON_ID):BEOS:ICON $(call BE_ICON_VECTOR,$(1))")
|
||||||
|
|
||||||
include $(REPO_DIR)/make/hosted.mk
|
include $(REPO_DIR)/make/hosted.mk
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ $(call requireTool,$(HAIKU32_ROOT)/cross/bin/i586-pc-haiku-g++,toolchain:beos)
|
||||||
$(call requireTool,$(BEOS_R5_SYSROOT)/system-lib/libbe.so,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)
|
BEOS_GCC2 := BE_R5_SYSROOT=$(BEOS_R5_SYSROOT) $(HAIKU32_GCC2) $(HAIKU32_ROOT)
|
||||||
HOSTED_CC := $(BEOS_GCC2) gcc
|
# -fno-common: gcc 2.95 merges a program's uninitialised 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++
|
HOSTED_CXX := $(BEOS_GCC2) g++
|
||||||
HOSTED_AR := $(BEOS_GCC2) ar
|
HOSTED_AR := $(BEOS_GCC2) ar
|
||||||
BE_CXX_RUNTIME := -lstdc++.r4
|
BE_CXX_RUNTIME := -lstdc++.r4
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,12 @@ REPO_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..)
|
||||||
# `./install.sh doctor` names exactly what is wrong.
|
# `./install.sh doctor` names exactly what is wrong.
|
||||||
requireTool = $(if $(filter-out clean%,$(or $(MAKECMDGOALS),all)),$(if $(wildcard $(1)),,$(error $(2) is not installed; run: ./install.sh install $(2))))
|
requireTool = $(if $(filter-out clean%,$(or $(MAKECMDGOALS),all)),$(if $(wildcard $(1)),,$(error $(2) is not installed; run: ./install.sh install $(2))))
|
||||||
|
|
||||||
|
# $(call requireSet,VARIABLE,WHAT): stop with a clear message when a part of
|
||||||
|
# a program's identity is not set -- who makes it (DVX's vendor and author),
|
||||||
|
# a Mac app's identifier -- which is the project's own and never JoeyLib's.
|
||||||
|
# WHAT says what to set it to. Goals that only clean need none.
|
||||||
|
requireSet = $(if $(filter-out clean%,$(or $(MAKECMDGOALS),all)),$(if $(strip $($(1))),,$(error $(1) is not set: $(2))))
|
||||||
|
|
||||||
# Letter case, for the port tags (IIGS) and the program names each port
|
# Letter case, for the port tags (IIGS) and the program names each port
|
||||||
# derives from a program's id (Hello on the Amiga, hello on DVX).
|
# derives from a program's id (Hello on the Amiga, hello on DVX).
|
||||||
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))
|
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))
|
||||||
|
|
@ -29,12 +35,14 @@ uc = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f
|
||||||
# $(call cQuote,TEXT) TEXT as a C string literal.
|
# $(call cQuote,TEXT) TEXT as a C string literal.
|
||||||
shellQuote = '$(subst ','\'',$(1))'
|
shellQuote = '$(subst ','\'',$(1))'
|
||||||
cQuote = "$(subst ",\",$(subst \,\\,$(1)))"
|
cQuote = "$(subst ",\",$(subst \,\\,$(1)))"
|
||||||
|
# A comma, for text inside a $(call) argument.
|
||||||
|
comma = ,
|
||||||
capFirst = $(patsubst a%,A%,$(patsubst b%,B%,$(patsubst c%,C%,$(patsubst d%,D%,$(patsubst e%,E%,$(patsubst f%,F%,$(patsubst g%,G%,$(patsubst h%,H%,$(patsubst i%,I%,$(patsubst j%,J%,$(patsubst k%,K%,$(patsubst l%,L%,$(patsubst m%,M%,$(patsubst n%,N%,$(patsubst o%,O%,$(patsubst p%,P%,$(patsubst q%,Q%,$(patsubst r%,R%,$(patsubst s%,S%,$(patsubst t%,T%,$(patsubst u%,U%,$(patsubst v%,V%,$(patsubst w%,W%,$(patsubst x%,X%,$(patsubst y%,Y%,$(patsubst z%,Z%,$(1)))))))))))))))))))))))))))
|
capFirst = $(patsubst a%,A%,$(patsubst b%,B%,$(patsubst c%,C%,$(patsubst d%,D%,$(patsubst e%,E%,$(patsubst f%,F%,$(patsubst g%,G%,$(patsubst h%,H%,$(patsubst i%,I%,$(patsubst j%,J%,$(patsubst k%,K%,$(patsubst l%,L%,$(patsubst m%,M%,$(patsubst n%,N%,$(patsubst o%,O%,$(patsubst p%,P%,$(patsubst q%,Q%,$(patsubst r%,R%,$(patsubst s%,S%,$(patsubst t%,T%,$(patsubst u%,U%,$(patsubst v%,V%,$(patsubst w%,W%,$(patsubst x%,X%,$(patsubst y%,Y%,$(patsubst z%,Z%,$(1)))))))))))))))))))))))))))
|
||||||
|
|
||||||
# The ports, and which of them can build here: a port is available when its
|
# The ports, and which of them can build here: a port is available when its
|
||||||
# real compiler binary is installed (DVX also needs the DVX SDK). The top
|
# real compiler binary is installed (DVX also needs the DVX SDK). The top
|
||||||
# Makefile's `make` and a project's `make` build exactly these.
|
# Makefile's `make` and a project's `make` build exactly these.
|
||||||
JOEY_PORTS := iigs amiga atarist ste dos x68000 dvx sdl3 web win32 win16 psp haiku haiku32 beos
|
JOEY_PORTS := iigs amiga atarist ste dos x68000 dvx sdl3 web macos win32 win16 psp haiku haiku32 beos
|
||||||
HAVE_IIGS := $(if $(wildcard $(LLVM816_ROOT)/tools/llvm-mos-build/bin/clang),1)
|
HAVE_IIGS := $(if $(wildcard $(LLVM816_ROOT)/tools/llvm-mos-build/bin/clang),1)
|
||||||
HAVE_AMIGA := $(if $(wildcard $(AMIGA_CC)),1)
|
HAVE_AMIGA := $(if $(wildcard $(AMIGA_CC)),1)
|
||||||
HAVE_ATARIST := $(if $(wildcard $(ST_CC)),1)
|
HAVE_ATARIST := $(if $(wildcard $(ST_CC)),1)
|
||||||
|
|
@ -44,6 +52,7 @@ HAVE_X68000 := $(if $(wildcard $(X68K_ROOT)/bin/m68k-xelf-gcc),1)
|
||||||
HAVE_DVX := $(if $(and $(wildcard $(DOS_CC)),$(wildcard $(DVX_SDK)/sdk.mk)),1)
|
HAVE_DVX := $(if $(and $(wildcard $(DOS_CC)),$(wildcard $(DVX_SDK)/sdk.mk)),1)
|
||||||
HAVE_SDL3 := $(if $(wildcard $(SDL3_DIR)/lib/libSDL3.a),1)
|
HAVE_SDL3 := $(if $(wildcard $(SDL3_DIR)/lib/libSDL3.a),1)
|
||||||
HAVE_WEB := $(if $(and $(wildcard $(EMCC)),$(wildcard $(SDL3_WEB_DIR)/lib/libSDL3.a)),1)
|
HAVE_WEB := $(if $(and $(wildcard $(EMCC)),$(wildcard $(SDL3_WEB_DIR)/lib/libSDL3.a)),1)
|
||||||
|
HAVE_MACOS := $(if $(and $(wildcard $(ZIG)),$(wildcard $(SDL3_MACOS_DIR)/lib/libSDL3.a)),1)
|
||||||
HAVE_WIN32 := $(if $(wildcard $(WIN32_CC)),1)
|
HAVE_WIN32 := $(if $(wildcard $(WIN32_CC)),1)
|
||||||
HAVE_WIN16 := $(if $(wildcard $(WIN16_WATCOM)/binl64/owcc),1)
|
HAVE_WIN16 := $(if $(wildcard $(WIN16_WATCOM)/binl64/owcc),1)
|
||||||
HAVE_PSP := $(if $(and $(wildcard $(PSP_CC)),$(wildcard $(SDL3_PSP_DIR)/lib/libSDL3.a)),1)
|
HAVE_PSP := $(if $(and $(wildcard $(PSP_CC)),$(wildcard $(SDL3_PSP_DIR)/lib/libSDL3.a)),1)
|
||||||
|
|
@ -68,8 +77,8 @@ INCLUDE_DIR := $(REPO_DIR)/include
|
||||||
# Machine-specific code lives in src/<machine>/ (iigs, amiga, atarist, dos,
|
# Machine-specific code lives in src/<machine>/ (iigs, amiga, atarist, dos,
|
||||||
# x68000, dvx); code shared across machines of one CPU lives in a processor
|
# 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
|
# folder (src/m68k, src/x86); the hosted core and its backends in src/hosted
|
||||||
# (sdl3, web and psp build the same SDL3 backend, natively, for a browser
|
# (sdl3, web, macos and psp build the same SDL3 backend, natively, for a
|
||||||
# and for the PSP; win32 and win16 their own).
|
# browser, for the Mac and for the PSP; win32 and win16 their own).
|
||||||
SRC_DIR := $(REPO_DIR)/src
|
SRC_DIR := $(REPO_DIR)/src
|
||||||
SRC_CORE := $(REPO_DIR)/src/core
|
SRC_CORE := $(REPO_DIR)/src/core
|
||||||
SRC_CG := $(REPO_DIR)/src/codegen
|
SRC_CG := $(REPO_DIR)/src/codegen
|
||||||
|
|
@ -93,7 +102,7 @@ CORE_C_SRCS_NO_MIX := $(filter-out $(CORE_MIX_C_SRCS), $(CORE_C_SRCS))
|
||||||
|
|
||||||
# Generic portable-C op defaults (the src/generic backend) -- present on every
|
# Generic portable-C op defaults (the src/generic backend) -- present on every
|
||||||
# target. spriteEmitStub.c is excluded: it is the no-codegen template and
|
# target. spriteEmitStub.c is excluded: it is the no-codegen template and
|
||||||
# redefines jlSpriteCompile / spriteCompiledDraw, which the real
|
# redefines jlSpriteCompile / jlSpriteCompiledDraw, which the real
|
||||||
# src/codegen/spriteCompile.c already provides.
|
# src/codegen/spriteCompile.c already provides.
|
||||||
GENERIC_C_SRCS := $(filter-out %/spriteEmitStub.c, $(wildcard $(SRC_DIR)/generic/*.c))
|
GENERIC_C_SRCS := $(filter-out %/spriteEmitStub.c, $(wildcard $(SRC_DIR)/generic/*.c))
|
||||||
|
|
||||||
|
|
@ -113,13 +122,12 @@ BINDIR := $(PROGRAM_BUILD)/bin
|
||||||
PORT_TAG := $(call uc,$(PLATFORM))
|
PORT_TAG := $(call uc,$(PLATFORM))
|
||||||
# Baked assets (sprite and tile banks) carry the target they were baked for.
|
# Baked assets (sprite and tile banks) carry the target they were baked for.
|
||||||
# DVX shows the DOS port's stage and runs its x86 code, so it takes the DOS
|
# DVX shows the DOS port's stage and runs its x86 code, so it takes the DOS
|
||||||
# bakes; the hosted ports' stage (SDL3, the web, Win32, Win16, the PSP,
|
# bakes; the hosted ports' stage (SDL3, the web, macOS, Win32, Win16, the
|
||||||
# Haiku, BeOS) is chunky like the DOS one, so they take them too (Win16 runs a
|
# PSP, Haiku, BeOS) is chunky like the DOS one, so they take them too (Win16
|
||||||
# pre-compiled bank's x86 code, as DVX does; the rest take its cels without
|
# runs a pre-compiled bank's x86 code, as DVX does; the rest take its cels
|
||||||
# their code); the STe build is the ST port, so it
|
# without their code); the STe build is the ST port, so it takes the ST's
|
||||||
# takes the ST's (same planes, same compiled sprites); every other port its
|
# (same planes, same compiled sprites); every other port its own.
|
||||||
# own.
|
ASSET_PLATFORM := $(if $(filter dvx sdl3 web macos win32 win16 psp haiku haiku32 beos,$(PLATFORM)),dos,$(if $(filter ste,$(PLATFORM)),atarist,$(PLATFORM)))
|
||||||
ASSET_PLATFORM := $(if $(filter dvx sdl3 web win32 win16 psp haiku haiku32 beos,$(PLATFORM)),dos,$(if $(filter ste,$(PLATFORM)),atarist,$(PLATFORM)))
|
|
||||||
# The host sprite pre-compiler for this port's bakes (.spr -> .spc); its rule
|
# The host sprite pre-compiler for this port's bakes (.spr -> .spc); its rule
|
||||||
# is make/programs.mk's.
|
# is make/programs.mk's.
|
||||||
SPRITEBAKE := $(JOEY_BUILD)/tools/spritebake-$(ASSET_PLATFORM)
|
SPRITEBAKE := $(JOEY_BUILD)/tools/spritebake-$(ASSET_PLATFORM)
|
||||||
|
|
|
||||||
14
make/dvx.mk
14
make/dvx.mk
|
|
@ -14,7 +14,7 @@
|
||||||
# port's entry (src/dvx/dvxApp.c), a generated one-topic help file and
|
# port's entry (src/dvx/dvxApp.c), a generated one-topic help file and
|
||||||
# resource list -- built and packaged by the SDK's own sdk.mk (KIND = app,
|
# resource list -- built and packaged by the SDK's own sdk.mk (KIND = app,
|
||||||
# LIBS = libjoey.a, DATA = its data files) and copied into build/dvx/bin/
|
# LIBS = libjoey.a, DATA = its data files) and copied into build/dvx/bin/
|
||||||
# laid out as it goes into DVX: apps/joeylib/<name>/<name>.app, <name>.hlp
|
# laid out as it goes into DVX: apps/<vendor>/<name>/<name>.app, <name>.hlp
|
||||||
# and DATA/. The gates and joey-run copy that tree into a DVX installation.
|
# and DATA/. The gates and joey-run copy that tree into a DVX installation.
|
||||||
#
|
#
|
||||||
# This file holds the library and programRules, the DVX rules for one
|
# This file holds the library and programRules, the DVX rules for one
|
||||||
|
|
@ -43,7 +43,7 @@ DVX_SDK_INCS := $(addprefix -I,$(filter-out %/basic %/net,$(patsubst %/,%,$(wild
|
||||||
# A program's exit() and atexit() are the port's (src/dvx/program.c): the
|
# A program's exit() and atexit() are the port's (src/dvx/program.c): the
|
||||||
# handlers run, and JoeyLib shuts down, when the program ends rather than
|
# handlers run, and JoeyLib shuts down, when the program ends rather than
|
||||||
# when DVX does. Applied to the library and the programs, never to the port.
|
# when DVX does. Applied to the library and the programs, never to the port.
|
||||||
PROGRAM_RENAMES := -Dexit=dvxPortExit -Datexit=dvxPortAtExit
|
PROGRAM_RENAMES := -Dexit=jlDvxPortExit -Datexit=jlDvxPortAtExit
|
||||||
|
|
||||||
CFLAGS := $(COMMON_CFLAGS) $(DVX_ARCH) -DJOEYLIB_PLATFORM_DVX -I$(SRC_X86) -I$(SRC_CG) -MMD -MP
|
CFLAGS := $(COMMON_CFLAGS) $(DVX_ARCH) -DJOEYLIB_PLATFORM_DVX -I$(SRC_X86) -I$(SRC_CG) -MMD -MP
|
||||||
LIB_CFLAGS := $(CFLAGS) $(PROGRAM_RENAMES)
|
LIB_CFLAGS := $(CFLAGS) $(PROGRAM_RENAMES)
|
||||||
|
|
@ -108,13 +108,13 @@ $(LIB): $(LIB_OBJS)
|
||||||
# under bin/: <name>.app, <name>.hlp and its DATA/. The name is the id in lower
|
# under bin/: <name>.app, <name>.hlp and its DATA/. The name is the id in lower
|
||||||
# case (DVX runs on DOS: an 8.3 name). DATA is staged apart, in
|
# case (DVX runs on DOS: an 8.3 name). DATA is staged apart, in
|
||||||
# build/dvx/data/<name>/DATA, for the SDK to package.
|
# build/dvx/data/<name>/DATA, for the SDK to package.
|
||||||
# DVX_VENDOR the vendor folder (default joeylib)
|
# DVX_VENDOR the vendor folder: whoever makes the program, 1 to 8
|
||||||
|
# letters, digits or _ (DVX names its package vendor.name)
|
||||||
|
# DVX_AUTHOR the resource list's author
|
||||||
# DVX_KIND what the help and resource texts call a program: "<title> is a
|
# DVX_KIND what the help and resource texts call a program: "<title> is a
|
||||||
# <kind> program" (default JoeyLib)
|
# <kind> program" (default JoeyLib)
|
||||||
# DVX_AUTHOR the resource list's author (default JoeyLib)
|
# The vendor and the author are the project's own: it must set them.
|
||||||
DVX_VENDOR := joeylib
|
|
||||||
DVX_KIND := JoeyLib
|
DVX_KIND := JoeyLib
|
||||||
DVX_AUTHOR := JoeyLib
|
|
||||||
PROGRAM_EXT := .app
|
PROGRAM_EXT := .app
|
||||||
PROGRAM_DATA_APART := yes
|
PROGRAM_DATA_APART := yes
|
||||||
|
|
||||||
|
|
@ -141,6 +141,8 @@ programRules = $(call programDvxRules,$(1),$(call programName,$(1)))
|
||||||
# the help line) is looked up here rather than passed in, and every line
|
# 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.
|
# written out is shellQuote'd, so a title or help line may hold quotes.
|
||||||
define programDvxRules
|
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)
|
||||||
.PHONY: app-$(2)
|
.PHONY: app-$(2)
|
||||||
app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(1),DATA) $(APPSDIR)/$(2)/icon32.bmp
|
app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(1),DATA) $(APPSDIR)/$(2)/icon32.bmp
|
||||||
@mkdir -p $(APPSDIR)/$(2)
|
@mkdir -p $(APPSDIR)/$(2)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@
|
||||||
# of bands to check that they reach it right.
|
# of bands to check that they reach it right.
|
||||||
# SERSLOT ST: SERTEST opening JL_SERIAL_SLOT unit 2 -- a Mega STe's SCC
|
# SERSLOT ST: SERTEST opening JL_SERIAL_SLOT unit 2 -- a Mega STe's SCC
|
||||||
# channel B through Bconmap -- for verify-atarist-serial-slot.sh.
|
# channel B through Bconmap -- for verify-atarist-serial-slot.sh.
|
||||||
|
# SERFAST Win16: SERTEST at 115200 for the throughput and loss gate,
|
||||||
|
# verify-win16-serial-fast.sh.
|
||||||
# FASTRAM Amiga: the Fast RAM gate's program: a surface made once chip
|
# FASTRAM Amiga: the Fast RAM gate's program: a surface made once chip
|
||||||
# RAM has run out draws and copies as a chip one does.
|
# RAM has run out draws and copies as a chip one does.
|
||||||
# WBLAUNCH Amiga: the icon gate's program: a program's icon read and
|
# WBLAUNCH Amiga: the icon gate's program: a program's icon read and
|
||||||
|
|
@ -43,11 +45,17 @@
|
||||||
# tones, voice and noise steps, sound effects, a module's end
|
# tones, voice and noise steps, sound effects, a module's end
|
||||||
# and tick rates, which every port must play alike.
|
# and tick rates, which every port must play alike.
|
||||||
|
|
||||||
PROGRAMS := HELLO PATTERN DRAW KEYS SERIAL SERTEST SERMOUSE SAVE TILEBANK SCB JOY SPRITE AUDIO UBER ADV ADV2
|
PROGRAMS := HELLO PATTERN DRAW KEYS SERIAL SERTEST SERMOUSE SAVE TILEBANK SCB JOY SPRITE SCROLL AUDIO UBER ADV ADV2
|
||||||
TEST_PROGRAMS := SCCPROBE SERLOSS SERLOSSS SERSLOT DIRTYMRK UBERPROB AUDIOPRB AUDPAR PALPROBE FASTRAM WBLAUNCH
|
TEST_PROGRAMS := SCCPROBE SERLOSS SERLOSSS SERSLOT SERFAST DIRTYMRK UBERPROB AUDIOPRB AUDPAR PALPROBE FASTRAM WBLAUNCH
|
||||||
|
|
||||||
# DVX's help and resource texts call these JoeyLib examples.
|
# 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.
|
||||||
|
DVX_VENDOR := kpunch
|
||||||
|
DVX_AUTHOR := Scott Duensing
|
||||||
DVX_KIND := JoeyLib example
|
DVX_KIND := JoeyLib example
|
||||||
|
MACOS_ID_PREFIX := com.kangaroopunch.joeylib
|
||||||
|
|
||||||
# The IIgs build has never included HELLO.
|
# The IIgs build has never included HELLO.
|
||||||
HELLO_SRCS := $(EXAMPLES)/hello/hello.c
|
HELLO_SRCS := $(EXAMPLES)/hello/hello.c
|
||||||
|
|
@ -87,6 +95,9 @@ JOY_HELP := Joysticks: shows the position and buttons of each joystick.
|
||||||
SPRITE_SRCS := $(EXAMPLES)/sprite/sprite.c
|
SPRITE_SRCS := $(EXAMPLES)/sprite/sprite.c
|
||||||
SPRITE_TITLE := Sprite
|
SPRITE_TITLE := Sprite
|
||||||
SPRITE_HELP := Compiled sprites moving over a background.
|
SPRITE_HELP := Compiled sprites moving over a background.
|
||||||
|
SCROLL_SRCS := $(EXAMPLES)/scroll/scroll.c
|
||||||
|
SCROLL_TITLE := Scroll
|
||||||
|
SCROLL_HELP := A tile layer scrolling a landscape under a sprite, with a bar showing the frames drawn a second.
|
||||||
AUDIO_SRCS := $(EXAMPLES)/audio/audio.c
|
AUDIO_SRCS := $(EXAMPLES)/audio/audio.c
|
||||||
AUDIO_TITLE := Audio
|
AUDIO_TITLE := Audio
|
||||||
AUDIO_HELP := Sound: tracker music, a MOD, sound effects, a streamed tone and a noise sweep.
|
AUDIO_HELP := Sound: tracker music, a MOD, sound effects, a streamed tone and a noise sweep.
|
||||||
|
|
@ -102,7 +113,7 @@ ADV2_HELP := A second small adventure game.
|
||||||
|
|
||||||
# Every example that ships as its own bootable IIgs volume (make
|
# Every example that ships as its own bootable IIgs volume (make
|
||||||
# iigs-game-disks).
|
# iigs-game-disks).
|
||||||
$(foreach p,DRAW PATTERN KEYS JOY SPRITE SAVE AUDIO ADV ADV2 UBER SERIAL SERTEST,$(eval $(p)_IIGS_GAME_DISK := yes))
|
$(foreach p,DRAW PATTERN KEYS JOY SPRITE SCROLL SAVE AUDIO ADV ADV2 UBER SERIAL SERTEST,$(eval $(p)_IIGS_GAME_DISK := yes))
|
||||||
|
|
||||||
# IIgs serial probes for the gates: SCCPROBE (verify-iigs-serial.sh: does
|
# IIgs serial probes for the gates: SCCPROBE (verify-iigs-serial.sh: does
|
||||||
# opening the modem port leave the printer port, AppleTalk's channel, alive?)
|
# opening the modem port leave the printer port, AppleTalk's channel, alive?)
|
||||||
|
|
@ -137,6 +148,10 @@ SERSLOT_SRCS := $(EXAMPLES)/sertest/sertest.c
|
||||||
SERSLOT_PORTS := atarist
|
SERSLOT_PORTS := atarist
|
||||||
SERSLOT_BINDIR := $(PROGRAM_BUILD)/test
|
SERSLOT_BINDIR := $(PROGRAM_BUILD)/test
|
||||||
SERSLOT_CFLAGS := -DSERTEST_DEVICE=JL_SERIAL_SLOT -DSERTEST_UNIT=2u
|
SERSLOT_CFLAGS := -DSERTEST_DEVICE=JL_SERIAL_SLOT -DSERTEST_UNIT=2u
|
||||||
|
SERFAST_SRCS := $(EXAMPLES)/sertest/sertest.c
|
||||||
|
SERFAST_PORTS := win16
|
||||||
|
SERFAST_BINDIR := $(PROGRAM_BUILD)/test
|
||||||
|
SERFAST_CFLAGS := -DSERTEST_BAUD=115200u
|
||||||
FASTRAM_SRCS := $(REPO_DIR)/tests/amiga/fastRam.c
|
FASTRAM_SRCS := $(REPO_DIR)/tests/amiga/fastRam.c
|
||||||
FASTRAM_PORTS := amiga
|
FASTRAM_PORTS := amiga
|
||||||
FASTRAM_BINDIR := $(PROGRAM_BUILD)/test
|
FASTRAM_BINDIR := $(PROGRAM_BUILD)/test
|
||||||
|
|
@ -153,10 +168,10 @@ DIRTYMRK_CFLAGS := -DDIRTY_MARK_SCENARIOS=$(DIRTYMARK_SCENARIOS) -DDIRTY_MAR
|
||||||
# and bench-iigs.sh look for (iigs-verify-shrtail). DVX and the windowed
|
# and bench-iigs.sh look for (iigs-verify-shrtail). DVX and the windowed
|
||||||
# hosted ports build it as part of `all`, into build/<port>/probe, for
|
# hosted ports build it as part of `all`, into build/<port>/probe, for
|
||||||
# their display gates (scripts/verify-dvx-display.sh,
|
# their display gates (scripts/verify-dvx-display.sh,
|
||||||
# verify-win16-display.sh, verify-win32-display.sh, and
|
# verify-win16-display.sh, verify-win32-display.sh, verify-be-display.sh,
|
||||||
# verify-be-display.sh).
|
# and verify-atarist-display.sh).
|
||||||
UBERPROB_SRCS := $(UBER_SRCS)
|
UBERPROB_SRCS := $(UBER_SRCS)
|
||||||
UBERPROB_PORTS := iigs dvx win16 win32 haiku haiku32 beos
|
UBERPROB_PORTS := iigs dvx win16 win32 haiku haiku32 beos atarist ste amiga
|
||||||
UBERPROB_CFLAGS := -DUBER_PROBE -DUBER_FRAMES=1
|
UBERPROB_CFLAGS := -DUBER_PROBE -DUBER_FRAMES=1
|
||||||
UBERPROB_TITLE := Uber Probe
|
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. Its results go to joeylog.txt in its settings folder.
|
||||||
|
|
@ -168,6 +183,9 @@ UBERPROB_WIN32_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
UBERPROB_HAIKU_BINDIR := $(PROGRAM_BUILD)/probe
|
UBERPROB_HAIKU_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
UBERPROB_HAIKU32_BINDIR := $(PROGRAM_BUILD)/probe
|
UBERPROB_HAIKU32_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
UBERPROB_BEOS_BINDIR := $(PROGRAM_BUILD)/probe
|
UBERPROB_BEOS_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
|
UBERPROB_ATARIST_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
|
UBERPROB_STE_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
|
UBERPROB_AMIGA_BINDIR := $(PROGRAM_BUILD)/probe
|
||||||
|
|
||||||
# AUDIO with its keys pressed on a timetable (-DAUDIO_PROBE): N once, S every
|
# AUDIO with its keys pressed on a timetable (-DAUDIO_PROBE): N once, S every
|
||||||
# 1.5 seconds, under the tracker song. The IIgs and X68000 audio gates read
|
# 1.5 seconds, under the tracker song. The IIgs and X68000 audio gates read
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
|
||||||
|
|
||||||
$(call requireTool,$(HAIKU32_ROOT)/cross/bin/i586-pc-haiku-g++,toolchain:haiku32)
|
$(call requireTool,$(HAIKU32_ROOT)/cross/bin/i586-pc-haiku-g++,toolchain:haiku32)
|
||||||
|
|
||||||
HOSTED_CC := $(HAIKU32_GCC2) $(HAIKU32_ROOT) gcc
|
# -fno-common: gcc 2.95 merges a program's uninitialised 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++
|
HOSTED_CXX := $(HAIKU32_GCC2) $(HAIKU32_ROOT) g++
|
||||||
HOSTED_AR := $(HAIKU32_GCC2) $(HAIKU32_ROOT) ar
|
HOSTED_AR := $(HAIKU32_GCC2) $(HAIKU32_ROOT) ar
|
||||||
BE_CXX_RUNTIME := -lstdc++.r4
|
BE_CXX_RUNTIME := -lstdc++.r4
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
# program, which make/programs.mk instantiates for every program.
|
# program, which make/programs.mk instantiates for every program.
|
||||||
# Included by make/sdl3.mk (a native program), make/web.mk (the same
|
# Included by make/sdl3.mk (a native program), make/web.mk (the same
|
||||||
# SDL3 backend compiled to WebAssembly), make/psp.mk (the same again, for
|
# SDL3 backend compiled to WebAssembly), make/psp.mk (the same again, for
|
||||||
# the PSP), make/win32.mk, make/win16.mk and make/be.mk (Haiku), after each
|
# the PSP), make/macos.mk (and again, for macOS), make/win32.mk,
|
||||||
# has included common.mk and set:
|
# make/win16.mk and make/be.mk (Haiku), after each has included common.mk
|
||||||
|
# and set:
|
||||||
# HOSTED_CC the compiler
|
# HOSTED_CC the compiler
|
||||||
# HOSTED_AR the archiver
|
# HOSTED_AR the archiver
|
||||||
# HOSTED_BACKEND the backend folder under src/hosted
|
# HOSTED_BACKEND the backend folder under src/hosted
|
||||||
|
|
@ -18,13 +19,16 @@
|
||||||
# PROGRAM_EXT the program file's extension
|
# PROGRAM_EXT the program file's extension
|
||||||
# and, when the backend is C++ (src/hosted/<backend>/*.cpp, the Be API's),
|
# and, when the backend is C++ (src/hosted/<backend>/*.cpp, the Be API's),
|
||||||
# HOSTED_CXX its C++ compiler
|
# HOSTED_CXX its C++ compiler
|
||||||
# and may set HOSTED_PROGRAM_DEPS, files every program is rebuilt after (the
|
# and may set HOSTED_ARFLAGS, what the archiver is given before the
|
||||||
# web page's shell), and, when a program needs its DATA at link time (the web
|
# library's name (rcs; LLVM's libtool, which archives macOS's universal
|
||||||
# port packs it into the program), HOSTED_LINK_DATA: the link flags for one
|
# objects, takes -static -o), and HOSTED_PROGRAM_DEPS, files every program
|
||||||
# program's DATA folder, $(1) the program's id. When the program file is
|
# is rebuilt after (the web page's shell), and, when a program needs its
|
||||||
# packed from what is linked (the PSP's EBOOT.PBP, from an ELF), the port
|
# DATA at link time (the web port packs it into the program),
|
||||||
# sets HOSTED_LINK_OUT, the file one program is linked to, and HOSTED_PACK,
|
# HOSTED_LINK_DATA: the link flags for one program's DATA folder, $(1) the
|
||||||
# the command that packs it into the program file ($(1) the id in both).
|
# program's id. When the program file is packed from what is linked (the
|
||||||
|
# PSP's EBOOT.PBP, from an ELF; a Mac app, from a program), the port sets
|
||||||
|
# HOSTED_LINK_OUT, the file one program is linked to, and HOSTED_PACK, the
|
||||||
|
# command that packs it into the program file ($(1) the id in both).
|
||||||
# A port whose programs show an icon (make/common.mk's programIcon, baked by
|
# A port whose programs show an icon (make/common.mk's programIcon, baked by
|
||||||
# tools/iconBake.py) sets HOSTED_ICON, the file one program's icon is baked
|
# tools/iconBake.py) sets HOSTED_ICON, the file one program's icon is baked
|
||||||
# to, and HOSTED_ICON_BAKE, the command that bakes it there from the
|
# to, and HOSTED_ICON_BAKE, the command that bakes it there from the
|
||||||
|
|
@ -42,6 +46,7 @@ HOSTED_DIR := $(SRC_DIR)/hosted
|
||||||
BACKEND_DIR := $(HOSTED_DIR)/$(HOSTED_BACKEND)
|
BACKEND_DIR := $(HOSTED_DIR)/$(HOSTED_BACKEND)
|
||||||
|
|
||||||
CFLAGS := $(COMMON_CFLAGS) $(HOSTED_DEFINES) -I$(HOSTED_DIR) -MMD -MP
|
CFLAGS := $(COMMON_CFLAGS) $(HOSTED_DEFINES) -I$(HOSTED_DIR) -MMD -MP
|
||||||
|
HOSTED_ARFLAGS ?= rcs
|
||||||
|
|
||||||
# HOSTED_X86 (make/win16.mk): sprites are compiled to x86 code as on DOS --
|
# HOSTED_X86 (make/win16.mk): sprites are compiled to x86 code as on DOS --
|
||||||
# src/x86's emitter and src/codegen's dispatch in place of the stub.
|
# src/x86's emitter and src/codegen's dispatch in place of the stub.
|
||||||
|
|
@ -56,11 +61,14 @@ endif
|
||||||
# system it runs on, built with the backend's flags; the backend includes
|
# system it runs on, built with the backend's flags; the backend includes
|
||||||
# its headers.
|
# its headers.
|
||||||
HOSTED_C_SRCS := $(wildcard $(HOSTED_DIR)/*.c)
|
HOSTED_C_SRCS := $(wildcard $(HOSTED_DIR)/*.c)
|
||||||
# -DJOEYLIB_HOSTED_POSIX_SERIAL in HOSTED_DEFINES (make/sdl3.mk, make/be.mk):
|
# -DJOEYLIB_HOSTED_POSIX_SERIAL in HOSTED_DEFINES (make/sdl3.mk, make/macos.mk,
|
||||||
|
# make/be.mk):
|
||||||
# the serial add-on through termios, src/hosted/posix.
|
# the serial add-on through termios, src/hosted/posix.
|
||||||
POSIX_C_SRCS := $(if $(filter -DJOEYLIB_HOSTED_POSIX_SERIAL,$(HOSTED_DEFINES)),$(wildcard $(HOSTED_DIR)/posix/*.c))
|
POSIX_C_SRCS := $(if $(filter -DJOEYLIB_HOSTED_POSIX_SERIAL,$(HOSTED_DEFINES)),$(wildcard $(HOSTED_DIR)/posix/*.c))
|
||||||
BACKEND_C_SRCS := $(wildcard $(BACKEND_DIR)/*.c)
|
BACKEND_C_SRCS := $(wildcard $(BACKEND_DIR)/*.c)
|
||||||
BACKEND_CXX_SRCS := $(wildcard $(BACKEND_DIR)/*.cpp)
|
BACKEND_CXX_SRCS := $(wildcard $(BACKEND_DIR)/*.cpp)
|
||||||
|
# Assembly of the backend's own, assembled by HOSTED_AS (make/win16.mk).
|
||||||
|
BACKEND_ASM_SRCS := $(wildcard $(BACKEND_DIR)/*.asm)
|
||||||
EXTRA_C_SRCS := $(if $(HOSTED_BACKEND_EXTRA),$(wildcard $(HOSTED_BACKEND_EXTRA)/*.c))
|
EXTRA_C_SRCS := $(if $(HOSTED_BACKEND_EXTRA),$(wildcard $(HOSTED_BACKEND_EXTRA)/*.c))
|
||||||
HOSTED_ICON_ID := 1
|
HOSTED_ICON_ID := 1
|
||||||
BACKEND_CFLAGS := $(CFLAGS) $(HOSTED_SDK_CFLAGS) $(if $(HOSTED_BACKEND_EXTRA),-I$(HOSTED_BACKEND_EXTRA)) -DHOSTED_ICON_ID=$(HOSTED_ICON_ID)
|
BACKEND_CFLAGS := $(CFLAGS) $(HOSTED_SDK_CFLAGS) $(if $(HOSTED_BACKEND_EXTRA),-I$(HOSTED_BACKEND_EXTRA)) -DHOSTED_ICON_ID=$(HOSTED_ICON_ID)
|
||||||
|
|
@ -73,6 +81,7 @@ LIB_OBJS := \
|
||||||
$(patsubst $(HOSTED_DIR)/posix/%.c,$(BUILD)/obj/posix/%.o,$(POSIX_C_SRCS)) \
|
$(patsubst $(HOSTED_DIR)/posix/%.c,$(BUILD)/obj/posix/%.o,$(POSIX_C_SRCS)) \
|
||||||
$(patsubst $(BACKEND_DIR)/%.c,$(BUILD)/obj/backend/%.o,$(BACKEND_C_SRCS)) \
|
$(patsubst $(BACKEND_DIR)/%.c,$(BUILD)/obj/backend/%.o,$(BACKEND_C_SRCS)) \
|
||||||
$(patsubst $(BACKEND_DIR)/%.cpp,$(BUILD)/obj/backend/%.o,$(BACKEND_CXX_SRCS)) \
|
$(patsubst $(BACKEND_DIR)/%.cpp,$(BUILD)/obj/backend/%.o,$(BACKEND_CXX_SRCS)) \
|
||||||
|
$(patsubst $(BACKEND_DIR)/%.asm,$(BUILD)/obj/backend/%.o,$(BACKEND_ASM_SRCS)) \
|
||||||
$(patsubst $(HOSTED_BACKEND_EXTRA)/%.c,$(BUILD)/obj/backendExtra/%.o,$(EXTRA_C_SRCS))
|
$(patsubst $(HOSTED_BACKEND_EXTRA)/%.c,$(BUILD)/obj/backendExtra/%.o,$(EXTRA_C_SRCS))
|
||||||
|
|
||||||
LIB := $(LIBDIR)/libjoey.a
|
LIB := $(LIBDIR)/libjoey.a
|
||||||
|
|
@ -110,6 +119,10 @@ $(BUILD)/obj/backend/%.o: $(BACKEND_DIR)/%.cpp
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(HOSTED_CXX) $(BACKEND_CFLAGS) -c $< -o $@
|
$(HOSTED_CXX) $(BACKEND_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD)/obj/backend/%.o: $(BACKEND_DIR)/%.asm
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(HOSTED_AS) $< $@
|
||||||
|
|
||||||
$(BUILD)/obj/backendExtra/%.o: $(HOSTED_BACKEND_EXTRA)/%.c
|
$(BUILD)/obj/backendExtra/%.o: $(HOSTED_BACKEND_EXTRA)/%.c
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(HOSTED_CC) $(BACKEND_CFLAGS) -c $< -o $@
|
$(HOSTED_CC) $(BACKEND_CFLAGS) -c $< -o $@
|
||||||
|
|
@ -119,7 +132,7 @@ $(BUILD)/obj/backendExtra/%.o: $(HOSTED_BACKEND_EXTRA)/%.c
|
||||||
$(LIB): $(LIB_OBJS)
|
$(LIB): $(LIB_OBJS)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
rm -f $@
|
rm -f $@
|
||||||
$(HOSTED_AR) rcs $@ $^
|
$(HOSTED_AR) $(HOSTED_ARFLAGS) $@ $^
|
||||||
|
|
||||||
# Pull in per-object header-dependency files generated by -MMD/-MP.
|
# Pull in per-object header-dependency files generated by -MMD/-MP.
|
||||||
-include $(LIB_OBJS:.o=.d)
|
-include $(LIB_OBJS:.o=.d)
|
||||||
|
|
|
||||||
18
make/iigs.mk
18
make/iigs.mk
|
|
@ -192,7 +192,7 @@ iigs-examples: programs
|
||||||
# examples as ProDOS S16 apps (an 800KB floppy can't hold every example; see
|
# examples as ProDOS S16 apps (an 800KB floppy can't hold every example; see
|
||||||
# make-iigs-disk.sh) with their DATA. NTP_BIN rides along so jlpAudioInit's
|
# make-iigs-disk.sh) with their DATA. NTP_BIN rides along so jlpAudioInit's
|
||||||
# runtime load of "ntpplayer" finds it on the volume -- without it every
|
# runtime load of "ntpplayer" finds it on the volume -- without it every
|
||||||
# example logs "audioInit failed".
|
# example logs "jlAudioOpen failed".
|
||||||
iigs-disk: programs $(NTP_BIN)
|
iigs-disk: programs $(NTP_BIN)
|
||||||
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" $(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey.2mg
|
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" $(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey.2mg
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ iigs-disk: programs $(NTP_BIN)
|
||||||
iigs-disk-all: programs $(NTP_BIN)
|
iigs-disk-all: programs $(NTP_BIN)
|
||||||
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" \
|
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" \
|
||||||
JOEY_DISK_SIZE="16MB" \
|
JOEY_DISK_SIZE="16MB" \
|
||||||
JOEY_DISK_EXAMPLES="DRAW PATTERN KEYS JOY SPRITE SERIAL SERTEST SAVE TILEBANK AUDIO UBER ADV2 ADV" \
|
JOEY_DISK_EXAMPLES="DRAW PATTERN KEYS JOY SPRITE SCROLL SERIAL SERTEST SAVE TILEBANK AUDIO UBER ADV2 ADV" \
|
||||||
$(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey-all.2mg
|
$(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey-all.2mg
|
||||||
|
|
||||||
# Headless visual-verification gate: boot GS/OS under MAME, launch the DRAW
|
# Headless visual-verification gate: boot GS/OS under MAME, launch the DRAW
|
||||||
|
|
@ -240,7 +240,11 @@ iigs-verify-serial-loss: $(call programBin,SERLOSS) $(call programBin,SERLOSSS)
|
||||||
iigs-verify-all: iigs-disk
|
iigs-verify-all: iigs-disk
|
||||||
$(REPO_DIR)/scripts/verify-iigs-all.sh
|
$(REPO_DIR)/scripts/verify-iigs-all.sh
|
||||||
|
|
||||||
# Gate the 712 display-state bytes ABOVE the pixel block: the SCB array
|
# Gate what the present leaves on the screen: every $E1 pixel byte equal to
|
||||||
|
# the stage after a run of draws, the marks' asm included (shrParity), every
|
||||||
|
# dirty band CLEAN again after its present (shrBands: a mark that skips its
|
||||||
|
# 8-row group flag is never cleared, and the present can skip its rows), and
|
||||||
|
# the 712 display-state bytes ABOVE the pixel block: the SCB array
|
||||||
# ($E1:9D00-$9DC7) and all 16 palettes ($E1:9E00-$9FFF). Nothing else in the
|
# ($E1:9D00-$9DC7) and all 16 palettes ($E1:9E00-$9FFF). Nothing else in the
|
||||||
# tree can see them -- the golden hash covers the STAGE, not $E1, and P9's
|
# tree can see them -- the golden hash covers the STAGE, not $E1, and P9's
|
||||||
# pixel compare stops at $9CFF -- so a wrong palette is invisible to every
|
# pixel compare stops at $9CFF -- so a wrong palette is invisible to every
|
||||||
|
|
@ -259,8 +263,14 @@ iigs-verify-shrtail: $(call programBin,UBERPROB) $(NTP_BIN)
|
||||||
$(REPO_DIR)/scripts/make-iigs-disk.sh $(IIGS_PROBE_DIR)/uberprobe.2mg
|
$(REPO_DIR)/scripts/make-iigs-disk.sh $(IIGS_PROBE_DIR)/uberprobe.2mg
|
||||||
BENCH_DATA_DISK="$(IIGS_PROBE_DIR)/uberprobe.2mg" BENCH_OUT_LOG="$(IIGS_PROBE_DIR)/probelog.txt" \
|
BENCH_DATA_DISK="$(IIGS_PROBE_DIR)/uberprobe.2mg" BENCH_OUT_LOG="$(IIGS_PROBE_DIR)/probelog.txt" \
|
||||||
BENCH_MAX_FRAMES="$(or $(PROBE_MAX_FRAMES),45000)" $(REPO_DIR)/scripts/bench-iigs.sh
|
BENCH_MAX_FRAMES="$(or $(PROBE_MAX_FRAMES),45000)" $(REPO_DIR)/scripts/bench-iigs.sh
|
||||||
@grep -E 'shrParity|shrTailUpload|shrTailSlam' $(IIGS_PROBE_DIR)/probelog.txt || \
|
@grep -E 'shrParity|shrBands|shrTailUpload|shrTailSlam' $(IIGS_PROBE_DIR)/probelog.txt || \
|
||||||
{ echo "iigs-verify-shrtail: FAIL - no probe rows captured" >&2; exit 1; }
|
{ echo "iigs-verify-shrtail: FAIL - no probe rows captured" >&2; exit 1; }
|
||||||
|
@test "$$(grep -cE 'shrParity|shrBands|shrTailUpload|shrTailSlam' $(IIGS_PROBE_DIR)/probelog.txt)" -eq 4 || \
|
||||||
|
{ echo "iigs-verify-shrtail: FAIL - not every probe row ran" >&2; exit 1; }
|
||||||
|
@grep -qE 'shrParity FAIL' $(IIGS_PROBE_DIR)/probelog.txt && \
|
||||||
|
{ echo "iigs-verify-shrtail: FAIL - \$$E1 pixels mismatched the stage" >&2; exit 1; } || true
|
||||||
|
@grep -qE 'shrBands FAIL' $(IIGS_PROBE_DIR)/probelog.txt && \
|
||||||
|
{ echo "iigs-verify-shrtail: FAIL - a band stayed dirty past its present" >&2; exit 1; } || true
|
||||||
@grep -qE 'shrTail(Upload|Slam) FAIL' $(IIGS_PROBE_DIR)/probelog.txt && \
|
@grep -qE 'shrTail(Upload|Slam) FAIL' $(IIGS_PROBE_DIR)/probelog.txt && \
|
||||||
{ echo "iigs-verify-shrtail: FAIL - \$$E1 SCB/palette block mismatched the stage" >&2; exit 1; } || true
|
{ echo "iigs-verify-shrtail: FAIL - \$$E1 SCB/palette block mismatched the stage" >&2; exit 1; } || true
|
||||||
@echo "iigs-verify-shrtail: PASS"
|
@echo "iigs-verify-shrtail: PASS"
|
||||||
|
|
|
||||||
65
make/macos.mk
Normal file
65
make/macos.mk
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
# See LICENSE.md for license information.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# macOS build rules (make macos): JoeyLib programs as Mac apps, through the
|
||||||
|
# hosted core and its SDL3 backend (src/hosted, src/hosted/sdl3), built on
|
||||||
|
# Linux. The rules are make/hosted.mk's; this file says what is macOS's. A
|
||||||
|
# project reaches it through make/project.mk.
|
||||||
|
#
|
||||||
|
# Built with make/support/macos/cc.sh: zig for Apple silicon and for Intel,
|
||||||
|
# against the Apple SDK, the two joined into universal files that run on
|
||||||
|
# either (macOS 13 or newer). Programs link the universal static SDL3 the
|
||||||
|
# installer builds (SDL3_MACOS_DIR), so they run without an SDL3 on the Mac.
|
||||||
|
# A program is an app, bin/NAME.app, with the program in Contents/MacOS and
|
||||||
|
# its DATA and icon in Contents/Resources, its folder, which the port makes
|
||||||
|
# the current directory when the window opens (SAVES/ and joeylog.txt go in
|
||||||
|
# the user's Application Support folder: src/hosted/sdl3/sdl3Backend.c). The
|
||||||
|
# app's identifier is MACOS_ID_PREFIX, a reverse-DNS name of the project's
|
||||||
|
# own, and the program's name; the project must set it. The Apple silicon
|
||||||
|
# half is signed ad hoc as zig links it, which macOS needs to run it at all.
|
||||||
|
|
||||||
|
PLATFORM := macos
|
||||||
|
include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
|
||||||
|
|
||||||
|
SDL3_MACOS_LIB := $(SDL3_MACOS_DIR)/lib/libSDL3.a
|
||||||
|
$(call requireTool,$(ZIG),toolchain:macos)
|
||||||
|
$(call requireTool,$(MACOS_LIBTOOL),toolchain:macos)
|
||||||
|
$(call requireTool,$(SDL3_MACOS_LIB),toolchain:macos)
|
||||||
|
|
||||||
|
SDL3_MACOS_PKG_CONFIG := PKG_CONFIG_LIBDIR=$(SDL3_MACOS_DIR)/lib/pkgconfig pkg-config
|
||||||
|
MACOS_SUPPORT := $(REPO_DIR)/make/support/macos
|
||||||
|
|
||||||
|
# LLVM's libtool archives the universal objects (it warns that audio.o and
|
||||||
|
# input.o are named twice, src/core's and src/hosted's, and keeps both).
|
||||||
|
HOSTED_CC := $(MACOS_SUPPORT)/cc.sh
|
||||||
|
HOSTED_AR := $(MACOS_LIBTOOL)
|
||||||
|
HOSTED_ARFLAGS := -static -o
|
||||||
|
HOSTED_BACKEND := sdl3
|
||||||
|
HOSTED_DEFINES := -DJOEYLIB_PLATFORM_HOSTED -DJOEYLIB_HOSTED_SDL3 -DJOEYLIB_HOSTED_POSIX_SERIAL
|
||||||
|
HOSTED_SDK_CFLAGS := $(shell $(SDL3_MACOS_PKG_CONFIG) --cflags sdl3)
|
||||||
|
HOSTED_LINK := $(shell $(SDL3_MACOS_PKG_CONFIG) --static --libs sdl3) -Wl,-dead_strip
|
||||||
|
PROGRAM_EXT :=
|
||||||
|
# A program is relinked when SDL changes, and with it repacked when the
|
||||||
|
# packing does.
|
||||||
|
HOSTED_PROGRAM_DEPS := $(SDL3_MACOS_LIB) $(MACOS_SUPPORT)/packApp.sh
|
||||||
|
|
||||||
|
# One program's app, $(1) its id: in bin/ (or ID_BINDIR), never in a folder
|
||||||
|
# of its own (an app is one). Its folder is the app's Contents/Resources,
|
||||||
|
# where its DATA goes, and SDL's base path for it.
|
||||||
|
macosApp = $(or $(call programVar,$(1),BINDIR),$(BINDIR))/$(call programName,$(1)).app
|
||||||
|
programDir = $(call macosApp,$(1))/Contents/Resources
|
||||||
|
programBin = $(call macosApp,$(1))/Contents/MacOS/$(call programName,$(1))
|
||||||
|
|
||||||
|
# Linked outside the app, which then gets a copy: the dependency file stays
|
||||||
|
# out of it. By the id, which a probe build (ID_BINDIR) does not share with
|
||||||
|
# the program of its name.
|
||||||
|
HOSTED_LINK_OUT = $(PROGRAM_BUILD)/link/$(1)
|
||||||
|
# Its identifier: the project's prefix and its name.
|
||||||
|
macosId = $(call requireSet,MACOS_ID_PREFIX,the start of its apps' identifiers$(comma) a reverse-DNS name of the project's own (com.example.games))$(MACOS_ID_PREFIX).$(call programName,$(1))
|
||||||
|
HOSTED_PACK = $(MACOS_SUPPORT)/packApp.sh $(call HOSTED_LINK_OUT,$(1)) $(call macosApp,$(1)) $(call macosId,$(1)) $(call shellQuote,$(or $(call programVar,$(1),TITLE),$(call programName,$(1))))
|
||||||
|
# Its icon is the app's (Finder, the Dock), not the window's.
|
||||||
|
HOSTED_ICON = $(call programDir,$(1))/icon.icns
|
||||||
|
HOSTED_ICON_BAKE = $(ICON_BAKE) icns $(call programIcon,$(1)) $(call HOSTED_ICON,$(1))
|
||||||
|
|
||||||
|
include $(REPO_DIR)/make/hosted.mk
|
||||||
|
|
@ -28,7 +28,8 @@
|
||||||
# on the Amiga Capitalised (Hello), on DVX lower case
|
# on the Amiga Capitalised (Hello), on DVX lower case
|
||||||
# ID_FOLDER non-empty: it ships in a folder of its own, bin/<name>/,
|
# 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;
|
# with its DATA beside it (the Amiga, ST, DOS and X68000;
|
||||||
# on the IIgs and DVX every program is packaged apart anyway)
|
# on the IIgs, DVX and macOS every program is packaged
|
||||||
|
# apart anyway)
|
||||||
# ID_BINDIR build it here instead of bin/ (probe builds)
|
# ID_BINDIR build it here instead of bin/ (probe builds)
|
||||||
# ID_DATA its DATA files, as the files the description's own rules
|
# ID_DATA its DATA files, as the files the description's own rules
|
||||||
# make under $(call programDataDir,ID) (or
|
# make under $(call programDataDir,ID) (or
|
||||||
|
|
@ -37,9 +38,12 @@
|
||||||
# where there is no icon), baked for each port that shows
|
# where there is no icon), baked for each port that shows
|
||||||
# programs by an icon -- the IIgs Finder, the Amiga's
|
# programs by an icon -- the IIgs Finder, the Amiga's
|
||||||
# Workbench, DVX, Win16, Win32, Haiku and BeOS, the PSP's
|
# Workbench, DVX, Win16, Win32, Haiku and BeOS, the PSP's
|
||||||
# menu, the SDL3 window, and the web page; by default
|
# menu, the SDL3 window, the Mac's Finder and Dock, and the
|
||||||
# JoeyLib's kangaroo (docs/BuildIntegration.adoc, Icons)
|
# web page; by default JoeyLib's kangaroo
|
||||||
# ID_TITLE DVX: its window title (default: its Amiga-style name)
|
# (docs/BuildIntegration.adoc, Icons)
|
||||||
|
# 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 (no apostrophes: the help is
|
||||||
# written through the shell in quotes)
|
# written through the shell in quotes)
|
||||||
# ID_IIGS_OVERFLOW_CAP IIgs: link large overflow segments (clang-build.sh's
|
# ID_IIGS_OVERFLOW_CAP IIgs: link large overflow segments (clang-build.sh's
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,13 @@ linkMap="$tmpdir/link.map"
|
||||||
linkReloc="$tmpdir/link.reloc"
|
linkReloc="$tmpdir/link.reloc"
|
||||||
linkManifest="$tmpdir/link.json"
|
linkManifest="$tmpdir/link.json"
|
||||||
linkErr="$tmpdir/link.err"
|
linkErr="$tmpdir/link.err"
|
||||||
|
# A kept map (JOEY_LINK_MAP, below) lists the local symbols too: static
|
||||||
|
# functions, which a profile otherwise folds into the global before them.
|
||||||
|
# link816 only: omfEmit rejects the flag.
|
||||||
|
mapLocals=()
|
||||||
|
if [ -n "${JOEY_LINK_MAP:-}" ]; then
|
||||||
|
mapLocals=(--map-locals)
|
||||||
|
fi
|
||||||
|
|
||||||
# The GS/OS load name is the output basename, uppercased to <=8 alnum chars.
|
# The GS/OS load name is the output basename, uppercased to <=8 alnum chars.
|
||||||
omfName="$(basename "$output" | tr '[:lower:]' '[:upper:]' | tr -cd 'A-Z0-9' | cut -c1-8)"
|
omfName="$(basename "$output" | tr '[:lower:]' '[:upper:]' | tr -cd 'A-Z0-9' | cut -c1-8)"
|
||||||
|
|
@ -171,7 +178,7 @@ emitMultiSeg() { # $1 = segment-cap, $2 = bss
|
||||||
"$LINK" -o "$linkBin" --text-base 0 --bss-base "$2" --segment-cap "$1" \
|
"$LINK" -o "$linkBin" --text-base 0 --bss-base "$2" --segment-cap "$1" \
|
||||||
--overflow-cap "$JOEY_OVERFLOW_CAP" --full-bank \
|
--overflow-cap "$JOEY_OVERFLOW_CAP" --full-bank \
|
||||||
--manifest "$linkManifest" --map "$linkMap" --reloc-out "$linkReloc" \
|
--manifest "$linkManifest" --map "$linkMap" --reloc-out "$linkReloc" \
|
||||||
--multi-seg-expressload "${runtimeObjs[@]}" "${objs[@]}" 2>"$linkErr" || return 1
|
--multi-seg-expressload "${mapLocals[@]}" "${runtimeObjs[@]}" "${objs[@]}" 2>"$linkErr" || return 1
|
||||||
"$OMFEMIT" --manifest "$linkManifest" --expressload --relocs "$linkReloc" \
|
"$OMFEMIT" --manifest "$linkManifest" --expressload --relocs "$linkReloc" \
|
||||||
--output "$output"
|
--output "$output"
|
||||||
}
|
}
|
||||||
|
|
@ -313,7 +320,7 @@ emitMultiSegFit() {
|
||||||
|
|
||||||
if "$LINK" -o "$linkBin" --text-base 0 --bss-base 0xA000 --full-bank \
|
if "$LINK" -o "$linkBin" --text-base 0 --bss-base 0xA000 --full-bank \
|
||||||
--map "$linkMap" --reloc-out "$linkReloc" \
|
--map "$linkMap" --reloc-out "$linkReloc" \
|
||||||
"${runtimeObjs[@]}" "${objs[@]}" 2>"$linkErr" && heapUsable; then
|
"${mapLocals[@]}" "${runtimeObjs[@]}" "${objs[@]}" 2>"$linkErr" && heapUsable; then
|
||||||
# Single-segment AND the heap is big enough: wrap as-is.
|
# Single-segment AND the heap is big enough: wrap as-is.
|
||||||
"$OMFEMIT" --input "$linkBin" --map "$linkMap" --base 0 --entry __start \
|
"$OMFEMIT" --input "$linkBin" --map "$linkMap" --base 0 --entry __start \
|
||||||
--output "$output" --name "$omfName" --expressload --relocs "$linkReloc"
|
--output "$output" --name "$omfName" --expressload --relocs "$linkReloc"
|
||||||
|
|
@ -325,10 +332,28 @@ fi
|
||||||
|
|
||||||
# The link map is the only record of where a symbol landed, and the OMF
|
# The link map is the only record of where a symbol landed, and the OMF
|
||||||
# carries no symbols at all, so anything that inspects a running program
|
# carries no symbols at all, so anything that inspects a running program
|
||||||
# (the PC profiler, the MAME state gates) needs it kept. Off by default.
|
# (the PC profiler, the MAME state gates) needs it kept. Off by default:
|
||||||
|
# JOEY_LINK_MAP names a directory, which gets <output name>.map and the
|
||||||
|
# segment images beside it, <output name>.seg<N>.bin (segment 1 is
|
||||||
|
# link816's main output), with link816's segment manifest, <output
|
||||||
|
# name>.json, for a multi-segment one. The Loader puts each segment in whatever bank it
|
||||||
|
# likes, and scripts/profile-iigs.sh finds them there by their bytes. A
|
||||||
|
# directory, not a file, because one make links several programs (a game
|
||||||
|
# disk's Start launcher, say) and each keeps its own.
|
||||||
if [ -n "${JOEY_LINK_MAP:-}" ] && [ -f "$linkMap" ]; then
|
if [ -n "${JOEY_LINK_MAP:-}" ] && [ -f "$linkMap" ]; then
|
||||||
mkdir -p "$(dirname "$JOEY_LINK_MAP")"
|
mapBase="$JOEY_LINK_MAP/$(basename "$output")"
|
||||||
cp "$linkMap" "$JOEY_LINK_MAP"
|
mkdir -p "$JOEY_LINK_MAP"
|
||||||
|
cp "$linkMap" "$mapBase.map"
|
||||||
|
rm -f "$mapBase".seg*.bin
|
||||||
|
cp "$linkBin" "$mapBase.seg1.bin"
|
||||||
|
for seg in "$tmpdir"/link.seg*.bin; do
|
||||||
|
[ -f "$seg" ] || continue
|
||||||
|
cp "$seg" "$mapBase.${seg##*/link.}"
|
||||||
|
done
|
||||||
|
rm -f "$mapBase.json"
|
||||||
|
if [ -f "$linkManifest" ]; then
|
||||||
|
cp "$linkManifest" "$mapBase.json"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Combined header-dependency file: union of clang -MM over the C sources,
|
# Combined header-dependency file: union of clang -MM over the C sources,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#
|
#
|
||||||
# clang-smoke.sh - End-to-end smoke test of the llvm-mos w65816 IIgs
|
# clang-smoke.sh - End-to-end smoke test of the llvm-mos w65816 IIgs
|
||||||
# build path. Compiles a tiny C main that calls JoeyLib's ported asm
|
# build path. Compiles a tiny C main that calls JoeyLib's ported asm
|
||||||
# (iigsInitRowLut), links it with clang-build.sh, runs it under MAME's
|
# (jlIigsInitRowLut), links it with clang-build.sh, runs it under MAME's
|
||||||
# apple2gs, and verifies the LUT the asm computed.
|
# apple2gs, and verifies the LUT the asm computed.
|
||||||
#
|
#
|
||||||
# This proves the whole toolchain end to end: clang -> llvm-mc ->
|
# This proves the whole toolchain end to end: clang -> llvm-mc ->
|
||||||
|
|
@ -32,15 +32,15 @@ export MAME_ROMPATH="$IIGS_ROMPATH"
|
||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
trap 'rm -rf "$tmp"' EXIT
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
# gRowOffsetLut[5] = 5 * 160 = 800 = 0x0320. Publish it to a 24-bit
|
# jlgRowOffsetLut[5] = 5 * 160 = 800 = 0x0320. Publish it to a 24-bit
|
||||||
# address MAME can read back (bank 2 is clear of IIgs ROM scribbling).
|
# address MAME can read back (bank 2 is clear of IIgs ROM scribbling).
|
||||||
cat > "$tmp/smoke.c" <<'EOF'
|
cat > "$tmp/smoke.c" <<'EOF'
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
extern void iigsInitRowLut(void);
|
extern void jlIigsInitRowLut(void);
|
||||||
extern uint16_t gRowOffsetLut[];
|
extern uint16_t jlgRowOffsetLut[];
|
||||||
int main(void) {
|
int main(void) {
|
||||||
iigsInitRowLut();
|
jlIigsInitRowLut();
|
||||||
*(volatile uint16_t *)0x025000 = gRowOffsetLut[5];
|
*(volatile uint16_t *)0x025000 = jlgRowOffsetLut[5];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -63,7 +63,7 @@ LINK="$LLVM816_ROOT/tools/link816"
|
||||||
"$RT/crt0.o" "$RT/libgcc.o" "$RT/libc.o" "$RT/extras.o" \
|
"$RT/crt0.o" "$RT/libgcc.o" "$RT/libc.o" "$RT/extras.o" \
|
||||||
"$tmp/smoke.o" "$tmp/joeyDraw.o"
|
"$tmp/smoke.o" "$tmp/joeyDraw.o"
|
||||||
|
|
||||||
echo "[clang-smoke] running under MAME apple2gs, checking gRowOffsetLut[5] == 0x0320"
|
echo "[clang-smoke] running under MAME apple2gs, checking jlgRowOffsetLut[5] == 0x0320"
|
||||||
bash "$RUN_IN_MAME" "$tmp/smoke.bin" --check 0x025000=0320
|
bash "$RUN_IN_MAME" "$tmp/smoke.bin" --check 0x025000=0320
|
||||||
|
|
||||||
echo "[clang-smoke] PASS"
|
echo "[clang-smoke] PASS"
|
||||||
|
|
|
||||||
106
make/support/macos/cc.sh
Executable file
106
make/support/macos/cc.sh
Executable file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# The macOS port's C compiler (make/macos.mk; the installer builds SDL with
|
||||||
|
# it too): zig cc once for each architecture in MACOS_ARCHS -- by default
|
||||||
|
# Apple silicon and Intel -- with the results joined into one universal file
|
||||||
|
# by llvm-lipo, as Apple's own clang does when given two -arch flags. It
|
||||||
|
# takes what a C compiler takes: with -c it makes a universal object, else a
|
||||||
|
# universal program. The first architecture's run is exactly the call it was
|
||||||
|
# given, so its dependency file (-MMD) comes out as a plain compiler's; the
|
||||||
|
# other runs leave theirs out. With one architecture, or with no -o (a
|
||||||
|
# preprocessing run), it is zig cc for the first architecture alone.
|
||||||
|
#
|
||||||
|
# Programs run on macOS MACOS_MIN or newer: Ventura, which every Apple
|
||||||
|
# silicon Mac runs and the Intel ones from 2017 on.
|
||||||
|
#
|
||||||
|
# From the environment (toolchains/config.mk, which make exports): ZIG and
|
||||||
|
# its cache folders, MACOS_SDK and MACOS_LIPO.
|
||||||
|
#
|
||||||
|
# Usage: cc.sh COMPILER-ARGUMENTS...
|
||||||
|
# cc.sh --minimum prints MACOS_MIN (for an Info.plist)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MACOS_MIN=13.0
|
||||||
|
read -r -a archs <<< "${MACOS_ARCHS:-aarch64 x86_64}"
|
||||||
|
|
||||||
|
if [[ "${1:-}" == --minimum ]]; then
|
||||||
|
printf '%s\n' "$MACOS_MIN"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [[ -z "${ZIG:-}" || -z "${MACOS_SDK:-}" || -z "${MACOS_LIPO:-}" ]]; then
|
||||||
|
echo "$0: ZIG, MACOS_SDK or MACOS_LIPO is not set; run ./install.sh install toolchain:macos" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
out=""
|
||||||
|
compile=0
|
||||||
|
preprocess=0
|
||||||
|
prev=""
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$prev" == -o ]]; then
|
||||||
|
out="$arg"
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
-c) compile=1 ;;
|
||||||
|
-E) preprocess=1 ;;
|
||||||
|
esac
|
||||||
|
prev="$arg"
|
||||||
|
done
|
||||||
|
|
||||||
|
# zig brings the C library's headers and libSystem's stubs; the SDK the
|
||||||
|
# rest, after them (so the caller's own come first): its other headers, its
|
||||||
|
# frameworks, and, for a link, the stub libraries beside them. zig cc
|
||||||
|
# builds for UBSan unless told not to, and would then want its runtime; and
|
||||||
|
# it warns, as no other port's compiler does, at __DATE__ and __TIME__ (a
|
||||||
|
# build that uses them cannot be reproduced), which -Werror would make fatal.
|
||||||
|
flags=(-fno-sanitize=undefined -Wno-date-time -idirafter "$MACOS_SDK/usr/include" -F"$MACOS_SDK/System/Library/Frameworks")
|
||||||
|
if [[ $compile -eq 0 && $preprocess -eq 0 ]]; then
|
||||||
|
flags+=(-L"$MACOS_SDK/usr/lib")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# zig refuses a target with a version for a run that only preprocesses (a
|
||||||
|
# build system asking what the compiler defines), and the version makes no
|
||||||
|
# difference to that.
|
||||||
|
target() {
|
||||||
|
if [[ $preprocess -eq 1 ]]; then
|
||||||
|
printf '%s-macos\n' "$1"
|
||||||
|
else
|
||||||
|
printf '%s-macos.%s\n' "$1" "$MACOS_MIN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${#archs[@]} -eq 1 || -z "$out" ]]; then
|
||||||
|
exec "$ZIG" cc -target "$(target "${archs[0]}")" "$@" "${flags[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
slices=()
|
||||||
|
trap 'rm -f "${slices[@]}"' EXIT
|
||||||
|
for arch in "${archs[@]}"; do
|
||||||
|
slice="$out.$arch"
|
||||||
|
slices+=("$slice")
|
||||||
|
if [[ ${#slices[@]} -eq 1 ]]; then
|
||||||
|
"$ZIG" cc -target "$(target "$arch")" "$@" "${flags[@]}"
|
||||||
|
mv -f "$out" "$slice"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
args=()
|
||||||
|
skip=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ $skip -eq 1 ]]; then
|
||||||
|
skip=0
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
-o) args+=(-o "$slice"); skip=1 ;;
|
||||||
|
-MD|-MMD|-MP) ;;
|
||||||
|
-MF|-MT|-MQ) skip=1 ;;
|
||||||
|
*) args+=("$arg") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
"$ZIG" cc -target "$(target "$arch")" "${args[@]}" "${flags[@]}"
|
||||||
|
done
|
||||||
|
"$MACOS_LIPO" -create "${slices[@]}" -output "$out"
|
||||||
64
make/support/macos/packApp.sh
Executable file
64
make/support/macos/packApp.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# Pack a linked macOS program into its app, as make/macos.mk's packing step:
|
||||||
|
# the program as Contents/MacOS/NAME (NAME the app's, without .app), and
|
||||||
|
# Contents/Info.plist, which tells macOS that is the program to run, the
|
||||||
|
# app's identifier and title, its icon (Contents/Resources/icon.icns, baked
|
||||||
|
# by make beside its DATA), the oldest macOS it runs on (cc.sh's), and that
|
||||||
|
# it draws at a Retina screen's own resolution. The program's Apple silicon
|
||||||
|
# half carries the ad hoc signature zig gave it, which a copy keeps.
|
||||||
|
#
|
||||||
|
# Usage: packApp.sh PROGRAM NAME.app IDENTIFIER TITLE
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 4 ]]; then
|
||||||
|
echo "usage: $0 PROGRAM NAME.app IDENTIFIER TITLE" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
program=$1
|
||||||
|
app=$2
|
||||||
|
identifier=$3
|
||||||
|
title=$4
|
||||||
|
name=$(basename "$app" .app)
|
||||||
|
minimum=$("$(dirname "$0")/cc.sh" --minimum)
|
||||||
|
|
||||||
|
# The title as XML text.
|
||||||
|
title=${title//&/&}
|
||||||
|
title=${title//</<}
|
||||||
|
title=${title//>/>}
|
||||||
|
|
||||||
|
mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources"
|
||||||
|
cp -f "$program" "$app/Contents/MacOS/$name"
|
||||||
|
cat > "$app/Contents/Info.plist" <<EOF
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>$title</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$name</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>icon</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$identifier</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$name</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>$minimum</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
# target, bound to its Windows 3.x supervisor.
|
# target, bound to its Windows 3.x supervisor.
|
||||||
#
|
#
|
||||||
# owcc.sh cc ARGS... compile (-c), or compile and link a program
|
# owcc.sh cc ARGS... compile (-c), or compile and link a program
|
||||||
|
# owcc.sh as SOURCE OBJECT assemble (Watcom's wasm: MASM syntax, OMF)
|
||||||
# owcc.sh ar rcs LIB OBJ... make a library (Watcom's wlib)
|
# owcc.sh ar rcs LIB OBJ... make a library (Watcom's wlib)
|
||||||
#
|
#
|
||||||
# WIN16_WATCOM is the Open Watcom tree (make exports it from
|
# WIN16_WATCOM is the Open Watcom tree (make exports it from
|
||||||
|
|
@ -47,13 +48,18 @@ here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
mode="$1"
|
mode="$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
|
if [ "$mode" = "as" ]; then
|
||||||
|
# as SOURCE OBJECT, quietly. wasm writes no object for a source that
|
||||||
|
# draws a warning, so a warning fails the build here too.
|
||||||
|
exec wasm -q -fo="$2" "$1"
|
||||||
|
fi
|
||||||
if [ "$mode" = "ar" ]; then
|
if [ "$mode" = "ar" ]; then
|
||||||
# ar rcs LIB OBJ...: the library is made new each time (-n), quietly, with
|
# ar rcs LIB OBJ...: the library is made new each time (-n), quietly, with
|
||||||
# case-sensitive names and no .bak copy.
|
# case-sensitive names and no .bak copy.
|
||||||
shift
|
shift
|
||||||
exec wlib -q -n -b -c "$@"
|
exec wlib -q -n -b -c "$@"
|
||||||
fi
|
fi
|
||||||
[ "$mode" = "cc" ] || { echo "owcc.sh: cc or ar, not '$mode'" >&2; exit 2; }
|
[ "$mode" = "cc" ] || { echo "owcc.sh: cc, as or ar, not '$mode'" >&2; exit 2; }
|
||||||
|
|
||||||
flags=(-bwin386 -fpc -I"$WATCOM/h" -include "$here/noAttributes.h" -Wc,-wcd=202)
|
flags=(-bwin386 -fpc -I"$WATCOM/h" -include "$here/noAttributes.h" -Wc,-wcd=202)
|
||||||
sources=()
|
sources=()
|
||||||
|
|
@ -127,7 +133,10 @@ done
|
||||||
if [ "$depFile" = 1 ]; then
|
if [ "$depFile" = 1 ]; then
|
||||||
phonyHeaders "$deps"
|
phonyHeaders "$deps"
|
||||||
fi
|
fi
|
||||||
owcc -bwin386 -o "$work/$name.rex" "${objects[@]}" "${libraries[@]}" "-Wl,option stack=$STACK_BYTES"
|
# noredefsok: a name the program and the library both define is an error,
|
||||||
|
# as on every other port; Watcom's linker otherwise warns and keeps the
|
||||||
|
# first, so the program's variable would silently stand in for JoeyLib's.
|
||||||
|
owcc -bwin386 -o "$work/$name.rex" "${objects[@]}" "${libraries[@]}" "-Wl,option stack=$STACK_BYTES" "-Wl,option noredefsok"
|
||||||
if [ -n "$resources" ]; then
|
if [ -n "$resources" ]; then
|
||||||
(cd "$work" && wbind "$name" -q -s "$WATCOM/binw/win386.ext" -R -q -bt=windows "$resources" "$name.exe")
|
(cd "$work" && wbind "$name" -q -s "$WATCOM/binw/win386.ext" -R -q -bt=windows "$resources" "$name.exe")
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -53,17 +53,17 @@ $(BUILD_DIR)/spritebake-dos: $(SPRITEBAKE_SRC) $(SRC_X86)/spriteEmitX86.c $(SPRI
|
||||||
@mkdir -p $(dir $@)
|
@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 $@
|
||||||
|
|
||||||
$(BUILD_DIR)/spritebake-amiga: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SPRITEBAKE_HDRS)
|
$(BUILD_DIR)/spritebake-amiga: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||||
@mkdir -p $(dir $@)
|
@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 $@
|
||||||
|
|
||||||
# The X68000 runs the Amiga's planar 68k emitter unchanged (see
|
# 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.
|
# 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 $(SPRITEBAKE_HDRS)
|
$(BUILD_DIR)/spritebake-x68000: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||||
@mkdir -p $(dir $@)
|
@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 $@
|
||||||
|
|
||||||
$(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c $(SPRITEBAKE_HDRS)
|
$(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS)
|
||||||
@mkdir -p $(dir $@)
|
@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 $@
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ $(call requireTool,$(WIN16_WATCOM)/binl64/owcc,toolchain:win16)
|
||||||
WIN16_OWCC := $(REPO_DIR)/make/support/win16/owcc.sh
|
WIN16_OWCC := $(REPO_DIR)/make/support/win16/owcc.sh
|
||||||
|
|
||||||
HOSTED_CC := $(WIN16_OWCC) cc
|
HOSTED_CC := $(WIN16_OWCC) cc
|
||||||
|
HOSTED_AS := $(WIN16_OWCC) as
|
||||||
HOSTED_AR := $(WIN16_OWCC) ar
|
HOSTED_AR := $(WIN16_OWCC) ar
|
||||||
HOSTED_BACKEND := win16
|
HOSTED_BACKEND := win16
|
||||||
HOSTED_DEFINES := -DJOEYLIB_PLATFORM_HOSTED -DJOEYLIB_HOSTED_WIN16 -DHOSTED_FRAME_INDEXED -DHOSTED_AUDIO_RATE=22050u -DHOSTED_AUDIO_CHANNELS=1u
|
HOSTED_DEFINES := -DJOEYLIB_PLATFORM_HOSTED -DJOEYLIB_HOSTED_WIN16 -DHOSTED_FRAME_INDEXED -DHOSTED_AUDIO_RATE=22050u -DHOSTED_AUDIO_CHANNELS=1u
|
||||||
|
|
@ -49,4 +50,8 @@ include $(REPO_DIR)/make/hosted.mk
|
||||||
# (scripts/verify-win16-display.sh).
|
# (scripts/verify-win16-display.sh).
|
||||||
ifndef JOEY_PROJECT
|
ifndef JOEY_PROJECT
|
||||||
all $(PLATFORM): $(call programTarget,UBERPROB)
|
all $(PLATFORM): $(call programTarget,UBERPROB)
|
||||||
|
|
||||||
|
# The serial throughput gate's program (scripts/verify-win16-serial-fast.sh).
|
||||||
|
.PHONY: win16-serfast
|
||||||
|
win16-serfast: $(call programTarget,SERFAST)
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,18 @@ LIB_OBJS := \
|
||||||
$(patsubst $(SRC_DIR)/x68000/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \
|
$(patsubst $(SRC_DIR)/x68000/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \
|
||||||
$(patsubst $(SRC_DIR)/x68000/%.s,$(BUILD)/obj/port/%.o,$(PORT_S_SRCS)) \
|
$(patsubst $(SRC_DIR)/x68000/%.s,$(BUILD)/obj/port/%.o,$(PORT_S_SRCS)) \
|
||||||
$(BUILD)/obj/68k/planarFill.o \
|
$(BUILD)/obj/68k/planarFill.o \
|
||||||
|
$(BUILD)/obj/68k/planarShape.o \
|
||||||
$(BUILD)/obj/68k/planarPlot.o \
|
$(BUILD)/obj/68k/planarPlot.o \
|
||||||
$(BUILD)/obj/68k/stageBands.o \
|
$(BUILD)/obj/68k/stageBands.o \
|
||||||
$(BUILD)/obj/68k/modMix.o \
|
$(BUILD)/obj/68k/modMix.o \
|
||||||
$(BUILD)/obj/68k/sfxMix.o \
|
$(BUILD)/obj/68k/sfxMix.o \
|
||||||
$(BUILD)/obj/68k/planarSpriteWalk.o \
|
$(BUILD)/obj/68k/planarSpriteWalk.o \
|
||||||
|
$(BUILD)/obj/68k/planarLayerBlit.o \
|
||||||
$(BUILD)/obj/68k/spriteEmitPlanar68k.o \
|
$(BUILD)/obj/68k/spriteEmitPlanar68k.o \
|
||||||
|
$(BUILD)/obj/68k/planarHash.o \
|
||||||
$(BUILD)/obj/68k/planarSprite.o \
|
$(BUILD)/obj/68k/planarSprite.o \
|
||||||
|
$(BUILD)/obj/68k/spriteC2p.o \
|
||||||
|
$(BUILD)/obj/68k/planarTileLayer.o \
|
||||||
$(BUILD)/obj/codegen/spriteCompile.o \
|
$(BUILD)/obj/codegen/spriteCompile.o \
|
||||||
$(BUILD)/obj/codegen/spriteStage.o
|
$(BUILD)/obj/codegen/spriteStage.o
|
||||||
|
|
||||||
|
|
@ -81,9 +86,11 @@ $(BUILD)/obj/port/%.o: $(SRC_DIR)/x68000/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(X68K_CC) $(CFLAGS) -c $< -o $@
|
$(X68K_CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# The assembler finds src/m68k/m68kCSym.inc through -I: the gcc driver hands
|
||||||
|
# it no include path.
|
||||||
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(X68K_CC) $(CFLAGS) -c $< -o $@
|
$(X68K_CC) $(CFLAGS) -Wa,-I$(SRC_68K) -c $< -o $@
|
||||||
|
|
||||||
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.c
|
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.c
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
#
|
#
|
||||||
# scripts/bench-amiga.sh # default 150s timeout
|
# scripts/bench-amiga.sh # default 150s timeout
|
||||||
# TIMEOUT=240 scripts/bench-amiga.sh # override
|
# TIMEOUT=240 scripts/bench-amiga.sh # override
|
||||||
|
# BENCH_PROG=Uberprob BENCH_BIN_DIR=build/amiga/probe scripts/bench-amiga.sh
|
||||||
|
# # another build of UBER (the probe:
|
||||||
|
# # scripts/verify-amiga-display.sh)
|
||||||
#
|
#
|
||||||
# Real Kickstart (installer component `kickstart`) is
|
# Real Kickstart (installer component `kickstart`) is
|
||||||
# required -- the HAL drives OCS directly and AROS is unreliable for it.
|
# required -- the HAL drives OCS directly and AROS is unreliable for it.
|
||||||
|
|
@ -25,25 +28,29 @@ source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
joeyNeed FSUAE fsuae
|
joeyNeed FSUAE fsuae
|
||||||
source "$repo/scripts/lib/amigaGate.sh"
|
source "$repo/scripts/lib/amigaGate.sh"
|
||||||
amigaMachine
|
amigaMachine
|
||||||
bin_dir=$repo/build/amiga/bin
|
bin_dir=${BENCH_BIN_DIR:-$repo/build/amiga/bin}
|
||||||
|
prog=${BENCH_PROG:-Uber}
|
||||||
timeout_s=${TIMEOUT:-150}
|
timeout_s=${TIMEOUT:-150}
|
||||||
|
|
||||||
if [[ ! -f $bin_dir/Uber ]]; then
|
if [[ ! -f $bin_dir/$prog ]]; then
|
||||||
echo "Uber not built. Run 'make amiga' first." >&2
|
echo "$prog not built in $bin_dir. Run 'make amiga' first." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
work=$(mktemp -d -t joeylib-amiga-bench.XXXXXX)
|
work=$(mktemp -d -t joeylib-amiga-bench.XXXXXX)
|
||||||
mkdir -p "$work/s"
|
mkdir -p "$work/s"
|
||||||
find "$bin_dir" -maxdepth 1 -type f -executable -exec cp -t "$work/" {} +
|
find "$bin_dir" -maxdepth 1 -type f -executable -exec cp -t "$work/" {} +
|
||||||
|
# A probe build's folder has no DATA of its own: UBER's is the main build's.
|
||||||
if [[ -d "$bin_dir/DATA" ]]; then
|
if [[ -d "$bin_dir/DATA" ]]; then
|
||||||
cp -r "$bin_dir/DATA" "$work/"
|
cp -r "$bin_dir/DATA" "$work/"
|
||||||
|
elif [[ -d "$repo/build/amiga/bin/DATA" ]]; then
|
||||||
|
cp -r "$repo/build/amiga/bin/DATA" "$work/"
|
||||||
fi
|
fi
|
||||||
# ':' anchors to the boot volume root. 32 KB task stack: the shell
|
# ':' anchors to the boot volume root. 32 KB task stack: the shell
|
||||||
# default (~4 KB) left UBER one deep call chain from overflow -- the
|
# default (~4 KB) left UBER one deep call chain from overflow -- the
|
||||||
# Phase-1 sprite-compile path found it (emitter accumulator frame +
|
# Phase-1 sprite-compile path found it (emitter accumulator frame +
|
||||||
# vsnprintf underneath = nondeterministic wandering crashes).
|
# vsnprintf underneath = nondeterministic wandering crashes).
|
||||||
printf 'stack 32768\n:Uber\n' > "$work/s/startup-sequence"
|
printf 'stack 32768\n:%s\n' "$prog" > "$work/s/startup-sequence"
|
||||||
|
|
||||||
log=$work/joeylog.txt
|
log=$work/joeylog.txt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
# scripts/bench-atarist.sh ste # the STe build on an STe
|
# scripts/bench-atarist.sh ste # the STe build on an STe
|
||||||
# scripts/bench-atarist.sh megaste # the STe build on a Mega STe
|
# scripts/bench-atarist.sh megaste # the STe build on a Mega STe
|
||||||
# TIMEOUT=120 scripts/bench-atarist.sh # override
|
# TIMEOUT=120 scripts/bench-atarist.sh # override
|
||||||
|
# BENCH_PRG / BENCH_BIN_DIR # another program and its folder
|
||||||
|
# # (UBERPROB, verify-atarist-display.sh)
|
||||||
#
|
#
|
||||||
# NOTE: requires the ST supervisor-mode fix in src/atarist/hal.c
|
# NOTE: requires the ST supervisor-mode fix in src/atarist/hal.c
|
||||||
# (jlpInit Super(0L)); without it UBER bus-errors on the first present.
|
# (jlpInit Super(0L)); without it UBER bus-errors on the first present.
|
||||||
|
|
@ -44,13 +46,14 @@ case $machine in
|
||||||
ste|megaste) build=ste ;;
|
ste|megaste) build=ste ;;
|
||||||
*) echo "usage: scripts/bench-atarist.sh [st|megast|ste|megaste]" >&2; exit 2 ;;
|
*) echo "usage: scripts/bench-atarist.sh [st|megast|ste|megaste]" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
bin_dir=$repo/build/$build/bin
|
bin_dir=${BENCH_BIN_DIR:-$repo/build/$build/bin}
|
||||||
|
prg=${BENCH_PRG:-UBER.PRG}
|
||||||
tos=$EMUTOS_IMG
|
tos=$EMUTOS_IMG
|
||||||
log=$bin_dir/joeylog.txt
|
log=$bin_dir/joeylog.txt
|
||||||
timeout_s=${TIMEOUT:-90}
|
timeout_s=${TIMEOUT:-90}
|
||||||
|
|
||||||
if [[ ! -f $bin_dir/UBER.PRG ]]; then
|
if [[ ! -f $bin_dir/$prg ]]; then
|
||||||
echo "UBER.PRG not built. Run 'make $build' first." >&2
|
echo "$prg not built. Run 'make $build' first." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ ! -f $tos ]]; then
|
if [[ ! -f $tos ]]; then
|
||||||
|
|
@ -71,7 +74,7 @@ cleanup() {
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
echo "bench-atarist: running the $build build's UBER headless on a $machine (timeout ${timeout_s}s)..." >&2
|
echo "bench-atarist: running the $build build's $prg headless on a $machine (timeout ${timeout_s}s)..." >&2
|
||||||
DISPLAY=$display "$HATARI" \
|
DISPLAY=$display "$HATARI" \
|
||||||
--machine "$machine" \
|
--machine "$machine" \
|
||||||
--tos "$tos" \
|
--tos "$tos" \
|
||||||
|
|
@ -81,7 +84,7 @@ DISPLAY=$display "$HATARI" \
|
||||||
--sound off \
|
--sound off \
|
||||||
--fast-forward on \
|
--fast-forward on \
|
||||||
--frameskips 4 \
|
--frameskips 4 \
|
||||||
--auto "C:\\UBER.PRG" >/tmp/bench-atarist-hatari.log 2>&1 &
|
--auto "C:\\$prg" >/tmp/bench-atarist-hatari.log 2>&1 &
|
||||||
hat_pid=$!
|
hat_pid=$!
|
||||||
|
|
||||||
# Poll for the completion marker UBER flushes just before its key wait.
|
# Poll for the completion marker UBER flushes just before its key wait.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
# Stages a scratch DVX (scripts/lib/dvxGate.sh) whose desktop is UBER, boots
|
# 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
|
# 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
|
# exit" line in its joeylog.txt -- which a DVX program writes to its settings
|
||||||
# folder, CONFIG\APPS\JOEYLIB\UBER on the staged drive. The log is copied to
|
# folder, CONFIG\APPS\<VENDOR>\UBER on the staged drive. The log is copied to
|
||||||
# build/dvx/bin/joeylog.txt (tools/diff-uber-hashes and tools/uber-perf-table
|
# build/dvx/bin/joeylog.txt (tools/diff-uber-hashes and tools/uber-perf-table
|
||||||
# read it from there) and DOSBox is stopped.
|
# read it from there) and DOSBox is stopped.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
# for hundreds of frames rather than one.
|
# for hundreds of frames rather than one.
|
||||||
#
|
#
|
||||||
# bash scripts/bench-x68000.sh
|
# bash scripts/bench-x68000.sh
|
||||||
|
# X68K_BENCH_CFLAGS="-DUBER_PROBE -DUBER_FRAMES=1" UBER built with more
|
||||||
|
# flags: its probe rows (scripts/verify-x68000-display.sh)
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
|
@ -52,7 +54,8 @@ trap 'rm -rf "$work"' EXIT
|
||||||
# stock ~900 KB of Human68k utilities in SYS/ BIN/ BASIC2/ ... still allocated
|
# stock ~900 KB of Human68k utilities in SYS/ BIN/ BASIC2/ ... still allocated
|
||||||
# and only ~330 KB usable. `xdftool empty` frees the whole 1221-cluster disk,
|
# and only ~330 KB usable. `xdftool empty` frees the whole 1221-cluster disk,
|
||||||
# so the size pressure is gone -- the strip is now just tidiness.)
|
# so the size pressure is gone -- the strip is now just tidiness.)
|
||||||
m68k-xelf-gcc -s -O2 -m68000 -fomit-frame-pointer \
|
# shellcheck disable=SC2086 # X68K_BENCH_CFLAGS is a list of flags
|
||||||
|
m68k-xelf-gcc -s -O2 -m68000 -fomit-frame-pointer ${X68K_BENCH_CFLAGS:-} \
|
||||||
-DJOEYLIB_PLATFORM_X68000 \
|
-DJOEYLIB_PLATFORM_X68000 \
|
||||||
-I"$repo/include" -I"$repo/src/core" -I"$repo/src/x68000" \
|
-I"$repo/include" -I"$repo/src/core" -I"$repo/src/x68000" \
|
||||||
"$repo/examples/uber/uber.c" \
|
"$repo/examples/uber/uber.c" \
|
||||||
|
|
@ -67,7 +70,7 @@ x68kDataDisk "$work/data.xdf" || exit 1
|
||||||
x68kXdf add "$work/data.xdf" "$work/UBER.X" UBER.X >/dev/null || 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
|
# Completion-driven exit. UBER's last act before jlWaitForAnyKey is
|
||||||
# jlSurfaceClear(gStage, 2) + jlStagePresent, so an all-colour-2 stage in GVRAM
|
# jlSurfaceClear(jlgStage, 2) + jlStagePresent, so an all-colour-2 stage in GVRAM
|
||||||
# is the "hashes are computed and flushed" signal. The benchmark's own
|
# 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 colour for an instant, so the
|
||||||
# reading only counts once it has held for GREEN_STABLE frames -- the finished
|
# reading only counts once it has held for GREEN_STABLE frames -- the finished
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ for o in glob.glob(sys.argv[1]+"/*.o"):
|
||||||
if len(p)>=3 and p[1] in "TDBRtdbr": defs.add(p[2])
|
if len(p)>=3 and p[1] in "TDBRtdbr": defs.add(p[2])
|
||||||
elif len(p)>=3 and p[1]=="U": und.add(p[2])
|
elif len(p)>=3 and p[1]=="U": und.add(p[2])
|
||||||
elif len(p)==2 and p[0]=="U": und.add(p[1])
|
elif len(p)==2 and p[0]=="U": und.add(p[1])
|
||||||
print(" ".join(sorted(s for s in und-defs if s.startswith(("jlp","jlSprite","spriteCompiledDraw")))))
|
print(" ".join(sorted(s for s in und-defs if s.startswith(("jlp","jlSprite","jlSpriteCompiledDraw")))))
|
||||||
PY
|
PY
|
||||||
)
|
)
|
||||||
if [ -n "$missing" ]; then
|
if [ -n "$missing" ]; then
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
# check-dirtymark.sh - Host-cc gate for the stage's dirty-band writers: the
|
# check-dirtymark.sh - Host-cc gate for the stage's dirty-band writers: the
|
||||||
# multi-row mark (surfaceMarkDirtyRows, whose four-row groups reuse a group's
|
# multi-row mark (jlSurfaceMarkDirtyRows, whose four-row groups reuse a group's
|
||||||
# result for the next group that held the same bytes) and the whole-stage
|
# result for the next group that held the same bytes) and the whole-stage
|
||||||
# fills, held byte for byte to the plain per-row widen over randomized band
|
# fills, held byte for byte to the plain per-row widen over randomized band
|
||||||
# states and marks of every height and phase (tests/host/dirtyMarkHost.c).
|
# states and marks of every height and phase (tests/host/dirtyMarkHost.c).
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
# win32 a group icon, number HOSTED_ICON_ID, among its resources
|
# win32 a group icon, number HOSTED_ICON_ID, among its resources
|
||||||
# (llvm-readobj)
|
# (llvm-readobj)
|
||||||
# win16 the same in its NE resource table
|
# win16 the same in its NE resource table
|
||||||
# sdl3 the window icon linked in (gHostedIconRgba)
|
# sdl3 the window icon linked in (jlgHostedIconRgba)
|
||||||
|
# macos the app's icon: Info.plist naming an ICNS file in its Resources
|
||||||
# web icon.png beside the page, the page naming it its icon
|
# web icon.png beside the page, the page naming it its icon
|
||||||
# psp the EBOOT's ICON0, the baked PNG, 144x80
|
# psp the EBOOT's ICON0, the baked PNG, 144x80
|
||||||
# haiku, haiku32, beos BEOS:L:STD_ICON and BEOS:M:STD_ICON resources
|
# haiku, haiku32, beos BEOS:L:STD_ICON and BEOS:M:STD_ICON resources
|
||||||
|
|
@ -47,7 +48,7 @@ makeValue() {
|
||||||
make -s -f "$repo/make/$1.mk" --eval "valueOf: ; @echo \$($2)" valueOf 2> /dev/null
|
make -s -f "$repo/make/$1.mk" --eval "valueOf: ; @echo \$($2)" valueOf 2> /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
for port in amiga win32 win16 sdl3 web psp haiku haiku32 beos dvx iigs; do
|
for port in amiga win32 win16 sdl3 macos web psp haiku haiku32 beos dvx iigs; do
|
||||||
program=$(joeyExampleProgram draw "$port" 2> /dev/null) || continue
|
program=$(joeyExampleProgram draw "$port" 2> /dev/null) || continue
|
||||||
[ -f "$program" ] || continue
|
[ -f "$program" ] || continue
|
||||||
checked="${checked:+$checked }$port"
|
checked="${checked:+$checked }$port"
|
||||||
|
|
@ -106,7 +107,21 @@ PY
|
||||||
;;
|
;;
|
||||||
sdl3)
|
sdl3)
|
||||||
symbols=$(nm "$program" 2> /dev/null)
|
symbols=$(nm "$program" 2> /dev/null)
|
||||||
grep -q ' gHostedIconRgba$' <<< "$symbols" || fail sdl3 "DRAW links no window icon"
|
grep -q ' jlgHostedIconRgba$' <<< "$symbols" || fail sdl3 "DRAW links no window icon"
|
||||||
|
;;
|
||||||
|
macos)
|
||||||
|
python3 - "$(dirname "$(dirname "$(dirname "$program")")")" <<'PY' || fail macos "DRAW.app's icon"
|
||||||
|
import plistlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
app = sys.argv[1]
|
||||||
|
with open(app + "/Contents/Info.plist", "rb") as f:
|
||||||
|
info = plistlib.load(f)
|
||||||
|
icon = Image.open("%s/Contents/Resources/%s.icns" % (app, info["CFBundleIconFile"]))
|
||||||
|
sys.exit(icon.format != "ICNS")
|
||||||
|
PY
|
||||||
;;
|
;;
|
||||||
web)
|
web)
|
||||||
folder=$(dirname "$program")
|
folder=$(dirname "$program")
|
||||||
|
|
|
||||||
98
scripts/check-macos-apps.sh
Executable file
98
scripts/check-macos-apps.sh
Executable file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# check-macos-apps.sh - Host gate for the macOS port's apps (make macos),
|
||||||
|
# which nothing here can run: every app in build/macos/bin is one macOS can
|
||||||
|
# open. Its Info.plist parses and names, as an application for the macOS
|
||||||
|
# make/support/macos/cc.sh builds for, a program that is in Contents/MacOS
|
||||||
|
# and an icon that is in Contents/Resources. The program is universal: an
|
||||||
|
# Apple silicon (arm64) and an Intel (x86_64) program, each built for that
|
||||||
|
# macOS and linking nothing but the system's own libraries, the Apple silicon
|
||||||
|
# one signed (zig signs it ad hoc as it links; macOS will not run an
|
||||||
|
# unsigned arm64 program at all). Read with LLVM's Mach-O tools.
|
||||||
|
#
|
||||||
|
# Usage: scripts/check-macos-apps.sh
|
||||||
|
# Exit: 0 PASS, 1 FAIL, 2 SKIP (the port not built, or no LLVM tools)
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
label=check-macos-apps
|
||||||
|
|
||||||
|
joeyNeed MACOS_LIPO toolchain:macos
|
||||||
|
objdump="$(dirname "$MACOS_LIPO")/llvm-objdump"
|
||||||
|
joeyNeedFile "$objdump" toolchain:macos
|
||||||
|
minimum=$("$repo/make/support/macos/cc.sh" --minimum)
|
||||||
|
bin="$repo/build/macos/bin"
|
||||||
|
if ! compgen -G "$bin/*.app" > /dev/null; then
|
||||||
|
echo "$label: SKIP - no app in $bin (make macos)"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$bin" "$MACOS_LIPO" "$objdump" "$minimum" "$label" <<'PY'
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import plistlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
bin, lipo, objdump, minimum, label = sys.argv[1:]
|
||||||
|
ARCHS = {"arm64", "x86_64"}
|
||||||
|
SIGNED = {"arm64"}
|
||||||
|
SYSTEM = ("/System/Library/", "/usr/lib/")
|
||||||
|
|
||||||
|
|
||||||
|
def run(*args):
|
||||||
|
return subprocess.run(args, capture_output=True, text=True, check=True).stdout
|
||||||
|
|
||||||
|
|
||||||
|
def sliceProblems(program, arch):
|
||||||
|
problems = []
|
||||||
|
headers = run(objdump, "--macho", "--private-headers", "--arch=" + arch, program).split()
|
||||||
|
if "LC_BUILD_VERSION" not in headers or headers[headers.index("minos") + 1] != minimum:
|
||||||
|
problems.append("%s not built for macOS %s" % (arch, minimum))
|
||||||
|
if arch in SIGNED and "LC_CODE_SIGNATURE" not in headers:
|
||||||
|
problems.append("%s unsigned" % arch)
|
||||||
|
for line in run(objdump, "--macho", "--dylibs-used", "--arch=" + arch, program).splitlines()[1:]:
|
||||||
|
library = line.split()[0]
|
||||||
|
if not library.startswith(SYSTEM):
|
||||||
|
problems.append("%s links %s" % (arch, library))
|
||||||
|
return problems
|
||||||
|
|
||||||
|
|
||||||
|
def appProblems(app):
|
||||||
|
contents = os.path.join(app, "Contents")
|
||||||
|
with open(os.path.join(contents, "Info.plist"), "rb") as f:
|
||||||
|
info = plistlib.load(f)
|
||||||
|
problems = []
|
||||||
|
if info.get("CFBundlePackageType") != "APPL" or info.get("LSMinimumSystemVersion") != minimum:
|
||||||
|
problems.append("Info.plist is not an application for macOS %s" % minimum)
|
||||||
|
if not os.path.isfile(os.path.join(contents, "Resources", info.get("CFBundleIconFile", "") + ".icns")):
|
||||||
|
problems.append("no icon")
|
||||||
|
program = os.path.join(contents, "MacOS", info.get("CFBundleExecutable", ""))
|
||||||
|
if not (os.path.isfile(program) and os.access(program, os.X_OK)):
|
||||||
|
return problems + ["no program"]
|
||||||
|
archs = set(run(lipo, "-archs", program).split())
|
||||||
|
if archs != ARCHS:
|
||||||
|
problems.append("holds %s, not %s" % (" ".join(sorted(archs)), " ".join(sorted(ARCHS))))
|
||||||
|
for arch in sorted(archs & ARCHS):
|
||||||
|
problems += sliceProblems(program, arch)
|
||||||
|
return problems
|
||||||
|
|
||||||
|
|
||||||
|
apps = sorted(glob.glob(os.path.join(bin, "*.app")))
|
||||||
|
fails = []
|
||||||
|
for app in apps:
|
||||||
|
try:
|
||||||
|
problems = appProblems(app)
|
||||||
|
except (OSError, ValueError, KeyError, plistlib.InvalidFileException, subprocess.CalledProcessError) as error:
|
||||||
|
problems = [str(error)]
|
||||||
|
if problems:
|
||||||
|
fails.append("%s: %s" % (os.path.basename(app), ", ".join(problems)))
|
||||||
|
if fails:
|
||||||
|
print("%s: FAIL - %s" % (label, "; ".join(fails)))
|
||||||
|
sys.exit(1)
|
||||||
|
print("%s: PASS (%d app%s: universal %s, arm64 signed, macOS %s on, system libraries only)" % (label, len(apps), "" if len(apps) == 1 else "s", "+".join(sorted(ARCHS)), minimum))
|
||||||
|
PY
|
||||||
34
scripts/check-tiled.sh
Executable file
34
scripts/check-tiled.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# check-tiled.sh - Host gate for tools/tiled2joey.py, which turns a map
|
||||||
|
# made in Tiled into a tile sheet (baked into tile banks by assetbake.py) and
|
||||||
|
# one jlTileMapPaste map per tile layer (docs/Assets.adoc). tests/host/
|
||||||
|
# tiledCheck.py writes one map as TMX and as TMJ, with every flip Tiled can
|
||||||
|
# ask for, two tilesets (one external, with a margin and spacing), and every
|
||||||
|
# layer encoding the tool reads; both must convert to the sheet and maps
|
||||||
|
# worked out by hand, the sheet must bake, and maps it cannot take (16x16
|
||||||
|
# tiles, an RGB tileset) must be refused.
|
||||||
|
#
|
||||||
|
# Usage: scripts/check-tiled.sh
|
||||||
|
# Exit: 0 PASS, 1 FAIL
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
label=check-tiled
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-tiled.XXXXXX)
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
||||||
|
out=$(python3 "$repo/tests/host/tiledCheck.py" "$repo" "$work")
|
||||||
|
status=$?
|
||||||
|
echo "$out"
|
||||||
|
checks=$(grep -c '^TILED:' <<< "$out")
|
||||||
|
if [ "$status" -ne 0 ]; then
|
||||||
|
echo "$label: FAIL ($(grep -c 'FAIL' <<< "$out") of $checks checks failed)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "$label: PASS ($checks checks: TMX and TMJ, every flip, two tilesets, three layer encodings, the bake, two refusals)"
|
||||||
|
exit 0
|
||||||
32
scripts/check-tilelayer.sh
Executable file
32
scripts/check-tilelayer.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# check-tilelayer.sh - Host gate for tile layers: the BLANK port built with
|
||||||
|
# the host cc, and tests/host/tileLayerHost.c checking every draw of 300 runs
|
||||||
|
# of random moves, bands, maps (some smaller than the view), and cell changes
|
||||||
|
# pixel for pixel against the map itself, plus the band's dirty marks, the
|
||||||
|
# rows outside it, and jlTileLayerCreate's refusals.
|
||||||
|
#
|
||||||
|
# Usage: scripts/check-tilelayer.sh
|
||||||
|
# Exit: 0 PASS, 1 FAIL
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
cc=${CC:-cc}
|
||||||
|
work=$(mktemp -d -t joeylib-tilelayer.XXXXXX)
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
||||||
|
cd "$repo"
|
||||||
|
"$cc" -O2 -DJOEYLIB_PLATFORM_BLANK \
|
||||||
|
-Iinclude -Iinclude/joey -Isrc/core -Isrc/codegen -Wall -Wextra -Werror \
|
||||||
|
src/core/*.c src/generic/*.c src/blank/blank.c tests/host/tileLayerHost.c \
|
||||||
|
-lm -o "$work/tileLayerHost"
|
||||||
|
|
||||||
|
if "$work/tileLayerHost"; then
|
||||||
|
echo "check-tilelayer: PASS (every draw matches the map pixel for pixel)"
|
||||||
|
else
|
||||||
|
echo "check-tilelayer: FAIL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
@ -10,16 +10,16 @@
|
||||||
# one of them the desktop, so DVX boots straight into it; and the DOSBox
|
# one of them the desktop, so DVX boots straight into it; and the DOSBox
|
||||||
# Staging run of that folder as drive C:. Nothing here writes to the DVX
|
# Staging run of that folder as drive C:. Nothing here writes to the DVX
|
||||||
# project or to toolchains/opt/dvx. Source after joeyEnv.sh. A project built
|
# project or to toolchains/opt/dvx. Source after joeyEnv.sh. A project built
|
||||||
# on make/project.mk sets DVX_BIN (and DVX_VENDOR, if it changed it) to its
|
# on make/project.mk sets DVX_BIN and DVX_VENDOR to its own before sourcing.
|
||||||
# own before sourcing.
|
|
||||||
|
|
||||||
DVX_GATE="$(basename "$0" .sh)"
|
DVX_GATE="$(basename "$0" .sh)"
|
||||||
DVX_BIN="${DVX_BIN:-$JOEY_REPO/build/dvx/bin}"
|
DVX_BIN="${DVX_BIN:-$JOEY_REPO/build/dvx/bin}"
|
||||||
# UBER's probe build (make dvx-probe), packaged apart from bin/.
|
# UBER's probe build (make dvx-probe), packaged apart from bin/.
|
||||||
DVX_PROBE_DIR="${DVX_PROBE_DIR:-$JOEY_REPO/build/dvx/probe}"
|
DVX_PROBE_DIR="${DVX_PROBE_DIR:-$JOEY_REPO/build/dvx/probe}"
|
||||||
# The folder a program installs in under apps/ (make/dvx.mk's DVX_VENDOR), and
|
# The folder a program installs in under apps/ (make/dvx.mk's DVX_VENDOR; for
|
||||||
# the name of its log.
|
# JoeyLib's examples make/examples.mk's, asked of make), and the name of its
|
||||||
DVX_VENDOR="${DVX_VENDOR:-joeylib}"
|
# 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
|
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 settings folder, and its
|
||||||
# full-screen and square-pixels switches (src/dvx/hal.c).
|
# full-screen and square-pixels switches (src/dvx/hal.c).
|
||||||
|
|
@ -68,7 +68,7 @@ dvxNeedBuild() {
|
||||||
|
|
||||||
# dvxStage DIR APPDIR... -- stage DVX in DIR/DVX with the programs in the
|
# dvxStage DIR APPDIR... -- stage DVX in DIR/DVX with the programs in the
|
||||||
# APPDIRs (each a folder holding NAME.app, from dvxAppDir or anywhere else),
|
# APPDIRs (each a folder holding NAME.app, from dvxAppDir or anywhere else),
|
||||||
# installed as apps/joeylib/NAME. The first one is the desktop. DIR may hold
|
# 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
|
# 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 settings, the programs' CONFIG folders) stays.
|
||||||
dvxStage() {
|
dvxStage() {
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,11 @@ joeyExampleProgram() {
|
||||||
printf '%s\n' "$bin/$up/EBOOT.PBP"
|
printf '%s\n' "$bin/$up/EBOOT.PBP"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
macos)
|
||||||
|
# Every program is an app, the program inside it.
|
||||||
|
printf '%s\n' "$bin/$up.app/Contents/MacOS/$up"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
amiga)
|
amiga)
|
||||||
# Mixed-case names.
|
# Mixed-case names.
|
||||||
up="${name,,}"
|
up="${name,,}"
|
||||||
|
|
@ -81,11 +86,11 @@ joeyExampleProgram() {
|
||||||
# apps/<vendor>/<name>/, whatever the vendor.
|
# apps/<vendor>/<name>/, whatever the vendor.
|
||||||
up="${up,,}"
|
up="${up,,}"
|
||||||
candidate="$(find "$bin/apps" -mindepth 3 -maxdepth 3 -type f -path "*/$up/$up.app" -print -quit 2>/dev/null)"
|
candidate="$(find "$bin/apps" -mindepth 3 -maxdepth 3 -type f -path "*/$up/$up.app" -print -quit 2>/dev/null)"
|
||||||
printf '%s\n' "${candidate:-$bin/apps/joeylib/$up/$up.app}"
|
printf '%s\n' "${candidate:-$bin/apps/VENDOR/$up/$up.app}"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "$(basename "$0"): unknown port '$2' (iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, win32, win16, psp, haiku, haiku32 or beos)" >&2
|
echo "$(basename "$0"): unknown port '$2' (iigs, amiga, atarist, ste, dos, x68000, dvx, sdl3, web, macos, win32, win16, psp, haiku, haiku32 or beos)" >&2
|
||||||
return 2
|
return 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,13 @@ CLOSE FAIL line more, and keeps the pty pair open until FILE exists or WALL
|
||||||
runs out, so the gate can read the guest end's settings after the close
|
runs out, so the gate can read the guest end's settings after the close
|
||||||
(verify-sdl3-serial.sh).
|
(verify-sdl3-serial.sh).
|
||||||
|
|
||||||
|
--stream N --rate RATE (TCP, nullmodem transparent:1) then streams N bytes of printable
|
||||||
|
ASCII through SERTEST's echo, at most STREAM_WINDOW bytes ahead of what has
|
||||||
|
come back, and checks every byte returns once, in order. Prints one STREAM
|
||||||
|
PASS / STREAM FAIL line more, with the round trip's bytes a second; PASS
|
||||||
|
needs that to be at least STREAM_MIN_PERCENT of what the line rate RATE
|
||||||
|
(bits a second) carries.
|
||||||
|
|
||||||
--dtr-check (TCP, with DOSBox's nullmodem in transparent:0 mode, which puts
|
--dtr-check (TCP, with DOSBox's nullmodem in transparent:0 mode, which puts
|
||||||
the guest's modem lines in the byte stream as they change: 0xFF 0xFF is a
|
the guest's modem lines in the byte stream as they change: 0xFF 0xFF is a
|
||||||
data 0xFF, 0xFF <b> the lines, bit 0 RTS, bit 1 DTR) then runs SERTEST's
|
data 0xFF, 0xFF <b> the lines, bit 0 RTS, bit 1 DTR) then runs SERTEST's
|
||||||
|
|
@ -52,6 +59,17 @@ CONNECT_RETRY_SECONDS = 0.5
|
||||||
READ_SIZE = 256
|
READ_SIZE = 256
|
||||||
SHOWN_BYTES = 80
|
SHOWN_BYTES = 80
|
||||||
|
|
||||||
|
# --stream: what the window, the chunks, and the pass mark are, and the bytes
|
||||||
|
# it sends (printable ASCII, so never SERTEST's DTR_CHECK).
|
||||||
|
STREAM_WINDOW = 1024
|
||||||
|
STREAM_CHUNK = 64
|
||||||
|
STREAM_POLL_SECONDS = 0.01
|
||||||
|
STREAM_SETTLE_SECONDS = 1.0
|
||||||
|
STREAM_MIN_PERCENT = 75
|
||||||
|
STREAM_FIRST_BYTE = 0x20
|
||||||
|
STREAM_BYTE_VALUES = 95
|
||||||
|
LINE_BITS_PER_BYTE = 10 # 8N1: a start bit, 8 data bits, a stop bit
|
||||||
|
|
||||||
# SERTEST's DTR check (examples/sertest/sertest.c).
|
# SERTEST's DTR check (examples/sertest/sertest.c).
|
||||||
DTR_CHECK = b"\x04"
|
DTR_CHECK = b"\x04"
|
||||||
DTR_ACK = b"\x06"
|
DTR_ACK = b"\x06"
|
||||||
|
|
@ -239,15 +257,59 @@ def loop(send, receive, wall, start, banner):
|
||||||
return 1, got
|
return 1, got
|
||||||
|
|
||||||
|
|
||||||
|
def stream(send, receive, wall, start, total, rate):
|
||||||
|
# The probe's last echoes go first, then the stream and its echo.
|
||||||
|
pattern = bytes(STREAM_FIRST_BYTE + (k % STREAM_BYTE_VALUES) for k in range(total))
|
||||||
|
settle = time.time() + STREAM_SETTLE_SECONDS
|
||||||
|
while time.time() < settle:
|
||||||
|
if receive() is None:
|
||||||
|
print("STREAM FAIL: the connection closed before the stream")
|
||||||
|
return 1
|
||||||
|
sent = 0
|
||||||
|
got = b""
|
||||||
|
began = time.time()
|
||||||
|
while len(got) < total and time.time() - start < wall:
|
||||||
|
if sent < total and sent - len(got) < STREAM_WINDOW:
|
||||||
|
chunk = pattern[sent:sent + STREAM_CHUNK]
|
||||||
|
send(chunk)
|
||||||
|
sent += len(chunk)
|
||||||
|
data = receive()
|
||||||
|
if data is None:
|
||||||
|
break
|
||||||
|
got += data
|
||||||
|
elapsed = time.time() - began
|
||||||
|
perSecond = len(got) / elapsed if elapsed > 0 else 0.0
|
||||||
|
floor = rate / LINE_BITS_PER_BYTE * STREAM_MIN_PERCENT / 100.0
|
||||||
|
if got != pattern:
|
||||||
|
at = next((k for k in range(min(len(got), total)) if got[k] != pattern[k]), min(len(got), total))
|
||||||
|
print("STREAM FAIL: %d of %d bytes back, the first wrong at %d; %r" % (len(got), total, at, got[at:at + 16]))
|
||||||
|
return 1
|
||||||
|
if perSecond < floor:
|
||||||
|
print("STREAM FAIL: %d bytes back in order, %.0f a second, under %.0f (%d%% of %d baud)" % (total, perSecond, floor, STREAM_MIN_PERCENT, rate))
|
||||||
|
return 1
|
||||||
|
print("STREAM PASS: %d bytes back in order, none lost, %.0f a second (%d baud carries %d)" % (total, perSecond, rate, rate // LINE_BITS_PER_BYTE))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
withDtrCheck = "--dtr-check" in argv
|
withDtrCheck = "--dtr-check" in argv
|
||||||
argv = [arg for arg in argv if arg != "--dtr-check"]
|
argv = [arg for arg in argv if arg != "--dtr-check"]
|
||||||
|
streamBytes = 0
|
||||||
|
rate = 0
|
||||||
|
if "--stream" in argv and argv.index("--stream") + 1 < len(argv):
|
||||||
|
at = argv.index("--stream")
|
||||||
|
streamBytes = int(argv[at + 1])
|
||||||
|
argv = argv[:at] + argv[at + 2:]
|
||||||
|
if "--rate" in argv and argv.index("--rate") + 1 < len(argv):
|
||||||
|
at = argv.index("--rate")
|
||||||
|
rate = int(argv[at + 1])
|
||||||
|
argv = argv[:at] + argv[at + 2:]
|
||||||
holdFile = None
|
holdFile = None
|
||||||
if "--close-hold" in argv and argv.index("--close-hold") + 1 < len(argv):
|
if "--close-hold" in argv and argv.index("--close-hold") + 1 < len(argv):
|
||||||
at = argv.index("--close-hold")
|
at = argv.index("--close-hold")
|
||||||
holdFile = argv[at + 1]
|
holdFile = argv[at + 1]
|
||||||
argv = argv[:at] + argv[at + 2:]
|
argv = argv[:at] + argv[at + 2:]
|
||||||
if len(argv) < 4 or argv[1] not in ("tcp-server", "tcp-client", "pty") or (withDtrCheck and argv[1] == "pty") or (holdFile is not None and argv[1] != "pty"):
|
if len(argv) < 4 or argv[1] not in ("tcp-server", "tcp-client", "pty") or (withDtrCheck and argv[1] == "pty") or (holdFile is not None and argv[1] != "pty") or (streamBytes > 0 and (argv[1] == "pty" or withDtrCheck or rate <= 0)):
|
||||||
sys.stderr.write(__doc__)
|
sys.stderr.write(__doc__)
|
||||||
return 2
|
return 2
|
||||||
mode = argv[1]
|
mode = argv[1]
|
||||||
|
|
@ -294,7 +356,12 @@ def main(argv):
|
||||||
return lines.feed(data) if withDtrCheck else data
|
return lines.feed(data) if withDtrCheck else data
|
||||||
|
|
||||||
status, got = loop(conn.sendall, receive, wall, start, banner)
|
status, got = loop(conn.sendall, receive, wall, start, banner)
|
||||||
if status != 0 or not withDtrCheck:
|
if status != 0:
|
||||||
|
return status
|
||||||
|
if streamBytes > 0:
|
||||||
|
conn.settimeout(STREAM_POLL_SECONDS)
|
||||||
|
return stream(conn.sendall, receive, wall, start, streamBytes, rate)
|
||||||
|
if not withDtrCheck:
|
||||||
return status
|
return status
|
||||||
return dtrCheck(conn.sendall, receive, wall, start, lines, got)
|
return dtrCheck(conn.sendall, receive, wall, start, lines, got)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ VOLSIZE="${JOEY_DISK_SIZE:-800KB}"
|
||||||
|
|
||||||
# Priority order: small primitive demos first so DRAW/PATTERN/etc. always fit;
|
# Priority order: small primitive demos first so DRAW/PATTERN/etc. always fit;
|
||||||
# the larger adventures land last and are dropped if the floppy is full.
|
# the larger adventures land last and are dropped if the floppy is full.
|
||||||
order=(${JOEY_DISK_EXAMPLES:-DRAW PATTERN KEYS JOY SPRITE UBER ADV2 ADV})
|
order=(${JOEY_DISK_EXAMPLES:-DRAW PATTERN KEYS JOY SPRITE UBER SCROLL ADV2 ADV})
|
||||||
|
|
||||||
work=$(mktemp -d -t joeylib-disk.XXXXXX)
|
work=$(mktemp -d -t joeylib-disk.XXXXXX)
|
||||||
trap 'rm -rf "$work"' EXIT
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
|
||||||
234
scripts/profile-iigs.sh
Executable file
234
scripts/profile-iigs.sh
Executable file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# profile-iigs.sh - PC-sampling profiler for a JoeyLib program on the IIgs.
|
||||||
|
#
|
||||||
|
# Boots a self-booting program volume (make-iigs-game-disk.sh's) under
|
||||||
|
# headless MAME and samples the 65816's program bank and counter every
|
||||||
|
# PROFILE_PERIOD_US of EMULATED time, from a Lua coroutine that emu.wait()s
|
||||||
|
# between samples. A per-frame sample would land at the same point of a
|
||||||
|
# frame-synced game loop every time; a period that is not a multiple of the
|
||||||
|
# frame's spreads the samples over the whole frame.
|
||||||
|
#
|
||||||
|
# The OMF carries no symbols and the GS/OS Loader puts each bank-aligned
|
||||||
|
# segment in whatever bank it likes, so the samples are tied back to the
|
||||||
|
# link map by the segments' BYTES: at the end the script reads the start of
|
||||||
|
# every bank a sample landed in and scores it against every segment image
|
||||||
|
# (the Loader's relocations change only a few bytes). A bank that matches
|
||||||
|
# no segment is GS/OS, a tool, NTP or the ROM, and is reported by bank.
|
||||||
|
#
|
||||||
|
# The map comes from building the program with JOEY_LINK_MAP=<dir>, which
|
||||||
|
# keeps <dir>/NAME.map, NAME.seg<N>.bin and NAME.json (clang-build.sh):
|
||||||
|
#
|
||||||
|
# rm -f build/iigs/bin/STAXI && JOEY_LINK_MAP=/some/dir make iigs
|
||||||
|
#
|
||||||
|
# The map lists static functions too (JOEY_LINK_MAP links with link816's
|
||||||
|
# --map-locals), but one the compiler inlined has no code of its own: its
|
||||||
|
# time is its caller's row.
|
||||||
|
#
|
||||||
|
# Usage: scripts/profile-iigs.sh <image> <map> [seconds] [skipSeconds]
|
||||||
|
# image the program's boot volume: .po (800KB, the 3.5
|
||||||
|
# drive) or .2mg (anything larger, a CFFA2 card)
|
||||||
|
# map NAME.map, with NAME.seg<N>.bin beside it
|
||||||
|
# seconds emulated seconds to run, default 120
|
||||||
|
# skipSeconds ignore samples before this, default 40 (boot)
|
||||||
|
# Env: PROFILE_PERIOD_US sample period in emulated microseconds,
|
||||||
|
# default 997
|
||||||
|
# PROFILE_TOP rows to print, default 40
|
||||||
|
# PROFILE_RAMSIZE MAME -ramsize, default 4M
|
||||||
|
# PROFILE_SAMPLES a file to keep the raw samples in (S addr
|
||||||
|
# count, F bank bytes, T total), for a look
|
||||||
|
# inside a bank no segment matched
|
||||||
|
# Requires: ./install.sh install iigs.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
img=${1:?usage: profile-iigs.sh <image> <map> [seconds] [skipSeconds]}
|
||||||
|
map=${2:?usage: profile-iigs.sh <image> <map> [seconds] [skipSeconds]}
|
||||||
|
seconds=${3:-120}
|
||||||
|
skip=${4:-40}
|
||||||
|
periodUs=${PROFILE_PERIOD_US:-997}
|
||||||
|
top=${PROFILE_TOP:-40}
|
||||||
|
ramsize=${PROFILE_RAMSIZE:-4M}
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
joeyNeed MAME mame
|
||||||
|
joeyNeed IIGS_ROMPATH iigsFirmware
|
||||||
|
|
||||||
|
[ -f "$img" ] || { echo "profile-iigs: no such image $img" >&2; exit 2; }
|
||||||
|
[ -f "$map" ] || { echo "profile-iigs: no such map $map" >&2; exit 2; }
|
||||||
|
[ -f "${map%.map}.seg1.bin" ] || { echo "profile-iigs: ${map%.map}.seg1.bin missing (build with JOEY_LINK_MAP=<dir>)" >&2; exit 2; }
|
||||||
|
|
||||||
|
# MAME opens a hard-disk image read-write: run a private copy.
|
||||||
|
work=$(mktemp -d -t joeylib-iigsprof.XXXXXX)
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
name=$(basename "$img")
|
||||||
|
cp "$img" "$work/$name"
|
||||||
|
if [ "$(stat -c %s "$img")" -le 900000 ]; then
|
||||||
|
media=(-flop3 "$work/$name")
|
||||||
|
else
|
||||||
|
media=(-sl7 cffa2 -hard1 "$work/$name")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Bytes of a bank compared against a segment's image.
|
||||||
|
fingerprintBytes=512
|
||||||
|
|
||||||
|
cat > "$work/prof.lua" <<LUA
|
||||||
|
local cpu = manager.machine.devices[":maincpu"]
|
||||||
|
local mem = cpu.spaces["program"]
|
||||||
|
local out = io.open("samples.txt", "w")
|
||||||
|
local counts = {}
|
||||||
|
local banks = {}
|
||||||
|
local taken = 0
|
||||||
|
|
||||||
|
local function finish()
|
||||||
|
for addr, n in pairs(counts) do
|
||||||
|
out:write(string.format("S %06X %d\n", addr, n))
|
||||||
|
end
|
||||||
|
for bank, _ in pairs(banks) do
|
||||||
|
local bytes = {}
|
||||||
|
for i = 0, $fingerprintBytes - 1 do
|
||||||
|
bytes[#bytes + 1] = string.format("%02X", mem:read_u8((bank << 16) + i))
|
||||||
|
end
|
||||||
|
out:write(string.format("F %02X %s\n", bank, table.concat(bytes)))
|
||||||
|
end
|
||||||
|
out:write(string.format("T %d\n", taken))
|
||||||
|
out:close()
|
||||||
|
manager.machine:exit()
|
||||||
|
end
|
||||||
|
|
||||||
|
local sampler = coroutine.create(function()
|
||||||
|
while manager.machine.time:as_double() < $seconds do
|
||||||
|
emu.wait($periodUs / 1000000.0)
|
||||||
|
if manager.machine.time:as_double() >= $skip then
|
||||||
|
local pb = cpu.state["PB"].value
|
||||||
|
local addr = (pb << 16) | cpu.state["PC"].value
|
||||||
|
counts[addr] = (counts[addr] or 0) + 1
|
||||||
|
banks[pb] = true
|
||||||
|
taken = taken + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
finish()
|
||||||
|
end)
|
||||||
|
|
||||||
|
local started = false
|
||||||
|
emu.register_frame_done(function()
|
||||||
|
if not started then
|
||||||
|
started = true
|
||||||
|
local ok, err = coroutine.resume(sampler)
|
||||||
|
if not ok then
|
||||||
|
io.write("PROFILE-IIGS lua error: " .. tostring(err) .. "\n")
|
||||||
|
manager.machine:exit()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
LUA
|
||||||
|
|
||||||
|
echo "profile-iigs: $name, ${seconds}s emulated (samples from ${skip}s, every ${periodUs} us)..." >&2
|
||||||
|
(cd "$work" && QT_QPA_PLATFORM=offscreen SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||||
|
timeout 3600 "$MAME" apple2gs \
|
||||||
|
-ramsize "$ramsize" -rompath "$IIGS_ROMPATH" "${media[@]}" \
|
||||||
|
-video none -sound none -nothrottle -skip_gameinfo \
|
||||||
|
-autoboot_script "$work/prof.lua" </dev/null >"$work/mame.log" 2>&1) || true
|
||||||
|
if [ ! -s "$work/samples.txt" ]; then
|
||||||
|
echo "profile-iigs: FAIL -- no samples (MAME output follows)" >&2
|
||||||
|
tail -15 "$work/mame.log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep 'PROFILE-IIGS' "$work/mame.log" >&2 || true
|
||||||
|
if [ -n "${PROFILE_SAMPLES:-}" ]; then
|
||||||
|
cp "$work/samples.txt" "$PROFILE_SAMPLES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$work/samples.txt" "$map" "$top" <<'PY'
|
||||||
|
import bisect
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
samplesPath, mapPath, top = sys.argv[1], sys.argv[2], int(sys.argv[3])
|
||||||
|
mapBase = mapPath[:-4] if mapPath.endswith('.map') else mapPath
|
||||||
|
|
||||||
|
# The segments: number -> (link bank, image bytes).
|
||||||
|
segments = {}
|
||||||
|
manifest = mapBase + '.json'
|
||||||
|
if os.path.exists(manifest):
|
||||||
|
for seg in json.load(open(manifest))['segments']:
|
||||||
|
segments[seg['num']] = int(seg['base'], 16) >> 16
|
||||||
|
else:
|
||||||
|
segments[1] = 0
|
||||||
|
images = {num: open('%s.seg%d.bin' % (mapBase, num), 'rb').read() for num in segments}
|
||||||
|
|
||||||
|
# The symbols, by address: the globals, and the locals (static functions)
|
||||||
|
# when the map has them; a global wins an address both name.
|
||||||
|
byAddr = {}
|
||||||
|
section = None
|
||||||
|
for line in open(mapPath):
|
||||||
|
if line.startswith('#'):
|
||||||
|
section = 'global' if line.startswith('# global symbols') else ('local' if line.startswith('# local symbols') else None)
|
||||||
|
continue
|
||||||
|
m = re.match(r'^0x([0-9a-fA-F]{6})\s+(\S+)$', line.strip())
|
||||||
|
if m and section is not None:
|
||||||
|
addr = int(m.group(1), 16)
|
||||||
|
if section == 'global' or addr not in byAddr:
|
||||||
|
byAddr[addr] = m.group(2)
|
||||||
|
syms = sorted(byAddr.items())
|
||||||
|
symAddrs = [a for a, _ in syms]
|
||||||
|
|
||||||
|
counts = {}
|
||||||
|
fingerprints = {}
|
||||||
|
total = 0
|
||||||
|
for line in open(samplesPath):
|
||||||
|
parts = line.split()
|
||||||
|
if parts[0] == 'S':
|
||||||
|
counts[int(parts[1], 16)] = int(parts[2])
|
||||||
|
elif parts[0] == 'F':
|
||||||
|
fingerprints[int(parts[1], 16)] = bytes.fromhex(parts[2])
|
||||||
|
elif parts[0] == 'T':
|
||||||
|
total = int(parts[1])
|
||||||
|
|
||||||
|
# Runtime bank -> link bank, by the best-scoring segment image.
|
||||||
|
bankMap = {}
|
||||||
|
for bank, fp in fingerprints.items():
|
||||||
|
best = None
|
||||||
|
for num, image in images.items():
|
||||||
|
n = min(len(fp), len(image))
|
||||||
|
if n < 16:
|
||||||
|
continue
|
||||||
|
same = sum(1 for i in range(n) if fp[i] == image[i])
|
||||||
|
score = same / n
|
||||||
|
if best is None or score > best[0]:
|
||||||
|
best = (score, num)
|
||||||
|
if best is not None and best[0] >= 0.85:
|
||||||
|
bankMap[bank] = (segments[best[1]], best[1], best[0])
|
||||||
|
|
||||||
|
def where(addr):
|
||||||
|
bank = addr >> 16
|
||||||
|
if bank in bankMap:
|
||||||
|
link = (bankMap[bank][0] << 16) | (addr & 0xFFFF)
|
||||||
|
i = bisect.bisect_right(symAddrs, link) - 1
|
||||||
|
if i >= 0 and (symAddrs[i] >> 16) == (link >> 16):
|
||||||
|
return syms[i][1]
|
||||||
|
return 'seg%d+%04X' % (bankMap[bank][1], addr & 0xFFFF)
|
||||||
|
if bank >= 0xFC:
|
||||||
|
return 'ROM $%02X' % bank
|
||||||
|
if bank in (0xE0, 0xE1):
|
||||||
|
return 'bank $%02X (firmware/vectors)' % bank
|
||||||
|
return 'bank $%02X (GS/OS, tools, NTP)' % bank
|
||||||
|
|
||||||
|
byName = {}
|
||||||
|
for addr, n in counts.items():
|
||||||
|
key = where(addr)
|
||||||
|
byName[key] = byName.get(key, 0) + n
|
||||||
|
|
||||||
|
print('profile-iigs: %d samples' % total)
|
||||||
|
for bank in sorted(bankMap):
|
||||||
|
link, num, score = bankMap[bank]
|
||||||
|
print(' bank $%02X = segment %d (link bank $%02X, %.0f%% of %d bytes match)' % (bank, num, link, score * 100, len(fingerprints[bank])))
|
||||||
|
print('%7s %6s %s' % ('samples', 'pct', 'where'))
|
||||||
|
for key, n in sorted(byName.items(), key=lambda kv: -kv[1])[:top]:
|
||||||
|
print('%7d %5.1f%% %s' % (n, 100.0 * n / max(total, 1), key))
|
||||||
|
PY
|
||||||
|
|
@ -16,12 +16,12 @@
|
||||||
# separates them is that Human68k RELOCATES the copy it runs: at a known
|
# separates them is that Human68k RELOCATES the copy it runs: at a known
|
||||||
# relocation site the running image holds fileValue + base, while the disk
|
# relocation site the running image holds fileValue + base, while the disk
|
||||||
# buffer still holds fileValue. From then on
|
# buffer still holds fileValue. From then on
|
||||||
# it samples the PC once per emulated frame, buckets by offset into .text, and
|
# it samples the PC every PROFILE_PERIOD_US of emulated time (a Lua coroutine
|
||||||
# attributes the buckets to functions with nm.
|
# that emu.wait()s between samples; a period that is not a multiple of the
|
||||||
#
|
# frame's spreads them over the whole frame, where a sample a frame landed on
|
||||||
# Sampling once per frame is coarse (55 Hz), but this port runs a game frame in
|
# the same instructions every time), buckets by offset into .text, and
|
||||||
# 150 ms or more, so each frame still gets several samples and the shape of the
|
# attributes the buckets to functions with nm. Read it as "which functions
|
||||||
# profile is clear. Read it as "which functions dominate", never as a timing.
|
# dominate", never as a timing.
|
||||||
#
|
#
|
||||||
# Usage: scripts/profile-x68000.sh program [seconds] [skipSeconds]
|
# Usage: scripts/profile-x68000.sh program [seconds] [skipSeconds]
|
||||||
# program the built .X: its path (a project's, say), or an
|
# program the built .X: its path (a project's, say), or an
|
||||||
|
|
@ -29,12 +29,16 @@
|
||||||
# seconds emulated seconds to run, default 240
|
# seconds emulated seconds to run, default 240
|
||||||
# skipSeconds ignore samples before this, default 130 (boot + menu)
|
# skipSeconds ignore samples before this, default 130 (boot + menu)
|
||||||
# Env: X68K_DIR dir holding HUMAN302.XDF + roms/ (default: the installer's toolchains/firmware/x68000)
|
# Env: X68K_DIR dir holding HUMAN302.XDF + roms/ (default: the installer's toolchains/firmware/x68000)
|
||||||
|
# PROFILE_PERIOD_US sample period in emulated microseconds, default 997
|
||||||
|
# PROFILE_SAMPLES a file to keep the raw samples in (offset count
|
||||||
|
# lines, -1 outside the program; the load base)
|
||||||
# Requires: the program built WITH symbols (<NAME>.X.elf beside it).
|
# Requires: the program built WITH symbols (<NAME>.X.elf beside it).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
prog=${1:?usage: profile-x68000.sh program [seconds] [skipSeconds]}
|
prog=${1:?usage: profile-x68000.sh program [seconds] [skipSeconds]}
|
||||||
seconds=${2:-240}
|
seconds=${2:-240}
|
||||||
skip=${3:-130}
|
skip=${3:-130}
|
||||||
|
periodUs=${PROFILE_PERIOD_US:-997}
|
||||||
|
|
||||||
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
source "$repo/scripts/lib/joeyEnv.sh"
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
|
@ -66,9 +70,9 @@ o = 64 + $relocSite
|
||||||
print(int.from_bytes(d[o:o+4],'big'))
|
print(int.from_bytes(d[o:o+4],'big'))
|
||||||
")
|
")
|
||||||
|
|
||||||
# gMouseX is written once per input poll, i.e. once per game frame.
|
# jlgMouseX is written once per input poll, i.e. once per game frame.
|
||||||
mouseXOff=$(m68k-xelf-nm "$elf" | awk '$3 == "gMouseX" { print strtonum("0x" $1); exit }')
|
mouseXOff=$(m68k-xelf-nm "$elf" | awk '$3 == "jlgMouseX" { print strtonum("0x" $1); exit }')
|
||||||
[[ -n ${mouseXOff:-} ]] || { echo "profile-x68000: gMouseX not found; cannot count frames" >&2; exit 2; }
|
[[ -n ${mouseXOff:-} ]] || { echo "profile-x68000: jlgMouseX not found; cannot count frames" >&2; exit 2; }
|
||||||
|
|
||||||
work=$(mktemp -d -t joey-x68prof.XXXXXX)
|
work=$(mktemp -d -t joey-x68prof.XXXXXX)
|
||||||
trap 'rm -rf "$work"' EXIT
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
@ -155,6 +159,46 @@ local function pickBase()
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The samples come every PROFILE_PERIOD_US of emulated time, from a
|
||||||
|
-- coroutine that emu.wait()s between them, not once a frame: a frame-synced
|
||||||
|
-- game loop puts the same instructions under every frame's sample, so a
|
||||||
|
-- short routine came out as a few hot addresses or none at all.
|
||||||
|
local samplerStarted = false
|
||||||
|
local sampler = coroutine.create(function()
|
||||||
|
while true do
|
||||||
|
emu.wait($periodUs / 1000000.0)
|
||||||
|
local t = manager.machine.time:as_double()
|
||||||
|
if t >= skipUntil then
|
||||||
|
local pc = cpu.state[pcReg].value
|
||||||
|
local key
|
||||||
|
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:
|
||||||
|
-- that is how a vblank wait came out looking like an
|
||||||
|
-- expensive input poll.
|
||||||
|
key = pc - base
|
||||||
|
else
|
||||||
|
key = -1 -- ROM, IOCS, anything not ours
|
||||||
|
end
|
||||||
|
buckets[key] = (buckets[key] or 0) + 1
|
||||||
|
total = total + 1
|
||||||
|
end
|
||||||
|
if t >= stopAt then
|
||||||
|
out:write(string.format("fps %.2f frames %d window %.1f tapAt %08X\n",
|
||||||
|
(t > fpsStart) and (frames / (t - fpsStart)) or 0.0, frames, t - fpsStart, base + mouseXOff))
|
||||||
|
out:write(string.format("total %d\n", total))
|
||||||
|
for k, v in pairs(buckets) do
|
||||||
|
out:write(string.format("%d %d\n", k, v))
|
||||||
|
end
|
||||||
|
out:write("DONE\n")
|
||||||
|
out:close()
|
||||||
|
manager.machine:exit()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
emu.register_frame_done(function()
|
emu.register_frame_done(function()
|
||||||
local t = manager.machine.time:as_double()
|
local t = manager.machine.time:as_double()
|
||||||
if base == nil then
|
if base == nil then
|
||||||
|
|
@ -166,7 +210,7 @@ emu.register_frame_done(function()
|
||||||
if base ~= nil then
|
if base ~= nil then
|
||||||
out:write(string.format("base %08X at %.1fs (of %d candidates)\n", base, t, #cands))
|
out:write(string.format("base %08X at %.1fs (of %d candidates)\n", base, t, #cands))
|
||||||
out:flush()
|
out:flush()
|
||||||
-- gMouseX is written once per jlpInputPoll, which the game calls
|
-- jlgMouseX is written once per jlpInputPoll, which the game calls
|
||||||
-- once a frame, so counting writes to it counts game frames. That
|
-- once a frame, so counting writes to it counts game frames. That
|
||||||
-- turns "how much of the frame is X" into "and how long is a
|
-- turns "how much of the frame is X" into "and how long is a
|
||||||
-- frame", which is the difference between a real speedup and one
|
-- frame", which is the difference between a real speedup and one
|
||||||
|
|
@ -186,31 +230,15 @@ emu.register_frame_done(function()
|
||||||
scanDone = false
|
scanDone = false
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if t < skipUntil then return end
|
if not samplerStarted then
|
||||||
local pc = cpu.state[pcReg].value
|
samplerStarted = true
|
||||||
local key
|
local ok, err = coroutine.resume(sampler)
|
||||||
if pc >= base and pc < base + textSize then
|
if not ok then
|
||||||
-- The EXACT offset, not a 64-byte bucket. Buckets straddle function
|
out:write("sampler error: " .. tostring(err) .. "\n")
|
||||||
-- boundaries, and a bucket credited to the symbol at its start hands
|
|
||||||
-- one function's samples to its neighbour: that is how a vblank wait
|
|
||||||
-- came out looking like an expensive input poll.
|
|
||||||
key = pc - base
|
|
||||||
else
|
|
||||||
key = -1 -- ROM, IOCS, anything not ours
|
|
||||||
end
|
|
||||||
buckets[key] = (buckets[key] or 0) + 1
|
|
||||||
total = total + 1
|
|
||||||
if t >= stopAt then
|
|
||||||
out:write(string.format("fps %.2f frames %d window %.1f tapAt %08X\n",
|
|
||||||
(t > fpsStart) and (frames / (t - fpsStart)) or 0.0, frames, t - fpsStart, base + mouseXOff))
|
|
||||||
out:write(string.format("total %d\n", total))
|
|
||||||
for k, v in pairs(buckets) do
|
|
||||||
out:write(string.format("%d %d\n", k, v))
|
|
||||||
end
|
|
||||||
out:write("DONE\n")
|
|
||||||
out:close()
|
out:close()
|
||||||
manager.machine:exit()
|
manager.machine:exit()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
LUA
|
LUA
|
||||||
|
|
||||||
|
|
@ -222,6 +250,9 @@ LUA
|
||||||
|
|
||||||
[[ -s $work/prof.txt ]] || { echo "profile-x68000: FAIL (no samples)" >&2; exit 1; }
|
[[ -s $work/prof.txt ]] || { echo "profile-x68000: FAIL (no samples)" >&2; exit 1; }
|
||||||
grep -q '^base ' "$work/prof.txt" || { echo "profile-x68000: FAIL (never found the load base)" >&2; exit 1; }
|
grep -q '^base ' "$work/prof.txt" || { echo "profile-x68000: FAIL (never found the load base)" >&2; exit 1; }
|
||||||
|
if [[ -n ${PROFILE_SAMPLES:-} ]]; then
|
||||||
|
cp "$work/prof.txt" "$PROFILE_SAMPLES"
|
||||||
|
fi
|
||||||
|
|
||||||
m68k-xelf-nm -n "$elf" > "$work/syms.txt"
|
m68k-xelf-nm -n "$elf" > "$work/syms.txt"
|
||||||
python3 - "$work/prof.txt" "$work/syms.txt" <<'PY'
|
python3 - "$work/prof.txt" "$work/syms.txt" <<'PY'
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@
|
||||||
# template (blank), file roots, the dirty-band marker, the line
|
# template (blank), file roots, the dirty-band marker, the line
|
||||||
# and circle fallbacks at the far ends of their coordinates, input,
|
# and circle fallbacks at the far ends of their coordinates, input,
|
||||||
# millis, the module player the software-mixing ports share,
|
# millis, the module player the software-mixing ports share,
|
||||||
# save, serial, sfxmix, tilebank, and the VGM converter (with
|
# save, serial, sfxmix, tilebank, tile layers against their maps,
|
||||||
# Furnace installed, on its demo songs too)
|
# the Tiled map importer, and the VGM converter (with Furnace
|
||||||
# golden UBER on each port, its 51 per-op surface hashes against
|
# installed, on its demo songs too)
|
||||||
|
# golden UBER on each port, its 54 per-op surface hashes against
|
||||||
# tests/goldens/uber (x68000 against the IIgs's, its own gate;
|
# tests/goldens/uber (x68000 against the IIgs's, its own gate;
|
||||||
# dvx, sdl3, web, win32, win16, psp, haiku, haiku32 and beos
|
# dvx, sdl3, web, win32, win16, psp, haiku, haiku32 and beos
|
||||||
# against the DOS port's, whose chunky stage they share; psp under
|
# against the DOS port's, whose chunky stage they share; psp under
|
||||||
|
|
@ -31,6 +32,13 @@
|
||||||
# which build runs where -- the STe build refuses a plain ST and
|
# which build runs where -- the STe build refuses a plain ST and
|
||||||
# a Mega ST through jlLastError, the ST build runs on an STe
|
# a Mega ST through jlLastError, the ST build runs on an STe
|
||||||
# visual iigs: every launchable example on joey.2mg boots and draws
|
# visual iigs: every launchable example on joey.2mg boots and draws
|
||||||
|
# iigs: also the display -- UBER's probe build's runs of draws,
|
||||||
|
# each presented, the $E1 pixels then equal to the stage, every
|
||||||
|
# dirty band clean again, and the SCB and palette blocks the
|
||||||
|
# stage's (make iigs-verify-shrtail)
|
||||||
|
# x68000 and amiga: the display -- UBER's probe build's battery
|
||||||
|
# of draws, each presented, the page or planes shown checked
|
||||||
|
# against the stage's
|
||||||
# dvx: full screen -- UBER's hashes through a suspend and a
|
# dvx: full screen -- UBER's hashes through a suspend and a
|
||||||
# resume, and KEYS's row read off the 320x200 VGA screen, then
|
# resume, and KEYS's row read off the 320x200 VGA screen, then
|
||||||
# in its window, then on the whole screen again
|
# in its window, then on the whole screen again
|
||||||
|
|
@ -63,6 +71,12 @@
|
||||||
# the port's VM)
|
# the port's VM)
|
||||||
# (dvx: and the present shows it, decoded from the desktop, also
|
# (dvx: and the present shows it, decoded from the desktop, also
|
||||||
# at 8/15/32 bpp and 2x/3x; Ctrl+Esc must not end the program)
|
# at 8/15/32 bpp and 2x/3x; Ctrl+Esc must not end the program)
|
||||||
|
# (iigs: and its asm key predicates -- examples/keys lights A's
|
||||||
|
# cell only while A is held, jlKeyDown, and Escape quits it,
|
||||||
|
# jlKeyPressed)
|
||||||
|
# (dos: and the mouse through the driver's INT 33h event
|
||||||
|
# callback -- moves both ways, all four edges, every column, both
|
||||||
|
# buttons)
|
||||||
# joy (every port but psp, whose input gate covers it, and
|
# joy (every port but psp, whose input gate covers it, and
|
||||||
# haiku, haiku32 and beos, as QEMU's PC has no joystick to give
|
# haiku, haiku32 and beos, as QEMU's PC has no joystick to give
|
||||||
# them; win16 through KPJOY.DRV, JoeyLib's own joystick driver for
|
# them; win16 through KPJOY.DRV, JoeyLib's own joystick driver for
|
||||||
|
|
@ -147,9 +161,14 @@
|
||||||
# modem port at 9600 and a Super Serial Card at 2400
|
# modem port at 9600 and a Super Serial Card at 2400
|
||||||
# x68000: both serial streams byte-exact and the mouse right while
|
# x68000: both serial streams byte-exact and the mouse right while
|
||||||
# the SCB changes palette on every line of a run (9600, 19200, 38400)
|
# the SCB changes palette on every line of a run (9600, 19200, 38400)
|
||||||
# disk iigs: a one-program boot volume (DRAW's) boots, launches the
|
# disk programs packaged to ship:
|
||||||
# program and draws: as a 2048KB volume off a CFFA2 card, then as
|
# iigs a one-program boot volume (DRAW's) boots, launches the
|
||||||
# an 800KB floppy on ROM 3 and on ROM 01
|
# program and draws: as a 2048KB volume off a CFFA2
|
||||||
|
# card, then as an 800KB floppy on ROM 3 and on ROM 01
|
||||||
|
# macos scripts/check-macos-apps.sh: every app is one macOS
|
||||||
|
# opens -- universal (Apple silicon and Intel), the Apple
|
||||||
|
# silicon half signed, for macOS 13 on, its Info.plist,
|
||||||
|
# program and icon in place (no Mac runs it here)
|
||||||
# icon a program's icon (DRAW's), where its system shows it:
|
# icon a program's icon (DRAW's), where its system shows it:
|
||||||
# host scripts/check-icons.sh: every built port's DRAW carries
|
# host scripts/check-icons.sh: every built port's DRAW carries
|
||||||
# its baked icon where the system looks (no emulator)
|
# its baked icon where the system looks (no emulator)
|
||||||
|
|
@ -191,8 +210,8 @@
|
||||||
#
|
#
|
||||||
# Usage: scripts/verify-all.sh KIND|all [port ...]
|
# Usage: scripts/verify-all.sh KIND|all [port ...]
|
||||||
# with no ports, every port the kind has. Ports: host iigs
|
# with no ports, every port the kind has. Ports: host iigs
|
||||||
# x68000 atarist ste amiga dos dvx sdl3 web win32 win16 psp
|
# x68000 atarist ste amiga dos dvx sdl3 web macos win32 win16
|
||||||
# haiku haiku32 beos
|
# psp haiku haiku32 beos
|
||||||
# Env: VERIFY_ALL_LOG also append the full transcript to this file
|
# Env: VERIFY_ALL_LOG also append the full transcript to this file
|
||||||
# Requires: whatever each selected gate requires; missing ones SKIP.
|
# Requires: whatever each selected gate requires; missing ones SKIP.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
@ -203,7 +222,7 @@ shift || true
|
||||||
label="verify-all $kind"
|
label="verify-all $kind"
|
||||||
|
|
||||||
KINDS="host golden visual tilebank input audio conform serial mouse save loss disk icon"
|
KINDS="host golden visual tilebank input audio conform serial mouse save loss disk icon"
|
||||||
ALL_PORTS="host iigs x68000 atarist ste amiga dos dvx sdl3 web win32 win16 psp haiku haiku32 beos"
|
ALL_PORTS="host iigs x68000 atarist ste amiga dos dvx sdl3 web macos win32 win16 psp haiku haiku32 beos"
|
||||||
# UBER on the Amiga needs longer than bench-amiga.sh's interactive default.
|
# UBER on the Amiga needs longer than bench-amiga.sh's interactive default.
|
||||||
AMIGA_UBER_SECONDS=400
|
AMIGA_UBER_SECONDS=400
|
||||||
|
|
||||||
|
|
@ -220,6 +239,8 @@ host host save scripts/check-save.sh
|
||||||
host host serial scripts/check-serial.sh
|
host host serial scripts/check-serial.sh
|
||||||
host host sfxmix scripts/check-sfxmix.sh
|
host host sfxmix scripts/check-sfxmix.sh
|
||||||
host host tilebank scripts/check-tilebank.sh
|
host host tilebank scripts/check-tilebank.sh
|
||||||
|
host host layer scripts/check-tilelayer.sh
|
||||||
|
host host tiled scripts/check-tiled.sh
|
||||||
host host vgm scripts/check-vgm.sh
|
host host vgm scripts/check-vgm.sh
|
||||||
golden iigs uber scripts/bench-iigs.sh && tools/diff-uber-hashes tests/goldens/uber/iigs.txt build/iigs/bin/joeylog.txt
|
golden iigs uber scripts/bench-iigs.sh && tools/diff-uber-hashes tests/goldens/uber/iigs.txt build/iigs/bin/joeylog.txt
|
||||||
golden x68000 uber scripts/verify-x68000-golden.sh
|
golden x68000 uber scripts/verify-x68000-golden.sh
|
||||||
|
|
@ -242,6 +263,7 @@ golden haiku uber scripts/bench-be.sh haiku && tools/diff-uber-hashes te
|
||||||
golden haiku32 uber scripts/bench-be.sh haiku32 && tools/diff-uber-hashes tests/goldens/uber/dos.txt build/haiku32/bin/joeylog.txt
|
golden haiku32 uber scripts/bench-be.sh haiku32 && tools/diff-uber-hashes tests/goldens/uber/dos.txt build/haiku32/bin/joeylog.txt
|
||||||
golden beos uber scripts/bench-be.sh beos && tools/diff-uber-hashes tests/goldens/uber/dos.txt build/beos/bin/joeylog.txt
|
golden beos uber scripts/bench-be.sh beos && tools/diff-uber-hashes tests/goldens/uber/dos.txt build/beos/bin/joeylog.txt
|
||||||
visual iigs examples scripts/verify-iigs-all.sh
|
visual iigs examples scripts/verify-iigs-all.sh
|
||||||
|
visual iigs display make -s -f make/iigs.mk iigs-verify-shrtail || exit 1
|
||||||
visual dvx full scripts/verify-dvx-fullscreen.sh
|
visual dvx full scripts/verify-dvx-fullscreen.sh
|
||||||
visual dvx display scripts/verify-dvx-display.sh
|
visual dvx display scripts/verify-dvx-display.sh
|
||||||
visual win16 display scripts/verify-win16-display.sh
|
visual win16 display scripts/verify-win16-display.sh
|
||||||
|
|
@ -250,12 +272,16 @@ visual haiku display scripts/verify-be-display.sh haiku
|
||||||
visual haiku32 display scripts/verify-be-display.sh haiku32
|
visual haiku32 display scripts/verify-be-display.sh haiku32
|
||||||
visual beos display scripts/verify-be-display.sh beos
|
visual beos display scripts/verify-be-display.sh beos
|
||||||
visual x68000 scb scripts/verify-x68000-scb.sh
|
visual x68000 scb scripts/verify-x68000-scb.sh
|
||||||
|
visual x68000 display scripts/verify-x68000-display.sh
|
||||||
visual atarist palette scripts/verify-atarist-palette.sh st
|
visual atarist palette scripts/verify-atarist-palette.sh st
|
||||||
visual ste palette scripts/verify-atarist-palette.sh ste megaste
|
visual ste palette scripts/verify-atarist-palette.sh ste megaste
|
||||||
|
visual atarist display scripts/verify-atarist-display.sh st
|
||||||
|
visual ste display scripts/verify-atarist-display.sh ste megaste
|
||||||
visual amiga palette scripts/verify-amiga-palette.sh
|
visual amiga palette scripts/verify-amiga-palette.sh
|
||||||
visual amiga a1200pal JOEY_AMIGA_MODEL=A1200 scripts/verify-amiga-palette.sh
|
visual amiga a1200pal JOEY_AMIGA_MODEL=A1200 scripts/verify-amiga-palette.sh
|
||||||
visual amiga a4000pal JOEY_AMIGA_MODEL=A4000 scripts/verify-amiga-palette.sh
|
visual amiga a4000pal JOEY_AMIGA_MODEL=A4000 scripts/verify-amiga-palette.sh
|
||||||
visual amiga fastram scripts/verify-amiga-fastram.sh
|
visual amiga fastram scripts/verify-amiga-fastram.sh
|
||||||
|
visual amiga display scripts/verify-amiga-display.sh
|
||||||
tilebank iigs tilebank make -s -f make/iigs.mk iigs-verify-tilebank || exit 1
|
tilebank iigs tilebank make -s -f make/iigs.mk iigs-verify-tilebank || exit 1
|
||||||
tilebank x68000 tilebank scripts/verify-x68000-tilebank.sh
|
tilebank x68000 tilebank scripts/verify-x68000-tilebank.sh
|
||||||
tilebank atarist tilebank scripts/verify-atarist-tilebank.sh
|
tilebank atarist tilebank scripts/verify-atarist-tilebank.sh
|
||||||
|
|
@ -268,6 +294,7 @@ tilebank win32 tilebank scripts/verify-win32-tilebank.sh
|
||||||
tilebank win16 tilebank scripts/verify-win16-tilebank.sh
|
tilebank win16 tilebank scripts/verify-win16-tilebank.sh
|
||||||
tilebank psp tilebank scripts/verify-psp-tilebank.sh
|
tilebank psp tilebank scripts/verify-psp-tilebank.sh
|
||||||
input iigs input scripts/verify-iigs-input.sh
|
input iigs input scripts/verify-iigs-input.sh
|
||||||
|
input iigs keys scripts/verify-iigs-keys.sh
|
||||||
input x68000 input scripts/verify-x68000-input.sh
|
input x68000 input scripts/verify-x68000-input.sh
|
||||||
input x68000 joy scripts/verify-x68000-joy.sh
|
input x68000 joy scripts/verify-x68000-joy.sh
|
||||||
input iigs joy scripts/verify-iigs-joy.sh
|
input iigs joy scripts/verify-iigs-joy.sh
|
||||||
|
|
@ -277,6 +304,7 @@ input ste joy scripts/verify-atarist-joy.sh ste
|
||||||
input amiga input scripts/verify-amiga-input.sh
|
input amiga input scripts/verify-amiga-input.sh
|
||||||
input amiga joy scripts/verify-amiga-joy.sh
|
input amiga joy scripts/verify-amiga-joy.sh
|
||||||
input dos input scripts/verify-dos-input.sh
|
input dos input scripts/verify-dos-input.sh
|
||||||
|
input dos mouse scripts/verify-dos-mouse.sh
|
||||||
input dos joy scripts/verify-dos-joy.sh
|
input dos joy scripts/verify-dos-joy.sh
|
||||||
input dvx input scripts/verify-dvx-input.sh
|
input dvx input scripts/verify-dvx-input.sh
|
||||||
input dvx depths scripts/verify-dvx-input.sh 640x480x8:1 800x600x15:2 1024x768x32:3
|
input dvx depths scripts/verify-dvx-input.sh 640x480x8:1 800x600x15:2 1024x768x32:3
|
||||||
|
|
@ -329,6 +357,7 @@ serial dos serial scripts/verify-dos-serial.sh
|
||||||
serial dvx serial scripts/verify-dvx-serial.sh
|
serial dvx serial scripts/verify-dvx-serial.sh
|
||||||
serial win32 serial scripts/verify-win32-serial.sh
|
serial win32 serial scripts/verify-win32-serial.sh
|
||||||
serial win16 serial scripts/verify-win16-serial.sh
|
serial win16 serial scripts/verify-win16-serial.sh
|
||||||
|
serial win16 fast scripts/verify-win16-serial-fast.sh
|
||||||
serial sdl3 serial scripts/verify-sdl3-serial.sh
|
serial sdl3 serial scripts/verify-sdl3-serial.sh
|
||||||
serial haiku serial scripts/verify-be-serial.sh haiku
|
serial haiku serial scripts/verify-be-serial.sh haiku
|
||||||
serial haiku32 serial scripts/verify-be-serial.sh haiku32
|
serial haiku32 serial scripts/verify-be-serial.sh haiku32
|
||||||
|
|
@ -345,6 +374,7 @@ loss x68000 scb scripts/verify-x68000-serial-scb.sh
|
||||||
disk iigs cffa2 make -s -f make/iigs.mk iigs-verify-game-disk GAME=DRAW GAME_DISK_SIZE=2048KB || exit 1
|
disk iigs cffa2 make -s -f make/iigs.mk iigs-verify-game-disk GAME=DRAW GAME_DISK_SIZE=2048KB || exit 1
|
||||||
disk iigs floppy make -s -f make/iigs.mk iigs-verify-game-disk GAME=DRAW || exit 1
|
disk iigs floppy make -s -f make/iigs.mk iigs-verify-game-disk GAME=DRAW || exit 1
|
||||||
disk iigs rom01 GAME_VERIFY_MACHINE=apple2gsr1 scripts/verify-iigs-game-disk.sh build/iigs/bin/DRAW.2mg
|
disk iigs rom01 GAME_VERIFY_MACHINE=apple2gsr1 scripts/verify-iigs-game-disk.sh build/iigs/bin/DRAW.2mg
|
||||||
|
disk macos apps scripts/check-macos-apps.sh
|
||||||
icon host icons scripts/check-icons.sh
|
icon host icons scripts/check-icons.sh
|
||||||
icon iigs finder scripts/verify-iigs-icon.sh
|
icon iigs finder scripts/verify-iigs-icon.sh
|
||||||
icon amiga icon scripts/verify-amiga-icon.sh
|
icon amiga icon scripts/verify-amiga-icon.sh
|
||||||
|
|
|
||||||
39
scripts/verify-amiga-display.sh
Executable file
39
scripts/verify-amiga-display.sh
Executable file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/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-amiga-display.sh - the Amiga's present, checked on the planes the
|
||||||
|
# copper shows. UBER's golden hashes are of the stage; a present that leaves
|
||||||
|
# stale rows on screen passes them. The display flips between two plane
|
||||||
|
# sets: each present draws into the one not shown, which holds the frame
|
||||||
|
# before last, from the union of two frames' dirty bands
|
||||||
|
# (src/amiga/presentScan.s). UBERPROB (UBER with UBER_PROBE,
|
||||||
|
# make/examples.mk) runs its Amiga display row (op_probePresentPageSum): a
|
||||||
|
# battery of draws, each presented and the shown planes summed against the
|
||||||
|
# stage's (jlAmigaProbePresentPageSums in src/amiga/hal.c). It must PASS.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-amiga-display.sh
|
||||||
|
# Exit: 0 PASS, 1 FAIL
|
||||||
|
# Requires: FS-UAE and a Kickstart (install.sh install emulator:amiga); it
|
||||||
|
# builds the probe (make -f make/amiga.mk amiga-uberprob).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
label=verify-amiga-display
|
||||||
|
dir=$repo/build/amiga/probe
|
||||||
|
|
||||||
|
if ! make -s -f "$repo/make/amiga.mk" amiga-uberprob; then
|
||||||
|
echo "$label: FAIL - building the UBERPROB failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f "$dir/joeylog.txt"
|
||||||
|
BENCH_PROG=Uberprob BENCH_BIN_DIR="$dir" TIMEOUT="${TIMEOUT:-300}" "$repo/scripts/bench-amiga.sh" > /dev/null 2>&1
|
||||||
|
line=$(grep "UBER-PROBE: presentPageSum" "$dir/joeylog.txt" 2>/dev/null)
|
||||||
|
if [[ "$line" == *"presentPageSum PASS"* ]]; then
|
||||||
|
echo "$label: PASS - ${line#UBER-PROBE: }"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "$label: FAIL - ${line:-no presentPageSum line in its log}" >&2
|
||||||
|
exit 1
|
||||||
56
scripts/verify-atarist-display.sh
Executable file
56
scripts/verify-atarist-display.sh
Executable file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/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-atarist-display.sh - the ST family's present, checked on the page
|
||||||
|
# the shifter shows. UBER's golden hashes are of the stage; a present that
|
||||||
|
# leaves stale rows on screen passes them. UBERPROB (UBER with UBER_PROBE,
|
||||||
|
# make/examples.mk) runs its display row (op_probeDisplayCheck): a tall band
|
||||||
|
# presented, then a small rect inside it, then a settle present and an idle
|
||||||
|
# one, comparing the shown page with the stage's planes after each
|
||||||
|
# (jlpProbeDisplayMismatch in src/atarist/hal.c). Every count must be 0.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-atarist-display.sh [MACHINE...] (default: st ste)
|
||||||
|
# st and megast run the ST build, ste and megaste the STe build.
|
||||||
|
# Exit: 0 PASS, 1 FAIL, 2 SKIP (Hatari or EmuTOS not installed)
|
||||||
|
# Requires: Hatari + EmuTOS (install.sh install emulator:atarist); it builds
|
||||||
|
# the probe (make PLATFORM-uberprob).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
joeyNeed HATARI hatari
|
||||||
|
joeyNeed EMUTOS_IMG emutos
|
||||||
|
|
||||||
|
label=verify-atarist-display
|
||||||
|
machines=("$@")
|
||||||
|
[ ${#machines[@]} -gt 0 ] || machines=(st ste)
|
||||||
|
failed=0
|
||||||
|
for machine in "${machines[@]}"; do
|
||||||
|
case $machine in
|
||||||
|
st|megast) build=atarist ;;
|
||||||
|
ste|megaste) build=ste ;;
|
||||||
|
*) echo "$label: unknown machine '$machine' (st, megast, ste, megaste)" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
makefile=$repo/make/$build.mk
|
||||||
|
if ! make -s -f "$makefile" "$build-uberprob"; then
|
||||||
|
echo "$label: FAIL - building the $build UBERPROB failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dir=$repo/build/$build/probe
|
||||||
|
rm -f "$dir/joeylog.txt"
|
||||||
|
BENCH_PRG=UBERPROB.PRG BENCH_BIN_DIR="$dir" TIMEOUT="${TIMEOUT:-120}" "$repo/scripts/bench-atarist.sh" "$machine" > /dev/null 2>&1
|
||||||
|
line=$(grep "UBER-PROBE: displayMismatch" "$dir/joeylog.txt" 2>/dev/null)
|
||||||
|
if [[ "$line" =~ afterB=0\ afterSettle=0\ afterIdle=0 ]]; then
|
||||||
|
echo "$label: $build build on $machine OK - ${line#UBER-PROBE: }"
|
||||||
|
else
|
||||||
|
echo "$label: $build build on $machine FAIL - ${line:-no displayMismatch line in its log}" >&2
|
||||||
|
failed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$failed" = 0 ]; then
|
||||||
|
echo "$label: PASS"
|
||||||
|
fi
|
||||||
|
exit "$failed"
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
# so a stale or misplaced rectangle would never reach a hash. UBER's probe
|
# so a stale or misplaced rectangle would never reach a hash. UBER's probe
|
||||||
# build (make <port> builds it, into build/<port>/probe) ends with P13, the
|
# build (make <port> builds it, into build/<port>/probe) ends with P13, the
|
||||||
# battery the other display gates run, which after every step asks the
|
# battery the other display gates run, which after every step asks the
|
||||||
# backend (hostBeProbeDisplayMismatch in src/hosted/be/beBackend.cpp) how many
|
# backend (jlHostBeProbeDisplayMismatch in src/hosted/be/beBackend.cpp) how many
|
||||||
# pixels differ: the frame's against the stage, the bitmap's against the
|
# pixels differ: the frame's against the stage, the bitmap's against the
|
||||||
# frame, and the window as the shows left it against the window fully
|
# frame, and the window as the shows left it against the window fully
|
||||||
# repainted, both read back from the screen (BScreen::ReadBitmap). Every
|
# repainted, both read back from the screen (BScreen::ReadBitmap). Every
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,9 @@
|
||||||
# unambiguous. This also covers the overlay specifically -- SFX on DOS mix
|
# unambiguous. This also covers the overlay specifically -- SFX on DOS mix
|
||||||
# into the module's buffer rather than a separate channel, so "SFX audible
|
# into the module's buffer rather than a separate channel, so "SFX audible
|
||||||
# while the module player is loaded but paused" is exactly the path that
|
# while the module player is loaded but paused" is exactly the path that
|
||||||
# can break. Measured under DOSBox Staging 0.83 (whose mixer
|
# can break. Measured under DOSBox Staging 0.83: music ~1590, paused ~17,
|
||||||
# runs quieter than the old 0.74's): music ~1220, paused ~17, sfx ~659.
|
# sfx ~108 -- the card's mixer holds digital 16 dB under FM (the canonical
|
||||||
|
# mix, src/dos/audio.c mixerBalance), so an effect is as loud as a voice.
|
||||||
#
|
#
|
||||||
# Keys are HELD (keydown, pause, keyup), not tapped: the demo tests
|
# Keys are HELD (keydown, pause, keyup), not tapped: the demo tests
|
||||||
# jlKeyPressed, an edge predicate polled once a frame, and a make/break
|
# jlKeyPressed, an edge predicate polled once a frame, and a make/break
|
||||||
|
|
@ -174,12 +175,13 @@ parecPid=""
|
||||||
|
|
||||||
[ -s "$work/out.wav" ] || { echo "$label: FAIL - recorded no audio at all" >&2; exit 1; }
|
[ -s "$work/out.wav" ] || { echo "$label: FAIL - recorded no audio at all" >&2; exit 1; }
|
||||||
|
|
||||||
# Thresholds sit at about half the measured levels (music ~1220, paused
|
# Thresholds sit at about half the measured levels (music ~1590, sfx
|
||||||
# ~17, sfx ~659) so ordinary mixing jitter cannot flip the verdict.
|
# ~108; paused ~17 under its ceiling) so ordinary mixing jitter cannot
|
||||||
|
# flip the verdict.
|
||||||
python3 "$grader" "$label" "$work/out.wav" \
|
python3 "$grader" "$label" "$work/out.wav" \
|
||||||
"music:1:5:600:" \
|
"music:1:5:600:" \
|
||||||
"paused:8:13::200" \
|
"paused:8:13::40" \
|
||||||
"sfx:16:22:330:" \
|
"sfx:16:22:54:" \
|
||||||
"resumed:27:32:600:"
|
"resumed:27:32:600:"
|
||||||
status=$?
|
status=$?
|
||||||
[ -n "${DOS_AUDIO_KEEP:-}" ] && cp "$work/out.wav" "$repo/build/dos/headless-audio.wav" 2>/dev/null
|
[ -n "${DOS_AUDIO_KEEP:-}" ] && cp "$work/out.wav" "$repo/build/dos/headless-audio.wav" 2>/dev/null
|
||||||
|
|
|
||||||
155
scripts/verify-dos-mouse.sh
Executable file
155
scripts/verify-dos-mouse.sh
Executable file
|
|
@ -0,0 +1,155 @@
|
||||||
|
#!/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-dos-mouse.sh - Headless mouse acceptance gate for the DOS port.
|
||||||
|
# The port has the mouse driver call it on every move and button change
|
||||||
|
# (INT 33h function 0Ch, a DPMI real-mode callback, src/dos/input.c); a
|
||||||
|
# callback that never runs leaves the pointer where it started. Runs
|
||||||
|
# KEYS.EXE in DOSBox Staging under Xvfb with the mouse captured from the
|
||||||
|
# start, moves it with xdotool (real X pointer motion), and decodes KEYS's
|
||||||
|
# mouse verification scanline (row 199: 5A5A, the button flags, X, Y as
|
||||||
|
# nibble pixels) from a raw screenshot after each step. Covered: the start
|
||||||
|
# at the stage's middle, a move each way on both axes, clamping at all
|
||||||
|
# four edges, an odd column, and press and release of the left and right
|
||||||
|
# buttons.
|
||||||
|
# DOSBox scales host motion into mickeys its own way, so a move is checked
|
||||||
|
# by its direction and only the clamps by position, and a one-pixel nudge
|
||||||
|
# by landing on an odd column: in mode 13h the driver steps X by 2 in a
|
||||||
|
# 640-wide virtual screen, which the port halves, so every column counts.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-dos-mouse.sh
|
||||||
|
# Requires: DOSBox Staging (install.sh install emulator:dos), Xvfb, xdotool, python3 + PIL;
|
||||||
|
# build/dos/bin/KEYS.EXE (make dos).
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
source "$repo/scripts/lib/dosboxKeys.sh"
|
||||||
|
joeyNeed DOSBOX dosbox
|
||||||
|
bin_dir=$repo/build/dos/bin
|
||||||
|
conf=$repo/scripts/dosbox-386sx16.conf
|
||||||
|
label=verify-dos-mouse
|
||||||
|
|
||||||
|
[ -f "$bin_dir/KEYS.EXE" ] || { echo "$label: build KEYS.EXE first (make dos)" >&2; exit 2; }
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-dos-mouse.XXXXXX)
|
||||||
|
display_num=$((RANDOM % 500 + 300))
|
||||||
|
cleanup() {
|
||||||
|
joeyStop "${dosbox_pid:-}"
|
||||||
|
[ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" 2>/dev/null
|
||||||
|
rm -rf "$work"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p "$work/shots"
|
||||||
|
dosboxKeysMapper "$work/mapper.txt"
|
||||||
|
|
||||||
|
Xvfb ":$display_num" -screen 0 1024x768x24 >/dev/null 2>&1 &
|
||||||
|
xvfb_pid=$!
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$work/xdg" DISPLAY=":$display_num" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \
|
||||||
|
--conf "$conf" \
|
||||||
|
--set "capture_dir=$work/shots" \
|
||||||
|
--set "mapperfile=$work/mapper.txt" \
|
||||||
|
--set "mouse_capture=onstart" \
|
||||||
|
-c "MOUNT C $bin_dir" -c "C:" -c "KEYS.EXE" \
|
||||||
|
>"$work/dosbox.log" 2>&1 &
|
||||||
|
dosbox_pid=$!
|
||||||
|
|
||||||
|
# Let DOS boot and KEYS reach its poll loop on the simulated 386SX-16.
|
||||||
|
sleep 12
|
||||||
|
|
||||||
|
win=$(joeyFindWindow ":$display_num" "$dosbox_pid")
|
||||||
|
if [ -z "$win" ]; then
|
||||||
|
echo "$label: FAIL - DOSBox window not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
DISPLAY=":$display_num" xdotool windowactivate --sync "$win" 2>/dev/null || true
|
||||||
|
|
||||||
|
# The mouse row of a fresh screenshot as "flags x y" (hex flags).
|
||||||
|
readRow() {
|
||||||
|
local png="$work/$1.png"
|
||||||
|
dosboxShot ":$display_num" "$win" "$work/shots" "$png" || return 1
|
||||||
|
python3 - "$png" <<'PY'
|
||||||
|
import sys
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
img = Image.open(sys.argv[1]).convert("RGB")
|
||||||
|
w, h = img.size
|
||||||
|
sx, sy = w // 320, h // 200
|
||||||
|
if sx < 1 or sy < 1 or w % 320 or h % 200:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def dac8(c4):
|
||||||
|
v6 = ((c4 << 2) | (c4 >> 2)) & 0x3F
|
||||||
|
return (v6 << 2) | (v6 >> 4)
|
||||||
|
|
||||||
|
# KEYS palette 0 (as verify-dos-input.sh decodes it).
|
||||||
|
pal12 = [(i << 8) | (i << 4) | i for i in range(16)]
|
||||||
|
pal12[0], pal12[1], pal12[2], pal12[3] = 0x000, 0x333, 0x0F0, 0xFFF
|
||||||
|
pal12[15] = 0xF00
|
||||||
|
palRgb = [(dac8((v >> 8) & 0xF), dac8((v >> 4) & 0xF), dac8(v & 0xF)) for v in pal12]
|
||||||
|
|
||||||
|
def pix(px):
|
||||||
|
r, g, b = img.getpixel((px * sx, 199 * sy))
|
||||||
|
return min(range(16), key=lambda n: (r - palRgb[n][0]) ** 2 + (g - palRgb[n][1]) ** 2 + (b - palRgb[n][2]) ** 2)
|
||||||
|
|
||||||
|
nib = [pix(i) for i in range(16)]
|
||||||
|
words = [(nib[4 * k] << 12) | (nib[4 * k + 1] << 8) | (nib[4 * k + 2] << 4) | nib[4 * k + 3] for k in range(4)]
|
||||||
|
if words[0] != 0x5A5A:
|
||||||
|
sys.exit(1)
|
||||||
|
print("%04X %d %d" % (words[1], words[2], words[3]))
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
fails=()
|
||||||
|
step() {
|
||||||
|
local tag="$1"
|
||||||
|
local row
|
||||||
|
shift
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
DISPLAY=":$display_num" xdotool "$@"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
if ! row=$(readRow "$tag"); then
|
||||||
|
fails+=("$tag: no mouse row in the screenshot")
|
||||||
|
row="---- -1 -1"
|
||||||
|
fi
|
||||||
|
read -r flags mx my <<< "$row"
|
||||||
|
echo " $tag: flags=$flags x=$mx y=$my"
|
||||||
|
}
|
||||||
|
|
||||||
|
step start
|
||||||
|
[ "$flags $mx $my" = "1000 160 100" ] || fails+=("start: expected flags 1000 at 160,100")
|
||||||
|
step upLeft mousemove_relative -- -60 -40
|
||||||
|
[ "$flags" = "1000" ] && [ "$mx" -lt 160 ] && [ "$my" -lt 100 ] || fails+=("a move up and left did not arrive")
|
||||||
|
lastX=$mx
|
||||||
|
lastY=$my
|
||||||
|
step downRight mousemove_relative -- 120 80
|
||||||
|
[ "$flags" = "1000" ] && [ "$mx" -gt "$lastX" ] && [ "$my" -gt "$lastY" ] || fails+=("a move down and right did not arrive")
|
||||||
|
step topLeft mousemove_relative -- -2000 -2000
|
||||||
|
[ "$flags $mx $my" = "1000 0 0" ] || fails+=("not held at the top left (0,0)")
|
||||||
|
step nudge mousemove_relative -- 1 0
|
||||||
|
[ "$flags" = "1000" ] && [ $((mx % 2)) -eq 1 ] || fails+=("a one-pixel nudge did not reach an odd column")
|
||||||
|
step bottomRight mousemove_relative -- 2000 2000
|
||||||
|
[ "$flags $mx $my" = "1000 319 199" ] || fails+=("not held at the bottom right (319,199)")
|
||||||
|
step leftDown mousedown 1
|
||||||
|
[ "$flags" = "1100" ] || fails+=("the left button down not seen")
|
||||||
|
step leftUp mouseup 1
|
||||||
|
[ "$flags" = "1000" ] || fails+=("the left button up not seen")
|
||||||
|
step rightDown mousedown 3
|
||||||
|
[ "$flags" = "1010" ] || fails+=("the right button down not seen")
|
||||||
|
step rightUp mouseup 3
|
||||||
|
[ "$flags" = "1000" ] || fails+=("the right button up not seen")
|
||||||
|
|
||||||
|
if [ ${#fails[@]} -eq 0 ]; then
|
||||||
|
echo "$label: PASS (moves both ways, all four edges, every column, both buttons down and up)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
for f in "${fails[@]}"; do
|
||||||
|
echo "$label: FAIL - $f" >&2
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
# (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 colours, bands and a small rect, moves
|
||||||
# rows to another palette and changes that palette, presenting after each,
|
# rows to another palette and changes that palette, presenting after each,
|
||||||
# and asks the port after every present how many pixels differ from the
|
# and asks the port after every present how many pixels differ from the
|
||||||
# stage (dvxPortProbeDisplayMismatch in src/dvx/hal.c): the window's content
|
# stage (jlDvxPortProbeDisplayMismatch in src/dvx/hal.c): the window's content
|
||||||
# buffer in the desktop's own pixel format, every screen pixel of a stage
|
# buffer in the desktop's own pixel format, every screen pixel of a stage
|
||||||
# pixel's block (scale across; down, the rows the window's shape gives it:
|
# pixel's block (scale across; down, the rows the window's shape gives it:
|
||||||
# 4:3 stretches every fifth row one more, square pixels take scale each),
|
# 4:3 stretches every fifth row one more, square pixels take scale each),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
# Manager takes the focus, the desktop comes back and UBER presents into
|
# Manager takes the focus, the desktop comes back and UBER presents into
|
||||||
# its window) and resumed (the Task Manager's Switch To): the
|
# its window) and resumed (the Task Manager's Switch To): the
|
||||||
# screenshots must be the 320x200 mode, the desktop, then 320x200 again;
|
# screenshots must be the 320x200 mode, the desktop, then 320x200 again;
|
||||||
# DVX.LOG must show the mode set twice; and UBER's 51 hashes must equal
|
# DVX.LOG must show the mode set twice; and UBER's 54 hashes must equal
|
||||||
# the DOS port's (tests/goldens/uber/dos.txt): the stage is DOS's
|
# the DOS port's (tests/goldens/uber/dos.txt): the stage is DOS's
|
||||||
# wherever it is shown.
|
# wherever it is shown.
|
||||||
# 2. KEYS on the whole screen: the typed string must reach jlInputGetChar
|
# 2. KEYS on the whole screen: the typed string must reach jlInputGetChar
|
||||||
|
|
@ -227,4 +227,4 @@ if [ -n "$fails" ]; then
|
||||||
echo "verify-dvx-fullscreen: FAIL - $fails"
|
echo "verify-dvx-fullscreen: FAIL - $fails"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "verify-dvx-fullscreen: PASS (UBER 51/51 on the whole screen through a suspend and resume; KEYS's typed row read off the 320x200 screen at (0,198), in its window, and full screen again)"
|
echo "verify-dvx-fullscreen: PASS (UBER 54/54 on the whole screen through a suspend and resume; KEYS's typed row read off the 320x200 screen at (0,198), in its window, and full screen again)"
|
||||||
|
|
|
||||||
120
scripts/verify-iigs-keys.sh
Executable file
120
scripts/verify-iigs-keys.sh
Executable file
|
|
@ -0,0 +1,120 @@
|
||||||
|
#!/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-keys.sh - Headless gate for the IIgs's key predicates, the
|
||||||
|
# asm jlKeyDown and jlKeyPressed (JL_HAS_INPUT_PREDICATES, joeyDraw.s).
|
||||||
|
# Boots GS/OS under MAME and launches the KEYS example off joey.2mg, which
|
||||||
|
# lights a key's cell while jlKeyDown says it is held and quits on
|
||||||
|
# jlKeyPressed(JL_KEY_ESCAPE). Holds A on the emulated ADB keyboard and
|
||||||
|
# reads the A cell straight out of SHR memory: unlit before, lit while
|
||||||
|
# held, unlit after. Then presses Escape: KEYS must quit to the Finder,
|
||||||
|
# whose 640-mode SCB replaces KEYS' 320-mode one.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-iigs-keys.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
|
||||||
|
|
||||||
|
sys_disk=$GSOS_SYSTEM_DISK
|
||||||
|
data_disk=$repo/build/iigs/bin/joey.2mg
|
||||||
|
|
||||||
|
for f in "$sys_disk" "$data_disk"; do
|
||||||
|
[ -f "$f" ] || { echo "verify-iigs-keys: missing $f (run 'make iigs-disk')" >&2; exit 2; }
|
||||||
|
done
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-keys-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-input.sh. The A cell is
|
||||||
|
# KEYS' row 2, column 0 (examples/keys: MARGIN 2, CELL 28x26, GAP 4); its
|
||||||
|
# middle, x 16 y 75, is the SHR byte $E1/2000 + 75*160 + 8 = $E1/4EE8:
|
||||||
|
# $11 unlit (COLOR_UNLIT), $22 lit (COLOR_LIT). SCB 0 is $E1/9D00.
|
||||||
|
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_a = field(":macadb:KEY0", "a A")
|
||||||
|
local key_esc = field(":macadb:KEY3", "Esc")
|
||||||
|
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)
|
||||||
|
io.write(string.format("VERIFY-IIGS-KEYS %s scb0=%02X cell=%02X\n", tag, mem:read_u8(0xE19D00), mem:read_u8(0xE14EE8)))
|
||||||
|
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("before") end},
|
||||||
|
{6610, function() press(key_a) end},
|
||||||
|
{6700, function() report("held") end},
|
||||||
|
{6710, function() release(key_a) end},
|
||||||
|
{6800, function() report("released") end},
|
||||||
|
{6810, function() press(key_esc) end},
|
||||||
|
{6830, function() release(key_esc) end},
|
||||||
|
{9000, 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-KEYS ' || true)
|
||||||
|
echo "$report"
|
||||||
|
value() {
|
||||||
|
echo "$report" | grep " $1 " | sed -E "s/.*$2=([0-9A-Fa-f]+).*/\1/"
|
||||||
|
}
|
||||||
|
fails=()
|
||||||
|
[ "$(value before scb0)" = "00" ] || fails+=("KEYS never launched (SCB 0 $(value before scb0))")
|
||||||
|
[ "$(value before cell)" = "11" ] || fails+=("the A cell lit before A was held ($(value before cell))")
|
||||||
|
[ "$(value held cell)" = "22" ] || fails+=("jlKeyDown: the A cell unlit while A was held ($(value held cell))")
|
||||||
|
[ "$(value released cell)" = "11" ] || fails+=("jlKeyDown: the A cell still lit after A was let go ($(value released cell))")
|
||||||
|
scb=$(value escape scb0)
|
||||||
|
[ -n "$scb" ] && [ $((16#$scb)) -ge 128 ] || fails+=("jlKeyPressed: Escape did not quit KEYS (SCB 0 ${scb:-none})")
|
||||||
|
if [ ${#fails[@]} -eq 0 ]; then
|
||||||
|
echo "verify-iigs-keys: PASS (A lit only while held; Escape quit KEYS)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
for f in "${fails[@]}"; do
|
||||||
|
echo "verify-iigs-keys: FAIL - $f" >&2
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
# so a stale or misplaced rectangle would never reach a hash. UBER's probe
|
# so a stale or misplaced rectangle would never reach a hash. UBER's probe
|
||||||
# build (make win16 builds it, into build/win16/probe) ends with P13, the
|
# build (make win16 builds it, into build/win16/probe) ends with P13, the
|
||||||
# battery verify-dvx-display.sh runs on DVX, which after every step asks the
|
# battery verify-dvx-display.sh runs on DVX, which after every step asks the
|
||||||
# backend (hostWin16ProbeDisplayMismatch in src/hosted/win16/win16Backend.c)
|
# backend (jlHostWin16ProbeDisplayMismatch in src/hosted/win16/win16Backend.c)
|
||||||
# how many pixels differ: the frame's against the stage, the window as the
|
# how many pixels differ: the frame's against the stage, the window as the
|
||||||
# shows left it against the window fully repainted, and, at a whole-number
|
# shows left it against the window fully repainted, and, at a whole-number
|
||||||
# scale, that repaint against GDI's own stretch of the frame. In the
|
# scale, that repaint against GDI's own stretch of the frame. In the
|
||||||
|
|
|
||||||
70
scripts/verify-win16-serial-fast.sh
Executable file
70
scripts/verify-win16-serial-fast.sh
Executable file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/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-win16-serial-fast.sh - the Win16 port's serial throughput and loss
|
||||||
|
# gate. SERFAST (SERTEST at 115200, make/examples.mk) runs on Windows 3.11
|
||||||
|
# under DOSBox with COM1 a nullmodem to the host peer
|
||||||
|
# (scripts/lib/serialLoopPeer.py), and the KPCOMM driver every Win16 run gets
|
||||||
|
# (win16Stage), which takes a COM port past 57600. After the banner and the
|
||||||
|
# probe's echo the peer streams STREAM_BYTES bytes through SERTEST's echo, a
|
||||||
|
# window at a time, and every one must come back once and in order, at no
|
||||||
|
# less than three quarters of what 115200 baud carries. The rate was measured
|
||||||
|
# once by hand when KPCOMM came in (11485 bytes a second echoed); this keeps
|
||||||
|
# it, and its loss, under test.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-win16-serial-fast.sh
|
||||||
|
# Env: WIN16_SERIAL_PORT the nullmodem's TCP port (default 6805)
|
||||||
|
# Exit: 0 PASS, 1 FAIL, 2 SKIP
|
||||||
|
# Requires: Windows on DOSBox (install.sh install emulator:win16), python3;
|
||||||
|
# the win16 toolchain (it builds SERFAST.EXE, make win16-serfast).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
source "$repo/scripts/lib/joeyEnv.sh"
|
||||||
|
source "$repo/scripts/lib/win16Gate.sh"
|
||||||
|
|
||||||
|
label=verify-win16-serial-fast
|
||||||
|
win16Need
|
||||||
|
make -s -f "$repo/make/win16.mk" win16-serfast || {
|
||||||
|
echo "$label: FAIL - building SERFAST.EXE failed" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
program=$(find "$repo/build/win16/test" -name SERFAST.EXE | head -1)
|
||||||
|
[ -f "$program" ] || { echo "$label: FAIL - no SERFAST.EXE after the build" >&2; exit 1; }
|
||||||
|
port="${WIN16_SERIAL_PORT:-6805}"
|
||||||
|
baud=115200
|
||||||
|
streamBytes=16384
|
||||||
|
# The peer's budget: Windows' boot, the banner and the echo, then the stream.
|
||||||
|
wall=120
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-win16-serial-fast.XXXXXX)
|
||||||
|
peerPid=""
|
||||||
|
cleanup() {
|
||||||
|
joeyStop "$peerPid"
|
||||||
|
win16Stop
|
||||||
|
rm -rf "$work"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
python3 "$repo/scripts/lib/serialLoopPeer.py" tcp-server "$port" "$wall" --stream "$streamBytes" --rate "$baud" > "$work/peer.log" 2>&1 &
|
||||||
|
peerPid=$!
|
||||||
|
sleep 1
|
||||||
|
mkdir -p "$work/program"
|
||||||
|
cp "$program" "$work/program/SERFAST.EXE"
|
||||||
|
win16Stage "$work" "$work/program" SERFAST.EXE
|
||||||
|
# transparent:1 -- the bytes alone, no modem lines in the stream.
|
||||||
|
WIN16_DOSBOX_ARGS=(--set "serial1=nullmodem server:127.0.0.1 port:$port transparent:1")
|
||||||
|
win16Start "$work"
|
||||||
|
wait "$peerPid"
|
||||||
|
rc=$?
|
||||||
|
peerPid=""
|
||||||
|
cat "$work/peer.log"
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
echo "$label: PASS ($streamBytes bytes through COM1 at $baud baud and back, none lost)"
|
||||||
|
else
|
||||||
|
echo "$label: FAIL" >&2
|
||||||
|
fi
|
||||||
|
exit "$rc"
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
# a hash. UBER's probe build (make win32 builds it, into build/win32/probe)
|
# a hash. UBER's probe build (make win32 builds it, into build/win32/probe)
|
||||||
# ends with P13, the battery verify-dvx-display.sh and
|
# ends with P13, the battery verify-dvx-display.sh and
|
||||||
# verify-win16-display.sh run, which after every step asks the backend
|
# verify-win16-display.sh run, which after every step asks the backend
|
||||||
# (hostWin32ProbeDisplayMismatch in src/hosted/win32/win32Backend.c) how
|
# (jlHostWin32ProbeDisplayMismatch in src/hosted/win32/win32Backend.c) how
|
||||||
# many pixels differ: the frame's against the stage, the window as the
|
# many pixels differ: the frame's against the stage, the window as the
|
||||||
# shows left it against the window fully repainted, and that repaint
|
# shows left it against the window fully repainted, and that repaint
|
||||||
# against GDI stretching the core's frame itself. Every count must be 0,
|
# against GDI stretching the core's frame itself. Every count must be 0,
|
||||||
|
|
|
||||||
37
scripts/verify-x68000-display.sh
Executable file
37
scripts/verify-x68000-display.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/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-x68000-display.sh - the X68000's present, checked on the text-plane
|
||||||
|
# page it draws. UBER's golden hashes are of the stage; a present that leaves
|
||||||
|
# stale rows on screen passes them. This page flips: each present draws into
|
||||||
|
# the page not shown, which holds the frame before last, from the union of
|
||||||
|
# two frames' dirty bands. UBER built with UBER_PROBE runs its X68000 display
|
||||||
|
# row (op_probeX68kDisplayCheck): a battery of draws, each presented and the
|
||||||
|
# drawn page compared with the stage's planes (jlX68kProbeDisplayMismatch in
|
||||||
|
# src/x68000/hal.c). The total must be 0.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-x68000-display.sh
|
||||||
|
# Exit: 0 PASS, 1 FAIL
|
||||||
|
# Requires: MAME and the X68000 firmware (install.sh install emulator:x68000),
|
||||||
|
# the library built (make x68000).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
label=verify-x68000-display
|
||||||
|
log=$repo/build/x68000/joeylog.txt
|
||||||
|
|
||||||
|
rm -f "$log"
|
||||||
|
if ! X68K_BENCH_CFLAGS="-DUBER_PROBE -DUBER_FRAMES=1" "$repo/scripts/bench-x68000.sh" > /dev/null 2>&1; then
|
||||||
|
echo "$label: FAIL - the UBER_PROBE run did not finish" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
line=$(grep "UBER-PROBE: x68kDisplay" "$log" 2>/dev/null)
|
||||||
|
if [[ "$line" == *" PASS (mismatch=0)" ]]; then
|
||||||
|
echo "$label: PASS - ${line#UBER-PROBE: }"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "$label: FAIL - ${line:-no x68kDisplay line in its log}" >&2
|
||||||
|
exit 1
|
||||||
|
|
@ -61,7 +61,7 @@ x68kDataDisk "$work/data.xdf" || exit 1
|
||||||
x68kXdf add "$work/data.xdf" "$work/UBER.X" UBER.X >/dev/null || 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
|
# Completion-driven exit. UBER's last act before jlWaitForAnyKey is
|
||||||
# jlSurfaceClear(gStage, 2) + jlStagePresent, so an all-colour-2 stage in GVRAM
|
# jlSurfaceClear(jlgStage, 2) + jlStagePresent, so an all-colour-2 stage in GVRAM
|
||||||
# is the "hashes are computed and flushed" signal. The benchmark's own
|
# 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 colour for an instant, so the
|
||||||
# reading only counts once it has held for GREEN_STABLE frames -- the finished
|
# reading only counts once it has held for GREEN_STABLE frames -- the finished
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#
|
#
|
||||||
# The X68000 port presents to the text plane, which has ONE 16-entry palette,
|
# 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 honours the SCB by reloading that palette as the beam scans: a CRTC
|
||||||
# raster interrupt (src/x68000/x68kRasterIsr.s) stores a band's sixteen
|
# raster interrupt (src/x68000/jlX68kRasterIsr.s) stores a band's sixteen
|
||||||
# colours in the horizontal blanking before the band's first line. Each reload
|
# colours 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
|
# 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
|
# line above and before the first stage pixel of the band. Land early and the
|
||||||
|
|
@ -48,19 +48,19 @@
|
||||||
# taps.
|
# taps.
|
||||||
# exit after ESC the program exits and gives the $138 vector and MFP
|
# exit after ESC the program exits and gives the $138 vector and MFP
|
||||||
# channel 14 back -- and the two clock vectors it held while it
|
# channel 14 back -- and the two clock vectors it held while it
|
||||||
# ran, which are checked then: $114 (x68kTickIsr, chained onto
|
# ran, which are checked then: $114 (jlX68kTickIsr, chained onto
|
||||||
# Human68k's Timer C) and $134 (x68kVdispIsr, the frame counter
|
# Human68k's Timer C) and $134 (jlX68kVdispIsr, the frame counter
|
||||||
# _VDISPST installs, with MFP channel 13 enabled).
|
# _VDISPST installs, with MFP channel 13 enabled).
|
||||||
#
|
#
|
||||||
# It also reports the interrupt's cost (time inside it per frame), the latency
|
# It also reports the interrupt's cost (time inside it per frame), the latency
|
||||||
# from each raster match to the vector fetch, and the slack of the compare
|
# from each raster match to the vector fetch, and the slack of the compare
|
||||||
# write the interrupt's timing rests on (x68kRasterIsr.s step 2).
|
# write the interrupt's timing rests on (jlX68kRasterIsr.s step 2).
|
||||||
#
|
#
|
||||||
# Human68k's Timer C tick runs throughout, and text is typed during the whole
|
# Human68k's Timer C tick runs throughout, and text is typed during the whole
|
||||||
# capture so the IOCS keyboard interrupt runs too: both are MFP interrupts at
|
# capture so the IOCS keyboard interrupt runs too: both are MFP interrupts at
|
||||||
# IPL 6, like the raster interrupt, and a raster match that falls inside one of
|
# IPL 6, like the raster interrupt, and a raster match that falls inside one of
|
||||||
# them is taken late (the Timer C dispatcher used to make reloads miss their
|
# them is taken late (the Timer C dispatcher used to make reloads miss their
|
||||||
# line; see src/x68000/x68kTickIsr.s). The DMAC-driven ADPCM path is not
|
# line; see src/x68000/jlX68kTickIsr.s). The DMAC-driven ADPCM path is not
|
||||||
# exercised: JoeyLib's audio does its work on the main thread
|
# exercised: JoeyLib's audio does its work on the main thread
|
||||||
# (src/x68000/audioPcm.c).
|
# (src/x68000/audioPcm.c).
|
||||||
#
|
#
|
||||||
|
|
@ -132,12 +132,12 @@ local TEXT_PALETTE, COLORS = 0xE82200, 16
|
||||||
local CRTC, CRTC_R09 = 0xE80000, 0xE80012
|
local CRTC, CRTC_R09 = 0xE80000, 0xE80012
|
||||||
local MFP_IERA, MFP_IMRA, MFP_ISRA = 0xE88007, 0xE88013, 0xE8800F
|
local MFP_IERA, MFP_IMRA, MFP_ISRA = 0xE88007, 0xE88013, 0xE8800F
|
||||||
local RASTER_VECTOR = 0x138
|
local RASTER_VECTOR = 0x138
|
||||||
local TICK_VECTOR = 0x114 -- MFP channel 5, Timer C: x68kTickIsr
|
local TICK_VECTOR = 0x114 -- MFP channel 5, Timer C: jlX68kTickIsr
|
||||||
local VDISP_VECTOR = 0x134 -- MFP channel 13, Timer A: x68kVdispIsr
|
local VDISP_VECTOR = 0x134 -- MFP channel 13, Timer A: jlX68kVdispIsr
|
||||||
local TIMER_A_BIT = 0x20 -- channel 13 in IERA / IMRA
|
local TIMER_A_BIT = 0x20 -- channel 13 in IERA / IMRA
|
||||||
local PROGRAM_RAM_END = 0x01000000 -- IOCS's own test for "a handler is installed"
|
local PROGRAM_RAM_END = 0x01000000 -- IOCS's own test for "a handler is installed"
|
||||||
local GPIP6_BIT = 0x40
|
local GPIP6_BIT = 0x40
|
||||||
local ISRA_EXIT = 0xBF -- x68kRasterIsr's in-service ack
|
local ISRA_EXIT = 0xBF -- jlX68kRasterIsr's in-service ack
|
||||||
local CPU_HZ = 10000000 -- 40 MHz / 4 (MAME x68k.cpp)
|
local CPU_HZ = 10000000 -- 40 MHz / 4 (MAME x68k.cpp)
|
||||||
local IRQ_HPOS = 2 -- x68k_crtc.cpp: R09 = N fires at (N - 1, 2)
|
local IRQ_HPOS = 2 -- x68k_crtc.cpp: R09 = N fires at (N - 1, 2)
|
||||||
|
|
||||||
|
|
@ -410,7 +410,7 @@ end
|
||||||
|
|
||||||
-- Interrupt cost, latency and slack from the entry/exit/R09 events. The first
|
-- Interrupt cost, latency and slack from the entry/exit/R09 events. The first
|
||||||
-- R09 write after an entry re-points the compare at the raster of the line
|
-- R09 write after an entry re-points the compare at the raster of the line
|
||||||
-- above the band (x68kRasterIsr.s step 2); it must land before that raster
|
-- above the band (jlX68kRasterIsr.s step 2); it must land before that raster
|
||||||
-- begins, and its slack is how far ahead it did. The last R09 write before an
|
-- begins, and its slack is how far ahead it did. The last R09 write before an
|
||||||
-- entry is the arm that raised it.
|
-- entry is the arm that raised it.
|
||||||
local function reportInterrupt()
|
local function reportInterrupt()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# verify-x68000-serial-mouse.sh - the X68000's RS-232C port and mouse at once.
|
# verify-x68000-serial-mouse.sh - the X68000's RS-232C port and mouse at once.
|
||||||
#
|
#
|
||||||
# Both live on the one Z8530 SCC: RS-232C on channel A, where JoeyLib's serial
|
# Both live on the one Z8530 SCC: RS-232C on channel A, where JoeyLib's serial
|
||||||
# add-on owns the receive interrupt (src/x68000/x68kSccRxIsr.s), and the mouse
|
# add-on owns the receive interrupt (src/x68000/jlX68kSccRxIsr.s), and the mouse
|
||||||
# on channel B, where the IOCS ROM owns the receive interrupt and solicits a
|
# on channel B, where the IOCS ROM owns the receive interrupt and solicits a
|
||||||
# packet every other Timer C tick. The chip has ONE register pointer for both
|
# packet every other Timer C tick. The chip has ONE register pointer for both
|
||||||
# channels, so a select-then-access register sequence split by the other
|
# channels, so a select-then-access register sequence split by the other
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# per-line palette change: the SCB raster interrupt and the SCC together.
|
# 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 honours the SCB by reloading the text plane's one palette as
|
||||||
# the beam scans (src/x68000/x68kRasterIsr.s). A palette change on every line
|
# 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)
|
# 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
|
# a change plus two rasters of lead, and the SCC is IPL 5, so every byte that
|
||||||
# arrives in the run waits in the Z8530's receive FIFO. No handler can let the
|
# arrives in the run waits in the Z8530's receive FIFO. No handler can let the
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,17 @@ extern struct Custom custom;
|
||||||
|
|
||||||
static volatile struct Custom *const gCust = &custom;
|
static volatile struct Custom *const gCust = &custom;
|
||||||
|
|
||||||
|
// BLTSIZE packs a blit's row count in its high 10 bits (<< 6) and its word
|
||||||
|
// count in the low 6 (a width of 0 would mean 64 words).
|
||||||
|
#define AMIGA_BLT_SIZESHIFT 6
|
||||||
|
#define AMIGA_BLT_MASK_ALL 0xFFFFu
|
||||||
|
|
||||||
|
// Minterms that keep the destination through C. With A the constant $FFFF
|
||||||
|
// cut by the first/last-word masks (amigaBlitterMaskedFillOne), set-bits is
|
||||||
|
// D = A OR C (the masked bits on, the rest kept) and clear-bits D = (NOT A)
|
||||||
|
// AND C (the masked bits off); with A source data, D = A OR C adds A's bits
|
||||||
|
// (amigaTileLayer.c's edge words).
|
||||||
|
#define AMIGA_BLT_MINTERM_SET 0xFAu // D = A | C
|
||||||
|
#define AMIGA_BLT_MINTERM_CLEAR 0x0Au // D = ~A & C
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
199
src/amiga/amigaTileLayer.c
Normal file
199
src/amiga/amigaTileLayer.c
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
||||||
|
// See LICENSE.md for license information.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// The Amiga's tile-layer ring and blit. The ring is the shared
|
||||||
|
// separate-plane one (src/m68k/planarTileLayer.h has the layout,
|
||||||
|
// planarTileLayer.c the Put and the CPU blit), put in chip RAM when there is
|
||||||
|
// room, so that the blitter can copy the band into a chip-RAM surface; the
|
||||||
|
// CPU blit copies it otherwise.
|
||||||
|
//
|
||||||
|
// Word k of a band plane row is (P[w + k] << s) | (P[w + k + 1] >> (16 - s)),
|
||||||
|
// P being that plane's 21 words of the ring row, w = ringX / 16, s = ringX &
|
||||||
|
// 15, and the indices wrapping at 21. Words 0..19 take their P[w + k] from P[w]
|
||||||
|
// to the ring row's end and then on from P[0]: at most two runs of whole
|
||||||
|
// source words, the second landing at band word 21 - w. A blit's channels
|
||||||
|
// walk the same number of words, and a run's last word needs one source word
|
||||||
|
// more than the run has, so a run is two blits:
|
||||||
|
// * the run itself, descending, so the blitter's shift goes left: A is
|
||||||
|
// the run's words shifted s, D the band's. Each band word takes its low
|
||||||
|
// bits from the source word to its right; the run's last band word, with
|
||||||
|
// none in the run, takes them from what the blitter shifted last.
|
||||||
|
// * its edge (s > 0): the source word after the run, ascending, shifted
|
||||||
|
// right 16 - s into the low s bits and or'd into the run's last band
|
||||||
|
// word through C.
|
||||||
|
// What a blit shifts in at a row's first word is the last A word of the row
|
||||||
|
// before, masked -- or, on its first row, of the blit before. So the masks
|
||||||
|
// keep each blit's last word to the bits the next one may take: a run's
|
||||||
|
// last-word mask clears the high s bits of each row's leftmost source word
|
||||||
|
// (the bits a run's next row would take), and an edge's masks keep only the
|
||||||
|
// high s bits it moves (an edge takes none). The runs all go first, then
|
||||||
|
// the edges, each group after a blit of a masked-off word, so its first row
|
||||||
|
// shifts in zeros too: the runs leave their last band words' low s bits
|
||||||
|
// clear for the edges to or into.
|
||||||
|
//
|
||||||
|
// The band's rows wrap at the ring's last row too, so each run and edge is a
|
||||||
|
// blit per plane per vertical stretch: at most 32 a band, each a register
|
||||||
|
// setup while the blitter moves 20 words a row per plane.
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <exec/memory.h>
|
||||||
|
#include <hardware/blit.h>
|
||||||
|
#include <proto/exec.h>
|
||||||
|
#include <proto/graphics.h>
|
||||||
|
|
||||||
|
#include "amigaCustom.h"
|
||||||
|
#include "planarTileLayer.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#define BAND_WORDS (PLANAR_TILE_BYTES_PER_ROW / 2u)
|
||||||
|
#define WORD_BITS 16u
|
||||||
|
#define SHIFT_MASK (WORD_BITS - 1u)
|
||||||
|
|
||||||
|
static void blitBandRows(const uint8_t *srcRow, uint8_t *dstRow, uint16_t rows, uint16_t w, uint16_t s, bool edges);
|
||||||
|
static void blitEdge(const uint8_t *src, uint8_t *dst, uint16_t rows, uint16_t s);
|
||||||
|
static void blitRun(const uint8_t *src, uint8_t *dst, uint16_t words, uint16_t rows, uint16_t s);
|
||||||
|
static void blitZeroCarry(void);
|
||||||
|
|
||||||
|
|
||||||
|
// `rows` rows of one plane, its runs or (edges) its edges: source plane rows
|
||||||
|
// from srcRow (one ring row apart), band plane rows from dstRow.
|
||||||
|
static void blitBandRows(const uint8_t *srcRow, uint8_t *dstRow, uint16_t rows, uint16_t w, uint16_t s, bool edges) {
|
||||||
|
uint16_t words1;
|
||||||
|
uint16_t words2;
|
||||||
|
|
||||||
|
words1 = (uint16_t)(PLANAR_RING_PLANE_WORDS - w);
|
||||||
|
if (words1 > BAND_WORDS) {
|
||||||
|
words1 = BAND_WORDS;
|
||||||
|
}
|
||||||
|
words2 = (uint16_t)(BAND_WORDS - words1);
|
||||||
|
if (edges) {
|
||||||
|
blitEdge(srcRow + ((w + words1) % PLANAR_RING_PLANE_WORDS) * 2u, dstRow + (words1 - 1u) * 2u, rows, s);
|
||||||
|
if (words2 != 0u) {
|
||||||
|
blitEdge(srcRow + words2 * 2u, dstRow + (BAND_WORDS - 1u) * 2u, rows, s);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
blitRun(srcRow + w * 2u, dstRow, words1, rows, s);
|
||||||
|
if (words2 != 0u) {
|
||||||
|
blitRun(srcRow, dstRow + words1 * 2u, words2, rows, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The source word at src, shifted right 16 - s, or'd into the band word at
|
||||||
|
// dst, row after row.
|
||||||
|
static void blitEdge(const uint8_t *src, uint8_t *dst, uint16_t rows, uint16_t s) {
|
||||||
|
uint16_t keep;
|
||||||
|
|
||||||
|
keep = (uint16_t)~(AMIGA_BLT_MASK_ALL >> s);
|
||||||
|
WaitBlit();
|
||||||
|
gCust->bltcon0 = (uint16_t)(((WORD_BITS - s) << ASHIFTSHIFT) | SRCA | SRCC | DEST | AMIGA_BLT_MINTERM_SET);
|
||||||
|
gCust->bltcon1 = 0u;
|
||||||
|
gCust->bltafwm = keep;
|
||||||
|
gCust->bltalwm = keep;
|
||||||
|
gCust->bltamod = (int16_t)(PLANAR_RING_ROW_BYTES - 2u);
|
||||||
|
gCust->bltcmod = (int16_t)(PLANAR_TILE_BYTES_PER_ROW - 2u);
|
||||||
|
gCust->bltdmod = (int16_t)(PLANAR_TILE_BYTES_PER_ROW - 2u);
|
||||||
|
gCust->bltapt = (APTR)src;
|
||||||
|
gCust->bltcpt = (APTR)dst;
|
||||||
|
gCust->bltdpt = (APTR)dst;
|
||||||
|
gCust->bltsize = (uint16_t)((rows << AMIGA_BLT_SIZESHIFT) | 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// `words` source words from src shifted left s into the band words from
|
||||||
|
// dst, row after row, descending: the pointers start at the last row's last
|
||||||
|
// word.
|
||||||
|
static void blitRun(const uint8_t *src, uint8_t *dst, uint16_t words, uint16_t rows, uint16_t s) {
|
||||||
|
uint32_t last;
|
||||||
|
|
||||||
|
last = (uint32_t)(words - 1u) * 2u;
|
||||||
|
WaitBlit();
|
||||||
|
gCust->bltcon0 = (uint16_t)((s << ASHIFTSHIFT) | SRCA | DEST | A_TO_D);
|
||||||
|
gCust->bltcon1 = BLITREVERSE;
|
||||||
|
gCust->bltafwm = AMIGA_BLT_MASK_ALL;
|
||||||
|
gCust->bltalwm = (uint16_t)(AMIGA_BLT_MASK_ALL >> s);
|
||||||
|
gCust->bltamod = (int16_t)(PLANAR_RING_ROW_BYTES - words * 2u);
|
||||||
|
gCust->bltdmod = (int16_t)(PLANAR_TILE_BYTES_PER_ROW - words * 2u);
|
||||||
|
gCust->bltapt = (APTR)(src + (uint32_t)(rows - 1u) * PLANAR_RING_ROW_BYTES + last);
|
||||||
|
gCust->bltdpt = (APTR)(dst + (uint32_t)(rows - 1u) * PLANAR_TILE_BYTES_PER_ROW + last);
|
||||||
|
gCust->bltsize = (uint16_t)((rows << AMIGA_BLT_SIZESHIFT) | words);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A one-word blit of a word the masks clear, through no channel, so the next
|
||||||
|
// blit's first row shifts in zeros.
|
||||||
|
static void blitZeroCarry(void) {
|
||||||
|
WaitBlit();
|
||||||
|
gCust->bltcon0 = 0u;
|
||||||
|
gCust->bltcon1 = 0u;
|
||||||
|
gCust->bltadat = 0u;
|
||||||
|
gCust->bltafwm = 0u;
|
||||||
|
gCust->bltalwm = 0u;
|
||||||
|
gCust->bltsize = (uint16_t)((1u << AMIGA_BLT_SIZESHIFT) | 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlpTileLayerBlit(jlSurfaceT *dst, const jlTileLayerT *layer, uint16_t ringX, uint16_t ringY) {
|
||||||
|
const AmigaPlanarT *pd;
|
||||||
|
const uint8_t *ring;
|
||||||
|
uint8_t *band;
|
||||||
|
uint16_t rows;
|
||||||
|
uint16_t rows1;
|
||||||
|
uint16_t w;
|
||||||
|
uint16_t s;
|
||||||
|
uint16_t p;
|
||||||
|
uint16_t pass;
|
||||||
|
|
||||||
|
pd = (const AmigaPlanarT *)dst->portData;
|
||||||
|
if (!pd->chip || (TypeOfMem(layer->ring) & MEMF_CHIP) == 0u) {
|
||||||
|
jlPlanarTileLayerCpuBlit(dst, layer, ringX, ringY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ring = (const uint8_t *)layer->ring;
|
||||||
|
rows = (uint16_t)((uint16_t)layer->viewHeight * JL_TILE_PIXELS_PER_SIDE);
|
||||||
|
rows1 = (uint16_t)((uint16_t)layer->ringRows * JL_TILE_PIXELS_PER_SIDE - ringY);
|
||||||
|
if (rows1 > rows) {
|
||||||
|
rows1 = rows;
|
||||||
|
}
|
||||||
|
w = (uint16_t)(ringX >> 4);
|
||||||
|
s = (uint16_t)(ringX & SHIFT_MASK);
|
||||||
|
OwnBlitter();
|
||||||
|
// Pass 0 the runs, pass 1 (s > 0) the edges.
|
||||||
|
for (pass = 0u; pass < (s != 0u ? 2u : 1u); pass++) {
|
||||||
|
blitZeroCarry();
|
||||||
|
for (p = 0u; p < PLANAR_TILE_PLANES; p++) {
|
||||||
|
band = pd->planes[p] + (uint16_t)layer->viewBy * JL_TILE_PIXELS_PER_SIDE * PLANAR_TILE_BYTES_PER_ROW;
|
||||||
|
blitBandRows(ring + (uint32_t)ringY * PLANAR_RING_ROW_BYTES + p * PLANAR_RING_PLANE_BYTES, band, rows1, w, s, pass != 0u);
|
||||||
|
if (rows1 < rows) {
|
||||||
|
blitBandRows(ring + p * PLANAR_RING_PLANE_BYTES, band + (uint32_t)rows1 * PLANAR_TILE_BYTES_PER_ROW, (uint16_t)(rows - rows1), w, s, pass != 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitBlit();
|
||||||
|
DisownBlitter();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Chip RAM first, so the blitter can read it; anywhere if chip RAM is short.
|
||||||
|
bool jlpTileLayerRingAlloc(jlTileLayerT *layer) {
|
||||||
|
ULONG bytes;
|
||||||
|
|
||||||
|
bytes = (ULONG)layer->ringRows * PLANAR_RING_SLOT_BYTES;
|
||||||
|
layer->ring = AllocMem(bytes, MEMF_CHIP | MEMF_CLEAR);
|
||||||
|
if (layer->ring == NULL) {
|
||||||
|
layer->ring = AllocMem(bytes, MEMF_ANY | MEMF_CLEAR);
|
||||||
|
}
|
||||||
|
return layer->ring != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlpTileLayerRingFree(jlTileLayerT *layer) {
|
||||||
|
if (layer->ring != NULL) {
|
||||||
|
FreeMem(layer->ring, (ULONG)layer->ringRows * PLANAR_RING_SLOT_BYTES);
|
||||||
|
layer->ring = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ extern UBYTE mt_E8Trigger;
|
||||||
#define PAULA_PERIOD_MIN 124 // Paula HW limit
|
#define PAULA_PERIOD_MIN 124 // Paula HW limit
|
||||||
#define PAULA_PERIOD_MAX 65535
|
#define PAULA_PERIOD_MAX 65535
|
||||||
// The fastest rate Paula plays: an effect faster than this plays decimated
|
// The fastest rate Paula plays: an effect faster than this plays decimated
|
||||||
// (audioDecimate), at its pitch.
|
// (jlAudioDecimate), at its pitch.
|
||||||
#define PAULA_MAX_RATE ((uint16_t)(PAULA_CLOCK_PAL / PAULA_PERIOD_MIN))
|
#define PAULA_MAX_RATE ((uint16_t)(PAULA_CLOCK_PAL / PAULA_PERIOD_MIN))
|
||||||
// A one-shot's head, in Chip RAM before it starts: the rest is copied while
|
// A one-shot's head, in Chip RAM before it starts: the rest is copied while
|
||||||
// Paula plays it (jlpAudioPlaySfx).
|
// Paula plays it (jlpAudioPlaySfx).
|
||||||
|
|
@ -132,8 +132,8 @@ typedef struct {
|
||||||
void *ctx;
|
void *ctx;
|
||||||
uint16_t rateHz;
|
uint16_t rateHz;
|
||||||
uint16_t dueFrame; // the VBL count the next chunk goes out at
|
uint16_t dueFrame; // the VBL count the next chunk goes out at
|
||||||
uint8_t decimate; // every decimate-th sample plays (audioDecimate), at rateHz
|
uint8_t decimate; // every decimate-th sample plays (jlAudioDecimate), at rateHz
|
||||||
uint8_t phase; // audioDecimate's phase between chunks
|
uint8_t phase; // jlAudioDecimate's phase between chunks
|
||||||
bool active;
|
bool active;
|
||||||
} SfxSlotT;
|
} SfxSlotT;
|
||||||
|
|
||||||
|
|
@ -403,7 +403,7 @@ void jlpAudioPlayMod(const uint8_t *data, uint32_t length, bool loop) {
|
||||||
if (chip == NULL) {
|
if (chip == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (loop || !modStopMarkerAdd(data, length, chip)) {
|
if (loop || !jlModStopMarkerAdd(data, length, chip)) {
|
||||||
memcpy(chip, data, length);
|
memcpy(chip, data, length);
|
||||||
}
|
}
|
||||||
gModuleChip = chip;
|
gModuleChip = chip;
|
||||||
|
|
@ -483,7 +483,7 @@ static bool streamChunk(uint8_t slot) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (s->decimate > 1u) {
|
if (s->decimate > 1u) {
|
||||||
got = audioDecimate(s->chunk, s->chunk, got, s->decimate, &s->phase);
|
got = jlAudioDecimate(s->chunk, s->chunk, got, s->decimate, &s->phase);
|
||||||
}
|
}
|
||||||
} while (got < 2u);
|
} while (got < 2u);
|
||||||
got &= ~1u; // Paula counts 16-bit words
|
got &= ~1u; // Paula counts 16-bit words
|
||||||
|
|
@ -601,7 +601,7 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Faster than Paula plays: every factor-th sample, at its pitch.
|
// Faster than Paula plays: every factor-th sample, at its pitch.
|
||||||
factor = audioDecimationFactor(rateHz, PAULA_MAX_RATE);
|
factor = jlAudioDecimationFactor(rateHz, PAULA_MAX_RATE);
|
||||||
kept = (length + factor - 1u) / factor;
|
kept = (length + factor - 1u) / factor;
|
||||||
if (kept < 2 || kept > 0x1FFFEUL) {
|
if (kept < 2 || kept > 0x1FFFEUL) {
|
||||||
return; // Paula sample length is 16 bits of words = 128KB max
|
return; // Paula sample length is 16 bits of words = 128KB max
|
||||||
|
|
@ -626,7 +626,7 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1
|
||||||
// long or fast sample starts as soon as a short one.
|
// long or fast sample starts as soon as a short one.
|
||||||
head = kept < SFX_HEAD_BYTES ? kept : SFX_HEAD_BYTES;
|
head = kept < SFX_HEAD_BYTES ? kept : SFX_HEAD_BYTES;
|
||||||
phase = 0u;
|
phase = 0u;
|
||||||
(void)audioDecimate(chip, sample, head * factor, factor, &phase);
|
(void)jlAudioDecimate(chip, sample, head * factor, factor, &phase);
|
||||||
words = (UWORD)(kept >> 1);
|
words = (UWORD)(kept >> 1);
|
||||||
|
|
||||||
s->samples = chip;
|
s->samples = chip;
|
||||||
|
|
@ -639,7 +639,7 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1
|
||||||
sfx.sfx_pri = SLOT_PRIORITY(slot);
|
sfx.sfx_pri = SLOT_PRIORITY(slot);
|
||||||
sfxPlay(slot, &sfx);
|
sfxPlay(slot, &sfx);
|
||||||
if (kept > head) {
|
if (kept > head) {
|
||||||
(void)audioDecimate(chip + head, sample + head * factor, length - head * factor, factor, &phase);
|
(void)jlAudioDecimate(chip + head, sample + head * factor, length - head * factor, factor, &phase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -668,7 +668,7 @@ void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Faster than Paula plays: every decimate-th sample, at its pitch.
|
// Faster than Paula plays: every decimate-th sample, at its pitch.
|
||||||
s->decimate = audioDecimationFactor(rateHz, PAULA_MAX_RATE);
|
s->decimate = jlAudioDecimationFactor(rateHz, PAULA_MAX_RATE);
|
||||||
s->phase = 0u;
|
s->phase = 0u;
|
||||||
s->fill = fill;
|
s->fill = fill;
|
||||||
s->ctx = ctx;
|
s->ctx = ctx;
|
||||||
|
|
|
||||||
498
src/amiga/hal.c
498
src/amiga/hal.c
|
|
@ -86,7 +86,6 @@ static void amigaBlitterCopyPlanes(uint8_t *dst[AMIGA_BITPLANES],
|
||||||
static void amigaBlitterCopyPlanesOwned(uint8_t *dst[AMIGA_BITPLANES], uint8_t *src[AMIGA_BITPLANES], uint16_t offsetBytes, uint16_t widthWords, uint16_t height, uint16_t modBytes);
|
static void amigaBlitterCopyPlanesOwned(uint8_t *dst[AMIGA_BITPLANES], uint8_t *src[AMIGA_BITPLANES], uint16_t offsetBytes, uint16_t widthWords, uint16_t height, uint16_t modBytes);
|
||||||
static void amigaBlitterFillOne(uint8_t *dst, uint16_t widthWords, uint16_t height, uint16_t dstModBytes, bool setBits);
|
static void amigaBlitterFillOne(uint8_t *dst, uint16_t widthWords, uint16_t height, uint16_t dstModBytes, bool setBits);
|
||||||
static void amigaBlitterMaskedFillOne(uint8_t *dst, uint16_t widthWords, uint16_t height, uint16_t dstModBytes, uint16_t fwm, uint16_t lwm, bool setBits);
|
static void amigaBlitterMaskedFillOne(uint8_t *dst, uint16_t widthWords, uint16_t height, uint16_t dstModBytes, uint16_t fwm, uint16_t lwm, bool setBits);
|
||||||
static void amigaPlanarSetPixel(AmigaPlanarT *pd, int16_t x, int16_t y, uint8_t color);
|
|
||||||
static void amigaPresentFlushRun(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t runFirst, int16_t runLast, uint16_t byteStart, uint16_t byteLen, bool *blitOwned);
|
static void amigaPresentFlushRun(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t runFirst, int16_t runLast, uint16_t byteStart, uint16_t byteLen, bool *blitOwned);
|
||||||
static void amigaPresentRectInner(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t firstRow, int16_t lastRow, uint16_t byteStart, uint16_t bytesPerRow, bool *blitOwned);
|
static void amigaPresentRectInner(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t firstRow, int16_t lastRow, uint16_t byteStart, uint16_t bytesPerRow, bool *blitOwned);
|
||||||
static uint8_t bandListUpdate(const jlSurfaceT *src);
|
static uint8_t bandListUpdate(const jlSurfaceT *src);
|
||||||
|
|
@ -105,7 +104,7 @@ static void trampolinePoint(const uint16_t *list);
|
||||||
|
|
||||||
// ----- Module state -----
|
// ----- Module state -----
|
||||||
|
|
||||||
struct Screen *gScreen = NULL; // shared with input.c
|
struct Screen *jlgScreen = NULL; // shared with input.c
|
||||||
static struct BitMap *gBitMap = NULL;
|
static struct BitMap *gBitMap = NULL;
|
||||||
// Phase 10 page-flip: two CHIP plane sets. gPlanes is set A (kept
|
// Phase 10 page-flip: two CHIP plane sets. gPlanes is set A (kept
|
||||||
// under the original name to keep diffs minimal); gPlanesB is set B.
|
// under the original name to keep diffs minimal); gPlanesB is set B.
|
||||||
|
|
@ -216,7 +215,7 @@ static uint8_t gPrevStageMaxWord[JL_SURFACE_HEIGHT] __attribute__((aligned(2)))
|
||||||
// present scan emits these into gPresentRuns; a separate loop flushes
|
// present scan emits these into gPresentRuns; a separate loop flushes
|
||||||
// each (blitter or CPU per amigaPresentRectInner). Splitting scan from
|
// each (blitter or CPU per amigaPresentRectInner). Splitting scan from
|
||||||
// flush lets the register-pressure-bound scan move to hand-rolled asm
|
// flush lets the register-pressure-bound scan move to hand-rolled asm
|
||||||
// (amigaPresentScan68k) without touching the flush path. Field layout
|
// (jlAmigaPresentScan68k) without touching the flush path. Field layout
|
||||||
// (firstRow@0, lastRow@2, byteStart@4, byteLen@6; 8 bytes) is an ABI
|
// (firstRow@0, lastRow@2, byteStart@4, byteLen@6; 8 bytes) is an ABI
|
||||||
// contract with that asm. At most one run per row, so JL_SURFACE_HEIGHT
|
// contract with that asm. At most one run per row, so JL_SURFACE_HEIGHT
|
||||||
// entries always suffice.
|
// entries always suffice.
|
||||||
|
|
@ -236,7 +235,7 @@ static AmigaPresentRunT gPresentRuns[JL_SURFACE_HEIGHT];
|
||||||
// 17. The asm hardcodes JL_SURFACE_HEIGHT, SURFACE_WORDS_PER_ROW, the clean
|
// 17. The asm hardcodes JL_SURFACE_HEIGHT, SURFACE_WORDS_PER_ROW, the clean
|
||||||
// sentinel, and the 8-byte run record; the guards below make a mismatch a
|
// sentinel, and the 8-byte run record; the guards below make a mismatch a
|
||||||
// compile error rather than a display bug.
|
// compile error rather than a display bug.
|
||||||
extern uint32_t amigaPresentScan68k(const uint8_t *curMin, const uint8_t *curMax, uint8_t *prvMin, uint8_t *prvMax, AmigaPresentRunT *runList);
|
extern uint32_t jlAmigaPresentScan68k(const uint8_t *curMin, const uint8_t *curMax, uint8_t *prvMin, uint8_t *prvMax, AmigaPresentRunT *runList);
|
||||||
|
|
||||||
typedef char amigaPresentScanAbiHeight[(JL_SURFACE_HEIGHT == 200) ? 1 : -1];
|
typedef char amigaPresentScanAbiHeight[(JL_SURFACE_HEIGHT == 200) ? 1 : -1];
|
||||||
typedef char amigaPresentScanAbiWords[(SURFACE_WORDS_PER_ROW == 80) ? 1 : -1];
|
typedef char amigaPresentScanAbiWords[(SURFACE_WORDS_PER_ROW == 80) ? 1 : -1];
|
||||||
|
|
@ -253,19 +252,15 @@ typedef char amigaPresentScanAbiRec[(sizeof(AmigaPresentRunT) == 8) ? 1 : -1];
|
||||||
// presents still run.
|
// presents still run.
|
||||||
static bool gPrevAnyDirty = true;
|
static bool gPrevAnyDirty = true;
|
||||||
|
|
||||||
// Change detection is the core gStageScbDirty / gStagePaletteDirty flags
|
// Change detection is the core jlgStageScbDirty / jlgStagePaletteDirty flags
|
||||||
// (surfaceInternal.h): the mutation APIs set them, and jlpPresent clears
|
// (surfaceInternal.h): the mutation APIs set them, and jlpPresent clears
|
||||||
// them once it has written the band list they ask for (bandListUpdate).
|
// them once it has written the band list they ask for (bandListUpdate).
|
||||||
|
|
||||||
// ----- Internal helpers (alphabetical) -----
|
// ----- Internal helpers (alphabetical) -----
|
||||||
|
|
||||||
// Blitter geometry shared by every block op here: each plane row is
|
// Blitter geometry shared by every block op here: each plane row is
|
||||||
// AMIGA_BYTES_PER_ROW (40) bytes = 20 words. BLTSIZE packs the row
|
// AMIGA_BYTES_PER_ROW (40) bytes = 20 words (amigaCustom.h has BLTSIZE).
|
||||||
// count in the high 10 bits (<< 6) and the word count in the low 6
|
|
||||||
// bits (a width of 0 would mean 64 words, but our rows are 20 wide).
|
|
||||||
#define AMIGA_BLT_WORDS_PER_ROW (AMIGA_BYTES_PER_ROW / 2u) // 20
|
#define AMIGA_BLT_WORDS_PER_ROW (AMIGA_BYTES_PER_ROW / 2u) // 20
|
||||||
#define AMIGA_BLT_SIZESHIFT 6
|
|
||||||
#define AMIGA_BLT_MASK_ALL 0xFFFFu
|
|
||||||
|
|
||||||
// BLTSIZE field guard (P7-3 audit follow-up): height occupies bits
|
// BLTSIZE field guard (P7-3 audit follow-up): height occupies bits
|
||||||
// 15..6 (10-bit == max 1024 rows) and width bits 5..0 (6-bit == max 64
|
// 15..6 (10-bit == max 1024 rows) and width bits 5..0 (6-bit == max 64
|
||||||
|
|
@ -275,14 +270,6 @@ static bool gPrevAnyDirty = true;
|
||||||
// catches a future taller/wider surface before it silently truncates.
|
// catches a future taller/wider surface before it silently truncates.
|
||||||
typedef char amigaBltSizeFieldsFit[(JL_SURFACE_HEIGHT <= 1024 && AMIGA_BLT_WORDS_PER_ROW <= 64) ? 1 : -1];
|
typedef char amigaBltSizeFieldsFit[(JL_SURFACE_HEIGHT <= 1024 && AMIGA_BLT_WORDS_PER_ROW <= 64) ? 1 : -1];
|
||||||
|
|
||||||
// Minterms for the masked solid-fill blit (amigaBlitterMaskedFillOne):
|
|
||||||
// A is the constant $FFFF cut to the rect by the first/last-word masks,
|
|
||||||
// C and D both address the destination. Set-bits writes D = A OR C
|
|
||||||
// (force the masked bits on, preserve the rest); clear-bits writes
|
|
||||||
// D = (NOT A) AND C (force the masked bits off, preserve the rest).
|
|
||||||
#define AMIGA_BLT_MINTERM_SET 0xFAu // D = A | C
|
|
||||||
#define AMIGA_BLT_MINTERM_CLEAR 0x0Au // D = ~A & C
|
|
||||||
|
|
||||||
// Blitter-vs-CPU crossover for jlpSurfaceCopyRect, in per-plane words
|
// Blitter-vs-CPU crossover for jlpSurfaceCopyRect, in per-plane words
|
||||||
// (height * widthWords). Below this the OwnBlitter + per-plane
|
// (height * widthWords). Below this the OwnBlitter + per-plane
|
||||||
// WaitBlit + register-setup fixed cost (~1,300 CPU cycles) beats a
|
// WaitBlit + register-setup fixed cost (~1,300 CPU cycles) beats a
|
||||||
|
|
@ -424,9 +411,9 @@ static uint8_t bandListUpdate(const jlSurfaceT *src) {
|
||||||
if (gTrampoline == NULL) {
|
if (gTrampoline == NULL) {
|
||||||
copperInstall(src);
|
copperInstall(src);
|
||||||
if (gCopperReady) {
|
if (gCopperReady) {
|
||||||
gStageScbDirty = false;
|
jlgStageScbDirty = false;
|
||||||
gStagePaletteDirty = false;
|
jlgStagePaletteDirty = false;
|
||||||
gStagePaletteDirtyMask = 0;
|
jlgStagePaletteDirtyMask = 0;
|
||||||
}
|
}
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
|
|
@ -437,7 +424,7 @@ static uint8_t bandListUpdate(const jlSurfaceT *src) {
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
flip = FLIP_PLANES;
|
flip = FLIP_PLANES;
|
||||||
} else if (!gStageScbDirty && !gStagePaletteDirty) {
|
} else if (!jlgStageScbDirty && !jlgStagePaletteDirty) {
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
flipWait(FLIP_LIST);
|
flipWait(FLIP_LIST);
|
||||||
|
|
@ -446,9 +433,9 @@ static uint8_t bandListUpdate(const jlSurfaceT *src) {
|
||||||
return flip;
|
return flip;
|
||||||
}
|
}
|
||||||
gListFront = back;
|
gListFront = back;
|
||||||
gStageScbDirty = false;
|
jlgStageScbDirty = false;
|
||||||
gStagePaletteDirty = false;
|
jlgStagePaletteDirty = false;
|
||||||
gStagePaletteDirtyMask = 0;
|
jlgStagePaletteDirtyMask = 0;
|
||||||
return (uint8_t)(flip | FLIP_LIST);
|
return (uint8_t)(flip | FLIP_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -593,12 +580,12 @@ static void copperInstall(const jlSurfaceT *src) {
|
||||||
CMOVE(ucl, custom.copjmp2, 0);
|
CMOVE(ucl, custom.copjmp2, 0);
|
||||||
CEND(ucl);
|
CEND(ucl);
|
||||||
Forbid();
|
Forbid();
|
||||||
if (gScreen->ViewPort.UCopIns != NULL) {
|
if (jlgScreen->ViewPort.UCopIns != NULL) {
|
||||||
FreeVPortCopLists(&gScreen->ViewPort);
|
FreeVPortCopLists(&jlgScreen->ViewPort);
|
||||||
}
|
}
|
||||||
gScreen->ViewPort.UCopIns = ucl;
|
jlgScreen->ViewPort.UCopIns = ucl;
|
||||||
Permit();
|
Permit();
|
||||||
MakeScreen(gScreen);
|
MakeScreen(jlgScreen);
|
||||||
view = ViewAddress();
|
view = ViewAddress();
|
||||||
Forbid();
|
Forbid();
|
||||||
MrgCop(view);
|
MrgCop(view);
|
||||||
|
|
@ -841,8 +828,8 @@ static void trampolinePoint(const uint16_t *list) {
|
||||||
// reaching the display -- the failure class the golden stage hashes
|
// reaching the display -- the failure class the golden stage hashes
|
||||||
// cannot see. Reads gFrontIdx's planes while the VBL has not yet made
|
// cannot see. Reads gFrontIdx's planes while the VBL has not yet made
|
||||||
// the flip a present asked for, and before the first copper install. Always compiled (the probe build links the stock library;
|
// the flip a present asked for, and before the first copper install. Always compiled (the probe build links the stock library;
|
||||||
// gPresentCopiedBytes precedent).
|
// jlgPresentCopiedBytes precedent).
|
||||||
void amigaProbePresentPageSums(const jlSurfaceT *src, uint32_t *frontSum, uint32_t *shadowSum) {
|
void jlAmigaProbePresentPageSums(const jlSurfaceT *src, uint32_t *frontSum, uint32_t *shadowSum) {
|
||||||
AmigaPlanarT *pd;
|
AmigaPlanarT *pd;
|
||||||
const uint8_t *fp;
|
const uint8_t *fp;
|
||||||
const uint8_t *sp;
|
const uint8_t *sp;
|
||||||
|
|
@ -897,7 +884,7 @@ bool jlpInit(const jlConfigT *config) {
|
||||||
// defaults.
|
// defaults.
|
||||||
gBitMap = (struct BitMap *)AllocMem((ULONG)sizeof(struct BitMap), (ULONG)(MEMF_PUBLIC | MEMF_CLEAR));
|
gBitMap = (struct BitMap *)AllocMem((ULONG)sizeof(struct BitMap), (ULONG)(MEMF_PUBLIC | MEMF_CLEAR));
|
||||||
if (gBitMap == NULL) {
|
if (gBitMap == NULL) {
|
||||||
coreSetError("out of memory for the screen's bitmap");
|
jlCoreSetError("out of memory for the screen's bitmap");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
InitBitMap(gBitMap, (LONG)AMIGA_BITPLANES, (LONG)JL_SURFACE_WIDTH, (LONG)JL_SURFACE_HEIGHT);
|
InitBitMap(gBitMap, (LONG)AMIGA_BITPLANES, (LONG)JL_SURFACE_WIDTH, (LONG)JL_SURFACE_HEIGHT);
|
||||||
|
|
@ -910,7 +897,7 @@ bool jlpInit(const jlConfigT *config) {
|
||||||
gPlanesB[i] = (UBYTE *)AllocMem((ULONG)AMIGA_PLANE_SIZE, (ULONG)(MEMF_CHIP | MEMF_CLEAR));
|
gPlanesB[i] = (UBYTE *)AllocMem((ULONG)AMIGA_PLANE_SIZE, (ULONG)(MEMF_CHIP | MEMF_CLEAR));
|
||||||
if (gPlanes[i] == NULL || gPlanesB[i] == NULL) {
|
if (gPlanes[i] == NULL || gPlanesB[i] == NULL) {
|
||||||
screenPlanesFree();
|
screenPlanesFree();
|
||||||
coreSetError("not enough chip RAM for the two screen pages");
|
jlCoreSetError("not enough chip RAM for the two screen pages");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Set A is the initial front buffer; BPL DMA fetches from it
|
// Set A is the initial front buffer; BPL DMA fetches from it
|
||||||
|
|
@ -931,11 +918,11 @@ bool jlpInit(const jlConfigT *config) {
|
||||||
// real planar screen rather than an RTG substitute. SA_BitMap
|
// real planar screen rather than an RTG substitute. SA_BitMap
|
||||||
// makes Intuition use OUR pre-allocated planes; CloseScreen will
|
// makes Intuition use OUR pre-allocated planes; CloseScreen will
|
||||||
// not free them -- our jlpShutdown does.
|
// not free them -- our jlpShutdown does.
|
||||||
gScreen = OpenScreenTags(NULL, (ULONG)SA_Width, (ULONG)JL_SURFACE_WIDTH, (ULONG)SA_Height, (ULONG)JL_SURFACE_HEIGHT, (ULONG)SA_Depth, (ULONG)AMIGA_BITPLANES, (ULONG)SA_DisplayID, (ULONG)(PAL_MONITOR_ID | LORES_KEY), (ULONG)SA_DetailPen, (ULONG)0, (ULONG)SA_BlockPen, (ULONG)1, (ULONG)SA_Title, (ULONG)"JoeyLib", (ULONG)SA_Type, (ULONG)CUSTOMSCREEN, (ULONG)SA_Quiet, (ULONG)TRUE, (ULONG)SA_BitMap, (ULONG)gBitMap, TAG_DONE);
|
jlgScreen = OpenScreenTags(NULL, (ULONG)SA_Width, (ULONG)JL_SURFACE_WIDTH, (ULONG)SA_Height, (ULONG)JL_SURFACE_HEIGHT, (ULONG)SA_Depth, (ULONG)AMIGA_BITPLANES, (ULONG)SA_DisplayID, (ULONG)(PAL_MONITOR_ID | LORES_KEY), (ULONG)SA_DetailPen, (ULONG)0, (ULONG)SA_BlockPen, (ULONG)1, (ULONG)SA_Title, (ULONG)"JoeyLib", (ULONG)SA_Type, (ULONG)CUSTOMSCREEN, (ULONG)SA_Quiet, (ULONG)TRUE, (ULONG)SA_BitMap, (ULONG)gBitMap, TAG_DONE);
|
||||||
|
|
||||||
if (gScreen == NULL) {
|
if (jlgScreen == NULL) {
|
||||||
screenPlanesFree();
|
screenPlanesFree();
|
||||||
coreSetError("Intuition could not open the 320x200 PAL screen");
|
jlCoreSetError("Intuition could not open the 320x200 PAL screen");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Force COLOR00 to black so the overscan/border region around the
|
// Force COLOR00 to black so the overscan/border region around the
|
||||||
|
|
@ -943,7 +930,7 @@ bool jlpInit(const jlConfigT *config) {
|
||||||
// on the first jlStagePresent. Apps that paint a non-black bg need
|
// on the first jlStagePresent. Apps that paint a non-black bg need
|
||||||
// do nothing -- the first present's band list loads their palette[0]
|
// do nothing -- the first present's band list loads their palette[0]
|
||||||
// into COLOR00, above the screen too (patchTopColors).
|
// into COLOR00, above the screen too (patchTopColors).
|
||||||
SetRGB4(&gScreen->ViewPort, 0, 0, 0, 0);
|
SetRGB4(&jlgScreen->ViewPort, 0, 0, 0, 0);
|
||||||
installVblServer();
|
installVblServer();
|
||||||
frameHzMeasure();
|
frameHzMeasure();
|
||||||
// The millisecond clock's E-clock (jlpMillisElapsed); without it the
|
// The millisecond clock's E-clock (jlpMillisElapsed); without it the
|
||||||
|
|
@ -975,7 +962,7 @@ bool jlpInit(const jlConfigT *config) {
|
||||||
* the copy must move the counter with it. */
|
* the copy must move the counter with it. */
|
||||||
static void amigaPresentFlushRun(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t runFirst, int16_t runLast, uint16_t byteStart, uint16_t byteLen, bool *blitOwned) {
|
static void amigaPresentFlushRun(AmigaPlanarT *pd, UBYTE *destPlanes[AMIGA_BITPLANES], int16_t runFirst, int16_t runLast, uint16_t byteStart, uint16_t byteLen, bool *blitOwned) {
|
||||||
amigaPresentRectInner(pd, destPlanes, runFirst, runLast, byteStart, byteLen, blitOwned);
|
amigaPresentRectInner(pd, destPlanes, runFirst, runLast, byteStart, byteLen, blitOwned);
|
||||||
gPresentCopiedBytes += (uint32_t)(runLast - runFirst + 1) * byteLen * AMIGA_BITPLANES;
|
jlgPresentCopiedBytes += (uint32_t)(runLast - runFirst + 1) * byteLen * AMIGA_BITPLANES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1093,13 +1080,18 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
uint8_t backIdx;
|
uint8_t backIdx;
|
||||||
uint8_t flip;
|
uint8_t flip;
|
||||||
|
|
||||||
if (src == NULL || gScreen == NULL) {
|
// Every way out leaves the stage's bands clean (JL_PRESENT_CLEARS_BANDS):
|
||||||
|
// the union scan clears them as it reads them, the idle early-out finds
|
||||||
|
// them clean, and the returns before them clear them here.
|
||||||
|
if (src == NULL || jlgScreen == NULL) {
|
||||||
|
jlStageDirtyClearAll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
flip = bandListUpdate(src);
|
flip = bandListUpdate(src);
|
||||||
|
|
||||||
pd = (AmigaPlanarT *)src->portData;
|
pd = (AmigaPlanarT *)src->portData;
|
||||||
if (pd == NULL) {
|
if (pd == NULL) {
|
||||||
|
jlStageDirtyClearAll();
|
||||||
flipRequest(flip);
|
flipRequest(flip);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1113,7 +1105,7 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
// clean row's minWord is the 0xFF CLEAN sentinel and any touched
|
// clean row's minWord is the 0xFF CLEAN sentinel and any touched
|
||||||
// row's is <= STAGE_DIRTY_FULL_MAX (79), so 4 clean rows read
|
// row's is <= STAGE_DIRTY_FULL_MAX (79), so 4 clean rows read
|
||||||
// exactly 0xFFFFFFFF -- 50 long tests replace 200 byte tests on
|
// exactly 0xFFFFFFFF -- 50 long tests replace 200 byte tests on
|
||||||
// the common all-clean idle frame (gStageMinWord is aligned(2),
|
// the common all-clean idle frame (jlgStageMinWord is aligned(2),
|
||||||
// all the 68000 needs for move.l). A failing group byte-walks its
|
// all the 68000 needs for move.l). A failing group byte-walks its
|
||||||
// 4 rows with the same per-row test as before, so the first-dirty
|
// 4 rows with the same per-row test as before, so the first-dirty
|
||||||
// row and the scanned-rows accounting are exactly the
|
// row and the scanned-rows accounting are exactly the
|
||||||
|
|
@ -1123,7 +1115,7 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
// guards that), preserving the 2-buffer staleness convergence.
|
// guards that), preserving the 2-buffer staleness convergence.
|
||||||
if (!gPrevAnyDirty) {
|
if (!gPrevAnyDirty) {
|
||||||
typedef uint32_t __attribute__((may_alias)) BandGroupT;
|
typedef uint32_t __attribute__((may_alias)) BandGroupT;
|
||||||
const BandGroupT *minGroups = (const BandGroupT *)(const void *)gStageMinWord;
|
const BandGroupT *minGroups = (const BandGroupT *)(const void *)jlgStageMinWord;
|
||||||
bool anyDirty = false;
|
bool anyDirty = false;
|
||||||
uint8_t g;
|
uint8_t g;
|
||||||
|
|
||||||
|
|
@ -1143,7 +1135,7 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gPresentScannedRows += (uint32_t)(anyDirty ? (y + 1) : JL_SURFACE_HEIGHT);
|
jlgPresentScannedRows += (uint32_t)(anyDirty ? (y + 1) : JL_SURFACE_HEIGHT);
|
||||||
if (!anyDirty) {
|
if (!anyDirty) {
|
||||||
flipRequest(flip);
|
flipRequest(flip);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1184,11 +1176,11 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
// register. Returns the run count (low 16) plus anyUnionDirty (bit
|
// register. Returns the run count (low 16) plus anyUnionDirty (bit
|
||||||
// 16) and anyCurDirty (bit 17) -- the two flags the flip and
|
// 16) and anyCurDirty (bit 17) -- the two flags the flip and
|
||||||
// idle-early-out logic below need.
|
// idle-early-out logic below need.
|
||||||
scanRes = amigaPresentScan68k(gStageMinWord, gStageMaxWord, gPrevStageMinWord, gPrevStageMaxWord, gPresentRuns);
|
scanRes = jlAmigaPresentScan68k(jlgStageMinWord, jlgStageMaxWord, gPrevStageMinWord, gPrevStageMaxWord, gPresentRuns);
|
||||||
numRuns = (uint16_t)scanRes;
|
numRuns = (uint16_t)scanRes;
|
||||||
anyUnionDirty = (scanRes & 0x00010000UL) != 0u;
|
anyUnionDirty = (scanRes & 0x00010000UL) != 0u;
|
||||||
anyCurDirty = (scanRes & 0x00020000UL) != 0u;
|
anyCurDirty = (scanRes & 0x00020000UL) != 0u;
|
||||||
gPresentScannedRows += JL_SURFACE_HEIGHT;
|
jlgPresentScannedRows += JL_SURFACE_HEIGHT;
|
||||||
|
|
||||||
// Flush the collected runs. Kept separate from the scan so the
|
// Flush the collected runs. Kept separate from the scan so the
|
||||||
// scan lives in hand-rolled asm (presentScan.s) without touching the
|
// scan lives in hand-rolled asm (presentScan.s) without touching the
|
||||||
|
|
@ -1230,8 +1222,8 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
// now folded into the scan loop above, eliminating the second
|
// now folded into the scan loop above, eliminating the second
|
||||||
// 200-row pass this block used to run. gPrevAnyDirty records whether
|
// 200-row pass this block used to run. gPrevAnyDirty records whether
|
||||||
// the CURRENT frame touched any row so the next present can take the
|
// the CURRENT frame touched any row so the next present can take the
|
||||||
// idle-frame early-out. jlStagePresent clears the live cur bands
|
// idle-frame early-out. The scan has left the live cur bands clean
|
||||||
// after jlpPresent returns; the fused snapshot already captured them.
|
// (JL_PRESENT_CLEARS_BANDS); the fused snapshot captured them first.
|
||||||
gPrevAnyDirty = anyCurDirty;
|
gPrevAnyDirty = anyCurDirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1419,16 +1411,16 @@ void jlpShutdown(void) {
|
||||||
// Tear down the VBL server before closing the screen so the
|
// Tear down the VBL server before closing the screen so the
|
||||||
// interrupt chain is clean if anything else is watching.
|
// interrupt chain is clean if anything else is watching.
|
||||||
removeVblServer();
|
removeVblServer();
|
||||||
if (gScreen != NULL) {
|
if (jlgScreen != NULL) {
|
||||||
// CloseScreen should free attached UCopList, but be explicit
|
// CloseScreen should free attached UCopList, but be explicit
|
||||||
// to catch any case where the screen close path skips it.
|
// to catch any case where the screen close path skips it.
|
||||||
Forbid();
|
Forbid();
|
||||||
if (gScreen->ViewPort.UCopIns != NULL) {
|
if (jlgScreen->ViewPort.UCopIns != NULL) {
|
||||||
FreeVPortCopLists(&gScreen->ViewPort);
|
FreeVPortCopLists(&jlgScreen->ViewPort);
|
||||||
}
|
}
|
||||||
Permit();
|
Permit();
|
||||||
CloseScreen(gScreen);
|
CloseScreen(jlgScreen);
|
||||||
gScreen = NULL;
|
jlgScreen = NULL;
|
||||||
// Intuition loads a display without the screen, and its jump to
|
// Intuition loads a display without the screen, and its jump to
|
||||||
// the band lists, at the next VBL: past that, nothing runs them.
|
// the band lists, at the next VBL: past that, nothing runs them.
|
||||||
WaitTOF();
|
WaitTOF();
|
||||||
|
|
@ -1447,15 +1439,14 @@ void jlpShutdown(void) {
|
||||||
// four separate plane buffers of 40 bytes per row, so a span of pixels is the
|
// four separate plane buffers of 40 bytes per row, so a span of pixels is the
|
||||||
// same bytes in the same places. src/m68k/planarFill.s does the area fills,
|
// same bytes in the same places. src/m68k/planarFill.s does the area fills,
|
||||||
// src/m68k/planarPlot.s the pixel walkers.
|
// src/m68k/planarPlot.s the pixel walkers.
|
||||||
extern void surface68kFillCircle(uint8_t *const *planes, uint16_t cx, uint16_t cy, uint16_t r, uint16_t color);
|
extern void jlSurface68kFillCircle(uint8_t *const *planes, uint16_t cx, uint16_t cy, uint16_t r, uint16_t color);
|
||||||
extern void surface68kFillRect(uint8_t *const *planes, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
|
|
||||||
// src/m68k/planarPlot.s, shared with the X68000: Bresenham walkers with 16-way
|
// 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
|
// colour 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
|
// 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
|
// walkers. Straight lines never reach jlpDrawLine -- draw.c diverts those to
|
||||||
// jlFillRect -- so the line body walks the general diagonal case only.
|
// jlFillRect -- so the line body walks the general diagonal case only.
|
||||||
extern void surface68kPlanarCircle(uint8_t *const *planes, uint16_t cx, uint16_t cy, uint16_t r, uint16_t color);
|
extern void jlSurface68kPlanarCircle(uint8_t *const *planes, uint16_t cx, uint16_t cy, uint16_t r, uint16_t color);
|
||||||
extern void surface68kPlanarLine(uint8_t *const *planes, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
|
extern void jlSurface68kPlanarLine(uint8_t *const *planes, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
|
||||||
|
|
||||||
|
|
||||||
// Amiga is pure planar (s->pixels is NULL). Each jlp<Op> below does the planar
|
// Amiga is pure planar (s->pixels is NULL). Each jlp<Op> below does the planar
|
||||||
|
|
@ -1476,7 +1467,7 @@ void jlpSurfaceClear(jlSurfaceT *s, uint8_t doubled) {
|
||||||
colorIndex = (uint8_t)(doubled & 0x0Fu);
|
colorIndex = (uint8_t)(doubled & 0x0Fu);
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
pd = (AmigaPlanarT *)s->portData;
|
||||||
if (!pd->chip) {
|
if (!pd->chip) {
|
||||||
surface68kFillRect(pd->planes, 0u, 0u, JL_SURFACE_WIDTH, JL_SURFACE_HEIGHT, colorIndex);
|
jlSurface68kFillRect(pd->planes, 0u, 0u, JL_SURFACE_WIDTH, JL_SURFACE_HEIGHT, colorIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1532,7 +1523,7 @@ void jlpFillRect(jlSurfaceT *s, int16_t x, int16_t y, int16_t w, int16_t h, uint
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!pd->chip) {
|
if (!pd->chip) {
|
||||||
surface68kFillRect(pd->planes, (uint16_t)x, (uint16_t)y, (uint16_t)w, (uint16_t)h, colorIndex);
|
jlSurface68kFillRect(pd->planes, (uint16_t)x, (uint16_t)y, (uint16_t)w, (uint16_t)h, colorIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1594,198 +1585,6 @@ void jlpFillRect(jlSurfaceT *s, int16_t x, int16_t y, int16_t w, int16_t h, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Phase 9 sprite save/restore plane-backup hooks.
|
|
||||||
// Sprite save at (x, y, w, h) records the destination region into the
|
|
||||||
// caller's backup buffer; restore writes it back. x and w are 2-pixel
|
|
||||||
// aligned by cross-platform code.
|
|
||||||
//
|
|
||||||
// Backup layout matches the CHUNKY-packed format the cross-platform
|
|
||||||
// contract sizes for ((widthTiles*4)+1)*(heightTiles*8) bytes (see
|
|
||||||
// include/joey/sprite.h): one byte per pixel-pair, 2 pixels/byte,
|
|
||||||
// high nibble = even (left) pixel, low nibble = odd (right) pixel,
|
|
||||||
// (w/2) bytes per row, (w/2)*h bytes total. This is the same format
|
|
||||||
// the Atari ST port uses and never overruns the contract buffer --
|
|
||||||
// unlike a plane-major layout, which would round x down to an 8-pixel
|
|
||||||
// boundary and width up and then write (4*roundedW/8)*h = up to
|
|
||||||
// (w/2 + 3)*h bytes for an odd-of-8 x, overrunning the buffer. Each
|
|
||||||
// pixel's 4bpp color is assembled from / written to the 4 plane bits
|
|
||||||
// at byte x>>3, bit 0x80 >> (x & 7).
|
|
||||||
|
|
||||||
void jlpSpriteSavePlanes(const jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t *dstPlaneBytes) {
|
|
||||||
const AmigaPlanarT *pd;
|
|
||||||
uint16_t pairs;
|
|
||||||
uint16_t row;
|
|
||||||
uint16_t pair;
|
|
||||||
uint8_t *dst;
|
|
||||||
|
|
||||||
pd = (const AmigaPlanarT *)s->portData;
|
|
||||||
if (pd == NULL || dstPlaneBytes == NULL || w == 0u || h == 0u) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Byte-aligned fast path: raw plane-byte copy, no per-pixel c2p.
|
|
||||||
* When x and w are both 8-px aligned and the rect is fully on
|
|
||||||
* surface, each sprite tile column maps to whole plane bytes, so we
|
|
||||||
* save the raw bytes -- ~8x cheaper than the chunky walker below and
|
|
||||||
* an exact round-trip on restore (the sprite fills its bytes, so
|
|
||||||
* restoring whole bytes touches no neighbor pixels). The backup row
|
|
||||||
* holds plane0..plane3, each `wb` bytes, contiguous. The chunky
|
|
||||||
* walker below stays as the arbitrary-x fallback. */
|
|
||||||
if ((x & 7) == 0 && (w & 7) == 0 && x >= 0 && (x + (int16_t)w) <= JL_SURFACE_WIDTH && y >= 0 && (y + (int16_t)h) <= JL_SURFACE_HEIGHT) {
|
|
||||||
uint16_t wb;
|
|
||||||
uint16_t wb2;
|
|
||||||
uint16_t wb3;
|
|
||||||
uint16_t startByte;
|
|
||||||
uint16_t rowOff;
|
|
||||||
uint8_t *bdst;
|
|
||||||
uint16_t col;
|
|
||||||
|
|
||||||
wb = (uint16_t)(w >> 3);
|
|
||||||
wb2 = (uint16_t)(wb << 1);
|
|
||||||
wb3 = (uint16_t)(wb2 + wb);
|
|
||||||
startByte = (uint16_t)((uint16_t)x >> 3);
|
|
||||||
rowOff = (uint16_t)((uint16_t)y * AMIGA_BYTES_PER_ROW + startByte);
|
|
||||||
bdst = dstPlaneBytes;
|
|
||||||
for (row = 0; row < (uint16_t)h; row++) {
|
|
||||||
const uint8_t *s0 = pd->planes[0] + rowOff;
|
|
||||||
const uint8_t *s1 = pd->planes[1] + rowOff;
|
|
||||||
const uint8_t *s2 = pd->planes[2] + rowOff;
|
|
||||||
const uint8_t *s3 = pd->planes[3] + rowOff;
|
|
||||||
for (col = 0; col < wb; col++) {
|
|
||||||
bdst[col] = s0[col];
|
|
||||||
bdst[wb + col] = s1[col];
|
|
||||||
bdst[wb2 + col] = s2[col];
|
|
||||||
bdst[wb3 + col] = s3[col];
|
|
||||||
}
|
|
||||||
bdst += (uint16_t)(wb << 2);
|
|
||||||
rowOff += AMIGA_BYTES_PER_ROW;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pairs = (uint16_t)(w >> 1);
|
|
||||||
for (row = 0; row < (uint16_t)h; row++) {
|
|
||||||
const uint8_t *p0 = pd->planes[0] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
const uint8_t *p1 = pd->planes[1] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
const uint8_t *p2 = pd->planes[2] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
const uint8_t *p3 = pd->planes[3] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
dst = dstPlaneBytes + (uint16_t)(row * pairs);
|
|
||||||
for (pair = 0; pair < pairs; pair++) {
|
|
||||||
uint16_t px = (uint16_t)(x + (int16_t)(pair << 1));
|
|
||||||
uint16_t byteOff = (uint16_t)(px >> 3);
|
|
||||||
uint8_t maskHi = (uint8_t)(0x80u >> (px & 7u));
|
|
||||||
uint8_t maskLo = (uint8_t)(0x80u >> ((px + 1u) & 7u));
|
|
||||||
uint16_t byteOffLo = (uint16_t)((px + 1u) >> 3);
|
|
||||||
uint8_t hi = 0u;
|
|
||||||
uint8_t lo = 0u;
|
|
||||||
if (p0[byteOff] & maskHi) {
|
|
||||||
hi = (uint8_t)(hi | 0x01u);
|
|
||||||
}
|
|
||||||
if (p1[byteOff] & maskHi) {
|
|
||||||
hi = (uint8_t)(hi | 0x02u);
|
|
||||||
}
|
|
||||||
if (p2[byteOff] & maskHi) {
|
|
||||||
hi = (uint8_t)(hi | 0x04u);
|
|
||||||
}
|
|
||||||
if (p3[byteOff] & maskHi) {
|
|
||||||
hi = (uint8_t)(hi | 0x08u);
|
|
||||||
}
|
|
||||||
if (p0[byteOffLo] & maskLo) {
|
|
||||||
lo = (uint8_t)(lo | 0x01u);
|
|
||||||
}
|
|
||||||
if (p1[byteOffLo] & maskLo) {
|
|
||||||
lo = (uint8_t)(lo | 0x02u);
|
|
||||||
}
|
|
||||||
if (p2[byteOffLo] & maskLo) {
|
|
||||||
lo = (uint8_t)(lo | 0x04u);
|
|
||||||
}
|
|
||||||
if (p3[byteOffLo] & maskLo) {
|
|
||||||
lo = (uint8_t)(lo | 0x08u);
|
|
||||||
}
|
|
||||||
dst[pair] = (uint8_t)((hi << 4) | lo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlpSpriteRestorePlanes(jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, const uint8_t *srcPlaneBytes) {
|
|
||||||
AmigaPlanarT *pd;
|
|
||||||
uint16_t pairs;
|
|
||||||
uint16_t row;
|
|
||||||
uint16_t pair;
|
|
||||||
const uint8_t *src;
|
|
||||||
|
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
|
||||||
if (pd == NULL || srcPlaneBytes == NULL || w == 0u || h == 0u) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Byte-aligned fast path mirroring jlpSpriteSavePlanes: writes the
|
|
||||||
* raw plane bytes straight back (whole-byte round-trip), skipping
|
|
||||||
* the per-pixel chunky->planar masking of the walker below. */
|
|
||||||
if ((x & 7) == 0 && (w & 7) == 0 && x >= 0 && (x + (int16_t)w) <= JL_SURFACE_WIDTH && y >= 0 && (y + (int16_t)h) <= JL_SURFACE_HEIGHT) {
|
|
||||||
uint16_t wb;
|
|
||||||
uint16_t wb2;
|
|
||||||
uint16_t wb3;
|
|
||||||
uint16_t startByte;
|
|
||||||
uint16_t rowOff;
|
|
||||||
const uint8_t *bsrc;
|
|
||||||
uint16_t col;
|
|
||||||
|
|
||||||
wb = (uint16_t)(w >> 3);
|
|
||||||
wb2 = (uint16_t)(wb << 1);
|
|
||||||
wb3 = (uint16_t)(wb2 + wb);
|
|
||||||
startByte = (uint16_t)((uint16_t)x >> 3);
|
|
||||||
rowOff = (uint16_t)((uint16_t)y * AMIGA_BYTES_PER_ROW + startByte);
|
|
||||||
bsrc = srcPlaneBytes;
|
|
||||||
for (row = 0; row < (uint16_t)h; row++) {
|
|
||||||
uint8_t *d0 = pd->planes[0] + rowOff;
|
|
||||||
uint8_t *d1 = pd->planes[1] + rowOff;
|
|
||||||
uint8_t *d2 = pd->planes[2] + rowOff;
|
|
||||||
uint8_t *d3 = pd->planes[3] + rowOff;
|
|
||||||
for (col = 0; col < wb; col++) {
|
|
||||||
d0[col] = bsrc[col];
|
|
||||||
d1[col] = bsrc[wb + col];
|
|
||||||
d2[col] = bsrc[wb2 + col];
|
|
||||||
d3[col] = bsrc[wb3 + col];
|
|
||||||
}
|
|
||||||
bsrc += (uint16_t)(wb << 2);
|
|
||||||
rowOff += AMIGA_BYTES_PER_ROW;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pairs = (uint16_t)(w >> 1);
|
|
||||||
for (row = 0; row < (uint16_t)h; row++) {
|
|
||||||
uint8_t *p0 = pd->planes[0] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
uint8_t *p1 = pd->planes[1] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
uint8_t *p2 = pd->planes[2] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
uint8_t *p3 = pd->planes[3] + ((uint16_t)y + row) * AMIGA_BYTES_PER_ROW;
|
|
||||||
src = srcPlaneBytes + (uint16_t)(row * pairs);
|
|
||||||
for (pair = 0; pair < pairs; pair++) {
|
|
||||||
uint8_t b = src[pair];
|
|
||||||
uint8_t hi = (uint8_t)(b >> 4);
|
|
||||||
uint8_t lo = (uint8_t)(b & 0x0Fu);
|
|
||||||
uint16_t px = (uint16_t)(x + (int16_t)(pair << 1));
|
|
||||||
uint16_t byteOff = (uint16_t)(px >> 3);
|
|
||||||
uint8_t maskHi = (uint8_t)(0x80u >> (px & 7u));
|
|
||||||
uint8_t notHi = (uint8_t)(~maskHi);
|
|
||||||
uint16_t byteOffLo = (uint16_t)((px + 1u) >> 3);
|
|
||||||
uint8_t maskLo = (uint8_t)(0x80u >> ((px + 1u) & 7u));
|
|
||||||
uint8_t notLo = (uint8_t)(~maskLo);
|
|
||||||
p0[byteOff] = (uint8_t)((p0[byteOff] & notHi) | ((hi & 0x01u) ? maskHi : 0u));
|
|
||||||
p1[byteOff] = (uint8_t)((p1[byteOff] & notHi) | ((hi & 0x02u) ? maskHi : 0u));
|
|
||||||
p2[byteOff] = (uint8_t)((p2[byteOff] & notHi) | ((hi & 0x04u) ? maskHi : 0u));
|
|
||||||
p3[byteOff] = (uint8_t)((p3[byteOff] & notHi) | ((hi & 0x08u) ? maskHi : 0u));
|
|
||||||
p0[byteOffLo] = (uint8_t)((p0[byteOffLo] & notLo) | ((lo & 0x01u) ? maskLo : 0u));
|
|
||||||
p1[byteOffLo] = (uint8_t)((p1[byteOffLo] & notLo) | ((lo & 0x02u) ? maskLo : 0u));
|
|
||||||
p2[byteOffLo] = (uint8_t)((p2[byteOffLo] & notLo) | ((lo & 0x04u) ? maskLo : 0u));
|
|
||||||
p3[byteOffLo] = (uint8_t)((p3[byteOffLo] & notLo) | ((lo & 0x08u) ? maskLo : 0u));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The sprite interpreter (uncompiled sprites, and compiled ones drawn partly
|
// The sprite interpreter (uncompiled sprites, and compiled ones drawn partly
|
||||||
// off the surface): the planar ports' shared walker over the sprite's
|
// off the surface): the planar ports' shared walker over the sprite's
|
||||||
// planar cache, src/m68k/planarSprite.c.
|
// planar cache, src/m68k/planarSprite.c.
|
||||||
|
|
@ -1796,7 +1595,7 @@ void jlpSpriteDrawPlanes(jlSurfaceT *s, const jlSpriteT *sp, int16_t x, int16_t
|
||||||
if (pd == NULL) {
|
if (pd == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
planarSpriteDraw(pd->planes, sp, x, y);
|
jlPlanarSpriteDraw(pd->planes, sp, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1884,76 +1683,13 @@ void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Phase 4 planar dual-write helper: set one pixel's bit in each of
|
|
||||||
// the four shadow planes. Caller (the per-primitive walker below) has
|
|
||||||
// already validated (x, y) is on-surface. byteOff is computed once
|
|
||||||
// and reused across all four planes since stride is the same in each.
|
|
||||||
|
|
||||||
|
|
||||||
static void amigaPlanarSetPixel(AmigaPlanarT *pd, int16_t x, int16_t y, uint8_t color) {
|
|
||||||
uint16_t byteOff;
|
|
||||||
uint8_t bitMask;
|
|
||||||
uint8_t notMask;
|
|
||||||
uint8_t *p0;
|
|
||||||
uint8_t *p1;
|
|
||||||
uint8_t *p2;
|
|
||||||
uint8_t *p3;
|
|
||||||
|
|
||||||
byteOff = (uint16_t)((uint16_t)y * AMIGA_BYTES_PER_ROW + ((uint16_t)x >> 3));
|
|
||||||
bitMask = (uint8_t)(0x80u >> ((uint16_t)x & 7u));
|
|
||||||
notMask = (uint8_t)~bitMask;
|
|
||||||
|
|
||||||
/* Unroll the 4-plane loop. Loop counter + array indexing inside
|
|
||||||
* the hot per-pixel path is the gating cost on circle outlines:
|
|
||||||
* UBER jlDrawCircle r=80 calls this ~640 times per call. */
|
|
||||||
p0 = &pd->planes[0][byteOff];
|
|
||||||
p1 = &pd->planes[1][byteOff];
|
|
||||||
p2 = &pd->planes[2][byteOff];
|
|
||||||
p3 = &pd->planes[3][byteOff];
|
|
||||||
|
|
||||||
if (color & 0x01u) {
|
|
||||||
*p0 = (uint8_t)(*p0 | bitMask);
|
|
||||||
} else {
|
|
||||||
*p0 = (uint8_t)(*p0 & notMask);
|
|
||||||
}
|
|
||||||
if (color & 0x02u) {
|
|
||||||
*p1 = (uint8_t)(*p1 | bitMask);
|
|
||||||
} else {
|
|
||||||
*p1 = (uint8_t)(*p1 & notMask);
|
|
||||||
}
|
|
||||||
if (color & 0x04u) {
|
|
||||||
*p2 = (uint8_t)(*p2 | bitMask);
|
|
||||||
} else {
|
|
||||||
*p2 = (uint8_t)(*p2 & notMask);
|
|
||||||
}
|
|
||||||
if (color & 0x08u) {
|
|
||||||
*p3 = (uint8_t)(*p3 | bitMask);
|
|
||||||
} else {
|
|
||||||
*p3 = (uint8_t)(*p3 & notMask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlpDrawPixel(jlSurfaceT *s, uint16_t x, uint16_t y, uint8_t colorIndex) {
|
|
||||||
AmigaPlanarT *pd;
|
|
||||||
uint8_t nibLo;
|
|
||||||
if (s->portData == NULL) {
|
|
||||||
jlpGenericDrawPixel(s, x, y, colorIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nibLo = (uint8_t)(colorIndex & 0x0Fu);
|
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
|
||||||
amigaPlanarSetPixel(pd, (int16_t)x, (int16_t)y, nibLo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlpDrawLine(jlSurfaceT *s, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t colorIndex) {
|
void jlpDrawLine(jlSurfaceT *s, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t colorIndex) {
|
||||||
AmigaPlanarT *pd;
|
AmigaPlanarT *pd;
|
||||||
if (s->portData == NULL) {
|
if (s->portData == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
pd = (AmigaPlanarT *)s->portData;
|
||||||
surface68kPlanarLine(pd->planes, x0, y0, x1, y1, (uint16_t)(colorIndex & 0x0Fu));
|
jlSurface68kPlanarLine(pd->planes, x0, y0, x1, y1, (uint16_t)(colorIndex & 0x0Fu));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1963,7 +1699,7 @@ void jlpDrawCircle(jlSurfaceT *s, int16_t cx, int16_t cy, uint16_t r, uint8_t co
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
pd = (AmigaPlanarT *)s->portData;
|
||||||
surface68kPlanarCircle(pd->planes, (uint16_t)cx, (uint16_t)cy, r, (uint16_t)(colorIndex & 0x0Fu));
|
jlSurface68kPlanarCircle(pd->planes, (uint16_t)cx, (uint16_t)cy, r, (uint16_t)(colorIndex & 0x0Fu));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1988,7 +1724,7 @@ bool jlpFillCircle(jlSurfaceT *s, int16_t cx, int16_t cy, uint16_t r, uint8_t co
|
||||||
if (s->portData == NULL) {
|
if (s->portData == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
surface68kFillCircle(((AmigaPlanarT *)s->portData)->planes, (uint16_t)cx, (uint16_t)cy, r, colorIndex);
|
jlSurface68kFillCircle(((AmigaPlanarT *)s->portData)->planes, (uint16_t)cx, (uint16_t)cy, r, colorIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2144,132 +1880,12 @@ bool jlpFloodScanRowPlanes(const jlSurfaceT *s, int16_t leftX, int16_t rightX, i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== Phase 9 reader hooks: pure-planar Amiga implementations =====
|
// The shared planar hash (src/m68k/planarHash.c), over separate planes.
|
||||||
*
|
|
||||||
* Cross-platform code that USED to read s->pixels (chunky shadow) now
|
|
||||||
* goes through these. On Amiga the chunky shadow doesn't exist;
|
|
||||||
* pixels are derived on demand by walking the plane bits.
|
|
||||||
*
|
|
||||||
* Per-pixel assembly: for pixel (x, y), read bit (7 - x%8) from plane
|
|
||||||
* byte at row*40 + x/8 in each of the 4 planes. Color index = sum
|
|
||||||
* of (bit_p << p). */
|
|
||||||
|
|
||||||
uint8_t jlpSamplePixel(const jlSurfaceT *s, int16_t x, int16_t y) {
|
|
||||||
AmigaPlanarT *pd;
|
|
||||||
uint16_t byteOff;
|
|
||||||
uint8_t bitMask;
|
|
||||||
uint8_t color;
|
|
||||||
uint16_t i;
|
|
||||||
|
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
|
||||||
if (pd == NULL) {
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
byteOff = (uint16_t)((uint16_t)y * AMIGA_BYTES_PER_ROW + ((uint16_t)x >> 3));
|
|
||||||
bitMask = (uint8_t)(0x80u >> ((uint16_t)x & 7u));
|
|
||||||
color = 0u;
|
|
||||||
for (i = 0; i < AMIGA_BITPLANES; i++) {
|
|
||||||
if (pd->planes[i][byteOff] & bitMask) {
|
|
||||||
color = (uint8_t)(color | (1u << i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Reverse-c2p: per row, derive 160 chunky bytes from 40 plane bytes
|
|
||||||
* (per plane, 4 planes). Used by jlpSurfaceHash to fold the planar
|
|
||||||
* surface into the same byte-stream the chunky ports hash, so cross-
|
|
||||||
* port hash comparisons stay valid.
|
|
||||||
* Walks 8 pixels per planar-byte column; per pixel assembles nibble
|
|
||||||
* from 4 plane bits. Output: 4 chunky bytes per planar-byte column
|
|
||||||
* (since 8 pixels = 4 chunky bytes at 2px/byte). */
|
|
||||||
static void amigaPlanesToChunkyRow(const AmigaPlanarT *pd, int16_t y, uint8_t *dstChunkyRow) {
|
|
||||||
uint16_t col;
|
|
||||||
uint16_t byteOff;
|
|
||||||
uint8_t b0;
|
|
||||||
uint8_t b1;
|
|
||||||
uint8_t b2;
|
|
||||||
uint8_t b3;
|
|
||||||
uint8_t pix;
|
|
||||||
uint8_t bitMask;
|
|
||||||
uint16_t p;
|
|
||||||
|
|
||||||
for (col = 0; col < AMIGA_BYTES_PER_ROW; col++) {
|
|
||||||
byteOff = (uint16_t)((uint16_t)y * AMIGA_BYTES_PER_ROW + col);
|
|
||||||
b0 = pd->planes[0][byteOff];
|
|
||||||
b1 = pd->planes[1][byteOff];
|
|
||||||
b2 = pd->planes[2][byteOff];
|
|
||||||
b3 = pd->planes[3][byteOff];
|
|
||||||
/* For each of 8 pixels in this planar byte (bit 7 = leftmost),
|
|
||||||
* assemble nibble from the 4 plane bits and pack into chunky
|
|
||||||
* bytes (high nibble = even pixel, low nibble = odd pixel). */
|
|
||||||
for (p = 0; p < 8u; p++) {
|
|
||||||
bitMask = (uint8_t)(0x80u >> p);
|
|
||||||
pix = 0u;
|
|
||||||
if (b0 & bitMask) {
|
|
||||||
pix = (uint8_t)(pix | 1u);
|
|
||||||
}
|
|
||||||
if (b1 & bitMask) {
|
|
||||||
pix = (uint8_t)(pix | 2u);
|
|
||||||
}
|
|
||||||
if (b2 & bitMask) {
|
|
||||||
pix = (uint8_t)(pix | 4u);
|
|
||||||
}
|
|
||||||
if (b3 & bitMask) {
|
|
||||||
pix = (uint8_t)(pix | 8u);
|
|
||||||
}
|
|
||||||
if ((p & 1u) == 0u) {
|
|
||||||
dstChunkyRow[col * 4u + (p >> 1)] = (uint8_t)(pix << 4);
|
|
||||||
} else {
|
|
||||||
dstChunkyRow[col * 4u + (p >> 1)] = (uint8_t)(dstChunkyRow[col * 4u + (p >> 1)] | pix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t jlpSurfaceHash(const jlSurfaceT *s) {
|
uint32_t jlpSurfaceHash(const jlSurfaceT *s) {
|
||||||
AmigaPlanarT *pd;
|
const AmigaPlanarT *pd;
|
||||||
uint16_t lo = 0xACE1u;
|
|
||||||
uint16_t hi = 0x1357u;
|
|
||||||
uint16_t n;
|
|
||||||
uint16_t v;
|
|
||||||
int16_t row;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t chunkyRow[JL_SURFACE_BYTES_PER_ROW];
|
|
||||||
const uint16_t *w;
|
|
||||||
|
|
||||||
pd = (AmigaPlanarT *)s->portData;
|
pd = (const AmigaPlanarT *)s->portData;
|
||||||
if (pd == NULL) {
|
return jlPlanarSurfaceHash(s, (const uint8_t *const *)pd->planes, AMIGA_BYTES_PER_ROW, PLANAR_HASH_SEPARATE_PAIR_STEP);
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
/* Pixel hash: derive chunky one row at a time, fold byte-by-byte
|
|
||||||
* via the shared SURFACE_HASH_MIX_BYTE so cross-port hash
|
|
||||||
* matches. */
|
|
||||||
for (row = 0; row < JL_SURFACE_HEIGHT; row++) {
|
|
||||||
amigaPlanesToChunkyRow(pd, row, chunkyRow);
|
|
||||||
for (n = 0; n < JL_SURFACE_BYTES_PER_ROW; n++) {
|
|
||||||
b = chunkyRow[n];
|
|
||||||
SURFACE_HASH_MIX_BYTE(lo, hi, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SCB: byte sequence, endian-independent.
|
|
||||||
for (n = 0; n < (uint16_t)JL_SURFACE_HEIGHT; n++) {
|
|
||||||
b = s->scb[n];
|
|
||||||
SURFACE_HASH_MIX_BYTE(lo, hi, b);
|
|
||||||
}
|
|
||||||
/* Palette: read uint16 values, fold high-then-low for endian-
|
|
||||||
* independence. */
|
|
||||||
w = &s->palette[0][0];
|
|
||||||
for (n = 0; n < (uint16_t)JL_SURFACE_PALETTE_ENTRIES; n++) {
|
|
||||||
v = *w++;
|
|
||||||
b = (uint8_t)((v >> 8) & 0xFFu);
|
|
||||||
SURFACE_HASH_MIX_BYTE(lo, hi, b);
|
|
||||||
b = (uint8_t)(v & 0xFFu);
|
|
||||||
SURFACE_HASH_MIX_BYTE(lo, hi, b);
|
|
||||||
}
|
|
||||||
return ((uint32_t)hi << 16) | (uint32_t)lo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2313,7 +1929,7 @@ uint8_t *jlpStageAllocPixels(void) {
|
||||||
/* Phase 9: Amiga has no chunky shadow. The stage pixels pointer
|
/* Phase 9: Amiga has no chunky shadow. The stage pixels pointer
|
||||||
* stays NULL; cross-platform code reads pixels via jlpSamplePixel
|
* stays NULL; cross-platform code reads pixels via jlpSamplePixel
|
||||||
* (or the other chunky reader ops) which read from planes. NULL is
|
* (or the other chunky reader ops) which read from planes. NULL is
|
||||||
* a valid return -- cross-platform stageAlloc treats NULL as
|
* a valid return -- cross-platform jlStageAlloc treats NULL as
|
||||||
* "port has no chunky storage" and skips the chunky memset. */
|
* "port has no chunky storage" and skips the chunky memset. */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ struct Library *LowLevelBase = NULL;
|
||||||
|
|
||||||
// ----- External from hal.c -----
|
// ----- External from hal.c -----
|
||||||
|
|
||||||
extern struct Screen *gScreen;
|
extern struct Screen *jlgScreen;
|
||||||
|
|
||||||
// ----- Empty pointer image -----
|
// ----- Empty pointer image -----
|
||||||
//
|
//
|
||||||
|
|
@ -192,16 +192,19 @@ static bool gJoyLocked[JL_JOYSTICK_COUNT];
|
||||||
// lowlevel.library reports a one-button stick and a two-button one alike
|
// lowlevel.library reports a one-button stick and a two-button one alike
|
||||||
// (a second button is pin 9, which reads as released until it is pressed),
|
// (a second button is pin 9, which reads as released until it is pressed),
|
||||||
// and most Amiga owners use one-button Atari sticks. So a joystick counts as
|
// and most Amiga owners use one-button Atari sticks. So a joystick counts as
|
||||||
// one-button (gJoyOneButton: a hold of its button is button 1) until its
|
// one-button (jlgJoyOneButton: a hold of its button is button 1) until its
|
||||||
// second button has been seen pressed, and again once its port has no
|
// second button has been seen pressed, and again once its port has no
|
||||||
// controller. A CD32 game controller always has both.
|
// controller. A CD32 game controller always has both.
|
||||||
static bool gJoySecondButton[JL_JOYSTICK_COUNT];
|
static bool gJoySecondButton[JL_JOYSTICK_COUNT];
|
||||||
|
|
||||||
// Keys and mouse buttons as Intuition's messages leave them, and pressed
|
// Keys and mouse buttons as Intuition's messages leave them, and pressed
|
||||||
// since the last poll: a tap between two polls still reads down for one
|
// since the last poll: a tap between two polls still reads down for one
|
||||||
// (inputLatch).
|
// (jlInputLatch). The keys are latched only when a message changed them or
|
||||||
|
// the last latch showed a tap that must now go up.
|
||||||
static uint8_t gKeyHeld [JL_KEY_COUNT];
|
static uint8_t gKeyHeld [JL_KEY_COUNT];
|
||||||
static uint8_t gKeyTapped [JL_KEY_COUNT];
|
static uint8_t gKeyTapped [JL_KEY_COUNT];
|
||||||
|
static bool gKeysChanged;
|
||||||
|
static bool gKeyTapShown;
|
||||||
static uint8_t gMouseHeld [JL_MOUSE_BUTTON_COUNT];
|
static uint8_t gMouseHeld [JL_MOUSE_BUTTON_COUNT];
|
||||||
static uint8_t gMouseTapped [JL_MOUSE_BUTTON_COUNT];
|
static uint8_t gMouseTapped [JL_MOUSE_BUTTON_COUNT];
|
||||||
|
|
||||||
|
|
@ -266,23 +269,25 @@ static void drainMessages(void) {
|
||||||
if (!isRelease) {
|
if (!isRelease) {
|
||||||
gKeyTapped[key] = 1u;
|
gKeyTapped[key] = 1u;
|
||||||
}
|
}
|
||||||
|
gKeysChanged = true;
|
||||||
}
|
}
|
||||||
// MapRawKey returns -1 on overflow (composition longer
|
// MapRawKey returns -1 on overflow (composition longer
|
||||||
// than the buffer) -- nothing usable, skip.
|
// than the buffer) -- nothing usable, skip.
|
||||||
// A Ctrl chord types nothing (inputCharTyped); Alt is a
|
// A Ctrl chord types nothing (jlInputCharTyped); Alt is a
|
||||||
// layer of the keymap's characters.
|
// layer of the keymap's characters.
|
||||||
for (mapIdx = 0; mapIdx < mapCount; mapIdx++) {
|
for (mapIdx = 0; mapIdx < mapCount; mapIdx++) {
|
||||||
inputCharTyped((uint8_t)mapBuf[mapIdx], mapChord);
|
jlInputCharTyped((uint8_t)mapBuf[mapIdx], mapChord);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IDCMP_MOUSEMOVE:
|
case IDCMP_MOUSEMOVE:
|
||||||
inputMouseMove(msgMouseX, msgMouseY);
|
jlInputMouseMove(msgMouseX, msgMouseY);
|
||||||
break;
|
break;
|
||||||
case IDCMP_INACTIVEWINDOW:
|
case IDCMP_INACTIVEWINDOW:
|
||||||
// The keys and buttons it lets go elsewhere are never
|
// The keys and buttons it lets go elsewhere are never
|
||||||
// reported here: let them all go now.
|
// reported here: let them all go now.
|
||||||
memset(gKeyHeld, 0, sizeof(gKeyHeld));
|
memset(gKeyHeld, 0, sizeof(gKeyHeld));
|
||||||
memset(gMouseHeld, 0, sizeof(gMouseHeld));
|
memset(gMouseHeld, 0, sizeof(gMouseHeld));
|
||||||
|
gKeysChanged = true;
|
||||||
break;
|
break;
|
||||||
case IDCMP_MOUSEBUTTONS:
|
case IDCMP_MOUSEBUTTONS:
|
||||||
switch (msgCode) {
|
switch (msgCode) {
|
||||||
|
|
@ -308,7 +313,7 @@ static void drainMessages(void) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// MOUSEBUTTONS messages also carry the mouse position.
|
// MOUSEBUTTONS messages also carry the mouse position.
|
||||||
inputMouseMove(msgMouseX, msgMouseY);
|
jlInputMouseMove(msgMouseX, msgMouseY);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -357,40 +362,40 @@ static void pollJoysticks(void) {
|
||||||
gJoyLocked[stick] = SetJoyPortAttrs(gAmigaPortForStick[stick], SJA_Type, SJA_TYPE_JOYSTK, TAG_DONE) != FALSE;
|
gJoyLocked[stick] = SetJoyPortAttrs(gAmigaPortForStick[stick], SJA_Type, SJA_TYPE_JOYSTK, TAG_DONE) != FALSE;
|
||||||
}
|
}
|
||||||
if (type != JP_TYPE_JOYSTK && type != JP_TYPE_GAMECTLR) {
|
if (type != JP_TYPE_JOYSTK && type != JP_TYPE_GAMECTLR) {
|
||||||
gJoyConnected[stick] = false;
|
jlgJoyConnected[stick] = false;
|
||||||
gJoyAxisX[stick] = 0;
|
jlgJoyAxisX[stick] = 0;
|
||||||
gJoyAxisY[stick] = 0;
|
jlgJoyAxisY[stick] = 0;
|
||||||
gJoyOneButton[stick] = false;
|
jlgJoyOneButton[stick] = false;
|
||||||
gJoySecondButton[stick] = false;
|
gJoySecondButton[stick] = false;
|
||||||
gJoyButtonState[stick][JL_JOY_BUTTON_0] = false;
|
jlgJoyButtonState[stick][JL_JOY_BUTTON_0] = false;
|
||||||
gJoyButtonState[stick][JL_JOY_BUTTON_1] = false;
|
jlgJoyButtonState[stick][JL_JOY_BUTTON_1] = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gJoyConnected[stick] = true;
|
jlgJoyConnected[stick] = true;
|
||||||
|
|
||||||
gJoyAxisX[stick] = 0;
|
jlgJoyAxisX[stick] = 0;
|
||||||
if (state & JPF_JOY_LEFT) {
|
if (state & JPF_JOY_LEFT) {
|
||||||
gJoyAxisX[stick] = JL_JOYSTICK_AXIS_MIN;
|
jlgJoyAxisX[stick] = JL_JOYSTICK_AXIS_MIN;
|
||||||
}
|
}
|
||||||
if (state & JPF_JOY_RIGHT) {
|
if (state & JPF_JOY_RIGHT) {
|
||||||
gJoyAxisX[stick] = JL_JOYSTICK_AXIS_MAX;
|
jlgJoyAxisX[stick] = JL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
gJoyAxisY[stick] = 0;
|
jlgJoyAxisY[stick] = 0;
|
||||||
if (state & JPF_JOY_UP) {
|
if (state & JPF_JOY_UP) {
|
||||||
gJoyAxisY[stick] = JL_JOYSTICK_AXIS_MIN;
|
jlgJoyAxisY[stick] = JL_JOYSTICK_AXIS_MIN;
|
||||||
}
|
}
|
||||||
if (state & JPF_JOY_DOWN) {
|
if (state & JPF_JOY_DOWN) {
|
||||||
gJoyAxisY[stick] = JL_JOYSTICK_AXIS_MAX;
|
jlgJoyAxisY[stick] = JL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard 1-button stick reports JPF_BUTTON_RED only. CD32
|
// Standard 1-button stick reports JPF_BUTTON_RED only. CD32
|
||||||
// game controllers also report BLUE for the second face button.
|
// game controllers also report BLUE for the second face button.
|
||||||
gJoyButtonState[stick][JL_JOY_BUTTON_0] = (state & JPF_BUTTON_RED) != 0;
|
jlgJoyButtonState[stick][JL_JOY_BUTTON_0] = (state & JPF_BUTTON_RED) != 0;
|
||||||
gJoyButtonState[stick][JL_JOY_BUTTON_1] = (state & JPF_BUTTON_BLUE) != 0;
|
jlgJoyButtonState[stick][JL_JOY_BUTTON_1] = (state & JPF_BUTTON_BLUE) != 0;
|
||||||
if (type == JP_TYPE_GAMECTLR || (state & JPF_BUTTON_BLUE) != 0) {
|
if (type == JP_TYPE_GAMECTLR || (state & JPF_BUTTON_BLUE) != 0) {
|
||||||
gJoySecondButton[stick] = true;
|
gJoySecondButton[stick] = true;
|
||||||
}
|
}
|
||||||
gJoyOneButton[stick] = !gJoySecondButton[stick];
|
jlgJoyOneButton[stick] = !gJoySecondButton[stick];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,21 +410,22 @@ void jlpJoystickReset(jlJoystickE js) {
|
||||||
|
|
||||||
|
|
||||||
void jlpInputInit(void) {
|
void jlpInputInit(void) {
|
||||||
memset(gKeyState, 0, sizeof(gKeyState));
|
memset(jlgKeyState, 0, sizeof(jlgKeyState));
|
||||||
memset(gKeyPrev, 0, sizeof(gKeyPrev));
|
memset(jlgKeyPrev, 0, sizeof(jlgKeyPrev));
|
||||||
memset(gKeyHeld, 0, sizeof(gKeyHeld));
|
memset(gKeyHeld, 0, sizeof(gKeyHeld));
|
||||||
memset(gKeyTapped, 0, sizeof(gKeyTapped));
|
memset(gKeyTapped, 0, sizeof(gKeyTapped));
|
||||||
|
gKeysChanged = true;
|
||||||
memset(gMouseHeld, 0, sizeof(gMouseHeld));
|
memset(gMouseHeld, 0, sizeof(gMouseHeld));
|
||||||
memset(gMouseTapped, 0, sizeof(gMouseTapped));
|
memset(gMouseTapped, 0, sizeof(gMouseTapped));
|
||||||
|
|
||||||
if (gScreen == NULL) {
|
if (jlgScreen == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gWindow = OpenWindowTags(NULL, (ULONG)WA_CustomScreen, (ULONG)gScreen, (ULONG)WA_Left, (ULONG)0, (ULONG)WA_Top, (ULONG)0, (ULONG)WA_Width, (ULONG)gScreen->Width, (ULONG)WA_Height, (ULONG)gScreen->Height, (ULONG)WA_Flags, (ULONG)(WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_ACTIVATE | WFLG_RMBTRAP | WFLG_NOCAREREFRESH | WFLG_REPORTMOUSE), (ULONG)WA_IDCMP, (ULONG)(IDCMP_RAWKEY | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_INACTIVEWINDOW), TAG_DONE);
|
gWindow = OpenWindowTags(NULL, (ULONG)WA_CustomScreen, (ULONG)jlgScreen, (ULONG)WA_Left, (ULONG)0, (ULONG)WA_Top, (ULONG)0, (ULONG)WA_Width, (ULONG)jlgScreen->Width, (ULONG)WA_Height, (ULONG)jlgScreen->Height, (ULONG)WA_Flags, (ULONG)(WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_ACTIVATE | WFLG_RMBTRAP | WFLG_NOCAREREFRESH | WFLG_REPORTMOUSE), (ULONG)WA_IDCMP, (ULONG)(IDCMP_RAWKEY | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_INACTIVEWINDOW), TAG_DONE);
|
||||||
|
|
||||||
if (gWindow != NULL) {
|
if (gWindow != NULL) {
|
||||||
gMouseAttached = true; // Intuition delivers IDCMP mouse events on this window
|
jlgMouseAttached = true; // Intuition delivers IDCMP mouse events on this window
|
||||||
|
|
||||||
gEmptyPointer = (UWORD *)AllocMem((ULONG)EMPTY_POINTER_BYTES, (ULONG)(MEMF_CHIP | MEMF_CLEAR));
|
gEmptyPointer = (UWORD *)AllocMem((ULONG)EMPTY_POINTER_BYTES, (ULONG)(MEMF_CHIP | MEMF_CLEAR));
|
||||||
if (gEmptyPointer != NULL) {
|
if (gEmptyPointer != NULL) {
|
||||||
|
|
@ -442,8 +448,11 @@ void jlpInputInit(void) {
|
||||||
|
|
||||||
void jlpInputPoll(void) {
|
void jlpInputPoll(void) {
|
||||||
drainMessages();
|
drainMessages();
|
||||||
inputLatch(gKeyState, gKeyHeld, gKeyTapped, JL_KEY_COUNT);
|
if (gKeysChanged || gKeyTapShown) {
|
||||||
inputLatch(gMouseButtonState, gMouseHeld, gMouseTapped, JL_MOUSE_BUTTON_COUNT);
|
gKeysChanged = false;
|
||||||
|
gKeyTapShown = jlInputLatch(jlgKeyState, gKeyHeld, gKeyTapped, JL_KEY_COUNT);
|
||||||
|
}
|
||||||
|
jlInputLatch(jlgMouseButtonState, gMouseHeld, gMouseTapped, JL_MOUSE_BUTTON_COUNT);
|
||||||
pollJoysticks();
|
pollJoysticks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@
|
||||||
// "<name> failed to load" to the CLI and exits(20) before main() runs.
|
// "<name> failed to load" to the CLI and exits(20) before main() runs.
|
||||||
//
|
//
|
||||||
// That behavior kills the examples on AROS, which does not provide
|
// That behavior kills the examples on AROS, which does not provide
|
||||||
// locale.library as a disk library. libnix unconditionally pulls the
|
// locale.library as a disk library, nor does a Kickstart 3.1 machine whose
|
||||||
// locale.library entry into the auto-open list because C runtime
|
// boot disk lacks LIBS:locale.library. A program that uses the time
|
||||||
// utilities (e.g. tzset via time()) take LocaleBase as an argument to
|
// (time(), localtime()) has locale.library on that list: libnix's tzset,
|
||||||
// ConvToLower -- even when the runtime does not actually need a real
|
// which its start-up runs, reads the time zone from the locale. It checks
|
||||||
// Locale to run.
|
// LocaleBase first, so with the library missing it leaves the time zone at
|
||||||
|
// GMT.
|
||||||
//
|
//
|
||||||
// Our replacement walks the same list but records NULL for any
|
// Our replacement walks the same list but records NULL for any
|
||||||
// library that cannot be opened, then continues. Code that actually
|
// library that cannot be opened, then continues. Code that actually
|
||||||
|
|
@ -72,14 +73,3 @@ void __exitlibraries(void) {
|
||||||
|
|
||||||
ADD2INIT(__initlibraries, -79);
|
ADD2INIT(__initlibraries, -79);
|
||||||
ADD2EXIT(__exitlibraries, -79);
|
ADD2EXIT(__exitlibraries, -79);
|
||||||
|
|
||||||
|
|
||||||
// libnix's tzset dereferences LocaleBase to call locale.library's
|
|
||||||
// ConvToLower. On AROS (where locale.library is unavailable), our
|
|
||||||
// tolerant __initlibraries leaves LocaleBase NULL, which would crash
|
|
||||||
// the first time time() is called (via tzset). Our pattern example
|
|
||||||
// uses time() for the 5-second display; override tzset with a no-op
|
|
||||||
// so time() can run. Difference-based timing is unaffected because
|
|
||||||
// any DST correction applied consistently cancels out.
|
|
||||||
void tzset(void) {
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
| * dirty row: snapshot gPrev[y] <- cur[y], accumulate anyCurDirty,
|
| * dirty row: snapshot gPrev[y] <- cur[y], accumulate anyCurDirty,
|
||||||
| fold union(cur,prev) into a plane-byte span, clamp, then coalesce
|
| fold union(cur,prev) into a plane-byte span, clamp, then coalesce
|
||||||
| consecutive same-span rows into runs.
|
| consecutive same-span rows into runs.
|
||||||
|
| and leaves the stage's bands clean as it reads them, so jlStagePresent
|
||||||
|
| does not clear them again (JL_PRESENT_CLEARS_BANDS): a clean group is
|
||||||
|
| clean already, and a dirty one's four rows are reset, as longs, once read.
|
||||||
| Runs are emitted into the caller's gPresentRuns[] as 8-byte records
|
| Runs are emitted into the caller's gPresentRuns[] as 8-byte records
|
||||||
| {firstRow.w, lastRow.w, byteStart.w, byteLen.w}; the caller flushes
|
| {firstRow.w, lastRow.w, byteStart.w, byteLen.w}; the caller flushes
|
||||||
| them (blitter or CPU). lastRow is NOT tracked in a register: a run is
|
| them (blitter or CPU). lastRow is NOT tracked in a register: a run is
|
||||||
|
|
@ -30,7 +33,7 @@
|
||||||
| ABI: cdecl. d2-d7/a2-a6 callee-save. Byte loads use moveq #0 first
|
| ABI: cdecl. d2-d7/a2-a6 callee-save. Byte loads use moveq #0 first
|
||||||
| (partial-register-write hygiene -- the fold and emit do word math).
|
| (partial-register-write hygiene -- the fold and emit do word math).
|
||||||
|
|
|
|
||||||
| uint32_t amigaPresentScan68k(const uint8_t *curMin, const uint8_t *curMax,
|
| uint32_t jlAmigaPresentScan68k(const uint8_t *curMin, const uint8_t *curMax,
|
||||||
| uint8_t *prvMin, uint8_t *prvMax,
|
| uint8_t *prvMin, uint8_t *prvMax,
|
||||||
| AmigaPresentRunT *runList);
|
| AmigaPresentRunT *runList);
|
||||||
|
|
|
|
||||||
|
|
@ -39,7 +42,7 @@
|
||||||
| bit 17 = anyCurDirty (current frame touched >=1 row).
|
| bit 17 = anyCurDirty (current frame touched >=1 row).
|
||||||
|
|
|
|
||||||
| Register allocation across the row loop:
|
| Register allocation across the row loop:
|
||||||
| a0 = gStageMinWord walker a1 = gStageMaxWord walker
|
| a0 = jlgStageMinWord walker a1 = jlgStageMaxWord walker
|
||||||
| a2 = gPrevStageMinWord (r/w) a3 = gPrevStageMaxWord (r/w)
|
| a2 = gPrevStageMinWord (r/w) a3 = gPrevStageMaxWord (r/w)
|
||||||
| a4 = run-list write cursor a5 = run-list base (for the count)
|
| a4 = run-list write cursor a5 = run-list base (for the count)
|
||||||
| d2 = y (row index 0..199) d4 = runFirst (-1.w == no open run)
|
| d2 = y (row index 0..199) d4 = runFirst (-1.w == no open run)
|
||||||
|
|
@ -49,9 +52,9 @@
|
||||||
|
|
||||||
.text
|
.text
|
||||||
|
|
||||||
.globl _amigaPresentScan68k
|
.globl _jlAmigaPresentScan68k
|
||||||
|
|
||||||
_amigaPresentScan68k:
|
_jlAmigaPresentScan68k:
|
||||||
movem.l %d2-%d7/%a2-%a6,-(%sp) | 44 bytes saved
|
movem.l %d2-%d7/%a2-%a6,-(%sp) | 44 bytes saved
|
||||||
move.l 48(%sp),%a0 | curMin base
|
move.l 48(%sp),%a0 | curMin base
|
||||||
move.l 52(%sp),%a1 | curMax base
|
move.l 52(%sp),%a1 | curMax base
|
||||||
|
|
@ -157,10 +160,8 @@ _amigaPresentScan68k:
|
||||||
cmp.w #200,%d2
|
cmp.w #200,%d2
|
||||||
bge .scanEnd
|
bge .scanEnd
|
||||||
move.l (%a0),%d0 | 4 cur min bytes
|
move.l (%a0),%d0 | 4 cur min bytes
|
||||||
addq.l #1,%d0 | 0xFFFFFFFF+1 == 0 <=> all clean
|
and.l (%a2),%d0 | and the 4 prev ones
|
||||||
bne .groupDirty
|
addq.l #1,%d0 | 0xFFFFFFFF+1 == 0 <=> all clean in both
|
||||||
move.l (%a2),%d0 | 4 prev min bytes
|
|
||||||
addq.l #1,%d0
|
|
||||||
bne .groupDirty
|
bne .groupDirty
|
||||||
| all 4 rows clean-clean: close any open run, skip 4
|
| all 4 rows clean-clean: close any open run, skip 4
|
||||||
tst.w %d4
|
tst.w %d4
|
||||||
|
|
@ -182,11 +183,17 @@ _amigaPresentScan68k:
|
||||||
|
|
||||||
.groupDirty:
|
.groupDirty:
|
||||||
| Some row in the group is touched (cur or prev): process
|
| Some row in the group is touched (cur or prev): process
|
||||||
| all 4 rows with the exact old one-row logic, fall-through.
|
| all 4 rows with the exact old one-row logic, fall-through,
|
||||||
|
| then leave the stage's four rows clean for the next frame
|
||||||
|
| (JL_PRESENT_CLEARS_BANDS), as longs.
|
||||||
PS_ONE_ROW
|
PS_ONE_ROW
|
||||||
PS_ONE_ROW
|
PS_ONE_ROW
|
||||||
PS_ONE_ROW
|
PS_ONE_ROW
|
||||||
PS_ONE_ROW
|
PS_ONE_ROW
|
||||||
|
moveq #-1,%d0
|
||||||
|
move.l %d0,-4(%a0)
|
||||||
|
moveq #0,%d0
|
||||||
|
move.l %d0,-4(%a1)
|
||||||
bra .groupLoop
|
bra .groupLoop
|
||||||
|
|
||||||
.scanEnd:
|
.scanEnd:
|
||||||
|
|
|
||||||
|
|
@ -231,10 +231,10 @@ void jlpSerialFlush(void) {
|
||||||
// Why the last jlpSerialOpen failed, for host-side diagnosis: a silent false was unattributable
|
// Why the last jlpSerialOpen failed, for host-side diagnosis: a silent false was unattributable
|
||||||
// through three swallowing layers (this file -> core gate -> the client's "non-fatal" open).
|
// through three swallowing layers (this file -> core gate -> the client's "non-fatal" open).
|
||||||
// stage: 1=CreateMsgPort 2=CreateIORequest 3=OpenDevice 4=SDCMD_SETPARAMS 5=write IORequest.
|
// stage: 1=CreateMsgPort 2=CreateIORequest 3=OpenDevice 4=SDCMD_SETPARAMS 5=write IORequest.
|
||||||
uint8_t gAmigaSerialFailStage = 0u;
|
uint8_t jlgAmigaSerialFailStage = 0u;
|
||||||
BYTE gAmigaSerialFailErr = 0;
|
BYTE jlgAmigaSerialFailErr = 0;
|
||||||
BYTE gAmigaSerialFailIoErr = 0;
|
BYTE jlgAmigaSerialFailIoErr = 0;
|
||||||
uint8_t gAmigaSerialFailDevice = 0u;
|
uint8_t jlgAmigaSerialFailDevice = 0u;
|
||||||
|
|
||||||
|
|
||||||
bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
||||||
|
|
@ -245,17 +245,17 @@ bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
||||||
if (gOpen) {
|
if (gOpen) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
gAmigaSerialFailStage = 0u;
|
jlgAmigaSerialFailStage = 0u;
|
||||||
gAmigaSerialFailErr = 0;
|
jlgAmigaSerialFailErr = 0;
|
||||||
|
|
||||||
gPort = CreateMsgPort();
|
gPort = CreateMsgPort();
|
||||||
if (gPort == NULL) {
|
if (gPort == NULL) {
|
||||||
gAmigaSerialFailStage = 1u;
|
jlgAmigaSerialFailStage = 1u;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
gReadReq = (struct IOExtSer *)CreateIORequest(gPort, sizeof(struct IOExtSer));
|
gReadReq = (struct IOExtSer *)CreateIORequest(gPort, sizeof(struct IOExtSer));
|
||||||
if (gReadReq == NULL) {
|
if (gReadReq == NULL) {
|
||||||
gAmigaSerialFailStage = 2u;
|
jlgAmigaSerialFailStage = 2u;
|
||||||
serialTeardown(false);
|
serialTeardown(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -294,12 +294,12 @@ bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
||||||
err = OpenDevice((CONST_STRPTR)"serial.device", unit, (struct IORequest *)gReadReq, 0ul);
|
err = OpenDevice((CONST_STRPTR)"serial.device", unit, (struct IORequest *)gReadReq, 0ul);
|
||||||
}
|
}
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
gAmigaSerialFailStage = 3u;
|
jlgAmigaSerialFailStage = 3u;
|
||||||
gAmigaSerialFailErr = err;
|
jlgAmigaSerialFailErr = err;
|
||||||
// Distinguish a REAL open failure from a lying return value: io_Error is the device's own
|
// Distinguish a REAL open failure from a lying return value: io_Error is the device's own
|
||||||
// verdict and io_Device is non-NULL if the device actually attached.
|
// verdict and io_Device is non-NULL if the device actually attached.
|
||||||
gAmigaSerialFailIoErr = gReadReq->IOSer.io_Error;
|
jlgAmigaSerialFailIoErr = gReadReq->IOSer.io_Error;
|
||||||
gAmigaSerialFailDevice = (gReadReq->IOSer.io_Device != NULL) ? 1u : 0u;
|
jlgAmigaSerialFailDevice = (gReadReq->IOSer.io_Device != NULL) ? 1u : 0u;
|
||||||
serialTeardown(false);
|
serialTeardown(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -316,8 +316,8 @@ bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
||||||
// SERF_SHARED fallback) -- do not strip it here.
|
// SERF_SHARED fallback) -- do not strip it here.
|
||||||
gReadReq->IOSer.io_Command = SDCMD_SETPARAMS;
|
gReadReq->IOSer.io_Command = SDCMD_SETPARAMS;
|
||||||
if (DoIO((struct IORequest *)gReadReq) != 0) {
|
if (DoIO((struct IORequest *)gReadReq) != 0) {
|
||||||
gAmigaSerialFailStage = 4u;
|
jlgAmigaSerialFailStage = 4u;
|
||||||
gAmigaSerialFailErr = gReadReq->IOSer.io_Error;
|
jlgAmigaSerialFailErr = gReadReq->IOSer.io_Error;
|
||||||
serialTeardown(false);
|
serialTeardown(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +325,7 @@ bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
|
||||||
// Second request sharing the same open device (copy carries io_Device/Unit).
|
// Second request sharing the same open device (copy carries io_Device/Unit).
|
||||||
gWriteReq = (struct IOExtSer *)CreateIORequest(gPort, sizeof(struct IOExtSer));
|
gWriteReq = (struct IOExtSer *)CreateIORequest(gPort, sizeof(struct IOExtSer));
|
||||||
if (gWriteReq == NULL) {
|
if (gWriteReq == NULL) {
|
||||||
gAmigaSerialFailStage = 5u;
|
jlgAmigaSerialFailStage = 5u;
|
||||||
serialTeardown(false);
|
serialTeardown(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
// * jlpAudioPlayMod starts the module player at the same 12288 Hz mono
|
// * jlpAudioPlayMod starts the module player at the same 12288 Hz mono
|
||||||
// unsigned-8-bit output that the ISR consumes one byte at a time.
|
// unsigned-8-bit output that the ISR consumes one byte at a time.
|
||||||
// * Each Timer A tick the ISR pops one byte off the buffer, writes its
|
// * Each Timer A tick the ISR pops one byte off the buffer, writes its
|
||||||
// YM level (gYmLevel: the YM's sixteen levels are logarithmic, so each
|
// YM level (jlgYmLevel: the YM's sixteen levels are logarithmic, so each
|
||||||
// sample plays as the level nearest it on a straight line) to YM
|
// sample plays as the level nearest it on a straight line) to YM
|
||||||
// register 8 (channel A volume), and advances the play pointer; the
|
// register 8 (channel A volume), and advances the play pointer; the
|
||||||
// frame tick sees which half it has left. The refill itself happens in
|
// frame tick sees which half it has left. The refill itself happens in
|
||||||
|
|
@ -101,8 +101,8 @@
|
||||||
// The module player and the SFX overlay mix unsigned samples centred on $80.
|
// The module player and the SFX overlay mix unsigned samples centred on $80.
|
||||||
#define MIX_SILENCE_UNSIGNED MOD_PLAYER_SILENCE_U8
|
#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
|
// 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 (modPlayerSetU8Shift,
|
// and a full-scale sound effect each swing half a byte (jlModPlayerSetU8Shift,
|
||||||
// audioSfxSetShift). On the STe a side's two channels fill it; the plain
|
// jlAudioSfxSetShift). On the STe a side's two channels fill it; the plain
|
||||||
// ST's mono sums four, which clip when three or four peak together. The
|
// ST's mono sums four, which clip when three or four peak together. The
|
||||||
// voices come down to the same level (kYmVolume).
|
// voices come down to the same level (kYmVolume).
|
||||||
#define MIX_UNIT_SHIFT 1u
|
#define MIX_UNIT_SHIFT 1u
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
#define MIX_SILENCE MIX_SILENCE_UNSIGNED
|
#define MIX_SILENCE MIX_SILENCE_UNSIGNED
|
||||||
// The YM's channel-A volume is the DAC, and its sixteen levels are
|
// The YM's channel-A volume is the DAC, and its sixteen levels are
|
||||||
// logarithmic, about 3 dB apart: each sample is played as the level nearest
|
// logarithmic, about 3 dB apart: each sample is played as the level nearest
|
||||||
// its value on a straight line from level 0 to level 15 (gYmLevel), so the
|
// its value on a straight line from level 0 to level 15 (jlgYmLevel), so the
|
||||||
// waveform keeps its shape and silence sits near the middle, level 13. The
|
// waveform keeps its shape and silence sits near the middle, level 13. The
|
||||||
// levels' amplitudes, as the YM2149 puts them out (ST-Sound's measured
|
// levels' amplitudes, as the YM2149 puts them out (ST-Sound's measured
|
||||||
// table).
|
// table).
|
||||||
|
|
@ -179,7 +179,7 @@ static const uint16_t kYmAmplitude[YM_LEVELS] = {
|
||||||
|
|
||||||
// jlAudioTickRegister rides TOS's 200 Hz Timer C (both builds: the plain
|
// jlAudioTickRegister rides TOS's 200 Hz Timer C (both builds: the plain
|
||||||
// ST's Timer A is the PWM pump's, B the per-line palettes', D the serial
|
// ST's Timer A is the PWM pump's, B the per-line palettes', D the serial
|
||||||
// port's), chained in front of TOS's own handler by stTickIsr
|
// port's), chained in front of TOS's own handler by jlStTickIsr
|
||||||
// (audio_isr.s). A carry paces the program's rate against the 200 ticks a
|
// (audio_isr.s). A carry paces the program's rate against the 200 ticks a
|
||||||
// second, so the average is exact at any rate; at most 200 a second come
|
// second, so the average is exact at any rate; at most 200 a second come
|
||||||
// one to an interrupt, faster rates several to one. The callback runs in
|
// one to an interrupt, faster rates several to one. The callback runs in
|
||||||
|
|
@ -194,7 +194,7 @@ static const uint16_t kYmAmplitude[YM_LEVELS] = {
|
||||||
// these must match) or the STe's DMA chip's. Even-aligned: the DMA chip can
|
// these must match) or the STe's DMA chip's. Even-aligned: the DMA chip can
|
||||||
// only start a frame on an even address (2 is also mint a.out's alignment
|
// only start a frame on an even address (2 is also mint a.out's alignment
|
||||||
// cap).
|
// cap).
|
||||||
volatile uint8_t gMixBuf[MIX_BUFFER] __attribute__((aligned(2)));
|
volatile uint8_t jlgMixBuf[MIX_BUFFER] __attribute__((aligned(2)));
|
||||||
// The halves to refill: a frame tick that finds the play position has left
|
// The halves to refill: a frame tick that finds the play position has left
|
||||||
// a half since the last one raises its flag (pollRefill), and refills it.
|
// a half since the last one raises its flag (pollRefill), and refills it.
|
||||||
static uint8_t gNeedRefill[2] = { 0, 0 };
|
static uint8_t gNeedRefill[2] = { 0, 0 };
|
||||||
|
|
@ -203,11 +203,11 @@ static uint8_t gPlayHalf = 0u;
|
||||||
|
|
||||||
#ifndef JOEYLIB_ATARI_STE
|
#ifndef JOEYLIB_ATARI_STE
|
||||||
// The PWM ISR's play pointer and the buffer's end (audio_isr.s).
|
// The PWM ISR's play pointer and the buffer's end (audio_isr.s).
|
||||||
volatile uint8_t *gMixPos = NULL;
|
volatile uint8_t *jlgMixPos = NULL;
|
||||||
volatile uint8_t *gMixEnd = NULL;
|
volatile uint8_t *jlgMixEnd = NULL;
|
||||||
// Each sample's YM level (kYmAmplitude), built at jlpAudioInit: the ISR
|
// Each sample's YM level (kYmAmplitude), built at jlpAudioInit: the ISR
|
||||||
// plays gYmLevel[sample], from beside its own code (audio_isr.s).
|
// plays jlgYmLevel[sample], from beside its own code (audio_isr.s).
|
||||||
extern uint8_t gYmLevel[SAMPLE_VALUES];
|
extern uint8_t jlgYmLevel[SAMPLE_VALUES];
|
||||||
// The drain (PWM_RAMP_SAMPLES): the ramp down is written, from
|
// The drain (PWM_RAMP_SAMPLES): the ramp down is written, from
|
||||||
// PWM_DRAIN_MARGIN past gDrainFrom, where the ISR was; and the silence
|
// PWM_DRAIN_MARGIN past gDrainFrom, where the ISR was; and the silence
|
||||||
// before it (PWM_IDLE_MS), since gIdleSinceMs.
|
// before it (PWM_IDLE_MS), since gIdleSinceMs.
|
||||||
|
|
@ -216,7 +216,7 @@ static uint16_t gDrainFrom = 0u;
|
||||||
static bool gIdle = false;
|
static bool gIdle = false;
|
||||||
static uint32_t gIdleSinceMs = 0u;
|
static uint32_t gIdleSinceMs = 0u;
|
||||||
|
|
||||||
extern void mfpTimerAIsr(void);
|
extern void jlMfpTimerAIsr(void);
|
||||||
|
|
||||||
static void (*gOldTimerAVec)(void) = NULL;
|
static void (*gOldTimerAVec)(void) = NULL;
|
||||||
|
|
||||||
|
|
@ -227,8 +227,8 @@ static volatile uint16_t gVoiceAFreqHz;
|
||||||
static volatile uint8_t gVoiceAAtten = 15u;
|
static volatile uint8_t gVoiceAAtten = 15u;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read by stTickIsr (audio_isr.s), so not static.
|
// Read by jlStTickIsr (audio_isr.s), so not static.
|
||||||
void (*gStTickOldVec)(void) = NULL;
|
void (*jlgStTickOldVec)(void) = NULL;
|
||||||
static volatile jlAudioTickFnT gStTickFn = NULL;
|
static volatile jlAudioTickFnT gStTickFn = NULL;
|
||||||
static volatile uint32_t gStTickHz = 0u;
|
static volatile uint32_t gStTickHz = 0u;
|
||||||
static volatile uint32_t gStTickCarry = 0u;
|
static volatile uint32_t gStTickCarry = 0u;
|
||||||
|
|
@ -236,8 +236,8 @@ static bool gStTickHooked = false;
|
||||||
// The status register at the outermost jlAudioCriticalEnter.
|
// The status register at the outermost jlAudioCriticalEnter.
|
||||||
static uint16_t gCriticalSr = 0u;
|
static uint16_t gCriticalSr = 0u;
|
||||||
|
|
||||||
extern void stTickIsr(void);
|
extern void jlStTickIsr(void);
|
||||||
void stTickRun(void);
|
void jlStTickRun(void);
|
||||||
|
|
||||||
static bool gReady = false;
|
static bool gReady = false;
|
||||||
|
|
||||||
|
|
@ -343,8 +343,8 @@ static long installTimerA(void) {
|
||||||
// = 128) before opening the IRQ gate.
|
// = 128) before opening the IRQ gate.
|
||||||
silenceMixBuffer();
|
silenceMixBuffer();
|
||||||
rampWrite(0u, 0u, MIX_SILENCE);
|
rampWrite(0u, 0u, MIX_SILENCE);
|
||||||
gMixPos = (volatile uint8_t *)gMixBuf;
|
jlgMixPos = (volatile uint8_t *)jlgMixBuf;
|
||||||
gMixEnd = (volatile uint8_t *)(gMixBuf + MIX_BUFFER);
|
jlgMixEnd = (volatile uint8_t *)(jlgMixBuf + MIX_BUFFER);
|
||||||
gNeedRefill[0] = 0;
|
gNeedRefill[0] = 0;
|
||||||
gNeedRefill[1] = 0;
|
gNeedRefill[1] = 0;
|
||||||
gPlayHalf = 0u;
|
gPlayHalf = 0u;
|
||||||
|
|
@ -381,7 +381,7 @@ static long installTimerA(void) {
|
||||||
// MFP Timer A: stop, install our vector, set prescaler 200 + data
|
// MFP Timer A: stop, install our vector, set prescaler 200 + data
|
||||||
// 1 (= 2.4576 MHz / 200 = 12288 Hz), then start.
|
// 1 (= 2.4576 MHz / 200 = 12288 Hz), then start.
|
||||||
*ST_MFP_TACR = MFP_TACR_STOP;
|
*ST_MFP_TACR = MFP_TACR_STOP;
|
||||||
gOldTimerAVec = (void (*)(void))Setexc(VEC_MFP_TA, (long)mfpTimerAIsr);
|
gOldTimerAVec = (void (*)(void))Setexc(VEC_MFP_TA, (long)jlMfpTimerAIsr);
|
||||||
*ST_MFP_TADR = 1;
|
*ST_MFP_TADR = 1;
|
||||||
*ST_MFP_TACR = MFP_TACR_DIV200;
|
*ST_MFP_TACR = MFP_TACR_DIV200;
|
||||||
Jenabint(INT_TIMER_A);
|
Jenabint(INT_TIMER_A);
|
||||||
|
|
@ -412,7 +412,7 @@ static long uninstallTimerA(void) {
|
||||||
|
|
||||||
// Silence in the output stage's own sample format (MIX_SILENCE).
|
// Silence in the output stage's own sample format (MIX_SILENCE).
|
||||||
static void silenceMixBuffer(void) {
|
static void silenceMixBuffer(void) {
|
||||||
memset((void *)gMixBuf, MIX_SILENCE, MIX_BUFFER);
|
memset((void *)jlgMixBuf, MIX_SILENCE, MIX_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -432,7 +432,7 @@ static uint16_t dmaPlayOffset(void) {
|
||||||
mid = *STE_DMA_COUNT_MID;
|
mid = *STE_DMA_COUNT_MID;
|
||||||
lo = *STE_DMA_COUNT_LO;
|
lo = *STE_DMA_COUNT_LO;
|
||||||
} while (hi != *STE_DMA_COUNT_HI);
|
} while (hi != *STE_DMA_COUNT_HI);
|
||||||
return (uint16_t)((((uint32_t)hi << 16) | ((uint32_t)mid << 8) | lo) - (uint32_t)gMixBuf);
|
return (uint16_t)((((uint32_t)hi << 16) | ((uint32_t)mid << 8) | lo) - (uint32_t)jlgMixBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -471,8 +471,8 @@ static long installDma(void) {
|
||||||
gNeedRefill[0] = 0;
|
gNeedRefill[0] = 0;
|
||||||
gNeedRefill[1] = 0;
|
gNeedRefill[1] = 0;
|
||||||
gPlayHalf = 0u;
|
gPlayHalf = 0u;
|
||||||
dmaSetFrame(STE_DMA_START_HI, STE_DMA_START_MID, STE_DMA_START_LO, (uint32_t)gMixBuf);
|
dmaSetFrame(STE_DMA_START_HI, STE_DMA_START_MID, STE_DMA_START_LO, (uint32_t)jlgMixBuf);
|
||||||
dmaSetFrame(STE_DMA_END_HI, STE_DMA_END_MID, STE_DMA_END_LO, (uint32_t)(gMixBuf + MIX_BUFFER));
|
dmaSetFrame(STE_DMA_END_HI, STE_DMA_END_MID, STE_DMA_END_LO, (uint32_t)(jlgMixBuf + MIX_BUFFER));
|
||||||
*STE_DMA_MODE = STE_DMA_STEREO_12K;
|
*STE_DMA_MODE = STE_DMA_STEREO_12K;
|
||||||
*STE_DMA_CTRL = STE_DMA_PLAY_LOOP;
|
*STE_DMA_CTRL = STE_DMA_PLAY_LOOP;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -494,7 +494,7 @@ static uint16_t playOffset(void) {
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
return dmaPlayOffset();
|
return dmaPlayOffset();
|
||||||
#else
|
#else
|
||||||
return (uint16_t)(gMixPos - gMixBuf);
|
return (uint16_t)(jlgMixPos - jlgMixBuf);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -523,10 +523,10 @@ static void refillHalf(uint8_t half) {
|
||||||
volatile uint8_t *dst;
|
volatile uint8_t *dst;
|
||||||
bool silentFill;
|
bool silentFill;
|
||||||
|
|
||||||
dst = gMixBuf + (uint16_t)half * MIX_HALF;
|
dst = jlgMixBuf + (uint16_t)half * MIX_HALF;
|
||||||
silentFill = !modPlayerActive() && !anySfxActive();
|
silentFill = !jlModPlayerActive() && !anySfxActive();
|
||||||
if (modPlayerActive()) {
|
if (jlModPlayerActive()) {
|
||||||
if (!modPlayerRenderU8((uint8_t *)dst, MIX_HALF_FRAMES, MIX_CHANNELS)) {
|
if (!jlModPlayerRenderU8((uint8_t *)dst, MIX_HALF_FRAMES, MIX_CHANNELS)) {
|
||||||
gModTail = true;
|
gModTail = true;
|
||||||
gModTailHalf = half;
|
gModTailHalf = half;
|
||||||
}
|
}
|
||||||
|
|
@ -534,9 +534,9 @@ static void refillHalf(uint8_t half) {
|
||||||
memset((void *)dst, MIX_SILENCE_UNSIGNED, MIX_HALF);
|
memset((void *)dst, MIX_SILENCE_UNSIGNED, MIX_HALF);
|
||||||
}
|
}
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
audioSfxOverlayMixStereo(dst, MIX_HALF_FRAMES, gSfxSlots, JL_AUDIO_SFX_SLOTS, gSfxPan, AUDIO_SFX_DST_UNSIGNED);
|
jlAudioSfxOverlayMixStereo(dst, MIX_HALF_FRAMES, gSfxSlots, JL_AUDIO_SFX_SLOTS, gSfxPan, AUDIO_SFX_DST_UNSIGNED);
|
||||||
#else
|
#else
|
||||||
audioSfxOverlayMix(dst, MIX_HALF, gSfxSlots, JL_AUDIO_SFX_SLOTS);
|
jlAudioSfxOverlayMix(dst, MIX_HALF, gSfxSlots, JL_AUDIO_SFX_SLOTS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
dmaSignBytes(dst, MIX_HALF);
|
dmaSignBytes(dst, MIX_HALF);
|
||||||
|
|
@ -552,9 +552,9 @@ static void refillHalf(uint8_t half) {
|
||||||
// mixed as they stand, in order, never flipped to unsigned and back.
|
// mixed as they stand, in order, never flipped to unsigned and back.
|
||||||
static void sfxOverlayAt(uint16_t at, uint16_t len, uint8_t slot) {
|
static void sfxOverlayAt(uint16_t at, uint16_t len, uint8_t slot) {
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
audioSfxOverlayMixStereo(gMixBuf + at, (uint16_t)(len / MIX_CHANNELS), &gSfxSlots[slot], 1u, &gSfxPan[slot], AUDIO_SFX_DST_SIGNED);
|
jlAudioSfxOverlayMixStereo(jlgMixBuf + at, (uint16_t)(len / MIX_CHANNELS), &gSfxSlots[slot], 1u, &gSfxPan[slot], AUDIO_SFX_DST_SIGNED);
|
||||||
#else
|
#else
|
||||||
audioSfxOverlayMix(gMixBuf + at, len, &gSfxSlots[slot], 1u);
|
jlAudioSfxOverlayMix(jlgMixBuf + at, len, &gSfxSlots[slot], 1u);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,7 +630,7 @@ static void drainCancel(void) {
|
||||||
gDraining = false;
|
gDraining = false;
|
||||||
at = (uint16_t)((playOffset() + PWM_DRAIN_MARGIN) % MIX_BUFFER);
|
at = (uint16_t)((playOffset() + PWM_DRAIN_MARGIN) % MIX_BUFFER);
|
||||||
for (i = 0u; i < MIX_BUFFER - 2u * PWM_DRAIN_MARGIN; i++) {
|
for (i = 0u; i < MIX_BUFFER - 2u * PWM_DRAIN_MARGIN; i++) {
|
||||||
gMixBuf[(at + i) % MIX_BUFFER] = MIX_SILENCE;
|
jlgMixBuf[(at + i) % MIX_BUFFER] = MIX_SILENCE;
|
||||||
}
|
}
|
||||||
rampWrite(at, 0u, MIX_SILENCE);
|
rampWrite(at, 0u, MIX_SILENCE);
|
||||||
gHalfSilent[0] = false;
|
gHalfSilent[0] = false;
|
||||||
|
|
@ -655,7 +655,7 @@ static void drainStart(void) {
|
||||||
gDrainFrom = playOffset();
|
gDrainFrom = playOffset();
|
||||||
at = (uint16_t)((gDrainFrom + PWM_DRAIN_MARGIN) % MIX_BUFFER);
|
at = (uint16_t)((gDrainFrom + PWM_DRAIN_MARGIN) % MIX_BUFFER);
|
||||||
for (i = 0u; i < MIX_BUFFER - 2u * PWM_DRAIN_MARGIN; i++) {
|
for (i = 0u; i < MIX_BUFFER - 2u * PWM_DRAIN_MARGIN; i++) {
|
||||||
gMixBuf[(at + i) % MIX_BUFFER] = 0u;
|
jlgMixBuf[(at + i) % MIX_BUFFER] = 0u;
|
||||||
}
|
}
|
||||||
rampWrite(at, MIX_SILENCE, 0u);
|
rampWrite(at, MIX_SILENCE, 0u);
|
||||||
gDraining = true;
|
gDraining = true;
|
||||||
|
|
@ -668,12 +668,12 @@ static void rampWrite(uint16_t at, uint8_t from, uint8_t to) {
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
for (i = 0u; i < PWM_RAMP_SAMPLES; i++) {
|
for (i = 0u; i < PWM_RAMP_SAMPLES; i++) {
|
||||||
gMixBuf[(at + i) % MIX_BUFFER] = (uint8_t)((int16_t)from + ((int16_t)to - (int16_t)from) * (int16_t)i / (int16_t)PWM_RAMP_SAMPLES);
|
jlgMixBuf[(at + i) % MIX_BUFFER] = (uint8_t)((int16_t)from + ((int16_t)to - (int16_t)from) * (int16_t)i / (int16_t)PWM_RAMP_SAMPLES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// gYmLevel: for each sample value, the level whose amplitude is nearest
|
// jlgYmLevel: for each sample value, the level whose amplitude is nearest
|
||||||
// the value's place on the line from level 0's amplitude to level 15's.
|
// the value's place on the line from level 0's amplitude to level 15's.
|
||||||
static void ymLevelsBuild(void) {
|
static void ymLevelsBuild(void) {
|
||||||
uint32_t target;
|
uint32_t target;
|
||||||
|
|
@ -685,12 +685,12 @@ static void ymLevelsBuild(void) {
|
||||||
for (s = 0u; s < SAMPLE_VALUES; s++) {
|
for (s = 0u; s < SAMPLE_VALUES; s++) {
|
||||||
target = kYmAmplitude[0] + ((uint32_t)(kYmAmplitude[YM_LEVELS - 1u] - kYmAmplitude[0]) * s) / YM_SAMPLE_MAX;
|
target = kYmAmplitude[0] + ((uint32_t)(kYmAmplitude[YM_LEVELS - 1u] - kYmAmplitude[0]) * s) / YM_SAMPLE_MAX;
|
||||||
best = UINT32_MAX;
|
best = UINT32_MAX;
|
||||||
gYmLevel[s] = 0u;
|
jlgYmLevel[s] = 0u;
|
||||||
for (v = 0u; v < YM_LEVELS; v++) {
|
for (v = 0u; v < YM_LEVELS; v++) {
|
||||||
gap = kYmAmplitude[v] > target ? kYmAmplitude[v] - target : target - kYmAmplitude[v];
|
gap = kYmAmplitude[v] > target ? kYmAmplitude[v] - target : target - kYmAmplitude[v];
|
||||||
if (gap < best) {
|
if (gap < best) {
|
||||||
best = gap;
|
best = gap;
|
||||||
gYmLevel[s] = v;
|
jlgYmLevel[s] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -746,7 +746,7 @@ bool jlpAudioInit(void) {
|
||||||
}
|
}
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
// The STe build's output stage is the DMA chip: no chip, no audio.
|
// The STe build's output stage is the DMA chip: no chip, no audio.
|
||||||
if (!steMachineCheck()) {
|
if (!jlSteMachineCheck()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
@ -755,8 +755,8 @@ bool jlpAudioInit(void) {
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
memset(gSfxPan, JL_AUDIO_PAN_CENTER, sizeof(gSfxPan));
|
memset(gSfxPan, JL_AUDIO_PAN_CENTER, sizeof(gSfxPan));
|
||||||
#endif
|
#endif
|
||||||
modPlayerSetU8Shift(MIX_UNIT_SHIFT);
|
jlModPlayerSetU8Shift(MIX_UNIT_SHIFT);
|
||||||
audioSfxSetShift(MIX_UNIT_SHIFT);
|
jlAudioSfxSetShift(MIX_UNIT_SHIFT);
|
||||||
gReady = true;
|
gReady = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -789,13 +789,13 @@ void jlpAudioShutdown(void) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
silenceMixBuffer();
|
silenceMixBuffer();
|
||||||
modPlayerStop();
|
jlModPlayerStop();
|
||||||
gReady = false;
|
gReady = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jlpAudioIsPlayingMod(void) {
|
bool jlpAudioIsPlayingMod(void) {
|
||||||
return gReady && (modPlayerActive() || gModTail);
|
return gReady && (jlModPlayerActive() || gModTail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -804,7 +804,7 @@ void jlpAudioPlayMod(const uint8_t *data, uint32_t length, bool loop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gModTail = false;
|
gModTail = false;
|
||||||
if (!modPlayerStart(data, length, MIX_RATE, loop)) {
|
if (!jlModPlayerStart(data, length, MIX_RATE, loop)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(void)pumpStart();
|
(void)pumpStart();
|
||||||
|
|
@ -815,7 +815,7 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1
|
||||||
if (!gReady || slot >= JL_AUDIO_SFX_SLOTS) {
|
if (!gReady || slot >= JL_AUDIO_SFX_SLOTS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
audioSfxSlotArm(&gSfxSlots[slot], sample, length, rateHz, MIX_RATE);
|
jlAudioSfxSlotArm(&gSfxSlots[slot], sample, length, rateHz, MIX_RATE);
|
||||||
sfxStartNow(slot, pumpStart());
|
sfxStartNow(slot, pumpStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -824,7 +824,7 @@ void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uin
|
||||||
if (!gReady || slot >= JL_AUDIO_SFX_SLOTS) {
|
if (!gReady || slot >= JL_AUDIO_SFX_SLOTS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
audioSfxSlotArmStream(&gSfxSlots[slot], fill, ctx, rateHz, MIX_RATE);
|
jlAudioSfxSlotArmStream(&gSfxSlots[slot], fill, ctx, rateHz, MIX_RATE);
|
||||||
sfxStartNow(slot, pumpStart());
|
sfxStartNow(slot, pumpStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -965,10 +965,10 @@ static long ymPsgAllOff(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The tick rides TOS's 200 Hz Timer C (stTickIsr chains to TOS's handler),
|
// The tick rides TOS's 200 Hz Timer C (jlStTickIsr chains to TOS's handler),
|
||||||
// counting the rate against it: see the Timer C notes at the top. The hook
|
// counting the rate against it: see the Timer C notes at the top. The hook
|
||||||
// may stay in place for the pump (tickHookUpdate), so the tick is switched
|
// may stay in place for the pump (tickHookUpdate), so the tick is switched
|
||||||
// off while its fields change -- at rate 0 stTickRun's carry never comes
|
// off while its fields change -- at rate 0 jlStTickRun's carry never comes
|
||||||
// due -- and its rate goes in last. Each store is one instruction, so this
|
// due -- and its rate goes in last. Each store is one instruction, so this
|
||||||
// needs no interrupts held off, and runs before jlInit's supervisor mode.
|
// needs no interrupts held off, and runs before jlInit's supervisor mode.
|
||||||
bool jlpAudioTickRegister(jlAudioTickFnT fn, uint16_t hz) {
|
bool jlpAudioTickRegister(jlAudioTickFnT fn, uint16_t hz) {
|
||||||
|
|
@ -999,18 +999,18 @@ static void tickHookUpdate(void) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (want) {
|
if (want) {
|
||||||
gStTickOldVec = (void (*)(void))Setexc(VEC_MFP_TC, SETEXC_READ);
|
jlgStTickOldVec = (void (*)(void))Setexc(VEC_MFP_TC, SETEXC_READ);
|
||||||
(void)Setexc(VEC_MFP_TC, (long)stTickIsr);
|
(void)Setexc(VEC_MFP_TC, (long)jlStTickIsr);
|
||||||
} else {
|
} else {
|
||||||
(void)Setexc(VEC_MFP_TC, (long)gStTickOldVec);
|
(void)Setexc(VEC_MFP_TC, (long)jlgStTickOldVec);
|
||||||
}
|
}
|
||||||
gStTickHooked = want;
|
gStTickHooked = want;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Called from stTickIsr (audio_isr.s) at each Timer C interrupt: the
|
// Called from jlStTickIsr (audio_isr.s) at each Timer C interrupt: the
|
||||||
// program's tick as many times as its rate has come due since the last.
|
// program's tick as many times as its rate has come due since the last.
|
||||||
void stTickRun(void) {
|
void jlStTickRun(void) {
|
||||||
gStTickCarry += gStTickHz;
|
gStTickCarry += gStTickHz;
|
||||||
while (gStTickCarry >= ST_TIMER_C_HZ) {
|
while (gStTickCarry >= ST_TIMER_C_HZ) {
|
||||||
gStTickCarry -= ST_TIMER_C_HZ;
|
gStTickCarry -= ST_TIMER_C_HZ;
|
||||||
|
|
@ -1019,7 +1019,7 @@ void stTickRun(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The tick callback runs from Timer C (stTickIsr), so a section holds
|
// The tick callback runs from Timer C (jlStTickIsr), so a section holds
|
||||||
// interrupts off, as ymWrite does; keep it short, as the PWM pump's Timer A
|
// interrupts off, as ymWrite does; keep it short, as the PWM pump's Timer A
|
||||||
// is held off with it. The core calls these for the outermost section only,
|
// is held off with it. The core calls these for the outermost section only,
|
||||||
// and Exit puts back the status register Enter found.
|
// and Exit puts back the status register Enter found.
|
||||||
|
|
@ -1076,7 +1076,7 @@ void jlpAudioStopMod(void) {
|
||||||
if (!gReady) {
|
if (!gReady) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modPlayerStop();
|
jlModPlayerStop();
|
||||||
gModTail = false;
|
gModTail = false;
|
||||||
silenceMixBuffer();
|
silenceMixBuffer();
|
||||||
// StopMod discards the mixed tail by design (buffer just went
|
// StopMod discards the mixed tail by design (buffer just went
|
||||||
|
|
@ -1103,7 +1103,7 @@ void jlpAudioStopSfx(uint8_t slot) {
|
||||||
// Soft-real-time constraint: a half must be refilled within MIX_HALF /
|
// Soft-real-time constraint: a half must be refilled within MIX_HALF /
|
||||||
// MIX_RATE = 1024 / 12288 ~= 83 ms of being left, or the play position
|
// MIX_RATE = 1024 / 12288 ~= 83 ms of being left, or the play position
|
||||||
// comes back round to it stale: a tick that comes a whole buffer late
|
// comes back round to it stale: a tick that comes a whole buffer late
|
||||||
// replays the old half once. A full MOD-frame mix plus audioSfxOverlayMix
|
// replays the old half once. A full MOD-frame mix plus jlAudioSfxOverlayMix
|
||||||
// can approach that budget under load, so callers should run the game loop
|
// can approach that budget under load, so callers should run the game loop
|
||||||
// comfortably faster than ~12 Hz.
|
// comfortably faster than ~12 Hz.
|
||||||
void jlpAudioFrameTick(void) {
|
void jlpAudioFrameTick(void) {
|
||||||
|
|
@ -1134,7 +1134,7 @@ void jlpAudioFrameTick(void) {
|
||||||
// is playing, so the ISR would stream silence forever -- park it (on
|
// is playing, so the ISR would stream silence forever -- park it (on
|
||||||
// the plain ST once PWM_IDLE_MS of it have gone by, after a ramp down
|
// the plain ST once PWM_IDLE_MS of it have gone by, after a ramp down
|
||||||
// to level 0: drainStart).
|
// to level 0: drainStart).
|
||||||
if (!modPlayerActive() && gHalfSilent[0] && gHalfSilent[1] && !anySfxActive()) {
|
if (!jlModPlayerActive() && gHalfSilent[0] && gHalfSilent[1] && !anySfxActive()) {
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
pumpStop();
|
pumpStop();
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
|
|
||||||
| Each interrupt:
|
| Each interrupt:
|
||||||
| 1. Reads one byte from the mix buffer (unsigned 8-bit).
|
| 1. Reads one byte from the mix buffer (unsigned 8-bit).
|
||||||
| 2. Writes its YM level (_gYmLevel[byte], the level nearest the
|
| 2. Writes its YM level (_jlgYmLevel[byte], the level nearest the
|
||||||
| sample's value: the YM's levels are logarithmic) to YM register 8
|
| sample's value: the YM's levels are logarithmic) to YM register 8
|
||||||
| (channel A volume), so the analog filter on the YM output emits
|
| (channel A volume), so the analog filter on the YM output emits
|
||||||
| that sample for that tick.
|
| that sample for that tick.
|
||||||
|
|
@ -19,17 +19,17 @@
|
||||||
| are reached by short addresses.
|
| are reached by short addresses.
|
||||||
|
|
|
|
||||||
| Communication with the C HAL is via three globals defined in audio.c:
|
| Communication with the C HAL is via three globals defined in audio.c:
|
||||||
| _gMixBuf -- start of the mix buffer (uint8_t array)
|
| _jlgMixBuf -- start of the mix buffer (uint8_t array)
|
||||||
| _gMixPos -- volatile pointer to the next sample to play
|
| _jlgMixPos -- volatile pointer to the next sample to play
|
||||||
| _gMixEnd -- end of buffer (used to detect wrap)
|
| _jlgMixEnd -- end of buffer (used to detect wrap)
|
||||||
| and _gYmLevel, each sample value's YM level (256 bytes), defined here
|
| and _jlgYmLevel, each sample value's YM level (256 bytes), defined here
|
||||||
| and filled by audio.c.
|
| and filled by audio.c.
|
||||||
|
|
|
|
||||||
| GAS-syntax (binutils m68k); assembled by m68k-atari-mint-as via the
|
| GAS-syntax (binutils m68k); assembled by m68k-atari-mint-as via the
|
||||||
| gcc driver.
|
| gcc driver.
|
||||||
|
|
||||||
.text
|
.text
|
||||||
.globl _mfpTimerAIsr
|
.globl _jlMfpTimerAIsr
|
||||||
|
|
||||||
.equ YM_SELECT, 0xFFFF8800
|
.equ YM_SELECT, 0xFFFF8800
|
||||||
.equ YM_DATA, 0xFFFF8802
|
.equ YM_DATA, 0xFFFF8802
|
||||||
|
|
@ -37,19 +37,19 @@
|
||||||
.equ MFP_ISRA, 0xFFFFFA0F
|
.equ MFP_ISRA, 0xFFFFFA0F
|
||||||
.equ ISRA_ACK, 0xDF | clear Timer A's in-service bit
|
.equ ISRA_ACK, 0xDF | clear Timer A's in-service bit
|
||||||
|
|
||||||
_mfpTimerAIsr:
|
_jlMfpTimerAIsr:
|
||||||
movem.l %d0/%a0,-(%sp)
|
movem.l %d0/%a0,-(%sp)
|
||||||
movea.l _gMixPos,%a0
|
movea.l _jlgMixPos,%a0
|
||||||
moveq #0,%d0
|
moveq #0,%d0
|
||||||
move.b (%a0)+,%d0
|
move.b (%a0)+,%d0
|
||||||
move.b (_gYmLevel,%pc,%d0.w),%d0
|
move.b (_jlgYmLevel,%pc,%d0.w),%d0
|
||||||
move.b #YM_VOL_A,(YM_SELECT).w
|
move.b #YM_VOL_A,(YM_SELECT).w
|
||||||
move.b %d0,(YM_DATA).w
|
move.b %d0,(YM_DATA).w
|
||||||
cmpa.l _gMixEnd,%a0
|
cmpa.l _jlgMixEnd,%a0
|
||||||
bcs.s 1f
|
bcs.s 1f
|
||||||
lea _gMixBuf,%a0
|
lea _jlgMixBuf,%a0
|
||||||
1:
|
1:
|
||||||
move.l %a0,_gMixPos
|
move.l %a0,_jlgMixPos
|
||||||
move.b #ISRA_ACK,(MFP_ISRA).w
|
move.b #ISRA_ACK,(MFP_ISRA).w
|
||||||
movem.l (%sp)+,%d0/%a0
|
movem.l (%sp)+,%d0/%a0
|
||||||
rte
|
rte
|
||||||
|
|
@ -59,5 +59,5 @@ _mfpTimerAIsr:
|
||||||
| anywhere else would cost an address register (saved and restored) and
|
| anywhere else would cost an address register (saved and restored) and
|
||||||
| its load every sample, and on the plain ST the mix already runs about as
|
| its load every sample, and on the plain ST the mix already runs about as
|
||||||
| fast as the interrupt plays it. TOS does not protect a program's text.
|
| fast as the interrupt plays it. TOS does not protect a program's text.
|
||||||
.globl _gYmLevel
|
.globl _jlgYmLevel
|
||||||
_gYmLevel: .space 256
|
_jlgYmLevel: .space 256
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
|
|
||||||
| ABI: cdecl. d2-d7/a2-a6 callee-save.
|
| ABI: cdecl. d2-d7/a2-a6 callee-save.
|
||||||
|
|
|
|
||||||
| void surface68kStCircleOutline(uint8_t *base,
|
| void jlSurface68kStCircleOutline(uint8_t *base,
|
||||||
| uint16_t cx, uint16_t cy,
|
| uint16_t cx, uint16_t cy,
|
||||||
| uint16_t r, uint8_t color);
|
| uint16_t r, uint8_t color);
|
||||||
|
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
| d5.w = cx (cached)
|
| d5.w = cx (cached)
|
||||||
| a4 = cy (cached, sign-extended)
|
| a4 = cy (cached, sign-extended)
|
||||||
| a3 = base
|
| a3 = base
|
||||||
| a5 = _gStBitMaskWordLut
|
| a5 = _jlgStBitMaskWordLut
|
||||||
| d0,d1,d6,d7 = scratch
|
| d0,d1,d6,d7 = scratch
|
||||||
|
|
|
|
||||||
| Scratch block (40 bytes) at sp+0..39. Each xp record is 8 bytes but
|
| Scratch block (40 bytes) at sp+0..39. Each xp record is 8 bytes but
|
||||||
|
|
@ -201,17 +201,17 @@
|
||||||
.equ SP_R, SP_OFF + 12 + 2
|
.equ SP_R, SP_OFF + 12 + 2
|
||||||
.equ SP_COLOR, SP_OFF + 16 + 3
|
.equ SP_COLOR, SP_OFF + 16 + 3
|
||||||
|
|
||||||
.globl _surface68kStCircleOutline
|
.globl _jlSurface68kStCircleOutline
|
||||||
|
|
||||||
_surface68kStCircleOutline:
|
_jlSurface68kStCircleOutline:
|
||||||
movem.l %d2-%d7/%a2-%a6,-(%sp)
|
movem.l %d2-%d7/%a2-%a6,-(%sp)
|
||||||
lea -SP_LOCAL(%sp),%sp
|
lea -SP_LOCAL(%sp),%sp
|
||||||
|
|
||||||
| Load base (a3) and bitMaskLut (a5). The mask LUT is
|
| Load base (a3) and bitMaskLut (a5). The mask LUT is
|
||||||
| the shared _gStBitMaskWordLut in lineSpan.s; reference
|
| the shared _jlgStBitMaskWordLut in lineSpan.s; reference
|
||||||
| absolute (same pattern as _gStRowOffsetLut below).
|
| absolute (same pattern as _jlgStRowOffsetLut below).
|
||||||
move.l SP_BASE(%sp),%a3
|
move.l SP_BASE(%sp),%a3
|
||||||
lea _gStBitMaskWordLut,%a5
|
lea _jlgStBitMaskWordLut,%a5
|
||||||
|
|
||||||
| Cache cx in d5, cy (sign-extended) in a4.
|
| Cache cx in d5, cy (sign-extended) in a4.
|
||||||
move.w SP_CX(%sp),%d5
|
move.w SP_CX(%sp),%d5
|
||||||
|
|
@ -230,7 +230,7 @@ _surface68kStCircleOutline:
|
||||||
| chain we used to do per YP_REC. Saved across all 4
|
| chain we used to do per YP_REC. Saved across all 4
|
||||||
| YP_RECs per Bresenham iter (~120 cyc/iter).
|
| YP_RECs per Bresenham iter (~120 cyc/iter).
|
||||||
| Shared LUT lives in lineSpan.s; reference absolute.
|
| Shared LUT lives in lineSpan.s; reference absolute.
|
||||||
lea _gStRowOffsetLut,%a6
|
lea _jlgStRowOffsetLut,%a6
|
||||||
|
|
||||||
| Dispatch on color (low 4 bits) -> one of 16 main loops.
|
| Dispatch on color (low 4 bits) -> one of 16 main loops.
|
||||||
moveq #0,%d6
|
moveq #0,%d6
|
||||||
|
|
@ -283,4 +283,4 @@ _surface68kStCircleOutline:
|
||||||
|
|
||||||
|
|
||||||
| (bitMaskWordLut and yLut both live in lineSpan.s as the shared
|
| (bitMaskWordLut and yLut both live in lineSpan.s as the shared
|
||||||
| _gStBitMaskWordLut / _gStRowOffsetLut.)
|
| _jlgStBitMaskWordLut / _jlgStRowOffsetLut.)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
| (its movem path beats a move.l loop past the crossover), so this only
|
| (its movem path beats a move.l loop past the crossover), so this only
|
||||||
| ever sees short spans.
|
| ever sees short spans.
|
||||||
|
|
|
|
||||||
| void stCopyRectSpans68k(uint8_t *dst, const uint8_t *src,
|
| void jlStCopyRectSpans68k(uint8_t *dst, const uint8_t *src,
|
||||||
| int longsPerRow, int numRows);
|
| int longsPerRow, int numRows);
|
||||||
| Copies numRows rows of longsPerRow longs each, both pointers strided by
|
| Copies numRows rows of longsPerRow longs each, both pointers strided by
|
||||||
| ST_BYTES_PER_ROW (160) between row STARTS. dst/src are long-aligned
|
| ST_BYTES_PER_ROW (160) between row STARTS. dst/src are long-aligned
|
||||||
|
|
@ -22,9 +22,9 @@
|
||||||
|
|
||||||
.text
|
.text
|
||||||
|
|
||||||
.globl _stCopyRectSpans68k
|
.globl _jlStCopyRectSpans68k
|
||||||
|
|
||||||
_stCopyRectSpans68k:
|
_jlStCopyRectSpans68k:
|
||||||
movem.l %d2/%d3,-(%sp) | 8 bytes saved
|
movem.l %d2/%d3,-(%sp) | 8 bytes saved
|
||||||
move.l 12(%sp),%a1 | dst
|
move.l 12(%sp),%a1 | dst
|
||||||
move.l 16(%sp),%a0 | src
|
move.l 16(%sp),%a0 | src
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
|
|
||||||
| ABI: cdecl. d2-d7/a2-a6 callee-save.
|
| ABI: cdecl. d2-d7/a2-a6 callee-save.
|
||||||
|
|
|
|
||||||
| void surface68kStFillCircle(uint8_t *base,
|
| void jlSurface68kStFillCircle(uint8_t *base,
|
||||||
| uint16_t cx, uint16_t cy,
|
| uint16_t cx, uint16_t cy,
|
||||||
| uint16_t r, uint8_t color);
|
| uint16_t r, uint8_t color);
|
||||||
|
|
||||||
|
|
@ -56,13 +56,13 @@
|
||||||
lsr.w #4,%d0 | groupLast
|
lsr.w #4,%d0 | groupLast
|
||||||
sub.w %d1,%d0 | numGroups
|
sub.w %d1,%d0 | numGroups
|
||||||
move.w %d0,4(%sp)
|
move.w %d0,4(%sp)
|
||||||
| leftMask via LUT[bitFirst]; a5 = _gStLeftMaskLut base
|
| leftMask via LUT[bitFirst]; a5 = _jlgStLeftMaskLut base
|
||||||
move.w 0(%sp),%d0
|
move.w 0(%sp),%d0
|
||||||
and.w #15,%d0
|
and.w #15,%d0
|
||||||
add.w %d0,%d0
|
add.w %d0,%d0
|
||||||
move.w (%a5,%d0.w),%d1
|
move.w (%a5,%d0.w),%d1
|
||||||
move.w %d1,0(%sp)
|
move.w %d1,0(%sp)
|
||||||
| rightMask via LUT[bitLast]; a6 = _gStRightMaskLut base
|
| rightMask via LUT[bitLast]; a6 = _jlgStRightMaskLut base
|
||||||
move.w 2(%sp),%d0
|
move.w 2(%sp),%d0
|
||||||
and.w #15,%d0
|
and.w #15,%d0
|
||||||
add.w %d0,%d0
|
add.w %d0,%d0
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
|
|
||||||
.macro SPAN_BODY color
|
.macro SPAN_BODY color
|
||||||
| a4 = base + y*160 + groupFirstByteOff
|
| a4 = base + y*160 + groupFirstByteOff
|
||||||
| y*160 via shared _gStRowOffsetLut (a2 holds lut base).
|
| y*160 via shared _jlgStRowOffsetLut (a2 holds lut base).
|
||||||
| byteOff (y*160 + groupFirstByteOff) fits in 16 bits
|
| byteOff (y*160 + groupFirstByteOff) fits in 16 bits
|
||||||
| (max 31992), so word-only ops + .w-indexed lea.
|
| (max 31992), so word-only ops + .w-indexed lea.
|
||||||
add.w %d0,%d0 | y * 2 (word index)
|
add.w %d0,%d0 | y * 2 (word index)
|
||||||
|
|
@ -204,9 +204,9 @@
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
|
||||||
.globl _surface68kStFillCircle
|
.globl _jlSurface68kStFillCircle
|
||||||
|
|
||||||
_surface68kStFillCircle:
|
_jlSurface68kStFillCircle:
|
||||||
movem.l %d2-%d7/%a2-%a6,-(%sp)
|
movem.l %d2-%d7/%a2-%a6,-(%sp)
|
||||||
lea -SP_FC_LOCAL(%sp),%sp
|
lea -SP_FC_LOCAL(%sp),%sp
|
||||||
|
|
||||||
|
|
@ -216,12 +216,12 @@ _surface68kStFillCircle:
|
||||||
move.b SP_FC_COLOR(%sp),%d7
|
move.b SP_FC_COLOR(%sp),%d7
|
||||||
|
|
||||||
| LUT bases. a5/a6 = mask LUTs (used by COMPUTE_PAIR_MASKS).
|
| LUT bases. a5/a6 = mask LUTs (used by COMPUTE_PAIR_MASKS).
|
||||||
| a2 = shared _gStRowOffsetLut (used by SPAN_BODY for y*160).
|
| a2 = shared _jlgStRowOffsetLut (used by SPAN_BODY for y*160).
|
||||||
| All three LUTs are the shared definitions in lineSpan.s;
|
| All three LUTs are the shared definitions in lineSpan.s;
|
||||||
| reference them absolute (single source of truth).
|
| reference them absolute (single source of truth).
|
||||||
lea _gStLeftMaskLut,%a5
|
lea _jlgStLeftMaskLut,%a5
|
||||||
lea _gStRightMaskLut,%a6
|
lea _jlgStRightMaskLut,%a6
|
||||||
lea _gStRowOffsetLut,%a2
|
lea _jlgStRowOffsetLut,%a2
|
||||||
|
|
||||||
| loLong = ((c&1)?0xFFFF0000:0) | ((c&2)?0x0000FFFF:0)
|
| loLong = ((c&1)?0xFFFF0000:0) | ((c&2)?0x0000FFFF:0)
|
||||||
moveq #0,%d5
|
moveq #0,%d5
|
||||||
|
|
@ -307,4 +307,4 @@ _surface68kStFillCircle:
|
||||||
|
|
||||||
|
|
||||||
| (leftMaskLut/rightMaskLut now live in lineSpan.s as the shared
|
| (leftMaskLut/rightMaskLut now live in lineSpan.s as the shared
|
||||||
| _gStLeftMaskLut / _gStRightMaskLut, referenced absolute above.)
|
| _jlgStLeftMaskLut / _jlgStRightMaskLut, referenced absolute above.)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -19,9 +19,9 @@
|
||||||
//
|
//
|
||||||
// The ISR writes into private gIsrState (keyboard) and gIsrMouse*
|
// The ISR writes into private gIsrState (keyboard) and gIsrMouse*
|
||||||
// buffers; jlpInputPoll copies them into the public globals.
|
// buffers; jlpInputPoll copies them into the public globals.
|
||||||
// jlKeyPressed edge detection requires that public gKeyState only
|
// jlKeyPressed edge detection requires that public jlgKeyState only
|
||||||
// advance during jlpInputPoll, never at interrupt time -- jlInputPoll
|
// advance during jlpInputPoll, never at interrupt time -- jlInputPoll
|
||||||
// snapshots gKeyState into gKeyPrev before jlpInputPoll runs.
|
// snapshots jlgKeyState into jlgKeyPrev before jlpInputPoll runs.
|
||||||
//
|
//
|
||||||
// Typed characters: replacing ikbdsys means TOS's own keyboard
|
// Typed characters: replacing ikbdsys means TOS's own keyboard
|
||||||
// processing never runs, so Bconin never sees our keys. Instead the
|
// processing never runs, so Bconin never sees our keys. Instead the
|
||||||
|
|
@ -160,7 +160,7 @@ static void altLayerFind(const KeyTabT *keyTab);
|
||||||
static void charRepeat(void);
|
static void charRepeat(void);
|
||||||
static long ikbdHandler(void);
|
static long ikbdHandler(void);
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
static void padToStick(const StePadT *pad, jlJoystickE js);
|
static inline void padToStick(uint16_t held, uint16_t buttonA, uint16_t buttonB, jlJoystickE js);
|
||||||
#endif
|
#endif
|
||||||
static long patchIkbdVector(void);
|
static long patchIkbdVector(void);
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
|
|
@ -247,17 +247,23 @@ static volatile uint8_t gPacketRemaining = 0;
|
||||||
static volatile uint8_t gPacketKind = PKT_KIND_NONE;
|
static volatile uint8_t gPacketKind = PKT_KIND_NONE;
|
||||||
static volatile uint8_t gMousePacketByte = 0; // bytes consumed in current packet
|
static volatile uint8_t gMousePacketByte = 0; // bytes consumed in current packet
|
||||||
static bool gHooked = false;
|
static bool gHooked = false;
|
||||||
// uint8_t (not bool) so element size matches gKeyState's. See
|
// uint8_t (not bool) so element size matches jlgKeyState's. See
|
||||||
// src/core/inputInternal.h for the full rationale.
|
// src/core/inputInternal.h for the full rationale.
|
||||||
static volatile uint8_t gIsrState[JL_KEY_COUNT];
|
static volatile uint8_t gIsrState[JL_KEY_COUNT];
|
||||||
// Each key's presses, counted by the ISR (which alone writes them), and the
|
// Each key's presses, counted by the ISR (which alone writes them), and the
|
||||||
// count the last poll saw: a key whose count moved was pressed since, so a
|
// count the last poll saw: a key whose count moved was pressed since, so a
|
||||||
// tap between two polls still reads down for one (inputLatch's meaning).
|
// tap between two polls still reads down for one (jlInputLatch's meaning).
|
||||||
// The poll writes nothing the ISR does, so it needs no interrupts held off:
|
// The poll writes nothing the ISR does, so it needs no interrupts held off:
|
||||||
// held off across the keys, the plain ST's 12288 Hz PWM pump lost five
|
// held off across the keys, the plain ST's 12288 Hz PWM pump lost five
|
||||||
// samples a poll, and digital sound played 1.4% flat.
|
// samples a poll, and digital sound played 1.4% flat.
|
||||||
static volatile uint8_t gIsrTaps[JL_KEY_COUNT];
|
static volatile uint8_t gIsrTaps[JL_KEY_COUNT];
|
||||||
static uint8_t gTapsSeen[JL_KEY_COUNT];
|
static uint8_t gTapsSeen[JL_KEY_COUNT];
|
||||||
|
// Key events the ISR has taken (after it updated the key), and the count the
|
||||||
|
// last poll scanned the keys at: a poll scans them only when one came since,
|
||||||
|
// or when the last scan showed a tap that must now go up.
|
||||||
|
static volatile uint16_t gIsrKeyEvents;
|
||||||
|
static uint16_t gKeyEventsSeen;
|
||||||
|
static bool gKeyTapShown;
|
||||||
|
|
||||||
// Mouse delta accumulator. Each ACIA mouse packet adds dx/dy here; the
|
// Mouse delta accumulator. Each ACIA mouse packet adds dx/dy here; the
|
||||||
// poll routine clamps the running absolute position into the surface
|
// poll routine clamps the running absolute position into the surface
|
||||||
|
|
@ -309,9 +315,26 @@ static uint32_t gRepeatDelayMs = 0u;
|
||||||
static uint32_t gRepeatRateMs = 0u;
|
static uint32_t gRepeatRateMs = 0u;
|
||||||
|
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
// Whether JL_JOYSTICK_0 is the Jaguar pad in enhanced port A, used more
|
// Whether the machine has the enhanced ports (jlSteMachineHasPads, kept by
|
||||||
// recently than port 1's stick.
|
// jlpInputInit), and whether JL_JOYSTICK_0 is the Jaguar pad in port A,
|
||||||
|
// used more recently than port 1's stick.
|
||||||
|
static bool gStePads = false;
|
||||||
static bool gPadAIsStick0 = false;
|
static bool gPadAIsStick0 = false;
|
||||||
|
|
||||||
|
// A pad's four direction bits (STE_PAD_UP, DOWN, LEFT, RIGHT) as its axes.
|
||||||
|
// Both directions of an axis read as the positive one, as on the IKBD ports.
|
||||||
|
static const int8_t kPadAxisX[STE_PAD_DIR_MASK + 1u] = {
|
||||||
|
0, 0, 0, 0,
|
||||||
|
JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MIN,
|
||||||
|
JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX,
|
||||||
|
JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX
|
||||||
|
};
|
||||||
|
static const int8_t kPadAxisY[STE_PAD_DIR_MASK + 1u] = {
|
||||||
|
0, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX,
|
||||||
|
0, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX,
|
||||||
|
0, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX,
|
||||||
|
0, JL_JOYSTICK_AXIS_MIN, JL_JOYSTICK_AXIS_MAX, JL_JOYSTICK_AXIS_MAX
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ----- Internal helpers -----
|
// ----- Internal helpers -----
|
||||||
|
|
@ -343,7 +366,7 @@ static void altLayerFind(const KeyTabT *keyTab) {
|
||||||
} else if (os->os_version >= TOS_ALT_LAYER_VERSION) {
|
} else if (os->os_version >= TOS_ALT_LAYER_VERSION) {
|
||||||
inTables = true;
|
inTables = true;
|
||||||
} else {
|
} else {
|
||||||
(void)stAltLayerFind((uint8_t)(os->os_conf >> TOS_CONF_COUNTRY_SHIFT), &gKeyTabAltUnshift, &gKeyTabAltShift, &gKeyTabAltCaps);
|
(void)jlStAltLayerFind((uint8_t)(os->os_conf >> TOS_CONF_COUNTRY_SHIFT), &gKeyTabAltUnshift, &gKeyTabAltShift, &gKeyTabAltCaps);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (inTables) {
|
if (inTables) {
|
||||||
|
|
@ -355,11 +378,16 @@ static void altLayerFind(const KeyTabT *keyTab) {
|
||||||
|
|
||||||
|
|
||||||
// The held key's character again, once TOS's delay has passed since it went
|
// The held key's character again, once TOS's delay has passed since it went
|
||||||
// down and then at TOS's rate (the ISR names the key, gIsrRepeatCode).
|
// down and then at TOS's rate (the ISR names the key, gIsrRepeatCode). No
|
||||||
|
// key held, the usual poll, reads no clock: a key the ISR takes just after
|
||||||
|
// the check is the next poll's.
|
||||||
static void charRepeat(void) {
|
static void charRepeat(void) {
|
||||||
uint32_t now;
|
uint32_t now;
|
||||||
uint8_t n;
|
uint8_t n;
|
||||||
|
|
||||||
|
if (gIsrRepeatNew == 0u && gIsrRepeatCode == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
now = jlpMillisElapsed();
|
now = jlpMillisElapsed();
|
||||||
if (gIsrRepeatNew != 0u) {
|
if (gIsrRepeatNew != 0u) {
|
||||||
gIsrRepeatNew = 0u;
|
gIsrRepeatNew = 0u;
|
||||||
|
|
@ -480,6 +508,7 @@ static long ikbdHandler(void) {
|
||||||
if (!isBreak) {
|
if (!isBreak) {
|
||||||
gIsrTaps[key]++;
|
gIsrTaps[key]++;
|
||||||
}
|
}
|
||||||
|
gIsrKeyEvents++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBreak && code == gIsrRepeatCode) {
|
if (isBreak && code == gIsrRepeatCode) {
|
||||||
|
|
@ -520,14 +549,14 @@ static long ikbdHandler(void) {
|
||||||
|
|
||||||
|
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
// A Jaguar pad as a stick: its directions, and its A and B as the two
|
// A Jaguar pad as a stick: its held direction bits, and its A and B (any
|
||||||
// buttons.
|
// nonzero is down) as the two buttons.
|
||||||
static void padToStick(const StePadT *pad, jlJoystickE js) {
|
static inline void padToStick(uint16_t held, uint16_t buttonA, uint16_t buttonB, jlJoystickE js) {
|
||||||
gJoyAxisX[js] = pad->axisX;
|
jlgJoyAxisX[js] = kPadAxisX[held];
|
||||||
gJoyAxisY[js] = pad->axisY;
|
jlgJoyAxisY[js] = kPadAxisY[held];
|
||||||
gJoyButtonState[js][JL_JOY_BUTTON_0] = pad->buttonA;
|
jlgJoyButtonState[js][JL_JOY_BUTTON_0] = buttonA != 0u;
|
||||||
gJoyButtonState[js][JL_JOY_BUTTON_1] = pad->buttonB;
|
jlgJoyButtonState[js][JL_JOY_BUTTON_1] = buttonB != 0u;
|
||||||
gJoyOneButton[js] = false;
|
jlgJoyOneButton[js] = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -545,22 +574,23 @@ static long patchIkbdVector(void) {
|
||||||
// last, and a pad in port B is JL_JOYSTICK_1 (port 0, the mouse's, gives
|
// last, and a pad in port B is JL_JOYSTICK_1 (port 0, the mouse's, gives
|
||||||
// no stick in TOS's IKBD mode).
|
// no stick in TOS's IKBD mode).
|
||||||
static void readStePads(void) {
|
static void readStePads(void) {
|
||||||
StePadT padA;
|
StePadsT pads;
|
||||||
StePadT padB;
|
uint16_t heldA;
|
||||||
|
|
||||||
if (!steMachineHasPads()) {
|
if (!gStePads) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stePadsRead(&padA, &padB);
|
stePadsRead(&pads);
|
||||||
if (padA.used) {
|
heldA = (uint16_t)((pads.directions >> STE_PAD_A_SHIFT) & STE_PAD_DIR_MASK);
|
||||||
|
if (heldA != 0u || ((pads.row0 | pads.row1) & STE_PAD_A_FIRE) != 0u) {
|
||||||
gPadAIsStick0 = true;
|
gPadAIsStick0 = true;
|
||||||
} else if (gIsrJoyByte[ST_PORT1_AS_JS] != 0u || gJoyButtonState[ST_PORT1_AS_JS][JL_JOY_BUTTON_0] != 0u) {
|
} else if (gIsrJoyByte[ST_PORT1_AS_JS] != 0u || jlgJoyButtonState[ST_PORT1_AS_JS][JL_JOY_BUTTON_0] != 0u) {
|
||||||
gPadAIsStick0 = false;
|
gPadAIsStick0 = false;
|
||||||
}
|
}
|
||||||
if (gPadAIsStick0) {
|
if (gPadAIsStick0) {
|
||||||
padToStick(&padA, ST_PORT1_AS_JS);
|
padToStick(heldA, (uint16_t)(pads.row0 & STE_PAD_A_FIRE), (uint16_t)(pads.row1 & STE_PAD_A_FIRE), ST_PORT1_AS_JS);
|
||||||
}
|
}
|
||||||
padToStick(&padB, ST_PORT0_AS_JS);
|
padToStick((uint16_t)((pads.directions >> STE_PAD_B_SHIFT) & STE_PAD_DIR_MASK), (uint16_t)(pads.row0 & STE_PAD_B_FIRE), (uint16_t)(pads.row1 & STE_PAD_B_FIRE), ST_PORT0_AS_JS);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -588,13 +618,19 @@ void jlpInputInit(void) {
|
||||||
KeyTabT *keyTab;
|
KeyTabT *keyTab;
|
||||||
uint32_t kbrate;
|
uint32_t kbrate;
|
||||||
|
|
||||||
gMouseAttached = true; // the IKBD mouse line is always wired on an ST
|
jlgMouseAttached = true; // the IKBD mouse line is always wired on an ST
|
||||||
memset(gKeyState, 0, sizeof(gKeyState));
|
memset(jlgKeyState, 0, sizeof(jlgKeyState));
|
||||||
memset(gKeyPrev, 0, sizeof(gKeyPrev));
|
memset(jlgKeyPrev, 0, sizeof(jlgKeyPrev));
|
||||||
memset((void *)gIsrState, 0, sizeof(gIsrState));
|
memset((void *)gIsrState, 0, sizeof(gIsrState));
|
||||||
memset((void *)gIsrTaps, 0, sizeof(gIsrTaps));
|
memset((void *)gIsrTaps, 0, sizeof(gIsrTaps));
|
||||||
memset(gTapsSeen, 0, sizeof(gTapsSeen));
|
memset(gTapsSeen, 0, sizeof(gTapsSeen));
|
||||||
memset((void *)gIsrJoyByte, 0, sizeof(gIsrJoyByte));
|
memset((void *)gIsrJoyByte, 0, sizeof(gIsrJoyByte));
|
||||||
|
gKeyEventsSeen = gIsrKeyEvents;
|
||||||
|
gKeyTapShown = false;
|
||||||
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
|
gStePads = jlSteMachineHasPads();
|
||||||
|
gPadAIsStick0 = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cache the TOS keyboard translation tables (the OS keymap) for
|
// Cache the TOS keyboard translation tables (the OS keymap) for
|
||||||
// the ISR's typed-character path. -1 leaves the tables unchanged
|
// the ISR's typed-character path. -1 leaves the tables unchanged
|
||||||
|
|
@ -624,16 +660,16 @@ void jlpInputInit(void) {
|
||||||
|
|
||||||
gMouseAbsX = JL_SURFACE_WIDTH / 2;
|
gMouseAbsX = JL_SURFACE_WIDTH / 2;
|
||||||
gMouseAbsY = JL_SURFACE_HEIGHT / 2;
|
gMouseAbsY = JL_SURFACE_HEIGHT / 2;
|
||||||
gMouseX = gMouseAbsX;
|
jlgMouseX = gMouseAbsX;
|
||||||
gMouseY = gMouseAbsY;
|
jlgMouseY = gMouseAbsY;
|
||||||
gIsrMouseDx = 0;
|
gIsrMouseDx = 0;
|
||||||
gIsrMouseDy = 0;
|
gIsrMouseDy = 0;
|
||||||
gIsrMouseButtons = 0;
|
gIsrMouseButtons = 0;
|
||||||
|
|
||||||
// Both ports always announce themselves on the ST -- packets
|
// Both ports always announce themselves on the ST -- packets
|
||||||
// arrive even when no stick is plugged in (state stays at zero).
|
// arrive even when no stick is plugged in (state stays at zero).
|
||||||
gJoyConnected[JL_JOYSTICK_0] = true;
|
jlgJoyConnected[JL_JOYSTICK_0] = true;
|
||||||
gJoyConnected[JL_JOYSTICK_1] = true;
|
jlgJoyConnected[JL_JOYSTICK_1] = true;
|
||||||
|
|
||||||
gKbdvecs = (_KBDVECS *)Kbdvbase();
|
gKbdvecs = (_KBDVECS *)Kbdvbase();
|
||||||
gPacketRemaining = 0;
|
gPacketRemaining = 0;
|
||||||
|
|
@ -655,15 +691,27 @@ void jlpInputPoll(void) {
|
||||||
uint8_t btn;
|
uint8_t btn;
|
||||||
uint8_t joy;
|
uint8_t joy;
|
||||||
uint8_t tail;
|
uint8_t tail;
|
||||||
|
uint8_t held;
|
||||||
uint8_t taps;
|
uint8_t taps;
|
||||||
|
uint8_t tapOnly;
|
||||||
|
uint16_t events;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
// Down when held, or pressed since the last poll (gIsrTaps).
|
// Down when held, or pressed since the last poll (gIsrTaps). The count
|
||||||
|
// is read first: an event the scan misses moves it for the next poll.
|
||||||
|
events = gIsrKeyEvents;
|
||||||
|
if (events != gKeyEventsSeen || gKeyTapShown) {
|
||||||
|
gKeyEventsSeen = events;
|
||||||
|
tapOnly = 0u;
|
||||||
for (i = 0u; i < JL_KEY_COUNT; i++) {
|
for (i = 0u; i < JL_KEY_COUNT; i++) {
|
||||||
|
held = gIsrState[i];
|
||||||
taps = gIsrTaps[i];
|
taps = gIsrTaps[i];
|
||||||
gKeyState[i] = (uint8_t)(gIsrState[i] | (taps != gTapsSeen[i]));
|
jlgKeyState[i] = (uint8_t)(held | (taps != gTapsSeen[i]));
|
||||||
|
tapOnly = (uint8_t)(tapOnly | (jlgKeyState[i] & (uint8_t)(held == 0u)));
|
||||||
gTapsSeen[i] = taps;
|
gTapsSeen[i] = taps;
|
||||||
}
|
}
|
||||||
|
gKeyTapShown = tapOnly != 0u;
|
||||||
|
}
|
||||||
|
|
||||||
// Drain ISR-translated characters into the core queue. Single-
|
// Drain ISR-translated characters into the core queue. Single-
|
||||||
// consumer side of the SPSC ring: only gIsrCharTail is written
|
// consumer side of the SPSC ring: only gIsrCharTail is written
|
||||||
|
|
@ -700,39 +748,39 @@ void jlpInputPoll(void) {
|
||||||
gMouseAbsX = (int16_t)newX;
|
gMouseAbsX = (int16_t)newX;
|
||||||
gMouseAbsY = (int16_t)newY;
|
gMouseAbsY = (int16_t)newY;
|
||||||
|
|
||||||
gMouseX = gMouseAbsX;
|
jlgMouseX = gMouseAbsX;
|
||||||
gMouseY = gMouseAbsY;
|
jlgMouseY = gMouseAbsY;
|
||||||
|
|
||||||
gMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_HDR_LEFT_BTN) != 0;
|
jlgMouseButtonState[JL_MOUSE_BUTTON_LEFT] = (btn & MOUSE_HDR_LEFT_BTN) != 0;
|
||||||
gMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_HDR_RIGHT_BTN) != 0;
|
jlgMouseButtonState[JL_MOUSE_BUTTON_RIGHT] = (btn & MOUSE_HDR_RIGHT_BTN) != 0;
|
||||||
gMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = false;
|
jlgMouseButtonState[JL_MOUSE_BUTTON_MIDDLE] = false;
|
||||||
|
|
||||||
// Decode latest joystick byte for each stick. Direction is digital
|
// Decode latest joystick byte for each stick. Direction is digital
|
||||||
// on the ST, so axes snap to the JOYSTICK_AXIS_* extremes.
|
// on the ST, so axes snap to the JOYSTICK_AXIS_* extremes.
|
||||||
for (i = 0; i < JL_JOYSTICK_COUNT; i++) {
|
for (i = 0; i < JL_JOYSTICK_COUNT; i++) {
|
||||||
joy = gIsrJoyByte[i];
|
joy = gIsrJoyByte[i];
|
||||||
gJoyAxisX[i] = 0;
|
jlgJoyAxisX[i] = 0;
|
||||||
gJoyAxisY[i] = 0;
|
jlgJoyAxisY[i] = 0;
|
||||||
if (joy & JOY_BIT_LEFT) {
|
if (joy & JOY_BIT_LEFT) {
|
||||||
gJoyAxisX[i] = JL_JOYSTICK_AXIS_MIN;
|
jlgJoyAxisX[i] = JL_JOYSTICK_AXIS_MIN;
|
||||||
}
|
}
|
||||||
if (joy & JOY_BIT_RIGHT) {
|
if (joy & JOY_BIT_RIGHT) {
|
||||||
gJoyAxisX[i] = JL_JOYSTICK_AXIS_MAX;
|
jlgJoyAxisX[i] = JL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
if (joy & JOY_BIT_UP) {
|
if (joy & JOY_BIT_UP) {
|
||||||
gJoyAxisY[i] = JL_JOYSTICK_AXIS_MIN;
|
jlgJoyAxisY[i] = JL_JOYSTICK_AXIS_MIN;
|
||||||
}
|
}
|
||||||
if (joy & JOY_BIT_DOWN) {
|
if (joy & JOY_BIT_DOWN) {
|
||||||
gJoyAxisY[i] = JL_JOYSTICK_AXIS_MAX;
|
jlgJoyAxisY[i] = JL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
gJoyButtonState[i][JL_JOY_BUTTON_0] = (joy & JOY_BIT_FIRE) != 0;
|
jlgJoyButtonState[i][JL_JOY_BUTTON_0] = (joy & JOY_BIT_FIRE) != 0;
|
||||||
gJoyOneButton[i] = true; // one fire button: a hold is button 1
|
jlgJoyOneButton[i] = true; // one fire button: a hold is button 1
|
||||||
}
|
}
|
||||||
// Port 1's fire and the mouse's right button are one IKBD line: while
|
// Port 1's fire and the mouse's right button are one IKBD line: while
|
||||||
// the IKBD reports the mouse (TOS's mode, kept here) a press on it
|
// the IKBD reports the mouse (TOS's mode, kept here) a press on it
|
||||||
// arrives as the right button, never in a joystick packet.
|
// arrives as the right button, never in a joystick packet.
|
||||||
if ((btn & MOUSE_HDR_RIGHT_BTN) != 0) {
|
if ((btn & MOUSE_HDR_RIGHT_BTN) != 0) {
|
||||||
gJoyButtonState[ST_PORT1_AS_JS][JL_JOY_BUTTON_0] = true;
|
jlgJoyButtonState[ST_PORT1_AS_JS][JL_JOY_BUTTON_0] = true;
|
||||||
}
|
}
|
||||||
#ifdef JOEYLIB_ATARI_STE
|
#ifdef JOEYLIB_ATARI_STE
|
||||||
readStePads();
|
readStePads();
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue