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>
This commit is contained in:
+45
-15
@@ -1,30 +1,60 @@
|
||||
/*
|
||||
* sleep — заблокироваться на N секунд по кадровому прерыванию 50 Гц.
|
||||
* sleep — заблокироваться на N секунд калиброванным busy-wait'ом (см.
|
||||
* _sleep_calib.c — общий движок с delayms()).
|
||||
*
|
||||
* ISR Sprinter срабатывает 50 раз в секунду. `halt` паркует CPU до
|
||||
* следующего IRQ, так что 50 halt'ов = ~1 секунда стенного времени.
|
||||
* Тот же трюк, что у solid-c в IO.ASM:285.
|
||||
* Старая версия (50 halt = 1 c) предполагала, что КАЖДОЕ прерывание —
|
||||
* кадровый тик 50 Гц; с CBL/клавиатурой на векторе 0xFF это неверно
|
||||
* (CBL может прерывать в разы/десятки раз чаще кадра — sleep() вернулся
|
||||
* бы намного раньше срока).
|
||||
*
|
||||
* Замечание: требует включённых прерываний (по умолчанию включены —
|
||||
* ESTEX настраивает IM 1 с кадровым ISR до запуска нашей программы).
|
||||
* sleep(seconds) — вложенный цикл: внешний по секундам, внутренний
|
||||
* ровно 50 раз калиброванный _sleep_spin() — та же структура, что и в
|
||||
* старой версии (внешний секунды, внутренний 50×halt), только вместо
|
||||
* halt — калиброванный спин. Umножения/32-бит арифметики нет.
|
||||
*
|
||||
* Fallback: если фрейм-хук уже занят другим irq_install()-клиентом
|
||||
* (EBUSY) — калибровку сделать некорректно, откатываемся к старому
|
||||
* "50 halt ~ 1 c" поведению для этого конкретного вызова.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include "_sleep_calib.h"
|
||||
|
||||
void sleep(unsigned int seconds) __naked
|
||||
/* Старое поведение — фолбэк, если calibrate() не может безопасно
|
||||
* получить кадровый хук. */
|
||||
static void _sleep_legacy(unsigned int seconds) __naked
|
||||
{
|
||||
(void)seconds;
|
||||
__asm
|
||||
inter:
|
||||
;; HL = seconds на входе (единственный int-аргумент SDCC).
|
||||
_slg_outer:
|
||||
ld a, h
|
||||
or a, l
|
||||
ret Z ; sleep(0) — вернуться сразу
|
||||
ld b, #50 ; 50 halt-ов на секунду (50 Гц)
|
||||
inner:
|
||||
or l
|
||||
ret Z
|
||||
ld b, #50 ; 50 halt-ов ~ 1 c (при ЕДИНСТВЕННОМ
|
||||
; источнике прерываний на векторе 0xFF)
|
||||
_slg_inner:
|
||||
halt
|
||||
djnz inner
|
||||
djnz _slg_inner
|
||||
dec hl
|
||||
jr inter
|
||||
jr _slg_outer
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void sleep(unsigned int seconds)
|
||||
{
|
||||
if (seconds == 0) return;
|
||||
|
||||
if (!_sleep_units_per_frame) _sleep_calibrate();
|
||||
|
||||
if (!_sleep_units_per_frame) {
|
||||
_sleep_legacy(seconds);
|
||||
return;
|
||||
}
|
||||
|
||||
while (seconds--) {
|
||||
uint8_t frame = 50; /* 50 кадров ~ 1 с при 50 Гц */
|
||||
while (frame--) {
|
||||
_sleep_spin(_sleep_units_per_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user