# 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.
