Группа: Дисциплина: Дата: Задание:

advertisement
Группа: 14 ПКС-1
Дисциплина: МДК01.01 «Разработка программных модулей программного
обеспечения для компьютерных систем»
Дата: 6.02.16
Задание: Самостоятельно в тетради составить конспект седьмой лекции по
теме: «Интерфейс между ассемблером и языком высокого уровня».
7. Интерфейс между ассемблером и языком высокого уровня.
Наиболее общим использованием языка Ассемблер в настоящее время является
применение его в качестве приложения к языку программирования высокого
уровня. При разработке программы, как правило, обычно используют язык
высокого уровня и лишь небольшую часть модулей пишут на языке Ассемблер.
Язык Ассемблер используется тогда, когда критичны скорость работы
программы или ее размер, или когда язык высокого уровня не обеспечивает
доступ к полным возможностям или к аппаратным средствам.
В прошлом, для языков высокого уровня было достаточно мало правил
регулировки соглашений о присвоении имен и последовательностей вызова.
Сегодня ситуация во многом изменилась, т.к. многие компиляторы следуют
стандартам Американского национального института стандартов (ANSI). В
связи с широким использованием компиляторов языков высокого уровня фирмы
"Майкрософт" и в связи с тем, что они придерживаются стандартов ANSI,
оказалось возможным выбрать компиляторы фирмы "Майкрософт" с языков
программирования: Бэйсик, Си, Фортран и Паскаль.
Компоновка с С-программой
Большинство компиляторов С снабжает глобальные символы из пространства
имен С префиксом ≪_≫. Например, функция printf будет доступна
программисту на ассемблере как _printf. Исключением является только формат
ELF (использующийся в Linux), не требующий символа подчеркивания.
Давайте напишем небольшую С-программу, вызывающую функцию printit,
которая вычисляет сумму глобальной переменной plus и единственного
аргумента и выводит ее на экран. Функцию printit напишем на языке
ассемблера, а из нее вызовем библиотечную функцию printf. Писать и запускать
программу будем в операционной системе Linux.
С-часть нашей программы очень проста:
const int plus = 6;
void printit(int);
int main(void) {
printit(5);
}
Мы определили глобальную константу plus и присвоили ей значение 6. Затем
идет объявление прототипа функции printit. После этого — функция main,
вызывающая функцию printit с аргументом 5.
В ассемблерной программе нам нужно объявить используемые нами глобальные
символы plus и printf:
extern plus
extern printf
Поскольку мы собираемся использовать компилятор gcc и формат исполняемого
файла ELF, нам не нужно возиться с символами подчеркивания. Благодаря
следующей директиве include нам доступны макросы proc, arg и endproc,
которые облегчают написание нашей функции:
%include ≪misc/c32.mac≫
Макрос ргос определяет начало функции printit. Подстановка превращает его в
две команды: push ebp и mov ebp,esp. Следующий макрос arg определяет наш
единственный аргумент. Если нужно передать несколько аргументов, то первый
arg будет соответствовать крайнему слева аргументу в коде С. Размер аргумента
по умолчанию — 4 байта.
ргос printit
%$what arg
Оставшаяся часть программы понятна:
mov eax,[ebp + %$what] ;читаем первый аргумент из стека
add eax,[plus] ;прибавляем глобальную переменную plus
push eax ;сохраняем в стеке последний аргумент функции printf
push strl ;первый аргумент - строка формата вывода
call printf /вызываем p r i n t f
endproc ;макрос endproc восстанавливает ЕВР,
;уничтожает все локальные переменные
;(у нас их нет) и выходит из подпрограммы
Компоновка с Pascal-программой
В общих чертах механизм передачи аргументов в Паскале не отличается от
только что рассмотренного механизма в языке С. Аргументы передаются через
стек, оба типа подпрограмм (процедуры и функции) используют стек-фрейм
(или его 16-битную версию). Переменные и аргументы доступны через регистр
ВР.
Паскаль изначально разрабатывался как язык для обучения программированию,
поэтому он не поддерживает некоторых конструкций языка С — например,
функций с переменным количеством аргументов. Поэтому компилятор Паскаля
помещает аргументы в стек слева направо, а не в обратном порядке, как в С.
Вызов внешних подпрограмм на Паскале из ассемблерной программы упрощен,
потому что в Паскале используются только FAR-вызовы. ≪Генеральная
уборка≫ стека — забота не вызывающей программы, а вызываемой
функции,которая должна выполнить команду retf с аргументом, указывающим,
сколько байтов выбросить из стека.
Рассмотрим таблицу размещения стека подпрограммы относительно ВР
Давайте напишем на Паскале программу, подобную только что рассмотренной.
Новая версия нашей программы для вывода значения суммы будет вызывать
функцию writeln из основной программы, то есть подпрограмма addit должна
только подсчитать сумму и вернуть ее в основную программу.
{$L addit.obj}
uses c r t ;
var plus:integer;
function addit(x:integer):longint;far;external;
begin
plus := 6;
writelnCSUM = ', addit(5));
end.
Функцию addit, как и в предыдущем примере, напишем на языке ассемблера.
Программе на Паскале она становится известна посредством ключевого слова
external. В той же строке декларации мы объявляем, что функции addit нужно
передать один целочисленный параметр, она возвращает значение типа longint
(4 байта) и вызывается как FAR. He забудьте также написать директиву $L,
указывающую имя объектного файла с откомпилированной функцией addit,
который нужно подключить при компоновке. Теперь напишем функцию addit,
которую мы сохраним в файле addit.asm.
Borland Turbo Pascal использует не стандартный формат объектного файла
obj, а свой собственный, что накладывает строгие ограничения на именование
секций программы. Секция кода должна называться CODE, CSEG или именем,
заканчивающимся на ≪_ТЕХТ≫, секция данных — CONST или именем,
заканчивающимся на ≪_DATA≫, а секция неинициализированных данных —
DATA, или DSEG, или именем, заканчивающимся на ≪_BSS≫.
Если вы уже привыкли называть секции программы именами .text, .data и .bss,
то в программах, предназначенных для компоновки с Паскаль-кодом, просто
добавьте знак подчеркивания после точки, и компилятор будет доволен.
Функция addit будет вызываться в 16-битной среде, где размер типа данных
integer равен 2 байтам. Возвращает функция значение типа longint (4 байта) и
передает его через пару регистров DX:AX.
Все эти вопросы, в том числе вопросы компоновки с Паскаль-кодом, решает
макрос с16.тас. Код нашей подпрограммы addit представлен в листинге:
SECTION ._TEXT %define PASCAL %include ≪misc/cl6.mac≫ extern plus global
addit proc addit %$what arg xor dx,dx mov ax,[bp+%$what] add ax,[plus] adc dx,0
начало секции кода будем использовать FAR-вызовы подключаем макросы
получаем доступ к внешней переменной plus экспортируем функцию addit
начало функции addit макрос включает создание стек-фрейма объявляем
единственный аргумент по имени what сбрасываем DX АХ = what АХ = АХ +
plus не забудьте флаг переноса
endproc удаляем полученные аргументы и выходим из подпрограммы
Макрос arg принимает в качестве необязательного параметра размер
передаваемого аргумента. Размер по умолчанию в 16-битной среде равен 2
байтам.
Если бы мы передавали аргумент типа longint или указатель, нам пришлось
бы явно задать его размер, то есть 4 байта.
После компиляции и запуска программы она выведет требуемую сумму
наэкран.
Download