- •Введение
- •1.1. Основные направления эволюции микрокомпьютеров
- •1.2. Основные сведения о компьютерах
- •1.3. Представление чисел
- •Заключение
- •Лекция 2 машинная организация процессора 80286
- •2.1. Введение
- •2.2. Структура памяти
- •2.3. Сегментация памяти
- •2.4. Структура ввода-вывода
- •2.5. Регистры
- •2.6. Операнды и режимы адресации операндов
- •2.7. Замечания о режимах адресации
- •Глава 3 базовая система команд процессора 80286
- •3.1. Нотация языка ассемблера
- •3.2. Команды передач данных
- •3.3. Арифметические команды
- •Лекция 6. Цепочечные команды
- •Лекция 7. Команды безусловной передачи управления
- •Лекция 8. Команды условной передачи управления
- •Лекция 9. Прерывания
- •Int n
- •Лекция 10. Флажковые команды
- •3.10. Команды синхронизации
- •3.11. Поддержка языков высокого уровня
- •3.12. Замечания о префиксах
- •3.13. Воздействие на флажки
- •Заключение
3.3. Арифметические команды
Процессор 80286 имеет четыре базовые арифметические операции в нескольких различных формах (табл. 3.6). Операции выполняются над 8-и 16-битными знаковыми и беззнаковыми операндами. Кроме того, предусмотрены команды для обработки десятичных чисел.
|
|
Таблица 3.6 Арифметические команды | |
|
Мнемоника команды |
Описание команды | |
|
|
Сложение | |
|
|
ПРИЁМНИК + ИСТОЧНИК ПРИЁМНИК | |
|
|
ПРИЁМНИК + ИСТОЧНИК + CFПРИЁМНИК | |
|
|
ПРИЁМНИК +1 ПРИЁМНИК | |
|
|
Вычитание | |
|
|
ПРИЁМНИК – ИСТОЧНИК ПРИЁМНИК | |
|
|
ПРИЁМНИК - ИСТОЧНИК – CF ПРИЁМНИК | |
|
|
ПРИЁМНИК –1 ПРИЁМНИК | |
|
|
0 - ПРИЁМНИК ПРИЁМНИК | |
|
|
ПРИЁМНИК – ИСТОЧНИК ? | |
|
|
Умножение | |
|
|
AL*ИСТОЧНИК8 AX | |
|
|
AX*ИСТОЧНИК16 DX, AX | |
|
IMUL(умножить со знаком) |
Команда MUL, но операнды знаковые. | |
|
|
Деление | |
|
|
AX/ИСТОЧНИК8 AL | |
|
|
Остаток AH | |
|
|
ИСDX, AX/ИСТОЧНИК16 AX | |
|
|
Остаток DX | |
|
IDIV (разделить со знаком) |
Команда DIV, но операнды знаковые. | |
Различие между знаковыми и беззнаковыми числами заключается в интерпретации двоичных наборов. Беззнаковые числа - это обычные двоичные числа (все биты значащие), а знаковые числа представлены в дополнительном коде. На рис. 3.7 показаны диапазон и представление знаковых и беззнаковых чисел. Операции сложения и вычитания одинаковы для обоих типов чисел, т.е. обычные команды двоичного сложения и вычитания, разработанные для беззнаковых чисел, дают правильный результат и применительно к знаковым числам. Единственное различие между знаковыми и беззнаковыми сложением и вычитанием заключается в механизме обнаружения выхода за диапазон. Команды сложения и вычитания устанавливают флажок CF, если результат, интерпретируемый как беззнаковое число, оказывается вне диапазона; они уже устанавливают флажок OF, если результат, интерпретируемый как знаковое число, выходит за диапазон. Возможны ситуации, когда знаковый или беззнаковый результат будет вне диапазона, хотя другой результат оказывается в диапазоне (рис. 3.8).
Большинство арифметических команд устанавливают или сбрасывают шесть флажков состояния, показывающих определенные свойства результата. В общем, флажки фиксируют следующие особенности:
флажок CF устанавливается, если операция дала беззнаковый результат вне диапазона;
флажок OF устанавливается, если в операции получился знаковый результат, находящийся вне диапазона (так называемое знаковое переполнение);
флажок ZF устанавливается, если результат операции (знаковый или беззнаковый) равен нулю;
флажок SF устанавливается, если старший бит результата операции содержит 1, показывая отрицательное число;
флажок PF устанавливается, если результат операции содержит четыре число единичных бит (так называемый четный паритет);
флажок AF устанавливается, если в десятичных операциях требуется коррекция (подробнее см. далее).

Поведение флажков в арифметических операциях показано далее в табл. 3.22.
В арифметике многократной точности беззнаковые числа, длина которых более 16 бит, разделяются на 8- или 16-битные поля, а затем операции циклически выполняются над последовательными полями, начиная с младших. Если любая из этих операций дает результат вне диапазона, результат оказывается правильным, но 1 либо переносится (сложение), либо занимается (вычитание) в операции над следующими полями. Рассмотрим, например, сложение 24-битных чисел 0011 1010 0000 0111 1011 0010 и 0010 0000 1100 0010 0101 0011. Его можно выполнить за три сложения 8-битных чисел.

Складываются младшие 8 бит:
+1011 0010
0101 0011
0000 0101 CF = 1
2. Суммируются средние 8 бит с учетом переноса, образованного в предыдущем сложении:
1(последний CF)
+0000 0111
1100 0010
1100 1010 CF = 0
3. Наконец, складываются старшие 8 бит с учетом переноса от предыдущего сложения:
0(последний CF)
+0011 1010
0010 0000
0101 1010 CF = 0
Получен результат 0101 1010 1100 1010 0000 0101.
Этот пример показывает необходимость наличия команды (сложить с переносом), которая суммирует значения двух операндов и значение флажка CF. Аналогичная команда (вычесть с заемом) удобна для вычитания с многократной точностью.
В арифметике многократной точности следует учитывать, что результат сложения и вычитания может оказаться вне диапазона. Это обычная ситуация, и она не свидетельствует об ошибке. Но выходящий за диапазон знаковый результат обычно не ожидается. Он показывает, что возникла ошибка и до продолжения расчетов результат необходимо скорректировать.
Команды сложения. К командам сложения относятся ADD, ADC и INC. В общем, они применимы к любым операндам.
Команда ADD (табл. 3.7) суммирует байты или слова источника и приемника и помещает результат в приемник. Один из операндов может находиться в регистре или памяти, а другой - в регистре или в самой команде (непосредственный операнд).
Команда ADC аналогична команде ADD, но она привлекает для сложения начальное значение CF, что упрощает реализацию арифметики с многократной точностью. Формы команды ADC такие же, как и у команды ADD (табл. 3.7).
Команда INC имеет всего один операнд. Она прибавляет 1 к содержимому операнда и помещает результат в этот же операнд. Примеры команды INC показаны в табл. 3.8. Команда INC идентична команде ADD с непосредственным операндом 1, но требует меньше байт. Она включена в систему команд потому, что прибавление (и вычитание) 1 встречается очень часто и должно осуществляться командой с минимальной длиной.
|
|
Таблица 3.7 Примеры двухоперандных и арифметических и логических команд |
| |||
|
Операнды |
Слово |
Байт | |||
|
Регистр с регистром в регистр |
КОП AX, BX |
КОП BL, CL | |||
|
Регистр с памятью в регистр |
КОП CX, MEMW |
КОП DL, MEMB | |||
|
Регистр с памятью в память |
КОП MEMW, CX |
КОП MEMB, DL | |||
|
Регистр с непосредственным операндом в регистр |
КОП DX, 490 |
КОП BL, 50 | |||
|
Память с непосредственным операндом в память |
КОП MEMW, 490 |
КОП MEMB, 50 | |||
|
КОП обозначает: ADD, ADC,SUB, SBB, CMP, AND, OR, XOR, TEST |
|
| |||
|
|
|
| |||
|
|
|
| |||
|
|
Таблица 3.8 Примеры однооперандных арифметических и логических команд |
| |||
|
Операнд |
Слово |
Байт | |||
|
Регистр |
КОП BX |
КОП DL | |||
|
Память |
КОП MEMW |
КОП MEMB | |||
|
КОП обозначает: INC, DEC, NEG, NOT |
|
| |||
Команды вычитания. К командам вычитания относятся SUB, SBB, DEC, NEC и СМР. Первые три из них аналогичны трем командам сложения. Примеры команд SUB, SBB и СМР показаны в табл. 3.7, а команд DEC и NEG - в табл. 3.8.
Команда NEG изменяет знак своего операнда. Если, например, операнд содержит представление -1 (1111 1111), то команда NEG изменит его на +1(0000 0001).
Команда СМР аналогична команде вычитания, но результат не запоминается в приемнике. По существу, результат не запоминается нигде; он просто теряется в процессоре. Наверное, вы удивитесь: "Зачем нужна команда, результат которой теряется?" Оказывается, что состояние флажков, отражающих определенные свойства результата, важнее чем сам результат. Благодаря им можно определить отношение между двумя операндами, участвующими в вычитании. Если, например, ZF = 1, то результат равен нулю и значения операндов должны быть одинаковыми.
Состояния флажков для всех возможных отношений показаны в табл. 3.9. После команды СМР обычно находится команда условного перехода (см. далее), которая проверяет по флажкам, удовлетворяется ли конкретное отношение.
|
|
|
Таблица 3.9 Состояния флажков после выполнения команды CMP |
|
|
| |||||||||||
|
Отношение между приёмником и источником |
|
CF |
ZF |
SF |
OF |
| ||||||||||
|
|
Равны |
0 |
1 |
0 |
0 |
| ||||||||||
|
Знаковые операторы |
Меньше |
- |
0 |
1 |
0 |
| ||||||||||
|
|
Меньше |
- |
0 |
0 |
1 |
| ||||||||||
|
|
Больше |
- |
0 |
0 |
0 |
| ||||||||||
|
|
Больше |
- |
0 |
1 |
1 |
| ||||||||||
|
Беззнаковые операнды |
Ниже |
1 |
0 |
- |
- |
| ||||||||||
|
|
Выше |
0 |
0 |
- |
- |
| ||||||||||
|
Вместо прочерка может быть 0 или 1 в зависимость от значения операндов |
|
|
|
|
|
| ||||||||||
Команды умножения и деления. Умножение двух 8-битных чисел может дать произведение длиной до 16 бит. Пример умножения беззнаковых
чисел показан на рис. 3.9.
А
налогично
произведение двух 16-битных чисел может
иметь длину 32 бита. Команды умножения
процессора 80286 позволяют умножать 8- или
16-битную величину, находящуюся в
регистрахAL
или АХ, на операнд такого же размера,
определяемый в команде. Произведение
длиной 16 или 32 бита помещается в регистре
АХ я при необходимости в регистре DX,
как показано на рис. 3.10.
К
оманды
деления рассчитаны на противоположное
по отношению к умножению действие. Они
делят 16-битное число из регистра АХ (или
32-битное число из регистров АХ иDX)
на операнд половинного размера,
определяемый в команде. Частное помещается
в регистр AL
(АХ), а остаток - в АН (DX),
как показано на рис. 3.11.
В отличие от команд сложения и вычитания команды обычного двоичного умножения и деления, работающие с беззнаковыми числами, не дают правильных результатов для знаковых чисел (рис. 3.12). Следовательно, для знаковых чисел потребуются специальные команды умножения и деления. В процессоре 80286 имеются команды MUL (беззнаковое умножение), IMUL (знаковое умножение, которое иногда называется целочисленным
у
множением),DIV
(беззнаковое деление) и IDIV
(знаковое деление, которое иногда
называется целочисленным делением).
Примеры команд умножения и деления
приведены в табл. 3.10.

|
|
Таблица 3.10 Примеры команд умножение и деления |
| |||
|
Операнд |
Слова |
Байты | |||
|
AX (AL) с регистром |
КОП BX |
КОП CL | |||
|
AX (AL) с памятью |
КОП MEMW |
КОП MEMB | |||
|
КОП обозначает команды MUL, IMUL, DIV, IDIV |
|
| |||
Поскольку при программировании часто возникает необходимость умножения на константу, в команде IMUL (но не в командах MUL, DIV или IDIV) разрешается определять множитель как непосредственный операнд. При этом множимое не обязательно должно быть в регистре АХ (AL), a может находиться в любом 16-битном регистре или в 16-битной ячейке памяти. Результат длиной 16 бит (а не 32 бита) допускается разместить в любом 16-битном регистре. Примеры такой команды IMUL показаны в табл. 3.11. Команды умножения IMUL с непосредственным операндом в микропроцессоре 8086 нет. Такую команду удобно применять для вычисления адресов элементов массивов с нечетным размером.
|
|
Таблица 3.11 Примеры команды IMUL с непосредственным операндом |
| ||
|
|
IMUL DX, BX, 115 |
; BX*115 DX | ||
|
|
IMUL CX, MEMW, 632 |
; MEMW*632 CX | ||
Сделаем замечание о знаковом делении. Если разделить -26 на +7, можно получить частное -4 и остаток +2, но можно получить частное -3 и остаток -5. Любая пара чисел дает правильный результат, но в первом случае остаток положителен, а во втором - отрицателен. Команда знакового деления IDIV действует так, что остаток всегда имеет такой же знак, как и делимое. В приведенном примере будут получены частное -3 и остаток -5. Определенное таким образом деление дает частное (и остаток) с одним и тем же абсолютным значением при делении -26 на +7, +26 на +7 и +26 на -7.
В табл. 3.12 показано число бит операндов и результатов различных арифметических команд. Команды действуют так, чтобы результат двойной длины при умножении можно было использовать в последующем делении. А если вам нужно использовать результат умножения для чего-то еще, кроме деления? Как, например, умножить 17 (0001 0001) на 10 (0000 1010) и прибавить к произведению 20 (0001 0100)? Для этого нужно просто отбросить старшие 8 бит произведения. Однако теперь возникает проблема деления числа, которое не образовано предыдущим умножением. Попробуйте, например, поделить 8-битное число 35 (0010 0011) на 7 (0000 0111). В команде деления предполагается, что 16-битное делимое находится в регистре АХ. Простое размещение 8-битного делимого в регистре AL не дает правильного результата, так как в делении будет участвовать "мусор", находящийся в регистре АН. Однако трудность преодолевается, если очистить регистр АН перед 8-битным делением или регистр DX перед 16-битным делением.
|
|
Таблица 3.12 Размеры операндов и результатов |
|
| |||
|
Операция |
Первый операнд |
Второй операнд |
Результат | |||
|
СЛОЖЕНИЕ |
8 (слагаемое) |
8 (слагаемое) |
8 (сумма) | |||
|
|
16 (слагаемое) |
16 (слагаемое) |
16 (сумма) | |||
|
ВЫЧИТАНИЕ |
8 (уменьшаемое) |
8 (вычитаемое) |
8 (разность) | |||
|
|
16 (уменьшаемое) |
16 (вычитаемое) |
16 (разность) | |||
|
УМНОЖЕНИЕ |
8 (множимое) |
8 (множитель) |
16 (произведение) | |||
|
|
16 (множимое) |
16 (множитель) |
32 (произведение) | |||
|
ДЕЛЕНИЕ |
16 (делимое) |
8 (делитель) |
8 (частное) | |||
|
|
|
|
8 (остаток) | |||
|
|
32 (делимое) |
16 (делитель) |
16 (частное) | |||
|
|
|
|
16 (остаток) | |||
С
брос
старшей половины делимого двойной длины
хорошо действует для беззнакового
деления, а как быть со знаковым делением?
Преобразование 8-битного числа -2
(1111 1110) в 16-битное (1111 1111 1111 1110) связано с
установкой 8 старших бит в 1, а преобразование
8-битного числа +3 (0000 0011) в 16-битное (0000
0000 0000 0011) - с установкой 8 старших бит в
0. Правило преобразования довольно
простое: нужно продублировать левый
бит (иногда называемый знаковым) 8-битного
числа в каждом разряде старшей половины
16-битного числа. Напомним, что "удлинение"
чисел путем дублирования знакового
бита называется расширением со знаком.
Для этой операции предусмотрены
специальные команды. Сначала они
называлисьSEX
(Sign
Extend),
а затем их переименовали в более умеренные
по звучанию команды CBW
(преобразовать байт в слово) и CWD
(преобразовать слово в двойное слово).
Команда CBW
расширяет знаковый бит регистра AL
во все биты регистра АН, а команда CWD
расширяет знаковый бит регистра АХ во
все биты регистра DX.
На рис. 3.13 показано, как осуществить
деление 8- и 16-битных операндов.
Десятичная арифметика.До сих пор мы рассматривали арифметические операции над двоичными числами. Компьютеры работают с двоичными числами, но для людей более привычны десятичные. Мы живем в "десятичном мире", и если бы всевышний захотел, чтобы мы работали с двоичными числами, мы бы рождались всего с двумя пальцами. Поэтому первая проблема, с которой мы сталкиваемся при общении с компьютером, заключается в преобразовании входных чисел на язык компьютеров и в обратном преобразовании результатов. Конечно, на эти преобразования расходуется время. Но гораздо хуже то, что компьютер решает задачи не так, как мы, а это может приводить к удивительным результатам. Например, нас привело бы в замешательство то, что показания компьютеризованного спидометра в автомобиле сбрасывались бы после 131 072 миль (а не 99 999) только потому, что 131 072 является степенью числа два.
Почему же большинство компьютеров "мыслят по двоичным законам"? Только потому, что они работают всего с двумя уровнями напряжения (0 и 1) и должны представлять числа в двоичной системе счисления. Конечно, нулями и единицами можно закодировать каждую десятичную цифру в отдельности. Например, вместо представления десятичного числа 37 его двоичным эквивалентом 0010 0101 можно взять двоичные коды 3 (0011) и 7 (0111), что дает представление 0011 0111. Такое двоичное изображение десятичных цифр называется двоично-десятичным кодированием (BCD). В табл. 3.13 показано кодирование всех десятичных цифр. Применение в компьютерах двоичных чисел вместо двоично-десятичных объясняется тем, что двоичное представление компактнее. Например, число 125 можно представить в 8 битах как двоичное число 01111101, но для BCD-кода потребуется 12 бит - 0001 0010 0101.
|
|
|
|
|
|
Таблица 3.13 BCD-кодирование десятичных чисел |
|
|
|
|
|
| ||||||||||
|
Цифра |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | |||||||||||
|
Код |
0000 |
0001 |
0010 |
0011 |
0100 |
0101 |
0110 |
0111 |
1000 |
1001 | |||||||||||
Как же выполнять арифметические операции с BCD-числами? Можно ли их складывать, вычитать, умножать и делить? Для этого в системе команд компьютера наряду (или вместо) с командами двоичных операций потребовалось бы ввести команды BCD-сложения, BCD-вычитания, BCD-умножения и BCD-деления. Но можно как второй вариант применить команды двоичной арифметики к BCD-числам, заранее зная о неправильном результате, а затем выполнить специальную команду коррекции, которая сформирует правильный BCD-результат. Именно такой вариант выбран в процессоре 80286.
Рассмотрим сложение BCD-чисел 23 и 14 с помощью команды 8-битного двоичного сложения. Будет получено:
+0010 0011 = 23
0001 0100 =14
0011 0111 = 37
Здесь двоичное сложение дает правильный BCD-результат! Коррекции не требуется. Попробуем теперь сложить 29 и 14:
+0010 1001 = 29
0001 0100 =14
0011 1101 =3?
Ответ неверен, так как код 1101 не соответствует никакой десятичной цифре. В четырех битах можно представить 16 различных цифр, но десятичных цифр всего десять. Поэтому сложение двух цифр, когда сумма превышает 9, ведет в запрещенный диапазон и дает неправильный результат. Коррекция заключается в том, чтобы прибавить 6 к сумме в тех разрядах, где получена запрещенная комбинация, компенсируя этим 6 запрещенных комбинации. В предыдущем примере сумма корректируется так:
+0011 1101 = 3?
0110 = 06
0100 0011 = 43
Получен правильный результат 43. Здесь переход в запрещенный диапазон обнаруживается очень просто. Более сложная ситуация возникает, когда сумма "проскакивает" запрещенный диапазон и становится допустимой цифрой. Рассмотрим, например, сложение BCD-чисел 29 и 18:
+0010 1001 = 29
0001 1000 = 18
0100 0001 = 41
Результат оказался неверным, так как младшая цифра суммы "проскочила" запрещенный диапазон, поэтому ее нужно скорректировать, прибавляя 6. Однако необходимость такой коррекции по самому результату установить невозможно. Признаком "проскакивания" цифрой запрещенного диапазона служит перенос из соответствующего бита (разряда). В приведенном примере им будет перенос из младшего (десятичного) разряда в старший. Следовательно, результат можно скорректировать, если знать переносы из десятичных разрядов. Флажок переноса CF показывает, что при сложении возник перенос из старшего бита (и, следовательно, из старшего десятичного разряда). Флажок вспомогательного переноса AF предназначен только для регистрации переноса из младшего десятичного разряда, зная который можно осуществить BCD-коррекцию. В приведенном выше примере после двоичного сложения CF = 0 и AF = 1.
Арифметические операции можно выполнять и над многоразрядными BCD-числами. Покажем сложение чисел 2889 и 3714, которое реализуется двумя операциями сложения и коррекции.1. Суммируются младшие пары цифр:
+1000 1001 = 89
0001 0100 = 14
1001 1101 = 9? CF = 0,AF = 0
2. Выполняется коррекция:
+1001 1101 =9?
0110 = коррекция
+1010 0011 = ?3
0110 = коррекция
0000 0011 = 03 CF=1, AF =0
3. Суммируются старшие пары цифр с учетом флажка CF:
1(последнее значение CF)
+0010 1000 =28
0011 0111 =37
0110 0000 =60 CF = 0, AF=1
4. Производится коррекция:
01100000 = 60
0110 = коррекция
0110 0110 = 66
5. Окончательный результат равен 0110 0110 0000 0011 = 6603
Десятичную коррекцию осуществляет команда DAA (десятичная коррекция для сложения), в которой предполагается, что сумма находится в регистре AL. С учетом содержимого регистра AL и состояний флажков AF и CF команда DAA определяет необходимость коррекции и реализует ее для регистра AL. Аналогичная команда DAS (десятичная коррекция для вычитания) корректирует результат после операции вычитания. Для умножения применить коррекцию невозможно, так как в BCD-результате "замешаны" перекрестные члены произведения. По аналогичной причине невозможно скорректировать результат деления. Следовательно, при необходимости умножения и деления десятичных чисел потребуется перейти к другому представлению десятичных чисел, на котором мы и остановимся.
Рассмотренный BCD-формат точнее назвать упакованным десятичным форматом, так как в байте упакованы две цифры. В неупакованном формате байт содержит всего одну десятичную цифру. Она находится в четырех младших битах, а старшие биты не влияют на значение цифры. Примером неупакованного десятичного формата служит код ASCII, в котором символы представляются семью битами. Кодирование цифр показано в табл. 3.14. Здесь четыре старших бита содержат 0011, но эта комбинация на значение цифры не влияет.
|
|
Таблица 3.14 Представление цифр в коде ASII |
|
| |||
|
Цифра |
Код ASII |
Цифра |
Код ASII | |||
|
0 |
0011 0000 |
5 |
0011 0101 | |||
|
1 |
0011 0001 |
6 |
0011 0110 | |||
|
2 |
0011 0010 |
7 |
0011 0111 | |||
|
3 |
0011 0011 |
8 |
0011 1000 | |||
|
4 |
0011 0100 |
9 |
0011 1001 | |||
Результат двоичного сложения и вычитания ASCII-чисел можно скорректировать аналогично BCD-коррекции, причем корректируется только младшая цифра. Команды, осуществляющие коррекцию, называются командами ASCII-коррекции: ААА (ASCII-коррекция для сложения), AAS (ASCII-коррекция для вычитания), ААМ (ASCII-коррекция для умножения) и AAD (ASCII-коррекция для деления).
Как пример ASCII-умножения, рассмотрим умножение числа 9 на 4. Предположим, что число 9 (0000 1001) находится в регистре BL, а число 4 (0000 0100) - в регистре AL. Команда беззнакового двоичного умножения с множителем BL (т.е. команда MUL AL, BL) образует в регистре АХ 16-бит-ное произведение, равное 36 (0000 0000 0010 0100). Команда коррекции ААМ должна "разложить" результат на 3 (0000 0011) в регистре АН и на 6 (0000 0110) в регистре AL. Для этого нужно просто разделить содержимое AL на десять и поместить частное в регистр АН, а остаток в AL. Оказывается, не случайно команда ААМ имеет длину два байта (ведь было бы достаточно одного байта), причем второй байт - это двоичное представление десяти (1010). По существу, команда ААМ является разновидностью команд деления (хотя она и не помещает частное и остаток в те же регистры, что и команды DIV и IDIV), в которой второй байт команды содержит делитель. Мы не удивимся, если изменение второго байта с 10 (0000 1010) на 7 (0000 0111) приведет к команде "деления на 7", хотя фирма Intel этого не обещает. Но можно предположить также, что при размещении во втором байте числа 16 (0001 0000) реализуется преобразование упакованного BCD-числа из регистра AL в неупакованное число в регистрах АН и AL.
В рассмотренном примере операнды 0000 1001 и 0000 0100 были неупакованными числами с нулями в четырех старших битах. Если бы в них были не нули, при умножении появились бы перекрестные члены, которые скрыли бы нужный результат 0010 0100 (именно такие члены делают невозможной коррекцию BCD-умножения). Поэтому перед умножением неупакованных чисел нужно сбросить четыре старших бита каждого операнда, если в исходном состоянии они не содержат нулей. Для сброса выбираемых бит в байте удобно воспользоваться командой AND (см. далее).
Мы рассмотрели умножение одноразрядных неупакованных десятичных чисел, а теперь попробуем умножить многоразрядное число на одну цифру, например 539 на 6. В школе нас учили умножать следующим образом:
девять, умноженное на 6, дает 54. Записываем 4 и переносим 5 ("четыре пишем, пять в уме"). Три умножаем на 6, получаем 18, прибавляем 5 и имеем 23. Записываем 3 и переносим 2. Пять умножаем на 6, получаем 30, прибавляем 2 и окончательно записываем 32. Процесс умножения выглядит примерно так:
25 (переносы)
x539
6
3234
Теперь посмотрим, как эту задачу решает процессор 80286. Предположим, что неупакованное число 539 хранится в переменных а3, а2 и a1, a число 6 - в переменной b. Будем полагать, что в старших четырех битах а3, а2, a1 и b находятся нули. Мы хотим умножить значения а3, а2, a1 на значение переменной b и поместить результат в переменные с4, с3, c2, c1:
Xa3a2a1
b
c4c3c2c1
П
рограмма
такого умножения состоит из следующих
шагов:
О
тметим
в программе наличие команд сложения и
соответствующих команд коррекции ААА.
Рассмотрим одну из команд ААА подробнее.
Когда команда ААА в строке 8 превращает
неверное число в регистреAL
(0000 1101) в правильное 3 (0000 0011), возникает
перенос из младшей цифры регистра AL.
Этот перенос не подается в старшую цифру
регистра AL,
а направляется в младшую цифру регистра
АН, корректируя содержимое АН с 1
(0000 0001) на 2 (0000 0010). Следовательно, команда
ААА осуществляет коррекцию содержимого
не только регистра AL,
а обоих регистров АН и AL.
Такое действие команды ААА было бы
ненужным, если бы она применялась только
для сложения, а не умножения.


Более компактный алгоритм с циклом для многоразрядного неупакованного умножения показан на рис. 3.14. Хотя мы рассмотрели только одноразрядные множители, обобщение алгоритма на многоразрядные множители не вызывает серьезных трудностей.
Обратимся теперь к делению неупакованных чисел, например разделим 42 на 6. Будем считать, что 42 находится в регистре АХ (0000 0100 - в АН, 0000 0010 в AL), а 6 (0000 0110) - в BL. Неупакованное представление одноразрядного числа, например 6, является и его двоичным представлением. Следовательно, делимое 42 нужно преобразовать в двоичное число. Для этого содержимое АН умножается на 10 и прибавляется k содержимому регистра AL. Тогда двоичное деление содержимого регистра AL (двоичного числа 42) на содержимое регистра BL (6) образует в AL двоичное представление 7, которое совпадает с неупакованным представлением 7; на этом неупакованное деление закончено.
Подчеркнем в рассмотренном примере три важных момента. Во-первых, коррекция деления (AAD) заключается в умножении содержимого, регистра АН на 10 и прибавлении к содержимому регистра AL (не случайно второй байт команды AAD содержит число 10). Во-вторых, коррекция деления предшествует операции деления, а в сложении, вычитании и умножении коррекция производится после соответствующей арифметической операции. Другими словами, коррекция сложения, вычитания и умножения исправляет "плохой" результат, а коррекция деления предотвращает получение "плохого" результата. В-третьих, делимое и делитель должны иметь нули в старших четырех битах. Это же требование относится и к умножению, но не обязательно для сложения и вычитания.
Многоразрядное делимое можно разделить на одноразрядный делитель примерно так же, как это было сделано для умножения (см. алгоритм на рис. 3.14). К сожалению, этот метод не допускает обобщения на многоразрядный делитель. Можно рекомендовать алгоритм деления, в котором делается предположение о частном, затем с помощью умножения и вычитания неупакованных чисел проверяется правильность предположения и оно последовательно уточняется. Примерно так мы осуществляем деление длинных чисел на бумаге. Однако имеются и более совершенные алгоритмы деления.
ЛЕКЦИЯ 5. ЛОГИЧЕСКИЕ КОМАНДЫ
Логические команды процессора 80286 состоят из булевых команд и команд сдвигов /циклических сдвигов (см. табл. 3.15).
|
Таблица 3. 15 Логические команды |
| ||
|
Мнемоника команды |
Описание команды | ||
|
|
ПРИЁМНИК ИСТОЧНИК ПРИЁМНИК | ||
|
|
ПРИЁМНИК ПРИЁМНИК ? | ||
|
|
ПРИЁМНИК ИСТОЧНИК ПРИЁМНИК | ||
|
|
ПРИЁМНИК ИСТОЧНИК ПРИЁМНИК | ||
|
|
ПРИЁМНИК ПРИЁМНИК |
| |
|
|
CF ПРИЁМНИК 0 |
| |
|
|
0 ПРИЁМНИК CF |
| |
|
SAL (сдвиг арифметический влево) |
Аналогично SHL |
| |
|
|
Знак ПРИЁМНИК CF |
| |
|
ROL (циклический сдвиг влево) |
|
| |
|
ROR (циклический сдвиг вправо) |
|
| |
|
RCL (циклический сдвиг влево через перенос) |
|
| |
|
RCR (циклический сдвиг вправо через перенос) |
|
| |
Булевы команды. К булевым относятся команды NOT, AND, OR, XOR и TEST. Примеры их были приведены в табл. 3.7 и 3.8.
Команды AND, OR и XOR выполняют логическую функцию над соответствующими битами источника и приемника, помещая результат в приемник. Команда NOT имеет всего один операнд; она выполняет инверсию каждого его бита и помещает результат в то же место. Логические функции, реализуемые рассматриваемыми командами, определены в табл. 3.16.
|
|
|
Таблица 3. 16 Определение логических функций |
|
| |||||||
|
|
|
Однооперандная |
|
| |||||||
|
Бит источника |
NOT |
|
|
| |||||||
|
0 |
1 |
|
|
| |||||||
|
1 |
0 |
|
|
| |||||||
|
|
Двухоперандные |
|
|
| |||||||
|
Бит источника |
Бит приёмника |
AND |
OR |
XOR | |||||||
|
0 |
0 |
0 |
0 |
0 | |||||||
|
0 |
1 |
0 |
1 |
1 | |||||||
|
1 |
0 |
0 |
1 |
1 | |||||||
|
1 |
1 |
1 |
1 |
0 | |||||||
Функцию AND (И) удобно применять для сброса (маскирования) указанных разрядов в числе; один операнд определяет разряды, а второй - само число. Например, можно сбросить старшие четыре бита в 8-битном числе, объединяя его по И с набором 0000 1111. (Напомним, что сброс этих бит требуется до выполнения десятичного умножения и деления.)
Функции OR (ИЛИ) и XOR (исключающее ИЛИ) применяются для установки в 1 и инвертирования указанных разрядов в числе. Например, для установки в 1 старшего бита 8-битного числа следует объединить его по ИЛИ с набором 1000 0000, а для инвертирования средних четырех бит 8-битного числа нужно объединить его по исключающему ИЛИ с набором ООП 1100. Команда XOR позволяет также сбросить содержимое регистра в нуль (регистр должен быть и источником, и приемником). Функция NOT (НЕ) инвертирует все биты числа; она эквивалентна команде XOR с операндом-источником 1111 1111.
Команда TEST объединяет возможности команд AND и СМР. Как команда AND, она выполняет объединение по И соответствующих бит операндов; как команда СМР, она сохраняет только состояния флажков, а не результат. Она удобна для проверки того, есть ли в указанных разрядах числа хотя бы одна 1. Здесь один операнд определяет разряды, а второй - число. Если (уничтожаемый) результат не равен 0, о чем сигнализирует флажок ZF = 0, то, по крайней мере, один из указанных разрядов содержит 1. Например, для проверки наличия 1 в четырех младших битах регистра BL следует поместить 0000 1111 в регистр ВН, выполнить команду TEST BL, ВН, а затем воспользоваться командой условного перехода, которая передает управление, если ZF = 0. Конечно, вместо команды TEST можно применить команду AND, но при этом операнд-приемник будет уничтожен.
Команды сдвигов. Команды сдвигов являются эффективным средством увеличения и уменьшения числа вдвое (меньше байт и тактов, чем в умножении и делении). Для удвоения беззнакового числа нужно сдвинуть все биты на один разряд влево, а в освобождающийся правый бит поместить 0. Если выдвинутый слева бит передать во флажок CF, то можно зафиксировать выход за диапазон, проверив условие CF - 1. Например, удвоение числа 65 (0100 0001) с помощью сдвига влево дает 130 (1000 0010) и CF = 0 (в диапазоне), а сдвиг влево числа 130 (1000 0010) дает 4 (0000 0100) и CF = 1 (вне диапазона). Аналогично уменьшение беззнакового числа вдвое осуществляется сдвигом всех бит на один разряд вправо, в освобождающийся бит помещается 0, а выдвигаемый справа бит передается во флажок CF. В этом случае CF = 1 показывает, что число было нечетным. Например, сдвиг вправо числа 9 (0000 1001) дает 4 (0000 0100) и CF = 1.
С беззнаковыми числами работают команды SHL (сдвиг влево) и SHR (сдвиг вправо), а команды SAL (арифметический сдвиг влево) и SAR (арифметический сдвиг вправо) предназначены для знаковых чисел.
Различие между уменьшением вдвое знакового числа (SAR) и беззна кового числа (SHR) заключается в том, что в первом случае левый бит (знак) изменяться не должен. Например, уменьшение вдвое числа +6 (0000 0110) должно дать +3 (0000 0011), а числа -120 (1000 1000) - дать результат -60 (1100 0100). Следовательно, команда SAR сдвигает все биты на один разряд вправо, но сохраняет знаковый бит неизменным; команда же SHR помещает в знаковый бит 0.
Отметим, что результатом команды SAR с операндом +5 (0000 0101) будет число +2 (0000 0010), а с операндом -5 (1111 1011) - число -3 (1111 1101). Сдвиг вправо нечетного числа всегда дает результат, который меньше половины числа (-3 < -21/2). К сожалению, это число не совпадает с результатом команды DIV; деление числа -5 на 2 с использованием DIV дает-2.
Различий между удвоением знакового и беззнакового чисел нет. Поэтому мнемоники SHL и SAL относятся к одной и той же команде.
Команды циклического сдвига позволяют переставить биты в числе. Команды ROL (циклический сдвиг влево) и ROR (циклический сдвиг вправо) обеспечивают циклический сдвиг влево и вправо: выдвигающийся бит подается в освобождающийся бит. В команде RCL (циклический сдвиг влево через перенос) и RCR (циклический сдвиг вправо через перенос) в кольцо сдвига включается флажок CF: выдвигающийся бит подается во флажок CF, а состояние флажка CF передается в освобождающийся бит.
Операнд команд сдвигов может находиться в памяти или в регистре; длина операнда равна 8 или 16 бит. Сдвиг осуществляется на предопределенное число бит (фиксированный сдвиг) или на любое число бит (переменный сдвиг). В первом случае число сдвигов определяется в команде, а во втором - содержимым регистра CL, называемого счетчиком (вот еще один пример специализации одного из регистров общего назначения). Примеры команд сдвигов показаны в табл. 3.17. (В микропроцессоре 8086 фиксированный сдвиг осуществляется только на один бит.)
|
|
Таблица 3. 17 Примеры команд сдвигов |
| ||
|
|
Фиксированный сдвиг |
| ||
|
Операнд |
Слово |
Байт | ||
|
Регистр |
КОП BX, 13 |
КОП DL, 1 | ||
|
Память |
КОП MEMW, 1 |
КОП MEMB, 7 | ||
|
|
Переменный сдвиг |
| ||
|
Операнд |
Слово |
Байт | ||
|
Регистр |
КОП AX, CL |
КОП BL, CL | ||
|
Память |
КОП MEMW, CL |
КОП MEMB, CL | ||

ADD
(сложить)
ADC
(сложить с переносом)
INC
(инкремент)
SUB
(вычесть)
SBB
вычесть с заёмом)
DEC(декремент)
NEG(изменить знак)
CMP
(сравнить)
MUL
(умножить)
DIV
(разделить)


Слово
в регистре
Слово
в памяти
AND
TEST
OR
XOR
NOT
SHL
(сдвиг
логический влево)
SHR
(сдвиг
логический вправо)
SAR
(сдвиг
арифметический вправо)