关于#define EnterPWDN(clkcon)

经常见到嵌入式设计中,将某一程序段的入口地址转换为一个函数,我们来分析一下它的成分:
      如在bootloader的0x00000020地址上的双字单元中有这样一条语句:
@Address is 0x00000020
      b powerdown          @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; /* 将func函数的首地址赋给指针f */

      那么调用函数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 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.

      跳到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转换为一个函数指针类型,这个函数的原型为 void fun(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,后面的东西查看硬件说明。

你可能感兴趣的:(编程,汇编,嵌入式,语言,编译器,fun)