8-900-374-94-44
[email protected]
Slide Image
Меню

Умножение и деление ассемблер – Деление и умножение в 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.

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

Поделиться ссылкой:

Похожее

codetown.ru

Учебный курс. Часть 11. Умножение и деление

Умножение и деление выполняются по-разному для чисел со знаком и без, поэтому в системе команд процессора 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. Эта команда имеет три формы, различающиеся количеством операндов:

  • С одним операндом — форма, аналогичная команде MUL. В качестве операнда указывается множитель. Местоположение другого множителя и результата определяется по таблице.
  • С двумя операндами — указываются два множителя. Результат записывается на место первого множителя. Старшая часть результата в этом случае игнорируется. Кстати, эта форма команды не работает с операндами размером 1 байт.
  • С тремя операндами — указывается положение результата, первого и второго множителя. Второй множитель должен быть непосредственным значением. Результат имеет такой же размер, как первый множитель, старшая часть результата игнорируется. Это форма тоже не работает с однобайтными множителями.

Примеры:

    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 может возникнуть прерывание (о прерываниях я подробно расскажу потом, пока старайтесь избегать таких случаев):

  • если делитель равен нулю;
  • если частное не помещается в отведённую под него разрядную сетку (например, если при делении слова на байт частное больше 255).

Примеры:

    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 ЧИСЛО

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

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

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

  • Область памяти (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, которое можно перевести как “умножить, умножать”.



av-assembler.ru

Команда DIV


Как выучить английский

В наше время любой человек должен знать английский язык. А тем более программист. Потому что довольно большая часть документации существует только на английском. А профессионал не может ждать, пока кто-то переведёт ему документацию. Он должен уметь быстро получать нужную инфорамцию и использовать её, независимо от того, на каком она языке — на английском или на русском… Ссылки на курсы по английскому…

Инструкция DIV в Ассемблере выполняет деление без знака. Использование этой инструкции похоже на работу команды MUL, хотя, конечно, имеет некоторые особенности, потому что деление — это не умножение )))

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

DIV ЧИСЛО

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

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

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

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

делать нельзя.

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

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

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

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

Затем надо в область памяти или в регистр общего назначения записать делитель — то есть число, на которое будем делить.

Далее мы выполняем деление, и получаем результат либо в регистр АL (если ЧИСЛО — это байт), либо в регистр AX (если ЧИСЛО — это слово).

Остаток от деления

Как вы понимаете, инструкция DIV выполняет целочисленное деление. При этом остаток от деления, если таковой имеется, будет записан:

  • В регистр АН, если ЧИСЛО — это байт
  • В регистр DX, если ЧИСЛО — это слово

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

Просто если есть сомнения, что деление выполнено без остатка, надо проверить содержимое регистров 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

Урок 5. Арифметические операции:умножение и деление

  • Подробности
  • Категория: Уроки

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

 

Умножение

Для умножения в ассемблере используется команда 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

Решение: Умножение/деление со сдвигом — Assembler

Помогите исправить код. Пожалуйста. Операции умножения и деления выполнять с помощью операций сдвига. F= y-(96*x)/16 (8-битные со знаком) Ввод значений переменных организовать с клавиатуры. Вывод результата организовать на экран. Если размер результата будет более 16 бит – результат преобразовывать в 16-битный
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

1-сложения, вычитания, умножения и деления двоичных чисел

Цель работы:изучение операций сложения, вычитания, умножения и деления двоичных чисел на языке Ассемблер.

ОСНОВНЫЕ ПОНЯТИЯ

Регистры общего назначения 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. Существует два вида умножения:

  1. Множимое находится в регистре AL, а множитель в памяти или регистре. После умножения произведение находится в регистре AX. Операция игнорирует и стирает любые данные, которые находились в регистре AH.

  2. Множимое находится в регистре 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. Ответственность за подбор подходящей команды лежит на программисте. Аналогично умножению существует два вида деления:

  1. Делимое находится в регистре AX, а делитель – в памяти или регистре. После деления остаток получается в регистре AH, а частное – в AL.

  2. Делимое находится в регистровой паре 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

ЗАДАНИЕ ДЛЯ ЛАБОРАТОРНОЙ РАБОТЫ

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

  2. Проассемблировать программу.

  3. Отладить и проследить пошаговое выполнение программы с помощью отладчика 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 и включает в себя:

  1. Название и цель лабораторной работы.

  2. Текст задания .

  3. Алгоритм реализации задания.

  4. Текст рабочей программы.

  5. Таблицу выполнения команд в отладчике DEBUG.

  6. Выводы по работе.

КОНТРОЛЬНЫЕ ВОПРОСЫ

  1. Структура и применение регистров общего назначения.

  2. Назначение и виды применения команды MOV.

  3. Операция сложения двоичных чисел.

  4. Операция вычитания двоичных чисел и какие у неё особенности?

  5. В чём различие между знаковыми и беззнаковыми данными?

  6. Операция умножения беззнаковых данных и её виды.

  7. В чём отличие операции умножения знаковых данных?

  8. Операция деления беззнаковых данных и её виды.

  9. В чём отличие операции деления знаковых данных?

  10. Как можно повысить эффективность умножения или деления на степень двойки?

ЛИТЕРАТУРА

  1. Абель П. Язык Ассемблера для IBMPCи программирования / Пер. с англ. Ю.В. Сальникова. – М.: Высшая школа, 1992 – 447с.

  2. Нортон П. Программно — аппаратная организация IBM PC / Пер. с англ. – М.: Радио и связь., 1991 –2 56с.

  3. Персональный компьютер для всех/ Под ред. А.Я. Савельева. – М.: Высшая школа, 1991 – 347с.

ВРЕМЯ, ОТВЕДЁННОЕ НА ЛАБОРАТОРНУЮ РАБОТУ

Подготовка к работе 1,5ч.

Выполнение работы 2ч.

Оформление отчёта 0,5ч.

7

studfiles.net

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

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