Деление ассемблер: Деление и умножение в Assembler

Деление и умножение в Assembler

Здравствуйте, уважаемые друзья! Продолжаем изучать нашу рубрику, на очереди тема умножения и деления в Assembler. Разберемся со всеми тонкостями этих операций, конечно же, на практическом примере.

Основные команды

  • Для умножения в Assembler используют команду mul
  • Для деления в Assembler используют команду div

Правила умножения в Assembler

Итак, как мы уже сказали, при умножении и делении в Assembler есть некоторые тонкости, о которых дальше и пойдет речь. Тонкости эти состоят в том, что от того, какой размерности регистр мы делим или умножаем многое зависит. Вот примеры:

  • Если аргументом команды mul является 1-байтовый регистр (например mul bl), то значение этого регистра bl умножится на значение регистра al, а результат запишется в регистр ax, и так будет всегда, независимо от того, какой 1-байтовый регистр мы возьмем.
    bl*al = ax
  • Если аргументом является регистр из 2 байт(напримерmul bx), то значение в регистре bx умножится на значение, хранящееся в регистре ax, а результат умножения запишется в регистр eax.
    bx*ax = eax
  • Если аргументом является регистр из 4 байт(напримерmul ebx), то значение в регистре ebx умножится на значение, хранящееся в регистре eax, а результат умножения запишется в 2 регистра: edx и eax.
    ebx*eax = edx:eax

Правила деления в Assembler

Почти аналогично реализуется и деление, вот примеры:

  • Если аргументом команды div является 1-байтовый регистр (например div bl), то значение регистра ax поделится на значение регистра bl, результат от деления запишется в регистр al, а остаток запишется в регистр ah.
    ax/bl = al, ah
  • Если аргументом является регистр из 2 байт(напримерdiv bx), то процессор поделит число, старшие биты которого хранит регистр dx, а младшие ax на значение, хранящееся в регистре bx. Результат от деления запишется в регистр ax, а остаток запишется в регистр dx.
    (dx,ax)/bx = ax, dx
  • Если же аргументом является регистр из 4 байт(напримерdiv ebx), то процессор аналогично предыдущему варианту поделит число, старшие биты которого хранит регистр edx, а младшие eax на значение, хранящееся в регистре ebx. Результат от деления запишется в регистр eax, а остаток запишется в регистр edx.
    (edx,eax)/ebx = eax, edx

Программа

Далее перейдем к примеру: он не должен вызвать у вас каких либо затруднений, если вы читали наши предыдущие статьи, особенно важна статья про вывод на экран, советую вам с ней ознакомиться. Ну а мы начнем:

.386
.model flat,stdcall
option casemap:none
include ..\INCLUDE\kernel32.inc 
include ..\INCLUDE\user32.inc 
includelib ..\LIB\kernel32.lib 
includelib ..\LIB\user32.lib 
BSIZE equ 15   
.data
ifmt db "%d", 0     ;строка формата
stdout dd ?         
cWritten dd ?
CRLF WORD ?
.data?
buf db BSIZE dup(?) ;буфер

Стандартное начало, в котором мы подключаем нужные нам библиотеки и объявляем переменные для вывода чисел на экран. Единственное о чем нужно сказать: новый для нас раздел .data? Знак вопроса говорит о том, что память будет выделяться на этапе компилирования и не будет выделяться в самом исполняемом файле с расширением .exe (представьте если бы буфер был большего размера) . Такое объявление — грамотное с точки зрения программирования.

.code
start:
invoke GetStdHandle, -11 
mov stdout,eax 
mov CRLF, 0d0ah
;-------------------------деление
mov eax, 99
mov edx, 0
mov ebx, 3
div ebx
invoke wsprintf, ADDR buf, ADDR ifmt, eax
invoke WriteConsoleA, stdout, ADDR buf, BSIZE, ADDR cWritten, 0
invoke WriteConsoleA, stdout, ADDR CRLF, 2, ADDR cWritten,0

В разделе кода, уже по традиции, считываем дескриптор экрана для вывода и задаем значения для перевода каретки. Затем помещаем в регистры соответствующие значения и выполняем деление регистра ebx, как оно реализуется описано чуть выше. Думаю, тут понятно, что мы просто делим число 99 на 3, что получилось в итоге выводим на экран консоли.

;-------------------------умножение
mov bx, 4
mov ax, 3
mul bx
invoke wsprintf, ADDR buf, ADDR ifmt, eax
invoke WriteConsoleA, stdout, ADDR buf, BSIZE, ADDR cWritten, 0
invoke ExitProcess,0  
end start

Думаю, что здесь тоже все понятно и без комментариев. Как производиться умножение в Assembler вы тоже можете прочитать чуть выше, ну и результат выводим на экран.

Просмотр консоли

Этот код я поместил в файл seventh.asm, сам файл поместил в папку BIN (она появляется при установке MASM32). Далее открыл консоль, как и всегда, с помощью команды cd перешел в эту папку и прописал amake.bat seventh. Скомпилировалось, затем запускаю исполняемый файл и в консоли получаются такие числа:

Как видите, мы правильно посчитали эти операции.

На этом сегодня все! Надеюсь вы научились выполнять деление и умножение на Assembler.

Скачать исходники

Assembler: div & mul. Приветствуем всех! С вами команда IT… | by ITRoot Corp

Приветствуем всех! С вами команда IT Root, и сегодня в планах у нас команды div и mul

  • Для умножения в Assembler используют команду MUL
  • Для деления в Assembler используют команду DIV

Умножение в Assembler

Для умножения чисел без знака предназначена команда MUL. У этой команды только один операнд — второй множитель, который должен находиться в регистре или в памяти. Местоположение первого множителя и результата задаётся неявно и зависит от размера операнда:

Отличие умножения от сложения и вычитания в том, что разрядность результата получается в 2 раза больше, чем разрядность сомножителей. Также и в десятичной системе — например, умножая двухзначное число на двухзначное, мы можем получить в результате максимум четырёхзначное. Запись «DX:AX» означает, что старшее слово результата будет находиться в DX, а младшее — в AX.

Если старшая часть результата равна нулю, то флаги CF и ОF будут иметь нулевое значение. В этом случае старшую часть результата можно отбросить. Это свойство можно использовать в программе, если результат должен быть такого же размера, как множители.

Если аргументом команды MUL является 1-байтовый регистр (например MUL bl), то значение этого регистра bl умножится на значение регистра al, а результат запишется в регистр ax, и так будет всегда, независимо от того, какой 1-байтовый регистр мы возьмем.

al * bl = ax;

Если аргументом является регистр из 2 байт(например MUL bx), то значение в регистре bx умножится на значение, хранящееся в регистре ax, а результат умножения запишется в регистр eax.

bx * ax = dx:ax

Если аргументом является регистр из 4 байт(например MUL ebx), то значение в регистре ebx умножится на значение, хранящееся в регистре eax, а результат умножения запишется в 2 регистра: edx и eax.

ebx * eax = edx:eax

Деление в Assembler

Для умножения чисел без знака предназначена команда DIV, которая относится к группе команд целочисленной арифметики и производит целочисленное деление с остатком беззнаковых целочисленных операндов.

Делимое, частное и остаток задаются неявно. Делимое является переменной в регистре (или регистровой паре) AX, DX:AX или EDX:EAX в зависимости от кода команды и размера операнда (что также определяет и разрядность делителя). Единственный явный операнд команды — операнд-источник (SRC), задающий делитель — может быть переменной в регистре или в памяти.

Целая часть частного помещается в регистр AL, AX или EAX в зависимости от заданного размера делителя (8, 16 или 32 бита). При этом остаток от целочисленного деления помещается в регистр AH, DX или EDX соответственно.

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

Если частное, получаемое в результате деления, оказывается слишком велико, чтобы поместиться в целевом регистре-назначении (то есть имеет место переполнение), или если делитель равен нулю, то генерируется особая ситуация #DE.

Если аргументом команды div является 1-байтовый регистр (например DIV bl), то значение регистра ax поделится на значение регистра bl, результат от деления запишется в регистр al, а остаток запишется в регистр ah.

ax / bl = al:ah

Если аргументом является регистр из 2 байт(например DIV bx), то процессор поделит число, старшие биты которого хранит регистр dx, а младшие ax на значение, хранящееся в регистре bx. Результат от деления запишется в регистр ax, а остаток запишется в регистр dx.

(dx:ax) / bx = ax:dx

Если же аргументом является регистр из 4 байт(например DIV ebx), то процессор аналогично предыдущему варианту поделит число, старшие биты которого хранит регистр edx, а младшие eax на значение, хранящееся в регистре ebx. Результат от деления запишется в регистр eax, а остаток запишется в регистр edx.

(edx:eax) / ebx = eax:edx

AfterWord

На этой ноте пост подходит к концу. Теперь вы знаете про команды умножения и деления в Assembler. В одном из следующих постов мы разберём умножение и деление чисел со знаком. Спасибо за внимание и до скорых встреч!)

Не забывайте, что множество интересных статей можно найти на нашем Telegram канале и в Telegram боте, также все статьи мы публикуем в Twitter и Facebook

сборка — Разделение на ассемблере x86

спросил

Изменено 4 года назад

Просмотрено 21к раз

Мой колледж дал мне упражнение:

1. Создайте новый документ в Jasmin

2. Используйте AL-Register, чтобы добавить 9до 8.

3. Вычесть 2.

4. Разделить на 7.

Мое решение:

 mov al,9
добавить аль,8
саб аль, 2
 

Но как мне разделить на 7? Я пробовал что-то вроде div al,7 , но это не работает.

  • сборка
  • x86

div операция делит (без знака) значение в регистрах AX, DX:AX или EDX:EAX (делимое) на исходный операнд (делитель) и сохраняет результат в AX (AH:AL), DX:AX или EDX:EAX.

source

итак, чтобы разделить значение на al, нужно сделать:

 mov ah, 0 # очистить ah, также можно сделать это раньше, как move ax, 9
mov bl, 7 # подготовить делитель
div bl # al = ax / bl, ah = ax % bl
 

после этого al будет содержать частное, а ah будет содержать остаток

6

Существует инструкция DIV , которая выполняет деление, но вам нужно сначала поместить делимое в AX (или один из его братьев и сестер).

Инструкции div и idiv не имеют форм, которые принимают немедленную форму. Они принимают только один явный операнд (регистр или память), а делимое неявно в AX, или DX:AX, EDX:EAX или RDX:RAX. См. этот ответ, чтобы узнать, как их использовать.

Но x86 в 16- и 32-битном режиме имеет инструкцию прямого деления, и на самом деле она немного быстрее, чем div r/m8 на процессорах Intel до Skylake: aam 7 разделит AL на непосредственную 7, помещая частное в AH, остаток в AL. (https://agner.org/optimize/ говорит, что на Haswell задержка на 1 такт ниже, а пропускная способность на 1 такт выше, чем у div r8 . И это 8 мопов вместо 9.)

Обратите внимание, что это отличается от mov cl, 7 / div cl , что берет все AX в качестве делимого и помещает

частное в AL, остаток в AH .

AAM недоступен в 64-битном длинном режиме , удален вместе с другими менее полезными устаревшими инструкциями BCD. Но если это сэкономит количество мопов в целом (включая mov непосредственно к реестру), это может быть полезно. На Skylake он стоит 11 мкп против 10 у div r8 , а пропускная способность на 1с хуже , и такая же латентность.

Этот код работает только для разделения однозначных чисел.

 .модель маленькая
.данные
 msg1 db 'введите дивиденд:$'
 msg2 db 10,13,'введите делитель:$'
 результат БД 10,13,'результат:$'
 дивиденды дб ?
 делитель дб?
.код
.запускать
мов топор,@данные
мов дс, топор
мов ах, 9
lea dx, msg1
через 21 час
мов ах, 1
через 21 час
саб аль, 30ч
mov дивиденды, al
мов ах, 9lea dx, msg2
через 21 час
мов ах, 1
через 21 час
саб аль, 30ч
mov divisor , al
движение, дивиденды
мов бл, делитель
мов ах,0
раздел бл
мов ах, 9
lea dx, результат
через 21 час
мов ах, 2
добавить al,30h
мов дл, аль
через 21 час
мов ах, 4ch
через 21 час
конец
 

Зарегистрируйтесь или войдите в систему

Зарегистрируйтесь с помощью Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя адрес электронной почты и пароль

Опубликовать как гость

Электронная почта

Обязательно, но не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Сборка

— Как разделить ввод на 2, а затем округлить

спросил

Изменено 6 лет, 10 месяцев назад

Просмотрено 4к раз

Я не совсем понимаю концепцию div в ассемблере, все, что я знаю, это то, что частное сохраняется в EAX, а остаток в EDX. Но я пытаюсь понять, как разделить пользовательский ввод на 2, и если есть остаток, округлить его. Например:

  • N= 3 / 2 = 1,5 округлить до 2
  • N= 9 / 2 = 4,5 Округлить до 5
  • N= 4 / 2 = 2
  • в сборе
  • x86

1

Чтобы разделить на два, вы сдвигаете регистр вправо на один разряд. Младший бит смещается во флаг переноса.

Таким образом, если число четное, 0 сдвигается в CF. Если число нечетное, 1 сдвигается в CF.

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

 ; предположим, что значение находится в EAX
шр акс, 1; разделить на 2. Младший бит в флаг переноса
адк акс, 0 ; добавить значение флага переноса
 

1

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

 6 / 2 = 3,0 (6 + 1) / 2 = 3 // остается прежним
7 / 2 = 3,5 (7 + 1) / 2 = 4 // округляется в большую сторону
 

В общем случае, если вы хотите разделить на н и округляем, перед делением прибавляем н/2 или н >> 1 . Где n четно, результат явно хороший. Там, где n нечетное, это работает так же, как в этом примере деление на 5, где вы добавляете 5/2 , т.е. 2

 5/5 = 1,0 (5 + 2) / 5 = 1 // остается прежним
6 / 5 = 1,2 (6 + 2) / 5 = 1 // округлить в меньшую сторону
7 / 5 = 1,4 (7 + 2) / 5 = 1 // округлить в меньшую сторону
8 / 5 = 1,6 (8 + 2) / 5 = 2 // округлить в большую сторону
9 / 5 = 1,8 (9+ 2) / 5 = 2 // округлить в большую сторону
 

5

 Н = (3 + 2 - 1) / 2
xor эдкс, эдкс
добавить eax, ecx
декабрь акс
раздел ecx
 

Или, если 2 является константой:

 inc eax
шр акс, 1
 

Здесь есть над чем подумать:

  • Будет ли число переполняться, если вы добавите единицу перед делением?
  • Вам нужно обрабатывать отрицательные числа?
  • Операция div дорогая, шр намного быстрее

shr делится на два и пол для чисел без знака

sar делится на два и пол для чисел со знаком .

Оставить комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *