inline assembly内联汇编

原文请点击wiki中的内联汇编

有些时候我们的代码可能需要使用到硬件,比如说通过一个端口输出数据,或是从某个端口读入数据,这个时候对硬件的调用就该使用汇编语言。内联汇编(inline assembly)便是很好的选择,可以使用asm()函数插入任意的汇编代码片段到C/C++代码中。下面的汇编语言都是工作在GCC编译器,因为GCC是目前在操作系统领域中使用最为广泛的编译器。

  调用asm的语法如下:

asm ( assembler template
    : output operands                   (optional)
    : input operands                    (optional)
    : clobbered registers list          (optional)
    );
assembler templates就是具体的汇编命令,比如movl %%eax, %%ebx,将eax寄存器的值存入ebx中。

output operands指定输出操作数,比如上面指令中的的%%eax便是输出部分,如果只使用寄存器,不需要使用C/C++变量,那么这里可以省略。

input operands指定输入操作数,也就是上边的%%eax,其它同上。

clobbered registers list修饰寄存器,指定在汇编语句中会使用到的寄存器,假如使用的这个寄存器中存了目前仍旧有用的值,那么系统就会在调用asm之前保存这个值,在调用结束后恢复值。

一个简单的例子,用汇编实现b = a 赋值


int a = 10, b;
asm("movl %1, %%eax;
movl %%eax, %0;"
:"=r"(b) /*output*/
:"r"(a) /*input*/
:"%eax"   /*clobbered register*/
);
GCC把变量命名为%0,%1这样的格式,所以为了将变量与寄存器更好的区分,GCC便将寄存器写出%%eax这样的格式,也就是有两个%%前缀。

在上面的代码中汇编代码做的事情是:将变量%1放入eax寄存器,接着将eax中的值放到变量%0中。

按照语法中的顺序,output操作数排在input操作数之前,所以%0在这里由output operands指定,%1则由input operands指定,

上面代码中将b指定为%0,a指定为%1,所以顺利实现了b=a的赋值啦。在括号里边指明C/C++中的变量。

关于"r",指的是可以使用任意的寄存器。除此之外,"a"指EAX,”b"指EBX,”c"指ECX,"d"指EDX,“S"指ESI,“D"指EDI。比如"r"(a)便是指将a映射到任意一个寄存器中。

因为使用了eax寄存器,所以在修饰寄存器列表中对eax进行说明。


下面这段代码便是将变量EAX赋值为0:

int EAX;
asm("movl $0, %0";
:"=a"(EAX)
);


Input Operands

如果想将一个变量值传给EAX寄存器,可以按如下操作

int randomness = 4;
asm("movl %0, %%eax"
:
:"b"(randomness)
:"%eax" /*或者写成 :%%eax  */
);

也可以写成

int randomness = 4;
asm("movl %%ebx, %%eax"
:
:"b"(randomness)
);


Wildcards: How you can let the compiler choose

一般来说,在将变量映射到寄存器中时,没有特殊必要的话,我们是不需要特别指定寄存器的。

通配符wildcard限制有以下选项:

"g":

“movl $0, %0" : "=g"(x)

x可以被放到编译器想放的任意地方:一个寄存器,一个内存引用,甚至是字符常量之类的。

"r":

"movl %%es, %0" : "=r"(x)

x可以放到任意一个寄存器。


”N":

“outl %0, %1” : :"a"(0xFE), "N"(0x21)

说明“0x21"可以被用作为一个常量数值。


os-lab中使用的in_byte和out_byte

OUT 和 IN 在汇编中是端口读写操作指令。端口是主机与外设进行数据交换使用的,分为数据端口,状态端口和控制端口三种。 

PC机给一个端口分配地址,所有端口成线性排列,形成一个独立于内存空间的I/O地址空间,一般用十六进制表示。8086中,端口地址的范围是0000H搜索-FFFFH。 
例如: 
IN AL, 21H 表示从21H端口读一个字节数据到AL; 
OUT 21H,AL 表示将AL持有的数据写入21H端口 
__volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

asm volatile("in %1, %0" : "=a"(data) : "d"(port));
从port端口处读入数据放到data中
asm volatile("out %%al, %%dx" : : "a"(data), "d"(port));
将数据放入到port端口处,等价于
asm volatile("out %0, %1" : : "a"(data), "d"(port));
函数定义在os-lab0/include/x86/io.h
/* 读I/O端口 */
static inline uint8_t
in_byte(uint16_t port) {
 uint8_t data;  
asm volatile("in %1, %0" : "=a"(data) : "d"(port));
return data;
} 
/* 写I/O端口 */
static inline voidout_byte(uint16_t port, int8_t data) 
{ 
asm volatile("out %%al, %%dx" : : "a"(data), "d"(port));
}


 
 
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值