|
 |
Операционная система MS-DOS
© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.
Приведем пример драйвера символьного
устройства, который Вы можете взять в качестве
прототипа своей разработки. Этот драйвер
выполняет следующие действия:
- принимает и анализирует строку параметров из
команды "DEVICE=" файла CONFIG.SYS, преобразует
параметры из символьной формы в двоичную и
проверяет их на корректность;
- если параметры заданы неправильно, в процессе
инициализации выводится сообщение, и драйвер не
подключается к операционной системе;
- драйвер переназначает одно прерывание, номер
которого задается в строке параметров;
- обработчик переназначенного прерывания
моделирует выполнение функций ввода, вывода и
выполняет обработку неправильной функции;
- демонстрируется использование функций IOCTL и
ввода/вывода, ввод данных драйвер производит с
клавиатуры, вывод осуществляет на экран дисплея.
Приведем полный текст драйвера:
Драйвер символьного устройства
; Демонстрационный драйвер символьного устройства
;
; 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);
}
![[Назад]](prev.gif) |
![[Содеожание]](sod.gif) |
|
|