ТОП 10:

Директивы распределения памяти



3.2.1. Псевдокоманды определения переменных

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

 

имя_переменной d* значение
где D* — одна из нижеприведенных псевдокоманд:

DB — определить байт;

DW — определить слово (2 байта);

DD — определить двойное слово (4 байта);

DF — определить 6 байт (адрес в формате 16-битный селектор: 32-битное смещение);

DQ — определить учетверенное слово (8 байт);

DT — определить десять байт (80-битные типы данных, используемые FPU).

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

 

text_string db ‘Hello world!’

number dw 7

table db 1,2,3,4,5,6,7,8,9,0Ah,0Bh,0Ch,0Dh,0Eh,0Fh

float_number dd 3.5e7

заполняет данными 33 байта. Первые 12 байт содержат ASCII-коды символов строки «Hello world!», и переменная text_string указывает на первую букву в этой строке, так что команда

mov al,text_string

считает в регистр AL число 48h (код латинской буквы «Н»). Если вмес­то точного значения указан знак «?», переменная считается неинициа­лизированной и ее значение на момент запуска программы может ока­заться любым. Если требуется заполнить участок памяти повторяющимися данными, используется специальный оператор DUP, имеющий формат счетчик DUP (значение). Например, определение

 

table_512w dw 512 dup(?)

создает массив из 512 неинициализированных слов, на первое из кото­рых указывает переменная table_512w. В качестве аргумента в операто­ре DUP могут выступать несколько значений, разделенных запятыми, и даже дополнительные вложенные операторы DUP.

Структуры

Директива STRUC позволяет определить структуру данных аналогич­но структурам в языках высокого уровня. Последовательность директив

 

имя struc

поля

имя ends

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

 

метка имя <значения>

И наконец, для чтения или записи в элемент структуры используется оператор «.» (точка). Например:

point struc ; определение структуры

x dw 0 ; три слова

y dw 0 ; со значениями по умолчанию

z dw 0 ; 0, 0, 0

color db 3 dup(?) ; и три байта

point ends

cur_point point <1,1,1,255,255,255> ; инициализация

mov ax,cur_point.x ; обращение к слову “х”

Если была определена вложенная структура, доступ к ее элементам осуществляется через еще один оператор «.» (точка).

color struc ; определить структуру color

red db ?

gren db ?

blue db ?

color ends

 

point struc

x dw 0

y dw 0

z dw 0

clr color <>

point ends

 

cur_point point <>

mov cur_point.clr.red,al ; обращение к красной компоненте цвета точки cur_point

 

Организация программы

Сегменты

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

Сегмент программы описывается директивами SEGMENT и ENDS

имя_сегмента segment readonly выравнивание тип разряд ‘класс’

имя_сегмента ends

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

Все пять операндов директивы SEGMENT необязательны.

READONLY: Если этот операнд присутствует, MASM выдаст сообщение об ошибке на все команды, выполняющие запись в этот сегмент. Другие ассемблеры этот операнд игнорируют.

Выравнивание: Указывает ассемблеру и компоновщику, с какого адреса может начинаться сегмент. Значения этого операнда: BYTE — с любого адреса; WORD — с четного адреса; DWORD — с адреса, кратного 4; PARA — с адреса, кратного 16 (граница параграфа); PAGE — с адреса, кратного 256.

По умолчанию используется выравнивание по границе параграфа.

Тип: Выбирает один из возможных типов комбинирования сегментов:

Тип PUBLIC (иногда используется синоним MEMORY) означает, что все такие сегменты с одинаковым именем, но разными класса­ми будут объединены в один.

Тип STACK — то же самое, что и PUBLIC, но должен использоваться для сегментов стека, так как при загрузке программы сегмент, полученный объединением всех сегментов типа STACK, будет ис­пользоваться как стек.

Сегменты типа COMMON с одинаковым именем также объединяются в один, но не последовательно, а по одному и тому же адресу, так что длина суммарного сегмента будет равна не сумме длин объединяемых сегментов, как в случае PUBLIC и STACK, а длине максимального. Таким способом иногда можно формировать оверлейные программы.

Тип AT — выражение указывает, что сегмент должен располагаться по фиксированному абсолютному адресу в памяти. Результат выражения, использующегося в качестве операнда для AT, равен этому адресу, деленному на 16. Например: segment at 40h — сегмент, начинающийся по абсолютному адресу 0400h. Такие сег­менты обычно содержат только метки, указывающие на области памяти, которые могут потребоваться программе.

PRIVATE (значение по умолчанию) — сегмент такого типа не объеди­няется с другими сегментами.

Разрядность: Этот операнд может принимать значения USE16 и USE32. Размер сегмента, описанного как USE16, не может превышать 64 Кб, и все команды и адреса в этом сегменте считаются 16-битными. В этих сегментах все равно можно применять команды, использующие 32-битные регистры или ссылающиеся на данные в 32-битных сегментах, но они будут использовать префикс изменения разрядности операнда или адреса и окажутся длиннее и медленнее. Сегменты USE32 соответственно могут занимать до 4 гигабайтов, и все команды и адреса в них по умолчанию 32-битпые. Если разрядность сегмента не указана явно, используется USE16 по умолчанию при условии, что перед директивой .MODEL не использовалась директива задания допустимого набора команд .386 или старше.

Класс сегмента — это любая метка, взятая в одинарные кавычки. Все сегменты с одинаковым классом, даже сегменты типа PRIVATE, будут расположены в исполнимом файле непосредственно друг за другом.

 

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

 

имя_группы group имя_сегмента

Операнды этой директивы — список имен сегментов (или выражений, использующих оператор SEG), которые объединяются в группу. Имя группы теперь можно использовать вместо имен сегментов для получения сегментного адреса и для директивы ASSUME.

 

assume регистр:связь. . .

Директива ASSUME указывает ассемблеру, с каким сегментом или группой сегментов связан тот или иной сегментный регистр. В качестве операнда «связь» могут использоваться имена сегментов, имена групп, выражения с оператором SEG или слово «NOTHING», означающее отмену действия предыдущего ASSUME для данного регистра. Эта директива не изменяет значений сегментных регистров, а только позволяет ассемблеру проверять допустимость ссылок и самостоятельно вставлять префиксы переопределения сегментов, если они необходимы.

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

3.3.2. Модели памяти и упрощенные директивы определения сегментов

Модели памяти задаются директивой .MODEL

.model модель, язык, модификатор

где модель — одно из следующих слов:

TINY — код, данные и стек размещаются в одном и том же ceгменте размером до 64 Кб. Эта модель памяти чаще всего используется при написании на ассемблере небольших программ.

SMALL — код размещается в одном сегменте, а данные и стек — в дру­гом (для их описания могут использоваться разные сегменты, но объединенные в одну группу). Эту модель памяти также удобно ис­пользовать для создания программ на ассемблере.

COMPACT — код размещается в одном сегменте, а для хранения данных могут использоваться несколько сегментов, так что для обра­щения к данным требуется указывать сегмент и смещение (данные дальнего типа).

MEDIUM — код размещается в нескольких сегментах, а все данные — в одном, так что для доступа к данным используется только смещение, а вызовы подпрограмм используют команды дальнего вызова процедуры.

LARGE и HUGE — и код, и данные могут занимать несколько сегмен­тов.

FLAT — то же, что и TINY, но используются 32-битные сегменты, так что максимальный размер сегмента, содержащего и данные, и код, и стек, — 4 мегабайта.

Язык — необязательный операнд, принимающий значения С, PASCAL, BASIC, FORTRAN, SYSCALL и STDCALL. Если он указан, ассемблер считает, что все процедуры рассчитаны на вызов из программ на соответствующем языке высокого уровня, так что, например, если указан язык С, все имена ассемблерных процедур, объявленных как PUBLIC, будут изменены так, чтобы начинаться с символа подчеркивания, как это принято в С.

Модификатор — необязательный операнд, принимающий значения NEARSTACK (по умолчанию) или FARSTACK. Во втором случае сег­мент стека не будет объединяться в одну группу с сегментами данных.

 

После того как модель памяти определена, вступают в силу упрощенные директивы определения сегментов, объединяющие действия директив SEGMENT и ASSUME. Кроме того, сегменты, объявленные упрощенными директивами, не требуется закрывать директивой ENDS — они закрываются автоматически, как только ассемблер обнаруживает новую директиву определения сегмента или конец программы.

Директива .CODE описывает основной сегмент кода. Описание

.code имя_сегмента

эквивалентно

_TEXT segment word public ‘CODE’

для моделей TINY, SMALL и COMPACT и

name_TEXT segment word public 'CODE'

для моделей MEDIUM, HUGE и LARGE (name — имя модуля, в котором описан данный сегмент). В этих моделях директива .CODE также допускает необязательный операнд — имя определяемого сегмента, но все сегменты кода, описанные так в одном и том же модуле, объединя­ются в один сегмент с именем NAME_TEXT.

 

.stack размер

Директива .STACK описывает сегмент стека и эквивалентна директиве

STACK segment para public ‘STACK’

Необязательный параметр указывает размер стека. По умолчанию он равен 1 килобайту.

.data

Описывает обычный сегмент данных и соответствует директиве

_DATA segment word public ‘DATA’

.data?

Описывает сегмент неинициализированных данных:

_BSS segment word public ‘BSS’

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

 

.const

Описывает сегмент неизменяемых данных:

 

CONST segment word public 'CONST'

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

 

.fardata имя_сегмента

Сегмент дальних данных:

имя_сегмента segment para private 'FAR_DATA’

Доступ к данным, описанным в этом сегменте, потребует загрузки сегментного регистра. Если не указан операнд, в качестве имени сегмента используется FAR_DATA.

 

.fardata? имя_сегмента

Сегмент дальних неинициализированных данных:

имя_сегмента segment para private 'FAR_BSS’

Так же как и в случае FARDATA, доступ к данным из этого сегмента потребует загрузки сегментного регистра. Если имя сегмента не указано, используется FAR_BSS.

Во всех моделях памяти сегменты, описанные директивами .DATA, .DATA?, .CONST, .FARDATA и .FARDATA?, а также сегмент, описанный директивой .STACK, если при описании модели не был указан модифи­катор FARSTACK, и сегмент .CODE в модели TINY автоматически объе­диняются в группу с именем FLAT — для модели памяти FLAT или DGROUP — для всех остальных моделей. При этом сегментный регистр DS (и SS, если не было FARSTACK, и CS в модели TINY) настраивается на всю эту группу, как если бы была выполнена команда ASSUME.

Процедуры

Во многих программах встречаются участки, которые требуется выполнять неоднократно. Такие повторяющиеся участки целесообразно выделить из общей программы, оформив их в виде подпрограмм, и обращаться к ним каждый раз, когда в основной программе возникает необходимость их выполнения. Эти подпрограммы в терминологии языка ассемблера называются процедурами. Управление процедуре передается командой CALL (вызов процедуры), и каждая процедура должна обеспечить возможность возврата управления в основную программу командой RET. Ассемблер, в отличие от некоторых других языков программирования, не накладывает на процедуры никаких ограничений — на любой адрес программы можно передать управление командой CALL, и оно вернется к вызвавшей процедуре, как только встретится команда RET. Такая свобода выражения легко может приводить к трудночитаемым программам, и в язык ассемблера были включены директивы логического оформления процедур.

 

метка proc язык тип USES регистры ; TASM

или

 

метка proc тип язык USES peгистры ; MASM

. . .

ret

метка endp

Все операнды PROC необязательны.

Тип может принимать значения NEAR и FAR, и если он указан, все команды RET в теле процедуры будут заменены соответственно на RETN и RETF. По умолчанию подразумевается, что процедура имеет тип NEAR в моделях памяти TINY, SMALL и COPACT.

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

USES-регистры — список регистров, значения которых изменяет процедура. Ассемблер поместит в начало процедуры набор команд PUSH, а перед командой RET — набор команд POP, так что значения перечисленных регистров будут восстановлены.

Конец программы

end start_label

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

3.3.6. Директивы задания набора допустимых команд

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

 

.8086 — используется по умолчанию. Разрешены только команды 8086.

.186 — разрешены команды 80186.

.286 и .286с — разрешены непривилегированные команды 80286.

.286р — разрешены все команды 80286.

.386 и .386с — разрешены непривилегированные команды 80386.

.386р — разрешены все команды 80386.

.486 и .486с — разрешены непривилегированные команды 80486.

.486р — разрешены все команды 80486.

.586 и .586с — разрешены непривилегированные команды Р5 (Pentium).

.586р — разрешены все команды Р5 (Pentium).

.686 — разрешены непривилегированные команды Р6 (Pentium Pro, Pentium II).

.686р — разрешены все команды Р6 (Pentium Pro, Pentium II).

.8087 — разрешены команды NPX 8087.

.287 — разрешены команды NPX 80287.

.387 — разрешены команды NPX 80387

.487 — разрешены команды FPU 80486.

.587 — разрешены команды FPU 80586.

.ММХ — разрешены команды IA ММХ.

.K3D — разрешены команды AMD 3D.

Не все ассемблеры поддерживают все эти директивы, например MASM не поддерживает .487 и .587, так как их действие не отличается от .387. Естественно, версии ассемблеров, вышедшие до появления последних процессоров и расширений, не поддерживают соответствующие команды.

Если перед директивой .model присутствует директива .386 или выше, ассемблер определяет все сегменты как 32-битные при условии, что не указан явно операнд USE16.







Последнее изменение этой страницы: 2016-08-15; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.235.172.213 (0.015 с.)