Раздельная трансляция с главным модулем на паскале
В этом разделе мы поработаем с программой, один модуль которой написан на паскале, а другой — на языке ассемблера.
Пример программы
Наша программа будет устроена так: основной модуль на паскале считывает со стандартного потока ввода два числа и вызывает процедуру из модуля на языке ассемблера, которая находит наибольшее из них. Основной модуль затем печатает максимум.
Модуль calc
Вспомогательный модуль на ассемблере содержит процедуру smax
, в заголовке
которой сразу за ключевым словом proc
перечислены параметры, x
и
y
— типа sdword
(signed double word, знаковое двойное слово), и
max
— типа near
(то есть адрес, куда будет записан результат):
; result^ := max(x, y)
smax proc x: sdword, y: sdword, result: near
Поскольку у функции три параметра, она получит декорированное имя _smax@12
.
Примечание. Для использования функции smax
в другом модуле на языке
ассемблера эту функцию надо объявить так: smax proto :sdword, :sdword,
:near
.
По умолчанию MASM, видя спецификацию параметров, генерирует для процедуры пролог и эпилог. Мы отключим это поведение при помощи директивы option
:
option prologue: none
option epilogue: none
Код модуля целиком выглядит так:
; calc.asm
.586
.model flat, stdcall
option prologue: none
option epilogue: none
public smax
.code
; result^ := max(x, y)
smax proc x: sdword, y: sdword, result: near
push ebp
mov ebp, esp
push ebx
push edi
mov eax, [ebp+8] ; x
mov ebx, [ebp+12] ; y
mov edi, [ebp+16] ; max
cmp eax, ebx
jg @mv
xchg eax, ebx
@mv: mov [edi], eax
pop edi
pop ebx
mov esp, ebp
pop ebp
ret 12
smax endp
end
Модуль main
В модуле main
, который мы напишем на паскале, нам потребуется указать, что мы собираемся вызывать внешнюю процедуру smax
. Для этого мы объявим ее вот так:
procedure smax(x, y: longint; var result: longint);
stdcall;
external name '_smax@12';
{$Link calc.obj}
Директива external
указывает, что это внешняя процедура, декорированное
имя которой — _smax@12
. Директива stdcall
сообщает, что соглашение
о связях для процедуры — stdcall. Искать эту функцию компоновщику Free
Pascal мы предлагаем в объектном файле calc.obj
.
Примечание. К сожалению, обойтись без явного указания декорированного
имени не получится. Если опустить фрагмент name '_smax@12'
, то Free Pascal
сформирует декорированное имя наподобие
P$MODULAR_$$_SMAX$LONGINT$LONGINT$LONGINT
, несмотря на то, что мы указали
соглашение о связях stdcall.
После этого мы сможем пользоваться процедурой smax
как обычной
паскалевской процедурой.
Программа целиком выглядит так:
program modular;
procedure smax(x, y: longint; var result: longint);
stdcall;
external name '_smax@12';
{$Link calc.obj}
var
x, y, result: longint;
begin
write('enter x, y: ');
readln(x, y);
smax(x, y, result);
writeln('max = ', result);
end.
Компиляция и компоновка
Оба модуля: main.pas
и calc.asm
— надо поместить в один каталог,
например, C:\work\pascal
.
Сначала оттранслируем модуль calc
, чтобы получить объектный файл:
C:\work\pascal>ml /nologo /c /coff /Fl calc.asm
После этого можо будет скомпилировать главный модуль на паскале привычным нам способом, при этом компоновка исполняемого файла будет выполнена компилятором Free Pascal:
C:\work\pascal>fpc main.pas
Для удобства можно создать такой файл build.bat
:
@echo off
ml /nologo /c /coff /Fl calc.asm || goto END
fpc main.pas || goto END
echo Running main.exe
echo ------------------
main
:END