При сложении каких чисел debug сообщить об ошибке

Debug — Программа-отладчик, которую используют для проверки и отладки
выполняемых файлов. Использовалась при операционной системе MS-DOS.
Под более поздние версии операционных систем работает через эмулятор MS-DOS и имеет
ограниченные возможности.

Данная программа является консольным приложением и предназначена для создания или
изменения кода файлов. С помощью неё можно создавать простые приложение под MS-DOS и
отслеживать их работу. Данный отладчик находится на самом низком уровне компиляторов assembler.
Но обладает неплохими возможностями такими как просмотр, изменение памяти и получение
состояния регистров.

Быстрый старт

Откройте окно командной строки, введите команду debug и нажмите
клавишу [Enter]. В результате будет запущен отладчик debug в интерактивном режиме. Дефис и
мигающий курсор означают, что отладчик ждет ввода команды. Одна из наиболее полезных возможностей
отладчика состоит в том, что с его помощью вы можете вводить небольшие фрагменты кода на ассемблере
и проверять их работу. Проиллюстрируем эти возможности на простом примере.
Введите букву a (на латинице) и нажмите клавишу [Enter]. Эта команда переводит отладчик
в режим ввода команд ассемблера. Каждая команда ассемблера записывается в одну строку
и заканчивается нажатием клавиши [Enter]. Чтобы закончить ввод ассемблерных команд
следует на новой пустой строке еще раз нажать клавишу [Enter].
Введите три строчки кода, представленного на рисунке.

Поясним что делает эта программа. Первая строка кода помещает число 2 в регистр ax.
Ассемблерная команда mov (от английского move — перемещать) копирует содержимое
второго операнда в первый операнд. Вторая строка кода аналогичным образом помещает
число 3 в регистр bx. И, наконец, третья строчка кода, использующая команду add
складывает содержимое регистров ax и bx и помещает результирующую сумму в регистр ax.

Отладчик позволяет достаточно просто и эффективно понаблюдать за работой программы.
Введите букву r и нажмите клавишу [Enter]. В результате появится информация о содержимом
регистров процессора. Поскольку в нашей программе используются только регистры ax и bx
сосредоточим свое внимание на их содержимом. Поскольку программа еще не запускалась
на исполнение, видим, что содержимое регистров равно нулю. Чтобы понять как работает
программа удобно использовать пошаговый режим отладки, когда на каждом шагу выполняется
одна строчка программы. Чтобы выполнить один шаг нужно ввести букву t и
нажать клавишу [Enter]. Видим, что в результате выполнения первой строки программы
содержимое регистра ax стало равным 2. Аналогичным образом можно выполнить оставшиеся
две строчки и пронаблюдать за результатом их исполнения.

Для более полного понимания работы микропроцессора рекомендуется обратить внимание
на содержимое других регистров, а также на третью строку выводимой информации.

Регистр IP (Instruction Pointer — указатель команды) всегда содержит адрес
следующей выполняемой команды. Обратите внимание что в самом начале содержимое
данного регистра равно 100 и указывает на адрес первой команды, т.к. вся программа
располагается в памяти, начиная именно с этого адреса. Кстати, адреса вводимых
команд указываются слева в тех строках где эти команды вводятся. Эти адреса
представляются в виде двух чисел разделенных двоеточием. Первое число определяет
так называемый сегментный адрес, а второе число смещение адреса от начала сегмента.
Заметьте, что на каждом следующем шаге отладки значение регистра IP увеличивается
таким образом, чтобы указывать на следующую команду программы. Заметьте также,
что третья выводимая строка содержит информацию о следующей выполняемой команде.
Эта информация включает в себя машинный адрес данной команды, саму команду на
машинном языке, т.е. в виде шестнадцатеричного числа, и ассемблерное представление
команды. Необходимо также обратить внимание, что содержимое всех сегментных
регистров DS, ES, SS и CS равно сегментному адресу программы.

Рекомендуется рассмотреть аналогичным образом ряд других команд ассемблера. Это позволит
не только узнать как применяются ассемблерные команды, но и освоить работу с отладчиком.

Арифметические команды

Команда сложения ADD имеет следующий формат

ADD приемник, источник

и может складывать как 8-, так и 16-битовые операнды.

Команда ADD складывает содержимое операнда-источника и операнда-приемника
и помещает результат в операнд-приемник. Ее действие можно описать как

приемник = приемник + источник

Команда ADD может воздействовать на шесть флагов:

Флаг переноса CF равен 1, если результат сложения не помещается в операнде-приемнике;
в противном случае он равен нулю.

Флаг четности PF равен 1, если результат имеет четное число битов со значением 1;
в противном случае он равен нулю.

Вспомогательный флаг переноса AF равен 1, если результат сложения десятичных чисел
требует коррекции; в противном случае он равен нулю.

Флаг нуля ZF равен 1, если результат равен 0; в противном случае он равен нулю.

Флаг знака SF равен 1, если результат отрицателен (старший бит равен 1) ;
в противном случае он равен нулю.

Флаг переполнения OF равен 1, если сложение двух чисел одного знака
(оба положительные или оба отрицательные) приводит к результату,
который превышает диапазон допустимых значений приемника в обратном коде,
а сам приемник при этом меняет знак. В противном случае он равен нулю.

Флаги SF и OF имеют смысл только при сложении чисел со знаком,
а флаг AF – только при сложении десятичных чисел.

Команда

INC приемник

добавляет 1 к содержимому регистра или ячейки памяти,
но в отличие от команды ADD не воздействует на флаг переноса CF.

Команда вычитания SUB имеет следующий формат

SUB приемник, источник

Команда SUB вычитает операнд-источник из операнда-приемника
и возвращает результат в операнд-приемник, т. е.

приемник = приемник – источник

Команда SUB может воздействовать на шесть флагов:

Флаг переноса CF равен 1, если требуется заем; в противном случае он равен нулю.

Флаг четности PF равен 1, если результат имеет четное число битов со значением 1;
в противном случае он равен нулю.

Вспомогательный флаг переноса AF равен 1, если результат вычитания
десятичных чисел требует коррекции; в противном случае он равен нулю.

Флаг нуля ZF равен 1, если результат равен 0; в противном случае он равен нулю.

Флаг знака SF равен 1, если результат отрицателен (старший бит равен 1) ;
в противном случае он равен нулю.

Флаг переполнения OF равен 1, если при вычитании чисел имеющих разные знаки,
результат превышает диапазон допустимых значений приемника в обратном коде,
а сам приемник при этом меняет знак. В противном случае он равен нулю.

Флаги SF и OF имеют смысл только при вычитании чисел со знаком,
а флаг AF – только при вычитании десятичных чисел.

Команда

DEC приемник

вычитает 1 из содержимого регистра или ячейки памяти, но при этом
(в отличие от команды SUB) не воздействует на флаг переноса CF.

Команда

NEG приемник

вычитает значение операнда-приемника из нулевого значения и
тем самым формирует его дополнение до двух. Команда NEG оказывает
на флаги то же действие, что и команда SUB.

Команда

CMP приемник, источник

вычитает операнд-источник из операнда-приемника и в зависимости
от результата устанавливает или обнуляет флаги.
Но в отличие от команды SUB команда CMP не сохраняет результат вычитания.
Таким образом команда CMP не изменяет операнды.
Она целиком предназначена для установки значений флагов,
на основании которых команды условного перехода будут принимать решение о передаче управления.

Команда

MUL источник

умножает числа без знака и может умножать как байты, так и слова.

Источник это регистр общего назначения или ячейка памяти размером в байт или слово.
В качестве второго операнда команда MUL использует содержимое регистра AL
(при операциях над байтами ) или регистра AX (при операциях над словами ).
Произведение имеет двойной размер и возвращается следующим образом:

Умножение байтов возвращает 16-битовое произведение в регистрах
AH ( старший байт ) и AL (младший байт ).

Умножение слов возвращает 32-битовое произведение в регистрах
DX ( старшее слово ) и AX (младшее слово ).

По завершении исполнения этих команд флаги переноса CF и
переполнения OF показывают, какая часть произведения существенна
для последующих операций. После исполнения команды MUL флаги CF и OF равны 0,
если старшая половина произведения равна 0; в противном случае оба этих флага равны 1.

Команда MUL не позволяет в качестве операнда использовать непосредственное значение.
Такое значение перед умножением надо загрузить в регистр или в ячейку памяти.

Команда

DIV источник

выполняет деление чисел без знака.

Источник это делитель размером в байт или слово, находящийся в регистре общего назначения или в ячейке памяти.

Делимое должно иметь двойной размер; оно извлекается из регистров
AH и AL ( при делении на 8-битовое число ) или из регистров DX и AX ( при делении на 16-битовое число ).
Результаты возвращаются следующим образом:

Если операнд-источник представляет собой байт, то частное возвращается в регистре AL, а остаток в регистре AH.

Если операнд-источник представляет собой слово, то частное возвращается в регистре AX, а остаток в регистре DX.

Команда оставляет состояние флагов неопределенным, но если частное не помещается
в регистре-приемнике ( AL или AX ), то процессор генерирует прерывание типа 0 ( деление на 0 ).

Hosted by uCoz

SlowAR

0 / 0 / 0

Регистрация: 30.04.2014

Сообщений: 48

1

20.05.2014, 06:53. Показов 2672. Ответов 3

Метки нет (Все метки)


Здравствуйте! Мне нужно найти сумму элементов массива с 3 по 5 элемент и вывести ее в четвертый. Все элементы массива равны числу 12223278h. Я написал код, но он правильно выводит только первую половину числа:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
MOV BX,200
MOV CX,3
MOV AX,[BX]
MOV DX,[BX+2]
ADD AX,[BX+4]
ADC DX,[BX+6]
ADD BX,4
INC CX
CMP CX,5
JNE 10B
MOV BX,200
MOV [BX+C],AX
MOV [BX+E],DX

В чем может быть проблема? Заранее спасибо!

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь

0

Ушел с форума

Автор FAQ

15552 / 7371 / 977

Регистрация: 11.11.2010

Сообщений: 13,308

20.05.2014, 08:19

2

Цитата
Сообщение от SlowAR
Посмотреть сообщение

найти сумму элементов массива с 3 по 5 элемент и вывести ее в четвертый

а размер элементов массива? байт? слово? двойное слово? и войдет ли сумма трех элементов в один элемент? Под сумму обычно отводят в два раза больше, то есть сумма байтов = слово, сумма слов = двойное слово и т.д.

0

0 / 0 / 0

Регистрация: 30.04.2014

Сообщений: 48

20.05.2014, 08:27

 [ТС]

3

Mikl___,
ну каждый элемент массива заполнен числом 12223278h. а вот формулировка самого задания: Вычислить сумму элементов массива с 3 по 5 и переслать результат в элемент массива с номером 4. Больше ничего не написано. должно получиться число: 36669769h, а получается: 36666900h.

0

Mikl___

Ушел с форума

Автор FAQ

15552 / 7371 / 977

Регистрация: 11.11.2010

Сообщений: 13,308

20.05.2014, 11:04

4

SlowAR,
а ты предложи своему преподавателю сложить три числа равных 0FFFFh = 65535, а потом пусть он попробует число 2FFFDh (=3*65535) поместить в два байта. Это у тебя известно, что в ячейках числа =12223278h (тогда и их сумма известна), но обычно величина хранящаяся в ячейках массива неизвестна, поэтому предполагается что там максимальное число. Но хозяин — барин, поэтому внимательно читаем задание «каждый элемент массива заполнен числом 12223278h» то есть каждый элемент по 4 байта «Вычислить сумму элементов массива с 3 по 5 и переслать результат» число 12223278h*3=36669768h «в четвертый»

Assembler
1
2
3
4
5
6
7
8
9
MOV BX,200 ;начало массива (адрес первого элемента)
MOV AX,[BX+8]; содержимое 3-его элемента 
MOV CX,[BX+0Ah]
ADD [BX+0Ch],AX; сложили содержимое 4-ого и 3-его элемента
ADC [BX+0Dh],CX
MOV AX,[BX+10h]; содержимое 5-oго элемента 
MOV CX,[BX+12h]
ADD [BX+0Ch],AX; сложили содержимое 4-ого и 5-oго элемента
ADC [BX+0Dh],CX

2

IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

20.05.2014, 11:04

4

2. Отладчик Debug
2.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) Ассемблирование текста программы. В отладчике имеется мини-ассемблер. Он переводит мнемонику команд в их машинные коды. Практически все команды отладчика вызываются набором одной буквы, далее, возможно, указываются параметры команды. Ассемблирование начинается по команде 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] (пропущена запятая)
^ 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 (можно и без ведущего нуля) и нажимаем пробельную клавишу, чтобы полу-чить содержимое следующего байта (если бы старое значение байта нас устраи-вало, мы сразу нажали бы пробел). По адресу 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) по результату выполнения команд. Но отладчик пока-зывает не содержимое регистра флагов в шестнадцатеричном представлении, а значения битов в закодированной форме в виде двухбуквенных сокращений. Сведем их в таблицу 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 — в нем адрес следующей выполняе-мой команды. Сама команда показана в третьей строке. Флаги не изменились.
-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, но теперь укажем имя регистра:
-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 приведет скорее всего к зависанию программы (лучше не проверять это!). Действительно, после наших четырех команд в опе-ративной памяти хранится «мусор», который будет интерпретироваться процес-сором как коды команд.
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, поэтому после наших четырех команд начнет выполняться «мусор» с непредсказуемыми последствиями.
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) программного продукта.

2.2. Параметры команд отладчика.
Запуск отладчика возможен в двух вариантах:
1) D:USER> debug
2) D:USER> debug <имя файла>
При втором варианте запуска отладчик загружает содержимое файла в нижний доступный сегмент (при этом файл с расширением .com загружается в этом сегменте со смещением 100h).
После запуска debug на выполнение пользователь получает приглашение (-) и может вводить команды. Формат команды:
буква [параметр(ы)] Здесь буква — первая буква команды, например ко-манда 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 — дамп
Эта команда бала разобрана ранее.
1.2) E адрес [список] Enter — ввод
Ввод данных по указанному адресу.
а) E адрес (список опущен)
-e200
1C36:0200 01.02 12. 1B.0
вводим пробел для перехода к следующему байту
Результат:
-d200L3
1C36:0200 02 12 00
Итак, при нажатии пробельной клавиши отладчик выводит очередной байт и запрашивает его изменение, выводя точку. Для перехода к предыдущему бай-ту нужно ввести дефис (попробуйте!).
Упражнение. С помощью команды E можно просматривать память. По-смотрите, что будет происходить, если вы подадите команду efff0 и несколь-ко раз нажмете пробельную клавишу. Перейдете ли Вы границу сегмента? А что произойдет, если вы наберете команду dfff0?
б) E адрес список
e200 02 12 00
Результат тот же, что и в предыдущем примере, но на этот раз замена дан-ных ведется «вслепую».
Слова приходится заносить в память побайтно. Байты заносятся в обратном порядке. Например, для ввода по адресу (смещению) 120 слова 1234 подаем команду:
-e120 34 12
Пример ввода строки символов:
-e130 string for example,D,A,$
(0Dh — код возврата каретки, 0Ah — код перевода строки. $ — терминатор (ограничитель) строки, который нужен при использовании системной функции вывода строки на экран. Эту функцию мы изучим позднее.)
1.3) R [имя регистра] Register — регистр
а) R (без параметра) — выводятся имена всех регистров и их содержимое. Последняя строка вывода имеет формат:
адрес код команды мнемоника команды [содержимое памяти]
1С36:0100 A10002 MOV AX,[0200] DS:0200=01E4
б) R имя регистра — debug выводит содержимое регистра, а на следую-щей строке помещает двоеточие — приглашение к вводу нового значения. К сожалению, можно указывать только 16-разрядные регистры.
-rbx
1107
: 1 (вводим новое содержимое регистра)
<Enter>
-rah
br ERROR (недопустимое имя регистра)
в) RF выводит содержимое регистра флагов
NV UP DI NG NZ AC PE NC —
После дефиса можно ввести новые значения флагов: — PLCY (если задано более чем один код значения для флага, появляется сообщение об ошибке df error если задан код значения, отсутствующий в списке — сообщение bf error).
1.4) F область список Fill—заполнение
Если размер области превосходит длину списка, то список используется повторно, пока область не будет заполнена. Остаток списка усекается.
Пример. Перед созданием программы в отладчике полезно заполнить уча-сток памяти кодом команды nop (90h) (если это не сделать, «мусор» в памяти сбивает с толку начинающего пользователя).
-f100L100 90
Упражнение. Выполнить f400L10 1 2 3 и изучить результат.

2) Работа с кодами команд.
2.1) A [адрес] Assemble — ассемблировать
Пример работы этой команды мы уже видели. Если в начале сеанса работы с debug не указывать адрес, то ассемблирование начнется с адреса XXXX:0100.
Отметим, что с использованием мини-ассемблера заполнение памяти дан-ными осуществить даже проще, чем с помощью команды E:
-a200
1C36:0200 dw ffd0,12,a1c7
1C36:0206

(В результате [200]=FFD0h, [202]=0012h, [204]=A1C7h.)
-a200
1C36:0200 db «String»,D,A,$
1C36:0209
Здесь использованы директивы dw (Define Word — задать слово) и db (Define Byte — задать байт).
2.2) U [область] Unassemble — дисассемблировать
Команда переводит коды из области памяти в мнемонику команд.
Если перевод невозможен, то выводится ???? или DB число. Если параметр область опущен, то команда дисассемблирует 20 байт, начиная с текущего значения IP. Вновь введем U — следующие 20 байт и т.д. (аналогично команде D без параметров).
3) Выполнение программы.
3.1) T[=стартовый адрес] [количество повторений] Trace — трассировка
После выполнения каждой команды выводится информация, как при пода-че команды R. Приведем примеры:
а) t — выполнить одну команду, адрес которой в CS:IP;
б) t5 — выполнить 5 команд, начиная с текущего адреса;
в) t=106 8 — выполнить 8 команд, начиная с адреса CS:106.
3.2) G[=стартовый адрес] [адрес останова] Go — прогон
а) g — выполнить программу, начиная с адреса, хранящегося в IP. Про-грамма должна заканчиваться инструкциями завершения (эти инструкции мы изучим позднее — сейчас упоминаем о них только в справочных целях), на-пример, INT 20h или MOV AH,4Ch /INT 21h. После выполнения INT 20h debug выводит сообщение:
Program terminated normally (Выполнение программы завершено) Теперь повторный запуск программы возможен только после выполнения команды L (см. ниже).
б) g=100 адрес команды nop
Мы рекомендуем на начальном этапе обучения завершать учебные про-граммы, набираемые в debug, командой nop и использовать ее адрес в качестве адреса останова.
4) Работа с файлами и дисками.
4.1) N имя файла Name — имя
Задание имени файла. Расширение указывать обязательно. Используется перед следующими двумя командами.
4.2) L [адрес] Load — загрузка
Содержимое файла переписывается в ОЗУ. Файлы с любым расширением кроме .exe — начиная с указанного адреса (если адрес опущен, то начиная с CS:0100). Для файлов с расширением .exe адрес игнорируется, используется стартовый адрес, записанный в самом файле.
По окончании загрузки в BX:CX содержится размер файла в байтах.
4.3) W [адрес] Write — запись
Запись файла на диск. Предварительно в BX:CX нужно записать размер файла. Если адрес не указан, то подразумевается CS:0100.
У команд L и W имеются также параметры для работы с секторами диска. Мы их не рассматриваем.
5) Сервис.
5.1) H <величина> <величина> Hex — шестнадцатеричный
Встроенный калькулятор. Вычисляет сумму и разность указанных величин:
H a b (a и b — слова)
a+b a-b
Примеры: 1) Программа начинается по адресу 0100h и заканчивается по ад-ресу 012Ah. Определить размер программы.
-h12a ff
0229 002B (Размер программы 2Bh)
2) В байте записано число FEh. Дополнительным кодом какого числа оно явля-ется? Расширяем знак до размеров слова
-H0 fffe
fffe 0002 (Дополнительный код ¬–2 есть FEh)
5.2) M область-источник адрес_приемника Move — перемещать
Пример. Предположим, мы набрали программу
-a100
11CF:0100 mov ax,1
11CF:0103 add ax,bx
11CF:0105 nop
11CF:0106
и обнаружили, что пропустили команду mov bx,2. Перемещаем часть про-граммы «подальше». Размер области берем «с запасом».
-m103L20 200
Вводим недостающую команду
-a103
11CF:0103 mov bx,2
11CF:0106
Теперь мы знаем адрес назначения (0106) и перемещаем хвост программы
-m200L20 106
-u100 (проверяем правильность результата) …
Упражнение. Разработайте последовательность действий, если нужно уда-лить часть команд программы. О «перекрытии» области-приемника и области-источника беспокоиться не нужно (проверьте!).
5.3) C область1 адрес_области Compare — сравнение
Выводится таблица в формате
адрес1 байт1 байт2 адрес2
в том случае, если байт1 не совпадает с байт2.
Упражнение. Придумайте пример. С помощью команды E введите данные в две области памяти (например, две незначительно различающиеся строки) и сравните содержимое областей с помощью команды C.
5.4) S область список Search — поиск
Список — это список байтов или строка. Debug выдает все адреса байтов или строк, которые совпадают с указанными.
Упражнение. Придумайте примеры.
6) Остальные команды.
6.1) Q Quit — завершение
Осуществляется выход из debug. При этом набранные программы и данные теряются, если они предварительно не были сохранены посредством команды W.
6.2) ? Выводит на экран алфавитный список команд debug.

3.5. Как получить текст программы, набранной в debug.
После отладки внесения необходимых изменений в команды программы хотелось бы получить файл с текстом программы, чтобы добавить в него ком-ментарии, распечатать и т.д. Можно рекомендовать следующую последова-тельность действий.
1) Определяем размер программного кода. Если вся программа состоит из чистого кода, как pr1.com, даем команды
D:USER>debug pr1.com
-r
и читаем в регистрах BX:CX шестнадцатеричный размер программы. (В нашем случае 9h).
2) Создадим с помощью любого текстового редактора файл (например, pr1.cmd), содержащий команды отладчика:
U100L9
Q
(последнюю команду Q обязательно завершить нажатием Enter). Вновь запуска-ем debug.
D:USER>debug pr1.com < pr1.cmd > pr1.txt
Входная информация поступает из pr1.cmd, выходная направляется в файл pr1.txt. Для этого используются символы > и < (перенаправление операций ввод-вывода).
Остается подправить содержимое файла pr1.txt в редакторе.
Упражнение. В предыдущей главе мы выясняли, какие флаги состояния будут выставлены центральным процессором при выполнении сложения слов: 2345h + 3219h и 5439h + 456Ah. Проверить правильность сделанных выводов, используя debug.

Краткие теоретические сведения.

  1. Форматы
    арифметических данных

  2. Команды
    сложения

  3. Команды
    вычитания

  4. Команда
    обращения знака

  5. Команды
    расширения знака

Форматы
арифметических данных.

Двоичные
числа могут иметь 8 или 16 битов и могут
быть со знаком или без знака. У числа
без знака

все 8 или 16 битов представляют его
значение. Следовательно, двоичные числа
без знака могут принимать значения от
0 до 255 (8-битовые) или до 65535 (16-битовые).
У числа
со знаком

старший бит (7 или 15) указывает его знак,
а остальные биты содержат значение
числа. Следовательно, числа со знаком
могут принимать значения от -128 до 127
(8-битовые) или от -32768 до 32767 (16-битовые).

Десятичные
числа

Микропроцессор
8×86 хранит десятичные числа в виде
последователь­ностей байтов без знака
в упакованном или неупакованном формате.
Каждый байт упакованного десятичного
числа содержит две цифры а двоично-десятичном
коде
BCD
(binary-coded
decimal).
При этом код старшей цифры числа занимает
четыре старших бита байта. Следовательно,
один упакованный десятичный байт может
содержать значения от 00 до 99.

Каждый
байт неупакованного десятичного числа
содержит только один двоич­но-десятичный
код цифры в четырех младших битах.
Следовательно, один неупа­кованный
десятичный байт может содержать лишь
значение от 0 до 9. При умноже­нии и
делении четыре старших бита должны быть
нулевыми, а при сложении или вычитании
их значение несущественно.

Как
же микропроцессор 8×86 узнает, с каким
видом данных он имеет дело? Пусть
требуется сложить два байта. Как он
определяет, какие числа они представ­ляют
(двоичные числа со знаком, двоичные
числа без знака, упакованные деся­тичные
числа или неупакованные десятичные
числа)? На самом деле микропро­цессор
8×86 об этом совершенно не заботится и
трактует все
операнды только как двоичные числа.

Это
хорошо в том случае, когда Ваши операнды
и в самом деле являются двоичными
числами, но если они оказались десятичными,
то результаты, конечно, будут ошибочными.
Для компенсации таких ошибок микропроцессор
8×86 имеет группу команд коррекции, которые
обеспечивают получение правильного
резуль­тата после выполнения операций
над десятичными числами. Эти команды
будут обсуждаться далее.

Хранение
чисел в памяти

Как
уже упоминалось, микропроцессор 8×86
хранит 16-битовые числа в порядке,
противоположном естественному
представлению, а именно он хранит младшие
биты числа в байте с меньшим адресом.
Например, при запоминании числа 1234Н в
ячейке по имени NUM
он размещает 34Н по адресу NUM,
a
12H
— по адресу NUM+1.
При чтении изображения (или дампа)
содержимого памяти учиты­вайте эту
схему свертки байтов. Запомните фразу:
«младший байт — младший адрес, старший
байт — старший адрес».

Команды
сложения.

Команда
сложения
ADD
и команда сложения с добавлением переноса
ADC.

Команды
ADD
(add
— сложить) и ADC
(add
with
carry
— сложить с переносом) могут складывать
как 8-, так и 16-битовые операнды. Команда
ADD
складывает содержимое операнда-источника
и операнда-приемника и помещает результат
в операнд-приемник. В символической
нотации ее действия можно описать как

приемник
= приемник + источник

Команда
ADC
делает то же, что и команда ADD,
но при сложении использует также флаг
переноса CF,
что можно записать следующим образом:

приемник
= приемник + источник + перенос

Перенос
при сложении двоичных чисел аналогичен
переносу при сложении десятичных чисел
в столбик. Например, пои сложении

98

+
13

79

190

возникает
два переноса: сложение единиц вызывает
добавление 2 к десяткам, а сложение
десятков и перенос из столбца единиц
вызывает другой перенос, а именно числа
1 в столбец сотен. Перенос возникает
тогда, когда сумма цифр столбца в нем
не помещается.

Аналогичным
образом возникает перенос, когда ЭВМ
складывает двоичные числа: если сумма
не помещается в операнде-приемнике, то
генерируется перенос. Как известно,
8-битовый регистр может содержать
значения без знака в диапазоне от 0 до
255. Если мы, например, выполним двоичное
сложение чисел 250 и 10, то получим

1111
1010 (двоичное представление числа 250)

+
0000 1010

(двоичное представление числа 10)

1
0000 01000 (ответ: десятичное значение 260)

Результат
верен, но занимает 9 двоичных битов! Если
при выполнении этой опера­ции мы
использовали 8-битовые регистры, то
младшие 8 битов будут занесены в
регистр-приемник, а девятый бит — во флаг
переноса

CF.

Теперь
Вам нетрудно понять, почему микропроцессор
8×86 имеет две разные команды сложения.
Одна из них (ADD)
может складывать значения, представля­емые
байтами или словами, а также младшие
части значений повышенной точнос­ти.
Другая команда (ADC) используется для
сложения старших частей значений
повышенной точности.

Например,
команда

ADD
AX,CX

складывает
16-битовые значения регистров АХ и СХ и
возвращает результат в регистр АХ. Если
Ваши операнды имеют длину более 16 битов,
то можно восполь­зоваться
последовательностью команд вида

ADD
АХ,СХ ; Сначала сложить младшие 16
битов, а затем

ADC
BX,DX
; старшие 16 битов

которая
складывает 32-битовое число, находящееся
в регистрах СХ и DX,
с 32-би­товым числом, находящимся в
регистрах АХ и ВХ. Использованная здесь
команда ADC добавляет к (DX)+(BX)
любой перенос от сложения (СХ)+(АХ).

Вы
можете также добавлять находящийся в
памяти операнд к регистру и наоборот
или добавлять непосредственный операнд
к регистру или операнду, находящемуся
в памяти. Приведем несколько примеров:

ADD
AX,MEM_WORD ;Добавить
значение ячейки памяти к регистру

ADD
MEM_WORD,AX ;или
наоборот

ADD
АL,10 ;Добавить константу к регистру

ADD
MEM_BYTE,OFH ;или
к ячейке памяти

Допускается
большинство возможных комбинаций, но
нельзя добавить значение одной ячейки
памяти к другой или использовать в
качестве приемника непосред­ственное
значение.

Команды
ADD
и ADC
могут воздействовать на шесть флагов:

Флаг
переноса CF
равен 1, если результат сложения не
помещается в операн­де-приемнике; в
противном случае он равен 0.

Флаг
четности PF
равен 1, если результат имеет четное
число битов со значени­ем 1; в противном
случае он равен 0.

Вспомогательный
флаг переноса AF
равен 1, если результат сложения
десятич­ных чисел требует коррекции;
в противном случае он равен 0.

Флаг
нуля ZF
равен 1, если результат равен 0; в противном
случае он равен 0.

Флаг
знака SF
равен 1, если результат отрицателен
(старший бит равен 1); в противном случае
он равен 0.

Флаг
переполнения OF
равен 1, если сложение двух чисел одного
знака (оба положительные или оба
отрицательные) приводит к результату,
который превышает диапазон допустимых
значений приемника в обратном коде, а
сам приемник при этом меняет знак. В
противном случае флаг OF
равен 0.

Флаги
SF и OF
имеют смысл только при сложении чисел
со знаком, а флаг AF
-только при сложении десятичных чисел.

Микропроцессор
8×86 имеет команды, которые проверяют
флаги и на основе результатов проверки
принимают решение о том, куда передать
управление. Например, при отрицательном
результате (SF=1)
должна исполняться одна группа команд,
а при положительном (SF=0)
— другая. Эти команды «принятия
решения» будут обсуждаться ниже.

Коррекция
результата сложения для представления
в кодах
ASCII
и в упакованном десятичном формате
(команды
AAA
и
DAA).

Как
уже упоминалось, при выполнении сложения
микропроцессор 8×86 рассматривает операнды
как двоичные числа. Что же произойдет,
если они будут двоично-десятичными
кодами чисел (кратко десятичными или
BCD-числа­ми)? Разберемся в этом на
примере. При сложении упакованных
BCD-чисел 26 и 55 микропроцессор 8×86 выполнит
следующее двоичное сложение:

00100110
(BCD-число 26)

+01010101
(BCD-число 55)

01111011
(??)

Вместо
правильного значения (BCD-число 81) мы
получим результат, у которого старшая
цифра 7, а младшая — шестнадцатеричная
цифра В. Означает ли это, что нельзя
складывать десятичные числа? Нет, это
означает лишь то, что результат должен
быть скорректирован
для представления в десятичной форме.

Коррекция
результата сложения десятичных чисел
осуществляется командами ААА (ASCII
adjust
for
addition
— скорректировать результат сложения
для представ­ления в кодах ASCII)
и DAA
(Decimal
adjust
for
addition
— скорректировать сложе­ние для
представления в десятичной форме). В
них не требуется наличия операн­да:
предполагается, что корректируемое
значение находится в регистре AL.

Команда
ААА преобразует содержимое регистра
AL в правильную неупакован­ную
десятичную цифру в младших четырех
битах регистра AL (и заполняет нуля­ми
старшие четыре бита). Она используется
в следующем контексте:

ADD
AL,BL
;Сложить неупакованные числа, находящиеся
в AL
и BL

ААА
; и преобразовать результат в
неупакованное число

Если
результат превышает 9, то команда ААА
добавляет 1 к содержимому регист­ра
АН (чтобы учесть избыточную цифру) и
полагает флаг CF
равным 1; в против­ном случае она
обнуляет флаг CF. Кроме того, команда ААА
изменяет состояние флага AF
и оставляет значения флагов PF,
ZF,
SF
и OF
неопределенными. Но так как в данном
случае только флаг CF имеет смысл, то
считайте значения остальных флагов
уничтоженными.

Команда
DAA преобразует содержимое регистра AL в
две правильные упако­ванные
десятичные цифры. Она используется в
следующем контексте:

ADD
AL,BL
;Сложить упакованные BCD-числа в AL и
BL

DAA
; и преобразовать результат
в упакованное число

Если
результат превышает предельное значение
для упакованных BCD-чисел (99), то команда
DAA добавляет 1 к содержимому регистра
АН и полагает флаг CF равным 1. Кроме того,
команда DAA изменяет состояния флагов
PF,
AF,
ZF
и CF и оставляет значение флага OF
неопределенным. Но так как в данном
случае только флаг CF имеет смысл, то
считайте остальные пять флагов
уничтоженными.

Команда
приращения значения приемника на единицу

Команда
INC
(increment
— прирастить) добавляет 1 к содержимому
регистра или ячейки памяти, но в отличие
от команды ADD
не воздействует на флаг переноса CF.
Команда INC
удобна для приращения значений счетчиков
в циклах команд. Ее можно использовать
и для приращения значения индексного
регистра или указателя при доступе к
последовательно расположенным ячейкам
памяти.

Приведем
несколько примеров:

INC
CX
;Прирастить значение 16-битового

INC
AL
;или 8-битового регистра

INC
MEM_ВYТЕ
;Прирастить значение байта

INC
MEM_WORD[BX] ;или
слова памяти

Как
ни странно, приращение значения 8-битового
регистра отнимает у микро­процессора
8×86 больше времени, чем приращение
значения 16-битового регистра. Это вызвано
тем, что разра­ботчики фирмы Intel
предполагали, что программисты будут
чаще пользоваться счетчиками размером
в слово, а не байт, и предусмотрели
специальную однобай­товую версию
команды INC
для 16-битовых регистров.

Команды
вычитания. Выполнение вычитания
микропроцессором 8086.

Внутри
микропроцессора 8×86, как и любого другого
микропроцессора общего назначения, нет
устройства вычитания. Однако он имеет
устройство сложения (сумматор) и может
вычитать числа путем сложения. Хотя это
и может показаться странным, тем не
менее это концепция, как сказал бы Шерлок
Холмс, «элементарна».

Чтобы
понять, как можно вычитать путем сложения,
посмотрим, как вычесть 7 из 10. В начальной
школе учат записывать это как

10-7,

но
в старших классах (скажем, в курсе
алгебры) учат и другому способу записи:

10+(-7).

Первым
способом (непосредственное вычитание)
вычитание может быть выпол­нено
микропроцессором, имеющим устройство
вычитания. Так как микропроцес­сор
8×86 его не имеет, то он вычитает в два
приема. Сначала он меняет знак у
вычитаемого (у второго числа), т.е.
обращает
его, а затем складывает уменьшае­мое
и обращенное вычитаемое. Так как
микропроцессор 8×86 оперирует двоичны­ми
числами, то обращение знака числа
производится путем так называемого
дополнения
до двух.

Чтобы
выполнить дополнение до двух, берется
исходная форма двоичного числа и значение
каждого его бита обращается (каждый 0
заменяется на 1, а 1 — на 0), а затем к
полученному числу добавляется 1.

Применяя
это к нашему примеру, получаем 8-битовые
представления чисел 10 и 7: 00001010В и
00000111В соответственно. Затем дополним
двоичное представле­ние 7 до двух:

1111
1000 (обратить все биты)

+_____1
(добавить 1)

1111
1001 (дополнение до двух числа 7, или — 7).

Теперь
операция вычитания примет следующий
вид:

0000
1010 (10)

+1111
1001

(-7)

0000
0011 (Ответ: 3)

Эврика!
Мы получили правильный ответ!

Так
как микропроцессор 8×86 выполняет
дополнение до двух автоматически, то
Вам эта операция понадобится в редких
случаях. Позже в этом разделе мы рассмотрим
команду NEG,
посредством которой можно выполнить
дополнение до двух, если оно когда-либо
Вам понадобится.

Команда
вычитания
SUB
и вычитания с заемом
SBB.

Команды
SUB
(substract
— вычесть) и SBB
(substract
with
borrow
— вычесть с заемом) аналогичны соответственно
командам сложения ADD
и ADC,
только при вычитании флаг переноса CF
действует как признак заема.
Команда SUB
вычитает операнд-источник из
операнда-приемника и возвращает результат
в операнд-приемник, т.е.

приемник
= приемник — источник

Команда
SBB
делает то же самое, но дополнительно
вычитает значение флага переноса CF:

приемник
= приемник — источник — перенос

Как
и в случае сложения, команды вычитания
выполняют две отдельные функ­ции.
Первая команда SUB
вычитает числа размером в байт или
слово, а также младшие биты чисел
повышенной точности. Другая команда
SBB вычитает старшие биты чисел повышенной
точности. Например, команда

SUB
AХ,СХ

вычитает
содержимое регистра СХ из содержимого
регистра АХ и возвращает результат в
регистр АХ.

Если
размеры операндов превышают 16 битов,
то пользуйтесь последователь­ностью
команд вида

SUB
AX,BX
;Вычесть младшие 16 битов,

SBB
BX,DX
; а затем — старшие 16 битов

Здесь
мы вычитаем 32-битовое число, помещенное
в регистры СХ и DX,
из 32-бито­вого числа, помещенного в
регистры АХ и ВХ. При вычитании содержимого
регист­ра DX из содержимого регистра
ВХ команда SBB учитывает возможность
заема при выполнении первого вычитания.

Можно
вычитать из содержимого регистра
содержимое ячейки памяти (и наоборот)
или вычитать из содержимого регистра
либо ячейки памяти непосредст­венное
значение. Ниже приведены примеры
допустимых команд:

SUB
AХ,MEM_WORD
; Вычесть из регистра содержимое
ячейки памяти

SUB
MEM_WORD[BX],AХ
; или наоборот

SUB
AL,10
; Вычесть константу из регистра

SUB
MEM_BYTE,OFH
; или из ячейки памяти

Нельзя
непосредственно вычесть значение одной
ячейки из другой или использо­вать
непосредственное значение как приемник.

Команды
SUB
и SBB могут воздействовать на шесть флагов
следующим обра­зом:

Флаг
переноса CF равен 1, если требуется заем;
в противном случае он равен 0.

Флаг
четности PF
равен 1, если результат вычитания имеет
четное число битов со значением 1; в
противном случае он равен 0.

Вспомогательный
флаг переноса AF
равен 1, если результат вычитания
десятич­ных чисел требует коррекции;
в противном случае он равен 0.

Флаг
нуля ZF
равен 1, если результат равен 0; в противном
случае он равен 0.

Флаг
знака SF
равен 1, если результат отрицателен
(старший бит равен 1); в противном случае
он равен 0.

Флаг
переполнения OF
равен 1, если при вычитании чисел, имеющих
разные знаки, результат превышает
диапазон значений приемника в обратном
коде, а сам приемник изменяет знак; в
противном случае флаг OF
равен 0.

Флаги
SF и OF
имеют смысл только при вычитании чисел
со знаком, а флаг AF
– только при вычитании десятичных
чисел.

Коррекция
результата вычитания для представления
в кодах
ASCII
и в упакованном десятичном формате
(команды
AAS
и
DAS).

При
вычитании, как и при сложении, микропроцессор
8×86 рассматри­вает операнды как двоичные
числа. Поэтому вычитание чисел,
представленных в двоично-десятичном
коде (BCD-чисел), может привести к
неправильным результа­там. Предположим,
например, что надо вычесть BCD-число 26 из
BCD-числа 55. Микропроцессор 8×86 выполнит
двоичное вычитание следующим образом:
допол­нит до двух двоично-десятичное
представление числа 26, а затем выполнит
сложе­ние:

0101
0101 (BCD-число 55)

+
1101 1010

(дополнение до двух BCD-числа 26)

1
0010 1111 (??).

Вместо
правильного значения (BCD-числа 29) мы
получили результат, у которого старшая
цифра 2, младшая цифра — шестнадцатеричная
цифра F,
и при этом бит переноса равен 1. Конечно,
этот результат требует коррекции.

Коррекция
результата вычитания двух десятичных
чисел осуществляется командами AAS
(ASCII
adjust
for
substraction
— скорректировать вычитание для
представления в кодах ASCII)
и DAS
(Decimal
adjust
for
substraction
— скорректиро­вать вычитание для
представления в десятичной форме). При
их исполнении предполагается, что
корректируемое число находится в
регистре AL.

Команда
AAS преобразует содержимое регистра AL в
правильную неупакован­ную
десятичную цифру в младших четырех
битах регистра AL (и обнуляет старшие
четыре бита). Она используется в следующем
контексте:

SUB
AL,BL
; Вычесть BCD-число (содержимое BL)
из AL

AAS
; и преобразовать результат в
неупакованное число

Если
результат превышает 9, то команда AAS
вычитает 1 из содержимого регистра АН
и полагает флаг CF
равным 1, в противном случае она обнуляет
флаг CF.
Кроме того, команда AAS изменяет состояние
флага AF
и оставляет значения флагов PF,
ZF,
SF
и OF
неопределенными. Но так как в данном
случае только флаг CF имеет смысл, то
считайте значения остальных флагов
уничтоженными.

Команда
DAS преобразует содержимое регистра AL в
две правильные упако­ванные десятичные
цифры. Она используется в следующем
контексте:

SUB
AL,BL
;Вычесть упакованное BCD-число(содержимое)
BL из AL

DAS
; и преобразовать результат в
упакованное число

Если
результат превышает предельное значение
для упакованных BCD-чисел (99), то команда
DAS вычитает 1 из содержимого регистра
АН и полагает флаг CF равным 1; в противном
случае она обнуляет флаг CF. Кроме того,
команда DAS изменяет состояния флагов
PF,
AF,
ZF
и SF, а значение флага OF
оставляет неопре­деленным. Но так как
в данном случае только флаг CF имеет
смысл, то считайте остальные упомянутые
флаги уничтоженными.

Команда
уменьшения содержимого приемника на
единицу
DEC

Команда
DEC
(decrement
— уменьшить) вычитает 1 из содержимого
регистра или ячейки памяти, но при этом
(в отличие от команды SUB)
не воздейст­вует на флаг переноса CF.
Команда DEC
часто используется в циклах для
умень­шения значения счетчика до тех
пор, пока оно не станет нулевым или
отрицатель­ным. Ее можно использовать
также для уменьшения значения индексного
регистpa
или указателя при доступе к последовательно
расположенным ячейкам памя­ти.

Приведем
несколько примеров:

DEC
CX
;Уменьшить знамение 16-битового

DEC
AL
; или 8-битового регистра

DEC
MEM_BYTE
;Уменьшить значение байта

DEC
MEM_WORD[BX]
;или слова памяти

Команда
обращения знака
NEG.

Команда
NEG вычитает значение операнда-приемника
из нулевого значения и тем самым формирует
его дополнение до двух. Команда NEG
оказыва­ет на флаг то же действие, что
и команда SUB.
Но поскольку один из операндов равен
0, то можно точнее описать условия
изменения состояний флагов. Итак, при
исполнении команды NEG флаги изменяются
следующим образом:

Флаг
переноса CF
и флаг знака SF
равны 1, если операнд представляет собой
ненулевое положительное число; в
противном случае они равны 0.

Флаг
четности PF
равен 1, если результат имеет четное
число битов, равных 1; в противном случае
он равен 0.

Флаг
нуля ZF
равен 1, если операнд равен 0; в противном
случае он равен 0.

Флаг
переполнения OF
равен 1, если операнд-байт имеет значение
80Н или операнд-слово имеет значение
8000Н; в противном случае он равен 0.

Команда
NEG полезна для вычитания значения
регистра или ячейки памяти из
непосредственного значения. Например,
Вам нужно вычесть значение регистра AL
из 100. Так как непосредственное значение
не может служить приемником, то команда
SUB
100, AL недопустима. В качестве альтернативы
можно обратить знак содержимого регистра
AL и добавить к нему 100:

NEG
AL

ADD
AL,100

Команда
расширения знака.

Существуют
две команды, позволяющие выполнять
операции над смешанными данными за счет
удвоения размера операнда со знаком.
Команда CBW
(convert
byte
to
word
— преобразовать байт в слово) воспроизводит
7-й бит регистра AL
во всех битах регистра AH.

Команда
CWD
(convert
word
to
double
word
— преобразовать слово в двойное слово)
воспроизводит 15-й бит регистра AX
во всех битах регистра DX.

Таким
образом, команда CBW
позволяет сложить байт и слово, вычесть
слово из байта и т. д. Аналогично, команда
CWD
позволяет разделить слово на слово.

Приведем
несколько примеров:

CBW ;Сложить
байт в AL
со словом в BX

ADD
AX,BX

CBW ;Умножить
байт в AL
на слово в BX

IMUL
BX

CWD ;Разделить
слово в AX
на слово в BX

IDIV
BX

Бонд. Джеймс Бонд.

*****

Рейтинг (т): 49

Использование стандартного отладчика debug.exe

Сегодня мы рассмотрим стандартный отладчик debug.exe, входящий в любую версию DOS/Windows. Должен заметить, что этот отладчик не такой мощный, как гиганты SoftICE или Turbo Debugger. Однако, в то же время debug.exe очень прост и доступен, и поэтому идеально подходит для начинающих программистов на языке Ассемблер.
Итак, запускаем debug.exe (для этого заходим в Пуск->Выполнить и выполняем команду debug) и начинаем изучать этот замечательный отладчик :)

Команды debug.exe

Для начала разберемся с правилами набора команд debug.exe:

  • В debug.exe не различается регистр букв.
  • Пробелы в командах используется только для разделения параметров.
  • Вводимые числа должны быть в шестнадцатеричной системе счисления, причем без завершающей буквы h.
  • Сегмент и смещение записываются с использованием двоеточия, в формате сегмент:смещение, например, CS:3C1 (смещение 3C1h в сегменте кода) или 40:17 (смещение 17h в сегменте, адрес начала которого — 40[0]h).

Разобравшись с правилами, давайте перейдем к изучению команд debug.exe. Замечу, что работа с командами debug.exe в чем-то похожа на работу с командной строкой DOS. Поcле загрузки отладчика на экране появится приглашение, выглядещее в виде дефиса:

ExpandedWrap disabled

    -_

Регистры CS, DS, ES, SS в этот момент инициализированы адресом 256-байтного префикса сегмента програмы, а рабочая области в памяти будет начинаться с адреса этого префикса + 100h.
Команды debug.exe вводятся сразу после приглашения на месте, которое отмечено курсором. Каждая команда состоит из идентификатора и параметров, идентификатор состоит из одной буквы.

Краткая таблица всех команд debug.exe

Команда Описание Формат
A (Assemble) Транслирование команд ассемблера в машинный код;
адрес по умолчанию — CS:0100h.
A [<адрес_начала_кода>]
C (Compare) Сравнение содержимого двух областей памяти; по умолчанию используется DS.
В команде указывается либо длина участков, либо диапазон адресов.
C <начальный_адрес_1> L<длина> <начальный_адрес_2>
C <начальный_адрес_1> <конечный_адрес_1> <начальный_адрес_2>
D (Display/Dump) Вывод содержимого области памяти в шестнадцатеричном и ASCII-форматах.
По умолчанию используется DS; можно указывать длину или диапазон.
D [<начальный_адрес> [L<длина>]]
D [начальный_адрес конечный_адрес]
E (Enter) Ввод в память данные или инструкции машинного кода;
по умолчанию используется DS.
E [<адрес> [<инструкции/данные>]]
F (Fill) Заполнение области памяти данными из списка; по умолчанию используется DS.
Использовать можно как длину, так и диапазон.
F <начальный_адрес_1> L<длина> ‘<данные>’
F <начальный_адрес> <конечный_адрес> ‘<данные>’
G (Go) Выполнение отлаженной программы на машинном языке до указанной точки останова;
по умолчанию используется CS. При этом убедитесь, что IP содержит корректный адрес.
G [=<начальный_адрес>] <адрес_останова> [<адрес_останова> …]
H (Hexadecimal) Вычисление суммы и разности двух шестнадцатеричных величин. H <величина_1> <величина_2>
I (Input) Считывание и вывод одного байта из порта. I <адрес_порта>
L (Load) Загрузка файла или данных из секторов диска в память; по умолчанию — CS:100h.
Файл можно указать с помощью команды N или аргумента при запуске debug.exe.
L [<адрес_в_памяти_для_загрузки>]
L [<адрес_в_памяти_для_загрузки> [<номер_диска> <начальный_сектор> <количество_секторов>]]
M (Move) Копирование содержимого ячеек памяти; по умолчанию используется DS.
Можно указывать как длину, так и диапазон.
M <начальный_адрес> L<длина> <адрес_назначения>
M <начальный_адрес> <конечный_адрес> <адрес_назначения>
N (Name) Указание имени файла для команд L и W. N <имя_файла>
O (Output) Отсылка байта в порт. O <адрес_порта> <байт>
P (Proceed) Выполнение инструкций CALL, LOOP, INT или повторяемой строковой инструкции
с префиксами REPnn, переходя к следующей инструкции.
P [=<адрес_начала>] [<количество_инструкций>]
Q (Quit) Завершение работы debug.exe. Q
R (Register) Вывод содержимого регистров и следующей инструкции. R <имя_регистра>
S (Search) Поиск в памяти символов из списка; по умолчанию используется DS.
Можно указывать как длину, так и диапазон.
S <начальный_адрес> L<длина> ‘<данные>’
S <начальный_адрес> <конечный_адрес> ‘<данные>’
T (Trace) Пошаговое выполнение программы. Как и в команде P, по умолчанию используется
пара CS:IP. Замечу, что для выполнения прерываний лучше пользоваться командой P.
T [=<адрес_начала>] [<количество_выполняемых_команд>]
U (Unassemble) Дизассемблирование машинного кода; по умолчанию используется пара CS:IP. К сожалению,
debug.exe некорректно дизассемблирует специфические команды процессоров 80286+,
хотя они все равно выполняются корректно.
U [<начальный_адрес>]
U [<начальный_адрес конечный_адрес>]
W (Write) Запись файла из debug.exe; необходимо обязательно задать имя файла командой N, если
он не был загружен. А программы записываются только в виде файлов .COM!
W [<адрес> [<номер_диска> <начальный_сектор> <количество_секторов>]]

Примечание. Символами [] отмечены необязательные параметры.

Просмотр областей памяти

В этой части нашей статьи мы рассмотрим подробно работу команды D, позволяющей просматривать содержимое отдельных областей памяти.
Этот пример использует команду D для просмотра области памяти, начиная с 0159:0240:

ExpandedWrap disabled

    -d 0159:0240

    0159:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ……….l…..

    0159:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   …………….

    0159:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   …………….

    0159:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   …………….

    0159:0280  00 00 00 00 00 00 00 00-00 FF FF FF FF 00 00 00   …………….

    0159:0290  FF 00 00 00 00 00 00 00-00 00 4E 4F 20 4E 41 4D   ……….NO NAM

    0159:02A0  45 20 20 20 20 00 26 81-4F 03 00 01 CB 00 00 00   E    .&.O…….

    0159:02B0  00 00 00 00 00 00 00 00-00 00 00 01 07 04 FF 02   …………….

    -_

Здесь на запрос просмотра участка памяти мы получили восемь строк, в которых указано содержимое выбранной области памяти. Каждая строка состоит из трех частей:

  • Адрес первого слева показанного байта в формате сегмент:смещение.
  • Шестнадцатеричное представление параграфа (16 байт), начинающегося с указанного в начале строки байта.
  • Символы этого же параграфа в ASCII-формате.

Адрес, указанный в строке, относится исключительно к первому байту в параграфе, а адреса последующих байтов следует вычислять самостоятельно. Шестнадцатеричное представление содержит по два знака в каждом байте, а сами байты разделены пробелами для облегчения чтения. Кроме того, следует отметить, что восьмой и девятый байты разделены дефисом, разделяя тем самым параграф на две части и облегяая вычисление адресов байтов в параграфе.

Полезные приемы с командой D

Проверка параллельных и последовательных портов
Первые 16 байт области данных BIOS содержат адреса параллельных и последовательных портов. Поэтому с помощью следующей команды можно проверить эти порты:

ExpandedWrap disabled

    -D 40:00_

Первые выведенные восемь байтов указывают на адреса последовательных портов COM1-COM4. Следующие 8 байтов указывают на адреса параллельных портов LPT1-LPT4.
Например, если на вашем компьютере есть один параллельный порт, то первые два байта будут, скорее всего, такими: 7803. Адрес порта записывается в обращенной последовательности, т.е. 0378.

Проверка оборудования
Первые два байта, располагающиеся в BIOS по адресу 410h, содержат информацию об установленном в системе оборудовании. Находим эти байты командой:

ExpandedWrap disabled

    -D 40:10_

Предположим, что первые два байта окажутся 23 44. Расшифруем эти байты для получения информации об установленных устройствах. Для этого обратим эти байты (44 23), затем переведем их в двоичную систему счисления. Получаем:

Значение бита

0

1

0

0

0

1

0 0 0 0 1 0 0 0 1 1
Позиция бита 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Что означают эти биты? Продолжаем расшифровывать:

Биты Устройство
15, 14 Число параллельных портов (01 = 1 порт, …)
11, 10, 9 Число последовательных портов (…, 010 = 2 порта, …)
7, 6 Число дисководов (00 = 1 дисковод, 01 = 2, 10 = 3, 11 = 4)
5, 4 Начальный видеорежим (01 = 40х25 цветной, 10 = 80х25 цветной, 11 = 80х25 монохромный)
1 Присутствие математического сопроцессора (0 = нет, 1 = есть)
0 Наличие привода для дискет (0 = нет, 1 = есть)

Проверка состояния регистра клавиатуры
В области данных BIOS по адресу 417h находится первый байт, который хранит состояние регистра клавиатуры. Выключаем Num Lock и Caps Lock, затем набираем команду:

ExpandedWrap disabled

    -d 40:17_

Первый байт будет равен 00. Включив Num Lock и Caps Lock, снова выполняем команду. Теперь первый байт должен равняться 60. Опытным путем установлено, что при включенном Num Lock первый байт равен 20, а при Caps Lock — 40. :)

Проверка состояния видеосистемы
По адресу 449h в BIOS находится первая область видеоданных. Для проверки набираем:

ExpandedWrap disabled

    -d 40:49_

Первый байт показывает текущий видеорежим (к примеру, 03 — цветной), а второй — число столбцов (например, 50 — режим с 80 столбцами). Число строк можно найти по адресв 484h (40:84).

Проверка копирайта BIOS и серийного номера
Сведения об авторских правах на BIOS встроены в ROM BIOS по адресу FE00:0. Строку с копирайтом можно легко найти в ASCII-последовательности, а серийный номер — в виде шестнадцатеричного числа. Хотя, строка с указанием авторских прав может быть длинной и не умещаться в выведенную область памяти. В таком случае следует просто ввести еще раз D.

Проверка даты произвоства BIOS
Эта дата также записана в ROM BIOS начиная с адреса FFFF:5. После выполнения соответствующей команды в ASCII-последовательности будет находиться эта дата, записанная в формате мм/дд/гг.

Пример:

ExpandedWrap disabled

    -d FFFF:5

    FFFF:0000                 31 31 2F-32 38 2F 30 35 00 FC 00        11/28/05…

    FFFF:0010  34 12 00 00 00 00 00 00-00 00 00 00 00 00 00 00   4……………

    -_

Непосредственный ввод программы в память с помощью debug.exe

debug.exe позволяет вводить программу непосредственно в память машины, а затем следить и управлять ее выполнением. Мы будем вводить программу в машинных кодах, используя команду E. При этом будьте бдительны — ввод ошибочных данных по ошибочному адресу чреват непредсказумыми последствиями! Хотя к серьезным проблемам в системе это вряд ли приведет, но потерять все данные, введенные в debug.exe, можно потерять запросто.
Программа, которую мы сейчас будем вводить, использует данные, заложенные непосредственно в теле инструкций. Далее показан листинг программы на Ассемблере, в комментариях указаны аналоги команд языка в машинных кодах, а также объяснение каждй команды. Замечу, что в числах нет символа h, поскольку, как было сказано выше, debug.exe понимает только числа в шестнадцатеричной системе.

ExpandedWrap disabled

    MOV AX, 0123 ; код B82301: заносим значение 0123h в AX

    ADD AX, 0025 ; код 052500: прибавляем 0225h к значению AX

    MOV BX, AX   ; код 8BD8: заносим значение AX в BX

    ADD BX, AX   ; код 03D8: прибавляем значение AX к BX

    MOV CX, BX   ; код 8BCB: заносим значение BX в CX

    SUB CX, AX   ; код 2BC8: отнимаем значение AX из CX

    SUB AX, AX   ; код 2BC0: очищаем AX

    JMP 100      ; код EBEE: переходим к началу программы

Как можно заметить, каждая машинная инструкция имеет длину от 1 до 3 байтов. Первый байт указывает операцию, последующие — ее операнды. Исполнение программы начинается соответственно с первой инструкции и последовательно проходит через все инструкции одну за другой.
Теперь можно ввести программу в память. Разделим машинный код на три части по шесть байт и введем каждую, используя команду E и начиная с адреса CS:100.

ExpandedWrap disabled

    -E CS:100 B8 23 01 05 25 00

    -E CS:106 8B D8 03 D8 8B CB

    -E CS:10C 2B C8 2B C0 EB EE

    -_

Теперь, когда программа введена в память, попробуем управлять ее выполнением. Для начала проверим текущее состояние регистров и флагов, для этого вводим команду R. Отладчик выведет содержимое регистров в шестнадцатеричной форме; на разных машинах содержимое регистров может различаться.

ExpandedWrap disabled

    -r

    AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

    DS=15D7  ES=15D7  SS=15D7  CS=15D7  IP=0100   NV UP EI PL NZ NA PO NC

    15D7:0100 B82301        MOV     AX,0123

    -_

Итак, как можно видеть, debug.exe инициализировал сегменты DS, ES, SS, CS одним и тем же адресом. Регистр IP содержит 0100, указывая на то, что инструкции выполняются со смещения 100h относительно CS (а мы, вводя инструкции в память, как раз указали этот адрес).
Здесь же указаны и значения флагов переполнения, направления, прерывания, знака, нуля, дополнительного переноса, четности и переноса:

Значение Описание
NV Отсутствие переполнения
UP Направление вверх или вправо
EI Разрешение прерываний
PL Положительный знак
NZ Ненулевое значение
NA Отсутствие дополнительного переноса
PO Нечетное слово
NC Отсутствие переноса

После регистров и состояния флагов debug.exe выводит информацию о первой инструкции, которая будет выполняться:

  • Адрес инструкции, в нашем случае это 15D7:0100, где 15D7 — адрес сегмента кода.
  • Машинный код, соответствующей этой инструкции (B82301).
  • Собственно инструкция, записанная на ассемблере (MOV AX,0123).

Теперь, после анализа содержимого регистров и флагов, давайте перейдем к выполнению программу. Выполнять программу мы будем пошагово, используя команду T. Использовав в первый раз команду T, мы выполняем инструкцию MOV. Здесь машинный код операнда инструкции — 2301. Операция помещает 23 в AL (младшая половина AX), а 01 — в AH (старшая).
После этого debug.exe снова выводит информацию о регистрах:

ExpandedWrap disabled

    -t

    AX=0123  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

    DS=15D7  ES=15D7  SS=15D7  CS=15D7  IP=0103   NV UP EI PL NZ NA PO NC

    15D7:0100 B82301        ADD     AX,0025

    -_

Теперь AX содержит 0123h, IP0103h (следовательно, длина выполненной инструкции: 0103h0100h = 3 байта), а в качестве следующей инструкции указана операция ADD.
Так, раз за разом выполняя команду T, мы дойдем до последней инструкции JMP 100. Она установит регистр IP в 100h, и debug.exe вернется к началу программы. Возвращаясь к началу программы, следует заметить, что в DS, ES, SS и CS содержится один и тот же адрес. Дело в том, что debug.exe рассматривает введенные программы исключительно как программы .COM. А в программах .COM, в отличие от .EXE, стек, код и данные хранятся в одном сегменте.

Ассемблирование и дизассемблирование

В прошлом примере мы вводили программу в машинных кодах, однако, debug.exe вполне способен понимать инструкции, записанные на ассемблере. Для работы с такими программами в debug.exe используются команды A и U.
Команда A запрашивает инструкции на ассемблере и преобразовывает их в машинный код. Для начала инициализируем начальный адрес для ввода инструкций (100h):

ExpandedWrap disabled

    -a 100_

Отладчик выведет адрес сегмента кода и смещения (например, 13F2:0100). Теперь мы должны ввести следующие инструкции на ассемблере в память, после каждой строки нажимая Enter:

ExpandedWrap disabled

    MOV CL, 42

    MOV DL, 2A

    ADD CL, DL

    JMP 100

После ввода последней инструкции нажимаем Enter дважды, чтобы указать отладчику, что мы закончили вводить текст программы. Теперь программу можно запускать, используя команды R для просмотра регистров и T для трассировки. Замечу, что в своих программах при наличии инструкций INT их следует обрабатывать не командой T, а командой P, которая обрабатывает все прерывание сразу.

Перейдем к процедуре дизассемблирования, а в качестве примера возьмем только что введенную программу. Используем адреса первой и последней инструкций для указания диапазона, который мы собираемся дизассемблировать, т.е. 100h и 107h.

ExpandedWrap disabled

    -u 100, 107_

После выполнения этой команды debug.exe выведет инструкции, находящиеся в указанном диапазоне, на ассемблере, в машинных кодах, а также адрес каждой инструкции:

ExpandedWrap disabled

    13F2:0100 B142          MOV     CL, 42

    13F2:0102 B22A          MOV     DL, 2A

    13F2:0104 00D1          ADD     CL, DL

    13F2:0106 EBF8          JMP     0100

Итог

А теперь, после небольшого обзора возможностей стандартного отладчика debug.exe давайте подведем итоги. Итак:

  • debug.exe можно применять для наблюдений и отладки программ на ассемблере и машинных кодах.
  • debug.exe позволяет трассировать программу, устанавливать точки останова, просматривать области памяти, вводить программы непосредственно в память компьютера.
  • debug.exe представляет загружаемые программы как программы .COM.
  • debug.exe воспринимает только числа в шестнадцатеричной системе.
  • debug.exe не различает регистр букв.

Дополнительные материалы:
Абель П. АССЕМБЛЕР: Язык и программирование для IBM PC. — К.: Век+, 2003.
http://www.allasm.ru/low_prog_01.php — низкоуровневое программирование для дZенствующих — DZebug: руководство юZверя
http://thestarman.pcministry.com/asm/debug/debug.htm — A Guide to DEBUG, by Daniel B. Sedory

Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Отладка

Основы Python

Даже у самых опытных разработчиков код редко работает идеально с первого раза. Но чем выше навыки разработчика, тем лучше он отлаживает код, то есть анализирует ошибки и устраняет их.

Навык отладки сам по себе не появится: его необходимо развивать, причем начинать нужно как можно раньше. По ходу обучения вы будете выполнять задания и практиковаться — все это поможет набраться опыта. Со временем анализ и устранение ошибок войдут в привычку, если уделять достаточно внимания практике.

Как найти ошибку в коде

Можно отлаживать неисправный код методом тыка, но это долго и непродуктивно. Будет намного проще, если вы сначала поймете проблему, а уже потом начнете устранять ее. Понимание — это ключевой этап, без которого дальнейшие шаги невозможны.

Перед отладкой кода надо понять, что в нем не так. Это можно сделать за два шага.

Шаг 1. Изучить трейсбек (traceback) — список всех вызовов функций от запуска программы до места с ошибкой. Трейсбек помогает отследить, как прошло выполнение программы: какие функции у нее получилось вызвать успешно, а с какими — возникли сложности. Каждая запись в трейсбеке указывает на файл и строчку, а затем на выполняемую функцию.

Представим, что вы написали код в файле users.py и решили запустить функцию main() в четвертой строчке. Запись в трейсбеке будет выглядеть так:

  File "users.py", line 4, in <module>
    main()

Как видите, здесь указаны не только файл и строчка, но еще и название модуля. По нему можно с легкостью определить, где возникла проблема: в вашем коде или в какой-то библиотеке, которую вы не писали, но используете.

Шаг 2. Когда трейсбек дойдет до проблемного места, он выдаст сообщение об ошибке. Например, такое:

NameError: name 'create' is not defined

Если владеете английским, то быстрее поймете, о чем идет речь в сообщении: «Название create не определено». Эта ошибка чаще всего происходит из-за опечатки в названии — нужно проверить этот момент. Без знания английского тоже можно разобраться, если обратиться к словарю или онлайн-переводчику.

Теперь посмотрим, как трейсбек и сообщение об ошибке выглядят вместе:

Traceback (most recent call last):
  File "users.py", line 4, in <module>
    main()
  File "users.py", line 2, in main
    create()
NameError: name 'create' is not defined

В примере выше видно всю цепочку событий: программа успешно справилась с функцией main(), а потом перешла к функции create() и столкнулась с ошибкой в названии.

Кроме NameError, в Python есть еще множество разных ошибок, которые можно разделить на три группы.

Типы ошибок

Самые простые и понятные ошибки — синтаксические. Они связаны исключительно с тем, что код неверно оформлен: например, использованы неправильные кавычки.

В выводе таких ошибок всегда присутствует фраза SyntaxError:. Чтобы отладить код в этом случае, нужно внимательно взглянуть на место с ошибкой. Посмотрим на примере. Здесь синтаксическая ошибка произошла потому, что использована кавычка ' вместо ":

Traceback (most recent call last):
  File "users.py", line 2
    print("Hello" + "world')
                           ^
SyntaxError: EOL while scanning string literal

Вторая большая группа ошибок — это ошибки программирования. Например, к ним относятся:

  • Вызов несуществующей функции
  • Использование необъявленной переменной
  • Передача неверных аргументов (например, аргументов неверного типа)

Эти ошибки исправить труднее, чем синтаксические. Обычно они возникают в результате неправильной логики в другом, более раннем вызове.

Последний тип ошибок — логические ошибки. Исправить ситуацию бывает очень сложно, потому что программа в целом работает, но при некоторых значениях выдает неверный результат. В большинстве случаев проблема кроется в неверной логике. Например, вместо сложения в программе выполняется вычитание:

# Функция должна считать сумму чисел, но считает разность:
def sum(a, b):
    return a - b

# при таком вызове ошибка неочевидна, потому что
# и при сложении, и при вычитании будет один и тот же результат
sum(4, 0)  # 4

Способы отладки

Есть множество способов отладки программ, но у всех одна общая идея — нужно проанализировать, как меняются значения переменных в процессе работы кода.

Рассмотрим на конкретном примере. Ниже описана функция, которая считает сумму чисел от числа start до числа finish. Если start равно трем, а finish — пяти, то программа должна вычислить: 3 + 4 + 5.

def sum_of_series(start, finish):
    result = 0
    n = start
    while n < finish:
        result = result + n
        n = n + 1
    return result

В этом коде допущена ошибка. Глядя на код функции sum_of_series() замечаем, что основных переменных там две: n и result. Из этого можно сделать такой вывод — нужно посмотреть, какие значения даются переменным на каждой итерации. После этого найти ошибку не составит труда.

Есть удобный способ отслеживать значения переменных во время выполнения кода — это визуальные отладчики. Они встраиваются в популярные редакторы кода и позволяют выполнить программу по шагам и наглядно увидеть все изменения. Если интересно узнать больше об отладчиках, можно поискать в Google по запросу Python Debuggers.

В среде Хекслета отладчика нет, поэтому здесь используется другой подход — отладочная печать. Суть такая же, как и в визуальном отладчике. Разница только в том, что для вывода значений переменных используется обычная печать на экран. То, что печатается на экран, отображается во вкладке OUTPUT, на которую автоматически переключается редактор во время проверки. Посмотрим на примере:

def sum_of_series(start, finish):
    result = 0
    n = start
    while n < finish:
        print('new iteration !!!!')
        print(n)
        result = result + n
        n = n + 1
        print(result)
    return result

sum_of_series(3, 5)

# new iteration !!!!
# 3
# 3
# new iteration !!!!
# 4
# 7

Здесь видно, что итераций цикла на одну меньше, чем нужно. Почему-то не выполняется сложение для последнего числа, которое обозначено как finish. И действительно, если посмотреть на определение, то видно, что там используется n < finish вместо n <= finish. Так с помощью отладки удалось найти ошибку — оказывается, был выбран знак < вместо <=.

Новички часто расстраиваются из-за ошибок, начинают считать себя невнимательными. На самом деле, в ошибках нет ничего страшного: опытные разработчики допускают их не реже новичков. Сложно научиться писать идеальный код, но можно развивать навыки отладки и насмотренность на ошибки.

Еще новички думают, что опытный разработчик может взглянуть на код и сразу же понять, в чем ошибка. Да, с опытом приходит насмотренность, но все не так просто. По фрагменту кода сложно понять, что пошло не так. Если хотите спросить совет у опытного разработчика, лучше покажите не сам неисправный код, а сообщение об ошибке.


Дополнительные материалы

  1. Как найти ошибки в коде?
  2. pdb — The Python Debugger

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

SlowAR

0 / 0 / 0

Регистрация: 30.04.2014

Сообщений: 48

1

20.05.2014, 06:53. Показов 2764. Ответов 3

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Здравствуйте! Мне нужно найти сумму элементов массива с 3 по 5 элемент и вывести ее в четвертый. Все элементы массива равны числу 12223278h. Я написал код, но он правильно выводит только первую половину числа:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
MOV BX,200
MOV CX,3
MOV AX,[BX]
MOV DX,[BX+2]
ADD AX,[BX+4]
ADC DX,[BX+6]
ADD BX,4
INC CX
CMP CX,5
JNE 10B
MOV BX,200
MOV [BX+C],AX
MOV [BX+E],DX

В чем может быть проблема? Заранее спасибо!



0



Ушел с форума

Автор FAQ

16129 / 7471 / 1014

Регистрация: 11.11.2010

Сообщений: 13,454

20.05.2014, 08:19

2

Цитата
Сообщение от SlowAR
Посмотреть сообщение

найти сумму элементов массива с 3 по 5 элемент и вывести ее в четвертый

а размер элементов массива? байт? слово? двойное слово? и войдет ли сумма трех элементов в один элемент? Под сумму обычно отводят в два раза больше, то есть сумма байтов = слово, сумма слов = двойное слово и т.д.



0



0 / 0 / 0

Регистрация: 30.04.2014

Сообщений: 48

20.05.2014, 08:27

 [ТС]

3

Mikl___,
ну каждый элемент массива заполнен числом 12223278h. а вот формулировка самого задания: Вычислить сумму элементов массива с 3 по 5 и переслать результат в элемент массива с номером 4. Больше ничего не написано. должно получиться число: 36669769h, а получается: 36666900h.



0



Mikl___

Ушел с форума

Автор FAQ

16129 / 7471 / 1014

Регистрация: 11.11.2010

Сообщений: 13,454

20.05.2014, 11:04

4

SlowAR,
а ты предложи своему преподавателю сложить три числа равных 0FFFFh = 65535, а потом пусть он попробует число 2FFFDh (=3*65535) поместить в два байта. Это у тебя известно, что в ячейках числа =12223278h (тогда и их сумма известна), но обычно величина хранящаяся в ячейках массива неизвестна, поэтому предполагается что там максимальное число. Но хозяин — барин, поэтому внимательно читаем задание «каждый элемент массива заполнен числом 12223278h» то есть каждый элемент по 4 байта «Вычислить сумму элементов массива с 3 по 5 и переслать результат» число 12223278h*3=36669768h «в четвертый»

Assembler
1
2
3
4
5
6
7
8
9
MOV BX,200 ;начало массива (адрес первого элемента)
MOV AX,[BX+8]; содержимое 3-его элемента 
MOV CX,[BX+0Ah]
ADD [BX+0Ch],AX; сложили содержимое 4-ого и 3-его элемента
ADC [BX+0Dh],CX
MOV AX,[BX+10h]; содержимое 5-oго элемента 
MOV CX,[BX+12h]
ADD [BX+0Ch],AX; сложили содержимое 4-ого и 5-oго элемента
ADC [BX+0Dh],CX



2



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

20.05.2014, 11:04

4

Debug — Программа-отладчик, которую используют для проверки и отладки
выполняемых файлов. Использовалась при операционной системе MS-DOS.
Под более поздние версии операционных систем работает через эмулятор MS-DOS и имеет
ограниченные возможности.

Данная программа является консольным приложением и предназначена для создания или
изменения кода файлов. С помощью неё можно создавать простые приложение под MS-DOS и
отслеживать их работу. Данный отладчик находится на самом низком уровне компиляторов assembler.
Но обладает неплохими возможностями такими как просмотр, изменение памяти и получение
состояния регистров.

Быстрый старт

Откройте окно командной строки, введите команду debug и нажмите
клавишу [Enter]. В результате будет запущен отладчик debug в интерактивном режиме. Дефис и
мигающий курсор означают, что отладчик ждет ввода команды. Одна из наиболее полезных возможностей
отладчика состоит в том, что с его помощью вы можете вводить небольшие фрагменты кода на ассемблере
и проверять их работу. Проиллюстрируем эти возможности на простом примере.
Введите букву a (на латинице) и нажмите клавишу [Enter]. Эта команда переводит отладчик
в режим ввода команд ассемблера. Каждая команда ассемблера записывается в одну строку
и заканчивается нажатием клавиши [Enter]. Чтобы закончить ввод ассемблерных команд
следует на новой пустой строке еще раз нажать клавишу [Enter].
Введите три строчки кода, представленного на рисунке.

Поясним что делает эта программа. Первая строка кода помещает число 2 в регистр ax.
Ассемблерная команда mov (от английского move — перемещать) копирует содержимое
второго операнда в первый операнд. Вторая строка кода аналогичным образом помещает
число 3 в регистр bx. И, наконец, третья строчка кода, использующая команду add
складывает содержимое регистров ax и bx и помещает результирующую сумму в регистр ax.

Отладчик позволяет достаточно просто и эффективно понаблюдать за работой программы.
Введите букву r и нажмите клавишу [Enter]. В результате появится информация о содержимом
регистров процессора. Поскольку в нашей программе используются только регистры ax и bx
сосредоточим свое внимание на их содержимом. Поскольку программа еще не запускалась
на исполнение, видим, что содержимое регистров равно нулю. Чтобы понять как работает
программа удобно использовать пошаговый режим отладки, когда на каждом шагу выполняется
одна строчка программы. Чтобы выполнить один шаг нужно ввести букву t и
нажать клавишу [Enter]. Видим, что в результате выполнения первой строки программы
содержимое регистра ax стало равным 2. Аналогичным образом можно выполнить оставшиеся
две строчки и пронаблюдать за результатом их исполнения.

Для более полного понимания работы микропроцессора рекомендуется обратить внимание
на содержимое других регистров, а также на третью строку выводимой информации.

Регистр IP (Instruction Pointer — указатель команды) всегда содержит адрес
следующей выполняемой команды. Обратите внимание что в самом начале содержимое
данного регистра равно 100 и указывает на адрес первой команды, т.к. вся программа
располагается в памяти, начиная именно с этого адреса. Кстати, адреса вводимых
команд указываются слева в тех строках где эти команды вводятся. Эти адреса
представляются в виде двух чисел разделенных двоеточием. Первое число определяет
так называемый сегментный адрес, а второе число смещение адреса от начала сегмента.
Заметьте, что на каждом следующем шаге отладки значение регистра IP увеличивается
таким образом, чтобы указывать на следующую команду программы. Заметьте также,
что третья выводимая строка содержит информацию о следующей выполняемой команде.
Эта информация включает в себя машинный адрес данной команды, саму команду на
машинном языке, т.е. в виде шестнадцатеричного числа, и ассемблерное представление
команды. Необходимо также обратить внимание, что содержимое всех сегментных
регистров DS, ES, SS и CS равно сегментному адресу программы.

Рекомендуется рассмотреть аналогичным образом ряд других команд ассемблера. Это позволит
не только узнать как применяются ассемблерные команды, но и освоить работу с отладчиком.

Арифметические команды

Команда сложения ADD имеет следующий формат

ADD приемник, источник

и может складывать как 8-, так и 16-битовые операнды.

Команда ADD складывает содержимое операнда-источника и операнда-приемника
и помещает результат в операнд-приемник. Ее действие можно описать как

приемник = приемник + источник

Команда ADD может воздействовать на шесть флагов:

Флаг переноса CF равен 1, если результат сложения не помещается в операнде-приемнике;
в противном случае он равен нулю.

Флаг четности PF равен 1, если результат имеет четное число битов со значением 1;
в противном случае он равен нулю.

Вспомогательный флаг переноса AF равен 1, если результат сложения десятичных чисел
требует коррекции; в противном случае он равен нулю.

Флаг нуля ZF равен 1, если результат равен 0; в противном случае он равен нулю.

Флаг знака SF равен 1, если результат отрицателен (старший бит равен 1) ;
в противном случае он равен нулю.

Флаг переполнения OF равен 1, если сложение двух чисел одного знака
(оба положительные или оба отрицательные) приводит к результату,
который превышает диапазон допустимых значений приемника в обратном коде,
а сам приемник при этом меняет знак. В противном случае он равен нулю.

Флаги SF и OF имеют смысл только при сложении чисел со знаком,
а флаг AF – только при сложении десятичных чисел.

Команда

INC приемник

добавляет 1 к содержимому регистра или ячейки памяти,
но в отличие от команды ADD не воздействует на флаг переноса CF.

Команда вычитания SUB имеет следующий формат

SUB приемник, источник

Команда SUB вычитает операнд-источник из операнда-приемника
и возвращает результат в операнд-приемник, т. е.

приемник = приемник – источник

Команда SUB может воздействовать на шесть флагов:

Флаг переноса CF равен 1, если требуется заем; в противном случае он равен нулю.

Флаг четности PF равен 1, если результат имеет четное число битов со значением 1;
в противном случае он равен нулю.

Вспомогательный флаг переноса AF равен 1, если результат вычитания
десятичных чисел требует коррекции; в противном случае он равен нулю.

Флаг нуля ZF равен 1, если результат равен 0; в противном случае он равен нулю.

Флаг знака SF равен 1, если результат отрицателен (старший бит равен 1) ;
в противном случае он равен нулю.

Флаг переполнения OF равен 1, если при вычитании чисел имеющих разные знаки,
результат превышает диапазон допустимых значений приемника в обратном коде,
а сам приемник при этом меняет знак. В противном случае он равен нулю.

Флаги SF и OF имеют смысл только при вычитании чисел со знаком,
а флаг AF – только при вычитании десятичных чисел.

Команда

DEC приемник

вычитает 1 из содержимого регистра или ячейки памяти, но при этом
(в отличие от команды SUB) не воздействует на флаг переноса CF.

Команда

NEG приемник

вычитает значение операнда-приемника из нулевого значения и
тем самым формирует его дополнение до двух. Команда NEG оказывает
на флаги то же действие, что и команда SUB.

Команда

CMP приемник, источник

вычитает операнд-источник из операнда-приемника и в зависимости
от результата устанавливает или обнуляет флаги.
Но в отличие от команды SUB команда CMP не сохраняет результат вычитания.
Таким образом команда CMP не изменяет операнды.
Она целиком предназначена для установки значений флагов,
на основании которых команды условного перехода будут принимать решение о передаче управления.

Команда

MUL источник

умножает числа без знака и может умножать как байты, так и слова.

Источник это регистр общего назначения или ячейка памяти размером в байт или слово.
В качестве второго операнда команда MUL использует содержимое регистра AL
(при операциях над байтами ) или регистра AX (при операциях над словами ).
Произведение имеет двойной размер и возвращается следующим образом:

Умножение байтов возвращает 16-битовое произведение в регистрах
AH ( старший байт ) и AL (младший байт ).

Умножение слов возвращает 32-битовое произведение в регистрах
DX ( старшее слово ) и AX (младшее слово ).

По завершении исполнения этих команд флаги переноса CF и
переполнения OF показывают, какая часть произведения существенна
для последующих операций. После исполнения команды MUL флаги CF и OF равны 0,
если старшая половина произведения равна 0; в противном случае оба этих флага равны 1.

Команда MUL не позволяет в качестве операнда использовать непосредственное значение.
Такое значение перед умножением надо загрузить в регистр или в ячейку памяти.

Команда

DIV источник

выполняет деление чисел без знака.

Источник это делитель размером в байт или слово, находящийся в регистре общего назначения или в ячейке памяти.

Делимое должно иметь двойной размер; оно извлекается из регистров
AH и AL ( при делении на 8-битовое число ) или из регистров DX и AX ( при делении на 16-битовое число ).
Результаты возвращаются следующим образом:

Если операнд-источник представляет собой байт, то частное возвращается в регистре AL, а остаток в регистре AH.

Если операнд-источник представляет собой слово, то частное возвращается в регистре AX, а остаток в регистре DX.

Команда оставляет состояние флагов неопределенным, но если частное не помещается
в регистре-приемнике ( AL или AX ), то процессор генерирует прерывание типа 0 ( деление на 0 ).

Hosted by uCoz

2. Отладчик Debug
2.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) Ассемблирование текста программы. В отладчике имеется мини-ассемблер. Он переводит мнемонику команд в их машинные коды. Практически все команды отладчика вызываются набором одной буквы, далее, возможно, указываются параметры команды. Ассемблирование начинается по команде 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] (пропущена запятая)
^ 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 (можно и без ведущего нуля) и нажимаем пробельную клавишу, чтобы полу-чить содержимое следующего байта (если бы старое значение байта нас устраи-вало, мы сразу нажали бы пробел). По адресу 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) по результату выполнения команд. Но отладчик пока-зывает не содержимое регистра флагов в шестнадцатеричном представлении, а значения битов в закодированной форме в виде двухбуквенных сокращений. Сведем их в таблицу 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 — в нем адрес следующей выполняе-мой команды. Сама команда показана в третьей строке. Флаги не изменились.
-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, но теперь укажем имя регистра:
-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 приведет скорее всего к зависанию программы (лучше не проверять это!). Действительно, после наших четырех команд в опе-ративной памяти хранится «мусор», который будет интерпретироваться процес-сором как коды команд.
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, поэтому после наших четырех команд начнет выполняться «мусор» с непредсказуемыми последствиями.
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) программного продукта.

2.2. Параметры команд отладчика.
Запуск отладчика возможен в двух вариантах:
1) D:USER> debug
2) D:USER> debug <имя файла>
При втором варианте запуска отладчик загружает содержимое файла в нижний доступный сегмент (при этом файл с расширением .com загружается в этом сегменте со смещением 100h).
После запуска debug на выполнение пользователь получает приглашение (-) и может вводить команды. Формат команды:
буква [параметр(ы)] Здесь буква — первая буква команды, например ко-манда 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 — дамп
Эта команда бала разобрана ранее.
1.2) E адрес [список] Enter — ввод
Ввод данных по указанному адресу.
а) E адрес (список опущен)
-e200
1C36:0200 01.02 12. 1B.0
вводим пробел для перехода к следующему байту
Результат:
-d200L3
1C36:0200 02 12 00
Итак, при нажатии пробельной клавиши отладчик выводит очередной байт и запрашивает его изменение, выводя точку. Для перехода к предыдущему бай-ту нужно ввести дефис (попробуйте!).
Упражнение. С помощью команды E можно просматривать память. По-смотрите, что будет происходить, если вы подадите команду efff0 и несколь-ко раз нажмете пробельную клавишу. Перейдете ли Вы границу сегмента? А что произойдет, если вы наберете команду dfff0?
б) E адрес список
e200 02 12 00
Результат тот же, что и в предыдущем примере, но на этот раз замена дан-ных ведется «вслепую».
Слова приходится заносить в память побайтно. Байты заносятся в обратном порядке. Например, для ввода по адресу (смещению) 120 слова 1234 подаем команду:
-e120 34 12
Пример ввода строки символов:
-e130 string for example,D,A,$
(0Dh — код возврата каретки, 0Ah — код перевода строки. $ — терминатор (ограничитель) строки, который нужен при использовании системной функции вывода строки на экран. Эту функцию мы изучим позднее.)
1.3) R [имя регистра] Register — регистр
а) R (без параметра) — выводятся имена всех регистров и их содержимое. Последняя строка вывода имеет формат:
адрес код команды мнемоника команды [содержимое памяти]
1С36:0100 A10002 MOV AX,[0200] DS:0200=01E4
б) R имя регистра — debug выводит содержимое регистра, а на следую-щей строке помещает двоеточие — приглашение к вводу нового значения. К сожалению, можно указывать только 16-разрядные регистры.
-rbx
1107
: 1 (вводим новое содержимое регистра)
<Enter>
-rah
br ERROR (недопустимое имя регистра)
в) RF выводит содержимое регистра флагов
NV UP DI NG NZ AC PE NC —
После дефиса можно ввести новые значения флагов: — PLCY (если задано более чем один код значения для флага, появляется сообщение об ошибке df error если задан код значения, отсутствующий в списке — сообщение bf error).
1.4) F область список Fill—заполнение
Если размер области превосходит длину списка, то список используется повторно, пока область не будет заполнена. Остаток списка усекается.
Пример. Перед созданием программы в отладчике полезно заполнить уча-сток памяти кодом команды nop (90h) (если это не сделать, «мусор» в памяти сбивает с толку начинающего пользователя).
-f100L100 90
Упражнение. Выполнить f400L10 1 2 3 и изучить результат.

2) Работа с кодами команд.
2.1) A [адрес] Assemble — ассемблировать
Пример работы этой команды мы уже видели. Если в начале сеанса работы с debug не указывать адрес, то ассемблирование начнется с адреса XXXX:0100.
Отметим, что с использованием мини-ассемблера заполнение памяти дан-ными осуществить даже проще, чем с помощью команды E:
-a200
1C36:0200 dw ffd0,12,a1c7
1C36:0206

(В результате [200]=FFD0h, [202]=0012h, [204]=A1C7h.)
-a200
1C36:0200 db «String»,D,A,$
1C36:0209
Здесь использованы директивы dw (Define Word — задать слово) и db (Define Byte — задать байт).
2.2) U [область] Unassemble — дисассемблировать
Команда переводит коды из области памяти в мнемонику команд.
Если перевод невозможен, то выводится ???? или DB число. Если параметр область опущен, то команда дисассемблирует 20 байт, начиная с текущего значения IP. Вновь введем U — следующие 20 байт и т.д. (аналогично команде D без параметров).
3) Выполнение программы.
3.1) T[=стартовый адрес] [количество повторений] Trace — трассировка
После выполнения каждой команды выводится информация, как при пода-че команды R. Приведем примеры:
а) t — выполнить одну команду, адрес которой в CS:IP;
б) t5 — выполнить 5 команд, начиная с текущего адреса;
в) t=106 8 — выполнить 8 команд, начиная с адреса CS:106.
3.2) G[=стартовый адрес] [адрес останова] Go — прогон
а) g — выполнить программу, начиная с адреса, хранящегося в IP. Про-грамма должна заканчиваться инструкциями завершения (эти инструкции мы изучим позднее — сейчас упоминаем о них только в справочных целях), на-пример, INT 20h или MOV AH,4Ch /INT 21h. После выполнения INT 20h debug выводит сообщение:
Program terminated normally (Выполнение программы завершено) Теперь повторный запуск программы возможен только после выполнения команды L (см. ниже).
б) g=100 адрес команды nop
Мы рекомендуем на начальном этапе обучения завершать учебные про-граммы, набираемые в debug, командой nop и использовать ее адрес в качестве адреса останова.
4) Работа с файлами и дисками.
4.1) N имя файла Name — имя
Задание имени файла. Расширение указывать обязательно. Используется перед следующими двумя командами.
4.2) L [адрес] Load — загрузка
Содержимое файла переписывается в ОЗУ. Файлы с любым расширением кроме .exe — начиная с указанного адреса (если адрес опущен, то начиная с CS:0100). Для файлов с расширением .exe адрес игнорируется, используется стартовый адрес, записанный в самом файле.
По окончании загрузки в BX:CX содержится размер файла в байтах.
4.3) W [адрес] Write — запись
Запись файла на диск. Предварительно в BX:CX нужно записать размер файла. Если адрес не указан, то подразумевается CS:0100.
У команд L и W имеются также параметры для работы с секторами диска. Мы их не рассматриваем.
5) Сервис.
5.1) H <величина> <величина> Hex — шестнадцатеричный
Встроенный калькулятор. Вычисляет сумму и разность указанных величин:
H a b (a и b — слова)
a+b a-b
Примеры: 1) Программа начинается по адресу 0100h и заканчивается по ад-ресу 012Ah. Определить размер программы.
-h12a ff
0229 002B (Размер программы 2Bh)
2) В байте записано число FEh. Дополнительным кодом какого числа оно явля-ется? Расширяем знак до размеров слова
-H0 fffe
fffe 0002 (Дополнительный код ¬–2 есть FEh)
5.2) M область-источник адрес_приемника Move — перемещать
Пример. Предположим, мы набрали программу
-a100
11CF:0100 mov ax,1
11CF:0103 add ax,bx
11CF:0105 nop
11CF:0106
и обнаружили, что пропустили команду mov bx,2. Перемещаем часть про-граммы «подальше». Размер области берем «с запасом».
-m103L20 200
Вводим недостающую команду
-a103
11CF:0103 mov bx,2
11CF:0106
Теперь мы знаем адрес назначения (0106) и перемещаем хвост программы
-m200L20 106
-u100 (проверяем правильность результата) …
Упражнение. Разработайте последовательность действий, если нужно уда-лить часть команд программы. О «перекрытии» области-приемника и области-источника беспокоиться не нужно (проверьте!).
5.3) C область1 адрес_области Compare — сравнение
Выводится таблица в формате
адрес1 байт1 байт2 адрес2
в том случае, если байт1 не совпадает с байт2.
Упражнение. Придумайте пример. С помощью команды E введите данные в две области памяти (например, две незначительно различающиеся строки) и сравните содержимое областей с помощью команды C.
5.4) S область список Search — поиск
Список — это список байтов или строка. Debug выдает все адреса байтов или строк, которые совпадают с указанными.
Упражнение. Придумайте примеры.
6) Остальные команды.
6.1) Q Quit — завершение
Осуществляется выход из debug. При этом набранные программы и данные теряются, если они предварительно не были сохранены посредством команды W.
6.2) ? Выводит на экран алфавитный список команд debug.

3.5. Как получить текст программы, набранной в debug.
После отладки внесения необходимых изменений в команды программы хотелось бы получить файл с текстом программы, чтобы добавить в него ком-ментарии, распечатать и т.д. Можно рекомендовать следующую последова-тельность действий.
1) Определяем размер программного кода. Если вся программа состоит из чистого кода, как pr1.com, даем команды
D:USER>debug pr1.com
-r
и читаем в регистрах BX:CX шестнадцатеричный размер программы. (В нашем случае 9h).
2) Создадим с помощью любого текстового редактора файл (например, pr1.cmd), содержащий команды отладчика:
U100L9
Q
(последнюю команду Q обязательно завершить нажатием Enter). Вновь запуска-ем debug.
D:USER>debug pr1.com < pr1.cmd > pr1.txt
Входная информация поступает из pr1.cmd, выходная направляется в файл pr1.txt. Для этого используются символы > и < (перенаправление операций ввод-вывода).
Остается подправить содержимое файла pr1.txt в редакторе.
Упражнение. В предыдущей главе мы выясняли, какие флаги состояния будут выставлены центральным процессором при выполнении сложения слов: 2345h + 3219h и 5439h + 456Ah. Проверить правильность сделанных выводов, используя debug.

DEBUG.EXE
– специальная системная программа для
ввода и пошагового выполнения программ,
написанных на машинном языке или с
помощью команд ассемблера.

Для
запуска этой программы нажмите кнопку
Пуск,
затем Выполнить,
введите Debug
и нажмите ввод,
в результате программа должна загрузиться
с диска в память.

После
окончания загрузки на экране появится
приглашение в виде дефиса, что
свидетельствует о готовности программы
Debug
для приема команд.

Наиболее
часто используемые инструкции
Debug:

1.
Q
– выход из программы.

2.
?

– получение справки.

3.
H
(Hexarithmetic)
– шестнадцатеричная арифметика. Если
после символа Н набрать 2 числа (размером
не более 4 цифр каждое) через пробел, то
получим сумму и разность набранных
чисел:

4.
R
[<имя регистра>] – работа с регистрами
(от слова Register).
Инструкция «R»
без параметра позволяет просмотреть
содержимое всех регистров, а также
значение флагов и команду, расположенную
по смещению 0100 в сегменте кода. Обычно
по смещению 0100 находится первая команда
программы.

С
помощью этой инструкции можно ввести
новое значение в один регистр. Для этого
после команды нужно ввести название
регистра:

Как
видим, при первом просмотре регистров
командой r,
в регистре АХ был 0, затем содержимое
регистра было изменено.

5.
U
[<сегментный
регистр>:]<начальный адрес>, <конечный
адрес> – просмотр ячеек оперативной
памяти, начиная с указанного смещения
в сегменте, заданном сегментным
регистром. Команда U
(Unassemble)
дизассемблирует команды, находящиеся
в памяти по заданному адресу.

Например,
в следующем примере мы просматриваем
команды, находящиеся в регистре CS,
начиная с адреса 100, по адрес 102.

6.
Е

[<сегментный регистр>:]<смещение>
– запись информации в ячейки оперативной
памяти.

Как
было сказано, с помощью Debug
можно вводить команды как на машинном
языке, так и на языке ассемблера.
Инструкция Debug
E
(Enter)
служит для ввода
команд на машинном языке
.
Здесь Debug
используется как интерпретатор, чтобы
работать непосредственно с микропроцессором.
Можно задавать машинные команды,
записывать их в определенное место
оперативной памяти, обычно, начиная с
0100 смещения относительно начала сегмента
кода. Затем выполнять пошагово (по одной
команде) либо сразу всю программу.

Так
как микропроцессор понимает только
двоичные числа (сокращенно их можно
записать шестнадцатеричными), то и коды
машинных команд записываются в виде
шестнадцатеричных чисел, причем команды
бывают одно-, двух- трехбайтовые и т.д.

Например,
команда для сложения значений из
регистров АХ и ВХ двухбайтовая и имеет
машинный код 01D8.

Инструкция
“E
<смещение>” при работе распечатывает
в следующей после ее ввода строке адрес,
состоящий из двух чисел, и старое
значение байта по этому адресу:

<содержимое CS>:<смещение><значение
байта>, далее компьютер ожидает ввода
нового значения байта.

Для
ввода, например, двухбайтовой машинной
команды 01D8
нужно записать 01 в сегмент кода по
смещению 100, и D8
– по смещению 101:

7.
Т
(от
Tracing)

запуск программы, находящейся в
оперативной памяти по адресу 0100, в
пошаговом режиме. Для вызова программы
на выполнение нужно предварительно
позаботиться, чтобы в регистре IP
(счетчик команд) было число 0100.

8.
А
<смещение>
ввод
команд в ассемблерном виде

(от Assemble).
Первую команду программы следует
начинать вводить со смещения 0100. Далее
система ждет поочередного ввода команд
ассемблера. Для окончания ввода нужно
нажать Enter
после пустой строки.

Пример.
Введем в оперативную память программу
на ассемблере, выполняющую сложение
двух чисел. Установим начальный адрес
следующим образом: A
0100 [Enter].

Отладчик
выдаст значение адреса сегмента кодов
и смещения в виде хххх:0100. Теперь можно
вводить каждую команду, завершая
клавишей Ввод.

Прежде
чем выполнить программу, проверим
сгенерированные машинные коды при
помощи команды U.
Необходимо сообщить отладчику адреса
первой и последней команд, которые
необходимо просмотреть, в данном случае
0100 и 0106. Введите: U
100,
106

Как
видим, машинные коды сгенерированы
верно.

С
помощью команды R
выведем содержимое регистров и первую
команду нашей программы:

С
помощью команд Т выполним последовательно
все команды программы:

Так
вводятся и трассируются программы на
языке ассемблера.

Изучим
остальные команды Debug,
после чего вернемся к примеру.

9.
G
(от
Go)
– запуск программы, находящейся в
оперативной памяти по адресу 0100. Для
вызова программы на выполнение нужно
предварительно позаботиться, чтобы в
регистре IP
(счетчик команд) было число 0100.

10.
N
<имя
.com-файла>
– задает имя программы (Name)
для последующей записи ее на диск либо
считывания с диска. Перед записью
предварительно нужно записать 0 в
регистр ВХ, а размер программы (в байтах)
– в регистр СХ.

11.
W
(от
Write)
– запись программы на диск. После
выполнения инструкции “W”
на диске в текущей директории появится
файл с расширением .COM
– точная копия веденной программы.

12.
L
загрузка
программы
с диска в оперативную память. Предварительно
имя программы задается с помощью команды
N.

Вернемся
к примеру со сложением двух чисел. Чтобы
записать эту программу на диск, следует
выполнить три действия:

1.
присвоить программе имя;

2.
указать длину программы;

3.
выполнить запись.

1.
Присвоим будущему файлу имя с помощью
команды N.

2.
Программа состоит из четырех двухбайтных
инструкций, занимает адреса со смещениями
с 0100 по 0106, значит ее длина – 8 байт.
Размер программы Debug
хранит в регистровой паре [BX:CX].
В нашем случае значение длины умещается
в одном регистре, поэтому старший
регистр пары мы просто обнулим: BX=0,
CX=8.

3.
Для записи программы на диск используем
программу W.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

  • При скачивании ошибка 504
  • При следующем подключении к интернету windows может провести поиск способа устранения этой ошибки
  • При скачивании код ошибки 924
  • При скачке торрента ошибка системе не удается найти указанный путь
  • При скачивании картинки пишет ошибка сети что делать