Флаги нуля, знака и переполнения
Инструкции условных переходов
Вывод на экран шестнадцатеричной цифры
Логический сдвиг числа
Логическая операция AND
В четвертой теме мы рассмотрели алгоритм вывода на экран двоичных чисел. Потребность в двоичном числовом формате возникает крайне редко. Но метод, используемый для вывода двоичных чисел, можно использовать и с другими числовыми форматами.
Флаги нуля, знака и переполнения
В последней теме был рассмотрен флаг переноса CF. В распечатке регистров он представлен значениями NC или CY. Кроме CF листинг регистров отображает значения еще семи флагов:
NV UP EI PL NZ NA PO NC
В ближайшее время нам потребуются три флага: ZF, SF, OF. Эти флаги хранят состояние, или статус последней арифметической операции.
Все флаги однобитовые, и могут находится в двух состояниях: "1" - установлен; "0" - сброшен.
-
ZF (Zero Flag - флаг нуля): устанавливается, если результат операции равен нулю, иначе - сбрасывается.
| ZR (Zero) - ноль 2h - 2h = 0000h (ZR)
ZF | 2h + 2h = 0004h (NZ)
| NZ (Not Zero) - не ноль 2h - 3h = FFFFh (NZ)
- SF (Sign Flag - флаг знака): устанавливается, если результат операции меньше нуля, иначе - сбрасывается.
| NG (Negative) - минус 0003h - 0009h = FFFAh (-6h) (NG)
SF | 0003h + 0007h = 000Ah (PL)
| PL (Plus) - плюс 7000h + 2000h = 9000h (-7000h) (NG)
- OF (Over Flow - флаг переполнения): устанавливается, если в результате операции превышена граница чисел со знаком 8000h, иначе - сбрасывается.
| OV (Overflow) 7000h + 2000h = 9000h (-7000h) (OV)
OF | 6000h + 1000h = 7000h (NV)
| NV (Not Overflow) 7000h - 8000h = F000h (-1000h) (OV)
Придумайте несколько примеров для установки и сброса флагов: ZF, SF, OF.
Инструкции условных переходов
Очень часто в программе требуется выполнять переход на несколько инструкций вперед или назад. Например, программа печати двоичных чисел содержит инструкцию LOOP, которая 8 раз возвращает управление на 4 шага назад. Но существуют и другие варианты переходов:
- Инструкция JZ (Jump if Zero - перейти, если ноль) выполяет переход на заданный адрес, если результат последней арифметической операции был ноль (ZF = ZR).
- Инструкция JNZ (Jump if Not Zero), выполняет переход на заданный адрес, если флаг нуля сброшен (ZF = NZ).
Например, используем инструкцию JNZ для вывода на экран строки из девяти звездочек:
0ABD:0100 B402
0ABD:0102 B22A
0ABD:0104 B109
0ABD:0106 CD21
0ABD:0108 80E901
0ABD:010B 75F9
0ABD:010D CD20
|
|
MOV AH,02 загружаем функцию печати символа
MOV DL,2A загружаем код символа "*"
MOV CL,09 устанавливаем счетчик повторений
INT 21 печатаем символ
SUB CL,01 уменьшаем CL на 1
JNZ 0106 если ZF = NZ, то переход на адрес 106
INT 20
|
Программа содержит условный переход на адрес 106. Перед инструкцией JNZ выполняется
уменьшение регистра CL на 1. Когда CL достигнет нуля, произойдет установка флага нуля, и
инструкция JNZ передаст управление прерыванию INT 20h.
Используя трассировку программы, проанализируйте действия инструкции JNZ.
Задачи:
- В последней программе замените "*" на знак "=", и увеличьте строку до 20 символов.
- В последней программе замените инструкцию JNZ на LOOP.
- В программе вывода двоичных чисел замените LOOP на JNZ.
Вывод на экран шестнадцатеричной цифры
Символ | ASCII код |
0 | 30h |
1 | 31h |
2 | 32h |
3 | 33h |
4 | 34h |
5 | 35h |
6 | 36h |
7 | 37h |
8 | 38h |
9 | 39h |
: | 3Ah |
; | 3Bh |
< | 3Ch |
= | 3Dh |
> | 3Eh |
? | 3Fh |
@ | 40h |
A | 41h |
B | 42h |
C | 43h |
D | 44h |
E | 45h |
F | 46h |
Ранее мы рассмотрели алгоритм вывода на экран цифр 0 и 1, где код символа формировался сложением соответствующей цифры с числом 30h. Аналогичный метод можно применить для вывода на экран шестнадцатеричных чисел.
Из таблицы ASCII кодов видно, что коды символов "0" ... "9" можно получить сложением 30h с соответствующим числом: 30h + 9h = 39h (код символа "9").
Коды символов "A" ... "F" формируются аналогично, только вместо числа 30h используется число 37h: 37h + Dh = 44h (код символа "D").
Для вывода на экран шестнадцатеричных чисел, нам потребуются две новые инструкции:
- CMP A,B (Compare - сравнение) сравнивает числа A и B. Если A = B, то устанавливается флаг нуля (ZF = ZR).
- JL (Jump if Less Than - перейти если меньше) проверяет результат сравнения чисел в инструкции CMP.
Если A < B, то выполняется переход на заданный адрес.
Следующая программа выводит на экран одноразрядное шестнадцатеричное число из регистра BL:
0ABD:0100 B30E
0ABD:0102 B402
0ABD:0104 88DA
0ABD:0106 80C230
0ABD:0109 80FA3A
0ABD:010C 7C03
0ABD:010E 80C207
0ABD:0111 CD21
0ABD:0113 CD20
|
|
MOV BL,0E исходное число
MOV AH,02 загружаем функцию печати символа
MOV DL,BL копируем исходное число в DL
ADD DL,30 формируем код символа
CMP DL,3A сравниваем число из DL и 3Ah
JL 0111 если DL < 3A, то переход на адрес 111
ADD DL,07 корректируем код для чисел A...F
INT 21 печатаем символ
INT 20
|
В программе выполняется проверка кода символа:
- если код меньше чем 3Ah (диапазон символов "0" ... "9"), то переходим к печати символа;
- иначе, корректируем код (смещаемся в диапазон "A" ... "F"), и печатаем символ.
Загружая в регистр BL числа от 0 до F, выполните трассировку программы. Посмотрите, как изменяет флаги инструкция CMP. Проанализируйте действия инструкции JL.
Логический сдвиг числа
Последняя программа корректно работает с числами: 0 ... F. Например, загрузите в BL число 3Eh и выполните программу. Вместо ожидаемого результата "3E", вы получите букву "u".
Изменим программу так, чтобы она печатала любые числа от 0 до FFh. Для этого нам надо научиться выделять старшую и младшую цифры двузначного шестнадцатеричного числа.
Каждая цифра шестнадцатеричного числа занимает четыре бита. Для выделения четырех бит старшей цифры мы используем инструкцию SHR:
- SHR (Shift Right - сдвиг вправо) логический сдвиг числа вправо, через флаг переноса. После прохождения флага переноса, биты не возвращаются в регистр, как в инструкции RCL, а безвозвратно теряются.
Например, необходимо выделить старшую цифру числа B9h:
Исходное число загружаем в регистр DL. Все восемь бит числа сдвигаем вправо, через флаг переноса.
Число, после сдвига на четыре бита вправо: (в освободившиеся ячейки регистра DL загружаются нули)
Программная реализация сдвига состоит из двух строк:
MOV CL,4 сдвиг на 4 бита
SHR DL,CL число в регистре DL сдвигается вправо на 4 бита
К сожалению Debug не позволяет указывать шаг сдвига непосредственно в инструкции, например: SHR DL,4 или RCL DL,4. Этот формат мы используем позже, при написании компилируемых ассемблерных программ.
Добавим логический сдвиг в программу печати шестнадцатеричной цифры:
0ABD:0100 B3B9 MOV BL,B9 загружаем исходное число
0ABD:0102 88DA MOV DL,BL копируем исходное число в DL
0ABD:0104 B402 MOV AH,02 загружаем функцию печати символа
0ABD:0106 B104 MOV CL,04 загружаем шаг сдвига
0ABD:0108 D2EA SHR DL,CL сдвигаем число в DL на CL бит вправо
0ABD:010A 80C230 ADD DL,30 формируем код символа
0ABD:010D 80FA3A CMP DL,3A определяем диапазон символа
0ABD:0110 7C03 JL 0115 если диапазон "0"..."9", то переход
0ABD:0112 80C207 ADD DL,07 иначе, корректируем код
0ABD:0115 CD21 INT 21 печатаем символ
0ABD:0117 CD20 INT 20
Регистр BL не принимает участия в программе, его задача - сохранить копию числа для вывода на экран младшей цифры.
Выполните программу c разными исходными числами. Убедитесь, что на экран выводится старшая цифра шестнадцатеричного числа.
Логическая операция AND
В предыдущем разделе мы освоили печать старшей цифры двузначного шестнадцатеричного числа. Остается освоить печать младшей цифры и объединить две программы в одну.
Для выделения четырех бит младшей цифры двузначного шестнадцатеричного числа, нужно обнулить четыре бита старшей цифры. Для обнуления заданной последовательности бит, мы используем инструкцию AND (логическое умножение двух чисел).
Рассмотрим все варианты логического умножения чисел 1 и 0:
0 AND 1 = 0 0 AND 0 = 0 1 AND 0 = 0 1 AND 1 = 1
Аналогично выглядит логическое умножение восьмиразрядных двоичных чисел:
10110101 10110110
AND 01110110 AND 00100101
00110111 00100100
Логическое умножение байта на маску 0Fh, позволяет обнулить старшую половину байта:
10111001 B9h
AND 00001111 <=> AND 0Fh
00001001 09h
Используем это свойство для вывода на экран младшей цифры шестнадцатеричного числа:
0ABD:0100 B3B9 MOV BL,B9 загружаем исходное число
0ABD:0102 88DA MOV DL,BL копируем исходное число в DL
0ABD:0104 B402 MOV AH,02 загружаем функцию печати символа
0ABD:0106 80E20F AND DL,0F обнуляем старшую половину байта
0ABD:0109 80C230 ADD DL,30 формируем код символа
0ABD:010C 80FA3A CMP DL,3A определяем диапазон символа
0ABD:010F 7C03 JL 0114 если диапазон "0"..."9", то переход
0ABD:0111 80C207 ADD DL,07 иначе, корректируем код
0ABD:0114 CD21 INT 21 печатаем символ
0ABD:0116 CD20 INT 20
Выполните программу c разными исходными числами. Убедитесь, что на экран выводится младшая цифра шестнадцатеричного числа.
Объединив два фрагмента кода, мы получим программу печати шестнадцатеричного байта:
0ABD:0100 B402 MOV AH,02
0ABD:0102 88DA MOV DL,BL
0ABD:0104 B104 MOV CL,04
0ABD:0106 D2EA SHR DL,CL
0ABD:0108 80C230 ADD DL,30
0ABD:010B 80FA3A CMP DL,3A
0ABD:010E 7C03 JL 0113
0ABD:0110 80C207 ADD DL,07
0ABD:0113 CD21 INT 21
0ABD:0115 88DA MOV DL,BL
0ABD:0117 80E20F AND DL,0F
0ABD:011A 80C230 ADD DL,30
0ABD:011D 80FA3A CMP DL,3A
0ABD:0120 7C03 JL 0125
0ABD:0122 80C207 ADD DL,07
0ABD:0125 CD21 INT 21
0ABD:0127 CD20 INT 20
В приведенном листинге нет загрузки исходного числа в регистр BL. Это делает программу более универсальной. Для ввода чисел в регистр используйте команду "R".
Задачи:
- Измените программу печати шестнадцатеричного байта так,
чтобы в конце числа добавлялся символ "h". Например: B9h.
- Напишите программу печати шестнадцатеричного слова из регистра CS.
Формат вывода: CS=0ABD. Запишите программу на диск.
- Напишите программу печати содержимого регистровой пары SS:SP.
Формат вывода: 0ABD:FFEE. Запишите программу на диск.
|