Главная страница
Предыдущая страница (основные команды процессора)

Итак, мы уже можем писать простейшие вставки на Ассемблере. Сейчас мы рассмотрим условные и безусловные переходы (аналогично if-then-else, goto в Паскале). Следует учитывать, что любые переходы в программе замедляют выполнение программы (обычно для выполнения перехода требуется 5 тактов ЦП, а иногда и больше). В современных процессорах для повышения тактовой частоты используется конвейеризация. Дело в том, что для обслуживания каждой команды процессора требуется несколько тактов: первый такт распознаёт команду, другой выполняет, третий делает ещё что-нибудь, четвёртый... В современных процессорах пока распознаётся одна команда, одновременно выполняется другая, что-то делается с третьей, четвёртой, и так далее. При появлении в программе условного перехода процессор загружает в конвейер данные, начиная с того места, куда по его мнению, приведёт переход. В случае неудачного предсказания перехода все команды текущего конвейера должны выполниться, затем конвейер сброситься, выполниться условный переход, и только затем конвейер будет загружаться снова. Понятно, что это плохо влияет на эффективность. Ведь по сути при неудачном переходе за единицу времени обрабатывается меньшее число команд. К тому же в конвейер загружаются ненужные данные. Чем длиннее конвейер у процессора, тем быстрее он может выполнять программы, но и тем больше потери при неудачном переходе. То есть при одинаковой частоте ЦП лучше тот, у которого короче конвейер. Следует понимать, что везде, где можно обойтись без переходов, лучше без них обходиться. Например, лучше использовать команды типа cmpxchg, чем cmp с условными переходами. В этом случае блок ЦП, отвечающий за предсказания переходов не будет задействован (что снизит вероятность простоя). Но без команд переходов наши возможности очень ограничены, поэтому стоит их рассмотреть (на самом деле, они используются сплошь и рядом, особенно компиляторами!)

Команды передачи управления


1) jmp метка – безусловный переход на метку. Например: jmp @exit; Используется очень часто. По сути выполняет то же самое действие, что и goto в Паскале

2) jcc метка (jcc – одна из команд в следующей таблице) – условный переход. Переход выполняется, если соответствующее условие выполнено. Реально каждое условие является каким-либо состоянием флагов. Обычно перед командой условного перехода выполняется команда cmp, хотя это совсем не обязательно. В приведённом ниже примере мы хотим найти модуль числа eax:
   cmp eax, 0;
   jge @NoNEG; {если больше или равно, то знак инвертировать не надо}
   neg eax;
@NoNEG:
Если вы разберётесь с флагами, и поймёте, какие флаги реально изменяет каждая команда, то сможете писать условные переходы, например, после команды dec. Это иногда позволяет получить некоторый выигрыш в производительности.
Команды условного перехода:
Код команды Реальное условие Условие для CMP Код
команды
Реальное
условие
Условие для CMP
JA
CF=0 и ZF=0
если выше
JG
ZF=0 и SF=OF
если больше
JAE
JNC
CF=0
если выше или равно
если нет переноса
JGE
SF=OF
если больше или равно
JB
JC
CF=1
если ниже
если перенос
JL
SF<>OF
если меньше
JBE
CF=1 | ZF=1
если ниже или равно
JLE
ZF=1 | SF<>OF
если меньше или равно
JE
JZ
ZF=1
если равно
если ноль
JNE
JNZ
ZF=0
если не равно
если не ноль
JO
OF=1
если есть переполнение
JNO
OF=0
если нет переполнения
JS
SF=1
если есть знак
JNS
SF=0
если нет знака
JP
PF=1
если есть четность
JNP
PF=0
если нет четности

3) call метка – вызов процедуры с сохранением в стеке адреса возврата (32-битное число)

4) ret|(ret число) – выход из процедуры. Если число указано, то после считывания адреса возврата из стека будет удалено указанное число байтов. Указывается именно число, а не регистр. Бывает нужно, если при вызове процедуры параметры ей передавались через стек

5) jecxz метка – переход, если ecx=0

6) loop метка – цикл. Уменьшает регистр ecx на 1 и выполняет переход типа short (не дальше, чем на 127 байт) на метку, если ecx<>0. Например, в следующем фрагменте команда add выполнится 10 раз:
    mov ecx, 10;
@loop_start:
    add eax, ecx;
    loop @loop_start;
Команда loop эквивалентна паре команд dec ecx; jnz метка, но не меняет флаги

7) loope|loopz метка – цикл пока равно|ноль
7) loopne|loopnz метка – цикл пока не_равно|не_ноль
Все перечисленные команды уменьшают ecx на 1, после чего выполняют переход типа short, если ecx<>0, и если выполняется условие. Для команд loope|loopz условие – равенство ZF=1, для loopne|loopnz – равенство ZF=0. Сами команды loopcc не изменяют значений флагов, так что ZF должен быть определён предыдущей командой. Например, следующий фрагмент копирует строку из esi в edi пока не кончится строка (ecx=0), или пока не встретится символ с ASCII-кодом 13 (конец строки):
    mov ecx, lenght_string;
@loop0:
    lodsb;
    stosb;
    cmp al, 13;
@loopnz loop0;


Следующая страница (работа с флагами)