65816-llvm-mos/demos/README.md
2026-05-18 14:43:35 -05:00

149 lines
6.2 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.
### `orcaFrameLike.c`
Port of ORCA-C's `Frame.cc` sample (`tools/orca-c/C.Samples/
Desktop.Samples/Frame.cc`). Builds a standard Apple+File+Edit
menu bar (`NewMenu` + `InsertMenu` + `FixAppleMenu` + `DrawMenuBar`)
and dispatches `wInMenuBar` / `wInSpecial` events from `TaskMaster`.
File→Quit exits. Skips the original's Dialog Manager About box.
### `orcaMiniCadLike.c`
Port of ORCA-C's `MiniCAD.cc` (`Desktop.Samples/MiniCAD.cc`). Slim
port — opens a Window Manager content window but omits the line-
drawing primitives because adding them pushes past the Loader's
cRELOC threshold. Demonstrates the NewWindow path under
`startdesk`.
### `orcaReversiLike.c`
Port of ORCA-C's `Reversi.cc` (`Desktop.Samples/Reversi.cc`).
Menu-bar app — the ~1600 line game logic is omitted; the demo
shows the desktop scaffolding (menu + TaskMaster) the original
sits on top of.
### `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.