Операционная система MS-DOS© Александр Фролов, Григорий ФроловТом 1, книги 1-2, М.: Диалог-МИФИ, 1991. 6.9. Пример драйвера символьного устройстваПриведем пример драйвера символьного устройства, который Вы можете взять в качестве прототипа своей разработки. Этот драйвер выполняет следующие действия:
Приведем полный текст драйвера: Драйвер символьного устройства; Демонстрационный драйвер символьного устройства ; ; Copyright (C)Frolov A., 1990 .MODEL tiny .CODE ; Драйвер состоит из одного ; сегмента кода org 0 ; Эта строка может отсутствовать include sysp.inc devdrv PROC far ; Заголовок драйвера dd 0ffffffffh ;адрес следующего драйвера dw 0C800h ;байт атрибутов dw dev_strategy ;адрес процедуры стратегии dw dev_interrupt ;адрес процедуры прерывания db 'DEVDRIVR' ;имя устройства (дополненное ; пробелами) ;=================================================== ; Программа стратегии 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 dw reserved dw reserved dw reserved dw generic_ioctl ; Выход из драйвера, если функция не поддерживается check_media: make_bpb: clear_input: output_verify: clear_output: Removable_media: reserved: generic_ioctl: ; Выводим сообщение о вызове ; неподдерживаемой драйвером команды mov ax,cs mov ds,ax mov si,offset errmsg call dpc ; Ожидаем нажатия на любую клавишу mov ax,0 int 16h ; Устанавливаем признак ошибки or es:word ptr [bx]+3,8103h jmp quit ;==================================================nondestruct_in: ; Выводим сообщение о начале неразрушающего ввода mov ax,cs mov ds,ax mov si,offset inpmsg_nd call dpc ; Вводим символ с клавиатуры и помещаем ; его в область запроса mov ax,0 int 16h mov BYTE PTR es:[bx]+0dh,al jmp quit ;=================================================== input_status: ; Выводим сообщение о вызове команды ; проверки состояния ввода mov ax,cs mov ds,ax mov si,offset statmsg_i call dpc ; Устанавливаем признак "Занято", т.к. ; последующая команда чтения приведет к ожиданию ; нажатия на клавишу (буферизация не используется) or es:word ptr [bx]+3,0200h jmp quit ;=================================================== output_status: ; Выводим сообщение о вызове команды ; проверки состояния вывода mov ax,cs mov ds,ax mov si,offset statmsg_o call dpc ; Бит занятости не устанавливаем, т.к. ; считаем, что консоль доступна для вывода or es:word ptr [bx]+3,0000h jmp quit ;=================================================== ; Обработчик команды вывода данных output_data: ; Записываем в регистр CL количество ; выводимых символов mov cl,es:[bx]+18 push cx ; Выводим сообщение о начале вывода mov ax,cs mov ds,ax mov si,offset outmsg call dpc pop cx ; Загружаем в DS:SI адрес буфера данных mov ax,es:[bx]+16 mov ds,ax mov si,es:[bx]+14 ; Выводим на экран символы из буфера out_loop: mov al,ds:byte ptr [si] @@out_ch al inc si loop out_loop jmp quit ;=================================================== ; Обработчик команды ввода данных input_data: ; Записываем в регистр CL количество ; вводимых символов mov cl,es:[bx]+18 push cx ; Выводим сообщение о начале ввода mov ax,cs mov ds,ax mov si,offset inpmsg call dpc ; Загружаем в DS:SI адрес буфера данных pop cx mov ax,es:[bx]+16 mov ds,ax mov di,es:[bx]+14 ; Вводим символы с клавиатуры и записываем в буфер inp_loop: mov ax,0 int 16h mov ds:byte ptr [di],al @@out_ch al inc di loop inp_loop jmp quit ;=================================================== ; Обработчик команды вывода данных IOCTL ioctl_out: ; Записываем в регистр CL количество ; выводимых символов mov cl,es:[bx]+18 ; Загружаем в DS:SI адрес буфера данных mov ax,es:[bx]+16 mov ds,ax mov si,es:[bx]+14 ; Выводим на экран символы из буфера ioctl_out_loop: mov al,ds:byte ptr [si] @@out_ch al inc si loop ioctl_out_loop jmp quit ;=================================================== ; Обработчик команды ввода данных IOCTL ioctl_in: ; Записываем в регистр CL количество ; вводимых символов mov cl,es:[bx]+18 ; Загружаем в DS:SI адрес буфера данных mov ax,es:[bx]+16 mov ds,ax mov di,es:[bx]+14 ; Вводим символы с клавиатуры и записываем в буфер ioctl_inp_loop: mov ax,0 int 16h mov ds:byte ptr [di],al @@out_ch al inc di loop ioctl_inp_loop jmp quit ;=================================================== Device_open: ; Выводим сообщение об открытии устройства mov ax,cs mov ds,ax mov si,offset openmsg call dpc jmp quit ;=================================================== Device_close: ; Выводим сообщение о закрытии устройства mov ax,cs mov ds,ax mov si,offset closemsg call dpc 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 ;=================================================== parm_off dw ? ; Смещение строки параметров parm_seg dw ? ; Сегмент строки параметров pc_type dw ? ; Область памяти для сохранения int_num dw ? ; значений параметров out_port dw ? inp_port dw ? ctrl_inp_port dw ? ctrl_out_port dw ? ;===================================================;** ; ;.Name INTERRUPT ;.Title Обработчик прерывания ;.Synopsis ;- ; int <NN> ; ; Вход: NN - Номер прерывания, заданный в файле ;CONFIG.SYS ; AH - Номер выполняемой функции: ; 0 - операция записи; ; 1 - операция чтения ; BH - Адрес (0...7Fh) ; BL - Данные для записи (0...FFh) ; Выход: BL - Прочитанные данные ;- ;.Description ;- ; Прерывание вызывается командой INT <NN> с ;параметрами: ; ; Вход: NN - Номер прерывания, заданный в файле ;CONFIG.SYS ; AH - Номер выполняемой функции: ; 0 - операция записи; ; 1 - операция чтения ; BH - Адрес (0...7Fh) ; BL - Данные для записи (0...FFh) ; Выход: BL - Прочитанные данные ;- ;.Returns ; BL - Прочитанные данные ;.Version 1.00 (c)Copyright Frolov A., 1990 ;- ;** interrupt: push ax push cx push dx push bp push si push di push ds push es cmp ah,0 ; команда записи jz int_write cmp ah,1 ; команда чтения jz int_read ; Обработка неизвестной команды @@out_ch 13,10,'?','?','?',13,10 ; Устанавливаем признак ошибки mov ax,0ffffh jmp int_exit int_write: ; Выводим сообщение о приходе прерывания, ; предназначенного для записи @@out_ch 13,10,'W','R','I','T','E',13,10 mov ax,0 jmp int_exit int_read: ; Выводим сообщение о приходе прерывания, ; предназначенного для чтения @@out_ch 13,10,'R','E','A','D',13,10 ; Имитация чтения, всегда возвращается значение 55h mov bl,55h mov ax,0 jmp int_exit int_exit: pop es pop ds pop di pop si pop bp pop dx pop cx pop ax iret ;=================================================== ; Процедура выводит на экран строку ; символов в формате 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,'¦ *DEVDRV* (C)Frolov A., 1990 ¦' db 13,10,'++' db 13,10,0 outmsg DB 13,10,'___ Вывод на устройство DEVDRIVR ___',0 inpmsg DB 13,10,'___ Ввод с устройства DEVDRIVR ___',0 openmsg db 13,10,'___ Открываем DEVDRIVR ___',0 closemsg db 13,10,'___ Закрываем DEVDRIVR ___',0 inpmsg_nd DB 13,10 DB '___ ND-ввод с устройства DEVDRIVR ___',0 statmsg_i DB 13,10 DB '___ Чтение состояния ввода DEVDRIVR ___',0 statmsg_o DB 13,10 DB '___ Чтение состояния вывода DEVDRIVR ___',0 errmsg DB 13,10 DB '___ Команда не поддерживается DEVDRIVR ___',0 ;=================================================== E_O_P: ;Метка конца программы initialize: lea ax,E_O_P ;смещение конца программы в AX mov es:word ptr [bx]+14,ax ;помещаем его в заголовок mov es:word ptr [bx]+16,cs ; mov ax,es:word ptr [bx]+18 ;смещение строки ; параметров mov cs:parm_off,ax mov ax,es:word ptr [bx]+20 ;сегмент строки ; параметров mov cs:parm_seg,ax ; Стираем экран 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 ; Раскодируем строку параметров и проверяем ; корректность заданных параметров push cs pop ds mov bx, cs:parm_off ; ES:BX - адрес строки ; параметров mov ax, cs:parm_seg mov es, ax ; Адрес начала области параметров mov bp, OFFSET cs:pc_type ; Анализируем параметры call parm jc parm_errors ; Устанавливаем вектор прерывания с номером, ; заданным в строке параметров push cs pop ds mov dx,OFFSET cs:interrupt mov ax,cs:int_num mov ah,25h int 21h jmp quit parm_errors: ; Если параметры заданы с ошибкой, ; установку драйвера не производим. mov ax,cs:req_seg ;ES:BX указывают на заголовок ; запроса mov es,ax mov bx,cs:req_off lea ax,devdrv ;смещение начала драйвера mov es:word ptr [bx]+14,ax ;помещаем его в заголовок mov es:word ptr [bx]+16,cs ; jmp quit ;=================================================== parm proc near xor si, si ; индекс в строке параметров next_chr: mov al, BYTE PTR es:[bx][si] cmp al, 0ah ; проверки на конец je parm_br ; строки параметров cmp al, 0dh je parm_br cmp al, 0h jz parm_br ; Копируем очередной байт строки параметров в буфер mov BYTE PTR cs:parm_buffer[si], al inc si jmp next_chr ; Закрываем скопированную строку параметров нулем parm_br: mov BYTE PTR cs:parm_buffer[si], 0 ; Подготавливаем регистры для вызова программы ; анализа параметров и проверяем правильность ; заданных параметров mov dx, OFFSET sep mov cl, 6 mov bx, OFFSET cs:parm_buffer call get_parm jc err_msg mov ax,cs:pc_type cmp ax,0 jz model_is_valid cmp ax,1 jz model_is_valid jmp err_msg model_is_valid: ; Если параметры заданы правильно, ; выводим их значения на экран mov si, OFFSET msg1 call dpc mov ax,cs:pc_type call print_word mov si, OFFSET msg2 call dpc mov ax,cs:int_num call print_word mov si, OFFSET msg3 call dpc mov ax,cs:out_port call print_word mov si, OFFSET msg4 call dpc mov ax,cs:inp_port call print_word mov si, OFFSET msg41 call dpc mov ax,cs:ctrl_inp_port call print_word mov si, OFFSET msg42 call dpc mov ax,cs:ctrl_out_port call print_word @@out_ch 13,10,13,10 clc jmp end_of_parm err_msg: ; Если были ошибки в параметрах, выводим ; сообщение об ошибке, ; саму ошибочную строку параметров ; и ожидаем нажатия на любую клавишу. ; На выходе устанавливаем флаг CARRY mov si, OFFSET msg5 call dpc mov ds,cs:parm_seg mov si,cs:parm_off call dpline mov ax,cs mov ds,ax mov si, OFFSET msg6 call dpc mov ax,0 int 16h stc end_of_parm: ret parm_buffer db 100 DUP (?) sep db " ",0 msg1 db 13,10,"Personal Computer Type ........ ",0 msg2 db 13,10,"Used Interrupt Number ........ ",0 msg3 db 13,10,"Device Input Port ........ ",0 msg4 db 13,10,"Device Output Port ........ ",0 msg41 db 13,10,"Device Inp Control Port ........ ",0 msg42 db 13,10,"Device Out Control Port ........ ",0 msg5 db 13,10,"Driver Parameters Error!",13,10,0 msg6 db 13,10,13,10," Press any key...",13,10,0 parm endp ;** ; ;.Name get_parm ;.Title Разбор строки параметров. ;.Synopsis ; ; ds:bx - исходная строка, закрытая нулем ; ds:dx - строка из сепараторов, закрытая ; нулем ; ds:bp - буфер параметров ; cx - число параметров ; ;.Description ; Исходная строка ds:bx, разделенная ; сепараторами ds:dx, состоящая из cx ; параметров и начинающаяся с полного пути ; программы, разбирается на слова, параметры - ; шестнадцатеричные цифры. Двоичные слова, ; соответствующие параметрам, последовательно ; заносятся в буфер параметров ds:bp. ; ;.Returns В случае ошибки устанавливается флаг ; переноса. ; ;.Version 1.00 (c)Copyright Frolov G., 1990 ;** get_parm proc near push bx push cx push ax push si xor ch, ch xor ax, ax mov si, ax call strtoc jc parm_end parm_loop: mov ax, 22h call strtoc jc parm_end call hex_to_bin jc parm_end mov ds:[bp][si], ax inc si inc si loop parm_loop parm_end: pop si pop ax pop cx pop bx ret get_parm endp ;** ; ;.Name strtoc ;.Title Выделяет очередное слово из строки. ;.Synopsis ; ; 1) при первом обращении к процедуре: ; Вход: ; ax = 0 ; ds:bx - исходная строка, закрытая нулем ; ds:dx - строка из сепараторов, закрытая ; нулем ; Выход: ; ds:bx - подстрока до первого разделителя, ; закрытая нулем ; 2) при последующих обращениях ; Вход: ; ax != 0 ; ds:dx - строка из сепараторов, закрытая ; нулем ; Выход: ; ds:bx - подстрока до следующего ; разделителя, закрытая нулем ; ;.Description При первом вызове выделяет из строки первое ; слово до разделителя. При повторном вызове ; возвращает указатель на следующее слово в ; исходной строке. Если все слова из строки ; уже выделены, устанавливается флаг ; переноса. ; ;.Version 1.00 (c)Copyright Frolov G., 1990 ;** strtoc proc near push bp push di mov space, 0 cmp ax, 0 jz first mov bx, cs:ds1_off mov ax, cs:ds1_seg mov ds, ax first: cmp BYTE PTR ds:[bx], 0 jz error mov bp, bx str_begin: mov di, dx compe: mov ah, BYTE PTR ds:[bp] cmp ah, 0 jz lab cmp BYTE PTR ds:[di], ah jne next mov BYTE PTR ds:[bp], 0 inc bp inc BYTE PTR cs:space jmp str_begin lab: mov WORD PTR cs:ds1_off, bp mov ax, ds mov WORD PTR cs:ds1_seg, ax jmp end_proc next: inc di cmp BYTE PTR ds:[di], 0 jnz compe cmp BYTE PTR cs:space, 0 jnz lab inc bp jmp str_begin error: stc end_proc: pop di pop bp ret ds1_off dw ? ds1_seg dw ? space db ? strtoc endp ;** ; ;.Name hex_to_bin ;.Title Преобразует hex строку в двоичное число. ;.Synopsis ; ; Вход: ; ds:bx - исходная строка, закрытая нулем ; Выход: ; ax - соответствующее строке число ; ;.Description Преобразует строку из ascii символов в hex ; форме в ее 16-битовый двоичный эквивалент. ; В случае переполнения или если строка ; содержит символы, не равные 0..9, A..F, a..f ; устанавливается флаг переноса. ; ;.Version 1.00 (c)Copyright Frolov G., 1990 ;** hex_to_bin PROC NEAR push bp push si push dx xor ax, ax mov bp, bx begin: cmp BYTE PTR ds:[bp], 0h jz end_pro call hex jc h_error mov si, 10h xor dx, dx mul si cmp dx, 0 jnz h_error add ax, bx inc bp jmp begin h_error: stc end_pro: pop dx pop si pop bp ret hex_to_bin endp hex proc near xor bh, bh mov bl, BYTE PTR ds:[bp] cmp bl, '0' jb hex_error cmp bl, '9' ja next_big and bx, 0fh jmp ok next_big: cmp bl, 'A' jb hex_error cmp bl, 'F' ja next_small sub bl, 37h jmp ok next_small: cmp bl, 'a' jb hex_error cmp bl, 'f' ja hex_error sub bl, 57h jmp ok hex_error: stc ok: ret hex endp ;** ; ;.Name dec_to_bin ;.Title Преобразует dec строку в двоичное число. ;.Synopsis ; ; Вход: ; ds:bx - исходная строка, закрытая нулем ; Выход: ; ax - соответствующее строке число ; ;.Description Преобразует строку из ascii символов в dec ; форме в ее 16-битовый двоичный эквивалент. В ; случае переполнения или если строка содержит ; символы, не равные 0..9, устанавливается флаг ; переноса. ; ;.Version 1.00 (c)Copyright Frolov G., 1990 ;** dec_to_bin proc near push bp push si push dx xor ax, ax mov bp, bx d_begin: cmp BYTE PTR ds:[bp], 0h jz d_end_pro cmp BYTE PTR ds:[bp], '0' jb d_error cmp BYTE PTR ds:[bp], '9' ja d_error mov si, 10 xor dx, dx mul si cmp dx, 0 jnz d_error mov bl, BYTE PTR ds:[bp] and bx, 0fh add ax, bx inc bp jmp d_begin d_error: stc d_end_pro: pop dx pop si pop bp ret dec_to_bin endp 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 ;======================================================= dpline proc near push si dpline_loop: cmp ds:byte ptr [si],0dh jz end_dpline cmp ds:byte ptr [si],0ah jz end_dpline mov al,ds:byte ptr [si] @@out_ch al inc si jmp dpline_loop end_dpline: pop si ret dpline endp devdrv ENDP END devdrv Для работы с этим драйвером и демонстрации его основных возможностей мы подготовили следующую программу: Работа с драйвером символьного устройства// Данная прорамма использует прерывание 80h, // которое устанавливается демонстрационным // драйвером. Для правильной установки файл // CONFIG.SYS должен содержать, например, // такую строку для подключения драйвера: // // device=e:\sysprg\devdrv.sys 1 80 378 379 37a 37a // // Число 80 означает номер используемого прерывания. #include <io.h> #include <conio.h> #include <stdio.h> #include <fcntl.h> #include <sys\types.h> #include <sys\stat.h> #include <malloc.h> #include <errno.h> #include <dos.h> int main(void); union REGS inregs, outregs; struct SREGS segregs; int main(void) { char buf[100], ch; int io_handle; unsigned count; // Открываем устройство с именем DEVDRIVR if( (io_handle = open("DEVDRIVR", O_RDWR)) == - 1 ) { // Если открыть устройство не удалось, выводим // код ошибки printf("Ошибка при открытии устройства %d",errno); return errno; } // Читаем 8 байт из устройства в буфер buf printf("\nВведите 8 символов с клавиатуры\n"); if( (count = read(io_handle, buf, 8)) == -1 ) { // Если при чтении произошла ошибка, // выводим ее код printf("Ошибка чтения %d",errno); return errno; } // Закрываем прочитанную строку нулем // для последующего вывода функцией printf buf[8]=0; printf("\n___ Введена строка: %s ___",buf); // Выводим только что прочитанные данные // обратно на то же устройство if( (count = write(io_handle, buf, 8)) == -1 ) { // Если при записи произошла ошибка, // выводим ее код printf("Ошибка записи %d",errno); return errno; } // Вводим строку IOCTL из устройства printf("\nВведите строку IOCTL (8 символов): "); inregs.h.ah = 0x44; inregs.h.al = 2; inregs.x.bx = io_handle; inregs.x.dx = (unsigned)buf; inregs.x.cx = 8; intdos( &inregs, &outregs ); if(outregs.x.cflag == 1) { // При ошибке выводим код ошибки printf("IOCTL error %x\n",&outregs.x.ax); exit(-1); } buf[8]=0; printf("\n___ Введена строка IOCTL: %s ___",buf); // Выводим строку IOCTL на устройства из buf printf("\nВыведена строка IOCTL: "); inregs.h.ah = 0x44; inregs.h.al = 3; inregs.x.bx = io_handle; inregs.x.dx = (unsigned)buf; inregs.x.cx = 8; intdos( &inregs, &outregs ); if(outregs.x.cflag == 1) { // При ошибке выводим код ошибки printf("IOCTL error %x\n",&outregs.x.ax); exit(-1); } printf("\n\n\nПроверяем вызов прерывания." "\n" "\nНажмите любую клавишу...\n\n"); getch(); printf("\nКоманда записи:\n"); inregs.h.ah = 0x0; /* WRITE */ inregs.h.bh = 0x777; inregs.h.bl = 0x13; int86( 0x80, &inregs, &outregs ); printf("\nКоманда чтения:\n"); inregs.h.ah = 0x1; /* READ */ inregs.h.bh = 0x776; int86( 0x80, &inregs, &outregs ); ch=outregs.h.bl; printf("Полученное значение: %x\n",ch); printf("\nНеизвестная команда:\n"); inregs.h.ah = 0x2; /* ??? */ int86( 0x80, &inregs, &outregs ); // Закрываем устройство close(io_handle); exit(0); } |