From a1b0ee7ec2a5c5334907f3fd68c24f1b421d2511 Mon Sep 17 00:00:00 2001 From: Willem Drijver Date: Wed, 4 Nov 2026 17:11:45 +0300 Subject: [PATCH 1/2] ApolloLib in toolchain/apollolib/, with its known bugs Describe ApolloLib (ApolloCrossDev Projects/ApolloLib, commit ce1fcbf, after the merge of the two versions in ApolloCrossDev #16 and #17): how a project uses it, sound on Arne, pictures, PIP and the pointer, input, memory and blit routines, system and timing, and the serial debug output. toolchain/pitfalls/apollolib-known-bugs.md lists what reading the code found, among them the 320-pixel fall-through in ApolloShowPicture, the VBlank server of ApolloTimer that is never removed, and the takeover helpers without restore. Co-Authored-By: Claude Opus 5.5 --- .github/CODEOWNERS | 8 ++ .../prefer-library-calls-for-saga-chips.md | 5 +- toolchain/apollolib/debug.md | 92 +++++++++++++ toolchain/apollolib/index.md | 120 +++++++++++++++++ toolchain/apollolib/input.md | 98 ++++++++++++++ toolchain/apollolib/memory-and-blit.md | 88 +++++++++++++ toolchain/apollolib/picture.md | 121 ++++++++++++++++++ toolchain/apollolib/sound.md | 106 +++++++++++++++ toolchain/apollolib/system-and-timing.md | 82 ++++++++++++ toolchain/index.md | 4 + toolchain/pitfalls/apollolib-known-bugs.md | 90 +++++++++++++ toolchain/project-makefile.md | 3 +- 12 files changed, 815 insertions(+), 2 deletions(-) create mode 100644 toolchain/apollolib/debug.md create mode 100644 toolchain/apollolib/index.md create mode 100644 toolchain/apollolib/input.md create mode 100644 toolchain/apollolib/memory-and-blit.md create mode 100644 toolchain/apollolib/picture.md create mode 100644 toolchain/apollolib/sound.md create mode 100644 toolchain/apollolib/system-and-timing.md create mode 100644 toolchain/pitfalls/apollolib-known-bugs.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8b66d1d..daa08c0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -186,7 +186,15 @@ /toolchain/project-makefile.md @WDrijver /toolchain/target-os-headers.md @WDrijver /toolchain/vasm-assembler.md @WDrijver +/toolchain/apollolib/debug.md @WDrijver +/toolchain/apollolib/index.md @WDrijver +/toolchain/apollolib/input.md @WDrijver +/toolchain/apollolib/memory-and-blit.md @WDrijver +/toolchain/apollolib/picture.md @WDrijver +/toolchain/apollolib/sound.md @WDrijver +/toolchain/apollolib/system-and-timing.md @WDrijver /toolchain/best-practices/gcc-code-generation-for-68080.md @TuKo1982 +/toolchain/pitfalls/apollolib-known-bugs.md @WDrijver /toolchain/pitfalls/calling-assembler-from-gcc.md @TuKo1982 /toolchain/pitfalls/float-settings-mismatch.md @TuKo1982 /toolchain/pitfalls/gcc-bbb-autoinc.md @TuKo1982 diff --git a/core/best-practices/prefer-library-calls-for-saga-chips.md b/core/best-practices/prefer-library-calls-for-saga-chips.md index b351dea..71cbff4 100644 --- a/core/best-practices/prefer-library-calls-for-saga-chips.md +++ b/core/best-practices/prefer-library-calls-for-saga-chips.md @@ -13,6 +13,7 @@ tags: [saga, arne, isabelle, maggie, library, apollolib, maggie-library, registe related: - core/saga-registers/index.md - os/libraries/maggie-library.md + - toolchain/apollolib/index.md - core/saga-audio-arne/index.md - core/saga-video-isabelle/index.md - core/saga-3d-maggie/index.md @@ -41,7 +42,9 @@ For the SAGA chips, prefer the library calls to direct register access - An official library for Arne and Isabelle is in development. Until it is released, ApolloLib is the best choice for library calls to Arne and - Isabelle. + Isabelle. It is described in + [toolchain/apollolib/](../../toolchain/apollolib/index.md); check its + [known bugs](../../toolchain/pitfalls/apollolib-known-bugs.md) first. - maggie.library is described in [os/libraries/maggie-library.md](../../os/libraries/maggie-library.md). When you need more than that file covers, the author's repository is the diff --git a/toolchain/apollolib/debug.md b/toolchain/apollolib/debug.md new file mode 100644 index 0000000..0b83fc8 --- /dev/null +++ b/toolchain/apollolib/debug.md @@ -0,0 +1,92 @@ +--- +title: ApolloLib serial debug output +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain] +applies_to: + core: "any" + os: [any] +tags: [apollolib, debug, serial, rawdofmt, logging] +related: + - toolchain/apollolib/index.md + - toolchain/project-makefile.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_Debug.h, _Debug.c, _Defines.h), commit ce1fcbf (2026-11-04)" +--- + +## Summary + +ApolloLib writes debug text straight to the serial port at 115200 baud, +without OS calls, so it also works without a console and with the OS +taken over. Two macro sets use it: `AD()`/`ADX()` around `sprintf`, and +the levelled `Debug_Info()` … `Debug_Flag()` macros. + +## Details + +### Functions (ApolloCrossDev_Debug.c) + +| Function | What it does | +|---|---| +| `ApolloDebugInit()` | Sets DTR and RTS on CIA-B and SERPER to 115200 baud (PAL clock) | +| `ApolloDebugPutChar(c)` | Waits for TBE, writes one character to SERDAT; `\n` becomes `\r\n` | +| `ApolloDebugMayGetChar()` | Returns a received character, or −1 | +| `ApolloDebugPutStr(s)` | Writes a string | +| `ApolloDebugPutDec(label, v)`, `ApolloDebugPutHex(label, v)`, `ApolloDebugPutHexVal(v)` | Write `label: value` in decimal or hex | +| `WriteRegisterWord(reg, v)`, `ReadRegisterWord(reg)` | Write and read a 16-bit hardware register | + +`char ApolloDebugMessage[256]` is the shared buffer for `sprintf`. + +### Macros, set 1: AD and ADX (ApolloCrossDev_Defines.h) + +| Macro | Active with | +|---|---| +| `AD(x)` | `-DAPOLLO_DEBUG` | +| `ADX(x)` | `-DAPOLLO_DEBUG -DAPOLLO_DEBUGEXTRA` | +| `ADXX(x)` | Never (always empty) | + +```c +AD(sprintf(ApolloDebugMessage, "loaded %s\n", name);) +AD(ApolloDebugPutStr(ApolloDebugMessage);) +``` + +ApolloLib's own code uses this set. The example Makefiles build with +`-DAPOLLO_DEBUG -DAPOLLO_DEBUGEXTRA`. + +### Macros, set 2: levels (ApolloCrossDev_Debug.h) + +Set `DEBUG` to a sum of levels, for example `-DDEBUG=7`: + +| Level | Value | Macro | +|---|---|---| +| `DBG_INFO` | 1 | `Debug_Info(fmt, ...)` | +| `DBG_WARN` | 2 | `Debug_Warn(...)` (yellow) | +| `DBG_ERROR` | 4 | `Debug_Error(...)` (red) | +| `DBG_FLAG` | 8 | `Debug_Flag(...)` (blinking magenta) | +| `DBG_TRACE` | 16 | `Debug_Trace(...)` (grey) | +| `DBG_CMD` | 32 | `traceCommand(req)`: device drivers only, not in ApolloLib | +| `DBG_MEM` | 64 | `AllocMem`/`FreeMem` wrappers: device drivers only, not in ApolloLib | + +The macros print `[project][file][function][line] [LEVEL]` and the +message through exec `RawDoFmt` inside `Forbid()`/`Permit()`. They need +`PROJECT_NAME` and `BASE_FILE_NAME` defined, for example by the Makefile. +`RawDoFmt` formats: use `%ld` for 32-bit values. + +`BUG_RED`, `BUG_BOLD` … are ANSI colour codes for your own messages. + +## Differences from AmigaOS / AROS / 68060 + +- `kprintf`/debug.lib need the OS and go through exec; ApolloLib writes + SERDAT directly and works in takeover code and interrupts. +- The serial port is shared: don't use the debug output while serial.device + owns the port. + +## Do / Don't + +- DO call `ApolloDebugInit()` once before the first output. +- DO use `%ld` in `Debug_…` formats (`RawDoFmt`) and the C types in `AD()` + (`sprintf`). +- DON'T set `DBG_CMD` or `DBG_MEM` in an ApolloLib program: their code is + not in ApolloLib. diff --git a/toolchain/apollolib/index.md b/toolchain/apollolib/index.md new file mode 100644 index 0000000..b891269 --- /dev/null +++ b/toolchain/apollolib/index.md @@ -0,0 +1,120 @@ +--- +title: ApolloLib, the ApolloCrossDev library for SAGA and the 68080 +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, saga-audio-arne, saga-video-isabelle] +applies_to: + core: "any" + os: [any] +tags: [apollolib, library, saga, arne, isabelle, pip, pointer, debug] +related: + - core/best-practices/prefer-library-calls-for-saga-chips.md + - core/saga-registers/index.md + - toolchain/project-makefile.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib, commit ce1fcbf (2026-11-04)" + - "WDrijver (maintainer), library calls for the SAGA chips, 2026-11-04" +--- + +## Summary + +ApolloLib is the C and assembly library that ships with ApolloCrossDev in +`Projects/ApolloLib`. It loads and plays sounds on Arne, loads and shows +pictures on the SAGA chunky plane, PIP and pointer sprite, reads mouse, +USB joypad and keyboard, and has fast copy, fill and blit routines and a +serial debug output. Until the official libraries for Arne and Isabelle +are released, ApolloLib is the preferred way to use them (see +[prefer-library-calls-for-saga-chips.md](../../core/best-practices/prefer-library-calls-for-saga-chips.md)). + +## Details + +### Using it in a project + +ApolloLib is not a link library: a project compiles its sources. The +example Makefiles (ApolloPointer, SAGAPiP) set +`APOLLOLIB = $(PWD)/../../Projects/ApolloLib`, add `-I$(APOLLOLIB)`, +and compile every `.c` and `.s` file there into the program (see +[project-makefile.md](../project-makefile.md)). + +```c +#include "ApolloCrossDev_Lib.h" /* includes _Defines.h and _Debug.h */ +``` + +| File | Content | +|---|---| +| `ApolloCrossDev_Lib.h` | Structures and prototypes | +| `ApolloCrossDev_Defines.h` | SAGA addresses, modes, formats, return codes, debug switches, Amiga includes | +| `ApolloCrossDev_LibC.c` | Files, sound, picture, PIP, pointer, input | +| `ApolloCrossDev_LibASM.s` | Copy, fill, blit, endian swap, timing, takeover (vasm, 68080 and AMMX) | +| `ApolloCrossDev_Debug.h`, `_Debug.c` | Serial debug output | + +The assembly functions take their arguments in registers, declared with +the macros `_A0(...)` … `_D7(...)` from `_Defines.h`. + +### Areas + +| Area | Functions | File | +|---|---|---| +| Sound on Arne | `ApolloLoadSound`, `ApolloPlaySound`, `ApolloStopSound`, `ApolloVolume…`, fades; the channel API `ApolloPlay`, `ApolloPlayFile`, `ApolloStartChannel`, `ApolloStopChannel` | [sound.md](sound.md) | +| Pictures, PIP, pointer | `ApolloLoadPicture`, `ApolloShowPicture`, `ApolloShowPiP1/2`, `ApolloHidePiP1/2`, `ApolloLoadPointer`, `ApolloShowPointer`, `ApolloShowFile` | [picture.md](picture.md) | +| Input | `ApolloMouse`, `ApolloJoypad`, `ApolloKeyboard`, `ApolloKeyboardToUnicode`, `ApolloGetMouseDelta` | [input.md](input.md) | +| Memory and blit | `ApolloCopyBlock`, `ApolloFillLongs`, `ApolloSwapLong`, `ApolloBlit…Loop` and others | [memory-and-blit.md](memory-and-blit.md) | +| System and timing | `ApolloWaitVBL`, `ApolloCPUTick`, `ApolloCPUDelay`, `ApolloTimer`, `ApolloTakeOver`, `ApolloForbid`, `ApolloDisable` | [system-and-timing.md](system-and-timing.md) | +| Debug | `ApolloDebugInit`, `ApolloDebugPutStr`, `AD()`, `Debug_Info()` … | [debug.md](debug.md) | +| Files | `ApolloLoadFile`, `ApolloCacheFile` | this file | + +### Files + +`ApolloLoadFile(struct ApolloFile *file)` reads `file->filename` from byte +`file->offset` to the end into `file->buffer`, which the caller allocates, +and sets `file->size`. `ApolloCacheFile(filename, &cache, &length, offset)` +does the same with plain arguments. Both use libnix `fopen`/`fread`. + +### Return codes + +| Value | Sound (`APOLLO_SOUND_…`) | Picture (`APOLLO_PICTURE_…`) | +|---|---|---| +| `$00` | OK | OK | +| `$01` | NOCHANNEL | W_ERROR (width has no SAGA mode) | +| `$02` | – | H_ERROR (height has no SAGA mode) | +| `$03` | ENDOFFILE | D_ERROR (depth not supported) | +| `$04` | LOADERROR | LOADERR | +| `$05` | NOHEADER | NOHEADER | +| `$06` | – | PLANEERR | +| `$07` | – | COMPRERR | +| `$09` | MEMERROR | MEMERROR | +| `$0A` | OPENERR | OPENERR | +| `$0B` | CLOSEERR | CLOSEERR | +| `$0C` | COMPRERR | – | + +`ApolloLoadFile` returns the sound codes. + +### Header mismatch + +`ApolloCrossDev_Lib.h` declares `ApolloShowPiP()` and `ApolloHidePiP()`, +which don't exist. The functions that exist, `ApolloShowPiP1/2` and +`ApolloHidePiP1/2`, are not declared: declare them yourself until the +header is fixed (see +[apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). + +## Differences from AmigaOS / AROS / 68060 + +- ApolloLib programs the SAGA registers directly, without graphics.library, + audio.device or input.device. Most functions are OS-takeover style: + under a running OS, `ApolloMouse` and `ApolloShowPicture` compete with + Intuition for the pointer and the screen. +- The assembly uses 68080 and AMMX instructions (`load`, `store`, `vperm`, + `dbra.l`, `move16`): it doesn't run on a 68060. + +## Do / Don't + +- DO use ApolloLib for Arne and Isabelle instead of programming the + registers yourself (a best practice, not a forced rule). +- DO check the known bugs before you rely on a function: + [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md). +- DO declare `ApolloShowPiP1/2` and `ApolloHidePiP1/2` yourself. +- DON'T call `ApolloShowPiP()` or `ApolloHidePiP()`: they don't exist. diff --git a/toolchain/apollolib/input.md b/toolchain/apollolib/input.md new file mode 100644 index 0000000..f283821 --- /dev/null +++ b/toolchain/apollolib/input.md @@ -0,0 +1,98 @@ +--- +title: ApolloLib input functions (mouse, USB joypad, keyboard) +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, general] +applies_to: + core: "any" + os: [any] + mode: [os-takeover] +tags: [apollolib, input, mouse, joypad, keyboard, pointer] +related: + - toolchain/apollolib/index.md + - core/saga-registers/input.md + - core/saga-registers/pointer.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s, _Defines.h), commit ce1fcbf (2026-11-04)" +--- + +## Summary + +ApolloLib polls the mouse, the first USB joypad and the keyboard directly +from the hardware, and moves the SAGA pointer with the mouse. Call the +functions once per frame. They bypass input.device, so they belong in +OS-takeover code. + +## Details + +### Mouse: ApolloMouse(ApolloMouseState *m) + +| Source | Register | +|---|---| +| X, Y counters | `$DFF00B`, `$DFF00A` (JOY0DAT) | +| Left button | CIA-A `$BFE001` bit 6 (0 = pressed) | +| Right button | POTINP `$DFF016` bit 10 (0 = pressed) | +| "Middle" | Low byte of the wheel register `$DFF212` (see the known bugs) | + +Each call: + +1. computes the X/Y delta since the last call (with wrap-around of the + 8-bit counters); +2. moves `MouseX_Pointer`/`MouseY_Pointer`, clamped to 1 … + `MouseX_Pointer_Max`/`MouseY_Pointer_Max` (set these first); +3. writes the pointer position + 16 (X) and + 8 (Y) to `$DFF1D0`/`$DFF1D2`; +4. sets `Button_State` from the `APOLLOMOUSE_…` flags. + +| Flag | Value | Set when | +|---|---|---| +| `APOLLOMOUSE_LEFTCLICK` / `RIGHTCLICK` / `MIDDLECLICK` | `$0001` / `$0002` / `$0004` | Button released | +| `APOLLOMOUSE_LEFTDOUBLECLICK` / `RIGHTDOUBLECLICK` / `MIDDLEDOUBLECLICK` | `$0008` / `$0010` / `$0020` | Released again within 15 calls (`APOLLOMOUSE_DOUBLECLICKCOUNTER`) | +| `APOLLOMOUSE_LEFTDOWN` / `RIGHTDOWN` / `MIDDLEDOWN` | `$0040` / `$0080` / `$0100` | Button held | + +`ApolloGetMouseDelta(UWORD *old)` (assembly) is a smaller variant: it +returns the X and Y delta since the value in `*old`, and updates `*old`. + +### USB joypad: ApolloJoypad(ApolloJoypadState *j) + +Reads joypad 0 (`$DFF220`) into `Joypad_Value` and decodes it: + +| Field | Bits | +|---|---| +| `Joypad_LeftX_Delta` | 15 → +1 (right), 14 → −1 (left) | +| `Joypad_LeftY_Delta` | 13 → +1 (down), 12 → −1 (up) | +| `Joypad_RightX_Delta` | 2 → +1, 3 → −1 | +| `Joypad_RightY_Delta` | 1 → +1, 4 → −1 | +| `Joypad_Start`, `Joypad_Back` | 10, 9 | +| `Joypad_TR`, `TL`, `BR`, `BL` | 8, 7, 6, 5 | +| `Joypad_Y`, `X`, `B`, `A` | 4, 3, 2, 1 | +| `Joypad_Connect` | 0 | + +The right stick shares bits 1–4 with A, B, X and Y. Joypads 1–3 +(`$DFF222`–`$DFF226`) need your own read. + +### Keyboard: ApolloKeyboard(ApolloKeyBoardState *k) + +Reads the raw key code from CIA-A `$BFEC01` (`not` and `ror #1`). A new +key below 127 goes to `Current_Key` (and `Previous_Key`); otherwise +`Current_Key` is 127. `ApolloKeyboardToUnicode(code)` maps a raw Amiga key +code to a character. + +## Differences from AmigaOS / AROS / 68060 + +- input.device, Intuition and lowlevel.library are not used. Under a + running OS, Intuition also moves the pointer and reads the keyboard: the + two fight. +- The keyboard read doesn't handshake with the keyboard controller, as the + OS keyboard interrupt does. + +## Do / Don't + +- DO set `MouseX_Pointer_Max` and `MouseY_Pointer_Max` before the first + `ApolloMouse` call. +- DO call these functions once per frame, for example after `ApolloWaitVBL`. +- DON'T use them while Intuition owns the input. +- DON'T rely on the middle-button flags (see the known bugs). diff --git a/toolchain/apollolib/memory-and-blit.md b/toolchain/apollolib/memory-and-blit.md new file mode 100644 index 0000000..4419344 --- /dev/null +++ b/toolchain/apollolib/memory-and-blit.md @@ -0,0 +1,88 @@ +--- +title: ApolloLib memory, endian and blit routines +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, cpu-68080] +applies_to: + core: "any" + os: [any] +tags: [apollolib, memcpy, fill, endian, blit, alpha, ammx, move16] +related: + - toolchain/apollolib/index.md + - core/cpu-68080/measured-instruction-costs.md + - core/best-practices/align-copy-destination.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibASM.s, _Lib.h), commit ce1fcbf (2026-11-04)" +--- + +## Summary + +ApolloLib's assembly file has copy, fill and endian-swap routines for +memory, and copy, fill and alpha-blit routines for pixel rectangles. They +use 68080 instructions (`dbra.l`, `move16`, AMMX `load`/`store`/`vperm`). +Most ignore a remainder: give sizes that are a multiple of the chunk size. + +## Details + +All take their arguments in registers; the prototypes in +`ApolloCrossDev_Lib.h` declare them with `_A0(...)` … `_D7(...)`. + +### Memory + +| Routine | Arguments | Chunk | Remainder | +|---|---|---|---| +| `ApolloCopyBlock(s, d, size)` | a0, a1, d3 = bytes | 16 bytes with `move16`, then bytes | Copied | +| `ApolloCopyBlock32(s, d, size)` | a0, a1, d3 = bytes | 32 bytes (2 × `move16`) | **Ignored** | +| `ApolloCopyLongs(s, d, size)` | a0, a1, d3 = bytes | 4 bytes | **Ignored** | +| `ApolloFillLongs(d, value, size)` | a1, d4, d3 = bytes | 4 bytes | **Ignored** | +| `ApolloFillBlock(d, count, value)` | a0, d3 = bytes, d4 | 32 bytes (4 × AMMX `store`), 32-byte aligned | **Ignored** | + +`move16` needs no alignment on the 68080, but is never faster than +aligned AMMX or quad moves (see +[measured-instruction-costs.md](../../core/cpu-68080/measured-instruction-costs.md)). + +### Endian swap + +| Routine | What it does | +|---|---| +| `ApolloSwapWord(w)`, `ApolloSwapLong(l)`, `ApolloSwapOcta(q)` | Return the value with its bytes reversed (d0) | +| `ApolloEndianSwapWordBuffer(s, length)` | Byte-swaps 16-bit words in place, 8 bytes per AMMX step; buffer ≥ 8 bytes, 2-byte aligned | +| `ApolloEndianSwapLongBuffer(s, length)` | Meant to byte-swap 32-bit longs in place; see the known bugs | +| `ApolloEndianSwap2Loop`, `ApolloEndianSwap8Loop` | Older swap loops, used by `ApolloShowFile` | + +### Pixel rectangles + +| Routine | What it does | +|---|---| +| `ApolloFillBitMap(dst, w, h, depth, dstmod, value)` | Fills a rectangle with a 32-bit value; modulo in pixels | +| `ApolloFillColor(dst, w, h, depth, dstmod, value)` | As above, value given byte-reversed | +| `ApolloCopyPicture(s, d, w, h, smod, dmod)` | Copies a rectangle; sizes and modulos in bytes | +| `ApolloCopyPicture32(...)` | As above, 32-byte aligned | +| `ApolloCopyLoop`, `ApolloCopy32Loop`, `ApolloBlitLoop` | Copy, aligned copy and copy with transparency; pitches in words | +| `ApolloFillLoop(d, w, h, dpitch, colour)` | Fills with an R5G6B5 colour | +| `ApolloBlitAlphaConstLoop(s, d, w, h, sp, dp, alpha)` | Blends with a constant alpha 0–255 (0 = transparent) | +| `ApolloBlitAlphaKeyTransLoop(..., alpha, key)` | Blends with alpha and skips the R5G6B5 key colour | +| `ApolloBlitAlphaHorizontalLineLoop(s, d, w, colour, alpha)` | Blends one line with an R8G8B8 colour | +| `ApolloBlitRed/Green/BlueAlphaBGLoop`, `ApolloBlitBlueAlphaUILoop` | Blend through a separate alpha map, one colour channel at a time | +| `ApolloUncompressLoop`, `ApolloUncompressVectorLoop` | Unpack sprites, with endian and colour swap (`$07C0` green becomes the key `$F81F`) | + +The blit routines work on R5G6B5 pixels (16-bit modes). + +## Differences from AmigaOS / AROS / 68060 + +- `CopyMem`/`CopyMemQuick` and the classic blitter are not used; these are + CPU loops for the 68080. +- AMMX (`load`, `store`, `vperm`) doesn't exist on the 68060: these + routines need a 68080. + +## Do / Don't + +- DO give sizes in multiples of 32 (`ApolloCopyBlock32`, `ApolloFillBlock`) + or 4 (`ApolloCopyLongs`, `ApolloFillLongs`). +- DO align the destination of large copies (see + [align-copy-destination.md](../../core/best-practices/align-copy-destination.md)). +- DON'T use `ApolloEndianSwapLongBuffer` until its known bug is fixed. diff --git a/toolchain/apollolib/picture.md b/toolchain/apollolib/picture.md new file mode 100644 index 0000000..6519b57 --- /dev/null +++ b/toolchain/apollolib/picture.md @@ -0,0 +1,121 @@ +--- +title: ApolloLib picture, PIP and pointer functions (Isabelle) +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, saga-video-isabelle] +applies_to: + core: "any" + os: [any] +tags: [apollolib, isabelle, picture, bmp, dds, pip, pointer, sprite] +related: + - toolchain/apollolib/index.md + - core/saga-registers/chunky-plane.md + - core/saga-registers/pip.md + - core/saga-registers/pointer.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h, _Defines.h), commit ce1fcbf (2026-11-04)" + - "Example SAGAPiP.c, github.com/WDrijver/ApolloCrossDev Projects/SAGAPiP, commit ce1fcbf" + - "Example ApolloPointer.c, github.com/WDrijver/ApolloCrossDev Projects/ApolloPointer, commit ce1fcbf" +--- + +## Summary + +ApolloLib loads BMP and DDS files into a 32-byte aligned buffer +(`ApolloLoadPicture`) and shows them on the SAGA chunky plane by picking +the GFXMODE that matches width, height and depth (`ApolloShowPicture`). +It switches PIP 1 and 2 on and off and loads the 32x32 pointer sprite of +Core 12500 and later. + +## Details + +### struct ApolloPicture + +| Field | Set by | Meaning | +|---|---|---| +| `filename[256]`, `format` | Caller | `APOLLO_BMP_FORMAT` (3), `APOLLO_DDS_FORMAT` (2), or raw (0: the whole file) | +| `endian` | Caller | `true`: swap the byte order of each 16-, 24- or 32-bit pixel after loading | +| `buffer`, `position`, `size` | `ApolloLoadPicture` | Allocated buffer; pixels at `buffer + position` (32-byte aligned) | +| `width`, `height`, `depth` | `ApolloLoadPicture` | From the header | +| `palette[256]`, `palettesize` | `ApolloLoadPicture` | For depth ≤ 8 | +| `modulo` | Caller | Bytes skipped after each row (BPLHMOD) | +| `fullscreen`, `target` | Caller | `target`: 0 SAGA screen, 1 PIP 1, 2 PIP 2, 3 pointer sprite | + +### Functions + +| Function | What it does | +|---|---| +| `ApolloLoadPicture(pic)` | BMP: uncompressed (or BI_BITFIELDS), 1 plane; bottom-up rows are flipped. DDS: the data after the header as it is, without a compression check, so only uncompressed DDS works; depth = pitch / width × 8. Returns an `APOLLO_PICTURE_…` code. | +| `ApolloAllocPicture(pic)` | Allocates an aligned buffer of `width × height × depth / 8` bytes (or `size`) | +| `ApolloFreePicture(pic)` | `FreeVec` of the buffer | +| `ApolloShowPicture(pic)` | Picks the GFXMODE from width × height (see below) and depth 8, 15, 16, 24 or 32, loads the chunky palette for depth ≤ 8, writes GFXMODE (`$DFF1F4`), modulo (`$DFF1E6`) and pointer (`$DFF1EC`) | +| `ApolloShowPiP1(pic)` / `ApolloShowPiP2(pic)` | Writes the PIP's DMA row length (`width × depth / 8`), which switches it on. Set position, pointer, format and modulo first (see [pip.md](../../core/saga-registers/pip.md)). | +| `ApolloHidePiP1()` / `ApolloHidePiP2()` | Writes 0 to the DMA row length: the PIP goes off | +| `ApolloLoadPointer(ptr, pic)` | Converts a 32x32 BMP (8-bit with palette, or 32-bit ARGB) into pointer sprite data: colour index + transparency byte per pixel and a 256-entry palette | +| `ApolloShowPointer(ptr)` | Writes the palette (`$DFF3A8` index, `$DFF3AA` colour) and copies the image to `$DFD000` | +| `ApolloShowFile(filename, &draw, &live, length, offset, gfx_mode, modulo, endianswap)` | Reads a raw frame into `draw`, swaps `draw` and `live`, and shows `live`: double buffering for streamed frames | +| `ApolloShowPattern(buffer, w, h, depth)` | Fills a buffer with a test pattern | + +### Screen modes of ApolloShowPicture + +| Width | Heights | +|---|---| +| 304 | 224 | +| 320 | 200, 240, 256 | +| 480 | 270 | +| 640 | 200, 360, 400, 480, 512 | +| 720 | 405, 576 | +| 800, 848, 960, 1024 | 600, 480, 540, 768 (height not checked) | +| 1280 | 720, 800, 1024 | +| 1440, 1920 | 900, 1080 (height not checked) | + +Another width returns `APOLLO_PICTURE_W_ERROR`, another height of a +checked width `APOLLO_PICTURE_H_ERROR`. **The 320-pixel modes are broken** +in this version (see [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). + +### Pointer + +`ApolloLoadPointer` accepts a 32x32 BMP: + +- 8-bit: the BMP palette becomes the sprite palette; every pixel is + opaque. Colour index 0 is transparent (see + [pointer.md](../../core/saga-registers/pointer.md)). +- 32-bit ARGB: alpha 0 becomes index 0 (transparent), alpha `$FF` opaque, + any other alpha semi-transparent (50 %). Colours are gathered into a + palette of up to 255 entries. + +`ApolloMouse` (see [input.md](input.md)) moves the pointer. + +### Example + +```c +struct ApolloPicture pic = {0}; +strcpy(pic.filename, "title.dds"); +pic.format = APOLLO_DDS_FORMAT; /* 1280x720, 16-bit */ +if (ApolloLoadPicture(&pic) == APOLLO_PICTURE_OK) { + pic.modulo = 0; + ApolloShowPicture(&pic); + /* ... */ + ApolloFreePicture(&pic); /* after switching the mode back */ +} +``` + +## Differences from AmigaOS / AROS / 68060 + +- No graphics.library or cybergraphics: `ApolloShowPicture` writes + GFXMODE and the chunky pointer directly, behind Intuition's back. Save + the old mode and pointer at `$DFE1F4`/`$DFE1EC` and restore them on exit. +- BMP pixels are little-endian. Use `endian` or the swap routines when the + SAGA format needs big-endian pixels. + +## Do / Don't + +- DO set the PIP registers before `ApolloShowPiP1/2`: they only write the + DMA row length. +- DO restore GFXMODE and the chunky pointer before the program exits. +- DON'T free a picture that is still shown. +- DON'T use `ApolloShowPicture` for 320-pixel-wide or 8-bit pictures in + this version (see [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). diff --git a/toolchain/apollolib/sound.md b/toolchain/apollolib/sound.md new file mode 100644 index 0000000..3c89330 --- /dev/null +++ b/toolchain/apollolib/sound.md @@ -0,0 +1,106 @@ +--- +title: ApolloLib sound functions (Arne) +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, saga-audio-arne] +applies_to: + core: "any" + os: [any] +tags: [apollolib, arne, audio, wav, aiff, sound] +related: + - toolchain/apollolib/index.md + - core/saga-registers/arne-channels.md + - core/saga-registers/arne-dma-and-interrupts.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h), commit ce1fcbf (2026-11-04)" +--- + +## Summary + +ApolloLib loads WAV and AIFF files into a 32-byte aligned buffer and plays +them on an Arne channel: `ApolloLoadSound`, then `ApolloPlaySound`. A +second, older API (`ApolloPlay`, `ApolloPlayFile`) takes a channel number +and a buffer instead of a `struct ApolloSound`. Both always play 16-bit +stereo. + +## Details + +### struct ApolloSound + +| Field | Set by | Meaning | +|---|---|---| +| `filename[256]`, `format` | Caller | File and format: `APOLLO_WAV_FORMAT` (4), `APOLLO_AIFF_FORMAT` (1), or raw (`APOLLO_RAW_FORMAT`, 0: the whole file) | +| `buffer`, `position` | `ApolloLoadSound` | Allocated buffer; the data starts at `buffer + position` (32-byte aligned) | +| `size` | `ApolloLoadSound` | Data size in bytes | +| `period` | `ApolloLoadSound` | `3546895 / sample rate` (PAL clock) | +| `stereo`, `datarate`, `bitspersample` | `ApolloLoadSound` (WAV) | Read from the header, but not used for playback | +| `channel` | Caller or `ApolloPlaySound` | Arne channel 0–15 | +| `staticchannel` | Caller | `true`: play on `channel`; `false`: `ApolloPlaySound` picks the first free channel from 1 to 15 | +| `loop`, `fadein`, `volume_left`, `volume_right` | Caller | Playback; volume `$00`–`$FF` per side | +| `fadeout`, `pan` | Caller | Not used by the library | + +### Functions + +| Function | What it does | +|---|---| +| `ApolloLoadSound(sound)` | Parses the WAV or AIFF header, allocates `buffer` (`AllocVec`, `MEMF_ANY`), reads the data. WAV: PCM only. Returns an `APOLLO_SOUND_…` code. | +| `ApolloAllocSound(sound)` | Allocates an aligned buffer of `sound->size` bytes for data you fill yourself | +| `ApolloFreeSound(sound)` | `FreeVec` of the buffer | +| `ApolloPlaySound(sound)` | Picks a channel if `staticchannel` is false, sets pointer, length (`size / 8`), volume 0, CTRL and period, starts DMA (DMACON for 0–3, DMACON2 for 4–15), then sets the volume or fades in | +| `ApolloStopSound(sound)` / `ApolloStartSound(sound)` | Clears or sets the channel's DMA bit | +| `ApolloVolumeSound(sound)` | Writes `volume_left << 8 \| volume_right` to AUDxVOL | +| `ApolloFadeInSound(sound)` / `ApolloFadeOutSound(sound)` | Steps the volume by 1 per VBlank (`ApolloWaitVBL`) to or from the average of left and right; fade-out then stops the channel. Both block the caller. | + +`ApolloPlaySound` writes CTRL `$0005` (16-bit, stereo, looping) when +`loop` is set and `$0007` (16-bit, stereo, one-shot) when not, whatever the +file contains. + +### Channel API (older, from the RHLOS project) + +| Function | What it does | +|---|---| +| `ApolloPlay(channel, vol_l, vol_r, loop, buffer, length, offset)` | Plays `buffer + offset`, length `(length - offset) / 8 - 32` pairs, at period 80 (44.1 kHz). Returns `false` if the channel is busy. | +| `ApolloPlayFile(filename, buffer, offset, channel, vol_l, vol_r, loop)` | Reads the file into `buffer` and plays it on `channel`, stopping the channel first if it runs | +| `ApolloCacheFile(filename, &cache, &length, offset)` | Reads a file into `cache` for `ApolloPlay` | +| `ApolloVolume(channel, vol_l, vol_r)` | Sets the volume | +| `ApolloStartChannel(channel)` / `ApolloStopChannel(channel)` | Starts or stops a channel's DMA | +| `ApolloFadeOut(channel, from, to)` | Steps the volume down per VBlank, then stops the channel | + +### Example + +```c +struct ApolloSound music = {0}; +strcpy(music.filename, "music.wav"); +music.format = APOLLO_WAV_FORMAT; /* 16-bit stereo PCM */ +if (ApolloLoadSound(&music) == APOLLO_SOUND_OK) { + music.volume_left = music.volume_right = 0x80; + music.loop = true; + if (ApolloPlaySound(&music) == APOLLO_SOUND_OK) { + /* ... */ + ApolloStopSound(&music); + } + ApolloFreeSound(&music); +} +``` + +## Differences from AmigaOS / AROS / 68060 + +- No audio.device or AHI: the functions write the Arne registers at + `$DFF400 + n × $10` and DMACON/DMACON2 directly. Don't use them on a + channel that audio.device or AHI also uses. +- Arne plays from any RAM, so `MEMF_ANY` buffers work; Paula needs Chip RAM. + +## Do / Don't + +- DO give `ApolloPlaySound` 16-bit stereo data: it always programs 16-bit + stereo. +- DO set `staticchannel` and `channel` if you need channel 0: the + automatic search starts at 1. +- DO call `ApolloFreeSound` when you are done. +- DON'T expect `stereo`, `bitspersample`, `fadeout` or `pan` to change + playback. +- DON'T use the channel API and audio.device on the same channel. diff --git a/toolchain/apollolib/system-and-timing.md b/toolchain/apollolib/system-and-timing.md new file mode 100644 index 0000000..a00fb62 --- /dev/null +++ b/toolchain/apollolib/system-and-timing.md @@ -0,0 +1,82 @@ +--- +title: ApolloLib system and timing routines (VBlank, CPU cycles, takeover) +type: reference +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, cpu-68080] +applies_to: + core: "any" + os: [any] +tags: [apollolib, vblank, timer, ccc, delay, takeover, forbid, disable] +related: + - toolchain/apollolib/index.md + - core/cpu-68080/performance-counters.md + - os/os-takeover.md + - toolchain/pitfalls/apollolib-known-bugs.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s), commit ce1fcbf (2026-11-04)" +--- + +## Summary + +ApolloLib waits for VBlank, reads the 68080 cycle counter and waits a +number of milliseconds, counts VBlanks in an interrupt server, and has +three takeover helpers. None of the takeover helpers has a counterpart +that undoes it: a program has to restore the system itself. + +## Details + +| Function | What it does | +|---|---| +| `ApolloWaitVBL()` | Polls INTREQR bit 5 (VERTB) until set, then clears it in INTREQ | +| `ApolloCPUTick()` | Returns the clock cycle counter CCC (`movec $809,d0`, see [performance-counters.md](../../core/cpu-68080/performance-counters.md)) | +| `ApolloCPUDelay(ms)` | Busy-waits `ms × 92000` cycles on CCC | +| `ApolloTimer()` | Adds a VBlank interrupt server (`AddIntServer`, `INTB_VERTB`) that increments `GlobalTimer` every frame | +| `ApolloTakeOver()` | Opens intuition.library, `CloseWorkBench()`, sets the task priority to 20 | +| `ApolloForbid()` | Calls exec `Forbid()` | +| `ApolloDisable()` | Calls exec `Disable()` | + +### Limits + +- **`ApolloWaitVBL` clears VERTB** itself. Under a running OS that takes + the event away from the OS's VBlank handling; use it in takeover code. +- **`ApolloCPUDelay` assumes 92 MHz** (92000 cycles per ms, also + `APOLLOCYCLES`). The V4 runs at 99.31 MHz (14 × 7.09379 MHz, see + [core-at-a-glance.md](../../core/cpu-68080/core-at-a-glance.md)), so a + delay is about 7 % short there. +- **No restore functions:** there is no Permit, Enable, `OpenWorkBench` + or priority reset, and no `RemIntServer` for `ApolloTimer`. See + [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md) for what + that means at exit. + +### Restore at exit (your code) + +| After | Call before exit | +|---|---| +| `ApolloForbid()` | `Permit()` | +| `ApolloDisable()` | `Enable()` | +| `ApolloTakeOver()` | `SetTaskPri(FindTask(NULL), old)`, `OpenWorkBench()`, `CloseLibrary()` on the intuition.library base it opened (you don't get it back) | +| `ApolloTimer()` | `RemIntServer(INTB_VERTB, …)`: the server node is internal to ApolloLib, so this isn't possible yet | + +This is what [os-takeover.md](../../os/os-takeover.md) asks of every +takeover program: restore multitasking and the Workbench screen on exit. + +## Differences from AmigaOS / AROS / 68060 + +- `ApolloCPUTick` reads a 68080 control register; there is no such + counter on the 68060. Use timer.device or the CIA timers for portable + timing. +- The takeover helpers wrap single exec and Intuition calls; they don't + save the display, the interrupt enables or the DMA state. + +## Do / Don't + +- DO pair `ApolloForbid` with `Permit()` and `ApolloDisable` with + `Enable()` in your exit code. +- DO reopen the Workbench after `ApolloTakeOver`. +- DO calibrate `ApolloCPUDelay` with the clock multiplier from `$DFF3FC` + if the delay must be exact. +- DON'T call `ApolloTimer` in a program that exits back to the OS (see the + known bugs). diff --git a/toolchain/index.md b/toolchain/index.md index 0b12317..4b3ec9e 100644 --- a/toolchain/index.md +++ b/toolchain/index.md @@ -15,6 +15,7 @@ related: - toolchain/vasm-assembler.md - toolchain/target-os-headers.md - toolchain/project-makefile.md + - toolchain/apollolib/index.md sources: - "github.com/WDrijver/ApolloCrossDev README.md" - "github.com/WDrijver/ApolloCrossDev GCC-6.50-Dev-Linux.sh" @@ -62,6 +63,9 @@ C and C++: `-O2 -m68080 -m68881 -ffast-math -fomit-frame-pointer`. Details in - DevPac includes for assembly, in `/m68k-amigaos/DevPac` - ApolloExplorer (`acp`) to upload files to an Apollo V4 over the network - bgdbserver, a GDB server that runs on the Apollo V4 for remote debugging +- ApolloLib in `Projects/ApolloLib`: sound, picture, PIP, pointer, input, + copy and blit routines and serial debug output for SAGA and the 68080, + see [toolchain/apollolib/](apollolib/index.md) ### Host requirements diff --git a/toolchain/pitfalls/apollolib-known-bugs.md b/toolchain/pitfalls/apollolib-known-bugs.md new file mode 100644 index 0000000..0c76998 --- /dev/null +++ b/toolchain/pitfalls/apollolib-known-bugs.md @@ -0,0 +1,90 @@ +--- +title: ApolloLib known bugs (commit ce1fcbf) +type: pitfall +owner: WDrijver +status: draft +origin: ai-assisted +basis: [source-code] +components: [toolchain, saga-audio-arne, saga-video-isabelle] +applies_to: + core: "any" + os: [any] +tags: [apollolib, bugs, showpicture, timer, takeover, endian, mouse] +related: + - toolchain/apollolib/index.md + - toolchain/apollolib/picture.md + - toolchain/apollolib/sound.md + - toolchain/apollolib/system-and-timing.md +sources: + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib, commit ce1fcbf (2026-11-04), read on 2026-11-04" +--- + +## Symptom + +A program built with ApolloLib shows a wrong screen mode or wrong colours, +crashes some time after it exits, never gets the Workbench back, or plays +a sound wrongly, while the calls look right. + +## Cause + +ApolloLib at commit ce1fcbf has bugs and gaps, found by reading the code +on 2026-11-04. "Confirmed" means the code shows it; "suspected" means the +code suggests it and a test on a V4 is still needed. None has been tested +on hardware. + +### Serious + +| Function | Problem | Status | +|---|---|---| +| `ApolloShowPicture` | `case 320:` has no `break` after its inner switch and falls into `case 480`, which ORs in `$0800`. 320x200 becomes `$0900` (304x224); 320x240 and 320x256 become `$0A00`/`$0B00` (1280x720, 640x360). | Confirmed | +| `ApolloTimer` | The VBlank interrupt server node lives in the program's code and is never removed. After the program exits and is unloaded, the next VBlank runs freed memory. | Confirmed (code); crash expected | +| `ApolloTakeOver`, `ApolloForbid`, `ApolloDisable` | No counterpart restores the Workbench, the task priority, `Permit()` or `Enable()`; the opened intuition.library is never closed | Confirmed | +| `ApolloShowPicture` | For depth ≤ 8 it reorders the bytes of each palette entry that `ApolloLoadPicture` already converted to `$IIRRGGBB`, which gives `$IIGGRRII` | Suspected: 8-bit pictures show wrong colours | + +### Others + +| Function | Problem | Status | +|---|---|---| +| `ApolloCrossDev_Lib.h` | Declares `ApolloShowPiP()`/`ApolloHidePiP()`, which don't exist; the real `ApolloShowPiP1/2`, `ApolloHidePiP1/2` are not declared | Confirmed | +| `ApolloMouse` | `Button_Middle` is the low byte of the wheel register `$DFF212` (the wheel counter), not a button | Suspected | +| `ApolloEndianSwapLongBuffer` | `vperm #$23016745` swaps the words of each long instead of reversing its bytes (`$32107654`); its remainder count (`and #5`) and that of `ApolloEndianSwapWordBuffer` (`and #3`) count bytes, not longs or words | Confirmed (code) | +| `ApolloCPUDelay` | Assumes 92 MHz (the V4 runs at 99.31 MHz); the 32-bit overflow check after `add.l` (`cmp.l #$FFFFFFFF`) can't detect a carry | Confirmed (code) | +| `ApolloPlaySound` | Always programs 16-bit stereo, LEN = size / 8, whatever the file contains; the free-channel search skips channel 0 | Confirmed | +| `ApolloPlayFile` | Applies `offset` twice (in `fseek` and again in pointer and length) | Confirmed | +| `ApolloPlay`, `ApolloPlayFile` | Subtract 32 from the length in sample pairs, without an explanation | Unclear | +| `ApolloLoadFile`, `ApolloLoadSound`, `ApolloLoadPicture` | Some error returns leave the file open (header checks, short reads) | Confirmed | +| `ApolloLoadPicture` | Reads the BMP palette from offset 54, right only for a 40-byte BITMAPINFOHEADER; uses `static` file state (not reentrant) | Confirmed | +| `ApolloShowPattern` | Writes only the first byte of each pixel for depths above 8 | Confirmed | + +## Wrong + +```c +pic.width = 320; pic.height = 200; /* 8-bit, 320x200 */ +ApolloShowPicture(&pic); /* sets GFXMODE $0901, not $0101 */ +ApolloTimer(); /* never removed */ +return 0; /* next VBlank jumps into freed code */ +``` + +## Right + +```c +/* Write the mode yourself for 320-pixel modes until the bug is fixed */ +*(volatile uint16_t *)0xDFF1F4 = APOLLO_SAGA_320_200 | APOLLO_SAGA_8_INDEX; +/* Count VBlanks in your own interrupt server, and remove it before exit */ +``` + +## Detection + +- Read GFXMODE back at `$DFE1F4` after `ApolloShowPicture`: the high byte + must match the picture's size (see + [chunky-plane.md](../../core/saga-registers/chunky-plane.md)). +- Search the program for `ApolloTimer`, `ApolloTakeOver`, `ApolloForbid` + and `ApolloDisable`, and check the exit path for the matching restore. + +## Evidence + +| Claim | Source | Core | Method | +|---|---|---|---| +| All bugs in the tables | (SRC) | any | Code reading of commit ce1fcbf, 2026-11-04 | +| The 14 earlier `-Wall` warnings, 5 of them bugs, are fixed | (SRC) | any | ApolloCrossDev pull request #17, merged 2026-11-04 | +| None of these is tested on V4 hardware | – | – | – | diff --git a/toolchain/project-makefile.md b/toolchain/project-makefile.md index e0a38e6..13d52a6 100644 --- a/toolchain/project-makefile.md +++ b/toolchain/project-makefile.md @@ -50,7 +50,8 @@ project folder must be two levels below the ApolloCrossDev root. | `C_LIBS_SDL`, `C_LIBS_MUI`, `C_LIBS_MATH` | off | Extra libraries, enable by removing `#` | The template only collects `.c` and `.s` files (`C_FILES`, `S_FILES`). It -also links every `.o` file in `Projects/ApolloLib`. +also links every `.o` file in `Projects/ApolloLib` (see +[apollolib/index.md](apollolib/index.md)). ### Targets From 38fef107e8a0a62f03ad695a7afb3dce80b93e52 Mon Sep 17 00:00:00 2001 From: Willem Drijver Date: Wed, 4 Nov 2026 17:30:35 +0300 Subject: [PATCH 2/2] ApolloLib: describe commit 639e195, with the bug fixes of ApolloCrossDev #18 ApolloCrossDev #18 fixed the bugs listed in apollolib-known-bugs.md and added ApolloTimerStop, ApolloRelease, ApolloPermit and ApolloEnable. Describe that version: the pitfall now lists what was fixed and the limits that remain; sound, picture, input, memory and system files follow the fixes, and the restore table names the new functions. Co-Authored-By: Claude Opus 5.5 --- .../prefer-library-calls-for-saga-chips.md | 3 +- toolchain/apollolib/debug.md | 2 +- toolchain/apollolib/index.md | 23 ++-- toolchain/apollolib/input.md | 7 +- toolchain/apollolib/memory-and-blit.md | 7 +- toolchain/apollolib/picture.md | 15 +-- toolchain/apollolib/sound.md | 38 ++++--- toolchain/apollolib/system-and-timing.md | 55 +++++----- toolchain/pitfalls/apollolib-known-bugs.md | 101 ++++++++++-------- 9 files changed, 139 insertions(+), 112 deletions(-) diff --git a/core/best-practices/prefer-library-calls-for-saga-chips.md b/core/best-practices/prefer-library-calls-for-saga-chips.md index 71cbff4..070a5d1 100644 --- a/core/best-practices/prefer-library-calls-for-saga-chips.md +++ b/core/best-practices/prefer-library-calls-for-saga-chips.md @@ -44,7 +44,8 @@ For the SAGA chips, prefer the library calls to direct register access released, ApolloLib is the best choice for library calls to Arne and Isabelle. It is described in [toolchain/apollolib/](../../toolchain/apollolib/index.md); check its - [known bugs](../../toolchain/pitfalls/apollolib-known-bugs.md) first. + [fixed bugs and remaining limits](../../toolchain/pitfalls/apollolib-known-bugs.md) + first. - maggie.library is described in [os/libraries/maggie-library.md](../../os/libraries/maggie-library.md). When you need more than that file covers, the author's repository is the diff --git a/toolchain/apollolib/debug.md b/toolchain/apollolib/debug.md index 0b83fc8..cf9c963 100644 --- a/toolchain/apollolib/debug.md +++ b/toolchain/apollolib/debug.md @@ -14,7 +14,7 @@ related: - toolchain/apollolib/index.md - toolchain/project-makefile.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_Debug.h, _Debug.c, _Defines.h), commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_Debug.h, _Debug.c, _Defines.h), commit 639e195 (2026-11-04)" --- ## Summary diff --git a/toolchain/apollolib/index.md b/toolchain/apollolib/index.md index b891269..698343a 100644 --- a/toolchain/apollolib/index.md +++ b/toolchain/apollolib/index.md @@ -16,7 +16,7 @@ related: - toolchain/project-makefile.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib, commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib, commit 639e195 (2026-11-04)" - "WDrijver (maintainer), library calls for the SAGA chips, 2026-11-04" --- @@ -63,7 +63,7 @@ the macros `_A0(...)` … `_D7(...)` from `_Defines.h`. | Pictures, PIP, pointer | `ApolloLoadPicture`, `ApolloShowPicture`, `ApolloShowPiP1/2`, `ApolloHidePiP1/2`, `ApolloLoadPointer`, `ApolloShowPointer`, `ApolloShowFile` | [picture.md](picture.md) | | Input | `ApolloMouse`, `ApolloJoypad`, `ApolloKeyboard`, `ApolloKeyboardToUnicode`, `ApolloGetMouseDelta` | [input.md](input.md) | | Memory and blit | `ApolloCopyBlock`, `ApolloFillLongs`, `ApolloSwapLong`, `ApolloBlit…Loop` and others | [memory-and-blit.md](memory-and-blit.md) | -| System and timing | `ApolloWaitVBL`, `ApolloCPUTick`, `ApolloCPUDelay`, `ApolloTimer`, `ApolloTakeOver`, `ApolloForbid`, `ApolloDisable` | [system-and-timing.md](system-and-timing.md) | +| System and timing | `ApolloWaitVBL`, `ApolloCPUTick`, `ApolloCPUDelay`, `ApolloTimer`/`ApolloTimerStop`, `ApolloTakeOver`/`ApolloRelease`, `ApolloForbid`/`ApolloPermit`, `ApolloDisable`/`ApolloEnable` | [system-and-timing.md](system-and-timing.md) | | Debug | `ApolloDebugInit`, `ApolloDebugPutStr`, `AD()`, `Debug_Info()` … | [debug.md](debug.md) | | Files | `ApolloLoadFile`, `ApolloCacheFile` | this file | @@ -93,13 +93,13 @@ does the same with plain arguments. Both use libnix `fopen`/`fread`. `ApolloLoadFile` returns the sound codes. -### Header mismatch +### Versions -`ApolloCrossDev_Lib.h` declares `ApolloShowPiP()` and `ApolloHidePiP()`, -which don't exist. The functions that exist, `ApolloShowPiP1/2` and -`ApolloHidePiP1/2`, are not declared: declare them yourself until the -header is fixed (see -[apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). +Many bugs were fixed on 2026-11-04 (commit 639e195): use that version or +later. An older copy has the bugs listed in +[apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md), among them +broken 320-pixel modes and no way to undo `ApolloTakeOver` or +`ApolloTimer`. ## Differences from AmigaOS / AROS / 68060 @@ -114,7 +114,8 @@ header is fixed (see - DO use ApolloLib for Arne and Isabelle instead of programming the registers yourself (a best practice, not a forced rule). -- DO check the known bugs before you rely on a function: +- DO build against ApolloLib 639e195 or later. +- DO check the limits that remain before you rely on a function: [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md). -- DO declare `ApolloShowPiP1/2` and `ApolloHidePiP1/2` yourself. -- DON'T call `ApolloShowPiP()` or `ApolloHidePiP()`: they don't exist. +- DON'T copy an old ApolloLib into a project: compile the one in + `Projects/ApolloLib`. diff --git a/toolchain/apollolib/input.md b/toolchain/apollolib/input.md index f283821..5dcc430 100644 --- a/toolchain/apollolib/input.md +++ b/toolchain/apollolib/input.md @@ -17,7 +17,7 @@ related: - core/saga-registers/pointer.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s, _Defines.h), commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s, _Defines.h), commit 639e195 (2026-11-04)" --- ## Summary @@ -36,7 +36,7 @@ OS-takeover code. | X, Y counters | `$DFF00B`, `$DFF00A` (JOY0DAT) | | Left button | CIA-A `$BFE001` bit 6 (0 = pressed) | | Right button | POTINP `$DFF016` bit 10 (0 = pressed) | -| "Middle" | Low byte of the wheel register `$DFF212` (see the known bugs) | +| Middle button | POTINP `$DFF016` bit 8 (0 = pressed) | Each call: @@ -95,4 +95,5 @@ code to a character. `ApolloMouse` call. - DO call these functions once per frame, for example after `ApolloWaitVBL`. - DON'T use them while Intuition owns the input. -- DON'T rely on the middle-button flags (see the known bugs). +- DON'T rely on the middle-button flags with an ApolloLib older than + 639e195: there they followed the wheel counter. diff --git a/toolchain/apollolib/memory-and-blit.md b/toolchain/apollolib/memory-and-blit.md index 4419344..cd45aee 100644 --- a/toolchain/apollolib/memory-and-blit.md +++ b/toolchain/apollolib/memory-and-blit.md @@ -16,7 +16,7 @@ related: - core/best-practices/align-copy-destination.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibASM.s, _Lib.h), commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibASM.s, _Lib.h), commit 639e195 (2026-11-04)" --- ## Summary @@ -51,7 +51,7 @@ aligned AMMX or quad moves (see |---|---| | `ApolloSwapWord(w)`, `ApolloSwapLong(l)`, `ApolloSwapOcta(q)` | Return the value with its bytes reversed (d0) | | `ApolloEndianSwapWordBuffer(s, length)` | Byte-swaps 16-bit words in place, 8 bytes per AMMX step; buffer ≥ 8 bytes, 2-byte aligned | -| `ApolloEndianSwapLongBuffer(s, length)` | Meant to byte-swap 32-bit longs in place; see the known bugs | +| `ApolloEndianSwapLongBuffer(s, length)` | Byte-swaps 32-bit longs in place, 8 bytes per AMMX step; buffer ≥ 8 bytes, 4-byte aligned. Before 639e195 it swapped words. | | `ApolloEndianSwap2Loop`, `ApolloEndianSwap8Loop` | Older swap loops, used by `ApolloShowFile` | ### Pixel rectangles @@ -85,4 +85,5 @@ The blit routines work on R5G6B5 pixels (16-bit modes). or 4 (`ApolloCopyLongs`, `ApolloFillLongs`). - DO align the destination of large copies (see [align-copy-destination.md](../../core/best-practices/align-copy-destination.md)). -- DON'T use `ApolloEndianSwapLongBuffer` until its known bug is fixed. +- DON'T use `ApolloEndianSwapLongBuffer` from an ApolloLib older than + 639e195: it swapped words, not bytes. diff --git a/toolchain/apollolib/picture.md b/toolchain/apollolib/picture.md index 6519b57..b23e765 100644 --- a/toolchain/apollolib/picture.md +++ b/toolchain/apollolib/picture.md @@ -17,9 +17,9 @@ related: - core/saga-registers/pointer.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h, _Defines.h), commit ce1fcbf (2026-11-04)" - - "Example SAGAPiP.c, github.com/WDrijver/ApolloCrossDev Projects/SAGAPiP, commit ce1fcbf" - - "Example ApolloPointer.c, github.com/WDrijver/ApolloCrossDev Projects/ApolloPointer, commit ce1fcbf" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h, _Defines.h), commit 639e195 (2026-11-04)" + - "Example SAGAPiP.c, github.com/WDrijver/ApolloCrossDev Projects/SAGAPiP, commit 639e195" + - "Example ApolloPointer.c, github.com/WDrijver/ApolloCrossDev Projects/ApolloPointer, commit 639e195" --- ## Summary @@ -73,8 +73,9 @@ Core 12500 and later. | 1440, 1920 | 900, 1080 (height not checked) | Another width returns `APOLLO_PICTURE_W_ERROR`, another height of a -checked width `APOLLO_PICTURE_H_ERROR`. **The 320-pixel modes are broken** -in this version (see [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). +checked width `APOLLO_PICTURE_H_ERROR`. Before ApolloLib 639e195 the +320-pixel modes were broken (see +[apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). ### Pointer @@ -117,5 +118,5 @@ if (ApolloLoadPicture(&pic) == APOLLO_PICTURE_OK) { DMA row length. - DO restore GFXMODE and the chunky pointer before the program exits. - DON'T free a picture that is still shown. -- DON'T use `ApolloShowPicture` for 320-pixel-wide or 8-bit pictures in - this version (see [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md)). +- DON'T use `ApolloShowPicture` for 320-pixel-wide or 8-bit pictures with + an ApolloLib older than 639e195. diff --git a/toolchain/apollolib/sound.md b/toolchain/apollolib/sound.md index 3c89330..07564ce 100644 --- a/toolchain/apollolib/sound.md +++ b/toolchain/apollolib/sound.md @@ -16,16 +16,17 @@ related: - core/saga-registers/arne-dma-and-interrupts.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h), commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _Lib.h), commit 639e195 (2026-11-04)" --- ## Summary ApolloLib loads WAV and AIFF files into a 32-byte aligned buffer and plays -them on an Arne channel: `ApolloLoadSound`, then `ApolloPlaySound`. A +them on an Arne channel: `ApolloLoadSound`, then `ApolloPlaySound`, which +programs 16-bit stereo, 16-bit mono or 8-bit mono to match the file. A second, older API (`ApolloPlay`, `ApolloPlayFile`) takes a channel number -and a buffer instead of a `struct ApolloSound`. Both always play 16-bit -stereo. +and a buffer instead of a `struct ApolloSound`, and always plays 16-bit +stereo at 44.1 kHz. ## Details @@ -37,7 +38,8 @@ stereo. | `buffer`, `position` | `ApolloLoadSound` | Allocated buffer; the data starts at `buffer + position` (32-byte aligned) | | `size` | `ApolloLoadSound` | Data size in bytes | | `period` | `ApolloLoadSound` | `3546895 / sample rate` (PAL clock) | -| `stereo`, `datarate`, `bitspersample` | `ApolloLoadSound` (WAV) | Read from the header, but not used for playback | +| `stereo`, `bitspersample` | `ApolloLoadSound` (WAV, AIFF) or caller | Select the format in `ApolloPlaySound`; `bitspersample` 0 means 16-bit stereo | +| `datarate` | `ApolloLoadSound` (WAV) | Not used for playback | | `channel` | Caller or `ApolloPlaySound` | Arne channel 0–15 | | `staticchannel` | Caller | `true`: play on `channel`; `false`: `ApolloPlaySound` picks the first free channel from 1 to 15 | | `loop`, `fadein`, `volume_left`, `volume_right` | Caller | Playback; volume `$00`–`$FF` per side | @@ -50,21 +52,29 @@ stereo. | `ApolloLoadSound(sound)` | Parses the WAV or AIFF header, allocates `buffer` (`AllocVec`, `MEMF_ANY`), reads the data. WAV: PCM only. Returns an `APOLLO_SOUND_…` code. | | `ApolloAllocSound(sound)` | Allocates an aligned buffer of `sound->size` bytes for data you fill yourself | | `ApolloFreeSound(sound)` | `FreeVec` of the buffer | -| `ApolloPlaySound(sound)` | Picks a channel if `staticchannel` is false, sets pointer, length (`size / 8`), volume 0, CTRL and period, starts DMA (DMACON for 0–3, DMACON2 for 4–15), then sets the volume or fades in | +| `ApolloPlaySound(sound)` | Picks a channel if `staticchannel` is false, sets pointer, length, volume 0, CTRL and period, starts DMA (DMACON for 0–3, DMACON2 for 4–15), then sets the volume or fades in | | `ApolloStopSound(sound)` / `ApolloStartSound(sound)` | Clears or sets the channel's DMA bit | | `ApolloVolumeSound(sound)` | Writes `volume_left << 8 \| volume_right` to AUDxVOL | | `ApolloFadeInSound(sound)` / `ApolloFadeOutSound(sound)` | Steps the volume by 1 per VBlank (`ApolloWaitVBL`) to or from the average of left and right; fade-out then stops the channel. Both block the caller. | -`ApolloPlaySound` writes CTRL `$0005` (16-bit, stereo, looping) when -`loop` is set and `$0007` (16-bit, stereo, one-shot) when not, whatever the -file contains. +`ApolloPlaySound` picks CTRL and the LEN unit from the format, and adds +the one-shot bit (`$0002`) when `loop` is not set: + +| Format | CTRL | LEN = size / | +|---|---|---| +| 16-bit stereo, or unknown (`bitspersample` 0) | `$0005` | 8 | +| 16-bit mono | `$0001` | 4 | +| 8-bit mono | `$0000` | 2 | + +8-bit stereo can't play: Arne's stereo mode is 16-bit. 8-bit WAV samples +are unsigned and are not converted to the signed format Arne expects. ### Channel API (older, from the RHLOS project) | Function | What it does | |---|---| | `ApolloPlay(channel, vol_l, vol_r, loop, buffer, length, offset)` | Plays `buffer + offset`, length `(length - offset) / 8 - 32` pairs, at period 80 (44.1 kHz). Returns `false` if the channel is busy. | -| `ApolloPlayFile(filename, buffer, offset, channel, vol_l, vol_r, loop)` | Reads the file into `buffer` and plays it on `channel`, stopping the channel first if it runs | +| `ApolloPlayFile(filename, buffer, offset, channel, vol_l, vol_r, loop)` | Reads the file from `offset` into `buffer` and plays it on `channel`, stopping the channel first if it runs | | `ApolloCacheFile(filename, &cache, &length, offset)` | Reads a file into `cache` for `ApolloPlay` | | `ApolloVolume(channel, vol_l, vol_r)` | Sets the volume | | `ApolloStartChannel(channel)` / `ApolloStopChannel(channel)` | Starts or stops a channel's DMA | @@ -96,11 +106,11 @@ if (ApolloLoadSound(&music) == APOLLO_SOUND_OK) { ## Do / Don't -- DO give `ApolloPlaySound` 16-bit stereo data: it always programs 16-bit - stereo. +- DO set `stereo` and `bitspersample` yourself for raw data that isn't + 16-bit stereo. - DO set `staticchannel` and `channel` if you need channel 0: the automatic search starts at 1. - DO call `ApolloFreeSound` when you are done. -- DON'T expect `stereo`, `bitspersample`, `fadeout` or `pan` to change - playback. +- DON'T play 8-bit WAV files without converting them to signed samples. +- DON'T expect `fadeout` or `pan` to change playback. - DON'T use the channel API and audio.device on the same channel. diff --git a/toolchain/apollolib/system-and-timing.md b/toolchain/apollolib/system-and-timing.md index a00fb62..7d1406f 100644 --- a/toolchain/apollolib/system-and-timing.md +++ b/toolchain/apollolib/system-and-timing.md @@ -16,15 +16,15 @@ related: - os/os-takeover.md - toolchain/pitfalls/apollolib-known-bugs.md sources: - - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s), commit ce1fcbf (2026-11-04)" + - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib (ApolloCrossDev_LibC.c, _LibASM.s), commit 639e195 (2026-11-04)" --- ## Summary ApolloLib waits for VBlank, reads the 68080 cycle counter and waits a number of milliseconds, counts VBlanks in an interrupt server, and has -three takeover helpers. None of the takeover helpers has a counterpart -that undoes it: a program has to restore the system itself. +three takeover helpers. From commit 639e195 every one of them has a +counterpart that undoes it; call it before the program exits. ## Details @@ -32,33 +32,36 @@ that undoes it: a program has to restore the system itself. |---|---| | `ApolloWaitVBL()` | Polls INTREQR bit 5 (VERTB) until set, then clears it in INTREQ | | `ApolloCPUTick()` | Returns the clock cycle counter CCC (`movec $809,d0`, see [performance-counters.md](../../core/cpu-68080/performance-counters.md)) | -| `ApolloCPUDelay(ms)` | Busy-waits `ms × 92000` cycles on CCC | +| `ApolloCPUDelay(ms)` | Busy-waits `ms × multiplier × 7094` cycles on CCC; the multiplier is the low byte of `$DFF3FC` (14 if 0) | | `ApolloTimer()` | Adds a VBlank interrupt server (`AddIntServer`, `INTB_VERTB`) that increments `GlobalTimer` every frame | -| `ApolloTakeOver()` | Opens intuition.library, `CloseWorkBench()`, sets the task priority to 20 | -| `ApolloForbid()` | Calls exec `Forbid()` | -| `ApolloDisable()` | Calls exec `Disable()` | +| `ApolloTimerStop()` | Removes that server (`RemIntServer`) | +| `ApolloTakeOver()` | Opens intuition.library, `CloseWorkBench()`, sets the task priority to 20, and remembers the library base and the old priority | +| `ApolloRelease()` | Restores the old priority, `OpenWorkBench()`, closes intuition.library | +| `ApolloForbid()` / `ApolloPermit()` | Call exec `Forbid()` / `Permit()` | +| `ApolloDisable()` / `ApolloEnable()` | Call exec `Disable()` / `Enable()` | + +`ApolloTimerStop`, `ApolloRelease`, `ApolloPermit` and `ApolloEnable` +exist from ApolloLib commit 639e195. ### Limits - **`ApolloWaitVBL` clears VERTB** itself. Under a running OS that takes the event away from the OS's VBlank handling; use it in takeover code. -- **`ApolloCPUDelay` assumes 92 MHz** (92000 cycles per ms, also - `APOLLOCYCLES`). The V4 runs at 99.31 MHz (14 × 7.09379 MHz, see - [core-at-a-glance.md](../../core/cpu-68080/core-at-a-glance.md)), so a - delay is about 7 % short there. -- **No restore functions:** there is no Permit, Enable, `OpenWorkBench` - or priority reset, and no `RemIntServer` for `ApolloTimer`. See - [apollolib-known-bugs.md](../pitfalls/apollolib-known-bugs.md) for what - that means at exit. +- **`ApolloCPUDelay`** counts `multiplier × 7.094` MHz cycles per ms, so + it follows the clock the board reports at `$DFF3FC` (see + [system.md](../../core/saga-registers/system.md)). Before 639e195 it + assumed 92 MHz, about 7 % short on a 99.31 MHz V4. +- **`ApolloRelease`** restores the priority that `ApolloTakeOver` saved. + Call it only after `ApolloTakeOver`; otherwise it sets priority 0. -### Restore at exit (your code) +### Restore at exit | After | Call before exit | |---|---| -| `ApolloForbid()` | `Permit()` | -| `ApolloDisable()` | `Enable()` | -| `ApolloTakeOver()` | `SetTaskPri(FindTask(NULL), old)`, `OpenWorkBench()`, `CloseLibrary()` on the intuition.library base it opened (you don't get it back) | -| `ApolloTimer()` | `RemIntServer(INTB_VERTB, …)`: the server node is internal to ApolloLib, so this isn't possible yet | +| `ApolloTimer()` | `ApolloTimerStop()`: the server node lives in the program, so a server left behind runs freed memory | +| `ApolloTakeOver()` | `ApolloRelease()` | +| `ApolloForbid()` | `ApolloPermit()` | +| `ApolloDisable()` | `ApolloEnable()` | This is what [os-takeover.md](../../os/os-takeover.md) asks of every takeover program: restore multitasking and the Workbench screen on exit. @@ -73,10 +76,8 @@ takeover program: restore multitasking and the Workbench screen on exit. ## Do / Don't -- DO pair `ApolloForbid` with `Permit()` and `ApolloDisable` with - `Enable()` in your exit code. -- DO reopen the Workbench after `ApolloTakeOver`. -- DO calibrate `ApolloCPUDelay` with the clock multiplier from `$DFF3FC` - if the delay must be exact. -- DON'T call `ApolloTimer` in a program that exits back to the OS (see the - known bugs). +- DO call `ApolloTimerStop`, `ApolloRelease`, `ApolloPermit` and + `ApolloEnable` in your exit code, in the reverse order of their + counterparts. +- DON'T use `ApolloTimer` with an ApolloLib older than 639e195 in a + program that exits back to the OS: there is no way to remove the server. diff --git a/toolchain/pitfalls/apollolib-known-bugs.md b/toolchain/pitfalls/apollolib-known-bugs.md index 0c76998..c1f8b75 100644 --- a/toolchain/pitfalls/apollolib-known-bugs.md +++ b/toolchain/pitfalls/apollolib-known-bugs.md @@ -1,5 +1,5 @@ --- -title: ApolloLib known bugs (commit ce1fcbf) +title: ApolloLib bugs fixed in 2026-11 and the limits that remain type: pitfall owner: WDrijver status: draft @@ -9,7 +9,7 @@ components: [toolchain, saga-audio-arne, saga-video-isabelle] applies_to: core: "any" os: [any] -tags: [apollolib, bugs, showpicture, timer, takeover, endian, mouse] +tags: [apollolib, bugs, showpicture, timer, takeover, endian, mouse, sound] related: - toolchain/apollolib/index.md - toolchain/apollolib/picture.md @@ -17,74 +17,85 @@ related: - toolchain/apollolib/system-and-timing.md sources: - "ApolloLib, github.com/WDrijver/ApolloCrossDev Projects/ApolloLib, commit ce1fcbf (2026-11-04), read on 2026-11-04" + - "ApolloCrossDev pull requests #17 and #18 (merged 2026-11-04), fixed in commit 639e195" --- ## Symptom -A program built with ApolloLib shows a wrong screen mode or wrong colours, -crashes some time after it exits, never gets the Workbench back, or plays -a sound wrongly, while the calls look right. +A program shows a 320-pixel picture in the wrong mode or an 8-bit picture +in wrong colours, crashes some time after it exits, never gets the +Workbench back, or plays a mono sound too fast. It was built against +ApolloLib from before 2026-11-04. ## Cause -ApolloLib at commit ce1fcbf has bugs and gaps, found by reading the code -on 2026-11-04. "Confirmed" means the code shows it; "suspected" means the -code suggests it and a test on a V4 is still needed. None has been tested -on hardware. - -### Serious - -| Function | Problem | Status | -|---|---|---| -| `ApolloShowPicture` | `case 320:` has no `break` after its inner switch and falls into `case 480`, which ORs in `$0800`. 320x200 becomes `$0900` (304x224); 320x240 and 320x256 become `$0A00`/`$0B00` (1280x720, 640x360). | Confirmed | -| `ApolloTimer` | The VBlank interrupt server node lives in the program's code and is never removed. After the program exits and is unloaded, the next VBlank runs freed memory. | Confirmed (code); crash expected | -| `ApolloTakeOver`, `ApolloForbid`, `ApolloDisable` | No counterpart restores the Workbench, the task priority, `Permit()` or `Enable()`; the opened intuition.library is never closed | Confirmed | -| `ApolloShowPicture` | For depth ≤ 8 it reorders the bytes of each palette entry that `ApolloLoadPicture` already converted to `$IIRRGGBB`, which gives `$IIGGRRII` | Suspected: 8-bit pictures show wrong colours | - -### Others - -| Function | Problem | Status | -|---|---|---| -| `ApolloCrossDev_Lib.h` | Declares `ApolloShowPiP()`/`ApolloHidePiP()`, which don't exist; the real `ApolloShowPiP1/2`, `ApolloHidePiP1/2` are not declared | Confirmed | -| `ApolloMouse` | `Button_Middle` is the low byte of the wheel register `$DFF212` (the wheel counter), not a button | Suspected | -| `ApolloEndianSwapLongBuffer` | `vperm #$23016745` swaps the words of each long instead of reversing its bytes (`$32107654`); its remainder count (`and #5`) and that of `ApolloEndianSwapWordBuffer` (`and #3`) count bytes, not longs or words | Confirmed (code) | -| `ApolloCPUDelay` | Assumes 92 MHz (the V4 runs at 99.31 MHz); the 32-bit overflow check after `add.l` (`cmp.l #$FFFFFFFF`) can't detect a carry | Confirmed (code) | -| `ApolloPlaySound` | Always programs 16-bit stereo, LEN = size / 8, whatever the file contains; the free-channel search skips channel 0 | Confirmed | -| `ApolloPlayFile` | Applies `offset` twice (in `fseek` and again in pointer and length) | Confirmed | -| `ApolloPlay`, `ApolloPlayFile` | Subtract 32 from the length in sample pairs, without an explanation | Unclear | -| `ApolloLoadFile`, `ApolloLoadSound`, `ApolloLoadPicture` | Some error returns leave the file open (header checks, short reads) | Confirmed | -| `ApolloLoadPicture` | Reads the BMP palette from offset 54, right only for a 40-byte BITMAPINFOHEADER; uses `static` file state (not reentrant) | Confirmed | -| `ApolloShowPattern` | Writes only the first byte of each pixel for depths above 8 | Confirmed | +ApolloLib up to commit ce1fcbf had these bugs. They were found by reading +the code while documenting the library, and fixed in commit 639e195 +(ApolloCrossDev pull request #18). None of the fixes is tested on V4 +hardware yet. + +### Fixed in 639e195 + +| Function | Bug before 639e195 | +|---|---| +| `ApolloShowPicture` | `case 320` fell through into `case 480`: 320x200 became 304x224, 320x240/256 became 1280x720/640x360 | +| `ApolloShowPicture` | The 8-bit palette was reordered a second time: wrong colours | +| `ApolloTimer` | The VBlank server was never removed; its node lives in the program, so the system crashed after the program exited. Now: `ApolloTimerStop()` | +| `ApolloTakeOver`, `ApolloForbid`, `ApolloDisable` | No way back. Now: `ApolloRelease()`, `ApolloPermit()`, `ApolloEnable()` | +| `ApolloCPUDelay` | Fixed 92 MHz; the overflow check could not work | +| `ApolloEndianSwapLongBuffer` | Swapped words instead of bytes; both buffer swaps counted their remainder wrongly | +| `ApolloPlaySound` | Always 16-bit stereo, whatever the file contained | +| `ApolloPlayFile` | Applied its offset twice | +| `ApolloMouse` | The "middle button" was the wheel counter | +| `ApolloLoadFile`/`Sound`/`Picture` | Left the file open on errors; BMP palette read from a fixed offset | +| `ApolloShowPattern` | Wrote only the first byte of each pixel | +| `ApolloCrossDev_Lib.h` | Declared `ApolloShowPiP`/`ApolloHidePiP`, which don't exist, and not `ApolloShowPiP1/2`/`ApolloHidePiP1/2` | +| Five functions (#17) | Wrong DMACON2R bit for channels 4–15, a write through an uninitialized pointer, missing return values, an always-on keyboard print | + +### Limits that remain + +| Function | Limit | +|---|---| +| `ApolloPlaySound` | 8-bit stereo can't play (Arne's stereo mode is 16-bit); 8-bit WAV samples are unsigned and are not converted to the signed format Arne expects | +| `ApolloPlaySound` | A sound without format information (`bitspersample` 0, for example RAW) plays as 16-bit stereo | +| `ApolloPlaySound` | The free-channel search starts at channel 1 | +| `ApolloPlay`, `ApolloPlayFile` | Subtract 32 sample pairs from the length, without an explanation | +| `ApolloLoadPicture` | Keeps its file state in `static` variables: not reentrant | +| `ApolloRelease` | Restores the priority saved by `ApolloTakeOver`; called without it, it sets priority 0 | ## Wrong ```c -pic.width = 320; pic.height = 200; /* 8-bit, 320x200 */ -ApolloShowPicture(&pic); /* sets GFXMODE $0901, not $0101 */ -ApolloTimer(); /* never removed */ -return 0; /* next VBlank jumps into freed code */ +ApolloTakeOver(); +ApolloTimer(); +/* ... */ +return 0; /* Workbench stays closed; next VBlank jumps into freed code */ ``` ## Right ```c -/* Write the mode yourself for 320-pixel modes until the bug is fixed */ -*(volatile uint16_t *)0xDFF1F4 = APOLLO_SAGA_320_200 | APOLLO_SAGA_8_INDEX; -/* Count VBlanks in your own interrupt server, and remove it before exit */ +ApolloTakeOver(); +ApolloTimer(); +/* ... */ +ApolloTimerStop(); /* ApolloLib 639e195 or later */ +ApolloRelease(); +return 0; ``` ## Detection +- Check which ApolloLib a project builds against: `ApolloRelease` and + `ApolloTimerStop` exist only from 639e195. A build that fails on them + uses an older copy. - Read GFXMODE back at `$DFE1F4` after `ApolloShowPicture`: the high byte must match the picture's size (see [chunky-plane.md](../../core/saga-registers/chunky-plane.md)). -- Search the program for `ApolloTimer`, `ApolloTakeOver`, `ApolloForbid` - and `ApolloDisable`, and check the exit path for the matching restore. ## Evidence | Claim | Source | Core | Method | |---|---|---|---| -| All bugs in the tables | (SRC) | any | Code reading of commit ce1fcbf, 2026-11-04 | -| The 14 earlier `-Wall` warnings, 5 of them bugs, are fixed | (SRC) | any | ApolloCrossDev pull request #17, merged 2026-11-04 | -| None of these is tested on V4 hardware | – | – | – | +| The bugs before 639e195 | (SRC) | any | Code reading of commit ce1fcbf, 2026-11-04 | +| Fixed in 639e195; builds without warnings | (SRC) | any | ApolloCrossDev #18: ApolloPointer, SAGAPiP and a link test build | +| None of the fixes is tested on V4 hardware | – | – | – |