Питер Абель. Ассемблер и программирование для IBM PC
страница №4
...рактическогоприменения ознакомьтесь с руководством по вашему aссемблера.
Ассемблер для IBM PC. Глава 5 109
На рис. 5.1 приведены примеры директивы DT для
неопределенного элемента и для двухсимвольной константы.
Программа на рис.5.1 содержит только сегмент данных. Xотя
асcемблер не выдает сообщений об ошибках, в таблице LINK MAP
появится предупреждение: "Warning: No STACK Segment", а
компановщик LINK выдаст "There were 1 errors detected"
(Обнаружена 1 ошибка). Несмотря на это предупреждение
можно использовать отладчик DEBUG для просмотра объектного
кода, как показано на рис. 5.2.
Правая сторона дампа отчетливо показывает символьные
данные, как, например, "Personal Computer".
НЕПОСРЕДСТВЕННЫЕ ОПЕРАНДЫ
На рис. 2.1 в главе 2 было показано использование
непосредственных операндов. Команда
MOV AX,0123H
пересылает непосредственную шест. константу 0123 в регистр
AX. Трехбайтный объектный код для этой команды есть B82301,
где B8 обозначает "переслать непосредственное значение в
регистр AX", a следующие два байта содержат само значение.
Многие команды имеют два операнда: первый может быть регистр
или адрес памяти, а второй - непосредственная константа.
Рис. 5.2. Дамп сегмента данных.
Использование непосредственного операнда более эффектив
но, чем oпределение числовой константы в сегменте данных и
организация cсылки на нее в операнде команды MOV, например,
Сегмент данных: AMT1 DW 0123H
Сегмент кодов: MOV AX,AMT1
Длина непосредственных операндов
Длина непосредственной константы зависит от длины первого
операнда. Например, следующий непосредственный операнд
является двухбайтовым, но регистр AL имеет только один байт:
MOV AL,0123H (ошибка)
однако, если непосредственный операнд короче, чем получающий
операнд, как в следующем примере
ADD AX,25H (нет ошибки)
то ассемблер расширяет непосредственный операнд до двух
байт, 0025 и записывает объектный код в виде 2500.
Ассемблер для IBM PC. Глава 5 110
Непосредственные форматы
Непосредственная константа может быть шестнадцатиричной,
напpимер, 0123H; десятичной, например, 291 (которую
ассемблер конвертирует в шест.0123); или двоичной,
например, 100100011В (которая преобразуется в шест. 0123).
Ниже приведен список команд, которые допускают
непосредственные операнды:
Команды пересылки и сравнения: MOV, CMP.
Арифметические команды: ADC, ADD, SBB, SUB.
Команды сдвига: RCL, RCR, ROL, ROR, SHL, SAR, SHR.
Логические команды: AND, OR, TEST, XOR.
На рис. 5.3 приведены примеры допустимых команд с
непосредственными операндами. В последующих главах будут
объяснены команды арифметического переноса, сдвига и
логические команды. Поскольку сейчас данные примеры не
предназначены для выполнения, в них опущено определение
стека и инициализация сегментных регистров.
Для создания элементов, длинее чем два байта, можно
использовать цикл (см. гл.7) или строковые команды (см.
гл.11).
Рис. 5.3. Команды с непосредственными данными.
ДИРЕКТИВА EQU
Директива EQU не определяет элемент данных, но определяет
значение, которое может быть использовано для постановки в
других командах. Предположим, что в сегменте данных
закодирована следующая директива EQU:
TIMES EQU 10
Имя, в данном случае TIMES, может быть представлено любым
допустимым в ассемблере именем. Теперь, в какой-бы команде
или директиве не использовалось слово TIMES ассемблер
подставит значение 10. Например, ассемблер преобразует
директиву
FIELDA DB TIMES DUP (?)
в
FIELDA DB 10 DUP (?)
Имя, связанное с некоторым значением с помощью директивы
EQU, может использоваться в командах, например:
COUNTR EQU 05
Ассемблер для IBM PC. Глава 5 111
...
MOV CX,COUNTR
Ассемблер заменяет имя COUNTR в команде MOV на значение
05, cоздавая операнд с непосредственным значением, как если
бы было закодировано
MOV CX,05 ;Ассемблер подставляет 05
Здесь приемущество директивы EQU заключается в том, что
многие команды могут использовать значение, определенное по
имени COUNTR. Если это значение должно быть изменено, то
изменению подлежит лишь одна директива EQU. Естественно,
что использование директивы EQU разумно лишь там, где
подстановка имеет смысл для ассемблера. В директиве EQU
можно использовать символические имена:
1. TP EQU TOTALPAY
2. MPY EQU MUL
Первый пример предполагает, что в сегменте данных программы
опpеделено имя TOTALPAY. Для любой команды, содержащей
операнд TP, ассемблер заменит его на адрес TOTALPAY. Второй
пример показывает возможность использования в программе
слова MPY вместо обычного мнемокода MUL.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
ъ Имена элементов данных в программе должны быть уникаль
ны и по возможности наглядны. Например, элемент для
зарплаты служащего может иметь имя EMPWAGE.
ъ Для определения символьных строк используйте директиву
DB, так как ее формат допускает строки длиннее двух
байт и формирует их в нормальной последовательности
(слева-направо).
ъ Будьте внимательны при указании десятичных и шест.
значений. Сравните, например, сложение содержимого
регистра AX с десятичным 25 и с шест. 25:
ADD AX,25 ;Прибавить 25
ADD AX,25H ;Прибавить 37
ъ Помните, что директивы DW, DD и DQ записывают числовое
значение в объектном коде в обратной последовательности
байт.
Ассемблер для IBM PC. Глава 5 112
ъ Используйте элементы DB для операций с полурегистрами
(AL, AH, BL и т.д.) и DW для операций с полными
регистрами (AX, BX, CX и т.д.). Числовые элементы,
определенные директивами DD и DQ имеют специальное
применение.
ъ Следите за соответствием непосредственных операндов
размеру регистра: однобайтовая константа - однобайтовый
регистр (AL, BH), двухбайтовая константа - полный
регистр (AX, BX).
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
5.1. Какова длина в байтах для элементов данных, определен
ных директивами: а) DW, б) DD, в) DT, г) DB, д) DQ?
5.2. Определите символьную строку по имени TITLE1,
содержащую константу RGB Electronics.
5.3. Определите следующие числовые значения в элементах
данных с именами от FLDA до FLDE:
a) четырехбайтовый элемент, содержащтй шест. эквивалент
десятичного числа 115;
b) однобайтовый элемент, содержащий шест. эквивалент
десятичного числа 25;
c) двухбайтовый элемент, содержащий неопределенное
значение;
d) однобайтовый элемент, содержащий двоичной эквивалент
десятичного числа 25;
e) директиву DW, содержащую последовательные значения
16, 19, 20, 27, 30.
5.4. Покажите сгенерированный шест. объектный код для
а) DB '26' и б) DB 26.
5.5. Определите ассемблерный шест. объектный код для
а) DB 26H, б) DW 2645H, в) DD 25733AH, г) DQ 25733AH.
5.6. Закодируйте следующие команды с непосредственными
операндами:
а) загрузить 320 в регистр AX;
б) сравнить поле FLDB с нулем;
в) прибавить шест. 40 к содержимому регистра BX;
г) вычесть шест. 40 из регистра CX;
д) сдвинуть содержимое поля FLDB на один бит влево;
е) сдвинуть содержимое регистра CH на один бит вправо.
5.7. Введите и ассемблируйте элементы данных и команды из
вопросов 5.2, 5.3 и 5.6. Стек для этого упражнения не
требуется. Также не следует выполнять компановку. Для
проверки ассемблированного кода используйте отладчик
DEBUG. Распечатайте LST-файл (листинг), если в результа
Ассемблер для IBM PC. Глава 5 113
те ассемблирования не будет сообщений об ошибках. Не
забудте команду MODE LPT1:132,6 для установки ширины
печати.
Ассемблер для IBM PC. Глава 6 123
ГЛАВА 6. Программы в COM-файлах
Программы в COM-файлах
Цель: Объяснить назначение и использование COM-файлов и
перевод ассемблерных программ в формат COM-файлов.
ВВЕДЕНИЕ
До сих пор вы писали, ассемблировали и выполняли програм
мы в EXE-формате. Компановщик LINK автоматически генерирует
особый формат для EXE-файлов, в котором присутствует
специальный начальный блок (заголовок) pазмером не менее 512
байт. (В главе 22 рассматривается содержимое начальных
блоков).
Для выполнения можно также создавать COM-файлы. Примером
часто используемого COM-файла является COMMAND.COM. Програм
ма EXE2BIN.COM в оперативной системе DOS преобразует EXE-
файлы в COM-файлы. Фактически эта программа создает BIN
(двоичный) файл, поэтому она и называется "преобразователь
EXE в Вin (EXE-to-BIN)". Выходной Вin-файл можно переимено
вать в COM-файл.
РАЗЛИЧИЯ МЕЖДУ ПРОГРАММАМИ В EXE и COM-файлах
Несмотря на то, что EXE2BIN преобразует EXE-файл в
COM-файл, cуществуют определенные различия между программой,
выполняемой как EXE-файл и программой, выполняемой как
COM-файл.
Размер программы. EXE-программа может иметь любой размер,
в то время как COM-файл ограничен размером одного сегмента и
не превышает 64К. COM-файл всегда меньше, чем соответствую
щий EXE-файл; одна из причин этого - отсутствие в COM-файле
512-байтового начального блока EXE-файла.
Сегмент стека. В EXE-программе определяется сегмент сте
ка, в то время как COM-программа генерирует стек автоматичес
ки. Таким образом при создании ассемблерной программы,
которая будет преобразована в COM-файл, стек должен быть
опущен.
Сегмент данных. В EXE программе обычно определяется сег
мент данных, а регистр DS инициализируется адресом этого
сегмента. В COM-программе все данные должны быть определены
в сегменте кода. Ниже будет показан простой способ решения
этого вопроса.
Ассемблер для IBM PC. Глава 6 124
Инициализация. EXE-программа записывает нулевое слово в
стек и инициализирует регистр DS. Так как COM-программа не
имеет ни стека, ни сегмента данных, то эти шаги отсутствуют.
Когда COM-программа начинает работать, все сегментные ре
гистры содержат адрес префикса программного сегмента (PSP),
- 256-байтового (шест. 100) блока, который резервируется
операционной системой DOS непосредственно перед COM или EXE
программой в памяти. Так как адресация начинается с шест.
смещения 100 от начала PSP, то в программе после оператора
SEGMENT кодируется директива ORG 100H.
Обработка. Для программ в EXE и COM форматах выполняется
ассемблирование для получения OBJ-файла, и компановка для
получения EXE-файла. Если программа создается для выполнения
как EXE-файл, то ее уже можно выполнить. Если же программа
создается для выполнения как COM-файл, то компановщиком
будет выдано сообщение:
Warning: No STACK Segment
(Предупреждение: Сегмент стека не определен)
Это сообщение можно игнорировать, так как определение стека
в программе не предполагалось. Для преобразования EXE-файла
в COM-файл используется программа EXE2BIN. Предположим, что
EXE2BIN имеется на дисководе A, а скомпанованный файл по
имени CALC.EXE - на дисководе B. Введите
EXE2BIN B:CALC,B:CALC.COM
Так как первый операнд всегда предполагает EXE файл, то
можно не кодировать тип EXE. Второй операнд может иметь
другое имя (не CALC.COM). Если не указывать тип COM, то
EXE2BIN примет по умолчанию тип BIN, который впоследствии
можно переименовать в COM. После того как преобразование
будет выполнено можно удалить OBJ- и EXE-файлы.
Если исходная программа написана для EXE-формата, то мож
но, используя редактор, заменить команды в исходном тексте
для COM файла.
ПРИМЕР COM-ПРОГРАММЫ
Программа EXCOM1, приведенная на рис. 6.1, аналогична
программе на рис. 4.3, но изменена согласно требований COM-
формата. Обратите внимание на следующие изменения в этой
COM-программе:
ъ Стек и сегмент данных отсутствует.
ъ Оператор ASSUME указывает ассемблеру установить относи
тельные адреса с начала сегмента кодов. Регистр CS
также содержит этот адрес, являющийся к тому же адресом
префикса программного сегмента (PSP). Директива ORG
служит для резервирования 100 (шест.) байт от начально
го адреса под PSP.
Ассемблер для IBM PC. Глава 6 125
ъ Директива ORG 100H устанавливает относительный адрес
для начала выполнения программы. Программный загрузчик
использует этот адрес для командного указателя.
ъ Команда JMP используется для обхода данных, определен
ных в программе.
Ниже показаны шаги для обработки и выполнения этой
программы:
MASM [ответы на запросы обычные]
LINK [ответы на запросы обычные]
EXE2BIN B:EXCOM1,B:EXCOM1.COM
DEL B:EXCOM1.OBJ,B:EXCOM1.EXE (удаление OBJ и EXE-файлов)
Размеры EXE- и COM-программ - 788 и 20 байт соответствен
но. Учитывая такую эффективность COM-файлов, рекомендуется
все небольшие программы создавать для COM-формата. Для
трассировки выполнения программы от начала (но не включая)
команды RET введите DEBUG B:EXCOM1.COM.
Некоторые программисты кодируют элементы данных после
команд так, что первая команда JMP не требуется.
Кодирование элементов данных перед командами позволяет
ускорить процесс ассемблирования и является методикой,
рекомендуемой в руководстве по ассемблеру.
Рис. 6.1. Пример COM-программы.
СТЕК ДЛЯ COM-ПРОГРАММЫ
Для COM-файла DOS автоматически определяет стек и устанав
ливает oдинаковый общий сегментный адрес во всех четырех
сегментных pегистрах. Если для программы размер сегмента в
64К является достаточным, то DOS устанавливает в регистре SP
адрес конца cегмента - шест.FFFE. Это будет верх стека.
Если 64К байтовый сегмент не имеет достаточно места для
стека, то DOS устанавливает стек в конце памяти. В обоих
случаях DOS записывает затем в стек нулевое слово.
Возможность использования стека зависит от размера про
граммы и ограниченности памяти. С помощью команды DIR можно
определить pазмер файла и вычислить необходимое пространство
для стека.
Все небольшие программы в этой книге в основном расчитаны
на COM-формат.
ОСОБЕННОСТЬ ОТЛАДКИ
Несоблюдение хотя бы одного требования COM-формата может
послужить причиной неправильной работы программы. Если
EXE2BIN обнаруживает oшибку, то выдается сообщение о
Ассемблер для IBM PC. Глава 6 126
невозможности преобразования файла без указания конкретной
причины. Необходимо проверить в этом случае директивы
SEGMENT, ASSUME и END. Если опущен ORG 100H, то на данные в
префиксе программного сегмента будут установлены неправиль
ные ссылки с непредсказуемым результатом при выполнении.
При выполнении COM-программы под управлением отладчика
DEBUG необходимо использовать команду D CS:100 для просмотра
данных и команд. Не следует выполнять в отладчике команду
RET; предпочтительнее использовать команду Q отладчика.
Некоторые программисты используют INT 20H вместо команды
RET.
Попытка выполнить EXE-модуль программы, написанной для
COM-формата, не имеет успеха.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
ъ Объем COM-файла ограничен 64К.
ъ COM-файл меньше, чем соответствующий EXE-файл.
ъ Программа, написанная для выполнения в COM-формате не
содержит стека и сегмента данных и не требует инициали
зации регистра DS.
ъ Программа, написанная для выполнения в COM-формате
использует директиву ORG 100H после директивы SEGMENT
для выполнения с адреса после префикса программного
сегмента.
ъ Программа EXE2BIN преобразует EXE-файл в COM-файл,
обусловленный указанием типа COM во втором операнде.
ъ Операционная система DOS определяет стек для COM-прог
раммы или в конце программы, если позволяет размер,
или в конце памяти.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
6.1. Каков максимальный размер COM-файла?
6.2. Какие сегменты можно определить в программе, которая
будет преобразована в COM-файл?
6.3. Как обходится COM-файл при выполнении с фактом отсут
ствия определения стека?
6.4. Программа в результате компановки получала имя
SAMPLE.EXE. Напишите команду DOS для преобразования ее
в COM-файл.
Ассемблер для IBM PC. Глава 6 127
6.5. Измените программу из вопроса 4.6 для COM-формата, обра
ботайте ее и выполните под управлением отладчика DEBUG.
Ассемблер для IBM PC. Глава 7 132
ГЛАВА 7. Логика и Организация Программы
Логика и Организация Программы
Цель: Раскрыть механизм передачи управления в программе
(циклы и переходы) для логических сравнений и программной
организации.
ВВЕДЕНИЕ
До этой главы примеры выполнялись последовательно команда
за командой. Однако, программируемые задачи редко бывают так
просты. Большинство программ содержат ряд циклов, в которых
несколько команд повторяются до достижения определенного
требования, и различные проверки, определяюшие какие из
нескольких действий следует выполнять. Обычным требованием
является проверка - должна ли программа завершить
выполнение.
Эти требования включают передачу управления по адресу
команды, которая не находится непосредственно за выполняемой
в текущий момент командой. Передача управления может осущест
вляться вперед для выполнения новой группы команд или назад
для повторения уже выполненных команд.
Некоторые команды могут передавать управление, изменяя
нормальную последовательность шагов непосредственной
модификацией значения смещения в командном указателе. Ниже
приведены четыре способа передачи управления (все будут
рассмотрены в этой главе):
Безусловный переход: JMP
Цикл: LOOP
Условный переход: Jnnn (больше,меньше,равно)
Вызов процедуры: CALL
Заметим, что имеется три типа адресов: SHORT, NEAR и FAR.
Адресация SHORT используется при циклах, условных пеpеходах
и некоторых безусловных переходах. Адресация NEAR и FAR
используется для вызовов процедур (CALL) и безусловных
переходов, которые не квалифицируются , как SHORT. Все три
типа передачи управления воздействуют на содержимое регистра
IP; тип FAR также изменяет регистр CS.
КОМАНДА JMP
Одной из команд обычно используемых для передачи управле
ния является команда JMP. Эта команда выполняет безусловный
переход, т.е. обеспечивает передачу управления при любых
обстоятельствах.
Ассемблер для IBM PC. Глава 7 133
В COM-программе на рис. 7.1 используется команда JMP. В
pегистры AX, BX, и CX загружается значение 1, и затем в
цикле выполняются следующие операции:
прибавить 1 к регистру AX,
прибавить AX к BX,
удвоить значение в регистре CX.
Повторение цикла приводит к увеличению содержимого регистра
AX: 1,2,3,4..., регистра BX: 1,3,6,10..., и регистра CX:
1,2,4,8... Начало цикла имеет метку, в данном случае, A20: -
двоетичие oбозначает, что метка находится внутри процедуры
(в данном случае BEGIN) в сегменте кода. В конце цикла
находится команда
JMP A20
которая указывает на то, что управление должно быть передано
команде c меткой A20. Обратите внимание, что адресная метка
в операнде команды указывается без двоеточия. Данный цикл
не имеет выхода и приводит к бесконечному выполнению - такие
циклы обычно не используются.
Рис.7.1. Использование команды JMP.
Метку можно кодировать на одной строке с командой:
A20: ADD AX,01
или на отдельной строке:
A20:
ADD AX,01
В обоих случаях адрес A20 указывает на первый байт команды
ADD. Двоеточие в метке A20 указывает на тип метки - NEAR.
Запомните: отсутствие двоеточия в метке является частой
ошибкой. В нашем примере A20 соответствует -9 байтам от
команды JMP, в чем можно убедиться по объектному коду
команды - EBF7. EB представляет собой машинный код для
короткого перехода JMP, а F7 - отрицательное значение
смещения (-9). Команда JMP прибавляет F7 к командному
указателю (IP), котоpый содержит адрес команды после JMP
(0112):
Дес. Шест.
Командный указатель: 274 112
Адрес в команде JMP: -9 F7 (двоичное дополнение)
Адрес перехода: 265 109
Ассемблер для IBM PC. Глава 7 134
В результате сложения получается адрес перехода - шест. 109.
Проверьте по листингу программы, что относительный адрес
метки действительно соответствует шест.109. Соответственно
операнд в команде JMP для перехода вперед имеет положитель
ное значение.
Команда JMP для перехода в пределах -128 до +127 байт
имеет тип SHORT. Ассемблер генерирует в этом случае
однобайтовый операнд в пределах от 00 до FF. Команда JMP,
превосходящая эти пределы, получает тип FAR, для которого
генерируется другой машинный код и двухбайтовый операнд.
Ассемблер в первом просмотре исходной программы определяет
длину каждой команды. Однако, команда JMP может быть длиной
два или три байта. Если к моменту просмотра команды JMP
ассемблер уже вычислил значение опеpанда (при переходе
назад):
A50:
...
JMP A50
то он генерирует двухбайтовую команду. Если ассемблер еще не
вычислил значение операнда (при переходе вперед)
JMP A90
...
A90:
то он не знает тип перехода NEAR или FAR, и автоматически
генерирует 3-х байтовую команду. Для того, чтобы указать
ассемблеру на необходимость генерации двухбайтовой команды,
следует использовать оператор SHORT:
JMP SHORT A90
...
A90:
В качестве полезного упражнения, введите программу,
проассемблируйте ее, скомпануйте и переведите в COM-формат.
Определение данных не требуется, поскольку непосредственные
операнды генерируют все необходимые данные. Используйте
отладчик DEBUG для пошагового выполнения COM-модуля и
просмотрите несколько повторений цикла. Когда регистр AX
будет содержать 08, BX и CX увеличатся до шест. 24 (дес.
36) и шест. 80 (дес. 128), соответственно. Для выхода из
отладчика используйте команду Q.
КОМАНДА LOOP
Команда JMP в примере на рис. 7.1 реализует бесконечный
цикл. Но более вероятно подпрограмма должна выполнять
определенное число циклов. Команда LOOP, которая служит для
этой цели, использует начальное значение в регистре CX. В
каждом цикле команда LOOP автоматически уменьшает содержимое
Ассемблер для IBM PC. Глава 7 135
регистра CX на 1. Пока значение в CX не равно нулю,
управление передается по адресу, указанному в операнде, и
если в CX будет 0, управление переходит на слудующую после
LOOP команду.
Программа на рис. 7.2, иллюстрирующая использование коман
ды LOOP, выполняет действия, аналогичные примеру на рис. 7.1
за исключением того, что после десяти циклов программа
завершается. Команда MOV инициализирует регистр CX значением
10. Так как команда LOOP использует регистр CX, то в
программе для удвоения начального значения 1 вместо регистра
CX используется DX. Команда JMP A20 заменена командой LOOP
и для эффективности команда ADD AX,01 заменена командой INC
AX (увеличение AX на 1).
Аналогично команде JMP, операнд команды LOOP определяет
расстояние от конца команды LOOP до адреса метки A20, кото
рое прибавляется к содержимому командного указателя. Для
команды LOOP это расстояние должно быть в пределах от -128
до +127 байт. Если операнд превышает эти границы, то ассемб
лер выдаст сообщение "Relative jump out of range" (превышены
границы перехода).
Для проверки команды LOOP рекомендуется изменить
соответствующим образом программу, приведенную на рис.7.1,
выполнить ее ассемблирование, компановку и преобразование в
COM-файл. Для трассировки всех десяти циклов используйте
отладчик DEBUG. Когда в значение регистре CX уменьшится до
нуля, содержимое регистpов AX, BX и DX будет соответственно
шест. 000B, 0042 и 0400. Для выхода из отладчика введите
команду Q.
Дополнительно существует две разновидности команды LOOP -
это LOOPE (или LOOPZ) и LOOPNE (или LOOPNZ). Обе команды
также уменьшают значение регистра CX на 1. Команда LOOPE
передает управление по адресу операнда, если регистр CX
имеет ненулевое значение и флаг нуля установлен (ZF=1).
Команда LOOPNE передает управление по адресу операнда, если
регистр CX имеет ненулевое значение и флаг нуля сброшен
(ZF=0).
Рис. 7.2. Использование команды LOOP.
ФЛАГОВЫЙ РЕГИСТР
Следующий материал данной главы требует более детального
ознакомления с флаговым регистром. Этот pегистр содержит 16
бит флагов, которые управляются различными командами для
индикации состояния операции. Во всех случаях флаги сохраня
ют свое значение до тех пор, пока другая команда не изменит
его. Флаговый регистр содержит следующие девять используемых
бит (звездочками отмечены неиспользуемые биты):
Номер бита: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Ассемблер для IBM PC. Глава 7 136
Флаг: * * * * O D I T S Z * A * P * C
Рассмотрим эти флаги в последовательности справа налево.
CF (Carry Flag) - флаг переноса. Содержит значение
"переносов" (0 или 1) из старшего разряда при арифметичес
ких операциях и некоторых операциях сдвига и циклического
сдвига (см. гл.12).
PF (Parity Flag) - флаг четности. Проверяет младшие
восемь бит pезультатов операций над данными. Нечетное число
бит приводит к установке этого флага в 0, а четное - в 1. Не
следует путать флаг четности с битом контроля на четность.
AF (Auxiliary Carry Flag) - дополнительный флаг переноса.
Устанавливается в 1, если арифметическая операция приводит
к переносу четвертого справа бита (бит номер 3) в регистро
вой однобайтовой команде. Данный флаг имеет отношение к
арифметическим операциям над символами кода ASCII и к
десятичным упакованным полям.
ZF (Zero Flag) - флаг нуля. Устанавливается в качестве
результата aрифметических команд и команд сравнения. Как
это ни странно, ненулевой результат приводит к установке
нулевого значения этого флага, а нулевой - к установке
единичного значения. Кажущееся несоответствие является,
однако, логически правильным, так как 0 обозначает "нет"
(т.е. результат не равен нулю), а единица обозначаeт "да"
(т.е. результат равен нулю). Команды условного перехода JE и
JZ проверяют этот флаг.
SF (SIgn Flag) - знаковый флаг. Устанавливается в соответ
ствии со знаком результата (старшего бита) после арифмети
ческих опеpаций: положительный результат устанавливает 0, а
отрицательный - 1. Команды условного перехода JG и JL
проверяют этот флаг.
TF (Trap Flag) - флаг пошагового выполнения. Этот флаг
вам уже приходилось устанавливать, когда использовалась ко
манда Т в отладчике DEBUG. Если этот флаг установлен в еди
ничное cостояние, то процессор переходит в режим пошагового
выполнения команд, т.е. в каждый момент выполняется одна
команда под пользовательским управлением.
IF (Interrupt Flag) - флаг прерывания. При нулевом состоя
нии этого флага прерывания запрещены, при единичном -
разрешены.
DF (DIrection Flag) - флаг направления. Используется в
строковых операциях для определения направления передачи
данных. При нулевом состоянии команда увеличивает содержимое
Ассемблер для IBM PC. Глава 7 137
регистров SI и DI, вызывая передачу данных слева направо,
при нулевом - уменьшает содержимое этих регистров, вызывая
передачу данных справа налево (см. гл.11).
OF (Overflow Flag) - флаг переполнения. Фиксирует арифме
тическое переполнение, т.е. перенос в/из старшего (знаково
го) бита при знаковых арифметических операциях.
В качестве примера: команда CMP сравнивает два операнда
и воздействуте на флаги AF, CF, OF, PF, SF, ZF. Однако, нет
необходимости проверять все эти флаги по отдельности. В сле-
дующем примере проверяется содержит ли регистр BX нулевое
значение:
CMP BX,00 ;Сравнение BX с нулем
JZ B50 ;Переход на B50 если нуль
. (действия при ненуле)
.
B50: ... ;Точка перехода при BX=0
Если BX содержит нулевое значение, команда CMP устанавливает
флаг нуля ZF в единичное состояние, и возможно изменяет (или
нет) другие флаги. Команда JZ (перехлд если нуль) проверяет
только флаг ZF. При единичном значении ZF, обозначающее
нулевой признак, команда передает управление на адрес,
указанный в ее операнде, т.е. на метку B50.
КОМАНДЫ УСЛОВНОГО ПЕРЕХОДА
В предыдущих примерах было показано, что команда LOOP
уменьшает на единицу содержимое регистра CX и проверяет его:
если не ноль, то управление передается по адресу, указанному
в операнде. Таким образом, передача управления зависит от
конкретного состояния. Ассемблер поддерживает большое
количество команд условного перехода, которые осуществляют
передачу управления в зависимости от состояний флагового
регистра. Например, при сравнении содержимого двух полей
последующий переход зависит от значения флага.
Команду LOOP в программе на рис.7.2 можно заменить на две
команды: одна уменьшает содержимое регистра CX, а другая
выполняет условный переход:
Использование LOOP Использование условного перехода
LOOP A20 DEC CX
JNZ A20
Команды DEC и JNZ действуют аналогично команде LOOP:
уменьшают содержимое регистра CX на 1 и выполняет переход на
метку A20, если в CX не ноль. Команда DEC кроме того
устанавливает флаг нуля во флаговом регистре в состояние 0
или 1. Команда JNZ затем проверяет эту установку. В рассмот
ренном примере команда LOOP хотя и имеет огпаниченное исполь
зование, но более эффективна, чем две команды: DEC и JNZ.
Ассемблер для IBM PC. Глава 7 138
Аналогично командам JMP и LOOP операнд в команде JNZ
cодержит значение расстояния между концом команды JNZ и
адресом A20, которое прибавляется к командному указателю.
Это расстояние должно быть в пределах от -128 до +127 байт.
В случае перехода за эти границы ассемблер выдаст сообщение
"Relative jump out of range" (превышены относительные грани
цы перехода).
Знаковые и беззнаковые данные.
Рассматривая назначение команд условного перехода следует
пояснить характер их использования. Типы данных, над которы
ми выполняются арифметические операции и операции сравнения
определяют какими командами пользоваться: беззнаковыми или
знаковыми. Беззнаковые данные используют все биты как биты
данных; характерным примером являются символьные строки:
имена, адреса и натуральные числа. В знаковых данных самый
левый бит представляет собой знак, причем если его значение
равно нулю, то число положительное, и если единице, то
отрицательное. Многие числовые значения могут быть как
положительными так и отрицательными.
В качестве примера предположим, что регистр AX содержит
11000110, а BX - 00010110. Команда
CMP AX,BX
сравнивает содержимое регистров AX и BX. Если данные
беззнаковые, то значение в AX больше, а если знаковые - то
меньше.
Переходы для беззнаковых данных.
Мнемоника Описание Проверяемые флаги
JE/JZ Переход, если равно/нуль ZF
JNE/JNZ Переход, если не равно/не нуль ZF
JA/JNBE Переход, если выше/не ниже или равно ZF,CF
JAE/JNB Переход, если выше или равно/не ниже CF
JB/JNAE Переход, если ниже/не выше или равно CF
JBE/JNA Переход, если ниже или равно/не выше CF,AF
Любую проверку можно кодировать одним из двух мнемоничес
ких кодов. Например, JB и JNAE генерирует один и тот же
объектный код, хотя положительную проверку JB легче понять,
чем отрицательную JNAE.
Переходыдля знаковых данных
Мнемоника Описание Проверяемые флаги
JE/JZ Переход, если равно/нуль ZF
JNE/JNZ Переход, если не равно/не нуль ZF
JG/JNLE Переход, если больше/не меньше или равно ZF,SF,OF
Ассемблер для IBM PC. Глава 7 139
JGE/JNL Переход, если больше или равно/не меньше SF,OF
JL/JNGE Переход, если меньше/не больше или равно SF,OF
JLE/JNG Переход, если меньше или равно/не больше ZF,SF,OF
Команды перехода для условия равно или ноль (JE/JZ) и не
равно или не ноль (JNE/JNZ) присутствуют в обоих списках
для беззнаковых и знаковых данных. Состояние равно/нуль
происходит вне зависимости от наличия знака.
Специальныеарифметическиепроверки
Мнемоника Описание Проверяемые флаги
JS Переход, если есть знак (отрицательно) SF
JNS Переход, если нет знака(положительно) SF
JC Переход, если есть перенос (аналогично JB) CF
JNC Переход, если нет переноса CF
JO Переход, если есть переполнение OF
JNO Переход, если нет переполнения OF
JP/JPE Переход, если паритет четный PF
JNP/JP Переход, если паритет нечетный PF
Еще одна команда условного перехода проверяет равно ли
содержимое регистра CX нулю. Эта команда необязательно
должна pасполагаться непосредственно за командой арифметики
или сравнения. Одним из мест для команды JCXZ может быть
начало цикла, где она проверяет содержит ли регистр CX
ненулевое значение.
Не спешите пока заучивать эти команды наизусть. Запомните
только, что для беззнаковых данных есть переходы по состоя
ниям равно, выше или ниже, а для беззнаковых - равно,
больше или меньше. Переходы по проверкам флагов переноса,
переполнения и паритета имеют особое назначение. Ассемблер
транслирует мнемонические коды в объектный код независимо
от того, какую из двух команд вы применили. Однако, команды
JAE и JGE являясь явно одинаковыми, проверяют различные
флаги.
ПРОЦЕДУРЫ И ОПЕРАТОР CALL
В предыдущих главах примеры содержали в кодовом сегменте
только oдну процедуру, оформленную следующим образом:
BEGIN PROC FAR
.
.
BEGIN ENDP
Операнд FAR информирует систему о том, что данный адрес явля
ется точкой входа для выполнения, а директива ENDP определя
ет конец процедуры. Кодовый сегмент, однако, может содержать
Ассемблер для IBM PC. Глава 7 140
любое количество процедур, которые разделяются директивами
PROC и ENDP. Типичная организация многопроцедурной программы
приведена на рис. 7.3.
Обратите внимание на следующие особенности:
ъ директивы PROC по меткам B10 и C10 имеют операнд NEAR
для указания того, что эти процедуры находятся в теку
щем кодовом сегменте. Во многих последующих примерах
этот операнд опущен, так как по умолчанию ассемблер
принимает тип NEAR.
ъ Каждая процедура имеет уникальное имя и содержит соб
ственную директиву ENDP для указания конца процедуры.
ъ Для передачи управления в процедуре BEGIN имеются две
команды: CALL B10 и CALL C10. В результате первой коман
ды CALL управление передается процедуре B10 и начинает
ся ее выполнение. Достигнув команды RET, управление
возвращается на команду непосредственно следующую за
CALL B10. Вторая команда CALL действует аналогично -
передает управление в процедуру C10, выполняет ее
команды и возвращает управление по команде RET.
Рис. 7.3. Вызов процедур.
ъ Команда RET всегда выполняет возврат в вызывающую про
грамму. Программа BEGIN вызывает процедуры B10 и C10,
которые возвращают управление обратно в BEGIN. Для
выполнения самой программы BEGIN операционная система
DOS вызывает ее и в конце выполнения команда RET возвра
щает управление в DOS. Если процедура B10 не содержит
завершающей команды RET, то выполнение команд продолжит
ся из B10 непосредственно в процедуре C10. Если
процедура C10 не содержит команды RET, то будут выпол
няться команды, оказавшиеся за процедурой C10 с непред
сказуемым результатом.
Использование процедур дает хорошую возможность организо
вать логическую структуру программы. Кроме того, операнды
для команды CALL могут иметь значения, выходящие за границу
от -128 до +127 байт.
Технически управление в процедуру типа NEAR может быть
передано с помощью команд перехода или даже обычным построч
ным кодированием. Но в большенстве случаев рекомендуется
использовать команду CALL для передачи управления в проце
дуру и команду RET для возврата.
СЕГМЕНТ СТЕКА
Ассемблер для IBM PC. Глава 7 141
До этого раздела в приводимых примерах встретились только
две команды, использующих стек, - это команды PUSH в начале
сегмента кодов, которые обеспечивают возврат в DOS, когда
EXE-программа завершается. Естественно для этих программ
требуется стек oчень малого размера. Однако, команда CALL
автоматически записывает в стек относительный адрес команды,
следующей непосредственно за командой CALL, и увеличивает
после этого указатель вершины стека. В вызываемой процедуре
команда RET использует этот адрес для возврата в вызывающую
процедуру и при этом автоматически уменьшается указатель
вершины стека.
Таким образом, команды PUSH записывают в стек двухбайто
вые адреса или другие значения. Команды POP обычно выбирают
из стека записанные в него слова. Эти операции изменяют отно
сительный адрес в регистре SP (т.е. в указатели стека) для
доступа к следующему слову. Данное свойство стека требует
чтобы команды RET и CALL соответствовали друг другу. Кроме
того, вызванная процедура может вызвать с помощью команды
CALL другую процедуру, а та в свою очередь - следующую. Стек
должен иметь достаточные размеры для того, чтобы хранить все
записываемые в него адреса. Для большенства примеров в дан
ной книге стек объемом в 32 слова является достаточным.
Команды PUSH, PUSHF, CALL, INT, и INTO заносят в стек
адрес возврата или содержимое флагового регистра. Команды
POP, POPF, RET и IRET извлекают эти aдреса или флаги из
стека.
При передаче управления в EXE-программу система устанавли
вает в регистрах следующие значения:
DS и ES: Адрес префикса программного сегмента - область в
256 (шест. 100) байт, которая предшествует выполняемому
программному модулю в памяти.
CS: Адрес точки входа в программу (адрес первой выполняемой
команды).
IP: Нуль.
SS: Адрес сегмента стека.
SP: Относительный адрес, указывающий на вершину стека.
Например, для стека в 32 слова (64 байта), определенного как
DW 32 DUP(?)
SP содержит 64, или шест. 40.
Выполним трассировку простой EXE-программы, приведенной
на рис.7.4. На практике вызываемые процедуры содержат любое
число команд.
Текущая доступная ячейка стека для занесения или
извлечения слова является вершина стека. Первая команда PUSH
уменьшает значение SP на 2 и заносит содержимое регистра
Ассемблер для IBM PC. Глава 7 142
DS (в данном примере 049f) в вершину стека, т.е. по адресу
4B00+3E. Вторая команда PUSH также уменьшает значение SP на
2 и записывает содержимое регистра AX (0000) по адресу
4B00+3C. Команда CALL B10 уменьшает значение SP и записывает
относительный адрес следующей команды (0007) в стек по
адресу 4B00+3A. Команда CALL C10 уменьшает значение SP и
записывает относительный адрес следующей команды (000B) в
стек по адресу 4B00+38.
При возврате из процедуры C10 команда RET извлекает 000B
из стека (4B00+38), помещает его в указатель команд IP и
увеличивает значение SP на 2. При этом происходит автомати
ческий возврат по относительному адресу 000B в кодовом
сегменте, т.е. в процедуру B10.
Рис. 7.4. Воздействие выполнения программы на стек.
Команда RET в конце процедуры B10 извлекает адрес 0007 из
стека (4B00+3A), помещают его в IP и увеличивает значение SP
на 2. При этом происходит автоматический возврат по относи
тельному адресу 0007 в кодовом сегменте. Команда RET по
адресу 0007 завершает выполнение программы, осуществляя
возврат типа FAR.
Ниже показано воздействие на стек при выполнении каждой
команды. Для трассировки программы можно использовать
отладчик DEBUG. Приведено только содержимое памяти с адреса
0034 до 003F и содержимое регистра SP:
Команда Стек SP
Начальное значение: хххх хххх хххх хххх хххх хххх 0040
PUSH DS (запись 049F) хххх хххх хххх хххх хххх 049F 003E
PUSH AX (запись 0000) хххх хххх хххх хххх 0000 049F 003C
CALL B10 (запись 0007) хххх хххх хххх 0700 0000 049F 003A
CALL C10 (запись 000B) хххх хххх 0B00 0700 0000 049F 0038
RET (выборка 000B) хххх хххх хххх 0700 0000 049F 003A
RET (выборка 0007) хххх хххх хххх хххх 0000 049F 003C
| | | | | |
Смещение в стеке: 0034 0036 0038 003A 003C 003E
Обратите внимание на два момента. Во-первых, слова в памя
ти содержат байты в обратной последовательности, так 0007
записывается в виде 0700. Во-вторых, отладчик DEBUG при
использовании его для просмотра стека заносит в стек другие
значения, включая содержимое IP, для собственных нужд.
ПРОГРАММА: РАСШИРЕННЫЕ ОПЕРАЦИИ ПЕРЕСЫЛКИ
В предыдущих программах были показаны команды пересылки
непосредcтвенных данных в регистр, пересылки данных из
памяти в регистр, пересылки содержимого регистра в память и
Ассемблер для IBM PC. Глава 7 143
пересылки содержимого oдного регистра в другой. Во всех
случаях длина данных была огpаничена одним или двумя байтами
и не предусмотрена пересылка данных из одной области памяти
непосредственно другую область. В данном разделе объясняется
процесс пересылки данных, которые имееют длину более двух
байт. В главе 11 будет показано использование операций над
строками для пересылки данных из одной области памяти
непосредственно в другую область.
В EXE-программе, приведенной на рис. 7.5, сегмент данных
cодержит три девятибайтовых поля, NAME1, NAME2, NAME3. Цель
программы - переслать данные из поля NAME1 в поле NAME2 и
переслать данные из поля NAME2 в поле NAME3. Так как эти
поля имеют длину девять байт каждая, то для пересылки данных
кроме простой команды MOV потребуются еще другие команды.
Программа содержит несколько новых особенностей.
Процедура BEGIN инициализирует сегментные регистры и
затем вызывает процедуры B10MOVE и C10MOVE. Процедура
B10MOVE пересылает содержимое поля NAME1 в поле NAME2. Так
как каждый раз пересылается только один байт, то процедура
начинает с самого левого байта в поле NAME1 и в цикле пересы
лает затем второй байт, третий и т.д.:
Рис. 7.5. Расширенные операции пересылки.
NAME1: A B C D E F G H I
| | | | | | | | |
NAME2: J K L M N O P Q R
Для продвижения в полях NAME1 и NAME2 в регистр CX заносится
значение 9, а регистры SI и DI используются в качестве
индексных. Две команды LEA загружают относительные aдреса
полей NAME1 и NAME2 в регистры SI и DI:
LEA SI,NAME1 ;Загрузка относительных адресов
LEA DI,NAME2 ; NAME1 и NAME2
Для пересылки содержимого первого байта из поля NAME1 в
первый байт поля NAME2 используются адреса в регистрах SI и
DI. kвадратные скобки в командах MOV обозначают, что для
доступа к памяти используется адрес в регистре, указанном в
квадратных cкобках. Таким образом, команда
MOV AL,[SI]
означает: использовать адрес в регистре SI (т.е.NAME1) для
пересылки соответствующего байта в регистр AL. А команда
MOV [DI],AL
означает: пересылать содержимое регистра AL по адресу,
лежащему в регистре DI (т.е. NAME2).
Ассемблер для IBM PC. Глава 7 144
Следующие команды увеличивают значения регистров SI и DI
и уменьшают значение в регистре SH. Если в регистре CX не
нулевое значение, управление передается на следующий цикл
(на метку B20).Т ак как содержимое регистров SI и DI было
увеличено на 1, то следующие команды MOV будут иметь дело с
адресами NAME1+1 и NAME2+1. Цикл продолжается таким образом,
пока не будет передано содержимое NAME1+8 и NAME2+8.
Процедура C10MOVE аналогична процедуре B10MOVE с двумя
исключениями: она пересылает данные из поля NAME2 в поле
NAME3 и использует команду LOOP вместо DEC и JNZ.
Задание: Введите программу, приведенную на рис.7.5,
выполните ее ассемблирование, компановку и трассировку с
помощью отладчика DEBUG. Обратите внимание на изменения в
регистрах, командном указателе и в стеке. Для просмотра
изменений в полях NAME2 и NAME3 используйте команду D DS:0.
КОМАНДЫ ЛОГИЧЕСКИХ ОПЕРАЦИЙ: AND, OR, XOR, TEST, NOT
Логические операции являются важным элементом в
проектировании микросхем и имеют много общего в логике
программирования. Команды AND, OR, XOR и TEST - являются
командами логических операций. Эти команды используются для
сброса и установки бит и для арифметических операций в коде
ASCII (см.гл.13). Все эти команды обрабатывают один байт или
одно слово в регистре или в памяти, и устанавливают флаги
CF, OF, PF, SF, ZF.
AND: Если оба из сравниваемых битов равны 1, то результат
равен 1; во всех остальных случаях результат - 0.
OR: Если хотя бы один из сравниваемых битов равен 1, то
результат равен 1; если сравниваемые биты равны 0, то
результат - 0.
XOR: Если один из сравниваемых битов равен 0, а другой равен
1, то результат равен 1; если сравниваемые биты одинаковы
(оба - 0 или оба - 1) то результат - 0.
TEST: действует как AND-устанавливает флаги, но не изменяет
биты.
Первый операнд в логических командах указывает на один
байт или слово в регистре или в памяти и является единствен
ным значением, которое может изменятся после выполнения
команд. В следующих командах AND, OR и XOR используются
одинаковые битовые значения:
AND OR XOR
0101 0101 0101
0011 0011 0011
Результат: 0001 0111 0110
Ассемблер для IBM PC. Глава 7 145
Для следующих несвязанных примеров, предположим, что AL
содержит 1100 0101, а BH содержит 0101 1100:
1. AND AL,BH ;Устанавливает в AL 0100 0100
2. OR BH,AL ;Устанавливает в BH 1101 1101
3. XOR AL,AL ;Устанавливает в AL 0000 0000
4. AND AL,00 ;Устанавливает в AL 0000 0000
5. AND AL,0FH ;Устанавливает в AL 0000 0101
6. OR CL,CL ;Устанавливает флаги SF и ZF
Примеры 3 и 4 демонстрируют способ очистки регистра. В
примере 5 обнуляются левые четыре бита регистра AL. Хотя
команды сравнения CMP могут быть понятнее, можно применить
команду OR для следующих целей:
1. OR CX,CX ;Проверка CX на нуль
JZ ... ;Переход, если нуль
2. OR CX,CX ;Проверка знака в CX
JS ... ;Переход, если отрицательно
Команда TEST действует аналогично команде AND, но
устанавливает только флаги, а операнд не изменяется. Ниже
придено несколько примеров:
1. TEST BL,11110000B ;Любой из левых бит в BL
JNZ ... ; равен единице?
2. TEST AL,00000001B ;Регистр AL содержит
...


