Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
jourdain_spravochnik_programmista.docx
Скачиваний:
15
Добавлен:
24.11.2018
Размер:
814.58 Кб
Скачать

Раздел 2. Создание драйвера устройства.

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

ляет обменом с периферийным устройством, таким как принтер или

дисковый накопитель. Поскольку параметры этих периферийных уст-

ройств меняются от производителя к производителю, то разным поль-

зователям программы может потребоваться дюжина различных драйве-

ров, чтобы он мог работать на имеющемся у него оборудовании.

Имеется 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) обработчик прерывания

устройства, который и содержит код, управляющий устройством.

7.2.1 Создание заголовка драйвера.

Драйверы устройств должны создаваться в виде 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 ' 'имя устройство (дополненное пробелами)

7.2.2 Создание стратегии устройства.

Процедура стратегии устройства требует только пяти строк.

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

называемый заголовком запроса. Он имеет две функции. Во-первых он

служит областью данных для внутренних операций системы. Более

важно то, что заголовок запроса служит областью, через которую

происходит обмен информацией между драйвером и вызывающей его

программой. Например, когда драйвер выводит данные, то ему дается

адрес данных через заголовок запроса. Когда же драйвер завершает

свою работу, то он устанавливает в заголовке запроса байт стату-

са, который доступен вызывающей программе, тем самым давая воз-

можность ей узнать об ошибке.

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 ?

7.2.3 Создание обработчика прерывания устройства.

Драйвер устройства начинается с двух порций кода, приведенных

в предыдущих разделах. За ними должна следовать соответствующая

процедура обработки прерывания. На самом деле, это неверно, назы-

вать эту процедуру процедурой обработки прерывания, так как она

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

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) и возвратит управление. Если опе-

рация успешна, то надо установить бит 8 слова статуса. Другие

возможности будут обсуждены позднее.

Низкий уровень.

В данном примере приведена общая форма процедуры обработки

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

;---инициализация обработчика прерывания устройства

DEV_INTERRUPT: PUSH ES ;сохраняем регистры

PUSH DS

PUSH AX

PUSH BX

PUSH CX

PUSH DX

PUSH SI

PUSH DI

PUSH BP

MOV AX,CS:KEEP_ES ;ES:BX указывают на заголовок запроса

MOV ES,AX ;

MOV BX,CS:KEEP_BX ;

MOV AL,ES:[BX]+2 ;получаем код команды из заголовка

SHL AL,1 ;умножаем на 2 (т.к. таблица словная)

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

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

CHECK_MEDIA:

MAKE_BPB:

IOCTL_IN:

INPUT_DATA:

NONDESTRUCT_IN:

INPUT_STATUS:

CLEAR_INPUT:

OUTPUT_VERIFY:

OUTPUT_STATUS:

CLEAR_OUTPUT:

IOCTL_OUT:

OR ES:WORD PTR [BX]+3,8103H ;модифицируем статус

JMP QUIT

;---процедуры для двух поддерживаемых кодов

INITIALIZE: LEA AX,E_O_P ;смещение конца программы в AX

MOV ES:WORD PTR [BX]+14,AX ;помещаем его в заголовок

MOV ES:WORD PTR [BX]+16,CS ;

.

(здесь идет инициализация устройства)

.

JMP QUIT

OUTPUT_DATA: MOV CL,ES:[BX]+18 ;получаем число символов

CBW CX ;CX используем как счетчик

MOV AX,ES:[BX]+16 ;получаем адрес буфера данных

MOV DS,AX ;

MOV DX,ES:[BX]+14 ;

.

(здесь идут операции по выводу)

.

JMP QUIT

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

QUIT: OR ES:WORD PTR [BX]+3,100H ;устанавливаем бит 8

POP BP ;восстанавливаем регистры

POP DI ;

POP SI ;

POP DX ;

POP CX ;

POP BX ;

POP AX ;

POP DS ;

POP ES ;

RET

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

DEVICE12 ENDP

CSEG ENDS

END DEVICE12

Перед возвратом драйвер устанавливает слово статуса в заголов-

ке запроса. В данном примере это делается в двух местах, в зави-

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

нет. Эти строки выглядят так: OR ES:WORD PTR [BX]+3,XXXXH. Значе-

ние битов XXXX следующее:

биты 0-7 код ошибки (если бит 15 = 1)

бит 8 устанавливается в 1, когда функция завершена

бит 9 устанавливается в 1, когда драйвер занят

биты 10-14 зарезервированы MS DOS

бит 15 устанавливается при возникновении ошибки

Младший байт этого слова содержит следующие коды ошибок, если

установлен бит 15, индицирующий ошибку:

0 попытка записи на защищенное от записи устройство

1 неизвестное устройство

2 устройство не готово

3 неизвестная команда

4 ошибка проверки по контрольной сумме

5 неверная длина запроса к устройству

6 ошибка поиска

7 неизвестный носитель

8 сектор не найден

9 нет бумаги в принтере

A ошибка записи

B ошибка чтения

C общая ошибка

7.2.4 Доступ к драйверу устройства.

Драйвер устройства устанавливается путем включения имени гото-

вой программы в файл конфигурации системы. Для установки пробной

программы поместите в файл CONFIG.SYS строку DEVICE = DEVI-

CE12.COM. Затем перезагрузите систему для установки драйвера.

Если машина не будет загружаться, то скорее всего имеется ошибка

в коде инициализации драйвера.

После того как драйвер установлен, для доступа к нему пользуй-

тесь обычными функциями MS DOS прерывания 21H. Какие функции

можно использовать зависит от того, заменяет ли устройство стан-

дартное устройство DOS (как в приведенном примере) или оно добав-

ляется как совершенно новое устройство. Для замены стандартного

последовательного устройства, назовите драйвер AUX, после чего

функции 3 [7.1.7] и 4 [7.1.6] прерывания 21H будут осуществлять

соответственно ввод и вывод. Если устройство параллельное, то

назовите его PRN, после чего функция 5 [6.3.1] будет выводить

данные на принтер. Другой возможностью является использование

функции 3FH [5.4.4] для ввода и [5.4.3] для вывода. В этом случае

используйте номер файла 3 - для последовательного устройства и 4

- для параллельного. Напоминаем, что при использовании предопре-

деленных номеров файла нет необходимости открывать устройство.

Если устройство не заменяет одно из стандартных устройств MS

DOS (т.е. если оно не названо одним из резервных слов, таким как

PRN, AUX и т.д.), то Вы можете открыть устройство с помощью одной

из функций для открытия файла. Вы можете использовать как метод

доступа с помощью управляющего блока файла, так и метод дескрип-

тора файла, хотя последний предпочтительнее. Чтобы быть уверен-

ным, что Вы по ошибке не откроете дисковый файл, поместите номер

файла в BX, 0 - в AL, посде чего выполните функцию 44H прерывания

21H. Это функция IOCTL и если бит 7 значения, возвращаемого в DL

установлен, то драйвер устройства загружен.

IOCTL требует, чтобы в байте атрибутов драйвера была соот-

ветствующая установка битов и чтобы по крайней мере основы проце-

дуры обработки IOCTL имелись в процедуре обработчика прерывания

драйвера. Функция IOCTL имеет 8 подфункций, пронумерованных от 0

до 7, при этом соответствующий кодовый номер помещается в AL при

вызове функции:

0 Возвратить информацию об устройстве в DX

1 Установить информацию об устройстве, используя DL (DH=0)

2 Считать CX байтов от драйвера устройства через управля-

щий канал и поместить их начиная с DS:DX

3 Записать CX байтов в драйвер устройства через управляющий

канал, взяв их начиная с DS:DX

4 То же, что и 2, но использовать номер накопителя в BL,

где 0 = накопитель по умолчанию, 1 = A и т.д.

5 То же, что и 3, но использовать номер накопителя как в 5

6 Получить статус ввода

7 Получить статус вывода

В ответ возвращается различная информация, в зависимости от

того, какая функция вызвана. Для подфункций 0 и 1 значение битов

регистра DX следующее (при условии, что бит 7 = 1, что означает,

что доступ получен к устройству, а не к файлу):

0 1 = устройство консольного ввода

1 1 = устройство консольного вывода

2 1 = нулевое устройство

3 1 = устройство часы

4 резерв

5 1 = нет проверки на Ctrl-Z, 0 = есть проверка на Ctrl-Z

6 1 = не конец файла, 0 = конец файла

7 1 = устройство, 0 = дисковый файл

8-13 резерв

14 1 = если можно использовать подфункции 2 и 3, 0 = нельзя

15 резерв

Подфункции 2-5 позволяют программе и устройству обмениваться

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

управляющие сообщения отдельно от основного потока данных, что

существенно упрощает дело. При возврате AX будет содержать число

переданных байтов. Подфункции 6-7 позволяют программе проверить,

готово ли устройство для ввода или вывода. Для устройств в AL

возвращается FF, если устройство готово и 0, если нет. При ис-

пользовании с открытым файлом (бит 7 = 0) в AL возвращается FF до

тех пор, пока не будет доститгнут конец файла.

Отметим, что в Бейсике 3.0 добавлены операторы IOCTL и IOCTL$.

Они позволяют бейсиковской программе, соответственно, посылать и

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

предварительно открыто оператором OPEN. Выходная строка должна

быть заключена в кавычки, как в IOCTL #3,"...". Подобным образом,

A$ = IOCTL$(3) принимает информацию о статусе через IOCTL.

7.2.5 Обнаружение и анализ ошибок устройства.

Устройства могут ошибаться по одной из трех причин. Устройство

может быть физически повреждено или находиться не в том состоя-

нии. Может быть плохим программное обеспечение, управляющее уст-

ройством. И, наконец, программа может послать устройству недопус-

тимый запрос (например, попытка писать на накопитель, где нахо-

дится дискета защищенная от записи). MS DOS обнаруживает и анали-

зирует большинство таких ошибок и обеспечивает возможности для

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

Высокий уровень.

Интерпретатор Бейсика обнаруживает многие ошибки, включая

ошибки драйверов устройств. При обнаружении ошибки возвращается

код ошибки и если не предусмотрена программа восстановления при

ошибках, то программа останавливается. Однако можно установить

обработку ошибок, с тем чтобы когда происходит критическая ошибка

Бейсик автоматически переходил на процедуру восстановления при

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

определить в какой строке программы произошла ошибка. После того

как это сделано, программа может принять меры по устранению ошиб-

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

раммы. После того, как эта процедура завершена, программа может

продолжить выполнение с любого места, с которого Вы захотите (с

некоторыми ограничениями). Код для тщательного анализа ошибочных

ситуаций может существенно увеличить размер программы. Отметим,

что компилятора Бейсика даже минимальные проверки на ошибки пот-

ребуют дополнительно по не менее чем 4 байта на каждую строку

программы.

Чтобы разрешить обработку ошибок в Бейсике поместите в начале

программы строку ON ERROR GOSUB n, где n это номер строки прог-

раммы, в которой начинается процедура обработки ошибок. При воз-

никновении критической ошибки управление будет передано на эту

строку. В начале процедуры поместите ряд строк вида IF ERR = n

THEN номерстроки, где n - номер ошибки, взятый из приложения к

руководству по Бейсику, содержащему сообщения об ошибках. Номера

строк в этих операторах соответствуют началу кода, обрабатывающе-

го данную конкретную ошибку. Эти части могут быть в свою очередь

разбиты на куски рядом операторов IF ERL = n THEN номерстроки.

ERL возвращает номер строки, в которой произошла ошибка, позволяя

процедуре восстановления точно определить ошибочное место.

После того как процедура восстановления завершила свою работу

надо использовать оператор RESUME для возврата управления в ту

строку, где произошла ошибка. За этим оператором может следовать

номер, в этом случае управление будет передано на строку с ука-

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

RESUME для перехода в точку программы, которая находится за пре-

делами процедуры, в которой произошла ошибка. Если восстановление

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

ла свою работу, то напишите RESUME NEXT и управление будет пере-

дано на строку, следующую за той, в которой произошла ошибка. Вот

общая структура процедуры восстановления в Бейсике:

100 ON ERROR GOSUB 5000 'разрешаем обработку ошибок

.

.

5000 IF ERR = 61 THEN 5100 'диск полон

5010 IF ERR = 71 THEN 5200 'диск не готов

.

.

5100 IF ERL = 2080 THEN 5120 'где произошла ошибка?

5110 BEEP: PRINT "Disk in drive B: is full": RESUME

5120 BEEP: PRINT "Disk in drive A: is full": RESUME

.

5200 BEEP: PRINT "A disk drive is not ready"

5210 PRINT "Strike any key when corrected"

5220 IF INKEY$ = "" THEN 5220 'ожидаем нажатия клавиши

5230 RESUME ERL - 10 'пытаемся повторить операцию

В Бейсике 3.0 введены инструкции ERDEV и ERDEV$. Обе они поз-

воляют получить переменные только для чтения от прерывания 24H,

обрабатывающего критичекие ошибки. Z% = ERDEV возвращает в Z%

слово статуса, в котором старший байт содержит 13-15 биты атрибу-

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

24H. Z$ = ERDEV$ помещает в Z$ 8-байтное имя устройства для сим-

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

устройств.

Низкий уровень.

Иногда драйверы устройств содержат такие серьезные ошибки, что

программа просто не может продолжаться, пока они не будут исправ-

лены. Когда такие ошибки происходят, то система вызывает обработ-

чик критических ошибок. Он может вступать в действие как для

стандартных устройств, так и для установленных драйверов. Пользо-

ватель наиболее часто сталкивается с ним, когда пытается произ-

вести дисковую операцию с дисководом, у которого открыта дверца.

В этом случае появляется сообщение: "Not ready error reading

drive A - Abort, Retry, Ignore?"

Обработчик критических ошибок может быть переписан, чтобы он

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

ваемые драйверы. Вектор прерывания 24H указывает на стандартную

процедуру MS DOS, но Вы можете перенаправить вектор на свою про-

цедуру. При вызове этой процедуры старший бит AH содержит 0 если

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

BP:SI указывают на заголовок драйвера виновного устройства, кото-

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

со смещения AH в заголовке содержат имя устройства, а обработчик

критичеких ошибок помещает код ошибки длиной в слово в DI. Вот

кодовые номера (они не представляют битовых позиций):

Код Проблема

0 попытка писать на диск, защищенный от записи

1 неизвестное устройство

2 накопитель не готов

3 неизвестная команда

4 ошибка обмена данными

5 неверная длина запроса

6 ошибка поиска

7 неизвестный тип носителя

8 сектор не найден

9 нет бумаги в принтере

A ошибка при записи

B ошибка при чтении

C общая ошибка

В случае дисковой ошибки AL содержит номер накопителя, на котором

произошла ошибка (0 = A, 1 = B и т.д.), а биты 2-0 AH индицируют

тип ошибки. Бит 0 устанавливается, если ошибка произошла во время

операции записи, и сбрасывается - если при чтении. Биты 2-1 со-

держат информацию о том, в каком месте диска произошла ошибка,

давая 00 - для начальных секторов DOS, 01 - для FAT, 10 - для

каталога и 11 - для всего остального диска.

Имеется три способа, которыми программа может восстановиться

после критической ошибки:

1. Можно попросить пользователя устранить причину ошибки (напри-

мер, закрыть дверцу накопителя), после чего система предоставит

устройству возможность повторить операцию.

2. Управление может быть возвращено инструкции, следующей за INT

21H, которая сделала попытку обратиться к драйверу.

3. Программа может завершиться и вернуть управление системе.

Ваша процедура обработки ошибок может восстановить ситуацию,

выдав инструкцию IRET, после того, как она поместила 0 в AL,

чтобы игнорировать ошибку, 1 - чтобы повторить операцию и 2 -

чтобы завершить программу. Если Вы хотите, чтобы Ваша процедура

провела восстановление сама, то она должна восстановить регистры

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

кроме последних трех слов. После этого инструкция IRET возвратит

управление программе, хотя сама система останется в нестабильном

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

большим, чем 12. Вот конфигурация стека (начиная сверху до низа)

когда вызывается обработчик критических ошибок:

Адрес возврата обработчика ошибок: IP, CS, флаги

Пользовательские регистры задачи, AX, BX, CX, DX, SI, DI, BP,

из которой был вызван драйвер: DS, ES, IP, CS, флаги

MS DOS обрабатывает также многие некритические ошибки. Сюда

включаются коды ошибок, которые могут возвращаться в регистрах,

когда вызывалась функция DOS. Эти коды обсуждаются в данной книге

в тех местах, в которых описываются соответствующие функции.

Однако имейте ввиду, что начиная с версии 3.0 MS DOS возвращает

расширенные коды ошибок для функций, использующих FCB или деск-

рипторы файлов. Когда при выполнении одной из этих функций уста-

навливается флаг переноса, то в AX возвращается обычный код ошиб-

ки. Дополнительный расширенный код доступен через прерывание 59H,

если в BX поместить 0. Эта функция сообщает также о критических

ошибках и она может быть использована из обработчика критических

ошибок, вызываемого через прерывание 24H.

Функция помещает в AX код ошибки, взятый из обычного списка

знакомых кодов ошибок (например, "недостаточно памяти") или один

из новых кодов (например, "ограничение доступа" для многопользо-

вательской системы). BH возвращает код класса ошибки, указывая

какого типа ошибка произошла. Например, код 1 указывает, что

исчерпаны ресурсы, т.е. что память, файловые буфера или что-то

еще израсходовано. Другие классы могут указывать на программные

ошибки, проблемы с носителями, форматированием и т.д. BL содержит

код, предполагающий действие для восстановления, такое как "пов-

торить", "прекратить" или "запросить у пользователя". Наконец, CH

возвращает число, определяющее место где возникли проблемы: на

блочном устройстве, на символьном, в памяти?

Данные для этих кодов ошибок весьма обширны. Полную информацию

о них см. в Техническом руководстве по MS DOS 3.0. Поскольку

предполагается, что MS DOS 3.0 не будет использоваться на маши-

нах, более ранних, чем AT, то использование этих кодов ограничи-

вает совместимость Ваших программ. Тем не менее, набор процедур,

предназначенный только для MS DOS 3.0 может дополняться поверх

обычных процедур обработки ошибок. В [1.1.3] показано как прог-

рамма может определить версию MS DOS, в которой она работает.

Наконец, имейте ввиду, что процесс может передавать код завер-

шения вызвавшему его процессу. Термин процесс относится к взаимо-

действующим программам. Например, когда одна программа загружает

и запускает другую с помощью функции EXEC, то запускаемая прог-

рамма называется потомком, а запускающая программа - родителем.

Родителю может потребоваться информация о том, как завершился

потомок. Чтобы использовать эту возможность, поместите желаемый

код завершения в AL и выполните функцию 4CH прерывания 21H для

завершения программы. Когда управление будет возвращено родителю,

то он выполнит функцию 4DH прерывания 21H (без входных регистров)

и в AL будет получен код завершения, который может затем быть

проанализирован. Кроме того, AH будет содержать информацию о том,

как завершился потомок: 0 - для нормального завершения, 1 - по

Ctrl-Break, 2 - по критической ошибке устройства и 3 - с помощью

функции 31H, оставляющей задачу резидентной.

Если программа завершилась с помощью этой функции (а не 20H -

см. обсуждение в [1.3.4]), то MS DOS получает код выхода и он

может быть включен в обработку командным файлом с помощью подко-

манды IF. Эта подкоманда позволяет условное исключение других

команд из командного файла. Код выхода рассматривается как номер

ERRORLEVEL и условные операции выполняются в зависимости от того,

больше он или нет определенного числа. С помощью этой возможности

командные файлы могут прекращать обработку и выводить сообющение

о возникновении ошибки в одной из запущенных программ. Более

подробная информация приведена в разделе "Команды пакетной обра-

ботки" руководства по операционной системе.