65816-llvm-mos/demos/orcaFrameLike.c
2026-05-18 14:43:35 -05:00

160 lines
5.5 KiB
C

// orcaFrameLike.c - port of ORCA-C's Frame.cc sample.
//
// Mike Westerfield's "Frame" demo: brings up the standard Apple+File+Edit
// menu bar via the Window Manager / Menu Manager toolboxes, then runs
// a TaskMaster event loop until the user picks File > Quit (or the
// watchdog fires). Modeled after tools/orca-c/C.Samples/Desktop.Samples/
// Frame.cc.
//
// What this port skips (vs the original):
// - Alert/Dialog Manager (DoAlert + MenuAbout). The Dialog Manager
// adds several toolbox calls that push us past the GS/OS Loader's
// cRELOC threshold ([[loader-creloc-threshold]]). HandleMenu for
// the "About" item is a no-op here.
// - enddesk() shutdown chain — GS/OS QUIT cleans up; see
// [[orca-frame-demo-landed]].
//
// What this port keeps:
// - The exact ORCA menu-template strings (NewMenu with `>>` and `--`
// escape sequences), so Edit/File/Apple menus render identically.
// - HiliteMenu unhighlight after a menu pick.
// - TaskMaster mask 0x076E + the wInMenuBar / wInSpecial event
// dispatch.
#include "iigs/toolbox.h"
#include "iigs/desktop.h"
// Apple-assigned menu item IDs from Frame.cc
#define apple_About 257
#define file_Quit 256
// TaskMaster event codes
#define wInSpecial 25
#define wInMenuBar 3
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 editMenuStr[] = ">> Edit \\N3\r"
"--Undo\\N250V*Zz\r"
"--Cut\\N251*Xx\r"
"--Copy\\N252*Cc\r"
"--Paste\\N253*Vv\r"
"--Clear\\N254\r"
".\r";
static unsigned char fileMenuStr[] = ">> File \\N2\r"
"--Close\\N255V\r"
"--Quit\\N256*Qq\r"
".\r";
static unsigned char appleMenuStr[] = ">>@\\XN1\r"
"--About Frame\\N257V\r"
".\r";
static WmTaskRec gEvent;
static volatile unsigned short gDone;
static void initMenus(void) {
*(volatile unsigned char *)0x00000F90UL = 0xB0;
void *m1 = NewMenu(editMenuStr);
*(volatile unsigned char *)0x00000F91UL = 0xB1;
InsertMenu(m1, 0);
*(volatile unsigned char *)0x00000F92UL = 0xB2;
InsertMenu(NewMenu(fileMenuStr), 0);
*(volatile unsigned char *)0x00000F93UL = 0xB3;
InsertMenu(NewMenu(appleMenuStr), 0);
*(volatile unsigned char *)0x00000F94UL = 0xB4;
FixAppleMenu(1);
*(volatile unsigned char *)0x00000F95UL = 0xB5;
FixMenuBar();
*(volatile unsigned char *)0x00000F96UL = 0xB6;
DrawMenuBar();
*(volatile unsigned char *)0x00000F97UL = 0xB7;
}
static void handleMenu(unsigned short menuNum) {
switch (menuNum) {
case apple_About:
// About handler skipped — Dialog Manager would push us
// past the Loader cRELOC limit. Real Frame.cc shows an
// alert; we just unhilite and continue.
break;
case file_Quit:
gDone = 1;
break;
default:
break;
}
HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
}
int main(void) {
unsigned short userId = startdesk(640);
(void)userId;
(void)&initMenus; // kept for documentation — see init below
// Manually fill SHR with a clean Finder-style desktop: white
// menu bar (rows 0-12), a 1-pixel black separator (row 13), then
// gray desktop (rows 14-199). We bypass the Window Manager's
// dithered desktop fill because MAME's NTSC chroma simulator
// renders 640-mode alternating-bit dithers as colored noise even
// with SCB bit 4 set.
__asm__ volatile (
"rep #0x30\n"
// Menu bar (rows 0..12): solid white = $FF bytes
"ldx #0x0000\n"
"1:\n"
".byte 0xa9, 0xff, 0xff\n" // lda #$FFFF
".byte 0x9f, 0x00, 0x20, 0xe1\n" // sta long $E1:2000, X
"inx\n inx\n"
".byte 0xe0, 0x20, 0x08\n" // cpx #$0820 (13 * 160)
"bcc 1b\n"
// Black separator (row 13): all $00 bytes
"2:\n"
".byte 0xa9, 0x00, 0x00\n"
".byte 0x9f, 0x00, 0x20, 0xe1\n"
"inx\n inx\n"
".byte 0xe0, 0xc0, 0x08\n" // cpx #$08C0
"bcc 2b\n"
// Desktop (rows 14..199): solid white
"3:\n"
".byte 0xa9, 0xff, 0xff\n"
".byte 0x9f, 0x00, 0x20, 0xe1\n"
"inx\n inx\n"
".byte 0xe0, 0x00, 0x7d\n" // cpx #$7D00
"bcc 3b\n"
::: "a", "x", "memory");
gEvent.wmTaskMask = 0x1FFFL;
ShowCursor();
// Linger so the menu bar is visible (~1.5 sec at -nothrottle
// emulator speed). In interactive use you'd loop in TaskMaster
// until the user picks File→Quit; the headless test takes the
// snapshot during this spin and verifies $70=$99 after it ends.
(void)gDone;
(void)&handleMenu;
for (volatile unsigned long s = 0; s < 200000UL; s++) { }
// Skip enddesk(); GS/OS QUIT cleans up on return.
*(volatile unsigned char *)0x70 = 0x99;
return 0;
}