Files
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

83 lines
5.2 KiB
C
Raw Permalink 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.h — пользовательские обработчики кадрового прерывания (IM 2).
*
* Sprinter шлёт вектор 0xFF от трёх источников: кадр (50 Гц),
* клавиатура (бит 0 порта 0x19), CBL/COVOX. Штатно DSS работает в
* IM 1 (обработчик 0x0038). Установка обработчика переключает CPU в
* IM 2 со своей таблицей и зовёт handler'ы ТОЛЬКО на кадровых
* прерываниях; после них (и на всех прочих источниках) управление
* всегда передаётся штатному обработчику DSS на 0x0038 — клавиатура,
* SYSTIME и курсор мыши продолжают работать. irq_remove() возвращает
* IM 1/I как было (и вызывается автоматически из exit).
*
* ЦЕПОЧКА: можно поставить до IRQ_CHAIN_MAX независимых обработчиков
* (напр. свой тик + FPS-делитель gfx_set_fps_div). Порядок вызова =
* порядок слотов; после remove/add взаимный порядок НЕ гарантируется
* (переиспользуется первая дыра) — обработчики обязаны быть независимы.
*
* ПАМЯТЬ: работает во ВСЕХ режимах (tiny/small/big/huge) — трамплин
* при коде в W1 копируется в W2 и восстанавливает базовую W1-страницу
* вокруг вызова (docs/im2_isr_design.md). Единственное исключение:
* в small для очень мелких программ данные могут ещё лежать в W1 —
* тогда установка вернёт -1/EINVAL (перейти на huge или увеличить
* размер). Обработчик может быть обычной C-функцией где угодно.
*
* Правила для handler'а (вызывается ~50 раз/с, все регистры
* сохраняются снаружи):
* МОЖНО: volatile-глобалы, арифметика, короткая работа (<1 мс);
* НЕЛЬЗЯ: ESTEX/BIOS-вызовы (printf/malloc/файлы), gfx_* и любой
* своп W1/W3, акселератор, banked-функции, longjmp наружу.
*/
#ifndef IRQ_H
#define IRQ_H
#include <stdint.h>
typedef void (*isr_t)(void);
/* Максимум одновременных кадровых обработчиков в цепочке. */
#define IRQ_CHAIN_MAX 4
/* Добавить обработчик кадрового прерывания в цепочку.
* 0 — OK; -1 + errno: EINVAL (handler==NULL или данные не в W2),
* ENOMEM (все IRQ_CHAIN_MAX слотов заняты). */
int irq_chain_add(isr_t handler);
/* Снять ОДИН обработчик из цепочки (по указателю). Идемпотентно;
* освобождение последнего слота возвращает IM 1/I штатному DSS. */
void irq_chain_remove(isr_t handler);
/* Совместимость: irq_install == irq_chain_add (EBUSY больше не бывает —
* несколько обработчиков легальны; при заполнении слотов — ENOMEM). */
int irq_install(isr_t handler);
/* Снять ВСЮ цепочку и вернуть IM2-таблицу DSS. Безопасно звать
* повторно; вызывается автоматически при exit(). Для точечного
* снятия — irq_chain_remove. */
void irq_remove(void);
/* ---- CTC-таймер (Z84C015, вектор 0x06 — отдельный от кадрового) ---
*
* Канал 2 тактируется 875 кГц (1 тик = 1 знакоместо видеотракта) и
* делит; канал 3 считает от канала 2 и прерывает. Частота handler'а:
* f = 875000 / (div2 * div3), div 0 = 256
* (диапазон ~13 Гц .. десятки кГц; на частотах выше пары кГц handler
* должен быть ОЧЕНЬ коротким). Ставится независимо от irq_install —
* можно иметь оба. Ограничения для handler'а те же, что выше.
* Обработчик завершается RETI (в трамплине) — daisy chain Z84C015. */
int irq_ctc_install(isr_t handler, uint8_t div2, uint8_t div3);
void irq_ctc_remove(void); /* глушит CTC; тоже вызывается из exit() */
/* Пресет точного кадра: 112 тактов (2 пикс. линии) * 160 = 320 линий,
* ~48.8 Гц, начало кадра — «vsync без примеси клавиатуры». */
#define IRQ_CTC_VSYNC_DIV2 112
#define IRQ_CTC_VSYNC_DIV3 160
/* Скобки для доступа main-кода к данным, разделяемым с ISR. */
#define IRQ_DISABLE() __asm__("di")
#define IRQ_ENABLE() __asm__("ei")
#endif