SysTools Logo SysTools


Разработка программ для текстового редактора NewsMaster ("Журналист").
Часть 1 из 3: клавиатурный драйвер


Эта статья была специально написана для публикации в номере N27'2019 журнала Downgrade.



640x400 NewsMaster 1.0 title screen
Титульный экран NewsMaster версии 1.0

Редактор NewsMaster, разработанный фирмой UNISON WORLD и выпущенный в 1986 году (первая версия 1.0), является текстовым процессором по типу Microsoft Word. Программа не только позволяет использовать разнообразные шрифты, но и делать разбивку страниц на колонки и столбцы, а также вставлять рисунки со всевозможными типами обтекания текстом из поставляющегося набора библиотек разнообразного клипарта. И, конечно же, производить вывод готового документа на печать - для этого через комплект внешних драйверов была предусмотрена поддержка весьма солидного количества принтеров того времени. Сам же редактор работает в чёрно-белом графическом режиме 06h с разрешением 640x200 пикселей.


640x400 Russian NewsMaster title screen
Титульный экран русской версии - краткая информация о переводчиках

В конце 80-х - начале 90-х редактор версии 1.0 попал в СССР и был переведён на русский язык отделом 260 (адаптация И К) - к сожалению, кроме этих скудных данных с титульного экрана, про перевод или тех, кто над ним работал, больше ничего неизвестно. При переводе NewsMaster стал "Журналистом" - именно под этим именем он и стал известен широкому отечественному пользователю. Среди всевозможных применений редактора на время его выхода стоит отметить, например, печать мелкого, но читаемого текста на документах (расшифровки и прочее) - именно русская версия "Журналиста", с мелкими, но отчётливо читающимися русскими шрифтами, не раз приходила на помощь и выручала в подобных ситуациях.


640x400 Russian NewsMaster main menu
Главное меню русской версии программы

Посмотреть и загрузить программу можно с сайта Old-Dos:

http://old-dos.ru/files/file_4512.html

Несмотря на свой солидный возраст, "Журналист" чувствует себя отлично не только под последними версиями DOS или DOSBox, но даже под NTVDM в Windows XP.

Однако одним из недостатков русского перевода было то, что русские шрифты были рассчитаны на раскладку JCUKEN, а не QWERTY, в результате чего русские буквы не совпадали с теми, что нанесены на клавиши клавиатуры. Вероятно, и сами авторы делали перевод под JCUKEN-клавиатуру.

Давайте попробуем этот недостаток устранить.


640x400 Russian NewsMaster document example
"Журналист", пример документа

Замечание. Текст ниже предполагает, что читатель на минимальном уровне знаком с языком программирования Assembler, а также работой прерываний и сегментной организацией памяти в DOS, потому что эти вещи выходят за рамки данной статьи. Далее будут разобраны только отдельные технические моменты, а также показано, как и почему для использования были выбраны именно они.

Самым простым и быстрым для реализации решением будет создание своего резидентного клавиатурного драйвера под DOS, который будет включаться и отключаться (для использования английских шрифтов) по нажатию определённой клавиши на клавиатуре. Проблема с невозможностью "нормального" перевода шрифтов "Журналиста", со вставкой туда русских букв в дополнение к английским, заключается ещё и в том, что помимо разбора файла со шрифтами (неизвестный формат), придётся ещё и как-то модифицировать программу, потому что любые символы, код которых более 7Fh (127), воспринимаются ей как управляющие клавиши (F1, F2, ...) - вероятно, именно в силу этого авторы перевода и были вынуждены пожертвовать английскими буквами и частью знаков пунктуации, чтобы разместить на их месте русские буквы.


640x400 Russian NewsMaster font selection
"Журналист", выбор шрифта

Но прежде чем начинать что-то делать, давайте оценим входные данные, граничные условия и прикинем примерный план работ.

Во-первых, резидентный драйвер - это хорошо, но писать именно полноценную резидентную программу не стоит, потому что это в разы увеличит сложность разработки: нужно будет как-то решать вопрос с предотвращением повторного запуска драйвера, а также, по-хорошему, и выгрузкой из памяти без перезагрузки, что при запуске каких-либо программ после драйвера (а уж тем более резидентных и тоже перехватывающих клавиатурное прерывание) будет приводить к дополнительным лишним заморочкам. Поэтому ограничимся простым загрузчиком такого вида:

а) Запуск загрузчика.

б) Загрузчик перехватывает клавиатурное прерывание 09h и через него работу с клавиатурным буфером.

в) Загрузчик запускает дочерний процесс NEWS.EXE, т.е. "Журналиста".

г) После завершения NEWS.EXE управление возвращается загрузчику, который восстанавливает клавиатурное прерывание назад и завершает свою работу.

По подобной схеме работают многие "трейнеры" и "кряки" для DOS-игр.

Отдельно стоит упомянуть ситуацию, когда дочерний процесс сам перехватывает клавиатурное прерывание для своих нужд, но "Журналист" этого не делает, ему вполне хватает и стандартных сервисов DOS.


640x400 NewsMaster clipart selection
NewsMaster, выбор клипарта из библиотеки

Во-вторых, неплохо будет сразу определиться с клавишей для переключения. Для удобства возьмём Scroll Lock - эта клавиша практически никем не используется, а световой индикатор на клавиатуре будет наглядно и просто подсказывать текущее состояние режима работы - включёно или выключено [1].

В-третьих, нужно разобраться, какой русской букве какой код соответствует. Для начала будет неплохо войти в редактор и напечатать там все четыре ряда клавиш на клавиатуре - цифры и буквы (см. скриншот ниже), а также снова их, но уже с нажатой клавишей Shift [2].

И здесь сразу можно заметить подвох - среди напечатанных букв отсутствует "ъ" (маленький твёрдый знак). Выясним, есть ли он вообще среди набора символов шрифта. Для этого будем печатать символы вручную при помощи комбинации клавиш Alt+число. Начнём перебирать все печатные символы: с 32 (пробел, его можно пропустить и начать с 33) и до 127 включительно. Цифры для числа вводятся на цифровой клавиатуре справа (keypad) следующим образом: нажимаем и удерживаем Alt, затем вводим номер (например, 33), после чего отпускаем Alt. Итак: Alt+33 даст символ "!", Alt+34 - символ '"' и так далее. К счастью, маленький твёрдый знак действительно оказался в наборе шрифта, но под номером 7Fh (127) - это, увы, непечатаемый символ, который иначе как через Alt+число набрать невозможно. Впрочем, для того и пишем программу, чтобы это исправить.

Теперь разберёмся со скан-кодами клавиш клавиатуры, у которых будем менять код символа. Но для начала стоит немного пояснить, как работает клавиатурный буфер в DOS. Важными для нашей программы являются три адреса в оперативной памяти:

0040:001A - адрес указателя на голову (head) клавиатурного буфера (2 байта, WORD);

0040:001C - адрес указателя на хвост (tail) клавиатурного буфера (2 байта, WORD);

0040:001E - адрес начала, собственно, самого клавиатурного буфера (массив, 32 байта).

Клавиатурный буфер организован в виде очереди FIFO (First In - First Out, "первый пришёл, первый ушёл"). При нажатии или отпускании какой-либо клавиши вызывается клавиатурное прерывание 09h, системный обработчик которого проверяет, что за клавиша была нажата/отпущена и, если это необходимо, помещает в буфер по адресу хвоста пару из кода символа и скан-кода клавиши, затем хвост увеличивается на 2 (1 код символа + 1 скан-код = 2 байта). Буфер кольцевой, поэтому при достижении хвостом или головой конца буфера происходит переход на его начало. При чтении из буфера через прерывание 16h по адресу головы извлекается слово (два байта) и передаётся запросившей его программе, после чего адрес головы также увеличивается на 2, сдвигаясь к хвосту. Когда адрес головы и хвоста сравняются - буфер пуст. Если не считывать (не забирать) нажатые клавиши из буфера, то туда поместится 15 клавиш, после чего компьютер будет пищать, оповещая о том, что буфер полон. Кого-то может удивить, почему 15, а не 16 (32/2 = 16), но, как уже было написано выше, когда адрес хвоста равен голове, то буфер считается пустым, поэтому положить туда 16-е значение не представляется возможным, ибо тогда адреса хвоста и головы совпадут.

Рассмотрим на простом примере - предположим, что на клавиатуре были последовательно нажаты три клавиши: "g", "h" и "j". Тогда содержимое буфера будет выглядеть следующим образом (здесь и далее все значения в шестнадцатеричном представлении):

(67 22) (68 23) (6A 24)

Теперь включим Caps Lock или нажмём Shift и снова наберём эти буквы:

(47 22) (48 23) (4A 24)

Наконец, переключимся на русский язык (при условии, что установлен русский клавиатурный драйвер для 866 кодировки) и опять наберём:

(AF 22) (E0 23) (AE 24)

На что можно обратить внимание:

а) Коды символов могут меняться, скан-коды - нет [3].

б) В зависимости от кодовой страницы или установленного национального клавиатурного драйвера, коды символов могут быть какими угодно.

в) Заглавные и маленькие английские буквы отличаются только 5-ым битом в байте (+20h к значению) - мы к этому чуть позже ещё вернёмся.

Таким образом, всё что осталось - это найти скан-коды клавиш, которые нужно заменить (меняться от системы к системе они не будут), и при нажатии на них помещать в буфер уже нужные нам коды символов. Для этого напишем небольшую программу "showcode", которая будет показывать код нажатой клавиши [4].

; FASM source code
; http://flatassembler.net/
;
; showcode.asm
; (c) SysTools 2019
; http://systools.losthost.org/
org 100h
  ; INT 16H: Keyboard Services
  ; AH = 00H - Read (Wait for) Next Keystroke
  mov  ah, 000h
  int  016h
  ; output: AH - scan code; AL - character code
  ; character
  mov  [_txt+6], al
  ; convert scan code value
  call @tohex
  mov  [_txt+1], dl ; str index 1, see below
  call @tohex
  mov  [_txt+0], dl
  ; convert char code value
  call @tohex
  mov  [_txt+4], dl
  call @tohex
  mov  [_txt+3], dl
  ; DOS Fn 09H: Display String
  mov  ah, 009h
  mov  dx, _txt
  int  021h
  ; DOS Fn 4cH: Terminate Program
  mov  ax, 04C00h
  int  021h
@tohex:
  mov  dl, al
  shr  ax, 004h
  and  dl, 00Fh
  cmp  dl, 9d
  jbe  @f
  add  dl, 'A' - '0' - 10d
  @@:
  add  dl, '0'
  ret
; index: 01 34 6
_txt db '## ## ?',00Dh,00Ah,'$'

Запустим получившуюся после компиляции программу showcode.com, и после нажатия упомянутой клавиши "g" увидим на экране уже известное нам:

67 22 g

Понажимаем на клавиши и выясним, что в общей сложности необходимые нам символы лежат в диапазоне от 02h до 35h включительно. Не будет только кодов для 1Dh и 2Ah - это левый Ctrl и левый Shift - клавиатурный драйвер не помещает их значения в буфер (впрочем, их статус сохраняется в другом месте, об этом ниже). Для удобства адресации по таблице расширим диапазон до нуля, но выкинем оттуда, помимо упомянутых левых Ctrl и Shift, также те клавиши, которые нам не интересны: Esc (01h), Backspace (0Eh), Tab (0Fh), Enter (1Ch) - вместе с нулём должно получиться 7 клавиш - именно в этих местах в нашей таблице перекодировки будут стоять нули - все эти клавиши, а также клавиши со скан-кодом более 35h мы будем передавать на обработку оригинальному клавиатурному драйверу. В итоге должны будут получиться примерно такие таблицы для нижнего (lowercase) и верхнего (uppercase) регистров:

; lowercase
db 0,0,'1234567890-=',0
db 0,'jcukeng{}zh',07Fh,0
db 0,'fywaproldv|',027h,0,'/'
db 'q~smitxb`.'
; uppercase
db 0,0,'!"#;%:?*()-+',0
db 0,'JCUKENG[]ZH_',0
db 0,'FYWAPROLDV\',027h,0,'/'
db 'Q^SMITXB@,'

640x400 Russian NewsMaster charsets
"Журналист", раскладка JCUKEN и работа драйвера

И здесь стоит вернуться к упоминанию отличия маленьких букв от заглавных - всего лишь на значение +20h. Если бы не несколько символов, которые нам нужно заменить (чтобы русские буквы не торчали в тех местах, где им быть не положено), то можно было бы обойтись одной таблицей с заглавными буквами и сэкономить 54 байта - достаточно было бы при необходимости добавлять к коду символа +20h, чтобы получать маленькую букву. Заодно и прояснилась ситуация, почему у символа маленького "ъ" код 7Fh - потому что заглавный "Ъ" здесь - это символ "_" (подчерк) с кодом 5Fh, отсюда 7Fh = 5Fh+20h.

Возможно, у кого-то в этот момент возникнет вопрос, а почему бы, собственно, не давать всегда сначала отработать оригинальному клавиатурному драйверу, а уже затем подменять только код нового символа, по адресу хвоста? Проблема здесь в том, что при вызове оригинального обработчика тот, в свою очередь, завершая свою работу, посылает контроллеру прерываний сигнал о том, что прерывание отработало, и дальнейшее выполнение кода идёт уже не в монопольном режиме. Из-за этого "Журналист" успевает вытащить клавишу из клавиатурного буфера через функцию прерывания 16h, и когда управление наконец-то возвращается назад к коду нашего перехватчика, то буфер уже пуст, а буква напечатана.

И осталась ещё пара важных вещей, достойных упоминания. Одна из них - это байт по адресу памяти 0040:0017 - в нём как раз хранится статус клавиш Ctrl, Shift и Alt, но и, важное для нас, статус Scroll Lock - за это отвечает 4-й бит (значение 10h). И этот же байт нам понадобится, чтобы узнать, какую букву писать - маленькую или заглавную, за это отвечает статус Shift (биты 0-й и 1-й - 01h и 02h) и Caps Lock (6-й бит - 40h). Важно помнить, что Caps Lock, в отличие от Shift, действует только на буквы, а, значит, все клавиши, которые печатают на русской раскладке цифры и символы, не должны быть затронуты в этом режиме. Для этого достаточно проверить, что скан-код в диапазоне от 10h до 34h включительно [5].

И, наконец, последняя важная вещь - мы должны отдавать оригинальному клавиатурному драйверу не только те клавиши, которые нас не интересуют, но и расширенные клавиши. Расширенные клавиши состоят из последовательности двух нажатых клавиш, где первая всегда 0E0h. Таким образом, если код первой считанной клавиши 0E0h - то необходимо передать эту клавишу, а также следующую за ней оригинальному драйверу.

А вот теперь можно, собрав всю полученную информацию воедино, написать свой клавиатурный драйвер.

; FASM source code
; http://flatassembler.net/
;
; newskeyb.asm
; (c) SysTools 2019
; http://systools.losthost.org/
@head:
org 100h
  ; get interrupt vector 009h (keyboard)
  mov  ax, 03509h
  int  021h
  ; save original vector
  mov  [@vect9addr+1], bx
  mov  [@vect9addr+3], es
  ; set new interrupt vector
  mov  dx, @vect9hook
  mov  ax, 02509h ; keyboard
  int  021h
  ; move stack down before shrinking
  mov  bx, @tail
  mov  sp, bx
  ; save stack registers
  mov  [_sp], sp
  mov  [_ss], ss
  ; fill segments for @execparm
  mov  [_tcmd + 2], ds
  mov  [_fcb1 + 2], ds
  mov  [_fcb2 + 2], ds
  ; adjust memory block
  mov  ax, cs
  mov  es, ax
  ; bx - size in 16 bytes paragraphs
  mov  bx, ((SIZE + 15) / 16)
  mov  ah, 04Ah
  int  021h
  ; execute program
  mov  dx, _filename
  mov  bx, @execparm
  mov  ax, 04B00h
  int  021h
  ; restore stack registers
  cli
  mov  sp, [cs:_sp]
  mov  ss, [cs:_ss]
  sti
  ; restore interrupt
  mov  dx, [cs:@vect9addr+1]
  mov  ds, [cs:@vect9addr+3]
  mov  ax, 02509h ; keyboard
  int  021h
  ; exit program
  mov  ax, 04C00h
  int  021h

  ; hook procedure
align  2
@vect9hook:
  ; save registers in use
  push ax
  push bx
  push es
  push ds
  push di
  ; data segment
  mov  ax, cs
  mov  ds, ax
  ; BIOS segment
  mov  ax, 040h
  mov  es, ax
  ; first of all check that Scroll Lock is on
  test byte [es:00017h], 010h
  jz   @stop
  ; read the key
  in   al, 060h
  ; last key was extended - skip current one (E0 xx)
  cmp  [_flag], 0
  mov  [_flag], 0
  jnz  @stop
  ; extended key - skip it
  cmp  al, 0E0h
  jnz  @f
  mov  [_flag], 1
  jmp  @stop
  @@:
  ; check that key in range
  cmp  al, 035h
  ja   @stop
  xor  ah, ah
  mov  di, ax
  ; check if that key should be mapped
  cmp  [@key_lower+di], ah
  jz  @stop
  ; check keyboard buffer head/tail
  mov  bx, [es:0001Ch] ; tail
  ; 1A - head
  ; 1C - tail
  ; 1E - buff
  ; calculate next position
  sub  bl, 01Eh
  add  bl, 002h
  and  bl, 01Fh
  add  bl, 01Eh
  cmp  bx, [es:0001Ah] ; head
  ; buffer not full (next(tail) == head)
  jnz  @work
  ; keep all jumps short - move @stop code here
@stop:
  ; restore registers
  pop  di
  pop  ds
  pop  es
  pop  bx
  pop  ax
  ; jump to original interrupt
@vect9addr:
  ; if for some reason vector was not saved here - perform a cold reboot
  jmp  far 0F000h:0FFF0h
@work:
  ; at lest one Shift pressed
  xor  ah, ah
  test byte [es:0017h], 003h
  jz   @f
  xor  ah, 1
  @@:
  ; Caps Lock only for letters
  cmp  al, 010h
  jb   @f
  cmp  al, 034h
  ja   @f
  ; Caps Lock enabled
  test byte [es:0017h], 040h
  jz   @f
  xor  ah, 1
  @@:
  ; lower or upper case
  test ah, ah
  mov  ah, [@key_lower+di]
  jz   @f
  mov  ah, [@key_upper+di]
  @@:
  ; al - char; ah - code
  xchg al, ah
  ; move buffer tail
  xchg [es:0001Ch], bx
  ; put key to the buffer
  mov  [es:bx], ax
  ; interrupt cleanup
  in   al, 061h ; get value of keyboard control lines
  mov  ah, al   ; save it
  or   al, 080h ; set the "enable kbd" bit
  out  061h, al ; and write it out the control port
  xchg ah, al   ; fetch the original control port value
  out  061h, al ; and write it back
  ; send End-Of-Interrupt signal to the 8259 Interrupt Controller
  mov  al, 020h
  out  020h, al
  ; restore registers
  pop  di
  pop  ds
  pop  es
  pop  bx
  pop  ax
  ; interrupt return
  iret

; static data here
align  2
; scan codes from [00h..035h]
@key_lower:
db 000h, 000h, 031h, 032h, 033h, 034h, 035h, 036h
db 037h, 038h, 039h, 030h, 02Dh, 03Dh, 000h, 000h
db 06Ah, 063h, 075h, 06Bh, 065h, 06Eh, 067h, 07Bh
db 07Dh, 07Ah, 068h, 07Fh, 000h, 000h, 066h, 079h
db 077h, 061h, 070h, 072h, 06Fh, 06Ch, 064h, 076h
db 07Ch, 027h, 000h, 02Fh, 071h, 07Eh, 073h, 06Dh
db 069h, 074h, 078h, 062h, 060h, 02Eh
@key_upper:
db 000h, 000h, 021h, 022h, 023h, 03Bh, 025h, 03Ah
db 03Fh, 02Ah, 028h, 029h, 02Dh, 02Bh, 000h, 000h
db 04Ah, 043h, 055h, 04Bh, 045h, 04Eh, 047h, 05Bh
db 05Dh, 05Ah, 048h, 05Fh, 000h, 000h, 046h, 059h
db 057h, 041h, 050h, 052h, 04Fh, 04Ch, 044h, 056h
db 05Ch, 027h, 000h, 02Fh, 051h, 05Eh, 053h, 04Dh
db 049h, 054h, 058h, 042h, 040h, 02Ch
; padding _filename to 12 characters (8.3) format
; so anyone can replace 'NEWS.EXE' to the program
; name they might want without recompiling source
_filename db 'NEWS.EXE',0,0,0,0,0
_flag db 0
@execparm:
_eseg dw 0
_tcmd dw 080h, 0
_fcb1 dw 05Ch, 0
_fcb2 dw 06Ch, 0
; dynamic data here
_sp rw 1
_ss rw 1
; stack size: 32 bytes (should be enough)
_stack rb 32
align  16
; size of whole program (PSP + code + stack)
@tail:
SIZE = ($ - @head)

Комментарии к листингу программы


Основная программа состоит из следующих частей:

- чтение и сохранение адреса оригинального клавиатурного прерывания;

- уменьшение размера стека (при запуске com-программы под стек отводится много места, но так как нам столько не нужно, к тому же необходимо освободить память для дочернего процесса, то уменьшаем его размер);

- сохранение регистров стека (значения всех регистров будут произвольные после возврата управления к нашей программе назад);

- заполнение сегментов в структуре для запуска дочернего процесса;

- уменьшение размера памяти, выделенного нашей программе (уменьшать размер стека нужно обязательно перед этой операцией);

- вызов дочернего процесса - NEWS.EXE;

- восстановление регистров стека после возврата в программу;

- восстановление оригинального клавиатурного прерывания;

- выход из программы.


Подпрограмма клавиатурного драйвера состоит из следующих частей:

- сохранение на стек используемых регистров;

- инициализация регистров для работы (текущий сегмент данных и сегмент 0040h для чтения значений из памяти);

- проверка, включён ли Scroll Lock (здесь и далее, если требуемое условие не выполнено, то восстанавливаем регистры и передаём управление оригинальному драйверу);

- чтение скан-кода клавиши из порта ввода-вывода и пропуск, если предыдущая клавиша была расширенной (0E0h);

- если текущая клавиша расширенная - пропускаем её;

- проверка, что скан-код текущей клавиши не более 035h (когда клавишу отпускают, то выставляется старший бит 80h - например, при нажатии на "g" код нажатия будет 22h, а отпускания - 22h+80h = A2h - заведомо больше 035h, поэтому данная проверка отсекает и ситуацию, когда клавиатурное прерывание 09h вызывается при отпускании клавиш);

- проверка по массиву, что новый код не ноль - т.е. эту клавишу нужно заменять;

- последняя проверка: берём адрес хвоста, затем вычисляем следующую позицию, и если она равна адресу головы, значит, буфер полон - возвращаем управление оригинальному клавиатурному драйверу (пусть пищит за нас);

- проверка верхнего регистра: хотя бы один Shift нажат, а также Caps Lock, но только для выбранного диапазона от 10h до 34h (Caps Lock при нажатом Shift отменяют друг друга - буквы снова будут маленькими);

- выбор символа из таблицы для нижнего или верхнего регистра и помещение в клавиатурный буфер по адресу хвоста, а также замена адреса хвоста на новый;

- завершение работы прерывания как у оригинального клавиатурного драйвера: посылаем сигнал клавиатуре, что клавиша обработана, а также сообщаем контроллеру прерываний, что прерывание завершено;

- восстановление регистров и завершение работы прерывания.


P.S. Так как в основной статье нет подходящего места, то интересные факты, связанные с "Журналистом", будут приводиться в постскриптуме.

В Интернете с некоторым трудом в том или ином виде удалось найти 4 версии NewsMaster (русская здесь не учитывается), но, возможно, их больше:


NewsMaster 1.0 (1986)

- изначальная версия программы.


NewsMaster 1.5 (1986)

- изменён формат библиотеки шрифтов NEWS.FLB;

- добавлены новые шрифты;

- добавлены новые библиотеки клипарта.


NewsMaster II (1988)

- драйвера для принтеров обновлены и упакованы в файл PRINTERS.PRG;

- поставляется с утилитой CAPTURE.EXE для захвата изображений в других программах;

- поддержка изображений в формате CAPTURE.SHP (PrintMaster capture format);

- в текстовый файл с настройками NEWS.CFG вынесено большее количество настроек, которые теперь можно изменять;

- добавлены новые библиотеки клипарта;

- небольшие изменения шрифтов.


NewsMaster II (1988)

- добавлены новые драйвера для принтеров.


Исходные коды из этой статьи вместе с готовыми скомпилированными программами доступны по ссылке:

http://systools.losthost.org/files/news_prg.zip


Специально для Downgrade N27'2019

© SysTools 2019

http://systools.losthost.org/


[1] После выхода или перед запуском загрузчика не забудьте выключить Scroll Lock. Да, его можно выключить программно, но DOSBox, например, физически этого не делает - индикатор на клавиатуре так и будет гореть.

[2] Стоит напомнить, что стандартный режим Caps Lock действует только на буквы и не действует на цифры и знаки.

[3] Строго говоря, ничто не мешает клавиатурному драйверу менять и скан-коды тоже, но практического применения этому мало.

[4] В листингах будет только минимальное количество необходимых комментариев. Детальное и подробное описание API прерываний DOS, использованных в коде программ, можно найти в справочнике TECH Help!, доступном для загрузки на сайте Old-Dos в виде интерактивной гипертекстовой справочной системы под DOS (по ссылке также доступна русская версия, правда, менее полная). TECH Help! версии 6 на английском в формате CHM можно загрузить здесь.

[5] В этот диапазон также попадают клавиша тильда (слева от цифры "1") и клавиша "\" (скан-коды 29h и 2Bh соответственно), но в нашей таблице их символы в верхнем и нижнем регистре совпадают, поэтому в данном случае этим можно пренебречь, однако написание полноценного клавиатурного драйвера, безусловно, потребует более строгого подхода.


2019.07.01


[ Статьи ]