Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Передача параметров в блоке параметров

Поиск

Блок параметров — это участок памяти, содержащий параметры, так же как и в предыдущем примере, но располагающийся обычно в сегменте данных. Процедура получает адрес начала этого блока при помощи любого метода передачи параметров (в регистре, в переменной, в стеке, в коде или даже в другом блоке параметров). В качестве примеров использования этого метода можно назвать многие функции DOS и BIOS, например поиск файла, использующий блок параметров DTA, или загрузка (и исполнение) программы, использующая блок параметров ЕРВ.

Локальные переменные

Часто процедурам требуются локальные переменные, которые не будут нужны после того, как процедура закончится. По аналогии с методами передачи параметров можно говорить о локальных переменных в регистрах — каждый регистр, который сохраняют при входе в процедуру и восстанавливают при выходе, фактически играет роль локальной переменной. Единственный недостаток регистров в роли локальных переменных — их слишком мало. Следующий вариант — хранение локальных данных в переменной в сегменте данных — удобен и быстр для большинства несложных ассемблерных программ, но процедуру, использующую этот метод, нельзя вызывать рекурсивно: такая переменная на самом деле является глобальной и находится в одном и том же месте в памяти для каждого вызова процедуры. Третий и наиболее распространенный способ хранения локальных переменных в процедуре — стек. Принято располагать локальные переменные в стеке сразу после сохраненного значения регистра ВР, так что на них можно ссылаться изнутри процедуры, как [ВР-2], [ВР-4], [ВР-б] и т.д.:

foobar proc near

foobar_x equ [bp+8]; параметры

foobar_y equ [bp+6]

foobar_z equ [bp+4]

foobar_l equ [bp-2]; локальные переменные

foobar_m equ [bp-4]

foobar_n equ [bp-6]

Внутри процедуры foobar стек будет заполнен следующим образом (см. рис. 16).

Рис. 16. Стек при вызове процедуры foobar

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

Связь ассемблера с ЯВУ. Модели памяти. Различные соглашения. Упрощенные директивы

Конвенция Pascal

Самый очевидный способ выражения вызова процедуры или функции языка высокого уровня, после того как решено, что параметры передаются в стеке и возвращаются в регистре АХ/ЕАХ, — это способ, принятый в языке PASCAL (а также в BASIC, FORTRAN, ADA, OBERON, MODULA2), — просто поместить параметры в стек в естественном порядке. В этом случае запись

some_proc(a,b,c,d,e)

превращается в

push a push b push с push d push e call some_proc

Это значит, что процедура some_proc, во-первых, должна очистить стек по окончании работы (например, завершившись командой ret 10) и, во-вторых, параметры, переданные ей, находятся в стеке в обратном порядке:

some_proc proc push bp mov bp,sp; создать стековый кадрa equ [bp+12]; определения для простого; доступа к параметрамb equ [bp+10]c equ [bp+8]d equ [bp+6]e equ [bp+4]; текст процедуры, использующей параметры а, Ь, с, d, e ret 10some_proc endp

Этот код в точности соответствует усложненной форме директивы proc, которую поддерживают все современные ассемблеры:

some_proc proc PASCAL,а:word,b:word,с:word,d:word,e:word; текст процедуры, использующей параметры а, Ь, с, d, e.; Так как ВР используется в качестве указателя стекового кадра,; его использовать нельзя! ret; эта команда RET будет заменена на RET 10some_proc endp

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

Конвенция С

Этот способ передачи параметров используется в первую очередь в языках С и C++, а также в PROLOG и других. Параметры помещаются в стек в обратном порядке, и, в противоположность PASCAL-конвенции, удаление параметров из стека выполняет вызывающая процедура. Запись

some_proc(a,b,c,d,e)

превращается в

push e push d push с push b push a call some_proc add sp,10; освободить стек

Вызванная таким образом процедура может инициализироваться так: some_proc proc push bp mov bp,sp; создать стековый кадр a equ [bp+4]; определения для простого доступа к параметрам b equ [bp+6] с equ [bp+8] d equ [bp+10] e equ [bp+12]; текст процедуры, использующей параметры a, b, с, d, e pop bp ret some_proc endp

Ассемблеры поддерживают и такой формат вызова при помощи усложненной формы директивы proc с указанием языка С:

some_proc proc С,а:word,b:word,с:word,d:word,e:word; текст процедуры, использующей параметры a, b, с, d, e.; Так как BP применяется как указатель стекового кадра,; его использовать нельзя! retsome_proc endp

Мы не пользовались до сих пор этими формами записи процедур в ассемблере потому, что они скрывают от нас тот факт, что регистр ВР используется для хранения параметров и его ни в коем случае нельзя изменять, и, в случае PASCAL, что команда ret на самом деле — команда ret N.

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

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

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

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

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

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

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

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

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

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

Модификатор — необязательный операнд, принимающий значения 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.




Поделиться:


Последнее изменение этой страницы: 2017-02-06; просмотров: 318; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

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