Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
16
Добавлен:
25.02.2016
Размер:
34.92 Кб
Скачать

Приложение 14. Использование ассемблерных вставок в программах на языке C

Практически все современные языки высокого уровня предоставляют программисту возможность написания части программных процедур в виде машинно-ориентированных инструкций. Это позволяет написать критичные по времени выполнения участки кода оптимальным образом. Тем самым достигается высокая эффективность программы. При этом полностью переводить программу на язык ассемблера не требуется.

Так, в среде программирования Borland C++ инструкции языка ассемблера можно добавлять в текст программы с использованием ключевого слова asm. Например:

void main()

{

int i;

...

asm mov i, 5; //инструкция пересылки в переменную i значения 5

i = i + 1;

...

}

Инструкция на языке ассемблера должна следовать сразу за ключевым словом asm. Если нужно вставить несколько ассемблерных инструкций, то вставку можно оформить в виде блока:

void main()

{

int i;

asm {

// начало ассемблерной вставки. Открывающая скобка блока

// – на одной строчке с ключевым словом asm.

mov i, 5; // пересылка в переменную i значения 5

inc i // увеличение значения переменной на 1

mov AX, i // пересылка значения переменной i в регистр AX

// конец ассемблерного блока

}

}

При написании ассемблерных вставок программист может использовать все ранее введенные имена переменных для обращения к соответствующим ячейкам памяти. Кроме того, становятся доступны регистры процессора (регистры общего назначения, сегментные регистры, регистры указатели).

Основные инструкции языка ассемблера

Рассмотрим основные инструкции, которые можно использовать для написания ассемблерных вставок в среде Borland C++.

Назначение

Синтаксис

Описание

Команда пересылки данных

mov dst,src

Пересылает 1 байт или 1 слово из источника (src) в приемник (dst). Приемником может быть регистр, ячейка или слово основной памяти. Источником может быть регистр, ячейка или слово памяти, также непосредственное значение (константа). Разрядности операндов должны совпадать:

mov AX, BX // корректно

mov AL, AH // корректно

mov CH, BX // некорректно

Пересылать данные между ячейками памяти (минуя регистры) командой mov нельзя. В качестве источника может стоять несложное выражение, например: mov AX, (152+FF)/10.

При пересылке данных можно использовать прямую или косвенную адресацию. Для прямой адресации адрес операнда указывается символьным обозначением регистра или переменной:

mov AX, BX

mov AX, UserVar

При косвенной адресации адрес операнда хранится в указанном регистре или ячейках памяти:

mov AX, [CS] // в регистр AX

// будет помещено значение первых двух

// ячеек памяти сегмента команд

mov AL, [DS+2] // в регистр AL

// будет помещено значение третьего

// байта сегмента данных

Команда занесения данных в стек

push src

Помещает на вершину стека содержимое src – 16-разрядного значения регистра или значение двух соседних ячеек памяти, если src – адрес первой ячейки или двухбайтная переменная. Значение источника и регистра флагов не изменяются.

Команда извлечения слова из стека

pop dst

Извлекает слова (2 байта) с вершины стека и помещает его в dst – любой 16-битовый регистр или две ячейки памяти. Значения регистра флагов при этом не меняется. Команды push и pop работают со стеком в сегменте стека программы. Значение указателя SP вершины стека при выполнении этих команд изменяется автоматически.

Команды сложения, вычитания и сравнения

add dst, src sub dst, src cmp dst, src

add – сложение двоичных чисел, sub – вычитание, cmp – сравнение. Операнды dst и src должны иметь одинаковый формат (байт или слово). По результату исполнения операции устанавливаются значения флагов.

Команды приращения

inc dst dec dst

Одноадресные команды, соответственно добавляющие и вычитающие 1 из значения операнда. По результату исполнения операции устанавливаются значения флагов.

Логические команды

and dst, src or dst, src xor dst, src test dst, src

Каждая из команд выполняет побитовые операции над операндами, которые должны иметь одинаковый формат (байт или слово). and – логическое умножение, or – логическое сложение, xor – сумма по модулю 2 (исключающее ИЛИ). По результату исполнения операции устанавливаются значения флагов.

Пример: xor AX, AX – обнуление регистра AX.

Команда test эквивалентна команде xor, но значение операнда dst в этом случае не изменяется (а лишь изменяется регистр флагов).

Команда безусловной передачи управления

jmp opr

Управление в программе передается по адресу операнда opr. Операнд opr может задаваться прямым или косвенным адресом. Переход по прямому адресу реализуется с использованием меток. Ассемблерные вставки в Borland С++ не поддерживают добавление меток, поэтому их следует размещать вне вставок в обычном формате языка С++.

Пример:

int i;

asm {

push CS;

mov AX, i;

sub AX, 3;

jmp L;

}

int j,k

L:

asm pop CS;

При косвенной адресации в качестве операнда указывается адрес следующей команды, с которой продолжится исполнение программы.

Команды условной передачи управления

j* opr

В языке ассемблера существует целое семейство команд условной передачи управления, которые проверяют тот или иной разряд регистра флагов и, если результат проверки – «истина», то переходят по адресу, указанному операндом opr. Некоторые команды условной передачи управления:

  • JE/JZ – переход, если нуль (ZF=1);

  • JNE/JNZ – переход, если не нуль (ZF=0);

  • JS – переход, если есть знак (SF=1);

  • JNS – переход, если нет знака (SF=0);

  • JC – переход, если есть перенос (CF=1);

  • JC – переход, если нет перенося (CF=0);

  • JO – переход, если есть переполнение (OF=1);

  • JNO – переход, если нет переполнения (OF=0).

Формат операнда opr такой же, как для команды jmp. Ниже дан пример функции, которая прибавляет к числу 1, если оно меньше 5, и отнимает – в противном случае:

int asmfunc(i: integer)

{

int r;

asm {

mov AX, i;

cmp AX, 5;

jc L1; // если AX < 5, то установится флаг переноса

dec AX;

jmp L2;

}

L1:

asm inc AX;

L2:

asm mov r, AX;

}

Команда управления циклами

loop L

Команда loop используется для повторения цикла заданное число раз. Счетчик повторений находится в регистре CX. При выполнении команды происходит проверка значения счетчика. Если оно больше нуля, то выполняется переход к началу цикла по метке L, причем значение счетчика CX автоматически уменьшается на 1. При обнулении счетчика выполнение цикла завершается.

Пример:

...

asm mov CX, 100 // инициализация счетчика

L:

asm {

// тело цикла

loop L

}

...

Команда вызова прерывания

int opr

Вызывает прерывание исполняемой программы и передает управление процедуре обработке прерываний с кодом, указанным операндом opr. Особый интерес представляют собой системные прерывания базовой системы ввода-вывода (BIOS) и прерывания ОС, которые позволяют программам на языке ассемблера взаимодействовать с такими устройствами ввода-вывода, как дисплей, клавиатура или принтер. При написании ассемблерных вставок вызов таких прерываний практически не используется (ввод-вывод информации реализуется средствами языков высокого уровня). Однако при изучении инструкций ассемблера получение навыков работы с системными прерываниями полезно.

Прерывание BIOS для работы с дисплеем имеет шестнадцатеричный код 10h. В зависимости от значения регистра AH оно обеспечивает 16 процедур работы с дисплеем. Например:

  • AH = 6, AL = 0 – очистка дисплея.

  • AH = 2 – перемещение курсора в заданную позицию. Координаты курсора (строка, столбец) должны записываться в регистры DH и DL перед вызовом прерывания.

Прерывание DOS для работы дисплеем и клавиатурой имеет шестнадцатеричный код 21h. В зависимости от значения регистра AH оно обеспечивает различные процедуры, например:

  • AH = 2 – вывод символа на экран дисплея. ASCII-код символа должен храниться в регистре DL перед вызовом прерывания.

  • AH = 9 – вывод строки символов. При вызове прерывания в регистрах DS:DX должен храниться адрес начала строки (DS – адрес сегмента, DX – адрес смещения в сегменте). Строка символов должна заканчиваться символом '$’.

  • AH = 1 – чтение символа с клавиатуры с ожиданием ввода и отображением символа на экране. После завершения обработки прерывания ASCII-код считанного символа помещается в регистр AL.

  • AH = 7 – чтение символа с клавиатуры с ожиданием ввода без отображения символа на экране. После завершения обработки прерывания ASCII-код считанного символа помещается в регистр AL.

Пример (очистка экрана):

...

asm {

xor AX, AX; // обнуление регистра AX

mov AH, 6; // код процедуры очистки дисплея

int 10h; // вызов прерывания BIOS

}

...

Пример использования ассемблерных вставок

char *asmfunc(char *S, char c)

{

char *r = 0;

L0:

asm {

mov SI,S;

mov DL,c;

}

L1:

asm {

mov AL,0;

add AL,[SI];

je L3;

xor AL,DL;

je L2;

add SI,1;

jmp L1;

}

L2:

asm {

mov r,SI;

L3:

return r;

}

Соседние файлы в папке 18