| Операционная система MS-DOS© Александр Фролов, Григорий ФроловТом 1, книга 3, М.: Диалог-МИФИ, 1992.
 
 5. Обработка критических
ошибок Операционная система MS-DOS позволяет программам
устанавливать собственный обработчик
критических ошибок аппаратуры. Мы уже говорили о
том, что вектор 0000:0090, соответствующий
прерыванию INT 24h, содержит адрес
обработчика критических ошибок. Этот обработчик
получает управление от операционной системы,
когда драйвер какого-либо устройства
обнаруживает ошибку аппаратуры.  Обратите внимание на то, что обработчик
критических ошибок не вызывается при работе с
диском через прерывания MS-DOS INT 25h/26h, и, тем
более, при работе с диском на уровне прерывания INT 13h
BIOSBIOS.  При запуске программы MS-DOS копирует адрес
обработчика в префикс сегмента программы PSP,
а после завершения работы программы -
восстанавливает его из PSP.  Стандартный обработчик MS-DOS выводит на экран
сообщение:  
Abort, Retry, Ignore, Fail?
 Если ваша программа должна сама обрабатывать
ошибки аппаратуры, она может установить свой
собственный обработчик критических ошибок.  Когда обработчик получает управление, регистры
процессора содержат информацию, необходимую для
определения причины и места появления ошибки:
 
  
    | AH | информация об ошибке: Биты:
 0 - тип операции:
 0 - чтение, 1 - запись
 1...2 область диска, где произошла ошибка:
 00 - системные файлы;
 01 - область FAT
 10 - область каталога;
 11 - область данных
 3 - 1 - возможен выход с кодом FAIL
 4 - 1 - возможен выход с кодом RETRY
 5 - 1 - возможен выход с кодом IGNORE
 6 зарезервирован, равен 0
 7 тип устройства: 0 - диск; 1 - символьное
    устройство
 |  
    | AL | номер диска (если бит 7 регистра AH
    равен 0) |  
    | DI | код ошибки (биты 0...7, остальные биты не
    определены) |  
    | BP:SI | адрес заголовка драйвера устройства, на
    котором произошла ошибка |  Биты 3, 4, 5 определены только для DOS версии 3.0
и для более поздних версий.  Обработчик критических ошибок не должен
пользоваться функциями MS-DOS с кодами, большими
чем 0Ch (из-за того, что функции MS-DOS не
реентерабельны).  Программа может вывести на экран сообщение об
ошибке и запросить оператора о необходимых
действиях. Ей разрешено также получить
дополнительную уточняющую информацию об ошибке
с помощью функции 59h прерывания INT 21h
или узнать версию DOS с помощью функции 30h
этого же прерывания. Дополнительная информация
об устройстве, в котором произошла ошибка, может
быть получена с использованием адреса заголовка
драйвера устройства, который передается
операционной системой при вызове обработчика в
регистрах BP:SI.  Для определения номера функции DOS, в которой
произошла критическая ошибка,
программа-обработчик может выполнить анализ
стека. Когда обработчик получает управление,
стек имеет следующую структуру:  
Адрес возврата в DOS для команды IRET
IP
CS
FLAGS
Содержимое регистров программы перед вызовом INT_21h
AX, BX, CX, DX, SI, DI, BP, DS, ES
Адрес возврата в программу, вызвавшую функцию DOS
IP
CS
FLAGS
 Выполнив анализ регистра AH, можно
определить номер функции DOS, при вызове которой
произошла ошибка, а зная содержимое остальных
регистров - и все параметры этой функции.  После выполнения всех необходимых действий,
программа обработки критических ошибок должна
возвратить в регистре AL код действия, которое
должна выполнить операционная система для
обработки данной ошибки:
 
  
    | 0 | игнорировать ошибку; |  
    | 1 | повторить операцию; |  
    | 2 | аварийно закончить задачу, используя
    адрес завершения, записанный в векторе
    прерывания INT 23h; |  
    | 3 | вернуть программе управление с
    соответствующим кодом ошибки (этот код можно
    задавать только для DOS версии 3.0 и для более
    поздних версий). |  Если вы пользуетесь операционной системой MS-DOS
версии 4.0, то при открытии файлов с помощью
функции 6Ch программа может заблокировать
вызов обработчика критических ошибок.  Для составления программы обработки
критических ошибок вы можете воспользоваться
языком ассемблера или функциями стандартных
библиотек трансляторов Microsoft QC 2.5 и C 6.0 _dos_getvect(),
_dos_setvect(), _chain_intr(). Однако лучше всего
использовать специально предназначенные для
этого (и входящие в состав стандартных библиотек
указанных трансляторов) функции _harderr(), _hardresume()
и _hardretn().  Функция _harderr() предназначена для установки
нового обработчика критических ошибок, она имеет
следующий прототип:  
void _harderr(void (_far *handler)());
 Параметр handler - указатель на новую функцию
обработки критических ошибок.  Функции _hardresume() и _hardretn() должны быть
использованы в обработчике критичеких ошибок,
установленном функцией _harderr().  Функция _hardresume() возвращает управление
операционной системе, она имеет прототип:  
_hardresume(int result);
 Парметр result может иметь следующие значения (в
соответствии с необходимыми действиями):
 
  
    | _HARDERR_ABORT | аварийно завершить программу; |  
    | _HARDERR_FAIL | вернуть код ошибки; |  
    | _HARDERR_IGNORE | игнорировать ошибку; |  
    | _HARDERR_RETRY | повторить операцию. |  Эти параметры описаны в файле dos.h.  Функция _hardretn() возвращает управление
непосредственно программе, передавая ей код
ошибки, определяемый параметром функции error:  
void _hardretn(int error);
 При этом программа получает после возврата из
вызванной ей функции DOS код ошибки error. Если
ошибка произошла при выполнении функции с
номером, большим чем 38h, дополнительно
устанавливается в 1 флаг переноса. Если номер
функции был меньше указанного значения, в
регистр AL записывается величина FFh.  Функция обработки критических ошибок handler
имеет следующие параметры:  
void _far handler(unsigned deverror, unsigned errcode,
                           unsigned _far *devhdr);
Первый параметр - код ошибки устройства. Он
равен содержимому регистра AX при вызове
обработчика прерывания INT 24h. Аналогично,
параметр errcode соответствует содержимому
регистра DI - код ошибки. Третий параметр - devhdr
- это указатель на заголовок драйвера устройства
(передаваемый в регистрах BP:SI).  Для демонстрации использования функций
установки обработчика критических ошибок
приведем программу, которая пытается создать
каталог на диске А:. Эта программа сама
обрабатывает критические ошибки, запрашивая у
оператора информацию о необходимых действиях.  
// Эту программу можно запускать только из командной
// строки. При запуске из интегрированной среды QC
// или PWB возможен конфликт с используемым в этих
// средах обработчиком критических ошибок.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <direct.h>
#include <string.h>
#include <dos.h>
#include <bios.h>
void main(void);
void _far hhandler(unsigned deverr,
                                 unsigned doserr, unsigned _far *hdr);
void _bios_str(char *p);
void main() {
// Устанавливаем обработчик критических ошибок
         _harderr(hhandler);
// Моделируем критическую ошибку. Выполняем попытку создать
// каталог на диске А:. Если мы "забудем" вставить
// в дисковод дискету, будет вызван обработчик
// критической ошибки
         printf("\nВставьте (или не вставляйте) дискету в дисковод A:"
                   "\nи нажмите любую клавишу..."
                   "\n");
         getch();
// Создаем каталог
         if(mkdir("a:\test_ctl")) {
                  printf("\nОшибка при создании каталога" );
                  exit(-1);
         }
         else {
                  printf("\nУспешное создание каталога");
// Удаляем только что созданный каталог
                  rmdir("a:test_ctl");
                  exit(0);
         }
}
// Новый обработчик критических ошибок
void _far hhandler(unsigned deverr,
                                 unsigned doserr, unsigned _far *hdr) {
         int ch;
         static char buf[200], tmpbuf[10];
// Выводим сообщение о критической ошибке
         sprintf(buf,"\n\r"
                                 "\n\rКод ошибки устройтсва: %04.4X"
                                 "\n\rКод ошибки DOS:        %d"
                                 "\n\r\n\r"
                                 "\n\rВыполняемые действия:"
                                 "\n\r  0 - повторить"
                                 "\n\r  1 - отменить"
                                 "\n\r  2 - завершить"
                                 "\n\r----> ?",
                                 deverr,
                                 doserr);
         _bios_str(buf);
// Вводим ответ с клавиатуры
         ch = _bios_keybrd(_KEYBRD_READ) & 0x00ff;
         _bios_str("\n\r");
         switch(ch) {
                  case '0':       // Пытаемся повторить операцию
                  default:
                                _hardresume(_HARDERR_RETRY);
                  case '2':       // Завершаем работу программы
                                _hardresume(_HARDERR_ABORT);
                  case '1':       // Возврат в DOS с кодом ошибки
                                _hardretn(doserr);
         }
}
// Программа для вывода строки символов на экран
// с помощью функции BIOS 0Eh
void _bios_str(char *ptr) {
         union REGS inregs, outregs;
         char *start = ptr;
         inregs.h.ah = 0x0e;
         for(; *ptr; ptr++) {
                  inregs.h.al = *ptr;
                  int86(0x10, &inregs, &outregs);
         }
}
 |