Лабораторные работы по СП (1-20) / 18 / ASM-C
.docxПриложение 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. Некоторые команды условной передачи управления:
Формат операнда 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 процедур работы с дисплеем. Например:
Прерывание DOS для работы дисплеем и клавиатурой имеет шестнадцатеричный код 21h. В зависимости от значения регистра AH оно обеспечивает различные процедуры, например:
Пример (очистка экрана): ... 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;
}