154 lines
5.9 KiB
ArmAsm
154 lines
5.9 KiB
ArmAsm
; 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
|