Пример простой программы

В данном разделе рассмотрим пример простой, но полноценной программы на ас­семблере. В главе 2 при обсуждении архитектуры было перечислено большое ко­личество регистров. Как правило, большинство из них задействовано практически в любой программе. Было бы интересно во время работы программы посмотреть (получить) их содержимое. Это нетрудно, если использовать специальную про­грамму — отладчик. Но как сделать это динамически и автономно (без помощи других программ)? Или, к примеру, как решить обратную задачу — ввести некото­рое значение с клавиатуры в регистр? Эта и подобная ей задачи являются задача­ми преобразования данных. Они довольно часто возникают на практике.Причина здесь в том, что компьютер воспринимает данные только тех типов, которые под­держиваются его системой команд. Поэтому на практике часто возникает необхо­димость преобразования данных из одного представления в другое. В этой главе в качестве примера рассмотрим частный случай решения одной из таких задач — задачи преобразования шестнадцатеричного числа из двух цифр, вводимого с кла­виатуры (то есть в символьном виде), в двоичное число. После выполнения этой операции полученное число можно использовать как операнд в двоичных ариф­метических операциях.

Для начала нужно продумать алгоритм и изучить предметную область. Ввод информации с клавиатуры и вывод се на экран осуществляются в символьном виде. Кодирование этой информации производится согласно таблице ASCII. В таблице ASCII каждый символ кодируется одним байтом. Если работа происходит с чис­лами, то при попытке их обработать сразу возникает проблема: команд для арифметическойобработки чисел в символьном виде нет. Что делать? Выход очевиден: нужно преобразовать символьную информацию в формат, поддерживаемый ма­шинными командами. После такого преобразования нужно выполнить необходимые вычисления и преобразовать результат обратно в символьный вид. Затем сле­дует отобразить информацию на мониторе.

Рассматриваемая в этом разделе программа является одним из вариантов решения задачи. В основу ее алгоритма положена особенность, связанная с ACSII-кодами символов соответствующих шестнадцатеричных цифр. Шестнадцатеричные цифрыформируются из символов 0,1,...,9,А,В,С, D, E, F, а, Ь, с, d,e,f, например: 12Af, 34ad. В таблице ASCII можно найти значения ASCII-кодов, соответствую­щие этим символам. На первый взгляд, непонятна популярность способапредставленияинформации в виде шестнадцатеричных чисел. Из предыдущих глав известно, что аппаратура компьютера построена на логических микросхемах и работает только с двоичной информацией. Если нам требуется проанализировать, например, состояние некоторой области памяти, то разобраться в нагромождении нулей и единиц будет очень непросто.

Для примера рассмотрим двоичную последовательность 010100010101011110101101110101010101000101001010.

Это представление не очень наглядно. В главе 2 отмечалось, что оперативная память состоит из ячеек — байтов — по 8 битов. Приведенная выше цепочка битов при разбиении ее на байты будет выглядеть так:

01010001 01010111 10101101 11010101 01010001 01001010.

С наглядностью стало лучше, но если исследуемая область памяти окажется больше, то разобраться будет все равно сложно. Проведем еще одну операцию: каж­дый байт разобьем на две части по 4 бита — тетрады:

0101 0001 0101 0111 1010 1101 1101 0101 0101 0001 0100 1010.

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

Если заменить в последней полученной строке тетрады соответствующими шестнадцатеричными цифрами, то получим последовательность 51 57 ad d5 51 4а.

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

Вернемся к нашей задаче. Если посмотреть внимательно на представление шестнадцатеричных цифр и их кодировку в АCSII-коде (табл. 6.1), то можно уви- деть полезные закономерности. К примеру, ASCII-код нуля (код символа 0) в ше- стнадцатеричном виде равен 30h. В двоичном представлении 30h записывается как 0011 0000, а соответствующее двоичное число должно быть двоичным представле­нием нуля 0000 0000. Отсюда следуют некоторые выводы.

Вывод первый. Для шестнадцатеричных цифр 0...9 ACSlI-код отличается от со­ответствующего двоичного представления на 00110000, или 30h. Поэтому для пре­образования кода символа шестнадцатеричной цифры в соответствующее двоич­ное число есть два пути:

- выполнить двоичное вычитание: (ACSII-код)h- 30h;

- обнулить старшую тетраду байта с символом шестнадцатеричной цифры в ACSII-коде.

Видно, что с шестнадцатеричными цифрами в диапазоне от 0 до 9 все просто. Что касается шестнадцатеричных цифр а, b, с, d, e, f, то здесь ситуация несколько сложнее. Алгоритм должен распознавать эти символы и производить дополнитель­ные действия при их преобразовании в соответствующее двоичное число. Внима­тельный взгляд на символы шестнадцатеричных цифр и соответствующие им двоичные представления (см. табл. 6.1) говорит о том, для преобразования уже не­достаточно простого вычитания или обнуления старшей тетрады. Анализ таблицы ASCII показывает, что символы прописных букв шестнадцатеричных цифр отли­чаются от своего двоичного эквивалента на величину 37h. Соответствующие строч­ные буквы шестнадцатеричных цифр отличаются от своего двоичного эквивален­та на 67h.

Отсюда следует вывод второй. Алгоритм преобразования должен различать прописные и строчные буквенные символы шестнадцатеричных цифр и корректи­ровать значение ASCII-кода на величину 37h или 67h.

Обратите внимание на то, что после записи значения шестнадцатеричной циф­ры следует символ «h». Как упоминалось в главе 4, это сделано для того, чтобы транслятор мог различить в программе одинаковые по форме записи десятичные и шестнадцатеричные числа.


В листинге 6.1 приведен исходный текст программы, которая решает задачу преобразования двузначного шестнадцатеричного числа в символьном виде в дво­ичное

представление.

В предыдущих главах говорилось, что процессор аппаратно поддерживает шесть адресно-независимых областей памяти: сегмент кода, сегмент данных, сегмент стека и три дополнительных сегмента данных. Наша программа использует только пер­вые три из них.

Строки 9-11 определяют сегмент данных. В строке 10 описана текстовая строка с сообщением «Введите две шестнадцатеричные цифры».

Строки 12-14 описывают сегмент стека, который является просто областью памяти длиной 256 байт, инициализированной символами «"?"*. Отличие сегмен та стека от сегментов других типов состоит в использовании и адресации памяти. В отличие от сегмента данных (наличие которого необязательно, если программ не работает с данными), сегмент стека желательно определять всегда.

Строки 15-44 содержат сегмент кода. В этом сегменте и строках 16-43 определена одна процедура main.

Строка 17 содержит директиву ассемблера, которая связывает сегментные регистры с именами сегментов.

Строки 18-19 выполняют инициализацию сегментного регистра DS.

Строки 20-22 выводят на экран сообщение message:

Введите две шестнадцатеричные цифры

Строка 23 подготавливает регистр АХ к работе, обнуляя его. Содержимое АХ после этой операции следующее: ах = 0000 0000 0000 0000

Строки 24-25 обращаются к средствам операционной системы для ввода символа с клавиатуры. Введенный символ операционная система помещает в регистр AL.

К примеру, пусть в ответ на сообщение с клавиатуры были введены две шестнадцатеричные цифры:

В результате после отработки команды в строке 25 появится ОДИН символ в ASCII-коде — 5, и состояние регистра АХ станет таким:

ах = 0000 0001 0011 0101

Строка 26 пересылает содержимое AL в регистр DL. Это делается для того, чтобы освободить AL для ввода второй цифры. Содержимое регистра DX после этой пере­сылки следующее: dx= 0000 0000 0011 0101

Строка 27 преобразует символьную цифру 5 в ее двоичный эквивалент пу­тем вычитания 30h, в результате чего в регистре DL будет двоичное значение числа 5: dx = 0000 0000 0000 0101

В строках 28-29 выясняется, нужно ли корректировать двоичное значение в DL. Если оно лежит в диапазоне 0...9, то в DLнаходится правильный двоичный эквива­лент введенного символа шестнадцатеричной цифры. Если значение в DL больше 9, то введенная цифра является одним из символов А, В, С, D, E, F(строчные буквы для экономии места обрабатывать не будем). В первом случае строка 29 передаст управление на метку Ml. При обработке цифры 5 это условие как paз выполняется, поэтому происходит переход на метку Ml (строка 31).

Каждая шестнадцатеричная цифра занимает одну тетраду. У нас две таких циф­ры, поэтому нужно их разместить так, чтобы старшинство разрядов сохранялось. В строках 32-33 значение в DL сдвигается на 4 разряда влево, освобождая место в младшей тетраде под младшую шестнадцатеричную цифру.

В строке 34 в регистр AL вводится вторая шестнадцатеричная цифра С (ее ASCII-код - 63h):

ах = 0000 0001 0100 0011

В строках 35-37 выясняется, попадает ли двоичный эквивалент второго сим­вола шестнадцатеричной цифры в диапазон 0...9. Наша вторая цифра не попадает в диапазон, поэтому для получения правильного двоичного эквивалента нужно произвести дополнительную корректировку. Это делается в строке 38. Состояние AL после выполнения строки 35 следующее:

ах = 0000 0001 0001 0011

В AL записано число 13h, а нужно, чтобы было 0Сh (помните о правилах записи шестнадцатеричных чисел!). Так как 0Сh не попадает в диапазон 0...9, то происхо­дит переход на строку 38. В результате работы команды вычитания в регистре AL получается правильное значение (al) - 0Сh:

аx = 0000 0001 0000 1100

И наконец, в строке 40 сдвинутое значение в DL складывается с числом в AL:

dx= 0000 0000 0101 0000

+

ax = 0000 0001 0000 1100

=

dx= 0000 0000 0101 1100

Таким образом, в регистре DL получен двоичный эквивалент двух введенных символов, изображающих двузначное шестнадцатеричное число:

(DL) - 05Ch

Строки -11 -42 предназначены для завершения программы и возврата управления операционной системе.

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: