joeylib2/docs/GettingStarted.adoc

540 lines
17 KiB
Text

== Getting Started
=== Setting Up the Toolchains
JoeyLib cross-compiles for five machines and for DVX, a DOS desktop whose
programs run in windows (see <<_dvx_programs>>). It builds on Linux x86-64,
and on Windows inside WSL2. One script installs every cross toolchain, pinned
to a known-good version and checked against a published checksum, into
`toolchains/` in the repository. The only things it installs anywhere else
are ordinary packages from your distribution (apt on Debian and Ubuntu, dnf
on Fedora), and only when you ask it to.
Two commands set up a new machine:
[source,console]
----
$ ./install.sh deps toolchains --install
$ ./install.sh install toolchains
----
The first installs the distribution packages the toolchains need; it runs
`sudo` once. The second downloads or builds every cross toolchain. The first
run takes about an hour, almost all of it compiling the Apple IIgs and Amiga
compilers. To start working sooner, install only the ports you need:
[source,console]
----
$ ./install.sh install toolchain:dos toolchain:atarist
----
Running `install` again is cheap. Components that are already current are
skipped, and a component is rebuilt only when its pinned version changes.
No environment setup is needed afterwards. `make` reads the tool paths the
installer records in `toolchains/config.mk`, and the scripts in
`scripts/` load the same settings themselves. To use a cross compiler by hand
in a shell, `source toolchains/env.sh` sets the same variables.
If a port's toolchain is missing, its build stops at once and says what to
install:
[source,console]
----
$ make amiga
make/amiga.mk:5: *** toolchain:amiga is not installed; run: ./install.sh install toolchain:amiga. Stop.
----
Check what the build can see:
[source,console]
----
$ make status
Toolchain availability:
iigs: yes
amiga: yes
atarist: yes
dos: yes
x68000: yes
dvx: yes
----
==== Installer reference
`./install.sh [options] COMMAND [TARGETS...]`, run from the top of the
repository.
[cols="1,3"]
|===
| Command | What it does
| `list`
| Every component, the groups it belongs to, and whether it is current,
outdated or missing.
| `install TARGETS`
| Installs or updates the targets and everything they depend on, and
smoke-tests each one it installs. A failure does not stop the rest; the
failures are listed at the end.
| `deps TARGETS`
| Lists the distribution packages the targets need and which are missing.
With `--install`, installs the missing ones.
| `doctor`
| Checks the host, then smoke-tests every installed component (each cross
compiler compiles a file, and every prebuilt program must find its shared
libraries, for example). It reports anything out of date, any component
whose files have gone, and any part of a port you installed only partly,
such as FS-UAE without a Kickstart.
| `import PATH...`
| Installs the files that are not downloaded (see <<Firmware that is not
downloaded>>) from files, folders, `.zip` archives or Amiga `.adf` disk
images, matched by checksum.
| `bundle FILE`
| Packs those files, as present on this machine, into `FILE` (a `.tar.gz`)
for a project to host.
|===
A target is a component name from `list`, or a group:
[cols="1,3"]
|===
| Target | Means
| `toolchain:PORT`
| Everything needed to build for one port: `iigs`, `amiga`, `atarist`, `dos`,
`x68000` or `dvx`.
| `emulator:PORT`
| Everything needed to run that port's programs: the emulator and its
firmware.
| `PORT`
| Both of the above.
| `toolchains`
| Every port's toolchain.
| `emulators`
| Every port's emulator.
| `all`
| Everything except optional components (`dvx`, which unpacks DVX from a DVX
tree of yours), which are installed by name. `toolchains` and `emulators`
leave them out too.
|===
[cols="1,3"]
|===
| Option | Effect
| `-y`, `--yes`
| Install missing distribution packages without stopping to ask.
| `--install`
| With `deps`: install the missing packages.
| `--force`
| Reinstall components even when they are current.
| `--jobs N`
| Parallel build jobs. The default is every core.
| `--keep-build`
| Keep the scratch build trees (normally deleted after a successful install).
| `--llvm816-dir PATH`
| Use an existing checkout of llvm816, the IIgs compiler, instead of cloning
it. For working on the compiler and JoeyLib together. The setting is
remembered; `--llvm816-dir clone` goes back to a managed clone.
| `--dvx-dir PATH`
| The DVX tree the `dvx` component takes DVX and its SDK from: its
distribution, `dist/dvx.zip` (DVX as it installs) and `dist/sdk.zip` (the
SDK). The default is a `dvxgui` checkout beside this repository; the
setting is remembered. The component only reads that tree: it unpacks both
zips into `toolchains/opt/dvx`, and a new zip there makes the component out
of date, so `install dvx` unpacks it again. (A tree whose `dist/` has no
`sdk.zip` yet gives its built SDK, `bin/sdk`, instead.)
|===
Everything the installer creates stays inside `toolchains/`:
[cols="1,3"]
|===
| Path | Contents
| `toolchains/opt/`
| The installed components, one directory each.
| `toolchains/firmware/`
| ROM and system-disk images for the emulators.
| `toolchains/cache/`
| Downloads, kept and re-verified by checksum on reuse, and scratch build
trees.
| `toolchains/state/`
| Which version of each component is installed, and settings such as
`--llvm816-dir` and `--dvx-dir`.
| `toolchains/config.mk`, `toolchains/env.sh`
| The tool paths, for `make` and for shells.
|===
Nothing in `toolchains/` is part of the repository: delete the whole folder
to return the machine to a clean state, and `./install.sh` re-creates it. The
installer itself lives in `install.sh` and `installer/` (its component
recipes, patches and package lists), and the build helpers the makefiles call
live in `make/support/`.
=== Building
[source,console]
----
$ make # every target whose toolchain is installed
$ make iigs # Apple IIgs only
$ make amiga # Commodore Amiga only
$ make atarist # Atari ST only
$ make dos # MS-DOS only
$ make x68000 # Sharp X68000 only
$ make dvx # DVX only
$ make clean # remove all build outputs
----
Built programs land under `build/<platform>/bin/`. The IIgs additionally
produces a bootable disk image:
[source,console]
----
$ make iigs-disk # build/iigs/bin/joey.2mg
----
[TIP]
====
Never judge a retro build by its exit status alone. Check that the file you
expect actually appeared and has a recent timestamp. `make iigs` builds the
library but *no examples* -- `make iigs-disk` is what builds and packages them.
====
=== Hello, JoeyLib
The smallest complete program. This is `examples/hello/hello.c`:
[source,c]
----
#include <stdio.h>
#include <joey/joey.h>
int main(void) {
jlConfigT config;
config.codegenBytes = 8 * 1024;
if (!jlInit(&config)) {
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
return 1;
}
printf("JoeyLib %s\n", jlVersionString());
printf("Platform: %s\n", jlPlatformName());
printf("Hello from JoeyLib.\n");
jlShutdown();
return 0;
}
----
Three things to notice, because they apply to every JoeyLib program:
. *One header.* `#include <joey/joey.h>` pulls in everything except the
optional serial add-on.
. *`jlInit` can fail*, and when it does `jlLastError()` tells you why. Always
check it.
. *`jlShutdown` is not optional.* It restores the display mode, releases the
Memory Manager blocks or DOS memory the library took, and on several ports
un-hooks interrupt vectors. Skipping it can leave the machine in an unusable
state.
=== Something on the Screen
`hello.c` prints text but never draws. Here is the smallest program that puts
graphics on screen and waits for a keypress:
[source,c]
----
#include <joey/joey.h>
int main(void) {
jlConfigT config;
jlSurfaceT *stage;
uint16_t colors[SURFACE_COLORS_PER_PALETTE];
uint8_t i;
config.codegenBytes = 8 * 1024;
if (!jlInit(&config)) {
return 1;
}
stage = jlStageGet(); // the library-owned back buffer
// Build palette 0. Entry 0 is forced black whatever we put here.
for (i = 0; i < SURFACE_COLORS_PER_PALETTE; i++) {
colors[i] = 0x000;
}
colors[1] = 0xFFF; // white
colors[2] = 0xF00; // red
colors[3] = 0x00F; // blue
jlPaletteSet(stage, 0, colors);
// Every scanline uses palette 0 (this is also the default).
jlScbSetRange(stage, 0, SURFACE_HEIGHT - 1, 0);
jlSurfaceClear(stage, 0);
jlFillRect(stage, 40, 30, 240, 140, 3);
jlDrawRect(stage, 40, 30, 240, 140, 1);
jlDrawLine(stage, 0, 0, SURFACE_WIDTH - 1, SURFACE_HEIGHT - 1, 2);
jlStagePresent(); // nothing is visible until this runs
jlWaitForAnyKey();
jlShutdown();
return 0;
}
----
The critical line is `jlStagePresent()`. All drawing goes to an off-screen
back buffer called the *stage*; nothing reaches the display until you present.
=== Running What You Built
`joey-run`, at the top of the repository, starts a built program in the right
emulator. The emulators, and the firmware they boot, come from the installer:
[source,console]
----
$ ./install.sh install emulators # every machine
$ ./install.sh install emulator:dos # or just one
----
MAME (for the IIgs and X68000) is always built from source, because the test
gates need JoeyLib's patches to it. DOSBox Staging is upstream's own Linux
release. Hatari and FS-UAE are distribution packages on Debian and Ubuntu;
Fedora does not ship the versions the gates expect, so there the installer
builds both from source.
Point `joey-run` at a program, or name an example together with `--port`:
[source,console]
----
$ ./joey-run build/dos/bin/DRAW.EXE
$ ./joey-run build/amiga/bin/Draw
$ ./joey-run --port atarist draw
$ ./joey-run build/iigs/bin/DRAW.2mg
$ ./joey-run --port dvx draw
----
It recognises the machine from the file itself. Interactive runs use the
program's own folder or disk image, so saves and logs persist from one run to
the next. Nothing is written to your home directory.
[cols="1,3"]
|===
| Option | Effect
| `--port PORT`
| The machine, when naming an example rather than a file.
| `--data DIR`
| Copy `DIR`'s files into the program's `DATA` folder for this run, for
example the game an interpreter plays.
| `--headless`
| No window and no sound, for scripts and continuous integration. Stops after
60 seconds unless `--seconds` says otherwise.
| `--seconds N`
| Stop after `N` seconds.
| `--snapshot FILE`
| Save a PNG of the screen when the run stops.
| `--machine rom01`
| Apple IIgs: the ROM 01 machine instead of ROM 03.
| `--ram SIZE`
| Apple IIgs: MAME's memory size. The default is `4M`.
| `--debug`
| Apple IIgs and X68000: start MAME's debugger.
| `--serial HOST:PORT`
| Connect the machine's serial port to the TCP server at `HOST:PORT`. See
<<Serial Ports>>.
| `--serial-listen PORT`
| The machine's serial port waits for a TCP connection on `PORT` instead.
| `--serial-baud N`
| Apple IIgs and X68000: the rate MAME's end of the wire runs at. The
default is 9600.
| `-- ARGS`
| Pass `ARGS` to the program. DOS only: the other machines' autostart cannot
pass arguments.
|===
==== DVX
`joey-run` runs a DVX program in DOSBox Staging on a copy of the DVX the
installer took from a DVX tree (`./install.sh install dvx`), set up so DVX
boots straight into the program: it is DVX's desktop program, in its own
window on a 1024x768 desktop (640x480 with `--headless`), with a Sound
Blaster 16. The DVX of interactive runs is kept in `build/dvx/run`, so what
the program saves, in DVX's `CONFIG` folder, is there the next time; closing
DOSBox ends the run. When the program ends, DVX stays up with an empty
desktop.
A headless run with a snapshot is the quickest way to check a change:
[source,console]
----
$ ./joey-run --headless --seconds 30 --snapshot draw.png --port dos draw
----
==== The Apple IIgs
The IIgs runs programs from disk images. `make -f make/iigs.mk iigs-game-disk
GAME=DRAW` builds a bootable one, `build/iigs/bin/DRAW.2mg`, which is also how
a game ships. Given a data disk instead, such as `joey.2mg`, `joey-run` boots
GS/OS beside it and you open the program in the Finder. Given a bare program,
`build/iigs/bin/DRAW`, it builds both disks itself, which needs the IIgs
toolchain.
==== Serial Ports
`--serial HOST:PORT` plugs the emulated machine's serial port into a TCP
connection: to a BBS, a program of your own, `nc -l PORT`, or another
`joey-run` started with `--serial-listen PORT`, which waits for the
connection instead. `HOST` is a name or an IPv4 address, and a listening
port accepts connections on every network interface. The link carries raw
bytes, with no telnet negotiation. `examples/sertest` sends a banner and then
echoes whatever it receives, which makes it a quick test of a link:
[source,console]
----
$ nc -l 2323 # in one terminal
$ ./joey-run --serial localhost:2323 --port dos sertest
----
The port is each machine's usual one:
[cols="1,2,3",options="header"]
|===
| Machine | Port | How the link is made
| Apple IIgs
| The modem port
| MAME connects by itself. To listen, `socat` holds the TCP port and MAME
connects to it. The printer port and a Super Serial Card are not connected.
| X68000
| The RS-232C port
| As on the IIgs.
| Atari ST
| The Modem port
| Hatari's serial port can only be a device, so `socat` joins a
pseudo-terminal to the network both ways.
| Amiga
| `serial.device` unit 0
| FS-UAE listens by itself, and holds the whole machine until the connection
arrives. To connect out, `socat` joins a pseudo-terminal to the network.
| DOS
| COM1
| DOSBox does both by itself.
| DVX
| COM1
| As on DOS; DVX's serial library drives the port.
|===
`socat` comes from your distribution: `./install.sh deps host --install`
fetches it with the other host tools, and `joey-run` says so when a run needs
it and it is missing.
A few differences are worth knowing:
* On the IIgs and X68000 MAME's null modem clocks the wire, at 9600 baud,
8N1, unless `--serial-baud` says otherwise. A program that opens the port at
another rate receives garbage. The other emulators pass bytes at whatever
rate the program sets.
* When nothing answers at `HOST:PORT`, `joey-run` stops, except on the IIgs
and X68000, where MAME prints `Unable to load image` and runs with the port
unplugged.
* While a port is listening, what the program sends waits for the connection,
except on DOS, which drops it just as a cable with nothing on the other end
would. `examples/sertest` sends its banner at once, so on DOS a client that
connects late sees only the echo.
==== Firmware that is not downloaded
Almost everything the emulators need is downloaded: EmuTOS for the Atari ST,
the Apple IIgs ROMs, GS/OS 6.0.4 and the CFFA2 card's firmware, and Sharp's own
free release of the X68000 IPL-ROM and Human68k 3.02. Three files are not,
because they were never released for free: the Amiga Kickstart 3.1 ROM, the
Amiga `serial.device` from Workbench 3.1 (Kickstart 3.1 has none in ROM, and
a program cannot open the serial port without it) and the X68000 CG (font)
ROM. Supply your copies once:
[source,console]
----
$ ./install.sh import ~/my-roms
----
`import` takes files, folders, `.zip` archives and Amiga `.adf` disk images
(point it at your Workbench 3.1 disk for `serial.device`), identifies each
file by its checksum (the list is `installer/files.sha1`) and puts it in
place.
A project that hands its setup to testers can host these files instead:
`./install.sh bundle joeylib-private.tar.gz` packs the ones present on
a machine that has them, and setting `JOEY_BUNDLE_URL` to where it is hosted,
in `installer/bundle.conf`, makes every `install` fetch it automatically.
=== The Example Programs
The `examples/` directory is the best available reference, and every example is
built for every platform:
[cols="1,4", options="header"]
|===
|Example |What it shows
|`hello` |Minimal init, version query, shutdown.
|`draw` |Every drawing primitive, laid out in four quadrants.
|`pattern` |Palette and per-scanline palette (SCB) work.
|`keys` |Typed-character input and the key predicates.
|`joy` |Joystick axes, buttons and recalibration.
|`sprite` |Sprite loading, compiling, save-under and restore.
|`audio` |Tracker music, `.MOD` playback, digital sound effects, a streamed sound and the noise generator.
|`save` |Save files and the disk-space query.
|`tilebank` |Loading a baked tile bank both ways, and a bank for another machine refused.
|`serial` |The optional RS-232 add-on.
|`adventure` |A small complete game.
|`uber` |The validation and benchmark harness.
|===
Two complete games built on JoeyLib live in repositories of their own and
show the library under real load: Space Taxi, a port of the 1984 Commodore 64
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
would (<<_building_your_program>>).
On DVX every example is a folder of its own under `build/dvx/bin/apps/joeylib/`
with its help file (<<_dvx_programs>>).