經常見到嵌入式設計中,將某一程式段的入口地址轉換為一個函數,我們來分析一下它的成分:
如在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,後面的東西查看硬體說明。 |