/* * _irq.h — внутренности IM2-модуля (НЕ публичный заголовок). * * Механика (docs/im2_isr_design.md): * - таблица векторов: 257 байт с выравниванием 256 внутри статического * буфера 513 байт (_irq_state.c), заполняется байтом H = старший * байт её же адреса; CPU по внешнему вектору 0xFF читает адрес * (H<<8|H) — трёхбайтовый `jp _irq_tramp` лежит ВНУТРИ таблицы по * смещению H (H < 0xC0 — с [0xFF]/[0x100] не пересекается; заливка * H перехватывает и вектора SIO 0x10..0x1E, если их кто-то включит); * - таблица общая для кадрового и CTC-хендлеров — жизнью управляет * счётчик ссылок (_irq_table_ref/_irq_table_unref в _irq_table.c); * unref последнего возвращает I и IM 1 (штатный режим DSS); * - кадровый трамплин (_irq_tramp.c): AF-пробник порта 0x19 (SIO-A * RR0, бит 0 = клавиатурный байт); кадр → полный save → user * handler → restore; затем ВСЕГДА jp на обработчик DSS 0x0038 * (DSS работает в IM 1) — EI/RETI делает DSS; * - CTC-трамплин (_irq_ctc_tramp.c): вектор 0x06 (Z84C015, каналы * 2+3 в связке, база 875 кГц); полный save → handler → restore → * EI/RETI — RETI ОБЯЗАТЕЛЕН (daisy chain: без него CTC не пошлёт * следующее прерывание); к DSS НЕ чейнится (наше личное прерывание). */ #ifndef _IRQ_INTERNAL_H #define _IRQ_INTERNAL_H #include #include /* Буфер под таблицу: 256 (выравнивание) + 257 (таблица). */ #define _IRQ_VEC_BUF_SIZE (256 + 257) /* Буфер под W2-копию кадрового трамплина (small/huge — _irq_tramp в * W1). С запасом над реальным размером трамплина (225 Б после * FIFO-drain цикла в raw-клавиатурной ветке, 2026-07-16 — проверять * __irq_tramp_end при правках, запас держать ≥ 60 Б). */ #define _IRQ_TRAMP_BUF_SIZE 288 extern uint8_t _irq_vec_buf[_IRQ_VEC_BUF_SIZE]; /* Цепочка кадровых обработчиков (см. _irq_state.c, docs/im2_isr_design). */ extern isr_t _irq_chain[IRQ_CHAIN_MAX]; extern uint8_t _irq_chain_n; /* число занятых слотов */ extern isr_t _irq_ctc_user; /* CTC-handler; NULL = нет */ extern uint8_t _irq_old_i; /* I до захвата таблицы */ extern uint8_t _irq_refs; /* счётчик пользователей таблицы */ /* W1-remap (all-modes): базовая W1-страница приложения + сохранённая * страница на входе в прерывание; W2-копия трамплина. */ extern uint8_t _irq_app_w1_page; extern uint8_t _irq_saved_w1; extern uint8_t _irq_tramp_w2buf[_IRQ_TRAMP_BUF_SIZE]; /* Порт-регистр окна W1 (страница). */ #define _IRQ_W1_PORT 0xA2 /* Хук CBL-насоса (libc/cbl регистрирует _cbl_pump). Косвенность * сознательная: прямая ссылка трамплина на cbl-модуль притянула бы * его (и 2 КБ кольца) в каждую irq-программу. NULL = CBL выключен, * трамплин НЕ проверяет бит 7 порта 0xFE (он подтянут к 1). */ extern isr_t _irq_cbl_hook; /* Порты встроенной периферии Z84C015 (docs/samples/SIO_CTC_KEY.asm). */ #define _IRQ_CTC_CH0 0x10 #define _IRQ_CTC_CH2 0x12 #define _IRQ_CTC_CH3 0x13 #define _IRQ_SIO_A 0x19 /* SIO-A RR0: бит 0 = Rx available */ #define _IRQ_SIO_DATA 0x18 /* SIO-A data — деструктивное чтение * (дренирует байт); читает либо DSS * (штатно), либо raw-канал клавиатуры * (libc/kbd/_kbdraw.h), но не оба сразу */ /* Захватить/отпустить IM2-таблицу (счётчик ссылок). ref: при первом * захвате строит таблицу, ставит I/IM2, взводит atexit-уборку; * 0 или -1+errno (EINVAL — код/буфер не в W2). unref: при последнем * отпускании возвращает I и IM 1. */ int _irq_table_ref(void); void _irq_table_unref(void); /* Адрес выровненной таблицы (валиден при _irq_refs > 0). */ uint8_t *_irq_table(void); /* Входы трамплинов (asm). */ void _irq_tramp(void); void _irq_ctc_tramp(void); /* Метка конца кадрового трамплина — для вычисления его размера при * копировании в W2 (_irq_table_ref). Объявлена как массив, чтобы * можно было взять адрес без вызова. */ extern uint8_t _irq_tramp_end[]; /* SMC-операнд `jp` на обработчик DSS (метка в _irq_tramp.c). */ extern uint16_t _irq_dss_target; #endif