Здравствуйте, уважаемые друзья! Продолжаем изучать нашу рубрику, на очереди тема умножения и деления в Assembler. Разберемся со всеми тонкостями этих операций, конечно же, на практическом примере.
mul
div
Итак, как мы уже сказали, при умножении и делении в Assembler есть некоторые тонкости, о которых дальше и пойдет речь. Тонкости эти состоят в том, что от того, какой размерности регистр мы делим или умножаем многое зависит. Вот примеры:
mul bl
), то значение этого регистра bl умножится на значение регистра al, а результат запишется в регистр ax, и так будет всегда, независимо от того, какой 1-байтовый регистр мы возьмем.bl*al = ax
mul bx
), то значение в регистре bx умножится на значение, хранящееся в регистре ax, а результат умножения запишется в регистр eax.bx*ax = eax
mul ebx
), то значение в регистре ebx умножится на значение, хранящееся в регистре eax, а результат умножения запишется в 2 регистра: edx и eax.ebx*eax = edx:eax
Почти аналогично реализуется и деление, вот примеры:
div bl
), то значение регистра ax поделится на значение регистра bl, результат от деления запишется в регистр al, а остаток запишется в регистр ah.ax/bl = al, ah
div bx
), то процессор поделит число, старшие биты которого хранит регистр dx, а младшие ax на значение, хранящееся в регистре bx. Результат от деления запишется в регистр ax, а остаток запишется в регистр dx.(dx,ax)/bx = ax, dx
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.
Скачать исходники
codetown.ru
Умножение и деление выполняются по-разному для чисел со знаком и без, поэтому в системе команд процессора x86 есть отдельные команды умножения и деления для чисел со знаком и для чисел без знака.
Умножение чисел без знака
Для умножения чисел без знака предназначена команда MUL. У этой команды только один операнд — второй множитель, который должен находиться в регистре или в памяти. Местоположение первого множителя и результата задаётся неявно и зависит от размера операнда:
Размер операнда | Множитель | Результат |
---|---|---|
Байт | AL | AX |
Слово | AX | DX:AX |
Отличие умножения от сложения и вычитания в том, что разрядность результата получается в 2 раза больше, чем разрядность сомножителей. Также и в десятичной системе — например, умножая двухзначное число на двухзначное, мы можем получить в результате максимум четырёхзначное. Запись «DX:AX» означает, что старшее слово результата будет находиться в DX, а младшее — в AX. Примеры:
mul bl ;AX = AL * BL mul ax ;DX:AX = AX * AX |
mul bl ;AX = AL * BL mul ax ;DX:AX = AX * AX
Если старшая часть результата равна нулю, то флаги CF и ОF будут иметь нулевое значение. В этом случае старшую часть результата можно отбросить. Это свойство можно использовать в программе, если результат должен быть такого же размера, как множители.
Умножение чисел со знаком
Для умножения чисел со знаком предназначена команда IMUL. Эта команда имеет три формы, различающиеся количеством операндов:
Примеры:
imul cl ;AX = AL * CL imul si ;DX:AX = AX * SI imul bx,ax ;BX = BX * AX imul cx,-5 ;CX = CX * (-5) imul dx,bx,134h ;DX = BX * 134h |
imul cl ;AX = AL * CL imul si ;DX:AX = AX * SI imul bx,ax ;BX = BX * AX imul cx,-5 ;CX = CX * (-5) imul dx,bx,134h ;DX = BX * 134h
CF = OF = 0, если произведение помещается в младшей половине результата, иначе CF = OF = 1. Для второй и третьей формы команды CF = OF = 1 означает, что произошло переполнение.
Деление чисел без знака
Деление целых двоичных чисел — это всегда деление с остатком! По аналогии с умножением, размер делителя, частного и остатка должен быть в 2 раза меньше размера делимого. Деление чисел без знака осуществляется с помощью команды DIV. У этой команды один операнд — делитель, который должен находиться в регистре или в памяти. Местоположение делимого, частного и остатка задаётся неявно и зависит от размера операнда:
Размер операнда (делителя) | Делимое | Частное | Остаток |
---|---|---|---|
Байт | AX | AL | AH |
Слово | DX:AX | AX | DX |
При выполнении команды DIV может возникнуть прерывание (о прерываниях я подробно расскажу потом, пока старайтесь избегать таких случаев):
Примеры:
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. Эта команда тоже генерирует прерывание при делении на ноль или слишком большом частном.
Пример программы
Допустим, в программе требуется вычислять координату какого-то движущегося объекта по формуле:
x = x0 + v0t + at2/2
Все числа в правой части — 8-битные целые без знака, а x — 16-битное целое и тоже без знака. Здесь нужно внимательно следить за размерами операндов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov al,[v0] ;AL = v0 mov cl,[t] ;CL = t mul cl ;AX = AL*CL = v0*t mov bx,ax ;BX = AX = v0*t mov al,[a] ;AL = a mul cl ;AX = AL*CL = a*t mov ch,0 ;Преобразуем t в слово в регистре CX mul cx ;DX:AX = AX*CX = a*(t^2) mov cl,2 ;CL = 2 = CX, так как CH = 0 div cx ;AX = DX:AX/2 = a*(t^2)/2 add ax,bx ;AX = AX+BX = v0*t + a*(t^2)/2 add al,[x0] ;\ adc ah,ch ;/ AX = AX+x0 = x0 + v0*t + a*(t^2)/2 mov [x],ax ;Сохраняем результат в x mov ax,4C00h ;\ int 21h ;/ Завершение программы ;------------------------------------------------------- x0 db 188 v0 db 7 a db 3 t db 25 x dw ? |
use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov al,[v0] ;AL = v0 mov cl,[t] ;CL = t mul cl ;AX = AL*CL = v0*t mov bx,ax ;BX = AX = v0*t mov al,[a] ;AL = a mul cl ;AX = AL*CL = a*t mov ch,0 ;Преобразуем t в слово в регистре CX mul cx ;DX:AX = AX*CX = a*(t^2) mov cl,2 ;CL = 2 = CX, так как CH = 0 div cx ;AX = DX:AX/2 = a*(t^2)/2 add ax,bx ;AX = AX+BX = v0*t + a*(t^2)/2 add al,[x0] ;\ adc ah,ch ;/ AX = AX+x0 = x0 + v0*t + a*(t^2)/2 mov [x],ax ;Сохраняем результат в x mov ax,4C00h ;\ int 21h ;/ Завершение программы ;——————————————————- x0 db 188 v0 db 7 a db 3 t db 25 x dw ?
В 7-й строке промежуточный результат сохраняется в bx. В 11-й строке происходит преобразование байта в слово, путём добавления нулевой старшей части. Такой метод подходит для чисел без знака, но приведёт к ошибке для чисел со знаком (в случае отрицательного числа). Прибавление x0 происходит в два этапа (строки 17 и 18) с учётом переноса, так как мы складываем слово и байт.
Упражнение
Напишите программу для вычисления формулы z = (x·y) / (x + y). Все числа 16-битные целые со знаком.
Сложное упражнение
Напишите программу для умножения двух 32-битных целых без знака. В результате должно получиться 64-битное целое без знака. Подсказка: используйте комбинацию умножения по частям и сложения, как в способе умножения столбиком. (Мой вариант решения вы можете посмотреть здесь).
Следующая часть »
asmworld.ru
Как выучить английский
В наше время любой человек должен знать английский язык. А тем более программист. Потому что довольно большая часть документации существует только на английском. А профессионал не может ждать, пока кто-то переведёт ему документацию. Он должен уметь быстро получать нужную инфорамцию и использовать её, независимо от того, на каком она языке — на английском или на русском… Ссылки на курсы по английскому… |
Инструкция MUL в Ассемблере выполняет умножение без знака. Понять работу команды MUL несколько сложнее, чем это было для команд, рассмотренных ранее. Но, надеюсь, что я помогу вам в этом разобраться.
Итак, синтаксис команды MUL такой:
MUL ЧИСЛО
Выглядит всё очень просто. Однако эта простота обманчива.
Прежде чем разобраться в подробностях работы этой инструкции, давайте посмотрим, что может быть ЧИСЛОМ.
ЧИСЛОМ может быть один из следующих:
Эта команда не работает с сегментными регистрами, а также не работает непосредственно с числами. То есть вот так
MUL 200 ; неправильно
делать нельзя.
А теперь алгоритм работы команды MUL:
Вот такая немного сложноватая команда. Хотя сложно это с непривычки. Сейчас мы разберём всё “по косточкам” и всё станет ясно.
Для начала обратите внимание, что инструкция 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, которое можно перевести как “умножить, умножать”.
av-assembler.ru
Как выучить английский
В наше время любой человек должен знать английский язык. А тем более программист. Потому что довольно большая часть документации существует только на английском. А профессионал не может ждать, пока кто-то переведёт ему документацию. Он должен уметь быстро получать нужную инфорамцию и использовать её, независимо от того, на каком она языке — на английском или на русском… Ссылки на курсы по английскому… |
Инструкция DIV в Ассемблере выполняет деление без знака. Использование этой инструкции похоже на работу команды MUL, хотя, конечно, имеет некоторые особенности, потому что деление — это не умножение )))
Итак, синтаксис команды DIV такой:
DIV ЧИСЛО
ЧИСЛОМ может быть один из следующих:
Эта команда не работает с сегментными регистрами, а также не работает непосредственно с числами. То есть вот так
DIV 200 ; неправильно
делать нельзя.
А теперь алгоритм работы команды DIV:
Если вы уже изучили инструкцию MUL, то ничего особо нового для вас здесь нет. Ну а если не изучали, то немного напомню.
Обратите внимание, что инструкция DIV работает либо с регистром АХ, либо с парой регистров DX AX. То есть перед выполнением этой команды нам надо записать в регистр АХ или пару регистров DX AX значение, которое требуется разделить. Сделать это можно, например, с помощью уже известной нам команды MOV.
Затем надо в область памяти или в регистр общего назначения записать делитель — то есть число, на которое будем делить.
Далее мы выполняем деление, и получаем результат либо в регистр АL (если ЧИСЛО — это байт), либо в регистр AX (если ЧИСЛО — это слово).
Как вы понимаете, инструкция DIV выполняет целочисленное деление. При этом остаток от деления, если таковой имеется, будет записан:
Никакие флаги при этом не изменяются. А если и меняются, то об этом ничего не сказано в документации, следовательно, проверять флаги нет необходимости.
Просто если есть сомнения, что деление выполнено без остатка, надо проверить содержимое регистров AL или DX в зависимости от того, какой размер имеет ЧИСЛО.
Итак, например, нам надо 250 разделить на 150. Тогда мы делаем так:
MOV AX, 250 ; Делимое в регистр AX MOV BL, 150 ; Делитель в регистр BL DIV BL ; Теперь АL = 250 / 150 = 1, AH = 100
Обратите внимание, что нам приходится два раза использовать команду MOV, так как команда DIV не работает непосредственно с числами, а только с регистрами общего назначения или с памятью.
После выполнения этого кода в регистре АL будет результат целочисленного деления числа 250 на число 150, то есть число 1, а в регистре АН будет остаток от деления — число 100 (64 в шестнадцатеричной системе).
Теперь попробуем число 50000000 разделить на 60000.
MOV DX, 762 ; Делимое - в пару регистров DX AX MOV AX, 61568 ; (DX AX) = 50000000 MOV BX, 60000 ; Делитель в регистр BX DIV BX ; Теперь АХ = 50000000 / 60000 = 833 (341h) ; DX = 20000 (4E20h)
Для записи делителя в пару регистров DX и AX используются две команды MOV. В нашем примере в регистр DX будет записано число 762 (02FA — в шестнадцатеричной системе), а в регистр АХ — число 61568 (F080 — в шестнадцатеричной системе). А если рассматривать их как одно число (двойное слово), где в старшем слове 762, а в младшем — 61568, то это и будет 50000000 (2FAF080 — в шестнадцатеричной системе).
Затем в регистр BX мы записываем число 60000 и выполняем команду деления. В результате в регистре АХ будет число 833 (или 341 в шестнадцатеричной системе), в регистре DX — остаток от деления, который в нашем случае будет равен 20000 (или 4E20 в шестнадцатеричной системе).
В конце как обычно расскажу, почему эта команда ассемблера называется DIV. Это сокращение от английского слова DIVIDE, которое можно перевести как “разделить”.
av-assembler.ru
В этом уроке по языку ассемблер мы будем говорит об умножении и делении, обрабатывать числа будем как со знаком так и без него. Хочу заметить то что процессор работает только с целыми числам, и это конечно нужно знать и учитывать. Для работы с вещественными числами(числа с плавающей точкой) существует сопроцессор, о котором мы еще поговорим в следующих уроках.
Для умножения в ассемблере используется команда MUL, она принимает лишь одни параметр это множитель а множимое находиться в AX или в AL. Это зависит от размера множителя. К примеру мы хотим умножит содержимое AL на BL для этого нам нужно занести в эти младшие части зачернения и выполнить команду mul bl, результат будет в AX. А чтоб умножить слово на слово: mul bx, но в этом случае результат будет находиться в двух регистрах DX:AX. И еще уточним одну деталь перед умножением слов желательно обнулить DX, это можно сделать просто поместив в него ноль. Также умножать можно и на переменные. Давайте посмотрим на пример кода:
org 100h mov ax,1111h mov bx,0 mov al,2 mov bl,3 mul bl mov dx,0 mov bx,0f002h mul bx mul [A] mov ax,4c00h int 21h A db 2
Как мы видим с рисунка трассировки при умножении на AL на BL старшая часть регистра AX принимает новое значение а старое стирается. Также мы видим при умножении двух слов AX=6h и BX=0f002h результатом будет шестнадцатеричное число 0005A00Ch которое не может поместиться в одном регистре, соответственно оно было помещено в два регистра. И последняя команда умножает переменную A на AL и результат соответственно будет в AX.
Для того чтоб выполнить знаковое умножение ассемблер имеет команду IMUL. Она работает точно также как и mul только с учетом значка числа. К примеру давайте выполним такой пример 50*(-2)+101 и результат запишем в BX.
org 100h mov al,-50 imul [A] mov bx,101 add bx,ax mov ax,4c00h int 21h A db 2
Для деление в ассемблере используется команда DIV. Она по своему принципу подобна и команде умножения. Единственно тут происходит не деление слова на слова а деление слова на байт и деление двойного слова на слово. При деление слова на байт а AX находиться делимое а после деления AH содержит остаток от деления а AL частное, то есть результат. При делении двойного слова на DX — содержит старшую часть числа а AX младшую после деления DX — содержит остаток а AX частное. Давайте рассмотрим пример кода:
org 100h mov dx,0 mov cx,0ffffh mov ax,17 mov bl,2 div bl mov ah,0 div [A] mul cx div cx mov ax,4c00h int 21h A db 2
Как видно из рисунка выше мы делим 17(11h) на 2 в итоге у нас остаток 1 в старшей части регистра AX (подчеркнуто зеленым) и в младшей находится результат от деления. Затем обнуляем старшую часть регистра AX и потом делим на переменную а то что вышло умножаем на CX, предварительно обнулили регистр DX так как у нас получиться в результате двойное слова и старшая часть будет находиться в DX. В итоге при умножении 4h на ffffh мы имеем число 0003fffch которое находиться в двух регистрах DX и AX затем мы это двойное слово делим на CX (ffffh) и как видим получили исходный результат в AX.
Деление чисел со знаком осуществляется также как и без знака, предлагаю Вам выполнить трассировку кода ассемблера в отладчике:
org 100h mov ax,-9 mov bl,-3 idiv bl mov ax,-0ffh mov cx,1234h mov dx,0 imul cx mov cx,-1234h idiv cx mov ax,4c00h int 21h
На этом наш урок подошел к концу. Надеюсь наши ассемблер уроки Вам помогают в изучении языка. Желаю Вам успехов.
assembler.com.ua
LOCALS .model small .stack 100h .data CrLf db 0Dh, 0Ah, '$' PromptGetX db 'Input X (-32768..+32767): ', '$' PromptGetY db 'Input Y (-32768..+32767): ', '$' KeyBuf db 7, 0, 8 dup(0) ;max,len,string,CR(0dh) msgError db 'Ошибка ввода числа', 0Dh, 0Ah, '$' X db ? Y db ? F db ? .code main proc mov ax, @data mov ds, ax @@GetX: ; ввод числа с клавиатуры (строки) lea dx, PromptGetX mov ah,09h int 21h mov ah, 0Ah mov dx, offset KeyBuf int 21h ; перевод строки (на новую строку) lea dx, CrLf mov ah,09h int 21h ; преобразование строки в число lea si, KeyBuf+1 lea di, X call Str2Num ; проверка на ошибку jnc @@GetY ; если есть ошибка ввода - напечатать сообщение об ошибке lea dx, msgError mov ah,09h int 21h jmp @@GetX @@GetY: ; ввод числа с клавиатуры (строки) lea dx, PromptGetY mov ah,09h int 21h mov ah, 0Ah mov dx, offset KeyBuf int 21h ; перевод строки (на новую строку) lea dx, CrLf mov ah,09h int 21h ; преобразование строки в число lea si, KeyBuf+1 lea di, Y call Str2Num ; проверка на ошибку jnc @@Calc ; если есть ошибка ввода - напечатать сообщение об ошибке lea dx, msgError mov ah,09h int 21h jmp @@GetY @@Calc: ;F= y-(96*x)/16 ??? mov cl, X sal cl,1 sal cl,1 sal cl,1 mov bh, Y sub bh,cl mov F, bh call Show_AX mov ax, 4C00h int 21h main endp ; выводит число из регистра AX на экран ; входные данные: ; ax - число для отображения Show_AX proc push ax push bx push cx push dx push di mov cx, 10 xor di, di ; di - кол. цифр в числе ; если число в ax отрицательное, то ;1) напечатать '-' ;2) сделать ax положительным or ax, ax jns @@Conv push ax mov dx, '-' mov ah, 2 ; ah - функция вывода символа на экран int 21h pop ax neg ax @@Conv: xor dx, dx div cx ; dl = num mod 10 add dl, '0' ; перевод в символьный формат inc di push dx ; складываем в стэк or ax, ax jnz @@Conv ; выводим из стэка на экран @@Show: pop dx ; dl = очередной символ mov ah, 2 ; ah - функция вывода символа на экран int 21h dec di ; повторяем пока di<>0 jnz @@Show pop di pop dx pop cx pop bx pop ax ret Show_AX endp ; преобразования строки в число ; на входе: ; ds:[si] - строка с числом ; ds:[di] - адрес числа ; на выходе ; ds:[di] - число ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен) Str2Num proc push ax push bx push cx push dx push ds push es push si push ds pop es mov cl, ds:[si] xor ch, ch inc si mov bx, 10 xor ax, ax ;если в строке первый символ '-' ; - перейти к следующему ; - уменьшить количество рассматриваемых символов cmp byte ptr [si], '-' jne @@Loop inc si dec cx @@Loop: mul bx ; умножаем ax на 10 ( dx:ax=ax*bx ) mov [di], ax ; игнорируем старшее слово cmp dx, 0 ; проверяем, результат на переполнение jnz @@Error mov al, [si] ; Преобразуем следующий символ в число cmp al, '0' jb @@Error cmp al, '9' ja @@Error sub al, '0' xor ah, ah add ax, [di] jc @@Error ; Если сумма больше 65535 cmp ax, 8000h ja @@Error inc si loop @@Loop pop si ;проверка на знак push si inc si cmp byte ptr [si], '-' jne @@Check ;если должно быть положительным neg ax ;если должно быть отрицательным jmp @@StoreRes @@Check: ;дополнительная проверка, когда при вводе положительного числа получили отрицательное or ax, ax ; js @@Error @@StoreRes: ;сохранить результат mov [di], ax clc pop si pop es pop ds pop dx pop cx pop bx pop ax ret @@Error: xor ax, ax mov [di], ax stc pop si pop es pop ds pop dx pop cx pop bx pop ax ret Str2Num endp end main
studassistent.ru
Цель работы:изучение операций сложения, вычитания, умножения и деления двоичных чисел на языке Ассемблер.
ОСНОВНЫЕ ПОНЯТИЯ
Регистры общего назначения AX, BX, CX и DX
Регистры общего назначения являются основными рабочими регистрами ассемблерных программ. Их отличает то, что к ним можно адресоваться одним словом или однобайтовым кодом. Левый байт считается старшим, а правый — младшим.
Регистр AX – является основным сумматором и применяется во всех операциях ввода/вывода, в некоторых операциях со строками и в некоторых арифметических операциях. Например, команды умножения, деления и сдвига предполагают использование регистра АХ.
АХ: \ АН \ АL\
Регистр BX — базовый регистр, единственный из регистров общего назначения, используемый в индексной адресации. Кроме того, регистр BX используется при вычислениях.
BX: \ BH \ BL \
Регистр CX — является счётчиком. Он необходим для управления числом повторений циклов и для операций сдвига влево или вправо и для вычислений.
CX: \ CH \ CL \
Регистр DX — регистр данных. Используется в некоторых операциях ввода/вывода, в операциях умножения и деления больших чисел совместно с регистром AX.
DX: \ DH \ DL \
Любой из регистров общего назначения может быть использован для суммирования или вычитания 8- , 16- или 32- разрядных величин.
Команда MOV.Пересылка (байта или слова). Признаки не меняются. Рассмотрим примеры использования данной команды с применением имён, имён в квадратных скобках и чисел. В примерах предположено, чтоWORDASопределяет слово в памяти.
MOV AX ,BX;переслать содержимое ВX в регистр AX.
MOV AX , 28 ; переслать значение 28 в регистр AX.
MOV AX , WORDAS ; переслать WORDAS в регистр AX.
MOV AX , [ BX ] ; переслать содержимое памяти по адресу в регистре
; BX в регистр АХ.
MOV AX , [ 28 ] ; переслать содержимое по смещению 28.
Команда ADD (сложение) и SUB (вычитание) выполняют сложение и вычитание байтов или слов, содержащих двоичные данные. Вычитание осуществляется в компьютере по методу сложения с двоичным дополнением : для второго операнда устанавливаются обратные значения битов и прибавляется 1, а затем происходит сложение с первым операндом. Во всём, кроме первого шага операции сложения и вычитания идентичны. Оба операнда могут быть байтами или словами, и оба операнда могут быть двоичными числами со знаком или без знака.
Возможные ситуации сложения / вычитания: регистр – регистр; память – регистр; регистр – память; регистр – непосредственное значение; память – непосредственное значение. Например,
ADD BH , 10H ; непосредственное значение и регистр
ADD AX , BX ; регистр и регистр
ADD WORDAS , CX ; память и регистр
ADD AX , [DX] ; регистр и память
SUB WORDAS , BX ; регистр из памяти
SUB BX , 100H ; непосредственное значение из регистра
SUB WORDAS , 16H ; непосредственное значение из памяти
Один байт содержит знаковый бит и семь битов данных, т.е. результат арифметической операции может легко превзойти ёмкость регистра, и возникает переполнение. Полное слово имеет также ограничение, что ограничивает возможности компьютера для выполнения арифметических операций. Поэтому используют специальные сопроцессоры, которые быстро и эффективно выполняют эти операции, представляя числа в специальных кодах. Иногда вместо команды ADD используется команда ADC — сложения с переносом, которая складывает два значения и если флаг уже установлен, к сумме прибавляется 1. Для аналогичных целей (вычитание с заёмом) вместо команды SUB используется команда SBB.
Числовое содержимое поля может интерпретироваться по разному. Многие числовые поля являются беззнаковыми, например номер абонента, адрес памяти. Некоторые числовые поля предполагаются всегда положительными, например норма выплаты, день недели, число PI. Другие числовые поля являются знаковыми, так как их содержимое может быть положительным и отрицательным. Команды ADD и SUB не делают разницы между знаковыми и беззнаковыми данными, они просто складывают и вычитают биты.
Например, для беззнакового числа биты представляют число 249, а для знакового –7:
Беззнаковые Знаковые
11111001 249 -7
+00000010 +2 +2
11111011 251 -5
Операция умножения для беззнаковых данных выполняется командой MUL , а знаковых — IMUL. Существует два вида умножения:
Множимое находится в регистре AL, а множитель в памяти или регистре. После умножения произведение находится в регистре AX. Операция игнорирует и стирает любые данные, которые находились в регистре AH.
Множимое находится в регистре AX, а множитель в памяти или регистре. После умножения произведение находится в двух регистрах: старшая (левая) часть произведения в регистре DX , а младшая (правая) в регистре AX. Операция игнорирует и стирает любые данные, которые были в регистре DX.
В единственном операнде команды MUL и IMUL указывается множитель:
MUL WERT
Если поле WERT определено как байт (DB) , то операция предполагает умножение содержимого AL на значение байта из поля WERT. Если поле WERT определено как слово (DW), то операция предполагает умножение содержимого AX на значение слова из поля WERT. Если множитель находится в регистре, то длина регистра определяет тип операции, как показано ниже:
MUL CL ; множимое в AL, произведение в AX.
MUL BX ; множимое в AX, произведение в DX:AX.
—————————————————————————————————
WERT1 DW 8000H
WERT2 DB 40H
—————————————————————————————————
MOV AL, 80H ; произведение 80Н*40Н=2000H в AX
MUL WERT2 ;
MOV AX, 2000H ; произведение 2000Н*8000Н вDX:AX
MUL WERT1 ;
MOV AL, 80H ; произведение 80Н*40=E000Н в AX
IMUL WERT2 ;
MOV AX, 2000H ; произведение 2000Н*8000Н вDX:AX
IMUL WERT1 ;
Первый пример команды MUL перемножает 80Н (128) на 40Н (64), произведение 2000Н (8192) получается в регистре AX . Второй пример этой команды генерирует 10000000Н в регистрах DX:AX.
Первый пример команды IMUL перемножает 80Н (отрицательное число) на 40Н (положительное число), произведение Е000 получается в регистре AX. Используя те же данные, команда MUL даёт в результате 2000, очевидна разница в использовании команд MUL и IMUL. Команда MUL рассматривает 80Н как +128, а команда IMUL – как –128. Второй пример аналогичен первому.
Таким образом, если множимое и множитель имеют одинаковый знаковый бит, то команды MUL и IMUL генерируют одинаковый результат, в противном случае команда MUL вырабатывает положительный результат умножения, команда IMUL отрицательный.
При умножении на степень числа 2 (2, 4, 8 и т. д.) более эффективен сдвиг влево на требуемое число битов. Сдвиг более чем на 1 требует загрузки счётчика сдвига в регистр CL. В следующих примерах предположим, что множимое находится в регистре AL или AX :
SHL AL, 1 ; умножение на 2
SHR AL, 1 ; деление на 2
MOV CL, 3 ; умножение на 8
SHL AX, CL
Для знаковых полей лучше использовать команду SAL.
Операция деления для беззнаковых данных выполняется командой DIV, а для знаковых — IDIV. Ответственность за подбор подходящей команды лежит на программисте. Аналогично умножению существует два вида деления:
Делимое находится в регистре AX, а делитель – в памяти или регистре. После деления остаток получается в регистре AH, а частное – в AL.
Делимое находится в регистровой паре DX:AX, а делитель – в памяти или регистре. После деления остаток получается в регистре DX, а частное — в регистре AX.
В единственном операнде команд DIV и IDIV указывается делитель
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
BET1 DB 80H
WERT1 DW 2000H
WERT2 DW 1000H
———————————————————————————————— MOV AX, WERT1 ; слово / байт
DIV BET1 ; остаток:частное в AH:AL
MOV DX, 0010H ; двойное слово / слово
MOV AX, 1000H ; делимое в DX:AX
DIV WERT1 ; остаток:частное в DX:AX
MOV AX, WERT1 ; слово / слово
SUB DX, DX ;расширение WERT1 до двойного слова в
; регистровой паре DX:AX
DIV WERT2 ; остаток: частное в DX:AX
Команда DIV делит беззнаковые числа. Первый пример команды DIV делит 2000Н (8092) на 80Н (128). В результате остаток 00 получается в регистре AH, а частное 40Н (64) – в регистре AL. Второй пример генерирует остаток 1000Н в регистре DX и частное 0080Н в регистре AX. В третьем примере команды DIV сначала выполняется расширение слова WERT1 до двойного слова в регистровой паре DX:AX. После деления остаток 0000Н получится в регистре DX, а частное 0002Н – в регистре AX.
Команда IDIV выполняет деление знаковых чисел. Если в примере вместо команды DIV записать команду IDIV, то первый пример делит 2000Н (положительное число) на 80Н (отрицательное число). Остаток от деления получается в регистре AH, а частное 0С0Н (-64) – в регистре AL.
Таким образом, если делимое и делитель имеют одинаковый знаковый бит, то команды DIV и IDIV генерируют одинаковый результат. Но если делимое и делитель имеют разные знаковые биты, то команда DIV генерирует положительное частное, а команда IDIV– отрицательное.
Для более эффективного деления на степень двойки лучше использовать команду SHRсдвига вправо для беззнаковых полей и командуSAR- для знаковых.
Команда NEG обеспечивает преобразование знака двоичных чисел из положительного в отрицательное и наоборот. Практически команда устанавливает противоположные значения битов и прибавляет 1. Например:
NEG AX
NEG BL
NEG WERT1
ЗАДАНИЕ ДЛЯ ЛАБОРАТОРНОЙ РАБОТЫ
Написать программу на языке Ассемблер, реализующей заданное выражение из таблицы. Номер варианта соответствует номеру по списку.
Проассемблировать программу.
Отладить и проследить пошаговое выполнение программы с помощью отладчика DEBUG либо TURBO DEBUGER. Результат представить в виде таблицы:
Выполняемая команда | Значения регистров | Значения флагов |
Задание для выполнения лабораторной работы:
№ варианта | Выражение | № Варианта | Выражение |
1 | Y= (a*b – 5c) / (a+d) | 16 | Y = (a*d — c) / (b+6) |
2 | Y = (4*d – b*b) / (a+c)*2 | 17 | Y = (a*4 – c*3) / (b+d) |
3 | Y = ((c — b)*4) / (b+d) | 18 | Y = ((a+d)*c) / (d -15)*2 |
4 | Y = (a*b+d) / (7*c) | 19 | Y = ((a + c)*8 – b) / d*2 |
5 | Y = ((a+b)*(d+c)) / 8 | 20 | Y = ((a+5) — d) / (c+b)*8 |
6 | Y = ((a — b)*4 ) / (d+c)*2 | 21 | Y = (a*b+7) / (c – d)*6 |
7 | Y = (a — b)*(d — c) / (b+d) | 22 | Y = ((a –b+c)*4) / (d*3) |
8 | Y = (a*c*4 — d) / (c*8) | 23 | Y = ((a*5 – b*4)*d) / c |
9 | Y = ((a+c — d)*6) / d*8 | 24 | Y = ((a – b)*3+(c*2)) / d |
10 | Y = ((a -d)*3 — c) / (b-5)*2 | 25 | Y = ((a*13 )- d*c)/ (b+12) |
11 | Y = (a*d — c) / (b+32) | 26 | Y = ((a+b)*4 – d) / (c*7) |
12 | Y = ((a+d)*3 )/ (d — b)*4 | 27 | Y = ((a*6 +d)*4) / (c+b) |
13 | Y = ((a — b)*4 — d) / c | 28 | Y = (a*b – d*2) / (c+123) |
14 | Y = (a*2 — d*3) / (c+b) | 29 | Y = ((a -43)*4 +b) / (c- d) |
15 | Y = ((a — 5)*8-(c+b)) / d | 30 | Y = ((a+d –c + d)*2 ) / 8 |
В таблице : A — № по списку + дата рождения (0-31)
B — № по списку – месяц рождения (0-12)
C — № по списку
D — № по списку * 2
СОДЕРЖАНИЕ И ОФОРМЛЕНИЕ ОТЧЁТА
Отчёт выполняется на отдельных листах формата А4 и включает в себя:
Название и цель лабораторной работы.
Текст задания .
Алгоритм реализации задания.
Текст рабочей программы.
Таблицу выполнения команд в отладчике DEBUG.
Выводы по работе.
КОНТРОЛЬНЫЕ ВОПРОСЫ
Структура и применение регистров общего назначения.
Назначение и виды применения команды MOV.
Операция сложения двоичных чисел.
Операция вычитания двоичных чисел и какие у неё особенности?
В чём различие между знаковыми и беззнаковыми данными?
Операция умножения беззнаковых данных и её виды.
В чём отличие операции умножения знаковых данных?
Операция деления беззнаковых данных и её виды.
В чём отличие операции деления знаковых данных?
Как можно повысить эффективность умножения или деления на степень двойки?
ЛИТЕРАТУРА
Абель П. Язык Ассемблера для IBMPCи программирования / Пер. с англ. Ю.В. Сальникова. – М.: Высшая школа, 1992 – 447с.
Нортон П. Программно — аппаратная организация IBM PC / Пер. с англ. – М.: Радио и связь., 1991 –2 56с.
Персональный компьютер для всех/ Под ред. А.Я. Савельева. – М.: Высшая школа, 1991 – 347с.
ВРЕМЯ, ОТВЕДЁННОЕ НА ЛАБОРАТОРНУЮ РАБОТУ
Подготовка к работе 1,5ч.
Выполнение работы 2ч.
Оформление отчёта 0,5ч.
7
studfiles.net