Выполняется ввод символа со стандартного
Таблица 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 | Вывод на принтер | Выводится символ на принтер. |
Для вывода строки символов можно использовать функцию 09h, но выводимая строка не может содержать символ '$', так как этот символ используется в качестве признака конца строки.
Таблица 1
(-2) 2 | mcb_seg | сегмент первого управляющего блока памяти (MCB) |
(0) 4 | dev_cb | указатель на первый блок управления устройствами DOS (DOS Device Control Block) |
(+4) 4 | file_tab | указатель на таблицу файлов DOS |
(+8) 4 | clock_dr | указатель на драйвер CLOCK$, установленный или резидентный |
(+12) 4 | con_dr | указатель на актуальный драйвер CON, установленный или резидентный |
------------------------- DOS 2.x ------------------------- | ||
(+16) 1 | num_lgdr | число логических драйверов в системе |
(+17) 2 | max_btbl | максимальное число байт/блоков любого блочного устройства |
(+19) 4 | disk_buf | указатель на первый дисковый буфер |
(+23) | null_dr | начало драйвера NUL - первого драйвера в списке драйверов DOS |
--------------- DOS 3.x, 4.x---------------- | ||
(+16) 2 | max_btbl | максимальное число байт в блоке блочного устройства |
(+18) 4 | disk_buf | указатель на первый дисковый буфер |
(+22) 4 | drv_info | указатель на массив информации об устройствах |
(+26) 4 | fcb_tabl | указатель на таблицу FCB |
(+30) 2 | fcb_size | размер таблицы FCB |
(+32) 1 | num_bdev | число блочных устройств |
(+33) 1 | lastdriv | значение LASTDRIVE в файле CONFIG.SYS (по умолчанию равно 5) |
(+34) | null_dr | начало драйвера NUL - первого драйвера в списке драйверов DOS |
Эта структура содержит описание полей векторной таблицы связи для 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) 2 | part_pag | длина последней страницы программы в байтах (страница содержит 512 байт) |
(+4) 2 | file_size | размер программы в страницах по 512 байт |
(+6) 2 | rel_item | число элементов в таблице расположения сегментов |
(+8) 2 | hdr_size | размер заголовка файла в параграфах (длина параграфа - 16 байт) |
(+10) 2 | min_mem | минимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы |
(+12) 2 | max_mem | максимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы |
(+14) 2 | ss_reg | величина смещения от начала программы, которая используется для загрузки сегментного регистра стека SS |
(+16) 2 | sp_reg | величина смещения от начала программы, которая используется для загрузки регистра SP |
(+18) 2 | chk_summ | контрольная сумма всех слов в файле |
(+20) 2 | ip_reg | значение для регистра IP, которое будет использовано при начальном запуске программы |
(+22) 2 | cs_reg | смещение от начала программы для установки сегментного регистра кода CS |
(+24) 2 | relt_off | смещение от начала файла таблицы расположения сегментов программы |
(+26) 2 | overlay | номер оверлея, равен 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). |
8 | IRQ0 - прерывание интервального таймера, возникает 18,2 раза в секунду. |
9 | IRQ1 - прерывание от клавиатуры. Генерируется при нажатии и при отжатии клавиши. Используется для чтения данных от клавиатуры. |
A | IRQ2 - используется для каскадирования аппаратных прерываний в машинах класса AT. |
B | IRQ3 - прерывание асинхронного порта COM2. |
C | IRQ4 - прерывание асинхронного порта COM1. |
D | IRQ5 - прерывание от контроллера жесткого диска для XT. |
E | IRQ6 - прерывание генерируется контроллером флоппи-диска после завершения операции. |
F | IRQ7 - прерывание принтера. Генерируется принтером, когда он готов к выполнению очередной операции. Многие адаптеры принтера не используют это прерывание. |
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 | Не используются. |
70 | IRQ8 - прерывание от часов реального времени. |
71 | IRQ9 - прерывание от контроллера EGA. |
72 | IRQ10 - зарезервировано. |
73 | IRQ11 - зарезервировано. |
74 | IRQ12 - зарезервировано. |
75 | IRQ13 - прерывание от математического сопроцессора. |
76 | IRQ14 - прерывание от контроллера жесткого диска. |
77 | IRQ15 - зарезервировано. |
78 - 7F | Не используются. |
80-85 | Зарезервированы для BASIC. |
86-F0 | Используются интерпретатором BASIC. |
F1-FF | Не используются. |
Таблица 1
(0) 4 | next | указатель на заголовок следующего драйвера. Если смещение адреса следующего драйвера равно FFFF, это последний драйвер в цепочке |
(+4) 2 | attrib | атрибуты драйвера |
(+6) 2 | strateg | смещение программы стратегии драйвера |
(+8) 2 | interrupt | смещение программы обработки прерывания для драйвера |
(+10) 8 | dev_name | имя устройства для символьных устройств или количество обслуживаемых устройств для блочных устройств. |
Программист, когда он составляет программу-драйвер, заносит в это поле либо 0FFFFh:0FFFFh, если исходный текст содержит только один драйвер, либо адрес следующего драйвера (в виде дальней ссылки на метку заголовка следующего драйвера). Если исходный текст содержит несколько драйверов, то в заголовке последнего в поле next должно находиться значение 0FFFFh:0FFFFh.
При загрузке драйверов в память операционная система изменит содержимое поля next в заголовках драйверов для того, чтобы это поле указывало на заголовок следующего драйвера в цепочке. (Изменит в памяти, а не в файле драйвера!)
Обычно исходный текст программы содержит один драйвер, и поле next задается следующим образом: next DD 0FFFFFFFFh
Следующее поле в заголовке драйвера - поле атрибутов драйвера atrib.
Это поле описывает устройство, обслуживаемое данным драйвером. Каждый бит слова отвечает за ту или иную особенность устройства. Прежде чем мы детально рассмотрим назначение всех битов этого слова, заметим, что бит 15 (самый старший бит) указывает, является ли это устройство символьным или блочным.
Следует специально отметить, что все драйверы можно разделить на две группы - драйверы символьных устройств и драйверы блочных устройств. Первые обслуживают устройства посимвольного ввода/вывода, такие как принтеры, клавиатура, контроллеры последовательной передачи данных RS232C и т.д., вторые ориентированы на ввод/вывод блоками - это диски.
Как правило, все нестандартные устройства, начиная от цифрового вольтметра до роботов и систем автоматизации производственных процессов, обслуживаются символьными драйверами. Хотя этот тип драйверов ориентирован на передачу данных посимвольно, для быстродействующих устройств ввода/вывода можно организовать буферизацию (средствами операционной системы).
Блочные драйверы могут Вам потребоваться в основном для обслуживания своих нестандартных дисковых устройств. Например, можно использовать более плотную запись информации на дискетах для повышения их емкости, можно через аппаратуру связи персонального компьютера и ЭВМ серии ЕС создавать псевдо-винчестеры на дисках ЕС (такие "винчестеры" будут восприниматься DOS как обычные стандартные диски). С помощью блочных драйверов можно организовать защиту информации на дисках от несанкционированного доступа, если Ваш драйвер будет шифровать записываемую на диск информацию и предоставлять ее по предъявлению пароля.
Мы рассмотрим оба типа драйверов, каждый раз будем отмечать особенности символьных и блочных устройств.
Приведем назначение отдельных битов слова атрибутов символьного драйвера:
Таблица 1
(0) 4 | next | указатель на заголовок следующего драйвера. Если смещение адреса следующего драйвера равно FFFF, это последний драйвер в цепочке |
(+4) 2 | attrib | атрибуты драйвера |
(+6) 2 | strateg | смещение программы стратегии драйвера |
(+8) 2 | interrupt | смещение программы обработки прерывания для драйвера |
(+10) 8 | dev_name | имя устройства для символьных устройств или количество обслуживаемых устройств для блочных устройств. |
Программист, когда он составляет программу-драйвер, заносит в это поле либо 0FFFFh:0FFFFh, если исходный текст содержит только один драйвер, либо адрес следующего драйвера (в виде дальней ссылки на метку заголовка следующего драйвера). Если исходный текст содержит несколько драйверов, то в заголовке последнего в поле next должно находиться значение 0FFFFh:0FFFFh.
При загрузке драйверов в память операционная система изменит содержимое поля next в заголовках драйверов для того, чтобы это поле указывало на заголовок следующего драйвера в цепочке. (Изменит в памяти, а не в файле драйвера!)
Обычно исходный текст программы содержит один драйвер, и поле next задается следующим образом: next DD 0FFFFFFFFh
Следующее поле в заголовке драйвера - поле атрибутов драйвера atrib.
Это поле описывает устройство, обслуживаемое данным драйвером. Каждый бит слова отвечает за ту или иную особенность устройства. Прежде чем мы детально рассмотрим назначение всех битов этого слова, заметим, что бит 15 (самый старший бит) указывает, является ли это устройство символьным или блочным.
Следует специально отметить, что все драйверы можно разделить на две группы - драйверы символьных устройств и драйверы блочных устройств. Первые обслуживают устройства посимвольного ввода/вывода, такие как принтеры, клавиатура, контроллеры последовательной передачи данных RS232C и т.д., вторые ориентированы на ввод/вывод блоками - это диски.
Как правило, все нестандартные устройства, начиная от цифрового вольтметра до роботов и систем автоматизации производственных процессов, обслуживаются символьными драйверами. Хотя этот тип драйверов ориентирован на передачу данных посимвольно, для быстродействующих устройств ввода/вывода можно организовать буферизацию (средствами операционной системы).
Блочные драйверы могут Вам потребоваться в основном для обслуживания своих нестандартных дисковых устройств. Например, можно использовать более плотную запись информации на дискетах для повышения их емкости, можно через аппаратуру связи персонального компьютера и ЭВМ серии ЕС создавать псевдо-винчестеры на дисках ЕС (такие "винчестеры" будут восприниматься DOS как обычные стандартные диски). С помощью блочных драйверов можно организовать защиту информации на дисках от несанкционированного доступа, если Ваш драйвер будет шифровать записываемую на диск информацию и предоставлять ее по предъявлению пароля.
Мы рассмотрим оба типа драйверов, каждый раз будем отмечать особенности символьных и блочных устройств.
Приведем назначение отдельных битов слова атрибутов символьного драйвера: