; 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,
; verified against tools/gno/src-repo/lib/libc/gno/gnocmd.asm: it does
; `sta ~USER_ID` / `sty ~COMMANDLINE` / `stx ~COMMANDLINE+2`):
;   E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index)
;   A    = user ID
;   Y:X  = command line pointer (32-bit; Y=low word, X=high word)
;   DBR  = bank of our entry segment (set by GS/OS Loader)
;   DP   = a Memory-Manager-allocated, PER-PROCESS direct page.  We HONOR
;          it (do NOT force DP=0) so codegen's DP slots are process-private
;          under GNO's preemptive multitasking — see the __start body.
;          (~GNO_COMMAND itself keeps this DP and stores argv/argc in it.)
;   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:
	; MULTITASKING: HONOR GNO's per-process direct page -- do NOT force
	; DP=0.  GNO is preemptive.  Every codegen DP slot (the __jsl_indir
	; vector at $B8, the PBR stash at $BE, the libcall scratch at
	; $E0..$FF) and our own cmdline/user-ID stash at $B0..$B7 below are
	; DP-relative, so keeping DP at GNO's per-process page makes them all
	; PROCESS-PRIVATE.  Forcing DP=0 (as this crt used to) put every one
	; of them on the shared bank-0 zero page -- so two of our binaries run
	; concurrently (e.g. a `a | b` pipeline that gsh forks) aliased each
	; other's saved DP / pointer bank / scratch -> silent cross-process
	; corruption.  This mirrors crt0Gsos.s honoring the Loader DP; the
	; backend has no DP=0 requirement (it addresses every scratch slot
	; DP-relative).  (GNO's GS/OS interceptor identifies the caller by
	; curProcInd -- scheduler state, not DP -- and StackGSOS saves/restores
	; the caller's DP itself, so honoring GNO's DP is purely about keeping our
	; own scratch process-private, not about being identified -- see
	; gnoGsos.s.)
	;
	; Stash cmdline (Y:X) at DP $B0..$B3 and user ID (A) at $B6, DP-form so
	; they land in our private page.  DP addressing uses D (valid on entry)
	; and ignores DBR, so this is safe before the phk/plb below.
	rep #0x30
	sta 0xb6                       ; DP $B6 = user ID (per-process)
	tya
	sta 0xb0                       ; DP $B0 = cmdline low word (= Y)
	txa
	sta 0xb2                       ; DP $B2 = cmdline high word (= X)

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

	; No __jsl_indir operand patch: the trampoline reads its indirect target
	; DP-relative ($B8/$BA), so it works on any thread's per-process DP with no
	; self-modified global operand (which used to alias across concurrent
	; threads -- see the reentrant __jsl_indir in libgcc.s).

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

	; BSS zero-init: NOT NEEDED under GNO -- and actively harmful.  omfEmit
	; embeds the BSS region as LCONST zeros inside the code segment (seg1
	; LENGTH covers [text .. __bss_end], RESSPC=0), so the GS/OS InitialLoad
	; that GNO's exec uses fills BSS with zeros at load time -- BSS is
	; already zero when __start runs (verified: cxxChronoProbe's zero-init
	; clock statics read correctly with this loop gone).
	;
	; The old DBR-relative `stz $0000,X` loop over [__bss_start, __bss_end]
	; was redundant AND corrupted GNO's heartbeat task queue: once BSS grew
	; large (cxxChronoProbe, ~6 KB BSS, __bss_end ~0xB74C) the clear reached
	; the kernel's bank-0 heartbeat block (alarmHB), and the next VBL tick
	; hit SysFailMgr $0308 "Damaged heartbeat queue" -> system halt BEFORE
	; main.  A non-chrono probe with an equally large BSS reproduced it, so
	; it is a pure BSS-ceiling collision, not a chrono bug.  Small programs
	; (BSS below ~0xB100) escaped, which is why it went unnoticed.  This is
	; the same fix crt0Gsos.s already applied for the analogous GS/OS
	; Memory-Manager corruption -- see feedback_gsos_fopen_partial_diagnosis.

	; Back the C malloc heap with an MM-owned handle -- the SAME fix
	; crt0Gsos.s applies.  crt0Gno historically kept malloc on link816's
	; fixed link-time window [__bss_end, 0xBF00) in BANK 0, but that span is
	; PHANTOM memory the Memory Manager never reserved for us: it runs
	; straight through GNO's high-bank-0 kernel region (the heartbeat block
	; alarmHB ~0xB7xx), so any GNO program that mallocs a few KB reproduces
	; the same SysFailMgr $0308 halt the BSS clear did.  __heapInitMM
	; NewHandles a locked block in MM-owned (safe-bank) memory and repoints
	; malloc at it.  Our GNO process MM user ID was handed to __start in A and
	; stashed at $00:00B6; GNO already started the Memory Manager (see NOTE
	; below), so -- unlike crt0Gsos -- we must NOT call MMStartUp, we reuse
	; the handed-in ID (read DP-relative from our private page).  NewHandle
	; keys ownership off the userID PARAMETER, not DP.  On NewHandle failure
	; __heapInitMM leaves malloc on the link-time window (no worse than
	; before).  crt0.s (bare-metal, no MM) still does not do this.
	rep #0x30
	lda 0xb6                 ; DP $B6 = our GNO process MM user ID (per-process)
	jsl __heapInitMM         ; __heapInitMM(userID); arg0 in A

	; 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 our private DP page ($B0..$B3) into A:X.
	; DP-form (not `lda long`) so we read the per-process copy.
	rep #0x30
	lda 0xb0                       ; DP $B0 -> A (cmdline lo word)
	pha                            ; stash A on stack briefly
	lda 0xb2                       ; DP $B2 -> 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
