Assembler / P02
.pdf1
3. Отладчик Debug
3.1. Пример работы с отладчиком.
Мы уже посмотрели примеры работы с отладчиком в предыдущей главе.
Теперь на очень простом примере продемонстрируем типичный сеанс работы с отладчиком.
Предположим, мы хотим изучить работу команды сложения двух целых чи-
сел. Первое слагаемое хранится в слове со смещением 200h (в текущем сегменте данных) — оно будет загружено в регистр AX, второе слагаемое — число 2 —
будет загружено в регистр BX, сумму требуется разместить в AX. Напишем с
этой целью простую программу, содержащую всего три команды.
mov ax,[200h] |
; Поместить в аккумулятор содержимое |
|
; слова по адресу DS:200 |
mov bx,2 |
; Поместить в регистр BX число 2 ( BX = 2) |
add ax,bx |
; Сложить содержимое AX и BX |
|
; и поместить результат в AX ( AX += BX) |
Комментарии отделены от текста программы точкой с запятой. (Рекоменду-
ется каждую фразу комментария начинать с прописной буквы. Если коммента-
рий продолжается на следующей строке, то продолжение фразы начинается со строчной буквы.) В скобках последние две команды записаны с использованием нотации языка Си — так проще запомнить, что первый операнд является при-
емником, а второй — источником. Следует понимать, что на самом деле эти три команды являются только фрагментом программы на языке ассемблера. Если мы собираемся использовать транслятор (например MASM или TASM), то к этим командам нужно добавить еще ряд директив. Однако для отладчика этих команд достаточно. Еще заметим, что вместо последних двух команд можно было написать одну: add ax,2.
Вызовем отладчик. Для этого наберем в командной строке имя утилиты debug
D:\USER\>debug
нажмем Enter и получим приглашение к диалогу — дефис.
1) Ассемблирование текста программы. В отладчике имеется мини-
ассемблер. Он переводит мнемонику команд в их машинные коды. Практически
2
все команды отладчика вызываются набором одной буквы, далее, возможно,
указываются параметры команды. Ассемблирование начинается по команде A (Assemble). Команды можно набирать и большими и малыми буквами. Команда
A имеет параметр: адрес в памяти, начиная с которого будут размещены ма-
шинные коды. (Здесь необходимо уточнение: полный адрес представлен в виде segment:offset — сегмент:смещение. Как правило, мы будем указывать только смещение). Разместим коды команд, начиная со смещения 100h (этот выбор не случаен: ниже смещения 100h что-либо записывать не рекомендуется, пока вы не изучите структуру области от начала сегмента до смещения 100h — она но-
сит название префикс программного сегмента (Program Segment Prefix — PSP)).
-a100
22B6:0100 mov ax,[200] (суффикс h не указываем!)
22B6:0103 mov bx,2
22B6:0106 add ax,bx
22B6:0108 nop
22B6:0109
В ответ на ввод команды a100 отладчик выдает адрес в формате сег-
мент:смещение (на вашей машине сегментная часть адреса может быть другой
— в дальнейшем это не оговаривается) и ожидает ввод команды. Набираем ко-
манду и нажимаем клавишу Enter. Мнемоника команды немедленно переводит-
ся мини-ассемблером в машинный код (чуть ниже мы увидим, как его посмот-
реть) и отладчик выводит следующий свободный адрес — 103. Следовательно,
код команды mov ax,[200] занимает три байта (с адресами 100, 101, 102).
Мы вводим следующую команду и т.д. Завершаем программу командой nop
(нет операции). Удобство ее включения в текст программы станет ясным из дальнейшего. В ответ на адрес 109 нажимаем Enter и получаем приглашение отладчика. Обратите внимание, что все числа в debug интерпретируются как 16-
ричные, поэтому суффикс h не указывается.
Если какая-либо команда будет введена неправильно, отладчик сообщит об этом и выдаст тот же адрес (естественно — он же не смог транслировать коман-
ду):
-a100
22B6:0100 mov ax[200] (пропущена запятая)
3
^ Error
22B6:0100
Код нашей программы занимает байты с 100-го по 108-й, т.е. девять байтов.
2) Дисассемблирование кода (восстановление мнемоники команд по машин-
ному коду).
-u100L9 |
|
|
|
22B6:0100 |
A10002 |
MOV |
AX,[0200] |
22B6:0103 |
BB0200 |
MOV |
BX,0002 |
22B6:0106 |
01D8 |
ADD |
AX,BX |
22B6:0108 |
90 |
NOP |
|
Команда расшифровывается так: дисассемблировать (U — Unassemble),
начиная с 100-го смещения код длиной (L — Length) 9 байтов. (Можно было набрать команду в иной форме: U100 108, т.е. указать начальный и конечный адреса кода).
Полученные в результате строки имеют формат:
сегмент:смещение машинный код мнемоника
В кодах команд можно увидеть коды операндов в "перевернутом виде":
Расшифруем код A10002. A1 — код пересылки содержимого ячейки памяти в аккумулятор, 00 — младший байт и 02 — старший байт операнда-источника.
Аналогично расшифровывается код второй команды. Можно убедиться в том,
что машинные коды имеют разную длину: в нашем примере — 1, 2 и 3 байта.
3) Заполнение памяти. Перед выполнением программы нужно занести в слово памяти, расположенное по адресу DS:200, число 1. Можно было добавить это действие в нашу программу в качестве первой команды (чуть позже мы узнаем, как это сделать), но мы воспользуемся средствами отладчика. Команда
E (Enter — ввод) позволяет занести в память новое содержимое (здесь то, что выводит debug, подчеркнуто):
-e200
22B6:0200 4D.01 E2.00
(нажимаем пробельную клавишу)
Итак, мы вводим команду E и адрес. Отладчик сообщает, что в байте по ад-
ресу 200 записано число 4Dh, точка — приглашение к вводу. Набираем 01 (можно и без ведущего нуля) и нажимаем пробельную клавишу, чтобы получить
4
содержимое следующего байта (если бы старое значение байта нас устраивало,
мы сразу нажали бы пробел). По адресу 201 записано число 0E2h — заносим нуль. Завершаем команду E, нажимая Enter.
Команду E можно было ввести и в другой форме, указывая в одной строке адрес и список вводимых байтов (т.е. на этот раз мы не видим прежнее содер-
жимое ячеек памяти):
-e200 1 0
Проверим результат занесения уже знакомой командой
-d200L2
22B6:0200 01 00
Записано слово 0001 (младший байт 01, старший байт 00).
Для ввода слова проще воспользоваться уже знакомой командой A:
-a200
22B6:0200 dw 1 (Define Word — задать слово)
Здесь есть одна тонкость. Команда E offset заносит данные по адресу
DS:offset, а команда A offset — по адресу CS:offset. Но сейчас содержимое сег-
ментных регистров CS и DS совпадает.
4) Содержимое регистров. Перед тем как выполнять программу посмотрим
содержимое регистров центрального процессора. Введем команду R (Register):
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0100 NV UP EI PL NZ NA PO NC 22B6:0100 A10002 MOV AX,[0200] DS:0200=0001
Отладчик вывел три строки. В первой строке перечислены регистры общего назначения и показано их содержимое. Начальное содержимое регистров — нулевое. Только указатель стека SP содержит FFEEh, т.е. показывает на адрес,
близкий к концу сегмента. Во второй строке присутствуют сегментные реги-
стры DS, ES, SS, CS — в них хранится одинаковый сегментный адрес; счетчик команд IP содержит смещение 100h; далее перечислены текущие значения флажков в регистре флагов. Отдельные биты этого регистра устанавливаются
(1) или сбрасываются (0) по результату выполнения команд. Но отладчик пока-
зывает не содержимое регистра флагов в шестнадцатеричном представлении, а
5
значения битов в закодированной форме в виде двухбуквенных сокращений.
Сведем их в таблицу 3.1.
Таблица 3.1. Кодирование значений флагов в debug.
Флаг |
Наименование |
Установлен (‘1’) |
Сброшен (‘0’) |
OF |
переполнение |
OV (OVerflow) |
NV (Non oVerflow) |
|
|
Есть знаковое переполнение |
Нет знакового переполнения |
DF |
направление |
DN (DowN) |
UP |
|
|
автоуменьшение указателя при |
автоувеличение указателя при |
|
|
выполнении строковых команд |
выполнении строковых команд |
IF |
прерывание |
EI (Enable Interrupt) |
DI (Disable Interrupt) |
|
|
внешние прерывания разрешены |
внешние прерывания запрещены |
SF |
знак |
NG (NeGativ) |
PL (PLus) |
|
|
результат отрицательный |
результат неотрицательный |
ZF |
ноль |
ZR (ZeRo) |
NZ (Non Zero) |
|
|
результат нулевой |
результат ненулевой |
AF |
вспомогательный |
AC (Adjust Carry) |
NA (Non Adjust carry) |
|
перенос |
произошел перенос из младшей |
переноса из младшей тетрады не |
|
|
тетрады |
было |
PF |
четность |
PE (Parity Even) |
PO (Parity Odd) |
|
|
сумма битов младшего байта чет- |
сумма битов — нечетная |
|
|
ная |
|
CF |
перенос |
CY (Carry Yes) |
NC (Non Carry) |
|
|
установлен флаг переноса |
флаг переноса сброшен |
В третьей строке представлены адрес, код и мнемоника команды, которая будет выполняться первой при запуске на выполнение. Ее сегментный адрес хранится в CS, а смещение в IP (именно этой информацией воспользовался от-
ладчик, чтобы отобразить команду). Кроме того, в третьей строке показано со-
держимое ячейки памяти, на которую ссылается один из операндов команды.
5) Выполнение программы "по шагам". В этом режиме после каждой коман-
ды выполнение будет приостанавливаться. Для этого вводим команду T (Trace
— трассировка). Выполняется команда, адрес которой содержится в IP, и пока-
зывается информация, как при подаче команды R.
-t
AX=0001 |
BX=0000 |
CX=0000 |
DX=0000 |
SP=FFEE BP=0000 SI=0000 DI=0000 |
|
DS=22B6 |
ES=22B6 |
SS=22B6 |
CS=22B6 |
IP=0103 NV UP EI PL NZ NA PO NC |
|
22B6:0103 |
BB0200 |
MOV BX,0002 |
Обратите внимание, что изменилось содержимое только двух регистров:
AX — в него загружено значение 1, и IP — в нем адрес следующей выполняе-
мой команды. Сама команда показана в третьей строке. Флаги не изменились.
6
-t
AX=0001 BX=0002 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0106 NV UP EI PL NZ NA PO NC
22B6:0106 |
01D8 |
ADD AX,BX |
-t |
|
|
AX=0003 BX=0002 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 |
||
DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0108 NV UP EI PL NZ NA PE NC |
||
22B6:0108 |
90 |
NOP |
Только теперь (после команды add ax,bx) изменилось содержимое реги-
стра флагов. Правда это коснулось только одного флага PF (в младшем байте результата (03h=00000011b) установлено два бита, то есть четное число битов).
6) Внесение изменений в программу: в регистр BX запишем –1. Заменим вторую команду нашей программы.
-a103
22B6:0103 mov bx,-1
22B6:0106
Проверим результат изменений (на всякий случай, хотя и так видно, что
длина команды не изменилась):
-u100L9 |
|
|
22B6:0100 A10002 |
MOV AX,[0200] |
|
22B6:0103 BBFFFF |
MOV BX,FFFF |
(дополнительный код числа –1) |
22B6:0106 01D8 |
ADD AX,BX |
|
22B6:0108 90 |
NOP |
|
Вновь выполним трассировку. Но сейчас в IP содержится 108h, поэтому первая команда трассировки выглядит иначе, чем раньше: после знака равенства указываем стартовый адрес.
-t=100
AX=0001 BX=0002 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0103 NV UP EI PL NZ NA PE NC 22B6:0103 BBFEFF MOV BX,FFFE
-t
.............
7) Изменение содержимого регистров. Если наша цель — изучение коман-
ды add, то проще изменять значения регистров средствами отладчика непо-
средственно перед выполнением команды add. Вновь воспользуемся командой
R, но теперь укажем имя регистра:
7
-rax
AX 0000
:-2
^ Error
Отладчик показывает содержимое AX и запрашивает новое содержимое.
Вводим число –2. Отладчик воспримет это как ошибку. Нужно сначала полу-
чить дополнительный код –2. Его можно узнать с помощью отладчика. Команда
H <1-е число> <2-е число> вычисляет сумму и разность аргументов-слов:
-h0 2
0002 FFFE (т.е. 0 + 2 = 0002 и 0 – 2 = 0FFFEh)
Теперь введем в AX дополнительный код числа –2.
-rax
AX 0000
:fffe
Изменим BX
-rbx
BX FFFE
:3
А теперь выполним трассировку только команды сложения.
-t=106
AX=0001 BX=0003 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0108 NV UP EI PL NZ AC PO CY 22B6:0108 90 NOP
8) Выполнение всей программы:
-g=100 108
AX=0000 BX=FFFF CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0108 NV UP EI PL ZR AC PE CY 22B6:0108 90 NOP
Итак, использована команда G (Go — прогон), далее после знака равенства указываем стартовый адрес, затем адрес точки останова (теперь мы видим поль-
зу команды nop — именно ее адрес служит адресом точки останова). Заметим,
что команда g=100 приведет скорее всего к зависанию программы (лучше не проверять это!). Действительно, после наших четырех команд в оперативной памяти хранится "мусор", который будет интерпретироваться процессором как коды команд.
8
9) Сохранение файла с программой на диске. Для этого выполним последо-
вательность действий:
а) дадим файлу имя pr1.com. Введем команду N (Name — имя)
-npr1.com
(отладчик создает исполняемые файлы только типа .com — не .exe!)
б) в регистры BX и CX занесем длину программы в байтах. BX:CX — длин-
ное беззнаковое целое: в BX — старшие разряды, в CX — младшие разряды.
Типичная ошибка: забывают занести в BX нуль. В результате на диске образует-
ся очень большой файл.
-rbx
BX FFFF :0
-rcx CX 0000 :9
Еще раз напомним, что диапазон адресов 100 – 108 имеет длину 9 байтов.
в) введем команду записи на диск W (Write — писать).
-w
Writing 00009 bytes
Отладчик сообщил количество записанных байтов.
10) Выход из отладчика. Вводим команду Q (Quit — мы с отладчиком "кви-
ты")
-q
D:\USER\>_
Убедитесь, что в текущем каталоге действительно имеется файл pr1.com
размера 9 байтов. Запускать его на выполнение из командной строки бессмыс-
ленно по двум причинам:
программа не отображает результаты работы на экране, проследить ее работу можно только средствами отладчика;
в программе нет команд, обеспечивающих возврат в DOS, поэтому после наших четырех команд начнет выполняться "мусор" с непредсказуемыми последствиями.
9
11) Отладка программы, хранящейся в файле. Мы хотим еще поработать с нашей программой, записанной в файле pr1.com. Начать работу можно двумя способами.
а) Указать имя файла в командной строке при запуске debug:
D:\USER\> debug pr1.com
-r
AX=0000 BX=0000 CX=0009 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=22B6 ES=22B6 SS=22B6 CS=22B6 IP=0100 NV UP EI PL NZ NA PO NC 22B6:0100 B80100 MOV AX,0001
В BX:CX записан размер считанной программы.
б) Загрузить нужный файл в оперативную память с помощью команд debug:
D:\USER\> debug
-n pr1.com (назовем имя)
-L (Load — загрузка содержимого файла в оперативную память)
Программа считана с диска. С ней можно работать. После внесения измене-
ний в программу может возникнуть желание сохранить новый вариант про-
граммы на диске. Для записи на диск достаточно установить нужные BX:CX и
ввести команду W, т.к. имя файла уже задано.
Теперь, после того как мы получили представление о возможностях отлад-
чика и типовых "связках" команд, дадим систематическое изложение. Отладчи-
ки, которые мы будем изучать позже, не будут сопровождаться столь подроб-
ным изложением, т.к. для них можно получить информацию в Справке (Help)
программного продукта.
3.2. Параметры команд отладчика.
Запуск отладчика возможен в двух вариантах:
1)D:\USER\> debug
2)D:\USER\> debug <имя файла>
При втором варианте запуска отладчик загружает содержимое файла в ниж-
ний доступный сегмент (при этом файл с расширением .com загружается в этом сегменте со смещением 100h).
После запуска debug на выполнение пользователь получает приглашение (-)
и может вводить команды. Формат команды:
10
буква [параметр(ы)] Здесь буква — первая буква команды, например ко-
манда F — (Fill —заполнять). параметр(ы) — перечень параметров, которые могут быть разделены пробелами или запятыми. Разделитель обязателен между последовательностью шестнадцатеричных величин. Буквы могут быть пропис-
ными и строчными.
Перечислим типы параметров: величина — от одной до четырех шестна-
дцатеричных цифр (суффикс h не указывается); байт — две шестнадцатеричные цифры; адрес — можно задать тремя способами:
1)сегмент:смещение, например, 057D:0020, или 57D:20 (ведущие нули можно не указывать);
2)сегментный регистр:смещение, например, cs:109
3)смещение. Если команда работает с машинными инструкциями (команды
A,G,L,T,U,W), подразумевается сегментный регистр CS, если работа идет с
данными — DS.
область — диапазон адресов; его можно задать двумя способами
1)адрес смещение, например, cs:100 110 (Неверно: cs:100 cs:110 — второй параметр не является смещением).
2)адресLвеличина, где величина — размер области в байтах, например
cs:100L11 задает точно такую же область длиной 17 байтов, как и в предыдущем примере.
строка — цепочка символов, заключенная в одинарные или двойные ка-
вычки (если кавычки встречаются внутри строки, их следует удваивать).
Примеры: 'Эта ''строка'' правильная'
"this ""string"" is okay"
список — последовательность байтов и/или строк.
пример: "Hello!",d,a,'$'
3.3. Команды отладчика Сгруппируем команды по их функциональному назначению. 1) Работа с данными.
1.1) D [область] |
Dump — дамп |
Эта команда бала разобрана ранее.