Электронный учебник - Assembler - Ответ 5516356

ГЛАВА 19
ДВОИЧНО-ДЕСЯТИЧНЫЕ ЧИСЛА

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

N

требуется

lg

(

N

) десятичных цифр и всего лишь

log

2

(

N

) двоичных цифр (бит). Поскольку для представления одной десятичной цифры требуется четыре бита, то для десятичного представления по сравнению с двоичным потребуется тем больше памяти, чем большее количество разрядов нам требуется. Причина появления команд двоично-десятичной арифметики в современных двоичных машинах состоит в следующем: полвека назад перевод десятичных чисел в двоичный код или наоборот требовал применения достаточно сложных схем-преобразователей и занимал относительно долгое время. Решаемые задачи того времени делились на две категории: научно-технические расчеты и обработка экономической информации. Первый род задач характеризуется огромным количеством вычислений при незначительном объеме ввода-вывода — программа при запуске считывала из входного потока несколько сотен чисел, затем несколько часов интенсивной обработки (например, интегральное или дифференциальное исчисление), после чего на печать выдавалось несколько строк результата. Экономические задачи отличаются интенсивным вводом-выводом, но обходятся сравнительно небольшими и достаточно простыми вычислениями — например, для расчета зарплаты требуется прочитать большое количество входной и справочной информации — табели учета рабочего времени, базовые ставки по должностям, всяческие добавки и поправки и тому подобное, а все вычисления сводятся к операциям сложения, вычитания, умножения и деления, экспоненциальная форма представления чисел не используется; после завершения расчетов печатаются огромные «простыни» (ведомости на зарплату, всевозможные отчеты и так далее). До появления в 1964 г. IBM System/360, в которой поддерживалась как двоичная, так и двоично-десятичная арифметика, производители крупных компьютеров предлагали компьютеры, хранящие числа в двоичном представлении и применяющие для обработки чисел двоичную арифметику для научных расчетов и компьютеры, хранящие числа в двоично-десятичном представлении и применяющие для обработки чисел двоично-десятичную арифметику для коммерческих приложений.

| | | |
| --- | --- | --- |
| 4 | 10 | 16 |
| 8 | 100 | 256 |
| 12 | 1000 | 4096 |
| 16 | 10000 | 65536 |

Двоично-десятичные числа

— специальный вид представления числовой информации, в основу которого положен принцип кодирования каждой десятичной цифры числа

тетрадой

(

nibble

) — группой из четырех бит. При этом каждый байт числа содержит одну или две десятичные цифры в так называемом

двоично-десятичном коде

(BCD —

binary-coded decimal

). Микропроцессор Intel может обрабатывать BCD-числа в двух форматах:

упакованном формате — в этом формате каждый байт содержит две десятичные цифры. Десятичная цифра представляет собой двоичное значение в диапазоне от 0 до 9 размером 4 бита. При этом код старшей цифры числа занимает старшую тетраду. Следовательно, диапазон представления упакованного BCD-числа в одном байте составляет от 00 до 99;
неупакованном формате — в этом формате каждый байт содержит одну десятичную цифру в младшей тетраде. Старшая тетрада имеет нулевое значение. Это так называемая зона. Следовательно, диапазон представления неупакованного BCD-числа в одном байте составляет от 0 до 9.

BCD-числа очень легко представляются в символьном виде (в ASCII- или EBCDIC-кодировке). Перевод BCD-числа в ASCII-код сводится к добавлению к неупакованному BCD-числу величины 30h. В результате оказалось выгодным в случае экономических задач вводить информацию в виде обычных текстовых строк, содержащих, в том числе, и числа, затем быстро переводить их из распакованного формата (когда каждый байт содержит код десятичной цифры в младшем полубайте и код «зоны» в старшем, предназначенный для превращения этой цифры в символьный код; для кода ASCII код зоны равен 3, а цифры 0-9 кодируются числами 30h-39h, для кода EBCDIC код зоны равен 0Fh, а цифры 0-9 кодируются 0F0h-0F9h) в упакованный (когда в каждом байте упакованы две десятичные цифры; операция эта элементарна), после чего проводить необходимые расчеты в упакованном BCD-виде, а перед печатью вновь преобразовывать в распакованный. По скорости это получалось значительно быстрее, чем переводить из десятичного в двоичный, потом выполнять те же самые расчеты и вновь переводить в десятичный.

Мэйнфреймы IBM имеют команды сложения, вычитания, умножения и деления упакованных BCD-чисел переменной длины от 1 до 16 байт, то есть от 1 до 31 десятичной цифры, еще один полубайт занимает знак числа.

Там же существуют несколько вспомогательных команд, упрощающих работу с BCD-числами. В результате программировать обработку таких чисел достаточно просто. А вот система команд микропроцессоров x86 содержит лишь команды десятичной коррекции вроде AAM, с помощью которых можно в цикле организовать обработку BCD-данных. На x86 обработка BCD-даных относительно медленная. Там, где мэйнфрейм IBM обходится одной командой, например, десятичного умножения, на x86 выполняется цикл из нескольких команд. FPU микропроцессора x86 поддерживает несколько более расширенный набор команд, но формат BCD-чисел всегда имеет фиксированную длину в 10 байт и содержит до 18 десятичных цифр.

Если значение упакованного BCD-числа является знаковым, то знаком BCD-числа считается старший бит старшей (знаковой) тетрады.

Остальные биты этой тетрады значения не имеют. Разницу между упакованными BCD-числами «-49» и «+49» можно найти только в знаковой тетраде, остальные тетрады задают абсолютную величину числа:

| | |
| --- | --- |
| 1XXX 0000 0000 ... 0100 1001 | -49 |
| 0XXX 0000 0000 ... 0100 1001 | +49 |

Директивой DT определяется упакованное BCD-число из 18 десятичных чисел и знакового байта

Для описания BCD-чисел в программе используют директивы описания и инициализации данных DB и DT.

Пример:

AssemblerВыделить код

1
2

Y1 DB 2,3,4,6,8,2 ;неупакованное BCD-число 234682
Y2 DT 9875645h ;упакованное BCD-число 9875645

Понятно, что нормальные арифметические (ADD/SUB/MUL/DIV) операции с такими числами невозможны, поэтому вместо того, чтобы выкинуть на помойку истории BCD-числа, программисты предпочитают заниматься с ними мазохизмом в виде команд AAA, AAS, AAM, AAD, DAA и DAS.

Команды DAA и DAS предназначены для работы с упакованными BCD-числами. Команды AAA, AAS, AAM и AAD предназначены для работы с неупакованными BCD-числами. Команда AAD — это коррекция ПЕРЕД делением, все остальные команды — коррекции ПОСЛЕ соответствующих операций.

Для работы с BCD-числами в регистре EFLAGS выделен флаг AF, который отвечает за дополнительный перенос (Auxiliary carry). Флаг AF устанавливает в 1, когда арифметическая операция вызывает перенос из 3-го бита результата в 4-й или заем из 4-го бита результата в 3-й.

AssemblerВыделить код

1
2
3
4
5
6
7
8
9

MOV AL,5
ADD AL,2
;AL=7 AF=0
MOV AL,0Fh
ADD AL,1
;AL=10h перенос из 3-го бита в 4-ый AF=1
MOV AL,10h
SUB AL,1
;AL=0Fh заем из 4-го бита в 3-ий AF=1

Сложение и вычитание двоично-десятичных
упакованных чисел. Команды DAA и DAS

Упакованные BCD-числа обрабатываются по две цифры за операцию. Для арифметических действий с BCD-числами используют те же команды, что и для двоичных чисел. Но результат арифметических операций с BCD-числами может оказаться неправильным. Команды десятичной коррекции предназначены для коррекции результата.

Десятичная коррекция после сложения DAA и десятичная коррекция после вычитания DAS используются для работы только с упакованными BCD-числами. Команды DAA и DAS работают только с байтом данных в регистре AL. Поэтому дополнительных операндов эти команды не имеют.

Синтаксис команд:

DAA

DAS

Если просуммировать два двухзначных упакованных числа 69 и 74, то мы не получим правильного результата без дополнительной корректировки. Это происходит по той причине, что процессор пытается сложить не BCD-, а двоичные числа.

Должно быть

| | | | |
| --- | --- | --- | --- |
| | 69 | =0000.0000.0110.1001b | |
| + | 74 | =0000.0000.0111.0100b | |
| | 143 | =0000.0001.0100.0011b | =323d |

Получаем

| | | | |
| --- | --- | --- | --- |
| | 69 | =0000.0000.0110.1001b | |
| + | 74 | =0000.0000.0111.0100b | |
| | 0DDh | =0000.0000.1101.1101b | =221d |

AssemblerВыделить код

1
2
3
4

MOV AX,69h ;к упакованному BCD-числу 69
ADD AL,74h ;добавить упакованное BCD-число 74
DAA;преобразуем результат AL=0DDh в упакованное BCD-число
ADC AH,0 ;CF=1, AL=43H учитываем перенос и AX=0143h

В этом примере суммируются два упакованных BCD-числа. Оба числа состоят из двух десятичных цифр, поэтому они представлены единственными байтами. Результат остается в регистре AL. Непосредственно за сложением следует команда DAA, которая корректирует результат сложения и преобразует его в упакованную десятичную форму. После команды DAA в регистре AL правильное упакованное десятичное число в диапазоне от 0 до 99. Если результат менее 100, то в регистре AL правильный ответ, а флаг переноса СF равен 0. Если результат находится в диапазоне от 100 до 198, то в регистр AL попадут две младшие десятичные цифры, а флаг переноса СF установится в 1 (был перенос).

Команда DAA правильно устанавливает регистр флагов. Если в результате сложения получилось значение в диапазоне от 100 до 198, флаг переноса CF показывает перенос из старшей десятичной позиции. При нулевом результате в 1 установлен флаг нуля ZF. В случае операций с упакованными десятичными числами флаги знака SF и переполнения OF не имеют значения, хотя флаг знака устанавливается, если старший бит регистра AL равен 1. Команда DAA использует флаг дополнительного переноса AF для определения вида коррекции, но после выполнения этой команды флаг дополнительного переноса не определен.

Пример, демонстрирующий сложение упакованных BCD-чисел повышенной точности.

AssemblerВыделить код

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
30

.code
start: xor ebx,ebx;начинаем обработку с нулевого байта
mov ecx,4;наши числа из 4 байтов
a1: mov al,byte ptr [a+ebx];в AL очередной байт
adc al,byte ptr [b+ebx];учет переноса при сложении
daa ;коррекция результата сложения
mov byte ptr [result+ebx],al;сохраняем обработанный байт
inc ebx;переходим к следующему байту
loop a1
push dword ptr sum
push offset format
push offset buffer
call _imp__wsprintfA
pop ecx;корректируем стек
pop ecx
pop ecx
push 0
push offset caption;заголовок
push offset buffer;текст
push 0
call _imp__MessageBoxA@16
ret
.data
a dd 71067h ;два упакованных BCD числа
b dd 61975h
result db 4 dup (0);место под результат
format db '%04x',0
buffer db 25 dup (0)
caption db 'DAA',0
end start

71067+61975=133042

Сложение упакованных BCD-чисел повышенной точности похоже на двоичную арифметику с повышенной точностью, за исключением того, что после сложения каждого байта появляется команда DAA. Из-за ограничений, присущих команде DAA, в примере нельзя было сразу сложить два упакованных двоично-десятичных числа как двойные слова, а затем применить коррекцию. С упакованными двоично-десятичными числами разрешена только байтовая арифметика.

Фрагмент программы демонстрирующий использование команды DAS:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13

.code
xor ebx,ebx;начинаем обработку с нулевого байта
mov ecx,4;наши числа из 4 байтов
a1: mov al,byte ptr [a+ebx];в AL очередной байт
sbb al,byte ptr [b+ebx];учет займа при вычитании
das;коррекция результата вычитания
mov byte ptr [result+ebx],al;переходим к следующему байту
inc ebx
loop a1
. . . . .
.data
a dd 71067h
b dd 61975h

71067-61975=9092

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

Типичный пример, который преподаватели любят задавать начинающим изучать ассемблер — написать программу, преобразующую двоичное число, находящееся, например, в регистре EAX в строку символов, адрес которой находится в EDI и вывести ее на экран или в файл. Ниже показаны два варианта такой программы,

использующей условные переходы:

1-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10
11
12

mov ecx,8; количество цифр в строке
@@: rol eax,4; преобразуемое число в регистре eax
push eax
and al,0Fh; обнуляем старшую тетраду
cmp al,9;если al < 10, CF = 1
ja a1
add al,'0'
jmp short a2
a1: add al,'7'
a2: stosb; помещаем значение из AL в строку
pop eax
loop @b; уменьшаем счетчик

2-й вариант:

AssemblerВыделить код

1
2
3
4
5
6

and al,0Fh; обнуляем старшую тетраду
add al,'0'
cmp al,'9'; если al < 10, CF = 1
jbe a1
add al,7
a1: stosb; помещаем значение из AL в строку

Для увеличения скорости выполнения программы стараются как

можно реже использовать условные переходы. Та же самая задача при использовании команд DAS и DAA может быть выполнена следующим образом:

1-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

mov ecx,4
@@: rol eax,8
push eax
push eax
shr al,4; работаем со старшей тетрадой
call Hex2
pop eax
and al,0Fh; обнуляем старшую тетраду
call Hex2; работаем с младшей тетрадой
pop eax
loop @b
...
Hex2:add al,90h ;если в al была цифра 0..9, тогда в al
;будет 90h..99h, если в al была цифра 0Ah..0Fh,
;тогда в al будет 9Ah..9Fh
daa ; 0-9: 90h..99h CF=0
; A-F: 00h..05h CF=1
adc al,40h ; 0-9: D0h..D9h
; A-F: 41h..46h
daa ; 0-9: 30h..39h
; A-F: 41h..46h
stosb; помещаем значение из AL в строку
ret

2-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13

mov ecx,8; количество цифр в строке
@@: rol eax,4
push eax; преобразуемое число в регистре eax
and al,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
sbb al,69h ;если в al была цифра 0..9, тогда в al
;будет 96h..9Fh, если в al была цифра 0Ah..0Fh,
;тогда в al будет 0A1h..0A6h
das ; 0-9: 96h..9Fh минус 66h -> 30h..39h,
; A-F: 0A1h..0A6h минус 60h -> 41h..46h
stosb; помещаем значение из AL в строку
pop eax
loop @b; уменьшаем счетчик

3-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7
8

and al,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
sbb ah,ah; если al < 10, то ah=0, иначе ah=0FFh
and ah,1; сбрасываем CF и AF, в ah окажется 0 или 1
daa; если в al было 0..9, тогда содержимое al не изменится
add al,'1'; если в al было A..F тогда в al стало 10h..15h
sub al,ah
stosb; помещаем значение из AL в строку

4-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7

and eax,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
adc ah,0; если al < 10, то ah=1, иначе ah=0
daa; если в al было 0..9, тогда содержимое al не изменится
add al,'1'; если в al было A..F тогда в al стало 10h..15h
sub al,ah
stosb; помещаем значение из AL в строку

5-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7

and eax,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
sbb ebx,ebx; если al < 10, то ebx=-1, иначе ebx=0
and ebx,ebx; сброс CF и AF
daa; если в al было 0..9, тогда содержимое al не изменится
lea eax,[ebx+eax+31h]; если A..F тогда стало 10h..15h
stosb; помещаем значение из AL в строку

6-й вариант:

AssemblerВыделить код

1
2
3
4
5
6

cmp al,10;если al < 10, CF = 1
sbb ebx,ebx; если al < 10, то ebx=0, иначе ebx=-1 CF=AF=1
daa ; если в al было A..F, тогда al равно 10h..15h
and ebx,-103; если 0..9, тогда al равно 66h..6Fh
lea eax,[eax+ebx+31h]; если A..F, тогда al равно 41h..46h
stosb; помещаем значение из AL в строку

7-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7

and al,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
sbb ebx,ebx; если СF=0, то ebx=0, иначе ebx=-1
and ebx,-13; сбрасываем CF и AF, в ebx окажется 0 или -13
das; если 0..9, тогда al не изменится
lea eax,[eax+ebx+3Dh]; если A..F, тогда al равно 3..9
stosb; помещаем значение из AL в строку

8-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7

and al,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
sbb ebx,ebx; если СF=0, то ebx=0, иначе ebx=-1 CF=AF=1
das; если 0..9, тогда al не изменится
and ebx,-167; если A..F, тогда al равно 3..9
lea eax,[eax+ebx+3Dh]
stosb; помещаем значение из AL в строку

9-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7
8

and al,0Fh; обнуляем старшую тетраду
cmp al,10; если al < 10, CF = 1
cmc ; если al < 10, CF = 0
adc al,30h;если в al 0..9, тогда в al будет 30h..39h,
; если в al 0Ah..0Fh, тогда в al будет 3Bh..40h
daa ; 0-9: 30h..39h,
; A-F: 3Bh..40h плюс 6 -> 41h..46h
stosb; помещаем значение из AL в строку

10-й вариант:

AssemblerВыделить код

1
2
3
4
5

and al,0Fh; обнуляем старшую тетраду
daa ;F->15h 1->1
add al,0F0h;F0+15h->05h CF=1 F0+1->F1 CF=0
adc al,40h ;46h 31h
stosb; помещаем значение из AL в строку

11-й вариант:

AssemblerВыделить код

1
2
3
4
5
6
7

and al,0Fh; обнуляем старшую тетраду
daa ; F->15h 1->1
add al,70h; 70h+15h->85h SF=1 70h+1->71h SF=0
cbw ; ah=0FFh ah=0
sub al,ah ; 85+1->86h 71h
sub al,40h; 46h 31h
stosb; помещаем значение из AL в строку

12-й вариант:

AssemblerВыделить код

1
2
3
4
5

and al,0Fh; обнуляем старшую тетраду
sub al,11h; F->FEh 1->F0h
db 0D4h,0F9h;FE->0105h F0->00F0h
add al,41h ;05+41->46h F0+41->31h
stosb; помещаем значение из AL в строку

13-й вариант:

AssemblerВыделить код

1
2
3
4
5

and al,0Fh; обнуляем старшую тетраду
aam; 0F->105h 01->0001
db 0D5h,11h; 105h->16h 0001->01
add al,30h ;16+30->46h 01+30->31h
stosb; помещаем значение из AL в строку

14-й вариант:

AssemblerВыделить код

1
2
3
4
5

and ax,0Fh; обнуляем старшую тетраду
aaa; 0F->105h 01->0001
db 0D5h,11h; 105h->16h 0001->01
add al,30h ;16+30->46h 01+30->31h
stosb; помещаем значение из AL в строку

Вариант с использованием команды XLAT, где-то в сегменте данных

должна быть таблица перекодировки:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10

table db '0123456789ABCDEF'
mov ecx,8; количество цифр в строке
mov ebx,offset table
@@: rol eax,4
push eax; преобразуемое число в регистре eax
and al,0Fh; обнуляем старшую тетраду
xlat
stosb; помещаем значение из AL в строку
pop eax
loop @b; уменьшаем счетчик

Вариант с использованием команды условной пересылки CMOVcc:

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

mov ecx,8; количество цифр в строке
mov bl,30h
@@: rol eax,4
push eax; преобразуемое число в регистре eax
and al,0Fh; обнуляем старшую тетраду
mov dl,37h
cmp al,10
cmovb edx,ebx
add al,dl
stosb; помещаем значение из AL в строку
pop eax
shr eax,4; переходим к следующей тетраде
loop @b; уменьшаем счетчик
Вариант без использования условного ветвления:
and al,0Fh; обнуляем старшую тетраду
cmp al,10
sbb ebx,ebx; если СF=0, то ebx= 0, иначе ebx=-1
and ebx,-7; в ebx окажется 0 или -7
lea eax,[eax+ebx+37h]
stosb; помещаем значение из AL в строку

Символьная коррекция сложения и вычитания.

Команды AAA и AAS

Команды символьной коррекции очень похожи на команды десятичной коррекции. Они следуют за сложением или вычитанием неупакованных BCD-чисел. Там же, где программа использует команды десятичной коррекции DAA и DAS для упакованных BCD-чисел, она использует символьную коррекцию для неупакованных BCD-чисел.

После сложения двух одноразрядных неупакованных BCD-чисел программа обычно выполняет команду символьной коррекции после сложения AAA, которая преобразует результат в правильное неупакованное представление двоично-десятичного числа. Правила сложения идентичны правилам для упакованных BCD-чисел. Поскольку сложение двух одноразрядных неупакованных BCD-чисел может дать в результате число больше 9, командам AAA и AAS требуется для работы не только регистр AL. В случае команды AAA младшая цифра скорректированного результата остается в регистре AL. Если десятичное сложение привело к переносу из младшей цифры, команда AAA устанавливает равными 1 флаги переноса CF и дополнительного переноса AF. В других случаях она сбрасывает их в ноль. Содержимое

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

Сложим два неупакованных BCD-числа 8 и 5:

AssemblerВыделить код

1
2
3
4

MOV AX,08;AH=0 AL=8
ADD AX,05;до коррекции AX=08+05=0Dh — не BCD-число
AAA;AX=0103h результат скорректирован — это 13
;в неупакованном десятичном виде

Псевдокод команды AAA:

IF ((AL AND 0FH) > 9) OR (AF = 1) THEN

AL = AL + 6;
AH = AH + 1;
AF = 1;
CF = 1;

ELSE

AF = 0;
CF = 0;

END IF

AL = AL AND 0Fh

Но если написать:

AssemblerВыделить код

1
2
3

MOV AX,00F6h
ADD AL,9 ;до коррекции AX=00FFh
AAA ;после коррекции AX=0205h

Хотя мы должны были получить AX=0105h, а не 0205h. То есть на

самом деле используется следующий псевдокод:

IF ((AL AND 0Fh) > 9) OR (AF = 1)THEN

AX = (AX + 0106h);
AF = 1;
CF = 1;

ELSE

AF = 0;
CF = 0;

END IF;

AL = AL AND 0Fh;

Для AAS существует такая же проблема, только нужно заменить «+» на «—». А как насчет флагов? AF и CF установлены как описано выше. OF остается без изменений, а SF очищается. ZF = 1, если AX=0, PF устанавливается в зависимости от значения AL (логично, не правда ли?).Символьная коррекция вычитания AAS используется в программе после вычитания одного неупакованного двоично-десятичного числа из другого, и результат этой байтовой операции должен быть помещен в регистр AL. Результат команды символьной коррекции остается в регистре AL, и если вычитание привело к появлению займа, команда AAS уменьшает регистр AH, а также устанавливает флаги переноса и дополнительного переноса. В противном случае флаги сбрасываются. Другие флаги не определены.

Операндами в команде SUB должны быть правильные одноразрядные BCD-числа. Рассмотрим возможные варианты вычитания одноразрядных BCD-чисел:

Из меньшего числа вычитается большее (на пример, 5—9) — для вычитания необходимо сделать заем в старшем разряде. Факт такого займа в микропроцессоре фиксируется установкой флагов CF и AF в 1 и вычитанием 1 из содержимого AH. В результате, после команды AAS в регистре AL получается правильное значение (модуль результата), которое для нашего примера (с учетом займа из старшего разряда) составляет 6. Одновременно моделируется займ из старшего разряда, что позволяет производить вычитание длинных чисел.

5 — 9 = —4 10 — 4 = 6;

Из большего числа вычитается меньшее (на пример, 8—6) — для вычитания нет необходимости делать заем в старшем разряде. Поэтому производится сброс флагов CF и AF в 0, а AH не изменяется. В результате, после команды AAS в регистре AL получается правильное значение (модуль результата), которое для нашего примера составляет 2.

AssemblerВыделить код

1
2
3
4
5
6
7
8

MOV AX,105h ;вычесть из 15 число 9.
SUB AL,09
AAS ;до коррекции AX=1FCh CF=AF=1
;после коррекции AX=0006 CF=AF=1
MOV AX,05 ;вычесть из 5 число 9.
SUB AL,09; AX=00FCh= -3,
AAS ;до коррекции AX=00FC AL=0FC=-6
; после коррекции AX=0FF06 модуль результата CF=AF=1

Команды AAA и AAS можно использовать после любой арифметической команды (они все изменяют AF), включая INC/DEC и CMP.

Подпрограммы для ASCII сложения/вычитания
с использованием BSWAP

Выполняем ASCII сложение с использованием команды BSWAP

Вернуться к обсуждению:

Электронный учебник Assembler