Вывод на экран шестнадцатеричных чисел

From AsIsWiki
Revision as of 12:01, 4 April 2015 by Alex (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Contents

Введение

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


Флаги нуля, знака и переполнения

В последней теме был рассмотрен флаг переноса CF. В распечатке регистров он представлен значениями NC или CY. Кроме CF листинг регистров отображает значения еще семи флагов:

NV UP EI PL NZ NA PO NC

В ближайшее время нам потребуются три флага:

ZF  SF  OF

Эти флаги хранят состояние, или статус последней арифметической операции.

Все флаги однобитовые, и могут находится в двух состояниях:

1 - установлен
0 - сброшен

ZF (Zero Flag) - флаг нуля.
Флаг устанавливается, если результат операции равен нулю, иначе флаг сбрасывается:

   ┌─> ZR (Zero) - ноль
ZF │
   └─> NZ (Not Zero) - не ноль

2h - 2h = 0000h (ZR)
2h + 2h = 0004h (NZ)
2h - 3h = FFFFh (NZ)

SF (Sign Flag) - флаг знака.
Флаг устанавливается, если результат операции меньше нуля, иначе флаг сбрасывается:

   ┌─> NG (Negative) - минус
SF │
   └─> PL (Plus) - плюс

0003h - 0009h = FFFAh    (-6h) (NG)
0003h + 0007h = 000Ah          (PL)
7000h + 2000h = 9000h (-7000h) (NG)

OF (Over Flow) - флаг переполнения.
Флаг устанавливается, если в результате операции превышена граница чисел со знаком 8000h, иначе флаг сбрасывается:

   ┌─> OV (Overflow) - переполнение
OF │
   └─> NV (Not Overflow) - нет переполнения

7000h + 2000h = 9000h (-7000h) (OV)
6000h + 1000h = 7000h          (NV)
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     	MOV     AH,02    загружаем функцию печати символа
0ABD:0102 B22A     	MOV     DL,2A    загружаем код символа "*"
0ABD:0104 B109     	MOV     CL,09    устанавливаем счетчик повторений
0ABD:0106 CD21     ┌─>	INT     21       печатаем символ
0ABD:0108 80E901   │ 	SUB     CL,01    уменьшаем CL на 1
0ABD:010B 75F9     └─	JNZ     0106     если ZF = NZ, то переход на адрес 106
0ABD:010D CD20     	INT     20

Программа содержит условный переход на адрес 106. Перед инструкцией JNZ выполняется уменьшение регистра CL на 1. Когда CL достигнет нуля, произойдет установка флага нуля, и инструкция JNZ передаст управление прерыванию INT 20h.

Используя трассировку программы, проанализируйте действия инструкции JNZ.

Задачи:

  1. В последней программе замените "*" на знак "=", и увеличьте строку до 20 символов.
  2. В последней программе замените инструкцию JNZ на LOOP.
  3. В программе вывода двоичных чисел замените 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     	MOV     BL,0E    исходное число
0ABD:0102 B402     	MOV     AH,02    загружаем функцию печати символа
0ABD:0104 88DA     	MOV     DL,BL    копируем исходное число в DL
0ABD:0106 80C230   	ADD     DL,30    формируем код символа
0ABD:0109 80FA3A   	CMP     DL,3A    сравниваем число из DL и 3Ah
0ABD:010C 7C03     ┌─	JL      0111     если DL < 3A, то переход на адрес 111
0ABD:010E 80C207   │ 	ADD     DL,07    корректируем код для чисел A ... F
0ABD:0111 CD21     └─>	INT     21       печатаем символ
0ABD:0113 CD20     	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. Все восемь бит числа сдвигаем вправо, через флаг переноса:

Image27.gif

Число, после сдвига на четыре бита вправо (в освободившиеся ячейки регистра DL загружаются нули):

Image28.gif

Программная реализация сдвига состоит из двух строк:

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 не принимает участия в программе, его задача - сохранить копию числа для вывода на экран младшей цифры.

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


Логическая операция AND

В предыдущем разделе мы освоили печать старшей цифры двузначного шестнадцатеричного числа. Остается освоить печать младшей цифры и объединить две программы в одну.

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

Рассмотрим все варианты логического умножения чисел 1 и 0:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

Аналогично выглядит логическое умножение восьмиразрядных двоичных чисел:

    10110101           10110110
AND 01110110       AND 00100101
    00110100           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

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

Объединив два фрагмента кода, мы получим программу печати шестнадцатеричного байта:

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".

Задачи:

  1. Измените программу печати шестнадцатеричного байта так, чтобы в конце числа добавлялся символ "h", например: B9h
  2. Напишите программу печати шестнадцатеричного слова из регистра CS. Формат вывода: CS=0ABD. Запишите программу на диск.
  3. Напишите программу печати содержимого регистровой пары SS:SP. Формат вывода: 0ABD:FFEE. Запишите программу на диск.



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools