ARM編程技術ppt課件_第1頁
ARM編程技術ppt課件_第2頁
ARM編程技術ppt課件_第3頁
ARM編程技術ppt課件_第4頁
ARM編程技術ppt課件_第5頁
已閱讀5頁,還剩171頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、第4章 ARM編程技術硅谷芯微 技術奉獻threeway.cc. ARM處置器支持兩種言語進展設計,一種是匯編言語ARM/Thumb,另一種是高級言語如C/C+言語。匯編言語的機器代碼生效果率很高,但可讀性卻不強,復雜的程序很難讀懂;而高級言語在大多數情況下其機器代碼生效果率較差,但可讀性和可移植性卻比匯編言語強很多,而且還可以嵌入匯編來處理高效性的代碼編寫問題。本章的內容主要有: 匯編言語編程技術; C言語編程技術; 匯編言語與C/C+言語的混合編程。.第4章 目錄1. 匯編言語 2. C言語程序設計3. 匯編言語與C/C+言語的混合編程4. ARM編程實戰5. 課后練習 .4.1 匯編言語

2、 運用匯編言語編寫程序,它的特點是程序執行速度快,程序代碼生成量少,但匯編言語是一種不易學習的編程言語,并且可讀性較差,這種言語屬于低級言語。每一種匯編言語對應每一款芯片,運用這種言語需求對硬件有深化的了解。在通常情況下,可以運用匯編言語編寫驅動程序、需求嚴厲計算執行時間的程序以及需求加速執行的程序。 .4.1.1 ARM匯編程序的格式1 先引見一個例子來闡明ARM匯編程序的格式。例1 計算20+8,結果放入R0存放器。 .4.1.1 ARM匯編程序的格式2 例1中定義了兩個段:數據段Buf和代碼段Example。數據段中定義了字節單元Count,其中Count用來保管一個被加數;代碼段中包含

3、了一切源程序代碼,程序中首先讀取Count字節單元的內容,然后與立刻數8相加,計算結果保管到R0中。 由例1可見,ARM匯編言語的源程序是分段的,由假設干個段組成一個源程序。源程序的普通格式為: .4.1.1 ARM匯編程序的格式3 每一個段都有一個名字,并且段名是獨一的。每個段以符號AREA作為段的開場,以碰到下一個符號AREA作為該段的終了。段都有本人的屬性,如是代碼段CODE還是數據段DATA,是只讀READONLY還是可讀寫READWRITE?這些屬性可以在attr欄中設定。留意:符號AREA和END都不能頂格寫,只需標號可以而且必需頂格寫。 .4.1.1.1 ARM匯編程序的書寫格式

4、1 ARM匯編源程序是由假設干段組成的,而一個段又是由假設干個語句行組成。語句就是完成一個動作的闡明。源程序中的語句可以分為以下兩種類型: 指令性語句:匯編程序會把指令性語句翻譯成機器代碼,然后利用這些機器代碼命令處置器執行某些操作。如由MOV、ADD等指令構造的語句。 指示性語句:匯編程序并不把它們翻譯成機器代碼,只是用來指示、引導匯編程序在匯編時進展一些操作。如由ENTRY、AREA等指令構造的語句,我們也稱這些指令為偽指令。從例1可知,語句行的根本格式如下:.4.1.1.1 ARM匯編程序的書寫格式2 在一條語句中, 號中的內容是可選的。在書寫ARM匯編程序時,需求留意以下3點: 標號必

5、需在一行的頂格書寫,其后面不要加“:,對于變量的設置、常量的定義,其標識符必需在一行的頂格書寫;而一切指令均不能頂格書寫。 匯編器對標識符大小寫敏感,書寫標號及指令時字母大小寫要一致。在ARM匯編程序時,一個ARM指令、偽指令、存放器名可以全部為大寫字母,也可以全部為小寫字母,但不要大小寫混合運用。 注釋運用“;。注釋內容由“;開場到此行終了,注釋可以在一行的頂格書寫。例2 某一段錯誤的匯編言語程序。.4.1.1.1 ARM匯編程序的書寫格式3例2 某一段錯誤的匯編言語程序。.4.1.1.2 語句行的符號1 任何一個匯編源程序都是由符號組成的。符號分為兩大類:指令助記符和用戶定義符。指令助記符

6、包括ARM指令、偽指令等,這些符號都是預先定義好的,且具備公用的目的和功能;用戶定義符是由用戶在編寫匯編程序時自行定義的,只在本程序中有意義,不具備通用性。本節所講的符號特指用戶定義符,符號的命名需留意以下規那么: 符號由大小寫字母、數字以及下劃線組成。 符號不能以數字開頭部分標號除外。 符號區分大小寫,且一切字符都是有意義的。 符號在其作用域范圍內必需是獨一的。 符號不能與系統內部或系統預定義的符號同名。 符號不要以指令助記符、偽指令同名。符號可以代表地址、數值、變量。當符號代表地址時又稱為標號,符號代表某個特定數值時又稱為符號常量,.4.1.1.2 語句行的符號2符號代表變量時又稱為變量名

7、。所以符號有3個用途:標號、符號常量、變量名。1標號: 標號代表一個地址,段內標號的地址在匯編時確定,而段外標號的地址值在鏈接時確定。根據標號的生成方式可以分為以下3種: 基于PC的標號:該標號是位于目的指令前的標號或程序中的數據定義偽指令前的標號。這種標號在匯編時將被處置成PC值加上或減去一個數字常量。它常用于表示跳轉指令的目的地址,或者代碼段中所嵌入的少量數據。 基于存放器的標號:該標號通常用MAP和FILED偽指令定義,也可以用于EQU偽指令定義。這種標號在匯編時被處置成存放器的值加上或減去一個數字常量。它常用于訪問位于數據段中的數據。.4.1.1.2 語句行的符號3 絕對地址:絕對地址

8、是一個32位的數字量,可尋址的范圍為0232-1,可以直接尋址整個內存空間。例3 標號舉例。.4.1.1.2 語句行的符號42符號常量: 在程序運轉過程中,其值不能被改動的量稱為常量。常量區分為3種不同的類型: 數字常量:數字常量表示某個特定的數字,如0、5、-19、0 xF8都是數字常量。同一個數字常量可以有十進制數、十六進制數等多種表達方式。 字符常量:字符常量由一對單引號及中間字符串表示,規范C言語中的本義符也是也可運用。假設需求包含雙引號或“$,必需運用“或“$替代。如H是一個字符常量,“Hello World是一個字符串常量。 布爾常量:布爾常量由括號和邏輯值TRUE、FALSE表示

9、。邏輯真為TRUE,邏輯假為FALSE。 為了程序書寫的方便,可以用一個標識符來代表一個常量,稱這個標識符為符號常量,即標識符方式的常量。.4.1.1.2 語句行的符號5例4 用EQU偽指令定義數字符號常量。 例子中定義了兩個數字符號常量:T_bit和PLLCON。所以程序中用到這兩個符號常量時,在程序鏈接時就會被相應的值0 x20、0 xE01FC080所替代。 .4.1.1.2 語句行的符號63變量名: 變量是指存放在存儲單元的操作數,并且它的值可以改動。變量名代表了一個變量,當程序中要用到變量時,只需求援用對應的變量名。 實踐上,變量名是一個符號地址,當程序編譯鏈接時,系統會給每一個變量

10、名分配一個內存地址。在程序中從變量中取值,實踐上是經過變量名找到相應的內存地址,從其存儲單元中讀取數據。 按照變量的作用范圍可分為全局變量和部分變量;按照變量的數值類型可分為數字變量、字符變量和邏輯變量。根據兩種類型的組合,變量共具有6種類型:全局數字變量、全局邏輯變量、全局字符串變量、部分數字變量、部分邏輯變量、部分字符串變量。詳細對這些類型的變量如何聲明、賦初值,將在4.1.2小節符號定義偽指令中詳細引見。.4.1.2 偽指令語句1 匯編言語程序由機器指令、偽指令和宏指令組成。偽指令不像機器指令那樣在處置器運轉期間由機器執行,而是在對源程序進展匯編期間由匯編工具處置的操作,它們可以完成如符

11、號定義、數據定義、分配存儲區、指示程序開場終了等功能。本小節只闡明一些常用的偽指令。另外,還有一些偽指令可查看相關手冊。 在前面的ARM指令集章節中,曾經接觸了幾條常用的ARM偽指令,如ADR、ADRL、LDR、NOP等。把它們和指令集一同引見是由于它們在匯編時會被適宜的機器指令替代,實現真正機器指令操作。偽指令大約可分為以下6種類型: ARM偽指令,如ADR、LDR、NOP等,本節不再反復引見。 符號定義偽指令。 段及段屬性定義偽指令。 .4.1.2 偽指令語句2 數據定義偽指令。 匯編控制偽指令。 雜項偽指令。.4.1.2.1 符號定義偽指令1 符號定義偽指令用于定義ARM匯編程序的常量、

12、標號和變量,對變量進展賦值等操作。符號定義偽指令包括EQU偽指令、變量聲明偽指令、變量賦值偽指令。變量的聲明與賦值偽指令如表4.1所示。表4.1 變量聲明與賦值偽指令.4.1.2.1 符號定義偽指令21EQU: EQU用于將程序中的數字常量、標號、基于存放器的值賦予一個等效的稱號,這一點類似于C言語中的#define,可用“*替代EQU。指令格式如下: 其中,name為要定義的常量的稱號;expr可以為數字常量、程序中的標號、32位地址常量、存放器的地址值等;type指示expr的數據類型,是可選項。例5 EQU偽指令的運用。.4.1.2.1 符號定義偽指令32變量聲明偽指令: 變量聲明偽指令

13、包括全局變量聲明偽指令和部分變量聲明偽指令。全局變量聲明偽指令包括GBLA、GBLL、GBLS,部分變量聲明偽指令包括LCLA、LCLL、LCLS。全局變量多用于程序體中,而部分變量用于宏定義體中。其中: GBLA、LCLA偽指令用于聲明一個數字變量,并將其初始化為0。 GBLL、LCLL偽指令用于聲明一個邏輯變量,并將其初始化為FALSE。 GBLS、LCLS偽指令用于聲明一個字符串變量,并將其初始化為空字符串。偽指令格式如下:.4.1.2.1 符號定義偽指令4 其中,GB/LCLA/L/S為變量聲明偽指令,可以為6個變量聲明偽指令GBLA、GBLL、GBLS、LCLA、LCLL、LCLS中

14、的任一個。variable是定義的變量名,其數據類型和作用范圍由變量聲明偽指令來確定,但變量名在其作用內必需獨一。例6 運用全局變量。例7 宏構造中運用部分變量。.4.1.2.1 符號定義偽指令53變量賦值偽指令: 變量賦值偽指令用于對已定義的全局變量或部分變量賦值,共有3條變量賦值偽指令:SETA、SETL、SETS。SETA偽指令用于給一個全局或部分的算術變量賦值。SETL偽指令用于給一個全局或部分的邏輯變量賦值。SETS偽指令用于給一個全局或部分的字符變量賦值。指令格式如下: 其中Variable_a、Variable_l、Variable_s就是前面全局變量或部分變量所定義的變量名。e

15、xpr_a為賦值的常數;expr_l為邏輯值,即TRUE或FALSE;expr_s為賦值的字符串。.4.1.2.1 符號定義偽指令6例8 給字符串變量賦值。.4.1.2.2 段及段屬性定義偽指令1 由前面分析得知,匯編言語的源程序是分段的,并且每個段都有本人的屬性,下面講述段定義和段屬性定義偽指令。該類指令引見如下: AREA:定義一個段開場。 END:整個文件終了。 ALIGN:定義邊境對齊方式。 ENTRY:定義程序入口。 CODE16:指明本段為16位Thumb代碼。 CODE32:指明本段為32位ARM代碼。例9 代碼段的例子。 .4.1.2.2 段及段屬性定義偽指令2 例9中AREA

16、偽指令定義了一個段,段名為Hello,段屬性是只讀的代碼段。 ENTRY偽指令用于指定程序的入口點。一個程序可以包含多個源文件至少要有一個ENTRY,可以有多個ENTRY,但一個源文件中最多只需一個ENTRY。 CODE32偽指令指示匯編編譯器后面的指令為32位的ARM指令。ARM9處置器支持兩種指令集:Thumb指令集和ARM指令集。其中CODE16偽指令指示匯編編譯器后面的指令為16位的Thumb指令,CODE32偽指令指示匯編編譯器后面的指令為32位的ARM指令。CODE16和CODE32偽指令只是指示匯編編譯器后面的指令的類型,偽指令本身并不進展程序形狀切換。要用BX指令操作才干進展切

17、換。 最后一條語句END偽指令用于通知編譯器曾經到了源程序的結尾。每一個匯編文件均要運用一個END偽指令指示根源程序終了。 .4.1.2.2 段及段屬性定義偽指令3下面引見略微復雜的AREA偽指令和ALIGN偽指令。1AREA偽指令: AREA偽指令用于定義一個代碼段或數據段。ARM匯編程序設計采用分段式設計,一個ARM源程序至少需求一個代碼段,而大的程序可以包含多個代碼段及數據段。偽指令格式如下: 其中sectionname為所定義的代碼段或數據段的稱號。假設該稱號是以數據開頭的,那么該稱號必需用“|括起來。attr為該代碼段或數據段的屬性。 在AREA偽指令中,各屬性之間用逗號隔開,以下為

18、段屬性及相關闡明: .4.1.2.2 段及段屬性定義偽指令4 ALIGN=expr。默許的情況下,代碼段和數據段是4字節對齊的,expr可以取031的數值,相應的對齊方式為2expr字節對齊。對于代碼段,expr不能為0或1。 ASSOC=section。指定與本段相關的ELF段。任何時候鏈接section段也必需包括sectionname段。 CODE為定義代碼段。屬性默以為READONLY。 DATA為定義數據段。屬性默以為READWRITE。 COMMON定義一個通用段。該段不包含任何用戶代碼和數據。鏈接器將其初始化為0。各源文件中同名的COMMON段共用同樣的內存單元,鏈接器為其分配適

19、宜的尺寸。 NOINIT指定本數據段僅僅保管了內存單元,而沒有將各個初始值寫入內存單元,或者將內存單元值初始化為0。 .4.1.2.2 段及段屬性定義偽指令5 READONLY指定本段為只讀,代碼段的默許屬性為READONLY。 READWRITE指定本段為可讀可寫,數據段的默許屬性為READWRITE。 運用AREA偽指令將程序分為多個ELF格式的段,段稱號可以一樣,這時同名的段被放在同一個ELF段中。例10 聲明了代碼段Example1,只讀,并且4字節對齊。 .4.1.2.2 段及段屬性定義偽指令62ALIGN偽指令: ALIGN偽指令可經過添加填充字節的方式,使當前位置滿足一定的對齊方

20、式。偽指令格式如下: 其中,表達式的值用于指定對齊方式。能夠的取值為2的冪,如1、2、4、8、16等,不能為0。假設偽指令中沒有指定表達式,那么編譯器會將當前位置對齊到下一個字的位置。偏移量也為一個數字表達式,假設運用該字段,那么當前位置的對齊方式為:2的表達式次方+偏移量。 ALIGN=expr:對齊方式為2expr,如expr=3,那么對齊方式為8字節對齊。表達式的取值范圍為031。.4.1.2.3 數據定義偽指令1 數據定義偽指令用于數據表定義、文字池定義、數據空間分配等,同時也可完成已分配存儲單元的初始化。該類偽指令有許多,這里只詳細引見如下常用的偽指令,感興趣的可參考相關手冊。 聲明

21、一個文字池:LTORG。 定義一個構造化的內存表的首地址:MAP。 定義構造化內存表中的一個數據域:FIELD。 分配一塊內存空間,并用0初始化:SPACE。 分配一段字節的內存單元,并用指定的數據初始化:DCB。 分配一段半字的內存單元,并用指定的數據初始化:DCW。 分配一段字的內存單元,并用指定的數據初始化:DCD。 分配一段雙字的內存單元,并用指定的數據初始化:DCQ。.4.1.2.3 數據定義偽指令21LTORG: LTORG用于聲明一個文字池,在運用LDR偽指令時,要在適當的地址參與LTORG聲明文字池,這樣就會把要加載的數據保管到文字池內,再用ARM的加載指令讀出數據假設沒有運用

22、LTORG聲明文字池,那么匯編器會在程序末尾自動聲明。偽指令格式如下:例11 文字池舉例。.4.1.2.3 數據定義偽指令3 LTORG偽指令常放在無條件跳轉指令之后,或者子程序前往指令之后,這樣處置器就不會錯誤地將文字池中的數據當作指令來執行。.4.1.2.3 數據定義偽指令42MAP: MAP偽指令用于定義一個構造化的內存表的首地址。此時內存表的位置計數器VAR設置為該地址值。VAR為匯編器的內置變量。MAP也可用“替代。偽指令格式如下: 其中,expr為程序中的標號或數字表達式。base_register基址存放器為可選項,當base_register選項不存在時,expr的值即為內存表

23、的首地址,當該選項存在時,內存表的首地址為expr的值與base_register的和。.4.1.2.3 數據定義偽指令5例12 MAP指令。 MAP偽指令通常與FIELD偽指令配合運用來定義構造化的內存表。但MAP、FIELD偽指令僅僅是定義數據構造,它們并不初始化內存單元的內容。MAP偽指令中的base_register存放器的值對于其后一切的FIELD偽指令定義的數據域是默許運用的,直到遇到新的包含base_register項的MAP偽指令。 .4.1.2.3 數據定義偽指令63FIELD: FIELD偽指令用于定義一個構造化內存表中的數據域。FIELD也可用“#替代。偽指令格式如下:

24、其中label為數據域標號,expr表示本數據域在內存表中所占的字節數。FIELD偽指令常與MAP偽指令配合運用來定義構造化的內存表。MAP偽指令定義內存表的首地址,FIELD偽指令定義內存表中的各個數據域,并可以為每個數據域指定一個標號供其他的指令援用。例13 MAP和FIELD偽指令的運用。.4.1.2.3 數據定義偽指令7例13 MAP和FIELD偽指令的運用。.4.1.2.3 數據定義偽指令84SPACE: SPACE用于分配一塊內存單元,并用0初始化。%與SPACE同義。偽指令格式如下: 其中,label為內存塊起始地址標號,expr為所要分配的內存字節數。例14 為Buf變量懇求空

25、間。.4.1.2.3 數據定義偽指令95DCB、DCW、DCD、DCQ: 這4條偽指令都是用于分配一段內存單元,并對該內存單元初始化。獨一的區別是它們分配內存單元的大小不同。這一類偽指令的格式是: 其中標號label字段是可有可無的,它表示分配的內存起始地址,作用與指令語句前的標號一樣。Operand為操作數,即內存單元的初始化數據。 助記符Mnemonic字段闡明所用偽指令的助記符,常用的有以下幾種: DCB分配一段字節的內存單元,其后的每個操作數都占有一個字節,操作數可以為-128255的數值或字符串。 .4.1.2.3 數據定義偽指令10 DCW分配一段半字的內存單元,其后的每個操作數都

26、占有兩個字節,操作數是16位二進制數,取值范圍為-3276865535。 DCD分配一段字的內存單元,其后的每個操作數都占有4個字節,操作數可以是32位的數字表達式,也可以是程序中的標號由于程序中的標號代表地址,也是32位二進制數值。 DCQ分配一段雙字的內存單元,其后的每個操作數都占有8個字節。例15 分配內存單元舉例操作數可以是常數,或者是表達式。.4.1.2.3 數據定義偽指令11 匯編程序在匯編期間對存儲器進展內存分配,分配結果如圖4.1所示,其中Addr代表一個隨機分配的內存地址。圖4.1中保管的數據都用十六進制表示,其中DCB偽指令定義的每個數據占用一個字節空間,DCW偽指令定義的

27、每個數據占用兩個字節空間,DCD偽指令定義的每個數據占用4個字節空間。.4.1.2.3 數據定義偽指令12圖4.1 例15內存分配表示圖 .4.1.2.3 數據定義偽指令13例16 分配內存單元舉例操作數也可以是字符串。 例16的內存分配結果如圖4.2所示,用了10個字節空間保管這個字符串,標號MESSAGE指向該內存塊的第一個地址。 .4.1.2.3 數據定義偽指令14圖4.2 例16內存分配表示圖 .4.1.2.3 數據定義偽指令15例17 向量中斷表操作數還可以是程序中的標號。.4.1.2.4 匯編控制偽指令1 匯編控制偽指令用于條件匯編、宏定義、反復匯編控制等。該類偽指令如下: 宏定義

28、:MACRO和MEND。 條件匯編控制:IF,ELSE和ENDIF。 反復匯編:WHILE和WEND。.4.1.2.4 匯編控制偽指令21宏定義偽指令MACRO和MEND: 宏定義偽指令包括MACRO、MEND、MEXIT。MACRO定義一個宏語句段的開場,MEND定義宏語句段的終了,MEXIT可以實現從宏程序段的跳出。用MACRO及MEND定義的一段代碼稱為宏定義體。在程序中就可以經過宏指令多次調用該代碼段。指令格式如下:.4.1.2.4 匯編控制偽指令3宏定義指令格式闡明如下: 標號:是可選項。當宏定義被展開時,可被交換成相應的符號,通常為一個標號,在一個符號前運用$表示被匯編時將運用相應

29、的值替代$后的符號。 宏名:所定義的宏的稱號。宏調用是經過調用宏的稱號來實現的。 參數:宏指令的參數。當宏指令被展開時將被交換成相應的值,類似于函數的方式參數。參數是可選項,可以有多個。 宏是一段功能完好的程序,可以實現一個特定的功能,在運用中可以把它視為一個子程序。在其他程序中可以調用宏來完成某個功能。調用宏是經過調用宏的稱號來實現的。在源程序被編譯時,匯編器將宏調用展開。用宏定義中的指令序列交換程序中宏名的調用,并將實踐參數的值傳送給宏定義中的參數。.4.1.2.4 匯編控制偽指令4 宏與子程序的區別,在于調用宏時編譯程序會在調用途插入宏的程序段,有多少次調用就會插入多少宏的程序段;而調用

30、子程序不添加新的程序段。調用宏的益處是不占用傳送參數的存放器,不用維護現場,但假設多次調用宏,那么無形中添加了代碼量。例18 宏運用舉例。.4.1.2.4 匯編控制偽指令5 上例定義了MAX宏,宏語句段的功能是完成將兩個參數date和time保管到起始地址為0 x1000的內存單元中。倒數第二條語句是宏調用語句,編譯后該條語句會展開。整個語句段編譯后的程序如下:.4.1.2.4 匯編控制偽指令62條件匯編控制偽指令IF、ELSE和ENDIF: IF,ELSE和ENDIF偽指令可以根據條件把一段代碼包括在匯編程序內或將其排除在程序之外。 與IF同義,|與ELSE同義,與ENDIF同義。偽指令格式

31、如下: 其中,logical_expr為用于控制的邏輯表達式。假設條件成立,那么代碼段1落在匯編源程序中有效。假設條件不成立,代碼段1無效,同時假設運用ELSE偽指令,代碼段2有效。.4.1.2.4 匯編控制偽指令7例19 條件匯編控制舉例。留意:IF,ELSE和ENDIF偽指令是可以嵌套運用的。 .4.1.2.4 匯編控制偽指令83反復匯編偽指令WHILE和WEND: WHILE和WEND偽指令用于根據條件反復匯編一樣的或幾乎一樣的一段源程序。偽指令格式如下: 其中,logical_expr為用于控制的邏輯表達式。假設條件成立,那么代碼段在匯編程序中有效,并不斷反復這段代碼直到條件不成立。例

32、20 反復匯編舉例。.4.1.2.4 匯編控制偽指令9例20 反復匯編舉例。留意:WHILE和WEND偽指令是可以嵌套運用的。 .4.1.2.5 雜項偽指令1雜項偽指令引見如下: 導出偽指令:EXPORT、GLOBAL。 導入偽指令:IMPORT、EXTERN。 文件包含偽指令:GET、INCLUDE。 一個程序可以由多個匯編源文件組成,多個文件間會相互援用符號變量或標號。當在一個源文件中定義的一個符號希望其他文件援用時,那么必需用導出偽指令定義這個符號;假設這個文件援用了外部定義的符號,那么必需用導入偽指令定義這個符號。.4.1.2.5 雜項偽指令21導出偽指令EXPORT和GLOBAL:

33、EXPORT聲明一個符號可以被其他文件運用,相當于聲明一個全局標號,可以被其他文件援用。GLOBAL與EXPORT一樣。偽指令格式如下: 其中,標號為要聲明的符號稱號。, WEAK為可選項,聲明其他文件有同名的標號時,那么該同名標號優先于該標號被援用。例21 定義全局標號InitStack和Vectors。.4.1.2.5 雜項偽指令3例21 定義全局標號InitStack和Vectors。.4.1.2.5 雜項偽指令42導入偽指令IMPORT和EXTERN: IMPORT通知編譯器這個標號要在當前源文件中運用,但標號是在其他的源文件中定義的。不論當前源文件能否運用過該標號,這個標號都會參與到

34、當前源文件的符號表中。EXTERN和IMPORT同義,都是聲明一個外部符號。偽指令格式如下: 其中,標號為要聲明的符號稱號。, WEAK為可選項,表示假設一切的源文件都沒有找到這個標號的定義,編譯器也不會提示錯誤信息。 .4.1.2.5 雜項偽指令5 運用IMPORT或EXTERN聲明外部標號時,假設鏈接器在鏈接處置時不能解釋該符號,而偽指令中沒有WEAK選項,那么鏈接器會報告錯誤;如偽指令中有WEAK選項,那么鏈接器不會報告錯誤,而是進展下面的操作: 假設該符號被B或者BL指令援用,那么該符號被設置成下一條指令的地址,該B或BL指令相當于一條NOP指令。 其他情況下該符號被設置為0。例22

35、導入標號InitStack和Vectors。.4.1.2.5 雜項偽指令63文件包含偽指令GET和INCLUDE: GET偽指令將一個源文件包含到當前的源文件中,并將被包含的源文件在當前位置展開進展匯編處置。INCLUDE與之同義。偽指令格式如下: 我們通常這樣運用這個偽指令:在某源文件中定義一些宏指令用MAP和FIELD定義構造化的數據類型,用EQU定義常量的符號稱號,然后用GET/INCLUDE將這個源文件包含到其他的源文件中。這樣的源文件類似于C言語中的頭文件,GET/INCLUDE不能用于包含目的文件,包含目的文件那么需求運用INCBIN偽指令。例23 包含inc文件。.4.1.3 匯

36、編言語程序設計及舉例 程序有順序、分支、循環和子程序4種構造方式。順序程序構造是指完全按順序逐條執行的指令序列,這在程序段中是大量存在的,但作為完好的程序那么很少見。本小節將引見順序、分支、循環和子程序這4種構造。.4.1.3.1 順序程序設計1 最簡單的程序是沒有分支、沒有循環的順序運轉程序。下面以一個算術運算程序進展闡明和引見。 例24 經過查表操作實現數組中的第1項數據和第5項數據相加,結果保管到數組中。程序中首先讀取數組的第1項數據,然后讀取第5項數據,之后將結果相加,最后保管結果,整個流程是順序執行的,如圖4.3所示。圖4.3 程序流程圖.4.1.3.1 順序程序設計2程序清單4.1

37、 例24的程序.4.1.3.2 分支程序設計1 在一個實踐的程序中,程序一直是順序執行的情況并不多見,通常都會有各種分支。在ARM匯編程序中,條件后綴能實現程序分支。例25 編寫匯編程序實現C言語if else分支程序。C言語程序如下分支流程圖如圖4.4所示:圖4.4 分支流程圖.4.1.3.2 分支程序設計2圖4.4 分支流程圖 設R0保管變量x的值,R1保管變量y的值,R2保管變量z的值x、y、z均為無符號整數,對應實現的匯編程序見程序清單4.2。程序清單4.2 例25的匯編程序.4.1.3.2 分支程序設計3程序清單4.2 例25的匯編程序 B和BL指令可以實現分支,程序清單4.3展現了

38、B指令實現匯編程序中常用的散轉算法。程序清單4.3 B指令實現散轉功能.4.1.3.2 分支程序設計4程序清單4.3 B指令實現散轉功能.4.1.3.3 循環程序設計1 在程序中,往往要求某一段程序反復執行多次,這時就可以利用循環程序構造。一個循環構造由以下兩部分組成。 循環體:就是要求反復執行的程序段部分。 循環終了條件:在循環程序中必需給出循環終了條件,否那么程序就會進入死循環。常見的循環有計數循環和條件循環。計數循環是當循環了一定次數后就終了循環;條件循環是當循環條件為假時就終了循環。 在C言語中,for和while語句可以實現這兩種循環。下面引見如何用匯編言語實現這兩種循環。例26 編

39、寫匯編程序實現計數循環。計數循環用C言語表達如下:.4.1.3.3 循環程序設計2設R0為x,R2為ii、x均為無符號整數,匯編言語程序段如下:例27 編寫匯編程序實現條件循環。條件循環用C言語表達如下:.4.1.3.3 循環程序設計3設x為R0,y為R1x、y均為無符號整數,匯編言語程序段如下: 例28 編寫循環語句實現數據塊復制。.4.1.3.3 循環程序設計4例28 編寫循環語句實現數據塊復制。.4.1.3.4 子程序設計1 在一個程序的不同部分往往要用到類似的程序段,這些程序段的功能和構造方式都一樣,只是某些變量的賦值不同,此時就可以把這些程序段寫成子程序方式,以便需求時可以調用它。

40、調用程序在調用子程序時,經常需求傳送一些參數給子程序;子程序運轉完后也經常要回送結果給調用程序。這種調用程序和子程序之間的信息傳送稱為參數傳送。參數傳送可以有以下兩種方法: 當參數比較少時,可以經過存放器傳送參數。 當參數比較多時,可以經過內存塊或堆棧傳送參數。 子程序的正確執行是由子程序的正確調用及正確前往保證的。這就要求調用程序在調入子程序時必需保管正確的前往地址,即當前PC值由于ARM流水線特性.4.1.3.4 子程序設計2,能夠要減去一個偏移量。PC值可以保管在公用的鏈接存放器R14中,也可以保管到堆棧中。根據這兩種情況,可以在子程序中采用如下的前往語句: 運用堆棧來恢復處置器的形狀時

41、,留意STMFD與LDMFD要配合運用。 普通來講,在ARM匯編言語程序中,子程序的調用是經過BL指令來實現的。該指令在執行時完成如下操作:將子程序的前往地址存放在鏈接存放器LR中針對流水線特性,曾經減去偏移量了,同時將程序計數器PC指向子程序的入口點,當子程序執行終了需求前往調用途時,只需求將存放在LR中的前往地址重新拷貝給程序計數器PC即可。 .4.1.3.4 子程序設計3 下面看一段C代碼。程序實現比較兩個數,取出其最大值。實現求最大值功能的C言語代碼見程序清單4.4的MAX函數,對應的匯編代碼見程序清單4.5的MAX標號。在程序清單4.5中,子程序和調用主程序之間經過存放器R0、R1、

42、R2傳送參數。程序清單4.4 MAX函數和調用主程序.4.1.3.4 子程序設計4程序清單4.5 MAX匯編子程序和調用主程序.4.2 C言語程序設計 C/C+言語是一種高級言語,運用它可以快速地編寫運用程序。用C/C+言語編寫的程序,一條語句可以替代多條匯編言語語句。運用高級言語進展程序設計有以下優點: 有豐富的程序庫支持。 程序容易編寫,具有構造化特點。 程序可讀性強,容易修正。 便于調試和維護。 可移植性好。 對于大中型工程來說,用C/C+言語編寫軟件其開發周期和開發本錢通常要小于匯編言語。所以在做工程時,普通會提倡用C/C+言語編寫代碼。但C言語程序設計的思想與方法不是本節重點,本節只

43、討論以下3方面的內容:. 運用Semihosting效力建立C言語調試環境。 移植ADS1.2中的C言語函數庫。 C程序編寫規范。 如對ADS1.2集成開發環境不太熟習,請參閱相關章節做輔助閱讀。.4.2.1 運用Semihosting的效力1 在C程序設計中,經常要用到C規范庫中提供的一些輸入/輸出函數,如printf()、scanf()、putchar()、getchar()等函數。這些I/O函數能顯示程序運轉結果,接納按鍵輸入,極大方便了程序的運轉與調試。在ADS1.2環境中,利用Semihosting效力功能,可以很方便地調用ADS1.2中的C言語函數庫,包括根本的輸入/輸出函數。 在

44、ADS的C言語函數庫中,某些ANSIC的功能是由主機的調試環境來提供的,這套機制有一個專門術語叫Semihosting。例如,Semihosting技術能將運用程序中的I/O懇求經過一定的通道傳送入主機的調試環境,由主機的調試環境來實現數據的輸入/輸出。 .4.2.1 運用Semihosting的效力2 Semihosting的效力功能是經過一組軟件中斷SWI指令來實現的。當一個Semihosting軟中斷被執行時,調試系統先識別這個SWI懇求,然后掛起正在運轉的程序,調用Semihosting的效力,完成后再恢復原來的程序執行。因此,主機執行的義務對于程序來說是透明的。 下面引見一個運用Se

45、mihosting的C言語程序例如,并對Semihosting進展簡單的分析。 .4.2.1.1 C言語程序例如1 1啟動ADS1.2,運用ARM Executable Image工程模板建立一個工程Semihosting。2建立源文件UseLib.c,編寫實驗程序,如圖4.5所示,然后添加到工程Semihosting中。圖4.5 建立源文件UseLib.c .4.2.1.1 C言語程序例如2 圖4.5 建立源文件UseLib.c .4.2.1.1 C言語程序例如3 3編譯鏈接工程,選擇ProjectDebug命令,啟動AXD進展仿真調試。4在AXD調試環境中選擇OptionConfigure

46、 Target命令,彈出Choose Target對話框,如圖4.6所示,選擇ARMUL仿真環境。圖4.6 選擇ARMUL仿真環境.4.2.1.1 C言語程序例如4 5選擇FileLoad Image命令,找到Semihosting工程中的映像文件Semihosting. axf,把映像文件加載到AXD環境中,加載后如圖4.7所示。圖4.7 把映像文件加載到AXD環境中.4.2.1.1 C言語程序例如5 圖4.7 把映像文件加載到AXD環境中.4.2.1.1 C言語程序例如6 6按F5鍵運轉,程序會在main()函數斷點處停頓,如圖4.8所示。圖4.8 按F5鍵運轉.4.2.1.1 C言語程序

47、例如7 7繼續運轉,輸出第一條語句的結果,如圖4.9所示。圖4.9 輸出第一條語句的結果8輸入一個大寫字符H,輸出結果如圖4.10所示。圖4.10 輸出結果.4.2.1.1 C言語程序例如8 9繼續運轉,最后程序終了,程序指針停留在“SWI 0 x123456語句處,如圖4.11所示。圖4.11 程序指針停在“SWI 0 x123456語句處.4.2.1.2 機器代碼分析1 圖4.12和圖4.13分別是映像文件反匯編后的部分匯編代碼,程序指針首先從_main函數處執行,執行一段程序后再執行main函數,其中main函數才是用戶本人編輯的程序。圖4.12 _main函數.4.2.1.2 機器代碼

48、分析2 圖4.13 main函數 在程序中運用庫函數,必需先建立一個庫函數可以執行的環境。從反匯編代碼中,可以看出程序在執行main函數前執行了一長串的程序,這些程序是ADS編譯器自動給用戶程序加上去的。.4.2.1.2 機器代碼分析3 ADS編譯器在編譯C言語程序時,假設程序中運用了main函數,那么編譯器將自動添加額外代碼,完成初始化堆棧和C庫等任務。生成的映像文件執行如下的任務流程。1將執行文件中的RO段和RW段從加載域地址load address復制到執行域地址execution address。2初始化ZI區域,用0來初始化變量。3跳轉到_rt_entry,執行如下4個調用: 調用_

49、rt_stackheap_init,建立程序的堆和棧。 調用_rt_lib_init,初始化程序用到的C庫,并為main傳送參數。 調用main,即用戶程序的入口。 調用exit,退出Semihosting。.4.2.1.2 機器代碼分析4 總結以上流程,ADS初始化過程如圖4.14所示??傮w上,初始化過程可以分為兩部分來看: _main擔任設置運轉映像存儲器映射,_rt_entry擔任庫函數的初始化。 _main完成代碼和數據的復制,并把ZI數據區清零。這一步只需當代碼和數據區在存儲和運轉時處于不同的存儲器位置時才有意義。接著_main跳進_rt_entry,進展STACK棧和HEAP堆等的

50、初始化。最后_rt_entry跳進運用程序的入口main()。當運用程序執行完時,_rt_entry又將控制權交還給調試器。圖4.14 ADS初始化過程.4.2.1.2 機器代碼分析5 圖4.14 ADS初始化過程.4.2.1.2 機器代碼分析6 函數main()在ADS中有特殊的意義。當一個程序工程工程中存在main()時,鏈接器會把_main和_rt_entry中的初始化代碼鏈接進來;假設沒有main()函數,初始化過程就不會被鏈接,結果就會導致一些規范的C庫函數無效。.4.2.2 移植ADS1.2中的C言語函數庫 1 1Semihost調用C言語函數庫的機制: 開發一個嵌入式運用軟件時,

51、ADS用戶能夠并不完全清楚目的硬件的一些參數目的。如有關外設、存儲器地址分布,甚至處置器類型等一些細節,都還沒有最終確定。為了在一切這些細節全部就緒前就能進展軟件開發,ADS工具有一套程序構建和調試的默許設置,使得用戶能在Semihost效力的支持下完成軟件的調試與開發。 在Semihost效力的支持下,開發人員可以很透明地調用C言語函數庫中的一切函數。從概念上來講,C言語函數可以被分成兩部分,一是ANSIC言語規范本身的一部分,一是只受某一特定ANSIC層次支持的函數,如圖4.15所示。其中,一些ANSIC的功能是由主機調試環境調用驅動程序級的函數完成的。例如,ADS的庫函數.4.2.2 移

52、植ADS1.2中的C言語函數庫 2 printf()把輸出信息輸出到調試器的控制臺窗口,這個功能經過調用_sys_write()實現。_sys_write()執行了一個把字符串輸出到主機控制臺的Semihosting軟中斷效力程序。 圖4.15 C言語庫函數構造.4.2.2 移植ADS1.2中的C言語函數庫 3 假設用戶在程序編譯時沒有指定映像的存儲器映射分布,ADS將為生成的目的代碼和數據分配一個默許的存儲器映射圖,如圖4.16所示。 圖4.16 存儲器映射.4.2.2 移植ADS1.2中的C言語函數庫 4 目的映像被鏈接至地址0 x8000,存儲和執行區域都位于該地址開場的空間。RO只讀部

53、分放在前面,接著是RW讀寫部分,最后是ZI零初始化部分。 在ZI部分之上緊跟著HEAP,所以HEAP確實切地址要在鏈接時才干確定。 STACK的基地址是在運用程序啟動時由一個Semihosting操作調用_rt_stackheap_init函數提供。.4.2.2 移植ADS1.2中的C言語函數庫 5 2根據目的環境移植C庫函數: 默許形狀下C庫函數利用Semihosting機制來實現設備驅動的功能。但一個真正的嵌入式系統要運用到詳細的外設或硬件獨立于主機環境運轉。 規范庫中包含了部分依賴于ARM Semihosted執行環境的函數,需求重新實現這部分函數。假設在程序中定義這些函數,那么編譯器就

54、會運用新定義的函數,這個過程叫做重定向C言語庫函數,如圖4.17所示。普通情況下,只需求重新定義很少的幾個函數就可以運用C庫。圖4.17 重定向C言語庫函數.4.2.2 移植ADS1.2中的C言語函數庫 6 圖4.17 重定向C言語庫函數.4.2.2 移植ADS1.2中的C言語函數庫 7 舉例來說,規范I/O庫中最常用的是printf系列函數,包括_printf()、printf()、_fprintf()、fprintf、_vprintf()和vprintf()。一切這些函數非透明地運用_FILE,并且僅依賴于fputc()和ferror()兩個函數。只需定義了本人的_FILE版本和fputc

55、()、ferror()函數,外加定義一個具有FILE類型的_stdout變量,就可以不作任何修正地運用printf系列、fwrite()、fputc()和puts函數了。 假設用戶有一個I/O設備如UART。本來庫函數fputc()是把字符輸出到調試器控制窗口中去的,但用戶把輸出設備改成UART端口,這樣一來,一切基于fputc()函數的printf()系列函數輸出都被重定向到UART端口上。下面是實現fputc()重定向的例子: .4.2.2 移植ADS1.2中的C言語函數庫 8 下面是實現fputc()重定向的例子: 這個例子簡單地將輸入字符重新定向到另一個函數sendchar(),sen

56、dchar()假定是一個另外定義的串口輸出函數。在這里,fputc()就好象目的硬件和規范C庫函數之間的一個籠統層。 .4.2.3 編程規范1 嵌入式系統的開發是一項艱巨復雜的工程,涉及很多方面的知識,系統的開發必需注重團隊協作。所謂“沒有規范,不成方圓,系統開發前必需建立一個編程規約,且該規約應貫穿整個開發一直。采用一致的規約能提高程序的可了解性,提高勞動消費率,并使開發軟件便于維護。 用C言語或其他言語編寫程序的風格有很多種。慣用的、好的書寫風格都具有如下的效果: 易看懂,干凈整齊,可了解性好,可維護性好。 具備良好的兼容性,可移植性好。 無論采用什么樣的編程風格,必需在整個開發過程中采用

57、一直如一的格式。對于一項大工程的整個團隊,建議運用一致的編程風格,可以少發生一些程序維護中令人頭疼的事情,降低維護本錢;采用一致的書寫方法,有助于防止代碼的反復編寫。這里展現的C言語程序.4.2.3 編程規范2 的編程風格,主要強調如何使源碼便于閱讀與維護。 為了加強程序的可了解性、可維護性,可以從4個方面去思索:恰當完備的注釋、一致合理的命名規那么、美觀良好的編輯風格和組織有序的文件構造。.4.2.3.1 恰當完備的注釋1 恰當完備的注釋有利于加強對程序文件或代碼的了解,但是不規范、胡亂的注釋反而妨礙人們對程序的了解。怎樣的注釋才是有效的注釋呢,這樣的注釋至少要遵照以下規那么: 注釋該當準確

58、。注釋的位置應與被描畫的代碼相鄰,并盡量防止在注釋中運用縮寫,不要讓人對代碼產生誤解;錯誤的注釋不但無益反而有害,修正代碼的同時修正相應的注釋,以保證注釋與代碼一致。 注釋該當簡明扼要。重要復雜的代碼段或代碼行一定要有注釋,注釋要簡明扼要。沒有用的注釋要刪除。假設代碼本來就是清楚的,那么不用加注釋。否那么多此一舉,令人膩煩。 注釋不可喧賓奪主。注釋是對代碼的“提示,但是過多的、花哨的注釋會讓人眼花繚亂,反而妨礙了了解。.4.2.3.1 恰當完備的注釋2 注釋主要出如今文件頭和程序代碼中。文件頭的注釋用于版本、版權聲明;程序代碼中的注釋用于函數接口闡明、重要的段落提示和重要的代碼行提示,如例29

59、所示。 普通來講,C/C+言語的注釋符有兩種,分別為“/*/和“/。通常情況下,程序塊的注釋采用“/*/,行注釋那么采用“/。例29 注釋范例。.4.2.3.1 恰當完備的注釋3 例29 注釋范例。.4.2.3.2 一致合理的命名規那么1 比較著名的命名規那么當推Microsoft公司的“匈牙利法,該命名規那么的主要思想是“在變量和函數名中參與前綴以增進人們對程序的了解。例如,一切的字符變量均以ch為前綴,假設是指針變量那么追加前綴p。假設一個變量由ppch開頭,那么闡明它是指向字符指針的指針。 “匈牙利法最大的缺陷是繁瑣,如下定義:倘假設采用“匈牙利命名規那么,那么該當寫成: 如此繁瑣的程序

60、會讓絕大多數程序員無法忍受。 .4.2.3.2 一致合理的命名規那么2 據調查,沒有一種命名規那么可以讓一切的程序員贊同,程序設計教科書普通都不指定命名規那么。命名規那么對軟件產品而言并不是“成敗悠關的事,我們不要花太多精神試圖發明世界上最好的命名規那么,而該當制定一種令大多數工程成員稱心的命名規那么,并在工程中貫徹實施。不論采取什么樣的命名規那么,至少要遵照以下3條規那么。1命名直觀明了: 標識符該當直觀且可以拼讀,可望文知義,不用進展“解碼。標識符最好采用英文單詞或其組合,便于記憶和閱讀。標識符通常采用“大小寫混排的方式,如AddView。切忌運用漢語拼音來命名。程序中的英文單詞普通不會太

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論