В этом разделе мы поработаем с программой, один модуль которой написан на паскале, а другой — на языке ассемблера.

Пример программы

Наша программа будет устроена так: основной модуль на паскале считывает со стандартного потока ввода два числа и вызывает процедуру из модуля на языке ассемблера, которая находит наибольшее из них. Основной модуль затем печатает максимум.

Модуль 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