; crt0Gsos.s — GS/OS S16 application crt0. ; ; Use this INSTEAD OF crt0.s when building an OMF that the real ; GS/OS Loader will launch. Differences from crt0.s: ; - No SEI / interrupt-source clearing (GS/OS owns the IRQ chain). ; - No language-card RAM enable (GS/OS configures memory). ; - No stack base reset (GS/OS allocated and set our SP). ; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup. ; - On main() return, calls GS/OS QUIT(pcount=0) to return control to ; the launching program (Finder / shell / boot chain) — the standard ; GS/OS application exit. ; ; Entry from the System Loader (per Apple IIgs Toolbox Reference): ; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index) ; DBR = the bank into which our entry segment was placed ; DP = pointer to a Memory-Manager-allocated DP page ; Stack at entry, top-down: ; PCL PCH PBR (3 bytes — JSL return addr to launcher) ; flags-lo flags-hi (2 bytes — launch flags) ; path-low path-mid path-bank pad (4 bytes — pathname long ptr) ; ; QUIT discards the entire stack so we never need to pop the launch ; frame ourselves. .text .globl __start __start: ; Set DBR := PBR so absolute (DBR-relative) loads/stores reach ; symbols within our segment. The Loader's documented contract is ; "DBR set to your bank" but verified empirically NOT always true ; for KIND=0x1000 segments — `lda absConst` was reading from the ; wrong bank without this. PHK + PLB copies PBR into DBR. phk plb ; Set DP=0. The C compiler assumes DP=0 for all `sta dp` and ; `[dp],y`-style accesses; GS/OS hands us a Memory-Manager- ; allocated DP page that we discard. rep #0x30 lda #0 tcd ; Persistent "current data bank" byte at DP $BE. Set to PBR ; (= our load bank) so the LDAptr/STAptr/STBptr inserters' ; "LDA $BE; STA $E2" sequence puts pointer derefs in our bank, ; matching DBR-relative absolute stores. $BE is outside the ; libcall scratch range ($E0..$FF used by libgcc.s for i64 ops). ; See crt0.s. sep #0x20 phk pla sta 0xbe stz 0xbf ; pad byte so `lda 0xbe` in 16-bit M reads $00PBR ; (codegen uses this as the bank-half of `&symbol` ; 32-bit pointers — see W65816AsmPrinter ; LDAi16imm_bank expansion) rep #0x20 ; BSS zero-init: NOT NEEDED under GS/OS. omfEmit embeds the BSS ; region as zeros inside the LCONST data, so the GS/OS Loader ; allocates+fills our BSS during segment load — by the time __start ; runs, BSS is already zero. A redundant `stz` loop here was found ; to HANG fopen / gsosOpen (Phase 1.1 root cause, 2026-06-02): when ; BSS extends past runtime offset ~$9E00 in the placed bank, ; re-zeroing that region corrupts GS/OS Memory-Manager / dispatcher ; state that lives in our allocated chunk between the Loader's ; LCONST-fill and our __start entry. Skipping the redundant zero ; eliminates the corruption; semantics are preserved because the ; Loader already did it. ; See feedback_gsos_fopen_partial_diagnosis (root-caused this session). .Lbss_done: rep #0x20 ; restore M=16 ; Walk .init_array (C++ ctors). The `jsl __jsl_indir` and the ; `jsl main` below are relocated by the GS/OS Loader: omfEmit emits ; a cRELOC (0xF5) IMM24 site for each intra-segment JSL, so the ; Loader patches the full 24-bit operand (bank included) to the ; placed address. Non-zero-bank placement works as long as the OMF ; is built with --relocs (demos/build.sh / buildGno.sh do). rep #0x30 ldx #__init_array_start .Linit_loop: cpx #__init_array_end bcs .Linit_done stx 0xe0 ldy #0 lda (0xe0), y sta 0xb8 ; __indirTarget pinned at DP $B8 (bank-0 fixed; see libgcc.s) phx jsl __jsl_indir plx ; Step by 4 bytes (sizeof(void*) under p:32:16). inx inx inx inx bra .Linit_loop .Linit_done: ; NOTE: do NOT add TLStartUp/MMStartUp here. The GS/OS Loader has ; already initialised the Tool Locator before transferring control to ; __start (helloBeep proves this — it does a SysBeep JSL $E10000 with ; zero init code). Per-process tool init (MM/QD/EM/WM) is the ; program's responsibility; the desktop demos use startdesk(640) ; from runtime/include/iigs/desktop.h. ; Seed rand() from the IIgs RTC via ReadTimeHex ($0D03). Without ; this, srand defaults to seed=1 and every run produces an identical ; PRNG sequence -- a correctness bug for mkstemp / tmpnam and any ; user code relying on Monte-Carlo-style uniqueness. TL is up ; (Loader brought it up), so the JSL inside __srandInitFromTime is ; safe. Linker drops the symbol when no rand-consuming code is in ; the link, so this costs ~0 bytes for non-PRNG programs (the ; reference is one weak-resolved JSL). rep #0x30 jsl __srandInitFromTime ; Call main. Standard W65816 C ABI: arg0 in A; we pass none. rep #0x30 jsl main ; Run global dtors registered via __cxa_atexit (LIFO order) before ; QUIT. A holds main()'s return value; QUIT itself doesn't consume ; it (we set up the parm block immediately below), but keep the ; save/restore for parity with crt0Gno. pha jsl __run_cxa_atexit pla ; ---- QUIT (pcount=0): return to the launching program ---------- ; The standard GS/OS application exit. A pcount=0 parm block (just ; a zero pcount word) tells QUIT to return control to whoever ; launched us (Finder, a shell, the boot chain) with default flags ; (non-restartable) — no hardcoded chain path needed. A program ; that wants to chain elsewhere can provide its own _exit/crt0. rep #0x30 stz 0x80 ; pcount = 0 (parm block at DP $0080, bank 0) tdc clc adc #0x80 pha ; parm-block ptr: low 16 (offset) pea 0 ; parm-block ptr: high 16 (bank 0) ldx #0x2029 ; QUIT class-1 call number jsl 0xe100a8 ; GS/OS dispatcher ; QUIT only returns on failure. Clean up the pushed pointer and ; spin (don't fall into a BRK, which headless MAME mis-vectors). pla pla .Lhang: bra .Lhang .size __start, . - __start