Files
Sprinter-SDCC/libc/irq/_irq_table.c
T
snark13 8a952b99eb 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>
2026-07-07 09:15:04 +03:00

116 lines
3.1 KiB
C
Raw 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_table — жизненный цикл общей IM2-таблицы (счётчик ссылок).
*
* Первый ref: проверка W2-адресов, построение таблицы (заливка H +
* jp _irq_tramp по смещению H), I := H, IM 2, atexit-уборка.
* Последний unref: I := старое, IM 1 (штатный режим DSS — его
* обработчик на 0x0038; см. docs/samples/sprinterIntLib.asm).
*/
#include <string.h>
#include <errno.h>
#include <sprinter_exit.h>
#include "_irq.h"
/* Прочитать регистр I. */
static uint8_t irq_get_i(void) __naked
{
__asm
ld a, i
ret
__endasm;
}
/* I := A, режим IM 2. */
static void irq_set_i_im2(uint8_t i_val) __naked
{
(void)i_val;
__asm
;; __sdcccall(1): uint8_t-аргумент уже в A.
ld i, a
im 2
ret
__endasm;
}
/* I := A, штатный IM 1 (DSS). */
static void irq_set_i_im1(uint8_t i_val) __naked
{
(void)i_val;
__asm
ld i, a
im 1
ret
__endasm;
}
/* Уборка на exit: снять оба хендлера (CTC глушится обязательно —
* иначе после выхода его прерывания задушат шелл). */
static void irq_atexit(void)
{
irq_ctc_remove();
irq_remove();
}
uint8_t *_irq_table(void)
{
return (uint8_t *)(((uint16_t)((uint16_t)_irq_vec_buf + 255u))
& 0xFF00u);
}
int _irq_table_ref(void)
{
if (_irq_refs) {
_irq_refs++;
return 0;
}
/* Трамплины и таблица обязаны жить в W2 (0x8000-0xBFFF): во время
* прерывания W1/W3 могут быть перемаплены. CODE вне W2 =
* неподходящий memory mode (small/huge). */
uint16_t tramp = (uint16_t)&_irq_tramp;
uint16_t buf = (uint16_t)_irq_vec_buf;
if (tramp < 0x8000 || tramp >= 0xC000 ||
buf < 0x8000 || buf + _IRQ_VEC_BUF_SIZE > 0xC000) {
errno = EINVAL;
return -1;
}
uint8_t *table = _irq_table();
uint8_t h = (uint8_t)((uint16_t)table >> 8);
IRQ_DISABLE();
/* Старое I — только чтобы вернуть регистр при unref: DSS работает
* в IM 1 (I=0x3F — наследие Spectrum ROM, НЕ таблица). */
_irq_old_i = irq_get_i();
_irq_dss_target = 0x0038;
memset(table, h, 257);
uint8_t *stub = table + h; /* h < 0xC0 — с [0xFF] не пересекается */
stub[0] = 0xC3; /* jp _irq_tramp */
stub[1] = (uint8_t)(tramp & 0xFF);
stub[2] = (uint8_t)(tramp >> 8);
irq_set_i_im2(h);
IRQ_ENABLE();
_irq_refs = 1;
static uint8_t atexit_armed;
if (!atexit_armed) {
atexit_armed = 1;
atexit(irq_atexit);
}
return 0;
}
void _irq_table_unref(void)
{
if (!_irq_refs) return;
if (--_irq_refs) return;
IRQ_DISABLE();
irq_set_i_im1(_irq_old_i);
IRQ_ENABLE();
}