libc+libbgi: raw-клавиатура (held-state) + column-major блит спрайтов
libc/kbd: kbd_raw_open/close/down — сырой PS/2-канал клавиатуры с held-state (битовая карта _kbdraw_down[512], EXT-клавиши +256). Пока raw открыт, кадровый IRQ-трамплин перехватывает байт SIO у DSS и декодирует make/break (0xF0/0xE0-префиксы) сам. FIFO вычерпывается В ЦИКЛЕ (приёмный буфер SIO 3 байта; пачка break-кодов при одновременном отпускании иначе теряется → залипание клавиши). Буфер W2-трамплина поднят 224→288 Б под выросший обработчик. libc/conio: kbd_mod_state() — live-состояние модификаторов (ESTEX CTRLKEY $33h), Shift/Ctrl/Alt/Lock прямо сейчас, KBD_MOD_* маска. libbgi: gfx_blit_cols(x,y,img,flip) + _bgi_blit_cols_raw — блит column-major спрайта вертикальным accel-проходом, бесплатный горизонтальный флип (sstride<0), клип по экрану. Для персонажей. libbgi/atlas_load: восстанавливать W3 ДО записи a->count (atlas_t в --bank памяти резолвится через W3; count оставался мусором). tests/kbdraw — тест raw-клавиатуры; size-baseline +kbdraw. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -163,6 +163,11 @@ libc отказывает сама (EMFILE, предохранитель _fd_gua
|
|||||||
|
|
||||||
Клавиатура: `kbhit getch getche getkey` (+KEY_* коды позиций),
|
Клавиатура: `kbhit getch getche getkey` (+KEY_* коды позиций),
|
||||||
`char *cgets(buf)`.
|
`char *cgets(buf)`.
|
||||||
|
`uint16_t kbd_mod_state(void)` — live-состояние модификаторов ПРЯМО
|
||||||
|
СЕЙЧАС (ESTEX CTRLKEY $33h, не событие из буфера — держится, пока
|
||||||
|
клавиша реально зажата); (mode<<8)|shift, расшифровка KBD_MOD_*.
|
||||||
|
Покрывает только Shift/Ctrl/Alt/Rus-Lat/Lock — для обычных клавиш
|
||||||
|
(стрелки и т.п.) live-state у ESTEX нет, см. `<kbd_raw.h>`.
|
||||||
Вывод с атрибутом: `putch cputs cprintf` (~10× медленнее stdio-пути;
|
Вывод с атрибутом: `putch cputs cprintf` (~10× медленнее stdio-пути;
|
||||||
'\n' НЕ транслируется — писать "\r\n").
|
'\n' НЕ транслируется — писать "\r\n").
|
||||||
Атрибуты: `textcolor textbackground textattr`, `set/get_text_attr`,
|
Атрибуты: `textcolor textbackground textattr`, `set/get_text_attr`,
|
||||||
@@ -285,6 +290,57 @@ Cross-lib: libbgi всегда линкуется с libc; _cbl_port_ref/unref
|
|||||||
Handler'у нельзя: ESTEX/BIOS-вызовы, gfx_*/своп окон, акселератор,
|
Handler'у нельзя: ESTEX/BIOS-вызовы, gfx_*/своп окон, акселератор,
|
||||||
banked-функции; только volatile-глобалы и быстрая работа (<1 мс).
|
banked-функции; только volatile-глобалы и быстрая работа (<1 мс).
|
||||||
|
|
||||||
|
## <kbd_raw.h> — эксклюзивный raw-канал клавиатуры (вектор 0xFF, свой путь)
|
||||||
|
|
||||||
|
Мотивация — applications/PoP/docs/PORT_PLAN.md §2: ESTEX
|
||||||
|
kbhit/getch/getkey/kbd_mod_state — событийные, без held-state для
|
||||||
|
обычных (не модификаторных) клавиш. Decode make/break (PS/2 Scan Code
|
||||||
|
Set 2: `0xF0` — префикс отпускания, `0xE0` — префикс расширенной
|
||||||
|
клавиши) прямо в трамплине прерывания (libc/irq/_irq_tramp.c), по
|
||||||
|
прецеденту приватного пути CBL (не чейнится к DSS, пока открыт).
|
||||||
|
|
||||||
|
| Сигнатура | Описание |
|
||||||
|
|---|---|
|
||||||
|
| `int kbd_raw_open(void)` | включить: с этого момента ВЕСЬ поток клавиатурных байт достаётся нам, DSS его не видит. 0 / -1+errno (EBUSY повтор) |
|
||||||
|
| `void kbd_raw_close(void)` | выключить, вернуть клавиатуру DSS; идемпотентно; висит на atexit |
|
||||||
|
| `uint8_t kbd_raw_down(uint16_t code)` | зажата ли code ПРЯМО СЕЙЧАС (0/1); code вне 0..511 — 0 |
|
||||||
|
| `KBD_EXT` | ИЛИ-флаг кода: клавиша была расширенной (0xE0-префикс на проводе) |
|
||||||
|
| `KBD_UP/DOWN/LEFT/RIGHT/SPACE/ENTER/ESC/LSHIFT/RSHIFT` | позиционные коды PS/2 Set 2 — LEFT/ESC/UP подтверждены (см. ниже); остальные — по стандарту, не перепроверены поштучно |
|
||||||
|
|
||||||
|
**ИСПРАВЛЕНО 2026-07-15 (был неверный вывод, ниже — то, что реально
|
||||||
|
подтвердилось):** изначально скриншот-тестирование (снимок экрана
|
||||||
|
после `press_key('up')`) не показывало видимого эффекта на
|
||||||
|
UP/DOWN/RIGHT — из этого сделан ОШИБОЧНЫЙ вывод «скрипт не может их
|
||||||
|
нажать». На самом деле причина была в неудачном таймінге снимков
|
||||||
|
относительно дуги прыжка, а не в отсутствии сигнала. Подтверждено
|
||||||
|
брейкпоинтом в отладчике MAME (adress уровня приложения на входе в
|
||||||
|
код `jumping=1` в `applications/PoP/poc/poc.c`): `press_key('up')`
|
||||||
|
ЧЕСТНО доходит до raw-декодера и триггерит код прыжка — брейкпоинт
|
||||||
|
сработал ровно один раз за одно удержание клавиши (это же заодно
|
||||||
|
подтвердило фикс двойного триггера через `up_prev` edge-detect в
|
||||||
|
`poc.c`). Так что скриптовый `press_key` для UP/DOWN/RIGHT РАБОТАЕТ
|
||||||
|
корректно так же, как для LEFT — предыдущая запись про «ограничение
|
||||||
|
именно в скриптовом инжекте» была неверной, оставлена в истории
|
||||||
|
git как урок: при повторных «нет эффекта» на скриншотах — проверять
|
||||||
|
брейкпоинтом/watchpoint'ом на конкретный адрес кода, не полагаться
|
||||||
|
только на визуальный снимок с произвольным таймингом.
|
||||||
|
|
||||||
|
**ГЛАВНОЕ СЛЕДСТВИЕ:** пока `kbd_raw_open()` активен, `kbhit/getch/getkey/
|
||||||
|
kbd_mod_state` НЕ получают новых событий (в т.ч. CTRLKEY подряд отдаёт
|
||||||
|
то же самое, что было на момент открытия — резидентный обработчик DSS,
|
||||||
|
от которого зависит его live-state, во время raw не выполняется).
|
||||||
|
ESC для выхода — через `kbd_raw_down(KBD_ESC)`, не через DSS.
|
||||||
|
|
||||||
|
Верификация (tests/kbdraw, MAME, 2026-07-15): `press_key` в мосте
|
||||||
|
MAME дёргает ОБЕ клавиатуры (PC ms_naturl + ZX-матрица `IO_LINE`)
|
||||||
|
одновременно — наблюдался паразитный незатухающий бит от ZX-пути
|
||||||
|
(обычный код без EXT-префикса, застревал в DOWN) — не воспроизводится
|
||||||
|
на реальном сценарии (только PC/AT-клавиатура, без матрицы, см.
|
||||||
|
applications/PoP/docs/PORT_PLAN.md §2 — пользователь подтвердил, что
|
||||||
|
матрица на Sprinter давно не используется). Для будущих MAME-тестов
|
||||||
|
этой функции — бить только по `:kbd:ms_naturl:*`, не через
|
||||||
|
удобный `press_key` (или перепроверить точную семантику полей моста).
|
||||||
|
|
||||||
## <cbl.h> — потоковый звук CBL/COVOX (вектор 0xFF, свой ISR)
|
## <cbl.h> — потоковый звук CBL/COVOX (вектор 0xFF, свой ISR)
|
||||||
|
|
||||||
Без собственного кольца (2026-07-07): у CBL уже есть аппаратный буфер
|
Без собственного кольца (2026-07-07): у CBL уже есть аппаратный буфер
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ gfxbanks 6044
|
|||||||
hello 4167
|
hello 4167
|
||||||
hello2 4244
|
hello2 4244
|
||||||
irqtest 6527
|
irqtest 6527
|
||||||
|
kbdraw 4911
|
||||||
ls 4835
|
ls 4835
|
||||||
malloc 4461
|
malloc 4461
|
||||||
mdview 18878
|
mdview 18878
|
||||||
|
|||||||
|
@@ -86,6 +86,15 @@ void _bgi_blit_rows_raw(const uint8_t *src, uint8_t *dst,
|
|||||||
uint8_t w, uint8_t h,
|
uint8_t w, uint8_t h,
|
||||||
int sstride, uint8_t y0);
|
int sstride, uint8_t y0);
|
||||||
|
|
||||||
|
/* КОЛОНОЧНАЯ специализация (column-major src -> экран): каждая колонка —
|
||||||
|
* вертикальный accel-burst (LD A,A, Port_Y авто-шаг); dst +1/колонку, src
|
||||||
|
* +sstride/колонку. Даёт бесплатный горизонтальный флип (sstride<0 + src
|
||||||
|
* с конца). w=колонок, h=высота(=размер блока), y0=Port_Y стартовый.
|
||||||
|
* bgi256/_bgi_blit_cols_raw.c. */
|
||||||
|
void _bgi_blit_cols_raw(const uint8_t *src, uint8_t *dst,
|
||||||
|
uint8_t w, uint8_t h,
|
||||||
|
int sstride, uint8_t y0);
|
||||||
|
|
||||||
/* Специализация для heal: src == dst == адрес экрана, оба страйда 0
|
/* Специализация для heal: src == dst == адрес экрана, оба страйда 0
|
||||||
* (строку выбирает Port_Y) — адресной арифметики в цикле нет, строка
|
* (строку выбирает Port_Y) — адресной арифметики в цикле нет, строка
|
||||||
* 77Т + burst'ы. y0 — стартовый Port_Y; w,h: 0 = 256. Без скобки и
|
* 77Т + burst'ы. y0 — стартовый Port_Y; w,h: 0 = 256. Без скобки и
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* _bgi_blit_cols_raw — accel block-copy КОЛОНКАМИ (буфер column-major ->
|
||||||
|
* экран). Парный к _bgi_blit_rows_raw, но вертикальный проход: каждая
|
||||||
|
* КОЛОНКА копируется одним accel-burst'ом вниз (Port_Y авто-шагает),
|
||||||
|
* опкод LD A,A (0x7F — «копирование блока для вертикального прохода»,
|
||||||
|
* см. docs/reference/accel_r.txt). dst двигается на +1 (следующий x) на
|
||||||
|
* колонку; Port_Y сбрасывается в y0 перед каждой колонкой (accel шагает
|
||||||
|
* его внутри burst'а); src двигается на sstride на колонку.
|
||||||
|
*
|
||||||
|
* ЗАЧЕМ: бесплатный ГОРИЗОНТАЛЬНЫЙ флип персонажей — вызывающий подаёт
|
||||||
|
* src с конца (src+(w-1)*h) и sstride=-h, тогда правая колонка источника
|
||||||
|
* ложится в левый x экрана (зеркало). Реверс без CPU и без удвоения
|
||||||
|
* памяти. Спрайты персонажей хранятся column-major.
|
||||||
|
*
|
||||||
|
* ВХОД (__sdcccall(1)): src→HL, dst→DE; стек: w 4(ix), h 5(ix),
|
||||||
|
* sstride 6/7(ix), y0 8(ix). w — число КОЛОНОК (1..256, 0=256); h —
|
||||||
|
* высота колонки = размер accel-блока (1..256, 0=256); sstride — шаг
|
||||||
|
* адреса src между колонками (±); y0 — стартовый Port_Y. Callee-pop 5 Б.
|
||||||
|
*
|
||||||
|
* Raw: без клиппинга (вызывающий); W3 замаплен (_bgi_begin/end снаружи);
|
||||||
|
* банк выставлен вызывающим (0x5C прозрачный / 0x50 обычный). src ВНЕ W3.
|
||||||
|
* ОДИН DI на весь блит. Колонки > 256 строк режет вызывающий (h ≤ 256).
|
||||||
|
* Клоббер: AF/BC/DE/HL; IX сохраняется.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void _bgi_blit_cols_raw(const uint8_t *src, uint8_t *dst,
|
||||||
|
uint8_t w, uint8_t h,
|
||||||
|
int sstride, uint8_t y0) __naked
|
||||||
|
{
|
||||||
|
(void)src; (void)dst; (void)w; (void)h; (void)sstride; (void)y0;
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld ix, #0
|
||||||
|
add ix, sp
|
||||||
|
;; SMC: размер accel-блока <- h (высота колонки)
|
||||||
|
ld a, 5 (ix)
|
||||||
|
ld (bc_len_imm), a
|
||||||
|
;; SMC: src-страйд между колонками (8-бит adc-цепочка, ± на вызов)
|
||||||
|
ld a, 6 (ix)
|
||||||
|
ld (bc_slo_imm), a
|
||||||
|
ld a, 7 (ix)
|
||||||
|
ld (bc_shi_imm), a
|
||||||
|
ld b, 4 (ix) ; B = число колонок (0 => 256: djnz)
|
||||||
|
ld c, 8 (ix) ; C = y0 (Port_Y, константа)
|
||||||
|
di ; один DI на весь блит
|
||||||
|
ld d, d ; 0x52 — режим размера блока
|
||||||
|
ld a, #0
|
||||||
|
bc_len_imm = . - 1
|
||||||
|
ld b, b ; 0x40 — стоп (B=счётчик не тронут)
|
||||||
|
bc_col:
|
||||||
|
ld a, c
|
||||||
|
out (#0x89), a ; Port_Y = y0 (сброс на верх колонки)
|
||||||
|
;; ЧТЕНИЕ — ГОРИЗОНТАЛЬНОЙ копией (LD L,L): src-колонка лежит в ОЗУ
|
||||||
|
;; СМЕЖНО, адрес шагает (не port_y). ЗАПИСЬ — ВЕРТИКАЛЬНОЙ (LD A,A):
|
||||||
|
;; port_y шагает вниз по видео-колонке. Режим меняется между read и
|
||||||
|
;; write (do_accel_block применяет один режим на весь блок).
|
||||||
|
ld l, l ; 0x6D — гориз. копия (для ЧТЕНИЯ смежных)
|
||||||
|
ld a, (hl) ; read-блок: h смежных байт src -> буфер accel
|
||||||
|
ld a, a ; 0x7F — верт. копия (для ЗАПИСИ колонки)
|
||||||
|
ld (de), a ; write-блок: буфер -> видео-колонка (port_y++)
|
||||||
|
ld b, b ; 0x40 — стоп
|
||||||
|
;; src += sstride (следующая колонка источника)
|
||||||
|
ld a, l
|
||||||
|
add a, #0
|
||||||
|
bc_slo_imm = . - 1
|
||||||
|
ld l, a
|
||||||
|
ld a, h
|
||||||
|
adc a, #0
|
||||||
|
bc_shi_imm = . - 1
|
||||||
|
ld h, a
|
||||||
|
inc de ; dst += 1 (следующий x)
|
||||||
|
djnz bc_col
|
||||||
|
ei ; закрыть DI-окно
|
||||||
|
pop ix
|
||||||
|
;; callee-pop 5 байт (w,h,sstride:2,y0)
|
||||||
|
pop hl ; ret-адрес
|
||||||
|
pop af
|
||||||
|
pop af
|
||||||
|
inc sp
|
||||||
|
jp (hl)
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
int atlas_load(atlas_t *a, const char *path)
|
int atlas_load(atlas_t *a, const char *path)
|
||||||
{
|
{
|
||||||
uint8_t *pg = (uint8_t *)0xC000;
|
uint8_t *pg = (uint8_t *)0xC000;
|
||||||
uint8_t saved_w3, blk, ok;
|
uint8_t saved_w3, blk, ok, count;
|
||||||
uint16_t stub;
|
uint16_t stub;
|
||||||
int fd, n;
|
int fd, n;
|
||||||
|
|
||||||
@@ -45,8 +45,9 @@ int atlas_load(atlas_t *a, const char *path)
|
|||||||
n = read(fd, pg, 16384);
|
n = read(fd, pg, 16384);
|
||||||
ok = (n >= (int)_ATL_DATA_OFF &&
|
ok = (n >= (int)_ATL_DATA_OFF &&
|
||||||
pg[0] == 'S' && pg[1] == 'P' && pg[2] == 'A' && pg[3] == '1');
|
pg[0] == 'S' && pg[1] == 'P' && pg[2] == 'A' && pg[3] == '1');
|
||||||
|
count = 0;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
a->count = pg[4];
|
count = pg[4];
|
||||||
stub = (uint16_t)&_gfx_w0_isr;
|
stub = (uint16_t)&_gfx_w0_isr;
|
||||||
pg[0x38] = 0xC3; /* JP _gfx_w0_isr */
|
pg[0x38] = 0xC3; /* JP _gfx_w0_isr */
|
||||||
pg[0x39] = (uint8_t)(stub & 0xFF);
|
pg[0x39] = (uint8_t)(stub & 0xFF);
|
||||||
@@ -55,6 +56,13 @@ int atlas_load(atlas_t *a, const char *path)
|
|||||||
pg[0x67] = 0x45;
|
pg[0x67] = 0x45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Восстановить W3 ДО записи в *a: если atlas_t лежит в банковской
|
||||||
|
* памяти (её адрес тоже резолвится через W3), запись при ещё
|
||||||
|
* замапленной странице атласа ушла бы не в тот физический банк
|
||||||
|
* (найдено 2026-07-15 — PoP room.c держит tile_atlas[] в --bank 1,
|
||||||
|
* a->count оставался мусором, room_draw читал пиксели не с того
|
||||||
|
* оффсета). a->blk/a->page выше пишутся ДО ремапа — их это не
|
||||||
|
* касалось, только a->count. */
|
||||||
sprinter_page_w3(saved_w3);
|
sprinter_page_w3(saved_w3);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
@@ -64,5 +72,6 @@ int atlas_load(atlas_t *a, const char *path)
|
|||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
a->count = count;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_cols — блит спрайта column-major (getimage-заголовок uint16 w,h;
|
||||||
|
* далее пиксели ПО КОЛОНКАМ: колонка 0 сверху вниз, потом колонка 1, ...)
|
||||||
|
* в (x,y) текущим банком (gfx_set_bank; 0x5C/0x58 прозрачный). Каждая
|
||||||
|
* колонка — вертикальный accel-burst (_bgi_blit_cols_raw).
|
||||||
|
*
|
||||||
|
* flip != 0 — ГОРИЗОНТАЛЬНОЕ зеркало (для направления персонажа): источник
|
||||||
|
* читается с последней колонки к первой (sstride отрицательный), правая
|
||||||
|
* колонка ложится в левый x экрана. Без CPU-реверса и без второй копии
|
||||||
|
* атласа. Зеркало происходит ВНУТРИ футпринта [x, x+w); чтобы держать
|
||||||
|
* "hot-point" персонажа на месте (как оригинал: xpos -= w), вызывающий
|
||||||
|
* при флипе подаёт x = anchor - w (см. pop_kid.c kid_draw).
|
||||||
|
*
|
||||||
|
* Клип по экрану ЕСТЬ (аналог _gfx_blit_full, но роли осей зеркальны:
|
||||||
|
* x = номер КОЛОНКИ, y = смещение ВНУТРИ колонки). sstride считается по
|
||||||
|
* ПОЛНОЙ высоте (колонки в ОЗУ лежат full_h друг от друга), длина accel-
|
||||||
|
* блока — по обрезанной. Спрайты Kid < 256 по обеим осям → нарезка на
|
||||||
|
* полосы >256 не нужна (raw принимает uint8_t w,h; 0 => 256). img вне W3.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_cols(int x, int y, const void *img, uint8_t flip)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
int full_w = p[0] | (p[1] << 8);
|
||||||
|
int full_h = p[2] | (p[3] << 8);
|
||||||
|
const uint8_t *px = p + 4;
|
||||||
|
int w = full_w, h = full_h;
|
||||||
|
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
|
||||||
|
int sy = 0; /* пропуск строк сверху колонки */
|
||||||
|
int cx = 0; /* пропуск колонок слева экрана */
|
||||||
|
const uint8_t *src;
|
||||||
|
uint8_t *dst;
|
||||||
|
|
||||||
|
if (full_w <= 0 || full_h <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Клип по экрану — только если хоть один край вылезает. */
|
||||||
|
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
|
||||||
|
if (x < 0) { cx = -x; w += x; x = 0; }
|
||||||
|
if (y < 0) { sy = -y; h += y; y = 0; }
|
||||||
|
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
|
||||||
|
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка.
|
||||||
|
* cx экранных колонок слева пропускаем шагом sstride (в флипе он
|
||||||
|
* отрицателен — уходим к более ранним колонкам источника); sy строк
|
||||||
|
* сверху — простым +sy внутри колонки (порядок строк флип не меняет). */
|
||||||
|
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
|
||||||
|
if (cx)
|
||||||
|
src += cx * sstride;
|
||||||
|
src += sy;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
|
||||||
|
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -93,6 +93,13 @@ void gfx_blit(int x, int y, const void *img);
|
|||||||
void gfx_blit_part(int x, int y, const void *img,
|
void gfx_blit_part(int x, int y, const void *img,
|
||||||
int sx, int sy, int w, int h);
|
int sx, int sy, int w, int h);
|
||||||
|
|
||||||
|
/* Блит спрайта column-major (пиксели по колонкам) — вертикальным accel-
|
||||||
|
* проходом. flip!=0 = горизонтальное зеркало (направление персонажа) без
|
||||||
|
* CPU-реверса/второй копии; зеркало ВНУТРИ футпринта [x,x+w) (для «hot-
|
||||||
|
* point» подавай x=anchor-w при флипе). Клип по экрану есть.
|
||||||
|
* Для персонажей (флип); фон — обычный gfx_blit (row-major). */
|
||||||
|
void gfx_blit_cols(int x, int y, const void *img, uint8_t flip);
|
||||||
|
|
||||||
/* Восстановить прямоугольник экрана из ОЗУ-копии (стирание спрайта/
|
/* Восстановить прямоугольник экрана из ОЗУ-копии (стирание спрайта/
|
||||||
* оверлея, нарисованного банком с битом 2: 0x54/0x5C). Всегда
|
* оверлея, нарисованного банком с битом 2: 0x54/0x5C). Всегда
|
||||||
* работает банком GFX_BANK_NORMAL независимо от gfx_set_bank;
|
* работает банком GFX_BANK_NORMAL независимо от gfx_set_bank;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* kbd_mod_state — обёртка ESTEX CTRLKEY ($33h): состояние
|
||||||
|
* модификаторов клавиатуры НА ДАННЫЙ МОМЕНТ (не из буфера — драйвер
|
||||||
|
* берёт данные прямо из последнего скана, см. ProgrammerManual.txt
|
||||||
|
* "CTRLKEY"), поэтому в отличие от kbhit/getch/getkey держится ровно
|
||||||
|
* столько, сколько клавиша реально зажата.
|
||||||
|
*
|
||||||
|
* ABI: C=0x33 на входе — функция; на выходе C=mode, B=shift (тот же
|
||||||
|
* регистр C переиспользуется под результат, как и во всех ESTEX-
|
||||||
|
* функциях клавиатуры). Расшифровка битов — KBD_MOD_* в <conio.h>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "_conio.h"
|
||||||
|
|
||||||
|
uint16_t kbd_mod_state(void) __naked
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld c, #0x33 ; ESTEX CTRLKEY
|
||||||
|
rst #0x10
|
||||||
|
pop ix
|
||||||
|
ld d, c ; D = mode (старший байт результата)
|
||||||
|
ld e, b ; E = shift (младший байт результата)
|
||||||
|
ret ; __sdcccall(1): DE = uint16_t
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
@@ -75,6 +75,29 @@ uint16_t getkey(void);
|
|||||||
#define KEY_INS 0x50 /* numpad 0; not verified */
|
#define KEY_INS 0x50 /* numpad 0; not verified */
|
||||||
#define KEY_DEL 0x55 /* numpad 5/.; not verified */
|
#define KEY_DEL 0x55 /* numpad 5/.; not verified */
|
||||||
|
|
||||||
|
/* kbd_mod_state — live-состояние модификаторов ПРЯМО СЕЙЧАС (ESTEX
|
||||||
|
* CTRLKEY $33h), а не событие из буфера: держится столько, сколько
|
||||||
|
* клавиша реально зажата (в отличие от kbhit/getch/getkey). Покрывает
|
||||||
|
* ТОЛЬКО Shift/Ctrl/Alt/Rus-Lat/Lock-клавиши — обычные клавиши
|
||||||
|
* (стрелки и т.п.) у ESTEX live-state не имеют, см. <kbd_raw.h>.
|
||||||
|
*
|
||||||
|
* Возврат: (mode << 8) | shift — расшифровка через KBD_MOD_* ниже. */
|
||||||
|
uint16_t kbd_mod_state(void);
|
||||||
|
|
||||||
|
#define KBD_MOD_RUSLAT 0x8000
|
||||||
|
#define KBD_MOD_NUMLOCK 0x0800
|
||||||
|
#define KBD_MOD_SCRLOCK 0x0400
|
||||||
|
#define KBD_MOD_INSERT 0x0200
|
||||||
|
#define KBD_MOD_CAPSLOCK 0x0100
|
||||||
|
#define KBD_MOD_LSHIFT 0x0080
|
||||||
|
#define KBD_MOD_RSHIFT 0x0040
|
||||||
|
#define KBD_MOD_CTRL 0x0020
|
||||||
|
#define KBD_MOD_ALT 0x0010
|
||||||
|
#define KBD_MOD_LCTRL 0x0008
|
||||||
|
#define KBD_MOD_LALT 0x0004
|
||||||
|
#define KBD_MOD_RCTRL 0x0002
|
||||||
|
#define KBD_MOD_RALT 0x0001
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* kbd_raw.h — эксклюзивный raw-канал клавиатуры: держит битовую карту
|
||||||
|
* «клавиша N зажата ПРЯМО СЕЙЧАС», обновляемую напрямую из
|
||||||
|
* прерывания (декодирует PS/2 Scan Code Set 2 — make/break, тот же
|
||||||
|
* протокол, что и в docs/samples/sprinterKeybLib.asm).
|
||||||
|
*
|
||||||
|
* Прецедент и мотивация — applications/PoP/docs/PORT_PLAN.md §2:
|
||||||
|
* ESTEX kbhit/getch/getkey/CTRLKEY — событийные (нажатие, без
|
||||||
|
* release), обычные клавиши (не модификаторы) вообще не имеют
|
||||||
|
* live-state в ESTEX; трамплин кадрового прерывания
|
||||||
|
* (libc/irq/_irq_tramp.c) в штатном режиме отдаёт клавиатурные байты
|
||||||
|
* ПРЯМО в DSS, не читая порт данных — байт из аппаратного приёмного
|
||||||
|
* регистра можно прочитать только ОДИН раз, поэтому «подсмотреть, не
|
||||||
|
* мешая DSS» невозможно технически.
|
||||||
|
*
|
||||||
|
* ЖИЗНЕННЫЙ ЦИКЛ И ГЛАВНОЕ СЛЕДСТВИЕ: пока kbd_raw_open() активен, ВЕСЬ
|
||||||
|
* поток клавиатурных байт достаётся ЭТОМУ модулю — DSS их не видит.
|
||||||
|
* kbhit/getch/getkey/CTRLKEY (в т.ч. kbd_mod_state) НЕ получают новых
|
||||||
|
* событий, пока raw-канал открыт. Приложение обязано проверять «выход»
|
||||||
|
* (например ESC — KBD_ESC) через kbd_raw_down() само. Перед экранами,
|
||||||
|
* которым нужен обычный ESTEX-ввод (диалоги/меню на консольном I/O),
|
||||||
|
* закрыть канал kbd_raw_close(). Паттерн один в один как у
|
||||||
|
* cbl_open()/cbl_close() (<cbl.h>) — тот же приватный IM2-хук, не
|
||||||
|
* чейнящийся к DSS.
|
||||||
|
*
|
||||||
|
* КОДЫ КЛАВИШ: значения ниже — общеизвестный стандарт AT/PS-2 Scan
|
||||||
|
* Code Set 2 (не специфика Sprinter). TODO до использования в PoC:
|
||||||
|
* подтвердить в MAME, что реальный поток байт с SIO-A клавиатуры
|
||||||
|
* Sprinter именно этот набор кодов (см. defer_unexplained_quirks).
|
||||||
|
*/
|
||||||
|
#ifndef KBD_RAW_H
|
||||||
|
#define KBD_RAW_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Включить raw-канал: 0 / -1+errno (EBUSY — уже открыт; прочие коды —
|
||||||
|
* см. errno.h, как у irq_install/cbl_open — общий IM2-механизм). */
|
||||||
|
int kbd_raw_open(void);
|
||||||
|
|
||||||
|
/* Выключить raw-канал, вернуть клавиатуру DSS. Идемпотентно; висит
|
||||||
|
* на atexit (страховка, как у cbl_close). */
|
||||||
|
void kbd_raw_close(void);
|
||||||
|
|
||||||
|
/* Зажата ли клавиша code ПРЯМО СЕЙЧАС (0/1). code вне 0..511 — 0
|
||||||
|
* (защитно). Для расширенных клавиш (стрелки и т.п., префикс 0xE0
|
||||||
|
* на проводе) прибавить KBD_EXT к базовому коду. */
|
||||||
|
uint8_t kbd_raw_down(uint16_t code);
|
||||||
|
|
||||||
|
#define KBD_EXT 0x0100 /* база кода была расширенной (0xE0-префикс) */
|
||||||
|
|
||||||
|
/* Позиционные коды PS/2 Set 2 — то, что реально нужно платформеру.
|
||||||
|
* TODO: подтвердить в MAME перед PoC (см. предупреждение выше). */
|
||||||
|
#define KBD_UP (KBD_EXT | 0x75)
|
||||||
|
#define KBD_DOWN (KBD_EXT | 0x72)
|
||||||
|
#define KBD_LEFT (KBD_EXT | 0x6B)
|
||||||
|
#define KBD_RIGHT (KBD_EXT | 0x74)
|
||||||
|
#define KBD_SPACE 0x29
|
||||||
|
#define KBD_ENTER 0x5A
|
||||||
|
#define KBD_ESC 0x76
|
||||||
|
#define KBD_LSHIFT 0x12
|
||||||
|
#define KBD_RSHIFT 0x59
|
||||||
|
|
||||||
|
#endif
|
||||||
+8
-2
@@ -30,8 +30,10 @@
|
|||||||
#define _IRQ_VEC_BUF_SIZE (256 + 257)
|
#define _IRQ_VEC_BUF_SIZE (256 + 257)
|
||||||
|
|
||||||
/* Буфер под W2-копию кадрового трамплина (small/huge — _irq_tramp в
|
/* Буфер под W2-копию кадрового трамплина (small/huge — _irq_tramp в
|
||||||
* W1). С запасом над реальным размером трамплина (~150 Б). */
|
* W1). С запасом над реальным размером трамплина (225 Б после
|
||||||
#define _IRQ_TRAMP_BUF_SIZE 224
|
* FIFO-drain цикла в raw-клавиатурной ветке, 2026-07-16 — проверять
|
||||||
|
* __irq_tramp_end при правках, запас держать ≥ 60 Б). */
|
||||||
|
#define _IRQ_TRAMP_BUF_SIZE 288
|
||||||
|
|
||||||
extern uint8_t _irq_vec_buf[_IRQ_VEC_BUF_SIZE];
|
extern uint8_t _irq_vec_buf[_IRQ_VEC_BUF_SIZE];
|
||||||
|
|
||||||
@@ -63,6 +65,10 @@ extern isr_t _irq_cbl_hook;
|
|||||||
#define _IRQ_CTC_CH2 0x12
|
#define _IRQ_CTC_CH2 0x12
|
||||||
#define _IRQ_CTC_CH3 0x13
|
#define _IRQ_CTC_CH3 0x13
|
||||||
#define _IRQ_SIO_A 0x19 /* SIO-A RR0: бит 0 = Rx available */
|
#define _IRQ_SIO_A 0x19 /* SIO-A RR0: бит 0 = Rx available */
|
||||||
|
#define _IRQ_SIO_DATA 0x18 /* SIO-A data — деструктивное чтение
|
||||||
|
* (дренирует байт); читает либо DSS
|
||||||
|
* (штатно), либо raw-канал клавиатуры
|
||||||
|
* (libc/kbd/_kbdraw.h), но не оба сразу */
|
||||||
|
|
||||||
/* Захватить/отпустить IM2-таблицу (счётчик ссылок). ref: при первом
|
/* Захватить/отпустить IM2-таблицу (счётчик ссылок). ref: при первом
|
||||||
* захвате строит таблицу, ставит I/IM2, взводит atexit-уборку;
|
* захвате строит таблицу, ставит I/IM2, взводит atexit-уборку;
|
||||||
|
|||||||
+76
-3
@@ -3,8 +3,16 @@
|
|||||||
* версия с ЦЕПОЧКОЙ кадровых обработчиков и W1-remap для all-modes.
|
* версия с ЦЕПОЧКОЙ кадровых обработчиков и W1-remap для all-modes.
|
||||||
*
|
*
|
||||||
* Разбор источника (трогаем только AF, пока не выяснили):
|
* Разбор источника (трогаем только AF, пока не выяснили):
|
||||||
* - клавиатура (бит 0 порта 0x19 = SIO-A RR0) → сразу к DSS, БЕЗ
|
* - клавиатура (бит 0 порта 0x19 = SIO-A RR0) → штатно сразу к DSS,
|
||||||
* remap (W1 не трогаем — прерванный контекст сохранён как есть);
|
* БЕЗ remap (W1 не трогаем — прерванный контекст сохранён как
|
||||||
|
* есть). ИСКЛЮЧЕНИЕ: если raw-канал открыт (`_kbdraw_active`,
|
||||||
|
* см. libc/kbd/_kbdraw.h, kbd_raw_open) — байт достаётся НАМ
|
||||||
|
* (порт данных 0x18 читается один раз, деструктивно — либо мы,
|
||||||
|
* либо DSS, не оба), декодируется make/break (PS/2 Scan Code
|
||||||
|
* Set 2: 0xF0 — префикс отпускания, 0xE0 — префикс расширенной
|
||||||
|
* клавиши) в битовую карту `_kbdraw_down[]`, и к DSS НЕ идём
|
||||||
|
* (private RETI — тот же паттерн, что и у CBL ниже). Пока raw
|
||||||
|
* открыт, DSS клавиатуру не видит вообще (см. <kbd_raw.h>).
|
||||||
* - CBL (бит 7 порта 0xFE) — только при зарегистрированном хуке
|
* - CBL (бит 7 порта 0xFE) — только при зарегистрированном хуке
|
||||||
* _irq_cbl_hook (иначе бит подтянут к 1); личное прерывание, RETI
|
* _irq_cbl_hook (иначе бит подтянут к 1); личное прерывание, RETI
|
||||||
* без чейна к DSS;
|
* без чейна к DSS;
|
||||||
@@ -29,6 +37,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "_irq.h"
|
#include "_irq.h"
|
||||||
|
#include "../kbd/_kbdraw.h"
|
||||||
|
|
||||||
void _irq_tramp(void) __naked
|
void _irq_tramp(void) __naked
|
||||||
{
|
{
|
||||||
@@ -38,7 +47,71 @@ void _irq_tramp(void) __naked
|
|||||||
rrca ; бит 0 — клавиатурный байт принят?
|
rrca ; бит 0 — клавиатурный байт принят?
|
||||||
jr NC, tr_notkbd
|
jr NC, tr_notkbd
|
||||||
|
|
||||||
;; --- клавиатура: прямо к DSS, без remap ---
|
;; --- клавиатура ---
|
||||||
|
ld a, (__kbdraw_active)
|
||||||
|
or a, a
|
||||||
|
jr Z, tr_kbd_dss ; raw выключен — прежнее поведение
|
||||||
|
|
||||||
|
;; --- raw включён: байт наш целиком, DSS его не увидит ---
|
||||||
|
;; ВЫЧЕРПЫВАЕМ FIFO SIO В ЦИКЛЕ (как эталон keybIntHandler /
|
||||||
|
;; PS2_Scan в docs/samples): приёмный FIFO Z80 SIO 3 байта
|
||||||
|
;; глубиной, а за один кадр при одновременном отпускании
|
||||||
|
;; нескольких клавиш приходит ПАЧКА break-кодов (0xF0 code ...).
|
||||||
|
;; Читать один байт за прерывание = переполнение FIFO = потеря
|
||||||
|
;; break-кода = клавиша «залипает» навсегда (найдено 2026-07-16).
|
||||||
|
push hl
|
||||||
|
push de
|
||||||
|
tr_kbd_drain:
|
||||||
|
in a, (#_IRQ_SIO_DATA) ; читаем — тем самым дренируем байт
|
||||||
|
cp #0xF0
|
||||||
|
jr Z, tr_kbd_setbrk
|
||||||
|
cp #0xE0
|
||||||
|
jr Z, tr_kbd_setext
|
||||||
|
|
||||||
|
;; --- обычный байт кода: индекс в _kbdraw_down[код(+256 EXT)] ---
|
||||||
|
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, tr_kbd_noext
|
||||||
|
ld de, #256
|
||||||
|
add hl, de
|
||||||
|
tr_kbd_noext:
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
bit 0, a ; _KBDRAW_PEND_BREAK?
|
||||||
|
jr Z, tr_kbd_mkdown
|
||||||
|
xor a, a ; break → снять (0)
|
||||||
|
jr tr_kbd_wr
|
||||||
|
tr_kbd_mkdown:
|
||||||
|
ld a, #1 ; make → взвести (1)
|
||||||
|
tr_kbd_wr:
|
||||||
|
ld (hl), a
|
||||||
|
xor a, a
|
||||||
|
ld (__kbdraw_pending), a ; сброс накопленных префиксов
|
||||||
|
jr tr_kbd_next
|
||||||
|
|
||||||
|
tr_kbd_setbrk:
|
||||||
|
ld hl, #__kbdraw_pending
|
||||||
|
set 0, (hl)
|
||||||
|
jr tr_kbd_next
|
||||||
|
tr_kbd_setext:
|
||||||
|
ld hl, #__kbdraw_pending
|
||||||
|
set 1, (hl)
|
||||||
|
;; fall through в tr_kbd_next
|
||||||
|
tr_kbd_next:
|
||||||
|
in a, (#_IRQ_SIO_A) ; ещё байт в FIFO?
|
||||||
|
rrca ; бит 0 → CF
|
||||||
|
jr C, tr_kbd_drain ; да — вычерпать следующий
|
||||||
|
pop de
|
||||||
|
pop hl
|
||||||
|
pop af
|
||||||
|
ei
|
||||||
|
reti ; raw владеет каналом целиком — к DSS НЕ идём
|
||||||
|
|
||||||
|
tr_kbd_dss:
|
||||||
|
;; --- прежнее поведение: прямо к DSS, без remap ---
|
||||||
pop af
|
pop af
|
||||||
.db 0xC3 ; jp nn
|
.db 0xC3 ; jp nn
|
||||||
__irq_dss_target::
|
__irq_dss_target::
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* _kbdraw.h — внутренности raw-канала клавиатуры (НЕ публичный
|
||||||
|
* заголовок; публичный интерфейс — <kbd_raw.h>).
|
||||||
|
*
|
||||||
|
* Всё состояние — плоские глобалы (живут в W2/_DATA во ВСЕХ memory-
|
||||||
|
* режимах, см. sprinter_memory_modes) — трамплин прерывания
|
||||||
|
* (libc/irq/_irq_tramp.c) обращается к ним БЕЗ W1-remap и без call,
|
||||||
|
* поэтому раскладка памяти приложения тут ни при чём.
|
||||||
|
*/
|
||||||
|
#ifndef _KBDRAW_INTERNAL_H
|
||||||
|
#define _KBDRAW_INTERNAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Биты _kbdraw_pending — накопленные префиксы PS/2 Scan Code Set 2,
|
||||||
|
* ждущие завершающего байта кода (см. _irq_tramp.c). */
|
||||||
|
#define _KBDRAW_PEND_BREAK 0x01
|
||||||
|
#define _KBDRAW_PEND_EXT 0x02
|
||||||
|
|
||||||
|
extern uint8_t _kbdraw_active; /* 0 = raw выключен (дефолт, трамплин
|
||||||
|
* ведёт себя как раньше — прямо к DSS) */
|
||||||
|
extern uint8_t _kbdraw_pending; /* FSM декодера make/break, см. выше */
|
||||||
|
extern uint8_t _kbdraw_down[512]; /* 0/1 по коду; +256 для расширенных
|
||||||
|
* (префикс 0xE0) — см. kbd_raw_down */
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* _kbdraw_state.c — состояние raw-канала клавиатуры (см. _kbdraw.h).
|
||||||
|
* Не инициализировать явно = 0 — crt0 зануляет _DATA/_BSS
|
||||||
|
* (memory/sdcc_static_storage_gotcha).
|
||||||
|
*/
|
||||||
|
#include "_kbdraw.h"
|
||||||
|
|
||||||
|
uint8_t _kbdraw_active;
|
||||||
|
uint8_t _kbdraw_pending;
|
||||||
|
uint8_t _kbdraw_down[512];
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* kbd_raw_close — выключить raw-канал, вернуть клавиатуру DSS
|
||||||
|
* (kbhit/getch/getkey/kbd_mod_state снова получают события).
|
||||||
|
* Идемпотентно; висит на atexit (kbd_raw_open).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "_kbdraw.h"
|
||||||
|
#include "../irq/_irq.h"
|
||||||
|
|
||||||
|
void kbd_raw_close(void)
|
||||||
|
{
|
||||||
|
if (!_kbdraw_active) return;
|
||||||
|
IRQ_DISABLE();
|
||||||
|
_kbdraw_active = 0;
|
||||||
|
IRQ_ENABLE();
|
||||||
|
_irq_table_unref();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* kbd_raw_down — зажата ли клавиша code ПРЯМО СЕЙЧАС (0/1).
|
||||||
|
* Читает битовую карту, которую ведёт трамплин прерывания
|
||||||
|
* (libc/irq/_irq_tramp.c) — O(1), никаких ESTEX-вызовов.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kbd_raw.h>
|
||||||
|
#include "_kbdraw.h"
|
||||||
|
|
||||||
|
uint8_t kbd_raw_down(uint16_t code)
|
||||||
|
{
|
||||||
|
if (code >= 512) return 0;
|
||||||
|
return _kbdraw_down[code];
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* kbd_raw_open — включить эксклюзивный raw-канал клавиатуры: с этого
|
||||||
|
* момента байты SIO-A клавиатуры декодирует трамплин ПРЯМО в
|
||||||
|
* прерывании (_kbdraw_down[], см. libc/irq/_irq_tramp.c), DSS их
|
||||||
|
* больше не видит — kbhit/getch/getkey/kbd_mod_state перестают
|
||||||
|
* получать новые события (applications/PoP/docs/PORT_PLAN.md §2).
|
||||||
|
*
|
||||||
|
* Захватывает IM2-таблицу тем же счётчиком ссылок, что
|
||||||
|
* irq_chain_add/cbl_open (_irq_table_ref) — можно сочетать с кадровой
|
||||||
|
* цепочкой и CBL одновременно, они независимы (разные условия ветвления
|
||||||
|
* в трамплине). atexit подстрахует close.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sprinter_exit.h>
|
||||||
|
#include <kbd_raw.h>
|
||||||
|
#include "_kbdraw.h"
|
||||||
|
#include "../irq/_irq.h"
|
||||||
|
|
||||||
|
int kbd_raw_open(void)
|
||||||
|
{
|
||||||
|
if (_kbdraw_active) { errno = EBUSY; return -1; }
|
||||||
|
if (_irq_table_ref() != 0) return -1;
|
||||||
|
|
||||||
|
memset(_kbdraw_down, 0, sizeof(_kbdraw_down));
|
||||||
|
_kbdraw_pending = 0;
|
||||||
|
|
||||||
|
static uint8_t atexit_armed;
|
||||||
|
if (!atexit_armed) {
|
||||||
|
atexit_armed = 1;
|
||||||
|
atexit(kbd_raw_close);
|
||||||
|
}
|
||||||
|
|
||||||
|
IRQ_DISABLE();
|
||||||
|
_kbdraw_active = 1;
|
||||||
|
IRQ_ENABLE();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Build kbdraw.exe — raw-канал клавиатуры (<kbd_raw.h>), TINY memory mode.
|
||||||
|
|
||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := kbdraw
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* kbdraw — тест raw-канала клавиатуры (<kbd_raw.h>). Держит на
|
||||||
|
* экране live-состояние нескольких клавиш плюс список ВСЕХ кодов,
|
||||||
|
* которые движок сейчас считает зажатыми (для калибровки KBD_* —
|
||||||
|
* см. PORT_PLAN.md §2: значения там взяты по общеизвестному стандарту
|
||||||
|
* PS/2 Set 2, не подтверждены на этом железе/MAME до этого теста).
|
||||||
|
* Заодно проверяет, что kbd_mod_state() (CTRLKEY, зависит от
|
||||||
|
* резидентного обработчика DSS) не обновляется, пока raw открыт —
|
||||||
|
* ожидаемое следствие §2 ("DSS не видит клавиатуру, пока raw
|
||||||
|
* активен").
|
||||||
|
*
|
||||||
|
* Выход — ESC через raw-канал (kbd_raw_down(KBD_ESC)), НЕ через DSS.
|
||||||
|
*/
|
||||||
|
#include <conio.h>
|
||||||
|
#include <kbd_raw.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void show(uint8_t row, const char *name, uint8_t down)
|
||||||
|
{
|
||||||
|
gotoxy(0, row);
|
||||||
|
printf("%-8s %s ", name, down ? "DOWN" : "up ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint16_t c;
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
puts("kbdraw test: hold arrows/space/shift; ESC to quit");
|
||||||
|
|
||||||
|
if (kbd_raw_open() != 0) {
|
||||||
|
puts("kbd_raw_open failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
show(2, "LEFT", kbd_raw_down(KBD_LEFT));
|
||||||
|
show(3, "RIGHT", kbd_raw_down(KBD_RIGHT));
|
||||||
|
show(4, "UP", kbd_raw_down(KBD_UP));
|
||||||
|
show(5, "DOWN", kbd_raw_down(KBD_DOWN));
|
||||||
|
show(6, "SPACE", kbd_raw_down(KBD_SPACE));
|
||||||
|
show(7, "LSHIFT", kbd_raw_down(KBD_LSHIFT));
|
||||||
|
show(8, "RSHIFT", kbd_raw_down(KBD_RSHIFT));
|
||||||
|
show(9, "ESC", kbd_raw_down(KBD_ESC));
|
||||||
|
|
||||||
|
gotoxy(0, 11);
|
||||||
|
printf("mod=%04X (expected frozen while raw is open) ",
|
||||||
|
kbd_mod_state());
|
||||||
|
|
||||||
|
gotoxy(0, 13);
|
||||||
|
printf("active codes: ");
|
||||||
|
gotoxy(14, 13);
|
||||||
|
for (c = 0; c < 512; c++)
|
||||||
|
if (kbd_raw_down(c)) printf("%03X ", c);
|
||||||
|
|
||||||
|
if (kbd_raw_down(KBD_ESC)) break;
|
||||||
|
delayms(60);
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd_raw_close();
|
||||||
|
clrscr();
|
||||||
|
puts("raw closed, DSS keyboard restored");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user