/*
 * 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 "../_bgi.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;
}
