Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
100
Добавлен:
02.05.2014
Размер:
107.52 Кб
Скачать

12

Арифметические команды

Прежде чем приступить к изучению арифметических команд, рассмотрим такую форму представления чисел, как десятичные числа.

Десятичные числа – специальный вид представления числовой информации, в основу которого положен принцип кодирования каждой десятичной цифры числа группой разрядов из 4-х бит. При этом каждый байт числа содержит 1 или 2 десятичные цифры в так называемом двоично-десятичном коде (BCD – Binary Coded Decimal), Микропроцессор может хранить такие числа в2-х форматах:

  • упакованный формат – в байте 2 десятичные цифры, при этом старшая цифра занимает старшие 4 бита, диапазон представления чисел в одном байте составляет 00-99;

  • неупакованный формат - в байте 1 цифра в 4 младших битах. Старшие биты все имеют нулевое значение и называются зоной.

Описываются BCD-числа неупакованные, как DB, а упакованные как DT. Цифры неупакованного числа перечисляются через “,” а упакованного- как обычное десятичное число, например:

PER_1 DB 2,3,4,5,6,8,2 ;неупаков. 2865432

PER_2 DT 9875645 ; упаков. 9875645.

Как мы увидим из рассмотрения арифметических команд, описание BCD-чисел и алгоритм их обработки являются делом вкуса программиста.

Арифметические команды сведены в таблицу

Мнемокод

Формат

Команды сложения

ADD

ADD приемник, источник

ADC

ADC приемник, источник

AAA

AAA

DAA

DAA

INC

INC приемник

Команды вычитания

SUB

SUB приемник, источник

SBB

SBB приемник, источник

AAS

AAS

DAS

DAS

DEC

DEC приемник

NEG

NEG приемник

CMP

CMP приемник, источник

Команды умножения

MUL

MUL источник

IMUL

IMUL источник

AAM

AAM

Команды деления

DIV

DIV источник

IDIV

IDIV источник

AAD

AAD

Команды расширения знака

CBW

CBW

CWD

CWD

Сложение и вычитание целых чисел

Кроме уже рассмотренной команды INC сложение осуществляют команды

ADD операнд1, операнд2 – (операнд1=операнд1+операнд2) или

ADD приемник, источник– (приемник = приемник + источник)

ADC операнд1, операнд2 – (операнд1=операнд1+операнд2 + значение cf) или

ADC приемник, источник – (приемник = приемник + источник + перенос)

Как видно из таблицы, специальных команд сложения десятичных чисел нет. Объясняется это тем, что микропроцессор все операнды интерпретирует как двоичные числа и складывает их по правилам сложения двоичных чисел (ранее мы с Вами их, эти правила, рассматривали). Операндами могут быть как 8-ми битовые, так и 16-ти битовые двоичные числа. Если результат операции не помещается в приемник, микропроцессор фиксирует ситуацию переполнения, устанавливая в 1 флаг переноса. Действия программы в этой ситуации могут быть следующими:

  • прекратить выполнение программы по переполнению разрядной сетки;

  • увеличить разрядность операндов.

Рассмотрим 2-ой вариант. Положим, складываются 8-миразрядные операнды из РОН AL и BL

ADD AL, BL ; результат можем превысить диапазон представления

; 8-ми разрядных чисел

JNC M1 ; проверяем наличие переноса и при отсутствии -> на М1

ADC AH,0 ; расширяем разрядную сетку, добавив в результат РОН

; AH, теперь расширенный результат сложения

;помещается в 16-битовом РОН AX

M1:

В этом фрагменте программы можно было опустить команду условного переноса и сразу после ADD выполнить команду ADC, однако следует учесть, что после метки М1 результат нужно выбирать из регистра АХ.

Аналогично можно складывать числа большей, чем 16 бит разрядности. Так, для сложения 32-битовых операндов (двойное слово) одно слагаемое можно поместить в РОН AX и BX, а второе - в DX и CX

SLAG1 DD 4bf8ff31h

SLAG2 DD a5244986h

MOV AX, SLAG1 ;младшая часть

MOV BX, SLAG1+2 ;старшая часть

MOV CX, SLAG2

MOV DX, SLAG2+2

ADD AX, CX

ADC BX, DX

Команды ADD и ADC могут воздействовать на 6 флагов:

  • флаг переноса CF – устанавливается в 1, если результат сложения не помещается в операнде-приемнике, и противном случае CF=0;

  • флаг четности PF=1, если результат имеет четное число битов со значением 1, и противном случае PF=0;

  • вспомогательный флаг переноса AF=1, если результат сложения десятичных чисел требует коррекции, в противном случае он равен 0;

  • флаг нуля ZF=1, если результат сложения равен 0;

  • флаг знака SF=1, если сумма отрицательна (старший бит числа со знаком равен 1), в противном случае SF=0;

  • флаг переполнения OF=1, если сложение чисел одного знака приводит к результату, который превышает диапазон допустимых значений приемника в обратном коде, а сам приемник при этом меняет знак (рекомендую просмотреть параграф ”сложение двоичных чисел со знаком” на стр.160-162 (учебник Юрова “Ассемблер”).

Чтобы уяснить, как происходит сложение десятичных чисел, рассмотрим такой пример: сложим числа 26 и 55 в упакованном виде:

0010 0110

0101 0101

0111 1011

Полученный результат 7В не является десятичным упакованным числом. Поэтому результат должен быть скорректирован для представления в десятичном виде. Для этих целей разработчики Ассемблера предложили 2 операции корректировки результата сложения:

ААА – корректировка результата для представления в кодах ASCII;

DAA – корректировка результата для представления в упакованном десятичном формате.

Обе команды не имеют операндов и по умолчанию корректируют значение из регистра AL.

Команда ААА преобразует содержимое регистра AL в правильную неупакованную десятичную цифру в младших 4-х битах регистра, а старшие 4 бита заполняет нулями. Используется команда в контексте

ADD AL, BL ; сложить неупакованные цифры, находящиеся в регистрах

AAA ; AL и BL и преобразовать результат в правильное

; десятичное число

Если результат операции превышает 9, то команда ААА добавляет 1 к содержимому регистра AH и устанавливает в 1 флаг CF, в противном случае флаг устанавливается в 0. Поскольку для дальнейшего анализа имеет значение только состояние флага CF, остальные флаги нужно считать неопределенными (иначе говоря, их значения после команды ААА нельзя использовать для анализа – команд условного перехода).

Команда DAA преобразует содержимое регистра AL в 2 правильные упакованные десятичные цифры. Она используется в следующем контексте:

ADD AL, BL ; сложить упакованные BCD-числа в регистрах AL и BL

DAA ; и преобразовать результат в упакованное число

Если результат превышает предельное значение для упакованных BCD-чисел, то DAA добавляет 1 к содержимому регистра AH и устанавливает в 1 флаг CF, в противном случае флаг устанавливается в 0. Замечание относительно остальных флагов для команды DAA такие же, как и для команды ААА.

Как уже говорилось ранее, микропроцессор не имеет устройства вычитания, а имеет только устройство сложения (сумматор). Вычитание на таком устройстве осуществляется в 2 этапа:

  1. меняется знак у вычитаемого - 2-го операнда или источника (иначе говоря, вычитаемое обращается);

  2. складываются уменьшаемое и обращенное вычитаемое.

Для обращения операнда в системе команд имеется самостоятельная команда

NEG приемник.

Эта команда вычитает значение операнда-приемника из 0 и, тем самым, формирует дополнительный код операнда (не забывайте, что дополнительный код числа в дополнительном коде будет являться модулем числа, или, иначе, обращение отрицательного числа даст число положительное).Установка флагов в этой команде осуществляется также, как и в команде вычитания.

Команды вычитания SUB и SBB аналогичны соответствующим командам сложения, только при вычитании флаг CF понимается как признак заема.

SUB приемник, источник– (приемник = приемник - источник)

SBB приемник, источник – (приемник = приемник - источник – перенос_заем)

Как и в случае сложения, команда SUB вычитает числа размером в байт или слово, а также младшие байты чисел повышенной точности. Совокупность команд SUB и SBB позволяет вычитать операнды повышенной точности (двойное слово), например:

per1 DD 4bf8ff31h

per2 DD a5244986h

MOV AX, per1 ;младшая часть

MOV BX, per1+2 ;старшая часть

MOV CX, per2

MOV DX, per2+2

SUB AX, CX

SBB BX, DX

Здесь 32-битовое число из регистров CX и DX вычитается из 32-битового числа, помещенного в регистры AX и BX. Ограничение при вычитании – нельзя вычесть значение регистра или ячейки памяти из константы, поскольку, например, команда SUB 100, AL недопустима. Однако, если заменить недопустимую операцию 2-мя следующими

NEG AL

ADD AL, 100

то вычитание из непосредственного значения будет выполнено и результат получен в AL.

Аналогично сложению, корректируются результаты вычитания при операциях с BCD-числами. Операция AAS корректирует результат вычитания неупакованной десятичной цифры из другой неупакованной десятичной цифры. Команда не имеет операндов и работает с регистром AL по следующему алгоритму:

1) если значение в регистре меньше или равно 9, то флаг CF устанавливается в 0 и управление передается следующей команде;

2) если значение в регистре AL больше 9,

а) из содержимого младшей тетрады этого регистра вычитается 6,

б) обнуляется старшая тетрада регистра AL;

в) флаг CF устанавливается в 1, тем самым фиксируя наличие заема из предыдущего воображаемого разряда.

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

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

Дадим некоторые пояснения относительно арифметического переноса и переполнения.

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

Двоичные

значения

Беззнаковая

десятичная

итог

Знаковая

десятичная

итог

OF

CF

11111100

+00000101

(1)00000001

252

+ 5

1

неверно

-4

+5

1

верно

0

1

01111001

+00001011

10000100

121

+11

132

верно

121

+11

132

неверно

1

0

11110110

+10001001

(1)01111111

246

+137

127

неверно

-10

-119

+127

неверно

1

1

В рассмотренных примерах первый столбец таблицы содержит двоичные значения складываемых переменных, следующие 2 столбца показывают результат, если складываемые переменные интерпретируются как беззнаковые числа (значения переменных переводятся в десятичную систему счисления и складываются), далее 4-ый и 5-ый столбец показывают результат, если эти же двоичные значения интерпретируются, как знаковые значения. Последние 6-ой и 7-ой столбцы таблицы показывают, как устанавливаются флаги переноса (CF) и переполнения (ОF) при выполнении операции суммирования (ADD).

Проанализировав данные из таблицы, сделайте вывод:

  • если складываются беззнаковые переменные, о выходе результата за разрядную сетку свидетельствует установка в 1 флага переноса CF;

  • если складываются знаковые переменные, о неверном результате свидетельствует установка в 1 флага переполнения OF (обратитесь к описанию регистра флагов в лекции 4).

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

Умножение и деление целых чисел

В отличие от сложения и вычитания в микропроцессоре существуют по 2 операции умножения и деления: отдельно для чисел без знака и для чисел со знаком.

Инструкции MUL используется для умножения беззнаковых величин, а инструкция IMUL – для умножения знаковых чисел. Обе инструкции могут изменять состояние флагов переноса CF и переполнения OF. На программиста возлагается задание формата данных, подлежащих обработке, и выбор подходящей инструкции умножения.

Формат инструкций следующий:

MUL регистр

MUL память

IMUL регистр

IMUL память.

Можно умножать байт на байт, слово на слово и в 32-разрядных моделях Intel – двойное слово на двойное слово.

Как видно из формата, в команде указывается только один из сомножителей, второй сомножитель по умолчанию располагается в регистре AL для байтовых операндов и в регистре АХ – для двухбайтовых операндов.

Для операции умножения правило операндов описано таблицей:

Сомножитель_1

Сомножитель_2

Результат

Байт (в РОН или ячейке памяти)

AL

16 бит в АХ: в AL – младшая часть результата, в AH - старшая

Слово (в РОН или ячейке памяти)

АХ

32 бита в паре DX: AX; в AX – младшая часть результата, в DX – старшая часть результата

Примеры команды MUL:

BYTE1 DB 80H

BYTE2 DB 40H

WORD1 DW 8000H

WORD2 DW 2000H

MOV AL,BYTE1 ;байт умножается на байт

MUL BYTE2 ;результат в АХ, равный 2000Н (8192=128*64)

(если до команды MOV AL,BYTE1 в АН содержались какие-либо данные, командой MUL BYTE2 эти данные будут затерты)

MOV AX,WORD1 ;слово умножается на слово

MUL WORD2 ;результат в в DX:AX, равный 1000 0000Н

Как видно из таблицы, операнды-сомножители должны иметь одинаковый формат: либо оба операнда – байты, либо оба операнда – слова. Если потребуется умножить байт на слово, необходимо сначала привести в соответствие размеры операндов. В Ассемблере существуют команды CBW –преобразовать байт в слово и CWD – преобразовать слово в двойное слово. Обе команды не имеют операндов. Команда CBW по умолчанию оперирует содержимым регистра AL, а результат помещает в регистр AX. Команда CWD по умолчанию выбирает слово из регистра AX и помещает результат в DX:AX. Преобразование заключается в заполнении битом знакового разряда старших регистров результата – для команды CBW – регистра АН, а для команды CWD – регистра DX.

СBW – преобразовать байт в регистре AL в слово в регистре АХ путем распространения старшего бита AL на все биты регистра AH;

CWD –преобразовать слово в регистре АХ в двойное слово в регистрах AX и DX, путем распространения старшего 15-ого бита регистра АХ на все биты регистра DX. Эти команды позволяют приводить разноформатные операнды к одному формату (большему).

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

MOV AL,BYTE2

CBW

позволит получить результат в АХ, равный 0020Н, а команды

MOV AL,BYTE1

CBW

даст результат в АХ, равный FF80H, что, конечно, неверно.

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

MOV AL,BYTE1

MOV AH, 0 ;поместили сомножитель в регистр АХ

MUL WORD2 ;результат в в DX:AX, равный 1 0000Н

Команда IMUL реализует перемножение знаковых чисел. Результаты применения этой инструкции к приведенным выше данным будут следующие:

MOV AL,BYTE1 ;байт умножается на байт

IMUL BYTE2 ;результат в АХ, равный E000Н (-8192=-128*64)

MOV AX,WORD1 ;слово умножается на слово

MUL WORD2 ;результат в в DX:AX, равный F0000000Н

MOV AL,BYTE1

CBW ;поместили сомножитель в регистр АХ (FF80H)

IMUL WORD1 ;результат в в DX:AX, равный 0040 0000Н.

После выполнения команд флаги СF и OF показывают какая часть произведения существенна для дальнейших операций. При умножении чисел без знака эти флаги равны 0, если старшая часть результата нулевая, в противном случае (результат превысил по значащим цифрам сомножители) флаги устанавливаются в 1. Приумножении чисел со знаком флаги равны 0, если старшая половина произведения содержит расширение знакового разряда младшей половины (при положительном результате это 0, при отрицательном –1). Обратите внимание, что эти операции не позволяют иметь в качестве операнда константу (непосредственное значение).

Результат перемножения правильных неупакованных BCD-чисел может быть представлен в неупакованном формате BCD-чисел с помощью команды ААМ. Команда работает с регистрами AL и AH и выполняет следующее: делит значение регистра AL на 10 и запоминает частное в регистре АН (старшая неупакованная цифра результата) и остаток – регистре AL (младшая неупакованная цифра результата). Однако очевидно, что этими операциями можно выполнить только табличное умножение. Для более сложных операций умножения необходимо разработать программу, реализующую умножение “в столбик”, получение частных произведений, их сдвиги и сложение. Операции, аналогичной ААМ для упакованных BCD-чисел в микропроцессоре не существует.

В операции деления также, как и при умножении учитывается знак: операция DIV обрабатывает беззнаковые числа, а опtрация IDIV – знаковые. Подходящую команду в каждом конкретном случае выбирает программист.

Основные форматы операций деления делят слово на байт, двойное слово на слово. А в 32-разрядных моделях компьютеров еще и учетверенное слово на двойное слово.

Формат инструкций следующий:

DIV регистр

DIV память

IDIV регистр

IDIV память.

Как видно из формата, в команде указывается только делитель, делимое по умолчанию располагается в регистре AХ или в паре регистров DX:АХ .

Для деления правило расположения операндов отражается в таблице:

Делимое

Делитель

Частное

Остаток

Слово 16 бит в регистре АХ

Байт (в РОН или ячейке памяти)

Байт в регистре AL

Байт в регистре AH

32 бита в DX –старшая часть, в АХ- младшая

Слово 16 бит (в РОН или ячейке памяти)

Слово 16 бит в регистре АХ

Слово 16 бит в регистре DX

Примеры команды DIV:

BYTE1 DB 80H

BYTE2 DB 16H

WORD1 DW 2000H

WORD2 DW 0010H

WORD3 DW 1000H

MOV AX,WORD1 ;делимое -> в АХ

DIV BYTE1 ;получим частное вAL, остаток – AH

;в AL – значение 40Н (8192:128=64-частное и 0 –остаток), в АН –00Н

Интерпретируем значение в BYTE1 как беззнаковое и разделим его на значение из BYTE2.

MOV AH,0 ;подготавливаем старшую часть делимого

MOV AL, BYTE1 ;заполняем младшую часть делимого

DIV BYTE2 ;делим слово на байт, в AL 05Н, в AH – 12Н

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

1) при делении чисел без знака для ситуации первой строки таблицы делимое более чем в 256 раз больше делителя,

2) при делении чисел без знака для ситуации второй строки таблицы делимое более чем в 65636 раз больше делителя,

3) при делении чисел со знаком для ситуации первой строки таблицы делимое более чем в 128 раз больше значения делителя,

4) при делении чисел со знаком для ситуации второй строки таблицы делимое более чем в 32768 раз больше значения делителя.

Приведу некоторые полезные приемы программирования.

Если частное слишком велико, можно выполнить деление с помощью последовательного вычитания делителя из делимого, например, делимое представляет слово, а делитель – байт, однако результат деления превышает байт (численные значения могут быть такими: делимое-64000, делитель – 80, результат деления 800 превышает максимальное значение, хранимое в одном байте). Фрагмент программы при условии, что делимое находится в регистре АХ, а делитель – в регистре ВХ, может быть следующим:

SUB CX,CX ;очистка регистра частного

L20: CMP AX,BX ;если делимое меньше делителя

JB L30 ;вычитания закончить

SUB AX,BX ;вычесть делитель из делимого

INC CX ;увеличить частное на 1

JMP L20 ;идти на начало цикла вычитания

L30: . . . . . . . .

При попадании в точку L30 СХ содержит частное, АХ – остаток.

Если делимое – двойное слово в паре DX:AX, то

SUB CX,CX ;очистка регистра частного

L20: CMP DX,0 ;пока DX не 0 – делимое больше делителя

JNE L30

CMP AX,BX ;если делимое меньше делителя

JB L40 ;вычитания закончить

L30: SUB AX,BX ;вычесть делитель из делимого

SBB DX,0 ;учесть возможный заем

;SUB AX,BX ;вычесть делитель из делимого

INC CX ;увеличить частное на 1

JMP L20 ;идти на начало цикла вычитания

L40: . . . . . . . .

Большое делимое и малый делитель вызовут многократное повторение команд!

Если необходимо разделить число на степени числа 2, то можно деление заменить сдвигами, например требуется разделить двойное слово в паре DX:AX на 16:

SHR AX,04 ;делим младшую часть делимого

MOV BL,DL ;сохраняем 3-ий байт в BL

SHR DX,04 ; делим старшую часть делимого

SHL BL,04 ;умножаем 3-ий байт на 16, поскольку младший полубайт старшего слова должен перейти в старший полубайт младшего слова результата

OR AH,BL добавляем полубайт к 3-ему байту результата

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

DX AX

0 1 0 1 0 1 0 1

1 1 1 1 1 1 1 1

1 0 1 1 0 1 0 1

1 1 1 0 1 0 1 1

0 1 0 1 0 1 0 1

1 1 1 1 1 1 1 1

0 0 0 0 1 0 1 1

0 1 0 1 1 1 1 0

Соседние файлы в папке Лекции по ассемблеру