Files
Sprinter-SDCC/testkit/crt0_ucsim.s
T
Александр Петров 61d4255091 testkit: модульные тесты под ucsim_z80 + планы покрытия
Обвязка для быстрых тестов plain-C логики: секунды вместо прогона в MAME,
без образа диска.  ucsim_z80 идёт в комплекте нашего SDCC — новых
зависимостей нет.

ПОЧЕМУ ПОД Z80, А НЕ ХОСТОВЫМ GCC.  У SDCC z80 int 16 бит, у хоста 32, и
расходится это НЕ в объявлениях, а в выражениях: integer promotion
повышает операнды до int независимо от того, объявлены они как uint8_t
или uint16_t.  Перевод кода на фиксированные типы разницу не убирает —
убирает только исполнение с z80-семантикой.  Побочно проверяется
кодогенерация SDCC и модули с inline-asm, которых хостовая сборка не
видит в принципе.

Устройство: crt0_ucsim.s (SP, зануление, main, halt), tcheck.* (итог в
структуру в ОЗУ), run_ucsim.py (гоняет ucsim, дампит tc_result, печатает
отчёт), host-tests.mk (общие правила).  Вывода через printf нет: тестовый
бинарь линкуется без Sprinter-libc.  Через ucsim-simif не идём — номера
его команд плавают между версиями, halt + dump работают везде.

Наборы лежат РЯДОМ с проверяемым кодом, обвязка общая:
  testkit/t_selftest.c                     — самопроверка (sizeof(int)==2)
  applications/PoP/roomtest/tests-host/    — движок PoP

Первый содержательный набор — t_geom: сверяет рукописный asm-LCG из
pop_geom.c с наивной 32-битной формулой на 128 шагах.  Заявка «бит-в-бит
как в SDLPoP» до сих пор держалась на комментарии.  Тест проверен
мутацией: порча эталонной константы даёт красный.

Планы дальнейшего покрытия:
  docs/host-tests-plan.md                    — libc и libbgi (не начато)
  applications/PoP/docs/host_tests_plan.md   — движок PoP

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 22:36:16 +03:00

104 lines
4.2 KiB
ArmAsm
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;; ----------------------------------------------------------------------
;; 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