/*
 * _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 <stdint.h>
#include <irq.h>

/* Буфер под таблицу: 256 (выравнивание) + 257 (таблица). */
#define _IRQ_VEC_BUF_SIZE (256 + 257)

/* Буфер под W2-копию кадрового трамплина (small/huge — _irq_tramp в
 * W1).  С запасом над реальным размером трамплина (267 Б после
 * упаковки held-карты клавиатуры в биты, 2026-07-30 — проверять
 * __irq_tramp_end при правках, запас держать >= 60 Б). */
#define _IRQ_TRAMP_BUF_SIZE 336

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
