// orcaFrame.c - ORCA-style desktop application.
//
// Opens a Window Manager window via startdesk()'s full toolset chain,
// runs an event loop via TaskMaster until the user clicks the close
// box, presses Q, or the watchdog fires.
//
// Modeled after ORCA-C's Frame.cc / Reversi.cc samples.  Exercises
// our LLVM/Clang toolchain + the new bank-byte relocation
// end-to-end against the real GS/OS 6.0.2 / 6.0.4 Window Manager.
//
// **Status (2026-05-16):** structurally green.  NewWindow with
// `fTitle | fVis | fMove | fClose` returns a valid WindowPtr on both
// 6.0.2 (sys602.po) and 6.0.4 (tools/gsos/6.0.4 - System.Disk.po).
// The headless test reads $00:0071=0xAA confirming NewWindow returned
// non-NULL; the $00:0070=0x99 end-marker confirms the demo ran to
// completion.  Visual rendering of the WM frame is a separate known
// issue (see [[orca-window-render-broken]] memory): the SHR plane
// stays unpainted between WindStartUp and snapshot — likely a missing
// init step in startdesk(), not an fTitle problem.

#include "iigs/toolbox.h"
#include "iigs/desktop.h"

// wFrameBits constants from ORCA's window.h
#define fTitle   0x0001
#define fVis     0x0020
#define fMove    0x0080
#define fClose   0x4000

// TaskMaster event codes
#define wInGoAway  17


typedef struct { short v1, h1, v2, h2; } Rect;

typedef struct {
    unsigned short paramLength;
    unsigned short wFrameBits;
    void          *wTitle;
    unsigned long  wRefCon;
    Rect           wZoom;
    void          *wColor;
    short          wYOrigin, wXOrigin;
    short          wDataH,   wDataV;
    short          wMaxHeight, wMaxWidth;
    short          wScrollVer, wScrollHor;
    short          wPageVer,   wPageHor;
    unsigned long  wInfoRefCon;
    short          wInfoHeight;
    void          *wFrameDefProc;
    void          *wInfoDefProc;
    void          *wContDefProc;
    Rect           wPosition;
    void          *wPlane;
    void          *wStorage;
} NewWindowParm;


typedef struct {
    unsigned short  wmWhat;
    unsigned long   wmMessage;
    unsigned long   wmWhen;
    short           wmWhereV, wmWhereH;
    unsigned short  wmModifiers;
    unsigned long   wmTaskData;
    unsigned long   wmTaskMask;
    unsigned long   wmLastClickTick;
    unsigned long   wmClickCount;
    unsigned long   wmTaskData2;
    unsigned long   wmTaskData3;
    unsigned long   wmTaskData4;
} WmTaskRec;


static unsigned char gMsg[]   = "\x14Hello from llvm816!";

static NewWindowParm gWp;
static WmTaskRec     gEvent;


int main(void) {
    unsigned short userId = startdesk(640);
    (void)userId;

    // Clean Finder-style backdrop: white menu bar, 1-pixel separator,
    // white desktop.  Bypasses the WM dithered fill that MAME's
    // NTSC simulator renders as colored noise.
    __asm__ volatile (
        "rep #0x30\n"
        "ldx #0x0000\n"
        "1:\n"
        ".byte 0xa9, 0xff, 0xff\n"
        ".byte 0x9f, 0x00, 0x20, 0xe1\n"
        "inx\n inx\n"
        ".byte 0xe0, 0x20, 0x08\n"
        "bcc 1b\n"
        "2:\n"
        ".byte 0xa9, 0x00, 0x00\n"
        ".byte 0x9f, 0x00, 0x20, 0xe1\n"
        "inx\n inx\n"
        ".byte 0xe0, 0xc0, 0x08\n"
        "bcc 2b\n"
        "3:\n"
        ".byte 0xa9, 0xff, 0xff\n"
        ".byte 0x9f, 0x00, 0x20, 0xe1\n"
        "inx\n inx\n"
        ".byte 0xe0, 0x00, 0x7d\n"
        "bcc 3b\n"
        ::: "a", "x", "memory");

    // Build the NewWindow ParamList: zero everything first, then set
    // only the fields we care about.
    {
        unsigned char *p = (unsigned char *)&gWp;
        for (unsigned short i = 0; i < sizeof gWp; i++) p[i] = 0;
    }
    gWp.paramLength  = (unsigned short)sizeof gWp;
    gWp.wFrameBits   = fVis | fMove | fClose;
    gWp.wTitle       = (void *)0;
    gWp.wMaxHeight   = 200;
    gWp.wMaxWidth    = 320;
    gWp.wPosition.v1 = 40;  gWp.wPosition.h1 = 60;
    gWp.wPosition.v2 = 140; gWp.wPosition.h2 = 580;
    gWp.wPlane       = (void *)-1L;

    ShowCursor();

    void *win = NewWindow(&gWp);
    if (win) {
        *(volatile unsigned char *)0x71 = 0xAA;
        BeginUpdate(win);
        SetPort(win);
        MoveTo(20, 30);
        DrawString(gMsg);
        EndUpdate(win);
    }

    (void)gEvent;
    for (volatile unsigned long s = 0; s < 300000UL; s++) { }

    if (win) {
        CloseWindow(win);
    }

    *(volatile unsigned char *)0x70 = 0x99;
    return 0;
}
