== 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 <>) 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 (`staxiData`, Space Taxi's level source, and `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//bin/`. The IIgs additionally produces a bootable disk image: [source,console] ---- $ make iigs-disk # build/iigs/bin/joey.2mg ---- Space Taxi's levels come from the original Commodore 64 game, which is not part of the repository. Without `stuff/spacetaxi/raw.bin` the build still compiles the STAXI program, skips only its data files, and says so: [source,console] ---- note: Space Taxi data skipped: stuff/spacetaxi/raw.bin is not present ---- [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 #include 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 ` 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 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/STAXI.2mg $ ./joey-run --port dvx staxi ---- 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 an AGI game's files. | `--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`; AGI wants `8M`. | `--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-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. Four 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), the X68000 CG (font) ROM, and the memory image of the original Space Taxi that its levels are extracted from. 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 and digital sound effects. |`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. |`agi` |A Sierra AGI interpreter, showing the library under real load. |`spacetaxi` |A full port of Space Taxi: the largest example. |`uber` |The validation and benchmark harness. |=== On DVX every example is a folder of its own under `build/dvx/bin/apps/joeylib/` with its help file (<<_dvx_programs>>).