Деление в ассемблере: Умножение и деление в ассемблере.

Содержание

Умножение и деление в ассемблере.

Умножение и деление в ассемблере. — it-black.ru Перейти к содержимому

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

Умножение положительных чисел

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

Размер операндаМножительРезультат
БайтALAX
СловоAXDX: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». У этой команды один операнд — делитель, который должен находиться в регистре или в памяти. Местоположение делимого, частного и остатка задаётся неявно и зависит от размера операнда:

Размер операнда
(делителя)
ДелимоеЧастноеОстаток
БайтAXALAH
СловоDX:AXAXDX

Некоторые тонкости деления:

  • Если аргументом команды 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. Эта команда также генерирует прерывание при делении на ноль или слишком большом частном.

Facebook

Twitter

  • No Comments

Группа в VK

Обнаружили опечатку?

Сообщите нам об этом, выделите текст с ошибкой и нажмите Ctrl+Enter, будем очень признательны!

Свежие статьи

Облако меток

Похожие статьи

Команды работы с битами.

Работать с отдельными битами операндов можно, используя логические операции и сдвиги. Также в системе команд x86 существуют специальные команды для работы с битами: это команды

Основы создания макросов в Assembler.

Макросы — это шаблоны для генерации кода. Один раз создав макрос, можно использовать его во многих местах в коде программы. Макросы делают процесс программирования на

Синтаксис объявления меток.

Метка в ассемблере – это символьное имя, обозначающее ячейку памяти, которая содержит некоторую команду. Метка может содержать следующие символы: Буквы (от A до Z и

Локальные переменные.

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

Instagram Vk Youtube Telegram Odnoklassniki

Полезно знать

Рубрики

Авторы

Урок 11.

Умножение и деление

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

Умножение чисел без знака

Допустим, нам требуется сделать для нашего микрокалькулятора программу умножения количества товаров и цен. Применим команду ассемблера MUL для умножения беззнаковых чисел. Работать с этой командой просто, первый множитель по умолчанию хранится в регистре AL или AX, а второй множитель команде передаётся как единственный операнд – хранимый в регистре или в памяти.

Размер операндаМножительРезультат
БайтALAX
СловоAXDX:AX

Как видно из таблицы, длина множителя (байт или слово) определяются по размеру переданного операнда. Если команда MUL перемножает два байтовых операнда, результатом будет слово, а если перемножаются двухбайтовые операнды, результатом будет двойное слово. При умножении в ассемблере разрядность результата будет ровно в 2 раза больше каждого из исходных значений. Говоря простым языком, если мы перемножаем числа одинаковой разрядности между собой, например 8 битное на 8 битное, то результат будет максимум из 16 бит.

mul bl ;AX = AL * BL mul ax ;DX:AX = AX * AX

mul bl    ;AX = AL * BL

mul ax    ;DX:AX = AX * AX

Если умножаются два операнда размеров в байт, то результат умножения помещается в регистр AX, а при умножении двух слов, результат комбинируется в регистрах DX:AX, где старшее слово результата будет в DX, а младшее в AX. Не всегда результатом умножения двух байт будет слово или при умножением двух слов – двойное слово. Если старшая часть результата содержит ноль, то флаги CF и OF будут также равны нулю. Соответственно старшую часть результата умножения можно игнорировать, что может быть полезно в определенных случаях.

Умножение чисел со знаком

Теперь переходим к миру математики, в котором знаковые числа перемножаются не реже беззнаковых. В ассемблере для таких операций есть команда IMUL. Инструкция имеет три формы, разнящиеся числом операндов:

Вызов с одним операндом – аналогично команде MUL. Передаваемым операндом может быть регистр или значение в памяти. Операнд умножается на значение в AL (операнд – байт) или AX (операнд – слово). Результат хранится в AX или комбинируется в DX:AX соответственно.

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

Вызов с тремя операндами – в этой форме используется операнд назначения и два операнда -источника, содержащие первый и второй множители. Первый множитель, которым может быть регистр общего назначение или область памяти, умножается на второй множитель (непосредственное значение). Итоговое произведение операндов (дважды от размера первого операнда – источника) усекается и хранится в операнде назначения (регистр общего назначения).

Примеры использования команды IMUL:

imul bl ;AX = AL * BL imul cx ;DX:AX = AX * CX imul si,-13 ;SI = SI * -13 imul bx,si,123h ;BX = SI * 123h

imul bl                 ;AX = AL * BL

imul cx                 ;DX:AX = AX * CX

imul si,-13             ;SI = SI * -13

imul bx,si,123h         ;BX = SI * 123h

Для первой формы флаги CF = 0 и OF = 0 означают, что результат умножения поместился в младшей части, в то время как CF = OF = 1 для команды IMUL в форме с двумя или тремя операндами сигнализируют переполнение, то есть потерю старшей части результата.

Деление чисел без знака

Команда DIV используется для выполнения деления беззнаковых чисел с остатком. Если при умножении разрядность произведения всегда в два раза больше множителей, то при делении действует обратный принцип – большим разрядом является делимое, а частное всегда в два раза меньше делимого. Инструкция принимает единственный операнд – делитель, помещаемый в регистр общего назначения или в память. Размер делителя распределяет делимое, частное и остаток согласно таблице:

Размер операнда
(делителя)
ДелимоеЧастноеОстаток
БайтAXALAH
СловоDX:AXAXDX

Операция деления в ассемблере может вызывать прерывание (подробнее о прерываниях будет написано в одном из следующих уроков) в ряде случаев:

  • При делении на ноль;
  • Когда происходит переполнение частного, то есть результат не помещается в отведенную разрядность (например, если делимое – слово (1000), а делитель – байт (2), то результат (500) не поместится в байт).

Примеры:

div cl ;AL = AX / CL, остаток в AH div di ;AX = DX:AX / DI, остаток в DX

div cl   ;AL = AX / CL, остаток в AH

div di   ;AX = DX:AX / DI, остаток в DX

Деление чисел со знаком

Команда IDIV используется для деления чисел со знаком. Вызов аналогичен команде DIV – передаётся единственный аргумент – делитель, который неявно определяет размеры делимого, частного и остатка. Прерывание генерируется, если выполняется деление на ноль или частное превышает отведенную разрядность.

Программа к уроку

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

s= s0+ v0t + at2/2

use16 org 100h ;===== Часть формулы: v0*t ===== mov al,[v] ;AL = v0 mov bl,[t] ;BL = t mul bl ;AX = AL * BL mov si,ax ;Произведение в SI ;===== Часть формулы: (a*t^2)/2 ===== mov al,[t] ;AL = t mul bl ;AX = AL * BL = a*t mov bl,[a] ;BX = a mov bh,0 ;BX — второй множитель, поэтому прибираемся в BH mul bx ;DX:AX = AX * BX = a*t^2 mov bx,2 ;BX = 2 div bx ;DX:AX / BX = a*t^2 / 2 ;===== Складываем все вместе ===== add ax,si ;v0*t + (a*t^2)/2 add al,[s] ;| adc ah,0 ;|. 2)/2

add al,[s]       ;|

adc ah,0         ;|… + s0

 

mov [r],ax       ;Храним результат в [r]

 

 

mov ax,4C00h     ;|

int 21h          ;|Заверешение программы

 

;—————————————

s db 180

v db 7

a db 4

t db 41

r dw ?

В 8-й строке результат умножения сохраняется в регистре SI. В 14-й строке старшая часть регистра BX – BH выставляется в ноль, что корректно только для беззнаковых чисел. Попытка присвоить ноль старшей части регистра, хранящего число со знаком может привести к ошибке, если младшая часть регистра хранит отрицательное число. Обратите внимание на строках 21-22 прибавление байта из переменной [s0] к слову AX происходит в два этапа: прибавление числа к AL, и корректировкой на перенос прибавлением нуля к AH.

Упражнение к уроку (1):

z =  a2 + 2ab + b2

Числа в формуле используйте 16 битные целые без знака.

Упражнение к уроку (2) (продвинутое):

Используя школьный курс сложения столбиком и команды MUL и ADD, напишите программу для умножения 32-битных чисел без знака. Результатом будет 64-битное число без знака.

Учебники по

AVR — 8-битное деление

В этом руководстве мы рассмотрим процедуры 8-битного деления в AVR Assembly. AVR не имеет возможности выполнять аппаратное деление (т. е. нет инструкций по делению), поэтому для выполнения этой задачи подпрограммы должны быть написаны с использованием других инструкций.

Руководство по применению Atmel AVR200 содержит полный список подпрограмм разделения — вместо того, чтобы изобретать велосипед, мы рассмотрим, как они работают.

8-битный беззнаковый раздел

8-битная подпрограмма беззнакового деления от Atmel показана ниже

 ;*************************************** **********************************************
;*
;* "div8u" - 8/8-битное беззнаковое деление
;*
;* Эта подпрограмма разделяет две регистровые переменные "dd8u" (дивиденд) и
;* "dv8u" (делитель).  Результат помещается в "dres8u", а остаток в
;* "дрем8у".
;*
;* Количество слов: 14
;* Количество циклов :97
;* Используются нижние регистры :1 (drem8u)
;* Использованы верхние регистры: 3 (dres8u/dd8u,dv8u,dcnt8u)
;*
;****************************************************** ****************************

;***** Переменные регистра подпрограммы

.def drem8u =r15 ;остаток
.def dres8u =r16 ;результат
.def dd8u =r16 ;дивиденд
.def dv8u =r17 ;делитель
.def dcnt8u =r18 ;счетчик циклов

;***** Код

div8u: sub drem8u,drem8u ;очистить остаток и перенести
лди dcnt8u, 9;инициализировать счетчик циклов
d8u_1: rol dd8u ;сдвиг делимого влево
dec dcnt8u ;счетчик уменьшения
brne d8u_2 ;если сделано
рет ; возвращаться
d8u_2: rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_3 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_1 ;иначе
d8u_3: сек ; установить перенос в результат
rjmp d8u_1 

В этой подпрограмме делитель вводится в регистр dd8u (r16), а делитель помещается в регистр dv8u (r17). По завершении результат будет сохранен в dd8u (r16), а остаток — в drem8u (r15).

Подпрограмма начинается с очистки регистра, в котором будет храниться остаток от деления, drem8u, с помощью инструкции sub.

 div8u: sub drem8u,drem8u ;очистить остаток и перенести 

Вы можете удивиться, а почему бы просто не использовать вместо этого инструкцию clr? Ну, этой подпрограмме нужно Флаг переноса сбрасывается перед выполнением, и clr оставит его без изменений. Если бы вместо этого использовался clr, clc должен был бы следовать за ним, например.

 div8u: clr drem8u ;очистить остаток
клк ; ясно нести
 

Вместо этого использование sub очищает регистр и флаг переноса в одной инструкции. Довольно хитро!

Затем dcnt8u инициализируется как счетчик циклов со значением 9.

 ldi dcnt8u,9 ;init loop counter 

A For Loop устанавливается на метке d8u_1

 d8u_1: rol dd8u ;сдвиг влево делимого
dec dcnt8u ;счетчик уменьшения
brne d8u_2 ;если сделано
рет ; return 

В каждом цикле dcnt8u будет уменьшаться, что дает 8 итераций, по одной для каждого бита в делимом. Подпрограмма вернется, когда достигнет нуля.

Затем старший бит делителя сдвигается в младший бит остатка. Мы проверяем, входит ли делитель в остаток, используя инструкцию sub. Вычитаем из этого значения делитель, и если он отрицательный (проверяется инструкцией brcc), то знаем, что делитель входит в остаток менее одного раза. В противном случае мы знаем, что он входит по крайней мере один раз.

 d8u_2: rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_3 ;если результат отрицательный 

Если делитель , а не идет в остаток, нам нужно восстановить его (поскольку он был изменен с помощью инструкции sub) и сдвинуть ноль в наш результат.

drem8u восстанавливается путем добавления к нему обратно dv8u, флаг переноса сбрасывается с помощью инструкции clc, а rol используется для сдвига нуля (значения флага переноса) в результат. Этот поток показан ниже с удаленными альтернативными ответвлениями.

 d8u_2: rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
. ..
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
↓
d8u_1: rol dd8u ;сдвиг нуля в делимое 

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

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

 d8u_2: rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_3 ;если результат отрицательный
...
d8u_3: сек ; установить перенос в результат
↓
d8u_1: rol dd8u ;сдвинуть единицу в делимое 

Для любого результата используется rjmp для возврата к началу цикла, где из делимого будет смещен еще один бит, и цикл будет повторяться для всех 8 битов.

 d8u_1: rol dd8u ;сдвиг влево делимого
...
rjmp d8u_1 

Использовать эту подпрограмму просто. Делимое просто нужно загрузить в r16, а делитель в r17, прежде чем он будет вызван.

 ldi r16,101 ; загрузить 101 в r16
лди r17,10 ; загрузить 10 в r17

вызов div8u ; вычислить 101/10 

В приведенном выше примере r16 будет содержать значение 10, когда подпрограмма закончит работу — результат деления, а r15 будет содержать 1, остаток.

Время выполнения и размер кода

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

Вышеприведенное «оптимизировано по размеру», но вместо этого рассмотрим эту «оптимизированную по скорости» версию подпрограммы.

 ;***************************************************** *******************************
;*
;* "div8u" - 8/8-битное беззнаковое деление
;*
;* Эта подпрограмма разделяет две регистровые переменные "dd8u" (дивиденд) и
;* "dv8u" (делитель).  Результат помещается в "dres8u", а остаток в
;* "дрем8у".
;*
;* Количество слов: 66 + возврат
;* Количество циклов: 50/58/66 (мин./средн./макс.) + возврат
;* Используются нижние регистры :1 (drem8u)
;* Использованы верхние регистры :2 (dres8u/dd8u,dv8u)
;*
;****************************************************** ****************************

;***** Переменные регистра подпрограммы

.def drem8u =r15 ;остаток
.def dres8u =r16 ;результат
.def dd8u =r16 ;дивиденд
.def dv8u =r17 ;делитель

;***** Код

div8u: sub drem8u,drem8u ;очистить остаток и перенести

rol dd8u ;сдвиг дивиденда влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_1 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_2 ;иначе
d8u_1: сек ; установить перенос в результат

d8u_2: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_3 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_4 ;еще
d8u_3: сек ; установить перенос в результат

d8u_4: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_5 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_6 ;иначе
d8u_5: сек ; установить перенос в результат

d8u_6: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_7 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_8 ;иначе
d8u_7: сек ; установить перенос в результат

d8u_8: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
бркк d8u_9;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_10 ;иначе
d8u_9: сек ; установить перенос в результат

d8u_10: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_11 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_12 ;еще
d8u_11: сек ; установить перенос в результат

d8u_12: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_13 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_14 ;еще
d8u_13: сек ; установить перенос в результат

d8u_14: rol dd8u ;сдвиг делимого влево
rol drem8u ;сдвинуть делимое в остаток
sub drem8u,dv8u ;остаток = остаток - делитель
brcc d8u_15 ;если результат отрицательный
добавить дрем8у,дв8у ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8u_16 ;иначе
d8u_15: сек ; установить перенос в результат

d8u_16: rol dd8u ;сдвиг делимого влево
рет
 

«Оптимизированная по скорости» версия значительно длиннее — 66 слов вместо 14. Однако она может выполняться за 58 циклов по сравнению с 97 для «оптимизированной по размеру» версии.

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

8-битное деление со знаком

8-битное деление со знаком на самом деле довольно просто, если вы знаете, как выполнять беззнаковое деление. Вам просто нужно проверить, являются ли делитель и делимое отрицательными, преобразовать их Дополнение до значения до величины, если оно есть, и продолжить беззнаковое деление, как и раньше. В конце концов, если один или другой был отрицательным (но не оба!), вам нужно преобразовать результат обратно в значение со знаком. Это показано ниже

 ;************************************************ **********************************
;*
;* "div8s" - 8/8-битное деление со знаком
;*
;* Эта подпрограмма разделяет две регистровые переменные "dd8s" (дивиденд) и
;* "dv8s" (делитель).  Результат помещается в "dres8s", а остаток в
;* "дрем8с".
;*
;* Количество слов: 22
;* Количество циклов: 103
;* Используются нижние регистры: 2 (d8s, drem8s)
;* Используются старшие регистры: 3 (dres8s/dd8s,dv8s,dcnt8s)
;*
;****************************************************** ****************************

;***** Переменные регистра подпрограммы

.def d8s =r14 ;регистр знака
.def drem8s =r15 ;остаток
.def dres8s =r16 ;результат
.def dd8s =r16 ;дивиденд
.def dv8s =r17 ;делитель
.def dcnt8s =r18 ;счетчик циклов

;***** Код

div8s: mov d8s,dd8s ;переместить делимое в регистр подписи
eor d8s,dv8s ;xor знак с делителем
sbrc dv8s,7 ;если установлен старший бит делителя
отрицательный dv8s ; поменять знак делителя
sbrc dd8s,7 ;если установлен старший бит дивиденда
отрицательный dd8s ; поменять знак делителя
sub drem8s, drem8s ;очистить остаток и перенести
лди dcnt8s, 9;инициализировать счетчик циклов
d8s_1: rol dd8s ;сдвинуть делимое влево
dec dcnt8s ;счетчик уменьшения
brne d8s_2 ;если сделано
sbrc d8s,7 ; если MSB знакового регистра установлен
отрицательные платья ; изменить знак результата
рет ; возвращаться
d8s_2: rol drem8s ;сдвинуть делимое в остаток
sub drem8s,dv8s ;остаток = остаток - делитель
brcc d8s_3 ;если результат отрицательный
добавить дрем8с,дв8с ; восстановить остаток
клк ; очистить перенос, чтобы перейти в результат
rjmp d8s_1 ;иначе
d8s_3: сек ; установить перенос в результат
rjmp d8s_1 

Эта процедура деления со знаком требует на один регистр больше, чем раньше — d8s (r14). d8s используется для отслеживания знаков операндов и результата.

Подпрограмма начинается с копирования делимого dd8s в регистр знаков d8s и выполнения исключающего ИЛИ между ним и делителем dv8s. Если делимое или делитель отрицательный, будет установлен бит 7 регистра знака, иначе он будет очищен.

 div8s: mov d8s,dd8s ;переместить делимое в регистр подписи
eor d8s,dv8s ;исключающий знак с делителем 

Далее по отдельности проверяются знак делимого и делителя. Если они отрицательны, они преобразуются в величину без знака с помощью инструкции neg.

 sbrc dv8s,7 ;если установлен старший бит делителя
отрицательный dv8s ; поменять знак делителя
sbrc dd8s,7 ;если установлен старший бит дивиденда
отрицательный dd8s ; изменить знак делителя 

После этого процедура деления может продолжаться точно так же, как и раньше, поскольку мы знаем, что используем два значения без знака. Разница лишь в том, что перед возвратом мы проверяем знаковый регистр d8s. Если установлен его самый старший бит, один из операндов был отрицательным, поэтому мы знаем, что результат должен быть отрицательным и преобразуется обратно в значение со знаком, используя neg 9.0003

 раздел 8: ...
sbrc d8s,7 ; если MSB знакового регистра установлен
отрицательные платья ; изменить знак результата
рет ; возвращаться
... 

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

Заключение

Вот и все. 8-битное деление в AVR Assembly. Не так уж и плохо, верно? Надеюсь, вы видите, насколько это дороже, чем аппаратные подпрограммы. Избегайте этого, если можете, но если необходимо… теперь вы знаете, как это сделать.

<< Предыдущая

Следующая >>

DIV — Беззнаковое деление

DIV — Беззнаковое деление
Код операции Инструкция Оп/Ан 64-битный режим Режим совместимости/этапа Описание
F6/6 РАЗДЕЛ об/м8 М Действительно Действительно Беззнаковое деление AX на об/м8 , результат сохраняется в AL := Частное, AH := Остаток.
REX + F6/6 РАЗДЕЛ об/м8 * М Действительно Н.В. Беззнаковое деление AX на об/м8 , результат сохраняется в AL := Частное, AH := Остаток.
Ф7/6 РАЗДЕЛ об/м16 М Действительно Действительно Беззнаковое деление DX:AX на об/м16 , результат сохраняется в AX := Частное, DX := Остаток.
Ф7/6 РАЗДЕЛ об/м32 М Действительно Действительно Беззнаковое деление EDX:EAX на об/м32 , результат сохраняется в EAX := Частное, EDX := Остаток.
REX.W + F7/6 РАЗДЕЛ об/м64 М Действительно Н.В. Беззнаковое деление RDX:RAX на r/m64 , результат сохраняется в RAX := Частное, RDX := Остаток.

* В 64-битном режиме r/m8 не может быть закодирован для доступа к следующим байтовым регистрам, если используется префикс REX: AH, BH, CH, DH.

Кодирование операнда инструкции ¶

Оп/Ан Операнд 1 Операнд 2 Операнд 3 Операнд 4
М ModRM:об/м (ш) нет данных нет данных Нет данных

Описание ¶

Делит беззнаковое значение в регистрах AX, DX:AX, EDX:EAX или RDX:RAX (делимое) на исходный операнд (делитель) и сохраняет результат в регистрах AX (AH:AL), DX:AX, EDX :EAX или RDX:RAX. Исходным операндом может быть регистр общего назначения или ячейка памяти. Действие этой инструкции зависит от размера операнда (делимое/делитель). Деление с использованием 64-битного операнда доступно только в 64-битном режиме.

Нецелочисленные результаты усекаются (отрезаются) в сторону 0. Остаток всегда меньше делителя по величине. Переполнение обозначается исключением #DE (ошибка деления), а не флагом CF.

В 64-битном режиме размер операции инструкции по умолчанию составляет 32 бита. Использование префикса REX.R разрешает доступ к дополнительным регистрам (R8-R15). Использование префикса REX.W способствует работе до 64 бит. В 64-битном режиме, когда применяется REX.W, инструкция делит значение без знака в RDX:RAX на исходный операнд и сохраняет частное в RAX, а остаток в RDX.

См. сводную таблицу в начале этого раздела для кодирования данных и ограничений. См. Таблицу 3-15.

Размер операнда Дивиденд Делитель Частное Остаток Максимальное частное
Слово/байт ТОПОР об/м8 АЛ АХ 255
Двойное слово/слово DX:AX об/м16 ТОПОР ДС 65 535
Квадратное/двойное слово EDX:EAX об/м32 ЭАКС ЭДС 2 32 − 1
Двойное четверное слово/четверное слово ГДРС:РАКС об/м64 РАКС гексоген 2 64 − 1
Таблица 3-15. DIV Действие

Операция ¶

 МСФО = 0
    ТОГДА #DE; ФИ; (* Ошибка деления *)
IF OperandSize = 8 (* Операция Word/Byte *)
    ЗАТЕМ
        темп := AX/SRC;
        ЕСЛИ температура > FFH
            ТОГДА #DE; (*Ошибка деления*)
            ЕЩЕ
                АЛ := темп;
                АХ := AX MOD SRC;
        ФИ;
    ELSE IF OperandSize = 16 (* операция двойное слово/слово *)
        ЗАТЕМ
            темп:= DX:AX/SRC;
            ЕСЛИ температура > FFFFH
                ТОГДА #DE; (*Ошибка деления*)
            ЕЩЕ
                AX := темп;
                DX := DX:AX MOD SRC;
            ФИ;
        ФИ;
    ELSE IF Размер операнда = 32 (* Операция с квадрословом/двойным словом *)
        ЗАТЕМ
            темп:= EDX:EAX/SRC;
            ЕСЛИ температура > FFFFFFFFH
                ТОГДА #DE; (*Ошибка деления*)
            ЕЩЕ
                EAX := темп;
                EDX := EDX:EAX MOD SRC;
            ФИ;
        ФИ;
    ИНАЧЕ, ЕСЛИ 64-битный режим и размер операнда = 64 (* операция двойного четверного слова/четверенного слова *)
        ЗАТЕМ
            темп:= RDX:RAX/SRC;
            ЕСЛИ температура > FFFFFFFFFFFFFFFFH
                ТОГДА #DE; (*Ошибка деления*)
            ЕЩЕ
                РАКС := темп;
                RDX := RDX:RAX MOD SRC;
            ФИ;
        ФИ;
ФИ;
 

Затронутые флаги ¶

Флаги CF, OF, SF, ZF, AF и PF не определены.

Исключения защищенного режима ¶

#DE Если исходный операнд (делитель) равен 0
Если частное слишком велико для указанного регистра.
#GP(0) Если эффективный адрес операнда памяти выходит за пределы сегмента CS, DS, ES, FS или GS.
Если регистр DS, ES, FS или GS содержит селектор сегмента NULL.
#SS(0) Если эффективный адрес операнда памяти выходит за пределы сегмента SS.
#PF(код неисправности) Если происходит ошибка страницы.
#AC(0) Если включена проверка выравнивания и делается невыровненная ссылка на память, когда текущий уровень привилегий равен 3.
#UD Если используется префикс LOCK.

Исключения режима реального адреса ¶

#DE Если исходный операнд (делитель) равен 0.
Если частное слишком велико для указанного регистра.
#GP Если эффективный адрес операнда памяти выходит за пределы сегмента CS, DS, ES, FS или GS.
Если регистр DS, ES, FS или GS содержит селектор сегмента NULL.
#SS(0) Если эффективный адрес операнда памяти выходит за пределы сегмента SS.
#УД Если используется префикс LOCK.

Исключения режима Virtual-8086 ¶

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

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

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

© 2019 Штирлиц Сеть печатных салонов в Перми

Цифровая печать, цветное и черно-белое копирование документов, сканирование документов, ризография в Перми.