/*
 * kbd_raw_poll — вычерпать приёмный FIFO клавиатуры ОПРОСОМ, не дожидаясь
 * прерывания.  Дополняет (не заменяет) декодер в трамплине: тот же разбор
 * PS/2 Set 2, та же битовая карта _kbdraw_down[], тот же FSM префиксов
 * _kbdraw_pending — просто вызванный из главного цикла приложения.
 *
 * ЗАЧЕМ (разбор 2026-08-01, applications/PoP/roomtest/TASKS.md, KBD-1).
 * Приёмный FIFO SIO — 3 байта, и рассчитывать на то, что каждый принятый
 * байт разбудит нас прерыванием, НЕЛЬЗЯ:
 *   - импульс запроса прерывания клавиатуры живёт лишь 32 такта CPU
 *     (dev-MAME: sprinter.cpp on_kbd_data → irq_off_timer), а ядра
 *     акселератора держат DI на весь блит — сотни микросекунд.  Импульс,
 *     попавший в такое окно, теряется НАСОВСЕМ: байт остаётся в FIFO до
 *     следующего прерывания (следующий байт либо кадровое, 50 Гц);
 *   - трафик от клавиатуры пачками: тап стрелки — 5 байт, а при зажатом
 *     Shift ещё и «fake shift» (E0 F0 12 / E0 12 вокруг расширенного кода)
 *     — по 5 байт на нажатие И на отпускание.  Три ячейки FIFO такую пачку
 *     не держат: потерянный make = «нажатие не сработало», потерянный
 *     break = залипшая клавиша (её потом лечит kbd_raw_sync, ценой сброса
 *     всех немодификаторных клавиш).
 * Опрос из главного цикла снимает зависимость от пойманного импульса: чем
 * чаще позвали, тем меньше шанс, что в FIFO успеет накопиться четвёртый
 * байт.  Звать между фазами кадра (после тяжёлых блитов), НЕ раз в кадр.
 *
 * ABI: __sdcccall(1), без аргументов, результат uint8 в A — 0 если FIFO
 * был пуст (быстрый выход, ~40 тактов), 1 если что-то вычерпано.  IX не
 * трогаем.  Тело идёт под DI: чтение порта 0x18 деструктивно, и гонка с
 * трамплином за read-modify-write ячейки _kbdraw_down[] стоила бы
 * потерянного бита.  DI/EI — БЕЗУСЛОВНЫЕ (как IRQ_DISABLE/IRQ_ENABLE в
 * kbd_raw_open): функция рассчитана на вызов из главного цикла, где
 * прерывания разрешены; звать её из ISR нельзя.
 */

#include <kbd_raw.h>
#include "_kbdraw.h"
#include "../irq/_irq.h"

uint8_t kbd_raw_poll(void) __naked
{
    __asm
        ld    a, (__kbdraw_active)
        or    a, a
        jr    Z, kp_off              ; raw-канал закрыт — не наше дело
        ;; DI ДО пробы «есть ли байт»: иначе между пробой и DI успело бы
        ;; влезть прерывание, трамплин вычерпал бы FIFO, а мы всё равно
        ;; пошли бы читать порт 0x18 — и получили бы из пустого приёмника
        ;; мусорный «скан-код» (взведённый бит клавиши, которую не нажимали).
        di
        in    a, (#_IRQ_SIO_A)       ; SIO-A RR0
        rrca                         ; бит 0 — байт принят?
        jr    C, kp_work
        ei
    kp_off:
        xor   a, a
        ret

    kp_work:
        push  hl
        push  de
        push  bc
    kp_drain:
        in    a, (#_IRQ_SIO_DATA)    ; чтение деструктивно = дренаж байта
        cp    #0xF0
        jp    Z, kp_setbrk   ; jp, а не jr: тело цикла длиннее 127 Б (fake shift)
        cp    #0xE0
        jp    Z, kp_setext   ; (в отличие от трамплина, здесь jp разрешён)

        ;; --- «fake shift» — тот же разбор, что в трамплине (см. _kbdraw.h):
        ;; расширенные 12/59 не клавиши, а обёртка вокруг расширенного кода,
        ;; которую клавиатура шлёт, пока реально зажат Shift. ---
        ld    e, a                   ; E = код
        ld    a, (__kbdraw_pending)
        bit   1, a                   ; EXT?
        ld    a, e                   ; A = код обратно (ld флаги не трогает)
        jr    Z, kp_code             ; обычный код — прежний путь
        cp    #0x12
        jr    Z, kp_fake
        cp    #0x59
        jr    Z, kp_fake
        ;; расширенная КЛАВИША: make без обёртки => Shift не зажат
        ld    a, (__kbdraw_pending)
        bit   0, a                   ; break — состояние Shift не судим
        jr    NZ, kp_fkclr
        ld    a, (__kbdraw_fakesh)
        or    a, a
        jr    NZ, kp_fkclr           ; обёртка была => Shift зажат
        ld    hl, #(__kbdraw_down + _KBDRAW_LSH_BYTE)
        res   _KBDRAW_LSH_BIT, (hl)
        ld    hl, #(__kbdraw_down + _KBDRAW_RSH_BYTE)
        res   _KBDRAW_RSH_BIT, (hl)
    kp_fkclr:
        xor   a, a
        ld    (__kbdraw_fakesh), a
        ld    a, e
        jr    kp_code

    kp_fake:
        ;; Бит взводит ОБЩИЙ писатель ниже: обнуляем префиксы — и он кладёт
        ;; код в PLAIN-половину карты (без +32) и именно взведёт бит.
        ld    a, (__kbdraw_pending)
        and   #1                     ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
        ld    (__kbdraw_fakesh), a
        xor   a, a
        ld    (__kbdraw_pending), a
        ld    a, e
        ;; fall through в kp_code

        ;; --- обычный байт кода: БИТ code&7 в _kbdraw_down[code>>3].
        ;; Маска сдвигом, а не таблицей — ровно как в трамплине, чтобы
        ;; расхождения декодеров были невозможны by construction. ---
    kp_code:
        ld    e, a                   ; E = код
        and   #7
        ld    b, a
        inc   b                      ; B = (код&7)+1 для djnz
        ld    a, #1
        jr    kp_mskdec
    kp_mskloop:
        add   a, a
    kp_mskdec:
        djnz  kp_mskloop             ; A = 1 << (код&7)
        ld    c, a                   ; C = маска бита
        ld    a, e
        rrca
        rrca
        rrca
        and   #0x1F                  ; A = код>>3 (код <= 255)
        ld    hl, #__kbdraw_down
        ld    d, #0
        ld    e, a
        add   hl, de
        ld    a, (__kbdraw_pending)
        bit   1, a                   ; _KBDRAW_PEND_EXT?
        jr    Z, kp_noext
        ld    de, #32                ; +256 кодов = +32 байта карты
        add   hl, de
    kp_noext:
        ld    a, (__kbdraw_pending)
        bit   0, a                   ; _KBDRAW_PEND_BREAK?
        jr    Z, kp_mkdown
        ld    a, c
        cpl                          ; break → сбросить бит
        and   a, (hl)
        jr    kp_wr
    kp_mkdown:
        ld    a, c                   ; make → взвести бит
        or    a, (hl)
    kp_wr:
        ld    (hl), a
        xor   a, a
        ld    (__kbdraw_pending), a  ; префиксы потреблены
        jr    kp_next

    kp_setbrk:
        ld    hl, #__kbdraw_pending
        set   0, (hl)
        jr    kp_next
    kp_setext:
        ld    hl, #__kbdraw_pending
        set   1, (hl)
    kp_next:
        in    a, (#_IRQ_SIO_A)
        rrca                         ; ещё байт в FIFO?
        jp    C, kp_drain

        ;; --- FIFO пуст: тот же разбор Rx-overrun, что и в трамплине.
        ;; RR1 бит5 = Rx Overrun; без Error Reset (WR0 = 0x30) бит залипнет
        ;; и следующий overrun будет неотличим. ---
        ld    a, #1
        out   (#_IRQ_SIO_A), a       ; указатель на RR1
        in    a, (#_IRQ_SIO_A)       ; читаем RR1 (указатель авто-сброс в 0)
        bit   5, a
        jr    Z, kp_noovr
        ld    a, #0x30
        out   (#_IRQ_SIO_A), a       ; WR0 Error Reset
        ;; Сброс FSM префиксов — как KBD_Receiver_Overrun у DSS (чистит
        ;; KEY_FLG): повисший 0xF0/0xE0 инвертировал бы следующий код.
        xor   a, a
        ld    (__kbdraw_pending), a
        inc   a
        ld    (__kbdraw_overrun), a  ; снимет kbd_raw_sync()
    kp_noovr:
        pop   bc
        pop   de
        pop   hl
        ei
        ld    a, #1
        ret
    __endasm;
}
