// qdProbe.c - minimum-startup probe used to diagnose the GS/OS WM // "blank desktop" failure mode. Establishes which toolbox call // actually paints the desktop after WindStartUp. // // Result (2026-05-16): WindStartUp does NOT paint. Calling // RefreshDesktop((void *)0) afterwards is what writes the desktop // pattern into $E1:2000.. Verified by ZP probe markers ($80..$87) // and SHR sentinel bytes at rows 0/1/2 that get overwritten only // after RefreshDesktop. See [[orca-window-render-broken]] + // [[loader-creloc-threshold]] memories. // // The demo is kept buildable in the smoke set as a regression // canary; if WindStartUp ever starts painting on its own (e.g. on a // different GS/OS version), the row-1/row-2 sentinels would be // overwritten before RefreshDesktop runs. // // Probe markers (read via `scripts/probeQdStartup.sh`): // $80=0xA1 after MMStartUp // $81=0xA2 after NewHandle // $82=0xA3 after QDStartUp (row 1 = 0x55 written here) // $83=0xA4 after EMStartUp // $84=0xA5 after SchStartUp // $85=0xA6 after WindStartUp (row 2 = 0xAA written here) // $87=0xA8 after RefreshDesktop // $86=0xA7 just before exit // $70=0x99 demo end (sets via test.sh) #include "iigs/toolbox.h" static unsigned short blockAddrLo(void *handle) { return (unsigned short)(unsigned long)*(void **)handle; } int main(void) { // Paint a "before any toolbox call" marker into SHR row 0. { volatile unsigned char *shr = (volatile unsigned char *)0xE12000UL; for (unsigned short i = 0; i < 160; i++) { shr[i] = 0xFF; } } *(volatile unsigned char *)0x80 = 0xA1; unsigned short userId = MMStartUp(); // QD needs $200 bytes (own DP + cursor mgr at +$100), EM at +$200. // masterSCB = $90 (640 mode, color burst OFF) avoids the NTSC chroma // simulator turning the WM's dithered desktop pattern into red/green // noise. See runtime/src/desktop.c for the full layout. void *dpH = NewHandle(0x400UL, userId, 0xC015, (void *)0); unsigned short dp = blockAddrLo(dpH); *(volatile unsigned char *)0x81 = 0xA2; QDStartUp(dp, 0x90, 640, userId); *(volatile unsigned char *)0x82 = 0xA3; // Match runtime/src/desktop.c's palette setup so the WM's dithered // desktop fill renders as a clean B/W stipple instead of chroma. for (unsigned short p = 0; p < 16; p++) { volatile unsigned short *pal = (volatile unsigned short *)(0xE19E00UL + (unsigned long)p * 32UL); for (unsigned short k = 0; k < 16; k++) { pal[k] = (k & 1) ? 0x0FFF : 0x0000; } } // SHR row 1 marker: 'After QDStartUp' { volatile unsigned char *shr = (volatile unsigned char *)(0xE12000UL + 160); for (unsigned short i = 0; i < 160; i++) shr[i] = 0x55; } EMStartUp((unsigned short)(dp + 0x200), 20, 0, 0, 639, 199, userId); *(volatile unsigned char *)0x83 = 0xA4; SchStartUp(); *(volatile unsigned char *)0x84 = 0xA5; WindStartUp(userId); *(volatile unsigned char *)0x85 = 0xA6; // SHR row 2 marker: 'After WindStartUp' { volatile unsigned char *shr = (volatile unsigned char *)(0xE12000UL + 320); for (unsigned short i = 0; i < 160; i++) shr[i] = 0xAA; } // Try explicit desktop refresh. RefreshDesktop((void *)0); *(volatile unsigned char *)0x87 = 0xA8; // Spin to let the WM emit any deferred paint AND give snapshot // tools time to capture the post-paint state. for (volatile unsigned long s = 0; s < 300000UL; s++) { } *(volatile unsigned char *)0x86 = 0xA7; *(volatile unsigned char *)0x70 = 0x99; return 0; }