Жанр: Учеба
Ассемблер и программирование для IBM PC
...фиксом REP.
11.2. Введите, ассемблируйте и выполните компоновку программы,
приведенной на рис.11.1. Не забудьте о инициализации регистра ES. Замените
команды MOVSB и MOVSW для пересылки справа налево. Измените процедуру
H10SCAS для сканирования поля NAME1 на слово "mb". Используя отладчик
DEBUG для трассировки процедур, обратите внимание на содержимое сегмента
данных и регистров.
11.3. Имеются следующие определения:
DATASG SEGMENT PARA
CONAME DB 'SPACE EXPLORERS INC.'
PRLINE DB 20 DUP(' ')
Используя цепочечные команды, выполните:
а) пересылку данных из CONAME в PRLINE слева направо;
б) пересылку данных из CONAME в PRLINE справа налево;
в) загрузку третьего и четвертого байтов области CONAME в регистр AX;
г) сохранение содержимого регистра AX в область по адресу PRLINE+5;
д) сравнение данных в областях CONAME и PRLINE (они должны быть не
равны);
е) сканирование областей CONAME и PRLINE, и поиск в ней символа
пробел. Если символ будет найден, то переслать его в регистр BH.
11.4. Переделайте процедуру H10SCAS (рис.11.1) так, чтобы выполнялось
сканирование поля NAME1 на символ "er". Обратите внимание, что символы
"er" не встречаются в поле NAME1 как одно слово: /As/se/mb/le/rs/. Для
решения этой проблемы возможны два варианта:
а) использовать команду SCASW дважды, причем первая должна начинаться
по адресу NAME1, а вторая - по адресу NAME1+1;
б) использовать команду SCASB для поиска символа "е" и сравнить затем
следующий байт на символ "r".
11.5. Определите поле, содержащее шест. значения 03, 04, 05 и B4.
Продублируйте это поле 20 раз и выдайте результат на экран.
ГЛАВА 12 Арифметические операции I: Обработка двоичных данных
__________________________________________________________________________
Ц е л ь: Дать сведения об операциях сложения, вычитания, умножения и
деления двоичных данных.
ВВЕДЕНИЕ
________________________________________________________________
Несмотря на то, что мы привыкли к десятичной арифметике (база 10),
компьютер работает только с двоичной арифметикой (база 2). Кроме того,
ввиду ограничения, накладываемого 16-битовыми регистрами, большие величины
требуют специальной обработки.
Данная глава дает сведения об операциях сложения, вычитания,
умножения и деления для беззнаковых и знаковых данных. В главе приводятся
много примеров и предупреждений о различных ловушках для опрометчивых
исследователей мира микропроцессора. В следующей главе будут раскрыты
операции преобразования между двоичными данными и ASCII кодами.
СЛОЖЕНИЕ И ВЫЧИТАНИЕ
________________________________________________________________
Команды ADD и SUB выполняют сложение и вычитание байтов или слов,
содержащих двоичные данные. Вычитание выполняется в компьютере по методу
сложения с двоичным дополнением: для второго операнда устанавливаются
обратные значения бит и прибавляется 1, а затем происходит сложение с
первым операндом. Во всем, кроме первого шага, операции сложения и
вычитания идентичны.
На рис.12.1 представлены примеры команд ADD и SUB, обрабатывающие
байты или слова. В процедуре B10ADD используется команда ADD для сложения
байтов, а в процедуре C10SUB команда SUB вычитает слова. Примеры
показывают все пять возможных ситуаций:
сложение/вычитание регистр-регистр;
сложение/вычитание память-регистр;
сложение/вычитание регистр-память;
сложение/вычитание регистр-непоср.значение;
сложение/вычитание память-непоср.значение.
__________________________________________________________________________
page 60,132
TITLE EXADD (СОМ) Сложение и вычитание
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; --------------------------------------------
BYTEA DB 64H ;Элементы данных
BYTEB DB 40H
BYTEC DB 16H
WORDA DW 4000H
WORDB DW 2000H
WORDC DW 1000H
; --------------------------------------------
MAIN PROC NEAR ;Основная процедура:
CALL B10ADD ;Вызвать сложение ADD
CALL C10SUB ;Вызвать вычитание SUB
RET
MAIN ENDP
; Пример сложения байт:
; --------------------
B10ADD PROC
MOV AL,BYTEA
MOV BL,BYTEB
ADD AL,BL ;Регистр и регистр
ADD AL,BYTEC ;Память и регистр
ADD BYTEA,BL ;Регистр и память
ADD BL,10H ;Непосредств. и регистр
ADD BYTEA,25H ;Непосредств. и память
RET
B10ADD ENDP
; Пример вычитания слов:
; ---------------------
C10SUB PROC
MOV AX,WORDA
MOV BX,WORDB
SUB AX,BX ;Регистр из регистра
SUB AX,WORDC ;Память из регистра
SUB WORDA,BX ;Регистр из памяти
SUB BX,1000H ;Непосредств. из pег.
SUB WORDA,256H ;Непосредств. из пам.
RET
C10SUB ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.12.1. Примеры команд ADD и SUB.
Поскольку прямой операции память-память не существует, данная
операция выполняется через регистр. В следующем примере к содержимому
слова WORDB прибавляется содержимое слова WORDA, описанных как DW:
MOV AX,WORDA
ADD AX,WORDB
MOV WORDB,AX
Переполнения
--------------
Опасайтесь переполнений в арифметических операциях. Один байт
содержит знаковый бит и семь бит данных, т.е. значения от -128 до +127.
Результат арифметической операции может легко превзойти емкость
однобайтового регистра. Например, результат сложения в регистре AL,
превышающий его емкость, автоматически не переходит в регистр AH.
Предположим, что регистр AL содержит шест.60, тогда результат команды
ADD AL,20H
генерирует в AL сумму - шест.80. Но операция также устанавливает флаг
переполнения и знаковый флаг в состояние "отрицательно". Причина
заключается в том, что шест.80 или двоичное 1000 0000 является
отрицательным числом. Т.е. в результате, вместо +128, мы получим -128. Так
как регистр AL слишком мал для такой операции и следует воспользоваться
регистром AX. В следующем примере команда CBW (Convert Byte to Word -
преобразовать байт в слово) преобразует шест.60 в регистре AL в шест.0060
в регистре AX, передавая при этом знаковый бит (0) через регистр AH.
Команда ADD генерирует теперь в регистре AX правильный результат:
шест.0080, или +128:
CBW ;Расширение AL до AX
ADD AX,20H ;Прибавить к AX
Но полное слово имеет также ограничение: один знаковый бит и 15 бит
данных, что соответствует значениям от -32768 до +32767. Рассмотрим далее
как можно обрабатывать числа, превышающие эти пределы.
Многословное сложение
-----------------------
Максимальное возможное значение в регистре +32767 ограничивает
возможность компьютера для выполнения арифметических операций. Рассмотрим
два способа выполнения арифметических операций. Первый способ - более
прост, но специфичен, второй - сложнее, но имеет общий характер.
__________________________________________________________________________
page 60,132
TITLE EXDBADD (COM) Пример сложения двойных слов
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; -------------------------------------------
WORD1A DW 0123H ;Элементы данных
WORD1B DW 0BC62H
WORD2A DW 0012H
WORD2B DW 553AH
WORD3A DW ?
WORD3B DW ?
; -------------------------------------------
MAIN PROC NEAR ;0сновная процедура:
CALL D10DWD ;Вызвать сложение 1
CALL E10DWD ;Вызвать сложение 2
RET
MAIN ENDP
; Пример сложения двойных слов:
; ----------------------------
D10DWD PROC
MOV AX,WORD1B ;Сложить правые слова
ADD AX,WORD2B
MOV WORD3B,AX
MOV AX,WORD1A ;Сложить левые слова
ADC AX,WORD2A ; с переносом
MOV WORD3A,AX
RET
D10DWD ENDP
; Сложение чисел любой длины:
; --------------------------
E10DWD PROC
CLC ;Очистить флаг переноса
MOV CX,2 ;Установить счетчик
LEA SI,WORD1B ;Левое слово DWORD1
LEA DI,WORD2B ;Левое слово DWORD2
LEA BX,WORD3B ;Левое слово суммы
Е20:
MOV AX,[SI] ;Поместить слово в AX
ADC AX,[DI] ;Сложить с переносом
MOV [BX],AX ;Сохранить слово
DEC SI
DEC SI
DEC DI
DEC DI
DEC BX
DEC BX
LOOP Е20 ;Повторить цикл
RET
E10DWD ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.12.2. Сложение двойных слов.
На рис.12.2 процедура D10DWD демонстрирует простой способ сложения
содержимого одной пары слов (WORD1A и WORD1B) с содержимым второй пары
слов (WORD2A и WORD2B) и сохранения суммы в третьей паре слов (WORD3A и
WORD3B). Сначала выполняется сложение правых слов:
WORD1B BC62
WORD2B 553A
-----
Сумма: 1119C
Сумма - шест.1119C превышает емкость регистра AX. Переполнение
вызывает установку флага переноса в 1. Затем выполняется сложение левых
слов, но в данном случае, вместо команды ADD используется команда сложения
с переносом ADC (ADd with Carry). Эта команда складывает два значения, и
если флаг CF уже установлен, то к сумме прибавляется 1:
WORD1A 0123
WORD2A 0012
Плюс перенос 1
----
Сумма: 0136
При использовании отладчика DEBUG для трассировки арифметических
команд можно увидеть эту сумму 0136 в регистре AX, и обратные значения
3601 в поле WORD3A и 9C11 в поле WORD3B.
На рис.12.2 процедура E10DWD демонстрирует подход к сложению значений
любой длины. Действие начинается со сложения самых правых слов
складываемых полей. В первом цикле складываются правые слова, во втором -
слова, расположенные левее. При этом адреса в регистрах SI, DI и BX
уменьшаются на 2. По две команда DEC выполняют эту операцию для каждого
регистра. Применять команду
SUB reg,02
в данном случае нельзя, т.к. при этом будет очищен флаг переноса, что
приведет к искажению результата сложения.
Ввиду наличия цикла, используется только одна команда сложения ADC.
Перед циклом команда CLC (CLear Carry - очистить флаг переноса)
устанавливает нулевое значение флага переноса. Для работы данного метода
необходимо: 1) обеспечить смежность слов, 2) выполнять обработку справа
налево и 3) загрузить в регистр CX число складываемых слов.
Для многословного вычитания используется команда SBB (SuBtract with
Borrow - вычитание с заемом) эквивалентная команде ADC. Заменив в
процедуре E10DWD (рис.12.2) команду ADC на SBB, получим процедуру для
вычитания.
БЕЗЗНАКОВЫЕ И ЗНАКОВЫЕ ДАННЫЕ
________________________________________________________________
Многие числовые поля не имеют знака, например, номер абонента, адрес
памяти. Некоторые числовые поля предлагаются всегда положительные,
например, норма выплаты, день недели, значение числа ПИ. Другие числовые
поля являются знаковые, так как их содержимое может быть положительным или
отрицательным. Например, долговой баланс покупателя, который может быть
отрицательным при переплатах, или алгебраическое число.
Для беззнаковых величин все биты являются битами данных и вместо
ограничения +32767 регистр может содержать числа до +65535. Для знаковых
величин левый байт является знаковым битом. Команды ADD и SUB не делают
разницы между знаковыми и беззнаковыми величинами, они просто складывают и
вычитают биты. В следующем примере сложения двух двоичных чисел, первое
число содержит единичный левый бит. Для беззнакового числа биты
представляют положительное число 249, для знакового - отрицательное число
-7:
Беззнаковое Знаковое
11111001 249 -7
00000010 2 +2
--- --
11111011 251 -5
Двоичное представление результата сложения одинаково для беззнакового и
знакового числа. Однако, биты представляют +251 для беззнакового числа и
-5 для знакового. Таким образом, числовое содержимое поля может
интерпретироваться по разному.
Состояние "перенос" возникает в том случае, когда имеется перенос в
знаковый разряд. Состояние "переполнение" возникает в том случае, когда
перенос в знаковый разряд не создает переноса из разрядной сетки или
перенос из разрядной сетки происходит без переноса в знаковый разряд. При
возникновении переноса при сложении беззнаковых чисел, результат
получается неправильный:
Беззнаковое Знаковое 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
(неправильно) (неправильно)
УМНОЖЕНИЕ
________________________________________________________________
Операция умножения для беззнаковых данных выполняется командой MUL, а
для знаковых - IMUL (Integer MULtiplication - умножение целых чисел).
Ответственность за контроль над форматом обрабатываемых чисел и за выбор
подходящей команды умножения лежит на самом программисте. Существуют две
основные операции умножения:
"Б а й т н а б а й т". Множимое находится в регистре AL, а
множитель в байте памяти или в однобайтовом регистре. После умножения
произведение находится в регистре AX. Операция игнорирует и стирает любые
данные, которые находились в регистре AH.
| AH | AL | | AX |
До умножения: | |Множимое| После: |Произведение|
"С л о в о н а с л о в о". Множимое находится в регистре AX, а
множитель - в слове памяти или в регистре. После умножения произведение
находится в двойном слове, для которого требуется два регистра: старшая
(левая) часть произведения находится в регистре DX, а младшая (правая)
часть в регистре AX. Операция игнорирует и стирает любые данные, которые
находились в регистре DX.
| AX | | DX || AX |
До умножения:|Множимое| После: |Ст.часть||Мл.часть|
| Произведение |
В единственном операнде команд MUL и IMUL указывается множитель.
Рассмотрим следующую команду:
MUL MULTR
Если поле MULTR определено как байт (DB), то операция предполагает
умножение содержимого AL на значение байта из поля MULTR. Если поле MULTR
определено как слово (DW), то операция предполагает умножение содержимого
AX на значение слова из поля MULTR. Если множитель находится в регистре,
то длина регистра определяет тип операции, как это показано ниже:
MUL CL ;Байт-множитель: множимое в AL, произвед. в AX
MUL BX ;Слово-множитель:множимое в AX, произвед. в DX:AX
Беззнаковое умножение: Команда MUL
------------------------------------
Команда MUL (MULtiplication - умножение) умножает беззнаковые числа.
На рис.12.3 в процедуре C10MUL дано три примера умножения: байт на байт,
слово на слово и слово на байт. Первый пример команды MUL умножает шест.80
(128) на шест.47 (64). Произведение шест.2000 (8192) получается в регистре
AX.
__________________________________________________________________________
page 60,132
TITLE EXMULT (COM) Пример команд умножения
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
OR6 100H
BEGIN: JMP SHORT MAIN
; -------------------------------------------
BYTE1 DB 80H
BYTE2 DB 40H
WORD1 DW 8000H
WORD2 DW 4000H
; -------------------------------------------
MAIN PROC NEAR ;Основная процедура:
CALL C10MUL ;Вызвать умнож. MUL
CALL D10IMUL ;Вызвать умнож. IMUL
RET
MAIN ENDP
; Пример умножения MUL:
; --------------------
C10MUL PROC
MOV AL,BYTE1 ;Байт * байт
MUL BYTE2 ; произведение в АХ
MOV AX,WORD1 ;Слово * слово
MUL WORD2 ; произведение в DX:AX
MOV AL,BYTE1 ;Байт * слово
SUB АН,АН ; расшир. множ. в АН
MUL WORD1 ; произведение в DX:AX
RET
C10MUL ENDP
; Пример умножения IMUL:
; ---------------------
D10IMUL PROC
MOV AL,BYTE1 ;Байт * байт
IMUL BYTE2 ; произведение в АХ
MOV AX,WORD1 ;Слово * слово
IMUL WORD2 ; произвед. в DX:AX
MOV AL,BYTE1 ;Байт * слово
CBW ; расшир. множ. в АН
IMUL WORD1 ; произвед. в DX:AX
RET
D10IMUL ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.12.3. Беззнаковое и знаковое умножение.
Второй пример команды MUL генерирует шест.10000000 в регистрах DX:AX.
Третий пример команды MUL выполняет умножение слова на байт и требует
расширение байта BYTE1 до размеров слова. Так как предполагаются
беззнаковые величины, то в примере левый бит регистра AH равен нулю. (При
использовании команды CBW значение левого бита регистра AL может быть 0
или 1). Произведение - шест.00400000 получается в регистрах DX:AX.
Знаковое умножение: Команда IMUL
----------------------------------
Команда IMUL (Integer MULtiplication - умножение целых чисел)
умножает знаковые числа. На рис.12.3 в процедуре D10IMUL используются те
же три примера умножения, что и в процедуре C10MUL, но вместо команд MUL
записаны команды IMUL.
Первый пример команды IMUL умножает шест.80 (отрицательное число) на
шест.40 (положительное число). Произведение - шест.E000 получается в
регистре AX. Используя те же данные, команда MUL дает в результате
шест.2000, так что можно видеть разницу в использовании команд MUL и IMUL.
Команда MUL рассматривает шест.80 как +128, а команда IMUL - как -128. В
результате умножения -128 на +64 получается -8192 или шест.E000.
(Попробуйте преобразовать шест.Е000 в десятичный формат).
Второй пример команды IMUL умножает шест.8000 (отрицательное
значение) на шест.2000 (положительное значение). Произведение -
шест.F0000000 получается в регистрах DX:AX и представляет собой
отрицательное значение.
Третий пример команды IMUL перед умножением выполняет расширение
байта BYTE1 до размеров слова в регистре AX. Так как значения
предполагаются знаковые, то в примере используется команда CBW для
перевода левого знакового бита в регистр AH: шест.80 в регистре AL
превращается в шест.FF80 в регистре AX. Поскольку множитель в слове WORD1
имеет также отрицательное значение, то произведение должно получится
положительное. В самом деле: шест.00400000 в регистрах DX:AX - такой же
результат, как и в случае умножения командой MUL, которая предполагала
положительные сомножители.
Таким образом, если множимое и множитель имеет одинаковый знаковый
бит, то команды MUL и IMUL генерируют одинаковый результат. Но, если
сомножители имеют разные знаковые биты, то команда MUL вырабатывает
положительный результат умножения, а команда IMUL - отрицательный.
Можно обнаружить это, используя отладчик DEBUG для трассировки
примеров.
П о в ы ш е н и е э ф ф е к т и в н о с т и у м н о ж е н и я: При
умножении на степень числа 2 (2,4,8 и т.д.) более эффективным является
сдвиг влево на требуемое число битов. Сдвиг более чем на 1 требует
загрузки величины сдвига в регистр CL. В следующих примерах предположим,
что множимое находится в регистре AL или AX:
Умножение на 2: SHL AL,1
Умножение на 8: MOV CL,3
SHL AX,CL
Многословное умножение
------------------------
Обычно умножение имеет два типа: "байт на байт" и "слово на слово".
Как уже было показано, максимальное знаковое значение в слове ограничено
величиной +32767. Умножение больших чисел требует выполнения некоторых
дополнительных действий. Рассматриваемый подход предполагает умножение
каждого слова отдельно и сложение полученных результатов. Рассмотрим
следующее умножение в десятичном формате:
х12
-----
-----
Представим, что десятичная арифметика может умножать только двузначные
числа. Тогда можно умножить 13 и 65 на 12 раздельно, следующим образом:
13 65
х12 х12
--- ---
26 130
13 65
--- ---
156 780
Следующим шагом сложим полученные произведения, но поскольку число 13
представляло сотни, то первое произведение в действительности будет 15600:
+780
-----
Ассемблерная программа использует аналогичную технику за исключением
того, что данные имеют размерность слов (четыре цифры) в шестнадцатеричном
формате.
У м н о ж е н и е д в о й н о г о с л о в а н а с л о в о.
Процедура E10XMUL на рис.12.4 умножает двойное слово на слово. Множимое,
MULTCND, состоит из двух слов, содержащих соответственно шест.3206 и
шест.2521. Определение данных в виде двух слов (DW) вместо двойного слова
(DD) обусловлено необходимостью правильной адресации для команд MOV,
пересылающих слова в регистр AX. Множитель MULTPLR содержит шест.6400.
Область для записи произведения, PRODUCT, состоит из трех слов. Первая
команда MUL перемножает MULTPLR и правое слово поля MULTCND; произведение
- шест.0E80 E400 записывается в PRODUCT+2 и PRODUCT+4. Вторая команда MUL
перемножает MULTPLR и левое слово поля MULTCND, получая в результате шест.
138A 5800. Далее выполняется сложение двух произведений следующим образом:
Произведение 1: 0000 0E80 E400
Произведение 2: 138A 5800
--------------
Результат: 138A 6680 E400
Так как первая команда ADD может выработать перенос, то второе
сложение выполняется командой сложения с переносом ADC (ADd with Carry). В
силу обратного представления байтов в словах в процессорах 8086/8088,
область PRODUCT в действительности будет содержать значение 8A13 8066
00E4. Программа предполагает, что первое слово в области PRODUCT имеет
начальное значение 0000.
__________________________________________________________________________
TITLE EXDWMUL - Умножение двойных слов
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; ---------------------------------------------
MULTCND DW 3206H ;Элементы данных
DW 2521H
MULTPLR DW 6400H
DW 0A26H
PRODUCT DW 0
DW 0
DW 0
DW 0
; ---------------------------------------------
MAIN PROC NEAR ;Основная процедура
CALL E10XMUL ;Вызвать 1-е умножение
CALL Z10ZERO ;Очистить произведение
CALL F10XMUL ;Вызвать 2-е умножение
RET
MAIN ENDP
; Умножение двойного слова на слово:
; -----------------------------------------------
E10XMUL PROC
MOV AX,MULTCND+2 ;Умножить правое слова
MUL MULTPLR ; множимого
MOV PRODUCT+4,AX ;Записать произведение
MOV PRODUCT+2,DX
MOV AX,MULTCND ;Умножить левое слово
MUL MULTPLR ; множимого
ADD PRODUCT+2,AX ;Сложить с полученным ранее
ADC PRODUCT,DX
RET
E10XMUL ENDP
; Перемножение двух двойных слов:
; --------------------------------------------
F10XMUL PROC
MOV AX,MULTCND+2 ;Слово-2 множимого
MUL MULTPLR+2 ; * слово-2 множителя
MOV PRODUCT+6,AX ;Сохранить результат
MOV PRODUCT+4,DX
MOV AX,MULTCND+2 ;Слово-2 мно
...Поділитися у соцмережах