;; ----------------------------------------------------------------------
;; crt0_minimal.s — Sprinter ESTEX C runtime startup, no argv parsing.
;;
;; This is the opt-out variant for programs where every byte counts.
;; The DEFAULT runtime is runtime/crt0.s which also parses argv from the
;; ESTEX command line and passes argc/argv to main().
;;
;; To use this instead of the default:
;;   crt0.rel: $(PROJ_ROOT)/runtime/crt0_minimal.s
;;       $(SDASZ80) -o $@ $<
;;
;; Otherwise identical to crt0.s — saves IX, calls gsinit, calls main
;; with no arguments, then EXIT via ESTEX $41.
;; ----------------------------------------------------------------------

    .module crt0_minimal
    .globl  _main

    ;; Linker-emitted symbols for areas (resolved at link time).
    .globl  s__INITIALIZER
    .globl  l__INITIALIZER
    .globl  s__INITIALIZED
    .globl  s__DATA
    .globl  l__DATA
    .globl  s__BSS
    .globl  l__BSS

;; =========================================================================
;; AREA ORDERING — emitted up-front so the linker walks them in this order.
;; =========================================================================
    .area _HOME
    .area _CODE
    .area _INITIALIZER
    .area _GSINIT
    .area _GSFINAL

    .area _DATA
    .area _INITIALIZED
    .area _BSEG
    .area _BSS
    .area _HEAP

;; =========================================================================
;; Entry point — first instruction in _CODE, hence at --code-loc (0x4100).
;; =========================================================================
    .area _CODE
_start::
    ;; ESTEX already set SP from the EXE header, but make it explicit.
    ld    sp, #0xBFFE

    ;; Save startup prefix (IX) for argv/exit-code helpers.
    ld    (_estex_startup_ix), ix

    ;; Run SDCC global initialisers (concatenated into _GSINIT).
    call  gsinit

    ;; Hand off to user main.
    call  _main

    ;; SDCC 4.5 __sdcccall(1): int return is in DE, so low byte of main()'s
    ;; return is in E. (See memory/sdcc_z80_abi.md.)
    ;; main returned — terminate via ESTEX EXIT directly.  We do NOT
    ;; expose a _exit symbol; programs that need exit() / atexit() must
    ;; link libc/io/atexit.c.
    ld    a, e
    ld    b, a
    ld    c, #0x41               ; ESTEX EXIT
    rst   #0x10
1$: halt
    jr    1$

;; =========================================================================
;; Runtime data
;; =========================================================================
    .area _CODE
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
;; _CODE — RAM on the Sprinter, always mapped.
_estex_startup_ix::
    .ds 2

;; =========================================================================
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
;;   SDCC appends per-unit init code between this label and the ret in _GSFINAL.
;; =========================================================================
    .area _GSINIT
    .area _CODE
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
zero_area:
    ld    a, b
    or    a, c
    ret   Z
    ld    (hl), #0
    dec   bc
    ld    a, b
    or    a, c
    ret   Z
    ld    d, h
    ld    e, l
    inc   de
    ldir
    ret

    .area _GSINIT
gsinit::
    ;; --- Zero _DATA + _BSS (C guarantees zero-valued statics) ---
    ld    hl, #s__DATA
    ld    bc, #l__DATA
    call  zero_area
    ld    hl, #s__BSS
    ld    bc, #l__BSS
    call  zero_area
    ;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
    ld    bc, #l__INITIALIZER
    ld    a, b
    or    a, c
    jr    Z, gsinit_done
    ld    de, #s__INITIALIZED
    ld    hl, #s__INITIALIZER
    ldir
gsinit_done:

    .area _GSFINAL
    ret
