Построение интерфейса между ассемблером и языками высокого уровня — задача нетривиальная, но вполне осуществимая. Совместное использование языка Си и ассемблера давно практикуется в сфере профессионального программирования. Паскаль тоже иногда используют в связке с 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 — транслятором уже не контролируется. Оптимизация Си-функции, содержащей ассемблерную вставку, отключается.