《第八章ARMC语言的使用.ppt》由会员分享,可在线阅读,更多相关《第八章ARMC语言的使用.ppt(51页珍藏版)》请在三一办公上搜索。
1、第八章ARM C語言的使用,DMATEK CO.,LTD深圳市長高科技有限公司,本章節將介紹ARM C語言程式設計概念,透過對本文的閱讀,希望讀者能瞭解ARM微處理器支援的C語言的使用方法,我們會搭配反組譯的功能,讓讀者更瞭解ARM是如何來支援高階語言的運作。本章的主要內容有:抽象化概念運算子區域變數/全域變數指標運算迴圈/條件判斷傳址呼叫和傳值呼叫,8-1 抽象化概念:當讀者熟讀前面幾章後,應該對ARM有深刻的印象,在指令集您會感覺到ARM的強大功能,單一指令可以同時完成許多動作,然而使用組合語言來撰寫程式,您一定倍感辛苦,而特別是靈活運用條件執行Conditional Execution,
2、把每一道指令添增了16種變化,相信您必須具備孫悟空72變化能力才能順利駕馭。本章將說明如何使用高階語言來撰寫ARM應用程式。在這之前先讓我們來看看組合語言及C語言抽象化的概念。組合語言層次的抽象化 程式設計師直接以指令來撰寫組合語言的程式,您必須熟悉指令格式、定址方式、暫存器及記憶體空間等觀念。通常組合言的指令和機器指令是採1對1的關係,在指令格式中要特別注意條件執行,若能妥善運用不但能增強管線的效能而且程式也變得更加精簡,程式長度有可能比高階語言還要短。ARM指令集可分成ARM及THUMB兩種,前者為32位元而後者則為16位元。通常對於要求效能可以使用32位元,但對於空間及節能等需求時則可以
3、考慮將部份程式用THUMB指令撰寫。,高階語言層次的抽象化高階語言允許程式設計師以跳脫機械層級的思考來撰寫程式。高階語言和機器語言間已不是1對1的關係,對高階語言的程式設計師而言,不見得要熟悉ARM指令或暫存器等配置。所以有很多高階語言程式設計師並不見得對ARM機器有很深的瞭解。然而建議讀者要清楚瞭解ARM的硬體結構,在撰寫C語言程式時,能多考慮到ARM機器的特質,如此才能事半功倍。例如:過長的迴圈將會使快取記憶體(Cache)無法發揮它的長才。,8-2 運算子:C語言提供強大的運算子來處理運算工作,運算子可分成一元運算子Unary Operators、二元運算子Binary Operator
4、s、及三元運算子Ternary Operators,依其功能可將運算子可分成下列:1.算術運算子Arithmetic Operators 2.關係運算子Relational Operators 3.邏輯運算子Logical Operators 4.指定運算子Assignment Operators 5.增減運算子Increments and Decrement Operators 6.條件運算子Conditional Operators 7.位元運算子Bitwise Operators 8.特殊運算子Special Operators,8-2.1 算術運算子 算術運算子可分成:加法、減法、乘法
5、、除法、及取餘數,分別用+、-、*、/、及%來表示。詳如下表:,以下為運算子的範例:int main()int a,b,c;a=1;b=2;c=a+b;return 0;其反組譯程式如下:1int main()2 3 int a,b,c;4 a=1;main0 xe3a01001*mov r1,#1,5 b=2;000080ac0 xe3a02002 mov r2,#2 6 c=a+b;000080b00 xe0813002 add r3,r1,r2 7 return 0;000080b40 xe3a00000 mov r0,#0 8 000080b80 xe1a0f00e mov pc,r1
6、4,接下來我們來觀察,ARM對於除法的支援情形,以下是除法範例的反組譯程式列表:3 int a,b,c;4 a=6;000080ac0 xe3a04006 mov r4,#6 5 b=2;000080b00 xe3a05002 mov r5,#2 6 c=a/b;000080b40 xe1a01004 mov r1,r4 000080b80 xe1a00005 mov r0,r5 000080bc0 xeb000004 bl _rt_sdiv 000080c00 xe1a06000 mov r6,r0,您應該可以發現ARM並未提供除法指令而由呼叫_rt_sdiv副程式取代,我們再來看看取餘數的
7、反組譯程式列表:3 int a,b,c;4 a=6;000080ac0 xe3a04006 mov r4,#6 5 b=2;000080b00 xe3a05002 mov r5,#2 6 c=a%b;000080b40 xe1a01004 mov r1,r4 000080b80 xe1a00005 mov r0,r5 000080bc0 xeb000004 bl _rt_sdiv 000080c00 xe1a06001 mov r6,r1,8-2.2 關係運算子 關係運算子有小於、小於等於、大於、大於等於、等於、及不等於,用、=、=、及!=來表示。詳如下表:,小於關係運算子的範例如下:3 in
8、t a,b,c;4 a=6;main0 xe3a01006*mov r1,#65 b=2;000080ac0 xe3a02002 mov r2,#2c=ab;000080b00 xe1510002 cmp r1,r2000080b40 xaa000001 bge 0 x80c0;(main+0 x18)000080b80 xe3a00001 mov r0,#1000080bc0 xea000000 b 0 x80c4;(main+0 x1c)000080c00 xe3a00000 mov r0,#0000080c40 xe1a03000 mov r3,r0 由上面範例可知小於關係運算子是利用c
9、mp指令來設計,並利用分支指令b及其條件執行參數命令ge配合來達到將運算結果真1或假0利用將r0暫存再指定到c變數r3來儲存。,8-2.3 邏輯運算子 邏輯運算子有且、或、及非分用&、|、及!來表示。詳如下表:,“且”邏輯運算子的範例如下:3 int a,b,c;4 a=6;main0 xe3a01006*mov r1,#65 b=2;000080ac0 xe3a02002 mov r2,#26 c=a(main+0 x24)000080c80 xe3a00000 mov r0,#0000080cc0 xe1a03000 mov r3,r0在上面範例您有發現“且”具備捷徑Shortcut的功能
10、,當變數a為假時則可以不需要考慮到變數b,此一作用我們稱為捷徑,若變數a為真時才需要測試變數b是否為真,當變數a及b都為真時,“且”關係運算子才會傳回真0,否則傳回假0。,8-2.4 指定運算子 指定運算子是用等號=來表示,要特別注意它具備指定的功能而非等於,它會把等號右側的常數值或變數值指定到等號左手邊的變數上。指定即為拷貝因此當指定運算子執行後,左側變數會等於右側計算後的結果,由於前面已使用過,在此不詳述。8-2.5 增減運算子 增減運算子就是加1或減1分別使用+和來表示。,其範例反組譯程式列表如下:3 int a,b;4 b=a+;main0 xe1a02001*mov r2,r1000
11、080ac0 xe2811001 add r1,r1,#15 b=+a;000080b00 xe2810001 add r0,r1,#1000080b40 xe1a01000 mov r1,r0000080b80 xe1a02000 mov r2,r06 b=a-;000080bc0 xe1a02001 mov r2,r1000080c00 xe2411001 sub r1,r1,#17 b=-a;000080c40 xe2410001 sub r0,r1,#1000080c80 xe1a01000 mov r1,r0000080cc0 xe1a02000 mov r2,r0,8-2.6 條件
12、運算子 條件運算子是一個三元運算子,它以?和:來組成,其語法如下:exp1?exp2:exp3 當exp1條件運算為真時,則傳回exp2運算值否則傳exp3運算值,其範例如下:a=10;b=15;x=(a b)?a:b在上面範例當變數a大於b時,則將變數x內容指定成a否則指定成b。其反組譯程式碼列表如下:3 int a,b,c;4 a=1;main0 xe3a01001*mov r1,#15 b=2;000080ac0 xe3a02002 mov r2,#26 c=(ab)?a:b;000080b00 xe1510002 cmp r1,r2000080b40 xda000001 ble 0 x
13、80c0;(main+0 x18)000080b80 xe1a00001 mov r0,r1000080bc0 xea000000 b 0 x80c4;(main+0 x1c)000080c00 xe1a00002 mov r0,r2000080c40 xe1a03000 mov r3,r0,8-2.7 位元運算子 位元運算子即為位元運算指令,有且、或、互斥、非、向右位移、及向左位移,用&、|、及來表示。詳如下表:,圖 8-1 顯示且位元運算子的運算結果,在圖中您可以發現且位元運算子&,使用and指令來組譯。當變數a和b分別指定為3和2時,再執行且位元運算式c=a&b後,變數c的結果為2,有興
14、趣的讀者不妨把這些數值用二進位來表示,然後再執行且位元運算子&後,就可以得知為何是2。,圖8-1 且位元運算子執行結果,8-2.8 特殊運算子 最後我們來看特殊運算子,分列如下:逗號運算子(comma operator)value=(x=10,y=5,x+y);變數大小運算子(size of operator)sizeof m=sizeof(sum);指標運算子(pointer operators)(&and*)成員選取運算子(member selection operators)(.and-),8-3 全域變數和區域變數:變數主要是用來讓程式設計者暫時存放數值的地方,當您有需要時可以將它取出
15、或修改它。在C語言中變數可分成兩大類:全域變數宣告在程式開頭處即為函式外面,其範圍涵蓋所有函式。通常使用記憶體空間來存放。區域變數 宣告在函式內部,都以是暫存器或堆疊來存放,其使用範圍僅限於函式內部。以下為全域變數和區域變數的程式範例:,/採用全域變數int a,b,c;int main()a=1;b=1;c=a+b;return 0;/採用區域變數int main()int a,b,c;a=1;b=1;c=a+b;return 0;,上面範例看起來程式長度一樣,但我們利用反組譯功能,您會發現多使用區域變數會提高程式的效能,其程式列表如下圖:,圖8-2 全域變數及區域變數效能比較,8-4 指標
16、變數:指標變數簡單來說是一種指位器,該變數儲存不是數值內容而是位址,我們可以使用*來宣告,使用如下:int*p;在計算指標的大小時,可分成編譯階段得知或執行階段得知,編譯階段得知範例如下:int*p;p=p+1;我們得知每次增加4位元組,因為指標的資料型態為整數佔四個位元組。但有些情形不易得知,例如:int*p;int i=4;p=p+i;在此情形則需要執行階段才能獲知,則編譯器會使用ADD指令來處理。C語言最迷人地方在於它擁有指標功能,但也是它可怕的地方。圖8-3展示其可怕之處,從圖中您會得知指標居然指到位址0,而且我們把它改成1。從此您可以得知指標能修改記憶體的內容,但必須要非常小心,否則
17、您會把重要的程式或資料給修改,造成不可預測的結果。,圖8-3 危險指標,圖8-4 指標一定要指向變數,圖8-4說明將變數指向變數,您會發現我們可以安全地變更p指標所指變數的內容,在圖中a變數為0 xc000而p變數則為0 xc004。,8-5 條件敍述:C語言條件敍述有if和switch兩種,if又可搭配else使用。if範例如下:if(ab)c=a;else c=b;上述程式說明當ab條件成立時c變數指定為變數a的內容否則指定為變數b。當上面範例組譯成組合語言時,可以反組譯成下面:CMP r0,r1;if(ab)MOVGT r2,r0;.c=a.MOVLEr2,r1;else c=b.以下程
18、式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對if.else的C語言程式碼如下,並進行C的反組譯如下圖8-5灰色字所示,執行結果,由於A小於B所以執行A加B的動作,結果在Global Variable 的值A等於9,B等於5。int a=4,b=5;int Main(void)if(ab)L a=a-b;else a=a+b;return 0;,圖 8-5 if.else範例程式示意圖,接下來我們來看另一個敍述switch,其語法如下:switch(條件表示式)case 常數1:敍述區塊1;break;case常數2:敍述區塊2;break;case常數N:敍述區塊
19、N;break;default:敍述區塊d;break;ARM在支援switch指令採用,有時會採取跳躍表格方式處理,利用一表格來儲存各常數值的目的地,其指令樣板如下:;r0 包含條件表示式的數值ADRr1,JUMPTABLE;取得跳躍表格的基值CMPr0,#TABLEMAX;表格最大值LDRLSpc,r1,r0,LSL#2;改變PC值來進行跳躍BExitL1:.BExit.LN.Exit.,以下程式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對switchcase 的C語言程式碼如下,並進行C的反組譯如下圖8-6灰色字所示,執行結果,由於B減A等於2,所以會執行ca
20、se 2這段程式碼,B減A的動作,結果如下圖8-6在Global Variable 的值A等於2,B等於5。int a=3,b=5;int Main(void)*9*switch(b-a)case 1:a=a+b;break;case 2:a=b-a;break;return 0;,圖 8-6 switch.case範例程式示意圖,8-6 迴圈敍述:C語言支援三種迴圈敍述有for、while、do.while。for範例如下:for(i=0;i10;i+)ai=0;由於上面範例僅將陣列10個元素全數設定為零,因此在組譯成組合語言時,可以利用在迴圈外增加指令MOV r0,#0,來加速指令的執行,
21、其反組譯為:MOVr1,#0 ADRr2,#a0 MOVr0,#0;i=0 LOOPCMPr0,#10 BGEExit STRr1,r2,r0,LSL#2 ADDr0,r0,#1 BLOOP Exit,以下程式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對for next的C語言程式碼如下,並進行C的反組譯如下圖8-7灰色字所示,執行結果,由於I小5,所以會執行A=A+I這段程式碼5次,執行過程0+1+2+3+4,所以A會一直累加I的值,結果如下圖8-7在Global Variable的值A等於10,I等於5。int i,a;int Main(void)a=0;for
22、(i=0;i5;i+)a=a+i;return 0;,圖 8-7 fornext範例程式示意圖,對於while迴圈則較簡單,可以使用下列組合語言程式來表示:LOOP;插入條件判斷的指定 BEQ exit 迴圈本體 B LOOPExit以下程式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對while的C語言程式碼如下並進行C的反組譯如下圖8-8灰色字所示,執行結果,由於I小5,所以會執行while這段程式碼會詢問6次,執行結果為1+2+3+4+5,而詢問第六次時,就會跳開while迴圈,而A會一直累加I的值,所以結果如下圖在Global Variable的值A等於15,
23、I等於5。int i,a;int Main(void)a=0,i=0;while(i5)i=i+1;a=a+i;return 0;,圖 8-8 while範例程式示意圖,對於do.while迴圈則較簡單,可以使用下列組合語言程式來表示:LOOP;插入迴圈本體程式;插入條件判斷的指定 BNE LOOP以下程式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對do.while的C語言程式碼如下,並進行C的反組譯如下圖8-9灰色字所示,執行結果,由於I小5,所以會執行dowhile這段程式碼只會執行5次,執行結果為1+2+3+4+5,而詢問第5次時,就會直接跳開do.while
24、迴圈,而A會一直累加I的值,所以結果如下圖8-9在Global Variable的值A等於15,I等於5。int i,a;int Main(void)a=0,i=0;do i=i+1;a=a+i;while(i5);return 0;,圖 8-9 do.while範例程式示意圖,8-7 程式呼叫標準:ARM支援一套程式呼叫的標準(ARM Procedure Call Standard),簡稱為APCS。APCS 定義下列內容:一般暫存器的特殊使用堆疊指標的使用堆疊的資料格式函式參數傳遞和傳值格式支援ARM共用函式庫,其參數使用暫存器如下表所示:,圖8-10 傳值呼叫範例執行結果,在使用函式時,
25、經常會利用參數來傳遞數值,最常見的就是傳值呼叫(Call by Vakue)及傳址呼叫(Call by Address),其範例如圖8-10和8-11所示。讀者請特別注意兩者在使用上的差異,它們之間的差別在於是否使用指標。,圖8-11 傳址呼叫範例執行結果,上面兩個範例,相信您已發現在Console的視窗,其執行結果不同,傳值呼叫不會改變主程式的變數值然而傳址呼叫會。接下來我們來看看函式指標如圖8-7所示,原來指標不是只用來指向資料變數,也可以用來指向函數本體。您可經過下列三步驟,即可輕輕鬆鬆地使用函式指標,我們以指向swap函式當成範例。步驟一、宣告函式指標。void*(pFn)(int a
26、,int b);步驟二、指定函式。pFn=,圖8-12 函式指標範例執行結果,上面函式的反組譯程式列表如下:int main()main0 xe92d400e*stmfd r13!,r1-r3,r14 int a=1,b=2;000080c00 xe3a01001 mov r1,#1000080c40 xe58d1008 str r1,r13,#8000080c80 xe3a02002 mov r2,#2000080cc0 xe58d2004 str r2,r13,#4 void(*pFn)(int*a,int*b);pFn=000080d80 xe28d1004 add r1,r13,#40
27、00080dc0 xe28d0008 add r0,r13,#8000080e00 xebfffff0 bl swap,printf(a=%d,b=%dn,a,b);000080e40 xe28f0010 add r0,pc,#0 x10;#0 x80fc000080e80 xe59d1008 ldr r1,r13,#8000080ec0 xe59d2004 ldr r2,r13,#4000080f00 xeb000006 bl _printf return 0;000080f40 xe3a00000 mov r0,#0000080f80 xe8bd800e ldmfd r13!,r1-r3,
28、pc000080fc0 x64253d61 dcd 0 x64253d61 a=%d000081000 x3d62202c dcd 0 x3d62202c,b=0 x000a6425 dcd 0 x000a6425%d.雖然函式指標是C語言的特色,而且具備強大的功能,但當您看過反組譯程式後,您會發現在這個強大功能的背後,原來是使用ADD以及BL指令來完成,ADD指令用來取得傳址的位址,而BL指令則是用來呼叫副程式。若能善用指令的相互搭配就能創造迷人的功能,利用ADD及BL指令來設計函式指標的功能,就是一個明顯的例子。,8-8 C與組合語言的混合撰寫設計:在應用系統的程式設計中,若所有的編寫程式
29、任務均用組合語言來完成,其工作量是可想而知的,同時,不利於系統升級或應用軟體移植,事實上,ARM體系結構支援C/C以及與組合語言的混合編寫程式,在一個完整的程式設計的中,除了初始化部分用組合語言完成以外,其主要的編寫程式任務一般都用C 完成。組合語言與C的混合編寫程式通常有以下幾種方式:在C程式碼中嵌入編譯指令。在組合語言程式和C的程式之間進行變數的互傳。組合語言程式、C程式間的相互呼叫。在以上的幾種混合編寫程式技術中,必須遵守一定的呼叫規則,如實體位址的使用、參數的傳遞等,這對於初學者來說,無疑顯得過於煩瑣。在實際的編寫程式應用中,使用較多的方式是:程式的初始化部分用組合語言完成,然後用C完
30、成主要的編寫程式任務,程式在執行時首先完成初始化過程,然後跳躍到C程式碼中,組合語言程式和C程式之間一般沒有參數的傳遞,也沒有頻繁的相互呼叫,因此,整個程式的結構顯得相對簡單,容易理解。以下是一個組合結構程式的基本範例:,IMPORT Main;通知編譯器該標號為一個外部標號AREA Init,CODE,READONLY;定義一個程式碼段ENTRY;定義程式的入口點LDRR0,=0 x3FF0000;初始化系統配置暫存器,LDRR1,=0 xE7FFFF80STRR1,R0LDRSP,=0 x3FE1000;初始化用戶堆疊BLMain;跳躍到Main()函數處的C程式碼執行END;標識組合語言
31、程式的結束以上的程式段完成一些簡單的初始化,然後跳躍到Main()函數所標識的C程式碼處執行主要的任務,此處的Main僅為一個標號,也可使用其他名稱,與C語言程式中的main()函數沒有關係。,void Main(void)int i;*(volatile unsigned long*)0 x3ff5000)=0 x0000000f;while(1)*(volatile unsigned long*)0 x3ff5008)=0 x00000001;for(i=0;i0 x7fFFF;i+);*(volatile unsigned long*)0 x3ff5008)=0 x00000002;for(i=0;i0 x7FFFF;i+);,8-9 問題與討論:一、請說明組合語言和C語言在抽象化概論上差異。二、C語言支援那些問算子。三、說明傳值呼叫和傳址呼叫。四、說明全域變數和區域變數的使用方式及其效能比較。五、請列出for、if反組譯成組合語言的指令。六、請撰寫一支程式可求1至100整數的總合,並利用ADS把該程式反組譯成組合語言。七、請利用ADD指來撰寫一個兩數相加的副程式,並使用C誩言宣告兩個整數變數,並呼叫該組合語言的副程式來進行加總。,