例程:
经常见到嵌入式设计中,将某一程序段的入口地址转换为一个函数,我们来分析一下它的成分:
如在bootloader的0x00000020地址上的双字单元中有这样一条语句:
@Address is 0x00000020
bpowerdown @Jump to the flag "powerdown"
...
...
powerdown:
...
...
然后在某一C头文件中可以见到这样的宏声明:
#define EnterPWDN(clkcon) ((void (*)(int))0x20)(clkcon)
初一看,乱七八糟的,现在,我们来整理一下。
不难看出,当我们编程调用EnterPWDN(clkcon)函数的时候,编译器在编译前首先把EnterPWDN(clkcon)转换为
((void (*)(int))0x20)(clkcon)语句。
对于这个语句,我们将之分解成3部分来看。
1:(void (*)(int)
2:0x20
3:(clkcon)
(clkcon)是函数的参数,就不用说了。0x20呢?当然就是要转换的函数的入口地址了。那对于(void(*)(int)呢??呵呵……,这一部分作为一个整体,描述了转换后的函数的类型,即无返回值,带一个整形参数。而中间那个”(*)“,就表示要转换成一个函数(或者说把0x20转换为一个地址,因为在ARM汇编中,就把C语言的函数名当作一个地址标号看待了)。就像我们平常用的强制类型转换一样,(int)temp,只不过这里是将一个数转换为另一类型,而那是将一个地址转换为一个函数罢了。
关于arm中: #define EnterPWDN(clkcon)((void (*)(int))0x00000020)(clkcon)
1。首先要知道带参数的宏定义。定义方法如下:
#define 宏名(参数) 字符串
如下:#define add(a,b) (a)+(b)
那么:程序中的 b=add((2*3),(4*5)) 等价于 b=(2*3)+(4*5) 。
具体请参考c语言书籍。
2。下面看:EnterPWDN(0x7fff4);
会被替换成:((void (*)(int))0x00000020)(0x7fff4);
这个涉及到函数指针,函数指针的用法如下:
void fun(int x);
void(*pfun) (int x);
pfun=fun;
那么调用函数fun可以这样写:a. fun(5); b. (*pfun)(5);
则:((void(*)(int))0x00000020)(0x7fff4); 中蓝色0x7fff4是传递的参数,红色(void(*)(int))0x00000020是函数名。其实函数名本身就是一个地址。像上式中,如果你知道函数fun存在地址0x012345,那么理论上你可以这么写:0x012345(5),当然实际上不能这么写,因为你虽然fun在0x012345地址,但是你编译器不知到这个函数的参数、返回值等信息。所以要强制转换一下。
接下来看一下怎么把一个地址转换成函数名:
如果把 char a ; 中的a转换成 int 形变量,要这样:b=(int)a;
同样要把0x00000020地址转换为函数名,也要强制转换,只要在0x00000020的前面加上要转换的类型。(void(*)(int))就是函数类型,和void (*pfun) (int x); // 声明一个函数指针 是相对应的。
3。再看一下EnterPWDN(0x7fff4); 调用之后具体的参数传递,这个涉及到c和汇编的相互调用。
看
__ENTRY
ResetEntry
b ResetHandler
b HandlerUndef ;handler forUndefined mode
b HandlerSWI ;handler for SWIinterrupt
b HandlerPabort ;handler forPAbort
b HandlerDabort ;handler forDAbort
b . ;reserved
b HandlerIRQ ;handler for IRQinterrupt
b HandlerFIQ ;handler for FIQinterrupt
;@0x20
b EnterPWDN ; Must be @0x20.
跳到0x00000020地址后会接着跳转到标号EnterPWDN 处。
EnterPWDN
mov r2,r0 ;r2=rCLKCON
tst r0,#0x8 ;SLEEP mode?
bne ENTER_SLEEP
看这里第一句:mov r2,r0 。其实 r0中存的就是参数0x7fff4 ,这个涉及到c调用汇编,自己找一下参考书吧,我就不献丑了。
ARM中#define EnterPWDN(clkcon) ((void(*)(int))0x20)(clkcon))代表什么意思
1.首先,这是一个带参数宏定义,所以,如果有语句EnterPWDN(0x7fff4)则展开以后就是如下语句 ((void(*)(int))0x20)(0xffff4))
2.(void(*)(int))0x20的意思
(void(*)(int))作为一个强制类型转换,将0x20转换为一个函数指针类型,这个函数的原型为 voidfun(int); 而后面的0xffff4就是代表的函数的参数,所以 ((void(*)(int))0x20)(0xffff4))整句话的意思就是调用地址在0x20处的函数,参数为0xffff4.其实看后面实际上就是跳转到地址0x20处执行代码,并且把参数传到r0,ATPCS中规定1-4个参数对应的存储寄存器为r0-r3。
3.再看在2440init.s中对该部分的定义
b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
;@0x20
b EnterPWDN ; Must be @0x20.
EnterPWDN
mov r2,r0 ;r2=rCLKCON
tst r0,#0x8 ;SLEEP mode?
bne ENTER_SLEEP
b EnterPWDN ; Must be @0x20.这条语句,对应的指令地址为0x20,这就是前面((void(*)(int))0x20)中0x20的意义,然后语句跳到EnterPWDN执行,单然,参数值就传给了r0,后面的东西查看硬件说明。