Умножение и деление в ассемблере.
Умножение и деление в ассемблере. — it-black.ru Перейти к содержимому SearchВсе мы знаем со школы что такое умножение и деление и конечно же в ассемблере эти команды присутствуют, и я расскажу Вам о них. В ассемблере умножение и деление для положительных и отрицательных чисел выполняются по-разному.
Умножение положительных чисел
Для умножения положительных чисел в ассемблере предназначена команда «MUL». У этой команды только один операнд — второй множитель, который должен находиться в регистре или в памяти. Местоположение первого множителя и результата задаётся неявно и зависит от размера операнда:
Размер операнда | Множитель | Результат |
---|---|---|
Байт | AL | AX |
Слово | AX | DX:AX |
Некоторые тонкости умножения:
- Если аргументом команды 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
Умножение отрицательных чисел
Для умножения чисел со знаком предназначена команда «IMUL». Эта команда имеет три формы, различающиеся количеством операндов:
- С одним операндом — форма, аналогичная команде MUL. В качестве операнда указывается множитель. Местоположение другого множителя и результата определяется по таблице.
- С двумя операндами — указываются два множителя. Результат записывается на место первого множителя. Старшая часть результата в этом случае игнорируется. Эта форма команды не работает с операндами размером 1 байта.
- С тремя операндами — указывается положение результата, первого и второго множителя. Второй множитель должен быть непосредственным значением. Результат имеет такой же размер, как первый множитель, старшая часть результата игнорируется. Это форма тоже не работает с однобайтными множителями.
Деление положительных чисел
Деление целых двоичных чисел — это всегда деление с остатком. По аналогии с умножением, размер делителя, частного и остатка должен быть в 2 раза меньше размера делимого. Деление положительных чисел осуществляется с помощью команды «DIV». У этой команды один операнд — делитель, который должен находиться в регистре или в памяти. Местоположение делимого, частного и остатка задаётся неявно и зависит от размера операнда:
Размер операнда (делителя) | Делимое | Частное | Остаток |
---|---|---|---|
Байт | AX | AL | AH |
Слово | DX:AX | AX | DX |
Некоторые тонкости деления:
- Если аргументом команды 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
Деление отрицательных чисел
Для деления отрицательных чисел предназначена команда IDIV. Единственным операндом является делитель. Местоположение делимого и частного определяется также, как для команды DIV. Эта команда также генерирует прерывание при делении на ноль или слишком большом частном.
- Виктор Черемных
- 13 августа, 2017
- No Comments
Группа в VK
Обнаружили опечатку?
Сообщите нам об этом, выделите текст с ошибкой и нажмите Ctrl+Enter, будем очень признательны!
Свежие статьи
Облако меток
Похожие статьи
Команды работы с битами.
Работать с отдельными битами операндов можно, используя логические операции и сдвиги. Также в системе команд x86 существуют специальные команды для работы с битами: это команды
Основы создания макросов в Assembler.
Макросы — это шаблоны для генерации кода. Один раз создав макрос, можно использовать его во многих местах в коде программы. Макросы делают процесс программирования на
Синтаксис объявления меток.
Метка в ассемблере – это символьное имя, обозначающее ячейку памяти, которая содержит некоторую команду. Метка может содержать следующие символы: Буквы (от A до Z и
Локальные переменные.
Локальные переменные в Assembler используются для хранения промежуточных результатов во время выполнения процедуры. В отличие от глобальных, эти переменные являются временными и создаются при запуске
Instagram Vk Youtube Telegram OdnoklassnikiПолезно знать
Рубрики
Авторы
Команда MUL
Лучшие книги по Ассемблеру
Сделал подборку не новых, но проверенных книг по программированию на языке ассемблера. |
Инструкция MUL в Ассемблере выполняет умножение без знака. Понять работу команды MUL несколько сложнее, чем это было для команд, рассмотренных ранее. Но, надеюсь, что я помогу вам в этом разобраться.
Итак, синтаксис команды MUL такой:
MUL ЧИСЛО
Выглядит всё очень просто. Однако эта простота обманчива.
Прежде чем разобраться в подробностях работы этой инструкции, давайте посмотрим, что может быть ЧИСЛОМ.
ЧИСЛОМ может быть один из следующих:
- Область памяти (MEM)
- Регистр общего назначения (REG)
Эта команда не работает с сегментными регистрами, а также не работает непосредственно с числами. То есть вот так
MUL 200 ; неправильно
делать нельзя.
А теперь алгоритм работы команды MUL:
- Если ЧИСЛО — это БАЙТ, то AX = AL * ЧИСЛО
- Если ЧИСЛО — это СЛОВО, то (DX AX) = AX * ЧИСЛО
Вот такая немного сложноватая команда. Хотя сложно это с непривычки. Сейчас мы разберём всё “по косточкам” и всё станет ясно.
Для начала обратите внимание, что инструкция MUL работает либо с регистром АХ, либо с регистром AL. То есть перед выполнением этой команды нам надо записать в регистр АХ или в регистр AL значение, которое будет участвовать в умножении. Сделать это можно, например, с помощью уже известной нам команды MOV.
Затем мы выполняем умножение, и получаем результат либо в регистр АХ (если ЧИСЛО — это байт), либо в пару регистров DX и AX (если ЧИСЛО — это слово). Причём в последнем случае в регистре DX будет старшее слово, а в регистре AX — младшее.
А теперь, чтобы совсем всё стало понятно, разберём пару примеров — с байтом и словом.
Итак, например, нам надо умножить 150 на 250. Тогда мы делаем так:
MOV AL, 150 ; Первый множитель в регистр AL MOV BL, 250 ; Второй множитель в регистр BL MUL BL ; Теперь АХ = 150 * 250 = 37500
Обратите внимание, что нам приходится два раза использовать команду MOV, так как команда MUL не работает непосредственно с числами, а только с регистрами общего назначения или с памятью.
После выполнения этого кода в регистре АХ будет результат умножения чисел 150 и 250, то есть число 37500 (927С в шестнадцатеричной системе).
Теперь попробуем умножить 10000 на 5000.
MOV AX, 10000 ; Первый множитель в регистр AX MOV BX, 5000 ; Второй множитель в регистр BX MUL BX ; Теперь (DX АХ) = 10000 * 5000 = 50000000
В результате мы получили довольно большое число, которое, конечно, не поместится в слово. Поэтому для результата используются два регистра — DX и AX. В нашем примере в регистре DX, будет число 762 (02FA — в шестнадцатеричной системе), а в регистре АХ — число 61568 (F080 — в шестнадцатеричной системе). А если рассматривать их как одно число (двойное слово), где в старшем слове 762, а в младшем — 61568, то это и будет 50000000 (2FAF080 — в шестнадцатеричной системе).
Если не верите — может перевести всё это в двоичное число и проверить.
Теперь о флагах.
После выполнения команды MUL состояния флагов ZF, SF, PF, AF не определены и могут быть любыми.
А если старшая секция результата (регистр AH при умножении байтов или регистр DX при умножении слов) равна нулю, то
CF = OF = 0
Иначе эти флаги либо не равны, либо равны 1.
В конце как обычно расскажу, почему эта команда ассемблера называется MUL. Это сокращение от английского слова MULTIPLY, которое можно перевести как “умножить, умножать”.
Подписаться на Дзен-канал
Вступить в группу «Основы программирования» Подписаться на рассылки по программированию |
Первые шаги в программирование
Главный вопрос начинающего программиста – с чего начать? Вроде бы есть желание, но иногда «не знаешь, как начать думать, чтобы до такого додуматься». У человека, который никогда не имел дело с информационными технологиями, даже простые вопросы могут вызвать большие трудности и отнять много времени на решение. Подробнее… |
x86 — функция MUL в ассемблере
Они называются инструкциями и определяют операции , которые должны выполняться процессором. mov
— это мнемоника для mov e, а mul
— мнемоника для mul tiply. Другие общие инструкции включают добавить
, sub
и div
. Я надеюсь, вы сможете понять, какую операцию они определяют!
Большинство инструкций принимают два параметра. На техническом жаргоне их часто называют 9.0003 операндов . Первый (слева) — это пункт назначения , а второй (справа) — источник . Таким образом, в случае mov bx, 5
это перемещает буквальное значение 5
в регистр назначения bx
. Порядок этих параметров, конечно, имеет значение, потому что вы не можете переместить содержимое регистра bx
в буквальное значение 5
!
Инструкция mul
немного странная, потому что некоторые из ее операндов являются неявными. То есть они явно не указаны как параметры. Для mul
, операнд назначения жестко запрограммирован как регистр x
. Исходный операнд — это тот, который вы передаете в качестве параметра: это может быть либо регистр, либо ячейка памяти.
Таким образом, вы можете представить, что mul cx
означает mul ax, cx
, но вы не напишите это так, потому что регистр назначения ax
является неявным.
Теперь инструкция mul
дает процессору команду умножить операнд-адресат на операнд-источник и сохранить результат в адресате. В коде вы можете представить, что mul cx
преобразуется в ax = ax * cx
. И теперь вы должны увидеть проблему: вы не инициализировали содержимое регистра x
, поэтому вы умножаете 10 (значение, которое вы поместили в cx
) на тот мусор, который остался в x
. Таким образом, результат бессмысленен!
Если вы действительно хотите сделать 5 * 10, то вам достаточно изменить один символ в вашем коде:
mov ax, 5 ; топор = 5 мов сх, 10 ; сх = 10 мул сх ; топор = топор * сх; на самом деле dx:ax = ax * cx
Результат будет сохранен в x
, который является неявным регистром назначения.
Ну, технически результат будет сохранен в dx:ax
. Это пара регистров , и это означает, что старшая часть результата будет сохранена в dx
, а младшая часть результата будет сохранена в x
. Зачем это дополнительное усложнение? Потому что умножение двух 16-битных значений может привести к значению, превышающему 16 бит! Возврат полного результата умножения в 9Пара 0003 16-битных регистров позволяет инструкции mul
возвращать 32-битный результат. Однако, когда вы только учитесь, вам не нужно беспокоиться об этом. Вы можете просто игнорировать возможность переполнения и извлечь нижнюю часть результата из x
. (Но помните, что 16-битный mul
перезаписывает dx
, хотите вы этого или нет. На 386 и более поздних версиях вы можете использовать imul axe, cx
, чтобы действительно сделать ax *= cx
, не тратя время на запись dx
.)
И хотя я уверен, что это всего лишь игрушечный пример, на самом деле нет причин писать код , который умножает две константы вместе. Это можно сделать во время сборки, либо используя калькулятор и жестко закодировав значение, либо записав умножение констант символически и позволив вашему ассемблеру выполнить вычисления. То есть mov ax, 50
. Или пусть ваш ассемблер сделает это за вас с mov ax, 5 * 10
. Но, как я уже сказал, я уверен, что вы уже знали это!
Если ничего не помогло, обратитесь к документации за инструкциями, которые вызывают у вас затруднения. Вы почти всегда можете найти это в Интернете, погуглив название инструкции и «x86». Например, документацию по mul
можно найти здесь, а также на нескольких других сайтах. Эта информация может быть довольно сложной, но, приложив немного усилий, вы сможете извлечь нужную информацию. Вы также найдете много другой полезной информации и ссылок в вики по тегам x86.
, но по какой-то причине я не вижу изменения регистров, когда отмечена функция MUL.
Я также должен отметить, что если вы используете отладчик для пошагового выполнения вашего кода, текущая помеченная/выделенная строка — это строка, которая около для выполнения. Он еще не выполнен, поэтому его влияние на регистры, память и т. д. еще не будет видно. Вы должны перешагнуть инструкцию, чтобы метка/выделение было на следующей строке, и тогда вы увидите эффекты предыдущей (только что выполненной) инструкции.
Если вы поняли мое объяснение выше, после инструкции mul
вы должны увидеть изменение содержимого регистров ax
и dx
. Вы также увидите изменение флагов и указателя инструкций, если ваш отладчик показывает какой-либо из них. Больше ничего не должно измениться! (Запись справочного руководства Intel для mul
не перечисляет никаких других эффектов на архитектурное состояние машины.)
x86 — как выполнить умножение в сборке x8086, но без команды mul
Выполнение умножения без использования инструкции mul
достаточно ясно, но сказать, что вы можете использовать только инструкции shl
, shr
, rol
и ror
, недостаточно для решения этой задачи.
В качестве примера см. приведенную ниже подпрограмму, которая умножает без использования инструкции mul
:
; IN (al=первое число [0,9], ah=второе число [0,9]) OUT (ax=произведение [0,81]) МалыйМул: мов [$+6], аль ; Это перезаписывает операнд по умолчанию AAD. мов аль, 0 аад ; -> AX является продуктом рет
и с помощью
добавить
ипод
можно сделать эту программу?
Да, используя повторное добавление , и пока вы не ожидаете , используйте только , добавьте
и под
, потому что вам всегда понадобится хотя бы какая-то инструкция ветвления для создания цикла. И даже если бы вы избегали создания цикла посредством развертывания, вам все равно пришлось бы тестировать и переходить при каком-то условии выхода.
пример: 5 * 3 --> 0 + 5 + 5 + 5 <--- 3x --->
; IN (al=первое число [0,9], bl=второе число [0,9]) OUT (cl=произведение [0,81]) МалыйМул: саб кл, кл ; Устанавливает CL=0 . a: добавить cl, al саб бл, 1 jnz .а ; Прыгает БЛ-1 раз (возможно 255 раз!) рет
- Если когда-либо второе число в BL было нулем, то этот простой код не работает! то этот простой код будет очень неэффективно повторяться 256 раз! Тем не менее, он даст правильный результат (равный 0)
- В качестве оптимизации мы можем выбрать наименьшее из двух чисел для управления циклом. Меньше итераций — это хорошо.
; IN (al=первое число [0,9], bl=второе число [0,9]) OUT (cl=произведение [0,81]) МалыйМул: саб кл, кл ; Устанавливает CL=0 смп бл, ал jbe .b хчг бл, ал jmp .b .a: добавить cl, al .b: sub bl, 1 джнс .а ; Прыгает BL раз рет
Простая версия алгоритма сдвига и добавления :
; IN (al=первое число [0,9], bl=второе число [0,9]) OUT (cl=произведение [0,81]) МалыйМул: саб кл, кл ; Устанавливает CL=0 джмп .с .a: добавить cl, al .б: шл ал, 1 ; Удваивает AL, то же, что и `ADD AL, AL` .