203 lines
6.6 KiB
C
203 lines
6.6 KiB
C
/**
|
||
* @file keys.c
|
||
* @brief PS/2 клавиатура через TSConfig CMOS FIFO (порты #EFF7/#DFF7/#BFF7)
|
||
* @see inc/keys.h
|
||
*
|
||
* Аппаратный PS/2 FIFO буфер в FPGA TSConfig, регистр CMOS #F0.
|
||
*
|
||
* ps2_poll() за один вызов полностью сливает FIFO (tight loop).
|
||
* Если среди прочитанных байтов есть make-код нашей клавиши — возвращает
|
||
* последний найденный KEY_*. Стая байтов никогда не копится.
|
||
*
|
||
* Единственный бит состояния — skip flag (после F0 нужно пропустить 1 байт).
|
||
* E0 (extended prefix) не требует skip: сам E0 не совпадает с нашими кодами,
|
||
* а release extended (E0 F0 SC) корректно обрабатывается через F0→skip.
|
||
*
|
||
* Опрос ~1 раз в TV-кадр (rate-limited по isr_tick_ctr).
|
||
* ps2_poll полностью на asm.
|
||
*/
|
||
|
||
#include "../inc/types.h"
|
||
#include "../inc/keys.h"
|
||
#include "../inc/isr.h" /* isr_tick_ctr (volatile uint8_t) */
|
||
#include "../inc/variant_cfg.h" /* ISR_TICKS_PER_FRAME */
|
||
|
||
/* Единственная FSM-переменная: skip flag (0 или 1). */
|
||
static uint8_t ps2_state; /* 0 = normal, 1 = skip next byte */
|
||
static uint8_t ps2_last_tick;
|
||
|
||
/* ── ps2_init: активация PS/2 FIFO + одноразовый слив ────────────── */
|
||
void ps2_init(void)
|
||
{
|
||
__asm
|
||
; Разрешить PS/2 порты (бит 7 #EFF7)
|
||
ld bc, #0xEFF7
|
||
ld a, #0x80
|
||
out (c), a
|
||
; Выбрать CMOS reg #F0, записать 2 (активация), re-select #F0
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
ld bc, #0xBFF7
|
||
ld a, #0x02
|
||
out (c), a
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
; Слив FIFO (tight loop, макс 256 итераций)
|
||
ld bc, #0xBFF7
|
||
ld e, #0
|
||
_pi_drain:
|
||
in a, (c)
|
||
or a, a
|
||
jr z, _pi_done ; 0x00 = пусто
|
||
inc a
|
||
jr z, _pi_ovf ; 0xFF = overflow
|
||
dec e
|
||
jr nz, _pi_drain
|
||
jr _pi_done
|
||
_pi_ovf:
|
||
ld bc, #0xDFF7
|
||
ld a, #0x0C
|
||
out (c), a
|
||
ld bc, #0xBFF7
|
||
ld a, #0x01
|
||
out (c), a
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
_pi_done:
|
||
__endasm;
|
||
ps2_state = 0;
|
||
ps2_last_tick = isr_tick_ctr;
|
||
}
|
||
|
||
/* ── ps2_deinit: слив FIFO перед выходом ──────────────────────────── */
|
||
void ps2_deinit(void)
|
||
{
|
||
__asm
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
ld bc, #0xBFF7
|
||
ld e, #0
|
||
_pd_loop:
|
||
in a, (c)
|
||
or a, a
|
||
jr z, _pd_done ; 0x00 = пусто
|
||
inc a
|
||
jr z, _pd_ovf ; 0xFF = overflow
|
||
dec e
|
||
jr nz, _pd_loop
|
||
jr _pd_done
|
||
_pd_ovf:
|
||
ld bc, #0xDFF7
|
||
ld a, #0x0C
|
||
out (c), a
|
||
ld bc, #0xBFF7
|
||
ld a, #0x01
|
||
out (c), a
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
_pd_done:
|
||
__endasm;
|
||
}
|
||
|
||
/* ── ps2_poll: tight-drain FIFO, rate-limited, полностью asm ──────── */
|
||
uint8_t ps2_poll(void) __naked
|
||
{
|
||
__asm
|
||
|
||
;; ── Rate limit: ~1 TV-кадр ────────────────────────
|
||
ld a, (_isr_tick_ctr)
|
||
ld hl, #_ps2_last_tick
|
||
ld e, a
|
||
sub a, (hl)
|
||
cp a, #ISR_TICKS_PER_FRAME
|
||
jp c, _pp_none
|
||
ld (hl), e
|
||
|
||
;; ── Подготовка: выбрать FIFO, init регистров ──────
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
ld bc, #0xBFF7 ; BC = порт FIFO на весь цикл
|
||
ld a, (_ps2_state)
|
||
ld d, a ; D = skip flag (от пред. вызова)
|
||
ld e, #0 ; E = результат (KEY_NONE)
|
||
|
||
;; ── Цикл: читаем FIFO до пустого ─────────────────
|
||
_pp_loop:
|
||
in a, (c) ; BC = #BFF7
|
||
or a, a
|
||
jr z, _pp_done ; 0x00 = пусто → выход
|
||
inc a
|
||
jr z, _pp_ovf ; 0xFF = overflow
|
||
dec a ; восстановить байт
|
||
|
||
bit 0, d
|
||
jr nz, _pp_clr_skip ; skip-флаг → пропустить байт
|
||
|
||
cp a, #0xF0
|
||
jr z, _pp_set_skip ; F0 → пропустить след. байт
|
||
|
||
;; ── Трансляция scan code → KEY_* ──────────────────
|
||
cp a, #0x76 ; ESC
|
||
jr z, _pp_k_esc
|
||
cp a, #0x29 ; SPACE
|
||
jr z, _pp_k_next
|
||
cp a, #0x4D ; P
|
||
jr z, _pp_k_prev
|
||
cp a, #0x16 ; 1
|
||
jr z, _pp_k_saa1
|
||
cp a, #0x1E ; 2
|
||
jr z, _pp_k_saa2
|
||
jr _pp_loop ; неизвестная → продолжаем слив
|
||
|
||
_pp_k_esc: ld e, #KEY_ESC
|
||
jr _pp_loop
|
||
_pp_k_next: ld e, #KEY_NEXT
|
||
jr _pp_loop
|
||
_pp_k_prev: ld e, #KEY_PREV
|
||
jr _pp_loop
|
||
_pp_k_saa1: ld e, #KEY_SAA1
|
||
jr _pp_loop
|
||
_pp_k_saa2: ld e, #KEY_SAA2
|
||
jr _pp_loop
|
||
|
||
_pp_set_skip:
|
||
ld d, #1
|
||
jr _pp_loop
|
||
|
||
_pp_clr_skip:
|
||
ld d, #0
|
||
jr _pp_loop
|
||
|
||
;; ── Overflow: сброс FIFO ─────────────────────────
|
||
_pp_ovf:
|
||
ld bc, #0xDFF7
|
||
ld a, #0x0C
|
||
out (c), a
|
||
ld bc, #0xBFF7
|
||
ld a, #0x01
|
||
out (c), a
|
||
ld bc, #0xDFF7
|
||
ld a, #0xF0
|
||
out (c), a
|
||
ld d, #0 ; сбросить skip
|
||
;; fall-through
|
||
|
||
;; ── Готово: сохранить skip, вернуть KEY ───────────
|
||
_pp_done:
|
||
ld a, d
|
||
ld (_ps2_state), a ; skip flag для след. вызова
|
||
ld a, e ; A = KEY_* (sdcccall(1))
|
||
ret
|
||
|
||
_pp_none:
|
||
xor a, a ; KEY_NONE = 0
|
||
ret
|
||
|
||
__endasm;
|
||
} |