5.11.1 Символьные устройства

Драйвер символьного устройства должен содержать в поле атрибутов драйве­ра (смещение 04 в заголовке) единицу в самом старшем бите. Тогда остальные

биты трактуются следующим образом:бит 15: 1

бит 14: драйвер поддерживает функции чтения/записи IOCTL бит 13: драйвер поддерживает функцию вывода до занятости бит 12:

бит 11: драйвер поддерживает функции открыть/закрыть устройство биты 10-8: 000

бит 7: драйвер поддерживает функцию запроса поддержки IOCTL бит 6: драйвер поддерживает обобщенный IOCTL бит 5: О

бит 4: драйвер поддерживает быстрый вывод (через INT 29h) бит 3: драйвер устройства «часы» бит 2: драйвер устройства NUL

бит 1: драйвер устройства STDOUT

бит 0: драйвер устройства STDIN

IOCTL - это большой набор функций (свыше пятидесяти), доступных как различные подфункции INT 21h АН = 44h и предназначенных для прямого взаи­модействия с драйверами. Но о IOCTL - чуть позже, а сейчас познакомимся с тем,

как устроен, возможно, самый простой из реально полезных драйверов.

В качестве первого примера рассмотрим драйвер, который вообще не обслужи-

никакое устройство, реальное или виртуальное, а просто увеличивает размер буфера клавиатуры BIOS до 256 (или больше) символов. Этого можно было бы до­биться обычной резидентной программой, но BIOS хранит в своей области данных

только ближние адреса для этого буфера, то есть смещения относительно сегмент­ного адреса Так как драйверы загружаются в память первыми, еще до коман­дного интерпретатора, они обычно попадают в область линейных адресов -10400h, в то время как с резидентными программами этого может не получиться.

Наш драйвер будет обрабатывать только одну команду, команду инициализа­ции драйвера OOh. Для нее буфер запроса выглядит следующим образом:

+00h: байт - 19h (длина буфера запроса)

байт - не используется

+02h: байт - 00 (код команды)

+03h: байт - слово состояния драйвера (заполняется драйвером)

+05h: 8 байт - не используется

+0Dh: байт - число обслуживаемых устройств (заполняется блочным драйвером) +0Eh: 4 байта - на входе - конец доступной для драйвера памяти; на выходе -

адрес первого байта из той части драйвера, которая не будет ре­зидентной (чтобы выйти без инсталляции - здесь надо записать адрес первого байта)

+12h: 4 байта - на входе - адрес строки в CONFIG.SYS, загрузившей драйвер; на выходе - адрес массива ВРВ (для блочных драйверов)

+16h: байт    — номер первого диска

+17h: 2 байта - сообщение об ошибке (OOOOh, если ошибки не было) - запол­няется драйвером

І

Процедура инициализации может использовать функции DOS Olh - OCh, 25h, 30h и 35h.

; kbdext.asm

; Драйвер символьного устройства, увеличивающий буфер клавиатуры до BUF_SIZE ;   (256 по умолчанию) символов.

BUF_SIZE equ

256

.model tiny

.186

.code

org 0

start:

;  Заголовок драйвера.

dd

dw

dw

dw

db

dd db

request

buffer -1

8000h

offset strategy

offset interrupt "$$KBDEXT"

BUF_SIZE*2 dup (?);

Новый размер буфера.

Для сдвигов и push 0040h. Драйвер начинается с

Адрес следующего драйвера - ОРРРРгг.ОРРРРп

для последнего.

; Атрибуты: символьное устройство, ничего не поддерживает. Адрес процедуры стратегии. Адрес процедуры прерывания. Имя устройства  (не должно совпадать с каким-нибудь именем файла).

Здесь процедура стратегии сохраняет адрес буфера запроса. А это - наш новый буфер клавиатуры размером BUF_SIZE символов

(два байта на символ).

Процедура стратегии.

На входе

= адрес

буфера запроса.

 

strategy proc

far

 

 

mov

cs:word

ptr

Сохранить этот адрес для

mov

cs:word

ptr request+2,es

процедуры прерывания.

ret

 

 

 

strategy

endp

 

 

; Процедура прерывания.

 

 

interrupt

proc

far

 

push

ds

 

Сохранить регистры.

push

bx

 

 

push

ax

 

 

Ids

bx,dword

ptr

- адрес запроса.

mov

ah,byte

ptr [bx+2]

Прочитать номер команды.

or

ah, ah

 

Если команда ОС* (инициализация),

jnz

exit

 

 

call

Init

 

обслужить ее.

 

 

 

Иначе:

exit: mov

ax,100h

 

установить бит 8   (команда обслужена)

mov

word ptr

[bx+3],ax

в слове состояния

pop

ax

 

и восстановить регистры.

pop pop

ret

interrupt

bx

ds

endp

; Процедура инициализации. ;   Вызывается только раз при загрузке драйвера. іпіт.

; Сюда too_big:

 

proc

near

 

push

cx

 

push

dx

 

mov

ax,offset buffer

 

mov

cx, cs

- адрес нашего буфера клавиат

cmp

cx,lOOOh

Если СХ слишком велик,

jnc

too.big

не надо

shl

cx,4

Иначе: умножить сегментный адрес на

add

cx.ax

добавить смещение - получился

 

 

линейный адрес.

sub

cx,400h

Вычесть линейный адрес начала данных

push

0€40h

 

pop

ds

 

mov

bx, 1Ah

DS:BX = 0040h:001Ah - адрес головы.

mov

word ptr [bx],cx

Записать новый адрес головы буфера.

mov

word ptr [bx+2],cx

Он же новый адрес хвоста.

mov

bl,80h

DS:BX = 0040h:0080h -

 

word ptr

адрес начала буфера.

mov

Записать новый адрес начала.

add

cx,BUF_SIZE*2

Добавить размер

mov

word ptr

и записать новый адрес конца.

mov

ah, 9

Функция BOS 09h.

mov

dx, offset succjnsg

DS:0X - адрес строки

push

cs

с сообщением об успешной установке.

pop

ds

 

int

21h

Вывод строки на экран.

Ids

ptr

- адрес запроса.

mov

ax, offset init

 

mov

word ptr [bx+0Eh],ax

- следующий байт после

mov

word ptr [bx+10h],cs

конца резидентной части.

jmp

short done

Конец процедуры инициализации.

передается                 если мы загружены слишком низко в памяти.

mov

ah,9

Функция DOS 09h.

mov

dx,offset fail_msg

DS:DX - адрес строки

push

cs

с сообщением о неуспешной

pop

ds

установке.

int

21h

Вывод строки на экран.

16,

Ids

bx,dword ptr cs: request

Драйверы устройств в

done:

mov

mov

pop pop

ret word ptr

word ptr [bx+10h],cs

dx

cx ;  Записать адрес начала драйвера ;   в поле "адрес первого ;   освобождаемого байта".

английском,  потому что в этот момент

init endp

;  Сообщение об успешной установке (на ;  русские шрифты еще не загружены). succjnsg db "Keyboard extender loaded",ODh,OAh,'$'

; Сообщение о неуспешной установке.

fail.msg db "Too many drivers in memory - "

db "put kbdext.sys first "

db "in config.sys",ODh,OAh, '$'

end start

Теперь более подробно рассмотрим функции, которые должен поддерживать

ROT13 - драйвер символьного устройства. ROT13 - это метод простой модифи­кации английского текста, применяющийся в электронной почте, чтобы текст

нельзя было прочитать сразу. Методика заключается в сдвиге каждой буквы ла­тинского алфавита на 13 позиций (в любую сторону, так как всего 26 букв). Рас­кодирование, очевидно, выполняется такой же операцией. Когда наш драйвер за­гружен, команда DOS

copy encrypt.txt rot13

приведет к тому, что текст из encrypttxt будет выведен на экран, зашифрованный или расшифрованный ROT13, в зависимости от того, был ли он зашифрован до этого.

Рассмотрим все команды, которые может поддерживать символьное устрой­ство, и буфера запросов, которые им передаются.

00h: инициализация (уже рассмотрена) 03h: IOCTL-чтение (если установлен бит 14 атрибута) +OEh: 4 байта - адрес буфера

+ 12h: 2 байта - на входе - запрашиваемое число байтов

- реально записанное в буфер число байтов

04h: чтение из устройства

структура буфера для символьных устройств совпадает с 03h чтение без удаления символа из буфера

+ODh: на выходе - прочитанный символ,

если символа нет - установить бит 9 слова состояния 06h: определить состояние буфера чтения,

если в буфере нет символов для чтения - установить бит 9 слова состояния 07h: сбросить буфер ввода 08h: запись в устройство +OEh: 4 байта - адрес буфера

+12h: 2 байта -   на входе - число байтов для записи

на выходе - число байтов, которые были записаны

■■ЯМІ:;      Сложные приемы программирования

09h: запись в устройство с проверкой

аналогично 08h OAh: определить состояние буфера записи,

если в устройство нельзя писать - установить бит 9 слова состояния OBh: сбросить буфер записи

ОСЬ: IOCTL-запись (если установлен бит 14 атрибута),

аналогично 08h ODh: открыть устройство (если установлен бит И атрибута) OEh: закрыть устройство (если установлен бит 11 атрибута) . llh: вывод, пока не занято (если установлен бит 13 атрибута),

аналогично 08h

в отличие от функций записи здесь не считается ошибкой записать не все байты

13h: обобщенный IOCTL (если установлен бит б атрибута)

+ODh: байт -   категория устройства (01, 03, 05 = COM, CON, LPT)

OOh - неизвестная категория +OEh: байт -   код подфункции: "

45h: установить число повторных попыток 65h: определить число повторных попыток 4Ah: выбрать кодовую страницу 6Ah: определить активную кодовую страницу 4Ch: начало подготовки кодовой страницы 4Dh: конец подготовки кодовой страницы 6Bh: получить список готовых кодовых страниц 5Fh: установить информацию о дисплее 7Fh: получить информацию о дисплее

+OFh: 4 байта - не используются

+13h: 4 байта - адрес структуры данных IOCTL - соответствует структуре, пе­редающейся в Б8:БХдля INT 21h, АХ = 440Ch 19h: поддержка функций IOCTL (если установлены биты 6 и У атрибута)

+ODh: байт - категория устройства

+OEh: байт - код подфункции

Если эта комбинация подфункции и категории устройства не поддерживается драйвером - надо вернуть ошибку 03h в слове состояния.

Итак, теперь мы можем создать полноценный драйвер символьного устрой­ства. Упрощая задачу, реализуем только функции чтения из устройства и ' будем возвращать соответствующие ошибки для других функций.

Еще одно отличие этого примера - в нем показано, как совместить в одной программе исполняемый файл типа ЕХЕ и драйвер устройства. Если такую про­грамму запустить обычным образом, она будет выполняться, начиная со своей точки входа (метка start в нашем примере), а если ее загрузить из CONFIG.SYS, DOS будет считать драйвером участок программы, начинающийся со смещения 0.

rot13.asm

Драйвер символьного устройства, выводящий посылаемые ему символы на экран

после выполнения над ними преобразования ROTl3

(каждая буква английского алфавита смещается на із позиций).

Реализованы только функции записи в устройство.

Пример использования: сору encrypted.txt $rotl3 Загрузка - из CONFIG.SYS DEVICE=c:\rot13.exe,

если rot13.exe находится в

директории

request small

.code .186

org 0

dd dw

dw

dw db

dd

-1

0A800h.

offset strategy

offset interrupt "$ROT13",20h,20h

Моделв для ЕХЕ-файла. Для ризпа/рора.

Код драйвера начинается с С8:0000. Адрес  следующего драйвера. Атрибуты нашего устройства. Адрес процедуры стратегии. Адрес процедуры прерывания. Имя устройства, дополненное пробелами до восвми символов.

Сюда процедура стратегии адрес буфера запроса.

будет писать

; Таблица адресов command_table

обработчиков для всех команд.

; Процедура стратегии

strategy

mov mov

ret

strategy

dw

offset init

 

OOh

 

 

dw

З dup(offset

unsupported)

01,

02, 03

 

dw

2 dup(offset

read)

04,

 

dw

2 dup(offset

unsupported)

06,

07

 

dw

2 dup(offset

write)

08h,

09h

 

dw

б dup(offset

unsupported)

OAh,

OBh,

OCh,

dw

offset write

 

l0h

 

 

dw

2 dup(offset

invalid)

llh,

l2h

 

dw

offset unsupported

l3h

 

 

dw

З dup(offset

invalid)

l4h,

l5h,

l6h

dw

З dup(offset

unsupported)

l7h,

l8h,

l9h

тегии -

одна и та же

для всех

 

 

OFh

proc far

word ptr cs: request, bx.

word ptr cs:request+2, es

endp ; Процедура

interrupt        proc far pushf

pusha

; Сохранить регистры

Сложные приемы программирования

push

ds

; и, на всякий случай, флаги.

push

es

 

push

cs

 

pop

ds

; ПО] = наш сегментный адрес.

les

si.dword ptr request

; Е8:81 = адрес буфера запроса.

xor

bx, bx

 

mov

bl.byte ptr es:[si+2]

; ВХ = номер функции.

cmp

bl,19h

; Проверить, что команда

 

 

: в пределах 00 - 19п.

jbe

command ok

 

-call

invalid

; Если нет - выйти с ошибкой.

jmp

.short interrupt_end

 

command_ok:

 

; Если команда находится в пределах 00 - 19п,

shl

bx,1

; умножить ее на 2, чтобы получить

 

 

; смещение в таблице слов соттапО_1аЬ1е,

call

word ptr                ; и вызвать обработчик.

interrupt_end

 

 

cmp

al,D

; АЬ = 0,  если не было ошибок.

je

no_error

 

or

ah,80h

; Если была ошибка - установить бит 15 в АХ.

no_error:

 

 

or

ah,01h

; В любом' случае установить бит 8

mov

word ptr

; и записать слово состояния.

pop

es

 

pop

ds

 

popa

 

 

popf

 

 

ret

 

 

interrupt

endp

 

; Обработчик команд,  предназначенных для

блочных устройств.

unsupported

near

 

xor

ах, ах

; Не возвращать никаких ошибок.

ret

 

 

unsupported

endp

 

; Обработчик

команд чтения.

 

read

proc near

 

mov

al.OBh

; Общая ошибка чтения.

ret

 

 

read

endp

 

; Обработчик

несуществующих команд.

 

invalid

proc near

 

mov

ax,03h

; Ошибка "неизвестная команда".

ret

 

 

invalid

endp

 

; Обработчик

функций записи.

 

write

near

 

push

si

 

mov cx.word ptr es: [si+12h]   ; Длина буфера в CX.

jcxz write_finished ; Если это 0 - нам делать нечего.

Ids si.dword ptr es: [si+OEh]  ; Адрес буфера в OS:SI.

Выполнить над

-

rot13_loop: Цикл по всем символам буфера.

lodsb ; AL = следующий символ из буфера в ES:SI.

cmp al.'A'                ; Если он меньше "А",

rot13_done это не буква.

cmp al.'Z'               ; Если он болвше "Z",

jg rotl3_low          ; может быть, это маленькая буква,

cmp al,("A"+13)        ; Иначе: если он болвше "А" + 13,

jge rot13_dec           ; вычесть из него 13,

jmp short rotl3_inc   ; а в противном случае - добавитв. rot13_low:

cmp a'l.'a'               ; Если символ менвше "а",

jl rotl3_done         ; это не буква,

cmp al.'z'               ; Если символ болвше "z" -

jg rot"l3_done         ; то.же самое,

cmp al,("a"+13)        ; Иначе:  если он болвше "а" + 13,

jge rot13_dec          ; вычеств из него 13, иначе: rotl3_inc:

add al, 13                ; добавитв 13 к коду символа,

jmp short rotl3_done rotl3_dec:

sub al,13               ; вычеств 13 из кода символа, rot13_done:

int 29h                  ; вывести символ на экран

loop rot13_loop             и повторить для всех символов.

write_finished:

xor ax,ax                ; Сообщить,  что ошибок не было.

pop si

ret

write endp

; Процедура инициализации драйвера.

init proc near

mov ah, 9 ; Функция DOS 09h.

mov dx,offset load_msg ; DS:DX - сообщение об установке.

int 21h ; Вывод строки на экран.

mov word ptr es:[si+OEh],offset init ;  Записати адрес

mov word ptr es:[si+lOh],cs   ;  конца резидентной части.

xor ax,ax ; Ошибок не произошло.

ret

init endp

; Сообщение об установке драйвера.

loadjnsg db         "R0T13 device driver loaded",OOh.OAh, '$'

wmm

Сложные приемы программирования

; Точка входа

push

cs

 

pop

ds

 

mov

dx,offset exejnsg

; DS:DX - адрес строки.

mov

ah,9

; Функция DOS.

int

21h

; Вывод строки на экран.

mov

ah,4Ch

; Функция DOS 4Ch.

int

21h

; Завершение ЕХЕ-программы.

;  Строка,  которая выводится при запуске не из CONFIG.SYS:

exejnsg db "Эту программу надо загружатв,   как драйвер устройства из"

db "CONFIG.SYS",ODh,OAh,'$'

.stack

end start