65816-llvm-mos/demos/README.md
Scott Duensing d95c30e819 Update.
2026-05-20 20:14:20 -05:00

148 lines
6.1 KiB
Markdown

# llvm816 GS/OS Demo Apps
Small Apple IIgs S16 applications that build with our LLVM/clang
toolchain, wrap as OMF v2.1 ExpressLoad, and launch under real
GS/OS 6.0.2 in MAME.
## Building / running
```
bash demos/build.sh helloBeep # build the OMF
bash demos/launch.sh helloBeep # interactive run in MAME (visible window, audio)
bash demos/test.sh helloBeep # headless test (boots, runs, checks marker)
```
The launch script runs MAME with no timeout; close the MAME
window when done (Esc, then Cmd-Q). The test script injects a
keystroke after the demo has launched, then checks `$00:0070`
for the `0x99` end-of-run marker the demo writes before exit.
## Demos
### `helloBeep.c`
Three `SysBeep()` calls then exit. The toolbox `SysBeep` lives in
Misc Tools; no other startup needed (Loader handles MM + TL).
### `helloText.c`
Initialises Event Manager (so `GetNextEvent` can read keystrokes)
and Text Tools (so `WriteCString` has an output device), writes
two greeting lines to the text screen, waits for any keypress via
`GetNextEvent`, beeps, exits. Uses these toolbox calls:
- `MMStartUp`, `NewHandle` (DP allocation for Event Manager)
- `EMStartUp`, `GetNextEvent` (event-driven keyboard)
- `TextStartUp`, `SetOutputDevice`, `WriteCString` (text I/O)
- `SysBeep`
### `helloWindow.c`
Initialises the full Window Manager startup chain (Memory, QD,
Event, Scheduler, Window), constructs an Apple-IIgs-Toolbox-Ref
NewWindow parm block, and calls `NewWindow`. When NewWindow
returns a real handle the demo calls `SetPort`, `ShowWindow`,
`MoveTo`, and `DrawString` to put a greeting in the window, then
waits for a keypress, beeps, and exits.
Three toolchain bugs needed fixing to make this work end-to-end
under real GS/OS 6.0.2 (all now landed):
- **omfEmit RESSPC=0** for code segments — BSS got no memory
allocation; writes past the image end silently vanished. Now
BSS is embedded as zeros in the LCONST data.
- **Loader cRELOC at segPlacedBase=$0000** (not $1000 like our
text-base) — host probes need to compute runtime addresses as
`link_addr - text_base + bank<<16`.
- **`&symbol` bank=0** — proper backend fix landed. `LDAi16imm_bank`
AsmPrinter pseudo lowers to `lda $BE`; crt0 stores `PBR` to $BE
+ zero to $BF at startup, so the high half of every `&symbol`
pointer carries the actual load bank at runtime. Toolbox
pointer args now Just Work without per-wrapper PBR overrides.
### `orcaFrame.c`
First ORCA-style desktop application. Opens a Window Manager
window via `startdesk()` (full toolset chain), runs a TaskMaster
event loop until the close box / Q key / 1000-iteration watchdog
fires. Both 6.0.2 (`sys602.po`) and 6.0.4 (`6.0.4 - System.Disk.po`)
launch it cleanly; fTitle works on both.
### `frame.c`
Full port of ORCA-C's `Frame.cc` sample. Builds the
Apple+File+Edit menu bar via the real ROM Menu Manager
(`NewMenu` / `InsertMenu` / `FixAppleMenu` / `FixMenuBar` /
`DrawMenuBar`) and renders the original "About Frame" dialog
(white-filled framed rect with the 1989 Byte Works copyright
text and an OK button).
### `minicad.c`
Full port of ORCA-C's `MiniCAD.cc` sample. Apple+File+Edit+
Options menu bar + a windowed canvas with three seeded line-art
patterns (curve-stitching, sunburst, Star of David).
### `reversi.c`
Full Othello game ported from ORCA-C's `Reversi.cc`. 100-byte
sentinel-bordered board, 8-direction capture detection, 1-ply
AI with corner/edge weighting, QD-rendered board with black/white
pieces.
### `qdProbe.c`
Diagnostic — minimal QD/EM/WM init followed by `RefreshDesktop`
plus ZP/SHR markers. Used to prove that `WindStartUp` does NOT
auto-paint the desktop and that `RefreshDesktop(NULL)` is what
actually fills SHR with the dithered desktop pattern. Run via
`scripts/probeQdStartup.sh`.
### Known limitations
- **fTitle on stripped 6.0.2:** orcaFrame uses fTitle and runs
fine on both 6.0.2 sys602.po and 6.0.4 System.Disk.po. Earlier
notes that fTitle required disk fonts were superseded — the
underlying bug was a `QDStartUp` argument-order mistake in
`runtime/src/desktop.c`, fixed 2026-05-16.
- **Window not visually painted:** `WindStartUp` does NOT paint
the desktop on its own; `RefreshDesktop(NULL)` is required.
Adding the call to `startdesk()` works for `qdProbe.c` but
pushes the orca demos past the GS/OS Loader's silent-rejection
threshold (see memory: `loader-creloc-threshold`).
- **GS/OS Loader cRELOC threshold:** anywhere from 65-90 cRELOCs
the Loader silently refuses to launch ExpressLoad OMFs. The
threshold is not a clean reloc-count cutoff; OMF byte layout
and reloc patch offsets both matter. The ORCA ports are slim
to stay under it.
## What got fixed during demo authoring
Substantive toolchain bugs surfaced and fixed:
- `iigsGsos.s` GS/OS wrappers and `genToolbox.py`'s 890 toolbox
wrappers were pushing 4-byte Long args low-word-first. ORCA-C's
PushLong macro is high-word-first. Anything passing a Long arg
through the toolbox dispatcher (NewHandle, NewWindow,
fopen->gsosOpen, etc.) was reading garbage parm-block pointers.
Fixed in both files plus the generator; regenerated 890
wrappers.
- `runtime/build.sh` wasn't rebuilding `iigsToolbox.s` -- the .o
was stale since May 4. Added the missing line.
- `(short)(*(void **)dpHandle)` was a double-dereference bug that
read garbage at the master pointer's destination instead of the
master pointer VALUE (= the DP address). Corrected to
`(unsigned short)(unsigned long)*(void **)dpHandle` for proper
block-address extraction.
- `helloText` used `TextStartUp + WriteCString` directly which
crashed to the IIgs monitor; fixed by adding `SetOutputDevice(1, 0)`
to wire stdout to the text screen.
- `helloText`'s event loop hung forever because `EMStartUp` wasn't
being called; fixed by adding it (with proper DP allocation).
## ptr32 mode note
All address constants in the demos (text screen $00:0400, SHR
RAM $E1:2000, soft switches $00:C0xx, keyboard register $00:C000)
are plain C 32-bit pointers like `(volatile unsigned char *)0xE12000UL`.
In ptr32 mode the compiler emits LONG addressing automatically;
no `switchToBank2`-style inline asm or DBR juggling required.