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

{toc_noshowall}В некоторых языках высокого уровня, таких, например, как ФОРТРАН, использование символического имени (переменной) в качестве операнда является указанием компилятору о необходимости резервирования места для значения данной переменной, которое будет определено позднее, а также о необходимости установить соответствие между адресом зарезервированной области памяти и данным именем. В языке ассемблера все обстоит иначе. Любое символическое имя, появляющееся в программе на языке ассемблера, должно быть определено помещением его в поле имени в одном из предложений этой программы. Мы уже видели, что, например, присваивание имени LOOP команде

LOOP AR 10,5

позволяет затем написать

В LOOP

для осуществления перехода на указанную команду AR. При рассмотрении подобной ситуации ассемблер просто связывает имя LOOP с адресом начала помеченной этим именем команды, в данном случае AR. Операндом в машинной команде безусловного перехода будет являться адрес, соответствующий имени LOOP.

Для резервирования памяти и установления связи между переменными и машинными адресами выделенных для них областей памяти используются псевдокоманды DS и DC. С помощью псевдокоманд EQU константам присваиваются имена.

Когда мы составляем программу для обработки некоторой информации, хранящейся в памяти, то нам нужно позаботиться о резервировании места для этой информации. Одним из способов резервирования памяти является использование команды DS (Define Storage — определить память).

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

В качестве примера рассмотрим предложение

WORDS DS 3F

Символ DS располагается в поле операции. Это предложение запрашивает резервирование трех полных слов (3F) и присвоение имени WORDS адресу первого из зарезервированных 12 байтов. Поскольку выделение памяти мы запросили полными словами, будет произведено выравнивание по границе полного слова, т.е. в качестве адреса WORDS будет выбрано значение, кратное 4.

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

WORDS

LR

AR

DS

MR

3,8

4,6

3F

2,6

Некоторое количество памяти может быть пропущено ассемблером для осуществления выравнивания. Ассемблирование первых двух предложений даст результат

18381А46

Если следующий байт является первым байтом полного слова, то память будет выделена так:

18381А4612 байтов зарезервировано 1С26

Если следующий после команды AR байт не является байтом полного слова, то для выравнивания будут пропущены 2 байта:

18381А46  2 байта пропущено  12 байтов зарезервировано  1С26

В рассмотренном примере для иллюстрации принципа выполнения команды DS ассемблером она была помещена между некоторыми машинными командами. Но при выполнении написанной программы машина будет интерпретировать то, что внесено в поле WORDS, как команды со всеми вытекающими отсюда последствиями. Поэтому никогда не следует резервировать память там, где информация, записанная в ней, может быть воспринята как часть выполняемой программы. К вопросу о том, куда же на самом деле следует помещать команды DS и DC, мы вернемся позднее.

Общей формой DS-предложения является

Имя DS КолТип

Поле имени может содержать любое имя, допустимое в языке ассемблера, а также может быть оставлено пустым. Сокращение «Кол» (количество) означает коэффициент повторения, т. е. число единиц памяти определенного типа, которое должно быть зарезервировано. Значения в поле «Тип» могу? быть следующими:

F: Полное слово

Н: Полуслово

D: Двойное слово

С: Байт

Результатом выполнения команды DS будет:

1. Резервирование стольких единиц памяти типа, указанного в поле «Тип», сколько задано полем «Кол» с выравниванием границ, если это необходимо.

2. Присвоение символического имени, указанного в поле имени

команды, адресу первого байта зарезервированной области. Например, предложение

HALVES DS 5Н

резервирует пять полуслов и присваивает имя HALVES первому зарезервированному байту. Аналогично предложение

DOUBLE DS D

резервирует одно двойное слово (единица, соответствующая значению поля «Кол», может быть опущена), которое получает имя DOUBLE.

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

Команда DC (Define Constant — Определить константу) работает так же, как и DS, но в отличие от нее позволяет производить загрузку начального значения в резервируемую область непосредственно при резервировании. Таким образом, к началу выполнения программы память, заказанная по командеDC, содержит вполне определенную информацию, тогда как никогда нельзя с уверенностью сказать, что содержит сегмент памяти после выполнения команды DS.

Предложение DC имеет формат

Имя DC КолТип'Значение'

«Кол» и «Тип» имеют тот же самый смысл, что и для команды DS. Пока для указания типа будем использовать только

F: Полное слово

D: Двойное слово

Н: Полуслово

Возможно задание и некоторых других типов, которые мы рассмотрим впоследствии.

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

В результате выполнения команды DC

1. Производится резервирование «Кол» единиц памяти типа «Тип», возможно, с выравниванием границ.

2. Символическое имя, указанное в поле имени, присваивается адресу первого байта зарезервированной области.

3. «Значение» в двоичной форме заносится в эту область памяти.

Отметим, что это значение должно быть заключено в апострофы.

Рассмотрим, например, к чему приведет выполнение предложения

ONE DC 1F' 1'

Именем в данном случае является ONE, количество повторений равно 1 (так же как и в случае DS-предложения, единицу можно было опустить), тип — F, а значением является 1. Включение в программу указанного предложения приведет к резервированию выровненного полного слова, присвоению имениONE адресу первого из зарезервированных байтов и записи на выделенное место значения 00000001.

Рассмотрим другой пример,

HALF DC ЗН' — 2'

Здесь резервируется место для трех полуслов, и в каждое из них записывается — 2 в двоичной форме, т. е. FFFE. Имя HALF будет идентифицировать адрес первого байта первого полуслова.

Пусть рассмотренное предложение включено в программу следующим образом (лучше никогда не помещать команды DS и DC в середину программы, сейчас это делается исключительно в целях иллюстрации):

 

AR

5,7

 

LR

8,5

HALF

DC

3H' — 2'

 

SR

8,7


Заметим, что, так как все указанные команды имеют четную длину, условия выравнивания резервируемой области по границе полуслова автоматически выполнены в данном случае. В оттранслированном виде данный фрагмент программы будет выглядеть так:

1A571885FFFEFFFEFFFE1В87

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

DUB DC D'100'

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

Куда же следует помещать предложения DC и DS?

Большая часть обрабатываемых программой данных не является машинными командами. В одном из предыдущих примеров команда DC была помещена в середину программы и был задан вопрос, как пойдет выполнение этой программы в данном случае. Ответ таков: команды AR и LR будут выполнены нормально, затем машина попытается выполнить «команду» с именем HALF. Содержимое первого полуслова (FFFE) будет рассматриваться как команда, а значит, кодом операции является FF. Операции с таким кодом не существует, поэтому выполнение программы будет прервано и будет отмечен особый случай, вызванный недействительным кодом операции. Это означает, что была произведена попытка выполнения несуществующей команды.

Таким образом, данные не должны находиться там, где они могут интерпретироваться как команды. Можно (и часто приходится) записывать данные в середину программы, но в этом случае в нее должны быть внесены и дополнительные команды перехода для предотвращения рассмотренных нежелательных последствий. На рис. 6.5 показано, как это может быть сделано.

Если нет особой необходимости делать это иначе, размещайте данные после последней реально выполняемой машинной команды в программе. Для нас это означает пока, что предложения DS и DC желательно помещать между последним предложением EOJ и предложением END программы.

Эта команда позволяет сопоставлять одни символические имена другим, а также вводить имена для обозначения выражений и констант. Символическое имя, находящееся в поле имени, сопоставляется с выражением в поле операндов. Рассмотрим пример:

REGSIX EQU 6

R7 EQU 7

Первое предложение присваивает имени REGSIX числовое значение 6, второе — имени R7 значение 7. Если приведенные предложения будут включены в программу, содержащую команду

AR REGSIX,R7

то ее выполнение будет эквивалентно выполнению команды

AR 6,7

Символические имена в поле операндов просто заменены соответствующими значениями.

Рассмотрим теперь другой пример:

A DS 3F

В EQU А

С EQU А+8

Первое предложение резервирует три полных слова и присваивает имя А первому из зарезервированных байтов. Второе сопоставляет имени В имя А, т. е. делает два имени взаимозаменяемыми. С этого момента использование В в качестве операнда становится эквивалентным использованию А. Команда EQU не оказывает непосредственного влияния на конечную форму программы на машинном языке. В частности, не происходит даже резервирования памяти для В. Имя В просто сопоставляется имени А.

Третье предложение демонстрирует еще одну возможность использования символических имен для идентификации адресов. А идентифицирует первый байт области, зарезервированной DS-предложением. Интуитивно ясно, что обозначает А+8: адрес, равный А+8, или байт, отстоящий от байта А на 8. Последнее предложение, следовательно, указывает, что имя С теперь присвоено третьему полному слову поля А.

Несмотря на то что в поле операндов можно записывать очень сложные выражения для обозначения адресов и констант, практически чаще всего используются выражения вида

имя ± константа

Общие правила составления и записи выражений можно найти в соответствующем руководстве по ассемблеру.

Наконец, рассмотрим результат выполнения пары команд

NEXTNUM EQU *

RWD 2

Символ * в поле операндов предложения языка ассемблера является ссылкой на текущее значение счетчика размещения. В данном случае, поскольку предложение EQU не вызывает генерации машинных кодов, текущим значением счетчика размещения является адрес первого байта команды RWD.Выполнение указанной пары команд эквивалентно выполнению всего лишь одной:

NEXTNUM RWD 2

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

На рис. 6.6 представлена программа, приведенная ранее на рис. 5.10, модифицированная путем внесения в нее команд EQU, присваивающих символические имена регистрам. При этом используются обозначения блок-схемы рис. 5.5.

 

AVERAGE

START 0

START OF PROGRAM

 

PRINT NOGEN

00 NOT PRINT MACRO EXPANSIONS

 

INITIAL

BEGIN EXECUTION HERE

SUM

ECU 5

SUM IS IN REGISTER 5

A

EQU 2

A IS IN REGISTER 2

К

EQU 9

К TS IN REGISTER 9

ONE

EQU 6

REGISTER 6 WILL CONTAIN A 1

KK

EQU 10

REGISTER 10 CONTAINS A COPY OF К

REM

EQU A

REGISTER 4 WILL CONTAIN THE REMAINDER

 

P. НО К

RE AO NUMBER OF NUMBERS TO USE

 

LR KK,K

KEEP A COPY IN REGISTER 10

 

L ONE,=F'1'

DECREMENT FOR COUNTING

 

SR SUM,SUM

START SUM AT 0

NEXTNUM

EQU *

 

 

RWD A

READ A NUMBER

 

AR SUM* A

ADO IT TO SUM

 

SR K, ONE

DECREMENT LOOP COUNTER

 

BNM NEXTNUM

GO BACK FOR MORE IF NOT DONE

 

MR REM,ONE

PREPARE FOR DIVISION BY NUMBER OF

 

CR REM,KK

NUMBERS TO COMPUTE AVERAGE

 

WWD SUM

QUOTIENT IN REGISTER 5

 

WWD REM

AND REMAINDER IN REGISTER 4

 

EOJ

PROCESSING FINISHED. STOP

 

END AVERAGE

END OF PROGRAM

Рис. 6.6. Применение предложения EQU для приписывания символических имен номерам регистров и адресам памяти.

Частота использования команд EQU зависит исключительно от самого программиста. Некоторые стараются совершенно исключить конкретные номера регистров из программы путем включения в нее соответствующих предложений EQU. Другие избегают их применения, устраняя тем самым опасность некорректного определения операндов. Есть и такие, которые практически не используют их, целиком полагаясь на программную документацию для определения содержимого конкретных регистров. Я считаю, что пока вам будет полезно придерживаться третьего пути.

 
Статьи раздела