1.C语言函数的调用规则
C程序与汇编混合编程还必须保证函数的相互调用,这也要求C与汇编共同遵循一定的调用规则。C的调用规则由C编译器决定,人为不能修改,所以通常来说,汇编要遵循C的规则即可保证C和汇编函数的相互调用。下面对C语言的函数调用规则进行说明,并以一个实例进行过程分析。
(1)寄存器规则
在TMS320C54x C环境中,定义了严格的寄存器规则。寄存器规则明确了编译器如何使用寄存器,并规定了在函数的调用过程中如何保护寄存器。在调用函数时,有些寄存器不必由调用者来保护,而是由被调用函数负责保护。如果调用者需要使用没有保护的寄存器,则调用者必须在调用函数前对这些寄存器予以保护。因此在编写汇编语言和C语言的接口程序时,这些规则非常重要。如果编写过程中不遵守寄存器的使用规则,C环境将可能会被破坏,造成不可预测的结果。
1)调用过程中寄存器使用保护规则概括见表5-8。被调用的函数需保存表5-8中第三列注明为“是”的寄存器,同样,主调函数需保存表5-8中第四列注明为“是”的寄存器。例如:辅助寄存器中ARl、AR6、AR7由被调用函数负责保护,即在被调函数执行过程中可修改,但在函数返回时必须恢复。在TMS320C54x C中,ARl和AR6常用做寄存器变量。其中AR1被用做第一个寄存器变量,AR6被用做第二个寄存器变量,顺序是不能改变的。另外5个辅助寄存器AR0、AR2、AR3、AR4、AR5则由调用函数负责保护,在被调函数中可以自由使用(在执行过程中可对它们自由修改),函数返回前也不必恢复。
表5-8 调用过程中寄存器使用保护规则
(续)
2)堆栈指针SP在函数调用时必须保护,但这种保护在C中是通过堆栈自动完成的,在返回时,压入堆栈的内容都将被弹出恢复。
3)ARP在函数进入和返回时,其值必须为0,即指明当前辅助寄存器为AR0,而函数执行时则可以是其他值。
4)OVM在默认情况下,编译器总认为OVM是0。因此,若在汇编程序中将OVM置为1,则在返回C环境时,必须将其恢复为0。
5)其他状态位和寄存器可以任意使用,不必保存与恢复。
(2)调用函数执行过程
调用函数执行以下几项任务:
1)保护寄存器。
2)参数传递。在函数调用前,将参数以逆序压入运行堆栈。逆序是指最右边的参数最先压入栈,然后自右向左将参数依次压入栈,直至第二个参数入栈完毕。但对第一个参数,则不需压入堆栈,而是放入累加器A中,由A进行传递。
若参数是长整型和浮点数时,则低位字先压入栈,高位字后压入栈。若参数中有结构,则调用函数先给结构分配空间,而该空间的地址则通过累加器A传递给被调用函数。
3)返回地址压入堆栈,调用被调函数。
(3)被调函数执行过程
被调用函数依次执行以下几项任务:
1)寄存器保护。如果被调用函数中使用修改寄存器(如AR1、AR6、AR7),则必须将它们压栈保护。
2)局部变量分配。当被调用函数需分配内存来建立局部变量及参数区时,SP向低地址移动一个常数(即SP减去一个常数),该常数的计算方法如下:
常数=局部变量长度+参数区中调用其他函数的参数长度
3)函数执行。
4)恢复函数入口所保护的寄存器,释放局部变量,设置好返回值,弹出返回地址,返回被调函数。
(4)函数调用过程分析
【例5-28】 函数调用过程。
一个典型的函数调用过程如图5-7所示,函数调用前SP指到当前局部变量区顶部;调用时,主调函数首先传递参数,参数传递的方法是第一个参数放入累加器A中,接着在当前堆栈中把剩余参数以逆序压入堆栈,形成调用者参数区;然后将返回地址也压入堆栈;接着在堆栈区开辟出一个空间用以存放调用过程中需要保护的信息及被调函数的局部变量;最后执行被调函数。图5-7列举了一个函数调用,用C/汇编混合显示的示例。函数调用过程源代码用C/汇编混合显示,如图5-8所示。
图5-7 函数调用过程中堆栈的变化示意图
图5-8 函数调用C/汇编混合显示示例
2.独立的C和汇编模块接口
编写独立的汇编模块,最重要的是必须遵守C编译器所定义的函数调用规则和寄存器使用规则,遵循了这个规则就可以保证所编写的汇编模块不破坏C的运行环境。在编写各自独立的汇编程序时,还特别要注意以下几点:
1)不论是用C编写的函数还是用汇编编写的函数,都必须遵循寄存器使用规则。
2)中断程序必须保护所有用到的寄存器。
3)从汇编程序调用C函数时,第一个参数放入累加器A中,其余的参数以逆序的次序压入堆栈中。
4)调用C函数时,注意C函数只保护了几个特定的寄存器(AR1、AR6、AR7),而其他寄存器C函数是自由使用的。
5)长整型和浮点数在存储器中存放的顺序是高字节在低地址。
6)如果函数有返回值,则返回值存放在累加器A中,若返回值为结构或指针,则A中存放的为指针。
7)汇编模块不能改变由C产生的.cinit块,如果改变其内容会引起不可预测的后果。
8)在汇编程序中需要被C识别的所有标识符(函数名、变量名等)前加一下画线“_”。
9)任何在汇编中定义的对象或函数,如果需要在C中访问或调用,则必须用汇编指令.global定义。同样,如果在C中定义的对象或函数,需要在汇编中访问或调用,在汇编程序中也必须用.global指令定义。
3.从C程序中调用汇编函数(www.xing528.com)
函数的调用规则在之前已经介绍过,这里不再详细说明。其中需要特别注意的是,如果在函数的调用过程中由于传递多个参数可能会改变SP正常值,一定注意保护返回指针值,否则破坏了堆栈平衡,程序执行就不可预知了。
(1)单参数传入、单值返回
在前面的变量调用中已说明,单参数调用时传递的参数是放在累加器A中的;若函数有返回值时,返回值也是放在累加器A中。
【例5-29】 C程序中调用汇编函数示例(单参数传入、单值返回情况)。
C程序:
(2)多参数传入,单返回值
多参数传入时,第一参数是放在累加器A中的,其余参数按逆序压入堆栈。此时应特别注意,传递的参数压栈完成后系统才将返回指针压栈(之后去执行被调函数),所以调用的汇编函数中应确保返回指针在函数返回时在堆栈中处于合适的位置,否则函数返回时弹出的指针出错会造成堆栈不平衡,导致程序运行状态不可知。下面的例子在汇编函数执行相应功能前先将返回指针保存在AR4中,在RET返回前再将返回指针AR4压入栈中。
【例5-30】 C程序中调用汇编函数示例(多参数传入、单值返回情况)。
C程序:
汇编程序:
(3)传数组指针给汇编程序
实现原理是将数组的指针地址传给累加器A。
【例5-31】 C程序中调用汇编函数示例(数组指针用做参数的情况)。
C程序:
汇编程序:
(4)从汇编程序中传回返回值(数据块指针)
汇编程序中将数据块的首地址指针送给累加器A中,C程序中将其赋给对应的数组指针。
【例5-32】 C程序中调用汇编函数示例(返回值为数据指针的情况)。
C程序:
汇编程序:
4.从汇编程序中调用C函数
(1)传入一个参数,返回一个参数的情况
依据函数命名规则,C语言里定义的add函数,在汇编里引用时,声名为_add。传递参数只有一个,是由寄存器A负责传递的。
【例5-33】 汇编程序中调用C函数(单输入参数,单返回值情况)。
C程序中定义的函数:
汇编程序中调用:
(2)传递多个参数,返回一个值
将第一个参数放入累加器A中,其余的参数按逆序的顺序压入堆栈中,结果值存于累加器中。
【例5-34】 汇编程序中调用C函数(多输入参数,单返回值情况)。
汇编程序:
C程序:
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。