0280b05933
Разгрузка W1/W2 под будущий ИИ стражей: _kbdraw_down был БАЙТОМ на
скан-код (512 Б в _DATA при 32-килобайтной раскладке). Теперь бит на
код: код>>3 = байт, код&7 = бит, расширенные (префикс 0xE0) — смещение
+32 байта вместо +256.
Трамплин прерывания строит маску СДВИГОМ, а не таблицей: таблица
потребовала бы `ld hl,#метка` внутри трамплина, а он копируется в W2
побайтно и обязан быть без абсолютных само-ссылок (см. его шапку).
Маска строится в BC, поэтому в клавиатурной ветке добавлен push/pop bc.
Трамплин вырос 244 -> 267 Б, буфер копии поднят 320 -> 336 (запас 69 Б).
Проверено в MAME на roomtest, все три класса клавиш:
- обычные: '=' (обход комнат) и 'K' (чит-убийство стража — читал
guardhp_curr/delta: 3/0 -> 0/-3);
- расширенные (E0): стрелка вправо — Kid добежал до края комнаты;
- модификаторы: удержание Shift ставит бит 2 байта 2 карты
(скан-код 0x12), отпускание снимает.
Скорость: кадр 334 716 -> 338 508 тактов (+1.1 %) на битовой арифметике
в kbd_raw_down (~15 вызовов за кадр); при бюджете 430 000 это 0.79
периода вместо 0.78 — регрессии нет.
Итог по roomtest: данные 4422 -> 4022 Б, куча W2 996 -> 1294 Б.
Эталон размеров принят заново (make size-baseline): _CODE десяти
программ вырос на 14-23 Б — это код битовой арифметики в трамплине и
kbd_raw_down, обмен на -448 Б данных, которые size_check не считает.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96 lines
5.5 KiB
C
96 lines
5.5 KiB
C
/*
|
||
* _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
|