128 lines
4.6 KiB
C
128 lines
4.6 KiB
C
// iigsStart -- a GS/OS boot launcher for the headless IIgs harnesses.
|
|
//
|
|
// Installed as /System/Start on a copy of the GS/OS system disk, this runs
|
|
// at boot INSTEAD of the Finder and hands off to one JoeyLib example. The
|
|
// point is that the Finder cannot be driven from a script: measured under
|
|
// gssquared, injected keystrokes reach the $C000 latch but never reach the
|
|
// Finder -- Open-Apple-A, Tab, arrows and bare letters all left the desktop
|
|
// untouched. (The one redraw seen after the first key of a run is the
|
|
// Finder's own settling: byte-identical for three different keys.) Reaching
|
|
// the desktop also costs ~61s of boot that a probe must sit through; this
|
|
// route has the example on screen in ~52s.
|
|
//
|
|
// Two GS/OS calls, in this order:
|
|
// SetPrefixGS -- point prefix 0 at the example's DIRECTORY. GS/OS does
|
|
// NOT do this for a QuitGS launch; prefix 0 was measured still pointing
|
|
// at the boot volume. That is not cosmetic: JoeyLib opens its NTP
|
|
// replayer as bare "ntpplayer", and when that read fails jlpAudioInit
|
|
// returns before ntpArm(), so the sound interrupt vector stays at its
|
|
// ROM default ($E1002C = 5C xxxx FF) and the example is SILENT. The
|
|
// Finder sets prefix 0 itself, which is why this only bites here.
|
|
// QuitGS with pCount = 2 and a pathname -- "quit, and launch this".
|
|
//
|
|
// Build: clang-build.sh -DJOEY_START_PATH='"/JOEYLIB/STAXI"' \
|
|
// -o START iigsStart.c iigsStartQuit.s
|
|
|
|
#include <stdint.h>
|
|
|
|
// The program to launch. Overridden per-disk from the build.
|
|
#ifndef JOEY_START_PATH
|
|
#define JOEY_START_PATH "/JOEYLIB/STAXI"
|
|
#endif
|
|
|
|
// The harness's marker, in the $E1:9DC8-$9DFD window that survives a
|
|
// present (SCB proper ends at $9DC7).
|
|
#define QUIT_MAILBOX ((volatile uint8_t *)0xE19DC8L)
|
|
|
|
// A GS/OS class-1 input string is a length WORD followed by the characters.
|
|
#define GSOS_STRING_HEADER 2u
|
|
// The parameter blocks: SetPrefixGS {pCount, prefixNum, prefix} and
|
|
// QuitGS {pCount, pathname, flags}.
|
|
#define PREFIX_PARM_BYTES 8u
|
|
#define PREFIX_PARM_PATH 4u
|
|
#define QUIT_PARM_BYTES 8u
|
|
#define QUIT_PARM_PATH 2u
|
|
#define GSOS_PCOUNT_2 2u
|
|
#define PREFIX_CURRENT 0u // prefix 0 = the current directory
|
|
// Room for the whole path, which is the longest either string gets.
|
|
#define PATH_STRING_BYTES (GSOS_STRING_HEADER + sizeof(JOEY_START_PATH))
|
|
|
|
// Markers for the harness.
|
|
#define QUIT_REACHED 0xAAu
|
|
#define QUIT_RETURNED 0xEEu
|
|
|
|
|
|
static void gsosString(uint8_t *dst, const char *src, uint16_t len);
|
|
void iigsStartQuit(uint8_t *parm);
|
|
void iigsStartSetPrefix(uint8_t *parm);
|
|
static void putLong(uint8_t *dst, uint32_t v);
|
|
|
|
|
|
// In the launcher's own data, where GS/OS reads them during each call.
|
|
static uint8_t gPrefixParm[PREFIX_PARM_BYTES];
|
|
static uint8_t gPrefixPath[PATH_STRING_BYTES];
|
|
static uint8_t gQuitParm[QUIT_PARM_BYTES];
|
|
static uint8_t gQuitPath[PATH_STRING_BYTES];
|
|
|
|
|
|
// Write a GS/OS input string: length word, then the characters.
|
|
static void gsosString(uint8_t *dst, const char *src, uint16_t len) {
|
|
uint16_t i;
|
|
|
|
dst[0] = (uint8_t)(len & 0xFFu);
|
|
dst[1] = (uint8_t)((len >> 8) & 0xFFu);
|
|
for (i = 0; i < len; i++) {
|
|
dst[GSOS_STRING_HEADER + i] = (uint8_t)src[i];
|
|
}
|
|
}
|
|
|
|
|
|
static void putLong(uint8_t *dst, uint32_t v) {
|
|
dst[0] = (uint8_t)(v & 0xFFu);
|
|
dst[1] = (uint8_t)((v >> 8) & 0xFFu);
|
|
dst[2] = (uint8_t)((v >> 16) & 0xFFu);
|
|
dst[3] = (uint8_t)((v >> 24) & 0xFFu);
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
const char *path = JOEY_START_PATH;
|
|
uint16_t len = 0;
|
|
uint16_t dir = 0;
|
|
uint16_t i;
|
|
|
|
while (path[len] != '\0') {
|
|
len++;
|
|
}
|
|
// Directory part, including the trailing '/': everything up to the last
|
|
// separator. "/JOEYLIB/STAXI" -> "/JOEYLIB/".
|
|
for (i = 0; i < len; i++) {
|
|
if (path[i] == '/') {
|
|
dir = (uint16_t)(i + 1u);
|
|
}
|
|
}
|
|
|
|
gsosString(gPrefixPath, path, dir);
|
|
gPrefixParm[0] = GSOS_PCOUNT_2;
|
|
gPrefixParm[1] = 0u;
|
|
gPrefixParm[2] = PREFIX_CURRENT;
|
|
gPrefixParm[3] = 0u;
|
|
putLong(gPrefixParm + PREFIX_PARM_PATH, (uint32_t)gPrefixPath);
|
|
iigsStartSetPrefix(gPrefixParm);
|
|
|
|
gsosString(gQuitPath, path, len);
|
|
gQuitParm[0] = GSOS_PCOUNT_2;
|
|
gQuitParm[1] = 0u;
|
|
putLong(gQuitParm + QUIT_PARM_PATH, (uint32_t)gQuitPath);
|
|
gQuitParm[6] = 0u; // flags
|
|
gQuitParm[7] = 0u;
|
|
|
|
*QUIT_MAILBOX = QUIT_REACHED;
|
|
iigsStartQuit(gQuitParm);
|
|
*QUIT_MAILBOX = QUIT_RETURNED;
|
|
|
|
// QuitGS only returns on failure. Nothing useful is left to do, so
|
|
// spin: the harness reads the mailbox to see what happened.
|
|
for (;;) {
|
|
}
|
|
}
|