|
Программа DEBUG (отладчик) дает средство
обнаружения ошибок при
работе с программой, транслированной в
машинный язык.Программа
DEBUG обеспечивает возможность пошагово
выполнять программу и
следить за тем, что при этом
происходит.Программа DEBUG - это еще
одно программное средство, поставляемое
как часть DOS.Вы
загружаете ее так же, как и любую другую
программу, и работаете в
диалоге, используя клавиатуру и
экран.Когда программа DEBUG
ожидает каких-либо действий со стороны
пользователя, то свой запрос
она обозначает символом "-".
Вместо перечисления всех команд, которые
входят в программу
DEBUG, используем данный отладчик для
проверки работы только что
составленной программы, приведенной на
Фиг. 5.13 и П5.14. На
Фиг. 5.17 приведен соответствующий листинг.
В данном примере сначала вызывается
программа DEBUG и
указывается та программа, которую
предполагается отлаживать - в
нашем случае программа FIG5=13.EXE. После
того, как программа DEBUG
загружена, она производит загрузку
отлаживаемой программы.
Управление теперь принадлежит отладчику, и
он с помощью символа "-"
показывает, что ожидает ввода. До тех пор,
пока вы не введете для
него указаний, с программой ничего
происходить не будет.
Команда R выводит содержимое всех регистров
в момент,
соответствующий загрузке программы FIG5=13
и передаче ей управления.
Содержимое регистров не требует пояснений,
за исключением, быть
может, значений флагов.Флаг NV указывает на отсутствие
переполнения, флаг UP - флаг направления и
т.д.При выводе
содержимого регистров в последней строке
приводится следующая
выполняемая команда.В ячейке 04C5:0000 записана команда PUSH DS.
B>A:DEBUGFIG5_13.EXE
-R
AX=0000BX=0000CX=0120DX=0000SP=FFF0BP=0000SI=0000DI=0000
DS=2C26ES=2C26SS=2C26CS=2C26IP=0000NV UP DI PL NZ NA PO NC
2C26:00001EPUSH
DS
-U
2C26:00001EPUSH
DS
2C26:0001B80000MOV
AX,0000
2C26:000450PUSH
AX
2C26:0005FCCLD
2C26:00068CC8 MOV
AX,CS
2C26:00088ED8 MOV
DS,AX
2C26:000A8D361D00LEA
SI,[001D]
2C26:000EACLODSB
2C26:000FA24000MOV
[0040],AL
2C26:0012E82C00CALL
0041
2C26:0015803E40000ACMP
[0040],0A
2C26:001A75F2 JNZ
000E
2C26:001CCBRET
Far
2C26:001D9DPOPF
2C26:001EE2A0 LOOP
FFC0
2C26:002020AFE0AEAND
[BX+AEE0],CH
2C26:0024A3E0A0MOV
[A0E0],AX
-D2C26:0
2C0E:0000CD 20 00 A0 00 9A EE FE 1D F0 ED 04 04 1C
3C 01. ............<.
2C0E:001022 1B EB 04 22 1B 04 1C 01 01 01 00 02 06
FF FF"..."...........
2C0E:0020FF FF FF FF FF FF FF FF FF FF FF FF 08 2C
D0 FF.............,..
2C0E:003004 1C 14 00 18 00 0E 2C FF FF FF FF 00 00
00 00.......,........
2C0E:004000 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00................
2C0E:0050CD 21 CB 00 00 00 00 00 00 00 00 00 00 20
20 20.!...........
2C0E:006020 20 20 20 20 20 20 20 00 00 00 00 00 20
20 20.....
2C0E:007020 20 20 20 20 20 20 20 00 00 00 00 00 00
00 00........
-RAX
AX 0000
:1234
-E 2C0E:21
2C0E:002169.73.20.61.2020.
-G 3C
AX=0E54BX=0000CX=003FDX=0000SP=FFEABP=0000SI=001DDI=0000
DS=2C26ES=2C26SS=2C26CS=2C26IP=003CNV UP DI PL NZ NA PO NC
2C26:003C CD10INT10
-T
AX=0E54BX=0000CX=003FDX=0000SP=FFEABP=0000SI=001DDI=0000
DS=2C26ES=2C26SS=2C26CS=F000IP=0165NV UP DI PL NZ NA PO NC
F000:F065 FBSTI
-T
Фиг. 5.17 Листинг отладчика для Фиг 5.13 и 4.14 (начало)
AX=0E54BX=0000CX=003FDX=0000SP=FFEABP=0000SI=001DDI=0000
DS=2C26ES=2C26SS=2C26CS=F000IP=0166NV UP DI PL NZ NA PE NC
F000:F066 FCCLD
-G 2C26:3E
T
AX=0E54BX=0000CX=003FDX=0000SP=FFEABP=0000SI=001DDI=0000
DS=2C26ES=2C26SS=2C26CS=2C26IP=013ENV UP DI PL NZ NA PO NC
2C26:003ERET
-G
Эта программа - тест
Program terminated normally
-R
AX=0754BX=0000CX=003FDX=0000SP=FFEABP=0000SI=001DDI=0000
DS=2C26ES=2C26SS=2C26CS=2C26IP=003ENV UP DI PL NZ NA PO NC
2C26:003E C3RET
-Q
B>
Фиг. 5.17 Листинг отладчика для Фиг 5.13 и
4.14 (продолжение)
Здесь следует немного задержаться и
проанализировать
информацию, которая записывается в
регистры. Содержимое регистров
соответствует моменту, когда программа
FIG5=13 получает управление
от командного процессора. Обратите
внимание, что пара регистров
CS:IP указывает на первую команду,
определяемую оператором END
программы. Регистры DS и ES указывают на
префикс программного
сегмента. И наконец, пара регистров SS:SP указывает на сегмент
STACK. Описанное состояние регистров будет
сравниваться позднее с
аналогичным состоянием регистров для файла
типа .COM.
Чтобы просмотреть большее число команд,
надо ввести символ "U"
(дизассемблировать), и на дисплей
выводится около двадцати
следующих команд. Это удобно при обладке
программы, для которой нет
листинга. Дизассемблирование программы
позволяет просмотреть ее
команды. Это может сэкономить вам бумагу и
время в случае, когда в
программу внесены небольшие изменения. Так
как ваш листинг больше
не соответствует той программе, которая
находится в памяти, то ее
дизассемблирование позволяет вам узнать
правильные адреса для
каждой команды.
Однако у дизассемблирования с помощью
программы DEBUG имеется
ряд недостатков по сравнению с
использованием листинга. Отсутствуют
комментарии (может быть очень важные для
понимания программы), и
ячейки памяти идентифицируются только по
адресу, а не по имени
переменной. Например, хранящаяся в ячейке
04C5:000E команда имеет,
как показано на Фиг.5.13, следующий вид:
MOVOUTPUT_CHARACTER,AL
а в дизассемблированном виде
MOV[0030],AL
Это одна и та же команда. Для
программиста, выполняющего
отладку, имя переменной OUTPUT_CHARACTER
говорит больше, чем адрес
ячейки [0030]. Однако программе DEBUG не
известны имена переменных,
и она вынуждена оперировать фактическими
адресами.
Кроме того, программа, DEBUG не
обеспечивает той же самой
ассемблерной мнемоники, которую
воспринимает ассемблер. Это значит,
что некоторые команды будут выглядеть
по-разному. Команда из ячейки
04C5:0014 будет при дмзассемблировании
иметь вид
CMPB,[3000],0A
но та же саммая команда на Фиг.5.13
представлена в виде:
CMPOUTPUT_CHARACTER,10
Программа дизассемблирования как на входе,
так и на выходе
работает только с шестнадцатеричными
значениями. Этим объясняется
появление значения 0A. Мы уже выяснили,
почему получается значение
[0030], вместо имени OUTPUT_CHARACTER. Что
же такое символ "B,"?
Ассемблер оперирует переменными вполне
определенного типа. Это
значит, что во время ассемблирования тип
переменных может иметь
значение: байт, слово или какой-нибудь
другой. Таким образом, когда
программист вводит команду, содержащую
ссылку на область памяти, то
ассемблеру известен размер этой области.
На программа DEBUG не
имеет представления о длине переменной,
записанной по адресу
[0030]. Однако программе
дизассемблирования точно известно, что
данная команда пересылает ровно один байт
данных, указанных
непосредственно в команде, по адресу
[0030]. Таким образом, символ
"B," указывает на то, что
непосредственная операция состоит в
пересылке одного байта. Для получения того
же самого результата с
помощью ассемблера соответствующая команда
должна иметь вид:
CMPBYTE
PTR [0030],10
Вы можете рассматривать символ
"B," как сокращение BYTE PTR.
Аналогично символ W используется для WORD
PTR,L - для длинного
(far) возврата и т.д.
Вместе с командой в дизассемблированном
виде вводится и ее
объектный код. Как вы можете заметить, по
адресу 04C5:001C записаны
какие-то команды, которых нет на Фиг.
5.13. Эта область данных,
содержащая строку "Это тест".
Однако команде, осуществляющей
дизассемблирование, не известно, где в
программе кончаются команды
и начинаются данные. Таким образом она все
интерпретирует как
команды. (Кстати, именно эта
последовательность команд выполнялась
бы, если в вашей программе был сделан
переход в рассматриваемую
область данных.)
Команда вывода на экран D позволяет
просмотреть на дисплее
области данных. Отображение на экране
состоит из двух частей.
Вместе с листингом содержимого ячеек
памяти в шестнадцатеричном
представлении приводятся символы в коде
ASCII, которые
соответствуют этим значениям. Если
отображение команд подобным
образом не имеет смысла, то область данных
представляется очень
ясно. Когда вам будет нечем особенно
заняться, вы можете
попробовать написать такую программу,
команды которой в коде ASCII
соответствуют инициалам вашего имени. С
помощью отладчика можно
изменять содержимое регистров и ячеек
памяти. Если ввести символ R
(регистр) и затем тип регистра, то на
дисплей будет выведено
содержимое этого регистра с возможностью
его коррекции. Если нажать
клавишу "возврат", то содержимое
регистра останется прежним. Оно
будет изменено, если ввести новое
значение.
Можно также модифицировать содержимое
ячеек памяти. Ввод
символа E (редактирование) позволяет это
сделать. При этом
программа DEBUG выводит на дисплей
значения отдельных ячеек памяти,
после которых следует символ . Вы можете
изменить содержимое
ячейки, вводя новое значение, либо нажать
клавишу пробела, чтобы
перейти к следующей ячейке, или - клавишу
"возврат", чтобы
вернуться к режиму запроса следующей
команды отладчика. В
рассматриваемом примере значения в первых
трех ячейках остаются
прежними. Содержимое ячейки 04C5:0024
изменено со значения 61H на
значенее 20H. Так как эта ячейка входит в
область данных, то
выводимое сообщение будет отличаться от
тог, которое было в
транслированной программе.
В любой команде, обращающейся к ячейкам
памяти, предполагается,
что адрес является частью команды. Команда
E, как и команда
отображения, выводит на дисплей содержимое
ячейки по указанному в
ней адресу. Точно так же можно было
использовать адрес в команде
дизассемблирования. Можно ввести адрес в
виде сегмента и смещения,
или только смещения. Если вы указали
только смещение, то
соответствующий сегментный регистр будет
выбран программой DEBUG. В
случае команды U используется регистр CS,
а для команд D и E по
умолчанию сегмент будет определяться
регистром DS.
Теперь попытаемся выполнить эту программу.
Ее можно просто
запустить и посмотреть, что будет
происходить. На для этого не
нужна программа DEBUG. Программа DEBUG
позволяет задать точки
останова, называемые "точками
прерывания" программы. Благодаря
введению в программу таких точек можно
возвращать управление
программе DEBUG. Это дает еще одну
возможность проверки состояния
памяти и регистров для того, чтобы
контролировать ход выполнения
программы.
Команда G, (выполнять) передает управление
от программы DEBUG
пользовательской программе. Выполнение
команд начинается с ячейки,
задаваемой парой регистров CS:IP (так же,
как в реальном
микропроцессоре). Тестируемая программа
продолжает выполняться до
тех пор, пока она не пройдет точку
прерывания. В нашем примере мы
задали точку прерывания по адресу 3CH. Так
как мы указали только
смещение, то для определения сегмента
программа DEBUG использует
содержимое регистра CS. Из листинга,
приведенного на Фиг. 5.14,
видно, что смещение 3CH соответствует
команде INT 10H. В
рассматриваемом примере программы было
выбрано именно это место,
потому что это - та точка, где управление
передается подпрограмме
BIOS, вызываемой из ПЗУ. Проверка
программы в этой точке
гарантирует, что мя установили регистры в
нужное состояние перед
выполнением подпрограммы BIOS.
Как только встречается точка прерывания,
управление
возвращается программе DEBUG. При этом,
так же как и в случае
команды R, на дисплей выводятся содержимое
регистров и следующая
выполняемая команда. Так как управление
опять передано программе
DEBUG, вы можете использовать любую из
команд отладки.
Имеются ограничения на использование точек
прерывания.
Фактически точка прерывания реализуется
кодом операции 0CCH.
Соответствующая этому коду команда
вызывает прерывание INT 3.
Данное математическое прерывание
возвращает управление программе
DEBUG. Если какая-то команда возвращает
управление отладчику, то
точка прерывания должна находиться в
начале этой команды. Если же
точка прерывания выбрана где-то в другом
месте, то управление не
будет возвращено отладчику и будет
выполняться не та команда
программы, которая предполагалась.
Например, если бы было задано
"=G 3D", то по адресу 3CH была
бы команда INT 0CCH, и дальнейшее
выполнение программы предсказать трудно.
Если точка прерывания выбрана
осмотрительно, то никаких
осложнений не будет. Командв "G"
позволяет задать до десяти точек
прерывания. После прохождения любой из них
происходит
восстановление исходных значений точек
прерывания. Выполнение
команды отладки "G" без указания
точек прерывания никогда не выйдет
ни на какую из ранее заданных точек
прерывания, потому что все они
были удалены. Если вы запустили программу,
и произошел ее останов и
она зациклилась, то возможно, что вернуть
управление, которое
передано программе, удастся только с
помощью клавиши системного
сброса Ctrl=Alt=Del, т.е. вам придется
начать все с начала.
Запуская незнакомую программу, следует
быть осторожным.
Если вы захотите ввести пермаментную точку
прерывания,
воспользуйтесь командой E для изменения
значения первого байта
команды на значение 0CCH. Эта точка
прерывания будет там постоянно
или, по крайней мере, до тех пор, пока вы
ее не измените. Возможно
вы захотите использовать это прерывание в
точке входа в
подпрограмму обработки ошибки и
проанализируете возникающие ошибки
более тщательно, чем при передаче их
обработки программе.
Существует еще одно обстоятельство,
связанное с точками
прерывания, о котором следует помнить.
Если вы попытаетесь задать
точку прерывания в области, относящейся к
ПЗУ, то ничего не
получится. Так как вы не можете менять
содержимое ПЗУ, то команда
0CCH никогда туда не запишется.
Рассмотрим следующую команду отладчика -
команда трассировки T.
Данная команда инициирует выполнение одной
команды отлаженной
программы. В нашем примере команда T
выполнена несколько раз. Как
вы можете убедиться, выполняется несколько
первых команд BIOS,
вызванной по прерыванию INT 10H.
Подпрограмма BIOS, естественно,
находится в ПЗУ. Команда трассировки
позволяет "приостановить"
программу при ее выполнении в ПЗУ.
Перед передачей управления
пользовательской программе команда
трассировки выставляет в регистре флагов
соответствующий бит
трассировки. Этот бит инициирует
прерывание INT 1 после выполнения
каждой команды. Вектор прерывания INT 1
возвращает управление
программе DEBUG. Выполнение прерывания INT
1 автоматически
сбрасывает бит трассировки в исходное
состояние. Это значит, что
программа DEBUG не прерывается после
выполнения каждой ее команды.
Команда трассировки служит прекрасным
средством "пробиться" через
трудный участок программы. При этом
программа DEBUG выводит на
экран каждую команду вместе с содержимым
регистров как раз в
момент, предшествующий выполнению этой
команды. Так как в данном
режиме используются не точки прерывания, а
собственно прерывания,
то можно выполнять трассировку даже
программ ПЗУ.
Вернемся к нашему примеру. Команда =G
4C5:3E обеспечивает
полное выполнение подпрограммы BIOS.
Обратите внимание, что
программа вывела на дисплей символ
"Э". Вызванная по прерыванию 10H
подпрограмма BIOS выводит символы на
дисплей. В данном случае это
первый символ выводимого сообщения. Так
как теперь можно быть
уверенными, что наша программа выполняется
правильно, то, введя
символ "G", мы обеспечим
выполнение программы до конца без точек
прерывания.
В данном примере рассматривался файл типа
.EXE, и потому для
возврата управления системе DOS мы не
могли использовать прерывание
INT 20H. Вместо этого программа записала в
стек состояние регистра
DS и значение 0. Управление передается
обратно системе DOS в конце
основной программы с помощью команды
возврата типа FAR. Программа
DEBUG распознает это и фиксирует состояние
машины в конце
тестируемой программы. Если бы это был
файл типа .EXE, то
прерывание INT 20H аналогичным образом
вернуло бы управление
программе DEBUG. Теперь, уделив достаточно
времени этому примеру,
мы можем выйти из программы DEBUG и
вернуться в систему DOS с
помощью команды завершения Q.
|