;; ---------------------------------------------------------------------- ;; 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