Все материалы взяты с сайта OS-FAQ
Inline Assembly
Синтаксис
Ниже показан прототип для вызова asm() из C/C++ :
asm ( assembler template
: output operands (optional)
: input operands (optional)
: clobbered registers list (optional)
);
Example:
asm ("addl %1,%0" : "=g"(x) : "g"(y));
// то же самое , что и : "x+=y"
Assembler Template
Мы используем синтаксис AT&T. Пример :
asm( "hlt" ); /* halts the CPU */
Output Operands
Output Operands говорят компилятору , куда положить результат инструкции.
Output Operands состоит из 2-х частей,первая часть - строковое обозначение регистра,
вторая часть - С-переменная в скобках.
Используемые регистры для output - EAX, EBX,ECX,EDX,ESI,EDI,
Знак равенства говорит о том , что это output operand.
int EAX;
asm( "movl $0, %%eax" : "=a" (EAX) ); /* pathetic way of EAX = 0; */
Можно явно не указывать регистр и вместо него прописывать r -
компилятор сам справится с задачей выбора регистра:
int current_task;
asm( "ltr %0" : "=r" (current_task) );
Использование %0, %1 расставляет порядок в использовании операндов.
Вместо такой нумерации можно явно прописывать имя переменной :
int current_task;
asm( "ltr %[output]" : [output] "=r" (current_task) );
Такую нотацию можно использовать как для операндов output , так и input .
Input Operands
Input Operands позволяют параметризировать ASM-код; т.е.
передавать чисто сишные переменные внутрь АСМ-кода.
// следующая конструкция бесполезна , ибо мы не знаем значений EDI, ECX и AL ...
asm( "cld; rep; stosb" );
// корректно , но несколько сложновато :
asm( "movl %0, %%edi; movl %1, %%ecx; movb %2, %%al; cld; rep; stosb"
: : "g"(dest),"g"(amount),"g"(value));
// более компактно
// Мы пишем в память , адрес которой находится в переменной dest , значение value
// столько раз , сколько это указано в переменной amount
asm( "cld; rep; stosb" : : "D" (dest), "c" (amount), "a" (value) );
Подразумевается , что входящие параметры - read-only .
Clobbered Registers List
Важно помнить о следующем : компилятор ничего не знает об Assembler.
Если в инлайн-конструкции будет отсутствовать output , компилятор может такую строку
интерпретировать как угодно или сделать произвольную оптимизацию .
. Этого можно избежать еще одним способом - asm volatile.
Нужно позаботиться также о том , чтобы сохранить значения регистров перед тем , как вы
будете делать свой собственный асм-вызов .
Clobbered Registers List - список регистров , разделенных запятыми.
Не нужно сильно утруждаться по поводу выбора регистров - компилятор может сделать это сам .
.
"movl $0, %0" : "=g" (x) |
x может быть чем угодно - компилятор может выбрать регистр , а может и память
|
Отличия At&t синтаксиса от Nasm
Кстати сказать , AT&T синтаксис является стандартом для большинства не-интеловских платформ .
В нем есть инструмент - objdump , который позволяет использовать дебаг.
А теперь несколько существенных отличий AT&T синтаксиса от Intel sytnax,
:
- case sensitive. "MOVL" и "movl" вообще говоря разные вещи :-).
- numerical base - такая же как и в C: 1 - десятичная система, 01 - 8-ричная, 0x01 - 16-ричная.
- escapes. Спец-символы - также в стиле C-style (\n, \", \#, \\, ...).
- comments - 2 варианта - либо C-style ("/* ... */") , либо shell style ("#").
- directives - вначале идет точка (".align 4" - выравнивание на двойное слово, ".word 0x1234" - то же что и "DW 1234h").
- strings - определяются с помощью 2-х директив ".ascii" и ".asciz" (с последующим добавлением '\0' конец). Example: msg: .ascii "Hello, World!\n"
- current location address - обозначается точкой (".", равно Intel syntax "$").
- initializing memory - .fill (почти то же что и Intel syntax 'times db').
Пример: .fill 0x1fe - (. - START) , 1, 0 ('1' - размер маски в байтах ,
START - стартовая метка) , это то же что и Intel syntax times 1FEh - ($-$$) db 0.
- code counter - может устанавливаться много раз,
используяu .org директиву (типа .org 0x1fe + START,
где START - стартовая метка.
- 16/32 bit code генерится с помощью .code16 либо .code32
(равно Intel syntax [BITS 16] и [BITS 32]).
- target CPU - устанавливается с помощью .arch директивы.
- label declarations оканчиваются двоеточием
- new identifier в начале строки должен оканчиваться знаком "равно"
и значением , например : FOO = 0xF00
- end of instruction - новая строка либо точка с запятой
- line continuation - как и в с строка продолжается с помощью ('\')
- registers - идут с префиксом процента: %eax, %cs, %esp, etc.
- source, destination: move, load, store,
и другие похожие операции всегда подразумевают наличие
'source, destination'.
"movl %eax, %ebx" копирует %eax в %ebx.
Может и непривычно - кому как
.
- operand size suffixes - добавляется в виде символа в конце инструкцуии
: movb для "move byte", movw для "move word", movl для "move long", etc.
- direct-address operands не имеют префикса.
Так, "movl foo, %eax" копирует "foo" в %eax.
- immediate operands - имеют префикс доллара ($):
"pushl $4" . Доллар добавляется к метке в случае:
"movl $foo, %eax" копирует значение метки foo в %eax.
- indexed / indirect operands - используются в формате
segment:displacement (base, index, scale), так: movl %eax, %ss:8(%ebp, 2, 3)
(то же что и Intel syntax mov dword [ss:ebp + 2 * 3 + 8], eax,
копирует значение %eax по смещению (%ebp + (2 *3) + 8)
в сегменте %ss).
- relative (short addressing) - используется
для jump и call инструкций.
Для использования (near) -адресации,
операнд должен иметь префикс (*).
- far jumps / calls / returns
для этого есть специальные инструкции 'ljmp', 'lcall' и 'lret' .
Macros
Встроенные макросы имеют синтаксис :
.macro <name> <args>
<operations>
.endm
Пример:
.macro write string
movw string, %si
call printstr
.endm
Что эквивалентно NASM macro:
%macro write 1
mov si, %1
call printstr
%endmacro
|