Tooprogram.ru

Компьютерный справочник
1 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Команда mul ассемблер

Ассемблер. Арифметические инструкции

Обновл. 20 Окт 2019 |

В этом уроке мы будем разбираться с арифметическими инструкциями в ассемблере на примере INC, DEC, ADD, SUB и пр.

Инструкция INC

Инструкция INC (от англ. «INCREMENT») используется для увеличения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.

Синтаксис инструкции INC:

Операндом место_назначения может быть 8-битный, 16-битный или 32-битный операнд.

Инструкция DEC

Инструкция DEC (от англ. «DECREMENT») используется для уменьшения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.

Синтаксис инструкции DEC:

Операндом место_назначения может быть 8-битный, 16-битный или 32-битный операнд.

Инструкции ADD и SUB

Инструкции ADD и SUB используются для выполнения простого сложения/вычитания двоичных данных размером в byte, word и doubleword, то есть для сложения или вычитания 8-битных, 16-битных или 32-битных операндов, соответственно.

Синтаксис инструкций ADD и SUB:

ADD/SUB место_назначения, источник

Инструкции ADD/SUB могут выполняться между:

регистром и регистром;

памятью и регистром;

регистром и памятью;

памятью и константами.

Однако, как и другие инструкции, операции типа память-в-память невозможны с использованием инструкций ADD/SUB. Операции ADD или SUB устанавливают или сбрасывают флаги переполнения и переноса.

В следующем примере мы спрашиваем у пользователя два числа, сохраняем их в регистрах EAX и EBX, затем выполняем операцию сложения, сохраняем результат в ячейке памяти res и выводим его на экран:

Результат выполнения программы выше:

Enter a digit:
3
Please enter a second digit:
4
The sum is:
7

Ниже рассмотрен пример, в котором, за счёт того, что значения переменных для арифметических выражений прописаны в самом коде программы, можно получить код программы короче и проще:

Результат выполнения программы выше:

Инструкции MUL и IMUL

Есть две инструкции для умножения двоичных данных:

инструкция MUL (от англ. «MULTIPLY») обрабатывает данные unsigned;

инструкция IMUL (от англ. «INTEGER MULTIPLY») обрабатывает данные signed.

Обе инструкции влияют на флаги переноса и переполнения.

Синтаксис инструкций MUL/IMUL:

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

Рассмотрим 3 разных сценария:

Сценарий №1: Когда перемножаются 2 значения типа byte — множимое находится в регистре AL, а множителем является значение типа byte в памяти или в другом регистре. Результат произведения находится в AX. Старшие 8 бит произведения хранятся в AH, а младшие 8 бит — хранятся в AL:

Сценарий №2: Когда перемножаются 2 значения типа word — множимое должно быть в регистре AX, а множителем является значение типа word в памяти или в другом регистре. Например, для такой инструкции, как MUL DX , вы должны сохранить множитель в DX, а множимое — в AX. В результате получится значение типа doubleword для которого понадобятся два регистра. Часть высшего порядка (крайняя слева) сохраняется в DX, а часть нижнего порядка (крайняя справа) — сохраняется в AX:

Сценарий №3: Когда перемножаются 2 значения типа doubleword — множимое должно находится в EAX, а множителем является значение типа doubleword, хранящееся в памяти или в другом регистре. Результат умножения сохраняется в регистрах EDX и EAX. Биты старшего порядка сохраняются в регистре EDX, а биты младшего порядка — сохраняются в регистре EAX:

Команда MUL

Что такое JavaScript

Если вы интересуетесь программированием вообще, и сайтостроением в частности, то вы наверняка слышали слово JavaScript. И, если вы до сих пор не узнали толком, что же это такое, то пришло время сделать это. Подробнее.

Инструкция MUL в Ассемблере выполняет умножение без знака. Понять работу команды MUL несколько сложнее, чем это было для команд, рассмотренных ранее. Но, надеюсь, что я помогу вам в этом разобраться.

Итак, синтаксис команды MUL такой:

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

Прежде чем разобраться в подробностях работы этой инструкции, давайте посмотрим, что может быть ЧИСЛОМ.

ЧИСЛОМ может быть один из следующих:

  • Область памяти (MEM)
  • Регистр общего назначения (REG)

Эта команда не работает с сегментными регистрами, а также не работает непосредственно с числами. То есть вот так

MUL 200 ; неправильно

А теперь алгоритм работы команды MUL:

  • Если ЧИСЛО — это БАЙТ, то AX = AL * ЧИСЛО
  • Если ЧИСЛО — это СЛОВО, то (DX AX) = AX * ЧИСЛО

Вот такая немного сложноватая команда. Хотя сложно это с непривычки. Сейчас мы разберём всё “по косточкам” и всё станет ясно.

Для начала обратите внимание, что инструкция MUL работает либо с регистром АХ, либо с регистром AL. То есть перед выполнением этой команды нам надо записать в регистр АХ или в регистр AL значение, которое будет участвовать в умножении. Сделать это можно, например, с помощью уже известной нам команды MOV.

Затем мы выполняем умножение, и получаем результат либо в регистр АХ (если ЧИСЛО — это байт), либо в пару регистров DX и AX (если ЧИСЛО — это слово). Причём в последнем случае в регистре DX будет старшее слово, а в регистре AX — младшее.

А теперь, чтобы совсем всё стало понятно, разберём пару примеров — с байтом и словом.

Пример умножения в Ассемблере

Итак, например, нам надо умножить 150 на 250. Тогда мы делаем так:

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

После выполнения этого кода в регистре АХ будет результат умножения чисел 150 и 250, то есть число 37500 (927С в шестнадцатеричной системе).

Теперь попробуем умножить 10000 на 5000.

В результате мы получили довольно большое число, которое, конечно, не поместится в слово. Поэтому для результата используются два регистра — DX и AX. В нашем примере в регистре DX, будет число 762 (02FA — в шестнадцатеричной системе), а в регистре АХ — число 61568 (F080 — в шестнадцатеричной системе). А если рассматривать их как одно число (двойное слово), где в старшем слове 762, а в младшем — 61568, то это и будет 50000000 (2FAF080 — в шестнадцатеричной системе).

Если не верите — может перевести всё это в двоичное число и проверить.

После выполнения команды MUL состояния флагов ZF, SF, PF, AF не определены и могут быть любыми.

А если старшая секция результата (регистр AH при умножении байтов или регистр DX при умножении слов) равна нулю, то

Иначе эти флаги либо не равны, либо равны 1.

В конце как обычно расскажу, почему эта команда ассемблера называется MUL. Это сокращение от английского слова MULTIPLY, которое можно перевести как “умножить, умножать”.

Система команд x86

Влияние команды на флаги и форматы команды:

EDX:EAX = EAX * r/m32

Описание:

Команда MUL (Unsigned Multiply) относится к группе команд целочисленной (или двоичной) арифметики (Binary Arithmetic Instructions) и производит умножение двух беззнаковых целочисленных операндов. Операнд-назначение (DEST) задается неявно и является переменной в регистре (или регистровой паре) AX, DX:AX или EDX:EAX в зависимости от кода команды и атрибута размера операнда. Единственный операнд команды — операнд-источник (SRC) — может быть переменной в регистре или в памяти (r/m8, r/m16, r/m32).

Действие команды MUL зависит от размера операнда-источника (SRC) следующим образом:

  • однобайтный операнд r/m8умножается на значение в регистре AL, результат помещается в регистр AX, флаги EFLAGS.CF и EFLAGS.OF сбрасываются, если результирующее значение в регистре AH равно нулю, иначе — устанавливаются в 1;
  • двухбайтный операнд r/m16умножается на значение в регистре AX, результат помещается в пару регистров DX:AX, регистр DX содержит старшие 16 бит результата, флаги EFLAGS.CF и EFLAGS.OF сбрасываются, если результирующее значение в регистре DX равно нулю, иначе — устанавливаются в 1.
  • четырехбайтный операнд r/m32умножается на значение в регистре EAX, результат помещается в пару регистров EDX:EAX, регистр EDX содержит старшие 32 бита результата, флаги EFLAGS.CF и EFLAGS.OF сбрасываются, если результирующее значение в регистре EDX равно нулю, иначе — устанавливаются в 1.

Команда MUL позволяет также производить вычисления в неупакованном двоично-десятичном формате (неупакованный BCD-формат). Используя команду AAM сразу же вслед за командой MUL, можно произвести так называемую ASCII-коррекцию результата умножения и получить произведение в таком же неупакованном BCD-формате, как и исходные операнды.

Для умножения целочисленных значений со знаком предназначены команды MUL, FIMUL, PMULLW и некоторые другие.

Операция:

IF (Операция над байтами)

THEN AX = AL * r/m8

ELSE (* Операция над словами или двойными словами *)

IF (OperandSize = 16)

THEN DX:AX = AX * r/m16

ELSE (* OperandSize = 32 *)

EDX:EAX = EAX * r/m32

Особые ситуации защищенного режима:

#GP(0), если при обращении к операнду в памяти в сегменте DS, ES, FS или GS используется нулевой селектор.
#GP(0), если любая часть операнда в памяти находится вне допустимого пространства эффективных адресов в сегменте CS, DS, ES, FS или GS.
#SS(0), если любая часть операнда в памяти находится вне допустимого пространства эффективных адресов в стековом сегменте SS.

Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.

Intel486 … :
#AC(0) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).

Особые ситуации режима реальной адресации:

#GP, если любая часть операнда в памяти находится вне допустимого для реального режима пространства эффективных адресов в сегменте CS, DS, ES, FS или GS.
#SS, если любая часть операнда в памяти выходит за допустимую для реального режима верхнюю границу стекового сегмента SS.

Intel386 … :
#UD при использовании префикса LOCK.

Особые ситуации режима V86:

#GP(0), если любая часть операнда в памяти находится вне допустимого пространства эффективных адресов в сегменте CS, DS, ES, FS или GS.
#SS(0), если любая часть операнда в памяти находится вне допустимого пространства эффективных адресов в стековом сегменте SS.

Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.

Intel486 … :
#AC(0) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).

Замечание:

К командам целочисленной арифметики относятся команды ADD, ADC, SUB, SBB, IMUL, MUL, IDIV, DIV, INC, DEC, NEG, CMP.

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

  • команды сложения (ADD, ADC, INC);
  • команды вычитания (SUB, SBB, DEC);
  • команды умножения (IMUL, MUL);
  • команды деления ( >У команды MUL есть специфическая особенность, которую использовали для идентификации процессоров-клонов V20/V30 производства компании NEC. У процессоров Intel значение флага FLAGS.ZF не меняется по результату выполнения команды, а в процессорах V20/V30 этот флаг устанавливается по результату операции. Следующий код позволяет отличать эти процессоры от продукции Intel.

xor AL, AL ; ZF = 1

mov AL, 40h
mul AL ; проверка ZF по результату операции

jz cpu_NEC_V20_V30 ; процессор V20/V30
jnz cpu_Intel_808x ; процессор Intel

Команда mul ассемблер

7.1. Сложение и вычитание.

7.1.1. ADD – команда для сложения двух чисел. Она работает как с числами со знаком, так и без знака.

Логика работы команды:

Возможные сочетания операндов для этой команды аналогичны команде MOV .

По сути дела, это – команда сложения с присвоением, аналогичная принятой в языке C / C ++:

Операнды должны иметь одинаковый размер. Результат помещается на место первого операнда.

После выполнения команды изменяются флаги, по которым можно определить характеристики результата:

  1. Флаг CF устанавливается, если при сложении произошёл перенос из старшего разряда. Для беззнаковых чисел это будет означать, что произошло переполнение и результат получился некорректным.
  2. Флаг OF обозначает переполнение для чисел со знаком.
  3. Флаг SF равен знаковому биту результата (естественно, для чисел со знаком, а для беззнаковых он равен старшему биту и особо смысла не имеет).
  4. Флаг ZF устанавливается, если результат равен 0.
  5. Флаг PF — признак чётности, равен 1, если результат содержит нечётное число единиц.

add ax ,5 ; AX = AX + 5

add dx,cx ;DX = DX + CX

add dx,cl ;Ошибка: разный размер операндов.

7.1.2. SUB — команда для вычитания одного числа из другого. Она работает как с числами со знаком, так и без знака.

Логика работы команды:

Возможные сочетания операндов для этой команды аналогичны команде MOV .

По сути дела, это – команда вычитания с присвоением, аналогичная принятой в языке C / C ++:

Операнды должны иметь одинаковый размер. Результат помещается на место первого операнда.

На самом деле вычитание в процессоре реализовано с помощью сложения. Процессор меняет знак второго операнда на противоположный, а затем складывает два числа.

sub ax ,13 ; AX = AX — 13

sub ax , bx ; AX = AX + BX

sub b x,cl ;Ошибка: разный размер операндов.

7.1.3. Инкремент и декремент. Очень часто в программах используется операция прибавления или вычитания единицы. Прибавление единицы называется инкрементом, а вычитание — декрементом. Для этих операций существуют специальные команды процессора: INC и DEC. Эти команды не изменяют значение флага CF.

Эти команды содержит один операнд и имеет следующий синтаксис:

Логика работы команд:

В качестве инкремента допустимы регистры и память: reg , mem .

inc ax ; AX = AX + 1

dec ax ; AX = AX — 1

7.1.4. NEG – команда для изменения знака операнда.

Логика работы команды:

В качестве декремента допустимы регистры и память: reg , mem .

7.2. Сложение и вычитание с переносом.

В системе команд процессоров x86 имеются специальные команды сложения и вычитания с учётом флага переноса (CF). Для сложения с учётом переноса предназначена команда ADC, а для вычитания — SBB. В общем, эти команды работают почти так же, как ADD и SUB, единственное отличие в том, что к младшему разряду первого операнда прибавляется или вычитается дополнительно значение флага CF.

Они позволяют выполнять сложение и вычитание многобайтных целых чисел, длина которых больше, чем разрядность регистров процессора (в нашем случае 16 бит). Принцип программирования таких операций очень прост — длинные числа складываются (вычитаются) по частям. Младшие разряды складываются(вычитаются) с помощью обычных команд ADD и SUB, а затем последовательно складываются(вычитаются) более старшие части с помощью команд ADC и SBB. Так как эти команды учитывают перенос из старшего разряда, то мы можем быть уверены, что ни один бит не потеряется. Этот способ похож на сложение(вычитание) десятичных чисел в столбик.

На следующем рисунке показано сложение двух двоичных чисел командой ADD:

При сложении происходит перенос из 7-го разряда в 8-й, как раз на границе между байтами. Если мы будем складывать эти числа по частям командой ADD, то перенесённый бит потеряется и в результате мы получим ошибку. К счастью, перенос из старшего разряда всегда сохраняется в флаге CF. Чтобы прибавить этот перенесённый бит, достаточно применить команду ADC:

//Сложение двух чисел с учетом переноса: FFFFFFAA + FFFF

Читать еще:  Метки в ассемблере
Ссылка на основную публикацию
Adblock
detector