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:
+63
-2
@@ -88,11 +88,50 @@ in-place регистр, латиница + кириллица CP866.
|
||||
| `int isatty(fd)` | fd <= 0 (файловые манипуляторы DSS с 1) |
|
||||
| `int mkdir/rmdir/chdir(path)` | ESTEX $1B/$1C/$1D |
|
||||
| `char *getcwd(buf, size)` | буфер 256 байт, size игнорируется |
|
||||
| `void sleep(seconds)` | 50 halt/с по кадровому IRQ |
|
||||
| `void sleep(seconds)` | калиброванный busy-wait (см. ниже) |
|
||||
| `void delayms(uint16_t ms)` | то же, гранулярность — миллисекунды (см. ниже) |
|
||||
|
||||
**Лимит: 8 одновременных манипуляторов**; 9-й OPEN вешает DSS —
|
||||
libc отказывает сама (EMFILE, предохранитель _fd_guard).
|
||||
|
||||
**`sleep()` (2026-07-07, редизайн)**: старая версия считала halt-
|
||||
пробуждения (50 = 1 c), предполагая, что КАЖДОЕ прерывание — кадровый
|
||||
тик; с CBL/клавиатурой на векторе 0xFF это уже не так (CBL прерывает
|
||||
намного чаще кадра — sleep() возвращался бы раньше срока). Теперь:
|
||||
лениво, один раз калибруется кратковременным `irq_install()` против
|
||||
РЕАЛЬНОГО кадрового тика (трамплин зовёт хук только на настоящих
|
||||
кадровых прерываниях — клавиатура/CBL уходят в свои ветки раньше),
|
||||
считая, сколько итераций тесного цикла умещается в один КАДР (не в
|
||||
секунду — калибровка на секунду переполняла `uint16_t`, был баг:
|
||||
`sleep(5)` отрабатывал быстрее секунды, найден пользователем на
|
||||
реальном прогоне и исправлен). `sleep(seconds)` — вложенный цикл:
|
||||
внешний по секундам, внутренний ровно 50 раз калиброванный busy-wait —
|
||||
без единого прерывания и БЕЗ умножения/32-битной арифметики (по духу
|
||||
`docs/samples/delayms.asm`, который тоже калибрует на 1 мс, а не на
|
||||
1 с). Погрешность ~5-10% (калибровочный и рабочий циклы не тактово-
|
||||
идентичны). Fallback на старое "50 halt" поведение, если фрейм-хук уже
|
||||
занят другим `irq_install()`-клиентом (EBUSY).
|
||||
**Cтоит дороже по размеру** (~+580 Б) — тянет весь модуль `irq`
|
||||
(install/remove/трамплин/IM2-таблицу), даже если программа больше
|
||||
ничего из irq не использует.
|
||||
|
||||
**`delayms(ms)`** — тот же движок (`libc/time/_sleep_calib.c`), общий с
|
||||
`sleep()`: калибровка одна на двоих (первый вызов ЛЮБОЙ из функций
|
||||
калибрует, вторая просто использует готовое). Производная величина
|
||||
"итераций на 1 мс" — одно 16-битное деление (`__divuint`, НЕ
|
||||
`__mullong`) на константу 20, вычисляется один раз при калибровке;
|
||||
`delayms(ms)` — простой цикл `ms` раз, без умножения вообще. Fallback
|
||||
при EBUSY — грубый (одно кадровое `halt`), точной альтернативы для
|
||||
миллисекундной гранулярности без калибровки нет.
|
||||
|
||||
**Квирк общий для sleep()/delayms()**: калибровка сама стоит ~20-40 мс
|
||||
(синхронизация на границу кадра + сам замер) — ПЕРВЫЙ вызов ЛЮБОЙ из
|
||||
двух функций в программе превысит запрошенное время на эту величину;
|
||||
для `delayms()` с маленьким `ms` это заметно (`delayms(5)` на первом
|
||||
вызове может растянуться на ~25-45 мс). Все последующие вызовы точны.
|
||||
Оставлено как есть по решению пользователя — не стали усложнять API
|
||||
отдельным calibrate()-примитивом.
|
||||
|
||||
## <errno.h>
|
||||
|
||||
`errno` (int), коды = коды DSS (EOK..EUNKERR) + POSIX-имена
|
||||
@@ -146,7 +185,20 @@ wrchar rdchar`; режимы `gettextmode/settextmode` (0x02=40×32,
|
||||
Setup: `gfx_init(mode,page)→prev`, `gfx_done(prev)`.
|
||||
Страницы/банк: `gfx_set/get_visible_page`, `gfx_set/get_draw_page`
|
||||
(double buffering), `gfx_set/get_bank` (0x50..0x5F, 0x58 = FF-
|
||||
прозрачность), `gfx_wait_vsync()`.
|
||||
прозрачность).
|
||||
|
||||
`gfx_wait_vsync()` (2026-07-07, редизайн): ждёт переход бита 5 порта
|
||||
0xFE из 1 в 0 — реальное аппаратное состояние луча (Y>256 → начало
|
||||
кадра, см. MAME sprinter.cpp kbd_fe_r), а не прерывание — поэтому не
|
||||
путается с клавиатурой/CBL/CTC, деляющими вектор 0xFF. Бит доступен
|
||||
только пока включён `cbl_mode()` (bit7 порта 0x004E) — если приложение
|
||||
уже играет через `cbl_open()`, бит достаётся бесплатно; иначе
|
||||
`gfx_wait_vsync()` лениво занимает bit7 "немым" кодом частоты через
|
||||
`_cbl_port_ref()`/`_cbl_port_unref()` (см. `<cbl.h>`, `_cbl_port.c`) —
|
||||
разделяемое владение портом 0x004E, безопасное при любом порядке
|
||||
использования с реальным CBL-звуком. Фолбэк на одно кадровое
|
||||
прерывание (`halt`), если бит не ведёт себя как ожидается за разумное
|
||||
число попыток.
|
||||
Примитивы (суффикс _256 / _16): `clear putpixel hline vline line
|
||||
rect fill_rect` — через акселератор (hline/vline burst до 256 байт;
|
||||
в 0x82 vline через RMW). Координаты int, клиппинг по краям.
|
||||
@@ -224,6 +276,15 @@ banked-функции; только volatile-глобалы и быстрая р
|
||||
блока) читается только пока `cbl_open` не закрыт (при выключенном CBL
|
||||
бит всегда 1 — MAME-квирк).
|
||||
|
||||
**Разделяемое владение портом 0x004E** (2026-07-07): бит 5 порта 0xFE
|
||||
(позиция луча — см. `<gfx.h>` `gfx_wait_vsync()`) доступен только пока
|
||||
включён bit7 порта 0x004E, независимо от того, играет ли реальный
|
||||
звук. `_cbl_port_ref()`/`_cbl_port_unref()` (internal, `_cbl_port.c`)
|
||||
дают gfx-модулю занять bit7 "немым" кодом частоты (не заводящим таймер
|
||||
CBL — без звука/прерываний), не мешая реальной `cbl_open()`-сессии,
|
||||
если она уже идёт (и наоборот — `cbl_close()` возвращает "немой" режим
|
||||
вместо полного выключения порта, если gfx его ещё держит).
|
||||
|
||||
## <palette.h> — низкий уровень (BIOS $A4/$A6)
|
||||
|
||||
`pal_load pal_get pal_set_color pal_get_color` (страница 0..7,
|
||||
|
||||
Reference in New Issue
Block a user