Построение интерфейса между ассемблером и языками высокого уровня — задача нетривиальная, но вполне осуществимая. Совместное использование языка Си и ассемблера давно практикуется в сфере профессионального программирования. Паскаль тоже иногда используют в связке с MASM или NASM, как правило, при обучении студентов низкоуровневому программированию.
Начинающим программистам известны как минимум два способа совместного применения ассемблера и языка Си. В первом случае фрагменты на языке ассемблера вставляются в исходную программу на Си и транслируются совместно. Во втором — модули на языке ассемблера и модули на языке Си транслируются раздельно; полученные объектные модули объединяются компоновщиком в исполняемую программу.
Мы рассмотрим оба варианта в их крайних проявлениях: Microsoft/Borland-C и, с другой стороны, Watcom-C. Начнем с общих вопросов.
Машинное представление данных языка Си
Рассмотрим машинное представление элементарных типов данных Си. Тип char занимает один байт. По умолчанию такие значения трактуются как знаковые и лежат в диапазоне -128 — +127. Тип unsigned char — беззнаковый байт в диапазоне 0 — 255.
Объект типа short int занимает ровно два байта. Он может принимать значения положительные и отрицательные, в диапазоне -32 768 — +32 767. Тип unsigned short — беззнаковый, принимает значения в диапазоне 0 — 65 535.
Типы int и short int ничем не отличаются в 16-разрядных приложениях. Тип long int занимает двойное слово. Принимает положительные и отрицательные значения от -2 147 483 648 до +2 147 483 647. Тип unsigned long представляет беззнаковые значения в диапазоне 0 — 4 294 967 295.
Начинающим программистам будет полезно вспомнить правила использования микропроцессорных регистров. Рассмотрим правила использования регистров (сегментных регистров, а также регистров общего назначения и регистров флагов), принятые при трансляции с языка Си.
Сегментные регистры
Регистры ds и ss устанавливаются при выполнении входного кода (Entry-кода) из стандартной библиотеки Си. После выполнения ассемблерной подпрограммы или вставки значения регистров ds и ss должны остаться прежними.
Значение регистра es в Microsoft/Borland-C не определено и может быть изменено как в Си-коде, так и в ассемблерных фрагментах. Если вы используете строковые команды (кроме lods), следует устанавливать es, причем заново каждый раз. В Watcom-C изменение es не допускается.
Регистры общего назначения
В Microsoft/Borland-C подпрограмма может произвольно изменять значения ах, bx, ex, dx. Напротив, значения si, di, bp, sp в результате выполнения ассемблерного фрагмента или подпрограммы не должны измениться. В Watcom-C никакие регистры не могут быть изменены.
Регистр флагов
Флаги состояния можно изменять как угодно. По всей программе флаг направления d должен быть сброшен. (Нулевое значение d задает положительное приращение указателей di и si при выполнении строковых команд.) Если в фрагменте на ассемблере выполнена инструкция std, перед выходом следует задать cld.
Перейдем к рассмотрению наиболее простого варианта соединения Си с ассемблером — ассемблерных вставок.
Ассемблерные вставки
При трансляции вставок ассемблер получает доступ к таблице имен Си-транслятора. Это значит, что из вставки можно ссылаться на имена объектов, определенных в Си-модуле. К ним относятся переменные, константы, функции, С-метки, формальные параметры функции и ее локальные переменные.
Ассемблерные вставки в Microsoft/Borland-C
В Microsoft/Borland-C вставка открывается ключевым словом asm, после которого записывается или один оператор ассемблера, или несколько операторов в фигурных скобках. Несколько операторов можно записывать в одной строке — через ‘;’. Как следствие, комментарии в стиле ассемблера недопустимы; зато имеется возможность воспользоваться комментариями в стиле Си — /* .. */. Числовая константа задается в манере ассемблера или Си, допускается любая форма.
Листинг 1. Ассемблерные вставки в среде Microsoft/Borland-C.
void main() { char x = ' *' ; asm ror x, 2 asm { test x, Oxff jpe skip xor x, 1 shl 8 skip: } }
Во второй вставке запрограммировано дополнение до четности числа установленных в 1 бит байта х. Обратите внимание: числовая константа Offh задана в стиле Си.
Ограничения на допустимые конструкции ассемблера во вставках зависят от способа взаимодействия трансляторов (С и ассемблера). В ранних реализациях Си, например, в Quick-C/Turbo-C, таблица С-имен доступна ассемблеру только на чтение. Поэтому никакие определения во вставках не допускаются. Запрещено все, что могло бы привести к расширению таблицы имен:
- ставить метку;
- определять массив директивой db или dw с именем;
- определять макрокоманды;
- пользоваться директивой «segment».
Во всех реализациях Microsoft/Borland-C ассемблерная вставка препятствует оптимизации с использованием регистров, поскольку применение большинства регистров — ах, bх, ex, dx, es — транслятором уже не контролируется. Оптимизация Си-функции, содержащей ассемблерную вставку, отключается.