irq: Phase 2a — CTC-таймер на отдельном векторе 0x06 (Z84C015)
- irq_ctc_install(handler, div2, div3) / irq_ctc_remove: канал 2 CTC делит видеотакт 875 кГц (1 тик = 1 знакоместо), канал 3 считает от него и прерывает; f = 875000/(div2*div3), div 0 = 256. Пресет IRQ_CTC_VSYNC_DIV2/3 = 112*160 — точное начало кадра ~48.8 Гц БЕЗ примеси клавиатуры (вектор 0x06 отделён от общего 0xFF) - CTC-трамплин: полный сейв -> handler -> EI/RETI; RETI обязателен (daisy chain Z84C015 снимает IUS только по опкоду RETI); к DSS не чейнится — личное прерывание - общая IM2-таблица под счётчиком ссылок (_irq_table.c): кадровый и CTC-хендлеры независимы, последний unref возвращает I/IM 1; atexit-уборка глушит CTC обязательно (иначе кГц-прерывания душат шелл после выхода) - порты/слова по docs/samples: CH0=0x10/CH2=0x12/CH3=0x13, 0x57/0xD7/вектор в CH0, стоп 0x03 - irqtest: CTC-vsync параллельно с кадровым + произвольная частота; MAME: frame 48 Гц, ctc(vsync) 49 Гц (parallel frame жив), ctc(50x50) 350 Гц точно по формуле, remove/выход чистые Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+38
-14
@@ -4,15 +4,21 @@
|
||||
* Механика (docs/im2_isr_design.md):
|
||||
* - таблица векторов: 257 байт с выравниванием 256 внутри статического
|
||||
* буфера 513 байт (_irq_state.c), заполняется байтом H = старший
|
||||
* байт её же адреса; CPU по вектору V читает адрес (H<<8|H) —
|
||||
* трёхбайтовый `jp _irq_tramp` лежит ВНУТРИ таблицы по смещению H
|
||||
* (безопасно: внешний вектор — 0xFF, а H < 0xC0; заливка H
|
||||
* перехватывает и вектора SIO/CTC, если их кто-то включит);
|
||||
* - трамплин (_irq_tramp.c): AF-пробник порта 0x19 (SIO-A RR0);
|
||||
* кадровое прерывание → полный save → user handler → restore;
|
||||
* затем ВСЕГДА jp на обработчик DSS 0x0038 (DSS работает в IM 1;
|
||||
* SMC-операнд патчится при install) — EI/RETI делает DSS;
|
||||
* - install: I := H, IM 2; remove: I := старое, IM 1 (штатный режим DSS).
|
||||
* байт её же адреса; 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
|
||||
@@ -24,14 +30,32 @@
|
||||
#define _IRQ_VEC_BUF_SIZE (256 + 257)
|
||||
|
||||
extern uint8_t _irq_vec_buf[_IRQ_VEC_BUF_SIZE];
|
||||
extern isr_t _irq_user; /* NULL = не установлен */
|
||||
extern uint8_t _irq_old_i; /* I до install */
|
||||
extern isr_t _irq_user; /* кадровый handler; NULL = нет */
|
||||
extern isr_t _irq_ctc_user; /* CTC-handler; NULL = нет */
|
||||
extern uint8_t _irq_old_i; /* I до захвата таблицы */
|
||||
extern uint8_t _irq_refs; /* счётчик пользователей таблицы */
|
||||
|
||||
/* Вход трамплина (asm, _irq_tramp.c). */
|
||||
/* Порты встроенной периферии 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 */
|
||||
|
||||
/* Захватить/отпустить 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);
|
||||
|
||||
/* SMC-операнд `jp` на старый DSS-обработчик — метка внутри трамплина,
|
||||
* пишется из irq_install (16-битный адрес). */
|
||||
/* SMC-операнд `jp` на обработчик DSS (метка в _irq_tramp.c). */
|
||||
extern uint16_t _irq_dss_target;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user