C语言的嵌入式汇编
为了使C语言程序具有更高的效率和更多的功能,需在C语言程序里嵌入用汇编语言编写的子程序。一方面是为提高子程序的执行速度和效率;另一方面,可解决某些用C 语言程序无法实现的机器语言操作。而C语言代码与汇编语言代码的接口是任何C编译器毋庸置疑要解决的问题。
通常,有两种方法可将汇编语言代码与C语言代码联合在一起。一种是把独立的汇编语言程序用C函数连接起来,通过API(Application Program Interface)的方式调用;另一种就是我们下面要讲的在线汇编方法,即将直接插入式汇编指令嵌入到C函数中。
编译器GCC认可的基本数据类型及其值域列示在表4.8中。
采用GCC规定的在线汇编指令格式进行指令的输入,是GCC实现将µ’nSP™汇编指令嵌入C函数中的方法。GCC在线汇编指令格式规定如下:
asm(“汇编指令模板”:输出参数:输入参数:clobbers参数);
若无clobber参数,则在线汇编指令格式可简化为:
asm(“汇编指令模板”:输出参数:输入参数);
下面,将对在线汇编指令格式中的各种成分之内容进行介绍。
1)汇编指令模板
模板是在线汇编指令中的主要成分,GCC据此可在当前位置产生汇编指令输出。例如,下面一条在线汇编指令:
asm("%0+=%1":"+r"(foo):"r"(bar));
此处,"%0+=%1"就是模板。其中,操作数"%0"、"%1"作为一种形式参数,分别会由第一个冒号后面实际的输出、输入参数取代。带百分号的数字表示的是第一个冒号后参数的序号。
如下例:
asm("%0=%1+%2":"=r"(foo):"r"(bar),"i"(10));
"%0"会由参数foo取代,"%1"会由参数bar取代,而"%2"则会由数值10取代。
在汇编输出中,一个汇编指令模板里可以挂接多条汇编指令。其方法是用换行符'\n'来结束每一条指令,并用Tab键符'\t'将同一模板产生在汇编输出中的各条指令在换行显示时缩进到同一列,以使汇编指令显示清晰。如下例:
asm("%0+=%1\n\t%0+=%1":"+r"(foo):"r"(bar));
2)操作数
在线汇编指令格式中,第一冒号后的参数为输出操作数,第二冒号后的参数为输入操作数,第三冒号后跟着的则是clobber操作数。在各类操作数中,引号里的字符代表的是其存储类型约束符;括弧里面的字符串表示的是实际操作数。
如果输出参数有若干个,可用逗号“,”将每个参数隔开。同样,该法则适用于输入参数或clobber参数。
3)操作数约束符
约束符的作用在于指示GCC,使用在汇编指令模板中的操作数的存储类型。表4.9列出了一些约束符和它们分别代表的操作数不同的存储类型,也列出了用在操作数约束符之前的两个约束符前缀。
4)GCC在线汇编指令举例
例1:asm("%0=%1+%2":"=m"(foo):"r"(bar),"i"(10));
操作数foo和bar都是局部变量。bar的值会分配给寄存器(此例中寄存器为R1),而foo的值会置入存储器中,其地址在此由BP寄存器指出。GCC对此会产生如下代码:
//GCC在线汇编起始
[BP]=R1+10
//GCC在线汇编结束
注意,本在线汇编指令产生的汇编代码不能被正确汇编。正确的在线汇编指令应当是:
asm("%0=%1+%2":"=r"(foo):"r"(bar),"i"(10));
它产生如下的汇编代码:
//GCC在线汇编起始
R1=R4+10
//GCC在线汇编结束
例2:
int a;
int b;
#define SEG(A,B)asm("%0=seg%1":"=r"(A):"p"(&B));
int main(void)
{
int foo;
int bar;
SEG(foo,a);
SEG(bar,b);
return foo;
}
例3:asm("%0+=%1":"+r"(foo):"r"(bar));
操作数foo在被赋值前先要参加运算,故其约束符为"+r",而非"=r"。
5利用嵌入式汇编实现对端口寄存器的操作
在C的嵌入式汇编中,当使用端口寄存器名称时,需要在C文件中加入汇编的包含文件,如下所示:
asm(“.include hardware.inc”);
那么,我们就可以使用端口寄存器的名称,而不必去使用端口的实际的地址。
1)写端口寄存器
现举例说明:要设定PortA端口为输出端口,需要对P_IOA_Dir赋值0xffff。那么在C中的嵌入式汇编的实现方式如下:
在C中有一个int型的变量i,传送到P_IOA_Dir中,则嵌入汇编的实现方式如下:
….
asm(“.include hareware.inc”);
….
int main(void){
int i;
….
asm("[P_IOA_Dir]=%0"
:
//没有输出参数
:"r"(i)
c语言中文网汇编语言//只有输入参数,通过寄存器传递变量i的内容
);
…
}
如果需要对端口寄存器直接赋值一个立即数(比如对P_IOA_Dir赋值0x1234),那么内嵌式汇编为:
….
asm(“.include hareware.inc”);
….
int main(void){
…
.
asm("[P_IOA_Dir]=%0"
:
//没有输出参数
:"r"(0x1234)
//只有输入参数,通过寄存器传递立即数0x1234
);
…
}
2)读端口寄存器
对端口寄存器进行读操作的方法,与写类似,下面仍然以P_IOA_Dir为例,进行说明。
如果要实现把端口的寄存器P_IOA_Dir的值读出并保存在C中的一个int变量j里,那么可以通过下面的方法来实现。
….
asm(“.include hareware.inc”);
….
int main(void){
int j;
….
asm("%0=[P_IOA_Dir]"
:"=r"(j)
//只有输出参数,而无输入参数
);
…
}
3)利用GCC编程举例
下面是一段GCC的代码,实现对A口的初始化:设定A口为同向输出高电平。
asm("[P_IOA_Attrib]=%0\n\t"
"[P_IOA_Data]=%0\n\t"
"[P_IOA_Dir]=%0\n\t"
:
:
"r"(0xffff)
);
上面代码通过GCC编译后的代码为:
R1=(-1)//QImode move
//GCC inline ASM start
[P_IOA_Attrib]=R1
[P_IOA_Data]=R1
[P_IOA_Dir]=R1
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论