Справочник программиста на персональном компьютере фирмы IBM. Приложения
Страница 5. Основные сведения о языке ассемблера


Приложение В. Основные сведения о языке ассемблера.


   Читатель  этой  книги, не знакомый с языком ассемблера,  скоро
поймет, что многие программистские трюки не могут быть достигнуты
другими  средствами.  Хотя изучение языка ассемблера требует  от-
дельной книги, в  этом  приложении  приводятся  основные понятия,
которые  помогут новичкам разобраться в примерах на  этом  языке.
Внимательный просмотр  разделов,  посвященных  среднему и низкому
уровням,  даст Вам возможность получить представление о том,  как
работает ассемблер, после чего намного легче изучить разные част-
ные  вопросы.  Здесь обсуждаются не все ассемблерные  инструкции,
встречающиеся в программах,  но  Вы  обнаружите,  что  около 95 %
инструкций, встреченных Вами в программах, описаны здесь, а  зна-
чение остальных может быть  понято благодаря комментариям к прог-
раммам.
   Микропроцессор 8088 имеет 13 16-разрядных регистров, каждый из
которых имеет свои  функции.  В  то  время  как в языках высокого
уровня  Вы можете поместить два числа в переменные, а затем  сло-
жить эти переменные, то в языке ассемблера эти числа помещаются в
регистры микропроцессора, а затем складываются значения, содержа-
щиеся в  регистрах.  Все  операции  в  языке ассемблера состоят в
обмене  данных  с регистрами, а затем выполнении операций на  ре-
гистрах, таких как изменение отдельных  битов, выполнение арифме-
тических  операций и т.д.  Одной из причин высокой  эффективности
языка ассемблера является  хранение  данных в регистрах микропро-
цессора;  компиляторы имеют тенденцию возвращать все  значения  в
память после выполнения операции, а доступ к памяти требует боль-
шого времени.  На рис. В-1 показаны 13 регистров микропроцессоров
8088 и 80286 (последний имеет дополнительные  средства для много-
задачной работы, которые мы не будем рассматривать здесь).

   Регистры  AX, BX, CX и DX являются регистрами общего  назначе-
ния.  Их особенность состоит в  том,  что операции могут произво-
диться  не только над содержимым всего регистра, но  также и  над
половиной. Каждый из четырех регистров делится на старшую и млад-
шую  части, например, AH обозначает старшую половину регистра AX,
а AL - младшую.  Точно так же  ассемблерная программа может иметь
доступ  к BH, BL, CH, CL, DH и DL.  Это свойство  очень  полезно,
поскольку часто программе  приходится работать с байтными величи-
нами.   Регистры  BP, SI и DI также достаточно удобны,  хотя  они
могут принимать только  16-битные  значения.  Каждый бит регистра
флагов сообщает о соответствующем статусе процессора, например, о
том, что при выполнении  арифметической  операции  был перенос за
разрядную сетку.
   В общем случае значения помещаются в регистры с помощью  инст-
рукции MOV. MOV  AX,BX  пересылает  содержимое  регистра BX в AX,
затирая  ранее содержащееся в AX значение.  MOV AH,BL приводит  к
пересылке байта из регистра в  регистр, но MOV AX,BL - недопусти-
мая  инструкция, так как значения должны иметь одинаковый размер.
Инструкция MOV можеть также передавать значения из памяти, напри-
мер,  MOV  AX,ACCT_NUMBER.  Здесь ACCT_NUMBER -  имя  переменной,
которую создал программист,  совсем  как в языке высокого уровня.
Переменная создается оператором вида ACCT_NUMBER DW 0.  Этот опе-
ратор оставляет  место  для  слова  (двух  байтов), присваивая им
значение 0.  Другие допустимые символы в этом операторе это DD  -
для двойного слова и DB - для байта  или строк.  Ассемблер следит

за адресами переменных, поэтому при ассемблировании оператора MOV
AX,ACCT_NUMBER имя переменной заменяется на ее адрес.
   Работа с именами переменных - самый простой способ идентифика-
ции данных в программах на языке ассемблера. Но имеются различные
способы хитрой  адресации,  которые  позволяют  программе хранить
массивы  или использовать указатели.  Например,  MOV  AX,[BX][SI]
посылает в AX значение, которое  содержится  по смещению, равному
сумме  значений регистров BX и SI.  Но от чего отсчитывать смеще-
ние? Ответ заключается в том, что все данные собраны в одну часть
программы,  а весь исполняемый код - в другую.  Часть, отведенная
под данные, называется сегментом данных,  а под программу - кодо-
вым  сегментом.  Все переменные, отведенные для хранения  данных,
адресуются через смещение относительно начала сегмента данных.
   Позиция в памяти, с которой начинается сегмент данных, хранит-
ся в регистре DS, одном из  четырех  сегментных  регистров. Как и
все  остальные регистры микропроцессора он 16-разрядный,  поэтому
он не может содержать числа, большие  чем 65535. Каким же образом
сегмент  даных может указывать на ячейки памяти, расположенные  в
верхней части мегабайтного адресного  пространства? Ответ состоит
в том, что сегментные регистры автоматически умножаются на 16,  а
результат указывает на  место  в  памяти,  с  которого начинается
сегмент.  Таким образом, сегменты всегда выравнены на  16-байтную
границу. После того как сегмент установлен, все остальные регист-
ры  могут  содержать смещения, указывающие на любой из  следующих
65535 байтов. Регистр дополнительного сегмента (ES) также исполь-
зуется для указания на данные, хранящиеся в памяти.
   Среди  ассемблерных инструкций, которые Вы часто будете встре-
чать в этой книге, есть инструкции  загрузки сегментных и относи-
тельных адресов переменных.  MOV AX,SEG ACCT_NUMBER помещает зна-
чение сегментного регистра,  в  котором  расположен ACCT_NUMBER в
AX, а впоследствии это значение будет переслано в DS. MOV BX,OFF-
SET ACCT_NUMBER помещает в BX  смещение  переменной ACCT_NUMBER в
сегменте данных.  После выполнения этих операций DS:BX будут ука-
зывать на ACCT_NUMBER. Если  ACCT_NUMBER является одномерным мас-
сивом,  то  для  указания на определенный элемент  массива  может
использоваться  добавочное  смещение.  Вы  часто будете встречать
также  инструкцию  LEA,  предоставляющую другой  способ  загрузки
смещения.
   Кодовый сегмент содержит  последовательность машинных инструк-
ций, составляющих программу.  Например, инструкция MOV существует
в виде нескольких байтов машинного кода, значение байтов которого
определяет  в какой регистр идет пересылка и откуда.  Регистр  IP
(счетчик команд) содержит величину смещения, которая указывает на
ту  инструкцию  в кодовом сегменте, которая сейчас должна  выпол-
няться.  После выполнения инструкции IP увеличивается таким обра-
зом,  чтобы  он указывал на следующую инструкцию.   В  простейшей
программе счетчик команд  будет  передвигаться  от  первого байта
кодового сегмента к последнему, где программа и завершится.   Но,
как и другие программы,  программа на языке ассемблера может быть
разбита на процедуры (подпрограммы), поэтому счетчик команд может
прыгать из одного места кодового сегмента в другое.
   Когда счетчик команд прыгает в другое место кодового сегмента,
то  его старое значение должно быть запомнено, с тем чтобы  можно
было вернуться в нужное место, так как это делает оператор RETURN

в  Бейсике, возвращая управление в то место, откуда была  вызвана
процедура. В языке ассемблера  процедуре присваивается имя, напр-
имер,  COMBINE_DATA, и оператор CALL COMBINE_DATA передает управ-
ление в процедуру.  Процедура  завершается инструкцией RET (возв-
рат).  При вызове процедуры процессор запоминает текущее значение
счетчика команд, заталкивая его на стек.
   Стек это область, используемая для временного хранения данных.
После завершения процедуры старое значение счетчика команд берет-
ся из стека и выполнение  программы  продолжается. Стек также со-
держится  в  отдельном сегменте, который, совершенно естественно,
называется сегментом стека. Ему  соответствует сегментный регистр
SS. В регистре SP хранится указатель стека, который всегда указы-
вает на вершину стека и  изменяется при засылке на стек и выборке
из стека.

 
« Предыдущая статья   Следующая статья »