Программа 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:DEBUG FIG5_13.EXE
-R
AX=0000 BX=0000 CX=0120 DX=0000 SP=FFF0 BP=0000 SI=0000 DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=2C26 IP=0000 NV UP DI PL NZ NA PO NC
2C26:0000 1E PUSH DS
-U
2C26:0000 1E PUSH DS
2C26:0001 B80000 MOV AX,0000
2C26:0004 50 PUSH AX
2C26:0005 FC CLD
2C26:0006 8CC8 MOV AX,CS
2C26:0008 8ED8 MOV DS,AX
2C26:000A 8D361D00 LEA SI,[001D]
2C26:000E AC LODSB
2C26:000F A24000 MOV [0040],AL
2C26:0012 E82C00 CALL 0041
2C26:0015 803E40000A CMP [0040],0A
2C26:001A 75F2 JNZ 000E
2C26:001C CB RET Far
2C26:001D 9D POPF
2C26:001E E2A0 LOOP FFC0
2C26:0020 20AFE0AE AND [BX+AEE0],CH
2C26:0024 A3E0A0 MOV [A0E0],AX
-D2C26:0
2C0E:0000 CD 20 00 A0 00 9A EE FE 1D F0 ED 04 04 1C 3C 01 . ............<.
2C0E:0010 22 1B EB 04 22 1B 04 1C 01 01 01 00 02 06 FF FF "..."...........
2C0E:0020 FF FF FF FF FF FF FF FF FF FF FF FF 08 2C D0 FF .............,..
2C0E:0030 04 1C 14 00 18 00 0E 2C FF FF FF FF 00 00 00 00 .......,........
2C0E:0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2C0E:0050 CD 21 CB 00 00 00 00 00 00 00 00 00 00 20 20 20 .!...........
2C0E:0060 20 20 20 20 20 20 20 20 00 00 00 00 00 20 20 20 .....
2C0E:0070 20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00 ........
-RAX
AX 0000
:1234
-E 2C0E:21
2C0E:0021 69. 73. 20. 61.20 20.
-G 3C
AX=0E54 BX=0000 CX=003F DX=0000 SP=FFEA BP=0000 SI=001D DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=2C26 IP=003C NV UP DI PL NZ NA PO NC
2C26:003C CD10 INT 10
-T
AX=0E54 BX=0000 CX=003F DX=0000 SP=FFEA BP=0000 SI=001D DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=F000 IP=0165 NV UP DI PL NZ NA PO NC
F000:F065 FB STI
-T
Фиг. 5.17 Листинг отладчика для Фиг 5.13 и 4.14 (начало)
AX=0E54 BX=0000 CX=003F DX=0000 SP=FFEA BP=0000 SI=001D DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=F000 IP=0166 NV UP DI PL NZ NA PE NC
F000:F066 FC CLD
-G 2C26:3E
T
AX=0E54 BX=0000 CX=003F DX=0000 SP=FFEA BP=0000 SI=001D DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=2C26 IP=013E NV UP DI PL NZ NA PO NC
2C26:003E RET
-G
Эта программа - тест
Program terminated normally
-R
AX=0754 BX=0000 CX=003F DX=0000 SP=FFEA BP=0000 SI=001D DI=0000
DS=2C26 ES=2C26 SS=2C26 CS=2C26 IP=003E NV UP DI PL NZ NA PO NC
2C26:003E C3 RET
-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, следующий вид:
MOV OUTPUT_CHARACTER,AL
а в дизассемблированном виде
MOV [0030],AL
Это одна и та же команда. Для программиста, выполняющего
отладку, имя переменной OUTPUT_CHARACTER говорит больше, чем адрес
ячейки [0030]. Однако программе DEBUG не известны имена переменных,
и она вынуждена оперировать фактическими адресами.
Кроме того, программа, DEBUG не обеспечивает той же самой
ассемблерной мнемоники, которую воспринимает ассемблер. Это значит,
что некоторые команды будут выглядеть по-разному. Команда из ячейки
04C5:0014 будет при дмзассемблировании иметь вид
CMP B,[3000],0A
но та же саммая команда на Фиг.5.13 представлена в виде:
CMP OUTPUT_CHARACTER,10
Программа дизассемблирования как на входе, так и на выходе
работает только с шестнадцатеричными значениями. Этим объясняется
появление значения 0A. Мы уже выяснили, почему получается значение
[0030], вместо имени OUTPUT_CHARACTER. Что же такое символ "B,"?
Ассемблер оперирует переменными вполне определенного типа. Это
значит, что во время ассемблирования тип переменных может иметь
значение: байт, слово или какой-нибудь другой. Таким образом, когда
программист вводит команду, содержащую ссылку на область памяти, то
ассемблеру известен размер этой области. На программа DEBUG не
имеет представления о длине переменной, записанной по адресу
[0030]. Однако программе дизассемблирования точно известно, что
данная команда пересылает ровно один байт данных, указанных
непосредственно в команде, по адресу [0030]. Таким образом, символ
"B," указывает на то, что непосредственная операция состоит в
пересылке одного байта. Для получения того же самого результата с
помощью ассемблера соответствующая команда должна иметь вид:
CMP BYTE 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.