системне програмування
Практична робота № 4
Характеристика команд для опрацювання цілочисельних даних
Система команд Intel8086
1. Арифметичні команди
2. Логічні операції: AND,OR,XOR,NOT,TEST.
3. Команди зсуву.
3.1. Логічний зсув:
SHL зсув вліво (на місце молодших бітів стають „0”).
SHR зсув вправо (на місце старших бітів стають „0”).
3.2. Арифметичний зсув:
SAL зсув вліво (на місце молодших бітів стають „0”).
SAR зсув вправо (розмноження знакового розряду).
3.3. Циклічний зсув:
ROL циклічний зсув вліво.
ROR циклічний зсув вправо.
RCL циклічний зсув вліво з участю CF.
RCR циклічний зсув вправо з участю CF.
4. Команди керування програмою.
4.1. Команда безумовного переходу JMP.
Дана команда додає значення вказане в операнді до значення в регістрі IP, який містить адресу команди, що розташована після JMP. Відповідно, якщо здійснюється перехід назад, то значення операнду буде від’ємне, якщо вперед – додатне.
4.2. Оператор циклу LOOP. Передає керування на мітку, вказану як операнд, поки значення в регістрі CX не стане рівне нулю.
Існують додатково також різновиди цієї команди:
LOOPE(або LOOPZ) – передає керування за адресою, вказаною в операнді якщо CX містить нульове значення і встановлений прапорець нуля, тобто ZF=1.
LOOPNE(або LOOPNZ) передає керування за адресою, вказаною в операнді якщо CX містить нульове значення і прапорець нуля скинуто, тобто ZF=0.
4.3. Команди умовних переходів.
Працюють аналогічно до JMP та LOOP, за винятком того, що довжина переходу повинна бути в межах від -128 до +128 байт.
Переходи для беззнакових даних.
Мнемоніка
Опис
Прапорці, що перевіряються
JE/JZ
Перехід, якщо рівно/нуль
ZF
JNE/JNZ
Перехід, якщо не рівно/не нуль
ZF
JA/JNBE
Перехід, якщо вище/не нижче або рівно
ZF,CF
JAE/JNB
Перехід, якщо вище або рівно/ не нижче
CF
JB/JNAE
Перехід, якщо нижче/не вище або рівно
CF
JBE/JNA
Перехід, якщо нижче або рівно/не вище
CF,AF
Будь яку перевірку можна закодувати двома мнемо кодами. Наприклад JB та JNAE генерують однаковий об’єктний код, хоча позитивну перевірку JB легше зрозуміти аніж заперечну JNAE.
Переходи для знакових даних.
Мнемоніка
Опис
Прапорці, що перевіряються
JE/JZ
Перехід, якщо рівно/нуль
ZF
JNE/JNZ
Перехід, якщо не рівно/не нуль
ZF
JG/JNLE
Перехід, якщо більше/не менше або рівно
ZF,SF,OF
JGE/JNL
Перехід, якщо більше або рівно/ не менше
SF,OF
JL/JNE
Перехід, якщо менше/не більше або рівно
SF,OF
JLE/JNG
Перехід, якщо менше або рівно/не більше
ZF,SF,OF
Команди переходу для умови рівно або нуль (JE/JZ) та не рівно або не нуль (JNE/JNZ) існують як для знакових так і для без знакових даних. Стан рівно/нуль наступає незалежно від знаку.
Спеціальні арифметичні перевірки.
Мнемоніка
Опис
Прапорці, що перевіряються
JS
Перехід, якщо є знак(від’ємне)
SF
JNS
Перехід, якщо немає знаку(додатнє)
SF
JC
Перехід, якщо є перенос (аналогічно JB)
CF
JNC
Перехід, якщо немає переносу
CF
JO
Перехід, якщо є переповнення
OF
JNO
Перехід, якщо немає переповнення
OF
JP/JPE
Перехід, якщо паритет парний
PF
JNP/JP
Перехід, якщо паритет непарний
PF
Ще одна команда умовного переходу перевіряє чи рівний вміст регістра CX нулю. Ця команда повинна знаходитися безпосередньо після арифметичної команди чи команди порівняння. Одним з можливих місць застосування команди JCXZ може бути початок циклу, де вона перевіряє чи не містить регістр CX нульового значення.
Таким чином, ми розглянули основні команди, що можуть бути корисні при обчисленні арифметичних виразів як над знаковими, так і над без знаковими даними.
Зупинимось детальніше на командах додавання та множення.
Додавання й віднімання
Команди ADD і SUB виконують додавання й віднімання байтів або слів, що містять двійкові дані. Віднімання виконується у комп'ютері по методу додавання із двійковим доповненням: для другого операнда встановлюються зворотні значення бітів і додається 1, а потім відбувається додавання з першим операндом. У всьому, крім першого кроку, операції додавання й віднімання ідентичні. Можливі п'ять ситуацій:
ADD / SUB регістр-регістр;
ADD / SUB пам'ять-регістр;
ADD / SUB регістр-пам'ять;
ADD / SUB регістр-безпосереднє значення;
ADD / SUB пам’ять-безпосереднє значення.
Переповнення.
При виконанні арифметичних операцій можливе переповнення. Один байт містить знаковий біт і сім біт даних, тобто значення від -128 до +127. Результат арифметичної операції може легко перевищити розрядність однобайтового регістра. Наприклад, результат додавання в регістрі AL, що перевищує його розрядність, автоматично не переходить у регістр AH. Припустимо, що регістр AL містить 60h, тоді результат команди ADD AL,20h генерує в AL суму -.80h. Але операція також встановлює прапорець переповнення і прапорець знаку у стан "негативно". Причина в тому, що.80h або двійкове 1000 0000 є від’ємним числом. Т.ч. в результаті, замість +128, ми одержимо -128. Оскільки регістр AL занадто малий для такої операції то варто скористатися регістром AX. Для розширення AL до AX можна скористатися командою CBW (Convert Byte to Word - перетворити байт у слово).
CBW
ADD AX,20H
Але повне слово має також обмеження: один знаковий біт і 15 біт даних, що відповідає значенням від -32768 до +32767. Розглянемо далі як можна обробляти числа, перевищуючі ці межі.
Додавання багатобайтних чисел
Наведемо універсальну процедуру додавання двох чисел довільної розмірності, використовуючи додавання окремих слів.
При цьому слід: 1) забезпечити сусідство слів; 2) виконувати обробку від молодшого до старшого слова; 3) завантажити в регістр CX кількість слів, що будуть додаватися
Дія починається з додавання наймолодших полів, тобто тих слів, що розташовані в пам’яті за молодшими адресами. У першому циклі додаються ліві слова, а у другому - слова, розташовані правіше. При цьому адреси в регістрах SI, DI і BX збільшуються на 2. Цю операцію виконують по дві команди INC для кожного регістра. Застосовувати команду ADD reg,02 у цьому випадку не можна, тому що при цьому буде очищений прапорець переносу, що приведе до спотворення результату додавання. Оскільки використовується цикл, виконується лише команда додавання ADC. Перед циклом команда CLC (CLear Carry - очистити прапор переносу) установлює нульове значення прапорця переносу.
DOSSEG
.MODEL SMALL
.STACK 100h
.DATA
X DW 1111h, 2222h
Y DW 3333h,4444h
Z DW 3 dup(0)
.CODE
mov ax,@data
mov ds,ax
clc
mov cx,2
lea si,X
lea di,y
lea bx,z
l1:
mov ax,[si]
adc ax,[di]
mov [bx],ax
inc si
inc si
inc di
inc di
inc bx
inc bx
loop l1
adc [bx],0
cmp [bx],0
je Lexit
mov [bx],0ffffh
Lexit:
mov ah,4Ch
int 21h
Беззнакові й знакові дані.
Числові поля можуть трактуватися як знакові, або як беззнакові. Це контролюється при написанні програми самостійно.
Для беззнакових величин всі біти є бітами даних і замість обмеження +32767 регістр може містити числа до +65535. Для знакових величин лівий біт є знаковим. Команди ADD і SUB не роблять різниці між знаковими і беззнаковими величинами, вони просто додають і віднімають біти.
Наприклад при додаванні двох двійкових чисел, одне з яких містить одиничний лівий біт можливе такі випадки. Для беззнакового числа біти представляють додатнє позитивне число 249, для знакового – від’ємна число -7:
Беззнакове
Знакове
11111001
249
-7
00000010
2
+2
11111011
251
-5
Двійкове представлення результату додавання однакове для беззнакового й знакового числа. Однак, біти представляють +251 для беззнакового числа й -5 для знакового. Таким чином, числовий вміст поля може інтерпретуватися по різному. Стан "перенос" виникає в тому випадку, коли є пеpенос у знаковий розряд. Стан "переповнення" виникає у тому випадку, коли перенос у знаковий розряд не створює переносу з розрядної сітки або перенос із розрядної сітки відбувається без переносу в знаковий розряд. При виникненні переносу при додаванні беззнакових чисел, результат одержується неправильний:
Беззнакове
Знакове
CF
OF
11111100
252
-4
00000101
5
+5
00000001
1
1
1
0
(неправильно)
При виникненні переповнення при додаванні знакових чисел, результат виходить неправильний:
Беззнакове
Знакове
CF
OF
01111001
121
+121
00001011
11
+11
10000100
132
-124
0
1
(неправильно)
При операціях додавання й віднімання може одночасно виникнути й переповнення, і перенос:
Беззнакове
Знакове
CF
OF
11110110
246
-10
10001001
137
-119
01111111
127
+127
1
1
(неправильно)
(неправильно)
Таким чином, в попередній програмі перенос, що може виникати при додаванні найстарших слів, слід трактувати для беззнакових даних як значущий біт і враховувати його в загальній сумі, а для знакових, вважати ознакою від’ємного числа.
Таким чином, універсальна програма для додавання знакових чисел повинна у випадку від’ємного результату заміняти найстарше слово результату.
Для віднімання багатобайтних чисел слід використовувати команду SBB (віднімання з запозиченням), яка є еквівалентом ADD.
Множення
Операція множення для беззнакових даних виконується командою MUL, а для знакових - IMUL (Integer MULtiplication - множення цілих чисел). У єдиному операнді команд MUL і IMUL вказується множник. Відповідальність за контроль над форматом чисел, що обробляються і за вибір відповідної команди множення лежить на самому програмісті. Існують дві основні операції множення:
"Байт на байт".
Множене перебуває в регістрі AL, а множник у байті пам'яті або в однобайтовом регістрі. Після множення добуток перебуває в регістрі AX. Операція ігнорує і витирає будь-які дані, які були в регістрі AH.
AX=AL*регістр8/пам’ять8
"Слово на слово".
Множене перебуває в регістрі AX, а множник - у слові пам'яті або у двобайтному регістрі. Після множення добуток знаходиться в подвійному слові, для якого потрібно два регістри: старша (ліва) частина добутку - в регістрі DX, а молодша (права) частина - в регістрі AX. Операція ігнорує й стирає будь-які дані, які перебували в регістрі DX.
DX:AX=AX* регістр16/пам’ять16
Розглянемо наступну команду:
MUL MULTR
Якщо поле MULTR визначене як байт (DB), то операція припускає множення вмісту AL на значення байта з поля MULTR. Якщо поле MULTR визначене як слово (DW), то операція припускає множення вмісту AX на значення слова з поля MULTR. Якщо множник перебуває в регістрі, то довжина регістра визначає тип операції, як це показано нижче:
MUL CL ; Байт-множник: множене в AL, добуток в AX.
MUL BX ; Слово-множник: множене в AX, добуток DX:AX.
Команда IMUL призначена для множення чисел зі знаком. Формат її використання аналогічний MUL.
Якщо множник і множене мають однаковий знак, то IMUL та MUL генерують однаковий результат, а якщо множники мають різні знаки, то команда MUL виробляє доданій результат, а команда IMUL – від’ємний.
Для підвищення ефективності операції множення, при множенні на степінь числа 2 більш доцільним є зсув вліво на необхідну кількість бітів. Зсув більше ніж на один розряд потребує параметру у регістрі CL. Наприклад:
Множення на 2 SHL al,1
Множення на 8: MOV CL,3
SHL AX,CL
Множення багатобайтних чисел
Наведемо процедуру множення двох подвійних слів.
При цьому слід: 1) забезпечити сусідство слів; 2) виконувати обробку від молодшого до старшого слова; 3) забезпечити достатній розмір для результату, тобто чотири слова.
dosseg
.model small
.stack 100h
.data
X DW 5678h,1234h ;елементи даних
Y DW 0deffh,9abch
Z DW 0,0,0,0
.code
mov ax,@data
mov ds,ax
CALL F10XMUL
mov ah,4Ch
int 21h
F10XMUL PROC
MOV AX,X
MUL Y
MOV Z,AX
MOV Z+2,DX
MOV AX,X+2
MUL Y
ADD Z+2,AX
ADC Z+4,DX
ADC Z+6,00
MOV AX,X
MUL Y+2
ADD Z+2,AX
ADC Z+4,DX
ADC Z+6,0
MOV AX,X+2
MUL Y+2
ADD Z+4,AX
ADC Z+6,DX
RET
F10XMUL ENDP
end
Ділення
Операція ділення для беззнакових даних виконується командою DIV, a для знакових - IDIV. Відповідальність за вибір відповідної команди лежить на програмісті. Існують дві основні операції ділення:
"Слово на байт".
Ділене перебуває в регістрі AX, а дільник - у байті пам'яті або а однобайтовому регістрі. Після ділення остача виходить у регістрі AH, а частка - в AL. Оскільки однобайтова частка дуже мала (максимально +255 (.FFh) для беззнакового ділення і +127 (7Fh) для знакового), то дана операція має обмежене використання.
AX / регістр8 = AL(частка) AH(остача)
AX / пам’ять8 = AL(частка) AH(остача)
"Подвійне слово на слово". Ділене перебуває в регістровій парі DX:AX, а дільник - у слові пам'яті або в регістрі. Після ділення остача виходить у регістрі DX, а частка в регістрі AX. Частка в одному слові допускає максимальне значення +32767 (FFFFh) для беззнакового ділення і +16383 (7FFFh) для знакового.
DX(старша частина) AX(молодша частина) / регістр16 = DX(остача) AX(частка)
DX(старша частина) AX(молодша частина) / пам’ять16 = DX(остача) AX(частка)
У єдиному операнді команд DIV і IDIV вказується дільник. Розглянемо наступну команду:
DIV DIVISOR
Якщо поле DIVISOR визначене як байт (DB), то операція припускає ділення слова на байт. Якщо поле DIVISOR визначене як слово (DW), те операція припускає ділення подвійного слова на слово. При діленні, наприклад, 13 на 3, виходить результат 4 1/3. Частка є 4, а остача - 1. Зауважимо,
Команда DIV ділить беззнакові числа і відповідно генерує без знаковий результат.
Знаковий ділення: Команда IDIV (Integer DIVide) виконує ділення знакових чисел. Таким чином, якщо ділене й дільник мають однаковий знаковий біт, то команди DIV і IDIV генерують однаковий резуультат . Але, якщо ділене й дільник мають різні знакові біти, то команда DIV генерує додатну частку, а команда IDIV – від’ємну частку.
Зауважимо, що для підвищення продуктивності, при діленні на степінь числа 2 (2, 4, і т.д.) варто використовувати зсув вправо на відповідну кількість війкових розрядів.
Переповнення й переривання
Використовуючи команди DIV і особливо IDIV, дуже легко викликати переповнення Переривання до непередбачених результатів. В операціях ділення передбачається, що дільник значно менший, ніж ділене. Ділення на нуль завжди викликає переривання. Але ділення на 1 генерує частку, що дорівнює діленому, що може також легко викликати переривання. Рекомендується використати наступне правило: якщо дільник - байт, то його значення повинне бути більше, ніж лівий байт (AH) діленого; якщо дільник - слово, то його значення повинне бути більше, ніж ліве слово (DX) діленого.
Проілюструємо дане правило для дільника, рівного 1:
Операція ділення: Ділене Дільник Частка
Слово на байт: 0123 01 (1)23
Подвійне слово на слово: 4026 0001 0001 (1)4026
В обох випадках частка перевищує можливий розмір. Для того щоб уникнути подібних ситуацій, корисно вставляти перед командами DIV і IDIV відповідну перевірку (CMP). Для команди IDIV дана логіка повинна враховувати той факт, що або ділене, або дільник можуть бути від’ємними, а порівнюються абсолютні значення, тому необхідно використати команду NEG для тимчасового перетворення від’ємного значення в додатне
Ділення відніманням
Якщо частка занадто велика, то ділення можна виконати з допомогою циклічного віднімання. Метод полягає в тому, що дільник віднімається з діленого й у цьому ж циклі частка збільшується на 1. Віднімання триває, поки ділене залишається більше ніж дільник. При такому методі, дуже велика частка й малий дільник можуть викликати тисячі циклів.
SUB CX,CX ;Очищення частки
C20: CMP AX,BX ;Якщо ділене < дільника,
JB C30 ; то вийти
SUB AX,BX ;Віднімання дільника від діленого
INC CX ;Інкремент частки
JMP C20 ;Повторити цикл
С30: RET ;Частка в CX, остача в AX
Наприкінці підпрограми регістр CX буде містити частку, а AX – остачу від ділення.
Якщо частка отримується в регістровій парі DX:AX, те необхідно зробити два доповнення:
1. У мітці C20 порівнювати AX і BX тільки при нульовому DX.
2. Після команди SUB вставити команду SBB DX,00.
ASCII та BCD формат та робота з ними.
Дані, що вводять із клавіатури, мають ASCII-формат, наприклад, букви SAM мають у пам'яті шістнадцяткове подання 53414D, цифри 1234 – 31323334h. У багатьох випадках формат алфавітних даних, наприклад, ім'я людини або опис статті, не міняється в програмі. Але для виконання арифметичних операцій над числовими значеннями, такими як 31323334h, потрібна спеціальна обробка. За допомогою наступних ассемблерных команд можна виконувати арифметичні операції безпосередньо над числами в ASCII-форматі:
AAA (ASCII Adjust for Addition - корекція для додавання ASCII-коду)
AAD (ASCII Adjust for Division - корекція для ділення ASCII-коду)
AAM (ASCII Adjust for Multiplication - корекція для множення ASCII-коду)
AAS (ASCII Adjust for Subtraction - корекція для віднімання ASCII-коду)
Ці команди кодуються без операндів і виконують автоматичну корекцію в регістрі AX. Корекція необхідна, тому що ASCII код представляє, так званий, розпакований десятковий формат, у той час, як комп'ютер виконує арифметичні операції у двійковому форматі.
Додавання в ASCII-форматі
Розглянемо процес додавання чисел 8 і 4 в ASCII-форматі: 38h+34h=6Ch. Отримана сума неправильна ні для ASCII-формату, ні для двійкового формату. Однак, ігноруючи ліву 6 і додавши 6 до правої .Ch: Ch + 6 =12h - одержимо правильний результат у десятковому форматі. Цей приклад злегка спрощено, але він добре демонструє процес, що виконує команда AAA при корекції.
Для прикладу, припустимо, що регістр AX містить 0038h, а регістр BX - 0034h. Числа 38 і 34 представляють два байти в ASCII форматі, які необхідно додати. Додавання й корекція кодується наступними командами:
ADD AL,BL ;Додати 34 і 38
AAA ;Корекція для додавання ASCII кодів.
Команда AAA перевіряє праву шістнадцяткову. цифру (4 біти) у регістрі AL. Якщо ця цифра знаходиться між Ah і Fh або прапорець AF дорівнює 1, то до регістра AL додається 6, а до регістра AH додається 1, прапорці AF і CF встановлюються в 1. У всіх випадках команда AAA встановлює в 0 ліву шіст. цифру в регістрі AL. Результат - у регістрі AX:
Після команди ADD: 006C
Після команди AAA: 0102
Для того, щоб отримати остаточне ASCII-значення, досить просто поставити трійки на місце лівих шістнадцяткових цифр:
OR AX,3030H ;Результат 3132
Все показане вище представляє додавання однобайтовых чисел. Додавання багатобайтних ASCII-чисел вимагає організації циклу, що виконує обробку справа наліво із врахуванням переносу. Приклад , показаний нижче, додає два трибайтні ASCII-числа в чотирибайтну суму.
.
TITLE ASCADD (COM) Додавання в ASCII-форматі
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
ASC1 DB '578' ; Елементи даних
ASC2 DB '694'
ASC3 DB '0000'
MAIN PROC NEAR
CLC
LEA SI,AASC1+2 ; Адреса ASCII-чисел
LEA DI,AASC2+2
LEA BX,AASC1+3
MOV CX,03 ; Виконати 3 цикли
A20:
MOV AH,00 ; Очистити регістр AH
MOV AL,[SI] ; Загрузить ASCII-байт
ADC AL,[DI] ; Додавання (з переносом)
AAA ; Корекція для ASCII
MOV [BX],AL ; Збереження суми
DEC SI
DEC DI
DEC BX
LOOP A20 ; Виконати 3 цикли
MOV [BX],AH ; Зберегти перенос
RET
MAIN ENDP
CODESG ENDS
END BEGIN
Зверніть увагу на наступне:
У програмі використається команда ADC, тому що будь-яке додавання може викликати перенос, що повинен бути доданий до наступного (лівого) байту.
Команда CLC установлює прапорець CF у нульовий стан.
Команда MOV очищає регістр AH у кожному циклі, тому що команда AAA може додати до нього одиницю. Команда ADC враховує пеpеноси. Зауважте, що використання команд XOR або SUB для обнулення регістра AH змінює прапорець CF.
Коли завершується кожний цикл, відбувається пересилання вмісту pегистра AH (00 або 01) у лівий байт суми.
В результаті виходить сума у вигляді 01020702. Програма не використає команду OR після команди AAA для занесення лівої трійки, тому що при цьому встановлюється прапорець CF, що змінить результат команди ADC. Одним з рішень у цьому випадку є збереження регістра прапорців за допомогою команди PUSHF, виконання команди OR, і, потім, відновлення регістра прапорців командою POPF:
ADC AL,[DI] ;Додавання з переносом
AAA ;Корекція для ASCII
PUSHF ;Збереження прапорців
OR AL,30H ;Запис лівої трійки
POPF ;Відновлення прапорців
MOV [BX],AL ;Збереження суми
Замість команд PUSHF і POPF можна використати команди LAHF (Load AH with Flags - завантаження прапорців у регістр AH) і SAHF (Store AH in Flag register - запис прапорців з регістра AH у регістр прапорців). Команда LAHF завантажує в регістр AH прапорці SF, ZF, AF, PF і CF; а команда SAHF записує вміст регістра AH у зазначені прапорці. У наведеному прикладі, однак, регістр AH уже використовується для арифметичних переповнень. Інший спосіб вставки трійок для одержання ASCII-кодів цифр - організувати обробку суми командою OR у циклі.
Віднімання в ASCII-форматі
Команда AAS (ASCII Adjust for Subtraction - корекція для Віднімання ASCII-кодів) виконується аналогічно до команди AAA. Команда AAS перевіряє праву шіст.цифру (чотири біти) в регістрі AL. Якщо ця цифра лежить між A і F або прапорець AF дорівнює 1, то з регістра AL віднімається 6, а з регістра AH віднімається 1, прапорці AF і CF встановлюються в 1. У всіх випадках команда AAS встановлює в 0 ліву шіст. цифру в регістрі AL. У наступних двох прикладах передбачається, що поле ASC1 містить 38h, а поле ASC2 – 34h:
Приклад 1:
AX
AF
MOV AL,ASC1
;0038
SUB AL,ASC2
;0034
0
AAS
;0004
0
Приклад 2:
AX
AF
MOV AL,ASC2
;0034
SUB AL,ASC1
;00FC
1
AAS
; FF06
1
У прикладі 1 команді AAS не потрібно виконувати корекцію.
В прикладі 2, оскільки права цифра в регістрі AL дорівнює Ch, команда AAS віднімає 6 від регістра AL і 1 з регістра AH та встановлює в 1 прапорці AF і CF. Результат (який повинен дорівнювати -4) має шістнадцяткове подання FF06, тобто десяткове доповнення числа -4.
Множення в ASCII-форматі
Команда AAM (ASCII Adjust for Multiplication - корекція для множення ASCII кодів) виконує коректування результату множення ASCII кодів у регістрі AX. Однак, шістнадцяткові цифри повинні бути очищені від трійок і отримані дані уже не будуть ASCII-кодами. (так званий розпакований десятковий формат). Наприклад, число в ASCII-форматі 31323334 має розпаковане десяткове подання 01020304. Крім цього, треба пам'ятати, що корекція здійснюється тільки для одного байта за одне виконання, тому можна множити тільки oднобайтові поля; для довгих полів необхідна організація циклу. Команда AAM ділить вміст регістра AL на 10 (0Ah) і записує частку в регістр AH, а остачу в AL. Припустимо, що в регістрі AL знаходиться 35h, а в регістрі CL – 39h. Наступні команди множать вміст регістра AL на вміст CL і перетворюють результат в ASCII-формат:
AX
AND CL,0FH
;Перетворити CL в 09
AND AL,0FH
;Перетворити AL в 05
0005
MUL CL
;Помножити AL на CL
002D
AAM
;Перетворити в розпакований десятковий.
0405
OR AX 3030H
;Перетворити в ASCII-ф-т
3435
Команда MUL генерує 45 (002Dh) у регістрі AX, після чого команда AAM ділить це значення на 10, записуючи частку 04 у регістр AH і остачу 05 у регістр AL. Команда OR перетворює розпаковане десяткове число в ASCII-формат.
Наступний приклад демонструє множення чотирибайтового множеного на однобайтовий множник. Оскільки команда AAM працює тільки з однобайтовими числами, то в програмі організований цикл, що обробляє байти справа наліво. Остаточний результат множення в даному прикладі - 0108090105. Якщо множник більший одного байта, то необхідно забезпечити ще один цикл, що обробляє множник. В цьому випадку простіше буде перетворити число з ASCII-формату у двійковий формат
TITLE ASCMUL (COM) Множення в ASCII-форматі
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP MAIN
; ---------------------------------------------
MULTCND DB '3783' ; Елементи даних
MULTPLR DB '5'
PRODUCT DB 5 DUP(0)
; ---------------------------------------------
MAIN PROC NEAR
MOV CX,04 ;4 цикли
LEA SI,MULTCND+3
LEA DI,PRODUCT+4
AND MULTPLR,0FH ; Забрати трійку
A20:
MOV AL,[SI] ; Загрузити ASCII-символ
; (можна LODSB)
AND AL,OFH ; Забрати трійку
MUL MULTPLR ; Помножити
AAM ; Корекція для ASCII
ADD AL,[DI] ; Додати до
AAA ; збереженого
MOV [DI],AL ; добутку
DEC DI
MOV [DI],AH ; Записати перенос
DEC SI
LOOP A20 ; Повторити 4 рази
RET
MAIN ENDP
CODESG ENDS
END BEGIN
Ділення в ASCII-форматі
Команда AAD (ASCII Adjust for Division - корекція для ділення ASCII-кодів) виконує коректування ASCII коду діленого до безпосереднього ділення. Однак, спочатку необхідно очистити ліві трійки ASCII-кодів для одержання розпакованого десяткового формату. Команда AAD може оперувати із двохбайтовим діленим в регістрі AX. Припустимо, що регістр AX містить ділене 3238 в ASCII- форматі,а регістр CL містить дільник 37 також в ASCII- форматі. Наступні команди виконують корекцію для подальшого ділення:
AND CL,0FH
;Перетворити CL у розпакований формат
АХ
AND AX,0F0FH
;Перетворити AX у розпакований формат
0208
AAD
;Перетворити у двійковий
001C
DIV CL
;Поділити на 7
0004
Команда AAD множить вміст AH на 10 (0Ah), додає результат 20 (14h) до регістра AL і очищає регістр AH. Значення 001C є шістнадцятковим еквівалентом десяткового числа 28. Дільник може бути тільки однобайтовий від 01 до 09. Наступний приклад виконує ділення чотирибайтового діленого на однобайтовий дільник. У програмі організований цикл обробки діленого справа наліво. Остача від ділення залишається в регістрі AH і команда AAD коректує її в регістрі AL. Остаточний результат: частка 00090204, а в регістрі AH остача 02.
Якщо дільник більше одного байта, то необхідно утворити інший цикл для обробки дільника, але доцільніше перетворити у двійковий формат і ділити в ньому.
TITLE ASCDIV (COM) Ділення в ASCII-форматі
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
DIVDND DB '3698' ;Елементи даних
DIVSOR DB '4'
QUOTNT DB 4 DUP(0)
MAIN PROC NEAR
MOV CX,04 ; 4 цикли
SUB AH,AH ; Стерти лівий байт діленого
AND DIVSOR,0FH ; Забрати трійку в дільнику
LEA SI,DIVDND
LEA DI,QUOTNT
A20:
MOV AL,[SI] ; Загрузити ASCII байт
; (можна LODSB)
AND AL,0FH ; Забрати трійку
AAD ; Корекція для ділення
DIV DIVSOR ; Ділення
MOV [DI],AL ; Зберегти частку
INC SI
INC DI
LOOP A20 ; Повторити 4 рази
RET
MAIN ENDP
CODEGS ENDS
END BEGIN
Двійково-десятковий формат
У попередньому прикладі ділення в ASCII-форматі була отримана частка 00090204. Якщо стиснути це значення, зберігаючи тільки праві цифри кожного байта, то одержимо 0924. Такий формат називається двійково-десятковим (BCD - Binary Coded Decimal) (або упакованим). Він містить тільки десяткові цифри від 0 до 9. Довжина двійково-десяткового представлення вдвічі менша за ASCII-формат.
Зауважими, що десяткове число 0924 має основу 10 і, будучи перетвореним у основу 16 (тобто в шістнадцятковий формат), дасть 039Ch. Можна виконувати додавання й віднімання чисел в двійково-десятковому форматі (BCD-форматі). Для цих цілей є існуються дві команди корекції:
DAA (Decimal Adjustment for Addition - десяткова корекція для додавання)
DAS (Decimal Adjustment for Subtraction - десяткова корекція для віднімання)
Обробка полів також здійснюється по одному байту за одне виконання.
Приклади розв’язку задач.
Задача1.
Написати програму, яка обчислює арифметичний вираз і результат записує в пам’ять
Z=(X4-16)-(Y2+75)/5,
де X,Y- операнди зі знаком і довжиною в байтах, згідно індексу.
DOSSEG
.MODEL SMALL
.STACK 100h
.DATA
X DW 1234h, 5678h
Y DW 9abch
Z DW 3 dup(0)
.CODE
mov ax,@data
mov ds,ax
mov dx,X+2
mov ax,X
sub ax,16
sbb dx,0
push dx
push ax
xor ax,ax
xor dx,dx
mov ax,Y
add ax,75
adc dx,0
mov bx,5
idiv bx
mov bx,ax
pop ax
pop dx
sub ax,bx
sbb dx,0
mov z,ax
mov z+2,dx
mov ah,4Ch
int 21h
end
Процедура яка передбачає, що введений дільник може бути значно менший за ділене і тому використання стандартної команди DIV неможливе. Така процедура повинна використовувати віднімання, поки старша частина діленого не стане меншою ніж дільник (процедура MY_DIV).
Для ділення подвійного слова на слово:
MY_DIV proc
sub cx,cx
sub bx,bx
mov dx,X+2
mov ax,X
M_D1: cmp dx,Y
jl M_D3
sub ax,Y
sbb dx,00
add cx,01
adc bx