1 寄存器规则
在C环境下对特殊操作使用特殊寄存器有严格的规定,C程序中嵌入汇编程序需要遵循这些规则,所以DSP程序员必须懂得寄存器规则。
寄存器规则规定了编译器如何使用寄存器和如何在函数调用时保存数值。
寄存器规则规定在函数调用时用到的寄存器要预先保存。这个工作部分由父函数完成,没有被父函数保存而子函数又用到的由子函数保存。
2 函数结构和调用规则
2.1.父函数如何调用其它函数
(1)将所要传递到子函数的参数放入寄存器或堆栈。
如果子函数的参量用省略号声明(表示参量数量可变),则首先把最后一个显式声明的参量传到堆栈,然后再把其它参量传到堆栈。堆栈地址将作为访问其它未声明参量的索引。
最后一个显式声明的参量之前所声明的参量遵循下述规则:
编译器通常先对要传递的参量分类,然后按照类型将参量放进寄存器。编译器使用的参量有3类:
数据指针(int *,long * 等)
16位数据(char,short,int)
32位数据(long,float,double,函数指针)
双字长(32位)或少于双字长的结构体会被当成32位数据参量,并通过寄存器传递。
如果结构体长度大于32位,编译器将该结构体的地址作为一个数据指针传递。
参量按照在函数声明中的排列顺序被分配给寄存器。参量放置的寄存器类型由参数的类型决定。如果参量的数量超过可用寄存器数量,多余的参量会被压入堆栈。
对于放入堆栈的参量按照下述方法处理:首先将堆栈调整到偶边界上;然后,每一个参数按照相应的参数类型被排列在堆栈上。
(2)子函数保存所有的入口保存寄存器(T2、T3、AR5~AR7)。父函数必须通过压入堆栈来保存其它在调用后会用到寄存器的值。
(3)父函数对子函数进行调用。
(4)父函数收集返回值。
短数据、长数据和数据指针分别返回在T0、AC0和(X)AR0中。如果子函数返回的是一个结构,则父函数在本地堆栈中为结构体分配相应大小的空间。
2.2.被调用函数(子函数)的响应
(1)被调用函数为局部变量、临时存储空间及函数可能调用的参数分配足够的存储空间。这些工作在函数调用开始的时候就完成了。
(2)如果子函数修改一些入口保存寄存器(T2、T3、AR5~AR7),必须将这些值压入堆栈或存储到一个没用的寄存器中。被调用函数可以修改其它的寄存器而不用保存其中的值。
(3)如果子函数的参数是一个结构体,则它所接收到的是一个指向该结构体的指针。如果在被调用函数中需要对结构体进行写操作,则需要把这个结构体复制到本地空间中。如果不进行写操作,则可以直接通过指针访问这个结构。
(4)子函数执行代码。
(5)如果子函数返回一个值,它将该值按照以下规则放置:
短整型数据值返回到T0中
长整型数据值返回到AC0中
数据指针值返回到(X)AR0中
如果子函数返回结构体,父函数就会为结构体分配存储空间并传送指向这个空间的指针到(X)AR0中。要返回这个结构体,被调用函数只要将该结构体复制到被这个指针所指的存储模块中。
(6)子函数恢复所有在第2步中保存的寄存器。
(7)子函数恢复存储在堆栈中的值到原始位置。
(8)函数返回。
3 C和汇编语言的接口
混合使用C代码和汇编语言代码的主要方法有:
使用几个独立的汇编代码模块,并将它们与编译了的C模块进行链接,这是最通用的方法。
在C源代码中使用汇编语言变量和常数。
将汇编语言程序直接嵌入C源代码中。
在C源代码中使用本征函数直接调用汇编语言语句。