/* * irq_ctc_install — периодический user-таймер на CTC Z84C015 * (каналы 2+3 в связке, вектор 0x06 — ОТДЕЛЬНЫЙ от кадрового 0xFF). * * Канал 2 тактируется видеотактом 875 кГц (1 тик = 1 знакоместо) и * работает делителем; канал 3 считает от канала 2 и генерирует * прерывание. Частота = 875000 / (div2 * div3), div 0 = 256. * Пресет кадра: 112 * 160 (2 пиксельные линии * 160 = 320 линий) — * точное начало кадра ~48.8 Гц без примеси клавиатуры. * * Программирование (docs/samples/sprinterIntLib.asm, SIO_CTC_KEY.asm; * значения control-слов «надо читать доку по z84c15»): * 0x57 -> CH2, div2 — счётчик, без прерывания * 0xD7 -> CH3, div3 — счётчик, прерывание разрешено * 0x00 -> CH0 — базовый вектор блока CTC = 0 (ch3 -> 0x06) */ #include #include "_irq.h" static uint8_t ctc_div2; static uint8_t ctc_div3; static void ctc_program(void) __naked { __asm ld a, #0x57 ; CH2: counter, int off, TC следом out (#_IRQ_CTC_CH2), a ld a, (_ctc_div2) out (#_IRQ_CTC_CH2), a ld a, #0xD7 ; CH3: counter, int on, TC следом out (#_IRQ_CTC_CH3), a ld a, (_ctc_div3) out (#_IRQ_CTC_CH3), a xor a, a ; базовый вектор CTC = 0 out (#_IRQ_CTC_CH0), a ret __endasm; } int irq_ctc_install(isr_t handler, uint8_t div2, uint8_t div3) { if (!handler) { errno = EINVAL; return -1; } if (_irq_ctc_user) { errno = EBUSY; return -1; } /* CTC-трамплин вектрится напрямую (table[0x06] = &_irq_ctc_tramp) в * обход W2-копии кадрового пути — поэтому CTC пока требует код в W2 * (tiny/big). All-modes для CTC (копия _irq_ctc_tramp) — follow-up. */ if ((uint16_t)&_irq_ctc_tramp < 0x8000) { errno = EINVAL; return -1; } if (_irq_table_ref() != 0) return -1; uint16_t tramp = (uint16_t)&_irq_ctc_tramp; uint8_t *table = _irq_table(); IRQ_DISABLE(); _irq_ctc_user = handler; /* Вектор 0x06 — обычная (не same-byte) запись в таблицу. */ table[0x06] = (uint8_t)(tramp & 0xFF); table[0x07] = (uint8_t)(tramp >> 8); ctc_div2 = div2; ctc_div3 = div3; ctc_program(); IRQ_ENABLE(); return 0; }