; crt0Gno.s — GNO/ME shell command crt0.
;
; Use this INSTEAD OF crt0.s / crt0Gsos.s when building an OMF intended
; to run as a GNO shell command (filetype $B5 EXE, aux 0).
;
; GNO/ME entry contract for a shell command (per ORCA's ~GNO_COMMAND):
;   E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index)
;   A:X  = command line pointer (32-bit; A=low word, X=high word)
;   Y    = user ID (currently unused by us)
;   DBR  = bank of our entry segment (set by GS/OS Loader)
;   DP   = pointer to a Memory-Manager-allocated DP page
;   The GS/OS Loader's launch frame on the stack is discarded by Quit.
;
; Differences from crt0Gsos.s:
;   - The kernel passes a command-line pointer in A:X (not on the stack).
;   - Instead of QUIT chaining to a hardcoded path, we just QUIT(pcount=0)
;     so control returns to the GNO shell that launched us.
;   - main() receives (int argc, char **argv) — crt0 calls a C helper
;     __gnoStartup(cmdline) which parses the command line and invokes
;     main(), then falls through to QUIT.

	.text

	.globl __start
__start:
	; GNO entry contract (from kern ~GNO_COMMAND, lib/libc/gno/gnocmd.asm):
	;   A   = user ID
	;   Y:X = command-line pointer (Y = low word, X = high word) — a
	;         NUL-terminated C string of the invocation command line.
	; Stash the cmdline pointer (Y:X) to bank-0 scratch $00:00B0..B3
	; and the user ID (A) to $00:00B6, BEFORE we switch to DP=0.
	; Bank-explicit `sta long` so we don't depend on DP/DBR here.
	rep #0x30
	.byte 0x8f, 0xb6, 0x00, 0x00   ; sta long $00:00B6 (user ID)
	tya
	.byte 0x8f, 0xb0, 0x00, 0x00   ; sta long $00:00B0 (cmdline low word = Y)
	txa
	.byte 0x8f, 0xb2, 0x00, 0x00   ; sta long $00:00B2 (cmdline high word = X)

	; Set DBR := PBR (our entry-segment bank) — same Loader-contract
	; mitigation as crt0Gsos.s.
	phk
	plb

	; Save GNO's per-process direct page BEFORE zeroing it.  GNO's
	; GS/OS-call interceptor uses the caller's DP to identify the
	; calling process (and find its fd table).  Our codegen needs
	; DP=0, so we stash GNO's DP at $00:00B4 and restore it around
	; GS/OS calls (see __gnoSaveDP / gsosWrite-under-GNO path).
	rep #0x30
	tdc                            ; A = current (GNO) DP
	.byte 0x8f, 0xb4, 0x00, 0x00   ; sta long $00:00B4 (stash GNO DP word)

	; Set DP=0 — backend assumes DP=0 for `sta dp` / `[dp],y` etc.
	lda #0
	tcd

	; Persistent "current data bank" byte at DP $BE = PBR (our bank).
	; See crt0.s comment for rationale (codegen reads this for the
	; bank half of `&symbol` 32-bit pointers).
	sep #0x20
	phk
	pla
	sta 0xbe
	stz 0xbf      ; pad so `lda 0xbe` in 16-bit M reads $00PBR
	rep #0x20

	; BSS zero-init (DBR-relative byte stores).  M is held at 8 across
	; the whole loop while X stays 16-bit, so we don't flip SEP/REP per
	; byte (llvm-mc still encodes cpx/ldx as 16-bit X-immediates in M=8).
	rep #0x30                ; M=16, X=16
	sep #0x20                ; M=8 for the byte stores; X remains 16-bit
	ldx #__bss_start
.Lbss_loop:
	cpx #__bss_end
	bcs .Lbss_done
	stz 0x0000, x            ; 1-byte store (M=8)
	inx
	bra .Lbss_loop
.Lbss_done:
	rep #0x20                ; restore M=16

	; Walk .init_array (C++ ctors).  Inlined from crt0Gsos.s.  The
	; `jsl __jsl_indir` and `jsl main` operands 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 is
	; handled correctly as long as the OMF is built with --relocs (which
	; demos/buildGno.sh does).
	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
	inx
	inx
	inx
	inx
	bra .Linit_loop
.Linit_done:

	; NOTE: do NOT add TLStartUp/MMStartUp here.  The GNO kernel pre-inits
	; the Tool Locator (the kernel itself uses JSL $E10000 to make GS/OS
	; calls), so TL is up before __start runs.  Per-process tool init
	; (MM/QD/EM/WM) is the program's responsibility.

	; Seed rand() from the IIgs RTC via ReadTimeHex ($0D03).  Same
	; rationale as crt0Gsos.s: default seed=1 produces identical PRNG
	; output across runs.  TL is already up (GNO kernel brought it up),
	; so this JSL is safe.
	rep #0x30
	jsl __srandInitFromTime

	; Reload cmdline ptr from $00:00B0..$00:00B3 into A:X.
	; Use bank-explicit `lda long` so we don't depend on DBR.
	rep #0x30
	.byte 0xaf, 0xb0, 0x00, 0x00   ; lda long $00:00B0 -> A (cmdline lo word)
	pha                            ; stash A on stack briefly
	.byte 0xaf, 0xb2, 0x00, 0x00   ; lda long $00:00B2 -> A (cmdline hi word)
	tax                            ; X = cmdline hi
	pla                            ; A = cmdline lo

	; Call __gnoStartup(cmdline) — C helper in libcGno.c which:
	;   1. parses the command line into argc/argv,
	;   2. calls main(argc, argv),
	;   3. returns whatever main returned.
	jsl __gnoStartup

	; Run global dtors registered via __cxa_atexit (LIFO order) before
	; QUIT.  A holds main()'s return value (exit status), which _exit
	; consumes — preserve it across the call.
	pha
	jsl __run_cxa_atexit
	pla

	; ---- Terminate via _exit (libcGno.c) ----
	; __gnoStartup returned with A = main()'s exit status.  _exit issues
	; GS/OS QUIT through GNO's INLINE dispatch (__gnoGsosCall), which is
	; REQUIRED under GNO: the old stack-based QUIT here (ldx #$2029 ;
	; jsl $e100a8 with the pblock pushed) was misread by GNO's OurGSOS
	; interceptor — it reads the call number + pblock from the inline
	; bytes after the jsl, not the stack — so the QUIT was mis-dispatched,
	; the process was left restartable, and GNO RE-RAN main() a second
	; time (silently draining redirected stdin on the first pass).
	jsl _exit

	; _exit does not return.  If it ever does, spin rather than fall
	; into a BRK (headless MAME mis-vectors BRK to $0000, which looks
	; like a wild crash) or off the end of the segment.
.Lhang:
	bra .Lhang

	.size __start, . - __start
