MS-DOS для программиста© Александр Фролов, Григорий ФроловТом 18, М.: Диалог-МИФИ, 1995, 254 стр. 6.7. Особенности отладки драйверовДрайверы достаточно сложны для отладки. На этапе инициализации драйвера (при выполнении команды инициализации) загрузка операционной системы еще не завершена, и воспользоваться обычным отладчиком невозможно. Отладчик Turbo Debugger позволяет вам отлаживать драйверы в режиме удаленной отладки, но для этого потребуется второй компьютер. Прикладная программа не вызывает драйвер напрямую, а делает это через функции прерывания MS-DOS, поэтому вам придется очень долго "добираться" до программы прерывания драйвера. Малейшие ошибки в программе инициализации могут привести к невозможности завершения загрузки операционной системы. Программа стратегии обычно очень проста и проблем не вызывает. Для отладки обработчика команды инициализации можно подготовить специальные функции, отображающие на экране содержимое наиболее важных переменных и областей памяти. Такие же функции можно использовать и для отладки обработчиков других команд драйвера. В качестве примера приведем текст функции ntrace, которая выводит на экран содержимое всех регистров процессора. После вывода выполнение программы приостанавливается до тех пор, пока пользователь не нажмет любую клавишу. Текст этой функции следует поместить в ту часть драйвера, которая останется резидентной. В этом случае вы сможете вызывать ее не только при инициализации, но и при выполнении других команд. В листинге 6.2 вы найдете исходный текст функции ntrace, а также исходный текст небольшой com-программы, которая вызывает эту функцию. Листинг 6.2. Файл ntrace\ntrace.asm @@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
@@out_str MACRO
mov ah,9
int 21h
ENDM
.MODEL tiny
.CODE
.STARTUP
mov ax, 1234h
mov bx, 5678h
call ntrace
.EXIT 0
;==============================================
; Функция выводит на экран содержимое
; всех регистров и приостанавливает выполнение
; программы до тех пор, пока пользователь не
; нажмет на любую клавишу.
; После возвращения из процедуры
; все регистры восстанавливаются.
;==============================================
ntrace proc near
; Сохраняем в стеке регистры,
; содержимое которых будет изменяться
pushf
push ax
push bx
push cx
push dx
push ds
push bp
push cs
pop ds
mov bp, sp
; Выводим сообщение об остановке
mov dx, offset cs:trace_msg
@@out_str
; Выводим содержимое всех регистров
mov ax, cs ; cs
call Print_word
@@out_ch ':'
mov ax, [bp]+14 ; ip
call Print_word
@@out_ch 13,10,13,10,'A','X','='
mov ax, [bp]+10
call Print_word
@@out_ch ' ','B','X','='
mov ax, [bp]+8
call Print_word
@@out_ch ' ','C','X','='
mov ax, [bp]+6
call Print_word
@@out_ch ' ','D','X','='
mov ax, [bp]+4
call Print_word
@@out_ch ' ','S','P','='
mov ax, bp
add ax, 16
call Print_word
@@out_ch ' ','B','P','='
mov ax, [bp]
call Print_word
@@out_ch ' ','S','I','='
mov ax, si
call Print_word
@@out_ch ' ','D','I','='
mov ax, di
call Print_word
@@out_ch 13,10,'D','S','='
mov ax, [bp]+2
call Print_word
@@out_ch ' ','E','S','='
mov ax, es
call Print_word
@@out_ch ' ','S','S','='
mov ax, ss
call Print_word
@@out_ch ' ','F','='
mov ax, [bp]+12
call Print_word
lea dx, cs:hit_msg
@@out_str
; Ожидаем, когда пользователь нажмет
; на любую клавишу
mov ax, 0
int 16h
; Восстанавливаем содержимое регистров
pop bp
pop ds
pop dx
pop cx
pop bx
pop ax
popf
ret
trace_msg db 13,10,'----> Break аt address ','$'
hit_msg db 13,10,'Press any key...','$'
ntrace 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
;==============================================
; Преобразование байта в два символа
; AL - преобразуемый байт
; DX - его символьное представление
;==============================================
Byte_to_hex proc near
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
Для ассемблирования и редактирования этой программы используйте пакетный файл, приведенный в листинге 6.3. Листинг 6.3. Файл ntrace\mk.bat tasm ntrace tlink ntrace /t При использовании другого метода отладки драйвера вы можете построить макет драйвера в виде com-программы. Используя свой любимый отладчик, вы сможете проверить работу большинства входящих в драйвер функций. Если у вас есть отладчик Advanced Fullscreen Debugger, можно использовать его способность оставаться резидентным. Отладчик активизируется в тот момент, когда пользователь нажмет комбинацию клавиш <Ctrl+Esc>. В интересующее вас место драйвера поместите вызов прерывания INT 16h , ожидающий ввод с клавиатуры, например: push ax mov ax,0 int 16h pop ax В этом фрагменте кода можно сохранить в стеке и регистр флагов, если его изменение нежелательно. После того как драйвер остановится, ожидая ввод, активизируйте отладчик, нажав комбинацию клавиш <Ctrl+Esc>. Вы окажетесь в теле обработчика прерывания INT 16h . Выполняя программу по шагам, довольно скоро вы достигнете выхода из этого обработчика - команды IRET. После выполнения команды IRET управление будет передано команде, следующей за командой INT 16h. Эта команда (в приведенном примере - pop ax) принадлежит вашему драйверу! Используя известное теперь значение адреса заголовка запроса (регистры ES:BX), можно просмотреть запрос и определить, какая команда выполняется драйвером. Выполнив все необходимые отладочные действия, запустите программу на выполнение без отладки. В нужный момент времени вы снова сможете вызвать отладчик тем же способом. И еще одно замечание. Драйвер использует системный стек, имеющий довольно небольшой размер. Поэтому при необходимости организуйте свой стек в области памяти, принадлежащей драйверу. Очень важно, чтобы драйвер перед началом работы сохранил содержимое всех регистров, включая регистр флагов, а перед возвращением управления операционной системе восстановил старое содержимое регистров. Критичные участки драйвера, связанные с обращением к портам периферийных устройств, должны выполняться с замаскированными прерываниями. |

