// 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;
}
