// 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 // The program to launch. Overridden per-disk from the build. #ifndef JOEY_START_PATH #define JOEY_START_PATH "/JOEYLIB/STAXI" #endif // Fixed scratch in bank $E1: the $E1:9DC8-$9DFD window that survives a // present (SCB proper ends at $9DC7). The asm stubs hard-code the two // parameter-block addresses, so these must agree with iigsStartQuit.s. // 9DC8 mailbox 9DCC QuitGS parm (8) // 9DD4 pathname string 9DE4 SetPrefix parm (8) // 9DEC prefix string #define QUIT_MAILBOX ((volatile uint8_t *)0xE19DC8L) #define QUIT_PARM ((volatile uint8_t *)0xE19DCCL) #define QUIT_PATH ((volatile uint8_t *)0xE19DD4L) #define PREFIX_PARM ((volatile uint8_t *)0xE19DE4L) #define PREFIX_PATH ((volatile uint8_t *)0xE19DECL) #define QUIT_PATH_ADDR 0x00E19DD4UL #define PREFIX_PATH_ADDR 0x00E19DECUL // A GS/OS class-1 input string is a length WORD followed by the characters. #define GSOS_STRING_HEADER 2u // Markers for the harness. #define QUIT_REACHED 0xAAu #define QUIT_RETURNED 0xEEu void iigsStartQuit(void); void iigsStartSetPrefix(void); // Write a GS/OS input string (length word + characters) and return its length. static uint16_t gsosString(volatile 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]; } return len; } static void putLong(volatile 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(PREFIX_PATH, path, dir); PREFIX_PARM[0] = 2u; // pCount PREFIX_PARM[1] = 0u; PREFIX_PARM[2] = 0u; // prefixNum 0 = current directory PREFIX_PARM[3] = 0u; putLong(PREFIX_PARM + 4, PREFIX_PATH_ADDR); iigsStartSetPrefix(); gsosString(QUIT_PATH, path, len); QUIT_PARM[0] = 2u; // pCount QUIT_PARM[1] = 0u; putLong(QUIT_PARM + 2, QUIT_PATH_ADDR); // pathname QUIT_PARM[6] = 0u; // flags QUIT_PARM[7] = 0u; *QUIT_MAILBOX = QUIT_REACHED; iigsStartQuit(); *QUIT_MAILBOX = QUIT_RETURNED; // QuitGS only returns on failure. Nothing useful is left to do and the // llvm-mos post-main exit path is broken on GS/OS, so spin rather than // crash: the harness reads the mailbox to see what happened. for (;;) { } }