Третий вариант команды IMUL позволяет указать и результат, и оба сомножителя. Однако результат может быть помещён только в регистр, а второй сомножитель может быть только непосредственным операндом. Первый сомножитель может быть регистром или ячейкой памяти.

Четвёртый вариант команды IMUL позволяет указать оба сомножителя. Первый должен быть регистром, а второй – регистром или ячейкой памяти. Результат помещается в регистр, являющийся первым операндом.

Команда IMUL устанавливает флаги так же, как и команда MUL. Однако расширение результата в регистр EDX/DX происходит только при использовании первого варианта команды IMUL. В остальных случаях часть произведения, не помещающаяся в регистр-результат, теряется, даже если в качестве результата указан регистр EAX/AX. При умножении двух 1-байтовых чисел, произведение которых больше байта, но меньше слова, в регистре-результате получается корректное произведение.

mov  eax, 5

mov  ebx, -7

imul  ebx                        ; EAX = ffffffdd, EDX = ffffffff, CF = 0

mov  ebx, 3

imul  ebx, 6                        ; EBX = EBX * 6

mov  ebx, 500000

imul  eax, ebx, 100000                ; EAX = EBX * 100000, старшая часть результата теряется

x dd 40

mov  eax, 55

imul  eax, x                        ; EAX = EAX * x

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

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

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

DIV  <операнд>                        ; Беззнаковое деление

IDIV <операнд>                        ; Знаковое деление

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

Если делитель имеет размер 1 байт, то делимое берётся из регистра AX. Если делитель имеет размер 2 байта, то делимое берётся из регистровой пары DX:AX. Если же делитель имеет размер 4 байта, то делимое берётся из регистровой пары EDX:EAX.

Поскольку процессор работает с целыми числами, то в результате деления получается сразу два числа – частное и остаток. Эти два числа также помещаются в определённые регистры. Если делитель имеет размер 1 байт, то частное помещается в регистр AL, а остаток – в регистр AH. Если делитель имеет размер 2 байта, то частное помещается в регистр AX, а остаток – в регистр DX. Если же делитель имеет размер 4 байта, то частное помещается в регистр EAX, а остаток – в регистр EDX.

mov  ax, 127

mov  bl, 5

div  bl                        ; AL = 19h = 25,  AH = 02h = 2

mov  ax, 127

mov  bl, -5

idiv  bl                        ; AL = e7h = -25, AH = 02h = 2

mov  ax, -127

mov  bl, 5

idiv  bl                        ; AL = e7h = -25, AH = feh = -2

mov  ax, -127

mov  bl, -5

idiv  bl                        ; AL = 19h = 25,  AH = feh = -2

; x = a * b + c

mov  eax, a

imul  b

add  eax, c                        ; Операнды команды сложения вычисляются слева направо

mov  x, eax

; x = a + b * c

mov  eax, b

imul  c

add  eax, a                        ; Операнды команды сложения вычисляются справа налево

mov  x, eax

3.5. Изменение размера числа

В операциях деления размер делимого в два раза больше, чем размер делителя. Поэтому нельзя просто загрузить данные в регистр EAX и поделить его на какое-либо значение, т. к. в операции деления будет задействован также и регистр EDX. Поэтому прежде чем выполнять деление, надо установить корректное значение в регистр EDX, иначе результат будет неправильным. Значение регистра EDX должно зависеть от значения регистра EAX. Тут возможны два варианта – для знаковых и беззнаковых чисел.

Если мы используем беззнаковые числа, то в любом случае в регистр EDX необходимо записать значение 0: aaaaaaaah → 00000000aaaaaaaah.

Если же мы используем знаковые числа, то значение регистра EDX будет зависеть от знака числа: 55555555h → 0000000055555555h, aaaaaaaah → ffffffffaaaaaaaah.

Записать значение 0 не сложно, а вот для знакового расширения необходимо анализировать знак числа. Однако нет необходимости делать это вручную, т. к. язык ассемблера имеет ряд команд, позволяющих расширять байт до слова, слово до двойного слова и двойное слово до учетверённого слова.

cbw                        ; Знаковое расширение AL до AX

cwd                        ; Знаковое расширение AX до DX:AX

cwde                        ; Знаковое расширение AX до EAX

cdq                        ; Знаковое расширение EAX до EDX:EAX

Таким образом, если делитель имеет размер 2 или 4 байта, то нужно устанавливать значение не только регистра AX/EAX, но и регистра DX/EDX. Если же делитель имеет размер 1 байт, то можно просто записать делимое в регистр AX.

x dd?

mov  eax, x                ; Заносим в регистр EAX значение переменной x, которое заранее неизвестно

cdq                        ; Знаковое расширение EAX в EDX:EAX

mov  ebx, 7

idiv  ebx

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

MOVSX <операнд1>, <операнд2>                ; Знаковое расширение – старшие биты заполняются знаковым битом

MOVZX <операнд1>, <операнд2>                ; Беззнаковое расширение – старшие биты заполняются нулём

Операнд1 и операнд2 могут иметь любой размер. Понятно, что операнд1 должен быть больше, чем операнд2. В случае равенства размера операндов следует использовать обычную команду пересылки MOV, которая выполняется быстрее.

Рассмотрим пример: необходимо вычислить x * x * x, где x – 1-байтовая переменная.

; Первый вариант

mov  al, x                        ; Пересылаем x в регистр AL

imul  al                        ; Умножаем регистр AL на себя, AX = x * x

movsx bx, x                        ; Пересылаем x в регистр BX со знаковым расширением

imul  bx                        ; Умножаем AX на BX. Но! – результат размещается в DX:AX

; Второй вариант

mov  al, x                        ; Пересылаем x в регистр AL

imul  al                        ; Умножаем регистр AL на себя, AX = x * x

cwde                                ; Расширяем AX до EAX

movsx ebx, x                        ; Пересылаем x в регистр EBX со знаковым расширением

imul  ebx                        ; Умножаем EAX на EBX. Поскольку x – 1-байтовая переменная, результат благополучно помещается в EAX

Рассмотрим ещё один пример.

mov  eax, x

mov  ebx, 429496730                ; 429496730 = 4294967296 / 10

imul  ebx                        ; EDX = x / 10. Выполняется в ≈5 раз быстрее, чем деление

Чем обусловлено получение такого результата? Всегда ли будет работать этот механизм?

Лекция №5. Переходы и циклы

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

4.1. Безусловный переход

Команда безусловного перехода имеет следующий синтаксис:

JMP <операнд>

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

4.1.1. Прямой переход

Если в команде перехода указывается метка команды, на которую надо перейти, то переход называется прямым.

  jmp  L

  ...

L: mov  eax, x

Вообще, любой переход заключается в изменении адреса следующей исполняемой команды, т. е. в изменении значения регистра EIP. Казалось бы, в команде перехода должен задаваться именно адрес перехода. Однако в команде прямого перехода задаётся не абсолютный адрес, а разность между адресом перехода и адресом команды перехода. Действие команды перехода заключается в прибавлении этой величины к текущему значению регистра EIP2. Операнд команды перехода рассматривается как поле со знаком, поэтому при сложении его со значением регистра EIP значение в этом регистре может как увеличиться, так и уменьшиться, т. е. возможен переход и вперёд, и назад.

Запись в команде перехода не абсолютного, а относительного адреса перехода позволяет уменьшить размер команды перехода. Абсолютный адрес должен быть 32-битным, а относительный может быть и 8-битным, и 16-битным.

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