/* * _irq_tramp — вход IM2-прерывания (вектор 0xFF: кадр/клавиатура/CBL), * версия с ЦЕПОЧКОЙ кадровых обработчиков и W1-remap для all-modes. * * Разбор источника (трогаем только AF, пока не выяснили): * - клавиатура (бит 0 порта 0x19 = SIO-A RR0) → сразу к DSS, БЕЗ * remap (W1 не трогаем — прерванный контекст сохранён как есть); * - CBL (бит 7 порта 0xFE) — только при зарегистрированном хуке * _irq_cbl_hook (иначе бит подтянут к 1); личное прерывание, RETI * без чейна к DSS; * - кадровое: проход по _irq_chain[] — все ненулевые слоты по порядку. * * W1-remap (docs/im2_isr_design.md «Вариант работает во всех режимах»): * в small/huge базовый код приложения (и хендлеры) в W1, а сам трамплин * во время прерывания исполняется из W2-КОПИИ (_irq_table_ref копирует * этот код в _irq_tramp_w2buf, т.к. оригинал в W1 может быть перемаплен * DSS). Перед вызовом хендлеров копия сохраняет текущую W1-страницу и * ставит базовую страницу приложения (_irq_app_w1_page), после — * возвращает сохранённую (перед chain к DSS / перед RETI). Единый * «restore W1=base» покрывает хендлер в W1, в W2 и на стыке 0x8000. * В tiny/big remap безвреден (хендлеры в W2, W1 всё равно восстановлен). * * COPY-SAFE: все внутренние переходы — jr/djnz (относительные); * единственный call — к _irq_call_hl (исполняется при W1=base, значит * оригинал в W1 достижим); оба jp — литерал 0x0038 (W0, всегда * замаплен). Никаких абсолютных само-ссылок → байты копируются как * есть, без релокации. ВНИМАНИЕ: не добавлять `jp <метка>` внутрь * трамплина — сломает копию. */ #include "_irq.h" void _irq_tramp(void) __naked { __asm push af ; сохранить прерванный AF in a, (#_IRQ_SIO_A) rrca ; бит 0 — клавиатурный байт принят? jr NC, tr_notkbd ;; --- клавиатура: прямо к DSS, без remap --- pop af .db 0xC3 ; jp nn __irq_dss_target:: .dw 0x0038 ; SMC-операбельно, но по умолчанию 0x0038 tr_notkbd: ;; --- CBL? --- старший байт хука ненулевой у любого W2-адреса. ld a, (__irq_cbl_hook + 1) or a, a jr Z, tr_frame in a, (#0xFE) rlca ; бит 7 — запрос блока CBL? jr NC, tr_frame ;; --- CBL: remap-скобка + хук + приватный RETI --- in a, (#_IRQ_W1_PORT) ; сохранить прерванную W1 ld (__irq_saved_w1), a ld a, (__irq_app_w1_page) out (#_IRQ_W1_PORT), a ; W1 = базовая страница приложения push bc push de push hl .db 0x08 ; ex af,af (теневой AF) push af exx push bc push de push hl push ix push iy ld hl, (__irq_cbl_hook) call _irq_call_hl pop iy pop ix pop hl pop de pop bc exx pop af .db 0x08 ; ex af,af pop hl pop de pop bc ld a, (__irq_saved_w1) ; вернуть прерванную W1 out (#_IRQ_W1_PORT), a pop af ei reti tr_frame: ;; --- кадровое: цепочка обработчиков --- ld a, (__irq_chain_n) or a, a jr Z, tr_frame_exit ; нет хендлеров → к DSS без remap/сейва in a, (#_IRQ_W1_PORT) ; remap-скобка: сохранить + база ld (__irq_saved_w1), a ld a, (__irq_app_w1_page) out (#_IRQ_W1_PORT), a ;; полный сейв обоих наборов + индексные (хендлеры клобберят всё) push bc push de push hl .db 0x08 push af exx push bc push de push hl push ix push iy ;; проход по слотам: HL идёт по _irq_chain, B = счётчик слотов ld hl, #__irq_chain ld b, #IRQ_CHAIN_MAX tr_loop: ld e, (hl) inc hl ld d, (hl) inc hl ld a, d or a, e jr Z, tr_skip ; пустой слот push bc ; сохранить счётчик + указатель цепи push hl ex de, hl ; HL = адрес хендлера call _irq_call_hl pop hl pop bc tr_skip: djnz tr_loop pop iy pop ix pop hl pop de pop bc exx pop af .db 0x08 pop hl pop de pop bc ld a, (__irq_saved_w1) ; вернуть прерванную W1 перед chain out (#_IRQ_W1_PORT), a tr_frame_exit: pop af .db 0xC3 ; jp 0x0038 (литерал — copy-safe) .dw 0x0038 _irq_call_hl: jp (hl) __irq_tramp_end:: __endasm; }