4.1.2. Косвенный переход

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

jmp  ebx

4.2. Команды сравнения и условного перехода

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

CMP <операнд1>, <операнд2>

Команда сравнения эквивалентна команде SUB за исключением того, что вычисленная разность никуда не заносится. Назначение команды CMP – установка и сброс флагов.

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

Jxx <метка>

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

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


Мнемокод

Название

Условие перехода после команды CMP op1, op2

Значения флагов

Примечание

JE

Переход если равно

op1 = op2

ZF = 1

Для всех чисел

JNE

Переход если не равно

op1 ≠ op2

ZF = 0

JL/JNGE

Переход если меньше

op1 < op2

SF ≠ OF

Для чисел со знаком

JLE/JNG

Переход если меньше или равно

op1 ≤ op2

SF ≠ OF или ZF = 1

JG/JNLE

Переход если больше

op1 > op2

SF = OF и ZF = 0

JGE/JNL

Переход если больше или равно

op1 ≥ op2

SF = OF

JB/JNAE

Переход если ниже

op1 < op2

CF = 1

Для чисел без знака

JBE/JNA

Переход если ниже или равно

op1 ≤ op2

CF = 1 или ZF = 1

JA/JNBE

Переход если выше

op1 > op2

CF = 0 и ZF = 0

JAE/JNB

Переход если выше или равно

op1 ≥ op2

CF = 0



Рассмотрим пример: даны две переменные x и y, в переменную z нужно записать максимальное из чисел x и y.

НЕ нашли? Не то? Что вы ищете?

  mov  eax, x

  cmp  eax, y

  jge/jae L                                ; Используем JGE для знаковых чисел и JAE – для беззнаковых

  mov  eax, y

L: mov  z, eax

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

Мнемокод

Условие перехода

Мнемокод

Условие перехода

JZ

ZF = 1

JNZ

ZF = 0

JS

SF = 1

JNS

SF = 0

JC

CF = 1

JNC

CF = 0

JO

OF = 1

JNO

OF = 0

JP

PF = 1

JNP

PF = 0



Рассмотрим пример: пусть a, b и c – беззнаковые переменные размером 1 байт, требуется вычислить c = a * a + b, но если результат превосходит размер байта, передать управление на метку ERROR.

mov  al, a

mul  al

jc  ERROR

add  al, b

jc  ERROR

mov  c, al

И, наконец, в третью группу входят две команды условного перехода, проверяющие не флаги, а значение регистра ECX или CX:

JCXZ <метка>                        ; Переход, если значение регистра CX равно 0

JECXZ <метка>                        ; Переход, если значение регистра ECX равно 0

Однако эта команда выполняется достаточно долго. Выгоднее провести сравнение с нулём и использовать обычную команду условного перехода.

С помощью команд перехода можно реализовать любые разветвления и циклы.

; if (x > 0) S

  cmp  x, 0

  jle  L

  ...                                ; S

L: 

; if (x) S1 else S2

  cmp  x, 0

  je  L1

  ...                                ; S1

  jmp  L2

L1: ...                                ; S2

L2:

; if (a > 0 && b > 0) S

  cmp  a, 0

  jle  L

  cmp  b, 0

  jle  L

  ...                                ; S

L: 

; if (a > 0 || b > 0) S

  cmp  a, 0

  jg  L1

  cmp  b, 0

  jle  L2

L1: ...                                ; S

L2:

; if (a > 0 || b > 0 && c > 0) S

  cmp  a, 0

  jg  L1

  cmp  b, 0

  jle  L2

  cmp  c, 0

  jle  L2

L1: ...                                ; S

L2:

; while (x > 0) do S

L1: cmp x, 0

  jle L2

  ...                                ; S

  jmp L1

L2:

; do S while (x > 0)

L:  ...                                ; S

  cmp x, 0

  jg L

4.3. Команды управления циклом

4.3.1. Команда LOOP

Команда LOOP позволяет организовать цикл с известным числом повторений:

  mov  ecx, n

L: ...

  ...

  loop  L

Команда LOOP требует, чтобы в качестве счётчика цикла использовался регистр ECX. Собственно, команда LOOP вычитает единицу именно из этого регистра, сравнивает полученное значение с нулём и осуществляет переход на указанную метку, если значение в регистре ECX больше 0. Метка определяет смещение перехода, которое не может превышать 128 байт.

При использовании команды LOOP следует также учитывать, что с её помощью реализуется цикл с постусловием, следовательно, тело цикла выполняется хотя бы один раз. Хуже того, если до начала цикла записать в регистр ECX значение 0, то при вычитании единицы, которое выполняется до сравнения с нулём, в регистре ECX окажется ненулевое значение, и цикл будет выполняться 232 раз.

Команда LOOP не относится к самым быстрым командам. В большинстве случаев её можно заменить последовательностью других команд.

4.3.2. Команды LOOPE/LOOPZ и LOOPNE/LOOPNZ

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

LOOPE <метка>                ; Команды являются синонимами

LOOPZ <метка>

Действие этой команды можно описать следующим образом: ECX = ECX - 1; if (ECX!= 0 && ZF == 1) goto <метка>;

До начала цикла в регистр ECX необходимо записать число повторений цикла. Команда LOOPE/LOOPZ, как и команда LOOP ставится в конце цикла, а перед ней помещается команда, которая меняет флаг ZF (обычно это команда сравнения CMP). Команда LOOPE/LOOPZ заставляет цикл повторяться ECX раз, но только если предыдущая команда фиксирует равенство сравниваемых величин (вырабатывает нулевой результат, т. е. ZF = 1).

По какой именно причине произошёл выход из цикла надо проверять после цикла. Причём надо проверять флаг ZF, а не регистр ECX, т. к. условие ZF = 0 может появиться как раз на последнем шаге цикла, когда и регистр ECX стал нулевым.

Команда LOOPNE/LOOPNZ аналогична команде LOOPE/LOOPZ, но досрочный выход из цикла осуществляется, если ZF = 1.

Рассмотрим пример: пусть в регистре ESI находится адрес начала некоторого массива двойных слов, а в переменной n – количество элементов массива, требуется проверить наличие в массиве элементов, кратных заданному числу x, и занести в переменную f значение 1, если такие элементы есть, и 0 в противном случае.

  mov  ebx, x

  mov  ecx, n

  mov  f, 1

L1: mov  eax, [esi]

  add  esi, 4

  cdq

  idiv  ebx

  cmp  edx, 0

  loopne L1

  je  L2

  mov  f, 0

L2:

Лекция №6. Массивы

5.1. Модификация адресов

Как уже было сказано, массивы в языке ассемблера описываются по директивам определения данных с использованием конструкции повторения (см. раздел 2.6). Для того чтобы обратиться к элементу массива, необходимо так или иначе указать адрес начала массива и смещение элемента в массиве. Смещение первого элемента массива всегда равно 0. Смещения остальных элементов массива зависят от размера элементов.

Пусть X – некий массив. Тогда адрес элемента массива можно вычислить по следующей формуле:

адрес(X[i]) = X + (type X) * i, где i – номер элемента массива, начинающийся с 0

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17