asm volatile("Instruction List" : Output : Input : Clobber/Modify);
当然,或者写作如下格式(Output、Input、Clobber/Modify都是可选的),也就是三个冒号,4个部分:
asm volatile("Instruction List"
: Output
: Input
: Clobber/Modify);
为了理解方便,以屏蔽本地irq相关函数的代码为例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
static
inline
unsigned
long
native_save_fl(
void
)
{
unsigned
long
flags;
/*
* "=rm" is safe here, because "pop" adjusts the stack before
* it evaluates its effective address -- this is part of the
* documented behavior of the "pop" instruction.
*/
asm
volatile
(
"# __raw_save_flags\n\t"
"pushf ; pop %0"
:
"=rm"
(flags)
:
/* no input */
:
"memory"
);
return
flags;
}
static
inline
void
native_restore_fl(unsigned
long
flags)
{
asm
volatile
(
"push %0 ; popf"
:
/* no output */
:
"g"
(flags)
:
"memory"
,
"cc"
);
}
static
inline
void
native_irq_disable(
void
)
{
asm
volatile
(
"cli"
: : :
"memory"
);
}
static
inline
void
native_irq_enable(
void
)
{
asm
volatile
(
"sti"
: : :
"memory"
);
}
|
本篇blog耗时2小时。
1、asm和volatile
volatile是c的关键字,是个修饰符,用于修饰变量或者函数,意思是告诉编译器,该变量或者函数是“易变”的,编译器会在优化时做相关的考虑。驱动中常用于修饰急促器变量。Linux下c中的使用内联汇编,就是这样,需要asm()的格式,可以没有volitile,但不能没有asm。
关于asm到底是什么?首先,它不是ANSI C关键字,c89/c99标准中没有asm关键字;asm属于“GNU C Language Extensions”,像typeof,inline等都是这种情况。
2、Instruction List
指令序列,就是指要执行的指令汇编指令,多条指令之间使用 分隔符“;”、“\n\t”等进行联合。
对应例子中,就是
- "pushf;pop %0"
- “push %0;popf”
- "cli"
- "sti"
含义分别为:
- 将EFLAGS寄存器的值压栈,再出栈给的1个变量(先这么叫吧)
- 将第1个变量压栈,再出栈给EFLAGS寄存器
- 禁止本地中断
- 开启本地中断
3、Output
指令序列执行的结果的输出,指指令执行的结果要输出到哪。Output和Input部分,通常使用下列格式:
“constraint”(variable)
constraint为限制的意思,翻译好听点就是“修饰”,用于限制variable的。
在例子中,只有navtive_save_fl函数有“=rm”(flags),这句还不好臆测,还得查资料(参考资料5):
- =,表示只写,也是output必须具备的(看见=,就是output)
- r表示寄存器
- m表示内存
- flags为变量名
联合起来表示将flags变量以内存或者寄存器的方式进行操作,flags是输出。
关于constraint:
- a,b,c,d,S,D 分别代表 eax,ebx,ecx,edx,esi,edi 寄存器
- r 上面的寄存器的任意一个(谁闲着就用谁)
- m 内存
- i 立即数(常量,只用于输入操作数)
- g 寄存器、内存、立即数 都行(gcc你看着办)
4、Input
Input和Output很像,就是没有“=”。Input为指令序列提供输入,在例子中,只有native_restore_fl函数有“g”(flags),g表示“Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.”,表示flags可以使用寄存器、内存、立即数等方式。
还有个问题,就是Input和Output如何与Instruction List相关联的?Instruction List中的%0,前面叫他“第一个变量”,实际上是不准确的,实际上应该叫做input/output operand,输入/输出操作数,从Output开始,0表示第一个,1就表示第二个,以此类推。从gcc3.1开始,支持直接将%0写作%[input]、%[output]。
input和Output可以有多个变量:中间用“,”隔开。
5、Clobber/Modify
该部分表示哪些寄存器、内存被修改,但是又没有出现在Input/Output中。通常看到的就是memory,表示汇编语句可能修改了内存,如果有变量缓存在寄存器中,需要重新读取该变量。
参考资料:
2、__asm__ __volatile__内嵌汇编用法简述
4、AT&T inline Assembly Constraint
5、Assembler Instructions with C Expression Operands
6、内联汇编
Published in C语言技巧 and tagged asm, 汇编 on 2013年11月24日 by rock3