在/linux0.11/kernel/trap.c文件中,第一次接触到C语言中的嵌入式汇编代码。详细的使用说明可以参考GNUgcc手册中第4章的内容或者参考文献《using assembly with gcc》。

    具有输入和输出参数的嵌入式汇编的基本格式为:


       在/linux0.11/kernel/trap.c文件中,第一次接触到C语言中的嵌入式汇编代码。详细的使用说明可以参考GNUgcc手册中第4章的内容或者参考文献《using assembly with gcc》。

    具有输入和输出参数的嵌入式汇编的基本格式为:


        asm("汇编语句“

       asm("汇编语句“

            : 输出寄存器

            : 输入寄存器

            : 会被修改的寄存器);

    其中,”汇编语句“是你写汇编指令的地方;”输出寄存器“表示当这段嵌入汇编执行完之后,哪些寄存器用于存放输出数据。”输入寄存器“表示在执行汇编代码时,这里指定的一些寄存器中应该存放的输入值,他们分别对应着一C变量或者常数值。下面将举例说明嵌入式汇编的具体因使用方法。

    例如1:

    #define get_seg_byte(seg,addr)\

    ({\

    register char _res;\

    __asm__("push %% fs; \

             mov %%ax,%%fs;\

             movb %%fs:%2,%%al;\

             pop %%fs\

            :"=a"(_res) \

            :"0"(seg),"m"(*(addr)));\

    _res;})

    这段代码定义了1个嵌入式汇编函数。因为是宏语句,需要在一行上定义,因此这里使用反斜行‘\’将这些语句连成1行。第1行定义了宏的名称,即宏函数名称为get_seg_byte(seg,addr)。第3行定义了一个寄存器变量_reg 。 第4行上的_asm_表示嵌入汇编语句开始。从第4行到第7行的4条AT&T 格式的汇编语句。

    第8行是输出寄存器,这句话的含义是在这段代码运行结束后将eax所代表的寄存器中的值放入_res 变量中,作为本函数的输出值。为了在上面汇编语句中使用该地址值,嵌入汇编程序规定把输入和输出寄存器统一按顺序编号,顺序是从输出寄存器序列从左到右从上到下以%0 开始,分别记为%0,%1,%2....%9。因此,输出寄存器编号为%0 输入寄存器前一部分""(seg)的编号为%1,而后一部分的编号为%2. 上面第6行上的%2代表(*(addr))这个内存偏移量。

    现在分析4-7行上代码的具体作用。第1句将fs段寄存器的内容入栈;第2句将eax中的段值赋给fs段寄存器;第3句是把fs:(*addr))所制定的字节放入al寄存器。当执行完汇编语句后,输出寄存器eax的值将被放入_res。

    经过上面的分析,我们知道宏名称中的seg代表一指定的内存段值,而addr表示一内存偏移地址量。到现在为止,我们应该很清楚这段程序的功能。该宏函数是从指定的段和偏移量的内存地址处取一个字节。

    例如2:

        asm("cld\n\t"

            "rep\n\t"

            "stol"

            : 

            :"c"(count-1),"a"(fill_value),"D"(des)

            :"%ecx","%edi");

    1-3行这三句是常用的汇编语句,用以清方向标识位,城府保存值。第四行说明没有用到输出寄存器。第5行的含义是将count-1的值将在到ecx寄存器中(加载码是“c“)fill_value 加载到eax中,dest放到edi中。 为什们要让gcc编译程序去做这样的加载,而不让我们自己做呢?因为gcc在它进行寄存器分配时可以进行某些优化工作。例如fill_value值可能已经加载到eax中。如果在一个循环语句中的话,gcc可能在整个循环操作中保存eax,这样就可以在每次循环中少用1个movl语句。

    最后1行是告诉gcc这些寄存器中的值已经改变了。很奇怪吧?不过,gcc知道你拿这些寄存器做了什么后,这确实能够对gcc的优化操作有所帮助。

    下面是可能会用的寄存器加载码及其具体含义:

代码    说明

a        使用寄存器eax

b        使用寄存器ebx

c        使用寄存器ecx

d        使用寄存器edx

S        使用寄存器esi

D        使用寄存器edi

q        使用动态分配字节可寻址寄存器 (eax、ebx、ecx或edx)

r        使用任意动态分配的寄存器

g        使用通用有效的地址即可(eax、ebx、edx、ecx 或者内存变量)

A        使用eax和edx联合(64bit)

m        使用内存地址

o        使用内存地址,并可以加载偏移值

I        使用常数0-31

J        使用常数0-63

K        使用常数0-255

L        使用常数0-65536

M        使用常数0-3

N        使用1字节常数(0-255)

O        使用常数0-31

    下面的例子不是让自己制定那个变量使用那个寄存器,而是让gcc为你选择

    asm("leal(%1,%1,4),%0"

        :"r"(y)

        :"0"(x));

    第1句leal(r1,r2,4),r3 语句表示r3=r1+r2×4。这个例子可以非常快的将x成5.其中"%0","%1" 是指gcc自动分配的寄存器。这里%1 代表输入值x要放入的寄存器,”%0“表示输出寄存器。所以如果gcc将r指定为eax的话,那么上面汇编语句的含义为”leal(eax,eax,4),eax"    注意 如果不希望汇编语句被gcc优化而挪动地方,就需要在asm符号后面添加volatile关键字

    asm volatile();或者

    __asm__ __volatile__();