《ARMC语言的使》PPT课件.ppt_第1页
《ARMC语言的使》PPT课件.ppt_第2页
《ARMC语言的使》PPT课件.ppt_第3页
《ARMC语言的使》PPT课件.ppt_第4页
《ARMC语言的使》PPT课件.ppt_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章 ARM C語言的使用,DMATEK CO.,LTD 深圳市長高科技有限公司,本章節將介紹ARM C語言程式設計概念,透過對本文的閱讀,希望讀者能瞭解ARM微處理器支援的C語言的使用方法,我們會搭配反組譯的功能,讓讀者更瞭解ARM是如何來支援高階語言的運作。 本章的主要內容有: 抽象化概念 運算子 區域變數/全域變數 指標運算 迴圈/條件判斷 傳址呼叫和傳值呼叫,8-1 抽象化概念: 當讀者熟讀前面幾章後,應該對ARM有深刻的印象,在指令集您會感覺到ARM的強大功能,單一指令可以同時完成許多動作,然而使用組合語言來撰寫程式,您一定倍感辛苦,而特別是靈活運用條件執行Conditional Execution,把每一道指令添增了16種變化,相信您必須具備孫悟空72變化能力才能順利駕馭。本章將說明如何使用高階語言來撰寫ARM應用程式。在這之前先讓我們來看看組合語言及C語言抽象化的概念。 組合語言層次的抽象化 程式設計師直接以指令來撰寫組合語言的程式,您必須熟悉指令格式、定址方式、暫存器及記憶體空間等觀念。通常組合言的指令和機器指令是採1對1的關係,在指令格式中要特別注意條件執行,若能妥善運用不但能增強管線的效能而且程式也變得更加精簡,程式長度有可能比高階語言還要短。ARM指令集可分成ARM及THUMB兩種,前者為32位元而後者則為16位元。通常對於要求效能可以使用32位元,但對於空間及節能等需求時則可以考慮將部份程式用THUMB指令撰寫。,高階語言層次的抽象化 高階語言允許程式設計師以跳脫機械層級的思考來撰寫程式。高階語言和機器語言間已不是1對1的關係,對高階語言的程式設計師而言,不見得要熟悉ARM指令或暫存器等配置。所以有很多高階語言程式設計師並不見得對ARM機器有很深的瞭解。然而建議讀者要清楚瞭解ARM的硬體結構,在撰寫C語言程式時,能多考慮到ARM機器的特質,如此才能事半功倍。例如:過長的迴圈將會使快取記憶體(Cache)無法發揮它的長才。,8-2 運算子: C語言提供強大的運算子來處理運算工作,運算子可分成一元運算子Unary Operators、二元運算子Binary Operators、及三元運算子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 算術運算子 算術運算子可分成:加法、減法、乘法、除法、及取餘數,分別用+、-、*、/、及%來表示。詳如下表:,以下為運算子的範例: int main() int a,b,c; a=1; b=2; c=a+b; return 0; 其反組譯程式如下: 1 int main() 2 3 int a,b,c; 4 a=1; main 0xe3a01001 * mov r1,#1,5 b=2; 000080ac 0xe3a02002 mov r2,#2 6 c=a+b; 000080b0 0xe0813002 add r3,r1,r2 7 return 0; 000080b4 0xe3a00000 mov r0,#0 8 000080b8 0xe1a0f00e mov pc,r14,接下來我們來觀察,ARM對於除法的支援情形,以下是除法範例的反組譯程式列表: 3 int a,b,c; 4 a=6; 000080ac 0xe3a04006 mov r4,#6 5 b=2; 000080b0 0xe3a05002 mov r5,#2 6 c=a/b; 000080b4 0xe1a01004 mov r1,r4 000080b8 0xe1a00005 mov r0,r5 000080bc 0xeb000004 bl _rt_sdiv 000080c0 0xe1a06000 mov r6,r0,您應該可以發現ARM並未提供除法指令而由呼叫_rt_sdiv副程式取代,我們再來看看取餘數的反組譯程式列表: 3 int a,b,c; 4 a=6; 000080ac 0xe3a04006 mov r4,#6 5 b=2; 000080b0 0xe3a05002 mov r5,#2 6 c=a%b; 000080b4 0xe1a01004 mov r1,r4 000080b8 0xe1a00005 mov r0,r5 000080bc 0xeb000004 bl _rt_sdiv 000080c0 0xe1a06001 mov r6,r1,8-2.2 關係運算子 關係運算子有小於、小於等於、大於、大於等於、等於、及不等於,用、=、=、及!=來表示。詳如下表:,小於關係運算子的範例如下: 3 int a,b,c; 4 a=6; main 0xe3a01006 * mov r1,#6 5 b=2; 000080ac 0xe3a02002 mov r2,#2 c=ab; 000080b0 0xe1510002 cmp r1,r2 000080b4 0xaa000001 bge 0x80c0 ; (main + 0x18) 000080b8 0xe3a00001 mov r0,#1 000080bc 0xea000000 b 0x80c4 ; (main + 0x1c) 000080c0 0xe3a00000 mov r0,#0 000080c4 0xe1a03000 mov r3,r0 由上面範例可知小於關係運算子是利用cmp指令來設計,並利用分支指令b及其條件執行參數命令ge配合來達到將運算結果真1或假0利用將r0暫存再指定到c變數r3來儲存。,8-2.3 邏輯運算子 邏輯運算子有且、或、及非分用&、|、及!來表示。詳如下表:,“且”邏輯運算子的範例如下: 3 int a,b,c; 4 a=6; main 0xe3a01006 * mov r1,#6 5 b=2; 000080ac 0xe3a02002 mov r2,#2 6 c=a (main + 0x24) 000080c8 0xe3a00000 mov r0,#0 000080cc 0xe1a03000 mov r3,r0 在上面範例您有發現“且”具備捷徑Shortcut的功能,當變數a為假時則可以不需要考慮到變數b,此一作用我們稱為捷徑,若變數a為真時才需要測試變數b是否為真,當變數a及b都為真時,“且”關係運算子才會傳回真0,否則傳回假0。,8-2.4 指定運算子 指定運算子是用等號=來表示,要特別注意它具備指定的功能而非等於,它會把等號右側的常數值或變數值指定到等號左手邊的變數上。指定即為拷貝因此當指定運算子執行後,左側變數會等於右側計算後的結果,由於前面已使用過,在此不詳述。 8-2.5 增減運算子 增減運算子就是加1或減1分別使用+和來表示。,其範例反組譯程式列表如下: 3 int a,b; 4 b=a+; main 0xe1a02001 * mov r2,r1 000080ac 0xe2811001 add r1,r1,#1 5 b=+a; 000080b0 0xe2810001 add r0,r1,#1 000080b4 0xe1a01000 mov r1,r0 000080b8 0xe1a02000 mov r2,r0 6 b=a-; 000080bc 0xe1a02001 mov r2,r1 000080c0 0xe2411001 sub r1,r1,#1 7 b=-a; 000080c4 0xe2410001 sub r0,r1,#1 000080c8 0xe1a01000 mov r1,r0 000080cc 0xe1a02000 mov r2,r0,8-2.6 條件運算子 條件運算子是一個三元運算子,它以?和:來組成,其語法如下: 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; main 0xe3a01001 * mov r1,#1 5 b=2; 000080ac 0xe3a02002 mov r2,#2 6 c=(ab)?a:b; 000080b0 0xe1510002 cmp r1,r2 000080b4 0xda000001 ble 0x80c0 ; (main + 0x18) 000080b8 0xe1a00001 mov r0,r1 000080bc 0xea000000 b 0x80c4 ; (main + 0x1c) 000080c0 0xe1a00002 mov r0,r2 000080c4 0xe1a03000 mov r3,r0,8-2.7 位元運算子 位元運算子即為位元運算指令,有且、或、互斥、非、向右位移、及向左位移,用&、|、及來表示。詳如下表:,圖 8-1 顯示且位元運算子的運算結果,在圖中您可以發現且位元運算子&,使用and指令來組譯。當變數a和b分別指定為3和2時,再執行且位元運算式c=a&b後,變數c的結果為2,有興趣的讀者不妨把這些數值用二進位來表示,然後再執行且位元運算子&後,就可以得知為何是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 全域變數和區域變數: 變數主要是用來讓程式設計者暫時存放數值的地方,當您有需要時可以將它取出或修改它。在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 指標變數: 指標變數簡單來說是一種指位器,該變數儲存不是數值內容而是位址,我們可以使用*來宣告,使用如下: int *p; 在計算指標的大小時,可分成編譯階段得知或執行階段得知,編譯階段得知範例如下: int *p; p=p+1; 我們得知每次增加4位元組,因為指標的資料型態為整數佔四個位元組。但有些情形不易得知,例如: int *p; int i=4; p=p+i; 在此情形則需要執行階段才能獲知,則編譯器會使用ADD指令來處理。C語言最迷人地方在於它擁有指標功能,但也是它可怕的地方。圖8-3展示其可怕之處,從圖中您會得知指標居然指到位址0,而且我們把它改成1。從此您可以得知指標能修改記憶體的內容,但必須要非常小心,否則您會把重要的程式或資料給修改,造成不可預測的結果。,圖8-3 危險指標,圖8-4 指標一定要指向變數,圖8-4說明將變數指向變數,您會發現我們可以安全地變更p指標所指變數的內容,在圖中a變數為0xc000而p變數則為0xc004。,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 MOVLE r2,r1 ;else c=b 以下程式碼是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: 敍述區塊N;break; default: 敍述區塊d;break; ARM在支援switch指令採用,有時會採取跳躍表格方式處理,利用一表格來儲存各常數值的目的地,其指令樣板如下: ;r0 包含條件表示式的數值 ADR r1,JUMPTABLE ;取得跳躍表格的基值 CMP r0,#TABLEMAX ;表格最大值 LDRLS pc,r1,r0,LSL#2 ;改變PC值來進行跳躍 B Exit L1: B Exit LN Exit ,以下程式碼是C與組合語言混合寫法,詳細語法請參照8-8節。首先使用AXD模擬針對switchcase 的C語言程式碼如下,並進行C的反組譯如下圖8-6灰色字所示,執行結果,由於B減A等於2,所以會執行case 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、dowhile。for範例如下: for (i=0;i10;i+) ai=0; 由於上面範例僅將陣列10個元素全數設定為零,因此在組譯成組合語言時,可以利用在迴圈外增加指令MOV r0,#0,來加速指令的執行,其反組譯為: MOV r1,#0 ADR r2,#a0 MOV r0,#0 ;i=0 LOOP CMP r0,#10 BGE Exit STR r1, r2,r0,LSL #2 ADD r0,r0,#1 B LOOP 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 (i=0;i5;i+) a=a+i; return 0; ,圖 8-7 fornext範例程式示意圖,對於while迴圈則較簡單,可以使用下列組合語言程式來表示: LOOP ;插入條件判斷的指定 BEQ exit 迴圈本體 B LOOP Exit 以下程式碼是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,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範例程式示意圖,對於dowhile迴圈則較簡單,可以使用下列組合語言程式來表示: 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迴圈,而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 傳值呼叫範例執行結果,在使用函式時,經常會利用參數來傳遞數值,最常見的就是傳值呼叫(Call by Vakue)及傳址呼叫(Call by Address),其範例如圖8-10和8-11所示。讀者請特別注意兩者在使用上的差異,它們之間的差別在於是否使用指標。,圖8-11 傳址呼叫範例執行結果,上面兩個範例,相信您已發現在Console的視窗,其執行結果不同,傳值呼叫不會改變主程式的變數值然而傳址呼叫會。接下來我們來看看函式指標如圖8-7所示,原來指標不是只用來指向資料變數,也可以用來指向函數本體。您可經過下列三步驟,即可輕輕鬆鬆地使用函式指標,我們以指向swap函式當成範例。 步驟一、宣告函式指標。 void *(pFn) (int a, int b); 步驟二、指定函式。 pFn = ,圖8-12 函式指標範例執行結果,上面函式的反組譯程式列表如下: int main() main 0xe92d400e * stmfd r13!,r1-r3,r14 int a=1,b=2; 000080c0 0xe3a01001 mov r1,#1 000080c4 0xe58d1008 str r1,r13,#8 000080c8 0xe3a02002 mov r2,#2 000080cc 0xe58d2004 str r2,r13,#4 void (*pFn)(int *a, int *b); pFn= 000080d8 0xe28d1004 add r1,r13,#4 000080dc 0xe28d0008 add r0,r13,#8 000080e0 0xebfffff0 bl swap,printf (“a=%d, b=%dn“,a,b); 000080e4 0xe28f0010 add r0,pc,#0x10 ; #0x80fc 000080e8 0xe59d1008 ldr r1,r13,#8 000080ec 0xe59d2004 ldr r2,r13,#4 000080f0 0xeb000006 bl _printf return 0; 000080f4 0xe3a00000 mov r0,#0 000080f8 0xe8bd800e ldmfd r13!,r1-r3,pc 000080fc 0x64253d61 dcd 0x64253d61 a=%d 00008100 0x3d62202c dcd 0x3d62202c , b= 0x000a6425 dcd 0x000a6425 %d 雖然函式指標是C語言的特色,而且具備強大的功能,但當您看過反組譯程式後,您會發現在這個強大功能的背後,原來是使用ADD以及BL指令來完成,ADD指令用來取得傳址的位址,而BL指令則是用來呼叫副程式。若能善用指令的相互搭配就能創造迷人的功能,利用ADD及BL指令來設計函式指標的功能,就是一個明顯的例子。,8-8 C與組合語言的混合撰寫設計: 在應用系統的程式設計中,若所有的編寫程式任務均用組合語言來完成,其工作量是可想而知的,同時,不利於系統升級或應用軟體移植,事實上,ARM體系結構支援C/C以及與組合語言的混合編寫程式,在一個完整的程式設計的中,除了初始化部分用組合語言完成以外,其主要的編寫程式任務一般都用C 完成。 組合語言與C的混合編寫程式通常有以下幾種方式: 在C程式碼中嵌入編譯指令。 在組合語言程式和C的程式之間進行變數的互傳。 組合語

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论