Add OPL4 playback and analyzer support

This commit is contained in:
2026-09-15 14:59:29 +03:00
commit 1686838e03
553 changed files with 50524 additions and 0 deletions
+203
View File
@@ -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;
}