Files
Sprinter-SDCC/libc/irq/_irq_tramp.c
T
snark13 2c6f4e33c3 libc/irq: цепочка кадровых обработчиков + all-modes W1-remap
_irq_user → _irq_chain[4]+_irq_chain_n (W2-BSS); трамплин tr_frame
проходит слоты (один тяжёлый сейв на всю цепь, пустой слот пропуск).
API irq_chain_add (0/-1+ENOMEM) / irq_chain_remove(h); irq_install →
обёртка chain_add (EBUSY исчез), irq_remove() рвёт всю цепь; refcount
таблицы на первом/последнем слоте, мутации под IRQ_DISABLE. Лимит
4 кадровых + 1 CTC.

All-modes: трамплин copy-safe (только jr/djnz + литерал jp 0x0038),
_irq_table_ref копирует его в _irq_tramp_w2buf (W2) когда оригинал в W1
(small/huge), вектор → на копию; вокруг вызова хендлеров восстанавливает
базовую W1-страницу (_irq_app_w1_page = IN 0xA2). Хендлер может лежать
где угодно в плоском 0x4000-0xBFFF.

Проверено в MAME (tests/irqtest, 2 хендлера): tiny/big/huge — chain
h1=h2 → remove h2 → h1 жив/h2=0; huge = код в W1, remap работает.
Follow-up: CTC в small/huge = EINVAL (нужна W2-копия _irq_ctc_tramp);
small для мелких программ (BSS в W1) = irq_install EINVAL, safe.

Доки: im2_isr_design (цепочка), sprite-api §9е (делитель поверх цепи),
fast_ram §8. rpgprof: gfx_sprite_ysort в профиль (+230 Б baseline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:23:39 +03:00

157 lines
6.2 KiB
C
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.
/*
* _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;
}