Ассемблер для процессоров Intel Pentium
Шрифт:
Таблица 5.3. Команды условных переходов для специальных проверок
Еще одна команда условного перехода проверяет равенство содержимого регистра СХ нулю. Эта команда необязательно должна располагаться непосредственно за арифметической командой или командой сравнения. Команда jcxz может быть помещена в начало цикла, где она проверяет содержимое регистра СХ.
5.2. Команда безусловного перехода jmp
При выполнении команды безусловного перехода jmp, независимо от состояния флагов процессора, программа продолжает выполняться с новой ветви. При этом новый адрес команды загружается
В языке ассемблера новое место, откуда продолжается выполнение программы, в большинстве случаев обозначается меткой, которую процессор преобразует в исполнительный адрес. Если переход происходит в текущий сегмент, то смещение метки загружается непосредственно в регистр-счетчик команд El Р. Если же метка находится в другом сегменте кода, то адрес сегмента дополнительно загружается в регистр CS. Для преобразования метки в вид сегментхмещение используются три формата команды jmp:
jmp short целевой_адрес
jmp near ptr целевой_адрес
jmp far ptr целевой_адрес
Здесь целевой_адрес – адрес команды, которая будет выполняться после перехода. Вот несколько примеров команды jmp:
jmp labell ; адрес команды, которая будет выполняться при
; переходе, находится в текущем сегменте команд
jmp near ptr labell ; адрес следующей команды находится
; в текущем сегменте команд
jmp short labell ; адрес команды, которая будет выполняться
; при переходе, находится в диапазоне
; -128 – +127
jmp far ptr labell ; адрес команды, которая будет
; выполняться при переходе, находится
; в другом сегменте
Рассмотрим операторы, указанные перед целевым адресом. Оператор short указывает на то, что нужно сделать переход на метку в диапазоне от -128 до +127, начиная от адреса следующей команды. В этом случае к содержимому регистра указателя команд EIP прибавляется 8-разрядное целое число.
Оператор near ptr указывает на метку в текущем сегменте, при этом к регистру указателя команд EIP прибавляется 16-разрядное смещение. Наконец, оператор far ptr указывает, что необходимо сделать переход на метку в другом сегменте. В этом случае сегментная часть адреса метки загружается в регистр CS, a смещение – в El Р.
Рассмотренные нами модификации команды jmp являются классическими для 16-разрядных приложений и берут свое начало от «времен MS-DOS», когда отдельный сегмент кода не мог использовать пространство памяти больше чем 64 Кбайта, а для создания больших программ требовалось определенным образом компоновать несколько сегментов кода и данных.
Любое современное приложение является 32-разрядным и оперирует с линейным пространством адресов размером до 4 Гбайт. При разработке ассемблерных программ, как упоминалось в главе 3, используется модель памяти flat, а это означает, что программа занимает непрерывную область адресов, в которой размещаются данные и код. По этой причине любые команды адресуются 32-разрядным смещением в пространстве адресов программы.
При запуске 32-разрядного приложения все сегментные регистры устанавливаются в одно и то же значение. Для программистов, работающих с программами в DOS, 32-разрядное Windows-приложение может напоминать СОМ-файл, поскольку в таком файле можно работать только со смещениями.
В 32-разрядных приложениях все метки и переходы считаются ближними (near ptr) в диапазоне адресов 4 Гбайт.Команду jmp можно использовать не только для безусловного перехода в сегменте программного кода, но и для организации ветвлений. Для этого можно применить один из ее форматов, показанных далее:
jmp reg16
jmp reg32
jmp word ptr [reg16]
jmp dword ptr [reg32]
Здесь reg16 (reg32) – один из 16– или 32-разрядных регистров. Для первых двух форматов команд из списка адрес, по которому передается управление, должен находиться в одном из этих регистров.
Если используется 32-разрядный регистр (reg32), то адрес команды, на которую передается управление, также является 32-разрядным. Этот формат команды jmp характерен для 32-разрядных Windows-приложений.
Последние два формата команды jmp используют механизм косвенной адресации, при этом регистр содержит адрес ячейки памяти, в которой находится адрес команды, получающей управление. Проиллюстрируем вышеизложенное примерами:
. . .
.code
. . .
L1:
xor EDX, EDX
. . .
lea ESI, L1
jmp ESI
. . .
В этом примере в регистр ESI помещается смещение метки L1, после чего с помощью команды jmp ESI управление передается на эту метку.
. . .
.data
label_offset DD LI
.code
. . .
L1:
xor EDX, EDX
lea ESI, label_offset
jmp dword ptr [ESI]
. . .
В этом примере в регистр ESI помещается адрес переменной label_offset, в то время как сама переменная label_offset содержит адрес метки L1. Команда jmp dword ptr [ESI] в этом случае передает управление на метку L1.
Как видно из примеров, использование в качестве операндов регистров или ячеек памяти придает команде безусловного перехода большую гибкость, чем применение прямого смещения, что позволяет создавать ветвления и переходы в программе. Далее мы рассмотрим несколько примеров таких ветвлений.
Следующее 16-разрядное приложение, исходный текст которого показан в листинге 5.1, выводит на экран строки s1 , s2 и s3.
Листинг 5.1. Вывод трех символьных строк на экран
.model small
.stack 100h
.data
s1 DB Odh, Oah, «String 1$»
s2 DB Odh, Oah, «String 2$»
s3 DB Odh, Oah, «String 3$»
sarray label word ; массив, в котором хранятся адреса строк
DW s1 ; s1 иs2
DW s2
DW s3
num DW 0 ; индекс в адресе перехода команды jmp
label_array label word ; массив адресов меток
DW LI ; адрес метки LI
DW L2 ; адрес метки L2
DW L3 ; адрес метки L3
.code
start:
mov AX, @data
mov DS, AX
mov ES, AX
;
mov CX, 3 ; счетчик цикла -> CX
lea DI, label_array ; адрес массива меток
next:
mov s1 , DI
mov BX, num ; индекс перехода -> BX
shl BX, 1 ; умножить на 2 для правильной адресации
; меток в массиве label_array
add SI, BX ; сформировать адрес перехода
; для команды jmp
jmp word ptr [SI] ; перейти по адресу, находящемуся
; в регистре s1 (L1 или L2)
wedge:
inc num ; инкремент индекса переходов