;; ----------------------------------------------------------------------
;; crt0_ucsim.s — стартовый код для тестовых бинарей, исполняемых в ucsim.
;;
;; Зачем отдельный crt0, а не runtime/crt0*.s: те завязаны на Sprinter —
;; префикс ESTEX, argv из командной строки, выход через ESTEX EXIT
;; (rst #0x10).  В ucsim нет ни ESTEX, ни DSS; нужен голый z80: поставить
;; SP, занулить данные, позвать main, встать.
;;
;; Контракт с раннером (tests-host/run_ucsim.sh):
;;   - выполнение заканчивается инструкцией `halt` — по ней ucsim
;;     останавливает симуляцию, и раннер получает управление;
;;   - результат тестов раннер читает из ОЗУ командой `dump` (адрес берётся
;;     из карты линковки, символ _tc_result — см. tcheck.c).
;; Никакого simif не используем: номера его команд от версии к версии
;; плавают, а halt + dump работают везде.
;; ----------------------------------------------------------------------

    .module crt0_ucsim
    .globl  _main

    ;; Символы, которые расставляет линкер.
    .globl  s__INITIALIZER
    .globl  l__INITIALIZER
    .globl  s__INITIALIZED
    .globl  s__DATA
    .globl  l__DATA
    .globl  s__BSS
    .globl  l__BSS

;; =========================================================================
;; Точка входа: ucsim стартует с PC = 0x0000.
;;
;; ВАЖНО: `.area _HEADER (ABS)` должна встречаться РОВНО ОДИН РАЗ и первой.
;; Если продублировать её в списке порядка областей ниже, sdas заведёт
;; вторую область _HEADER0, и линкер положит стартовый код не по 0x0000, а
;; куда придётся (проверено: уезжал за _DATA на 0x830A, и с PC=0
;; исполнялся мусор).
;; =========================================================================
    .area _HEADER (ABS)
    .org 0x0000
    di
    ld    sp, #0xFEF0            ; стек под верхней границей ОЗУ ucsim
    call  gsinit
    call  _main
    halt                         ; ucsim остановит симуляцию здесь
1$: jr    1$                     ; страховка, если halt проигнорирован

;; =========================================================================
;; Порядок остальных областей — объявляем заранее, чтобы линкер разложил их
;; так (без _HEADER, см. комментарий выше).
;; =========================================================================
    .area _HOME
    .area _CODE
    .area _INITIALIZER
    .area _GSINIT
    .area _GSFINAL

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

;; =========================================================================
;; gsinit — занулить _DATA/_BSS и скопировать _INITIALIZER -> _INITIALIZED.
;; SDCC вставляет между gsinit и ret в _GSFINAL код инициализации модулей.
;; =========================================================================
    .area _CODE
;; zero_area — заполнить BC байт по адресу HL нулями (BC может быть 0).
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::
    ld    hl, #s__DATA
    ld    bc, #l__DATA
    call  zero_area
    ld    hl, #s__BSS
    ld    bc, #l__BSS
    call  zero_area
    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
