docs: справочники по железу Sprinter + правка gfx_scroll_h
- docs/new/ — сводные справочники (архитектура, BIOS, DSS, память, графика, акселератор, IRQ, порты, ввод, звук, известные баги); - docs/Original/ — первоисточники, из которых они собраны (BIOS, Estex DSS, мануалы, описание акселератора), + Форум.doc/.docx в reference; - libbgi/common/gfx_scroll_h.c — обход бага скролла при ширине >256 (правка автора: шаг банды 255 и продвижение указателей на cw; старый вариант с 256 оставлен закомментированным с TODO); - удалён applications/PoP/roomtest/hang_variants.png — рабочая раскладка из разбора позы виса, в репозитории ей не место. Большие архивы (docs/extra ~568 МБ, docs/sources с вложенными git-репо ~68 МБ) в коммит НЕ включены — см. обсуждение. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,595 @@
|
||||
# 9. Клавиатура и мышь
|
||||
|
||||
## 9.1 Общие сведения
|
||||
|
||||
Клавиатура Sprinter — AT-совместимая (101 key, PS/2). В Sprinter-1
|
||||
дополнительно присутствует ZX-клавиатурная матрица через порт `#FE`
|
||||
(скан-код через AT-контроллер преобразуется в ZX-матрицу). Sprinter-2+
|
||||
использует AT-клавиатуру через внутренний SIO Z84C15 (канал A).
|
||||
|
||||
Мышь — Microsoft Serial Mouse (2 кнопки) через RS-232 на SIO-B (канал B
|
||||
Z84C15).
|
||||
|
||||
### Порты SIO
|
||||
|
||||
| Порт | Назначение |
|
||||
|------|-----------|
|
||||
| `#18` | SIO-A data — клавиатура |
|
||||
| `#19` | SIO-A control — статус/управление |
|
||||
| `#1A` | SIO-B data — мышь |
|
||||
| `#1B` | SIO-B control — мышь |
|
||||
|
||||
Бит 0 порта `#19` — признак прерывания от клавиатуры (используется в IM2
|
||||
для различения источников с общим вектором `#FF`).
|
||||
|
||||
### Прерывания
|
||||
|
||||
Клавиатура и мышь работают в IM2, общий вектор `#FF` (совместно с
|
||||
кадровым и CBL). Различение источника — по портам SIO и флаговым битам
|
||||
(`07-irq.md §7.2`).
|
||||
|
||||
---
|
||||
|
||||
## 9.2 Клавиатура: BIOS и ESTEX
|
||||
|
||||
### FN_KBD_OUT (BIOS $EA)
|
||||
|
||||
Отправляет байт непосредственно на AT-клавиатуру [BIOS_v3.txt:0EAh].
|
||||
|
||||
```
|
||||
Вход: C = $EA
|
||||
A = байт команды/данных для AT-клавиатуры
|
||||
Выход: нет
|
||||
```
|
||||
|
||||
Используется для низкоуровневой настройки клавиатуры (LED-индикаторы,
|
||||
повтор, задержка).
|
||||
|
||||
### ESTEX: функции клавиатуры (30h–37h)
|
||||
|
||||
Через `RST 10h` с C=30h–37h [DiskSyscalls.txt:30h–37h].
|
||||
|
||||
**WAITKEY (30h):** ждёт нажатия, возвращает код.
|
||||
|
||||
```
|
||||
Вход: C = 30h
|
||||
Выход: A = код символа
|
||||
D = скан-код
|
||||
E = ASCII-код (0 для функциональных клавиш)
|
||||
```
|
||||
|
||||
**SCANKEY (31h):** опрос без ожидания.
|
||||
|
||||
```
|
||||
Вход: C = 31h
|
||||
Выход: A = код символа (0 если нажатий нет)
|
||||
D = скан-код
|
||||
E = ASCII-код
|
||||
```
|
||||
|
||||
**ECHOKEY (32h):** ждёт + эхо-вывод.
|
||||
|
||||
```
|
||||
Вход: C = 32h
|
||||
Выход: A = код символа
|
||||
D = скан-код
|
||||
E = ASCII-код
|
||||
```
|
||||
|
||||
**CTRLKEY (33h):** состояние модификаторов и режимов. libc: `kbd_mod_state()`.
|
||||
|
||||
```
|
||||
Вход: C = 33h
|
||||
Выход: C = mode (биты режимов — см. ниже)
|
||||
B = shift (биты зажатых модификаторов)
|
||||
```
|
||||
|
||||
| Бит C | Режим |
|
||||
|-------|-------|
|
||||
| 0 | РУС/ЛАТ (0=ЛАТ, 1=РУС) |
|
||||
| 1 | NumLock |
|
||||
| 2 | ScrollLock |
|
||||
| 3 | CapsLock |
|
||||
| 4 | Insert |
|
||||
|
||||
| Бит B | Модификатор |
|
||||
|-------|------------|
|
||||
| 0 | Left Shift |
|
||||
| 1 | Right Shift |
|
||||
| 2 | Ctrl (любой) |
|
||||
| 3 | Alt (любой) |
|
||||
| 4 | Left Ctrl |
|
||||
| 5 | Left Alt |
|
||||
| 6 | Right Ctrl |
|
||||
| 7 | Right Alt |
|
||||
|
||||
**K_CLEAR (35h):** сбросить буфер клавиатуры.
|
||||
|
||||
```
|
||||
Вход: C = 35h
|
||||
```
|
||||
|
||||
**K_SETUP (36h):** настройка раскладки. [DiskSyscalls.txt:36h]
|
||||
|
||||
```
|
||||
Вход: C = 36h
|
||||
A = 0 — получить текущий номер раскладки
|
||||
A = 1 — установить раскладку по номеру из B
|
||||
A = 2 — получить имя текущей раскладки (адрес HL)
|
||||
Выход: при A=0: A = номер раскладки
|
||||
при A=2: HL = адрес строки с именем
|
||||
```
|
||||
|
||||
| Номер | Раскладка |
|
||||
|-------|----------|
|
||||
| 0 | Английская (US) |
|
||||
| 1 | Русская |
|
||||
| 2 | Английская (UK) |
|
||||
| 3 | Украинская |
|
||||
|
||||
Переключение раскладки по Ctrl+Space (если не переназначено).
|
||||
|
||||
**TESTKEY (37h):** тест на нажатие (без извлечения из буфера).
|
||||
|
||||
```
|
||||
Вход: C = 37h
|
||||
Выход: A = код символа (0 если нет)
|
||||
D = скан-код
|
||||
E = ASCII-код
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9.3 Клавиатура: необработанный (raw) доступ
|
||||
|
||||
Штатный DSS (WAITKEY/SCANKEY/CTRLKEY) даёт событийный интерфейс —
|
||||
нажатия, а не состояния клавиш. Для игр и real-time приложений требуется
|
||||
чтение **зажатых клавиш в каждый момент**. Это реализуется через
|
||||
перехват SIO-A напрямую в обработчике IM2, минуя DSS.
|
||||
|
||||
### 9.3.1 Принцип
|
||||
|
||||
1. Установить свой обработчик IM2 (общий вектор `#FF`).
|
||||
2. В обработчике читать байты с SIO-A (порт `#18`), декодировать
|
||||
PS/2 Scan Code Set 2, обновлять битовую карту held-состояний.
|
||||
3. Основной код по кадровому прерыванию (или раз в кадр) читает карту
|
||||
опросом — без ожидания.
|
||||
4. DSS не получает байт с клавиатуры, пока обработчик их забирает.
|
||||
Для восстановления нормального ввода — временно отключить перехват.
|
||||
|
||||
### 9.3.2 SIO-A и прерывания
|
||||
|
||||
SIO-A (порт данных `#18`, управление `#19`) работает в IM2.
|
||||
|
||||
**Инициализация SIO-A** (выполняется BIOS при загрузке, повторно не требуется):
|
||||
|
||||
| WR | Значение | Эффект |
|
||||
|:--|:---------|--------|
|
||||
| WR0 | `0x10` | Сброс RR1 (ошибка) |
|
||||
| WR1 | `0x07` | Прерывания по Rx готовности |
|
||||
| WR3 | `0xC1` | Rx 8 бит, без CRC |
|
||||
| WR4 | `0x44` | ×1 клок, 1 стоп-бит, без паритета |
|
||||
| WR5 | `0xEA` | Tx 8 бит, DTR+RTS=1 |
|
||||
|
||||
**Различение в IM2:** при входе в обработчик проверить бит 0 порта `#19`.
|
||||
Если 0 — прерывание не от SIO-A (кадровое, CBL — пропустить).
|
||||
|
||||
### 9.3.3 PS/2 Scan Code Set 2 — протокол
|
||||
|
||||
SIO-A передаёт скан-коды PS/2 Set 2. Каждый байт с порта `#18` —
|
||||
один байт протокола. Размер сообщения:
|
||||
|
||||
| Последовательность | Значение |
|
||||
|:------------------|:---------|
|
||||
| `<code>` | Make — клавиша нажата (код < 0xF0) |
|
||||
| `0xF0 <code>` | Break — клавиша отпущена |
|
||||
| `0xE0 <code>` | Extended — расширенная клавиша (стрелки, RAlt, RCtrl) |
|
||||
| `0xE0 0xF0 <code>` | Extended break |
|
||||
|
||||
Make-байт устанавливает бит клавиши в held-карте.
|
||||
Break-байт сбрасывает.
|
||||
|
||||
**FSM декодирования (2 состояния):**
|
||||
|
||||
```
|
||||
PEND_MAKE:
|
||||
получить байт B
|
||||
if B == 0xF0 → переход PEND_BREAK
|
||||
if B == 0xE0 → переход PEND_EXT, Bзап = B
|
||||
иначе → make(B)
|
||||
|
||||
PEND_BREAK:
|
||||
получить байт B
|
||||
переход PEND_MAKE
|
||||
if (ext_flag) → break_ext(B); ext_flag = 0
|
||||
иначе → break(B)
|
||||
|
||||
PEND_EXT:
|
||||
получить байт B
|
||||
if B == 0xF0 → ext_flag = 1, переход PEND_BREAK_EXT
|
||||
иначе → make_ext(B); переход PEND_MAKE
|
||||
|
||||
PEND_BREAK_EXT:
|
||||
получить байт B
|
||||
ext_flag = 0
|
||||
break_ext(B)
|
||||
переход PEND_MAKE
|
||||
```
|
||||
|
||||
### 9.3.4 Битовая карта held-состояний
|
||||
|
||||
Массив 128 байт (1024 бита для кодов 0..1023, расширенные — с битом
|
||||
`0x0100`).
|
||||
|
||||
Простая реализация:
|
||||
|
||||
```asm
|
||||
; HL = code (0..0x1FF)
|
||||
; IX = base адреса карты
|
||||
LD A, H
|
||||
AND A, 0x07 ; номер бита 8..10
|
||||
LD D, A
|
||||
LD A, L
|
||||
RRCA ; HL/8 (3 сдвига)
|
||||
RRCA
|
||||
RRCA
|
||||
AND A, 0x1F
|
||||
LD L, A
|
||||
LD A, H
|
||||
AND A, 0x07
|
||||
ADD A, A ; +8*...
|
||||
ADD A, A
|
||||
ADD A, A
|
||||
ADD A, L
|
||||
LD L, A
|
||||
LD H, 0x00
|
||||
ADD IX, HL ; IX = &bitmap[code / 8]
|
||||
LD A, D
|
||||
INC A
|
||||
LD B, A ; B = номер бита в байте + 1
|
||||
XOR A
|
||||
SCF ; carry=1 для make, 0 для break
|
||||
RL A
|
||||
DJNZ $-2 ; A = 1 << (code & 7)
|
||||
; make: OR (IX), A ; break: CPL + AND (IX), A
|
||||
```
|
||||
|
||||
### 9.3.5 Overrun recovery
|
||||
|
||||
SIO FIFO глубиной 3 байта. Если обработчик прерывания не успевает
|
||||
забрать байты (длинная DI-секция, акселератор), FIFO переполняется —
|
||||
RR1 bit5 = 1. С этого момента цепочка make+break нарушена, возможны
|
||||
залипшие клавиши.
|
||||
|
||||
**Алгоритм восстановления:**
|
||||
|
||||
1. Обнаружить overrun (читать SIO RR1, бит 5).
|
||||
2. Сбросить held-карту **всех клавиш, кроме**:
|
||||
|
||||
| Код PS/2 | Клавиша | Почему |
|
||||
|:--------:|:--------|:-------|
|
||||
| `0x12` | Left Shift | PS/2 автоповтор — только последняя |
|
||||
| `0x59` | Right Shift | нажатая клавиша. Если сбросить |
|
||||
| `0x14` | Left Ctrl | модификатор, он не восстановится до |
|
||||
| `0x11` | Left Alt | физического отпускания. Игра «теряет» |
|
||||
| `0xE014` | Right Ctrl | шифт/контрол/альт при каждом overrun. |
|
||||
| `0xE011` | Right Alt | |
|
||||
|
||||
3. Взвести флаг «был overrun» — основной код может проигнорировать
|
||||
held-состояние на этот кадр.
|
||||
|
||||
### 9.3.6 Координация с DSS
|
||||
|
||||
Пока обработчик забирает байты с SIO-A, FIFO пуст — DSS не видит
|
||||
клавиатуру. Для вызова DSS-функций (консоль, диалоговые окна):
|
||||
|
||||
1. Переключить флаг «raw active = 0» в IM2-обработчике.
|
||||
2. Обработчик начинает пропускать байты в DSS (JP `0038h` или
|
||||
через штатный трамплин).
|
||||
3. После завершения ввода — переключить флаг обратно.
|
||||
|
||||
**Внимание:** если обработчик просто перестаёт читать SIO-A, но не
|
||||
возвращает управление DSS, порт SIO-A перестанет генерировать
|
||||
прерывания (FIFO полон, Rx готов = 0). Для корректной передачи
|
||||
управления нужно разрешить DSS-обработчику читать SIO-A.
|
||||
|
||||
### 9.3.7 Пример: минимальный raw-обработчик
|
||||
|
||||
```asm
|
||||
; Флаги: raw_active = 1 — перехватывать, 0 — пропускать в DSS
|
||||
; Карта: kbd_held — 128 байт, занулена при старте
|
||||
; FSM: kbd_state — 0=PEND_MAKE, 1=PEND_BREAK, 2=PEND_EXT, 3=PEND_BREAK_EXT
|
||||
; kbd_ext — флаг extended (0/1)
|
||||
|
||||
KBD_ISR:
|
||||
; Проверить, что прерывание от SIO-A
|
||||
IN A, (#19) ; SIO-A RR0
|
||||
BIT 0, A ; бит 0 = прерывание от SIO-A?
|
||||
RET Z ; нет — пропустить
|
||||
|
||||
IN A, (#18) ; прочитать байт скан-кода
|
||||
BIT raw_active ; перехват активен?
|
||||
JP Z, DSS_IRQ ; нет — пусть DSS обработает
|
||||
|
||||
; FSM: текущее состояние в kbd_state
|
||||
LD HL, kbd_state
|
||||
LD A, (HL)
|
||||
AND A, 3
|
||||
JP Z, .pend_make
|
||||
DEC A
|
||||
JP Z, .pend_break
|
||||
DEC A
|
||||
JP Z, .pend_ext
|
||||
; .pend_break_ext
|
||||
LD (HL), 0 ; → PEND_MAKE
|
||||
LD A, B ; код клавиши
|
||||
CALL break_ext
|
||||
EI
|
||||
RETI
|
||||
|
||||
.pend_make:
|
||||
LD A, B ; байт с SIO-A (уже в B из IN)
|
||||
CP 0xF0
|
||||
JR Z, .set_break
|
||||
CP 0xE0
|
||||
JR Z, .set_ext
|
||||
CALL make ; обычный make
|
||||
LD (HL), 0 ; остаёмся PEND_MAKE
|
||||
EI
|
||||
RETI
|
||||
|
||||
.set_break:
|
||||
LD (HL), 1 ; → PEND_BREAK
|
||||
EI
|
||||
RETI
|
||||
|
||||
.set_ext:
|
||||
LD (HL), 2 ; → PEND_EXT
|
||||
EI
|
||||
RETI
|
||||
|
||||
.pend_break:
|
||||
LD (HL), 0 ; → PEND_MAKE
|
||||
CALL break
|
||||
EI
|
||||
RETI
|
||||
|
||||
.pend_ext:
|
||||
LD A, B
|
||||
CP 0xF0
|
||||
JR Z, .set_ext_break
|
||||
CALL make_ext
|
||||
LD (HL), 0
|
||||
EI
|
||||
RETI
|
||||
|
||||
.set_ext_break:
|
||||
LD (HL), 3 ; → PEND_BREAK_EXT
|
||||
EI
|
||||
RETI
|
||||
```
|
||||
Размер кода — ~60 байт.
|
||||
|
||||
---
|
||||
|
||||
## 9.4 Мышь
|
||||
|
||||
Драйвер мыши установлен в системном shell Sprinter и доступен через
|
||||
SST-вызов `RST 30h`.
|
||||
|
||||
### 9.4.1 SST-функции мыши
|
||||
|
||||
Вызов: `RST 30h` с A = номер функции. Выход в зависимости от функции
|
||||
(см. таблицу).
|
||||
|
||||
| № | Назначение | Вход | Выход |
|
||||
|:-:|:-----------|:-----|:------|
|
||||
| `$00` | Инициализация | A=$00 | — |
|
||||
| `$01` | Показать курсор | A=$01 | — |
|
||||
| `$02` | Спрятать курсор | A=$02 | — |
|
||||
| `$03` | Читать состояние | A=$03 | DE=X, HL=Y, A=buttons (b0=left, b1=right) |
|
||||
| `$04` | Переместить курсор | A=$04, DE=X, HL=Y | — |
|
||||
| `$05` | Установить границы X | A=$05, DE=min, HL=max | — |
|
||||
| `$06` | Установить границы Y | A=$06, DE=min, HL=max | — |
|
||||
| `$07` | Вид курсора (текст) | A=$07, DE=sym_and:sym_xor, HL=attr_and:attr_xor | — |
|
||||
| `$09` | Загрузить курсор (граф.) | A=$09, IX=pointer | — |
|
||||
| `$0B` | Прочитать курсор | A=$0B | IX=pointer (копия) |
|
||||
| `$0C` | Чувствительность X | A=$0C, D=value (1..255) | — |
|
||||
| `$0D` | Чувствительность Y | A=$0D, D=value (1..255) | — |
|
||||
| `$0E` | Прочитать чувств. X | A=$0E | D=value |
|
||||
| `$0F` | Прочитать чувств. Y | A=$0F | D=value |
|
||||
| `$11` | Уведомить о смене режима | A=$11, D=режим (0=текст, 1=граф-256, 2=граф-16) | — |
|
||||
|
||||
**Пример: прочитать состояние мыши в asm:**
|
||||
|
||||
```asm
|
||||
LD A, $03 ; READ
|
||||
RST 30h
|
||||
; DE = X, HL = Y, A = buttons
|
||||
LD (mouse_x), DE
|
||||
LD (mouse_y), HL
|
||||
AND A, 3
|
||||
LD (mouse_btn), A
|
||||
```
|
||||
|
||||
### 9.4.2 Чувствительность
|
||||
|
||||
Значение — **делитель**: драйвер считает N raw-шагов мыши, прежде чем
|
||||
сдвинуть курсор на 1 пиксель. **Меньше = быстрее** (чувствительнее).
|
||||
Рекомендуемое начальное значение: 2 по обеим осям.
|
||||
|
||||
```asm
|
||||
LD A, $0C ; SET_SENS_H
|
||||
LD D, 2
|
||||
RST 30h
|
||||
LD A, $0D ; SET_SENS_V
|
||||
LD D, 2
|
||||
RST 30h
|
||||
```
|
||||
|
||||
### 9.4.3 Порядок инициализации
|
||||
|
||||
После загрузки драйвер мыши уже инициализирован shell. Если программа
|
||||
начинает «с чистого листа» (без shell), инициализация обязательна:
|
||||
|
||||
```asm
|
||||
LD A, $00 ; INIT — инициализировать драйвер
|
||||
RST 30h
|
||||
LD A, $05 ; BOUNDS_X: границы экрана
|
||||
LD DE, 0
|
||||
LD HL, 319 ; для 320×256
|
||||
RST 30h
|
||||
LD A, $06 ; BOUNDS_Y
|
||||
LD DE, 0
|
||||
LD HL, 255
|
||||
RST 30h
|
||||
LD A, $01 ; SHOW — показать курсор
|
||||
RST 30h
|
||||
```
|
||||
|
||||
### 9.4.4 Смена видеорежима
|
||||
|
||||
После переключения видеорежима (графический → текстовый и обратно)
|
||||
обязательно вызвать `$11`:
|
||||
|
||||
```asm
|
||||
LD A, $11
|
||||
LD D, 1 ; 0=текст, 1=граф-256, 2=граф-16
|
||||
RST 30h
|
||||
```
|
||||
|
||||
Иначе драйвер применяет старые координаты, и курсор рисуется
|
||||
неправильно.
|
||||
|
||||
### 9.4.5 Прямой доступ к SIO-B (raw mouse)
|
||||
|
||||
Мышь подключена через SIO-B (порты `#1A`/`#1B`), Microsoft Serial Mouse,
|
||||
1200 бод 7E1. Если программа хочет читать мышь напрямую, минуя
|
||||
SST-драйвер (например для нестандартного протокола или экономии на
|
||||
SST), SIO-B нужно инициализировать самостоятельно:
|
||||
|
||||
**Инициализация SIO-B (1200 7E1):**
|
||||
|
||||
```asm
|
||||
LD HL, .sio_init
|
||||
LD B, 6
|
||||
LD C, #1B ; SIO-B control
|
||||
OTIR
|
||||
JR .done
|
||||
.sio_init:
|
||||
DB #10 ; WR0: сброс RR1
|
||||
DB #04 ; WR1: прерывания выкл
|
||||
DB #44 ; WR4: ×1 клок, 1 стоп, без паритета
|
||||
DB #C1 ; WR3: Rx 8 бит
|
||||
DB #EA ; WR5: Tx 8 бит, DTR+RTS=1
|
||||
DB #15 ; WR0: сброс ошибки + выбран RR1
|
||||
.done:
|
||||
```
|
||||
|
||||
**Чтение байта с мыши (опрос, без прерываний):**
|
||||
|
||||
```asm
|
||||
IN A, (#1B) ; SIO-B RR0
|
||||
BIT 0, A ; бит 0 = Rx готов?
|
||||
JR Z, .no_data
|
||||
IN A, (#1A) ; прочитать байт
|
||||
; обработать ...
|
||||
```
|
||||
|
||||
**Формат пакета Microsoft Serial Mouse:**
|
||||
|
||||
Длина: 3 байта.
|
||||
|
||||
| Байт | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|:----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| 0 | 1 | 0 | 0 | 0 | L | R | Y7 | Y6 |
|
||||
| 1 | 0 | X6 | X5 | X4 | X3 | X2 | X1 | X0 |
|
||||
| 2 | 0 | Y5 | Y4 | Y3 | Y2 | Y1 | Y0 | X7 |
|
||||
|
||||
- Координаты X/Y — **относительные** (приращения с последнего пакета), знаковые.
|
||||
- `X7` — старший бит X (знак), `X6..X0` — младшие.
|
||||
- `Y7..Y6` — старшие биты Y, `Y5..Y0` — младшие.
|
||||
- `L` — левая кнопка (1 = нажата), `R` — правая.
|
||||
|
||||
**Декодирование:**
|
||||
|
||||
```asm
|
||||
; B = байт 0, C = байт 1, D = байт 2
|
||||
; X:
|
||||
LD A, C ; байт 1: X6..X0
|
||||
LD E, A
|
||||
LD A, D ; байт 2: X7
|
||||
RLCA
|
||||
RLCA
|
||||
AND A, 0x80
|
||||
OR A, E ; A = X (7 бит + знак)
|
||||
; знаковое расширение в HL
|
||||
LD L, A
|
||||
RLCA
|
||||
SBC A, A
|
||||
LD H, A ; HL = X (знаковое 16-бит)
|
||||
|
||||
; Y:
|
||||
LD A, B ; байт 0: Y7..Y6
|
||||
RLCA
|
||||
RLCA ; A[1:0] = Y7..Y6
|
||||
AND A, 0xC0
|
||||
LD E, A
|
||||
LD A, D ; байт 2: Y5..Y0
|
||||
RLCA
|
||||
RLCA ; A[7:2] = Y5..Y0
|
||||
AND A, 0xFC
|
||||
OR A, E ; A = Y (7 бит + знак)
|
||||
; знаковое расширение в DE
|
||||
LD E, A
|
||||
RLCA
|
||||
SBC A, A
|
||||
LD D, A ; DE = Y (знаковое 16-бит)
|
||||
|
||||
; Кнопки:
|
||||
LD A, B
|
||||
AND A, 0x30 ; биты L (4) и R (5)
|
||||
RRCA ; A = 0..3
|
||||
RRCA
|
||||
RRCA
|
||||
RRCA
|
||||
LD (buttons), A
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9.5 Особенности игровой клавиатуры
|
||||
|
||||
Документ: `kbd-games.md`. Ниже — ключевые паттерны для реализации.
|
||||
|
||||
- **Held-state vs edge-detect:** DSS — событийный (сообщает только момент
|
||||
нажатия). Raw-канал (§9.3) — state-based (зажата/не зажата прямо сейчас).
|
||||
Играм нужен второй.
|
||||
- **Sync once per frame:** раз в кадр (по кадровому прерыванию) проверять
|
||||
held-карту. Между кадрами карта обновляется только в IM2-обработчике.
|
||||
- **Edge-detect:** для одноразовых действий (прыжок, выстрел) — запоминать
|
||||
предыдущее held-состояние и сравнивать:
|
||||
```
|
||||
pressed = held & ~prev_held
|
||||
prev_held = held
|
||||
```
|
||||
- **Типоматик:** игнорировать повторные make, пока не придёт break.
|
||||
Реализация: при make — установить бит только если он ещё не был
|
||||
установлен; при break — сбросить.
|
||||
- **Автоотмена (reject):** если за кадр нажато больше N клавиш (например
|
||||
4) — игнорировать все (защита от баунса и мусора).
|
||||
- **Recovery:** при overrun — очистить held-карту всех клавиш, кроме
|
||||
модификаторов (LShift 0x12, RShift 0x59, LCtrl 0x14, LAlt 0x11,
|
||||
RCtrl 0xE014, RAlt 0xE011).
|
||||
|
||||
---
|
||||
|
||||
## 9.6 Кросс-ссылки
|
||||
|
||||
- Прерывания (IM2, вектор #FF): `07-irq.md §7.2`
|
||||
- DSS-функции клавиатуры: `03-dss.md §3.6`
|
||||
- BIOS FN_KBD_OUT: `02-bios.md §2.7`
|
||||
- Порт клавиатуры `#FE`: `11-ports.md §11.3`
|
||||
- Игровая клавиатура: `kbd-games.md`
|
||||
- Баги: `12-bugs.md`
|
||||
- SIO и порты прерываний: `11-ports.md §11.2`
|
||||
Reference in New Issue
Block a user