; 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=2) to chain to a
;     known next application (default: /SYSTEM/START.ORIG which
;     test setups must save off the original boot launcher to).
;
; 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
	; Diagnostic: stash a marker at $00:007F via `sta long` (bank-
	; explicit, immune to DBR uncertainty) immediately on entry.
	; Runtime probes use this to detect whether the Loader actually
	; reached our code or silently rejected the OMF earlier.
	; The assembler doesn't track SEP/REP state, so the LDA #imm
	; needs explicit `.byte` form: the standard `lda #$7F` would
	; assemble as 3 bytes (16-bit imm) and the trailing $00 would
	; execute as BRK at runtime once M=8.
	sep #0x20
	.byte 0xa9, 0x7f                 ; lda #$7F (8-bit imm)
	.byte 0x8f, 0x7f, 0x00, 0x00     ; sta long $00:007F
	rep #0x20

	; 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

	; --- Diagnostic markers between crt0 phases.  Bank-explicit
	; `sta long` to $00:007E so they're visible from a host probe
	; regardless of DBR/PBR.
	sep #0x20
	.byte 0xa9, 0x7e               ; lda #$7E
	.byte 0x8f, 0x7e, 0x00, 0x00   ; sta long $00:007E  (post-DP)
	rep #0x20

	; BSS zero-init. With DBR=our bank, `stz abs,X` writes to
	; ourBank:X — correct as long as __bss_start/__bss_end fit in
	; the segment's bank.
	rep #0x30
	ldx #__bss_start
.Lbss_loop:
	cpx #__bss_end
	bcs .Lbss_done
	sep #0x20
	stz 0x0000, x
	rep #0x20
	inx
	bra .Lbss_loop
.Lbss_done:
	sep #0x20
	.byte 0xa9, 0x7d               ; lda #$7D
	.byte 0x8f, 0x7d, 0x00, 0x00   ; sta long $00:007D  (post-BSS)
	rep #0x20

	; Walk .init_array (C++ ctors).
	;
	; ⚠ KNOWN BROKEN under real GS/OS Loader for non-zero-bank
	; placement: `jsl __jsl_indir` bakes a bank-0 operand at link
	; time. When the Loader places us at bank $1f or similar, the
	; JSL targets bank 0 (= GS/OS code) instead of our actual bank
	; — so this loop crashes if init_array has any entries. Same
	; applies to `jsl main` below. Closing the gap requires either
	; RELOC opcode emission in omfEmit (so the Loader patches the
	; JSL bank bytes at load time) or runtime self-patching of JSL
	; opcodes in crt0. Tracked separately.
	rep #0x30
	ldx #__init_array_start
.Linit_loop:
	cpx #__init_array_end
	bcs .Linit_done
	stx 0xe0
	ldy #0
	lda (0xe0), y
	sta __indirTarget
	phx
	jsl __jsl_indir
	plx
	; Step by 4 bytes (sizeof(void*) under p:32:16).
	inx
	inx
	inx
	inx
	bra .Linit_loop
.Linit_done:

	; Marker: about to JSL main.
	sep #0x20
	.byte 0xa9, 0x7c               ; lda #$7C
	.byte 0x8f, 0x7c, 0x00, 0x00   ; sta long $00:007C  (about to call main)
	rep #0x20

	; Call main. Standard W65816 C ABI: arg0 in A; we pass none.
	rep #0x30
	jsl main

	; ---- QUIT (pcount=2) chain to gChainPath ---------------------
	; Parm block layout in DP $80..$87:
	;   $80,$81  pcount = 2
	;   $82..$85 pathname long ptr  (lo, mid, bank, pad)
	;   $86,$87  flags = 0
	;
	; The path is a GSString (2-byte length + chars). It must live
	; in bank-0 memory (GS/OS reads parm fields as bank-0). DP is in
	; bank 0, so we copy the GSString from our segment into DP $A0.

	rep #0x30

	; Copy length byte first to compute total bytes to copy.
	sep #0x20
	lda gChainPath           ; low byte of GSString length
	clc
	adc #2                   ; +2 for the length word itself
	tay                      ; Y = bytes to copy (paths < 256 chars)
	rep #0x20

	ldx #0
.LcopyPath:
	sep #0x20
	lda gChainPath, x        ; DBR-relative read (DBR = our bank)
	sta 0xa0, x              ; DP write (in bank 0)
	rep #0x20
	inx
	dey
	bne .LcopyPath

	; Build parm block at DP $80.
	rep #0x30
	lda #2
	sta 0x80                 ; pcount

	tdc
	clc
	adc #0xa0
	sta 0x82                 ; pathname long-ptr low+mid 16
	lda #0
	sta 0x84                 ; bank byte (0) + pad byte (0)
	sta 0x86                 ; flags = 0

	; Push 32-bit parm-block pointer (low half + bank-0).
	tdc
	clc
	adc #0x80
	pha
	pea 0
	ldx #0x2029              ; QUIT class-1 call number
	jsl 0xe100a8             ; GS/OS dispatcher

	; QUIT only returns on failure. Clean up + BRK.
	pla
	pla
	.byte 0x00, 0x00

	.size __start, . - __start


; gChainPath — GSString chain target for QUIT after main(). Default
; is "/SYSTEM/START.ORIG" (saved-original boot launcher). Programs
; that need a different target must rename this symbol; the linker
; resolves whichever def is present.
;
; GSString: 2-byte length word + N chars. Length here = 18
; ("/SYSTEM/START.ORIG").

	.section .rodata,"a"
	.globl gChainPath
gChainPath:
	.byte 18, 0
	.ascii "/SYSTEM/START.ORIG"
