Files
Sprinter-SDCC/tests/irqtest/irqtest.c
T
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

184 lines
5.6 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.
/*
* irqtest — smoke-тест IM2-модуля (Phase 1+2, docs/im2_isr_design.md).
*
* Проверяет:
* 1. кадровый handler тикает ~50 Гц (сверка с time() за 3 секунды);
* 1b. цепочка: второй обработчик (irq_chain_add) тикает синхронно с
* первым; irq_chain_remove снимает только его, первый жив;
* 2. клавиатура/SYSTIME живы при установленном handler'е (чейн к DSS);
* 3. CTC-таймер (вектор 0x06): пресет vsync ~49 Гц ОДНОВРЕМЕННО с
* кадровым; затем быстрый режим 875 кГц/(50*50) = 350 Гц;
* 4. irq_remove/irq_ctc_remove останавливают тики;
* 5. чистый выход в шелл (I/IM восстановлены, CTC заглушен).
*/
#include <stdio.h>
#include <time.h>
#include <conio.h>
#include <errno.h>
#include <irq.h>
static volatile uint16_t ticks;
static volatile uint16_t ticks2;
static volatile uint16_t ctc_ticks;
static void on_tick(void)
{
ticks++;
}
static void on_tick2(void)
{
ticks2++;
}
static void on_ctc(void)
{
ctc_ticks++;
}
/* Диагностика: регистр I до установки (что за режим у DSS?). */
static uint8_t get_i(void) __naked
{
__asm
ld a, i
ret
__endasm;
}
/* Busy-пауза без единого ESTEX-вызова (~симв. полсекунды). */
static void busy_wait(void)
{
for (volatile uint16_t i = 0; i < 30000u; i++) ;
}
/* Атомарное чтение 16-битного счётчика, разделяемого с ISR. */
static uint16_t read_ticks(void)
{
uint16_t v;
IRQ_DISABLE();
v = ticks;
IRQ_ENABLE();
return v;
}
int main(void)
{
/* --- диагностика ДО установки --- */
uint8_t old_i = get_i();
const uint8_t *vec = (const uint8_t *)(((uint16_t)old_i << 8) + 0xFF);
printf("I=%02x, [I:FF]=%02x%02x (DSS isr cand: %04x)\n",
old_i, vec[1], vec[0],
(unsigned)(vec[0] | ((uint16_t)vec[1] << 8)));
if (irq_install(on_tick) != 0) {
printf("irq_install failed: errno=%d\n", errno);
return 1;
}
puts("irq installed");
/* Фаза 1: busy-пауза БЕЗ ESTEX — жив ли трамплин/чейн вообще. */
busy_wait();
printf("phase1: ticks=%u (expect >0)\n", read_ticks());
puts("phase2: measuring 3 s vs time()...");
/* Выровняться на границу секунды (time() зовёт ESTEX SYSTIME в
* цикле — заодно стресс чейна к DSS). */
time_t t0 = time(0);
while (time(0) == t0) ;
uint16_t start = read_ticks();
time_t t1 = time(0);
while (time(0) < t1 + 3) ;
uint16_t got = read_ticks() - start;
printf("ticks: %u in 3 s (~%u Hz, expect ~50)\n", got, got / 3);
/* --- Phase 1b: цепочка — второй кадровый обработчик --- */
if (irq_chain_add(on_tick2) != 0) {
printf("chain_add failed: errno=%d\n", errno);
return 1;
}
puts("chain: 2nd handler added");
{
IRQ_DISABLE();
uint16_t a0 = ticks, b0 = ticks2;
IRQ_ENABLE();
time_t tc = time(0);
while (time(0) < tc + 2) ;
IRQ_DISABLE();
uint16_t da = ticks - a0, db = ticks2 - b0;
IRQ_ENABLE();
printf("chain both: h1=%u h2=%u in 2 s (both ~100, must match)\n",
da, db);
}
/* Снять ТОЛЬКО второй — первый обязан продолжать тикать. */
irq_chain_remove(on_tick2);
{
IRQ_DISABLE();
uint16_t a0 = ticks, b0 = ticks2;
IRQ_ENABLE();
time_t tc = time(0);
while (time(0) < tc + 1) ;
IRQ_DISABLE();
uint16_t da = ticks - a0, db = ticks2 - b0;
IRQ_ENABLE();
printf("chain after remove h2: h1=%u (>0) h2=%u (must be 0)\n",
da, db);
}
/* --- Phase 2: CTC-таймер одновременно с кадровым --- */
if (irq_ctc_install(on_ctc, IRQ_CTC_VSYNC_DIV2, IRQ_CTC_VSYNC_DIV3)) {
printf("irq_ctc_install failed: errno=%d\n", errno);
return 1;
}
{
time_t t3 = time(0);
while (time(0) == t3) ;
IRQ_DISABLE();
uint16_t c0 = ctc_ticks, f0 = ticks;
IRQ_ENABLE();
time_t t4 = time(0);
while (time(0) < t4 + 2) ;
IRQ_DISABLE();
uint16_t dc = ctc_ticks - c0, df = ticks - f0;
IRQ_ENABLE();
printf("ctc(vsync): %u in 2 s (~%u Hz), frame in parallel: %u\n",
dc, dc / 2, df);
}
irq_ctc_remove();
/* CTC на произвольной частоте: 875000/(50*50) = 350 Гц. */
if (irq_ctc_install(on_ctc, 50, 50)) {
printf("ctc fast install failed: errno=%d\n", errno);
return 1;
}
{
time_t t5 = time(0);
while (time(0) == t5) ;
IRQ_DISABLE();
uint16_t c0 = ctc_ticks;
IRQ_ENABLE();
time_t t6 = time(0);
while (time(0) < t6 + 2) ;
IRQ_DISABLE();
uint16_t dc = ctc_ticks - c0;
IRQ_ENABLE();
printf("ctc(50x50): %u in 2 s (~%u Hz, expect ~350)\n", dc, dc / 2);
}
irq_ctc_remove();
puts("press any key (keyboard must work)...");
getch();
irq_remove();
uint16_t frozen = read_ticks();
time_t t2 = time(0);
while (time(0) < t2 + 1) ;
if (read_ticks() == frozen) puts("remove: ticks frozen, OK");
else puts("remove: FAIL, still ticking");
puts("irqtest done.");
return 0;
}