/* * gfx_set_fps_div — задать делитель кадровой частоты (frame pacing). * n: логический кадр = РОВНО n кадровых интервалов (1=50 fps дефолт, * 2=25, 3=~16.7, 4=12.5, ...; 0 трактуется как 1). gfx_wait_vsync() * при n>=2 переключается со лучевого поллинга на счётчиковый путь * (docs/sprite-api-design.md §9е). * * n>=2: однократно ставит _gfx_frame_isr в ЦЕПОЧКУ кадровых IRQ * (irq_chain_add — свой слот, не мешает собственному хендлеру * приложения); повторные вызовы только меняют делитель. * n<=1: снимает ТОЛЬКО свой слот (irq_chain_remove, НЕ irq_remove — * чтобы не снести хендлер приложения) и возвращает старый путь. * * Возврат 0 / -1+errno: EINVAL (данные не в W2 — неподходящий memory * mode, из irq_chain_add), ENOMEM (все слоты цепи заняты). ТОЛЬКО этот * модуль ссылается на irq-механику и _gfx_frame_isr — программа без * вызова сеттера не линкует ни цепочку, ни ISR (DCE). */ #include "../_bgi.h" #include int gfx_set_fps_div(uint8_t n) { if (n < 2) { /* Выключение: снять свой слот, если ставили. */ if (_gfx_fps_div >= 2) { irq_chain_remove(_gfx_frame_isr); _gfx_fps_div = 1; } return 0; } /* Первое включение — занять слот цепи под свой ISR. */ if (_gfx_fps_div < 2) { if (irq_chain_add(_gfx_frame_isr) != 0) return -1; /* errno уже выставлен (EINVAL/ENOMEM) */ /* База отсчёта — с текущего тика, чтобы первый wait не «опоздал». */ _gfx_fps_last = _gfx_frame_tick; } _gfx_fps_div = n; return 0; }