Files
Sprinter-SDCC/libc/gfx/gfx_wait_vsync.c
T
snark13 46bd9ad0f1 gfx/time: vsync через polling бита кадра, sleep/delayms без halt-подсчёта
gfx_wait_vsync() и sleep() раньше предполагали, что КАЖДОЕ прерывание
на векторе 0xFF — кадровый тик; с CBL/клавиатурой на том же векторе
это уже не так.

gfx_wait_vsync(): вместо halt — polling бита 5 порта 0xFE (реальная
позиция луча, см. MAME kbd_fe_r), доступного пока включён CBL bit7
порта 0x004E. Разделяемое владение портом с CBL через
_cbl_port_ref/unref (тот же ref-counting паттерн, что у IM2-таблицы) —
cbl_close() возвращает "немой" режим вместо полного выключения, если
gfx ещё держит ссылку. Fallback на halt при таймауте.

sleep()/delayms(): калиброванный busy-wait по духу delayms.asm вместо
подсчёта halt-пробуждений. Калибровка одна на кадр (не на секунду —
не переполняет uint16_t и не требует умножения/32-бит арифметики),
общий движок libc/time/_sleep_calib.c для обеих функций. Fallback на
старое поведение при EBUSY (фрейм-хук занят другим irq_install()).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 10:47:57 +03:00

74 lines
2.8 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.
/*
* gfx_wait_vsync — ждать переход бита 5 порта 0xFE из 1 в 0: выход из
* нижнего blank/border (Y > 256 — см. MAME sprinter.cpp kbd_fe_r:
* `vpos() >= BORDER_TOP+SCREEN_YSIZE`) — начало нового кадра, идеальный
* момент для свопа страниц без разрывов. Это чтение РЕАЛЬНОГО
* состояния луча (не событие/прерывание), поэтому не подвержено
* путанице с другими источниками вектора 0xFF (клавиатура, CBL, CTC) —
* подробнее см. memory/gfx_wait_vsync_redesign.
*
* Бит доступен ТОЛЬКО пока включён cbl_mode() (bit7 порта 0x004E) —
* см. libc/cbl/_cbl_port.c: если приложение уже играет через
* cbl_open(), берём бит бесплатно (порт не трогаем); иначе
* лениво занимаем bit7 "немым" кодом частоты через _cbl_port_ref()
* при первом вызове, освобождаем в atexit.
*
* Fallback: если ни разу не увидели ожидаемое состояние за разумное
* число попыток (иное железо, бит ведёт себя не как в MAME) —
* откатываемся на одно кадровое прерывание (halt), чтобы не
* зависнуть намертво.
*/
#include "_gfx.h"
#include "../cbl/_cbl.h"
#include <sprinter_exit.h>
static uint8_t vsync_ready;
static void vsync_cleanup(void)
{
_cbl_port_unref();
}
void gfx_wait_vsync(void)
{
if (!vsync_ready) {
vsync_ready = 1;
_cbl_port_ref();
atexit(vsync_cleanup);
}
__asm
;; --- фаза 1: дождаться bit5==1 (гарантированно в blank) ---
ld bc, #0 ; ~65536 попыток на фазу таймаут
_gwv_wait1:
in a, (#0xFE)
bit 5, a
jr nz, _gwv_have1
dec bc
ld a, b
or c
jr nz, _gwv_wait1
jr _gwv_fallback ; bit5 ни разу не встал за таймаут
_gwv_have1:
;; --- фаза 2: дождаться bit5==0 (сам переход начало кадра) ---
ld bc, #0
_gwv_wait0:
in a, (#0xFE)
bit 5, a
jr z, _gwv_done
dec bc
ld a, b
or c
jr nz, _gwv_wait0
;; таймаут и здесь переходим к тому же фолбэку
_gwv_fallback:
ei
halt ; аварийный путь: одно кадровое прерывание
_gwv_done:
__endasm;
}