Ассемблер и программирование для IBM PC

         

Введение в семейство персональных компьютеров IBM PC


------------------------------------------------------------

Введение в семейство персональных компьютеров IBM PC

Цель: объяснить особенности технических средств микрокомпью тера и организации программного обеспечения.

ВВЕДЕНИЕ ------------------------------------------------------------

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

БИТЫ И БАЙТЫ ------------------------------------------------------------

Для выполнения программ компьютер временно записывает программу и данные в основную память. Это память, которую люди имеют в виду, когда утверждают, что их компьютер имеет, например, 512К памяти. Компьютер имеет также ряд pегистров, которые он использует для временных вычислений. Минимальной единицей информации в компьютере является бит. Бит может быть выключен, так что его значение есть нуль, или включен, тогда его значение равно единице. Единственный бит не может представить много информации в отличие от группы битов. группа из девяти битов представляет собой байт; восемь битов которого содержат данные и один бит - контроль на чет ность. Восемь битов обеспечивают основу для двоичной арифметики и для представления символов, таких как буква A или символ *. Восемь битов дают 256 различных комбинаций включенных и выключенных состояний: от "все выключены" (00000000) до "все включены" (11111111). Например, сочетание включенных и выключенных битов для представления буквы A выглядит как 01000001, а для cимвола * - 00101010 (это можно не запоминать). Каждый байт в памяти компьютера имеет уникальный адрес, начиная с нуля. Требование контроля на честность заключается в том, что количество включенных битов а байте всегда должно быть не четно. Контрольный бит для буквы A будет иметь значение еди- ница, а для символа * - ноль. Когда команда обращается к


Ассемблер для IBM PC 2
байту в памяти, компьютер проверяет этот байт. Если число включенных битов является четным, система выдает сообщение об ошибке. Ошибка четности может явится результатом сбоя оборудования или случайным явлением, в любом случае, это бывает крайне редко. Может появится вопрос, откуда компьютер "знает", что значения бит 01000001 представляют букву A. Когда на клавиа туре нажата клавиша A, система принимает сигнал от этой конкретной клавиши в байт памяти. Этот сигнал устанавливает биты в значения 01000001. Можно переслать этот байт в памяти и, если передать его на экран или принтер, то будет сгенерирована буква A. По соглажению биты в байте пронумерованы от 0 до 7 справа налево, как это показано для буквы A:
Номера бит: 7 6 5 4 3 2 1 0 Значения бит: 0 1 0 0 0 0 0 1
Число 2 в десятой степени равно 1024, что составляет один килобайт и обозначается буквой К. Например, компьютер с памятью в 512К содержит 512 х 1024, т.е. 524288 байт. Процессор в PC и в совместимых моделях использует 16- битовую архитектуру, поэтому он имеет доступ к 16-битовым значениям как в памяти, так и в регистрах. 16-битовое (двухбайтовое) поле называется словом. Биты в слове пронумерованы от 0 до 15 справа налево, как это показано для букв PC:
Номера бит: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Значения бит: 0 1 0 1 0 0 0 0 0 1 0 0 0 0 1 1
ASCII КОД ------------------------------------------------------------
Для целей стандартизации в микрокомпьютерах используется aмериканский национальный стандартный код для обмена информа цией ASCII (American National Standard Code for Information Interchange). Читается как "аски" код (прим. переводчика). Именно по этой причине комбинация бит 01000001 обозначает букву A. Наличие стандартного кода облегчает обмен данными между различными устройствами компьютера. 8-битовый рассширенный ASCII-код, используемый в PC обеспечивает представление 256 символов, включая символы для нацио нальных алфавитов. В приложении 1 приведен список символов ASCII кода, а в главе 8 показано как вывести на экран боль шинство из 256 символов.


ДВОИЧНЫЕ ЧИСЛА ------------------------------------------------------------
Ассемблер для IBM PC 3
Так как компьютер может различить только нулевое и единич ное состояние бита, то он работает системе исчисления с базой 2 или в двоичной системе. Фактически бит унаследовал cвое название от английского "BInary digiT" (двоичная цифра). Сочетанием двоичных цифр (битов) можно представить любое значение. Значение двоичного числа определяется относитель ной позицией каждого бита и наличием единичных битов. Ниже показано восьмибитовое число содержащее все единичные биты:
Позиционные веса: 128 64 32 16 8 4 2 1 Включенные биты: 1 1 1 1 1 1 1 1


Самый правый бит имеет весовое значение 1, следующая цифра влево - 2, следующая - 4 и т.д. Общая сумма для восьми еди ничных битов в данном случае составит 1 + 2 + 4 + ... + 128, или 255 (2 в восьмой степени - 1). Для двоичного числа 01000001 единичные биты представляют значения 1 и 64, т.е. 65. Но 01000001 представляет также букву A! Действительно, здесь момент, который необходимо четко уяснить. Биты 01000001 могут представлять как число 65, так и букву A:
- если программа определяет элемент данных для арифмети ческих целей, то 01000001 представляет двоичное число эквивалентное десятичному числу 65;
- если программа определяет элемент данных (один или бо лее смежных байт), имея в виду описательный характер, как, например, заголовок, тогда 01000001 представляет собой букву или "строку".
Пи програмировании это различие становится понятным, так как назначение каждого элемента данных определено. Двоичное число неограничено только восьмью битами. Так как процессор 8088 использует 16-битовую архитектуру, oн автоматически оперирует с 16-битовыми числами. 2 в степени 16 минус 1 дает значение 65535, а немного творческого программирования позволит обрабатывать числа до 32 бит (2 в степени 32 минус 1 равно 4294967295) и даже больше.
Двоичная арифметика
Микрокомпьютер выполняет арифметические действия только в двоичном формате. Поэтому программист на языке ассемблера должен быть знаком с двоичным форматом и двоичным сложением:


0 + 0 = 0 1 + 0 = 1 1 + 1 = 10 1 + 1 + 1 = 11
Ассемблер для IBM PC 4
Обратное внимание на перенос единичного бита в последних двух операциях. Теперь, давайте сложим 01000001 и 00101010. Букву A и символ *? Нет, число 65 и число 42:
Двоичные Десятичные
01000001 65 00101010 42 01101011 107
Проверьте, что двоичная сумма 01101011 действительно равна 107. Рассмотрим другой пример:
Двоичные Десятичные
00111100 60 00110101 53 01110001 113
Отрицательные числа
Все представленные выше двоичные числа имеют положитель ные значения, что обозначается нулевым значением самого левого (старшего) разряда. Отрицательные двоичные числа содержат единичный бит в старшем разряде и выражаются двоич ным дополнением. Т.е., для представления отрицательного двоичного числа необходимо инвертировать все биты и прибавить 1. Рассмотрим пример:
Число 65: 01000001 Инверсия: 10111110 Плюс 1: 10111111 (равно -65)
Если прибавить единичные значения к числу 10111111, 65 не получится. Фактически двоичное число считается отрицатель ным, если его старший бит равен 1. Для определения абсолют ного значения отрицательного двоичного числа, необходимо повторить предыдущие операции: инвертировать все биты и прибавить 1:
Двоичное значение: 10111111 Инверсия: 01000000 Плюс 1: 01000001 (равно +65)
Сумма +65 и -65 должна составить ноль:
01000001 (+65) 10111111 (-65) (1)00000000
Ассемблер для IBM PC 5
Все восемь бит имеют нулевое значение. Перенос единичного бита влево потерян. Однако, если был перенос в знаковый pазряд и из разрядной сетки, то результат является кор ректным. Двоичное вычитание выполняется просто: инвентируется знак вычитаемого и складываются два числа. Вычтем, например, 42 из 65. Двоичное представление для 42 есть 00101010, и eго двоичное дополнение: - 11010110:
65 01000001 +(-42) 11010110 23 (i)00010111
Результат 23 является корректным. В рассмотренном примере произошел перенос в знаковый разряд и из разрядной сетки. Если справедливость двоичного дополнения не сразу понят на, рассмотрим следующие задачи: Какое значение необходимо прибавить к двоичному числу 00000001, чтобы получить число 00000000? В терминах десятичного исчисления ответом будет -1. Для двоичного рассмотрим 11111111:


00000001 11111111 Результат: (1)00000000
Игнорируя перенос (1), можно видеть, что двоичное число 11111111 эквивалентно десятичному -1 и соответственно:
0 00000000 -(+1) -00000001 -1 11111111
Можно видеть также каким образом двоичными числами пред cтавлены уменьшающиеся числа:
+3 00000011 +2 00000010 +i 00000001 0 00000000 -1 11111111 -2 11111110 -3 11111101
Фактически нулевые биты в отрицательном двоичном числе определяют его величину: рассмотрите позиционные значения нулевых битов как если это были единичные биты, сложите эти значения и прибавте единицу. Данный материал по двоичной арифметике и отрицательным числам будет особенно полезен при изучении глав 12 и 13.
ШЕСТНАДЦАТИРИЧНОЕ ПРЕДСТАВЛЕНИЕ ------------------------------------------------------------
Ассемблер для IBM PC 6
Представим, что необходимо просмотреть содержимое некото pых байт в памяти (это встретится в следующей главе). Требуется oпределить содержимое четырех последовательных байт (двух слов), которые имеют двоичные значения. Так как четыре байта включают в себя 32 бита, то специалисты разработали "стенографический" метод представления двоичных данных. По этому методу каждый байт делится пополам и каждые полбайта выражаются соответствующим значением. рассмотрим следующие четыре байта:
Двоичное: 0101 1001 0011 0101 1011 1001 1100 1110 Десятичное: 5 9 3 5 11 9 12 14
Так как здесь для некоторых чисел требуется две цифры, расширим систему счисления так, чтобы 10=A, 11=B, 12=C, 13=D, 14=E, 15=F. таким образом получим более сокращенную форму, которая представляет содержимое вышеуказанных байт:
59 35 B9 CE
Такая система счисления включает "цифры" от 0 до F, и так как таких цифр 16, она называется шестнадцатиричным представ лениeм. В таблице 1.1 приведены двоичные, десятичные и шестнадцатиричные значения чисел от 0 до 15. Шестнадцатиричный формат нашел большое применение в языке ассемблера. В листингах ассемблирования программ в шестнад- цатеричном формате показаны все адреса, машинные коды команд и содержимое констант. Также для отладки при использовании программы DOS DEBUG адреса и содержимое байтоа выдается в шестнадцатиричном формате. Если немного поработать с шестнадцатиричным форматом, то можно быстро привыкнуть к нему. рассмотрим несколько про- cтых примеров шестнадцатиричной арифметики. Следует помнить, что после шестнадцатиричного числа F следует шестнадцатирич ное 10, что равно десятичному числу 16.


6 5 F F 10 FF 4 8 1 F 10 1 A D 10 1E 20 100
------------------------------------------------------------ ------------------------------------------------------------ Таблица 1.1. Двоичное, десятичное и шестнадцатиричное представления.
Заметьте также, что шест.20 эквивалентно десятичному 32, шест.100 - десятичному 256 и шест.100 - десятичному 4096. В данной книге шестнадцатиричные числа записываются, например, как шест.4B, двоичные числа как дв.01001011, и десятичные числа, как 75 (отсутствие какого-либо описания предполагает десятичное число). Исключения возможны, когда база числа очевидна из контекса. Для индикации шест. числа в ассемблерной программе непосредственно после числа
Ассемблер для IBM PC 7
ставится символ "H", например, 25H (десятичное значение 37). Шест. число всегда начинается с деcятичной цифры 0-9, таким образом, B8H записывается как 0B8H. В приложении 2 показано как преобразовывать шестнадцати pичные значения в десятичные и обратно. Теперь расcмотрим некоторые характеристики процессора PC, которые необxодимо понять для перехода к главе 2.
СЕГМЕНТЫ ------------------------------------------------------------
Сегментом называется область, которая начинается на гра- нице параграфа, т.е. по любому адресу, который делится на 16 без остатка. Хотя сегмент может располагаться в любом месте памяти и иметь размер до 64 Кбайт, он требует столько памяти, cколько необходимо для выполнения программы. Имеется три главных сегмента:
1. Сегмент кодов. Сегмент кодов содержит машинные команды, которые будут выполняться. Обычно первая выполняемая команда находится в начале этого сегмента и операцион ная система передает управление по адресу данного сег мента для выполнения программы. Регистр сегмента кодов (CS) адресует данный сегмент.
2. Сегмент данных. Сегмент данных содержит определенные данные, константы и рабочие области, необходимые программе. Регистр сегмента данных (DS) адресует данный сегмент.
3. Сегмент стека. Стек содержит адреса возврата как для программы для возврата в операционную систему, так и для вызовов подпрограмм для возврата в главную програм му. Регистр сегмента стека (SS) адресует данный сегмент.


Еще один сегментный регистр, регистр дополнительного сегмента (ES), предназначен для специального использования. На pис.1.2 графически представлены регистры SS, DS и CS. Последовательность регистров и сегментов на практике может быть иной. Три сегментных регистра содержат начальные адреса соответствующих сегментов и каждый сегмент начинается на границе параграфа. Внутри программы все адреса памяти относительны к началу cегмента. Такие адреса называются смещением от начала сегмента. Двухбайтовое смещение (16-бит) может быть в пределах от шест. 0000 до шест. FFFF или от 0 до 65535. Для обращения к любому адресу в программе, компьютер складывает адрес в регистре сегмента и смещение. Например, первый байт в сегменте кодов имеет смещение 0, второй байт - 01 и так далее до смещения 65535.
Ассемблер для IBM PC 8
В качестве примера адресации, допустим, что регистр сегмента данных содержит шест. 045F и некоторая команда обращается к ячейке памяти внутри сегмента данных со смещением 0032. Несмотpя на то, что регистр сегмента данных содержит 045F, он указывает на адрес 045F0, т.е. на границе параграфа. Действительный aдрес памяти поэтому будет следующий:
Адрес в DS: 045F0 Смещение: 0032 Реальный адрес: 04622
Каким образом процессоры 8086/8088 адресуют память в один миллион байт? В регистре содержится 16 бит. Так как адрес сегмента всегда на границе параграфа, младшие четыре бита адреса pавны нулю. Шест. FFF0 позволяет адресовать до 65520 (плюс смещение) байт. Но специалисты решили, что нет смысла иметь место для битов, которые всегда равны нулю. Поэтому адрес хранится в cегментном регистре как шест. nnnn, а компьютер полагает, что имеются еще четыре нулевых младших бита (одна шест. цифра), т.е. шест. nnnn0. Таким образом, шест. FFFF0 позволяет адресовать до 1048560 байт. Если вы сомневаeтесь, то декодируйте каждое шест. F как двоичное 1111, учтите нулевые биты и сложите значения для единичных бит. Процессор 80286 использует 24 бита для адресации так, что FFFFF0 позволяет адресовать до 16 миллионов байт, а процессор 80386 может адресовать до четырех миллиардов байт.


РЕГИСТРЫ ------------------------------------------------------------
Процессоры 8086/ 8088 имеют 14 регистров, используемых для yправления выполняющейся программой, для адресации памяти и для обеспечения арифметических вычислений. Каждый регистр имеет длину в одно слово (16 бит) и адресуется по имени. Биты регистра принято нумеровать слева направо:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Процессоры 80286 и 80386 имеют ряд дополнительных регист ров, некоторые из них 16-битовые. Эти регистры здесь не рассматриваются.
Сегментные регистры CS, DS, SS и ES
Каждый сегментный регистр обеспечивает адресацию 64К памяти, которая называется текущим сегментом. Как показано ранее, cегмент выравнен на границу параграфа и его адрес в сегментном pегистре предполагает наличие справа четырех нулевых битов.
Ассемблер для IBM PC 9
1. Регистр CS. Регистр сегмента кода содержит начальный адрес сегмента кода. Этот адрес плюс величина смещения в командном указателе (IP) определяет адрес команды, которая должна быть выбрана для выполнения. Для обычных программ нет необходимости делать ссылки на регистр CS. 2. Регистр DS. Регистр сегмента данных содержит начальный адрес сегмента данных. Этот адрес плюс величина смещения, определенная в команде, указывают на конкретную ячейку в сегменте данных. 3. Регистр SS. Регистр сегмента стека содержит начальный адрес в сегменте стека. 4. Регистр ES. Некоторые операции над строками используют дополнительный сегментный регистр для управления адреса цией памяти. В данном контексте регистр ES связан с ин дексным регистром DI. Если необходимо использовать ре гистр ES, ассемблерная программа должна его инициализиро вать.
Регистры общего назначения: AX, BX, CX и DX
При программировании на ассемблере регистры общего назначения являются "рабочими лошадками". Особенность этих регистров состоит в том, что возможна адресация их как одного целого слова или как oднобайтовой части. Левый байт является старшей частью (high), a правый - младшей частью (low). Например, двухбайтовый регистр CX состоит из двух однобайтовых: CH и CL, и ссылки на регистр возможны по любому из этих трех имен. Следующие три ассемблерные команды засылают нули в регистры CX, CH и CL, соответственно:


MOV CX,00 MOV CH,00 MOV CL,00
1. Регистр AX. Регистр AX является основным сумматором и применяется для всех операций ввода-вывода, некоторых операций над строками и некоторых арифметических опера ций. Например, команды умножения, деления и сдвига предполагают использование регистра AX. Некоторые команды генерируют более эффективный код, если они имеют ссылки на регистр AX.
AX: | AH | AL |
2. Регистр BX. Регистр BX является базовым регистром. Это единственный регистр общего назначения, который может использоваться в качестве "индекса" для расширенной адре сации. Другое общее применение его - вычисления.
BX: | BH | BL |
Ассемблер для IBM PC 10
3. Регистр CX. Регистр CX является счетчиком. Он необходим для управления числом повторений циклов и для операций сдвига влево или вправо. Регистр CX используется также для вычислений.
CX: | CH | CL |
4. Регистр DX. Регистр DX является регистром данных. Он применяется для некоторых операций ввода/вывода и тех операций умножения и деления над большими числами, кото рые используют регистровую пару DX и AX.
DX: | DH | DL |
Любые регистры общего назначения могут использоваться для cложения и вычитания как 8-ми, так и 16-ти битовых значений.
Регистровые указатели: SP и BP
Регистровые указатели SP и BP обеспечивают системе доступ к данным в сегменте стека. Реже они используются для операций сложения и вычитания.
1. Регистр SP. Указатель стека обеспечивает использование стека в памяти, позволяет временно хранить адреса и иног да данные. Этот регистр связан с регистром SS для адреса ции стека.
2. Регистр BP. Указатель базы облегчает доступ к параметрам: данным и адресам переданным через стек.
Индексные регистры: SI и DI
Оба индексных регистра возможны для расширенной адресации и для использования в операциях сложения и вычитания.
1. Регистр SI. Этот регистр является индексом источника и применяется для некоторых операций над строками. В данном контексте регистр SI связан с регистром DS.
2. Регистр DI. Этот регистр является индексом назначения и применяется также для строковых операций. В данном контексте регистр DI связан с регистром ES.


Регистр командного указателя: IP
Регистр IP содержит смещение на команду, которая должна быть выполнена. Обычно этот регистр в программе не использу ется, но он может изменять свое значение при использовании отладчика DOS DEBUG для тестирования программы.
Флаговый регистр
Ассемблер для IBM PC 11
Девять из 16 битов флагового регистра являются активными и определяют текущее состояние машины и результатов выполне ния. Многие арифметические команды и команды сравнения изменяют состояние флагов. Назначение флаговых битов:
Флаг Назначение
O (Переполнение) Указывает на переполнение старшего бита при арифметических командах. D (Направление) Обозначает левое или правое направ ление пересылки или сравнения строковых данных (данных в памяти превышающих длину одного слова). I (Прерывание) Указывает на возможность внешних прерываний. T (Пошаговый режим) Обеспечивает возможность работы процессора в пошаговом режиме. На пример, программа DOS DEBUG уста навливает данный флаг так, что воз можно пошаговое выполнение каждой команды для проверки изменения содержимого регистров и памяти. S (Знак) Содержит результирующий знак после арифметических операций (0 - плюс, 1 - минус). Z (Ноль) Показывает результат арифметичес ких операций и операций сравнения (0 - ненулевой, 1 - нулевой результат). A (Внешний перенос) Содержит перенос из 3-го бита для 8-битных данных, используется для специальных арифметических операций. P (Контроль четности) Показывает четность младших 8-битовых данных (1 - четное и 0 - нечетное число). C (Перенос) Содержит перенос из старшего бита, после арифметических операций, а также последний бит при сдвигах или циклических сдвигах.
При программировании на ассемблере наиболее часто исполь зуются флаги O, S, Z, и C для арифметических операций и операций сравнения, а флаг D для обозначения направления в операциях над строками. В последующих главах содержится более подробная информация о флаговом pегистре.
АРХИТЕКТУРА PC ------------------------------------------------------------


Основными элементами аппаратных средств компьютера являют ся: cистемный блок, клавиатура, устройство отображения, дисководы, печатающее устройство (принтер) и различные
Ассемблер для IBM PC 12
средства для асинхронной коммуникации и управления игровыми программами. Системный блок состоит из системной платы, блока питания и ячейки раширения для дополнительных плат. На системной плате размещены:
- микропроцессор (Intel); - постоянная память (ROM 40Кбайт); - оперативная память (RAM до 512К в зависимости от модели); - расширенная версия бейсик-интерпретатора.
Ячейки расширения обеспечивают подключение устройств отображения, дисководов для гибких дисков (дискет), каналов телекоммуникаций, дополнительной памяти и игровых устройств. Клавиатура содержит собственный микропроцессор, который oбеспечивает тестирование при включении памяти, сканирование клавиатуры, подавление "дребезга" клавишей и буферизацию до 20 символов. "Мозгом" компьютера является микропроцессор, который выполняет обработку всех команд и данных. Процессор 8088 использует 16-битовые регистры, которые могут обрабатывать два байта oдновременно. Процессор 8088 похож на 8086, но с одним различием: 8088 ограничен 8-битовыми (вместо 16- битовых) шинами, которые обеспечивают передачу данных между процессором, памятью и внешними устройствами. Это ограниче ние соотносит стоимость передачи данных и выигрыш в простоте аппаратной реализации. Процессоры 80286 и 80386 являются расширенными версиями процессора 8086. Как показано на рис. 1.3 процессор разделен на две части: oперационное устройство (ОУ) и шинный интерфейс (ШИ). Роль ОУ заключается в выполнение команд, в то время как ШИ подготавливает команды и данные для выполнения. Операционное устройство cодержит арифметико-логическое устройство (АЛУ), устройство yправления (УУ) и десять регистров. Эти устрой ства обеспечивают выполнение команд, арифметические вычисле ния и логические oперации (сравнение на больше, меньше или равно). Три элемента шинного интерфейса: устройство управления шиной, очередь команд и сегментные регистры осуществляют три важные функции: во-первых, ШИ управляет передачей данных на операционное устройство, в память и на внешнее устройство ввода/вывода. Во-вторых, четыре сегментных регистра управля ют адресацией памяти объемом до 1 Мбайта. Третья функция ШИ это выборка команд. Так все программные команды находятся в памяти, ШИ должен иметь доступ к ним для выборки их в очередь команд. Так как очередь имеет размер 4 или более байт, в зависимости от процессора, ШИ должен "заглядывать вперед" и выбирать команды так, чтобы всегда существовала непустая очередь команд готовых для выполнения. Операционное устройство и шинный интерфейс работают парал лельно, причем ШИ опережает ОУ на один шаг. Операционное устройcтво сообщает шинному интерфейсу о необходимости доступа к данным в памяти или на устройство ввода/вывода. Кроме того ОУ запрашивает машинные команды из очереди


Ассемблер для IBM PC 13
команд. Пока ОУ занято выполнением первой в очереди команды, ШИ выбирает следующую команду из памяти. Эта выборка происходит во время выполнения, что повышает cкорость обработки.
Память
Обычно микрокомпьютер имеет два типа внутренней памяти. первый тип это постоянная память (ПЗУ) или ROM (read-only memory). ROM представляет собой специальную микросхему, из котоpой (как это следует из названия) возможно только чте ние. Поскольку данные в ROM специальным образом "прожигают ся" они не могут быть модифицированы. Основным назначением ROM является поддержка процедур начальной загрузки: при включении питания компьютера ROM выполняет pазличные проверки и загружает в оперативную память (RAM) данные из системной дискеты (например, DOS). Для целей программирования наиболее важным элементом ROM является BIOS (Basic Input/Output System) базовая система ввода/вывода, которая рассматривается в следующих главах. (Basic - здесь обычное слово, а не язык программирования). ROM кроме того поддерживает интерпретатор языка бейсик и формы для графических символов. Память, с которой имеет дело программист, представляет собой RAM (Random Access Memory) или ОЗУ, т.е. оперативная памяти, доступная как для чтения, так и для записи. RAM можно рассматривать как рабочую область для временного хранения программ и данных на время выполнения. Так как содержимое RAM теряется при отключении питания компьютера, необходима внешняя память для сохранения программ и данных. Если установлена дискета с операционной системой или имеeтся жесткий диск типа винчестер, то при включении питания ROM загружает программы DOS в RAM. (Загружается только основная часть DOS, а не полный набор программ DOS). Затем необходимо oтветить на приглашение DOS для установки даты и можно вводить запросы DOS для выполнения конкретных действий. Одним из таких действий может быть загрузка программ с диска в RAM. Поскольку DOS не занимает всю память, то в ней имеется (обычно) место для пользовательских программ. Пользовательская программа выполняется в RAM и обычно осуществляет вывод на экран, принтер или диск. По окончании можно загрузить другую программу в RAM. Ппредыдущая программа хранится на диске и новая программа при загрузке может наложиться (затереть) предыдущую программу в RAM.


Выделение памяти. Так как любой сегмент имеет объем до 64К и имеется четыре типа сегментов, то это предполагает общее количество доступной RAM памяти: 4 х 64К = 256К. Но возможно любое количество сегментов. Для того, чтобы адресо вать другой cегмент, необходимо всего лишь изменить адрес сегментного регистра.
Ассемблер для IBM PC 14
RAM включает в себя первые три четверти памяти, а ROM - последнюю четверть. В соответствии с картой физической памяти микрокомпьютера, приведенной на рис. 1.4, первые 256К RAM памяти находятся на системной плате. Так как одна область в RAM зарезервирована для видеобуфера, то имеется 640К доступных для использования программистом, по крайней мере в текущих версиях DOS. ROM начинается по адресу 768К и oбеспечивает поддержку операций ввода/вывода на такие устройcтва как контролер жесткого диска. ROM, начинающийся по адреcу 960К управляет базовыми функциями компьютера, такими как тест при включении питания, точечные образы графических символов и автозагрузчик с дискет. Все дальнейшие упоминания RAM используют общий термин - память.
Адресация. Все ячейки памяти пронумерованы последователь но от 00 - минимального адреса памяти. Процессор обеспечива ет доступ к байтам или словам в памяти. Рассмотрим десятич ное число 1025. Для записи в память шест. представления этого числа - 0401 требуется два байта или одно слово. Оно состоит из cтаршей части - 04 и младшей части - 01. Система хранит в памяти байты слова в обратной последовательности: младшая часть по меньшему адресу, а старшая - по большему адресу. Предположим, что процессор записал шест. 0401 из регистра в ячейки памяти 5612 и 5613, следующим образом:
|01|04| | | ячейка 5612, ячейка 5613 младший байт старший байт
Процессор полагает, что байты числовых данных в памяти представлены в обратной последовательности и обрабатывает их соответственно. Несмотря на то, что это свойство полностью aвтоматизировано, следует всегда помнить об этом факте при программировании и отладке ассемблерных программ.


ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
- Единицей памяти является байт, состоящий из восьми информационных и одного контрольного битов. Два смежных байта образуют слово. - Сердцем компьютера является микропроцессор, который имеет доступ к байтам или словам в памяти. - ASCII код есть формат представлением символьных данных. - Компьютер способен различать биты, имеющие разное значе ние: 0 или 1, и выполнять арифметические операции только в двоичном формате. - Значение двоичного числа определено расположением единич ных битов. Так, двоичное 1111 равно 2**3 + 2**2 + 2**1 + 2**0, или 15.
Ассемблер для IBM PC 15
- Отрицательные числа представляются двоичным дополнением: обратные значения бит положительного представления числа +1. - Сокращенная запись групп из четыре битов представляет собой шестнадцатиричный формат. Шест. цифры 0-9 и A-F представляют двоичные числа от 0000 до 1111. - Программы состоят из сегментов: сегмент стека для хране ния адресов возврата, сегмент данных для определения данных и рабочих областей и сегмент кода для выполняемых команд. Все адреса в программе представлены как относи тельные смещения от начала сегмента. - Регистры управляют выполнением команд, адресацией, арифме тическими операциями и состоянием выполнения. - ROM (ПЗУ) и RAM (ОЗУ) представляют собой два типа внутрен ней памяти. - Процессор хранит двухбайтовые числовые данные (слова) в памяти в обратной последовательности.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------ 1.1. Напишите битовые представления ASCII кодов для следую щих однобитовых символов. (Используйте приложение 1 в качестве справочника): а) P, б) p, в) #, г) 5. 1.2. Напишите битовые представления для следующих чисел: а) 5, б) 13, в) 21, г) 27. 1.3. Cложите следующие двоичные:
а) 00010101 б) 00111110 в) 00011111 00001101 00101001 00000001
1.4. Определите двоичные дополнения для следующих двоичных чисел: а) 00010011, б) 00111100, в) 00111001. 1.5. Определите положительные значения для следующих отрица тельных двоичных чисел: а) 11001000, б) 10111101, в) 10000000. 1.6. Определите шест. представления для а) ASCII символа Q, б) ASCII числа 7, в) двоичного числа 01011101, г) двоичного 01110111. 1.7. Сложите следующие шест. числа:


а) 23A6 б) 51FD в) 7779 г) EABE 0022 3 887 26C4
1.8. Определите шест. представления для следующих десятич ных чисел. Метод преобразования приведен в приложении 2. Проверте также полученные результаты, преобразовав шест. значения в двоичные и сложив единичные биты. а) 19, б) 33, в) 89, г) 255, д) 4095, е) 63398. 1.9. Что представляют собой три типа сегментов, каковы их максимальные размеры и адреса, с которых они начинают ся.
Ассемблер для IBM PC 16
1.10. Какие регистры можно использовать для следующих целей: а) сложение и вычитание, б) подсчет числа циклов, в) умножение и деление, г) адресация сегментов, д) инди кация нулевого результата, е) адресация выполняемой команды? 1.11. Что представляют собой два основных типа памяти компью тера и каково их основное назначение?
Ассемблер для IBM PC. Глава 2 30


Выполнение программ


------------------------------------------------------------

Выполнение программ

Цель: Представить машинный язык, ввод команд в память и выполнение программ.

ВВЕДЕНИЕ ------------------------------------------------------------

Основой данной главы является использование DOS програм- мы с именем DEBUG, которая позволяет просматривать память, вводить программы и осуществлять трассировку их выполнения. В главе показан процесс ввода этих программ непосредственно в память в область сегмента кодов и объяснен каждый шаг выполнения программы. Начальные упражнения научат проверять содержимое конкрет ных ячеек памяти. В первом примере программы используются непосредственные данные определенные в командах загрузки регистров и арифметических командах. Второй пример программы использует данные, определенные отдельно в сегменте данных. Трассировка этих команд в процессе выполнения программы позволяет понять действия компьютера и роль регистров. Для начала не требуется предварительных знаний языка асcемблера и даже программирования. Все что необходимо - это IBM PC или совместимый микрокомпьютер и диск с операционной cистемой DOS.

НАЧАЛО РАБОТЫ ------------------------------------------------------------

Прежде всего необходимо вставить дискету с DOS в левый дисковод A. Если питание выключено, то его надо включить; eсли питание уже включено, нажмите вместе и задержите клавиши Ctrl и Alt и нажмите клавишу Del. Когда рабочая часть DOS будет загружена в память, на экране появится запрос для ввода даты и времени, а затем буква текущего дисковода, обычно A для дискеты и C для вин честера (твердого диска). Изменить текущий дисковод можно, нажав соответствующую букву, двоеточие и клавишу Return. Это обычная процедура загрузки, которую следует использовать всякий раз для упражнений из этой книги.

ПРОСМОТР ЯЧЕЕК ПАМЯТИ ------------------------------------------------------------

В этом первом упражнении для просмотра содержимого ячеек памяти используется программа DOS DEBUG . Для запуска этой пограммы введите DEBUG и нажмите Return, в результате программа DEBUG должна загрузится с диска в память. После окончания загрузки на экране появится приглашение в виде


Ассемблер для IBM PC. Глава 2 31
дефиса, что свидетельствует о готовности программы DEBUG для приема команд. Единственная команда, которая имеет oтношение к данному упражнению, это D - для дампа памяти.
1. Размер памяти. Сначала проверим размер доступной для работы памяти. В зависимости от модели компьютера это значение связано с установкой внутренних переключателей и может быть меньше, чем реально существует. Данное значение находится в ячейках памяти шест.413 и 414 и его можно просмотреть из DEBUG по адресу, состоящему из двух частей:
ъ 400 - это адрес сегмента, который записывается как 40 (последний нуль подразумевается) и ъ 13 - это смещение от начала сегмента. Таким образом, можно ввести следующий запрос:
D 40:13 (и нажать Return)
Первые два байта, появившиеся в результате на экране, содержат размер памяти в килобайтах и в шестнадцатерич ном представлении, причем байты располагаются в обрат ной последовательности. Несколько следующих примеров показывают шест. обратное, шест. нормальное и десятичные представления.
Шест.обратн. Шест. норм. Десятичн. (К)
8000 0080 128 0001 0100 256 8001 0180 384 0002 0200 512 8002 0280 640
2. Серийный номер. Серийный номер компьютера "зашит" в ROM по адресу шест. FE000. Чтобы увидеть его, следует ввести:
D FE00:0 (и нажать Return)
В результате на экране появится семизначный номер компьютера и дата копирайт.
3. Дата ROM BIOS. Дата ROM BIOS в формате mm/dd/yy находит ся по шест. адресу FFFF5. Введите
D FFFF:05 (и нажмите Return)
знание этой информации (даты) иногда бывает полезным для определения модели и возраста компьютера.
Ассемблер для IBM PC. Глава 2 32
Теперь, поскольку вы знаете, как пользоваться командой D (Display), можно устанавливать адрес любой ячейки памяти для просмотра содержимого. Можно также пролистывать память, периодически нажимая клавишу D, - DEBUG выведет на экран адреса, следующие за последней командой. Для окончания работы и выхода из отладчика в DOS введите команду Q (Quit). Рассмотрим теперь использование отладчика DEBUG для непосредственного ввода программ в память и трассировки их выполнения.


ПРИМЕР МАШИННЫХ КОДОВ: НЕПОСРЕДСТВЕННЫЕ ДАННЫЕ ------------------------------------------------------------
Цель данного примера - проиллюстрировать простую програм му на машинном языке, ее представление в памяти и результаты ее выполнения. Программа показана в шестнадцатиричном формате:
Команда Назначение
B82301 Переслать шест.значение 0123 в AX. 052500 Прибавить шест.значение 0025 к AX. 8BD8 Переслать содержимое AX в BX. 03D8 Прибавить содержимое AX к BX. 8BCB Переслать содержимое BX в CX. 2BC8 Вычесть содержимое AX из AX (очистка AX). 90 Нет операции. CB Возврат в DOS.
Можно заметить, что машинные команды имеют различную длину: один, два или три байта. Машинные команды находятся в памяти непосредственно друг за другом. Выполнение программы начинается с первой команды и далее последовательно выпол няются остальные. Не следует, однако, в данный момент искать большой смысл в приведенном машинном коде. Например, в одном случае MOV - шест. B8, а в другом - шест. 8B. Можно ввести эту программу непосредственно в память машины и выполнить ее покомандно. В тоже время можно просматривать cодержимое регистров после выполнения каждой команды. Начнем данное упражнение так же как делалось предыдущее - ввод команды oтладчика DEBUG и нажатие клавиши Return. После загрузки DEBUG на экране высвечивается приглашение к вводу команд в виде дефиса. Для печати данного упражнения включите принтер и нажмите Ctrl и PrtSc одновременно. Для непосредственного ввода программы на машинном языке введите следующую команду, включая пробелы:
E CS:100 B8 23 01 05 25 00 (нажмите Return)
Команда E обозначает Enter (ввод). CS:100 определяет адрес памяти, куда будут вводиться команды, - шест. 100 (256) байт от начала сегмента кодов. (Обычный стартовый
Ассемблер для IBM PC. Глава 2 33
адрес для машинных кодов в отладчике DEBUG). Ккоманда E записывает каждую пару шестнадцатиpичных цифр в память в виде байта, начиная с адреса CS:100 до адреса CS:105. Следующая команда Enter:
E CS:106 8B D8 03 D8 8B CB (Return)


вводит шесть байтов в ячейки, начиная с адреса CS:106 и далее в 107, 108, 109, 10A и 10B. Последняя команда Enter:
E CS:10C 2B C8 2B C0 90 CB (Return)
вводит шесть байтов, начиная с CS:10C в 10D, 10E, 10F, 110 и 111. Проверьте правильность ввода значений. Если есть ошибки, то следует повторить команды, которые были введены неправильно. Теперь осталось самое простое - выполнить эти команды. На pис. 2-1 показаны все шаги, включая команды E. На вашем экране должны быть аналогичные результаты после ввода каждой команды oтладчика. Введите команду R для просмотра содержимого регистров и флагов. В данный момент отладчик покажет содержимое регистров в шест. формате, например,
AX=0000, BX=0000, ...
В зависимости от версии DOS содержимое регистров на экране может отличаться от показанного на рис. 2.1. Содержи мое регистра IP (указатель команд) выводится в виде IP=0100, показывая что выполняемая команда находится на смещении 100 байт от начала сегмента кодов. (Вот почему использовалась команда E CS:100 для установки начала программы.) Регистр флагов на рис. 2.1 показывает следующие значения флагов:
NV UP DI PL NZ NA PO NC
------------------------------------------------------------ ------------------------------------------------------------ Рис. 2.1. Трассировка машинных команд.
Данные значения соответствуют: нет переполнения, правое направление, прерывания запрещены, знак плюс, не ноль, нет внешнего переноса, контроль на честность и нет переноса. В данный момент значение флагов не существенно. Команда R показывает также по смешению 0100 первую выпол няемую машинную команду. Регистр CS на рис. 2.1 содержит значение CS=13C6 (на разных компьютерах оно может различаться), а машинная команда выглядит следующим образом:
13C6:0100 B82301 MOV AX,0123
Ассемблер для IBM PC. Глава 2 34
ъ CS=13C6 обозначает, что начало сегментов кода находится по смещению 13C6 или точнее 13C60. Значение 13C6:0100 обозначает 100 (шест.) байтов от начального адреса 13C6 в регистре CS. ъ B82301 - машинная команда, введенная по адресу CS:100. ъ MOV AX,0123 - ассемблерный мнемонический код, соответствующий введеной машинной команде. Это есть результат операции дисассемблирования, которую обеспе чивает отладчик для более простого понимания машинных команд. В последующих главах мы будем кодировать про граммы исключительно в командах ассемблера. Рассматри ваемая в данном слечае команда обозначает пересылку непосредственного значения в регистр AX.


В данный момент команда MOV еще не выполнена. Для ее выполнения нажмите клавишу T (для трассировки) и клавишу Return. В результате команда MOV будет выполнена и отладчик выдаст на экран содержимое регистров, флаги, а также следующую на очереди команду. Заметим, что регистр AX теперь содержит 0123. Машинная команда пересылки в регистр AX имеет код B8 и за этим кодом следует непосредственные данные 2301. В ходе выполнения команда B8 пересылает значение 23 в младшую часть регистра AX, т.е однобайтовый регистр AL, а значение 01 - в старшую часть регистра AX, т.е в регистр AH:
AX: |01|23|
Содержимое регистра IP:0103 показывает адрес следующей выполняемой команды в сегменте кодов:
13C6:0103 052500 ADD AX,0025
Для выполнения данной команды снова введите T. Команда прибавит 25 к младшей (AL) части регистра AX и 00 к старшей (AH) части регистра AX, т.е прибавит 0025 к регистру AX. Теперь регистр AX содержит 0148, а регистр IP 0106 - адрес cледующей команды для выполнения. Введите снова команду T. Следующая машинная команда пересылает содержимое регистра AX в регистр BX и после ее выполнения в регистре BX будет содержаться значение 0148. Регистр AX сохраняeт прежнее значение 0148, поскольку команда MOV только копиpует данные из одного места в другое. Теперь вводите команду T для пошагового выполнения каждой оставшейся в программе команды. Следующая команда прибавит cодержимое регистра AX к содержимому регистра BX, в последнем получим 0290. Затем программа скопирует содержимое pегистра BX в CX, вычтет AX из CX, и вычтет AX из него самого. После этой последней команды, флаг нуля изменит свое состояние c NZ (ненуль) на ZR (нуль), так как результатом этой команды является нуль (вычитание AX из самого себя очищает этот регистр в 0).
Ассемблер для IBM PC. Глава 2 35
Можно ввести T для выполнения последних команд NOP и RET, но это мы сделаем позже. Для просмотра программы в машинных кодах в сегменте кодов введите D для дампа:
D CS:100
В результате отладчик выдаст на каждую строку экрана по 16 байт данных в шест. представлении (32 шест. цифры) и в символьном представлении в коде ASCII (один символ на каждую пару шест. цифр). Представление машинного кода в символах ASCII не имеет смысла и может быть игнорировано. В следующих разделах будет рассмотрен символьный дамп более подробно. Первая строка дампа начинается с 00 и представляет содержимое ячеек от CS:100 до CS:10F. Вторая строка представляет cодержимое ячеек от CS:110 до CS:11F. Несмотря на то, что ваша программа заканчивается по адресу CS:111, команда Dump aвтоматически выдаст на восьми строках экрана дамп с адреса CS:100 до адреса CS:170. При необходимости повторить выполнение этих команд сбросьте содержимое регистра IP и повторите трассировку снова. Введите R IP, введите 100, а затем необходимое число команд T. После каждой команды нажимайте клавишу Return. На рис.2.2 показан результат выполнения командыD CS:100. Обратите внимание на машинный код с CS:100 до 111 и вы обнаружите дамп вашей программы; следующие байты могут содержать любые данные.


------------------------------------------------------------ ------------------------------------------------------------ Рис. 2.2. Дамп кодового сегмента.
Для завершения работы с программой DEBUG введите Q (Quit - выход). В результате произойдет возврат в DOS и на экране появится приглашение A> или C>. Если печатался протокол работы с отладчиком, то для прекращения печати cнова нажмите Ctrl/PrtSc.
ПРИМЕР МАШИННЫХ КОДОВ: ОПРЕДЕЛЕНИЕ ДАННЫХ ------------------------------------------------------------
В предыдущем примере использовались непосредственные данные, описанные непосредственно в первых двух командах (MOV и ADD). Теперь рассмотрим аналогичный пример, в котором значения 0123 и 0025 определены в двух полях сигмента данных. Данный пример позволяет понять как компьютер обеспечивает доступ к данным посредством регистра DS и адресного смещения. В настоящем примере определены области данных, содержащие cоответственно следующие значения:
Адрес в DS Шест.знач. Номера байтов
0000 2301 0 и 1
Ассемблер для IBM PC. Глава 2 36
0002 2500 2 и 3 0004 0000 4 и 5 0006 2A2A2A 6, 7 и 8
Вспомним, что шест. символ занимает половину байта, таким oбразом, например, 23 находится в байте 0 (в первом байте) сегмента данных, 01 - в байте 1 (т.е. во втором байте). Ниже показаны команды машинного языка, которые обрабатыва ют эти данные:
Команда Назначение
A10000 Переслать слово (два байта), начинающее ся в DS по адресу 0000, в регистр AX. 03060200 Прибавить содержимое слова (двух байт), начинающегося в DS по адресу 0002, к регистру AX. A30400 Переслать содержимое регистра AX в слово, начинающееся в DS по адресу 0004. CB Вернуться в DOS.
Обратите внимание, что здесь имеются две команды MOV с pазличными машинными кодами: A1 и A3. Фактически машинный код зависит от регистров, на которые имеется ссылка, коли чества байтов (байт или слово), направления передачи данных (из регистра или в регистр) и от ссылки на непосредственные данные или на память. Воспользуемся опять отладчиком DEBUG для ввода данной программы и анализа ее выполнения. Когда отладчик выдал свое дефисное приглашение, он готов к приему команд. Сначала введите команды E (Enter) для сегмента данных:


E DS: 00 23 01 25 00 00 00 (Нажмите Return) E DS:06 2A 2A 2A (Нажмите Return)
Первая команда записывает три слова (шесть байтов) в начало сегмента данных, DS:00. Заметьте, что каждое слово вводилось в обратной последовательности, так что 0123 есть 2301, a 0025 есть 2500. Когда команда MOV будет обращаться к этим cловам, нормальная последовательность будет восстанов лена и 2301 станет 0123, а 2500 - 0025. Вторая команда записывает три звездочки (***) для того, чтобы их можно было видеть впоследствии по команде D (Dump) - другого назначения эти звездочки не имеют. Введем теперь команды в сегмент кодов, опять начиная с адреса CS:100:
E CS:100 A1 00 00 03 06 02 00 E CS:107 A3 04 00 CB
Ассемблер для IBM PC. Глава 2 37
Теперь команды находятся в ячейках памяти от CS:100 до CS:10A. Эти команды можно выполнить как это делалось ранее. На рис. 2.3 показаны все шаги, включая команды E. На экране дисплея должны появиться такие же результаты, хотя адреса CS и DS могут различаться. Для пересмотра информации в сегменте данных и в сегменте кодов введите команды D (Dump) соответственно:
для сегмента данных: D DS:000 (Return) для сегмента кодов: D CS:100 (Return)
Сравните содержимое обоих сегментов с тем, что вводилось и с изображенным на рис. 2.3. Содержимое памяти от DS:00 до DS:08 и от CS:100 до CS:10A должно быть идентично рис. 2.3. Теперь введите R для просмотра содержимого регистров и флагов и для отображения первой команды. Регистры содержат те же значения, как при старте первого примера. Команда ото- бразится в виде:
13C6:0100 A10000 MOV AX,[0000]
Так, как регистр CS содержит 13C6, то CS:100 содержит первую команду A10000. Отладчик интерпретирует эту команду как MOV и определяет ссылку к первому адресу [0000] в сегменте данных. Квадратные скобки необходимы для указания ссылки к адресу памяти, а не к непосредственным данным.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 2.3. Трассировка машинных команд


Если бы квадратных скобок не было, то команда
MOV AX,0000
oбнулила бы регистр AX непосредственным значением 0000. Теперь введем команду T. Команда MOV AX,[0000] перешлет cодержимое слова, находящегося по нулевому смещению в сегменте данных, в регистр AX. Содержимое 2301 преобразуется командой в 0123 и помещается в регистр AX. Следующую команду ADD можно выполнить, введя еще раз команду T. В результате содержимое слова в DS по смещению 0002 прибавится в регистр AX. Теперь регистр AX будет содержать сумму 0123 и 0025, т.е 0148. Следующая команда MOV [0004],AX выполняется опять по вводу T. Эта команда пересылает содержимое регистра AX в слово по смешению 0004. Для просмотра изменений содержимого сегмента данных введите D DS:00. Первые девять байт будут следующими:
значение в сегменте данных: 23 01 25 00 48 01 2A 2A 2A величина смещения: 00 01 02 03 04 05 06 07 08
Ассемблер для IBM PC. Глава 2 38
Значение 0148, которое было занесено из регистра AX в сег мент данных по смещению 04 и 05, имеет обратное представле ние 4801. Заметьте что эти шест. значения представлены в правой части экрана их символами в коде ASCII. Например, шест.23 генерируeтся в символ #, а шест.25 - в символ %. Три байта с шест. значениями 2A высвечиваются в виде трех звездочек (***). Левая часть дампа показывает действительные машинные коды, которые находятся в памяти. Правая часть дампа только помогает проще локализовать символьные (сроч ные) данные. Для просмотра содержимого сегмента кодов введите D DS:100 так, как показано на рис. 2.3. В заключении введите Q для завершения работы с программой.
МАШИННАЯ АДРЕСАЦИЯ ------------------------------------------------------------
Для доступа к машинной команде процессор определяет ее адрес из содержимого регистра CS плюс смещение в регистре IP. Например, предположим, что регистр CS содержит шест. 04AF (действительный адрес 04AF0), а регистр IP содержит шест. 0023:
CS: 04AF0 IP: 0023 Адрес команды: 04B13
Если, например, по адресу 04B13 находится команда:


A11200 MOV AX,[0012] | Адрес 04B13
то в памяти по адресу 04B13 содержится первый байт команды. Процессор получает доступ к этому байту и по коду команды (A1) oпределяет длину команды - 3 байта. Для доступа к данным по смещению [0012] процессор опреде ляет aдрес, исходя из содержимого регистра DS (как правило) плюс cмещение в операнде команды. Если DS содержит шест.04B1 (реальный адрес 04B10), то результирующий адрес данных определяется cледующим образом:
DS: 04B10 Смещение: 0012 Адрес данных: 04B22
Предположим, что по адресам 04B22 и 04B23 содержатся следующие данные:
Содержимое: 24 01 | | Адрес: 04B22 04B23
Ассемблер для IBM PC. Глава 2 39
Процессор выбирает значение 24 из ячейки по адресу 04B22 и помещает его в регистр AL, и значение 01 по адресу 04B23 - в регистр AH. Регистр AX будет содержать в результате 0124. В процессе выборки каждого байта команды процессор увеличивает значение регистра IP на единицу, так что к началу выполнения следующей команды в нашем примере IP будет содержать смещение 0026. Таким обpазом процессор теперь готов для выполнения следующей команды, которую он получает по адресу из регистра CS (04AF0) плюс текущее смещение в регистре IP (0026), т.е 04B16.
Четная адресация
Процессор 8086, 80286 и 80386 действуют более эффективно, eсли в программе обеспечиваются доступ к словам, расположен ным по четным адресам. В предыдущем примере процессор может сделать oдну выборку слова по адресу 4B22 для загрузки его непосредственно в регистр. Но если слово начинается на нечет ном адресе, процессор выполняет двойную выборку. Предполо жим, например, что команда должна выполнить выборку слова, начинающегося по адреcу 04B23 и загрузить его в регистр AX:
Содержимое памяти: |хх|24|01|хх| | Адрес: 04B23
Сначала процессор получает доступ к байтам по адресам 4B22 и 4B23 и пересылает байт из ячейки 4B23 в регистр AL. Затем он получает доступ к байтам по адресам 4B24 и 4B25 и пересылает байт из ячейки 4B23 в регистр AH. В результате регистр AX будет содержать 0124. Нет необходимости в каких-либо специальных методах программирования для получения четной или нечетной адрессации, не обязательно также знать является адрес четным или нет. Важно знать, что, во-первых, команды обращения к памяти меняют слово при загрузке его в регистр так, что получается правильная последовательность байт и, во-вторых, если программа имеет частый доступ к памяти, то для повышения эффективности можно определить данные так, чтобы они начинались по четным адресам. Например, поскольку начало сегмента должно всегда находиться по четному адресу, первое поле данных начинается также по четному адресу и пока следующие поля определены как слова, имеющие четную длину, они все будут начинаться на четных адресах. В большинстве случаев, однако, невозможно заметить ускорения работы при четной адрессации из-за очень высокой скорости работы процессоров. Ассемблер имеет директиву EVEN, которая вызывает выравнив нивание данных и команд на четные адреса памяти.


ПРИМЕР МАШИННЫХ КОДОВ: ОПРЕДЕЛЕНИЕ РАЗМЕРА ПАМЯТИ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 2 40
В первом упражнении в данной главе проводилась проверка размера памяти (RAM), которую имеет компьютер. BIOS (базовая система ввода/вывода) в ROM имеет подпрограмму, которая определяет pазмер памяти. Можно обратиться в BIOS по команде INT, в данном cлучае по прерыванию 12H. В результате BIOS возвращает в регистр AX размер памяти в килобайтах. Загрузите в память DEBUG и введите для INT 12H и RET следующие машинные коды:
E CS:100 CD 12 CB
Нажмите R (и Return) для отображения содержимого регистров и первой команды. Регистр IP содержит 0100, при этом высвечивается команда INT 12H. Теперь нажмите T (и Return) несколько раз и просмотрите выполняемые команды BIOS (отладчик показывает мнемокоды, хотя в действительности выполняются машинные коды):
STI PUSH DS MOV AX,0040 MOV DS,AX MOV AX,[0013] POP DS IRET
В этот момент регистр AX содержит размер памяти в шестнадца тиpичном формате. Теперь введите еще раз команду T для выхода из BIOS и возврата в вашу программу. На экране появится команда RET для машинного кода CB, который был введен вами.
СПЕЦИАЛЬНЫЕ СРЕДСТВА ОТЛАДЧИКА ------------------------------------------------------------
В операционной системе DOS версии 2.0 и старше можно использовать DEBUG для ввода команд ассемблера так же, как и команд машинного языка. На практике можно пользоваться обоими методами.
Команда A
Команда отладчика A (Assemble) переводит DEBUG в режим приема команд ассемблера и перевода их в машинные коды. Установим начальный адрес следующим образом:
A 100 [Return]
Отладчик выдаст значение адреса сегмента кодов и смещения в ввиде хххх:0100. Теперь можно вводить каждую команду, завер шая клавишей Return. Когда вся программа будет введена, нажмите снова клавишу Return для выхода из режима ассембле ра. Введите следующую программу:
Ассемблер для IBM PC. Глава 2 41
MOV AL,25 [Return] MOV BL,32 [Return] ADD AL,BL [Return] RET [Return]


по завершению на экране будет следующая информация:
хххх:0100 MOV AL,25 хххх:0102 MOV BL,32 хххх:0104 ADD AL,BL хххх:0106 RET
В этот момент отладчик готов к приему следующей команды. При нажатии Return операция будет прекращена. Можно видеть, что отладчик определил стартовые адреса каждой команды. Прежде чем выполнить программу, проверим сгенерированные машинные коды.
Команда U
Команда отладчика U (Unassemble) показывает машинные коды для команд ассемблера. Необходимо сообщить отладчику адреса первой и последней команды, которые необходимо просмотреть (в данном cлучае 100 и 106). Введите:
U 100,106 [и Return]
и на экране появится
хххх:0100 B025 MOV AL,25 хххх:0102 B332 MOV BL,32 хххх:0104 00D8 ADD AL,BL хххх:0106 C3 RET
Теперь проведем трассировку выполнения программы, начиная с команды R для вывода содержимого регистров и первой команды программы. С помощью команд T выполним последовательно все команды программы. Теперь вы знаете, как вводить программу в машинном коде или на языке ассемблера. Обычно используется ввод на языке ассемблера, когда машинный код неизвестен, а ввод в машинном коде - для изменения программы во время выполнения. Однако в действительности программа DEBUG предназначена для отладки программ и в следующих главах основное внимание будет уделено использованию языка ассемблера.
Сохранение программы из отладчика
Можно использовать DEBUG для сохранения программ на диске в следующих случаях:
Ассемблер для IBM PC. Глава 2 42
1. После загрузки программы в память машины и ее модифика ции необходимо сохранить измененный вариант. Для этого следует: ъ загрузить программу по ее имени: DEBUG n:имяфайла[Return] ъ просмотреть программу с помощью команды D и ввести изменения по команде E, ъ записать измененную программу: W [Return]
2. Необходимо с помощью DEBUG написать небольшую по объему программу и сохранить ее на диске. Для этого следует: ъ вызвать отладчик DEBUG, ъ с помощью команд A (assemble) и E (enter) написать программу, ъ присвоить программе имя: N имяфайла.COM [Return]. Тип программы должен быть COM (см. главу 6 для пояснений по COM-файлам). ъ Так как только программист знает, где действительно кончается его программа, указать отладчику длину программы в байтах. В последнем примере концом программы является команда


хххх:0106 C3 RET
Эта команда однобайтовая и поэтому размер программы будет равен 106 (конец) минус 100 (начало), т.е. 6. ъ запросить регистр CX командой: R CX [Return] ъ отладчик выдаст на этот запрос CX 0000 (нулевое значение) ъ указать длину программы - 6, ъ записать измененную программу: W [Return]
В обоих случаях DEBUG выдает сообщение "Writing nnnn bytes." (Запись nnnn байтов). Если nnnn равно 0, то произошла ошибка при вводе длины программы, и необходимо повторить запись cнова.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
Отладчик DOS DEBUG это достаточное мощное средство, полезное для отладки ассемблерных программ. Однако следует быть осторожным с ее использованием, особенно для команды E (ввод). Ввод данных в неправильные адреса памяти или ввод некорректных данных могут привести к непредсказуемым результатам. На экране в этом случае могут появиться "странные" символы, клавиатура заблокирована или даже DOS прервет DEBUG и перезагрузит себя с диска. Какие либо серьезные повреждения вряд ли произойдут, но возможны некоторые неожиданности, а также потеря данных, которые вводились при работе с отладчиком.
Ассемблер для IBM PC. Глава 2 43
Если данные, введенные в сегмент данных или сегмент кодов, оказались некорректными, следует, вновь используя команду E, исправить их. Однако, можно не заметить ошибки и начать трассиpовку программы. Но и здесь возможно еще использовать команду E для изменений. Если необходимо начать выполнение с первой команды, то следует установить в регистре командного указателя (IP) значение 0100. Введите команду R (register) и требуемый регистр в следующем виде:
R IP [Return]
Отладчик выдаст на экран содержимое регистра IP и перей дет в ожидание ввода. Здесь следует ввести значение 0100 и нажать для проверки результата команду R (без IP). 0тладчик выдаст содержимое регистров, флагов и первую выполняемую команду. Теперь можно, используя команду T, вновь выполнить трассировку программы. Если ваша программа выполняет какие-либо подсчеты, то возможно потребуется очистка некоторых облостей памяти и регистров. Но yбедитесь в сохранении содержимого регистров CS, DS, SP и SS, которые имеют специфическое назначение. Прочитайте в руководстве по DOS главу о программе DEBUG. В настоящий момент рекомендуется: вводный материал и следующие команды oтладчика: дамп (D), ввод (E), шестнадцатиричный (H), имя (N), выход (Q), регистры (R), трассировка (T) и запись (W). Можно oзнакомиться также и с другими командами и проверить как они работают.


ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
2.1. Напишите машинные команды для а) пересылки шест. значения 4629 в регистр AX; б) сложения шест. 036A с содержимым регистра AX.
2.2. Предположим, что была введена следующая е команда:
E CS:100 B8 45 01 05 25 00
Вместо шест.значения 45 предполагалось 54. Написшите команду E для корректировки только одного неправильно введенного байта, т.е. непосредственно замените 45 на 54.
2.3. Предположим, что введена следующая е команда:
E CS:100 B8 04 30 05 00 30 CB
а) Что представляют собой эти команды? (Сравните с первой программой в этой главе). б) После выполнения этой программы в регистре AX должно быть значение 0460, но в действительности оказалось 6004. В чем ошибка и как ее исправить?
Ассемблер для IBM PC. Глава 2 44
в) После исправления команд необходимо снова выполнить программу с первой команды. Какие две команды отладчика потребуются?
2.4. Имеется следующая программа в машинных кодах:
B0 25 D0 E0 B3 15 F6 E3 CB
Программа выполняет следующее: - пересылает шест.значение 25 в регистр AL; - сдвигает содержимое регистра AL на один бит влево (в результате в AL будет 4A); - пересылает шест.значение 15 в регистр BL; - умножает содержимое регистра AL на содержимое регистра BL.
Используйте отладчик для ввода (E) этой программы по адресу CS:100. Не забывайте, что все значения представ лены в шестнадцатиричном виде. После ввода программы наберите D CS:100 для просмотра сегмента кода. Затем введите команду R и необходимое число команд T для пошагового выполнения программы до команды RET. Какое значение будет в регистре AX в результате выполнения программы?
2.5. Используйте отладчик для ввода (E) следующей программы в машинных кодах:
Данные: 25 15 00 00 Машинный код: A0 00 00 D0 E0 F6 26 01 00 A3 02 00 CB
Программа выполняет следующее: - пересылает содержимое одного байта по адресу DS:00 (25) в регистр AL; - сдвигает содержимое регистра AL влево на один бит (получая в результате 4A); - умножает AL на содержимое одного байта по адресу DS:01 (15); - пересылает результат из AX в слово, начинающееся по адресу DS:02.


После ввода программы используйте команды D для просмот ра сегмента данных и сегмента кода. Затем введите коман ду R и необходимое число команд T для достижения конца программы (RET). В этот момент регистр AX должен содер жать результат 0612. Еще раз используйте команду D DS:00 и заметьте, что по адресу DS:02 значение записано как 1206.
2.6. Для предыдущего задания (2.5) постройте команды для записи программы на диск под именем TRIAL.COM.
Ассемблер для IBM PC. Глава 2 45
2.7. Используя команду A отладчика, введите следующую про грамму:
MOV BX,25 ADD BX,30 SHL BX,01 SUB BX,22 NOP RET
сделайте ассемблирование и трассировку выполнения этой программы до команды NOP.
Ассемблер для IBM PC. Глава 3 60


Требования языка ассемблер


------------------------------------------------------------

Требования языка ассемблер

Цель: показать основные требования к программам на языке ассемблера и этапы ассемблирования, компановки и выполнения программы.

ВВЕДЕНИЕ ------------------------------------------------------------

В главе 2 было показано как ввести и выполнить программу на машинном языке. Несомненно при этом ощутима трудность расшифровки машинного кода даже для очень небольшой программы. Сомнительно, чтобы кто-либо серьезно кодировал программы на машинном языке, за исключением разных "заплат" (корректировок) в программе на языках высокого уровня и прикладные программы. Более высоким уровнем кодирования является уровень ассемблера, на котором программист пользуется символическими мнемокодами вместо машинных команд и описательными именами для полей данных и адресов памяти. Программа написанная символическими мнемокодами, которые используются в языке ассемблера, представляет собой исходный модуль. Для формирования исходного модуля применяют программу DOS EDLIN или любой другой подходящий экранный редактор. Затем с помощью программы ассемблерного транслято ра исходный текст транслируется в машинный код, известный как объектная программа. И наконец, программа DOS LINK определяет все адресные ссылки для объектной программы, генерируя загрузочный модуль. В данной главе объясняются требования для простой програм мы на ассемблере и показаны этапы ассемблирования, компанов ки и выполнения.

КОММЕНТАРИИ В ПРОГРАММАХ НА АССЕМБЛЕРЕ ------------------------------------------------------------

Использование комментариев в программе улучшает ее ясность, oсобенно там, где назначение набора команд непонятно. Комментаpий всегда начинаются на любой строке исходного модуля с символа точка с запятой (;) и ассемблер полагает в этом случае, что все символы, находящиеся справа от ; являются комментарием. Комментарий может содержать любые печатные символы, включая пробел. Комментарий может занимать всю строку или следовать за командой на той же строке, как это показано в двух следующих примерах:


1. ; Эта строка полностью является комментарием 2. ADD AX,BX ;Комментарий на одной строке с командой
Ассемблер для IBM PC. Глава 3 61
Комментарии появляются только в листингах ассемблиро- вания исходного модуля и не приводят к генерации машинных кодов, поэтому можно включать любое количество комментариев, не оказывая влияния на эффективность выполнения программы. В данной книге команды ассемблера представлены заглавными буквами, а комментарии - строчными (только для удобочитае мости).
ФОРМАТ КОДИРОВАНИЯ ------------------------------------------------------------
Основной формат кодирования команд ассемблера имеет следующий вид:
[метка] команда [операнд(ы)]
Метка (если имеется), команда и операнд (если имеется) pазделяются по крайней мере одним пробелом или символом табуляции. Максимальная длина строки - 132 символа, однако, большинство предпочитают работать со строками в 80 символов (соответственно ширине экрана). Примеры кодирования:
Метка Команда Операнд COUNT DB 1 ;Имя, команда, один операнд MOV AX,0 ;Команда, два операнда
Метки
Метка в языке ассемблера может содержать следующие симво лы: Буквы: от A до Z и от a до z Цифры: от 0 до 9 Спецсимволы: знак вопроса (?) точка (.) (только первый символ) знак "коммерческое эт" (@) подчеркивание (-) доллар ($)
Первым символом в метке должна быть буква или спецсимвол. Ассемблер не делает различия между заглавными и строчными буквами. Максимальная длина метки - 31 символ. Примеры меток: COUNT, PAGE25, $E10. Рекомендуется использовать описательные и смысловые метки. Имена регистров, например, AX, DI или AL являются зарезервированными и используются только для указания соответствующих регистров. Например, в команде ADD AX,BX
ассемблер "знает", что AX и BX относится к регистрам. Однако, в команде
MOV REGSAVE,AX
Ассемблер для IBM PC. Глава 3 62
ассемблер воспримет имя REGSAVE только в том случае, если оно будет определено в сегменте данных. В приложении 3 приведен cписок всех зарезервированных слов ассемблера.


Команда
Мнемоническая команда указывает ассемблеру какое действие должен выполнить данный оператор. В сегменте данных команда (или директива) определяет поле, рабочую oбласть или константу. В сегменте кода команда определяет действие, например, пересылка (MOV) или сложение (ADD).
Операнд
Если команда специфирует выполняемое действие, то операнд определяет а) начальное значение данных или б) элементы, над которыми выполняется действие по команде. В следующем примере байт COUNTER определен в сегменте данных и имеет нулевое значение:
Метка Команда Операнд COUNTER DB 0 ;Определить байт (DB) ; с нулевым значением
Команда может иметь один или два операнда, или вообще быть без операндов. Рассмотрим следующие три примера:
Команда Операнд Комментарий Нет операндов RET ;Вернуться Один операнд INC CX ;Увеличить CX Два операнда ADD AX,12 ;Прибавить 12 к AX
Метка, команда и операнд не обязательно должны начинаться с какой-либо определенной позиции в строке. Однако, рекомен дуется записывать их в колонку для большей yдобочитаемости программы. Для этого, например, редактор DOS EDLIN обеспечи вает табуляцию чепез каждые восемь позиций.
ДИРЕКТИВЫ ------------------------------------------------------------
Ассемблер имеет ряд операторов, которые позволяют упpав лять процессом ассемблирования и формирования листинга. Эти операторы называются псевдокомандами или директивами. Они действуют только в процессе ассемблирования программы и не генерируют машинных кодов. Большинство директив показаны в следующих разделах. В главе 24 подробно описаны все директивы ассемблера и приведено более чем достаточно соответствующей информации. Главу 24 можно использовать в качестве справочника.
Директивы управления листингом: PAGE и TITLE
Ассемблер для IBM PC. Глава 3 63
Ассемблер содержит ряд директив, управляющих форматом печати (или листинга). Обе директивы PAGE и TITLE можно использовать в любой программе.
Директива PAGE. В начале программы можно указать количест во строк, распечатываемых на одной странице, и максимальное количество символов на одной строке. Для этой цели cлужит директива PAGE. Следующей директивой устанавливается 60 строк на страницу и 132 символа в строке:


PAGE 60,132
Количество строк на странице межет быть в пределах от 10 до 255, а символов в строке - от 60 до 132. По умолчанию в ассемблере установлено PAGE 66,80. Предположим, что счетчик строк установлен на 60. В этом случае ассемблер, распечатав 60 строк, выполняет прогон листа на начало следующей страницы и увеличивает номер страницы на eдиницу. Кроме того можно заставить ассемблер сделать прогон листа на конкретной строке, например, в конце сегмента. Для этого необходимо записать директиву PAGE без операндов. Ассемблер автоматически делает прогон листа при обработке диpективы PAGE.
Директива TITLE. Для того, чтобы вверху каждой страницы листинга печатался заголовок (титул) программы, используется диpектива TITLE в следующем формате:
TITLE текст
Рекомендуется в качестве текста использовать имя програм мы, под которым она находится в каталоге на диске. Например, если программа называется ASMSORT, то можно использовать это имя и описательный комментарий общей длиной до 60 символов:
TITLE ASMSORT - Ассемблерная программа сортировки имен
В ассемблере также имеется директива подзаголовка SUBTTL, которая может оказаться полезной для очень больших программ, содержащих много подпрограмм.
Директива SEGMENT
Любые ассемблерные программы содержат по крайней мере один сегмент - сегмент кода. В некоторых программах используется сегмент для стековой памяти и сегмент данных для определения данных. Асcемблерная директива для описания сегмента SEGMENT имеет следующий формат:
Имя Директива Операнд имя SEGMENT [параметры] . .
Ассемблер для IBM PC. Глава 3 64
. имя ENDS
Имя сегмента должно обязательно присутствовать, быть уникальным и соответствовать соглашениям для имен в ассемблере. Директива ENDS обозначает конец сегмента. Обе директивы SEGMENT и ENDS должны иметь одинаковые имена. Директива SEGMENT может содержать три типа параметров, определяющих выравнивание, объединение и класс.
1. Выравнивание. Данный параметр определяет границу начала сегмента. Обычным значением является PARA, по которму сегмент устанавливается на границу параграфа. В этом случае начальный адрес делится на 16 без остатка, т.е. имеет шест. адрес nnn0. В случае отсутствия этого операнда ассемблер принимает по умолчанию PARA. 2. Объединение. Этот элемент определяет объединяется ли данный сегмент с другими сегментами в процессе компановки после ассемблирования (пояснения см. в следующем разделе "Компановка программы"). Возможны следующие типы объединений: STACK, COMMON, PUBLIC, AT выражение и MEMORY. Сегмент стека определяется следующим образом:


имя SEGMENT PARA STACK
Когда отдельно ассемблированные программы должны объеди няться компановщиком, то можно использовать типы: PUBLIC, COMMON и MEMORY. В случае, если программа не должна об'единяться с другими программами, то данная опция может быть опущена. 3. Класс. Данный элемент, заключенный в апострофы, исполь зуется для группирования относительных сегментов при компановке:
имя SEGMENT PARA STACK 'Stack'
Фрагмент программы на рис. 3.1. в следующем разделе иллюстрирует директиву SEGMENT и ее различные опции.
Директива PROC
Сегмент кода содержит выполняемые команды программы. Кроме того этот сегмент также включает в себя одну или несколько процедур, определенных директивой PROC. Сегмент, содержащий только одну процедуру имеет следующий вид:
имя-сегмента SEGMENT PARA имя-процедуры PROC FAR Сегмент . кода . с . одной RET процедурой
Ассемблер для IBM PC. Глава 3 65
имя-процедуры ENDP имя-сегмента ENDS
Имя процедуры должно обязательно присутствовать, быть уникальным и удовлетворять соглашениям по именам в ассембле ре. Операнд FAR указывает загрузчику DOS, что начало данной процедуры является точкой входа для выполнения программы. Директива ENDP определяет конец процедуры и имеет имя, аналогичное имени в директиве PROC. Команда RET завершает выполнение программы и в данном случае возвращает управление в DOS. Сегмент может содержать несколько процедур (см. гл.7).
Директива ASSUME
Процессор использует регистр SS для адресации стека, ркгистр DS для адресации сегмента данных и регистр CS для адресации cегмента кода. Ассемблеру необходимо сообщить назначение каждого сегмента. Для этой цели служит директива ASSUME, кодируемая в сегменте кода следующим образом:
Директива Операнд ASSUME SS:имя_стека,DS:имя_с_данных,CS:имя_с_кода
Например, SS:имя_стека указывает, что ассемблер должен ассоциировать имя сегмента стека с регистром SS. Операнды могут записываться в любой последовательности. Регистр ES также может присутствовать в числе операндов. Если программа не использует регистр ES, то его можно опустить или указать ES:NOTHING.


Директива END
Как уже показано, директива ENDS завершает сегмент, а директива ENDP завершает процедуру. Директива END в свою очередь полностью завершает всю программу:
Директива Операнд END [имя_процедуры]
Операнд может быть опущен, если программа не предназначе на для выполнения, например, если ассемблируются только опре деления данных, или эта программа должна быть скомпанована с другим (главным) модулем. Для обычной программы с одним модулем oперанд содержит имя, указанное в директиве PROC, которое было oбозначено как FAR.
ПАМЯТЬ И РЕГИСТРЫ ------------------------------------------------------------
Рассмотрим особенности использования в командах имен, имен в квадратных скобках и чисел. В следующих примерах положим, что WORDA определяет слово в памяти:
Ассемблер для IBM PC. Глава 3 66
MOV AX,BX ;Переслать содержимое BX в регистр AX MOV AX,WORDA ;Переслать содержимое WORDA в регистр AX MOV AX,[BX] ;Переслать содержимое памяти по адресу ; в регистре BX в регистр AX MOV AX,25 ;Переслать значение 25 в регистр AX MOV AX,[25] ;Переслать содержимое по смещению 25
Новым здесь является использование квадратных скобок, что потребуется в следующих главах.
ИНИЦИАЛИЗАЦИЯ ПРОГРАММЫ ------------------------------------------------------------
Существует два основных типа загрузочных программ: EXE и COM. Рассмотрим требования к EXE-программам, а COM-программы будут представлены в главе 6. DOS имеет четыре требования для инициализации ассемблерной EXE-программы: 1) указать ассемблеру, какие cегментные регистры должны соответствовать сегментам, 2) сохранить в стеке адрес, находящийся в регист ре DS, когда программа начнет выполнение, 3) записать в стек нелевой адрес и 4) загрузить в регистр DS адрес сегмента данных. Выход из программы и возврат в DOS сводится к использова нию команды RET. Рис.3.1 иллюстрирует требования к инициали зации и выходу из программы:
1. ASSUME - это ассемблерная директива, которая устанавли вает для ассемблера соответствие между конкретными сегментами и сегментными регистрами; в данном случае, CODESG - CS, DATASG - DS и STACKSG - SS. DATASG и STACKSG не определены в этом примере, но они будут представлены следующим образом:


STACKSG SEGMENT PARA STACK Stack 'Stack' DATASG SEGMENT PARA 'Data'
Ассоциируя сегменты с сегментными регистрами, ассемблер сможет определить смещения к отдельным областям в каждом сегменте. Например, каждая команда в сегменте кодов имеет определенную длину: первая команда имеет смещение 0, и если это двухбайтовая команда, то вторая команда будет иметь смещение 2 и т.д.
2. Загрузочному модулю в памяти непосредственно предшеству ет 256-байтовая (шест.100) область, называемая префик сом программного сегмента PSP. Программа загрузчика использует регистр DS для установки адреса начальной точки PSP. Пользовательская программа должна сохранить этот адрес, поместив его в стек. Позже, команда RET использует этот адрес для возврата в DOS.
Ассемблер для IBM PC. Глава 3 67
3. В системе требуется, чтобы следующее значение в стеке являлось нулевым адресом (точнее, смещением). Для этого команда SUB очищает регистр AX, вычитая его из этого же регистра AX, а команда PUSH заносит это значение в стек.
4. Загрузчик DOS устанавливает правильные адреса стека в регистре SS и сегмента кодов в регистре CS. Поскольку программа загрузчика использует регистр DS для других целей, необходимо инициализировать регистр DS двумя командами MOV, как показано на рис.3.1. В следующем разделе этой главы "Исходная программа. Пример II" детально поясняется инициализация регистра DS.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 3.1. Инициализация EXE-программы.
5. Команда RET обеспечивает выход из пользовательской программы и возврат в DOS, используя для этого адрес, записанный в стек в начале программы командой PUSH DS. Другим обычно используемым выходом является команда INT 20H.
Теперь, даже если приведенная инициализация программы до конца не понятна - не отчаивайтесь. Каждая программа фактически имеет аналогичные шаги инициализации, так что их можно дублировать всякий раз при кодировании программ.
ПРИМЕР ИСХОДНОЙ ПРОГРАММЫ ------------------------------------------------------------


Рис. 3.2. обобщает предыдущие сведения в простой исходной программе на ассемблере. Программа содержит сегмент стека - STACKSG и сегмент кода - CODESG. STACKSG содержит один элемент DB (определить байт), который определяет 12 копий слова 'STACKSEG'. В последующих программах стек не опpеделяется таким способом, но при использовании отладчика для просмотра ассемблированной программы на экране, данное определение помогает локализо вать стек. CODESG содержит выполняемые команды программы, хотя первая директива ASSUME не генерирует кода. Директива ASSUME назначает регистр SS для STACKSG и регистр CS для CODESG. В действительности, эта директива сообщает ассемблеру, что для адресации в STACKSG необходимо использовать адрес в регистре SS и для адресации в CODESG - адрес в регистре CS. Системный загрузчик при загрузке программы с диска в память для выполнения устанавливает действительные адреса в регистрах SS и CS. Программа не имеет сегмента данных, так как в ней нет определения данных и, соответственно, в ASSUME нет необходимости ассигновать pегистр DS.
Ассемблер для IBM PC. Глава 3 68
Команды, следующие за ASSUME - PUSH, SUB и PUSH выполняют стандартные действия для инициализации стека текущим адресом в регистре DS и нулевым адресом. Поскольку, обычно, программа выполняется из DOS, то эти команды обеспечивают возврат в DOS после завершения программы. (Можно также выполнить программу из отладчика, хотя это особый случай). Последующие команды выполняют те же действия, что показаны на pис.2.1 в предыдущей главе, когда рассматривался отладчик.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Не забывайте ставить символ "точка с запятой" перед комментариями.
ъ Завершайте каждый сегмент директивой ENDS, каждую процедуру - директивой ENDP, а программу - директивой END.
ъ В директиве ASSUME устанавливайте соответствия между сегментными регистрами и именами сегментов.
ъ Для EXE-программ (но не для COM-программ, см. гл.6) обеспечивайте не менее 32 слов для стека, соблюдайте соглашения по инициализации стека командами PUSH, SUB и PUSH и заносите в регистр DS адрес сегмента данных.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
3.1. Какие команды заставляют ассемблер печатать заголовок в начале каждой страницы листинга и делать прогон листа?
3.2. Какие из следующих имен неправильны: а) PC_AT, б) $50, в) @$_Z, г) 34B7, д) AX?
3.3. Какое назначение каждого из трех сегментов, описанных в этой главе?
3.4. Что конкретно подразумевает директива END, если она завершает а) программу, б) процедуру, в) сегмент?
3.5. Укажите различия между директивой и командой.
3.6. Укажите различия в назначении RET и END.
3.7. Для сегментов кода, данных и стека даны имена CDSEG, DATSEG и STKSEG соответственно. Сформируйте директиву ASSUME.
3.8. Напишите три команды для инициализации стека адресом в DS и нулевым адресом.
Ассемблер для IBM PC. Глава 4 79


Ассемблирование и выполнение программ


------------------------------------------------------------

Ассемблирование и выполнение программ

Цель: показать процессы ассемблирования, компановки и выполнения программ.

ВВЕДЕНИЕ ------------------------------------------------------------

В данной главе объясняется, как ввести в компьютер исходный ассемблерный текст программы, как осуществить ассемблирование, компановку и выполнение программы. Кроме того, показана генерация таблицы перекрестных ссылок для целей отладки.

ВВОД ПРОГРАММЫ ------------------------------------------------------------

На рис.3.2. был показан только исходный текст программы, предназначенный для ввода с помощью текстового редактора. Теперь можно использовать DOS EDLIN или другой текстовый редактор для ввода этой программы. Если вы никогда не пользовались программой EDLIN, то именно сейчас необходимо выполнить ряд упражнений из руководства по DOS. Для запуска программы EDLIN вставте дискету DOS в дисковод A и форматизованную дискету в дисковод B. Чтобы убедиться в наличии на дискете свободного места для исходного текста, введите CHKDSK B:. Для винчестера во всех следующих примерах следует использовать C: вместо B:. Для ввода исходной программы EXASM1, наберите команду

EDLIN В:EXASM1.ASM [Return]

В результате DOS загрузит EDLIN в памяти и появится сообщение "New file" и приглашение "*-". Введите команду I для ввода строк, и затем наберите каждую ассемблерную команду так, как они изобpажены на рис. 3.2. Хотя число пробелов в тексте для ассемблера не существенно, старайтесь записывать метки, команды, операнды и комментарии, выровнен ными в колонки, программа будет более yдобочитаемая. Для этого в EDLIN используется табуляция через каждые восемь позиций. После ввода программы убедитесь в ее правильности. Затем наберите E (и Return) для завершения EDLIN. Можно проверить наличие программы в каталоге на диске, введите

DIR B: (для всех файлов) или DIR B:EXASM1.ASM (для одного файла)

Ассемблер для IBM PC. Глава 4 80


Если предполагается ввод исходного текста большего объема, то лучшим применением будет полноэкранный редактор. Для получения распечатки программы включите принтер и установите в него бумагу. Вызовите программу PRINT (для DOS 2.0 и старше). DOS загрузит программу в память и распечатает текст на принтере:
PRINT B:EXASM1.ASM [Return]
Программа EXASM.ASM еще не может быть выполнена - прежде необходимо провести ее ассемблирование и компановку. В следующем pазделе показана эта же программа после ассемблирования и пояснены этапы ассемблирования и получения листинга.
ПОДГОТОВКА ПРОГРАММЫ ДЛЯ ВЫПОЛНЕНИЯ ------------------------------------------------------------
После ввода на диск исходной программы под именем EXASM1.ASM необходимо проделать два основных шага, прежде чем программу можно будет выполнить. Сначала необходимо ассемблиpовать программу, а затем выполнить компановку. Программисты на языке бейсик могут выполнить программу сразу после ввода исходного текста, в то время как для ассемблера и компиллярных языков нужны шаги трансляции и компановки. Шаг ассемблирования включает в себя трансляцию исходного кода в машинный объектный код и генерацию OBJ-модуля. Вы уже встречали примеры машинного кода в главе 2 и примеры исxодно го текста в этой главе. OBJ-модуль уже более приближен к исполнительной форме, но еще не готов к выполнению. Шаг компановки включает преобразо вание OBJ-модуля в EXE (исполнимый) модуль, содержащий машинный код. Прогрпмма LINK, находящаяся на диске DOS, выполняет следующее:
1. Завершает формирование в OBJ-модуле адресов, которые остались неопределенными после ассемблирования. Во мно гих следующих программах такие адреса ассемблер отмеча ет как ----R. 2. Компанует, если необходимо, более одного отдельно ассем блированного модуля в одну загрузочную (выполнимую) про грамму; возможно две или более ассемблерных программ или ассемблерную программу с программами, написанными на языках высокого уровня, таких как Паскаль или Бейсик. 3. Инициализирует EXE-модуль командами загрузки для выполнения.


После компановки OBJ-модуля (одного или более) в EXE-модуль, можно выполнить EXE- модуль любое число раз. Но, если необходимо внести некоторые изменения в EXE-модуль, следует скорректировать исходную программу, ассемблировать ее в другой OBJ-модуль и выполнить компановку OBJ-модуля в
Ассемблер для IBM PC. Глава 4 81
новый EXE-модуль. Даже, если эти шаги пока остаются непо нятными, вы обнаружите, что, получив немного навыка, весь процесс подготовки EXE-модуля будет доведен до автоматизма. Заметьте: определенные типы EXE-программ можно преобразовать в oчень эффективные COM-программы. Предыдущие примеры, однако, не cовсем подходят для этой цели. Данный вопрос рассматривается в главе 6.
АССЕМБЛИРОВАНИЕ ПРОГРАММЫ ------------------------------------------------------------
Для того, чтобы выполнить исходную ассемблерную програм му, необходимо прежде провести ее ассемблирование и затем компановку. На дискете с ассемблерным пакетом имеются две версии aссемблера. ASM.EXE - сокращенная версия с отсутстви ем некоторых незначительных возможностей и MASM.EXE - полная версия. Если размеры памяти позволяют, то используйте версию MASM (подробности см. в соответствующем руководстве по ассемблеру). Для ассемблирования, вставте ассемблерную дискету в дисковод A, а дискету с исходной программой в дисковод B. Кто имеет винчестер могут использовать в следующих примеpах C вместо A и B. Простейший вариант вызова программы это ввод команды MASM (или ASM), что приведет к загрузке программы ассемблера с диска в память. На экране появится:
source filename [.ASM]: object filename [filename.OBJ]: source listing [NUL.LST]: cross-reference [NUL.CRF]:
Курсор при этом расположится в конце первой строки, где необходимо указать имя файла. Введите номер дисковода (если он не определен умолчанием) и имя файла в следующем виде: B:EXASM1. Не следует набирать тип файла ASM, так как ассем блер подразумевает это. Во-втором запросе предполагается аналогичное имя файла (но можно его заменить). Если необходимо, введите номер дисковода B:. Третий запрос предполагает, что листинг ассемблирования программы не требуется. Для получения листинга на дисководе B наберите B: и нажмите Return. Последний запрос предполагает, что листинг перекрестных cсылок не требуется. Для получения листинга на дисководе B, наберите B: и нажмите Return. Если вы хотите оставить значения по умолчанию, то в трех последних запросах просто нажмите Return. Ниже приведен пример запросов и ответов, в результате которых ассемблер должен cоздать OBJ, LST и CRF-файлы. Введите ответы так, как показано, за исключением того, что номер дисковода может быть иной.


source filename [.ASM]:B:EXASM1 [Return]
Ассемблер для IBM PC. Глава 4 82
object filename [filename.OBJ]:B: [Return] source listing [NUL.LST]:B: [Return] cross-reference [NUL.CRF]:B: [Return]
Всегда необходимо вводить имя исходного файла и, обычно, запрашивать OBJ-файл - это требуется для компановки программы в загрузочный файл. Возможно потребуется указание LST-файла, особенно, если необходимо проверить сгенерирован ный машинный код. CRF-файл полезен для очень больших программ, где необходимо видеть, какие команды ссылаются на какие поля данных. Кроме того, ассемблер генерирует в LST-файле номера строк, которые используются в CRF-файле. В приложении 4 "Режимы ассемблирования и редактирования" перечислены режимы (опции) для ассемблера версий 1.0 и 2.0. Ассемблер преобразует исходные команды в машинный код и выдает на экран сообщения о возможных ошибках. Типичными ошибками являются нарушения ассемблерных соглашений по именам, неправильное написание команд (например, MOVE вместо MOV), а также наличие в опеpандах неопределенных имен. Программа ASM вадает только коды ошибок, которые объяснены в руководстве по ассемблеру, в то время как программа МASM выдает и коды ошибок, и пояснения к ним. Всего имеется около 100 сообщений об ошибках. Ассемблер делает попытки скорректировать некоторые ошибки, но в любом случае следует перезагрузить текстовый редактор, исправить исходную программу (EXASM1.ASM) и повторить ассемблирование. На рис. 4.1. показан листинг, полученный в результате асcемблирования программы и записанный на диск под именем EXASM1.LST. В начале листинга обратите внимание на реакцию ассемблера на директивы PAGE и TITLE. Никакие директивы, включая SEGMENT, PROC, ASSUME и END не генерируют машинных кодов. Листинг содержит не только исходный текст, но также слева транслированный машинный код в шестнадцатиричном формате. В самой левой колонке находится шест.адреса команд и данных. Сегмент стека начинается с относительного адреса 0000. В действительности он загружается в память в соответствии с адpесом в регистре SS и нулевым смещением относительно этого адpеса. Директива SEGMENT устанавливает 16-кратный адрес и указывает ассемблеру, что это есть начало стека. Сама директива не генерирует машинный код. Команда DB, также находится по адресу 0000, содержит 12 копий слова 'STACKSEG'; машинный код представлен шест.0C (десятичное 12) и шест. представлением ASCII символов. (В дальнейшем можно использовать отладчик для просмотра результатов в памяти). Сегмент стека заканчивается по адресу шест. 0060, который эквивалентен десятичному значению 96 (12х8).


------------------------------------------------------------ ------------------------------------------------------------ Рис. 4.1. Листинг ассемблирования программы
Ассемблер для IBM PC. Глава 4 83
Сегмент кода также начинается с относительного адреса 0000. Он загружается в память в соответствии с адресом в pегистре CS и нулевым смещением относительно этого адреса. Поскольку ASSUME является директивой ассемблеру, то первая команда, которая генерирует действительный машинный код есть PUSH DS - однобайтовая команда (1E), находящаяся на нулевом смещении. Следующая команда SUB AX,AX генерирует двухбайто вый машинный код (2B C0), начинающийся с относительного адреса 0001. Пробел между байтами только для удобочитаемос ти. В данном примере встречаются одно-, двух- и трехбайтовые команды. Последняя команда END содержит операнд BEGIN, который имеeт отношение к имени команды PROC по смещению 0000. Это есть адрес сегмента кодов, с которого начинается выполнение после загрузки программы. Листинг ассемблирования программы EXASM1.LST, имеет по директиве PAGE шиpину 132 символа и может быть распечатан. Многие принтеры могут печатать текст сжатым шрифтом. Включите ваш принтер и введите команду
MODE LPT1:132,6
Таблица идентификаторов
За листингом ассемблирования программы следует таблица идентификаторов. Первая часть таблицы содержит определенные в программе сегменты и группы вместе с их размером в байтах, выравниванием и классом. Вторая часть содержит идентификато ры - имена полей данных в сегменте данных (в нашем примере их нет) и метки, назначенные командам в сегменте кодов (одна в нашем примере). Для того, чтобы ассемблер не создавал эту таблицу, следует указать параметр /N вслед за командой MASM, т.е. MASM/N.
Двухпроходный ассемблер
В процессе трансляции исходной программы ассемблер делает два просмотра исходного текста, или два прохода. Одной из основных причин этого являются ссылки вперед, что происходит в том случае, когда в некоторой команде кодирует ся метка, значение которой еще не определено ассемблером. В первом проходе ассемблер просматривает всю исходную прогpамму и строит таблицу идентификаторов, используемых в программе, т.е. имен полей данных и меток программы и их относительных aдресов в программе. В первом проходе подчитывается объем объектного кода, но сам объектный код не генерируется. Во втором проходе ассемблер использует таблицу идентифи каторов, построенную в первом проходе. Так как теперь уже известны длины и относительные адреса всех полей данных и команд, то ассемблер может сгенерировать объектный код для каждой команды. Ассемблер создает, если требуется, файлы: OBJ, LST и CRF.


Ассемблер для IBM PC. Глава 4 84
КОМПАНОВКА ПРОГРАММЫ ------------------------------------------------------------
Если в результате ассемблирования не обнаружено ошибок, то cледующий шаг - компановка объектного модуля. Файл EXASM1.OBJ содержит только машинный код в шестнадцатеричной форме. Так как программа может загружаться почти в любое место памяти для выполнения, то ассемблер может не определить все машинные адреса. Кроме того, могут использоваться другие (под) программы для объединения с основной. Назначением программы LINK является завершение определения адресных ссылок и объединение (если требуется) нескольких программ. Для компановки ассемблированной программы с дискеты, вставте дискету DOS в дисковод A, а дискету с программой в дисковод B. Пользователи винчестерского диска могут загрузить компановщик LINK прямо с дисковода C. Введите команду LINK и нажмите клавишу Return. После загрузки в память, компановщик выдает несколько запросов (аналогично MASM), на которые необходимо ответить:
Запрос компановщика Ответ Действие
Object Modules [.OBJ]: B:EXASM1 Компанует EXASM1.OBJ Run file [EXASM1.EXE]: B: Создает EXASM1.EXE List file [NUL.MAP]: CON Создает EXASM1.MAP Libraries [.LIB]: [Return] По умолчанию
Первый запрос - запрос имен объектных модулей для компа новки, тип OBJ можно опустить. Второй запрос - запрос имени исполнимого модуля (файла), (по умолчанию A:EXASM1.EXE). Ответ B: требует, чтобы компановщик создал файл на дисководе В. Практика сохранения одного имени (при разных типах) файла упрощает работу с программами. Третий запрос предполагает, что LINK выбирает значение по yмолчанию - NUL.MAP (т.е. MAP отсутствует). MAP-файл содержит таблицу имен и размеров сегментов и ошибки, которые обнаружит LINK. Типичной ошибкой является неправильное определение сегмента стека. Ответ CON предполагает, что таблица будет выведена на экран, вместо записи ее на диск. Это позволяет сэкономить место в дисковой памяти и сразу просмотреть таблицу непосредственно на экране. В нашем примере MAP-файл содержит следующую информацию:


Start Stop Length Name
00000H 00015H 0016H CODESG 00020H 0007FH 0060H STACKSG
Ассемблер для IBM PC. Глава 4 85
Для ответа на четвертый запрос - нажмите Return, что укажет компановщику LINK принять остальные параметры по yмолчанию. Описание библиотечных средств можно найти в руководстве по DOS. На данном этапе единственной возможной ошибкой может быть yказание неправильных имен файлов. Исправить это можно только перезапуском программы LINK. В приложении 4 перечис лен ряд pежимов компановщика LINK.
ВЫПОЛНЕНИЕ ПРОГРАММЫ ------------------------------------------------------------
После ассемблирования и компановки программы можно (наконец-то!) выполнить ее. На рис. 4.2 приведена схема команд и шагов для ассемблирования, компановки и выполнения программы EXASM1. Если EXE-файл находится на дисководе B, то выполнить ее можно командой:
B:EXASM1.EXE или B:EXASM1
DOS предполагает, что файл имеет тип EXE (или COM), и загружает файл для выполнения. Но так как наша программа не вырабатывает видимых результатов, выполним ее трассировкой под отладчиком DEBUG. Введите
DEBUG B:EXASM1.EXE
В результате DOS загрузит программу DEBUG, который, в свою очередь, загрузит требуемый EXE-модуль. После этого отладчик выдаст дефис (-) в качестве приглашения. Для просмотра сегмента стека введите
D SS:0
Эту область легко узнать по 12-кратному дублированию константы STACKSEG. Для просмотра сегмента кода введите
D CS:0
Сравните машинный код с листингом ассемблера:
1E2BC050B823010525008BD803 ...
Непосредственные операнды, приведенные в листинге ассемблирования как 0123 и 0025 в памяти представлены в виде 2301 и 2500 соответственно. В данном случае листинг ассемблирования не вполне соответствует машинному коду. Все двухбайтовые адреса (слова) и непосредственные операнды в машинном коде хранятся в обратном порядке.
Ассемблер для IBM PC. Глава 4 86
Введите R для просмотра содержимого регистров и выполните прогpамму с помощью команды T (трассировка). Обратите внимание на воздействие двух команд PUSH на стек - в вершине стека теперь находится содержимое регистра DS и нулевой адрес. В процессе пошагового выполнения программы обратите внимание на содержимое регистров. Когда вы дойдете до команды RET, можно ввести Q (Quit - выход) для завершения работы отладчика. Используя команду dir, можно проверить наличие ваших файлов на диске:


DIR B:EXASM1.*
------------------------------------------------------------ ------------------------------------------------------------ Рис. 4.2. Схема ассемблирования, компановки и выполнения программы.
В результате на экране появится следующие имена файлов: EXASM1.BAK (если для корректировки EXASM1.ASM использовался редактор EDLIN), EXASM1.ASM, EXASM1.OBJ, EXASM1.LST, EXASM1.EXE и EXASM1.CRF. Последовательность этих файлов может быть иной в зависимости от того, что уже находится на диске. Очевидно, что разработка ряда программ приведет к занятию дискового пространства. Для проверки оставшегося свободного места на диске полезно использовать команду DOS CHKDSK. Для удаления OBJ-, CRF-, BAK- и LST-файлов с диска следует использовать команду ERASE (или DEL):
ERASE B:EXASM1.OBJ, ...
Следует оставить (сохранить) ASM-файл для последующих изменений и EXE-файл для выполнения. В следующем разделе представлено определение данных в сегменте данных. Позже будет описана таблица перекрестных cсылок.
ПРИМЕР ИСХОДНОЙ ПРОГРАММЫ ------------------------------------------------------------
Особенность программы, приведенной на рис. 4.1, состоит в том, что она не содержит определения данных. Обычно все программы имеют определенные константы, рабочие поля для арифметических вычислений и области для операций ввода-вывода. В главе 2 (рис.2.3) была рассмотрена программа в машинных кодах, в которой были определены два поля данных. В этой главе на рис. 4.3 приводится аналогичная программа, но на этот раз написанная на языке ассемблера и для краткости уже ассемблированная. Эта программа знакомит с несколькими новыми особенностями.
Ассемблер для IBM PC. Глава 4 87
Сегмент стека содержит директиву DW (Define Word - опреде лить cлово), описывающая 32 слова, в которых генерируется неопределенное значение обозначенное знаком вопроса (?). Определение размера стека в 32 слова является наиболее реальным, так как в больших программах может потребоваться много "прерываний" для ввода-вывода и вызовов подпрограмм - все они используют стек. Определение стека дублированием константы 'STACKSEG' в примере на pис. 3.2 необходимо лишь для удобства при работе с отладчиком DEBUG. Замечание: Определяйте размер стека не менее 32 слов. При малых размерах стека ни ассемблер, ни компановщик не смо- гут определить этого и выполнение программы может разрушить ся самым непредсказуемым образом. В примере на рис. 4.3 определен сегмент данных DATASG, начинающийся по относительному адресу 0000. Этот сегмент содержит три значения в формате DW. Поле FLDA определяет слово (два байта), содержащее десятичное значение 250, которое ассемблер транслирует в шест. 00FA (см. на рисунке слева). Поле FLDB определяет слово с десятичным значением 125, котоpое транслируется в шест. 007D. Действительные значения этих двух констант в памяти - FA00 и 7D00 соответственно, что можно проверить c помощью отладчика DEBUG.


------------------------------------------------------------ ------------------------------------------------------------ Рис. 4.3. Листинг ассемблирования программы с сегментом данных.
Поле FLDC определяет слово с неизвестным значением, обозначенным знаком вопроса (?). Сегмент кода в данном примере имеет имя CODESG и отли- чается новыми особенностями, связанными с сегментом данных. Во-первых, директива ASSUME указывает на определние DATASG через регистр DS. Данной программе не требуется регистр ES, но некоторые программисты описывают его для стандартизации. Во-вторых, после команд PUSH, SUB и PUSH, которые инициали- зируют стек, следуют две команды, обеспечивающие адресацию сегмента данных:
0004 B8 ---- R MOV AX,DATASG 0007 8E D8 MOV DS,AX
Первая команда MOV загружает DATASG в регистр AX. Конечно, на самом деле команда не может загрузить сегмент в регистр - она загружает лишь адрес сегмента DATASG. Обратите внимание на машинный код слева:
B8 ---- R
Четыре дефиса говорят о том, что ассемблер не может опреде лить aдрес DATASG; он определяется лишь когда объектная программа будет скомпанована и загружена для выполнения.
Ассемблер для IBM PC. Глава 4 88
Поскольку загpузчик может расположить программу в любом месте памяти, асcемблер оставляет данный адрес открытым и показывает это символом R; компановщик должен будет подста вить в это место действительный адрес. Вторая команда MOV пересылает содержимое регистра AX в регистр DS. Таким образом, данная программа имеет директиву ASSUME, которая соотносит регистр DS с сегментом данных, и команды, инициализирующие регистр DS относительным адресом DATASG. Могут возникнуть два вопроса по поводу этой программы. Во-первых, почему не использовать одну команду для инициали зации регистра DS, например,
MOV DS,DATASG ?
Дело в том, что не существует команд для непосредственной переcылки данных из памяти в регистр DS. Следовательно, для инициализации DS необходимо кодировать две команды. Во-вторых, почему программа инициализирует регистр DS, а регистры SS и CS нет? Оказывается, регистры SS и CS инициализируются автоматически при загрузке программы для выполнения, а ответственность за инициализацию регистра DS и, если требуется ES, лежит полностью на самой программе. Пока все эти требования могут показаться весьма туман ными, но cейчас нет необходимости понимать их. Все последую щие программы используют аналогичную стандартную инициализа цию стека и сегмента данных. Поэтому можно просто копировать данные коды для каждой новой программы. Действительно, вы можете сохранить на диске стандартную часть программы и для каждой новой программы копировать эту часть с новым именем, и, используя затем редактор, записать дополнительные команды. В качестве упражнения, создайте с помощью вашего редактора программу, приведенную на рис. 4.3, выполните ее ассемблирование и компановку. Затем с помощью отладчика DEBUG просмотрите сегмент кодов, сегмент данных, регистры и проделайте пошаговое выполнение программы.


ФАЙЛ ПЕРЕКРЕСТНЫХ ССЫЛОК ------------------------------------------------------------
В процессе трансляции ассемблер создает таблицу идентификаторов (CRF), которая может быть представлена в виде листинга перекрестных ссылок на метки, идентификаторы и переменные в программе. Для получения данного фала, необходимо на четвертый запрос ассемблера, oтветить B:, полагая, что файл должен быть создан на диске B:
cross-reference [NUL.CRF]:B: [Return]
Ассемблер для IBM PC. Глава 4 89
Далее необходимо преобразовать полученный CRF-файл в отсортиpованную таблицу перекрестных ссылок. Для этого на ассемблерном диске имеется соответствующая программа. После успешного ассемблирования введите команду CREF. На экране появится два запроса:
Cref filename [.CRF]: List filename [cross-ref.REF]:
На первый запрос введите имя CRF-файла, т.е. B:EXASM1. На второй запрос можно ввести только номер дисковода и получить имя по умолчанию. Такой выбор приведет к записи CRF в файл перекрестных ссылок по имени EXASM1.REF на дисководе B. Для распечатки файла перекрестных ссылок используйте команду DOS PRINT. В приложении 4 приведен ряд режимов программы CREF.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 4.4. Таблица перекрестных ссылок
На рис. 4.4 показана таблица перекрестных ссылок для программы, приведенной на рис. 4.3. Все идентификаторы в таблице предcтавлены в алфавитном порядке и для каждого из них указаны номеpа строк в исходной программе, где они определены и имеют ссылки. Имена сегментов и элементов данных представлены в алфавитном поpядке. Первое число справа в формате n# указывает на номер строки в LST-файле, где определен соответствующий идентификатор. Еще правее находятся числа, указывающие на номера строк, где имеются cсылки на этот идентификатор. Например, CODESG определен в строке 17 и имеет ссылки на строках 19 и 32.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------


ъ Ассемблер преобразует исходную программу в OBJ-файл, а компановщик - OBJ-файл в загрузочный EXE-файл. ъ Внимательно проверяйте запросы и ответы на них для программ (M)ASM, LINK и CREF прежде чем нажать клавишу Return. Будьте особенно внимательны при указании диско вода. ъ Программа CREF создает распечатку перекрестных ссылок. ъ Удаляйте ненужные файлы с вашего диска. Регулярно пользуйтесь программой CHKDSK для проверки свободного места на диске. Кроме того периодически создавайте резервные копии вашей программы, храните резервную дискету и копируйте ее заново для последующего программирования.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 4 90
4.1. Введите команду MASM и ответьте на запросы для ассемблирования программы по имени TEMPY.ASM с получением файлов LST, OBJ и CRF, полагая, что дискета с программой находится на дисководе B.
4.2. Введите команды для программы TEMPY (из вопроса 4.1) а) для выполнения через отладчик DEBUG, б) для непосредст венного выполнения из DOS.
4.3. Объясните назначение каждого из следующих файлов: а) file.BAK, б) file.ASM, в) file.LST, г) file.CRF, д) file.OBJ, е) file.EXE, ж) file.MAP.
4.4. Напишите две команды для инициализации регистра DS, полагая, что имя сегмента данных - DATSEG.
4.5. Составте ассемблерную программу для:
- пересылки шест. 30 (непосредственное значение) в регистр AL; - сдвига содержимого регистра AL на оди бит влево (команда SHL); - пересылки шест. 18 (непосредственное значение) в регистр BL; - умножения регистра AL на BL (команда MUL BL).
Не забывайте команду RET. В программе нет необходимости определять и инициализировать сегмент данных. Не забы вайте также копировать стандартную часть программы (ос нову программы) и использовать редактор для ее разви тия. Выполните ассемблирование и компановку. Используя отладчик DEBUG, проверте сегмент кодов, регистры и про делайте пошаговое выполнение (трассировку) программы.
4.6. Модифицируйте программу из вопроса 4.5 для:
- определения однобайтовых элементов (директива DB) по имени FLDA, содержащего шест. 28, и по имени FLDB, содержащего шест. 14; - определения двухбайтового элемента (директива DW) по имени FLDC, не имеющего значения; - пересылки содержимого поля FLDA в регистр AL и сдвига на один бит; - умножения содержимого регистра AL на значение в поле FLDB (MUL FLDB); - пересылки результата из регистра AX в поле FLDC.
Для данной программы необходим сегмент данных. Выполни те ассемблирование, компановку программы и тестирование с помощью отладчика DEBUG.
Ассемблер для IBM PC. Глава 5 104


Определение Данных


------------------------------------------------------------

Определение Данных

Цель: Показать методам определения констант и рабочих полей в ассемблерной программе.

ВВЕДЕНИЕ ------------------------------------------------------------

Сегмент данных предназначен для определения констант, рабочих полей и областей для вводв-вывода. В соответствии с имеющимися директивами в ассемблере разрешено определение данных различной длины: например, директива DB определяет байт, а директива DW oпределяет слово. Элемент данных может содержать непосредственное значение или константу, определен ную как символьная строка или как числовое значение. Другим способом определения константы является непосред ственное значение, т.е. указанное прямо в ассемблерной команде, например:

MOV AL,20H

В этом случае шестнадцатеричное число 20 становится частью ма шинного объектного кода. Непосредственное значение ограничено oдним байтом или одним словом, но там, где оно может быть применено, оно является более эффективным, чем использование конcтанты.

ДИРЕКТИВЫ ОПРЕДЕЛЕНИЯ ДАННЫХ ------------------------------------------------------------

Ассемблер обеспечивает два способа определения данных: во-первых, через указание длины данных и, во-вторых, по их cодержимому. Рассмотрим основной формат определения данных:

[имя] Dn выражение

ъ Имя элемента данных не обязательно (это указывается квадратными скобками), но если в программе имеются ссылки на некоторый элемент, то это делается посредством имени. Правила написания имен приведены в разделе "Формат кодирования" в главе 3. ъ Для определения элементов данных имеются следующие директивы: DB (байт), DW (слово), DD (двойное слово), DQ (учетверенное слово) и DT (десять байт). ъ Выражение может содержать константу, например:

FLD1 DB 25

или знак вопроса для неопределенного значения, например

Ассемблер для IBM PC. Глава 5 105

FLDB DB ?

Выражение может содержать несколько констант, разделенных запятыми и ограниченными только длиной строки:


FLD3 DB 11, 12, 13, 14, 15, 16, ...
Ассемблер определяет эти константы в виде последовательности cмежных байт. Ссылка по имени FLD3 указывает на первую константу, 11, по FLD3+1 - на вторую, 12. (FLD3 можно представить как FLD3+0). Например команда
MOV AL,FLD3+3
загружает в регистр AL значение 14 (шест. 0E). Выражение допускает также повторение константы в следующем формате:
[имя] Dn число-повторений DUP (выражение) ...
Следующие три примера иллюстрируют повторение:
DW 10 DUP(?) ;Десять неопределенных слов DB 5 DUP(14) ;Пять байт, содержащих шест.14 DB 3 DUP(4 DUP(8));Двенадцать восмерок
В третьем примере сначала генерируется четыре копии десятич ной 8 (8888), и затем это значение повторяется три раза, давая в pезультате двенадцать восмерок. Выражение может содержать символьную строку или числовую константу.
Символьные строки
Символьная строка используются для описания данных, таких как, например, имена людей или заголовки страниц. Содержимое строки oтмечается одиночными кавычками, например, 'PC' или двойными кавычками - "PC". Ассемблер переводит символьные строки в объектный код в обычном формате ASCII. Символьная строка определяется только директивой DB, в котоpой указывается более двух символов в нормальной последо вательности слева направо. Следовательно, директива DB представляет единственно возможный формат для определения символьных данных. На рис. 5.1 приведен ряд примеров.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 5.1. Определение символьных строк и числовых величин.
Числовые константы
Ассемблер для IBM PC. Глава 5 106
Числовые константы используются для арифметических величин и для aдресов памяти. Для описания константы кавычки не ставятся. Ассемблер преобразует все числовые константы в шестнадцитеричные и записывает байты в объектном коде в обратной последовательности - справа налево. Ниже показаны различные числовые форматы.
Десятичный формат. Десятичный формат допускает десятичные цифры от 0 до 9 и обозначается последней буквой D, которую можно не указывать, например, 125 или 125D. Несмотря на то, что ассемблер позволяет кодирование в десятичном формате, он преобразует эти значения в шест. объектный код. Например, десятичное число 125 преобразуется в шест. 7D.


Шестнадцатиричный формат. Шест. формат допускает шест. цифры от 0 до F и обозначается последней буквой H. Так как ассемблер полагает, что с буквы начинаются идентификаторы, то первой цифрой шест. константы должна быть цифра от 0 до 9. Например, 2EH или 0FFFH, которые ассемблер преобразует соответственно в 2E и FF0F (байты во втором примере записы ваются в объектный код в обратной последовательности).
Двоичный формат. Двоичный формат допускает двоичные цифры 0 и 1 и обозначается последней буквой B. Двоичный формат обычно используется для более четкого представления битовых значений в логических командах AND, OR, XOR и TEST. Десятичное 12, шест. C и двоичное 1100B все генерируют один и тот же код: шест. 0C или двоичное 0000 1100 в зависимости от того, как вы рассматриваете содержимое байта.
Восмеричный формат. Восмеричный формат допускает восмерич ные цифры от 0 до 7 и обозначается последней буквой Q или O, например, 253Q. На сегодня восмеричный формат используется весьма редко.
Десятичный формат с плавающей точкой. Этот формат поддер живается только ассемблером МASM.
При записи символьных и числовых констант следует помнить, что, например, символьная константа, определенная как DB '12', представляет символы ASCII и генерирует шест. 3132, а числовая константа, oпределенная как DB 12, представ ляет двоичное число и генерирует шест. 0C. Рис. 5.1 иллюстрирует директивы для определения различных символьных строк и числовых констант. Сегмент данных был ассемблирован для того, чтобы показать сгенерированный объектный код (слева).
ДИРЕКТИВА ОПРЕДЕЛЕНИЯ БАЙТА (DB) ------------------------------------------------------------
Ассемблер для IBM PC. Глава 5 107
Из различных директив, определяющих элементы данных, наиболее полезной является DB (определить байт). Символьное выражение в диpективе DB может содержать строку символов любой длины, вплоть до конца строки (см. FLD2DB и FLD7DB на рис. 5.1). Обратите внимание, что константа FLD2DB содержит символьную строку 'Personal Computer'. Объектный код показывает символы кода ASCII для каждого байта. Шест. 20 представляет символ пробела. Числовое выражение в директиве DB может содержать одну или более однобайтовых констант. Один байт выражается двумя шест. цифpами. Наибольшее положительное шест. число в одном байте это 7F, все "большие" числа от 80 до FF представляют отрицательные значения. В десятичном исчислении эти пределы выражаются числами +127 и -128. В примере на рис. 5.1 числовыми константами являются FLD3DB, FLD4DB, FLD5DB и FLD8DB. Поле FLD6DB представляет смесь из числовых и строковых констант, используемых для построения таблицы.


ДИРЕКТИВА ОПРЕДЕЛЕНИЯ СЛОВА (DW) ------------------------------------------------------------
Директива DW определяет элементы, которые имеют длину в одно слово (два байта). Символьное выражение в DW ограничено двумя символами, которые ассемблер представляет в объектном коде так, что, например, 'PC' становится 'CP'. Для определения символьных строк директива DW имеет ограниченное применение. Числовое выражение в DW может содержать одно или более двухбайтовых констант. Два байта представляются четырьмя шест. цифрами. Наибольшее положительное шест. число в двух байтах это 7FFF; все "большие" числа от 8000 до FFFF представляют отрицательные значения. В десятичном исчислении эти пределы выражаются числами +32767 и -32768. В примере на рис. 5.1 поля FLD1DW и FLD2DW определяют числовые константы. Поле FLD3DW определяет адрес - в данном случае смещение на адрес FLD7DB. В результате генерируется объектный код 0021 (R обозначает перемещаемость). Проверяя выше по рисунку, видно, что относительный адрес поля FLD7DB действительно 0021. Поле FLD4DW определяет таблицу из пяти числовых констант. Заметим, что объектный код для каждой константы имеет длину в oдно слово (два байта). Для форматов директив DW, DD и DQ ассемблер преобразует константы в шест. объектный код, но записывает его в обратной последовательности. Таким образом десятичное значение 12345 преобразуется в шест.3039, но записывается в объектном коде как 3930.
ДИРЕКТИВА ОПРЕДЕЛЕНИЯ ДВОЙНОГО СЛОВА (DD) ------------------------------------------------------------
Ассемблер для IBM PC. Глава 5 108
Директива DD определяет элементы, которые имеют длину в два cлова (четыре байта). Числовое выражение может содержать одну или более констант, каждая из которых имеет максимум четыре байта (восемь шест. цифр). Наибольшее положительное шест. число в четырех байтых это 7FFFFFFF; все "большие" числа от 80000000 до FFFFFFFF представляют отрицательные значения. В десятичном исчислении эти пределы выражаются числами +2147483647 и -2147483648. В примере на рис. 5.1 поле FLD3DD определяет числовую константу. В поле FLD4DD генерируется разница между двумя адресами, в данном случае результатом является длина поля FLD2DB. Поле FLD5DD определяет две числовые константы. Ассемблер преобразует все числовые константы в директиве DD в шест. представление, но записывает объектный код в обратной последовательности. Таким образом десятичное значение 12345 преобразуется в шест. 00003039, но записывается в oбъектном коде как 39300000. Символьное выражение директивы DD ограничено двумя символами. Ассемблер преобразует символы и выравнивает их слева в четырехбайтовом двойном слове, как показано в поле FLD2DD в объектном коде.


ДИРЕКТИВА ОПРЕДЕЛЕНИЯ УЧЕТВЕРЕННОГО СЛОВА (DQ) ------------------------------------------------------------
Директива DQ определяет элементы, имеющие длину четыре слова (восемь байт). Числовое выражение может содержать одну или более констант, каждая из которых имеет максимум восемь байт или 16 шест.цифр. Наибольшее положительное шест. число - это семерка и 15 цифр F. Для получения представления о величине этого числа, покажем, что шест. 1 и 15 нулей эквивалентен следующему десятичному числу:
1152921504606846976
В примере на рис. 5.1 поля FLD2DQ и FLD3DQ иллюстрируют числовые значения. Ассемблер преобразует все числовые кон станты в директиве DQ в шест. представление, но записывает объектный код в обратной последовательности, как и в дирек- тивах DD и DW. Обработка ассемблером символьных строк в директиве DQ aналогично директивам DD и DW.
ДИРЕКТИВА ОПРЕДЕЛЕНИЯ ДЕСЯТИ БАЙТ (DT) ------------------------------------------------------------
Директива DT определяет элементы данных, имеющие длину в десять байт. Назначение этой директивы связано с "упакованными десятичными" числовыми величинами (см. гл.13). По директиве DT генерируются различные константы, в зависимости от версии ассемблера; для практического применения ознакомьтесь с руководством по вашему aссемблера.
Ассемблер для IBM PC. Глава 5 109
На рис. 5.1 приведены примеры директивы DT для неопределенного элемента и для двухсимвольной константы. Программа на рис.5.1 содержит только сегмент данных. Xотя асcемблер не выдает сообщений об ошибках, в таблице LINK MAP появится предупреждение: "Warning: No STACK Segment", а компановщик LINK выдаст "There were 1 errors detected" (Обнаружена 1 ошибка). Несмотря на это предупреждение можно использовать отладчик DEBUG для просмотра объектного кода, как показано на рис. 5.2. Правая сторона дампа отчетливо показывает символьные данные, как, например, "Personal Computer".
НЕПОСРЕДСТВЕННЫЕ ОПЕРАНДЫ ------------------------------------------------------------


На рис. 2.1 в главе 2 было показано использование непосредственных операндов. Команда
MOV AX,0123H
пересылает непосредственную шест. константу 0123 в регистр AX. Трехбайтный объектный код для этой команды есть B82301, где B8 обозначает "переслать непосредственное значение в регистр AX", a следующие два байта содержат само значение. Многие команды имеют два операнда: первый может быть регистр или адрес памяти, а второй - непосредственная константа.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 5.2. Дамп сегмента данных.
Использование непосредственного операнда более эффектив но, чем oпределение числовой константы в сегменте данных и организация cсылки на нее в операнде команды MOV, например,
Сегмент данных: AMT1 DW 0123H Сегмент кодов: MOV AX,AMT1
Длина непосредственных операндов
Длина непосредственной константы зависит от длины первого операнда. Например, следующий непосредственный операнд является двухбайтовым, но регистр AL имеет только один байт:
MOV AL,0123H (ошибка)
однако, если непосредственный операнд короче, чем получающий операнд, как в следующем примере
ADD AX,25H (нет ошибки)
то ассемблер расширяет непосредственный операнд до двух байт, 0025 и записывает объектный код в виде 2500.
Ассемблер для IBM PC. Глава 5 110
Непосредственные форматы
Непосредственная константа может быть шестнадцатиричной, напpимер, 0123H; десятичной, например, 291 (которую ассемблер конвертирует в шест.0123); или двоичной, например, 100100011В (которая преобразуется в шест. 0123). Ниже приведен список команд, которые допускают непосредственные операнды:
Команды пересылки и сравнения: MOV, CMP. Арифметические команды: ADC, ADD, SBB, SUB. Команды сдвига: RCL, RCR, ROL, ROR, SHL, SAR, SHR. Логические команды: AND, OR, TEST, XOR.
На рис. 5.3 приведены примеры допустимых команд с непосредственными операндами. В последующих главах будут объяснены команды арифметического переноса, сдвига и логические команды. Поскольку сейчас данные примеры не предназначены для выполнения, в них опущено определение стека и инициализация сегментных регистров. Для создания элементов, длинее чем два байта, можно использовать цикл (см. гл.7) или строковые команды (см. гл.11).


------------------------------------------------------------ ------------------------------------------------------------ Рис. 5.3. Команды с непосредственными данными.
ДИРЕКТИВА EQU ------------------------------------------------------------
Директива EQU не определяет элемент данных, но определяет значение, которое может быть использовано для постановки в других командах. Предположим, что в сегменте данных закодирована следующая директива EQU:
TIMES EQU 10
Имя, в данном случае TIMES, может быть представлено любым допустимым в ассемблере именем. Теперь, в какой-бы команде или директиве не использовалось слово TIMES ассемблер подставит значение 10. Например, ассемблер преобразует директиву
FIELDA DB TIMES DUP (?) в FIELDA DB 10 DUP (?)
Имя, связанное с некоторым значением с помощью директивы EQU, может использоваться в командах, например:
COUNTR EQU 05
Ассемблер для IBM PC. Глава 5 111
... MOV CX,COUNTR
Ассемблер заменяет имя COUNTR в команде MOV на значение 05, cоздавая операнд с непосредственным значением, как если бы было закодировано
MOV CX,05 ;Ассемблер подставляет 05
Здесь приемущество директивы EQU заключается в том, что многие команды могут использовать значение, определенное по имени COUNTR. Если это значение должно быть изменено, то изменению подлежит лишь одна директива EQU. Естественно, что использование директивы EQU разумно лишь там, где подстановка имеет смысл для ассемблера. В директиве EQU можно использовать символические имена:
1. TP EQU TOTALPAY 2. MPY EQU MUL
Первый пример предполагает, что в сегменте данных программы опpеделено имя TOTALPAY. Для любой команды, содержащей операнд TP, ассемблер заменит его на адрес TOTALPAY. Второй пример показывает возможность использования в программе слова MPY вместо обычного мнемокода MUL.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Имена элементов данных в программе должны быть уникаль ны и по возможности наглядны. Например, элемент для зарплаты служащего может иметь имя EMPWAGE.


ъ Для определения символьных строк используйте директиву DB, так как ее формат допускает строки длиннее двух байт и формирует их в нормальной последовательности (слева-направо).
ъ Будьте внимательны при указании десятичных и шест. значений. Сравните, например, сложение содержимого регистра AX с десятичным 25 и с шест. 25:
ADD AX,25 ;Прибавить 25 ADD AX,25H ;Прибавить 37
ъ Помните, что директивы DW, DD и DQ записывают числовое значение в объектном коде в обратной последовательности байт.
Ассемблер для IBM PC. Глава 5 112
ъ Используйте элементы DB для операций с полурегистрами (AL, AH, BL и т.д.) и DW для операций с полными регистрами (AX, BX, CX и т.д.). Числовые элементы, определенные директивами DD и DQ имеют специальное применение.
ъ Следите за соответствием непосредственных операндов размеру регистра: однобайтовая константа - однобайтовый регистр (AL, BH), двухбайтовая константа - полный регистр (AX, BX).
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
5.1. Какова длина в байтах для элементов данных, определен ных директивами: а) DW, б) DD, в) DT, г) DB, д) DQ?
5.2. Определите символьную строку по имени TITLE1, содержащую константу RGB Electronics.
5.3. Определите следующие числовые значения в элементах данных с именами от FLDA до FLDE: a) четырехбайтовый элемент, содержащтй шест. эквивалент десятичного числа 115; b) однобайтовый элемент, содержащий шест. эквивалент десятичного числа 25; c) двухбайтовый элемент, содержащий неопределенное значение; d) однобайтовый элемент, содержащий двоичной эквивалент десятичного числа 25; e) директиву DW, содержащую последовательные значения 16, 19, 20, 27, 30.
5.4. Покажите сгенерированный шест. объектный код для а) DB '26' и б) DB 26.
5.5. Определите ассемблерный шест. объектный код для а) DB 26H, б) DW 2645H, в) DD 25733AH, г) DQ 25733AH.
5.6. Закодируйте следующие команды с непосредственными операндами: а) загрузить 320 в регистр AX; б) сравнить поле FLDB с нулем; в) прибавить шест. 40 к содержимому регистра BX; г) вычесть шест. 40 из регистра CX; д) сдвинуть содержимое поля FLDB на один бит влево; е) сдвинуть содержимое регистра CH на один бит вправо.
5.7. Введите и ассемблируйте элементы данных и команды из вопросов 5.2, 5.3 и 5.6. Стек для этого упражнения не требуется. Также не следует выполнять компановку. Для проверки ассемблированного кода используйте отладчик DEBUG. Распечатайте LST-файл (листинг), если в результа
Ассемблер для IBM PC. Глава 5 113
те ассемблирования не будет сообщений об ошибках. Не забудте команду MODE LPT1:132,6 для установки ширины печати.
Ассемблер для IBM PC. Глава 6 123


Программы в COM-файлах


------------------------------------------------------------

Программы в COM-файлах

Цель: Объяснить назначение и использование COM-файлов и перевод ассемблерных программ в формат COM-файлов.

ВВЕДЕНИЕ ------------------------------------------------------------

До сих пор вы писали, ассемблировали и выполняли програм мы в EXE-формате. Компановщик LINK автоматически генерирует особый формат для EXE-файлов, в котором присутствует специальный начальный блок (заголовок) pазмером не менее 512 байт. (В главе 22 рассматривается содержимое начальных блоков). Для выполнения можно также создавать COM-файлы. Примером часто используемого COM-файла является COMMAND.COM. Програм ма EXE2BIN.COM в оперативной системе DOS преобразует EXE- файлы в COM-файлы. Фактически эта программа создает BIN (двоичный) файл, поэтому она и называется "преобразователь EXE в Вin (EXE-to-BIN)". Выходной Вin-файл можно переимено вать в COM-файл.

РАЗЛИЧИЯ МЕЖДУ ПРОГРАММАМИ В EXE и COM-файлах ------------------------------------------------------------

Несмотря на то, что EXE2BIN преобразует EXE-файл в COM-файл, cуществуют определенные различия между программой, выполняемой как EXE-файл и программой, выполняемой как COM-файл.

Размер программы. EXE-программа может иметь любой размер, в то время как COM-файл ограничен размером одного сегмента и не превышает 64К. COM-файл всегда меньше, чем соответствую щий EXE-файл; одна из причин этого - отсутствие в COM-файле 512-байтового начального блока EXE-файла.

Сегмент стека. В EXE-программе определяется сегмент сте ка, в то время как COM-программа генерирует стек автоматичес ки. Таким образом при создании ассемблерной программы, которая будет преобразована в COM-файл, стек должен быть опущен.

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

Ассемблер для IBM PC. Глава 6 124


Инициализация. EXE-программа записывает нулевое слово в стек и инициализирует регистр DS. Так как COM-программа не имеет ни стека, ни сегмента данных, то эти шаги отсутствуют. Когда COM-программа начинает работать, все сегментные ре гистры содержат адрес префикса программного сегмента (PSP), - 256-байтового (шест. 100) блока, который резервируется операционной системой DOS непосредственно перед COM или EXE программой в памяти. Так как адресация начинается с шест. смещения 100 от начала PSP, то в программе после оператора SEGMENT кодируется директива ORG 100H.
Обработка. Для программ в EXE и COM форматах выполняется ассемблирование для получения OBJ-файла, и компановка для получения EXE-файла. Если программа создается для выполнения как EXE-файл, то ее уже можно выполнить. Если же программа создается для выполнения как COM-файл, то компановщиком будет выдано сообщение:
Warning: No STACK Segment (Предупреждение: Сегмент стека не определен)
Это сообщение можно игнорировать, так как определение стека в программе не предполагалось. Для преобразования EXE-файла в COM-файл используется программа EXE2BIN. Предположим, что EXE2BIN имеется на дисководе A, а скомпанованный файл по имени CALC.EXE - на дисководе B. Введите
EXE2BIN B:CALC,B:CALC.COM
Так как первый операнд всегда предполагает EXE файл, то можно не кодировать тип EXE. Второй операнд может иметь другое имя (не CALC.COM). Если не указывать тип COM, то EXE2BIN примет по умолчанию тип BIN, который впоследствии можно переименовать в COM. После того как преобразование будет выполнено можно удалить OBJ- и EXE-файлы. Если исходная программа написана для EXE-формата, то мож но, используя редактор, заменить команды в исходном тексте для COM файла.
ПРИМЕР COM-ПРОГРАММЫ ------------------------------------------------------------
Программа EXCOM1, приведенная на рис. 6.1, аналогична программе на рис. 4.3, но изменена согласно требований COM- формата. Обратите внимание на следующие изменения в этой COM-программе: ъ Стек и сегмент данных отсутствует. ъ Оператор ASSUME указывает ассемблеру установить относи тельные адреса с начала сегмента кодов. Регистр CS также содержит этот адрес, являющийся к тому же адресом префикса программного сегмента (PSP). Директива ORG служит для резервирования 100 (шест.) байт от начально го адреса под PSP.


Ассемблер для IBM PC. Глава 6 125
ъ Директива ORG 100H устанавливает относительный адрес для начала выполнения программы. Программный загрузчик использует этот адрес для командного указателя. ъ Команда JMP используется для обхода данных, определен ных в программе.
Ниже показаны шаги для обработки и выполнения этой программы:
MASM [ответы на запросы обычные] LINK [ответы на запросы обычные] EXE2BIN B:EXCOM1,B:EXCOM1.COM DEL B:EXCOM1.OBJ,B:EXCOM1.EXE (удаление OBJ и EXE-файлов)
Размеры EXE- и COM-программ - 788 и 20 байт соответствен но. Учитывая такую эффективность COM-файлов, рекомендуется все небольшие программы создавать для COM-формата. Для трассировки выполнения программы от начала (но не включая) команды RET введите DEBUG B:EXCOM1.COM. Некоторые программисты кодируют элементы данных после команд так, что первая команда JMP не требуется. Кодирование элементов данных перед командами позволяет ускорить процесс ассемблирования и является методикой, рекомендуемой в руководстве по ассемблеру.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 6.1. Пример COM-программы.
СТЕК ДЛЯ COM-ПРОГРАММЫ ------------------------------------------------------------
Для COM-файла DOS автоматически определяет стек и устанав ливает oдинаковый общий сегментный адрес во всех четырех сегментных pегистрах. Если для программы размер сегмента в 64К является достаточным, то DOS устанавливает в регистре SP адрес конца cегмента - шест.FFFE. Это будет верх стека. Если 64К байтовый сегмент не имеет достаточно места для стека, то DOS устанавливает стек в конце памяти. В обоих случаях DOS записывает затем в стек нулевое слово. Возможность использования стека зависит от размера про граммы и ограниченности памяти. С помощью команды DIR можно определить pазмер файла и вычислить необходимое пространство для стека. Все небольшие программы в этой книге в основном расчитаны на COM-формат.
ОСОБЕННОСТЬ ОТЛАДКИ ------------------------------------------------------------


Несоблюдение хотя бы одного требования COM-формата может послужить причиной неправильной работы программы. Если EXE2BIN обнаруживает oшибку, то выдается сообщение о
Ассемблер для IBM PC. Глава 6 126
невозможности преобразования файла без указания конкретной причины. Необходимо проверить в этом случае директивы SEGMENT, ASSUME и END. Если опущен ORG 100H, то на данные в префиксе программного сегмента будут установлены неправиль ные ссылки с непредсказуемым результатом при выполнении. При выполнении COM-программы под управлением отладчика DEBUG необходимо использовать команду D CS:100 для просмотра данных и команд. Не следует выполнять в отладчике команду RET; предпочтительнее использовать команду Q отладчика. Некоторые программисты используют INT 20H вместо команды RET. Попытка выполнить EXE-модуль программы, написанной для COM-формата, не имеет успеха.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Объем COM-файла ограничен 64К.
ъ COM-файл меньше, чем соответствующий EXE-файл.
ъ Программа, написанная для выполнения в COM-формате не содержит стека и сегмента данных и не требует инициали зации регистра DS.
ъ Программа, написанная для выполнения в COM-формате использует директиву ORG 100H после директивы SEGMENT для выполнения с адреса после префикса программного сегмента.
ъ Программа EXE2BIN преобразует EXE-файл в COM-файл, обусловленный указанием типа COM во втором операнде.
ъ Операционная система DOS определяет стек для COM-прог раммы или в конце программы, если позволяет размер, или в конце памяти.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
6.1. Каков максимальный размер COM-файла?
6.2. Какие сегменты можно определить в программе, которая будет преобразована в COM-файл?
6.3. Как обходится COM-файл при выполнении с фактом отсут ствия определения стека?
6.4. Программа в результате компановки получала имя SAMPLE.EXE. Напишите команду DOS для преобразования ее в COM-файл.
Ассемблер для IBM PC. Глава 6 127
6.5. Измените программу из вопроса 4.6 для COM-формата, обра ботайте ее и выполните под управлением отладчика DEBUG.
Ассемблер для IBM PC. Глава 7 132


Логика и Организация Программы


------------------------------------------------------------

Логика и Организация Программы

Цель: Раскрыть механизм передачи управления в программе (циклы и переходы) для логических сравнений и программной организации.

ВВЕДЕНИЕ ------------------------------------------------------------

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

Безусловный переход: JMP Цикл: LOOP Условный переход: Jnnn (больше,меньше,равно) Вызов процедуры: CALL

Заметим, что имеется три типа адресов: SHORT, NEAR и FAR. Адресация SHORT используется при циклах, условных пеpеходах и некоторых безусловных переходах. Адресация NEAR и FAR используется для вызовов процедур (CALL) и безусловных переходов, которые не квалифицируются , как SHORT. Все три типа передачи управления воздействуют на содержимое регистра IP; тип FAR также изменяет регистр CS.

КОМАНДА JMP ------------------------------------------------------------

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


В COM-программе на рис. 7.1 используется команда JMP. В pегистры AX, BX, и CX загружается значение 1, и затем в цикле выполняются следующие операции:
прибавить 1 к регистру AX, прибавить AX к BX, удвоить значение в регистре CX.
Повторение цикла приводит к увеличению содержимого регистра AX: 1,2,3,4..., регистра BX: 1,3,6,10..., и регистра CX: 1,2,4,8... Начало цикла имеет метку, в данном случае, A20: - двоетичие oбозначает, что метка находится внутри процедуры (в данном случае BEGIN) в сегменте кода. В конце цикла находится команда
JMP A20
которая указывает на то, что управление должно быть передано команде c меткой A20. Обратите внимание, что адресная метка в операнде команды указывается без двоеточия. Данный цикл не имеет выхода и приводит к бесконечному выполнению - такие циклы обычно не используются.
------------------------------------------------------------ ------------------------------------------------------------ Рис.7.1. Использование команды JMP.
Метку можно кодировать на одной строке с командой:
A20: ADD AX,01
или на отдельной строке:
A20: ADD AX,01
В обоих случаях адрес A20 указывает на первый байт команды ADD. Двоеточие в метке A20 указывает на тип метки - NEAR. Запомните: отсутствие двоеточия в метке является частой ошибкой. В нашем примере A20 соответствует -9 байтам от команды JMP, в чем можно убедиться по объектному коду команды - EBF7. EB представляет собой машинный код для короткого перехода JMP, а F7 - отрицательное значение смещения (-9). Команда JMP прибавляет F7 к командному указателю (IP), котоpый содержит адрес команды после JMP (0112):
Дес. Шест. Командный указатель: 274 112 Адрес в команде JMP: -9 F7 (двоичное дополнение) Адрес перехода: 265 109
Ассемблер для IBM PC. Глава 7 134
В результате сложения получается адрес перехода - шест. 109. Проверьте по листингу программы, что относительный адрес метки действительно соответствует шест.109. Соответственно операнд в команде JMP для перехода вперед имеет положитель ное значение. Команда JMP для перехода в пределах -128 до +127 байт имеет тип SHORT. Ассемблер генерирует в этом случае однобайтовый операнд в пределах от 00 до FF. Команда JMP, превосходящая эти пределы, получает тип FAR, для которого генерируется другой машинный код и двухбайтовый операнд. Ассемблер в первом просмотре исходной программы определяет длину каждой команды. Однако, команда JMP может быть длиной два или три байта. Если к моменту просмотра команды JMP ассемблер уже вычислил значение опеpанда (при переходе назад): A50: ... JMP A50


то он генерирует двухбайтовую команду. Если ассемблер еще не вычислил значение операнда (при переходе вперед)
JMP A90 ... A90:
то он не знает тип перехода NEAR или FAR, и автоматически генерирует 3-х байтовую команду. Для того, чтобы указать ассемблеру на необходимость генерации двухбайтовой команды, следует использовать оператор SHORT:
JMP SHORT A90 ... A90:
В качестве полезного упражнения, введите программу, проассемблируйте ее, скомпануйте и переведите в COM-формат. Определение данных не требуется, поскольку непосредственные операнды генерируют все необходимые данные. Используйте отладчик DEBUG для пошагового выполнения COM-модуля и просмотрите несколько повторений цикла. Когда регистр AX будет содержать 08, BX и CX увеличатся до шест. 24 (дес. 36) и шест. 80 (дес. 128), соответственно. Для выхода из отладчика используйте команду Q.
КОМАНДА LOOP ------------------------------------------------------------
Команда JMP в примере на рис. 7.1 реализует бесконечный цикл. Но более вероятно подпрограмма должна выполнять определенное число циклов. Команда LOOP, которая служит для этой цели, использует начальное значение в регистре CX. В каждом цикле команда LOOP автоматически уменьшает содержимое
Ассемблер для IBM PC. Глава 7 135
регистра CX на 1. Пока значение в CX не равно нулю, управление передается по адресу, указанному в операнде, и если в CX будет 0, управление переходит на слудующую после LOOP команду. Программа на рис. 7.2, иллюстрирующая использование коман ды LOOP, выполняет действия, аналогичные примеру на рис. 7.1 за исключением того, что после десяти циклов программа завершается. Команда MOV инициализирует регистр CX значением 10. Так как команда LOOP использует регистр CX, то в программе для удвоения начального значения 1 вместо регистра CX используется DX. Команда JMP A20 заменена командой LOOP и для эффективности команда ADD AX,01 заменена командой INC AX (увеличение AX на 1). Аналогично команде JMP, операнд команды LOOP определяет расстояние от конца команды LOOP до адреса метки A20, кото рое прибавляется к содержимому командного указателя. Для команды LOOP это расстояние должно быть в пределах от -128 до +127 байт. Если операнд превышает эти границы, то ассемб лер выдаст сообщение "Relative jump out of range" (превышены границы перехода). Для проверки команды LOOP рекомендуется изменить соответствующим образом программу, приведенную на рис.7.1, выполнить ее ассемблирование, компановку и преобразование в COM-файл. Для трассировки всех десяти циклов используйте отладчик DEBUG. Когда в значение регистре CX уменьшится до нуля, содержимое регистpов AX, BX и DX будет соответственно шест. 000B, 0042 и 0400. Для выхода из отладчика введите команду Q. Дополнительно существует две разновидности команды LOOP - это LOOPE (или LOOPZ) и LOOPNE (или LOOPNZ). Обе команды также уменьшают значение регистра CX на 1. Команда LOOPE передает управление по адресу операнда, если регистр CX имеет ненулевое значение и флаг нуля установлен (ZF=1). Команда LOOPNE передает управление по адресу операнда, если регистр CX имеет ненулевое значение и флаг нуля сброшен (ZF=0).


------------------------------------------------------------ ------------------------------------------------------------ Рис. 7.2. Использование команды LOOP.
ФЛАГОВЫЙ РЕГИСТР ------------------------------------------------------------
Следующий материал данной главы требует более детального ознакомления с флаговым регистром. Этот pегистр содержит 16 бит флагов, которые управляются различными командами для индикации состояния операции. Во всех случаях флаги сохраня ют свое значение до тех пор, пока другая команда не изменит его. Флаговый регистр содержит следующие девять используемых бит (звездочками отмечены неиспользуемые биты):
Номер бита: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Ассемблер для IBM PC. Глава 7 136
Флаг: * * * * O D I T S Z * A * P * C
Рассмотрим эти флаги в последовательности справа налево.
CF (Carry Flag) - флаг переноса. Содержит значение "переносов" (0 или 1) из старшего разряда при арифметичес ких операциях и некоторых операциях сдвига и циклического сдвига (см. гл.12).
PF (Parity Flag) - флаг четности. Проверяет младшие восемь бит pезультатов операций над данными. Нечетное число бит приводит к установке этого флага в 0, а четное - в 1. Не следует путать флаг четности с битом контроля на четность.
AF (Auxiliary Carry Flag) - дополнительный флаг переноса. Устанавливается в 1, если арифметическая операция приводит к переносу четвертого справа бита (бит номер 3) в регистро вой однобайтовой команде. Данный флаг имеет отношение к арифметическим операциям над символами кода ASCII и к десятичным упакованным полям.
ZF (Zero Flag) - флаг нуля. Устанавливается в качестве результата aрифметических команд и команд сравнения. Как это ни странно, ненулевой результат приводит к установке нулевого значения этого флага, а нулевой - к установке единичного значения. Кажущееся несоответствие является, однако, логически правильным, так как 0 обозначает "нет" (т.е. результат не равен нулю), а единица обозначаeт "да" (т.е. результат равен нулю). Команды условного перехода JE и JZ проверяют этот флаг.


SF (SIgn Flag) - знаковый флаг. Устанавливается в соответ ствии со знаком результата (старшего бита) после арифмети ческих опеpаций: положительный результат устанавливает 0, а отрицательный - 1. Команды условного перехода JG и JL проверяют этот флаг.
TF (Trap Flag) - флаг пошагового выполнения. Этот флаг вам уже приходилось устанавливать, когда использовалась ко манда Т в отладчике DEBUG. Если этот флаг установлен в еди ничное cостояние, то процессор переходит в режим пошагового выполнения команд, т.е. в каждый момент выполняется одна команда под пользовательским управлением.
IF (Interrupt Flag) - флаг прерывания. При нулевом состоя нии этого флага прерывания запрещены, при единичном - разрешены.
DF (DIrection Flag) - флаг направления. Используется в строковых операциях для определения направления передачи данных. При нулевом состоянии команда увеличивает содержимое
Ассемблер для IBM PC. Глава 7 137
регистров SI и DI, вызывая передачу данных слева направо, при нулевом - уменьшает содержимое этих регистров, вызывая передачу данных справа налево (см. гл.11).
OF (Overflow Flag) - флаг переполнения. Фиксирует арифме тическое переполнение, т.е. перенос в/из старшего (знаково го) бита при знаковых арифметических операциях. В качестве примера: команда CMP сравнивает два операнда и воздействуте на флаги AF, CF, OF, PF, SF, ZF. Однако, нет необходимости проверять все эти флаги по отдельности. В сле- дующем примере проверяется содержит ли регистр BX нулевое значение:
CMP BX,00 ;Сравнение BX с нулем JZ B50 ;Переход на B50 если нуль . (действия при ненуле) . B50: ... ;Точка перехода при BX=0
Если BX содержит нулевое значение, команда CMP устанавливает флаг нуля ZF в единичное состояние, и возможно изменяет (или нет) другие флаги. Команда JZ (перехлд если нуль) проверяет только флаг ZF. При единичном значении ZF, обозначающее нулевой признак, команда передает управление на адрес, указанный в ее операнде, т.е. на метку B50.
КОМАНДЫ УСЛОВНОГО ПЕРЕХОДА ------------------------------------------------------------


В предыдущих примерах было показано, что команда LOOP уменьшает на единицу содержимое регистра CX и проверяет его: если не ноль, то управление передается по адресу, указанному в операнде. Таким образом, передача управления зависит от конкретного состояния. Ассемблер поддерживает большое количество команд условного перехода, которые осуществляют передачу управления в зависимости от состояний флагового регистра. Например, при сравнении содержимого двух полей последующий переход зависит от значения флага. Команду LOOP в программе на рис.7.2 можно заменить на две команды: одна уменьшает содержимое регистра CX, а другая выполняет условный переход:
Использование LOOP Использование условного перехода
LOOP A20 DEC CX JNZ A20 Команды DEC и JNZ действуют аналогично команде LOOP: уменьшают содержимое регистра CX на 1 и выполняет переход на метку A20, если в CX не ноль. Команда DEC кроме того устанавливает флаг нуля во флаговом регистре в состояние 0 или 1. Команда JNZ затем проверяет эту установку. В рассмот ренном примере команда LOOP хотя и имеет огпаниченное исполь зование, но более эффективна, чем две команды: DEC и JNZ.
Ассемблер для IBM PC. Глава 7 138
Аналогично командам JMP и LOOP операнд в команде JNZ cодержит значение расстояния между концом команды JNZ и адресом A20, которое прибавляется к командному указателю. Это расстояние должно быть в пределах от -128 до +127 байт. В случае перехода за эти границы ассемблер выдаст сообщение "Relative jump out of range" (превышены относительные грани цы перехода).
Знаковые и беззнаковые данные.
Рассматривая назначение команд условного перехода следует пояснить характер их использования. Типы данных, над которы ми выполняются арифметические операции и операции сравнения определяют какими командами пользоваться: беззнаковыми или знаковыми. Беззнаковые данные используют все биты как биты данных; характерным примером являются символьные строки: имена, адреса и натуральные числа. В знаковых данных самый левый бит представляет собой знак, причем если его значение равно нулю, то число положительное, и если единице, то отрицательное. Многие числовые значения могут быть как положительными так и отрицательными. В качестве примера предположим, что регистр AX содержит 11000110, а BX - 00010110. Команда


CMP AX,BX
сравнивает содержимое регистров AX и BX. Если данные беззнаковые, то значение в AX больше, а если знаковые - то меньше.
Переходы для беззнаковых данных.
Мнемоника Описание Проверяемые флаги
JE/JZ Переход, если равно/нуль ZF JNE/JNZ Переход, если не равно/не нуль ZF JA/JNBE Переход, если выше/не ниже или равно ZF,CF JAE/JNB Переход, если выше или равно/не ниже CF JB/JNAE Переход, если ниже/не выше или равно CF JBE/JNA Переход, если ниже или равно/не выше CF,AF
Любую проверку можно кодировать одним из двух мнемоничес ких кодов. Например, JB и JNAE генерирует один и тот же объектный код, хотя положительную проверку JB легче понять, чем отрицательную JNAE.
Переходыдля знаковых данных
Мнемоника Описание Проверяемые флаги
JE/JZ Переход, если равно/нуль ZF JNE/JNZ Переход, если не равно/не нуль ZF JG/JNLE Переход, если больше/не меньше или равно ZF,SF,OF
Ассемблер для IBM PC. Глава 7 139
JGE/JNL Переход, если больше или равно/не меньше SF,OF JL/JNGE Переход, если меньше/не больше или равно SF,OF JLE/JNG Переход, если меньше или равно/не больше ZF,SF,OF
Команды перехода для условия равно или ноль (JE/JZ) и не равно или не ноль (JNE/JNZ) присутствуют в обоих списках для беззнаковых и знаковых данных. Состояние равно/нуль происходит вне зависимости от наличия знака.
Специальныеарифметическиепроверки
Мнемоника Описание Проверяемые флаги
JS Переход, если есть знак (отрицательно) SF JNS Переход, если нет знака(положительно) SF JC Переход, если есть перенос (аналогично JB) CF JNC Переход, если нет переноса CF JO Переход, если есть переполнение OF JNO Переход, если нет переполнения OF JP/JPE Переход, если паритет четный PF JNP/JP Переход, если паритет нечетный PF
Еще одна команда условного перехода проверяет равно ли содержимое регистра CX нулю. Эта команда необязательно должна pасполагаться непосредственно за командой арифметики или сравнения. Одним из мест для команды JCXZ может быть начало цикла, где она проверяет содержит ли регистр CX ненулевое значение. Не спешите пока заучивать эти команды наизусть. Запомните только, что для беззнаковых данных есть переходы по состоя ниям равно, выше или ниже, а для беззнаковых - равно, больше или меньше. Переходы по проверкам флагов переноса, переполнения и паритета имеют особое назначение. Ассемблер транслирует мнемонические коды в объектный код независимо от того, какую из двух команд вы применили. Однако, команды JAE и JGE являясь явно одинаковыми, проверяют различные флаги.


ПРОЦЕДУРЫ И ОПЕРАТОР CALL ------------------------------------------------------------
В предыдущих главах примеры содержали в кодовом сегменте только oдну процедуру, оформленную следующим образом:
BEGIN PROC FAR . . BEGIN ENDP
Операнд FAR информирует систему о том, что данный адрес явля ется точкой входа для выполнения, а директива ENDP определя ет конец процедуры. Кодовый сегмент, однако, может содержать
Ассемблер для IBM PC. Глава 7 140
любое количество процедур, которые разделяются директивами PROC и ENDP. Типичная организация многопроцедурной программы приведена на рис. 7.3. Обратите внимание на следующие особенности:
ъ директивы PROC по меткам B10 и C10 имеют операнд NEAR для указания того, что эти процедуры находятся в теку щем кодовом сегменте. Во многих последующих примерах этот операнд опущен, так как по умолчанию ассемблер принимает тип NEAR.
ъ Каждая процедура имеет уникальное имя и содержит соб ственную директиву ENDP для указания конца процедуры.
ъ Для передачи управления в процедуре BEGIN имеются две команды: CALL B10 и CALL C10. В результате первой коман ды CALL управление передается процедуре B10 и начинает ся ее выполнение. Достигнув команды RET, управление возвращается на команду непосредственно следующую за CALL B10. Вторая команда CALL действует аналогично - передает управление в процедуру C10, выполняет ее команды и возвращает управление по команде RET.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 7.3. Вызов процедур.
ъ Команда RET всегда выполняет возврат в вызывающую про грамму. Программа BEGIN вызывает процедуры B10 и C10, которые возвращают управление обратно в BEGIN. Для выполнения самой программы BEGIN операционная система DOS вызывает ее и в конце выполнения команда RET возвра щает управление в DOS. Если процедура B10 не содержит завершающей команды RET, то выполнение команд продолжит ся из B10 непосредственно в процедуре C10. Если процедура C10 не содержит команды RET, то будут выпол няться команды, оказавшиеся за процедурой C10 с непред сказуемым результатом.


Использование процедур дает хорошую возможность организо вать логическую структуру программы. Кроме того, операнды для команды CALL могут иметь значения, выходящие за границу от -128 до +127 байт. Технически управление в процедуру типа NEAR может быть передано с помощью команд перехода или даже обычным построч ным кодированием. Но в большенстве случаев рекомендуется использовать команду CALL для передачи управления в проце дуру и команду RET для возврата.
СЕГМЕНТ СТЕКА ------------------------------------------------------------
Ассемблер для IBM PC. Глава 7 141
До этого раздела в приводимых примерах встретились только две команды, использующих стек, - это команды PUSH в начале сегмента кодов, которые обеспечивают возврат в DOS, когда EXE-программа завершается. Естественно для этих программ требуется стек oчень малого размера. Однако, команда CALL автоматически записывает в стек относительный адрес команды, следующей непосредственно за командой CALL, и увеличивает после этого указатель вершины стека. В вызываемой процедуре команда RET использует этот адрес для возврата в вызывающую процедуру и при этом автоматически уменьшается указатель вершины стека. Таким образом, команды PUSH записывают в стек двухбайто вые адреса или другие значения. Команды POP обычно выбирают из стека записанные в него слова. Эти операции изменяют отно сительный адрес в регистре SP (т.е. в указатели стека) для доступа к следующему слову. Данное свойство стека требует чтобы команды RET и CALL соответствовали друг другу. Кроме того, вызванная процедура может вызвать с помощью команды CALL другую процедуру, а та в свою очередь - следующую. Стек должен иметь достаточные размеры для того, чтобы хранить все записываемые в него адреса. Для большенства примеров в дан ной книге стек объемом в 32 слова является достаточным. Команды PUSH, PUSHF, CALL, INT, и INTO заносят в стек адрес возврата или содержимое флагового регистра. Команды POP, POPF, RET и IRET извлекают эти aдреса или флаги из стека. При передаче управления в EXE-программу система устанавли вает в регистрах следующие значения:


DS и ES: Адрес префикса программного сегмента - область в 256 (шест. 100) байт, которая предшествует выполняемому программному модулю в памяти.
CS: Адрес точки входа в программу (адрес первой выполняемой команды).
IP: Нуль.
SS: Адрес сегмента стека.
SP: Относительный адрес, указывающий на вершину стека. Например, для стека в 32 слова (64 байта), определенного как
DW 32 DUP(?)
SP содержит 64, или шест. 40.
Выполним трассировку простой EXE-программы, приведенной на рис.7.4. На практике вызываемые процедуры содержат любое число команд. Текущая доступная ячейка стека для занесения или извлечения слова является вершина стека. Первая команда PUSH уменьшает значение SP на 2 и заносит содержимое регистра
Ассемблер для IBM PC. Глава 7 142
DS (в данном примере 049f) в вершину стека, т.е. по адресу 4B00+3E. Вторая команда PUSH также уменьшает значение SP на 2 и записывает содержимое регистра AX (0000) по адресу 4B00+3C. Команда CALL B10 уменьшает значение SP и записывает относительный адрес следующей команды (0007) в стек по адресу 4B00+3A. Команда CALL C10 уменьшает значение SP и записывает относительный адрес следующей команды (000B) в стек по адресу 4B00+38. При возврате из процедуры C10 команда RET извлекает 000B из стека (4B00+38), помещает его в указатель команд IP и увеличивает значение SP на 2. При этом происходит автомати ческий возврат по относительному адресу 000B в кодовом сегменте, т.е. в процедуру B10.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 7.4. Воздействие выполнения программы на стек.
Команда RET в конце процедуры B10 извлекает адрес 0007 из стека (4B00+3A), помещают его в IP и увеличивает значение SP на 2. При этом происходит автоматический возврат по относи тельному адресу 0007 в кодовом сегменте. Команда RET по адресу 0007 завершает выполнение программы, осуществляя возврат типа FAR. Ниже показано воздействие на стек при выполнении каждой команды. Для трассировки программы можно использовать отладчик DEBUG. Приведено только содержимое памяти с адреса 0034 до 003F и содержимое регистра SP:


Команда Стек SP
Начальное значение: хххх хххх хххх хххх хххх хххх 0040 PUSH DS (запись 049F) хххх хххх хххх хххх хххх 049F 003E PUSH AX (запись 0000) хххх хххх хххх хххх 0000 049F 003C CALL B10 (запись 0007) хххх хххх хххх 0700 0000 049F 003A CALL C10 (запись 000B) хххх хххх 0B00 0700 0000 049F 0038 RET (выборка 000B) хххх хххх хххх 0700 0000 049F 003A RET (выборка 0007) хххх хххх хххх хххх 0000 049F 003C | | | | | | Смещение в стеке: 0034 0036 0038 003A 003C 003E
Обратите внимание на два момента. Во-первых, слова в памя ти содержат байты в обратной последовательности, так 0007 записывается в виде 0700. Во-вторых, отладчик DEBUG при использовании его для просмотра стека заносит в стек другие значения, включая содержимое IP, для собственных нужд.
ПРОГРАММА: РАСШИРЕННЫЕ ОПЕРАЦИИ ПЕРЕСЫЛКИ ------------------------------------------------------------
В предыдущих программах были показаны команды пересылки непосредcтвенных данных в регистр, пересылки данных из памяти в регистр, пересылки содержимого регистра в память и
Ассемблер для IBM PC. Глава 7 143
пересылки содержимого oдного регистра в другой. Во всех случаях длина данных была огpаничена одним или двумя байтами и не предусмотрена пересылка данных из одной области памяти непосредственно другую область. В данном разделе объясняется процесс пересылки данных, которые имееют длину более двух байт. В главе 11 будет показано использование операций над строками для пересылки данных из одной области памяти непосредственно в другую область. В EXE-программе, приведенной на рис. 7.5, сегмент данных cодержит три девятибайтовых поля, NAME1, NAME2, NAME3. Цель программы - переслать данные из поля NAME1 в поле NAME2 и переслать данные из поля NAME2 в поле NAME3. Так как эти поля имеют длину девять байт каждая, то для пересылки данных кроме простой команды MOV потребуются еще другие команды. Программа содержит несколько новых особенностей. Процедура BEGIN инициализирует сегментные регистры и затем вызывает процедуры B10MOVE и C10MOVE. Процедура B10MOVE пересылает содержимое поля NAME1 в поле NAME2. Так как каждый раз пересылается только один байт, то процедура начинает с самого левого байта в поле NAME1 и в цикле пересы лает затем второй байт, третий и т.д.:


------------------------------------------------------------ ------------------------------------------------------------ Рис. 7.5. Расширенные операции пересылки.
NAME1: A B C D E F G H I | | | | | | | | | NAME2: J K L M N O P Q R
Для продвижения в полях NAME1 и NAME2 в регистр CX заносится значение 9, а регистры SI и DI используются в качестве индексных. Две команды LEA загружают относительные aдреса полей NAME1 и NAME2 в регистры SI и DI:
LEA SI,NAME1 ;Загрузка относительных адресов LEA DI,NAME2 ; NAME1 и NAME2
Для пересылки содержимого первого байта из поля NAME1 в первый байт поля NAME2 используются адреса в регистрах SI и DI. kвадратные скобки в командах MOV обозначают, что для доступа к памяти используется адрес в регистре, указанном в квадратных cкобках. Таким образом, команда
MOV AL,[SI]
означает: использовать адрес в регистре SI (т.е.NAME1) для пересылки соответствующего байта в регистр AL. А команда
MOV [DI],AL
означает: пересылать содержимое регистра AL по адресу, лежащему в регистре DI (т.е. NAME2).
Ассемблер для IBM PC. Глава 7 144
Следующие команды увеличивают значения регистров SI и DI и уменьшают значение в регистре SH. Если в регистре CX не нулевое значение, управление передается на следующий цикл (на метку B20).Т ак как содержимое регистров SI и DI было увеличено на 1, то следующие команды MOV будут иметь дело с адресами NAME1+1 и NAME2+1. Цикл продолжается таким образом, пока не будет передано содержимое NAME1+8 и NAME2+8. Процедура C10MOVE аналогична процедуре B10MOVE с двумя исключениями: она пересылает данные из поля NAME2 в поле NAME3 и использует команду LOOP вместо DEC и JNZ. Задание: Введите программу, приведенную на рис.7.5, выполните ее ассемблирование, компановку и трассировку с помощью отладчика DEBUG. Обратите внимание на изменения в регистрах, командном указателе и в стеке. Для просмотра изменений в полях NAME2 и NAME3 используйте команду D DS:0.
КОМАНДЫ ЛОГИЧЕСКИХ ОПЕРАЦИЙ: AND, OR, XOR, TEST, NOT ------------------------------------------------------------


Логические операции являются важным элементом в проектировании микросхем и имеют много общего в логике программирования. Команды AND, OR, XOR и TEST - являются командами логических операций. Эти команды используются для сброса и установки бит и для арифметических операций в коде ASCII (см.гл.13). Все эти команды обрабатывают один байт или одно слово в регистре или в памяти, и устанавливают флаги CF, OF, PF, SF, ZF.
AND: Если оба из сравниваемых битов равны 1, то результат равен 1; во всех остальных случаях результат - 0.
OR: Если хотя бы один из сравниваемых битов равен 1, то результат равен 1; если сравниваемые биты равны 0, то результат - 0.
XOR: Если один из сравниваемых битов равен 0, а другой равен 1, то результат равен 1; если сравниваемые биты одинаковы (оба - 0 или оба - 1) то результат - 0.
TEST: действует как AND-устанавливает флаги, но не изменяет биты.
Первый операнд в логических командах указывает на один байт или слово в регистре или в памяти и является единствен ным значением, которое может изменятся после выполнения команд. В следующих командах AND, OR и XOR используются одинаковые битовые значения:
AND OR XOR 0101 0101 0101 0011 0011 0011 Результат: 0001 0111 0110
Ассемблер для IBM PC. Глава 7 145
Для следующих несвязанных примеров, предположим, что AL содержит 1100 0101, а BH содержит 0101 1100:
1. AND AL,BH ;Устанавливает в AL 0100 0100 2. OR BH,AL ;Устанавливает в BH 1101 1101 3. XOR AL,AL ;Устанавливает в AL 0000 0000 4. AND AL,00 ;Устанавливает в AL 0000 0000 5. AND AL,0FH ;Устанавливает в AL 0000 0101 6. OR CL,CL ;Устанавливает флаги SF и ZF
Примеры 3 и 4 демонстрируют способ очистки регистра. В примере 5 обнуляются левые четыре бита регистра AL. Хотя команды сравнения CMP могут быть понятнее, можно применить команду OR для следующих целей:
1. OR CX,CX ;Проверка CX на нуль JZ ... ;Переход, если нуль 2. OR CX,CX ;Проверка знака в CX JS ... ;Переход, если отрицательно
Команда TEST действует аналогично команде AND, но устанавливает только флаги, а операнд не изменяется. Ниже придено несколько примеров:


1. TEST BL,11110000B ;Любой из левых бит в BL JNZ ... ; равен единице? 2. TEST AL,00000001B ;Регистр AL содержит JNZ ... ; нечетное значение? 3. TEST DX,OFFH ;Регистр DX содержит JZ ... ; нулевое значение?
Еще одна логическая команда NOT устанавливает обpатное значе ние бит в байте или в слове, в регистре или в памяти: нули становятся единицами, а единицы - нулями. Если, например, pегистр AL содержит 1100 0101, то команда NOT AL изменяет это значение на 0011 1010. Флаги не меняются. Команда NOT не эквивалентна команде NEG, которая меняет значение с положительного на отрицательное и наоборот, посредством замены бит на противоположное значение и прибавления единицы (см."Отрицательные числа" в гл.1.).
ПРОГРАММА: ИЗМЕНЕНИЕ СТРОЧНЫХ БУКВ НА ЗАГЛАВНЫЕ ------------------------------------------------------------
Существуют различные причины для преобразований между строчными и заглавными буквами. Например, вы могли получить файл данных, созданный на компьютере, который работает только с заглавными буквами. Или некая программа должна позволить пользователям вводить команды как заглавными, так и строчными буквами (например, YES или yes) и преобразовать их в заглавные для проверки. Заглавные буквы от A до Z имеют
Ассемблер для IBM PC. Глава 7 146
шест.коды от 41 до 5A, а строчные буквы от a до z имеют шест.коды от 61 до 7A. Единственная pазница в том, что пятый бит равен 0 для заглавных букв и 1 для строчных:
Биты: 76543210 Биты: 76543210 Буква A: 01000001 Буква a: 01100001 Буква Z: 01011010 Буква z: 01111010
COM-программа, приведенная на рис. 7.6, преобразует данные в поле TITLEX из строчных букв в прописные, начиная с адреса TITLEX+1. Программа инициализирует регистр BX адресом TITLEX+1 и использует его для пересылки символов в регистр AH, начиная с TITLEX+1. Если полученное значение лежит в пределах от шест.61 и до 7A, то команда AND устанавливает бит 5 в 0:
AND AH,11011111B
Все символы, отличные от строчных букв (от a до z), не изменяются. Измененные символы засылаются обратно в область TITLEX, значение в регистре BX увеличивается для очередного символа и осуществляется переход на следующий цикл. Используемый таким образом регистр BX действует как индексный регистр для адресации в памяти. Для этих целей можно использовать также регистры SI и DI.


------------------------------------------------------------ ------------------------------------------------------------ Рис. 7.6. Изменение строчных букв на прописные.
КОМАНДЫ СДВИГА И ЦИКЛИЧЕСКОГО СДВИГА ------------------------------------------------------------
Команды сдвига и циклического сдвига, которые представля ют собой часть логических возможностей компьютера, имеют следующие свойства: - обрабатывают байт или слово; - имеют доступ к регистру или к памяти; - сдвигают влево или вправо; - сдвигают на величину до 8 бит (для байта) и 16 бит (для слова); - сдвигают логически (без знака) или арифметически (со знаком). Значение сдвига на 1 может быть закодировано как непосред cтвенный операнд, значение больше 1 должно находиться в регистре CL.
Команды сдвига
При выполнении команд сдвига флаг CF всегда содержит зна чение последнего выдвинутого бита. Существуют следующие команды cдвига:
Ассемблер для IBM PC. Глава 7 147
SHR ;Логический (беззнаковый) сдвиг вправо SHL ;Логический (беззнаковый) сдвиг влево SAR ;Арифметический сдвиг вправо SAL ;Арифметический сдвиг влево
Следующий фрагмент иллюстрирует выполнение команды SHR:
MOV CL,03 ; AX: MOV AX,10110111B ; 10110111 SHR AX,1 ; 01011011 ;Сдвиг вправо на 1 SHR AX,CL ; 00001011 ;Сдвиг вправо на 3
Первая команда SHR сдвигает содержимое регистра AX вправо на 1 бит. Выдвинутый в результате один бит попадает в флаг CF, а самый левый бит регистра AX заполняется нулем. Вторая команда cдвигает содержимое регистра AX еще на три бита. При этом флаг CF последовательно принимает значения 1, 1, 0, а в три левых бита в регистре AX заносятся нули. Рассмотрим действие команд арифметического вправо SAR:
MOV CL,03 ; AX: MOV AX,10110111B ; 10110111 SAR AX,1 ; 11011011 ;Сдвиг вправо на 1 SAR AX,CL ; 11111011 ;Сдвиг вправо на 3
Команда SAR имеет важное отличие от команды SHR: для заполне ния левого бита используется знаковый бит. Таким образом, положительные и отрицательные величины сохраняют свой знак. В приведенном примере знаковый бит содержит единицу. При сдвигах влево правые биты заполняются нулями. Таким обpазом, результат команд сдвига SHL и SAL индентичен. Сдвиг влево часто используется для удваивания чисел, а сдвиг вправо - для деления на 2. Эти операции осуществляются значительно быстрее, чем команды умножения или деления. Деление пополам нечетных чисел (например, 5 или 7) образует меньшие значения (2 или 3, соответственно) и устанавливаеют флаг CF в 1. Кроме того, если необходимо выполнить сдвиг на 2 бита, то использование двух команд сдвига более эффектив но, чем использование одной команды с загрузкой регистра CL значением 2. Для проверки бита, занесенного в флаг CF используется команда JC (переход, если есть перенос).


Команды циклического сдвига
Циклический сдвиг представляет собой операцию сдвига, при которой выдвинутый бит занимает освободившийся разряд. Существуют следующие команды циклического сдвига:
ROR ;Циклический сдвиг вправо ROL ;Циклический сдвиг влево RCR ;Циклический сдвиг вправо с переносом RCL ;Циклический сдвиг влево с переносом
Ассемблер для IBM PC. Глава 7 148
Следующая последовательность команд иллюстрирует операцию циклического сдвига ROR:
MOV CL,03 ; BX: MOV BX,10110111B ; 10110111 ROR BX,1 ; 11011011 ;Сдвиг вправо на 1 ROR BX,CL ; 01111011 ;Сдвиг вправо на 3
Первая команда ROR при выполнении циклического сдвига переносит правый единичный бит регистра BX в освободившуюся левую позицию. Вторая команда ROR переносит таким образом три правых бита. В командах RCR и RCL в сдвиге участвует флаг CF. Выдвигае мый из регистра бит заносится в флаг CF, а значение CF при этом поступает в освободившуюся позицию. Рассмотрим пример, в котором используются команды циклического и простого сдвига. Предположим, что 32-битовое значение находится в регистрах DX:AX так, что левые 16 бит лежат в регистре DX, а правые - в AX. Для умножения на 2 этого значения возможны cледующие две команды:
SHL AX,1 ;Умножение пары регистров RCL DX,1 ; DX:AX на 2
Здесь команда SHL сдвигает все биты регистра AX влево, причем самый левый бит попадает в флаг CF. Затем команда RCL сдвигает все биты регистра DX влево и в освободившийся правый бит заносит значение из флага CF.
ОРГАНИЗАЦИЯ ПРОГРАММ ------------------------------------------------------------
Ниже даны основные рекомендации для написания ассемблер ных программ:
1. Четко представляйте себе задачу, которую должна решить программа
2. Сделайте эскиз задачи в общих чертах и спланируйте общую логику программы. Например, если необходимо прове рить операции пеpесылки нескольких байт (как в примере на рис.7.5), начните c определения полей с пересылаемы ми данными. Затем спланируйте общую стратегию для инициализации, условного перехода и команды LOOP. Приведем основную логику, которую используют многие программисты в таком случае: инициализация стека и сегментных регистров вызов подпрограммы цикла возврат Подпрограмма цикла может быть спланирована следующим образом: инициализация регистров значениями адресов


Ассемблер для IBM PC. Глава 7 149
и числа циклов Метка: пересылка одного байта увеличение адресов на 1 уменьшение счетчика на 1: если счетчик не ноль, то идти на метку если ноль, возврат
3. Представьте программу в виде логических блоков, следую щих друг за другом. Процедуры не превышающие 25 строк (размер экрана) удобнее для отладки.
4. Пользуйтесь тестовыми примерами программ. Попытки запом нить все технические детали и программирование сложных программ "из головы" часто приводят к многочисленным ошибкам.
5. Используйте комментарии для описания того, что должна делать процедура, какие арифметические действия или операции сравнения будут выполняться и что делают редко используемые команды. (Например, команда XLAT, не имеющая операндов).
6. Для кодирования программы используйте заготовку програм мы, скопированной в файл с новым именем.
В следующих программах данной книги важным является использование команды LEA, индексных регистров SI и DI, вызываемых процедур. Получив теперь базовые знания по ассемблеру, можем перейти к более развитому и полезному программированию.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Метки процедур (например, B20:) должны завершаться двое точием для указания типа NEAR. Отсутствие двоеточия приводит к ассемблерной ошибке.
ъ Метки для команд условного перехода и LOOP должны лежать в границах -128 до +127 байт. Операнд таких команд генерирует один байт объектного кода. Шест. от 01 до 7F соответствует десятичным значениям от +1 до +127, а шест. от FF до 80 покрывает значения от -1 до +128. Так как длина машинной команды может быть от 1 до 4 байт, то соблюдать границы не просто. Практически можно ориентироваться на размер в два экрана исходного текста (примерно 50 строк).
ъ При использовании команды LOOP, инициализируйте регистр CX положительным числом. Команда LOOP контролирует только нулевое значение, при отрицательном программа будет продолжать циклиться.
Ассемблер для IBM PC. Глава 7 150


ъ Если некоторая команда устанавливает флаг, то данный флаг сохраняет это значение, пока другая команда его не изменит. Например, если за арифметической командой, которая устанавливает флаги, следуют команды MOV, то они не изменят флаги. Однако, для минимизации числа возможных ошибок, cледует кодировать команды условного перехода непосредственно после команд, устанавливающих проверяемые флаги.
ъ Выбирайте команды условного перехода соответственно операциям над знаковыми или беззнаковыми данными.
ъ Для вызова процедуры используйте команду CALL, а для возврата из процедуры - команду RET. Вызываемая процеду ра может, в свою очередь, вызвать другую процедуру, и если следовать существующим соглашениям, то команда RET всегда будет выбирать из стека правильный адрес возвра та. Единственные примеры в этой книге, где используется переход в процедуру вместо ее вызова - в начале COM- программ.
ъ Будьте внимательны при использовании индексных операн дов. Сравните: MOV AX,SI MOV AX,[SI]
Первая команда MOV пересылает в регистр AX содержимое регистра SI. Вторая команда MOV для доступа к пересылае мому слову в памяти использует относительный адрес в регистре SI.
ъ Используйте команды сдвига для удванивания значений и для деления пополам, но при этом внимательно выбирайте соответствующие команды для знаковых и беззнаковых данных.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
7.1. Какое максимальное количество байт могут обойти коман ды коpоткий JMP, LOOP и относительный переход? Какой машинный код операнда при этом генерируется? 7.2. Команда JMP начинается на шест. 0624. Определите адрес перехода, если шест. объектный код для операнда команды JMP: а) 27, б) 6B, в) C6. 7.3. Напишите программу вычисления 12 чисел Фибоначи: 1, 1, 2, 3, 5, 8, 13,... (каждое число в последовательности представляет собой сумму двух предыдущих чисел). Для организации цикла используйте команду LOOP. Выполните ассемблирование, компановку и с помощью отладчика DEBUG трассировку программы.


Ассемблер для IBM PC. Глава 7 151
7.4. Предположим, что регистры AX и BX содержат знаковые данные, a CX и DX - беззнаковые. Определите команды CMP (где необходимо) и команды безусловного перехода для следующих проверок: а) значение в DX больше, чем в CX? б) значение в BX больше, чем в AX? в) CX содержит нуль? г) было ли переполнение? д) значение в BX равно или меньше, чем в AX? е) значение в DX равно или меньше, чем в CX? 7.5. На какие флаги воздействуют следующие события и какое значение этих флагов? a) произошло переполнение; б) результат отрицательный; в) результат нулевой; г) обработка в одношаговом режиме; д) передача данных должна быть справа налево. 7.6. Что произойдет при выполнении программы , приведенной на рис.7.4, если в процедуре BEGIN будет отсутствовать команда RET? 7.7. Какая разница между кодированием в директиве PROC опеpанда с типом FAR и с типом NEAR? 7.8. Каким образом может программа начать выполнение процедуры? 7.9. В EXE-программе процедура A10 вызывает B10, B10 вызывает C10, а C10 вызывает D10. Сколько адресов, кроме начальных адресов возврата в DOS, содержит стек? 7.10. Предположим , что регистр BL содержит 11100011 и поле по имени BOONO содержит 01111001. Определите воздейст вие на регистр BL для следующих команд: а) XOR BL,BOONO; б) AND BL,BOONO; в) OR BL,BOONO; г) XOR BL,11111111B; д) AND BL,00000000B. 7.11. Измените программу на рис.7.6 для: а) определения содержимого TITLEX заглавными буквами; б) преобразова ние заглавных букв в строчные. 7.12. Предположим, что регистр DX содержит 10111001 10111001, а pегистр CL - 03. Определите содержимое регистра DX после следующих несвязанных команд: а) SHR DX,1; б) SHR DX,CL; в) SHL DX,CL; г) SHL DL,1; д) ROR DX,CL; е) ROR DL,CL; ж) SAL DH,1. 7.13. Используя команды сдвига, пересылки и сложения, умножьте содержимое регистра AX на 10. 7.14. Пример программы, приведенной в конце раздела "сдвиг и циклический сдвиг", умножает содержимое пары регистров DX:AX на 2. Измените программу для: а) умножения на 4; б) деления на 4; в) умножения 48 бит в регистрах DX:AX:BX на 2.
Ассемблер для IBM PC. Глава 8 172


Экранные операции I: Основные свойства


------------------------------------------------------------

Экранные операции I: Основные свойства

Цель: Объяснить требования для вывода информации на экран, а также для ввода данных с клавиатуры.

ВВЕДЕНИЕ ------------------------------------------------------------

В предыдущих главах мы имели дело с программами, в котор ых данные oпределялись в операндах команд (непосредственные данные) или инициализировались в конкретных полях программы. Число практических применений таких программ в действитель ности мало. Большинcтво программ требуют ввода данных с клавиатуры, диска или модема и обеспечивают вывод данных в удобном формате на экран, принтер или диск. Данные, предназначенные для вывода на экран и ввода с клавиатуры, имеют ASCII формат. Для выполнения ввода и вывода используется команда INT (прерывание). Существуют различные требования для указания системе какое действие (ввод или вывод) и на каком устройстве необходимо выполнить. Данная глава раскрывает основные требования для вывода информации на экран и ввода данных с клавиатуры. Все необходимые экранные и клавиатурные операции можно выполнить используя команду INT 10H, которая передает управление непосредственно в BIOS. Для выполнения некоторых более сложных операций существует прерывание более высокого уровня INT 21H, которое сначала передает управление в DOS. Например, при вводе с клавиатуры может потребоваться подсчет введенных символов, проверку на максимальное число символов и проверку на символ Return. Преpывание DOS INT 21H выполняет многие из этих дополнительных вычислений и затем автоматически передает управление в BIOS. Материал данной главы подходит как для монохромных (черно-белых, BW), так и для цветных видеоммониторов. В главах 9 и 10 приведен материал для управления более совершенными экранами и для использоваения цвета.

КОМАНДА ПРЕРЫВАНИЯ: INT ------------------------------------------------------------

Команда INT прерывает обработку программы, передает управление в DOS или BIOS для определенного действия и затем возвращает управление в прерванную программу для продолжения обработки. Наиболее часто прерывание используется для выполнения операций ввода или вывода. Для выхода из программы на обработку прерывания и для последующего возврата команда INT выполняет следующие действия:


Ассемблер для IBM PC. Глава 8 173
ъ уменьшает указатель стека на 2 и заносит в вершину стека содержимое флагового регистра; ъ очищает флаги TF и IF; ъ уменьшает указатель стека на 2 и заносит содержимое регистра CS в стек; ъ уменьшает указатель стека на 2 и заносит в стек значение командного указателя; ъ обеспечивает выполнение необходимых ддействий; ъ восстанавливает из стека значение регистра и возвращает управление в прерванную программу на команду, следующую после INT.
Этот процесс выполняется полностью автоматически. Необхо димо лишь определить сегмент стека достаточно большим для записи в него значений регистров. В данной главе рассмотрим два типа прерываний: команду BIOS INT 10H и команду DOS INT 21H для вывода на экран и ввода с клавиатуры. В последующих примерах в зависимости от требований используются как INT 10H так и INT 21H.
УСТАНОВКА КУРСОРА ------------------------------------------------------------
Экран можно представить в виде двумерного пространства с адресуемыми позициями в любую из которых может быть установ лен курсор. Обычный видеомонитор, например, имеет 25 строк (нумеруемых от 0 до 24) и 80 столбцов (нумеруемых от 0 до 79). В следующей таблице приведены некоторые примеры положений курсора на экране:
-------------------------------------------------------- Дес. формат Шест.формат -------------- -------------- Положение строка столбец строка столбец -------------------------------------------------------- Верхний левый угол 00 00 00 00 Верхний правый угол 00 79 00 4F Центр экрана 12 39/40 00 27/28 Нижний левый угол 24 00 18 00 Нижний правый угол 24 79 18 4F --------------------------------------------------------
Команда INT 10H включает в себя установку курсора в любую позицию и очистку экрана. Ниже приведен пример установки курсора на 5-ую строку и 12-ый столбец:
MOV AH,02 ;Запрос на установку курсора MOV BH,00 ;Экран 0 MOV DH,05 ;Строка 05 MOV DL,12 ;Столбец 12 INT 10H ;Передача управления в BIOS
Ассемблер для IBM PC. Глава 8 174


Значение 02 в регистре AH указывает команде INT 10H на выпол нение операции установки курсора. Значение строки и столбца должны быть в регистре DX, а номер экрана (или страницы) в регистре BH (обычно 0). Содержимое других регистров несущест венно. Для установки строки и столбца можно также использо вать одну команду MOV c непосредственным шест. значением:
MOV DX,050CH ;Строка 5, столбец 12
ОЧИСТКА ЭКРАНА ------------------------------------------------------------
Запросы и команды остаются на экране пока не будут смеще ны в результате прокручивания ("скролинга") или переписаны на этом же месте другими запросами или командами. Когда программа начинает cвое выполнение, экран может быть очищен. Очищаемая область экрана может начинаться в любой позиции и заканчиваться в любой другой позиции с большим номером. Начальное значение строки и столбца заносится в регистр DX, значение 07 - в регистр BH и 0600H в AX. В следующем примере выполняется очистка всего экрана:
MOV AX,0600H ;AH 06 (прокрутка) ;AL 00 (весь экран) MOV BH,07 ;Нормальный атрибут (черно/белый) MOV CX,0000 ;Верхняя левая позиция MOV DX,184FH ;Нижняя правая позиция INT 10H ;Передача управления в BIOS
Значение 06 в регистре AH указывает команде INT 10H на выполнение опарации очистки экрана. Эта операция очищает экран пробелами; в следующей главе скролинг (прокрутка) будет пассмотрен подробнее. Если вы по ошибке установили нижнюю правую позицию больше, чем шест. 184F, то очистка перейдет вновь к началу экрана и вторично заполнит некоторые позиции прробелами. Для монохромных экранов это не вызывает каких-либо неприятностей, но для некоторых цветных мониторов могут возникнуть серьезные ошибки.
ЭКРАННЫЕ И КЛАВИАТУРНЫЕ ОПЕРАЦИИ: БАЗОВАЯ ВЕРСИЯ DOS ------------------------------------------------------------
Обычно программы должны выдать на экран сообщение о завер шении или об обнаружении ошибки, отобразить запрос для ввода данных или для получения указания пользователя. Рассмотрим сначала методы, применяемые в базовой версии DOS, в последую щих pазделах будут показаны расширенные методы, введенные в DOS версии 2.0. Операции из базовой DOS работают во всех версиях, хотя в руководстве по DOS рекомендуется применять расширенные возможности для новых разработок. В базовой версии DOS команды вывода на экран более сложны, но команды ввода с клавиатуры проще в использовании, благодаря встроен ным проверкам.


Ассемблер для IBM PC. Глава 8 175
ВЫВОД НА ЭКРАН: БАЗОВАЯ ВЕРСИЯ DOS ------------------------------------------------------------
Вывод на экран в базовой версии DOS требует определения текстового сообщения в области данных, установки в регистре AH значения 09 (вызов функциии DOS) и указания команды DOS INT 21H. В процессе выполнения операции конец сообщения определяется по oграничителю ($), как это показано ниже:
NAMPRMP DB 'Имя покупателя?','$' . . MOV AH,09 ;Запрос вывода на экран LEA DX,NAMPRMP ;Загрузка адреса сообщ. INT 21H ;Вызов DOS
Знак ограничителя "$" можно кодировать непосредственно после cимвольной строки (как показано в примере), внутри строки: 'Имя покупателя?$', или в следующем операторе DB '$'. Используя данную операцию, нельзя вывести на экран символ доллара "$". Кроме того, если знак доллара будет отсутство вать в коце строки, то на экран будут выводиться все последующие символы, пока знак "$" не встретиться в памяти. Команда LEA загружает адрес области NAMPRMP в регистр DX для передачи в DOS адреса выводимой информации. Адрес поля NAMPRMP, загружаемый в DX по команде LEA, является oтноси тельным, поэтому для вычисления абсолютного адреса данных DOS складывает значения регистров DS и DX (DS:DX).
ПРОГРАММА: ВЫВОД НА ЭКРАН НАБОРА СИМВОЛОВ КОДА ASCII ------------------------------------------------------------
Большинство из 256 кодов ASCII имеют символьное представ ление, и могут быть выведены на экран. Шест. коды 00 и FF не имеют символов и выводятся на экран в виде пробелов, хотя символ пробела имеет в ASCII шест. код 20. На рис. 8.1 показана COM-программа, которая выводит на экран полный набор символов кода ASCII. Программа вызывает три процедуры; B10CLR, C10SET и D10DISP. Процедура B10CLR очищает экран, а процедура C10SET устанавливает курсор в положение 00,00. Процедура D10DISP выводит содержимое поля CTR, которое в начале инициализировано значением 00 и затем yвеличивается на 1 при каждом выводе на экран, пока не достигнет шест. значения FF.


------------------------------------------------------------ ------------------------------------------------------------ Рис. 8.1. Вывод на экран набора символов кода ASCII
Так как символ доллара не выводится на экран и кроме того коды от шест. 08 до шест. 0D являются специальными управляющими cимволами, то это приводит к перемещению
Ассемблер для IBM PC. Глава 8 176
курсора и другим управляющим воздействиям. Задание: введите программу (рис.8.1), выполните ассемблирование, компановку и преобразование в COM-файл. Для запуска программы введите ее имя, например, В:ASCII.COM. Первая выведенная строка начинается с пробельного символа (шест.00), двух "улыбающихся лиц" (шест. 01 и 02) и трех карточных символов (шест.03, 04 и 05). Код 07 выдает звуко вой сигнал. Код 06 должен отобразиться карточным символом "пики", но управляющие символы от шест.08 до 0D сотрут его. Код 0D является "возвратом каретки" и приводит к переходу на новую (следующую)строку. Код шест.0E - представляется в виде музыкальной ноты. Символы после шест. 7F являются графически ми. Можно изменить программу для обхода управляющих символов. Ниже приведен пример фрагмента программы, позволяющий обойти все символы между шест. 08 и 0D. Вы можете поэкспериментировать, oбходя только, скажем, шест. 08 (возврат на символ) и 0D (возврат каретки):
CMP CTR,08H ;Меньше чем 08? JB D30 ; да - принять CMP CTR,0DH ; Меньше/равно 0D? JBE D40 ; да - обойти D30: MOV AH,40H ;Вывод символов < 08 ... ; и > 0D INT 21H D40: INC CTR
ВВОД ДАННЫХ С КЛАВИАТУРЫ: БАЗОВАЯ ВЕРСИЯ DOS ------------------------------------------------------------
Процедура ввода данных с клавиатуры проще, чем вывод на экран. Для ввода, использующего базовую DOS, область ввода требует наличия cписка параметров, содержащего поля, которые необходимы при выполнении команды INT. Во-первых, должна быть определена максимальная длина вводимого текста. Это необходимо для предупреждения пользователя звуковым сигна лом, если набран слишком длинный текст; символы, превышающие максимальную длину не принимаются. Во-вторых, в списке параметров должно быть определенное поле, куда команда возвращает действительную длину введенного текста в байтах. Ниже приведен пример, в котором определен список парамет ров для области ввода. LABEL представляет собой директиву с атрибутом BYTE. Первый байт содержит максимальную длину вводимых данных. Так как это однобайтовое поле, то возможное максимальное значение его - шест. FF или 255. Второй байт необходим DOS для занесения в него действительного числа введенных символов. Третьим байтом начинается поле, которое будет содержать введенные символы.


NAMEPAR LABEL BYTE ;Список параметров:
Ассемблер для IBM PC. Глава 8 177
MAXLEN DB 20 ; Максимальная длина ACTLEN DB ? ; Реальная длина NAMEFLD DB 20 DUP (' ') ; Введенные символы
Так как в списке параметров директива LABEL не занимает места, то NAMEPAR и MAXLEN указывают на один и тот же aдрес памяти. В трансляторе MASM для определения списка параметров в виде структуры может использоваться также директива STRUC. Однако, в связи с тем, что ссылки на имена, определенные внутри, требуют специальной адресации, воздержимся cейчас от рассмотрения данной темы до главы 24 "Директивы ассемблера". Для запроса на ввод необходимо поместить в регистр AH номер функции - 10 (шест. 0AH), загрузить адрес списка пара метров (NAMEPAR в нашем примере) в регистр DX и выполнить INT 21H:
MOV AH,0AH ;Запрос функции ввода LEA DX,NAMEPAR ;Загрузить адреса списка параметров INT 21H ;Вызвать DOS
Команда INT ожидает пока пользователь не введет с клавиа туры текст, проверяя при этом, чтобы число введенных cимво лов не превышало максимального значения, указанного в списке параметров (20 в нашем примере). Для указания конца ввода пользователь нажимает клавишу Return. Код этой клавиши (шест. 0D) также заносится в поле ввода (NAMEFLD в нашем примере). Если, например, пользователь ввел имя BROWN (Return), то cписок параметров будет содержать информацию:
дес.: |20| 5| В| R| O| W| N| #| | | | | ... шест.: |14|05|42|52|4F|57|4E|0D|20|20|20|20| ...
Во второй байт списка параметров (ACTLEN в нашем примере) команда заносит длину введенного имени - 05. Код Return находится по адресу NAMEFLD +5. Символ # использован здесь для индикации конца данных, так как шест. 0D не имеет отображаемого символа. Поскольку максимальная длина в 20 символов включает шест.0D, то действительная длина вводимого текста может быть только 19 символов.
ПРОГРАММА: ВВОД И ВЫВОД ИМЕН ------------------------------------------------------------
EXE-программа, приведенная на рис. 8.2, запрашивает ввод имени, затем отображает в середине экрана введенное имя и включает звуковой сигнал. Программа продолжает запрашивать и отображать имена, пока пользователь не нажмет Return в ответ на очередной запрос. Рассмотрим ситуацию, когда пользователь ввел имя TED SMITH:


------------------------------------------------------------ ------------------------------------------------------------ Рис. 8.2. Ввод и отображение имен
Ассемблер для IBM PC. Глава 8 178
1. Разделим длину 09 на 2 получим 4, и 2. Вычтем это значение из 40, получим 36
Команда SHR в процедуре E10CENT сдвигает длину 09 на oдин бит вправо, выполняя таким образом деление на 2. Значение бит 00001001 переходит в 00000100. Команда NEG меняет знак +4 На -4. Команда ADD прибавляет значение 40, получая в регистре DL номер начального столбца - 36. При установке курсора на строку 12 и столбец 36 имя будет выведено на экран в следующем виде:
Строка 12: TED SMITH | | Столбец: 36 40
В процедуре E10CODE имеется команда, которая устанавлива ет cимвол звукового сигнала (07) в области ввода непосред ственно после имени:
MOV NAMEFLD[BX],07
Предшествующая команда устанавливает в регистре BX значение длины, и команда MOV затем, комбинируя длину в регистре BX и адрес поля NAMEFLD, пересылает код 07. Например, при длине имени 05 код 07 будет помещен по адресу NAMEFLD+05 (замещая значение кода Return). Последняя команда в процедуре E10CODE устанавливает ограничитель "$" после кода 07. Таким образом, когда процедура F10CENT выводит на экран имя, то генериpует ся также звуковой сигнал.
Ввод единственного символа Return
При вводе имени, превышающего по длине максимальное значение, указанное в списке параметров, возникает звуковой сигнал и система oжидает ввода только символа Return. Если вообще не вводить имя, а только нажать клавишу Return, то система примет ее и yстановит в списке параметров нулевую длину следующим образом:
Список параметров (шест.): |14|00|0D|...
Для обозначения конца вводимых имен пользователь может прос то нажать Return в ответ на очередной запрос на ввод имени. Прогpамма определяет конец ввода по нулевой длине.
Замена символа Return
Вводимые значения можно использовать для самых разных целей, например: для печати сообщений, сохранения в таблице, записи на диск. При этом, возможно, появится необходимость замены символа Return (шест.0D) в области NAMEFLD на символ пробела (шест.20). Поле NAMELEN содержит


Ассемблер для IBM PC. Глава 8 179
действительную длину или отноcительный адрес кода 0D. Если, например, NAMELEN содержит длину 05, то адрес кода 0D равен NAMEFLD+5. Можно занести эту длину в регистр BX для индексной адресации в поле NAMEFLD:
MOV BH,00 ;Установить в регистре BX MOV BL,NAMELEN ; значение 0005 MOV NAMEFLD[BX],20H ;Заменить 0D на пробел
Третья команда MOV заносит символ пробела (шест.20) по адресу, oпределенному первым операндом: адрес поля NAMEFLD плюс содержимое регистра BX, т.е. NAMEFLD+5.
Очистка области ввода
Вводимые символы заменяют предыдущее содержимое области ввода и остаются там, пока другие символы не заменят их. Рассмотрим следующие три успешных ввода имен:
Ввод NAMEPAR (шест.) 1. BROWN |14|05|42|52|4F|57|4E|0D|20|20|20| ... |20| 2. HAMILTON |14|08|48|41|4D|49|4C|54|4F|4E|0D| ... |20| 3. ADAMS |14|05|41|44|41|4D|53|0D|4F|4E|0D| ... |20|
Имя HAMILTON заменяет более короткое имя BROWN. Но, так как имя ADAMS короче имени HAMILTON, то оно заменяет только HAMIL. Код Return заменяет символ T. Остальные буквы - ON oстаются после имени ADAMS. Для очистки поля NAMEFLD до ввода очередного имени может служить следующая программа:
MOV CX,20 ;Установить 20 циклов MOV SI,0000 ;Начальная позиция поля B30: MOV NAMEFLD[si],20H ;Переслать один пробел INC SI ;Следующая позиция поля LOOP B30 ;20 циклов
Вместо регистра SI можно использовать DI или BX. Более эффек тивный способ очистки поля, предпологающий пересылку слова из двух пробелов, требует только десять циклов. Однако, ввиду того что поле NAMEFLD определено как DB (байтовое), необходимо изменить длину в команде пересылки, посредством операнда WORD, a также воспользоваться операндом PTR (указатель), как показано ниже:
MOV CX,10 ;Установить 10 циклов LEA SI,NAMEFLD ;Начальный адрес B30: MOV WORD PTR[SI],2020H ;Переслать два пробела INC SI ;Получить адрес INC SI ; следующего слова LOOP B30 ;10 циклов
Ассемблер для IBM PC. Глава 8 180
Команда MOV по метке B30 обозначает пересылку слова из двух пробелов по адресу, находящемуся в регистре SI. В последнем примеpе используется команда LEA для инициализации регистра SI и несколько иной способ в команде MOV по метке В30, так как нельзя закодировать, например, следующую команду:


MOV WORD PTR[NAMEFLD],2020H ;Неправильно
Очистка входной области решает проблему ввода коротких имен, за которыми следуют предыдущие данные. Еще более эффек тивный cпособ предпологает очистку только тех байт, которые расположены после введенного имени.
ЭКРАННЫЕ И КЛАВИАТУРНЫЕ ОПЕРАЦИИ: РАСШИРЕННАЯ ВЕРСИЯ DOS ------------------------------------------------------------
Рассмотрим теперь расширенные возможности, введенные в DOS 2.0 (реализованные в стиле операционной системы UNIX). Если вы используете более младшую версию DOS, то не сможете выполнить примеры из данного раздела. Расширенные возможнос ти включают файловый номер (file handle), который yстанав ливается в регистре BX, когда требуется выполнить операцию ввода/вывода. Существуют следующие стандартные файловые номера:
0 Ввод (обычно с клавиатуры) CON 1 Вывод (обычно на экран) CON 2 Вывод по ошибке (на экран) CON 3 Ввод/вывод на внешнее устройство AUX 4 Вывод на печать LPT1 или PRN
Прерывание DOS для ввода/вывода - INT 21H, необходимая функция запрашивается через регистр AH: шест.3F - для ввода, шест.40 - для вывода. В регистр CX заносится число байт для ввода/вывода, а в регистр DX - адрес области ввода/вывода. В результате успешного выполнения операции ввода/вывода очищается флаг переноса (CF) и в регистр AX устанавливается действительное число байт, участвующих в операции. При неуспешной oперации устанавливается флаг CF, а код ошибки (в данном случае 6) заносится в регистр AX. Поскольку регистр AX может содержать как длину данных, так и код ошибки, то единственный способ определить наличие ошибки - проверить флаг CF, хотя ошибки чтения с клавиатуры и вывода на экран - явления крайне редкие. Аналогичным oбразом используются файловые номера для дисковых файлов, здесь oшибки ввода/вывода встречаются чаще. Можно использовать эти функции для перенаправления ввода- вывода на другие устройства, однако эта особенность здесь не рассматpивается.
ВЫВОД НА ЭКРАН: РАСШИРЕННАЯ ВЕРСИЯ DOS ------------------------------------------------------------


Ассемблер для IBM PC. Глава 8 181
Следующие команды иллюстрируют операцию вывода на экран в расширенной версии DOS:
DISAREA DB 20 DUP(' ') ;Область данных ... MOV AH,40H ;Запрос на вывод MOV BX,01 ;Выводное устройство MOV CX,20 ;Максимальное число байт LEA DX,DISAREA ;Адрес области данных INT 21H ;Вызов DOS
Команда LEA загружает в регистр DX адрес DISAREA для возможности DOS локализовать информацию, предназначенную для вывода. В результате успешной операции флаг переноса очищает ся (это можно проверить), а в регистре AX устанавливается число выведенных символов. Ошибка в данной операции может произойти, если yстановлен неправильный файловый номер. В этом случае будет установлен флаг CF и код ошибки (в данном случае 6) в регистре AX. Поскольку регистр AX может содержать или длину, или код ошибки, то единственный способ определить состояние ошибки - проверить флаг CF.
Упражнение: Вывод на экран
Воспользуемся отладчиком DEBUG для проверки внутренних эффектов прерывания. Загрузите DEBUG и после вывода на экран приглашения введите A 100 для ввода ассемблерных команд (не машинных коман) по адpесу 100. Не забудьте, что DEBUG предполагает, что все числа вводятся в шеснадцатеричном формате.
100 MOV AH,40 102 MOV BX,01 105 MOV CX,хх (введите длину вашего имени) 108 MOV DX,10E 10B INT 21 10D RET 10E DB 'Ваше имя'
программа устанавливает в регистре AH запрос на вывод и устанавливает шест. значение 10F в регистре DX - адрес DB, содержащей ваше имя в конце программы. Когда вы наберете все команды, нажмите еще раз Return. С помощью команды U (U 100,10D) дисассемблируйте программу для проверки. Затем используйте команды R и T для трассиров ки выполнения. При выполнении команды INT 21H отладчик перейдет в BIOS, поэтому при достижении адреса 10B введите команду GO (G 10D) для перехода к команде RET. Ваше имя будет выведено на экран. С помощью команды Q вернитесь в DOS.
ВВОД С КЛАВИАТУРЫ: РАСШИРЕННЫЙ DOS ------------------------------------------------------------
Ассемблер для IBM PC. Глава 8 182


Ниже приведены команды, иллюстрирующие использование функции ввода с клавиатуры в расширенной версии DOS:
INAREA DB 20 DUP (' ') ;Область ввода MOV AH,3FH ;Запрос на ввод MOV BX,00 ;Номер для клавиатуры MOV CX,20 ;Максимум байт для ввода LEA DX,INAREA ;Адрес области ввода INT 21H ;Вызов DOS
Команда LEA загружает относительный адрес INAREA в регистр DX. Команда INT ожидает, пока пользователь не введет cимволы с клавиатуры, но не проверяет превышает ли число введенных символов максимальное значение в регистре CX (20 в приведенном примере). Нажатие клавиши Return (код шест. 0D) указывает на завершение ввода. Например, после ввода текста "PC Users Group" INAREA будет содержать:
PC Users Group, шест.0D, шест.0A
После введенного текста непосредственно следует символ воз врата коретки (шест. 0D), который был введен, и символ конца строки (шест. 0A), который не был введен. В силу данной особенности максимальное число символов и размер области ввода должны предусматpивать место для двух символов. Если будет введено cимволов меньше максимальноого значения, то область памяти за введенными символами сохранит прежнее значение. В результате успешной операции будет очищен флаг CF (что можно проверить) и в регистре AX будет установлено число байт, введенных с клавиатуры. В предыдущем примере это число будет равно 14 плюс 2 для перевода коретки и конца строки, т.е.16. Соответствующим образом программа может определить действительное число введенных символов. Хотя данное свой ство весьма тривиально для ответов типа YES или NO, оно может быть полезно для ответов с пеpеменной длиной, таких, например, как имена. Ошибка ввода может возникнуть, если определен неправиль ный номер файла. В этом случае будет установлен флаг CF и в регистр AX будет помещен код ошибки (6 в данном случае). Так как регистр AX может содержать или длину введенных данных, или код ошибки, то единственный способ определения наличия ошибки - проверка флага CF. Если вводить текст, который превышает максимальную длину, yстановленную в регистре CX, то будут приниматься все символы. Рассмотрим ситуацию, когда регистр CX содержит 08,а пользователь введет символы "PC Exchange". В результате первые восемь символов "PC Excha" попадут в область ввода без кодов возврата каретки и конца строки. В регистре AX будет установлена длина 08. Следующая команда INT будет принимать данные не с клавиатуры, а из собственного буфера, поскольку там еще остались предыдущие данные. Таким образом,


Ассемблер для IBM PC. Глава 8 183
в область ввода будут приняты символы "ngе", символ перевода коретки и символ новой строки, в регистре AX будет установ лено значение 05. Обе операции ввода являются вполне нормаль ными и флаг CF будет очищен.
Первый INT: PC Excha AX = 08 Второй INT: ngе,0D,0A AX = 05
Программа может определить факт ввода законченного текста, если а) в регистре AX получится значение меньше, чем в регистре CX или б) если содержимые AX и CX равны, но последние два символа в области ввода - 0D и 0A. Встроенные в DOS проверки по функции 0AH для ввода с клавиатуры имеют более мощные средства. Их выбор для исполь зования в программах является предпочтительным.
Упражнение: Ввод данных
Выполним упражнение в котором можно проследить операцию ввода c клавиатурры с помощью отладчика DEBUG. Предполагае мая программа позволяет вводить до 12 символов, включая символы конца каретки и конца строки. Загрузите DEBUG и после вывода на экран приглашения введите A 100 для ввода ассемблерных команд, начиная c адреса 100. Не забудьте, что DEBUG предпологает, что все числа вводятся в шеснадцатиричном формате.
100 MOV AH,3F 102 MOV BX,00 105 MOV CX,0C 108 MOV DX,10F 10B INT 21 10D JMP 100 10F DB ' '
Программа устанавливает регистры AH и BX для запроса на ввод c клавиатуры, заносит максимальную длину ввода в ре гистр CX и загружает в регистр DX значение 10F - область DB в конце программы. В эту область будут помещаться вводимые символы. Когда вы наберете все команды, нажмите еще раз Return. С помощью команды U 100,108 выполните дисассемблирование программы для проверки. Затем используйте команды R и T для трассировки четырех команд MOV. Остановившись по адресу 10B, введите G 10D для выполнения команды INT (входить в BIOS не следует). Теперь отладчик позволит ввести данные, завершаемые клавишей Return. Проверьте содержимое регистра AX, состояние флага CF и используя команду D 10F, просмот рите введенные данные в памяти. Для завершения работы введите команду Q.
ИСПОЛЬЗОВАНИЕ СИМВОЛОВ ВОЗВРАТА КАРЕТКИ, КОНЦА СТРОКИ И ТАБУЛЯЦИИ ДЛЯ ВЫВОДА НА ЭКРАН
Ассемблер для IBM PC. Глава 8 184
------------------------------------------------------------
Ассемблер для IBM PC. Глава 9 200


Экранные операции II: Расширенные возможности


------------------------------------------------------------

Экранные операции II: Расширенные возможности

Цель: Показать более развитые возможности управления экраном, включая прокрутку, инвертирование, мигание, а также использование скэн-кодов для ввода с клавиатуры.

ВВЕДЕНИЕ ------------------------------------------------------------

В главе 8 были показаны основные возможности системы для управления выводом на экран и ввода с клавиатуры. В данной главе приводятся более развитые возсожности, обеспечисающие прокрутку данных на экране и установку байта-атрибута для подчеркивания, мигания, выделения яркости. Материал первого раздела этой главы (по прерыванию BIOS 10) подходит, как для монохромных, так и для цветных дисплеев. Другие расширенные возможности включают использование cкэн-кодов для определения нажатой клавиши или комбинации клавишей на клавиатуре.

Монохромный дисплей

Для работы монохромного дисплея имеется память объемом 4К, начинающаяся по адресу шест. B0000 (дисплейный буфер). Эта память обеспечивает: - 2К для символов на экране(25 строк х 80 столбцов); - 2К для байтов-атрибутов, обеспечивающих инвертирование, мигание, выделение яркостью и подчеркивание.

Цветной/графический дисплей

Для работы стандартного цветного графического дисплея имеется 16 Кбайт памяти (дисплейный буфер), начинающийся по адресу шест.B8000. Такой дисплей может являться текстовым (для нормального ASCII-кода) или графическим и работать как в цветном, так и в черно-белом (BW) режиме. Дисплейный буфер обеспечивает экpанные страницы, пронумерованные от 0 до 3 для экрана на 80 cтолбцов и от 0 до 7 для экрана на 40 столбцов. Номер страницы по умолчанию - 0. В следующей главе будет подробно рассмотрено управление цветом и графикой.

БАЙТ АТРИБУТОВ ------------------------------------------------------------

Байт атрибутов, как для монохромного, так и для графичес кого дисплея в текстовом (не графическом) режиме определяет характеристики каждого отображаемого символа. Байт-атрибут имеет следующие 8 бит:


Ассемблер для IBM PC. Глава 9 201
Фон Текст Атрибут: BL R G B I R G B Номер битов: 7 6 5 4 3 2 1 0
Буквы RGB представляют битовые позиции, управляющие красным (red), зеленым (green) и синим (blue) лучем в цветном моноторе. Бит 7 (BL) устанавливает мигание, а бит 3 (I) - уровень яркости. На монохромных мониторах текст высвечивается зеленым или оранжевым на темном фоне, хотя в данной главе такое изображение называется черно-белым (BW). Для модификации атрибутов можно комбинировать биты следующим oбразом:
Эффект выделения Фон Текст RGB RGB Неотображаемый (черный по черному) 000 000 Подчеркивание (не для цвета) 000 001 Нормальный (белый по черному) 000 111 Инвертированный (черный по белому) 111 000
Цветные мониторы не обеспечивают подчеркивания; вместо этого установка бит подчеркивания выбирает синий цвет для текста и получается отображение синим по черному. Ниже приведены некоторые атрибуты, основанные на комбинации битов фона, текста, мигания и выделения яркостью:
Двоичный Шест. Эффект выделения код код 0000 0000 00 Неотображаемый (для паролей) 0000 0111 07 Белый по черному (нормальный) 1000 0111 87 Белый по черному (мигание) 0000 1111 0F Белый по черному (яркий) 0111 0000 70 Черный по белому (инвертированый) 1111 0000 F0 Черный по белому (инверт. мигающий)
Эти атрибуты подходят для текстового режима, как для моно хромных, так и для цветных дисплеев. В следующей главе будет показано, как выбирать конкретные цвета. Для генерации атри бута можно использовать команду INT 10H. При этом регистр BL должен содержать значение байта-атрибута, а регистр AH один из следующих кодов: 06 (прокрутка вверх), 07 (прокрутка вниз), 08 (ввод атрибута или символа), 09 (вывод атрибута или символа). Если программа установила некотоpый атрибут, то он остается таким, пока программа его не изменит. Если установить значение байта атрибута равным шест.00, то символ вообще не будет отображен.
ПРЕРЫВАНИЕ BIOS INT 10H ------------------------------------------------------------
Ассемблер для IBM PC. Глава 9 202


Прерывание INT 10H обеспечивает управление всем экраном. В регистре AH устанавливается код, определяющий функцию прерывания. Команда cохраняет содержимое регитров BX, CX, DX, SI и BP. Ниже описывается все возможные функции.
AH=00: Установка режима. Данная функция позволяет пере ключать цветной монитор в текстовый или графический режим. Установка pежима для выполняемой в текущий момент программы осуществляется c помощью INT 10H. При установке происходит очистка экрана. Содержимое регистра AL может быть следующим:
00 40 х 25 черно-белый текстовый режим 01 40 х 25 стандартный i6-цветовой текстовый режим 02 80 х 25 черно-белый текстовый режим 03 80 х 25 стандартный 16-цветовой текстовый режим 04 320 х 200 стандартный 4-цветовой графический режим 05 320 х 200 черно-белый графический режим 06 640 х 200 черно-белый графический режим 07 80 х 25 черно-белый стандартный монохромный 08 - 0A форматы для модели PCjr 0D 320 х 200 16-цветовой графический режим (EGA) 0E 640 х 200 16-цветовой графический режим (EGA) 0F 640 х 350 черно-белый графический режим (EGA) 10 640 х 350 64-цветовой графический режим (EGA)
EGA (Enhanced Graphics Adapter) - обозначает усовершенст вованный графический адаптер. Следующий пример показывает установку стандартного 16-цветового текстового режима
MOV AH,00 ;Функция установки режима MOV AL,03 ;Стандартный цветной текст 80 х 25 INT 10H ;Вызвать BIOS
Для определения типа адаптера, установленного в системе, служит прерывание BIOS INT 11H. Данная команда возвращает в регистре AX значение, в котором биты 5 и 4 указывают на видео режим:
01 40 х 25 черно-белый режим в цветном адаптере 10 80 х 25 черно-белый режим в цветном адаптере 11 80 х 25 черно-белый режим в черно-белом адаптере
Программа, работающая с неизвестным типом монитора, может провеpить тип по регистру AX после INT 11H и затем устано вить необходимый режим.
AH=01: Установка размера курсора. Курсор не является символом из набора ASCII-кодов. Компьютер имеет собственное аппаратное обеспечение для управления видом курсора. Для этого имеется специальная обработка по INT прерыванию. Обычно символ курсоpа похож на символ подчеркивания. Используя INT 10H, можно управлять вертикальным размером курсора: биты 4-0 в регистре CH для верхней линии


Ассемблер для IBM PC. Глава 9 203
сканирования, а биты 4-0 в регистре CL - для нижней. Можно установить любой pазмер курсора по вертикали: от 0 до 13 для монохромных и EGA монитоpов и от 0 до 7 для большинства цветных мониторов. Приведем пример для увеличения размера курсора от его верхней до нижней линии сканирования:
MOV AH,01 ;Установить размер курсора MOV CH,00 ;Верхняя линия сканирования MOV CL,13 ;Нижняя линия сканирования INT 10H ;Вызвать BIOS
В результате выполнения этих команд курсор превратится в сплошной мигающий прямоугольник. Можно установить любой размер курсора между верхней и нижней границами, например, 04/08, 03/10 и т.д. Курсор сохраняет свой вид, пока программа не изменит его. Использование размеров 12/13 (для моно) и 6/7 (для цвета) переводит курсор в его нормальный вид.
AH=02: Установка позиции курсора. Эта функция устанавлива ет курcор в любую позицию на экране в соответствии с коорди натами cтроки и столбца. Номер страницы обычно равен 0, но может иметь значение от 0 до 3 при 80 столбцах на экране. Для установки позиции курсора необходимо занести в регистр AH значение 02, в регистр BH номер страницы и в регистр DX координаты строки и столбца:
MOV AH,02 ;Установить положение курсора MOV BH,00 ;Страница 0 MOV DH,строка ;Строка MOV DL,столбец ;Столбец INT 10H ;Вызвать BIOS
AH=03: Чтение текущего положения курсора. Программа может определить положение курсора на экране (строку и столбец), а также pазмер курсора, следующим образом:
MOV AH,03 ;Определить положение курсора MOV BH,00 ;Установить страницу 0 INT 10H ;Вызвать BIOS
После возврата регистр DH будет содержать номер строки, а регистр DL - номер столбца. В регистре CH будет верхняя линия cканирования, а в регистре CL - нижняя.
AH=04: Чтение положения светового пера. Данная функция используeтся в графическом режиме для определения положения светового пеpа.
AH=05: Выбор активной страницы. Новая страница устанавли вается для цветных текстовых режимов от 0 до 3. Для режима 40 х 25 возможно устанавливать до 8 страниц (от 0 до 7), а для режима 80 х 25 - до 4 страниц (от 0 до 3).


Ассемблер для IBM PC. Глава 9 204
MOV AH,05 ;Установить активную страницу MOV AL,страница ;Номер страницы INT 10H ;Вызвать BIOS
AH=06: Прокрутка экрана вверх. Когда программа пытается выдать текст на строку ниже последней на экране, то происхо дит переход на верхнюю строку. Даже если с помощью прерыва ния будет специфиpован нулевой столбец, все равно предпола гается новая строка, и нижние строки на экране будут испорчены. Для решения этой проблемы используется прокрутка экрана. Ранее код 06 использовался для очистки экрана. В тексто вом режиме установка в регистре AL значения 00 приводит к полной прокрутке вверх всего экрана, очищая его пробелами. Установка ненулевого значения в регистре AL определяет количество строк прокрутки экрана вверх. Верхние строки уходят с экрана, а чистые строки вводятся снизу. Следующие команды выполняют прокрутку всего экрана на одну строку:
MOV AX,0601H ;Прокрутить на одну строку вверх MOV BH,07 ;Атрибут: нормальный, черно-белый MOV CX,0000 ;Координаты от 00,00 MOV DX,184FH ; до 24,79 (полный экран) INT 10H ;Вызвать BIOS
Для прокрутки любого количества строк необходимо устано вить соответствующее значение в регистре AL. Регистр BH содержит атрибут для нормального или инвертированного отобра жения, мигания, установки цвета и т.д. Значения в регистрах CX и DX позволяют прокручивать любую часть экрана. Ниже объясняется стандартный подход к прокрутке:
1. Определить в элементе ROW (строка) значение 0 для установки строки положения курсора. 2. Выдать текст и продвинуть курсор на следующую строку. 3. Проверить, находится ли курсор на последней строке (CMP ROW,22). 4. Если да, то увеличить элемент ROW (INC ROW) и выйти. 5. Если нет, то прокрутить экран на одну строку и, исполь зуя ROW переустановить курсор.
AH=07: Прокрутка экрана вниз. Для текстового режима прокрутка экрана вниз обозначает удаление нижних строк и вставка чистых строк сверху. Регистр AH должен содержать 07, значения остальных регистpов аналогичны функции 06 для прокрутки вверх.


AH=08: Чтение атрибута/символа в текущей позиции курсора. Для чтения символа и байта атрибута из дисплейного буфера, как в текстовом, так и в графическом режиме используются следующие команды:
Ассемблер для IBM PC. Глава 9 205
MOV AH,08 ;Запрос на чтение атр./симв. MOV BH,00 ;Страница 0 (для текстового реж.) INT 10H ;Вызвать BIOS
Данная функция возвращает в регистре AL значение символа, а в AH - его атрибут. В графическом режиме функция возвращает шест. 00 для не ASCII-кодов. Так как эта функция читает только один cимвол, то для символьной строки необходима организация цикла.
AH=09: Вывод атрибута/символа в текущую позицию курсора. Для вывода на экран символов в текстовом или графическом режиме с установкой мигания, инвертирования и т.д. можно воспользоваться следующими командами:
MOV AH,09 ;Функция вывода MOV AL,символ ;Выводимый символ MOV BH,страница ;Номер страницы (текст.реж.) MOV BL,атрибут ;Атрибут или цвет MOV CX,повторение ;Число повторений символа INT 10H ;Вызвать BIOS
В регистр AL должен быть помещен выводимый на экран символ. Значение в регистре CX определяет число повторений символа на экране. Вывод на экран последовательности различных симво лов требует организации цикла. Данная функция не перемещает курсор. В следующем примере на экран выводится пять мигающих "сердечек" в инвертированном виде:
MOV AH,09 ;Функция вывода MOV AL,03H ;Черви (карточная масть) MOV BH,00 ;Страница 0 (текст. режим) MOV BL,0F0H ;Мигание, инверсия MOV CX,05 ;Пять раз INT 10H ;Вызвать BIOS
В текстовом (но не в графическом) режиме символы автомати чески выводятся на экран и переходят с одной строки на другую. Для вывода на экран текста запроса или сообщения необходимо составить программу, которая устанавливает в регистре CX значение 01 и в цикле загружает в регистр AL из памяти выводимые символы текста. Так как регистр CX в данном случае занят, то нельзя использовать команду LOOP. Кроме того, при выводе каждого символа необходимо дополнительно продвигать курсор в следующий столбец (функция 02). В графическом режиме регистр BL используется для определе ния цвета графики. Если бит 7 равен 0, то заданный цвет заме няет текущий цвет точки, если бит 7 равен 1, то происходит комбинация цветов с помощью команды XOR.


AH=0A: Вывод символа в текущую позицию курсора. Единствен ная разница между функциями 0A и 09 состоит в том, что функция 0A не устанавливает атрибут:
Ассемблер для IBM PC. Глава 9 206
MOV AH,0AH ;Функция вывода MOV AL,символ ;Выводимый символ MOV BH,страница ;Номер страницы (для текста) MOV CX,повторение ;Число повторений символа INT 10H ;Вызвать BIOS
Для большинства применений команда прерывания DOS INT 21H более удобна.
AH=0E: Вывод в режиме телетайпа. Данная функция позволяет использовать монитор, как простой терминал. Для выполнения этой функции необходимо установить в регистре AH шест. значение 0E, в pегистр AL поместить выводимый символ, цвет текста (в графическом режиме) занести в регистр BL и номер страницы для текстового режима - в регистр BH. Звуковой сигнал (код 07H), возврат на одну позицию (08H), конец строки (0AH) и возврат каретки (0DH) действуют, как команды для форматизации экрана. Данная функция автоматически продви гает курсор, переводит символы на следующую cтроку, выполня ет прокрутку экрана и сохраняет текущие атрибуты экрана.
AH=0F: Получение текущего видео режима. Данная функция возвращает в регистре AL текущий видео режим (см.функцию AH=00), в pегистре AH - число символов в строке (20, 40 или 80), в регистре BH - номер страницы.
AH=13: Вывод символьной строки (только для AT). Данная функция позволяет на компьютерах типа AT выводить на экран символьные строки с установкой атрибутов и перемещением курсора:
MOV AH,13H ;Функция вывода на экран MOV AL,сервис ;0, 1, 2 или 3 MOV BH,страница ; LEA BP,адрес ;Адрес строки в ES:BP MOV CX,длина ;Длина строки MOV DX,экран ;Координаты на экране INT 10H ;Вызвать BIOS
Возможен следующий дополнительный сервис: 0 - использовать атрибут и не перемещать курсор; 1 - использовать атрибут и переместить курсор; 2 - вывести символ, затем атрибут и не перемещать курсор; 3 - вывести символ, затем атрибут и переместить курсор.
ПРОГРАММА: МИГАНИЕ, ИНВЕРСИЯ И ПРОКРУТКА ------------------------------------------------------------


Ассемблер для IBM PC. Глава 9 207
Программа, приведенная на рис. 9.1, принимает ввод имен с клавиатуры и выводит их на экран. Запрос выдается в инвертированном отображении, имена принимаются в нормальном отображении, а вывод имен осуществляется с 40 столбца в той же строке с миганием и инвертированием:
Name? Francis Bacon Francis Bacon [мигание] | | Столбец 0 Столбец 40
Для управления положением курсора в программе определены переменные ROW (вертикальное перемещение вниз) и COL (гори зонтальное перемещение вправо). Команда INT 10H не перемеща ет курсор автоматически. Программа выводит имена сверху вниз, пока не достигнет 20-й строки. После этого выполняется прокрутка экрана вверх на одну строку для каждого нового запроса. Для ввода имен в процедуре D10INPT используется команда DOS INT 21H. Для замены на BIOS INT 10H необходимо:
1. Инициализировать счетчик для адреса области ввода и счетчик для длины имени. 2. Выполнить INT 10H (функция 08) с 08 в регистре AH и 00 в BH. Функция возвращает каждый символ в регистре AL. 3. Если регистр AL не содержит символа RETURN и счетчик длины достиг максимального значения, выдать звуковой сигнал и выйти из процедуры. 4. Переслать содержимое AL в область ввода имени. 5. Если регистр AL содержит символ RETURN, выйти из процедуры. 6. Увеличить счетчик длины и адрес области ввода имени. 7. Переместить курсор на один столбец. 8. Перейти на пункт 2.
При выходе из процедуры область ввода содержит имя и символ RETURN, а счетчик - число введенных символов.
РАСШИРЕННЫЙ ASCII КОД ------------------------------------------------------------
ASCII-коды от 128 до 255 (шест. 80-FF) представляют собой ряд специальных символов полезных при формировании запросов, меню, специальных значков с экранными атрибутами. Например, используя cледующие символы можно нарисовать прямоугольник:
Шест. Символ
DA Верхний левый угол BF Верхний правый угол C0 Нижний левый угол D9 Нижний правый угол C4 Горизонтальная линия B3 Вертикальная линия
Ассемблер для IBM PC. Глава 9 208


Следующие команды с помощью INT 10H выводят горизонталь ную линию на 25 позиций в длину:
MOV AH,09 ;Функция вывода на экран MOV AL,0C4H ;Горизонтальная линия MOV BH,00 ;Страница 0 MOV BL,0FH ;Выделение яркостью MOV CX,25 ;25 повторений MOV 10H ;Вызвать BIOS
Напомним, что курсор не перемещается. Вывод вертикальной линии включает цикл, в котором курсор перемещается вниз на одну строку и выводится символ шест. B3. Для штриховки может быть полезен символ с точками внутри:
Шест. Символ
B0 Одна четверть точек (светлая штриховка) B1 Половина точек (средняя штриховка) B2 Три четверти точек (темная штриховка)
------------------------------------------------------------ ------------------------------------------------------------ Рис. 9.1. Мигание, инвертирование и прокрутка
Можно извлечь много полезных идей, изучая программное обеспечение с профессионально организованным выводом, или самому изобрести оригинальные идеи для отображения информации.
ДРУГИЕ ОПЕРАЦИИ ВВОДА/ВЫВОДА В DOS ------------------------------------------------------------
Ниже перечислены другие функции DOS, которые могут оказаться полезными в работе. Код функции устанавливается в регистре AH и, затем, выдается команда INT 21H.
AH=01: Ввод с клавиатуры с эхо отображением. Данная функ ция возвращает значение в регистре AL. Если содержимое AL не равно нулю, то оно представляет собой стандартный ASCII- cимвол, например, букву или цифру. Нулевое значение в регист ре AL свидетельствует о том, что на клавиатуре была нажата специальная функциональная клавиша, например, Номе, F1 или PgUp. Для определения скэн-кода клавиш, необходимо повторить вызов функции (см. "Дополнительные функциональные клавиши" в последующих разделах). Данная функция реагирует на запрос Ctrl/Break.
AH=02: Вывод символа. Для вывода символа на экран в текущую позицию курсора необходимо поместить код данного символа в pегистр DL. Коды табуляции, возврата каретки и конца строки действуют обычным образом.
Ассемблер для IBM PC. Глава 9 209


AH=07: Прямой ввод с клавиатуры без эхо отображения. Дан ная функция работает аналогично функции 01 с двумя отличия ми: введенный символ не отображается на экране, т.е. нет эхо, и oтсутствует реакция на запрос Ctrl/Break.
AH=08: Ввод с клавиатуры без эхо отображения. Данная функ ция действует аналогично функции 01 с одним отличием: введенный символ не отображается на экран, т.е. нет эхо.
AH=0B: Проверка состояния клавиатуры. Данная функция возвращает шест.FF в регистре AL, если ввод с клавиатуры возможен, в противном случае - 00. Это средство связано с функциями 01, 07 и 08, которые не ожидают ввода с клавиатуры.
ВВОД С КЛАВИАТУРЫ ПО КОМАНДЕ BIOS INT 16H ------------------------------------------------------------
Команда BIOS INT 16H выполняет специальную операцию, которая в соответствии с кодом в регистре AH обеспечивает следующие три функции ввода с клавиатуры.
AH=00: Чтение символа. Данная функция помещает в регистр AL oчередной ASCII символ, введенный с клавиатуры, и устанавливает скэн код в регистре AH. (Скэн-коды объясняются в следующем разделе). Если на клавиатуре нажата одна из специальных клавишей, например, Номе или F1, то в регистр AL заносится 00. Автоматическое эхо символа на экран по этой функции не происходит.
AH=01: Определение наличия введенного символа. Данная функция сбрасывает флаг нуля (ZF=0), если имеется символ для чтения с клавиатуры; очередной символ и скэн-код будут помещены в регистры AL и AH соответственно и данный элемент останется в буфере.
AH=02: Определение текущего состояния клавиатуры. Данная функция возвращает в регистре AL состояние клавиатуры из адреса памяти шест. 417:
Бит 7 Состояние вставки активно (Ins) 6 Состояние фиксации верхнего регистра (Caps Lock) переключено 5 Состояние фиксации цифровой клавиатуры (Num Lock) переключено 4 Состояние фиксации прокрутки (Scroll Lock) переключено 3 Нажата комбинация клавишей Alt/Shift 2 Нажата комбинация клавишей Ctrl/Shift 1 Нажата левая клавиша Shift 0 Нажата правая клавиша Shift


Ассемблер для IBM PC. Глава 9 210
ФУНКЦИОНАЛЬНЫЕ КЛАВИШИ ------------------------------------------------------------
Клавиатура располагает тремя основными типами клавишей:
1. Символьные (алфавитно-цифровые) клавиши: буквы от a до z, цифры от 0 до 9, символы %, $, # и т.д.
2. Функциональные клавиши: Номе, End, Возврат на позицию, стрелки, Return, Del, Ins, PgUp, PgDn и программно- функциональные клавиши.
3. Управляющие клавиши: Alt, Ctrl и Shift, которые работают совместно с другими клавишами.
Функциональная клавиша не вырабатывает какой-либо символ, но чаще формирует запрос на некоторые действия. Аппаратная реализация не требует от функциональных клавишей выполнения каких-либо специфических действий. Задачей программиста является определить, например, что нажатие клавиши Номе должно присести к установке курсора в верхний левый угол экрана, или нажатие клавиши End должно установить курсор в конец текста на экране. Можно легко запрограммировать функциональные клавиши для выполнения самых различных действий. Каждая клавиша имеет собственный скэн-код от 1 (Esc) до 83 (Del) или от шест.01 до шест.53. Посредством этих скэн- кодов программа может определить нажатие любой клавиши. Нпример, запрос на ввод одного символа с клавиатуры включает загрузку 00 в регистр AH и обращение к BIOS через INT 16H:
MOV AH,00 ;Функция ввода с клавиатуры INT 16H ;Вызвать BIOS
Данная операция имеет два типа ответов в зависимости от того, нажата символьная клавиша или функциональная. Для символа (например, буква A) клавиатура посылает в компьютер два элемента информации:
1. ASCII-код символа A (шест.41) в регистре AL; 2. Скэн-код для клавиши A (шест.1E) в регистре AH.
Если нажата функциональная клавиша (например, Ins) клавиатура также передает два элемента:
1. Нуль в регистре AL; 2. Сскэн-код для клавиши Ins (шест.52) в регистре AH.
Ассемблер для IBM PC. Глава 9 211
Таким образом, после выполнения команы INT 16H необходимо прежде проверить содержимое регистра AL. Если AL содержит нуль, то была нажата функциональная клавиша, если не нуль, то получен код символьной клавиши. Ниже приведен пример такой проверки:


MOV AH,00 ;Функция ввода INT 16H ;Вызвать BIOS CMP AL,00 ;Функциональная клавиша? JZ exit ; да - выйти
Скэн-Коды
На рис. 9.2 приведены скэн- коды для некоторых функциональ ных клавишей. Клавиатура имеет по две клавиши для таких символов как *, + и -. Нажатие "звездочки", например, устанавливает код символа шест.2A в регистре AL и один из двух скэн-кодов в регистре AH в зависимости от того, какая из клавишей была нажата: шест.09 для звездочки над цифрой 8 или шест.29 для звездочки на клавише PrtSc. Ниже приведена логика проверки скэн-кода для звездочки:
CMP AL,2AH ;Звездочка? JNE EXIT1 ; нет - выйти CMP AH,09H ;Какой скэн-код? JE EXIT2
------------------------------------------------------------ Функциональные клавиши Скэн-коды Alt/A - Alt/Z 1E - 2C F1 - F10 3B - 44 Home 47 Стрелка вверх 48 PgUp 49 Стрелка влево 4B Стрелка вправо 4D End 4F Стрелка вниз 50 PgDn 51 Ins 52 Del 53 ------------------------------------------------------------ Рис. 9.2. Скэн-коды некоторых функциональных клавишей
Приведем пример программы для установки курсора в строку 0 и cтолбец 0 при нажатии клавиши Номе (скэн-код 47):
MOV AH,00 ;Выполнить ввод с клавиатуры INT 16H ; CMP AL,00 ;Функциональная клавиша? JNE EXIT1 ; нет -- выйти CMP AH,47H ;Скэн-код для клавиши Home?
Ассемблер для IBM PC. Глава 9 212
JNE EXIT2 ; нет -- выйти MOV AH,02 ; MOV BH,00 ;Установить курсор MOV DX,00 ; по координатам 0,0 INT 10H ;Вызвать BIOS
Функциональные клавиши F1 - F10 генерируют скэн-коды от шест.3B до шест.44. Следующий пример выполняет проверку на функциональную клавишу F10:
CMP AH,44H ;Клавиша F10? JE EXIT1 ; Да
По адресу EXIT1 программа может выполнить любое необходимое действие. Полный список скэн-кодов приводится в руководстве по языку BASIC. Техническое описание IBM PC содержит подробное описание всех скэн-кодов, а также описание использования клавишей Alt, Ctrl и Shift.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Монохромный дисплей использует 4К байт памяти, 2К байт на символы и 2К байт на атрибуты для каждого символа. ъ Цветной дисплей использует 16К байт памяти и может рабо тать в цветном или черно-белом (BW) режимах. Возможно использование, как текстового режима для отображения ASCII-символов, так и графического режима для любых изображений. ъ Байт-атрибут используется и для монохромных дисплеев и для цветных в текстовом режиме. Атрибут обеспечивает мигание, инвертирование и выделение яркостью. Для цветных дисплеев в текстовом режиме биты RGB позволяют выбирать цвета, но не имеют режима подчеркивания. ъ Команда BIOS INT 10H обеспечивает полную экранную обработку: установку режимов, установку положения курсора, прокрутку экрана, чтение с клавиатуры и вывод на экран. ъ Если ваша программа выводит вниз экрана, то не забывай те выполнять прокрутку прежде, чем курсор выйдет из последней строки. ъ При использовании атрибутов для мигания и инвертирова ния, не забывайте сбрасывать их в отключенное состояние. ъ Для функций по команде INT 10H, выплняющих чтение и вывод на экран, помните о перемещении курсора. ъ Команда BIOS INT 16H обеспечивает прием и распознавание функциональных клавишей. ъ Функциональные клавиши предполагают запрограммированный вызов некоторых действий.


Ассемблер для IBM PC. Глава 9 213
ъ Каждая клавиша на клавиатуре имеет конкретный скэн-код, пронумерованный от 1 (Esc) до 83 (Del), или от шест.01 до шест.53. ъ Нажатие символьной клавиши на клавиатуре передает код символа в регистр AL и скэн-код клавиши в регистр AH. ъ Нажатие функциональной клавиши на клавиатуре передает нуль в регистр AL и скэн-код клавиши в регистр AH.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
9.1. Определите атрибуты экрана для а) мигания с подчеркива нием, б) нормальной яркости, в) инвертирования с выделением яркостью.
9.2. Составте процедуры для а) установки режима экрана BW (черно-белый) на 80 столбцов, б) установки вида курсо ра, начинающегося на 5 линии сканирования и заканчиваю щегося на 12 линии, в) прокрутки экрана на 10 строк, г) вывода десяти мигающих символов штриховки с половиной точек (шест. B1).
9.3. Напишите скэн-коды для следующих функциональных клавишей: а) стрелка вверх, б) клавиша F3, в) Home, г) PgUp.
9.4. Используя отладчик DEBUG, проверте воздействие на содер жимое регистра AX при нажатии клавишей на клавиатуре. Для ввода ассемблерных команд используйте команду A 100 (Return). Ведите следующие команды:
MOV AH,00 INT 16H JMP 100
Используя команду U 100,104, дисассемблируйте программу и с помощью G 104 выполните команды MOV и INT. На команде INT выполнение программы остановиться и система перейдет в ожидание вашего ввода. Для проверки регистра AH нажмите любую клавишу. Продолжая вводить команду G 104, и, нажимая различные клавиши, проверьте работу программы. Для выхода введите команду Q.
9.5. Составте команды для определения нажатия клавиши: если нажата клавиша PgDn, то необходимо установить курсор по координатам - строка 24 и столбец 0.
Ассемблер для IBM PC. Глава 10 228


Экранные операции III: Цвет и графика


------------------------------------------------------------

Экранные операции III: Цвет и графика

Цель: Показать расширенные возможности компьютера, связанные с использованием цвета и графики на экране.

ВВЕДЕНИЕ ------------------------------------------------------------

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

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

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

3. RGB-монитор, посылающий входные сигналы на три раздель ные электронные пушки - красную, зеленую и синию для каждого из трех основных цветов. Являясь наиболее дорогим, RGB-монитор обеспечивает наилучшее качество изображения.

Стандартный адаптер для цветного графического монитора (CGA - Color/Graphics Adapter) использует 16К байт памяти, начинающейся по адресу шест.B8000, 8К байт - для символов и 8К байт для их атрибутов. При работе в формате 80х25 адаптер может хранить четыре страницы (0-3) дисплейного буфера по 4К байт каждая. При работе в формате 40х25 адаптер может хранить восемь страниц (0-7) по 2К байт каждая. По умолчанию используется нулевая страница (в начале дисплейной памяти). Программа может вывести на экран любую страницу и в это время формировать другую страницу в памяти для последующего вывода на экран. Усовершенствованный графический адаптер (EGA - Enhanced Graphics Adapter) обеспечивает более высокую разрешающую способность, по сравнению со стандартным цветным адаптером (CGA) и в большинстве случаев является совместимым с ним. Разрешающая способность обеспечивает 320х200, 640х200 и 640х350 точек на экране. Цветные адаптеры имеют два основных режима работы: текстовой (алфовитно-цифровой) и графический, и возможны также дополнительные режимы между двумя основными. По умолчанию используется текстовой режим. Установка режима описана в главе 9 в разделе "Преpывание BIOS INT 10H"


Ассемблер для IBM PC. Глава 10 229
(AH=0). Для установки графического режима или возврата в текстовой режим используется прерывание BIOS INT 10H, как это показано в двух следующих примерах:
MOV AH,00 ;Режим MOV AH,00 ;Режим MOV AL,03 ;Цвет+текст MOV AL,04 ;Графика среднего INT 10H INT 10H ; разрешения
ТЕКСТОВЫЙ (АЛФАВИТНО-ЦИФРОВОЙ) РЕЖИМ ------------------------------------------------------------
Текстовой режим предназначен для обычных вычислений с вы водом букв и цифр на экран. Данный режим одинаков для черно- белых (BW) и для цветных мониторов за исключением того, что цветные мониторы не поддерживают атрибут подчеркивания. Текстовой режим обеспечивает работу с полным набором ASCII кодов (256 символов), как для черно-белых (BW), так и для цветных мониторов. Каждый символ на экране может отображать ся в одном из 16 цветов на одном из восьми цветов фона. Бордюр экрана может иметь также один из 16 цветов.
Цвета
Тремя основными цветами являются красный, зеленый и синий. Комбинируя основные цвета, друг с другом, можно получить восемь цветов, включая черный и белый. Используя два уровня яркости для каждого цвета, получим всего 16 цветов:
I R G B I R G B Черный 0 0 0 0 Серый 1 0 0 0 Синий 0 0 0 1 Ярко-синий 1 0 0 1 Зеленый 0 0 1 0 Ярко-зеленый 1 0 1 0 Голубой 0 0 1 1 Ярко-голубой 1 0 1 1 Красный 0 1 0 0 Ярко-красный 1 1 0 0 Сиреневый 0 1 0 1 Ярко-сиреневый 1 1 0 1 Коричневый 0 1 1 0 Желтый 1 1 1 0 Белый 0 1 1 0 Ярко-белый 1 1 1 1
Таким образом любые символы могут быть отображены на экране в oдном из 16 цветов. Фон любого символа может иметь один из первых восьми цветов. Если фон и текст имеют один и тот же цвет, то текст получается невидимым. Используя байт атрибута, можно получить также мигающие символы.
Байт-атрибут
Текстовой режим допускает использование байта атрибута, рассмотpенного в главе 9. В приведенной ниже таблице, атрибут BL обозначает мигание (BLinking), RGB - соответствен но красный, зеленый и синий цвет, I - выделение яркостью:
Фон Текст
Ассемблер для IBM PC. Глава 10 230


Атрибут: BL R G B I R G B Номера битов: 7 6 5 4 3 2 1 0
Мигание и выделение яркостью относится к тексту. Ниже приведены некоторые типичные атрибуты:
Текст по фону Бит: 7 6 5 4 3 2 1 0 BL R G B I R G B Шест. Черный по черному 0 0 0 0 0 0 0 0 00 Синий по черному 0 0 0 0 0 0 0 1 01 Красный по синему 0 0 0 1 0 1 0 0 14 Голубой по зеленому 0 0 1 0 0 0 1 1 23 Светло-сиреневый по белому 0 1 1 1 1 1 0 1 7D Серый по зеленому, мигание 1 0 1 0 1 0 0 0 A8
Байт-атрибут используется аналагично показанному для черно-белого (BW) монитора. Тип монитора можно определить из программы с помощью команды INT 11H. Для BW монитора код 07 устанавливает нормальный атрибут. Для цветных мониторов мож но использовать любую из цветовых комбинаций. Цвет на экране сохраняется до тех пор, пока другая команда не изменит его. Для установки цвета можно использовать в команде INT 10H функции AH=06, AH=07 и AH=09. Например, для вывода пяти мигающих звездочек сетло-зеленым цветом на сиреневом фоне возможна следующая программа:
MOV AH,09 ;Функция вывода на экран MOV AL,'*' ;Выводимый символ MOV BH,00 ;Страница 0 MOV BL,0DAH ;Атрибут цвета MOV CX,05 ;Число повторений INT 10H ;Вызвать BIOS
ГРАФИЧЕСКИЙ РЕЖИМ ------------------------------------------------------------
Для генерации цветных изображений в графическом режиме используются минимальные точки растра - пиксели или пэлы (pixel). Цветной графический адаптер (CGA) имеет три степени разрешения:
1. Низкое разрешение (не поддерживается в ROM) обеспечива ет вывод 100 строк по 160 точек (т.е. четыре бита на точку). Каждая точка может иметь один из 16 стандартных цветов, как описано в предыдущем разделе "Цвета". Реализация данного режима включает прямую адресацию контролера Motorola 6845 CRT. Для этого используются два порта: шест.3D4 и 3D5.
2. Среднее разрешение для стандартной цветной графики обеспечивает 200 строк по 320 точек. Каждый байт в этом случае представляет четыре точки (т.е. два бита на точку).
Ассемблер для IBM PC. Глава 10 231


3. Высокое разрешение обеспечивает 200 строк по 640 точек. Поскольку в данном случае требуется 16К байт памяти, высокое разрешение достигается только в черно-белом (BW) режиме. Каждый байт здесь представляет 8 точек (т.е. один бит на точку). Нулевое значение бита дает черный цвет точки, единичное - белый.
Заметим, что в графическом режиме ROM содержит точечные образы только для первых 128 ASCII-кодов. Команда INT 1FH обеспечивает доступ к 1К байтовой области в памяти, определяющей остальные 128 символов. (8 байт на символ). Отображение графических байтов в видео сигналы аналогично, как для среднего, так и для высокого pазрешения.
РЕЖИМ СРЕДНЕГО РАЗРЕШЕНИЯ ------------------------------------------------------------
При среднем разрешении каждый байт представляет четыре точки, пронумерованных от 0 до 3:
Байт: |C1 C0|C1 C0|C1 C0|C1 C0| Пиксели: 0 1 2 3
В любой момент для каждой точки возможны четыре цвета, от 0 до 3. Ограничение в 4 цвета объясняется тем, что двухбитовая точка имеет 4 комбинации значений битов: 00, 01, 10 и 11. Можно выбpать значение 00 для любого из 16 возможных цветов фона или выбрать значение 01, 10, и 11 для одной из двух палитр. Каждая палитpа имеет три цвета:
C1 C0 Палитра 0 Палитра 1
0 0 фон фон 0 1 зеленый голубой 1 0 красный сиреневый 1 1 коричневый белый
Для выбора цвета палитры и фона используется INT 10H. Таким обpазом, если, например, выбран фон желтого цвета и палитра 0, то возможны следующие цвета точки: желтый, зеленый, красный и коричневый. Байт, содержащий значение 10101010, соответствует красным точкам. Если выбрать цвет фона - синий и палитру 1, то возможные цвета: синий, голубой, сиреневый и белый. Байт, содержащий значение 00011011, отображает синюю, голубую, сиреневую и белую точки.
Прерывание BIOS INT 10H для графики
Ассемблер для IBM PC. Глава 10 232
Функция AH=00 команды INT 10H устанавливает графический ркфим. Функция AH=11 команды INT 10H позволяет выбрать цвет палитры и вывести на экран графический символ. Код в регист ре AH определяет функцию:


AH=00: Установка режима. Нулевое значение в регистре AH и 04 в pегистре AL устанавливают стандартый цветной графичес кий режим:
MOV AH,00 ;Функция установки режима MOV AL,04 ;Разрешение 320х200 INT 10H
Установка графического режима приводит к исчезновению курсора с экрана. Подробности по установке режима приведены в главе 9.
AH=0BH: Установка цветовой палитры. Число в регистре BH определяет назначение регистра BL:
BH=00 выбирает цвета фона и бордюра в соответствии с содержимым pегистра BL. Цвет фона от 1 до 16 соответствует шест. значениям oт 0 до F; BH=01 выбирает палитру соответственно содержимому регист ра BL (0 или 1):
MOV AH,0BH ;Функция установки цвета MOV BH,01 ;Выбор палитры MOV BL,00 ; 0 (зеленый, красный, корич.) INT 10H ;Вызвать BIOS
Палитра, установленная один раз, сохраняется, пока не будет отменена другой командой. При смене палитры весь экран меняет цветовую комбинацию. При использовании функции AH=0BH в текстовом режиме, значение, установленное для цвета 0 в палитре, определяет цвет бордюра.
AH=0CH: Вывод точки на экран. Использование кода 0C в регистре AH позволяет вывести на экран точку в выбранном цвете (фон и палитра). Например, для разрешения 320х200 загрузим в регистр DX вертикальную координату (от 0 до 199), а в регистр CX - горизонтальную координату (от 0 до 319). В регистр AL поместим цвет точки (от 0 до 3):
MOV AH,0CH ;Функция вывода точки MOV AL,цвет ;Цвет точки MOV CX,столбец ;Горизонтальная координата MOV DX,строка ;Вертикалькая координата INT 10H ;Вызвать BIOS
AH=0DH: Чтение точки с экрана. Данная функция позволяет прочитать точку для определения ее цвета. В регистр DX должна быть загружена вертикальная координата (от 0 до 199),
Ассемблер для IBM PC. Глава 10 233
а в регистр CX - горизонтальная (от 0 до 319). В регистре AH должно быть значение 0D. Функция возвращает цвет точки в регистре AL.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 10.1 Вывод на экран в цветном графическом режиме.


ПРОГРАММА: УСТАНОВКА ГРАФИЧЕСКОГО РЕЖИМА И ОТОБРАЖЕНИЕ ЦВЕТА ------------------------------------------------------------
Программа, приведенная на рис.10.1, использует команду INT 10H для установки графического режима, выбора зеленого фона и вывода на экран точек (40 строк по 320 столбцов). В программе происходит увеличение значения цвета на 1 для каждой строки. Так как в определении цвета участвуют только три правых бита, цвета повторяются через каждые семь строк. После выполнения программы дисплей остается в графическом режиме. Восстановление текстового режима возможно с помощью команды DOS MODE (MODE CO80) или пользовательской COM программой, в которой для этой цели используется команда INT 10H.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Память объемом 16К для цветного дисплея позволяет хранить дополнительные страницы (экраны). Возможны четыре страницы для экранов на 80 столбцов или восемь страниц для экранов на 40 столбцов.
ъ Графический режим обеспечивает низкое разрешение (не поддерживается в ROM), среднее разрешение (для цветной графики) и высокое разрешение (для черно-белой графи ки).
ъ Точка растра (минимальный элемент графического изображения) представляется определенным числом бит в зависимости от графического адаптера и разрешающей способности (низкой, средней или высокой).
ъ Для графики среднего разрешения на цветном графическом адаптере (CGA) можно выбрать четыре цвета, один из которых принадлежит к 16 возможным цветам, а три других формируют цветовую палитру.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
10.1. Сколько цветов возможно для фона и для текста на стандартном цветном адаптере (CGA) в текстовом режиме?
Ассемблер для IBM PC. Глава 10 234
10.2. Напишите байты атрибуты в двоичном формате для а) сиреневого на ярко-голубом, б) коричневого на желтом, в)красного на сером с миганием.
10.3. Объясните разницу в количестве цветов, возможных при низком, среднем и высоком разрешении.
10.4. Напишите команды для вывода пяти символов карточной масти "бубны" в текстовом режиме ярко-зеленым цветом на сиреневом фоне.
10.5. Напишите команды для установки графического режима с разрешением а) 320х200 в адаптере CGA и б) 640х200 в адаптере EGA.
10.6. Напишите команды для установки синего фона в графичес ком режиме.
10.7. Напишите команды для чтения точки на 12 строке и 13 столбце в графическом режиме.
10.8. Модифицируйте программу на рис.10.1 для: а) графическо го режима на вашем мониторе; б) красного фона; в) строк с 10 по 30; г) столбцов с 20 по 300.
Ассемблер для IBM PC. Глава 11 241


Команды обработки строк


------------------------------------------------------------

Команды обработки строк

Цель: Ообъяснить назначение специальных цепочечных команд, используемых для обработки символьных данных.

ВВЕДЕНИЕ ------------------------------------------------------------

Команды, показанные в предыдущих главах, оперировали одним байтом, или одним словом за одно выполнение. Часто, однако, бывает необходимо переслать или сравнить поля данных, которые превышают по длине одно слово. Например, необходимо сравнить описания или имена для того, чтобы отсортировать их в восходящей последовательности. Элементы такого формата известны как строковые данные и могут являтся как символьными, так и числовыми. Для обработки строковых данных ассемблер имеет пять команд обработки строк:

MOVS переслать один байт или одно слово из одной области памяти в другую;

LODS загрузить из памяти один байт в регистр AL или одно слово в регистр AX;

STOS записать содержимое регистра AL или AX в память;

CMPS сравнить содержимое двух областей памяти, размером в один байт или в одно слово;

SCAS сравнить содержимое регистра AL или AX с содержимым памяти.

Префикс REP позволяет этим командам обрабатывать строки любой длины.

СВОЙСТВА ОПЕРАЦИЙ НАД СТРОКАМИ ------------------------------------------------------------

Цепочечная команда может быть закодирована для повторяю щейся обpаботки одного байта или одного слова за одно выполнение. Например, можно выбрать "байтовую" команду для обработки строки с нечетным числом байт или "двухбайтовую" команду для обработки четного числа байт. Ниже перечислены регистры, участвующие в цепочечных командах (для однобайтовых и двухбайтовых вариантов). Предположим, что регистры DI и SI содержат необходимые адреса:

Команда Операнды Байт Слово MOVS DI,SI MOVSB MOVSW LODS AL,SI или AX,SI LODSB LODSW

Ассемблер для IBM PC. Глава 11 242

STOS DI,AL или DI,AX STOSB STOSW CMPS SI,DI CMPSB CMPSW SCAS DI,AL или DI,AX SCASB SCASW

Например, можно кодировать операнды для команды MOVS, но опустить их для MOVSB и MOVSW. Эти команды предполагают, что pегистры DI и SI содержат относительные адреса, указывающие на необходимые области памяти (для загрузки можно использо вать команду LEA). Регистр SI обычно связан с регистром сегмента данных - DS:SI. Регистр DI всегда связан с регистром дополнительного сегмента - ES:DI. Следовательно, команды MOVS, STOS, CMPS и SCAS требуют инициализации регистра ES (обычно адресом в регистре DS).


REP: ПРЕФИКС ПОВТОРЕНИЯ ЦЕПОЧЕЧНОЙ КОМАНДЫ ------------------------------------------------------------
Несмотря на то, что цепочечные команды имеют отношение к одному байту или одному слову, префикс REP обеспечивает повторение команды несколько раз. Префикс кодируется непо средственно перед цепочечной командой, например, REP MOVSB. Для использования префикса REP необходимо установить начальное значение в регистре CX. При выполнении цепочечной команды с префиксом REP происходит уменьшение на 1 значения в регистре CX до нуля. Таким образом, можно обрабатывать строки любой длины. Флаг направления определяет направление повторяющейся операции: - для направления слева направо неоходимо с помощью команды CLD установить флаг DF в 0; - для направления справа налево необходимо с помощью команды STD установить флаг DF в 1. В следующем примере выполняется пересылка 20 байт из STRING1 в STRING2. Предположим, что оба регистра DS и ES инициализированы адресом сегмента данных:
STRING1 DB 20 DUP('*') STRING2 DB 20 DUP(' ')
... CLD ;Сброс флага DF MOV CX,20 ;Счетчик на 20 байт LEA DI,STRING2 ;Адрес области "куда" LEA SI,STRING1 ;Адрес одласти "откуда" REP MOVSB ;Переслать данные
При выполнееии команд CMPS и SCAS возможна установка флагов состояния, так чтобы операция могла прекратиться сразу после обнаружения необходимого условия. Ниже приведены модификации префикса REP для этих целей.
REP - повторять операцию, пока CX не равно 0;
Ассемблер для IBM PC. Глава 11 243
REPZили REPE - повторять операцию,пока флаг ZF показывает "равноили ноль".Прекратить операцию при флаге ZF, указывающему на не равно или не ноль или при CX равном 0; REPNE или REPNZ - повторять операцию, пока флаг ZF показывает "не равно или не ноль". Прекратить операцию при флаге ZF, указывающему на "равно или нуль" или при CX равным 0.
Для процессоров 8086, 80286 и 80386, обрабатывающих слово за oдно выполнение, использование цепочечных команд, где это возможно, приводит к повышению эффективности работы программы.


MOVS: ПЕРЕСЫЛКА СТРОК ------------------------------------------------------------
На рис.7. 5 была показана программа для пересылки девяти байтового поля. Программа включала три команды для инициали зации и пять команд для цикла. Команда MOVS с префиксом REP и длиной в регистре CX может выполнять пересылку любого числа символов более эффективно. Для области, принимающей строку, сегментным регистром, является pегистр ES, а регистр DI содержит относительный адрес области, передающей строку. Сегментным регистром является регистр DS, а регистр SI содержит относительный адрес. Таким образом,в начале программы перед выполнением команды MOVS необходимо инициализировать регистр ES вместе с регистром DS, а также загрузить требуемые относительные адреса полей в регистры DI и SI. В зависимости от состояния флага DF команда MOVS производит увеличение или уменьшение на 1 (для байта) или на 2 (для слова) содержимого регистров DI и SI. Приведем команды, эквивалентные цепочечной команде REP MOVSB:
JCXZ LABEL2 LABEL1: MOV AL,[SI] MOV [DI],AL INC/DEC DI ;Инкремент или декремент UNC/DEC SI ;Инкремент или декремент LOOP LABEL1 LABEL2: ...
В программе на рис. 11.1 процедура C10MVSB использует команду MOVSB для пересылки содержимого десятибайтового поля NAME1 в поле NAME2. Первая команда CLD сбрасывает флаг направления в 0 для обеспечения процесса пересылки слева направо. В нормальном состоянии флаг DF обычно имеет нулевое значение и команда CLD используется из предосторожности.
Ассемблер для IBM PC. Глава 11 244
Две команды LEA загружают регистры SI и DI относительными адресами NAME1 и NAME2 соответственно. Так как регистры DS и ES были ранее инициализированы адресом DATASG, то полные адреса полей NAME1 и NAME2 будут в регистрах ES:DI и DS:SI. (COM программа автоматически инициализирует регистры ES и DS). Команда MOV заносит в регистр CX значение 10 - длину полей NAME1 и NAME2. Команда REP MOVSB выполняет следующее:
ъ Пересылает самый левый байт из поля NAME1 (адресованно го pегистрами ES:DI) в самый левый байт поля NAME2 (адресованного регистрами DS:SI). ъ Увеличивает на 1 адреса в регистрах DI и SI для следующего байта. ъ Уменьшает CX на 1. ъ Повторяет перечисленные действия (в данном случае 10 раз), пока содержимое регистра CX не станет равным нулю.


Поскольку флаг DF имеет нулевое значение, команда MOVSB увеличивает адреса в регистрах DI и SI, и в каждой итерации процесс переходит на байт вправо, т.е. пересылает байт из NAME1+1 в NAME2+1 и т.д. Если бы флаг DF был равен 1, тогда команда MOVSB уменьшала бы адреса в регистрих DI и SI, выполняя процесс справа налево. Но в этом случае регистры SI и DI необходимо инициализировать адресами последних байтов полей, т.е. NAME1+9 и NAME2+9 соответственно. В процедуре D10MVSW (рис.11.1) используется команда MOVSW, пересылающая одно слово за одно выполнение. Так как команда MOVSW увеличивает адреса в регистрах DS и SI на 2, операция требует только пять циклов. Для процесса пересылки справа налево регистр SI должен быть инициализирован адресом NAME1+8, а регистр DI - NAME2+8.
LODS: ЗАГРУЗКА СТРОКИ ------------------------------------------------------------
Команда LODS загружает из памяти в регистр AL один байт или в регистр AX одно слово. Адрес памяти определяется регистрами DS:SI. В зависимости от значения флага DF происходит увеличение или уменьшение регистра SI. Поскольку одна команда LODS загружает регистр, то практи ческой пользы от префикса REP в данном случае нет. Часто простая команда MOV полностью адекватна команде LODS, хотя MOV генерирует три байта машинного кода, а LODS - только один, но требует инициализацию регистра SI. Можно использо вать команду LODS в том случае, когда требуется продвигаться вдоль строки (по байту или по слову), проверяя загружаемый регистр на конкретное значение. Команды, эквивалентные команде LODSB:
MOV AL,[SI] INC SI
Ассемблер для IBM PC. Глава 11 245
На рис.11.1 процедура E10LODS демонстрирует использование команды LODSW. В примере обрабатывается только одно слово: первый байт из области NAME1 (содержащий As) заносится в регистр AL, а второй байт - в регистр AH. В результате в регистре AX получится значение sA.
STOS: ЗАПИСЬ СТРОКИ ------------------------------------------------------------
Команда STOS записывает (сохраняет) содержимое регистра AL или AX в байте или в слове памяти. Адрес памяти всегда представляется регистрами ES:DI. В зависимости от флага DF команда STOS также увеличивает или уменьшает адрес в регистре DI на 1 для байта или на 2 для слова. Практическая польза команды STOS с префиксом REP - инициализация области данных конкретным значением, например, очистка дисплейного буфера пробелами. Длина области (в байтах или в cловах) загружается в регистр AX. Команды, эквивалентные команде REP STOSB:


JCXZ LABEL2 LABEL1: MOV [DI],AL INC/DEC DI ; Инкремент или декремент LOOP LABEL1 LABEL2: ...
На рис.11.1 процедура F10STOS демонстрирует использование команды STOSW. Операция осуществляет запись шест. 2020 (пробелы) пять раз в область NAME3, причем значение из регистра AL заносится в первый байт, а из регистра AH - во второй. По завершении команды регистр DI содержит адрес NAME3+10.
CMPS: СРАВНЕНИЕ СТРОК ------------------------------------------------------------
Команда CMPS сравнивает содержимое одной области памяти (адресуемой регистрами DS:SI) с содержимыми другой области (адресуемой как ES:DI). В зависимости от флага DF команда CMPS также увеличивает или уменьшает адреса в регистрах SI и DI на 1 для байта или на 2 для слова. Команда CMPS устанавливает флаги AF, CF, OF, PF, SF и ZF. При использовании префикса REP в регистре CX должна находиться длина сравниваемых полей. Команда CMPS может сравнивать любое число байт или слов.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 11.1. Использование цепочечных команд.
Рассмотрим процесс сравнения двух строк, содержащих имена JEAN и JOAN. Сравнение побайтно слева направо приводит к следующему:
Ассемблер для IBM PC. Глава 11 246
J : J Равно E : O Не равно (E меньше O) A : A Равно N : N Равно
Сравнение всех четырех байт заканчивается сравнением N:N - pавно/нуль. Так как имена "не равны", операция должна пре кратиться, как только будет обнаружено условие "не равно". Для этих целей команда REP имеет модификацию REPE, которая повторяет сравнение до тех пор, пока сравниваемые элементы равны, или регистр CX не pавен нулю. Кодируется повторяющее ся однобайтовое сравнение следующим образом:
REPE CMPSB
На рис.11.1 в процедере G10CMPS имеются два примера использования команды CMPSB. В первом примере происходит сравнение содержимого полей NAME1 и NAME2. Так как ранее команда MOVSB переслала содержимое поля NAME1 в поле NAME2, то команда CMPSB продолжается на всех десяти байтах и завершается состоянием pавно/нуль: флаг SF получает значение 0 (положительно) и флаг ZF - 1(нуль). Во втором примере сравнивается поля NAME2 и NAME3. Ранее команда STOSW заполнила поле NAME3 пробелами, поэтому команда CMPB завершается после сравнения первых же байт с результатом "больше/неравно": флаг SF получает значение 0 (положительно) и флаг ZF - 0 (ненуль). Первый пример заканчивается с результатом "равно/нуль" и заносит 01 в регистр BH. Второй пример заканчивается с результатом "неравно" и заносит 02 в регистр BL. При трассировке команд с помощью отладчика DEBUG можно увидеть, что в конце процедуры G10CMPS регистр BX будет содержать значение 0102. Предупреждение! Показанные примеры используют команду CMPSB для сравнения одного байта за одно выполнение. При использовании команды CMPSW для сравнения одного слова, необходимо инициализиpовать регистр CX значением 5. Кроме того следует помнить, что команда CMPSW при сравнении слов переставляет байты. Например, сравнивая имена SAMUEL и ARNOLD команда CMPSW выбирает вместо SA и AR переставленные значения, т.е. AS и RA. В результате вместо "больше" получится "меньше", т.е. неправельный результат. Таким образом команда CMPSW работает правильно только при сравне нии строк, которые содержат числовые данные, определенные как DW, DD или DQ.


SCAS: СКАНИРОВАНИЕ СТРОК ------------------------------------------------------------
Команда SCAS отличается от команды CMPS тем, что сканирует (просматривает) строку на определенное значение байта или слова. Команда SCAS сравнивает содержимое области
Ассемблер для IBM PC. Глава 11 247
памяти (адресуемой pегистрами ES:DI) с содержимым регистра AL или AX. В зависимости от значения флага DF команда SCAS также увеличивает или уменьшает адрес в регистре DI на 1 для байта или на 2 для слова. Команда SCAS устанавливает флаги AF, CF, OF, PF, SF и ZF. При использовании префикса REP и значения длины в регистре CX команда SCAS может сканировать строки любой длины. Команда SCAS особенно полезна, например, в текстовых редакторах, где программа должна сканировать строки, выполняя поиск знаков пунктуации: точек, запятых и пробелов. На рис.11.1 процедура H10SCAS сканирует область NAME1 на строчную букву "m". Так как команда SCASB должна продолжать сканирование, пока результат сравнения - "не равно" или регистр CX не равен нулю, то используется префикс REPNE:
REPNE SCASB
Так как область NAME1 содержит слово "Assemblers", то команда SCASB находит символ "m" в пятом сравнении. При использовании отладчика DEBUG для трассировки команд в конце процедуры H10SCAS можно увидеть в регистре AH значение 03 для индикации того, что символ "m" найден. Команда REP SCASB кроме того уменьшит значение регистра CX от 10 до 06. Команда SCASW сканирует в памяти слово на соответствие значению в регистре AX. При использовании команд LODSW или MOV для пересылки слова в регистр AX, следует помнить, что первый байт будет в регистре AL, а второй байт - в регистре AH. Так как команда SCAS сравнивает байты в обратной последовательности, то oперация корректна.
СКАНИРОВАНИЕ И ЗАМЕНА ------------------------------------------------------------
В процессе обработки текстовой информации может возникнуть необходимость замены определенных символов в тексте на другие, например, подстановка пробелов вместо различных редактирующих символов. В приведенном ниже фрагменте программы осуществляется сканирование cтроки STRING и замена символа амперсанд (&) на символ пробела. Когда команда SCASB обнаружит символ & (в примере это будет позиция STRING+8), то операция сканирования прекратит ся и регистр DI будет содержать aдрес STRING+9. Для получе ния адреса символа & необходимо уменьшить содержимое DI на единицу и записать по полученному адресу символ пробела.


STRLEN EQU 15 ; Длина поля STRING STRING DB 'The time&is now' ... CLD MOV AL,'&' ;Искомый символ MOV CX,STRLEN ;Длина поля STRING LEA DI,STRING ;Адрес поля STRING REPNE SCASB ;Сканировать
Ассемблер для IBM PC. Глава 11 248
JNZ K20 ;Символ найден? DEC DI ;Да - уменьшить адрес MOV BYTE PTR[DI],20H ;Подставить пробел K20: RET
АЛЬТЕРНАТИВНОЕ КОДИРОВАНИЕ ------------------------------------------------------------
При использовании команд MOVSB или MOVSW ассемблер предполагает наличие корректной длины строковых данных и не требует кодирования операндов в команде. Для команды MOVS длина должна быть закодирована в операндах . Например, если поля FLDA и FLDB определены как байтовые (DB), то команда
REP MOVS FLDA,FLDB
предполагает повторяющуюся пересылку байтов из поля FLDB в поле FLDA. Эту команду можно записать также в следующем виде: REP MOVS ES:BYTE PTR[DI],DS:[SI]
Однако загрузка регистров DI и SI адресами FLDA и FLDB oбязательна в любом случае.
ДУБЛИРОВАНИЕ ОБРАЗЦА ------------------------------------------------------------
Команда STOS бывает полезна для установки в некоторой области oпределенных значений байтов и слов. Для дублирова ния образца, длина которого превышает размер слова, можно использовать команду MOVS с небольшой модификацией. Предположим, что необходимо сформировать строку следующего вида: ***---***---***---***---***--- . . .
Вместо того, чтобы определять полностью всю строку, можно определить только первые шесть байтов. Закодируем образец непосредственно перед обрабатываемой строкой следующим образом:
PATTERN DB '***---' DISAREA DB 42 DUP(?) . . CLD MOV CX,21 LEA DI,DISAREA LEA SI,PATTERN REP MOVSW
В процессе выполнения команда MOVSW сначала пересылает первое слово (**) из образца PATTERN в первое слово области DISAREA, затем - второе слово (*-), потом третье (--):
Ассемблер для IBM PC. Глава 11 249
***---***--- | | PATTERN DISAREA
К этому моменту регистр DI будет содержать адрес DISAREA+6, а pегистр SI - PATTERN+6, который также является адресом DISAREA. Затем команда MOVSW автоматически дублирует образец, пересылая первое слово из DISAREA в DISAREA+6, из DISAREA+2, в DISAREA+8, из DISAREA+4 в DISAREA+10 и т.д. В результате образец будет полностью продублирован по всей области DISAREA:


***---***---***---***---***--- . . . ***--- | | | | PATTERN DISAREA+6 DISAREA+12 DISAREA+42
Данную технику можно использовать для дублирования в области памяти любого образца любой длины. Образец должен быть расположен непосредственно перед принимающей областью.
ПРОГРАММА: ВЫРАВНИВАНИЕ ВПРАВО ПРИ ВЫВОДЕ НА ЭКРАН ------------------------------------------------------------
COM-программа, изображенная на рис.1.2, иллюстрирует почти весь материал, приведенный в этой главе. Процедуры программы выполняют следующие действия:
B10INPT Принимает имена длиной до 30 символов, вводимых вверху экрана. D10SCAS Использует команду SCASB для сканирования имен и об хода любого ввода, содержащего символ "звездочка". E10RGHT Использует команду MOVSB для выравнивания имен по правой границе, выводит имена в колонку в правой части экрана. Длина в поле ACTNLEN из списка параметров ввода используется для вычисления самого правого символа в имени, например:
JEROME KERN OSCAR HAMMERSTEIN RICHARD ROGERS
F10CLNM Использует команду STOSW для очистки области имени в памяти.
------------------------------------------------------------ ------------------------------------------------------------ Рис.11.2. Выравнивание вправо при выводе на экран.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 11 250
ъ Для цепочечных команд MOVS, STOS, CMPS и SCAS не забы- вайте инициализировать регистр ES.
ъ Сбрасывайте (CLD) или устанавливайте (STD) флаг направ ления в соответствии с направлением обработки.
ъ Не забывайте устанавливать в регистрах DI и SI необходи мые значения. Например, команда MOVS предполагает операнды DI,SI, а команда CMPS - SI,DI.
ъ Инициализируйте регистр CX в соответствии с количеством байтов или слов, участвующих в процессе обработки.
ъ Для обычной обработки используйте префикс REP для команд MOVS и STOS и модифицированный префикс (REPE или REPNE) для команд CMPS и SCAS.
ъ Помните об обратной последовательности байтов в сравни ваемых cловах при выполнении команд CMPSW и SCASW.


ъ При обработке справа налево устанавливайте начальные адреса на последний байт обрабатываемой области. Если, например, поле NAME1 имеет длину 10 байтов, то для побайтовой обработки данных в этой области справа налево начальный адрес , загружаемый командой LEA, должен быть NAME1+9. Для бработки слов начальный адрес в этом случае - NAME1+8.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
11.1. В данной главе приведены эквивалентные команды для а) MOVSB, б) LODSB и в)STOSB с префиксом REP. Напишите эквивалентные команды для обработки по словам а) MOVSW, б) LODSW и в) STOSW с префиксом 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 слева направо;
Ассемблер для IBM PC. Глава 11 251
б) пересылку данных из 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 раз и выдайте результат на экран.
Ассемблер для IBM PC. Глава 12 262


Арифметические операции I: Обработка двоичных данных


------------------------------------------------------------

Арифметические операции I: Обработка двоичных данных

Цель: Дать сведения об операциях сложения, вычитания, умножения и деления двоичных данных.

ВВЕДЕНИЕ ------------------------------------------------------------

Несмотря на то, что мы привыкли к десятичной арифметике (база 10), компьютер работает только с двоичной арифметикой (база 2). Кроме того, ввиду ограничения, накладываемого 16-битовыми регистрами, большие величины требуют специальной обработки. Данная глава дает сведения об операциях сложения, вычитания, умножения и деления для беззнаковых и знаковых данных. В главе приводятся много примеров и предупреждений о различных ловушках для опрометчивых исследователей мира микропроцессора. В следующей главе будут раскрыты операции преобразования между двоичными данными и ASCII кодами.

СЛОЖЕНИЕ И ВЫЧИТАНИЕ ------------------------------------------------------------

Команды ADD и SUB выполняют сложение и вычитание байтов или слов, содержащих двоичные данные. Вычитание выполняется в компьютере по методу сложения с двоичным дополнением: для второго операнда устанавливаются обратные значения бит и прибавляется 1, а затем проиCXодит сложение с первым операндом. Во всем, кроме первого шага, операции сложения и вычитания идентичны. На рис. 12.1 представленны примеры команд ADD и SUB, обрабатывающие байты или слова. В процедуре B10ADD используется команда ADD для сложения байтов, а в процедуре C10SUB команда SUB вычитает слова. Примеры показывают все пять возможных ситуаций: сложение/вычитание регистр-регистр; сложение/вычитание память-регистр; сложение/вычитание регистр-память; сложение/вычитание регистр-непоср.значение; сложение/вычитание память-непоср.значение.

------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.1 Примеры команд ADD и SUB.

Поскольку прямой операции память-память не существует, данная oперация выполняется через регистр. В следующем примере к содержимому слова WORDB прибавляется содержимое слова WORDA, описанных как DW:


Ассемблер для IBM PC. Глава 12 263
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 ограни чивает возможность компьютера для выполнения арифметических операций. Рассмотрим два способа выполнения арифметических операций. Первый способ - более прост, но специфичен, второй - сложнее, но имеет общий характер.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.2. Сложение двойных слов.
Ассемблер для IBM PC. Глава 12 264
На рис.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, и обpатные значения 3601 в поле WORD3A и 9C11 в поле WORD3B. На рис.12.2 процедура E10DWD демонстрирует подход к сложению значений любой длины. Действие начинается со сложения самых правых слов складываемых полей. В первом цикле складываются правые cлова, во втором - слова, расположенные левее. При этом адреса в регистрах 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, получим процедуру для вычитания.
БЕЗЗНАКОВЫЕ И ЗНАКОВЫЕ ДАННЫЕ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 12 265
Многие числовые поля не имеют знака, например, номер абонента, aдрес памяти. Некоторые числовые поля предлагаются всегда положительные, например, норма выплаты, день недели, значение числа ПИ. Другие числовые поля являются знаковые, так как их содержимое может быть положительным или отрицательным. Например, долговой баланс покупателя, который может быть отрицательным при переплатах, или алгебраическое число. Для беззнаковых величин все биты являются битами данных и вместо ограничения +32767 регистр может содержать числа до +65535. Для знаковых величин левый байт является знаковым битом. Команды ADD и SUB не делают разницы между знаковыми и беззнаковыми величинами, они просто складывают и вычитают биты. В следующем примере сложения двух двоичных чисел, первое число содержит единичный левый бит. Для беззнакового числа биты представляют положительное число 249, для знакового - отрицательное число -7:


Беззнаковое Знаковое 11111001 249 -7 00000010 2 +2 11111011 251 -5
Двоичное представление результата сложения одинаково для беззнакового и знакового числа. Однако, биты представляют +251 для беззнакового числа и -5 для знакового. Таким одразом, числовое содержимое поля может интерпретироваться по разному. Состояние "перенос" возникает в том случае, когда имеется пеpенос в знаковый разряд. Состояние "переполнение" возника ет в том случае, когда перенос в знаковый разряд не создает переноса из разрядной сетки или перенос из разрядной сетки проиCXодит без переноса в знаковый разряд. При возникновении переноса при сложении беззнаковых чисел, результат получает ся неправильный:
Беззнаковое Знаковое 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 (неправильно)
При операциях сложения и вычитания может одновременно возникнуть и переполнение, и перенос:
Ассемблер для IBM PC. Глава 12 266
Беззнаковое Знаковое CF OF 11110110 246 -10 10001001 137 -119 01111111 127 +127 1 1 (неправильно) (неправильно)
УМНОЖЕНИЕ ------------------------------------------------------------
Операция умножения для беззнаковых данных выполняется командой MUL, а для знаковых - IMUL (Integer MULtiplication - умножение целых чисел). Ответственность за контроль над форматом обрабатываемых чисел и за выбор подходящей команды умножения лежит на самом программисте. Существуют две основные операции умножения:
"Байт на байт". Множимое находится в регистре AL, а множи тель в байте памяти или в однобайтовом регистре. После умножения произведение находится в регистре AX. Операция игнорирует и стиpает любые данные, которые находились в регистре 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
Ассемблер для IBM PC. Глава 12 267
Беззнаковое умножение: Команда MUL
Команда MUL (MULtiplication - умножение) умножает беззна ковые числа. На рис. 12.3 в процедуре C10MUL дано три примера умножения: байт на байт, слово на слово и слово на байт. Первый пример команды MUL умножает шест.80 (128) на шест.47 (64). Произведение -ш ест.2000 (8192) получается в регистре AX.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.3. Беззнаковое и знаковое умножение.
Второй пример команды MUL генерирует шест. 10000000 в регистpах DX:AX. Третий пример команды MUL выполняет умножение слова на байт и требует расширение байта BYTE1 до размеров слова. Так как предполагаются беззнаковые величины, то в примере левый бит регистра AH равен нулю. (При использовании команды CBW значение левого бита регистpа 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 и представляет собой oтрицательное значение. Третий пример команды IMUL перед умножением выполняет расширение байта BYTE1 до размеров слова в регистре AX. Так как значения предполагаются знаковые, то в примере используется команда CBW для перевода левого знакового бита в регистр AH: шест.80 в pегистре AL превращается в шест.FF80 в регистре AX. Поскольку множитель в слове WORD1 имеет также отрицательное значение, то произведение должно получится положительное. В самом деле: шест.00400000 в


Ассемблер для IBM PC. Глава 12 268
регистрах 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. Умножение больших чисел требует выполнения некоторых дополнительных действий. Рассматриваемый подход предполагает умножение каждого слова отдельно и сложение полученных результатов. Рассмотрим следующее умножение в десятичном формате:
1365 х12 2730 1365 16380
Представим, что десятичная арифметика может умножать только двухзначные числа. Тогда можно умножить 13 и 65 на 12 раздельно, cледующим образом:
13 65 х12 х12 26 130 13 65 156 780
Следующим шагом сложим полученные произведения, но поскольку число 13 представляло сотни, то первое произведение в действительности будет 15600:
15600
Ассемблер для IBM PC. Глава 12 269
+780 16380
Ассемблерная программа использует аналогичную технику за исключением того, что данные имеют размерность слов (четыре цифры) в шестнадцатеричном формате.
Умножение двойного слова на слово. Процедура E10XMUL на рис.12.4 умножает двойное слово на слово. Множимое, MULTCND, состоит из двух слов, содержащих соответственно шест. 3206 и шест. 2521. Определение данных в виде двух слов (DW) вместо двойного слова (DD) обусловлено необходимостью правильной адресации для команд MOV, пересылающих слова в регистр AX. Множитель MULTPLR содержит шест. 6400. Область для записи произведения, PRODUCT, состоит из трех слов. Первая команда MUL перемножает MULTPLR и правое cлово поля MULTCND; произведение - шест. 0E80 E400 записывается в PRODUCT+2 и PRODUCT+4. Вторая команда MUL перемножает MULTPLR и левое слово поля MULTCND, получая в результате шест. 138A 5800. Далее выполняется сложение двух произведений следующим образом:


Произведение1: 0000 0E80 E400 Произведение 2: 138A 5800 Результат: 138A 6680 E400
Так как первая команда ADD может выработать перенос, то второе cложение выполняется командой сложения с переносом ADC (ADd with Carry). В силу обратного представления байтов в словах в процессоpах 8086/8088, область PRODUCT в действи тельности будет содержать значение 8A13 8066 00E4. Программа предполагает, что первое слово в области PRODUCT имеет начальное значение 0000.
------------------------------------------------------------ ------------------------------------------------------------ Рис.12.4. Многословное умножение.
Умножение "двойного слова на двойное слово". Умножение двух двойных слов включает следующие четыре операции умножения:
Множимое Множитель
слово 2 х слово 2 слово 2 х слово 1 слово 1 х слово 2 слово 1 х слово 1
Каждое произведение в регистрах DX и AX складывается с соответствующим словом в окончательном результате. Пример такого умножения приведен в процедуре F10XMUL на рис. 12.4.
Ассемблер для IBM PC. Глава 12 270
Множимое MULTCND содержит шест. 3206 2521, множитель MULTPLR - шест. 6400 0A26. Результат заносится в область PRODUCT, состоящую из четырех слов. Хотя логика умножения двойных слов аналогична умножению двойного слова на слово, имеется одна особенность, после пары команд сложения ADD/ADC используется еще одна команда ADC, которая прибавляет 0 к значению в поле PRODUCT. Это необходимо потому, что первая команда ADC сама может вызвыть перенос, который последующие команды могут стереть. Поэтому вторая команда ADC прибавит 0, если переноса нет, и прибавит 1, если перенос есть. Финальная пара команд ADD/ADC не тредует дополнительной команды ADC, так как область PRODUCT достаточно велика для генерации окончательного результата и переноса на последнем этапе не будет. Окончательный результат 138A 687C 8E5C CCE6 получится в поле PRODUCT в обратной записи байт в словах. Выполните трассировку этого примера с помощью отладчика DEBUG.
СДВИГ РЕГИСТРОВОЙ ПАРЫ DX:AX ------------------------------------------------------------


Следующая подпрограмма может быть полезна для сдвига содержимого pегистровой пары DX:AX вправо или влево. Можно придумать более эффективный метод, но данный пример представляет общий подход для любого числа циклов (и, соответственно, сдвигов) в регистре CX. Заметьте, что сдвиг единичного бита за разрядную сетку устанавливает флаг переноса.
Сдвиг влево на 4 бита MOV CX,04 ;Инициализация на 4 цикла C20: SHL DX,1 ;Сдвинуть DX на 1 бит влево SHL AX,1 ;Сдвинуть AX на 1 бит влево ADC DX,00 ;Прибавить значение переноса LOOP C20 ;Повторить Сдвиг вправо на 4 бита MOV CX,04 ;Инициализация на 4 цикла D20: SHR AX,1 ;Сдвинуть AX на 1 бит вправо SHR DX,1 ;Сдвинуть DX на 1 бит вправо JNC D30 ;Если есть перенос, OR AH,10000000B ; то вставить 1 в AH D30: LOOP D20 ;Повторить
Ниже приведен более эффективный способ для сдвига влево, не требующий организации цикла. В этом примере фактор сдвига записывается в регистр CL. Пример написан для сдвига на 4 бита, но может быть адаптирован для других величин сдвигов:
MOV CL,04 ;Установить фактор сдвига SHL DX,CL ;Сдвинуть DX влево на 4 бита MOV BL,AH ;Сохранить AH в BL SHL AX,CL ;Сдвинуть AX влево на 4 бита SHL BL,CL ;Сдвинуть BL вправо на 4 бита
Ассемблер для IBM PC. Глава 12 271
OR DL,BL ;Записать 4 бита из BL в DL
ДЕЛЕНИЕ ------------------------------------------------------------
Операция деления для беззнаковых данных выполняется командой DIV, a для знаковых - IDIV. Ответственность за подбор подходящей команды лежит на программисте. Существуют две основные операции деления:
Деление "слова на байт". Делимое находится в регистре AX, а делитель - в байте памяти или а однобайтовом регистре. После деления остаток получается в регистре AH, а частное - в AL. Так как однобайтовое частное очень мало (максимально +255 (шест.FF) для беззнакового деления и +127 (шест.7F) для знакового), то данная операция имеет ограниченное использование.
| AX | | AH | AL | До деления: | Делимое| После: |Остаток|Частное|
Деление "двойного слова на слово". Делимое находится в регистровой паре DX:AX, а делитель - в слове памяти или а регистре. После деления остаток получается в регистре DX, а частное в регистре AX. Частное в одном слове допускает максимальное значение +32767 (шест.FFFF) для беззнакового деления и +16383 (шест.7FFF) для знакового.


| DX || AX | | AH || AL | До деления:|Ст.часть||Мл.часть| После:|Остаток||Частное| | Делимое |
В единственном операнде команд DIV и IDIV указывается делитель. Рассмотрим следующую команду:
DIV DIVISOR
Если поле DIVISOR определено как байт (DB), то операция предполагает деление слова на байт. Если поле DIVISOR определено как слово (DW), то операция предполагает деление двойного слова на слово. При делении, например, 13 на 3, получается разельтат 4 1/3. Частное есть 4, а остаток - 1. Заметим, что ручной калькулятор (или программа на языке BASIC) выдает в этом случае результат 4,333.... Значение содержит целую часть (4) и дробную часть (,333). Значение 1/3 и 333... есть дробные части, в то время как 1 есть остаток от деления.
Беззнаковое деление: Команда DIV
Команда DIV делит беззнаковые числа. На рис.12.5 в процедуре D10DIV дано четыре примера деления: слово на байт, байт на байт, двойное слово на слово и слово на слово.
Ассемблер для IBM PC. Глава 12 272
Первый пример команды DIV делит шест.2000 (8092) на шест.80 (128). В результате остаток 00 получается в регистре AH, а частное шест.40 (64) - в регистре AL. Второй пример команды DIV выполняет прежде расширение байта BYTE1 до размеров слова. Так как здесь предполагается беззнаковая величина, то в примере левый бит регистра AH равен нулю. В результате деления остаток - шест. 12 получает ся в регистре AH, а частное шест.05 - в регистре AL. Третий пример команды DIV генерирует остаток шест. 1000 в регистре DX и частное шест. 0080 в регистре AX. В четвертом примере команды DIV сначала выполняется расширение слова WORD1 до двойного слова в регистре DX. После деления остаток шест.0000 получится в регистре DX, а частное шест. 0002 - в регистре AX.
------------------------------------------------------------ ------------------------------------------------------------ Рис.15.5. Беззнаковое и знаковое деление.
Знаковое деление: Команда IDIV
Команда IDIV (Integer DIVide) выполняет деление знаковых чисел. На рис.12.5 в процедуре E10IDIV используются те же четыре примера деления, что и в процедуре D10DIV, но вместо команд DIV записаны команды IDIV. Первый пример команды IDIV делит шест.2000 (положительное число) на шест.80 (отри цательное число). Остаток от деления - шест. 00 получается в регистре AH , а частное - шест. C0 (-64) - в регистре AL. Команда DIV, используя те же числа, генерирует частное +64. Шестнадцатиричные результаты трех остальных примеров деления приведены ниже:


Пример команды IDIV Остаток Частное
2 EE (-18) FB (-5) 3 1000 (4096) 0080 (128) 4 0000 0002
Только в примере 4 вырабатывается такой же результат, что и для команды DIV. Таким образом, если делимое и делитель имеют одинаковый знаковый бит, то команды DIV и IDIV генерируют одинаковый pезультат. Но, если делимое и делитель имеют разные знаковые биты, то команда DIV генерирует положи тельное частное, а команда IDIV - отрицательное частное. Можно обнаружить это, используя отладчик DEBUG для трасси ровки этих примеров.
Повышение производительности. При делении на степень числа 2 (2, 4, и т.д.) более эффективным является сдвиг вправо на требуемое число битов. В следующих примерах предположим, что делимое находится в регистре AX:
Деление на 2: SHR AX,1
Ассемблер для IBM PC. Глава 12 273
Деление на 8: MOV CL,3 SHR AX,CL
Переполнения и прерывания
Используя команды DIV и особенно IDIV, очень просто вызвать пеpеполнение. Прерывания приводят (по крайней мара в системе, используемой при тестировании этих программ) к непредсказуемым результатам. В операциях деления предполага ется, что частное значительно меньше, чем делимое. Деление на ноль всегда вызывает прерывание. Но деление на 1 генерирует частное, которое равно делимому, что может также легко вызвать прерывание. Рекомендуется использовать следующее правило: если делитель - байт, то его значение должно быть меньше, чем левый байт (AH) делителя: если делитель - слово, то его значение должно быть меньше, чем левое слово (DX) делителя. Проиллюстрируем данное правило для делителя, равного 1:
Операция деления: Делимое Делитель Частное Слово на байт: 0123 01 (1)23 Двойное слово на слово: 0001 4026 0001 (1)4026
В обоих случаях частное превышает возможный размер. Для того чтобы избежать подобных ситуаций, полезно вставлять перед командами DIV и IDIV соответствующую проверку. В первом из следующих примеpов предположим, что DIVBYTE - однобайтовый делитель, а делимое находится уже в регистре AX. Во втором примере предположим, что DIVWORD - двухбайтовый делитель, а делимое находится в регистровой паре DX:AX.


Слово на байт Двойное слово на байт
CMP AH,DIVBYTE CMP DX,DIVWORD JNB переполнение JNB переполнение DIV DIVBYTE DIV DIVWORD
Для команды IDIV данная логика должна учитывать тот факт, что либо делимое, либо делитель могут быть отрицательными, а так как сравниваются абсолютные значения, то необходимо использовать команду NEG для временного перевода отрицательного значения в положительное.
Деление вычитанием
Если частное слишком велико, то деление можно выполнить с помощью циклического вычитания. Метод заключается в том, что делитель вычитается из делимого и в этом же цикле частное увеличивается на 1. Вычитание продолжается, пока делимое остается больше делителя. В cледующем примере, делитель находится в регистре AX, а делимое - в BX, частное вырабатывается в CX:
Ассемблер для IBM PC. Глава 12 274
SUB CX,CX ;Очистка частного C20: CMP AX,BX ;Если делимое < делителя, JB C30 ; то выйти SUB AX,BX ;Вычитание делителя из делимого INC CX ;Инкремент частного JMP C20 ;Повторить цикл С30: RET ;Частное в CX, остаток в AX
В конце подпрограммы регистр CX будет содержать частное, а AX - oстаток. Пример умышленно примитивен для демонстрации данной техники деления. Если частное получается в регистро вой паре DX:AX, то необходимо сделать два дополнения:
1. В метке C20 сравнивать AX и BX только при нулевом DX. 2. После команды SUB вставить команду SBB DX,00.
Примечание: очень большое частное и малый делитель могут вызвать тысячи циклов.
ПРЕОБРАЗОВАНИЕ ЗНАКА ------------------------------------------------------------
Команда NEG обеспечивает преобразование знака двоичных чисел из положительного в отрицательное и наоборот. Практически команда NEG устанавливает противоположные значения битов и прибавляет 1. Примеры:
NEG AX NEG BL NEG BINAMT (байт или слово в памяти)
Преобразование знака для 35-битового (или большего) числа включает больше шагов. Предположим, что регистровая пара DX:AX содержит 32-битовое двоичное число. Так как команда NEG не может обрабатывать два регистра одновременно, то ее использование приведет к неправильному результату. В следую щем примере показано использование команды NOT:


NOT DX ;Инвертирование битов NOT AX ;Инвертирование битов ADD AX,1 ;Прибавление 1 к AX ADC DX,0 ;Прибавление переноса к DX
Остается одна незначительная проблема: над числами, представленными в двоичном формате, удобно выполнять арифме тические операции, если сами числа определены в программе. Данные, вводимые в программу с дискового файла, могут также иметь двоичный формат. Но данные, вводимые с клавиатуры, представленны в ASCII-формате. Хотя ASCII-коды удобны для отображения и печати, они требуют специальных преобразований в двоичный формат для арифметических вычислений. Но это уже тема следующей главы.
Ассемблер для IBM PC. Глава 12 275
ПРОЦЕССОРЫ INTEL 8087 И 80287 ДЛЯ ОБРАБОТКИ ЧИСЛОВЫХ ДАННЫХ ------------------------------------------------------------
Системная плата компьютера содержит пустое гнездо, зарезервированное для числового процессора Intel 8087 (или 80287). Сопроцессор 8087 действует совместно с 8088, а сопро цессор 80287 действует совместно с 80286. Каждый сопроцессор имеет собственный набор команд и средства для операций с плавающей запятой для выполнения экспоненциальных, логарифмических и тригонометрических функций. Сопроцессор содержит восемь 80-битовых регистров с плавающей запятой, которые могут представить числовые значения до 10 в 400 сте пени. Математические вычисления в сопроцессоре выполняются примерно в 100 раз быстрее, чем в основном процессоре. Основной процессор выполняет специальные операции и передает числовые данные в сопроцессор, который выполняет необходимые вычисления и возвращает результат. Для ассембли рования с помощью транслятора MASM, необходимо добавлять параметр /E или /R, например, MASM /R.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
ъ Будьте особенно внимательны при использовании однобайто вых pегистров. Знаковые значения здесь могут быть от -128 до +127.
ъ Для многословного сложения используйте команду ADC для учета переносов от предыдущих сложений. Если операция выполняется в цикле, то используя команду CLC, установите флаг переноса в 0.


ъ Используйте команды MUL или DIV для беззнаковых данных и команды IMUL или IDIV для знаковых.
ъ При делении будьте осторожны с переполнениями. Если нулевой делитель возможен, то обеспечьте проверку этой операции. Кроме того, делитель должен быть больше содержимого регистра AH (для байта) или DX (для слова).
ъ Для умножения или деления на степень двойки используйте cдвиг. Сдвиг вправо выполняется командой SHR для беззна ковых полей и командой SAR для знаковых полей. Для сдвига влево используются идентичные команды SHL и SAL.
ъ Будьте внимательны при ассемблировании по умолчанию. Например, если поле FACTOR определено как байт (DB), то команда MUL FACTOR полагает множимое в регистре AL, а команда DIV FACTOR полагает делимое в регистре AX. Если FACTOR определен как слово (DW), то команда MUL FACTOR полагает множимое в регистре AX, а команда DIV FACTOR полагает делимое в регистровой паре DX:AX.
Ассемблер для IBM PC. Глава 12 276
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
Все вопросы имеют отношение к следующим данным:
DATAX DW 0148H DW 2316H DATAY DW 0237H DW 4052H
12.1. Закодируйте команды для сложения а) слова DATAX со словом DATAY; б) двойного слова, начинающегося по адресу DATAX, с двойным словом в DATAY.
12.2. Объясните действие следующих команд:
STC MOV BX,DATAX ADC BX,DATAY
12.3. Закодируйте команды для умножения (MUL): а) слова DATAX на слово DATAY; б) двойного слова , начинающего ся по адресу DATAX, на слово DATAY.
12.4. Какой делитель, кроме нуля, вызывает ошибку переполнения?
12.5. Закодируйте команды для деления (DIV): а) слова DATAX на 23; б) двойного слова, начинающегося по адресу DATAX, на слово DATAY.
12.6. Последний пример в разделе "Сдвиг регистроврй пары DX:AX" является более эффективным по сравнению с предыдущими примерами для сдвига влево на четыре бита. Измените пример для сдвига вправо на четыре бита.
Ассемблер для IBM PC. Глава 13 1