;============================================================================== ; @file crt0.s ; @brief Стартовый код WC-плагина (SDCC Z80, --no-std-crt0) ; ; Этот файл заменяет стандартный crt0 SDCC. Он должен линковаться ; ПЕРВЫМ в цепочке .rel-файлов, чтобы _plugin_entry оказался ; по адресу --code-loc (обычно #8000). ; ; Входные параметры WC (при CALL plugin_base): ; A' = индекс расширения файла ; BC = указатель на имя файла (z-строка) ; HL = размер файла (младшее слово) ; DE = размер файла (старшее слово) ; IX = указатель на структуру активной панели WC ; ; Выход: A = код возврата (0=ESC, 2=следующий, 4=предыдущий) ; ; Адресное пространство: ; #8000-#BFFF = код + данные плагина ; #C000-#FFFF = VPL окно (данные VGM через WC API) ; ; ВАЖНО: gsinit обнуляет _DATA и копирует _INITIALIZER → _INITIALIZED. ; Без этого при повторном запуске (NEXT/PREV) стат. переменные ; содержат мусор от предыдущего запуска. ;============================================================================== .module crt0 .globl _main .globl _wc_exit_code .globl _wc_file_idx .globl _wc_file_count ; Линкер-символы для runtime-инициализации C .globl s__DATA .globl l__DATA .globl s__INITIALIZED .globl s__INITIALIZER .globl l__INITIALIZER ;------------------------------------------------------------------------------ ; Entry point (WC вызывает по "CALL plugin_base") ; Вход: ; A' = индекс расширения файла ; BC = указатель на имя файла (z-строка) ; HL = размер файла (младшее слово) ; DE = размер файла (старшее слово) ; IX = указатель на структуру активной панели (нужен WC API) ;------------------------------------------------------------------------------ .area _CODE _plugin_entry:: ;-------------------------------------------------------------- ; Сохранить параметры WC на стеке: gsinit обнулит _DATA, ; поэтому нельзя записывать в глобалы до инициализации. ;-------------------------------------------------------------- push hl ; size_lo push de ; size_hi push bc ; file_name ptr ex af, af' push af ; file_ext (в A) ex af, af' ;--- Сохранить HL'/DE' (номер объекта / кол-во файлов) --- exx push hl ; file_idx (HL') push de ; file_count (DE') exx ;-------------------------------------------------------------- ; Инициализировать C runtime (обнулить _DATA, ; скопировать начальные значения в _INITIALIZED) ;-------------------------------------------------------------- call gsinit ;-------------------------------------------------------------- ; Восстановить параметры WC из стека в (теперь чистые) глобалы ;-------------------------------------------------------------- pop de ; file_count (DE') ld (_wc_file_count), de pop hl ; file_idx (HL') ld (_wc_file_idx), hl pop af ld (_wc_file_ext), a pop bc ld (_wc_file_name), bc pop de ; size_hi pop hl ; size_lo ld (_wc_file_size+0), hl ld (_wc_file_size+2), de ; Вызвать main() call _main ; main() вернула void, код выхода — в _wc_exit_code ld a, (_wc_exit_code) ; IX не менялся — просто возврат ret ;------------------------------------------------------------------------------ ; Глобальные переменные параметров WC (читаются из main.c) ;------------------------------------------------------------------------------ .area _DATA _wc_file_ext:: .db 0 _wc_file_name:: .dw 0 _wc_file_size:: .dw 0, 0 ; 32-bit LE _wc_exit_code:: .db 0 _wc_file_idx:: .dw 0 ; HL': номер объекта в панели (от 1) _wc_file_count::.dw 0 ; DE': кол-во объектов в панели (вкл. "..") ;------------------------------------------------------------------------------ ; Инициализация C runtime (SDCC: _GSINIT выполняется при старте) ; ; 1) Обнулить _DATA — все глобальные/статические переменные без ; явного инициализатора (аналог BSS). ; 2) Скопировать _INITIALIZER → _INITIALIZED — переменные с явным ; начальным значением (static uint8_t x = 5; и т.п.). ;------------------------------------------------------------------------------ .area _GSINIT gsinit:: ; ── 1. Обнулить _DATA ──────────────────────────────────────── ld bc, #l__DATA ld a, b or a, c jr z, 00001$ ; l__DATA == 0 → нечего обнулять ld hl, #s__DATA ld (hl), #0 ; первый байт = 0 dec bc ld a, b or a, c jr z, 00001$ ; l__DATA == 1 → уже готово ld d, h ld e, l inc de ; DE = s__DATA + 1 ldir ; заполнить остаток нулями 00001$: ; ── 2. Скопировать _INITIALIZER → _INITIALIZED ────────────── ld bc, #l__INITIALIZER ld a, b or a, c jr z, 00002$ ; нет инициализированных данных ld hl, #s__INITIALIZER ; источник (в ROM / code page 1) ld de, #s__INITIALIZED ; приёмник (в RAM / _DATA area) ldir 00002$: .area _GSFINAL gsfinal:: ret