一个函数调用另一个函数时,需要进行以下步骤:
1) 被调用的函数并不需要保存寄存器变量,但是当函数返回的时候,返回值需要保存在Stack中;
2) 如果被调用的函数返回参数为结构体,则调用函数将会为该结构申请一个内存空间,并且将该内存空间的地址作为被调用函数的第一个参数;
3) 被调用函数的参数是通过寄存器存放,必要时,可以存在放在Stack中。参数传递的规则如下:
a) 32-bit的参数(longs或者floats),第一存放在32-bit ACC(AH/AL)。其他32-bit参数或者函数指针以reverse order存放在Stack中。
b) 指针参数存放在XAR4和XAR5中,其他所有参数存放在Stack中。
c) 剩下的16-bit参数按照次序存放在AL、AH、XAR4、XAR5中(这些寄存器并没有使用的情况下)。
4) 所有不存放在寄存器中的参数都以reverse次序push到Stack中(最左边的参数在stack中位置为stack的末尾)。所有32-bit参数在stack中以偶数地址为边界。
结构参数事实上是以结构体的地址作为参数传入,被调用的函数必须有一个本地局部变量copy。
如果函数以省略号进行申明,表示函数可以接受变长的参数,这边进行了简单的修正。最后一个明确的参数存放在stack中,所以它的stack地址可以作为读取其他非申明参数的索引。
5) 调用者使用LCR指令调用函数,RPC寄存器变量将被push到stack中,函数的返回值将被存放在RPC寄存器中。
一个被调用函数需要进行以下步骤:
1) 如果被调用函数修改了XAR1、XAR2、或者XAR3,则必须保存修前的值,因为调用函数在这些寄存器中保存了变量,其他寄存器使用之前并不需要预先保存;
2) 被调用函数在stack中为局部变量、临时变量、函数可能使用的参数分配了足够的内存空间,在函数初始化时增加常量到SP寄存器中,内存空间的分配会发生;
3) 如果被调用函数需要使用结构体参数,它只会接收到一个结构体指针。如果被调用函数需要对该结构进行写操作,该结构体必须在stack中有一个本地保存的局部变量,接收的结构体指针被copy到本地保存的局部变量中。如果对于该结构没有写操作,被调用函数直接使用该指针参数作为引用。
当被调用函数需哟啊接收一个结构体参数时,必须谨慎的声明函数,包括它们被使用的地方(结构体参数为作为地址传递的)和它们被声明的地方(函数知道copy该结构体到本地局部变量)。
4) 被调用函数执行函数的code;
5) 被调用函数返回值被保存在寄存器中,规则如下:
16-bit 整形变量:AL
32-bit 整形变量:ACC
16-或者22-bit 指针:XAR4
如果函数返回结构体,调用者会为这个结构体申请内存空间,同时将被调用函数的返回内存空间地址传递进去(XAR4),对于返回结构体,被调用函数通过外部参数copy结构体到内存块中。
通过这种方法,调用者可以通知被调用函数是否返回结构体。
6) 被调用函数分配内存通过减去之前加在SP的值。
7) 被调用函数恢复之前Step1保存的所有寄存器变量。
8) 被调用函数通过LRETR指令返回。PC使用RPC寄存器保存的变量,之前RPC的值从stack中pop出,并保存。
2008-1-13
写中断程序时,需要注意以下几点:
1) 中断程序没有参数,即使声明了,它们也会被忽略掉;
2) 中断程序可以被一般的C/C++代码调用,但是其效率很低,因为调用时会保存所有的寄存器;
3) 中断程序能够响应一个中断或者多个中断。编译器不会为中断增加特殊代码,除了系统复位中断程序:c_init00。当进入中断程序时,不能认为run-time的stack已经建立起来了,因此,你无法分配局部变量以及保存任何信息在run-time stack中。
4) 为了使得中断程序和中断结合,中断函数的地址必须放在合适的中断向量表中。可以使用assembler和linker通过.sect创建中断地址表。
5) 在汇编程序中,记得在符号名称前增加下划线,例如:c_int00到_c_int00。
c_int00函数执行了以下操作,初始化C/C++环境:
1) 为run-time stack申请内存空间,设置初始的stack pointer变量;
2) 初始化寄存器状态位,并设置寄存器为期望值;
3) 将.cinit内初始化表的数据copy到申请内存空间的.bss内,在RAM自动初始化模式下,loader在函数运行前进行这一步的操作(而不是由boot程序);
4) 执行.pinit段中断全局构造;
5) 调用main()函数开始C/C++程序。