Страница:
жился следующий принимаемый символ. После того как символ считан,
бит 0 опять становится равным 0 и остается таковым, пока не при-
будет новый символ.
Хотя здесь об этом не говорилось, но коммуникационные процеду-
ры обычно создают циклический буфер для сбора поступающих симво-
лов. Циклические буфера обсуждались в [3.1.1]. Вы должны также
знать, что если поступающие данные подавать на экран со скоростью
1200 бод, то процедура сдвига экрана BIOS [4.5.1] не будет успе-
вать и произойдет переполнение. Простое решение этих проблем
состоит в использовании коммуникационного прерывания, как объяс-
нено в [7.1.8].
Следующий пример частично дублирует содержимое предыдущего
раздела, относящегося к передаче символов. Как и в том случае код
начинается с бесконечного цикла. Объедините эти 2 процедуры с
процедурами инициализации из [7.1.2] и [7.1.5] для создания за-
конченной процедуры ввода/вывода через коммуникационный канал.
KEEP_TRYING: MOV DX,BASE_ADDRESS ;базовый адрес
ADD DX,5 ;указываем на регистр статуса линии
IN AL,DX ;получаем байт статуса
TEST AL,00011110B ;проверяем на ошибку
JNZ ERROR_ROUTINE ;если да, то на обработку ошибки
TEST AL,00000001B ;проверяем получены ли данные
JNZ RECEIVE ;на процедуру приема данных
TEST AL,00100000B ;проверяем готовность к передаче
JZ KEEP_TRYING ;если нет, то к началу цикла
.
(здесь расположена процедура передачи - см. [7.1.6])
.
;---получаем данные и выводим их на экран
RECEIVE: MOV DX,BASE_ADDRESS ;базовый адрес
IN AL,DX ;читаем полученный символ
CMP AL,19 ;проверка на XOFF
JE XOFF_ROUTINE ;
.
(и т.д.)
.
MOV DL,AL ;готовим символ для вывода на экран
MOV AH,2 ;функция вывода символа
INT 21H ;выводим его
JMP SHORT KEEP_TRYING ;возвращаемся на начало цикла
Хорошая коммуникационная программа имеет слишком много работы,
чтобы посвятить себя целиком вводу/выводу. Поступающие данные
должны анализироваться, передаваемые данные должны собираться, а
большие блоки данных могут записываться на диск или считываться с
него. Коммуникационное прерывание позволяет программе не тратить
на ввод/вывод больше времени, чем он того требует. Например,
после установки прерывания, управление передается процедуре пере-
дачи данныз только в том случае, когда регистр хранения передат-
чика пуст и возвращается программе, как только послан байт дан-
ных, позволяя ей продолжать свою работу до тех пор, пока регистр
хранения передатчика не будет снова готов. Не забудьте ознако-
миться с обсуждением прерываний в [1.2.3], прежде чем продолжить
чтение.
IBM PC отводит два аппаратных прерывания для коммуникационных
каналов, номер 3 (COM1) и 4 (COM2). Отметим, что у PCjr, встроен-
ный модем имеет номер 3, а COM1 - номер 4. Микросхема UART 8250
допускает 4 класса прерываний для каждого канала, используя сле-
дующие двоичные кодовые числа:
00 изменение в регистре статуса модема
01 регистр хранения передатчика пуст
10 получены данные
11 ошибка приема, или получено условие перерыва
Эти коды содержатся в битах 2-1 регистра идентификации прерыва-
ния, адрес порта которого на 2 больше, чем базовый адрес исполь-
зуемого коммуникационного адаптера. Бит 0 этого регистра устанав-
ливается при возникновении прерывания, а остальные биты не ис-
пользуются и всегда равны 0.
Чтобы выбрать одно или более прерываний, надо запрограммиро-
вать регистр разрешения прерывания, адрес которого на 1 больше
базового адреса. Значение его битов такое:
бит 0 1 = прерывание при получении данных
1 1 = прерывание когда регистр хранения передатчика пуст
2 1 = прерывание при ошибке приема данных
3 1 = прерывание при изменении регистра статуса модема
7-4 не используются, всегда 0
Когда одно из этих событий происходит, то инициируется аппаратное
прерывание, возникающее в микросхеме обработки прерываний 8259 по
каналу 3 для COM1 и по каналу 4 для COM2. Процедура обработки
прерываний передает управление тому коду, на который указывает
соответствующий вектор прерывания. Поскольку это аппаратное пре-
рывание, то оно может быть маскировано [1.2.2]. Помните, что
процедура обработки прерывания должна завершаться стандартным
кодом выхода из аппаратного прерывания MOV AL,20H/OUT 20H,AL. На
рис. 7-3 показано коммуникационное прерывание.
Любое число типов прерывания может быть разрешено одновремен-
но. Но если разрешен более чем один тип, то процедура обработки
прерывания должна сама определять какой из типов прерывания прои-
зошел, проверяя регистр идентификации прерывания. Одновременно
могут происходить более чем одно прерывание, поэтому бит 0 ре-
гистра идентификации сообщает о том, что поступило еще одно пре-
рывание. Когда два или более прерываний поступило в один и тот же
момент времени, то они обрабатываются в порядке, указанном в
нижеприведенной таблице. Добавочные прерывания должны быть обра-
ботаны до завершения процедуры обработки прерывания. Условия
предшествующих прерываний "отменяются" с помощью действий, при-
веденных в правом столбце следующей таблицы:
Код Тип Действия для "сброса"
11 ошибка или перерыв чтение регистра статуса линии
10 получены данные чтение регистра приемника данных
01 передатчик готов вывод символа в регистр хранения
передатчика
00 изменение статуса модема чтение регистра статуса модема
Низкий уровень.
Вот общая форма программы, обрабатывающей коммуникационные
прерывания:
;---установка вектора коммуникационного прерывания
PUSH DS ;сохраняем DS
MOV DX,OFFSET IO_INT ;DS:DX указывают на процедуру
MOV AX,SEG IO_INT ;
MOV DS,AX ;
MOV AL,0BH ;номер вектора для COM1
MOV AH,25H ;функция изменения вектора
INT 21H ;меняем вектор прерывания
;---инициализация регистра разрешения прерывания (COM1)
MOV AX,40H ;DS указывает на данные BIOS
MOV DS,AX ;
MOV DX,DS:[0] ;получаем базовый адрес COM1
INC DX ;указываем на регистр разрешения
MOV AL,3 ;прерываний и разрешаем прерывания
OUT DX,AL ;приема и передачи
POP DS ;восстанавливаем регистр
;---процедура обработки прерывания - сначала определяем его тип
IO_INT PROC FAR
NEXT_INT: MOV DX,BASEADDRESS ;базовый адрес
INC DX ;указываем на регистр идентификации
INC DX ;прерывания
IN AL,DX ;читаем его значение
TEST AL,10B ;это прерывание передатчика?
JNZ TRANSMIT ;если да, то на передачу
RECEIVE: ;иначе на прием
.
.
JMP SHORT ANOTHER ;проверяем нет ли другого прерывания
TRANSMIT: ;здесь код для передачи
.
.
;---перед выходом, проверяем нет ли другого прерывания
ANOTHER: MOV DX,BASEADDRESS ;базовый адрес
INC DX ;указываем на регистр идентификации
INC DX ;прерывания
IN AL,DX ;читаем его значение
TEST AL,1 ;проверяем бит 1
JNZ NEXT_INT ;если он установлен, то на начало
MOV AL,20H ;иначе код завершения аппаратного
OUT 20H,AL ;прерывания
IRET
IO_INT ENDP
Эта таблица содержит 32 управляющих кода ASCII, которые ис-
пользуются при коммуникации, а также при работе принтера и других
устройств. Добавлен также код ASCII 127 - DEL (Забой), который
обычно используется как управляющий код, хотя его и нельзя выдать
с клавиатуры с помощью сочетания Ctrl + клавиша. Применение неко-
торых кодов, таких как возврат каретки, инвариантно. Но боль-
шинство других управляющих кодов имеют широкий диапазон интерпре-
тации, во многом из-за отсутствия совместимости оборудования.
Номер кода ASCII Комбинация Мнемо-
10-й 16-й Символ с Ctrl ника Назначение
00 00 ^@ NUL Символ-разделитель (не имеющий значения, поэтому полезен
для задержек)
01 01 ^A SOH Начало заголовка. Начинает передачу блока данных или но-
вого файла.
02 02 ^B STX Начало текста. Отмечает начало текста, следующего за за-
головком данных.
03 03 ^C ETX Конец текста. Может отмечать начало данных, служащих для
контроля ошибок.
04 04 ^D EOT Конец передачи. Код остановки, но иногда он просто отме-
чает конец файла.
05 05 ^E ENQ Запрос. Запрашивает статусную информацию у отдаленной станции.
06 06 ^F ACK Подтверждение. Подтверждает успешный обмен между станциями.
07 07 ^G BEL Звонок. Инициирует звонок, чтобы привлечь внимание.
08 08 ^H BS Возврат на шаг.
09 09 ^I HT Горизонтальная табуляция.
10 0A ^J LF Перевод строки.
11 0B ^K VT Вертикальная табуляция.
12 0C ^L FF Перевод формата.
13 0D ^M CR Возврат каретки.
14 0E ^N SO Сдвиг выключен. Переключает набор символов.
15 0F ^O SI Сдвиг включен. Переключает набор символов.
16 10 ^P DLE Data Link Escape. Модифицирует значение следующих символов
(аналогично Esc).
17 11 ^Q DC1 Управление устройством 1. Используется как сигнал XON для
удаленной станции на передачу.
18 12 ^R DC2 Управление устройством 2. Сигнал переключения общего назна-
чения.
19 13 ^S DC3 Управление устройством 3. Используется как сигнал XOFF для
удаленной станции для прекращения передачи.
20 14 ^T DC4 Управление устройством 4. Сигнал переключения общего назна-
чения.
21 15 ^U NAK Отрицание. Передача неуспешна.
22 16 ^V SYN Промежуток синхронизации. Используется между блоками данных
при синхронной связи.
23 17 ^W TB Конец блока передачи. Вариант ETX.
24 18 ^X CAN Отмена. Обычно сигнализирует об ошибке передачи.
25 19 ^Y EM Конец среды. Сигнализирует о физическом конце источника
данных.
26 1A ^Z SUB Подстановка. Заменяет символы, которые незаконны или невоз-
можно вывести.
27 1B ^[ ESC Отмечает последующие символы, как управляющую последова-
тельность.
28 1C ^/ FS Разделитель файлов. Отмечает логическую границу между фай-
лами.
29 1D ^] GS Разделитель групп. Отмечает логическую границу между груп-
пами данных.
30 1E ^^ RS Разделитель записей. Отмечает логическую границу между за-
писями данных.
31 1F ^_ US Разделитель объектов. Отмечает логическую границу между
объектами данных.
127 7F нет DEL Забой. Удаляет другие символы.
Драйвер устройства это специальная программа, которая управ-
ляет обменом с периферийным устройством, таким как принтер или
дисковый накопитель. Поскольку параметры этих периферийных уст-
ройств меняются от производителя к производителю, то разным поль-
зователям программы может потребоваться дюжина различных драйве-
ров, чтобы он мог работать на имеющемся у него оборудовании.
Имеется 4 способа включения драйверов устройств в программу:
1. Можно поместить код для всех драйверов прямо в программу.
Например, чтобы поддерживать различные принтеры, можно создать
таблицу управляющих последовательностей и искать в ней нужный код
каждый раз когда он потребуется. Этот подход тратит много памяти
и может быть достаточно медленным.
2. Создать ряд драйверов устройств и потребовать, чтобы прог-
рамма загружала необходимый в качестве оверлея (т.е. помещать его
в область программы, специально оставленную для этой цели
[1.3.5]).
3. Создать драйвер устройства как отдельную программу, которая
указывается в командном файле, выполняемом при загрузке системы.
Программа запускается и устанавливает драйвер устройства как
программу обработки прерывания. После этого программа завершает-
ся, но остается резидентной в памяти, как объяснено в [1.3.4].
Впоследствии наша программа использует этот драйвер через вектор
прерывания.
4. Создать полноценный драйвер устройства, который будет заг-
ружаться при старте с помощью файла CONFIG.SYS. MS DOS поддержи-
вает такой тип драйверов устройств и однажды загруженный он может
использовать все возможности команд DOS, включая проверку ошибок.
Специальная команда IOCTL (Контроль ввода/вывода) позволяет прог-
рамме узнать статус драйвера и послать ему управляющую строку,
помимо обычного потока данных.
Первые три стратегии легко реализуются с помощью информации,
приведенной в остальных частях данной книги. Но устанавливаемые
драйверы устройств очень сложны. Зато когда он есть, то он очень
мощен. В этом случае система будет работать с устройством нас-
только же тесно, как с клавиатурой или дисковым накопителем.
Устройству может быть присвоено имя, например, SERIALPR для пос-
ледовательного принтера, и затем это устройство может быть откры-
то для доступа из любого языка. В Бейсике оператор OPEN "SE-
RIALPR" FOR OUTPUT AS #2 подготовит последовательный принтер для
вывода. В языке ассемблера Вы сможете получить доступ к принтеру
как с помощью метода управляющего блока файла, так и с помощью
метода дескриптора файла, включая очень мощную функцию IOCTL. При
этом пользователь имеет возможность доступа к устройству на уров-
не операционной системы и может просто ввести команду COPY A:MY-
FILE SERIALPR:, чтобы скопировать содержимое файла на принтер.
Устанавливаемые драйверы устройств могут быть написаны только
на языке ассемблера. Они могут обслуживать два типа устройств:
символьные и блочные. Эти имена описывают единицы, которыми уст-
ройство обрабатывает данные. Обычно драйверы блочных устройств
обслуживают дисковые накопители, а драйверы символьных - все
остальное, начиная от последовательных принтеров и кончая робота-
ми. Блочные устройства обмениваются блоками данных, поэтому они
занимаются накоплением данных. Символьные устройства обмениваются
данными побайтно, поэтому они лучше подходят для управляющих
устройств, а также для устройств, которые не могут обеспечить
высокую скорость обмена данными. Драйверы блочных устройств очень
сложны и здесь нет достаточно места, чтобы объяснить их структу-
ру. Очень редко кому требуется написать такой драйвер. Техничес-
кое руководство по MS DOS предоставляет всю необходимую информа-
цию и содержит полный пример драйвера виртуального диска в опера-
тивной памяти. Вы можете просмотреть эту информацию после того
как изучите обсуждение драйверов символьных устройств, приведен-
ное здесь.
Устанавливаемые драйверы устройств беспощадны к программистс-
ким ошибкам. Поскольку драйверы автоматически загружаются систе-
мой при загрузке, то невозможно использовать отладчики для выяв-
ления причин неполадок. Поэтому будьте предельно внимательны при
их написании.
Программа драйвера устройства разбивается на три части, каждая
из которых обсуждается отдельно в следующих разделах. Это (1)
заголовок драйвера, который именует устройство и содержит инфор-
мацию об остальных частях драйвера, (2) стратегия драйвера, кото-
рая хранит информацию об области данных, создаваемой MS DOS,
которая называетя заголовком запроса, и (3) обработчик прерывания
устройства, который и содержит код, управляющий устройством.
Драйверы устройств должны создаваться в виде COM файлов
[1.3.6]. Однако они не являются настоящими программами, поскольку
у них отсутствует префикс программного сегмента. Чтобы добиться
этого не надо включать оператор ORG 100H в начале программы, как
это делается для COM файлов. Либо запишите ORG 0, либо вообще
ничего не пишите. Драйвер должен быть описан как далекая (far)
процедура, как и в любой программе. В нижеприведенном примере
приведен начальный код для драйвера устройства с именем DEVICE12.
Оно заменяет стандартное устройство AUX, используемое MS DOS,
принимая вывод функции 4 прерывания 21H. Весь драйвер устройства
состоит из кода этого раздела вместе с кодом, приведенном в сле-
дующих двух разделах; поместите их подряд один за другим, чтобы
получить полную программу.
Драйвер устройства должен начинаться с заголовка драйвера. Он
имеет длину 18 байтов, разделенных на 5 полей. Первое поле (DD)
всегда содержит значение -1 (FFFFFFFFH), и когда MS DOS загружает
драйвер, то оно заменяется на стартовый адрес следующего драйве-
ра. Таким образом, система может искать следующий драйвер по
цепочке. У последнего загруженного драйвера в этом поле остается
значение -1.
Второе поле это байт атрибутов драйвера. Имеют значение только
7 битов этого слова:
бит 15 1 = символьное устройство, 0 = блочное устройство
14 1 = поддерживает IOCTL, 0 = не поддерживает IOCTL
13 1 = формат блоков IBM, 0 = другой формат блоков
3 1 = часы, 0 = не часы
2 1 = нулевое устройство, 0 = не нулевое устройство
1 1 = устройство стандартного вывода, 0 = нет
0 1 = устройство стандартного ввода, 0 = нет
Обычно установлен только бит 15, или биты 15 и 14, если устройст-
во поддерживает IOCTL (как обсуждается в [7.2.4]). Бит 13 уста-
навливается только для блочных устройств. Остальные биты исполь-
зуются для замены устройств, используемых MS DOS по умолчанию
(устройствами стандартного ввода и вывода являются клавиатура и
видеодисплей; устройство часов объединяет часы реального времени
с часами времени суток BIOS; а нулевое устройство (NULL) - это
псевдоустройство, используемое для тестовых целей).
Третье и четвертое поля содержат смещения для процедур страте-
гии и обработки прерывания, которые будут рассмотрены в следующих
разделах. Наконец, последнее поле содержит имя устройства. Имя
может содержать до 8 символов и оно должно быть выравнено по
левому краю с завершающими пробелами. Для замены существующих в
DOS устройств, таких как LPT1 или COM1, используйте то же имя
устройства, как в данном примере.
Низкий уровень.
В данном примере создается драйвер для последовательного уст-
ройства. "DEVICE12" - имя файла, который должен быть указан в
файле конфигурации сиситемы, чтобы этот драйвер был загружен. В
байте атрибутов установлен только бит 15, указывая что это сим-
вольное устройство и что оно не поддерживает IOCTL. DEV_STRATEGY
и DEV_INTERRUPT - имена процедур, обсуждаемых в следующих разде-
лах. Устройство названо AUX, с тем чтобы заменить обычное уст-
ройство MS DOS с этим именем. Это позволяет очень просто обра-
щаться к этому устройству, поскольку система имеет предопределен-
ный номер файла для обращения к устройству AUX (последовательно-
му). В пример включен начальный код для драйвера, определяющий
его как COM программу.
CSEG SEGMENT PUBLIC 'CODE' 'устанавливаем кодовый сегмент
ORG 0 'эта строка необязательна
ASSUME CS:CSEG,DS:CSEG,ES:CSEG
DEVICE12 PROC FAR 'драйвер это далекая процедура
DD 0FFFFFFFFH 'адрес следующего драйвера
DW 8000H 'байт атрибутов
DW DEV_STATEGY 'адрес процедуры стратегии
DW DEV_INTERRUPT 'адрес процедуры прерывания
DB 'AUX ' 'имя устройство (дополненное пробелами)
Процедура стратегии устройства требует только пяти строк.
Когда система загружает устройство, то она создает блок данных,
называемый заголовком запроса. Он имеет две функции. Во-первых он
служит областью данных для внутренних операций системы. Более
важно то, что заголовок запроса служит областью, через которую
происходит обмен информацией между драйвером и вызывающей его
программой. Например, когда драйвер выводит данные, то ему дается
адрес данных через заголовок запроса. Когда же драйвер завершает
свою работу, то он устанавливает в заголовке запроса байт стату-
са, который доступен вызывающей программе, тем самым давая воз-
можность ей узнать об ошибке.
MS DOS создает заголовок запроса при установке драйвера уст-
ройства (когда система загружается). Процедура стратегии уст-
ройства выполняется только один раз в этот момент. При этом ES:BX
указывают на вновь созданный заголовок запроса и процедуре нужно
просто скопировать их, чтобы впоследствии он мог быть обнаружен
при обращении к драйверу. Адреса смещения и сегмента заголовка
помещаются в две переменные. В следующем разделе Вы увидите, что
при обращении к драйверу, первое что он делает - восстанавливает
значения ES:BX, чтобы можно было получить информацию из заголовка
запроса.
Размер заголовка запроса может меняться, в зависимости от типа
сделанного запроса к драйверу (напр. инициализация, вывод данных
или возврат статуса). Однако первые 13 байт заголовка всегда одни
и те же. Их формат таков:
1. Длина заголовка запроса (DB).
2. Код устройства (DB). Определяет номер для блочных устройств.
3. Код команды (DB). Здесь хранится номер последней посланной
драйверу команды. Эти коды перечислены в [7.2.3].
4. Статус (DW). Статус устанавливается каждый раз при вызове
драйвера. Если установлен бит 15, то в младших восьми битах нахо-
дится код ошибки. Коды ошибок перечислены в [7.2.3].
5. Резервная область (8 байтов). Используется MS DOS.
6. Данные необходимые для работы драйвера (переменной длины).
Низкий уровень.
Вот 5 строк процедуры стратегии устройства. Отмечаем, что две
словные переменные, хранящие значения ES и BX, следуют за инст-
рукцией RET, как и положено в формате COM.
DEV_STRATEGY: MOV CS:KEEP_ES,ES
MOV CS:KEEP_BX,BX
RET
KEEP_CS DW ?
KEEP_BX DW ?
Драйвер устройства начинается с двух порций кода, приведенных
в предыдущих разделах. За ними должна следовать соответствующая
процедура обработки прерывания. На самом деле, это неверно, назы-
вать эту процедуру процедурой обработки прерывания, так как она
вовсе не обслуживает прерывание и завершается обычной инструкцией
RET.
Имеется 13 типов функций, которые может выполнять устанавли-
ваемый драйвер устройства. Когда драйвер вызывается функцией DOS
(скажем функцией 3FH прерывания 21H, которая читает данные из
файла или устройства), то функция помещает кодовый номер от 1 до
13 в однобайтное поле по смещению 2 в заголовке запроса (для
ввода - кодовый номер 5). Затем управление передается процедуре
обработки прерывания драйвера, адоес которой определяется при
просмотре заголовка драйвера [7.2.1]. Эта процедура в первую
очередь восстанавливает ES:BX, с тем чтобы они указывали на заго-
ловок запроса, а затем читает кодовый номер команды. По этому
коду процедура обработки прерывания вызывает нужную процедуру,
которая выполнит требуемую функцию. Процедура ищется с помощью
13-словной таблицы, содержащей смещения для 13 типов функций.
Функции всегда перечисляются в следующем порядке:
1. INITIALIZE (инициализация)
2. CHECK_MEDIA (проверка носителя)
3. MAKE_BPB
4. IOCTL_IN
5. INPUT_DATA (ввод данных)
6. NONDESTRUCT_IN
7. INPUT_STATUS (статус ввода)
8. CLEAR_INPUT (очистка ввода)
9. OUTPUT_DATA (вывод данных)
10. OUTPUT_VERIFY (проверка вывода)
11. OUTPUT_STATUS (статус вывода)
12. CLEAR_OUTPUT (очистка вывода)
13. IOCTL_OUT
После завершения процедуры, процедура обработки прерывания
завершается инструкцией RET и управление возвращается в вызываю-
щую программу. Драйвер устройства может включать код для обработ-
ки только некоторых функций, в зависимости от устройства и тре-
буемой степени контроля ошибок и управления устройством. Номера
функций, для которых не написаны процедуры, должны завершаться
выходом из драйвера без выполнения чего-либо. В этом случае надо
только перед выходом установить биты 15, 8, 1 и 0 в заголовке
запроса, чтобы информировать вызывающую задачу, что была затребо-
вана несуществующая функция (бит 15 индицирует ошибку, бит 8
показывает, что драйвер работает нормально, а биты 0 и 1 дают код
ошибки 3, что соответствует "неизвестной команде").
Но одна функция должна присутствовать во всех драйверах уст-
ройств, и это функция номер 1 - инициализация. Эта функция авто-
матически выполняется при загрузке драйвера, а затем нет. Одна из
важных задач, выполняемая этой процедурой, состоит установке
адреса конца драйвера в четырех байтах, начинающихся со смещения
14 в заголовке запроса. В нижеприведенном примере конец программы
отмечен меткой eop:. Кроме этой задачи, процедура инициализации
должна также выполнить всю необходимую для данного устройства
инициализацию. На рис. 7-4 показана структура драйвера устройст-
ва.
Какие из оставшихся 12-ти функций будут включены в драйвер
устройства зависит от того, что драйвер должен делать. Некоторые,
такие как CHECK_MEDIA и MAKE_BPB, относятся только к блочным
устройствам (они устанавливают тип диска, размер секторов и
т.д.). Для символьных устройств наиболее важными являются две
функции: INPUT_DATA и OUTPUT_DATA (отметим, что эти имена несу-
щественны - важна позиция в таблице функций, которая неизменна).
В обоих случаях заголовок запроса имеет следующую структуру:
13 байтов стандартный формат заголовка запроса
1 байт байт описания среды (только для блочных устройств)
4 байта смещение/сегмент буфера обмена данных
2 байта число байтов, которое надо передать
2 байта стартовый номер сектора (только для блочных)
В нижеприведенном примере используется функция вывода. Процедура,
выполняющая вывод получает из заголовка запроса адрес буфера, в
котором находятся выводимые данные (смещение 14). Она также счи-
тывает число байтов, которое надо вывести (смещение 18). Когда
процедура завершит вывод данных, то она установит слово статуса в
заголовке запроса (смещение 3) и возвратит управление. Если опе-
бит 0 опять становится равным 0 и остается таковым, пока не при-
будет новый символ.
Хотя здесь об этом не говорилось, но коммуникационные процеду-
ры обычно создают циклический буфер для сбора поступающих симво-
лов. Циклические буфера обсуждались в [3.1.1]. Вы должны также
знать, что если поступающие данные подавать на экран со скоростью
1200 бод, то процедура сдвига экрана BIOS [4.5.1] не будет успе-
вать и произойдет переполнение. Простое решение этих проблем
состоит в использовании коммуникационного прерывания, как объяс-
нено в [7.1.8].
Следующий пример частично дублирует содержимое предыдущего
раздела, относящегося к передаче символов. Как и в том случае код
начинается с бесконечного цикла. Объедините эти 2 процедуры с
процедурами инициализации из [7.1.2] и [7.1.5] для создания за-
конченной процедуры ввода/вывода через коммуникационный канал.
KEEP_TRYING: MOV DX,BASE_ADDRESS ;базовый адрес
ADD DX,5 ;указываем на регистр статуса линии
IN AL,DX ;получаем байт статуса
TEST AL,00011110B ;проверяем на ошибку
JNZ ERROR_ROUTINE ;если да, то на обработку ошибки
TEST AL,00000001B ;проверяем получены ли данные
JNZ RECEIVE ;на процедуру приема данных
TEST AL,00100000B ;проверяем готовность к передаче
JZ KEEP_TRYING ;если нет, то к началу цикла
.
(здесь расположена процедура передачи - см. [7.1.6])
.
;---получаем данные и выводим их на экран
RECEIVE: MOV DX,BASE_ADDRESS ;базовый адрес
IN AL,DX ;читаем полученный символ
CMP AL,19 ;проверка на XOFF
JE XOFF_ROUTINE ;
.
(и т.д.)
.
MOV DL,AL ;готовим символ для вывода на экран
MOV AH,2 ;функция вывода символа
INT 21H ;выводим его
JMP SHORT KEEP_TRYING ;возвращаемся на начало цикла
Хорошая коммуникационная программа имеет слишком много работы,
чтобы посвятить себя целиком вводу/выводу. Поступающие данные
должны анализироваться, передаваемые данные должны собираться, а
большие блоки данных могут записываться на диск или считываться с
него. Коммуникационное прерывание позволяет программе не тратить
на ввод/вывод больше времени, чем он того требует. Например,
после установки прерывания, управление передается процедуре пере-
дачи данныз только в том случае, когда регистр хранения передат-
чика пуст и возвращается программе, как только послан байт дан-
ных, позволяя ей продолжать свою работу до тех пор, пока регистр
хранения передатчика не будет снова готов. Не забудьте ознако-
миться с обсуждением прерываний в [1.2.3], прежде чем продолжить
чтение.
IBM PC отводит два аппаратных прерывания для коммуникационных
каналов, номер 3 (COM1) и 4 (COM2). Отметим, что у PCjr, встроен-
ный модем имеет номер 3, а COM1 - номер 4. Микросхема UART 8250
допускает 4 класса прерываний для каждого канала, используя сле-
дующие двоичные кодовые числа:
00 изменение в регистре статуса модема
01 регистр хранения передатчика пуст
10 получены данные
11 ошибка приема, или получено условие перерыва
Эти коды содержатся в битах 2-1 регистра идентификации прерыва-
ния, адрес порта которого на 2 больше, чем базовый адрес исполь-
зуемого коммуникационного адаптера. Бит 0 этого регистра устанав-
ливается при возникновении прерывания, а остальные биты не ис-
пользуются и всегда равны 0.
Чтобы выбрать одно или более прерываний, надо запрограммиро-
вать регистр разрешения прерывания, адрес которого на 1 больше
базового адреса. Значение его битов такое:
бит 0 1 = прерывание при получении данных
1 1 = прерывание когда регистр хранения передатчика пуст
2 1 = прерывание при ошибке приема данных
3 1 = прерывание при изменении регистра статуса модема
7-4 не используются, всегда 0
Когда одно из этих событий происходит, то инициируется аппаратное
прерывание, возникающее в микросхеме обработки прерываний 8259 по
каналу 3 для COM1 и по каналу 4 для COM2. Процедура обработки
прерываний передает управление тому коду, на который указывает
соответствующий вектор прерывания. Поскольку это аппаратное пре-
рывание, то оно может быть маскировано [1.2.2]. Помните, что
процедура обработки прерывания должна завершаться стандартным
кодом выхода из аппаратного прерывания MOV AL,20H/OUT 20H,AL. На
рис. 7-3 показано коммуникационное прерывание.
Любое число типов прерывания может быть разрешено одновремен-
но. Но если разрешен более чем один тип, то процедура обработки
прерывания должна сама определять какой из типов прерывания прои-
зошел, проверяя регистр идентификации прерывания. Одновременно
могут происходить более чем одно прерывание, поэтому бит 0 ре-
гистра идентификации сообщает о том, что поступило еще одно пре-
рывание. Когда два или более прерываний поступило в один и тот же
момент времени, то они обрабатываются в порядке, указанном в
нижеприведенной таблице. Добавочные прерывания должны быть обра-
ботаны до завершения процедуры обработки прерывания. Условия
предшествующих прерываний "отменяются" с помощью действий, при-
веденных в правом столбце следующей таблицы:
Код Тип Действия для "сброса"
11 ошибка или перерыв чтение регистра статуса линии
10 получены данные чтение регистра приемника данных
01 передатчик готов вывод символа в регистр хранения
передатчика
00 изменение статуса модема чтение регистра статуса модема
Низкий уровень.
Вот общая форма программы, обрабатывающей коммуникационные
прерывания:
;---установка вектора коммуникационного прерывания
PUSH DS ;сохраняем DS
MOV DX,OFFSET IO_INT ;DS:DX указывают на процедуру
MOV AX,SEG IO_INT ;
MOV DS,AX ;
MOV AL,0BH ;номер вектора для COM1
MOV AH,25H ;функция изменения вектора
INT 21H ;меняем вектор прерывания
;---инициализация регистра разрешения прерывания (COM1)
MOV AX,40H ;DS указывает на данные BIOS
MOV DS,AX ;
MOV DX,DS:[0] ;получаем базовый адрес COM1
INC DX ;указываем на регистр разрешения
MOV AL,3 ;прерываний и разрешаем прерывания
OUT DX,AL ;приема и передачи
POP DS ;восстанавливаем регистр
;---процедура обработки прерывания - сначала определяем его тип
IO_INT PROC FAR
NEXT_INT: MOV DX,BASEADDRESS ;базовый адрес
INC DX ;указываем на регистр идентификации
INC DX ;прерывания
IN AL,DX ;читаем его значение
TEST AL,10B ;это прерывание передатчика?
JNZ TRANSMIT ;если да, то на передачу
RECEIVE: ;иначе на прием
.
.
JMP SHORT ANOTHER ;проверяем нет ли другого прерывания
TRANSMIT: ;здесь код для передачи
.
.
;---перед выходом, проверяем нет ли другого прерывания
ANOTHER: MOV DX,BASEADDRESS ;базовый адрес
INC DX ;указываем на регистр идентификации
INC DX ;прерывания
IN AL,DX ;читаем его значение
TEST AL,1 ;проверяем бит 1
JNZ NEXT_INT ;если он установлен, то на начало
MOV AL,20H ;иначе код завершения аппаратного
OUT 20H,AL ;прерывания
IRET
IO_INT ENDP
Эта таблица содержит 32 управляющих кода ASCII, которые ис-
пользуются при коммуникации, а также при работе принтера и других
устройств. Добавлен также код ASCII 127 - DEL (Забой), который
обычно используется как управляющий код, хотя его и нельзя выдать
с клавиатуры с помощью сочетания Ctrl + клавиша. Применение неко-
торых кодов, таких как возврат каретки, инвариантно. Но боль-
шинство других управляющих кодов имеют широкий диапазон интерпре-
тации, во многом из-за отсутствия совместимости оборудования.
Номер кода ASCII Комбинация Мнемо-
10-й 16-й Символ с Ctrl ника Назначение
00 00 ^@ NUL Символ-разделитель (не имеющий значения, поэтому полезен
для задержек)
01 01 ^A SOH Начало заголовка. Начинает передачу блока данных или но-
вого файла.
02 02 ^B STX Начало текста. Отмечает начало текста, следующего за за-
головком данных.
03 03 ^C ETX Конец текста. Может отмечать начало данных, служащих для
контроля ошибок.
04 04 ^D EOT Конец передачи. Код остановки, но иногда он просто отме-
чает конец файла.
05 05 ^E ENQ Запрос. Запрашивает статусную информацию у отдаленной станции.
06 06 ^F ACK Подтверждение. Подтверждает успешный обмен между станциями.
07 07 ^G BEL Звонок. Инициирует звонок, чтобы привлечь внимание.
08 08 ^H BS Возврат на шаг.
09 09 ^I HT Горизонтальная табуляция.
10 0A ^J LF Перевод строки.
11 0B ^K VT Вертикальная табуляция.
12 0C ^L FF Перевод формата.
13 0D ^M CR Возврат каретки.
14 0E ^N SO Сдвиг выключен. Переключает набор символов.
15 0F ^O SI Сдвиг включен. Переключает набор символов.
16 10 ^P DLE Data Link Escape. Модифицирует значение следующих символов
(аналогично Esc).
17 11 ^Q DC1 Управление устройством 1. Используется как сигнал XON для
удаленной станции на передачу.
18 12 ^R DC2 Управление устройством 2. Сигнал переключения общего назна-
чения.
19 13 ^S DC3 Управление устройством 3. Используется как сигнал XOFF для
удаленной станции для прекращения передачи.
20 14 ^T DC4 Управление устройством 4. Сигнал переключения общего назна-
чения.
21 15 ^U NAK Отрицание. Передача неуспешна.
22 16 ^V SYN Промежуток синхронизации. Используется между блоками данных
при синхронной связи.
23 17 ^W TB Конец блока передачи. Вариант ETX.
24 18 ^X CAN Отмена. Обычно сигнализирует об ошибке передачи.
25 19 ^Y EM Конец среды. Сигнализирует о физическом конце источника
данных.
26 1A ^Z SUB Подстановка. Заменяет символы, которые незаконны или невоз-
можно вывести.
27 1B ^[ ESC Отмечает последующие символы, как управляющую последова-
тельность.
28 1C ^/ FS Разделитель файлов. Отмечает логическую границу между фай-
лами.
29 1D ^] GS Разделитель групп. Отмечает логическую границу между груп-
пами данных.
30 1E ^^ RS Разделитель записей. Отмечает логическую границу между за-
писями данных.
31 1F ^_ US Разделитель объектов. Отмечает логическую границу между
объектами данных.
127 7F нет DEL Забой. Удаляет другие символы.
Драйвер устройства это специальная программа, которая управ-
ляет обменом с периферийным устройством, таким как принтер или
дисковый накопитель. Поскольку параметры этих периферийных уст-
ройств меняются от производителя к производителю, то разным поль-
зователям программы может потребоваться дюжина различных драйве-
ров, чтобы он мог работать на имеющемся у него оборудовании.
Имеется 4 способа включения драйверов устройств в программу:
1. Можно поместить код для всех драйверов прямо в программу.
Например, чтобы поддерживать различные принтеры, можно создать
таблицу управляющих последовательностей и искать в ней нужный код
каждый раз когда он потребуется. Этот подход тратит много памяти
и может быть достаточно медленным.
2. Создать ряд драйверов устройств и потребовать, чтобы прог-
рамма загружала необходимый в качестве оверлея (т.е. помещать его
в область программы, специально оставленную для этой цели
[1.3.5]).
3. Создать драйвер устройства как отдельную программу, которая
указывается в командном файле, выполняемом при загрузке системы.
Программа запускается и устанавливает драйвер устройства как
программу обработки прерывания. После этого программа завершает-
ся, но остается резидентной в памяти, как объяснено в [1.3.4].
Впоследствии наша программа использует этот драйвер через вектор
прерывания.
4. Создать полноценный драйвер устройства, который будет заг-
ружаться при старте с помощью файла CONFIG.SYS. MS DOS поддержи-
вает такой тип драйверов устройств и однажды загруженный он может
использовать все возможности команд DOS, включая проверку ошибок.
Специальная команда IOCTL (Контроль ввода/вывода) позволяет прог-
рамме узнать статус драйвера и послать ему управляющую строку,
помимо обычного потока данных.
Первые три стратегии легко реализуются с помощью информации,
приведенной в остальных частях данной книги. Но устанавливаемые
драйверы устройств очень сложны. Зато когда он есть, то он очень
мощен. В этом случае система будет работать с устройством нас-
только же тесно, как с клавиатурой или дисковым накопителем.
Устройству может быть присвоено имя, например, SERIALPR для пос-
ледовательного принтера, и затем это устройство может быть откры-
то для доступа из любого языка. В Бейсике оператор OPEN "SE-
RIALPR" FOR OUTPUT AS #2 подготовит последовательный принтер для
вывода. В языке ассемблера Вы сможете получить доступ к принтеру
как с помощью метода управляющего блока файла, так и с помощью
метода дескриптора файла, включая очень мощную функцию IOCTL. При
этом пользователь имеет возможность доступа к устройству на уров-
не операционной системы и может просто ввести команду COPY A:MY-
FILE SERIALPR:, чтобы скопировать содержимое файла на принтер.
Устанавливаемые драйверы устройств могут быть написаны только
на языке ассемблера. Они могут обслуживать два типа устройств:
символьные и блочные. Эти имена описывают единицы, которыми уст-
ройство обрабатывает данные. Обычно драйверы блочных устройств
обслуживают дисковые накопители, а драйверы символьных - все
остальное, начиная от последовательных принтеров и кончая робота-
ми. Блочные устройства обмениваются блоками данных, поэтому они
занимаются накоплением данных. Символьные устройства обмениваются
данными побайтно, поэтому они лучше подходят для управляющих
устройств, а также для устройств, которые не могут обеспечить
высокую скорость обмена данными. Драйверы блочных устройств очень
сложны и здесь нет достаточно места, чтобы объяснить их структу-
ру. Очень редко кому требуется написать такой драйвер. Техничес-
кое руководство по MS DOS предоставляет всю необходимую информа-
цию и содержит полный пример драйвера виртуального диска в опера-
тивной памяти. Вы можете просмотреть эту информацию после того
как изучите обсуждение драйверов символьных устройств, приведен-
ное здесь.
Устанавливаемые драйверы устройств беспощадны к программистс-
ким ошибкам. Поскольку драйверы автоматически загружаются систе-
мой при загрузке, то невозможно использовать отладчики для выяв-
ления причин неполадок. Поэтому будьте предельно внимательны при
их написании.
Программа драйвера устройства разбивается на три части, каждая
из которых обсуждается отдельно в следующих разделах. Это (1)
заголовок драйвера, который именует устройство и содержит инфор-
мацию об остальных частях драйвера, (2) стратегия драйвера, кото-
рая хранит информацию об области данных, создаваемой MS DOS,
которая называетя заголовком запроса, и (3) обработчик прерывания
устройства, который и содержит код, управляющий устройством.
Драйверы устройств должны создаваться в виде COM файлов
[1.3.6]. Однако они не являются настоящими программами, поскольку
у них отсутствует префикс программного сегмента. Чтобы добиться
этого не надо включать оператор ORG 100H в начале программы, как
это делается для COM файлов. Либо запишите ORG 0, либо вообще
ничего не пишите. Драйвер должен быть описан как далекая (far)
процедура, как и в любой программе. В нижеприведенном примере
приведен начальный код для драйвера устройства с именем DEVICE12.
Оно заменяет стандартное устройство AUX, используемое MS DOS,
принимая вывод функции 4 прерывания 21H. Весь драйвер устройства
состоит из кода этого раздела вместе с кодом, приведенном в сле-
дующих двух разделах; поместите их подряд один за другим, чтобы
получить полную программу.
Драйвер устройства должен начинаться с заголовка драйвера. Он
имеет длину 18 байтов, разделенных на 5 полей. Первое поле (DD)
всегда содержит значение -1 (FFFFFFFFH), и когда MS DOS загружает
драйвер, то оно заменяется на стартовый адрес следующего драйве-
ра. Таким образом, система может искать следующий драйвер по
цепочке. У последнего загруженного драйвера в этом поле остается
значение -1.
Второе поле это байт атрибутов драйвера. Имеют значение только
7 битов этого слова:
бит 15 1 = символьное устройство, 0 = блочное устройство
14 1 = поддерживает IOCTL, 0 = не поддерживает IOCTL
13 1 = формат блоков IBM, 0 = другой формат блоков
3 1 = часы, 0 = не часы
2 1 = нулевое устройство, 0 = не нулевое устройство
1 1 = устройство стандартного вывода, 0 = нет
0 1 = устройство стандартного ввода, 0 = нет
Обычно установлен только бит 15, или биты 15 и 14, если устройст-
во поддерживает IOCTL (как обсуждается в [7.2.4]). Бит 13 уста-
навливается только для блочных устройств. Остальные биты исполь-
зуются для замены устройств, используемых MS DOS по умолчанию
(устройствами стандартного ввода и вывода являются клавиатура и
видеодисплей; устройство часов объединяет часы реального времени
с часами времени суток BIOS; а нулевое устройство (NULL) - это
псевдоустройство, используемое для тестовых целей).
Третье и четвертое поля содержат смещения для процедур страте-
гии и обработки прерывания, которые будут рассмотрены в следующих
разделах. Наконец, последнее поле содержит имя устройства. Имя
может содержать до 8 символов и оно должно быть выравнено по
левому краю с завершающими пробелами. Для замены существующих в
DOS устройств, таких как LPT1 или COM1, используйте то же имя
устройства, как в данном примере.
Низкий уровень.
В данном примере создается драйвер для последовательного уст-
ройства. "DEVICE12" - имя файла, который должен быть указан в
файле конфигурации сиситемы, чтобы этот драйвер был загружен. В
байте атрибутов установлен только бит 15, указывая что это сим-
вольное устройство и что оно не поддерживает IOCTL. DEV_STRATEGY
и DEV_INTERRUPT - имена процедур, обсуждаемых в следующих разде-
лах. Устройство названо AUX, с тем чтобы заменить обычное уст-
ройство MS DOS с этим именем. Это позволяет очень просто обра-
щаться к этому устройству, поскольку система имеет предопределен-
ный номер файла для обращения к устройству AUX (последовательно-
му). В пример включен начальный код для драйвера, определяющий
его как COM программу.
CSEG SEGMENT PUBLIC 'CODE' 'устанавливаем кодовый сегмент
ORG 0 'эта строка необязательна
ASSUME CS:CSEG,DS:CSEG,ES:CSEG
DEVICE12 PROC FAR 'драйвер это далекая процедура
DD 0FFFFFFFFH 'адрес следующего драйвера
DW 8000H 'байт атрибутов
DW DEV_STATEGY 'адрес процедуры стратегии
DW DEV_INTERRUPT 'адрес процедуры прерывания
DB 'AUX ' 'имя устройство (дополненное пробелами)
Процедура стратегии устройства требует только пяти строк.
Когда система загружает устройство, то она создает блок данных,
называемый заголовком запроса. Он имеет две функции. Во-первых он
служит областью данных для внутренних операций системы. Более
важно то, что заголовок запроса служит областью, через которую
происходит обмен информацией между драйвером и вызывающей его
программой. Например, когда драйвер выводит данные, то ему дается
адрес данных через заголовок запроса. Когда же драйвер завершает
свою работу, то он устанавливает в заголовке запроса байт стату-
са, который доступен вызывающей программе, тем самым давая воз-
можность ей узнать об ошибке.
MS DOS создает заголовок запроса при установке драйвера уст-
ройства (когда система загружается). Процедура стратегии уст-
ройства выполняется только один раз в этот момент. При этом ES:BX
указывают на вновь созданный заголовок запроса и процедуре нужно
просто скопировать их, чтобы впоследствии он мог быть обнаружен
при обращении к драйверу. Адреса смещения и сегмента заголовка
помещаются в две переменные. В следующем разделе Вы увидите, что
при обращении к драйверу, первое что он делает - восстанавливает
значения ES:BX, чтобы можно было получить информацию из заголовка
запроса.
Размер заголовка запроса может меняться, в зависимости от типа
сделанного запроса к драйверу (напр. инициализация, вывод данных
или возврат статуса). Однако первые 13 байт заголовка всегда одни
и те же. Их формат таков:
1. Длина заголовка запроса (DB).
2. Код устройства (DB). Определяет номер для блочных устройств.
3. Код команды (DB). Здесь хранится номер последней посланной
драйверу команды. Эти коды перечислены в [7.2.3].
4. Статус (DW). Статус устанавливается каждый раз при вызове
драйвера. Если установлен бит 15, то в младших восьми битах нахо-
дится код ошибки. Коды ошибок перечислены в [7.2.3].
5. Резервная область (8 байтов). Используется MS DOS.
6. Данные необходимые для работы драйвера (переменной длины).
Низкий уровень.
Вот 5 строк процедуры стратегии устройства. Отмечаем, что две
словные переменные, хранящие значения ES и BX, следуют за инст-
рукцией RET, как и положено в формате COM.
DEV_STRATEGY: MOV CS:KEEP_ES,ES
MOV CS:KEEP_BX,BX
RET
KEEP_CS DW ?
KEEP_BX DW ?
Драйвер устройства начинается с двух порций кода, приведенных
в предыдущих разделах. За ними должна следовать соответствующая
процедура обработки прерывания. На самом деле, это неверно, назы-
вать эту процедуру процедурой обработки прерывания, так как она
вовсе не обслуживает прерывание и завершается обычной инструкцией
RET.
Имеется 13 типов функций, которые может выполнять устанавли-
ваемый драйвер устройства. Когда драйвер вызывается функцией DOS
(скажем функцией 3FH прерывания 21H, которая читает данные из
файла или устройства), то функция помещает кодовый номер от 1 до
13 в однобайтное поле по смещению 2 в заголовке запроса (для
ввода - кодовый номер 5). Затем управление передается процедуре
обработки прерывания драйвера, адоес которой определяется при
просмотре заголовка драйвера [7.2.1]. Эта процедура в первую
очередь восстанавливает ES:BX, с тем чтобы они указывали на заго-
ловок запроса, а затем читает кодовый номер команды. По этому
коду процедура обработки прерывания вызывает нужную процедуру,
которая выполнит требуемую функцию. Процедура ищется с помощью
13-словной таблицы, содержащей смещения для 13 типов функций.
Функции всегда перечисляются в следующем порядке:
1. INITIALIZE (инициализация)
2. CHECK_MEDIA (проверка носителя)
3. MAKE_BPB
4. IOCTL_IN
5. INPUT_DATA (ввод данных)
6. NONDESTRUCT_IN
7. INPUT_STATUS (статус ввода)
8. CLEAR_INPUT (очистка ввода)
9. OUTPUT_DATA (вывод данных)
10. OUTPUT_VERIFY (проверка вывода)
11. OUTPUT_STATUS (статус вывода)
12. CLEAR_OUTPUT (очистка вывода)
13. IOCTL_OUT
После завершения процедуры, процедура обработки прерывания
завершается инструкцией RET и управление возвращается в вызываю-
щую программу. Драйвер устройства может включать код для обработ-
ки только некоторых функций, в зависимости от устройства и тре-
буемой степени контроля ошибок и управления устройством. Номера
функций, для которых не написаны процедуры, должны завершаться
выходом из драйвера без выполнения чего-либо. В этом случае надо
только перед выходом установить биты 15, 8, 1 и 0 в заголовке
запроса, чтобы информировать вызывающую задачу, что была затребо-
вана несуществующая функция (бит 15 индицирует ошибку, бит 8
показывает, что драйвер работает нормально, а биты 0 и 1 дают код
ошибки 3, что соответствует "неизвестной команде").
Но одна функция должна присутствовать во всех драйверах уст-
ройств, и это функция номер 1 - инициализация. Эта функция авто-
матически выполняется при загрузке драйвера, а затем нет. Одна из
важных задач, выполняемая этой процедурой, состоит установке
адреса конца драйвера в четырех байтах, начинающихся со смещения
14 в заголовке запроса. В нижеприведенном примере конец программы
отмечен меткой eop:. Кроме этой задачи, процедура инициализации
должна также выполнить всю необходимую для данного устройства
инициализацию. На рис. 7-4 показана структура драйвера устройст-
ва.
Какие из оставшихся 12-ти функций будут включены в драйвер
устройства зависит от того, что драйвер должен делать. Некоторые,
такие как CHECK_MEDIA и MAKE_BPB, относятся только к блочным
устройствам (они устанавливают тип диска, размер секторов и
т.д.). Для символьных устройств наиболее важными являются две
функции: INPUT_DATA и OUTPUT_DATA (отметим, что эти имена несу-
щественны - важна позиция в таблице функций, которая неизменна).
В обоих случаях заголовок запроса имеет следующую структуру:
13 байтов стандартный формат заголовка запроса
1 байт байт описания среды (только для блочных устройств)
4 байта смещение/сегмент буфера обмена данных
2 байта число байтов, которое надо передать
2 байта стартовый номер сектора (только для блочных)
В нижеприведенном примере используется функция вывода. Процедура,
выполняющая вывод получает из заголовка запроса адрес буфера, в
котором находятся выводимые данные (смещение 14). Она также счи-
тывает число байтов, которое надо вывести (смещение 18). Когда
процедура завершит вывод данных, то она установит слово статуса в
заголовке запроса (смещение 3) и возвратит управление. Если опе-