Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lec_asm_06_3495.doc
Скачиваний:
1
Добавлен:
03.01.2020
Размер:
178.18 Кб
Скачать

7.2. Использование функций на языке Ассемблера

в программах на языке Си

Основной целью создателей языка Си была разработка такого языка программирования высокого уровня, который можно было бы использовать вместо язы­ка Ассемблера для системного программирования. Язык Си вполне соответствует своему предназначению.

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

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

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

Будем считать, что в дальнейшем используется модель распределения памяти с малыми кодами (до 64 Кбайт). Изменения, которые надо внести в программы, если вызывающая функция написана для модели больших кодов, обговорим в конце.

Процесс создания программы:

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

– откомпилировать ассемблерный модуль,

– с помощью программы компоновщика создать на основе двух объектных файлов один выполняемый файл *.ЕХЕ.

7.2.1. Основы взаимодействия языков Си и Ассемблера

Первое требование, которое необходимо выполнить при совместной компоновке нескольких объектных файлов, созданных ассемблером и компилятором языка Си, состоит в том, что все объектные файлы должны иметь одинаковый формат – тот, ко­торый нужен загрузчику. Проблемы могут возникнуть при работе с объектными файлами, получаемыми с помощью средств разных производителей.

Другое требование – функция на языке ас­семблера должна учитывать соглашения по вызову функций, используемые компилятором языка Си. Для этого программист должен усвоить ряд деталей, которые можно почерпнуть либо из документации компилятора, либо анализируя машинный код, выдаваемый компилятором.

Изучая соглашения по вызову функций, будем называть вызывающую функцию на языке Си программой, вызываемую функцию на языке ассемблера – подпрограммой.

Необходимо учитывать следующие соглашения:

– подпрограмма не должна уничтожать регистровые значения программы (называемые средой);

– программа и подпрограмма должны придерживаться общих соглашений о передаче данных из программы в подпрограмму и о возвращении данных из под­программы в программу.

7.2.2. Передача управления в подпрограмму и обратно

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

1. Программа должна сохранить адрес команды, с которой будет продолжено ее исполнение после завершения вызова подпрограммы. Затем программа передает управление подпрограмме.

2. После завершения подпрограмма должна возвратить управление по адресу, сохраненному ранее программой.

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

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

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

f1( );

компилятор языка Си генерирует машинную команду

CALL _f1

Символическое имя _f1 соответствует глобальному идентификатору, определенно­му в подпрограмме.

Приведем пример подпрограммы на языке ассемблера:

PGROUP GROUP PROG ; не обязателен

PROG SEGMENT BYTE PUBLIC 'PROG'

ASSUME CS: PGROUP

PUBLIC _F1 ; Сделать это имя глобальным

_F1 PROC NEAR ; Малая модель

RET ; Вернуться в вызывающую программу

_F1 ENDP ; Конец программы

PROG ENDS ; Конец сегмента

END ; Конец файла

Псевдооператор GROUP служит для указания ассемблеру на то, что этот сегмент команд надо поместить вместе с командами других модулей в один блок памяти раз­мером 64 Кбайт.

Псевдооператор PUBUC предлагает ассемблеру сделать идентификатор F1 глобальным, чтобы он стал доступен компоновщику. Это позволяет компоновщику выполнять требуемую связь с вызывающей функцией на языке Си.

Рассмотрим способы обмена данными между функцией на языке Си и подпрограммой на языке ассемблера.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]