joeylib2/docs/BuildIntegration.adoc
2026-09-29 19:31:34 -05:00

221 lines
7.1 KiB
Text

== Adding Your Program to the Build
JoeyLib does not install as a system library. Programs are built from inside
the repository by the same makefiles that build the library, because each
target needs its own cross compiler, link script, packaging step and disk
image. Adding a program means adding it to each port's makefile.
=== The Shape of a Program Directory
Put your sources under `examples/`, one directory per program:
----
examples/mygame/
mygame.c
mygame.h
assets/
tiles.png
sprites.png
music.song
----
Keep the authoring assets next to the code and let the makefiles bake them into
`DATA/`, as described in <<_creating_assets>>.
=== Adding It to One Port
Each port's makefile declares programs explicitly. The pattern in
`make/atarist.mk`, for a single-source program:
[source,makefile]
----
MYGAME_SRC := $(EXAMPLES)/mygame/mygame.c
MYGAME_BIN := $(BINDIR)/MYGAME.PRG
----
Add the binary to the port's `all` target so it gets built:
[source,makefile]
----
all atarist: $(LIB) ... $(MYGAME_BIN) $(DATA_FILES)
----
And give it a build rule, copying the shape of an existing example in that
file. Do not invent your own compiler invocation: the flags, include paths and
link order in those rules encode real constraints, several of them the result
of long debugging.
For a multi-file program, list the sources:
[source,makefile]
----
MYGAME_SRCS := $(EXAMPLES)/mygame/mygame.c \
$(EXAMPLES)/mygame/world.c \
$(EXAMPLES)/mygame/render.c
----
Repeat for each port you want to support: `make/iigs.mk`, `make/amiga.mk`,
`make/atarist.mk`, `make/dos.mk`, `make/x68000.mk` and `make/dvx.mk`.
`make/dvx.mk` is the one exception to the pattern. A DVX program is a DVX SDK
project built by the SDK's own `sdk.mk`, which `make/dvx.mk` writes for every
name in its `APPS` list from three variables, plus a fourth for a program
with data files:
[source,makefile]
----
mygame_SRCS := $(EXAMPLES)/mygame/mygame.c $(EXAMPLES)/mygame/world.c
mygame_TITLE := My Game
mygame_HELP := One line on what the program is, for its help file.
mygame_DATA := $(DATADIR)/mygame/DATA # optional: staged by a rule of yours
APPS := hello pattern ... mygame
----
[NOTE]
====
Yes, that is six makefile edits for one program. The ports genuinely differ:
the IIgs needs OMF segmentation and a disk image, the Amiga a startup-sequence
entry, DOS a DPMI stub, the X68000 a Human68k floppy layout, and DVX a module
with a help file. There is no single rule that covers them all.
====
=== Naming
File naming conventions differ per platform and the makefiles reflect that:
[cols="2,2,4", options="header"]
|===
|Platform |Convention |Example
|Apple IIgs |Uppercase, 8 characters |`MYGAME`
|Atari ST |Uppercase 8.3, `.PRG` |`MYGAME.PRG`
|MS-DOS |Uppercase 8.3, `.EXE` |`MYGAME.EXE`
|Amiga |Mixed case, no extension |`MyGame`
|Sharp X68000 |Uppercase 8.3, `.X` |`MYGAME.X`
|DVX |Lowercase, 8 characters, a folder of its own |`apps/joeylib/mygame/mygame.app`
|===
Keep the base name to 8 characters and letters and digits only, so the same
name works everywhere.
=== Disk Images and Packaging
Some ports need a packaging step beyond compiling.
*Apple IIgs.* Programs are placed on an 800 KB ProDOS disk image:
[source,console]
----
$ make iigs-disk
$ JOEY_DISK_EXAMPLES="MYGAME" make iigs-disk # just yours
----
The floppy cannot hold every example at once, so the packager drops some. If
your program is missing from the image, build it with `JOEY_DISK_EXAMPLES`
naming it explicitly, and check the `added:` line the packager prints. A
program plus large assets may not fit 800 KB at all, in which case use the
bigger game-disk image on a CFFA2 card:
[source,console]
----
$ JOEY_GAME_DISK_SIZE=2048KB scripts/make-iigs-game-disk.sh \
build/iigs/bin/mygame.2mg MYGAME
----
*Sharp X68000.* Programs ship on a Human68k floppy. Be aware that a stale image
can hide hundreds of kilobytes in subdirectories that a directory listing does
not show; rebuild from empty rather than deleting files if space seems to
vanish.
*Amiga.* `joey-run` writes an `s/startup-sequence` that invokes your binary.
*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
`mygame.hlp` (DVX gives every program one; `make/dvx.mk` writes a one-topic
help 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
(<<_dvx_programs>>).
=== Verifying It Actually Runs
Build success is weak evidence on retro targets. Run it with `joey-run` (see
<<Running What You Built>>), on every machine:
[source,console]
----
$ ./joey-run --port iigs mygame
$ ./joey-run --port amiga mygame
$ ./joey-run --port atarist mygame
$ ./joey-run --port dos mygame
$ ./joey-run --port x68000 mygame
$ ./joey-run --port dvx mygame
----
Without a window, which is what you want while iterating, run it headless and
look at the snapshot; the program's log lands next to it as usual:
[source,console]
----
$ ./joey-run --headless --seconds 90 --snapshot mygame.png --port atarist mygame
----
=== The Gates
The repository carries automated checks. They exist because several classes of
bug on these machines are invisible until measured, and they are worth running
against your own program too.
[cols="2,4", options="header"]
|===
|Command |What it proves
|`make verify-audio`
|Every port's audio gate: the IIgs DOC and X68000 chips are read directly,
the ST's YM2149 register writes are traced, and the Amiga, DOS and DVX
output is recorded and graded. Exits 3, not 0, if a gate had to be skipped.
|`make iigs-verify`
|The IIgs actually reached the program and drew something.
|`scripts/verify-<port>-tilebank.sh`
|The port's own tile-bank bake loads pixel-exact both ways, with the same
surface hash as the host check `scripts/check-tilebank.sh`, and banks baked
for the other machines are refused. `scripts/verify-all.sh tilebank` runs
every port's.
|`scripts/bench-<port>.sh`
|Per-operation throughput for that port.
|`tools/diff-uber-hashes`
|Compares a port's rendered output against the IIgs golden reference,
operation by operation, by surface hash.
|===
That last one is the strongest tool in the repository. `jlSurfaceHash` produces
the same value for the same logical pixels on every platform regardless of
internal storage format, so a hash mismatch means one port is genuinely drawing
different pixels from the others.
=== Logging from the Target
Debugging a program running on an emulated 1988 machine is awkward. JoeyLib
provides a logger that writes to a file next to the executable (on DVX, in
the program's settings folder):
[source,c]
----
jlLogReset();
jlLogF("level %d loaded, %u cels", levelNum, celCount);
jlLogFlush();
----
The headless runners retrieve that file automatically.
[NOTE]
====
The logger needs a multi-kilobyte ring buffer, which some targets cannot
afford. Space Taxi compiles it out entirely on the IIgs and X68000 for that
reason. If you are debugging a memory-tight program, expect to do the same, and
fall back to observing hardware state through an emulator instead.
====