Files
Sprinter-SDCC/libbgi/common/_gfx_fps_state.c
T
snark13 1b4fbeaa6b libbgi: FPS-делитель gfx_set_fps_div(n) поверх цепочки кадровых IRQ
Логический кадр = ровно n кадровых интервалов (1=50/2=25/3=~16.7 fps);
при переполнении слота — выравнивание на ближайший фронт (без дрейфа
фазы, в отличие от наивного «жди n фронтов»).

Механика: фоновый счётчик _gfx_frame_tick инкрементит _gfx_frame_isr,
поставленный в СВОЙ слот цепи (irq_chain_add); gfx_set_fps_div(1) снимает
только этот слот (irq_chain_remove), не трогая хендлер приложения.
gfx_wait_vsync: ветка n>=2 (счётчик + halt) перед лучевым поллингом;
поллинг вынесен в static gfx_wait_vsync_beam (функция с хвостовым __asm
не должна иметь переходов через asm — SDCC не эмитит эпилог-метку;
ранний return делителя в чистом-C gfx_wait_vsync).

Файлы: common/_gfx_fps_state.c (данные), _gfx_frame_isr.c (ISR),
gfx_set_fps_div.c (сеттер, единственная ссылка на irq-механику → DCE).
Работает tiny/big/huge (цепочка all-modes); small для мелких программ
= EINVAL.

Проверено MAME (tests/fpsdiv): n=1/2/3 → 20/40/60 кадров на 20 wait'ов
(drift=0); n=2 с рендер-заглушкой ~1 кадр → период держится 2
(поглощение перерасхода, наивный путь дал бы ~60); huge идентично;
small = EINVAL graceful.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:36:37 +03:00

22 lines
1.2 KiB
C

/*
* _gfx_fps_state — данные FPS-делителя (frame pacing, docs/sprite-api-
* design.md §9е). Только данные; НЕ инициализировать (crt0 зануляет
* _DATA — див=0 трактуется как «делитель выключен»).
*
* _gfx_fps_div — n: логический кадр = n кадровых интервалов; 0/1 =
* старый лучевой поллинг gfx_wait_vsync (путь не активен);
* _gfx_frame_tick— счётчик кадров, инкрементит _gfx_frame_isr; вся
* арифметика ожидания разностная по модулю 256 (wrap
* безопасен);
* _gfx_fps_last — значение счётчика на прошлом выходе из wait (база
* отсчёта интервалов).
*
* Модуль отделён от сеттера/ISR/wait, чтобы программа без вызова
* gfx_set_fps_div() тянула только эти 3 байта, но не irq-механику.
*/
#include "../_bgi.h"
uint8_t _gfx_fps_div;
volatile uint8_t _gfx_frame_tick;
uint8_t _gfx_fps_last;