Создание внешних ссылок
Когда вы пишите модуль на Си, в котором встречаются переменные или функции, определенные в других модулях, вы должны использовать ключевое слово EXTERN, сообщающее компилятору, что переменные или функции будут определены позже (па этапе компоновки). MASM 5.0 и более старшие версии также поддерживают эту возможность.
В наших ассемблерных функциях может понадобиться передать значение глобальной переменной обратно в вызывающую Си программу. В принципе, мы можем ее передавать как параметр каждый раз и не думать о внешних переменных. Но данный способ критичен по времени и весьма архаичен. В видеоиграх, как ни в каких других программах, мы должны использовать как можно больше глобальных переменных. Причина очевидна — скорость выполнения. Нет смысла терять драгоценные такты процессора на выполнение команд PUSH и POP в момент вызова функций.
Синтаксис директивы EXTRN следующий:
EXTRN symbol: type, symbol: type,...
где symbol — имя переменной, a type — ее размер (например, BYTE, WORD, DWORD).
Директива EXTRN разрешает разместить переменную в вашем Си-коде и получить к ней доступ через параметры. Это имеет и обратную сторону: переменная, обозначенная как EXTRN означает, что она занимает текущий сегмент данных, адресуемых через регистр DS. Если вы все будете делать в модели SMALL или MEDIUM, то не стоит беспокоиться, если же вы работаете в модели LARGE, то никто не гарантирует, что вы получите доступ к вашей глобальной переменной, используя текущее значение регистра DS. Чтобы избежать этого, всегда применяйте модели SMALL и MEDIUM.
Давайте для примера напишем процедуру, которая складывает два целых числа и помещает результат в третье. Фокус заключается в том, что все эти величины будут глобальными и по отношению к ассемблерной процедуре - внешними. Листинг 2.6 демонстрирует код Си для этой программы, а в Листинге 2.7-показана ее реализация на ассемблере.
Листинг 2.6. Си-часть примера.
#include <stdio.h>
int first = 1, second = 2, third = 0;
// Это те числа, с которыми
// мы хотим работать
void main (void)
{
printf ("\nBefore adding third = %d\ third);
Add_Ext();
//вызываем ассемблерную процедуру
printf("\nAfter adding third = %d",third);
} // конец функции main
Листинг 2.7. Ассемблерная часть примера.
.MODEL MEDIUM ; будем использовать MEDIUM модель
EXTRN first:WORD, second:WORD, third:WORD
.CODE ; начало кодового сегмента
_Add_Ext PROC FAR ;процедура имеет тип FAR (дальняя)
mov AX, first ; помещаем первое число в аккумулятор
add AX, second ; прибавляем второе число
mov third, AX ; помещаем результат в переменную third
_Add_Ext ENDP ; конец процедуры
END ; конец кодового сегмента
Листинги 2.6 и 2.7 - это примеры использования внешних переменных first, second и third. Программа вызывает Add_Ext, которая складывает переменные first и second и сохраняет результат в third. Это подводит нас к теме возврата результатов обратно в программу на Си, которая вызывала ассемблерную процедуру.