|
Как стать программистом Как устроен компьютер. Что такое программа. Как написать свою программу. И многое другое узнаете вы из этой книги. Получить бесплатно! |
Ассемблер в Dev-C++
Главная / Ассемблер / Ассемблер и языки высокого уровня /Давно хотел разобраться с этой темой. И вот наконец собрался.
Дело в том, что инструкции процессора Интел и синтаксис вставок ассемблерного кода в программы на Visual C++ не будут работать в Dev-C++.
Потому что Dev-C++ использует компилятор GCC (бесплатный компилятор языка С++). Этот компилятор имеет встроенный ассемблер, но это не MASM и не TASM с привычным набором команд. Это ассемблер AT&T, синтаксис которого очень сильно отличается от синтаксиса MASM/TASM и подобных.
Кроме того, если в Паскале или Visual C++ вы просто используете ключевые слова - операторные скобки (в Паскале это asm...end, в Visual C++ это __asm {...}), и между этими скобками пишите инструкции ассемблера как вы привыкли, то с компилятором GCC это не проканает.
Я сначала никак не мог понять, почему. Но когда немного познакомился с документацией, то понял.
Оказывается, в компиляторе GCC, как и в Паскале и в Visual C++, есть ключевые слова asm и __asm. Вот только это вовсе не операторные скобки!!!
По сути это функции, которые вызываются с определённым набором параметров. И в эти функции в качестве параметров передаются инструкции ассемблера!
Вот уж воистину - зачем просто, если можно сложно!
В общем, использование встроенного ассемблера GCC - это целая наука. Если интересно её освоить, то можете начать вот с этой статьи (это мой перевод английского оригинала).
А здесь я просто в самых общих чертах покажу, как можно использовать вставки на ассемблере в Dev-C++ (это будет также справедливо для других средств разработки, использующих компилятор GCC).
Ассемблер AT&T
Как я уже сказал, этот ассемблер сильно отличается от привычных нам инструкций процессора Интел. Здесь я не буду об этом говорить. Если кому интересно, то основные отличия описаны здесь.
Вставка на ассемблере в Dev-C++
Основной формат вставки кода ассемблера показан ниже:
asm("Здесь код на ассемблере");
Пример:
/* помещает содержимое ecx в eax */
asm("movl %ecx %eax");
/* помещает байт из bh в память, на которую указывает eax */
__asm__("movb %bh (%eax)");
Как вы могли заметить, здесь используются два варианта встраивания ассемблера: asm и __asm__. Оба варианта правильные. Следует использовать __asm__, если ключевое слово asm конфликтует с каким-либо участком вашей программы (например, в вашей программе есть переменная с именем asm).
Если встраивание кода на ассемблере содержит более одной инструкции, то мы пишем по одной инструкции в строке в двойных кавычках, а также суффикс ’\n’ и ’\t’ для каждой инструкции.
Пример
__asm__ ("movl %eax, %ebx\n\t"
"movl $56, %esi\n\t"
"movl %ecx, $label(%edx,%ebx,$4)\n\t"
"movb %ah, (%ebx)");
Однако в большинстве случаев требуется обмен данными между кодом на ассемблере и переменными, которые объявлены в исходных кодах на языке высокого уровня.
Это тоже возможно. Общий формат ассемблерной вставки для компилятора GCC такой:
asm (assembler template
: output operands /* не обязательно */
: input operands /* не обязательно */
: list of clobbered registers /* не обязательно */
);
Не буду здесь подробно всё это расписывать, так как это уже сделано здесь. Там же вы найдёте все подробности использования встроенного ассемблера компилятора GCC (ну хотя не все, а основные).
Я же здесь приведу пример, и на этом успокоюсь.
Для начала не очень хороший пример.
int x = 0, y = 0;
cout << x << endl;
__asm__("movl $1, %eax"); //mov eax, 1
__asm__("addl $10, %eax"); //add eax, 10
__asm__("movl %%eax, %0\n":"=b"(x)); //x = 11
cout << x << endl;
Не очень хороший он потому, что мы изменяем значения регистров, а потом получаем значение регистра eax в переменную х. Но при этом мы не заботимся о том, что состояние этих регистров может быть изменено где-то в другом месте. Так что это может привести к потенциальным неожиданностям.
Теперь попробуем сделать всё чуть более правильно (хотя и не идеально).
int y = 15, z = 10;
cout << "y = " << y << ", z = " << z << endl;
__asm__("addl %%ebx, %%eax" //eax = eax + ebx (eax = y + z)
:"=a"(y) //Выход: из eax в y
:"a"(y), "b"(z) //Вход: y будет в eax, z будет в ebx
);
cout << "y + z = y = " << y << endl;
Здесь в ассемблерный код мы передаём значения переменных y и z. Значение у помещается в регистр еах (на это указывает буква “a”), а значение z помещается в регистр ebx (на это указывает буква “b”).
Сам ассемблерный код выполняет сложение значений регистров eax и ebx, и помещает результат в eax. А уже этот результат выводится в переменную y. То, что у - это выходная переменная, определяет модификатор “=”.
Ну вот как-то так. Это, конечно, в самых общих чертах. Если кого интересуют подробности, то см. здесь.
|
Вступить в группу "Основы программирования"
Подписаться на канал в РУТУБ Подписаться на рассылки по программированию |