Взаимосвязь языков C и ассемблера
Раздел 1: Регистры и параметры
1.1 Использование регистров
В создаваемых ассемблерных программах можно использовать все регистры
процессора. Но чтобы предотвратить путаницу с функциями С и С++, необходимо
восстанавливать bp, cs, sp и ss, которые они имели до запуска созданной
подпрограммы. Тогда можно быть совершенно уверенным, что обращение к другим
функциям не изменит эти регистры. Также нельзя забывать, что С использует
регистры si и di для регистровых переменных, поэтому при использовании
встроенного ассемблера замедляется общая работа программы.
К регистрам ax, bx, cx, dx и es можно обращаться свободно и не нужно
резервировать их значения до окончания подпрограммы. Эта свобода касается и
других функций, поэтому надо помнить, что эти регистры изменяться, если
вызываются функции С и С++ из ассемблерных подпрограмм.
1.2 Ассемблерные операторы Inline
Ассемблерные операторы inline начинаются словом asm, за которым
следует инструкция и ее операнды. Например, чтобы синхронизировать
программу с внешним сигналом прерывания, можно написать:
/* ожидание прерывания*/
asm sti
asm hlt
printf(“Прерывание получено\n”)
Когда ранние версии Turbo C компилируют программу со встроенными
командами asm, компилятор сперва создает ассемблерный текст для всей
программы, вставляя в текст наши ассемблерные инструкции вместе с
откомпилированным кодом для остальных операторов С. Затем компилятор
вызывает Turbo Assembler и Linker (компоновщик), чтобы провести
ассемблирование и подключить программу к конечному файлу кода. Более
поздние версии Turbo и Borland C++ могут компилировать операторы asm без
вызова TASM. Полный синтаксис asm:
asm[метка] мнемоника/директива операнды [;] [/*С комментарий*/]
Точки с запятыми в конце строк asm и комментарии С, расположенные
между /*и*/ удаляются из текста перед ассемблированием, поэтому их можно
опускать в тексте программы.
1.3 Размещение данных и операторов в тексте программы
Каждая строка текста программы С и С++ находится либо внутри, либо
снаружи функции, и операторы asm могут вставляться как в одном, так и в
другом месте. Конкретное положение оператора asm влияет на то, куда
ассемблируется код или директива. Если оператор asm появляется снаружи
функции, то он ассемблируется в сегмент данных программы, если внутри
функции - в кодовый сегмент. Обычно, чтобы создать переменные, операторы
asm вставляются снаружи функций; для создания кода их следует вставлять
внутрь функций. Например:
asm count db ?
int main()
{
asm shl [count], 1/*умножение count на 4*/
asm shl [count], 1
return 0;
}
Переменная count объявляется в сегменте данных программы
(относительно ds). Операторы внутри функции main умножают count на 4,
используя вместо mul быстрые инструкции сдвига shl. Если объявлять
переменные внутри функции, данные ассемблируются в кодовый сегмент, требуя
особого обхождения:
int main()
{
asm jmp OverThere
asm count db ?
OverThere:
asm shl [count], 1 /* умножение count на 4*/
asm shl [count], 1
return 0;
}
Поскольку теперь переменная count находится в кодовом сегменте,
требуется инструкция jmp, чтобы избежать случайного восприятия значения
count в качестве машинного кода и его исполнения.