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

В операциях деления размер делимого в два раза больше, чем размер делителя. Поэтому нельзя просто загрузить данные в регистр 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 раз быстрее, чем деление

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

Переходы и циклы

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

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

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

JMP <операнд>

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

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

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

jmp L

...

L: mov eax, x

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

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

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

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

jmp ebx


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: