Операционная система MSDOS


Выполняется ввод символа со стандартного



Таблица 1

Код НазначениеОписание
01hВвод с клавиатуры Выполняется ввод символа со стандартного ввода и эхо-вывод символа на стандартное устройство вывода. Выполняется проверка на нажатие комбинации клавиш CTRL/C и CTRL-BREAK
06hВвод с клавиатуры Ввод символа со стандартного ввода без ожидания и вывод его на устройство стандартного вывода. Комбинации CTRL/C и CTRL-BREAK не проверяются.
07hПрямой ввод Ввод символа со стандартного с клавиатуры устройства ввода. Комбинации клавиш CTRL/C и CTRL-BREAK не проверяются.
08hВвод с клавиатуры Аналогично функции 07h, но проверяются комбинации клавиш CTRL/C и CTRL-BREAK.
02hОтобразить символ Отображаемый символ посылается на стандартное устройство вывода.
09hОтобразить строку На стандартное устройство вывода символов посылается строка, закрытая символом '$'.
03hВвод из последовательного порта Вводится символ из последовательного порта
04hВывод в последовательный порт Выводится символ на последовательный порт
05hВывод на принтер Выводится символ на принтер.
Из таблицы видно, что для ввода с клавиатуры можно использовать несколько функций. Ввод без эхо-вывода удобен для такой информации, как пароли. Если логика работы программы не допускает прерывания по нажатию комбинаций клавиш CTRL-C или CTRL-BREAK, нужно использовать функции, которые не проверяют эти комбинации.


Для вывода строки символов можно использовать функцию 09h, но выводимая строка не может содержать символ '$', так как этот символ используется в качестве признака конца строки.


Таблица 1

(-2) 2mcb_seg сегмент первого управляющего блока памяти (MCB)
(0) 4 dev_cb указатель на первый блок управления устройствами DOS (DOS Device Control Block)
(+4) 4 file_tab указатель на таблицу файлов DOS
(+8) 4clock_dr указатель на драйвер CLOCK$, установленный или резидентный
(+12) 4con_dr указатель на актуальный драйвер CON, установленный или резидентный
------------------------- DOS 2.x -------------------------
(+16) 1num_lgdr число логических драйверов в системе
(+17) 2max_btbl максимальное число байт/блоков любого блочного устройства
(+19) 4disk_buf указатель на первый дисковый буфер
(+23) null_dr начало драйвера NUL - первого драйвера в списке драйверов DOS
--------------- DOS 3.x, 4.x----------------
(+16) 2 max_btbl максимальное число байт в блоке блочного устройства
(+18) 4disk_buf указатель на первый дисковый буфер
(+22) 4drv_info указатель на массив информации об устройствах
(+26) 4fcb_tabl указатель на таблицу FCB
(+30) 2fcb_size размер таблицы FCB
(+32) 1num_bdev число блочных устройств
(+33) 1lastdriv значение LASTDRIVE в файле CONFIG.SYS (по умолчанию равно 5)
(+34) null_dr начало драйвера NUL - первого драйвера в списке драйверов DOS
Функция get_cvt возвращает адрес поля dev_cb. Для удобства работы с векторной таблицей связи определим тип CVT: #pragma pack(1) typedef struct _CVT_ { unsigned mcb_seg; void far *dev_cb; void far *file_tab; void far *clock_dr; void far *con_dr; unsigned max_btbl; void far *disk_buf; void far *drv_info; void far *fcb_tabl; unsigned fcb_size; unsigned char num_bdev; unsigned char lastdriv; } CVT; #pragma pack()
Эта структура содержит описание полей векторной таблицы связи для MS-DOS версий 3.х, 4.х и 5.0.
Директива #pragma pack(1) предназначена для выравнивания полей структуры на границу байта. Эта директива необходима потому, что по умолчанию транслятор Microsoft выравнивает поля в структуре на границу 16-ти битового слова. Неправильное выравнивание может привести к тому, что поля структуры не будут располагаться в памяти последовательно.
Заметьте, что функция get_cvt возвращает указатель на поле dev_cb. Модифицируем эту функцию так, чтобы можно было использовать для обращения к полям векторной таблицы связи структуру _CVT_: /** *.Name get_mcvt * *.Title Получить адрес векторной таблицы связи * *.Descr Функция возвращает адрес векторной таблицы связи * для DOS версий 2.х, 3.х, 4.00, 4.01 * *.Params Нет * *.Return Указатель на векторную таблицу связи **/ #include <dos.h> #include <stdio.h> #include "sysp.h" CVT far *get_mcvt(void) { union REGS inregs, outregs; struct SREGS segregs; inregs.h.ah = 0x52; intdosx( &inregs, &outregs, &segregs ); return((CVT far*)FP_MAKE(segregs.es,outregs.x.bx-2)); }
Ниже будут подробно описаны отдельные поля векторной таблицы связи и приведены примеры использования информации из этой таблицы.



Таблица 1

(0) 2 signature два байта 'MZ' (4Dh, 5Ah), индентифицирующие файл в формате EXE
(+2) 2part_pag длина последней страницы программы в байтах (страница содержит 512 байт)
(+4) 2file_size размер программы в страницах по 512 байт
(+6) 2rel_item число элементов в таблице расположения сегментов
(+8) 2hdr_size размер заголовка файла в параграфах (длина параграфа - 16 байт)
(+10) 2min_mem минимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы
(+12) 2max_mem максимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы
(+14) 2ss_reg величина смещения от начала программы, которая используется для загрузки сегментного регистра стека SS
(+16) 2sp_reg величина смещения от начала программы, которая используется для загрузки регистра SP
(+18) 2chk_summ контрольная сумма всех слов в файле
(+20) 2ip_reg значение для регистра IP, которое будет использовано при начальном запуске программы
(+22) 2cs_reg смещение от начала программы для установки сегментного регистра кода CS
(+24) 2relt_off смещение от начала файла таблицы расположения сегментов программы
(+26) 2overlay номер оверлея, равен 0 для основного модуля
Таблица расположения сегментов программы начинается сразу после форматированной области и состоит из четырехбайтовых значений в формате "смещение:сегмент".
Область файла после таблицы расположения сегментов выравнивается на границу параграфа с помощью байта-заполнителя, и дальше начинается сама программа.
В файле sysp.h есть описание заголовка файла и таблицы расположения сегментов, которые вы можете использовать при обработке заголовка EXE-файла: typedef struct _EXE_HDR_ { unsigned signature; unsigned part_pag; unsigned file_size; unsigned rel_item; unsigned hdr_size; unsigned min_mem; unsigned max_mem; unsigned ss_reg; unsigned sp_reg; unsigned chk_summ; unsigned ip_reg; unsigned cs_reg; unsigned relt_off; unsigned overlay; } EXE_HDR; typedef struct _RELOC_TAB_ { unsigned offset; unsigned segment; } RELOC_TAB;
В качестве примера приведем программу, которая считывает форматированную часть заголовка EXE-файла, проверяет наличие в его первых двух байтах признака EXE-формата ('MZ'). Если признак имеется, программа выводит на экран расшифрованное содержимое заголовка и таблицу перемещений, если такая таблица присутствует. В качестве параметра программе надо при запуске передать имя исследуемого EXE-файла. #include <stdio.h> #include <stdlib.h> #include "sysp.h" void main(int, char *[]); void main(int argc, char *argv[]) { printf("Распечатка заголовка EXE-файла\n" "Copyright (C)Frolov A., 1990\n\n"); if( argc != 2 ) { printf( " Задайте путь EXE-файла в качестве" "параметра\n" ); exit(0); } if( gethdr( argv[1]) != 0) { printf( "Ошибка в формате файла или нет такого" "файла\n" ); exit(0); } exit(0); } int gethdr( char *path) { EXE_HDR header; RELOC_TAB *reloc; FILE *inpfile; int i; if((inpfile = fopen(path,"rb")) == 0) return(-1); if(get_exeh(&header,&reloc,inpfile) != 0) { fclose(inpfile); return(-1); } printf("Магическое число: %04X\n" "Длина последней страницы файла: %d\n" "Количество страниц в файле: %d\n" "Кол. элементов табл. перемещений: %d\n" "Размер заголовка в параграфах: %d\n" "Минимальная память для программы: %04X\n" "Максимальная память для программы: %04X\n" "Значение адреса стека SS:SP: 04X:%04X\n" "Контрольная сумма: %04X\n" "Значения для регистров CS:IP: %04X:%04X\n" "Смещение табл. перемещений: %02X\n" "Номер оверлея: %d\n", header.signature, header.part_pag, header.file_size, header.rel_item, header.hdr_size, header.min_mem, header.max_mem, header.ss_reg, header.sp_reg, header.chk_summ, header.cs_reg, header.ip_reg, header.relt_off, header.overlay); if(reloc != 0) { printf("\nСодержимое таблицы перемещений:\n\n"); for(i=0;i < header.rel_item; i++) { printf("%04X:%04X\n", (reloc+i)->segment, (reloc+i)->offset); } free(reloc); } fclose(inpfile); return(0); }
Приведенная выше программа для чтения заголовка EXE-файла пользуется функцией get-exeh: /** *.Name get_exeh * *.Title Прочитать заголовок EXE-файла * *.Descr Функция читает заголовок EXE-файла в * структуру типа EXE_HDR, заказывает память * для таблицы размещений сегментов и считывает * таблицу в эту область. Адрес заказанной области * помещается по адресу, передаваемому в rtb. * Если таблица размещений отсутствует, память для * нее не заказывается. * *.Params int get_exeh(EXE_HDR *exeh,RELOC_TAB **rtb, * FILE *exe_file) * * exeh - указатель на структуру, которая * должна быть заполнена информацией * из заголовка EXE-файла * * rtb - указатель на указатель на таблицу * размещений сегментов программы * * exe_file - указатель на открытый EXE-файл * (до вызова функции нельзя обращаться * к этому файлу, т.к. считается, что * указатель текущего смещения * установлен на начало файла) * *.Return 0 при успешном считывании заголовка; * -1 в случае неправильного формата заголовка **/ #include <stdlib.h> #include <stdio.h> #include "sysp.h" int get_exeh(EXE_HDR *exeh,RELOC_TAB **rtb,FILE *exe_file) { int i,j,k; // считываем форматированную часть заголовка for(i=0; i < sizeof(EXE_HDR); i++) { *(((char*)exeh) + i) = fgetc(exe_file); if(feof(exe_file)) break; } // это EXE-файл? if(exeh->signature != 0x5a4d) return(-1); if((i=exeh->rel_item) != 0) { // если есть таблица перемещений, заказываем для нее память *rtb = (RELOC_TAB*)malloc(i*sizeof(RELOC_TAB)+16); // считываем таблицу перемещений for(k=0; k<i; k++) { for(j=0;j < sizeof(RELOC_TAB);j++) { *((char*)(*rtb)+j+k*sizeof(RELOC_TAB))= fgetc(exe_file); if(feof(exe_file)) break; } } } else *rtb = (RELOC_TAB *)0; return(0);}



Таблица 1

Номер Описание
0 Ошибка деления. Вызывается автоматически после выполнения команд DIV или IDIV, если в результате деления происходит переполнение (например, при делении на 0). DOS обычно при обработке этого прерывания выводит сообщение об ошибке и останавливает выполнение программы. Для процессора 8086 при этом адрес возврата указывает на следующую после команды деления команду, а в процессоре 80286 - на первый байт команды, вызвавшей прерывание.
1Прерывание пошагового режима. Вырабатывается после выполнения каждой машинной команды, если в слове флагов установлен бит пошаговой трассировки TF. Используется для отладки программ. Это прерывание не вырабатывается после выполнения команды MOV в сегментные регистры или после загрузки сегментных регистров командой POP.
2Аппаратное немаскируемое прерывание. Это прерывание может использоваться по-разному в разных машинах. Обычно вырабатывается при ошибке четности в оперативной памяти и при запросе прерывания от сопроцессора.
3Прерывание для трассировки. Это прерывание генерируется при выполнении однобайтовой машинной команды с кодом CCh и обычно используется отладчиками для установки точки прерывания.
4Переполнение. Генерируется машинной командой INTO, если установлен флаг OF. Если флаг не установлен, то команда INTO выполняется как NOP. Это прерывание используется для обработки ошибок при выполнении арифметических операций.
5Печать копии экрана. Генерируется при нажатии на клавиатуре клавиши PrtScr. Обычно используется для печати образа экрана. Для процессора 80286 генерируется при выполнении машинной команды BOUND, если проверяемое значение вышло за пределы заданного диапазона.
6Неопределенный код операции или длина команды больше 10 байт (для процессора 80286).
7Особый случай отсутствия математического сопроцессора (процессор 80286).
8IRQ0 - прерывание интервального таймера, возникает 18,2 раза в секунду.
9IRQ1 - прерывание от клавиатуры. Генерируется при нажатии и при отжатии клавиши. Используется для чтения данных от клавиатуры.
AIRQ2 - используется для каскадирования аппаратных прерываний в машинах класса AT.
BIRQ3 - прерывание асинхронного порта COM2.
CIRQ4 - прерывание асинхронного порта COM1.
DIRQ5 - прерывание от контроллера жесткого диска для XT.
EIRQ6 - прерывание генерируется контроллером флоппи-диска после завершения операции.
FIRQ7 - прерывание принтера. Генерируется принтером, когда он готов к выполнению очередной операции. Многие адаптеры принтера не используют это прерывание.
10Обслуживание видеоадаптера.
11Определение конфигурации устройств в системе.
12Определение размера оперативной памяти в системе.
13Обслуживание дисковой системы.
14Последовательный ввод/вывод.
15Расширенный сервис для AT-компьютеров.
16Обслуживание клавиатуры.
17Обслуживание принтера.
18Запуск BASIC в ПЗУ, если он есть.
19Загрузка операционной системы.
1AОбслуживание часов.
1BОбработчик прерывания Ctrl-Break.
1CПрерывание возникает 18.2 раза в секунду, вызывается программно обработчиком прерывания таймера.
1DАдрес видеотаблицы для контроллера видеоадаптера 6845.
1EУказатель на таблицу параметров дискеты.
1FУказатель на графическую таблицу для символов с кодами ASCII 128-255.
20-5FИспользуется DOS или зарезервировано для DOS.
60-67Прерывания, зарезервированные для пользователя.
68-6FНе используются.
70IRQ8 - прерывание от часов реального времени.
71IRQ9 - прерывание от контроллера EGA.
72IRQ10 - зарезервировано.
73IRQ11 - зарезервировано.
74IRQ12 - зарезервировано.
75IRQ13 - прерывание от математического сопроцессора.
76IRQ14 - прерывание от контроллера жесткого диска.
77IRQ15 - зарезервировано.
78 - 7FНе используются.
80-85Зарезервированы для BASIC.
86-F0Используются интерпретатором BASIC.
F1-FFНе используются.
IRQ0 - IRQ15 - это аппаратные прерывания, о них будет рассказано позже.



Таблица 1

(0) 4next указатель на заголовок следующего драйвера. Если смещение адреса следующего драйвера равно FFFF, это последний драйвер в цепочке
(+4) 2attrib атрибуты драйвера
(+6) 2strateg смещение программы стратегии драйвера
(+8) 2interrupt смещение программы обработки прерывания для драйвера
(+10) 8dev_name имя устройства для символьных устройств или количество обслуживаемых устройств для блочных устройств.
Как уже было сказано, все драйверы связаны в цепочку. Самый первый драйвер находится сразу за векторной таблицей связи. Поле next заголовка драйвера указывает на следующий драйвер (на его заголовок). Это поле имеет формат DWORD-указателя и состоит из компоненты адреса сегмента и смещения. Признаком того, что данный драйвер последний в цепочке, служит значение 0FFFFh в компоненте смещения поля next.
Программист, когда он составляет программу-драйвер, заносит в это поле либо 0FFFFh:0FFFFh, если исходный текст содержит только один драйвер, либо адрес следующего драйвера (в виде дальней ссылки на метку заголовка следующего драйвера). Если исходный текст содержит несколько драйверов, то в заголовке последнего в поле next должно находиться значение 0FFFFh:0FFFFh.
При загрузке драйверов в память операционная система изменит содержимое поля next в заголовках драйверов для того, чтобы это поле указывало на заголовок следующего драйвера в цепочке. (Изменит в памяти, а не в файле драйвера!)
Обычно исходный текст программы содержит один драйвер, и поле next задается следующим образом: next DD 0FFFFFFFFh
Следующее поле в заголовке драйвера - поле атрибутов драйвера atrib.
Это поле описывает устройство, обслуживаемое данным драйвером. Каждый бит слова отвечает за ту или иную особенность устройства. Прежде чем мы детально рассмотрим назначение всех битов этого слова, заметим, что бит 15 (самый старший бит) указывает, является ли это устройство символьным или блочным.
Следует специально отметить, что все драйверы можно разделить на две группы - драйверы символьных устройств и драйверы блочных устройств. Первые обслуживают устройства посимвольного ввода/вывода, такие как принтеры, клавиатура, контроллеры последовательной передачи данных RS232C и т.д., вторые ориентированы на ввод/вывод блоками - это диски.
Как правило, все нестандартные устройства, начиная от цифрового вольтметра до роботов и систем автоматизации производственных процессов, обслуживаются символьными драйверами. Хотя этот тип драйверов ориентирован на передачу данных посимвольно, для быстродействующих устройств ввода/вывода можно организовать буферизацию (средствами операционной системы).
Блочные драйверы могут Вам потребоваться в основном для обслуживания своих нестандартных дисковых устройств. Например, можно использовать более плотную запись информации на дискетах для повышения их емкости, можно через аппаратуру связи персонального компьютера и ЭВМ серии ЕС создавать псевдо-винчестеры на дисках ЕС (такие "винчестеры" будут восприниматься DOS как обычные стандартные диски). С помощью блочных драйверов можно организовать защиту информации на дисках от несанкционированного доступа, если Ваш драйвер будет шифровать записываемую на диск информацию и предоставлять ее по предъявлению пароля.
Мы рассмотрим оба типа драйверов, каждый раз будем отмечать особенности символьных и блочных устройств.
Приведем назначение отдельных битов слова атрибутов символьного драйвера:



Таблица 1

(0) 4next указатель на заголовок следующего драйвера. Если смещение адреса следующего драйвера равно FFFF, это последний драйвер в цепочке
(+4) 2attrib атрибуты драйвера
(+6) 2strateg смещение программы стратегии драйвера
(+8) 2interrupt смещение программы обработки прерывания для драйвера
(+10) 8dev_name имя устройства для символьных устройств или количество обслуживаемых устройств для блочных устройств.
Как уже было сказано, все драйверы связаны в цепочку. Самый первый драйвер находится сразу за векторной таблицей связи. Поле next заголовка драйвера указывает на следующий драйвер (на его заголовок). Это поле имеет формат DWORD-указателя и состоит из компоненты адреса сегмента и смещения. Признаком того, что данный драйвер последний в цепочке, служит значение 0FFFFh в компоненте смещения поля next.
Программист, когда он составляет программу-драйвер, заносит в это поле либо 0FFFFh:0FFFFh, если исходный текст содержит только один драйвер, либо адрес следующего драйвера (в виде дальней ссылки на метку заголовка следующего драйвера). Если исходный текст содержит несколько драйверов, то в заголовке последнего в поле next должно находиться значение 0FFFFh:0FFFFh.
При загрузке драйверов в память операционная система изменит содержимое поля next в заголовках драйверов для того, чтобы это поле указывало на заголовок следующего драйвера в цепочке. (Изменит в памяти, а не в файле драйвера!)
Обычно исходный текст программы содержит один драйвер, и поле next задается следующим образом: next DD 0FFFFFFFFh
Следующее поле в заголовке драйвера - поле атрибутов драйвера atrib.
Это поле описывает устройство, обслуживаемое данным драйвером. Каждый бит слова отвечает за ту или иную особенность устройства. Прежде чем мы детально рассмотрим назначение всех битов этого слова, заметим, что бит 15 (самый старший бит) указывает, является ли это устройство символьным или блочным.
Следует специально отметить, что все драйверы можно разделить на две группы - драйверы символьных устройств и драйверы блочных устройств. Первые обслуживают устройства посимвольного ввода/вывода, такие как принтеры, клавиатура, контроллеры последовательной передачи данных RS232C и т.д., вторые ориентированы на ввод/вывод блоками - это диски.
Как правило, все нестандартные устройства, начиная от цифрового вольтметра до роботов и систем автоматизации производственных процессов, обслуживаются символьными драйверами. Хотя этот тип драйверов ориентирован на передачу данных посимвольно, для быстродействующих устройств ввода/вывода можно организовать буферизацию (средствами операционной системы).
Блочные драйверы могут Вам потребоваться в основном для обслуживания своих нестандартных дисковых устройств. Например, можно использовать более плотную запись информации на дискетах для повышения их емкости, можно через аппаратуру связи персонального компьютера и ЭВМ серии ЕС создавать псевдо-винчестеры на дисках ЕС (такие "винчестеры" будут восприниматься DOS как обычные стандартные диски). С помощью блочных драйверов можно организовать защиту информации на дисках от несанкционированного доступа, если Ваш драйвер будет шифровать записываемую на диск информацию и предоставлять ее по предъявлению пароля.
Мы рассмотрим оба типа драйверов, каждый раз будем отмечать особенности символьных и блочных устройств.
Приведем назначение отдельных битов слова атрибутов символьного драйвера:

Содержание раздела