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

указатель на заголовок следующего драйвера.



Таблица 8

(0) 4next указатель на заголовок следующего драйвера. Если смещение адреса следующего драйвера равно FFFF, это последний драйвер в цепочке
(+4) 2attrib атрибуты драйвера
(+6) 2strateg смещение программы стратегии драйвера
(+8) 2interrupt смещение программы обработки прерывания для драйвера
(+10) 8dev_name имя устройства для символьных устройств или количество обслуживаемых устройств для блочных устройств.
Мы приведем программу, которая сканирует список драйверов и выводит на стандартное устройство вывода адрес драйвера, его атрибуты, имя устройства для символьных устройств и количество обслуживаемых устройств для блочных драйверов.
Эта программа написана на языке ассемблера. ; Программа выводит информацию о загруженных драйверах ; ; Mакроопределение печатает символы на экране ; @@out_ch MACRO c1,c2,c3,c4,c5 mov ah,02h IRP chr,<c1,c2,c3,c4,c5> IFB <chr> EXITM ENDIF mov dl,chr int 21h ENDM ENDM .MODEL tiny DOSSEG .STACK 100h

.DATA msg DB 13,10,"Device Drivers Information V1.00", 13, 10 DB "Copyright (C)Frolov A.,1990",13,10,13,10 DB "Address Attr Device Name",13,10 DB "------- ---- -----------",13,10 DB "$" bl_msg DB "------> Block Device, Number of Units: ","$" .CODE .STARTUP mov ah, 9h ; Выводим заголовок mov dx, OFFSET msg int 21h mov ah,52h ; Получаем адрес первого int 21h ; драйвера в цепочке add bx,22h ; es:bx - адрес первого драйвера dr_loop: call show_driver_info ; выводим параметры ; драйвера cmp WORD PTR es:[bx+2], 0ffffh ; последний ? jz end_of_driver_list cmp WORD PTR es:[bx], 0ffffh jz end_of_driver_list mov ax,es:[bx] ; получаем адрес следующего mov cx,es:[bx+2] ; драйвера mov bx,ax mov es,cx jmp dr_loop end_of_driver_list: .EXIT 0 show_driver_info proc near ;es:bx - адрес драйвера push es push bx mov ax,es ; выводим адрес драйвера call Print_word @@out_ch ':' mov ax,bx call Print_word @@out_ch ' ',' ' mov ax,es:[bx+4] ; выводим атрибут драйвера call Print_word @@out_ch ' ',' ' test WORD PTR es:[bx+4],8000h ; проверяем, это ; символьный jz is_block ; драйвер или ; блочный mov cx,8 ; для символьного выводим mov si,bx ; имя драйвера pr_name: mov al,BYTE PTR es:[si+10] @@out_ch al inc si loop pr_name jmp nxt is_block: mov ah, 9h ; для блочного драйвера mov dx, OFFSET bl_msg ; выводим количество int 21h ; логических устройств, mov al,BYTE PTR es:[bx+10] ; которые ; обслуживает mov ah,0 ; этот драйвер call Print_word nxt: @@out_ch 13,10 pop bx pop es ret show_driver_info endp ; Вывод на экран содержимого регистра AX Print_word proc near ;-------------------- push ax push bx push dx ; push ax mov cl,8 rol ax,cl call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop ax call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop dx pop bx pop ax ret Print_word endp ; Byte_to_hex proc near ;-------------------- ; al - input byte ; dx - output hex ;-------------------- push ds push cx push bx ; lea bx,tabl mov dx,cs mov ds,dx ; push ax and al,0fh xlat mov dl,al ; pop ax mov cl,4 shr al,cl xlat mov dh,al ; pop bx pop cx pop ds ret ; tabl db '0123456789ABCDEF' Byte_to_hex endp ; END
Если запустить эту программу, она выведет на экран сведения о всех загруженных драйверах: Device Drivers Information V1.00 Copyright (C)Frolov A.,1990 Address Attr Device Name ------- ---- ----------- 02C1:0048 8004 NUL 112F:0000 8800 RBUSDRIV 10E4:0000 0800 ------> Block Device, Number of Units: 0001 0D86:0000 C800 SMARTAAR 0CC7:0000 A000 XMSXXXX0 0BA5:0000 6842 ------> Block Device, Number of Units: 0003 0070:016E 8013 CON 0070:0180 8000 AUX 0070:0192 A040 PRN 0070:01A4 8008 CLOCK$ 0070:01B6 0842 ------> Block Device, Number of Units: 0003 0070:01CA 8000 COM1 0070:01DC A040 LPT1 0070:01EE A040 LPT2 0070:0200 A040 LPT3 0070:0212 8000 COM2 0070:0224 8000 COM3
К этой картинке мы еще вернемся при обсуждении процесса загрузки драйверов.


Таблица 8

(0) 2seg_env сегментный адрес, по которому загружается программа
(+2) 4reloc фактор перемещения, аналогичен элементу таблицы перемещений в заголовке EXE-файла
Следующая демонстрационная программа загружает программу PARM.COM_как оверлей без передачи ей управления: .MODEL small DOSSEG .STACK 100h .DATA path db "PARM.COM",0 epb dw 0 reloc dd 0 .CODE .STARTUP mov ax,ds mov es,ax mov bx,SEG buff mov epb,bx mov bx,OFFSET epb ; ES:BX указывают на EPB mov dx,OFFSET path ; DS:DX указывают на путь ; загружаемой программы mov ax, 4B03h ; AH = 4Bh ; AL = 0 загрузить оверлей int 21h .EXIT 0 buff: dd 100 dup(?) END
Программа загружается в буфер buff.
Пользователи языка Си имеют в своем распоряжении три возможности запустить программу.
Самый простой способ - использовать функцию system(). Эта функция может выполнить любую команду DOS или любую программу, пакетный файл. Например: system("FORMAT A:");
При использовании этой функции должен быть доступен COMMAND.COM. К сожалению, хотя system и возвращает код завершения, по нему нельзя сделать вывод о том, как была выполнена запускаемая программа. Если в качестве аргумента функции будет передано неправильное имя, на экране появится сообщение: Bad command or file name
Код возврата в этом случае будет 0 - как будто все нормально!
Другие две возможности запустить программу - использовать функции spawn и exec. Функция spawn и ее разновидности запускают программу как дочерний процесс. Функция exec загружает новую программу как оверлей на место старой и передает ей управление без возврата. После завершения дочерней программе управление будет передано COMMAND.COM или программе, которая запустила родительскую программу.
Семейство функций spawn обеспечивает запуск дочерней программы с родительской или со специально сформированной средой. Кроме того, в файле process.h описаны параметры, которые можно передать функции spawn:



Таблица 8

(0) 2sect_sizКоличество байтов в одном секторе диска.
(+2) 1clustsizКоличество секторов в одном кластере.
(+3) 2res_sectКоличество зарезервированных секторов.
(+5) 1fat_cntКоличество таблиц FAT.
(+6) 2root_siz Максимальное количество дескрипторов файлов, содержащихся в корневом каталоге диска.
(+8) 2tot_sectОбщее количество секторов на носителе данных (в разделе DOS).
(+10) 1mediaБайт-описатель среды носителя данных.
(+11) 2fat_sizeКоличество секторов, занимаемых одной копией FAT.
Подробно формат и назначение полей BPB будут описаны в разделе, посвященном файловой системе DOS.
Приведем фрагмент исходного текста драйвера, возвращающего при инициализации указатель на массив BPB: lea dx,bpb_ptr mov es:[bx+18],dx mov es:[bx+20],cs . . . . . . . . . .
В этом примере предполагается, что ES:BX содержит адрес заголовка запроса.
Область данных может содержать, например, такое описание bpb_ptr: bpb: DW 512 ; количество байтов на сектор DB 1 ; количество секторов на кластер DW 1 ; зарезервировано секторов DB 2 ; количество копий FAT DW 64 ; максимальное количество файлов ; в корневом каталоге DW 360 ; общее число секторов на диске DB 0FCh ; описатель среды DW 2 ; количество секторов в FAT ; bpb_ptr DW bpb ; таблица для трех одинаковых DW bpb ; логических устройств DW bpb
Если Ваш драйвер работает с несколькими разными по параметрам логическими устройствами, для каждого устройства необходимо подготовить свой BPB и занести его адрес в соответствующее порядковому номеру устройства место таблицы указателей на блоки BPB.
Какие действия должна выполнить функция инициализации, если выяснилось, что по тем или иным причинам установка драйвера невозможна? Например, ошибочно заданы параметры, не хватает оперативной памяти, других ресурсов или, наконец, отсутствует само обслуживаемое устройство.
Драйвер символьного устройства при этом может указать в качестве конечного адреса резидентной части программы адрес начала драйвера, т.е. адрес заголовка драйвера. Размер резидентной части при этом будет равен нулю.
Блочные драйверы дополнительно должны записать ноль в поле количества обслуживаемых логических устройств n_units.
В обоих случаях включения драйвера в состав операционной системы не произойдет.
Приведем пример простейшего драйвера символьного устройства, который обслуживает только команду инициализации. Фактически этот драйвер не работает ни с каким физическим устройством. Он просто стирает содержимое экрана при инициализации, выводит сообщение и ожидает нажатия оператором любой клавиши. После нажатия драйвер завершает свою работу без включения себя в состав DOS.
Исходный текст драйвера: .MODEL tiny .CODE ; Драйвер состоит из одного ; сегмента кода org 0 ; Эта строка может отсутствовать include sysp.inc ;======================================================== simple PROC far ; Драйвер - это FAR-процедура ;======================================================== E_O_P: ;Метка конца программы, ;устанавливаем ее в начало ;драйвера, т.к. не надо ;оставлять драйвер резидентно ;в памяти ; Заголовок драйвера dd 0ffffffffh ;адрес следующего драйвера dw 8000h ;байт атрибутов dw dev_strategy ;адрес процедуры стратегии dw dev_interrupt ;адрес процедуры прерывания db 'SIMPLE_D' ;имя устройство (дополненное ;пробелами) ;======================================================== ; Программа стратегии dev_strategy: mov cs:req_seg,es mov cs:req_off,bx ret ; Здесь запоминается адрес заголовка запроса req_seg dw ? req_off dw ? ;======================================================== ;Обработчик прерывания dev_interrupt: push es ;сохраняем регистры push ds push ax push bx push cx push dx push si push di push bp ; Устанавливаем ES:BX на заголовок запроса mov ax,cs:req_seg mov es,ax mov bx,cs:req_off ; Получаем код команды из заголовка запроса и умножаем ; его на два, чтобы использовать в качестве индекса ; таблицы адресов обработчиков команд mov al,es:[bx]+2 shl al,1 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 dw Device_open dw Device_close dw Removable_media ; Выход из драйвера, если функция не поддерживается check_media: make_bpb: ioctl_in: nondestruct_in: input_status: clear_input: output_verify: output_status: clear_output: ioctl_out: Removable_media: Device_open: Device_close: output_data: input_data: or es:word ptr [bx]+3,8103h jmp quit ;======================================================== quit: or es:word ptr [bx]+3,100h pop bp pop di pop si pop dx pop cx pop bx pop ax pop ds pop es ret ;======================================================== ; Процедура выводит на экран строку ; символов в формате ASCIIZ dpc proc near push si dpc_loop: cmp ds:byte ptr [si],0 jz end_dpc mov al,ds:byte ptr [si] @@out_ch al inc si jmp dpc_loop end_dpc: pop si ret dpc endp ;======================================================== hello db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+' db 13,10,'¦ *SIMPLE* (C)Frolov A., 1990 ¦' db 13,10,'+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+' db 13,10 db 13,10,'Hit any key...' db 13,10,0 ;======================================================== initialize: lea ax,E_O_P ;смещение конца программы в AX mov es:word ptr [bx]+14,ax ;помещаем его в заголовок mov es:word ptr [bx]+16,cs ; ; Стираем экран mov dh,18h mov dl,80h xor cx,cx mov bh,7 xor al,al mov ah,6 int 10h ; Устанавливаем курсор в левый верхний угол экрана mov bh,0 xor dx,dx mov ah,2 int 10h ; Выводим сообщение mov ax,cs mov ds,ax mov si,offset hello call dpc ; Ожидаем нажатия на любую клавишу mov ax,0 int 16h jmp quit simple ENDP END simple
В программе используется макро @@out_ch, описанное в файле sysp.inc и предназначенное для вывода символов на экран дисплея. Файл sysp.inc имеется на дискете, прилагающейся к книге. Приведем текст макро @@out_ch: @@out_ch MACRO c1,c2,c3,c4,c5,c6,c7,c8,c9,c10 mov ah,02h IRP chr,<c1,c2,c3,c4,c5,c6,c7,c8,c9,c10> IFB <chr> EXITM ENDIF mov dl,chr int 21h ENDM ENDM
Текст этого драйвера транслировался при помощи ассемблера, входящего в состав QuickC 2.01. Полученный объектный модуль обрабатывался пакетным файлом: link %1.obj; exe2bin %1.exe %1.sys
Вы можете также использовать макроассемблер MASM версии 5.0 или более поздней версии.
Нетрудно заметить, что процедура получения загрузочного модуля драйвера действительно похожа на процедуру получения COM-программы. Сообщение редактора об отсутствии сегмента стека следует проигнорировать, сегмента стека действительно нет и быть не может, так как драйвер состоит из единственного сегмента кода.
Для испытания этого и других драйверов запишите драйвер в корневой каталог системной дискеты (с которой можно загрузить операционную систему) и поместите в файл CONFIG.SYS, находящийся на этой дискете строку: DEVICE=a:\simple.sys
Когда Ваш драйвер будет отлажен, его можно переписать на диск и подключить к файлу CONFIG.SYS, находящемуся на диске С:.

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