Add OPL4 playback and analyzer support
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user