1、基本格式
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
注意:内联汇编最多三个冒号,隔开了四个部分:指令、输出、输入和破坏性说明。
2、指令部分
指令必须用引号括起来,可以每条指令一对引号,也可以所有指令公用一对引号,Linux常用的格式是:
__asm__ __volatile__(".align 2\n\t"
"movl %eax, %ebx\n\t"
"test %ebx, %ecx\n\t"
"jne error\n\t"
"sti\n\t"
"error: popl %edi\n\t"
"subl %ecx, %ebx");
3、输出部分
Output用来指定当前内联汇编语句的输出。我们看一看这个例子:
__asm__("movl %%cr0, %0": "=a" (cr0));
这个内联汇编语句的输出部分为"=a"(cr0),这个输出操作由两部分组成:括号括住的部分(cr0)和引号引住的部分"=a"。表示cr0 = eax,最终这一点被转化成汇编指令就是movl %eax, address_of_cr0。
除了=a,还有其他的定义:
=a 从eax中得到输出;
=d 从edx中得到输出;
其他更多定义请参考后面的“限定符”
4、输入部分
Input域的内容用来指定当前内联汇编语句的输入。我们看一看这个例子:
__asm__("movl %0, %%db7" : : "a" (cpu->db7));
例中Input域的内容为一个表达式"a"(cpu->db7),被称作“输入表达式”,用来表示一个对当前内联汇编的输入。像输出表达式一样,一个输入表达式也分为两部分:带括号的部分(cpu->db7)和带引号的部分"a"。这两部分对于一个内联汇编输入表达式来说也是必不可少的。
5、破坏说明部分
表示本内联汇编部分破坏了哪些东西,可以写:"%eax", "%edx", "memory"等
6、限定符
分类 限定符 描述
通用寄存器 “a” 将输入变量放入eax
这里有一个问题:假设eax已经被使用,那怎么办?
其实很简单:因为GCC 知道eax 已经被使用,它在这段汇编代码
的起始处插入一条语句pushl %eax,将eax 内容保存到堆栈,然
后在这段代码结束处再增加一条语句popl %eax,恢复eax的内容
“b” 将输入变量放入ebx
“c” 将输入变量放入ecx
“d” 将输入变量放入edx
“s” 将输入变量放入esi
“d” 将输入变量放入edi
“q” 将输入变量放入eax,ebx,ecx,edx中的一个
“r” 将输入变量放入通用寄存器,也就是eax,ebx,ecx,
edx,esi,edi中的一个
“A” 把eax和edx合成一个64 位的寄存器(use long longs)
内存 “m” 内存变量
“o” 操作数为内存变量,但是其寻址方式是偏移量类型,
也即是基址寻址,或者是基址加变址寻址
“V” 操作数为内存变量,但寻址方式不是偏移量类型
“ ” 操作数为内存变量,但寻址方式为自动增量
“p” 操作数是一个合法的内存地址(指针)
寄存器或内存 “g” 将输入变量放入eax,ebx,ecx,edx中的一个
或者作为内存变量
“X” 操作数可以是任何类型
立即数
“I” 0-31之间的立即数(用于32位移位指令)
“J” 0-63之间的立即数(用于64位移位指令)
“N” 0-255之间的立即数(用于out指令)
“i” 立即数
“n” 立即数,有些系统不支持除字以外的立即数,
这些系统应该使用“n”而不是“i”
匹配 “ 0 ”, 表示用它限制的操作数与某个指定的操作数匹配,
“1” ... 也即该操作数就是指定的那个操作数,例如“0”
“9” 去描述“%1”操作数,那么“%1”引用的其实就
是“%0”操作数,注意作为限定符字母的0-9 与
指令中的“%0”-“%9”的区别,前者描述操作数,
后者代表操作数。
& 该输出操作数不能使用过和输入操作数相同的寄存器
操作数类型 “=” 操作数在指令中是只写的(输出操作数)
“+” 操作数在指令中是读写类型的(输入输出操作数)
浮点数 “f” 浮点寄存器
“t” 第一个浮点寄存器
“u” 第二个浮点寄存器
“G” 标准的80387浮点常数
% 该操作数可以和下一个操作数交换位置
例如addl的两个操作数可以交换顺序
(当然两个操作数都不能是立即数)
# 部分注释,从该字符到其后的逗号之间所有字母被忽略
* 表示如果选用寄存器,则其后的字母被忽略
7、例句
__asm__ __volatile__(" " : : : "memory" ); // 表示更改过内存
__asm__ __volatile__("rdtsc" : "=a"(low) : : "edx"); //rdtsc将64位tsc寄存器分别放在eax和edx寄存器里,各32位
__asm__ __volatile__("mov %%eax, %%ebx" : "=b"(rv) : "a"(foo) : "eax", "ebx");
__asm__ __volatile__("lidt %0": "=m" (idt_descr));
__asm__ __volatile__("subl %2,%0\n\t"
"sbbl %3,%1"
: "=a" (endlow), "=d" (endhigh)
: "g" (startlow), "g" (starthigh), "0" (endlow), "1" (endhigh));