61d4255091
Обвязка для быстрых тестов 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>
104 lines
4.2 KiB
ArmAsm
104 lines
4.2 KiB
ArmAsm
;; ----------------------------------------------------------------------
|
||
;; 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
|