C语言与汇编语言互相调用
- 格式:doc
- 大小:27.50 KB
- 文档页数:4
汇编语言的过程调用与c语言的函数调用姓名:孙贵森学号:201212301118汇编语言的过程调用,如果需要传递参数,一般有2种方法,通过寄存器来“传递”,或是通过参数来传递。
(还有将所有参数制成参数列表并压栈的传递方法,但较少用。
)通过寄存器来“传递”,不是真正意义上的传递,其只不过是事先在几个有限的CPU寄存器中设置相应的值后,再调用过程,过程再直接读取这些寄存器的内容。
可想而知,此法犹如C语言中的全局变量,极易感染。
而如果通过参数来传递,又不得不面临手工维护堆栈框架(stack frame)的重担。
堆栈框架动态地存放着参数、调用过程的返回地址、过程局部变量、过程内的压栈等内容,也是不好对付的。
一般情况下,一个普通的过程可能如下编写: Sum PROCpush ebpmov ebp, esp .....pop ebpretSum ENDP作为遵从C调用约定(Calling Convention)调用者,则需这样调用上述过程:push 5 ;push 8;call Sum ;add esp, 4 * 2;而如果遵从STDCALL调用约定,则:Sum PROCpush ebpmov ebp, esp......mov eax, [ebp + 12] ;add eax, [ebp + 8]; ......pop ebpret 4 * 2 ;Sum ENDPSum PROCpush ebpmov ebp, essub esp, 8 ;......mov eax, [ebp + 12];add eax, [ebp + 8];add eax, [ebp - 4;add eax, [ebp - 8];......mov esp, ebp;pop eret 4 * 2;Sum ENDP在被调用的过程内,分为3种情况:1. 无参数,也无局部变量2. 有参数3. 有局部变量当无参数且无局部变量时,堆栈中只是保存call语句的下一条语句的地址,可以很安全地返回。
关于c语言和汇编语言相互嵌套调用的学习总结在计算机编程中,C语言和汇编语言是两种常用的编程语言。
C语言是一种高级语言,而汇编语言则是一种低级语言。
尽管C语言在编程方面更为简单和人性化,但是汇编语言却更为底层和灵活。
因此,在一些特定的情况下,C语言与汇编语言会相互嵌套调用,以充分发挥各自的优势。
首先,理解C语言和汇编语言的基本特点是学习的关键。
C语言是一种结构化的高级语言,它具有变量和函数的特性。
C语言通过调用函数来完成特定的任务,使用变量来存储和操作数据。
相比之下,汇编语言是一种低级语言,它直接操作计算机硬件,使用寄存器和内存地址来存储和操作数据。
汇编语言的指令直接映射到底层的CPU指令。
其次,学习如何在C语言中嵌入汇编代码。
在C语言中,可以使用内联汇编语句来嵌入汇编代码。
为了实现这一点,需要使用特殊的语法。
在GCC编译器中,可以使用asm关键字来指定内联汇编代码。
内联汇编语句由汇编语句和C语言代码组成,以实现C函数内的底层操作。
通过内联汇编,可以直接访问底层硬件功能,并在C函数中实现特定的优化。
而在汇编语言中嵌入C代码则需要借助外部汇编调用接口。
在C语言中编写函数时,可以使用extern关键字声明函数为外部函数。
对于汇编语言而言,可以使用特定的语法和指令来调用C函数。
在调用C函数时,需要将参数传递给C函数,并处理返回值。
通过外部汇编调用接口,可以在汇编语言中利用C函数的高级功能,如数组操作、内存分配等。
当C语言和汇编语言相互嵌套调用时,需要注意以下几点。
首先,理解数据传递的原理。
C语言和汇编语言使用不同的参数传递方式。
C语言通常使用栈来传递参数,而汇编语言则使用寄存器。
在混合编程中,需要确保参数正确地传递给函数。
其次,需要注意变量的声明和使用。
C语言和汇编语言使用不同的编译规则和约定。
在混合编程中,需要正确地定义和使用变量,以免引起错误。
此外,需要注意寄存器的保存和恢复,以避免影响程序的正确执行。
汇编与C语言的相互调用汇编与C语言的相互调用一、实验目的阅读Embest EduKit-III启动代码,观察处理器启动过程;学会使用Embest IDE辅助信息窗口来分析判断调试过程和结果;学会在Embest IDE环境中编写、编译与调试汇编和C语言相互调用的程序。
二、实验设备硬件:PC机。
软件:Embest IDE Pro2004集成开发环境,Windows98/2000/NT/XP。
三、实验原理1.ARM过程调用ATPCS(ARM)ATPCS是一系列用于规定应用程序之间相互调用的基本规则,这此规则包括:支持数据栈限制检查;支持只读段位置无关(ROPI);支持可读/写段位置无关(RWPI);支持ARM程序和Thumb程序的混合使用;处理浮点运算。
使用以上规定的ATPCS规则时,应用程序必须遵守如下:程序编写遵守ATPCS;变量传递以中间寄存器和数据栈完成;汇编器使用-apcs开关选项。
关于其他ATPCS规则,用户可以参考ARM处理器相关书籍或登录ARM公司网站。
程序只要遵守ATPCS相应规则,就可以使用不同的源代码编写程序。
程序间的相互调用最主要的是解决参数传递问题。
应用程序之间使用中间寄存器及数据栈来传递参数,其中,第一个到第四个参数使用R0-R3,多于四个参数的使用数据栈进行传递。
这样,接收参数的应用程序必须知道参数的个数。
但是,在应用程序被调用时,一般无从知道所传递参数的个数。
不同语言编写的应用程序在调用时可以自定义参数传递的约定,使用具有一定意义的形式来传递,可以很好地解决参数个数的问题。
常用的方法是把第一个或最后一个参数作为参数个数(包括个数本身)传递给应用程序。
ATPCS中寄存器的对应关系如表3-5所列:ARM寄存器ARPCS别名APTCS寄存器说明R0~R3a1~a4参数/结果/scratch寄存器1-4R4v1局部变量寄存器1R5v2局部变量寄存器2R6v3局部变量寄存器3R7v4,wr局部变量寄存器4Thumb状态工作寄存器R8v5ARM状态局部变量寄存器5R9v6,sb ARM状态局部变量寄存器6RWPI的静态基址寄存器R10v7,s1ARM状态局部变量寄存器7数据栈限制指针寄存器R11v8ARM状态局部变量寄存器8R12ip子程序内部调用的临时(scratch)寄存器R13sp数据栈指针寄存器R14lr链接寄存器R15PC程序计数器2.main()函数与__gccmain()当应用程序中包含了main()函数,将会引起对C运行时库的初始化。
实验一一、汇编语言调用c语言主要内容为使用汇编语言调用C语言完成20!的计算。
将64位结果保存到寄存器R0、R1中,其中R1存放高32位结果1.建立汇编源文件start.s.global _start.extern Factorial.equ Ni,20.text_start:Mov R0,#NiBL FactorialStop:B Stop.end2.建立C语言源文件factorialLong long Factorial(char N){Char i;Long long Nx=1;For(i=1;i<=N;i++){Return Nx;}}二、C语言调用汇编语言在GUN ARM 编译环境下设计程序,用C语言调用ARM汇编语言C语言实现20!操作,并将64位结果保存到0xFFFFFFF0开始的内存地址单元,按照小端格式低位数据存放在低位地址单元。
1、建立C语言源文件main.c/* main.c */extern void Factorial(char Nx);Main(){Char N =20;Factoral(N);While(1);}2、建立汇编语言源文件factorial/* factorial.s */.global FactorialFactoral:Mov R8,R0Mov R9,#0SUB R0,R8,#1Loop:Mov R1,R9UMULL R8,R9,R0,R8MLA R9,R1,R0,R9SUBS R0,R0,#1BNE LoopLDR R0,=0xFFFFFFF0STMIA R0,{R8,R9}MOV PC,LR三、实验一内容在汇编语言中定义符号常量A和B,调用C语言程序求这两个数的最大值,返回汇编程序后,将结果存入R3。
(1)编汇编源文件start.s文件/*Start.s */.global _start.extern Factoiral.equ N1,20.equ N2,30.text_start:Mov R0,#N1Mov R1,#N2BL FactoralMov R3,R0Stop:B stop.end(2)编写C语言文件/* factorial.c*/int Factorial(int num1,int num2){if(a>=b) return a;else return b}用C语言编程,现将10000000开始的100个单元赋初值,然后求平均值、最大值和最小值,并将结果存放1在000100开始(1)编写汇编源文件start.s文件/*start.s*/global _startextern Factorial.text_start:B FactorialStopB stop.end(2)编写C语言文件/*Factorial.c*/Void Factrial(){int i;int 32-t,*p1,*p2; int max =*p1;long sum=0;double ove=0;p1=(int 32-t)0x10000000;p2=(int 32-t)0x10001000;for(i=0;i<=100;i++) *(p1+i)=i;for(i=1;i<=100;i++){if(*(p1+i)>max){max =*(p1+i);if(*(p1+i)<min)max=*(p1+i); }}ove=sum/100.0;*p2=sum;*(p2+2)=max;*(p2+3)=min;}实验二、GPIO延时函数static void delay_ms(int ms){int i,j;While(ms--){for(i=0;i<5;i++)for(j=0;j<514;j++);}}主函数Int main(void){GPX2PUD=GPX2PUD&(~(0x3<<4));GPX2CON=GPX2CON&(~(0xf<<28))|(0x1<<28); while(1){GPX2DAT=GPX2DAT|(0x1<<7);delay_ms(1000);GPX2DAT=GPX2DAT&(~(0x1<<7));delay_ms(1000);}return 0;}实验三、PWM定时器1.PWM的初始化void PWM_init(void){GPDO.CON=(GPDO.CON &~(0xf)) | 0x2; PWM.TCFGO=(PWM.TCFG0&~(0xFF))|0x63; PWM.TCFG1=(PWM.TCFG1 &~(0xF)) 0x3; PWM.TCNTBO=200;PWM TCMTBO=100;PWM.TCON=(PWM.TCON &~(0xF)) | OxA; PWM.TCON=(PWM.TCON &~(0xF)) | Ox9;}2.代码实现int main(void) {GPX2.CON=0x1<<28;PWM_init();while(1){GPX2.DAT=GPX2.DAT|0x1<<7;mydelay_ms(500);GPX2.DAT=GPX2.DAT & ~(0x1<<7);mydelay_ms(500);}return 0;}PWM输出软件设计PWM0初始化函数Void init_pwm0(void){PWM.TCFGO=PWM.TCFG0&(~(0xff<<0))|249; PWM.TCFG1=PWM.TCFG1&(~(0xf<<0))|4;//TCNT_CLK=PCLK(100M)/(249+1)/(16)=25KHZ) PWM.TCNTB0=100;PWM.TCNTB0=50;PWM.TCON=PWM.TCON|(0x1<<1);PWM.TCON=PWM.TCON&(~(0xf<<0))|(Ox9<<0))} 主函数int main(void){GPD0.PUD=0x0;GPD0.CON=GPD0.CON&(~(0xf<<0))|(0x2<<0); init_pwm0();while(1);return 0;}。
昨天好好研究了一下内嵌汇编的情况。
更进一步的,该是看看独立编译的汇编程序与C/C++互相调用的情况了。
呵呵,最近怎么好像老在搞这个,想当年学习的时候,一门心思的学C++,到现在老是在弄诸如怎么在C/C++中调用LUA函数,或者反过来,怎么C/C++中调用Python函数,或者反过来,今天又是怎么在C/C++中调用汇编的函数,或者反过来。
呵呵,自从学习的语言越来越多,类似的情况碰到的也就越来越多了,但是,只懂一门语言就不能在合适的时候使用合适的语言来解决问题,并且看问题会带有狭隘的偏见,谁说的来着?毕竟无论是lua,python,asm都会有用的上的时候,我最近似乎老是喜欢说闲话。
这是某些哥们说的自己的见解,还是某些时候无聊的牢骚呢?谁知道呢,过年了嘛,还不让人多说几句话啊。
-_-!首先来看C中调用汇编的函数先添加一个汇编文件写的函数吧,在VS2005中对此有了明显的进步,因为就《加密与解密》一书描述,在2003中需要自己配置编译选项,但是在VS2005中很明显的,当你添加asm 文件后,可以自己选定masm的编译规则,然后一切就由IDE把你做好了,这也算是IDE的一个好用的地方吧。
非常不好的一点就是,VS2005中对于汇编没有任何语法高亮。
damnit!IDE怎么做的?就这点而言,其甚至不如一般的文本编辑工具!。
又是废话了。
因为是C,这个目前全世界可能是最具有可移植性的语言,所以问题要简单的多。
但是。
也不全是那么简单,先看看直觉的写法:汇编代码:PUBLIC GetArgument.486 ; create 32 bit code.model flat ; 32 bit memory model;option casemap :none ; case sensitive_TEXT SEGMENT PUBLIC 'CODE'GetArgument PROCMOV EAX, [ESP+4]RETNGetArgument ENDP_TEXT ENDSENDC语言代码:#include <stdio.h>#include <windows.h>int GetArgument(int);int _tmain(int argc, _TCHAR* argv[]){printf("%d/n",GetArgument(10));system("PAUSE");return 0;}声明是必不可少的,毕竟汇编没有头文件给你包含,不过多的话,可以考虑组织一个专门用于包含汇编函数实现的头文件。
c语言和汇编语言的关系随着计算机技术的不断发展,编程语言也在不断更新迭代。
C语言作为一种高级语言,其强大的功能和简洁的语法赢得了广泛的用户群体。
而汇编语言则是一种低级语言,它与硬件紧密相关,可以直接操作计算机的各种硬件设备。
虽然C语言和汇编语言在语言结构和语法上有很大的差异,但是它们之间有着紧密的联系和互动。
首先,C语言和汇编语言都是计算机语言的一种。
C语言是一种高级语言,具有可移植性和代码可读性的特点。
C语言的语法结构简洁明了,易于学习和使用,是很多程序员的首选语言。
而汇编语言则是一种低级语言,它直接操作计算机的硬件,可以对计算机的各种硬件设备进行直接控制,因此具有很高的效率和灵活性。
C语言和汇编语言之间的联系主要体现在以下几个方面:1. C语言可以调用汇编语言编写的函数。
在C语言中,可以使用汇编语言编写的函数来实现一些特殊的功能。
例如,在C语言中,可以使用汇编语言编写的函数来实现对硬件设备的直接控制,从而提高程序的运行效率。
2. C语言可以嵌入汇编语言代码。
在C语言程序中,可以嵌入一些汇编语言代码,用来实现一些特殊的功能。
例如,在C语言中,可以使用汇编语言代码来实现对特定寄存器的操作,从而提高程序的效率。
3. C语言可以生成汇编语言代码。
在C语言编译器中,可以将C 语言代码编译成汇编语言代码。
这样,程序员可以通过查看汇编语言代码来深入了解程序的运行过程,从而更好地优化程序。
4. 汇编语言可以调用C语言编写的函数。
在汇编语言中,可以直接调用C语言编写的函数,从而实现更加复杂的功能。
例如,在汇编语言中,可以调用C语言编写的函数来实现对字符串的操作。
5. 汇编语言可以直接操作C语言中的变量。
在汇编语言中,可以直接访问C语言程序中的变量,从而实现更加灵活的操作。
例如,在汇编语言中,可以直接操作C语言程序中的数组,从而实现更高效的数据处理。
总的来说,C语言和汇编语言之间的联系非常紧密,它们可以相互调用、嵌入、生成和操作,从而实现更加高效、灵活和复杂的功能。
浅谈C程序中调用汇编模块的方法
C语言是目前非常流行的一种编程语言,除具有高级语言使用方便灵活、数据处理能力强、编程简单等优点外,还可实现汇编语言的大部分功能,如可直接对硬件进行操作、生成的目标代码质量较高且执行的速度较快等。
所以在工程上对硬件处理速度要求不很高的情况下,基本可以用C代替汇编语言,编写接口电路的控制软件。
但C也不能完全取代汇编语言,如在一些对速度要求很高的实时控制系统中,以及对硬件的特殊控制方面,C有时也不能完全很好胜任,还需要汇编语言来编写。
因为汇编语言目标代码更精练,对硬件直接控制能力更强和执行速度更快,但汇编语言编程烦难、表达能力差也显而易见。
比较好的解决办法是C与汇编语言混合编程,即用C编写软件的调度程序、用户界面以及速度要求不高的控制部分,而用汇编语言对速度敏感部分提供最高速度的处理模块,供C调用。
这种方法提供了最佳的软件设计方案,做到了兼顾速度效率高和灵活方便。
由于本人的毕业设计需要C 程序中调用汇编模块的方法来提高ARM定点指令的执行速度,故对这方面进行了学习。
学习心得如下:
对于C和汇编语言的接口主要有两个问题需要解决。
一、调用者与被调用者的参数传递
这种数据传递通过堆栈完成,在执行调用时从调用程序参数表中的最后一个参数开始,自动依次压入堆栈;将所有参数压入堆栈后,再自动将被调用程序执行结束后的返回地址(断点)压入堆栈,以使被调程序结束后能返回主调程序的正确位置而继续执行。
例如一调用名为add汇编程序模块的主函数:main( ){...... add(dest,op1,op2,flages);......}。
在此例中对主函数进行反汇编,主函数在调用add函数前自动组织的堆栈。
.
.
.
lea 0xfffffffe8(%ebp),%eax #flages数组的首地址入栈
push %eax
pushl 0xfffffff8(%ebp) #OP2入栈
pushl 0xfffffffc(%ebp) #OP1 入栈
pushl 0xfffffff0(%ebp) #dest地址入栈
call 0x80483f0 <add> #调用add函数
.
.
执行完add调用语句后,栈内数据结果如图一所示。
进入汇编子程序后,为了能正确获取主调程序并存入堆栈中的数据,被调的汇编子程序先后要做如下一些工作:
1、保存esp的副本
进入汇编子程序后,子程序中免不了要有压栈和出栈的操作,故ESP时刻在变化。
为了能用ESP访问堆栈中的参数,安全办法是一进入子程序后,先为ESP制副本,以后对传递参数的访问都用副本进行。
一般可用EBP保存ESP,如:
push %ebp
mov %ebp,%esp
2、保留数据空间
如果汇编子程序中需要一些局部数据,可以简单地减小ESP的值,以便在栈空间中保留出一段存贮区,用于存放局部数据,该区域须在子程序结束后恢复。
如下语句可以保留一个局部数据区:
push %ebp
mov %ebp ,%esp
subl space,%esp;设space=4
movl $0x0,%ebp
movl $0x0,-2(%ebp)
如上语句段中,space是局部数据的总字节数。
在以后的应用中,由于ESP是变化的,而EBP是固定的,用负偏移量可以存取局部变量。
上例利用EBP及偏移量,将两个字的局部数据初始化为0。
3、保留寄存器值
如果在被调子程序中用到ESI、EDI等其它寄存器,则应先把它们压入堆栈,以保留寄存器原值。
例如,下例就是将ESI和EDI寄存器的值压栈:
pushl %ebp
movl %ebp ,%esp
subl $space ,%esp,
pushl %esi
pushl %edi
4、获取传递参数
作完了1~3步的操作后,结合上面C程序传送参数这一例子,现在栈结构如图二所示。
由此可见,EBP保留了ESP在参数传递完并将EBP压栈后的一个副本,利用EBP可以很方便地访问各参数。
现假设各参数都是2字节的整数值,在小模式编译方式共占用2个字节。
如果要将传递的参数op1、op2取出,并分别赋给ebx、ecx寄存器,可由下列语句完成这一功能:
movl 0x8(%ebp),%eax
movl 0xc(%ebp),%ecx
5、子程序返回值
当子程序的执行结果需要返回时,根据返回值的字长,C按如下约定接收返回值:1字节在AL 寄存器中;2字节在EAX寄存器中;4字节则高位部分在EDX中、低位部分在EAX 寄存器中。
C可从这些寄存器中取出返回值。
6、退出汇编子程序
结束汇编子程序的步骤如下:
1)若ESS、EDS、ESI或EDI已被压栈,则需按保存它们的相反顺序弹出它们。
2)若在过程开始时分配了局部数据空间,则以指令mov %esp和%ebp 恢复%esp。
3)以指令pop %ebp 恢复%ebp ,该步是必须的。
或者可以用leave语句来恢复%ebp 。
它相当于movl %ebp, %esp; popl %ebp
4)最后以ret结束汇编程序。
二、说明和建立调用者与被调用者间的连系
为了建立调用与被调用模块间的连接关系,被调用的汇编程序应用global,说明其可被外部模块调用;而调用程序则应预先说明要引用的外部模块名。
下面通过我的例子进行说明,该例是C调用add0的汇编子程序。
程序清单如下:
/* add.c */
#include <stdio.h>
extern void add(int *dest,int op1,int op2,short int*flages);
/*声明调用外部的汇编函数*/
int main(void){
int op1,op2,result;
int *dest=&result;
short int flages[4]={0,0,0,0};
printf("please enter two soure operater:");
scanf("%x%x",&op1,&op2);
add(dest,op1,op2,flages);/*调用add0函数*/
printf("The result of ADD is :%x\n flages N(negative) Z(zero) C(carry) V(overflow:%d,%d,%d,%d\n",*dest,flages[3],flages[2],flages[1],flages[0]);
return 0;
}
#add.s
.text
.align 2
.global add
.type add,function
#定义add为外部可调用的函数
add:
push %ebp #ebp寄存器内容压栈,保存add函数的上级调用函数的栈基地址
mov %esp,%ebp #esp值赋给ebp,设置add函数的栈基地址
mov 0x8(%ebp),%edx
mov 0x10(%ebp),%eax
add 0xc(%ebp),%eax
mov %eax,(%edx)
mov 0x14(%ebp),%eax
jo OF
C:
jc CF
S:
js SF
jz ZF
jmp out
OF:
movw $0x1,(%eax)
jmp C
CF:
movw $0x1,0x2(%eax)
jmp S
SF:
movw $0x1,0x6(%eax)
movw $0x0,0x4(%eax)
jmp out
ZF:
movw $0x1,0x4(%eax)
movw $0x0,0x6(%eax)
out:
leave #将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给#ebp,恢复原栈基址
ret #add函数返回,回到上级的调用函数
其中.text 标志一个代码段的开始,这是A T&T的段格式;global add;\n
type add,function说明add是公用的,可以由外部其它单独编译模块调用。
将C源程序以文件名add.c存盘,汇编语言源程序以add.s 存盘;通过MAKE进行编译和连接连接代码如下:
all: myadd
myadd: adds.o addc.o
gcc –o myadd adds.o adc.o
adds.o: add.s
as –o adds.o add.s
addc.o: add.c
gcc –g –o addc.o add.c
由上可见,在C中调用汇编模块很方便。
所以我们在实际软件开发中,可以采用混合编程的技术,从而尽可能利用各语言的优势。
既满足实际问题的需要,又简化设计过程,达到事半功倍的效果。