Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.

[Назад] [Содеожание]

6.5. Функции загружаемого драйвера

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

0 - Инициализация драйвера

1 - Проверка замены носителя данных

2 - Построить блок BPB

3 - IOCTL чтение

4 - Чтение

5 - Неразрушающее чтение без ожидания

6 - Проверить состояние устройства ввода

7 - Сброс буфера устройства ввода

8 - Запись

9 - Запись с проверкой

10 - Проверить состояние устройства вывода

11 - Сброс буфера устройства вывода

12 - IOCTL запись

13 - Открыть устройство

14 - Закрыть устройство

15 - Проверка сменяемости диска

16 - Вывод, пока не занято

19 - Функции управления вводом/выводом (IOCTL)

23 - Получить активное логическое устройство

24 - Установить активное логическое устройство

# 0 - Инициализация драйвера

Эта функция выполняется только один раз при загрузке драйвера и подключении его к операционной системе.

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

Приведем формат запроса для команды инициализации:

(0) 13 header Заголовок запроса.
(+13) 1 n_units Количество устройств, обслуживаемых драйвером. Это поле заполняется только блочным драйвером.
(+14) 4 end_addr Конечный FAR-адрес резидентной части кода драйвера. В это поле драйвер записывает адрес байта памяти, следующего за той частью кода драйвера, которая должна стать резидентной.
(+18) 4 parm FAR-адрес строки параметров инициализации драйвера из файла CONFIG.SYS. Эта строка содержит все, что находится в строке файла после команды 'DEVICE=', она заканчивается символами перевода строки и возврата каретки 0Ah, 0Dh. При возврате драйвер блочного устройства должен записать в это поле адрес массива указателей на блоки параметров BIOSBIOS (BPB), по одному указателю на каждое устройство, обслуживаемое драйвером.
(+22) 1 drive Номер устройства. Для версии DOS 3.0 и более поздних версий в это поле при загрузке драйвера операционная система заносит номер, назначенный устройству, обслуживаемому драйвером. Например, для устройства А: это 0, для B: - 1 и т.д.

При инициализации драйвер символьного устройства сохраняет в своей внутренней области данных параметры инициализации, используя адрес parm. Если параметры содержат числовые величины, программа инициализации может произвести их перекодировку и сохранить значения в двоичном формате.

Затем драйвер может выполнить инициализацию обслуживаемого физического устройства ввода/вывода, инициализацию своих внутренних переменных, вывести на экран какие-либо сообщения либо даже запросить у оператора дополнительные данные - функция инициализации может пользоваться для организации диалога с оператором и других действий функциями прерывания 21h с номерами от 01h до 0Ch, 25h, 30h, 35h и функциями BIOS.

Кроме этого, драйвер должен заполнить поле end_addr адресом конца резидентной части драйвера. Так как программа инициализации выполняется только один раз, обычно ее располагают в конце драйвера и для экономии памяти не оставляют резидентной.

Драйверы блочных устройств дополнительно должны возвратить DOS количество обслуживаемых устройств (в поле n_units) и указатель на массив указателей на блоки BPB (в поле parm).

Количество устройств используется DOS для определения логических имен устройств. Например, если Ваш драйвер обслуживает три логических устройства, и на момент его загрузки в системе имеются устройства A:, B: и C:, то устройства, обслуживаемые Вашим драйвером, получат имена D:, E: и F:. Количество устройств необходимо указывать также и в заголовке драйвера, в первом байте поля имени устройства dev_name.

Для каждого логического устройства драйвер должен содержать так называемый блок параметров BIOS (BIOS Parameter Block) BPB.

Блок BPB содержится в загрузочном секторе диска и содержит информацию, необходимую BIOS для работы с диском. Приведем формат BPB:

(0) 2 sect_siz Количество байтов в одном секторе диска.
(+2) 1 clustsiz Количество секторов в одном кластере.
(+3) 2 res_sect Количество зарезервированных секторов.
(+5) 1 fat_cnt Количество таблиц FAT.
(+6) 2 root_siz Максимальное количество дескрипторов файлов, содержащихся в корневом каталоге диска.
(+8) 2 tot_sect Общее количество секторов на носителе данных (в разделе DOS).
(+10) 1 media Байт-описатель среды носителя данных.
(+11) 2 fat_size Количество секторов, занимаемых одной копией FAT.

Подробно формат и назначение полей BPB будут описаны в разделе, посвященном файловой системе DOS.

Приведем фрагмент исходного текста драйвера, возвращающего при инициализации указатель на массив BPB:

        lea     dx,bpb_ptr
        mov     es:[bx+18],dx
        mov     es:[bx+20],cs
        . . . . . . . . . .

В этом примере предполагается, что ES:BX содержит адрес заголовка запроса.

Область данных может содержать, например, такое описание bpb_ptr:

bpb:            DW      512     ; количество байтов на сектор
                DB      1       ; количество секторов на кластер
                DW      1       ; зарезервировано секторов
                DB      2       ; количество копий FAT
                DW      64      ; максимальное количество файлов 
                                ; в корневом каталоге
                DW      360     ; общее число секторов на диске
                DB      0FCh    ; описатель среды
                DW      2       ; количество секторов в FAT
;
bpb_ptr DW      bpb         ; таблица для трех одинаковых
                DW      bpb ; логических устройств
                DW      bpb

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

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

Драйвер символьного устройства при этом может указать в качестве конечного адреса резидентной части программы адрес начала драйвера, т.е. адрес заголовка драйвера. Размер резидентной части при этом будет равен нулю.

Блочные драйверы дополнительно должны записать ноль в поле количества обслуживаемых логических устройств n_units.

В обоих случаях включения драйвера в состав операционной системы не произойдет.

Приведем пример простейшего драйвера символьного устройства, который обслуживает только команду инициализации. Фактически этот драйвер не работает ни с каким физическим устройством. Он просто стирает содержимое экрана при инициализации, выводит сообщение и ожидает нажатия оператором любой клавиши. После нажатия драйвер завершает свою работу без включения себя в состав DOS.

Исходный текст драйвера:

          .MODEL tiny
          .CODE        ; Драйвер состоит из одного
                                ; сегмента кода

          org 0        ; Эта строка может отсутствовать

          include sysp.inc

;========================================================

simple    PROC far  ; Драйвер - это FAR-процедура

;========================================================

E_O_P:              ;Метка конца программы,
                                ;устанавливаем ее в начало
                                ;драйвера, т.к. не надо
                                ;оставлять драйвер резидентно
                                ;в памяти

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

          dd   0ffffffffh       ;адрес следующего драйвера
          dw   8000h            ;байт атрибутов
          dw   dev_strategy     ;адрес процедуры стратегии
          dw   dev_interrupt    ;адрес процедуры прерывания
          db   'SIMPLE_D'       ;имя устройство (дополненное
                                            ;пробелами)

;========================================================

; Программа стратегии

dev_strategy:
                mov  cs:req_seg,es
                mov  cs:req_off,bx
                ret

; Здесь запоминается адрес заголовка запроса

req_seg         dw ?
req_off         dw ?

;========================================================

;Обработчик прерывания

dev_interrupt:
        push es     ;сохраняем регистры
        push ds
        push ax
        push bx
        push cx
        push dx
        push si
        push di
        push bp

; Устанавливаем ES:BX на заголовок запроса

        mov  ax,cs:req_seg
        mov  es,ax
        mov  bx,cs:req_off

; Получаем код команды из заголовка запроса и умножаем
; его на два, чтобы использовать в качестве индекса
; таблицы адресов обработчиков команд

        mov  al,es:[bx]+2
        shl  al,1

        sub  ah,ah            ; Обнуляем AH
        lea  di,functions     ; DI указывает на смещение
                                          ; таблицы
        add  di,ax            ; Добавляем смещение в таблице
        jmp  word ptr [di]    ; Переходим на адрес из таблицы

functions   LABEL  WORD    ; Таблица функций

   dw   initialize
   dw   check_media
   dw   make_bpb
   dw   ioctl_in
   dw   input_data
   dw   nondestruct_in
   dw   input_status
   dw   clear_input
   dw   output_data
   dw   output_verify
   dw   output_status
   dw   clear_output
   dw   ioctl_out
   dw   Device_open
   dw   Device_close
   dw   Removable_media

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

check_media:
make_bpb:
ioctl_in:
nondestruct_in:
input_status:
clear_input:
output_verify:
output_status:
clear_output:
ioctl_out:
Removable_media:
Device_open:
Device_close:
output_data:
input_data:

        or   es:word ptr [bx]+3,8103h
     jmp  quit

;========================================================

quit:
        or   es:word ptr [bx]+3,100h
        pop bp
        pop di
        pop si
        pop dx
        pop cx
        pop bx
        pop ax
        pop ds
        pop es
        ret

;========================================================

; Процедура выводит на экран строку
; символов в формате ASCIIZ

dpc proc near

        push si

dpc_loop:
        cmp  ds:byte ptr [si],0
        jz   end_dpc
        mov  al,ds:byte ptr [si]
        @@out_ch al
        inc si
        jmp dpc_loop

end_dpc:
        pop si
        ret

dpc endp

;========================================================

hello   db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+'
        db 13,10,'¦  *SIMPLE* (C)Frolov A., 1990   ¦'
        db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+'
        db 13,10
        db 13,10,'Hit any key...'
        db 13,10,0

;========================================================

initialize:

   lea  ax,E_O_P      ;смещение конца программы в AX
   mov  es:word ptr [bx]+14,ax   ;помещаем его в заголовок
   mov  es:word ptr [bx]+16,cs   ;

; Стираем экран

        mov   dh,18h
        mov   dl,80h

        xor   cx,cx
        mov   bh,7
        xor   al,al
        mov   ah,6
        int   10h

; Устанавливаем курсор в левый верхний угол экрана

        mov   bh,0
        xor   dx,dx
        mov   ah,2
        int   10h

; Выводим сообщение

        mov ax,cs
        mov ds,ax
        mov si,offset hello
        call dpc

; Ожидаем нажатия на любую клавишу

        mov   ax,0
        int   16h

        jmp  quit

simple   ENDP

            END simple

В программе используется макро @@out_ch, описанное в файле sysp.inc и предназначенное для вывода символов на экран дисплея. Файл sysp.inc имеется на дискете, прилагающейся к книге. Приведем текст макро @@out_ch:

@@out_ch  MACRO c1,c2,c3,c4,c5,c6,c7,c8,c9,c10
                mov   ah,02h
                IRP   chr,<c1,c2,c3,c4,c5,c6,c7,c8,c9,c10>
                IFB   <chr>
                EXITM
                ENDIF
                mov   dl,chr
                int   21h
                ENDM
                ENDM

Текст этого драйвера транслировался при помощи ассемблера, входящего в состав Quick C 2.01. Полученный объектный модуль обрабатывался пакетным файлом:

link %1.obj;
exe2bin %1.exe %1.sys

Вы можете также использовать макроассемблер MASM версии 5.0 или более поздней версии.

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

Для испытания этого и других драйверов запишите драйвер в корневой каталог системной дискеты (с которой можно загрузить операционную систему) и поместите в файл CONFIG.SYS, находящийся на этой дискете строку:

DEVICE=a:\simple.sys

Когда Ваш драйвер будет отлажен, его можно переписать на диск и подключить к файлу CONFIG.SYS, находящемуся на диске С:.

# 1 - Проверка замены носителя данных

Эту команду DOS выдает драйверу, когда она хочет проверить, не произошла ли замена носителя данных, например, замена дискеты.

Вообще говоря, довольно трудно определить, заменил ли оператор дискету или там все еще стоит старая, с которой DOS начала работу. Наиболее достоверные результаты можно было бы получить от аппаратуры, если бы она следила за заменой дискет. Но когда аппаратура не отслеживает замены дискет, приходится анализировать метку диска или как-то иначе определять, поменялась дискета или нет. Метка диска - слабое подспорье в этом вопросе, так как обычно при форматировании дискет пользователь не задает метку. Однако MS-DOS 4.01 при форматировании автоматически записывает на дискету уникальный серийный номер, который можно использовать для проверки факта замены дискеты в приемном кармане дисковода.

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

К чему может привести некорректный ответ DOS на вопрос о замене дискеты?

К очень тяжелым последствиям.

Возможны три варианта ответа на вопрос о замене дискеты:

  1. Да, дискета заменена.
  2. Нет, дискета все та же.
  3. Неизвестно, произошла замена дискеты или нет.

Если DOS получила ответ, что дискета не заменена, она продолжает работу, которой занималась раньше.

Если пришел ответ, что носитель данных заменен, DOS выдает драйверу команду с номером 2. Это запрос драйверу на построение нового BPB. Все буфера, связанные с данным устройством, при этом очищаются, и, если они не были записаны на диск, может произойти потеря информации. Осуществляется чтение каталога и FAT.

Если пришел ответ, что неизвестно, сменили ли носитель данных или нет, действия DOS зависят от наличия в данный момент непустых дисковых буферов. Если непустых буферов нет, DOS считает, что носитель сменился, и ведет себя так, как это было описано раньше. Если есть непустые буфера, DOS записывает буфера на диск. При этом структура информации на новой дискете может оказаться полностью нарушенной.

Приведем формат запроса для команды проверки замены носителя:

(0) 13 header Заголовок запроса.
(+13) 1 media В этом поле драйверу передается байт-описатель среды носителя данных, с которым DOS работала раньше.
(+14) 1 reply В это поле драйвер должен поместить ответ о факте замены среды:

1 - диск не заменялся;

0 - неизвестно;

-1 - диск был заменен.

(+15) 4 vol_id Указатель на предыдущую метку тома (если установлен бит 11 слова атрибута устройства и диск был заменен)

Если драйвер поддерживает функцию проверки замены среды носителя данных (бит 11 слова атрибута установлен в 1) и оказалось, что произошла замена диска, драйвер должен вернуть в поле vol_id указатель на область памяти, содержащую предыдущую метку тома в формате ASCIIZ. Если метка тома не используется драйвером, а бит 11 слова атрибутов установлен, необходимо вернуть указатель на строку "NO_NAME", закрытую двоичным нулем.

Байт-описатель среды media классифицирует используемую среду носителя данных, но делает это неоднозначно. Мы приведем характерные для этого байта параметры дисков:

0FFh 2 стороны, 8 секторов на дорожку;
0FEh 1 сторона, 8 секторов на дорожку;
0FDh 2 стороны, 8 секторов на дорожку;
0FCh 1 сторона, 9 секторов на дорожку;
0F9h 2 стороны, 15 секторов на дорожку;
0F8h жесткий диск;
0FEh, 0FDh используются восьмидюймовые дискеты.

Более подробно все, что касается дисков, будет изложено в разделах, посвященных файловой системе.

# 2 - Построить блок BPB

Эта команда имеет смысл только для блочных устройств и вызывается после предыдущей команды, если произошла смена носителя данных.

Драйвер должен возвратить адрес нового блока BPB.

Формат запроса для этой команды:

(0) 13 header Заголовок запроса.
(+13) 1 media В этом поле драйверу передается байт-описатель среды носителя данных, с которым DOS работала раньше.
(+14) 4 buf_adr Адрес буфера обмена. Содержимое этого буфера при вызове драйвера зависит от установки бита 13 слова атрибутов устройства (IBM-формат). Если этот бит равен 0 (устройство формата IBM), буфер содержит первый сектор первой копии FAT. В противном случае указатель установлен на буфер свободного сектора.
(+18) 4 bpb_adr Указатель на новый BPB, записывается в это поле драйвером.

Операционная система MS-DOS версии 3.0 и более поздних версий включает поддержку аппаратных средств проверки смены носителя данных. Если драйвер обнаружил, что смена носителя данных произошла неправильно, в неподходящий момент времени, он может вернуть ошибку с кодом 15 (неразрешенная замена диска).

# 3 - IOCTL чтение, 4 - Чтение, 8 - Запись, 9 - Запись с проверкой,

12 - IOCTL запись, 16 - Вывод, пока не занято

Все эти команды используются для чтения/записи информации из устройства или в устройство соответственно. Они имеют практически одинаковый формат запроса, поэтому мы их будем рассматривать вместе.

В операциях чтения или записи участвуют сектора для блочных устройств и байты для символьных устройств. Область запроса содержит указатель на буфер обмена, куда нужно поместить прочитанные данные или откуда взять данные для записи, поле количества записываемых/читаемых байт для символьных устройств или секторов для блочных.

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

Приведем формат запроса для этих команд:

(0) 13 header Заголовок запроса.
(+13) 1 media В этом поле драйверу передается байт-описатель среды носителя данных.
(+14) 4 buf_adr Адрес буфера для передачи данных.
(+18) 2 count Количество передаваемых байтов для символьных устройств или секторов для блочных устройств.
(+20) 2 sector Номер начального сектора, если драйвер использует 16-битовую адресацию секторов или -1 для 32-битовой адресации. Это поле не используется символьными драйверами.
(+22) 4 vol_id Указатель на метку тома в формате ASCIIZ. Возвращается блочным драйвером, если он выставляет ошибку 15 (неправильная смена диска). Это поле должно содержать ссылку на метку требуемого диска.
(+26) 4 sect32 Номер начального сектора, если содержимое поля sector равно -1. Первым идет старшее слово номера сектора. Если обнаружена ошибка с номером 15, в это поле записывается указатель на метку тома.

После выполнения операций чтения или записи драйвер обязательно должен записать в поле count количество действительно переданных байтов для символьных устройств или секторов для блочных. В случае ошибки также требуется запись правильного значения в поле count, недостаточно только установить признак и код ошибки в слове состояния драйвера.

Для команды 9 (запись с проверкой) драйвер должен после выполнения записи проверить записанные данные. Если с консоли введена команда DOS VERIFY ON, то для дисковых устройств DOS вместо команды записи 8 использует команду записи с проверкой 9.

Драйвер может возвратить количество переданных байтов меньше запрошенного, это само по себе не является ошибкой. Иногда может получиться так, что надо выполнить запись 64 Кбайтов. Такая операция может вызвать переход за границу сегмента в буфере передачи данных. В этом случае драйвер должен проигнорировать лишние байты. Например, надо записать 10000h байтов, распределенных по секторам из буфера с адресом ХХХХ:0001. В этом случае драйвер должен проигнорировать последние два байта. Они будут записаны в следующий раз.

IOCTL чтение/запись - это обмен с устройством управляющей информацией. Мы будем подробно говорить об этом ниже.

Команда с кодом 16 - "вывод, пока не занято", предназначена для работы с такими устройствами ввода/вывода, которые имеют свой собственный буфер, например, принтеры.

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

          .MODEL tiny
          .CODE        ; Драйвер состоит из одного
                       ; сегмента кода

          org 0        ; Эта строка может отсутствовать

          include sysp.inc

;========================================================

iodrv    PROC far         ;драйвер - это FAR-процедура

;========================================================

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

          dd   0ffffffffh       ;адрес следующего драйвера
          dw   8000h            ;байт атрибутов
          dw   dev_strategy     ;адрес процедуры стратегии
          dw   dev_interrupt    ;адрес процедуры прерывания
          db   'IODRIVER'       ;имя устройства (дополненное
                                            ; пробелами)

;========================================================

; Программа стратегии

dev_strategy:
                mov  cs:req_seg,es
                mov  cs:req_off,bx
                ret

; Здесь запоминается адрес заголовка запроса

req_seg         dw ?
req_off         dw ?

;========================================================

;Обработчик прерывания

dev_interrupt:
        push es     ;сохраняем регистры
        push ds
        push ax
        push bx
        push cx
        push dx
        push si
        push di
        push bp

; Устанавливаем ES:BX на заголовок запроса

        mov  ax,cs:req_seg
        mov  es,ax
        mov  bx,cs:req_off

; Получаем код команды из заголовка запроса и умножаем
; его на два, чтобы использовать в качестве индекса
; таблицы адресов обработчиков команд

        mov  al,es:[bx]+2
        shl  al,1

        sub  ah,ah            ; Обнуляем AH
        lea  di,functions     ; DI указывает на смещение
                                          ; таблицы
        add  di,ax            ; Добавляем смещение в таблице
        jmp  word ptr [di]    ; Переходим на адрес из таблицы

functions   LABEL  WORD  ; Таблица функций

   dw   initialize
   dw   check_media
   dw   make_bpb
   dw   ioctl_in
   dw   input_data
   dw   nondestruct_in
   dw   input_status
   dw   clear_input
   dw   output_data
   dw   output_verify
   dw   output_status
   dw   clear_output
   dw   ioctl_out
   dw   Device_open
   dw   Device_close
   dw   Removable_media

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

check_media:
make_bpb:
ioctl_in:
nondestruct_in:
input_status:
clear_input:
output_verify:
output_status:
clear_output:
ioctl_out:
Removable_media:
Device_open:
Device_close:

        or   es:word ptr [bx]+3,8103h
        jmp  quit

;========================================================

; Обработчик команды вывода данных

output_data:

; Записываем в регистр CL количество
; выводимых символов

        mov  cl,es:[bx]+18
        push cx

; Выводим сообщение о начале вывода

        mov ax,cs
        mov ds,ax
        mov si,offset outmsg
        call dpc

        pop cx

; Загружаем в DS:SI адрес буфера данных

        mov  ax,es:[bx]+16
        mov  ds,ax
        mov  si,es:[bx]+14

; Выводим на экран символы из буфера

out_loop:
        mov  al,ds:byte ptr [si]
        @@out_ch al
        inc si
        loop out_loop

        jmp  quit


;========================================================

; Обработчик команды ввода данных

input_data:

; Записываем в регистр CL количество
; вводимых символов

        mov  cl,es:[bx]+18
        push cx

; Выводим сообщение о начале ввода

        mov ax,cs
        mov ds,ax
        mov si,offset inpmsg
        call dpc

; Загружаем в DS:SI адрес буфера данных

        pop  cx
        mov  ax,es:[bx]+16
        mov  ds,ax
        mov  di,es:[bx]+14

; Вводим символы с клавиатуры и записываем в буфер

inp_loop:
        mov  ax,0
        int  16h
        mov  ds:byte ptr [di],al
        @@out_ch al
        inc di
        loop inp_loop

        jmp  quit


;========================================================

quit:
        or   es:word ptr [bx]+3,100h
        pop bp
        pop di
        pop si
        pop dx
        pop cx
        pop bx
        pop ax
        pop ds
        pop es
   ret

;========================================================

; Процедура выводит на экран строку
; символов в формате ASCIIZ

dpc proc near
        push si
dpc_loop:
        cmp  ds:byte ptr [si],0
        jz   end_dpc
        mov  al,ds:byte ptr [si]
        @@out_ch al
        inc si
        jmp dpc_loop

end_dpc:
        pop si
        ret
dpc endp

;========================================================

hello   db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+'
        db 13,10,'¦   *IODRV* (C)Frolov A., 1990   ¦'
        db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+'
        db 13,10,0

outmsg   DB 13,10,'___ Вывод на устройство IODRIVER ___',0
inpmsg   DB 13,10,'___ Ввод с устройства   IODRIVER ___',0

;========================================================

E_O_P:            ;Метка конца программы

initialize:

   lea  ax,E_O_P      ;смещение конца программы в AX
   mov  es:word ptr [bx]+14,ax   ;помещаем его в заголовок
   mov  es:word ptr [bx]+16,cs   ;

; Стираем экран

        mov   dh,18h
        mov   dl,80h

        xor   cx,cx
        mov   bh,7
        xor   al,al
        mov   ah,6
        int   10h

; Устанавливаем курсор в левый верхний угол экрана

        mov   bh,0
        xor   dx,dx
        mov   ah,2
        int   10h

; Выводим сообщение

        mov ax,cs
        mov ds,ax
        mov si,offset hello
        call dpc

        jmp  quit

iodrv   ENDP

                  END iodrv

Для работы с этим драйвером можно использовать приводимую ниже программу, составленную на языке Си. Программа открывает устройство, вводит из него восемь символов, печатает введенные символы на экране и выводит их обратно на устройство:

#include <io.h>
#include <conio.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <malloc.h>
#include <errno.h>
#include <dos.h>

int main(void);

int main(void) {

    char buf[100];
         int io_handle;
         unsigned count;

         // Открываем устройство с именем IODRIVER

         if( (io_handle = open("IODRIVER", O_RDWR)) == - 1 ) {

                // Если открыть не удалось, выводим
                // код ошибки

                printf("Ошибка при открытии устройства %d",errno);
                return errno;
         }

         // Читаем 8 байт из устройства в буфер buf

         if( (count = read(io_handle, buf, 8)) == -1 ) {

                // Если при чтении произошла ошибка,
                // выводим ее код

                printf("Ошибка чтения %d",errno);
                return errno;
        }

        // Закрываем прочитанную строку нулем
        // для последующего вывода функцией printf

        buf[8]=0;

        printf("\n___ Введена строка: %s ___",buf);

        // Выводим только что прочитанные данные
        // обратно на то же устройство

        if( (count = write(io_handle, buf, 8)) == -1 ) {

                // Если при записи произошла ошибка,
                // выводим ее код

                printf("Ошибка записи %d",errno);
                return errno;
        }

        // Закрываем устройство
        close(io_handle);
        exit(0);
}

Эта программа служит примером того, как можно организовать взаимодействие драйвера и прикладной программы, работающей с драйвером. Позже мы приведем пример более сложного драйвера символьного устройства.

# 5 - Неразрушающее чтение без ожидания

Эта команда предназначена для выборки одного байта из буфера символьного устройства. Для блочных устройств эта команда не используется.

Если в буфере символьного устройства есть байты, которые могут быть прочитаны командой чтения, драйвер возвращает следующий символ, который был бы прочитан.

Если в результате проверки выяснится, что в буфере есть символы, драйвер должен установить бит 9 слова состояния устройства (занятость) в 0, в противном случае - в 1.

Эта команда используется перед выдачей команды чтения для проверки, есть ли в буфере готовые для чтения данные. Такая проверка позволяет избежать длительного ожидания готовности данных при вводе. Кроме того, команда используется для проверки наличия в буфере клавиатуры символа CTRL-BREAK.

Формат запроса для команды 5:

(0) 13 header Заголовок запроса.
(+13) 1 byte В это поле драйвер записывает извлеченный из буфера байт, который будет считан по следующей команде ввода.

# 6 - Проверить состояние устройства ввода.

10 - Проверить состояние устройства вывода.

Эти команды проверяют состояние устройства ввода и устройства вывода соответственно (только символьные устройства).

Для устройства ввода бит занятости слова состояния (бит 9) сбрасывается в ноль, если буфер устройства не пуст и в нем есть готовые для чтения символы. Этот бит устанавливается драйвером в единицу, когда буфер пуст, и последующая команда чтения приведет к ожиданию ввода от устройства, например, к ожиданию нажатия на клавишу.

Для устройства вывода бит занятости в слове состояния (бит 9) сбрасывается в ноль, если нет текущих ожидающих готовности устройства запросов на вывод и последующая команда вывода может быть немедленно выполнена. Бит устанавливается в 1, если предыдущий запрос на вывод еще не обработан.

Для команд проверки состояния запрос состоит только из заголовка, область переменного формата отсутствует.

# 7 - Сброс буфера устройства ввода

11 - Сброс буфера устройства вывода

Эти функции заставляют драйвер сбросить все текущие запросы на ввод/вывод.

Сброс буфера входного устройства используется, например, для удаления всех символов из буфера клавиатуры. После выполнения очистки буферов драйвер выставляет слово состояния и возвращает управление операционной системе.

Запрос состоит только из заголовка.

# 13 - Открыть устройство

14 - Закрыть устройство

Для того чтобы драйвер мог использовать эти команды, бит 11 в слове атрибутов устройства в заголовке драйвера должен быть установлен в 1.

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

Драйвер блочного устройства с помощью этих команд может производить подсчет открытых файлов. Если содержимое счетчика открытых файлов для данного устройства равно 0, то открытых файлов на этом устройстве нет. Если драйвер теперь сбросит все буфера на диск, пользователь сможет заменить диск на другой. Однако если программы открывают файлы без их закрытия, то могут возникнуть ошибки при использовании этого метода.

Стандартные устройства CON, AUX, PRN всегда открыты.

Запрос для этих команд состоит только из заголовка.

# 15 - Проверка сменяемости диска

Эта команда используется только для тех драйверов, в слове атрибутов которых бит 11 установлен в 1. Драйвер должен сообщить DOS, возможна ли замена носителя данных. Например, замена жесткого диска обычно невозможна, хотя есть накопители со сменными жесткими дисками.

Драйвер возвращает информацию о возможности замены носителя в бите 9 слова состояния устройства. Значение этого бита, равное 0, драйвер устанавливает, если возможна замена носителя данных, в противном случае устанавливается значение, равное 1.

Запрос состоит только из заголовка.

# 19 - Функции управления вводом/выводом (IOCTL)

Команда с кодом 19 предназначена для выполнения нескольких функций и поддерживается только теми драйверами, у которых в слове атрибутов устройства установлен в 1 бит поддержки IOCTL (бит 14). Эта команда поддерживается DOS версии 3.2 и более поздних версий.

Команда используется для выполнения ряда операций с дисками, доступных обычно лишь на уровне BIOS, например, форматирования, чтение/запись секторов по их абсолютному номеру и т.д. Имеется стандартный интерфейс для различных типов дисков, обеспечиваемых драйвером логических дисков DRIVER.SYS.

DOS имеет специальную функцию номер 44h прерывания 21h. Эта функция имеет множество подфункций и предназначена для поддержки IOCTL. Очень скоро мы займемся этой функцией DOS, а сейчас приведем формат запроса для команды с кодом 19:

(0) 13 header Заголовок запроса.
(+13) 1 funct Это поле содержит код функции команды общего IOCTL.
(+14) 1 subfunc Код подфункции для функции funct.
(+15) 2 si_reg Значение регистра SI при вызове функции 44h прерывания 21h. Эта функция DOS предназначена для управления вводом/выводом.
(+17) 2 di_reg Значение, передаваемое при вызове функции 44h прерывания 21h через регистр DI.
(+19) 4 buf Указатель на буфер данных, содержащий управляющую информацию для устройства или предназначенный для приема информации от устройства.

# 23 - Получить активное логическое устройство

24 - Установить активное логическое устройство

Эти команды обрабатываются только теми драйверами, у которых в слове атрибутов установлен бит 6 - поддержка логических устройств. Команды используются в DOS версии 3.2 и в более поздних версиях.

Команды обеспечивают метод опроса номера текущего активного логического устройства на физическом диске или установления активного логического устройства.

Приведем формат запроса:

(0) 13 header Заголовок запроса.
(+13) 1 unit Код логического устройства, которое должно стать активным при использовании команды 24, или код активного устройства, помещаемый драйвером по команде 23.
(+14) 1 cmd Код команды.
(+15) 4 status Слово состояния.
(+19) 4 reserved Зарезервировано.

По команде 23 (получить активное логическое устройство) драйвер должен поместить идентификатор устройства в поле unit, для устройства А: помещается 1, для В: - 2 и т.д. Если драйвер управляет единственным устройством, он должен записать в поле unit ноль.

После обзора команд перейдем к описанию функции 44h прерывания 21h. Эта функция предназначена для управления вводом/выводом и обладает широкими возможностями.

[Назад] [Содеожание]