54 KiB
Промт для новой сессии: изучение и модификация клавиатурной подсистемы DSS
Задача
Изучить и при необходимости модифицировать алгоритмы работы с клавиатурой,
реализованные в DSS/KEYINTER.ASM — модуле, который
включается в ядро DSS через INCLUDE из DSS/DSS-MAIN.ASM
(строка 402, сразу после таблицы DSS_API_TABLE). Это код для компьютера
Sprinter (Z84C15), работающего как PS/2-хост для AT-клавиатуры через
встроенный в Z84C15 SIO канал A.
Перед изменениями сверяй свои выводы с исходным кодом: в этом проекте уже
зафиксирована память о том, что доверять разбору регистров/стека от
суб-агентов нельзя — трассируй PUSH/POP/EX AF/EXX лично (см.
[[verify-subagent-reg-flow-claims]] в auto-memory).
1. Рабочее окружение
- Основная директория проекта (kernel + сборка):
/Users/tolik/Documents/SP_Projects/ASM/GIT/Estex DSS - Доп. директория (архив/референс, не собирается):
/Users/tolik/Documents/SP_Projects/ASM/Another(подключена вEstex DSS.code-workspaceкак второй корень) - Подмодуль git: Shared_Includes/ — общие
константы/структуры/доки; ветка
main, url../Shared_Includes.git. - Кодировка/переводы строк: исходники в CP866 (русский DOS) с
CRLF. Это включает
DSS-MAIN.ASM,KEYINTER.ASMи большинствоShared_Includes/constants/*.inc.grep -aIнужен, если grep считает файлы бинарными. - Ассемблер:
sjasmplus --syntax=afw. Сборка идёт через VS Code (.vscode/tasks.json) — Makefile нет. Полезные задачи:Build SYSTEM.DOS,Build SYSTEM.DOS [NEW](--define INCREASE_BUILD→ инкрементDSS/build.txt),Build SYSTEM.EXE,Build SYS.EXE,Copy all files to MAME(вызывает RUN/Image.sh →hdfmonkey putв/Users/tolik/Documents/MAME/IMG/test_2g.img),Run MAME. - Выходы сборки:
Build/DSS/SYSTEM.DOS(ядро),Build/DSS/SYSTEM.EXE(шелл),Build/DSS/SYS.EXE(бутлоадер),Build/DSS-MAIN.LST(листинг ~1 МБ),Build/Variables.inc(экспортированные символы через--exp). - Версия:
VERS=1, MODF=71, билд изDSS/build.txt(сейчас 68) → "DSS 1.71.68". Логика в DSS/VERSION.INC + LUA в Shared_Includes/LUA/Functions.lua.
2. Архитектура DSS и место KEYINTER
2.1. Точка входа ядра — DSS/DSS-MAIN.ASM
ORG 0, ядро располагается с адреса #0000 на странице ядра.- RST-векторы (DSS-MAIN.ASM около строк 30-310):
RST 00— RETFAR / реентри для дочерних процессовRST 08(ToBIOS) — портал в BIOS в SLOT0RST 10(ToDSS) — DSS API gateway →RST_10диспетчер на строке 187RST 18— портал в страницу драйверовRST 20,RST 28— NOPSRST 30(ToDSS.Mouse) — мышиный APIRST 38(IM 1) —INTx38_Handlerна строке 275: сохраняет все регистры (главный набор + альтернативный + IX/IY), вызываетCALL KEYSCAN, затемRST ToDSS.Mouse GetPackets, затемcursor_interrupt,RETI.
- Диспетчер
RST_10(строка 187):LD H,high DSS_API_TABLE / LD L,C— C=номер функции, индексирует таблицу адресов с раздельными байтами (низкие байты строки 340-361, высокие 369-390). - Клавиатурные слоты в
DSS_API_TABLE(строки 344-345 / 373-374):#30 WAITKEY,#31 SCANKEY,#32 ECHOKEY,#33 CTRLKEY,#34 NOPS(зарезервирован, не реализован — константаDss.EDITесть, кода нет),#35 K_CLEAR,#36 K_SETUP,#37 TESTKEY.
- INCLUDE KEYINTER.ASM — строка 402.
- Boot-флоу
F_START(строка 567): DEPLOY →CALL KEYBOARD_INIT(строка 592) → PRINT_INIT → инициализация мыши → инициализация дисков →EI→CLEAR_BUFFER_AND_INIT_PROC. - Карта памяти (DSS/DSS_MAP.TXT):
#0000..#0038— RESTARTS#0200..#03FF—DSS_API_TABLE#0400..#043F— SBUF (буфер клавиатуры, 64 байта)#0440..#0BD1— код KEYINTER (~1936 байт)#0BD2..#0EF5— драйвер экрана (для совместного использования сCursor_On/Off)
2.2. Соседние модули
- DSS/API.asm — агрегатор-INCLUDE для всех
DSS/API/*.asm. - DSS/FS_Module.asm —
FS/FAT.asm+FS/CDFS.ASM. - DSS/DOS_Proc.asm — DOS-уровневые помощники
(имена файлов, маски,
SET_FM,OPENDSKи т.п.). - DSS/Procedures.asm — общие хелперы
(
PUTCHARстрока 103,PUTCHAR.NO_SCROLLстрока 104,MK_TIMEи др.). - DSS/DRV-MAIN.ASM — отдельная страница драйверов
(DISP/ENT-блок в
DSS-MAIN.ASM:695-699), своя таблица RST по адресам#A0000..#A0038. Клавиатура там НЕ живёт. - DSS/drivers/Input/MOUSE.ASM — мышь;
делит окно INT 38h с KEYINTER (сначала
KEYSCAN, потом мышь). - DSS/defines.inc — compile-time флаги (см. §5).
- DSS/DSS_MACROSES.Z80 — макросы. Закомментированный
BUFFER_KEYINTER(строки 2-82) задокументирован как канонический layoutSBUF/KEYFLAG/KEYCTRL/KEYFLG/SOUND_K, но реальные определения сейчас inline вKEYINTER.ASM.SET_PAGE_X(строки 137-143) — рабочий макрос переключения страниц SLOT3.
2.3. Внешние символы, на которые ссылается KEYINTER
| Символ | Что | Где определён |
|---|---|---|
PUTCHAR, PUTCHAR.NO_SCROLL |
вывод символа | DSS/Procedures.asm:103-104 |
LOCATE |
установка курсора | DSS/API/Locate.asm:8 |
VMODE |
байт текущего видеорежима | DSS/API/SetVMod.asm:90 |
BANKTBL |
таблица логическая→физическая страница | DSS/DSS-MAIN.ASM:423 |
RST_10 |
диспетчер DSS | DSS/DSS-MAIN.ASM:187 |
ToBIOS, BIOS.* |
BIOS API | Shared_Includes/constants/BIOS_equ.inc |
ToDSS, Dss.*, DSS_Error.* |
DSS константы | Shared_Includes/constants/dss_equ.inc |
Z84.SIO.Ch_A.{Data,Ctrl}, SP_SND.Beeper, SLOT3, TXTPAGE |
железо | Shared_Includes/constants/SP2000.inc (модуль Z84, строки 2111-2127); часть EQU может приходить из внешнего SP2000-инклюда не из снимка репо — проверять Build/Variables.inc |
3. KEYINTER.ASM подробно
Файл — 1371 строка, CRLF, CP866. Заголовок R01 (10.02.2003, DNS — добавлен
курсор в ECHOKEY) и R02 (13.04.2023, BAO — исправлен stack overflow в
K_CLEAR).
3.1. Публичный API (вызывается через RST 10 с C= номер функции)
| C | Метка | Строки | Назначение |
|---|---|---|---|
| #30 | WAITKEY |
112-119 | Блокирующее ожидание. Выход: A=E=ASCII, D= поз.код, B=KEYCTRL, C=KEYFLAG |
| #31 | SCANKEY |
122-128 | Неблокирующее. ZF=1 если буфер пуст |
| #32 | ECHOKEY |
195-212 | SCANKEY + программный курсор (Cursor_On/Off), эхо через PUTCHAR.NO_SCROLL |
| #33 | CTRLKEY |
383-391 | Прочесть BC=(KEYFLAG) без извлечения; A=0 если буфер пуст, A=#FF если есть |
| #34 | (NOPS) | — | Dss.EDIT определён, но функция не реализована в v1.71 |
| #35 | K_CLEAR |
431-445 | Очистить буфер (HEAD:=HOST), потом JP RST_10 для повторной диспетчеризации, если B ∈ [#30..#34]. Иначе A=#01 INVALID_FUNCTION, CF=1 |
| #36 | K_SETUP |
934-... | Мультиплексор: см. §3.6 |
| #37 | TESTKEY |
393-408 | Peek текущей записи без сдвига HOST. ZF=1 если пуст |
3.2. Кольцевой буфер
SBUF(строка 14):_mInfoALIGN 256,0+BLOCK 64,0— 64 байта на границе страницы. Поэтому high-byte адреса слота =high SBUF, аHEAD/HOST— только low-byte (0..#3F).HEAD(строка 33) — указатель писателя.HOST(строка 34) — указатель читателя.- Слот = 4 байта:
byte+0=E (ASCII),byte+1=D (поз.код | #80),byte+2=B (KEYCTRL snapshot),byte+3=C (KEYFLAG snapshot). - Макс. 16 записей.
- Пуст:
HEAD == HOST. Полон:HEAD == (HOST-4) & #3F(проверкаPUTSYMстроки 450-453). GetSymAddr(строки 467-476) — общий помощник:A := (HL),INC (HL)×4,RES 6,(HL)(wrap modulo 64), формируетHL = high SBUF : старый offset.PUTSYM(448-464) — продьюсер. На переполнении прыгает вFULL_BF(496-505): еслиSF_BUFFбит SOUND_K взведён —BEEPсDE=230, HL=50, ключ отбрасывается.GETSYM(479-493) — консьюмер.
3.3. Байты состояния (адресуются через LD IX,KEYFLAG на строке 519)
KEYFLAG (offset 0, alias K_LOCK, строка 45, init #02 — INS_L on):
| Бит | Имя | Значение |
|---|---|---|
| 7 | LANG_L |
язык RUS/LAT |
| 6 | PAUSE_L |
Pause Lock |
| 5 | RES5_L |
резерв (в комментариях помечено как историческое X_SHIFT) |
| 4 | RES4_L |
резерв |
| 3 | NUM_L |
Num Lock |
| 2 | SCRL_L |
Scroll Lock |
| 1 | INS_L |
Insert |
| 0 | CAPS_L |
Caps Lock |
Переключается: CAPS_X 668-671, LANG_X 674-690, INS_X 693-696,
NUM_X 699-702, PAUSE_X 705-718, SCL_X 721-724.
KEYCTRL (offset 1, alias K_SHIFT, строка 56, init #00):
| Бит | Имя |
|---|---|
| 7 | L_SHIFT |
| 6 | R_SHIFT |
| 5 | X_CTRL (любой Ctrl) |
| 4 | X_ALT (любой Alt) |
| 3 | L_CTRL |
| 2 | L_ALT |
| 1 | R_CTRL |
| 0 | R_ALT |
Устанавливаются в SHIFTS (799-836) на нажатии, сбрасываются в UNSHIFT
(738-797) на отпускании. Также чистятся в KBD_Receiver_Overrun (1240).
KEY_FLG (offset 2, alias KEYFLG, строка 67, init #00):
| Бит | Имя |
|---|---|
| 7 | FLAG_E0 — защёлкнут E0-префикс (extended) |
| 6 | FLAG_F0 — защёлкнут F0-префикс (release) |
| 5 | FLAG_E1 — E1-префикс (только если USE_E1_SCANCODE) |
| 4-1 | резерв |
| 0 | CTRL_SHIFT — Ctrl+Shift зафиксирован (для LANG_X при CHANGE_LANG_CTRL_SHIFT) |
SOUND_K (offset 3, строка 78, init #03 — оба звука включены):
| Бит | Имя |
|---|---|
| 7-2 | FLAG_S7..S2, резерв |
| 1 | SF_ALT — звук при смене языка |
| 0 | SF_BUFF — звук при переполнении буфера |
UNCODE (offset 4, WORD, строка 81) — последний отпущенный скан-код
(пишется в UN_KEY строка 663).
3.4. Таблица трансляции скан-кодов
XLAT_T(строки 21-30) — сырая таблица 144 байта, AT scancode (Set 2) → внутренний позиционный код 0..#5A.XLAT_T.Size = 144. Bounds-check вXLATна строке 860.XLAT(839-870) — собственно переводчик. Если защёлкнутFLAG_E0, жёстко мапит специальные коды (#11→#39 R-ALT,#14→#3A R-CTRL,#5A→#4E numpad Enter,#4A→#4A /,#7C→#47 PrintScreen); иначеL = XLAT_T[A].
3.5. ASCII-таблицы (MODULE ASCII_TABLES, строки 1285-1368)
Все таблицы ровно 90 байт (Size EQU 90):
ASCII_TABLES.ENGLISH(1287-1295) — без шифта, англASCII_TABLES.SHIFT_ENG(1297-1305) — с шифтом, англASCII_TABLES.CAPS_ENG(1307-1315) — Caps, англASCII_TABLES.CAPS_SHIFT_ENG(1317-1325) — Caps+Shift, англASCII_TABLES.RUSSIAN(1328-1336) — без шифта, рус CP866ASCII_TABLES.SHIFT_RUS(1338-1346)ASCII_TABLES.CAPS_RUS(1348-1356)ASCII_TABLES.CAPS_SHIFT_RUS(1358-1366)
Старые метки NORMTAB / SHIFTAB / CAPSTAB / SHF2TAB / NORMRUS / SHIFRUS / CAPSRUS / SHF2RUS сохранились ТОЛЬКО в закомментированной legacy-копии
K_SETUP (строки 1097-1167) и не являются живыми. Все
KEYMAP/READMAP используют ASCII_TABLES.*.
3.6. K_SETUP подфункции (диспетчер на строке 934)
INC B / DJNZ K_SND_R — цепочка DJNZ:
| B | Метка | Поведение |
|---|---|---|
| 0 | KEYMAP (937-972) / READMAP (974-1006) |
Запись/чтение одной из 8 ASCII-таблиц. HL= буфер, A bit7=0 запись, bit7=1 чтение, биты0-2 = номер подтаблицы 0..7. Объём LDIR = ASCII_TABLES.Size=90 байт. Неверный A → XOR A : SCF : RET |
| 1 | K_SND_R (1019-1023) |
A := SOUND_K |
| 2 | K_SND_W (1025-1030) |
SOUND_K := A |
| 3 | K_CURSOR_ON (1034-1038) |
Если VMODE показывает текстовый режим — JP Cursor_On |
| 4 | K_CURSOR_OFF (1042-1046) |
То же → JP Cursor_Off |
| ≥5 | K_SETUP.ERROR (1015-1017) |
A = #0E INVALID_ACCESS, SCF, RET |
3.7. Сканер клавиатуры — KEYSCAN (519-629)
LD IX,KEYFLAG— IX указывает на блок состояния.- В текущем билде сканер ПОЛЛИНГ (
KEYBOARD_INT_ENABLED=0): IF-блок строк 521-526 (который бы делалCALL .RESCAN, выход из INT) НЕ ассемблируется.KEYSCANпадает прямо в.RESCAN. На практике он вызывается изINTx38_Handler(DSS-MAIN.ASM:275-306), который сам висит наIM 1RST 38h. - Логика
.RESCAN(от строки 527):IN A,(Z84.SIO.Ch_A.Ctrl)— читаем RR0.- Если
bit0(Rx Character Available) = 0 →RET. - Иначе пишем 1 в Ctrl (указать на регистр 1), читаем обратно,
тестируем
bit5(Receiver Overrun) → ошибка →JP KBD_Receiver_Overrun(1228) — дренаж FIFO,OUT %00110000в WR0 для error reset, обнулениеKEYCTRL/KEY_FLG. - Иначе
IN A,(Z84.SIO.Ch_A.Data)(строка 538) — байт скан-кода. - Префиксы:
#F0→FULL_BF.F0_KEY: устанавитьFLAG_F0,JP .RESCAN.#E0→FULL_BF.E0_KEY: устанавитьFLAG_E0,JP .RESCAN.#E1→ еслиUSE_E1_SCANCODE=1устанавитьFLAG_E1, иначе игнорировать.
- Обычный байт +
FLAG_F0→UN_KEY(657-664):XLAT→UNSHIFTмодификаторов,UNCODE := HL(последний отпущенный). - Обычный байт без
FLAG_F0→XLAT→SHIFTS→ сбросFLAG_E0/E1→ приCHANGE_LANG_CTRL_SHIFT=1опционально установитьCTRL_SHIFT→INPCODE→PUTSYM→JP .RESCAN(продолжать дренировать FIFO, строка 628) — поскольку SIO имеет 3-байтовый Rx FIFO (см. §6.4).
3.8. INPCODE/RUSCODE/CONVERT (875-930)
INPCODE(875-906) — выбор одной из 4 английских таблиц на основеKEYCTRLбит шифта иKEYFLAG.CAPS_L. ЕслиLANG_Lвзведён →JP RUSCODE.RUSCODE(908-930) — то же для русских.CONVERT(901-906):SET 7,D(флаг keystroke),HL = таблица+L,E := (HL). После этогоPUTSYM.
3.9. Курсор и звук
Cursor(215-218) — переключатель:CPL.Flag, falls intoCursor_On.Cursor_On(220-253):BIOS.LP_GET_PLACE(#8E) → DE=Y/X, сохранить;BIOS.WIN_GET_SYM(#B4); вычислить глиф —NORM_ZG=#1Bобычно,INS_CUR_ZG=#9BприINS_L,CURSOR_ZG=#5Bдля фазы blink;BIOS.WIN_PUT_SYM.NoChangeZG(#01B5);LOCATE.Cursor_Off(257-284): обратное действие;JP LOCATE.cursor_interrupt(287-295): тик. Если.Flag != 0, декрементировать таймер; на нулеCALL Cursor.SETUP_CURSORS(298-375): один раз при старте подгружает кастомные глифы (#5B блок и #9B инверт.) в страницу шрифта (BANKTBL+TXTPAGE) черезBIOS.WIN_GET_ZG(#B8) → CPL →BIOS.WIN_SET_ZG(#B6).BEEP(1173-1192):DE=период,HL=повторы. Тогглит A=#10/#00 в(SP_SND.Beeper)= порт #FE бит 4.
3.10. Инициализация — KEYBOARD_INIT (1194-1224)
Последовательность для SIO Ch_A (стандартная Z80 SIO init):
DI- WR0 := 0 (clear pointer)
- WR4 := парам. (clock, stop, parity) — точные байты в коде, ср. с
Another/SPRINTER/TASM/Keybdrv.z80(там 9 байт init) - WR3 :=
#C1(Rx enable, 8 бит/символ) - WR5 :=
#62(0110'0010: бит3 Tx Enable = 0 → передатчик ВЫКЛЮЧЕН; биты5-6 = 8 бит/символ). Передача данных клавиатуре аппаратно отключена — см. §6.5 - WR1 :=
%00011001еслиKEYBOARD_INT_ENABLED=1, иначе 0 EI : RET
4. Конвенция вызова DSS API
- DSS:
LD C,Dss.Xxx(номер функции изShared_Includes/constants/dss_equ.inc, модульDss:),RST 10(=ToDSS). Параметры — в регистрах согласно спецификации (см. Shared_Includes/Docs/DSS_1.71_Functions.asm, раздел 6). - Выход:
CF=0— успех (значения в регистрах согласно функции);CF=1— ошибка,A=код ошибки (DSS_Error.sys.*). - BIOS:
LD C,BIOS.XXX(Shared_Includes/constants/BIOS_equ.inc, модульBIOS:),RST 08(=ToBIOS). Перед вызовом необходимDIиOUT (#7C),0для переключения банка BIOS; стек должен быть в#8000-#BFFF. Подробно — Shared_Includes/Docs/BIOS functions.asm, строки 1-60. - Подфункции: верхний байт BC (т.е.
B) — индекс подфункции; константа обычно собрана какEQU subfn*256 + .Func. Пример:Dss.K_SETUP.SetSoundVars EQU 2*256+#36.
5. Compile-time флаги (DSS/defines.inc)
| Флаг | Текущее | Что переключает в KEYINTER |
|---|---|---|
KEYBOARD_INT_ENABLED (стр. 21) |
0 |
0 = поллинг из INT 38h; 1 = SIO Ch_A прерывание (WR1 := #19), отдельный обработчик в начале KEYSCAN |
CHANGE_LANG_CTRL_SHIFT (стр. 22) |
1 |
1 = переключение языка Ctrl+Shift (через CTRL_SHIFT бит и UNSHIFT.CHECK_CTRL_SHIFT); 0 = Ctrl+Space (legacy путь IFN CHANGE_LANG_CTRL_SHIFT строки 585-591) |
BREAK_PROCESS_CODE (стр. 23) |
#AC00 |
Cooked-код Ctrl+C для WaitKey/EDIT → DSS_Error.sys.USER_ABORT (#25) |
USE_E1_SCANCODE (стр. 24) |
0 |
1 = обрабатывать E1 (Pause/Break), 0 = игнорировать |
ENABLED_KEYBOARD_CONTROL (стр. 24) |
0 |
1 = компилировать FAKE_SHIFT (игнор AT fake-shift E0 12/E0 59 вокруг серых nav-клавиш). 0 = выключено: на железе SIO Tx отключён (WR5=#62), хост не может слать 0xED, клавиатура не знает NumLock → fake-shift не приходит. Включать, только если появится двунаправленный канал к клавиатуре. См. §6.5 |
SHORT_RSTx10_TABLE (стр. 10) |
0 |
Должен быть 0 (либо ≥#37), иначе клавиатурные слоты не войдут в API table |
COMPILE_UNUSED_CODE (стр. 11) |
0 |
Гейтит legacy/debug куски |
Также в defines.inc — мышиные флаги (MOUSE_INT_ENABLED, ...) которые
делят с клавиатурой инфраструктуру SIO/IRQ.
6. Аппаратура Sprinter / Z84C15
6.1. Карта внутренних портов Z84C15
Shared_Includes/constants/SP2000.inc, MODULE Z84 (строки 2111-2127):
| Порт | Назначение |
|---|---|
#10..#13 |
CTC.Ch_0..3 (таймеры) |
#18 |
Z84.SIO.Ch_A.Data — PS/2 клавиатура DAT_A |
#19 |
Z84.SIO.Ch_A.Ctrl — PS/2 клавиатура COM_A |
#1A |
SIO.Ch_B.Data (DMOUSE) |
#1B |
SIO.Ch_B.Ctrl (CMOUSE) |
#1C..#1D |
PIO.Port_A — LPT1 (принтер) |
#1E..#1F |
PIO.Port_B — LPT2 |
#F0..#F1 |
WDT |
#F4 |
IntPrior_Reg |
#EE/#EF |
SYS.Control / SYS.Data |
Так что коммит e334545 "M_INT: RFI is CH-A ONLY (per Z80 SIO spec)"
относится к SIO Ch_A (клавиатура) — принтер на PIO, не на SIO.
6.2. Порт бипера и банкование
SP_SND.Beeper= порт#FE, бит 4 (%00010000). Прямая запись из KEYINTER (нет BIOS-функции для звука).SLOT0..SLOT3=#82, #A2, #C2, #E2— внешние порты переключения страниц памяти на окна#0000/#4000/#8000/#C000.SET_PAGE_XизDSS_MACROSES.Z80(137-143) использует SLOT3 +BANKTBL[page].TXTPAGE— индекс вBANKTBLдля страницы шрифта (используется вSETUP_CURSORSстрока 304). Если EQU не определён в снимке репо, он приходит из внешнего SP2000-инклюда (см.Build/Variables.incпосле первой сборки).
6.3. Прерывания
- IM 1, единый вектор
#FFдля кадрового И клавиатурного прерывания. - Различие: после входа в
INT 38h(DSS-MAIN.ASM:275), читать порт#19(SIO Ch_A Ctrl/RR0);bit0=1→ пришёл байт от клавиатуры (в FIFO). Это и делаетKEYSCAN.RESCAN. - SIO Ch_A имеет Rx FIFO на 3 байта —
sp2000.pdf §9.4явно предписывает: «после обработки одного байта проверять, не пришло ли ещё, и возвращаться только если FIFO пуст». - Включение режима, при котором клавиатурный INT работает:
Port_All_Mode=#204E, битыSPECTRUM_MODE_OFF=1(бит 0) иSTOP_KBD_INT_OFF=8(бит 3) (см.SP2000.inc). - Активный указатель ISR хранится в
SYS_PAGE.INT_ADRESS = #FEC124,INT_PAGE = #FEC126,INT_ID = #FEC127(сентинель#AA— установлено).
6.4. Z80 SIO регистры (для модификаций инициализации)
- WR0 — command + pointer на следующий WR/RR.
- WR1 — interrupt enable (бит 0 ExtINT, биты 1-2 Tx Int, биты 3-4 Rx Int
mode
%01100= INT_ON_RXBYTE), бит 5 Wait/Ready. - WR2 — Interrupt vector (только Ch_B по Z80 SIO spec — нерелевантно для клавиатуры).
- WR3 — Rx control: bit0 Rx Enable, биты 6-7 Rx bits/character.
- WR4 — clock multiplier, stop bits, parity.
- WR5 — Tx control: бит 3 Tx Enable, биты 5-6 Tx bits/character.
- RR0 — статус: бит 0 = Rx Character Available (то что тестирует KEYINTER), бит 1 = Int Pending (CH-A only), бит 2 = Tx Buffer Empty, бит 5 = Sync/Hunt, бит 6 = CTS, бит 7 = Break.
- RR1 — special Rx conditions: framing/parity/overrun errors.
- Полный datasheet — Zilog Z80 SIO Technical Manual (UM008); в репо его нет, см. ссылки в §8.
6.5. Команды PS/2 хост→клавиатура (host commands)
Если потребуется управлять светодиодами Caps/Num/Scroll или autorepeat, то протокол описан в /Users/tolik/Documents/SP_Projects/ASM/Another/Docs/Keyboard/KBD.TXT:
#FFreset+self-test,#FEresend,#F4enable scanning,#F5disable+default,#EDset LEDs (bit0 Scroll, bit1 Num, bit2 Caps),#F3set typematic rate+delay (биты 6-5 delay 250/500/750/1000 ms, биты 4-0 rate 6..30 cps),#F2read keyboard ID,#FB-#FD,#F7-#FAper-key/all-keys typematic.
Передача клавиатуре аппаратно невозможна в текущей конфигурации: KEYBOARD_INIT
ставит WR5 = #62 (бит3 Tx Enable = 0), т.е. передатчик SIO Ch_A выключен.
Поэтому host-команды (0xED Set LEDs, autorepeat и др.) не отправляются и не могут.
Важное следствие про fake-shift: по стандарту AT/PS-2 клавиатура шлёт fake-shift
(E0 12 / E0 59 вокруг серых extended nav-клавиш — стрелки/Ins/Del/Home/End/PgUp/PgDn)
ТОЛЬКО когда её внутренний Num Lock-LED включён. А LED выставляет ТОЛЬКО хост командой
0xED (нажатие Num Lock для клавиатуры — обычный скан-код 0x77, своё состояние она не
меняет). Раз 0xED послать нельзя → клавиатура всегда считает Num Lock выключенным →
fake-shift на реальном железе не генерируется, выделенные стрелки всегда приходят
чисто (E0 6B и т.п.). Обработка fake-shift реализована в FAKE_SHIFT, но компилируется
только при ENABLED_KEYBOARD_CONTROL=1 (§5) — на железе это мёртвый код, нужен лишь для
эмуляторов (MAME msnat сам моделирует Num Lock+fake-shift, чего на железе быть не может).
Чтобы реально слать команды — включить Tx (WR5 бит3=1) и писать в #18 после проверки
готовности Tx Buffer (RR0 bit2).
6.6. CMOS-привязка
CMOS_CELL.BootUpParams=#0E,.Mask.Language = %0000'0100— пресет языка при загрузке.CMOS_CELL.Typematic=#0F, маски.Enabled %1000'0000,.Delay %0110'0000,.Rate %0000'0111.- Доступ через
BIOS.CMOS_RD (#F6) / CMOS_WR (#F7)или прямые портыCMOS.Port.{Data.Read=#FFBD, Data.Write=#BFBD, Address.Write=#DFBD}. - Сейчас KEYINTER НЕ читает CMOS — состояние локов и языка
сбрасывается на каждый boot к дефолтам. Если нужна persistence,
добавлять в
KEYBOARD_INITили в высокоуровневыйAUTOEXEC.BAT.
7. Reference материалы
7.1. В репозитории
| Документ | Что искать |
|---|---|
| Shared_Includes/Docs/sp2000.pdf | §1.1 Sp2000 hardware (p.4), §1.3 block diagram (p.5), §9 Внутренние порты Z84C15 (p.21), §9.4 AT-Клавиатура p.25-26 (COM_A/DAT_A, sample code), §13.2 карта портов p.31 |
| Shared_Includes/Docs/BIOS functions.asm | Раздел 5 «Функции печати и управления режимом экрана» (стр. 628-1100): LP_PRINT_SYM #82, LP_PRINT_ALL #81, LP_SET_PLACE #84, LP_GET_PLACE #8E, WIN_GET_SYM #B4, WIN_PUT_SYM #B5, WIN_GET_ZG #B8, WIN_SET_ZG #B6, LP_SCROLL_UD #8A, LP_PR_LINE_DIR #E0. Конвенция вызова — стр. 1-60 |
| Shared_Includes/Docs/DSS_1.71_Functions.asm | Раздел 6 «Функции ввода с клавиатуры» (строки ~364-446) — авторитетная спецификация на функции #30..#37 с корректировками из последней сессии (Pause Lock bit6, CTRLKey A=#FF/A=0, K_CLEAR INVALID_FUNCTION, K_SETUP B=0 это Get/Set table transfer) |
| Shared_Includes/constants/dss_equ.inc | Dss.WaitKey..TestKey (137-149), Dss.K_SETUP.* (144-148), DSS_Error.sys.* (280-336), Приложение А — позиционные коды клавиш (547-595) |
| Shared_Includes/constants/BIOS_equ.inc | BIOS.* функции (см. §7.1 ниже) |
| Shared_Includes/constants/SP2000.inc | MODULE Z84 (2111-2127), SP_SND, ACEX.*, ZXKeys.Line_* (#FEFE..#7FFE legacy ZX), Port_All_Mode #204E, SYS_PAGE.INT_*, CMOS_CELL.* |
| Shared_Includes/constants/zx_char_codes.inc | Control codes ZX: .cursor_left=#08 (backspace), .right=#09 (== TAB!), .cursor_down=#0A, .cursor_up=#0B, .delete_left=#0C, .carriage_return=#0D, и т.п. |
| Shared_Includes/constants/dss_errors.z80 | Строки ошибок: #01 Invalid function, #0E Unknown operation, #13 Access denied, #20 Operation not supported, #25 User abort, #27 Unexpected app termination |
| DSS/DSS_MAP.TXT | Карта памяти ядра — #0400-#043F SBUF, #0440-#0BD1 KEYBOARD DRIVER |
| DSS/CHANGES.LOG | Хронология; ECHOKEY cursor 2003-02-10, txt 40x32 cursor bug, OVR cursor square |
| DSS/KNOWN.BUG | §3.3-3.4 — legacy ECHOKEY/PUTCHAR закомментированы, можно убирать; буферы #100/#80 без bounds-check |
7.2. В Another/ (референсы, не собираются)
| Файл | Зачем |
|---|---|
| /Users/tolik/Documents/SP_Projects/ASM/Another/Docs/Sprinter_Programming.pdf | p.22 «Прерывания» (вектор #FF, бит 0 порта #19), p.25-26 «Клавиатура» (пример K_SETUP layout-table, 9×16=144 байта на подтаблицу, Ctrl+Space layout switch), p.27 коды ошибок |
| /Users/tolik/Documents/SP_Projects/ASM/Another/Docs/Keyboard/KBD.TXT | PS/2 host→keyboard команды (см. §6.5) |
| /Users/tolik/Documents/SP_Projects/ASM/Another/Docs/Keyboard/AT-Keyboard.pdf | Scan Code Set 2 на 3 страницах |
| /Users/tolik/Documents/SP_Projects/ASM/Another/SPRINTER/TASM/Keybdrv.z80 | Минималистский standalone-драйвер клавиатуры Дениса Паринова (517 строк): полная init-последовательность 9 байт WR0..WR5 на ComA=#19, чтение DataA=#18. Полезно при отладке железа отдельно от DSS |
| /Users/tolik/Documents/SP_Projects/ASM/Another/SPRINTER/TASM/Scantabl.z80, Scantabl2.z80 | Альтернативные scancode→symbol таблицы (для сверки XLAT_T) |
| /Users/tolik/Documents/SP_Projects/ASM/Another/SPRINTER/SRC/cpm3-s/SCAN.PLM | CP/M-3 Sprinter scan-логика на PL/M (третий независимый референс) |
НЕ нужно: Another/scancode.inc |
NASM/x86 BIOS INT16h (Sergey Kiselev Micro-8088). Другая архитектура, пропустить |
7.3. Внешние (нужно скачать)
- Zilog Z80 SIO Technical Manual (UM008) — главы 3-4: полный layout
WR0..WR7/RR0..RR2.sp2000.pdf §9явно отсылает туда («Ссылка на описание PIO, SIO и т.д. от Zilog»). - Zilog Z84C15 Product Specification — карта внутренних портов, CTC/PIO timing.
8. История изменений KEYINTER (git log)
git log --oneline -- DSS/KEYINTER.ASM:
9235af1RUS keys fixf5eacf4KEYINTER.ASM: russian chars fixe334545M_INT: RFI is CH-A ONLY (per Z80 SIO spec) — сохранять CH-A-only RFI семантику при правках ISR73c7ab5K_SETUP show/hide cursor sub-fns12c0287ECHOKEY scroll fixeae2582PutChar via BIOS LP_PR_LINE_DIR (#E0)23fa77aXLAT_T overflow + Ctrl+Cff2f2b9layout-switch combo (Ctrl+Space vs Shift+Ctrl) compile flag54b1e80SIO experiments
При работе всегда смотреть git log -p DSS/KEYINTER.ASM | head -200 —
часто старые комментарии R-номерами (R01, R02 ...) поясняют исторические
правки.
9. Полезные команды на хост-системе
- Сборка ядра: VS Code task
Build SYSTEM.DOS(или[NEW]с инкрементом билда). Из терминала примерно:cd "/Users/tolik/Documents/SP_Projects/ASM/GIT/Estex DSS" sjasmplus --syntax=afw --fullpath -Wno-shortblock \ --lst=Build/DSS-MAIN.LST --raw=Build/DSS/SYSTEM.DOS \ --exp=Build/Variables.inc DSS/DSS-MAIN.ASM - Деплой в MAME: VS Code task
Copy all files to MAME→ запускает RUN/Image.sh (zsh,hdfmonkey). - Запуск MAME: VS Code task
Run MAME. - Прочитать CP866-файл с правильной кодировкой:
iconv -f CP866 -t UTF-8 DSS/KEYINTER.ASM | less - grep по бинарно-определяемым файлам (CP866 ломает heuristic):
Флагgrep -anE "WAITKEY|KEYSCAN" DSS/KEYINTER.ASM-aфорсит text-режим. - Просмотр с табами:
sed -n '519,540p' DSS/KEYINTER.ASM | tr -d '\r' | cat -tv - Дамп диспетч-таблицы:
sed -n '337,396p' DSS/DSS-MAIN.ASM
10. Стилевые соглашения
- Кодировка CP866 (DOS Russian), переводы строк CRLF. Edit'ы должны их сохранять.
- Отступы — табы. Комментарии справа от инструкции выровнены табами до
колонки
;. При вставке новых комментариев-продолжений использовать 4 таба +;. - Рамки секций —
;████...████(символ █ = U+2588). Не ломать. - Стиль меток: метки целиком капсом (
KEYSCAN,WAITKEY), вложенные через.(KEYSCAN.RESCAN,K_SETUP.ERROR,Cursor_Off.Flag). - Self-modifying code маркирован комментариями вроде
;R01,;R13с ревизионными номерами — НЕ удалять без причины (это история). - Хроноразметка
[ ] DD/MM/YYYY/[x]для done — в комментариях.
10а. Работа с файлами CP866 + CRLF (практические рецепты)
Это главный источник тонких багов при редактировании. Прочитай эту секцию ДО первой правки.
10а.1. Что именно в CP866
- Все
DSS/*.asm,DSS/*.ASM,DSS/*.inc,DSS/*.Z80. - Все
Shared_Includes/constants/*.incиShared_Includes/Docs/*.asm. DSS/CHANGES.LOG,DSS/KNOWN.BUG,DSS/DSS_MAP.TXT.Another/SPRINTER/...,Another/Docs/Keyboard/KBD.TXT.
PDF в Shared_Includes/Docs/sp2000.pdf и Another/Docs/*.pdf — отдельная
история (текст внутри PDF в своей кодировке, см. pdftotext).
UTF-8 в этом проекте бывает только в *.md (вроде этого файла), *.json
(VSCode-конфиги), *.sh, *.lua и .gitmodules.
10а.2. Байтовая карта CP866 (минимум, который надо помнить)
| Байт(ы) | Что |
|---|---|
#80..#8F |
А..О (рус. заглавные, первая половина) |
#90..#9F |
П..Я (рус. заглавные, вторая половина) |
#A0..#AF |
а..п (рус. строчные, первая половина) |
#B0..#B2 |
░ ▒ ▓ (shading) |
#B3..#DA |
псевдографика │ ─ ┌ ┐ ┘ └ ┤ ├ ┬ ┴ ┼ … и двойные ║ ═ |
#DB |
█ — full block, из которого строятся рамки ;██..██ в этом проекте |
#DC..#DF |
▄ ▌ ▐ ▀ |
#E0..#EF |
р..я (рус. строчные, вторая половина) |
#F0, #F1 |
Ё ё |
#FC |
№ |
#FE |
■ |
Важно:
█= один байт#DBв CP866, а в UTF-8 это три байтаE2 96 88. Поэтому строчка;████████длиной ~110 символов в CP866 занимает ~110 байт, а в UTF-8 — ~330 байт.- Если случайно конвертнуть файл в UTF-8 и забыть конвертнуть обратно, размер файла резко вырастает, выравнивание комментариев плывёт, а sjasmplus может выдать странные ошибки на строковых литералах.
10а.3. Просмотр и поиск
Просмотр через тулзу Read: русский текст может отображаться как заполнители или мусор, потому что Read декодирует как UTF-8. Полагаться на это для содержания комментариев нельзя — но для проверки кода (меток, инструкций Z80, hex-литералов) подходит.
Просмотр в человеческой форме — через Bash + iconv:
iconv -f CP866 -t UTF-8 DSS/KEYINTER.ASM | less
iconv -f CP866 -t UTF-8 DSS/KEYINTER.ASM | sed -n '519,540p'
Просмотр с табами/CRLF:
sed -n '519,540p' DSS/KEYINTER.ASM | iconv -f CP866 -t UTF-8 | cat -tev
# ^I = табы, $ = конец строки (CRLF будет показан как ^M$)
grep по ASCII-меткам — -a обязательно:
grep -an "WAITKEY\|KEYSCAN\|K_SETUP" DSS/KEYINTER.ASM
Без -a grep с какого-то порога считает CP866 бинарным и молча выдаёт
только имя файла.
grep по русским словам — через pipe iconv (номера строк сохраняются, т.к. количество переводов строк не меняется):
iconv -f CP866 -t UTF-8 DSS/KEYINTER.ASM | grep -an "клавиш"
Альтернатива: сконвертить запрос в CP866 и грепать байтами (но это крайне неудобно — pipe-метод лучше).
Полный hex-дамп строки (для разбора, что именно лежит в файле):
sed -n '519p' DSS/KEYINTER.ASM | xxd | head
10а.4. Редактирование — критическое
Главная проблема Edit-инструмента: он работает на байтовом уровне.
Если файл в CP866, а old_string в твоей tool-call'е содержит русский
текст в UTF-8, то байты НЕ совпадут и Edit упадёт с String not found.
Если случайно new_string запишется UTF-8'ом в CP866-файл, файл
ломается локально (на конкретной строке).
Безопасные паттерны:
-
ASCII-only анкоры (90% случаев). Выбирай
old_stringтак, чтобы он состоял из инструкций Z80, меток, hex-литералов и английских/латинских фрагментов. Кириллический хвост строки пройдёт «как есть» — Edit не трогает то, что не в old_string.old_string: " RST ToDSS\t\t\t; "Это сработает, даже если справа после
;идёт русский комментарий. -
Если кириллица обязана быть в old_string (например, надо заменить текст внутри русского комментария) — сначала сконвертировать фрагмент в CP866 через Bash:
echo -n "максимальный номер диска" | iconv -f UTF-8 -t CP866 | xxdполучишь байты, и можешь искать/заменять через
sedилиperlсLC_ALL=C(байтовый режим), а не через Edit. -
Удобный трюк: убедиться, что
old_stringуникально совпадает с ASCII-частью комментария или ID:old_string: " LD A,disk_num\t\t\t; " new_string: " LD H,device_num\t\t; "и оставить хвостовую кириллицу в покое.
-
Не вставляй русский текст напрямую через Edit, если файл в CP866. Если требуется добавить русский комментарий, сделай это через Bash:
echo -n "; новый комментарий" | iconv -f UTF-8 -t CP866 >> /tmp/append # затем вставляй байты через sed/perlИли согласись писать новые комментарии на английском/латинице (ASCII) — это безопаснее, и стиль файла это терпит.
10а.5. Сохранение CRLF
- Файлы используют
\r\n(Windows-стиль), не\n. - При вставке новых строк через Edit убедись, что в
new_stringесть\rперед\n. Если Edit-инструмент это съест и поставит только\n, файл получит смешанные line endings — sjasmplus съест, ноgit diffбудет показывать всю окрестность как изменённую, и в Windows-редакторах текст рассыпется. - Альтернатива: после Edit-сессии нормализуй переводы строк:
# Проверить: file DSS/KEYINTER.ASM # Должно быть: "with CRLF line terminators" # Если стало "with CRLF, LF line terminators" — есть смесь, чинить: perl -i -pe 's/\r?\n/\r\n/g' DSS/KEYINTER.ASM
10а.6. Проверка после правки (обязательная)
file DSS/KEYINTER.ASM
# Ожидается: "Non-ISO extended-ASCII text, with CRLF line terminators"
# НЕ должно быть "UTF-8 Unicode text" или "with LF line terminators"
# Проверка, что рамки █ не превратились в UTF-8 (3 байта):
grep -c $'\xDB\xDB\xDB' DSS/KEYINTER.ASM
# Должно быть положительное число (несколько штук). 0 = рамки сломаны.
# git diff с пониманием бинарного представления:
git diff DSS/KEYINTER.ASM | cat -v | head -50
# Cyrillic должен показываться как M-X M-X последовательности байт
10а.7. Откат, если что-то сломалось
# Один файл — мгновенный откат:
git checkout HEAD -- DSS/KEYINTER.ASM
# Если уже закоммитил битый файл — посмотреть предыдущую версию:
git show HEAD~1:DSS/KEYINTER.ASM | iconv -f CP866 -t UTF-8 | less
10а.8. Конвертация всего файла «туда-обратно» (последнее средство)
Если правок очень много и проще править в UTF-8:
# 1. Конвертация в UTF-8 + переход на LF (так удобнее редактировать):
iconv -f CP866 -t UTF-8 DSS/KEYINTER.ASM | tr -d '\r' > /tmp/KEYINTER.utf8
# 2. Правки в /tmp/KEYINTER.utf8 любым инструментом (Edit/sed/perl).
# 3. Обратно в CP866 + восстановление CRLF:
iconv -f UTF-8 -t CP866 /tmp/KEYINTER.utf8 | perl -pe 's/\r?\n/\r\n/g' > DSS/KEYINTER.ASM
# 4. Проверка:
file DSS/KEYINTER.ASM
git diff --stat DSS/KEYINTER.ASM
Риск: если в файле есть байты, не имеющие пары CP866↔UTF-8 (теоретически
вряд ли в этом проекте, но возможно при кривых правках в истории), iconv
ругнётся с illegal input sequence. Тогда iconv -c пропустит проблемные
байты — но они потеряются. Безопаснее в этом случае работать
ASCII-only анкорами через Edit.
10а.9. VS Code как замена тулинга
Если хочется визуальной правки:
- VS Code: правый-нижний угол →
Reopen with Encoding → Cyrillic (CP866). - Для постоянной ассоциации
.asm/.ASM/.incс CP866 добавить в.vscode/settings.json:{ "[asm]": { "files.encoding": "cp866" }, "files.eol": "\r\n" } - При сохранении проверь нижнюю панель — индикатор должен показывать «CP866» и «CRLF», а не «UTF-8» / «LF».
10а.10. Чек-лист: «изменения сохранят кодировку»
file <изменённый.asm>→Non-ISO extended-ASCII text, with CRLF line terminatorsgrep -c $'\xDB\xDB\xDB' <изменённый.asm>> 0 (если в файле были рамки)git diff <изменённый.asm> | wc -lразумного размера (не в 3× больше ожидаемого — иначе UTF-8 раздувание)- Сборка
Build SYSTEM.DOSпроходит без warning'ов вроде «invalid character in string literal» - Открыть результат в VS Code с CP866 — комментарии читаются по-русски
11. Что важно помнить, прежде чем менять KEYINTER
- Файл подключается
INCLUDE'ом вDSS-MAIN.ASM:402, сборка идёт как монолит. Все символы видны глобально. - Состояние клавиатуры — RAM-only: не персистится в CMOS. Любая настройка (язык, локи, sound flags, ASCII-таблицы) теряется на reset/power-off, если её не сохранить через AUTOEXEC.BAT.
- Клавиатура и мышь делят окно INT 38h — мышь идёт после
KEYSCAN. Долгая работа вKEYSCANзадерживает мышь. - SIO Ch_A FIFO = 3 байта —
.RESCANдолжен дренировать его полностью за один заход (текущая реализация это делает черезJP .RESCANна строке 628). KEYBOARD_INT_ENABLED=0— текущий билд поллит, не использует SIO INT. Если хочется перейти на прерывания, нужно переключить флаг, убедиться чтоKEYBOARD_INITпишет WR1=#19, и проверить что обработчик RFI вDSS-MAIN.ASMкорректно настроен (Z80 SIO Ch_A only).- При правке HEAD/HOST убедиться, что
RES 6,(HL)сохранён — это обеспечивает wrap modulo 64. Если изменишь размерSBUF, поменяй и маску (бит-логикаRES 6работает только для 64 = #40). - При правке
XLAT_T— длина 144 байта (Size), bounds-checkCP XLAT_T.Sizeна строке 860. Если расширяешь, проверь, что не натыкаешься на следующую область (CURRENT DIR NAME BUFFERпо DSS_MAP.TXT идёт от #1343). - Двойственность
Dв записях буфера: D[7] = «keystroke flag», D[6:0] = позиционный код 0..#5A.INPCODE/CONVERTставит бит 7 черезSET 7,D— это видимый паттерн. - При правке ASCII-таблиц — каждая ровно 90 байт, проверяется
ASSERT .Size = Size. Если меняешь размер, меняй иSize EQU 90на строке 1286. - Звук работает в обход BIOS — прямой
OUT (SP_SND.Beeper),A(порт #FE бит 4). Это запрещает звуковые эффекты, если процесс держит порт #FE для других целей (ZX-режим). Учитывать при модификацияхBEEP/FULL_BF/LANG_X.
12. Чек-лист первого подхода
Сначала открой и прочитай (именно в таком порядке):
- DSS/KEYINTER.ASM целиком (1371 строка).
- DSS/DSS-MAIN.ASM: строки 187-213 (
RST_10), 275-310 (INTx38_Handler), 337-396 (DSS_API_TABLE), 402 (INCLUDE), 423 (BANKTBL), 567-600 (boot-флоу). - DSS/defines.inc (короткий).
- Shared_Includes/Docs/DSS_1.71_Functions.asm, раздел 6.
- Shared_Includes/constants/dss_equ.inc, приложение А (позиционные коды).
Another/Docs/Sprinter_Programming.pdfp.22, p.25-26 (страницы 25-26 содержат пример K_SETUP layout-table).sp2000.pdf§9.4 (PDF, страница ~25-26).- Скачать Zilog Z80 SIO Technical Manual (UM008), если будешь трогать
KEYBOARD_INITилиKBD_Receiver_Overrun.
Только после этого формулируй гипотезу и план правок.
13. Контрольные вопросы перед коммитом
- Тестовая сборка
Build SYSTEM.DOSпрошла без ошибок? - Кодировка файлов сохранена CP866/CRLF? (
file DSS/KEYINTER.ASMдолжен показатьNon-ISO extended-ASCII text, with CRLF line terminators) - Табы и выравнивание комментариев не нарушены?
- Если изменён публичный API (#30..#37) — обновлён ли Shared_Includes/Docs/DSS_1.71_Functions.asm?
- Если изменены константы (
Dss.K_SETUP.*, ASCII-таблица size, коды ошибок) — обновлён лиdss_equ.inc? - Если изменена инициализация SIO — проверено, что коммит совместим
с
e334545 "RFI is CH-A ONLY"? - MAME-прогон (
Run MAME/Copy all files to MAME): клавиатура реагирует, переключение языка работает, INS/CAPS/NUM/SCRL ведут себя ожидаемо? - Echo (
ECHOKEY) рисует курсор корректно во всех текстовых режимах (40×32, 80×32)? XLAT_Tне вылез заSize=144?
Удачи.