Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Листинг 2.1. Прототип процедуры для MASM 5.0 и более старших версий.






.MODEL MEDIUM; тип модели памяти

.CODE

; начало кода

PUBLIC _function_name; информация для компоновщика. Функция

; может экспортироваться

_function_name PROC FAR; название и тип функции(ближняя

; или дальняя). Ближние функции можно использовать для моделей

; памяти SMALL и COMPACT, а дальние применяются для моделей

; памяти MEDIUM, LARGE и HUGE.

push ВР; готовим фрейм стека–пролог

; функции

mov BP, SP; сохраним стек

; Работа функции

pop ВР; восстанавливаем фрейм стека

; эпилог функции

_function_name ENDP; конец процедуры

END

; конец кода

Давайте проанализируем программу, приведенную в Листинге 2.1.

§ Первая директива, которую мы встречаем — это.MODEL. Как и компилятор Си, MASM должен знать, какая из моделей памяти используется. Ключевое слово MEDIUM означает, что мы собираемся использовать модель памяти именно типа MEDIUM. Теперь я хочу напомнить вам свойства основных моделей памяти:

· Модель SMALL имеет один 64-килобайтный сегмент для кода и один сегмент для данных;

· Модель COMPACT имеет один 64-килобайтный сегмент для кода и несколько сегментов данных;

· Модель MEDIUM имеет один 64-килобайтный сегмент для данных и несколько сегментов для кода;

· Модель LARGE имеет несколько сегментов как для кода, так и для данных;

· Модель HUGE разрешает данным быть больше, чем 64К, но в остальном полностью похожа на модель LARGE.

Чаще всего мы будем использовать модели памяти MEDIUM и LARGE.

§ Следующая директива — PUBLIC. Она говорит MASM, что следующее имя будет экспортировано, то есть станет «видимо» из других модулей;

§ Теперь мы переходим к началу самой функции. В ассемблере функция начинается с директивы PROC, которая следует сразу за именем функции;

§ В этом месте мы находимся внутри исполняемой части кода. Первые две инструкции устанавливают стек таким образом, что процедура получает доступ к параметрам, передаваемым через стек. К этому мы еще не раз вернемся;

§ В конце процедуры мы очищаем стек;

§ В конце каждой процедуры ставится ключевое слово ENDP;

§ В одном блоке мы можем иметь сколько угодно процедур, но надо помнить, что самой последней должна быть директива END. Она сообщает ассемблеру об окончании программы.

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

Передача параметров

Языки Си и ассемблер похожи на дальних родственников, живущих в одном доме - они вынуждены придерживаться сложных взаимных условностей. Однако ассемблер значительно более примитивен. Поэтому при передаче параметров ассемблерной процедуре нам приходится сочинять множество дополнительных строк кода, обеспечивающих доступ к ним. Вначале необходимо оформить фрейм стека, как показано в Листинге 2.1. Далее необходимо получить доступ к переданным параметрам, основываясь на новом значении регистра базы (ВР). Для обеспечения доступа к параметрам вы должны четко представлять себе, как именно передаваемые параметры размещаются в стеке. К примеру, вы хотите написать процедуру, вычисляющую сумму двух чисел и возвращающую результат в регистре АХ. На языке Си, описание этой функции выглядит так:

int Add_Int(int number_1, int number_2);

При выполнении этой процедуры компилятор языка Си создаст фрейм стека и поместит туда параметры. Иными словами, значения number_1 и number_2 будут расположены в стеке. Вы можете подумать, что сначала в стек будет помещено значение number 1, а затем - number_2. Однако компилятор Си думает несколько иначе. Он помещает параметры в стек в обратном порядке, что облегчает доступ к ним. За счет применения обратного порядка размещения параметров, адрес каждого из них будет задаваться некоторым положительным смещением относительно регистра ВР, что делает жизнь намного легче. В частности, именно благодаря такому механизму, некоторые функции (например, printf) могут получать переменное число параметров. Таким образом, при вызове функции Add_Int фрейм стека будет выглядеть, как показано па рисунке 2.1 или 2.2, в зависимости от используемой модели памяти. Причина, по которой вид фрейма стека зависит от модели памяти, состоит в следующем: при вызове процедуры в стек помещается адрес команды, следующей непосредственно за командой вызова. Если мы применили модель памяти SMALL, все процедуры по определению находятся внутри одного кодового сегмента. Следовательно, для доступа из программы к любой из них нам необходимо знать только смещение. Как известно, значение смещения занимает два байта. Если же мы применяем модель памяти MEDIUM или LARGE, то должны сохранить как смещение, так и сегментную часть адреса. Вместе сегмент и смещение занимают уже целых четыре байта.

Как видно из рисунков 2.1 и 2.2, параметры помещаются в стек в том порядке, который обеспечивает их адресацию положительными смещениями относительно значения регистра базы (ВР). Следовательно, для доступа к параметру number 1 вы должны использовать [ВР+4] или [ВР+6], в зависимости от установленной модели памяти. В качестве примера рассмотрим полный текст функции Add_Int. Она вычисляет сумму двух передаваемых в качестве аргументов чисел. Результат возвращается в регистре АХ, который, в соответствии с соглашениями языка Си, используется для возврата 16-битных значений.






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.