arm匯編編程(示例)

ARM 258瀏覽

一、arm的認知及基本概念

().arm的基本概念

1. 什么是arm

   arm是一家英國電子公司的名字,全名是Advanced RISC Machine

   這家企業設計了大量高性能、廉價、耗能低的RISC(精簡指令集)處理器,ARM公司只設計芯片而不生產,它將

技術授權給世界上許多公司和廠商。目前采用arm技術知識產權內核的微處理器,即通常所說的arm微處理器

   

   所以arm也是對一類微處理器的通稱。

   

   arm指令集體系版本號(軟件)為V1 ~ V7 目前V1 ~ V3已很少見。從V4版不再與以前的版本兼容。

   armCPU系列(硬件)主要有 ARM7 ~ ARM11

   

   

2. 典型的嵌入式處理器

   arm        占市場79.5%  ARM

   mips       占市場13.9%  MIPS

   microSPARC 占市場3.1%   SUN 

   PowerPc    占市場2.8%   IBM

   其它       占市場0.8%

   

3. arm的應用范圍:

   工業控制:如機床、自動控制等

   無線通信:如手機 

   網絡應用:如

   電子產品:如音視頻播放噐、機頂盒、游戲機、數碼相機、打印機

   其它各領域:如軍事、醫療、機器人、智能家居等

4.計算機體系結構

  見圖:馮.諾依曼計算機體系圖

  

  馮.諾依曼體系結構

     處理器使用同一個存儲器,經由同一個總線傳輸

     完成一條指令需要3個步驟:即取指令->指令譯碼->執行指令

     指令和數據共享同一總線的結構

  哈佛體系結構

     將程序指令存儲和數據存儲分開

     中央處理器首先到程序指令存儲器中讀取程序指令。解碼后到數據地址,再到相應的數據存儲器讀取數據,然后執行指令

     程序指令存儲與數據存儲分開,可以使指令和數據有不同的數據寬度。   

5.復雜指令集與精簡指令集

  CISC 復雜指令集:采用馮.諾依曼體系結構。數據線和指令線分時復用(只能通過一輛車)。

       存儲器操作指令多 匯編程序相對簡單 指令結束后響應中斷 CPU電路豐富 面積大功耗大

  

  RISC 精簡指令集:采用哈佛體系結構。數據線和指令線分離(同時能通過多輛車)。

       對存儲器操作有限 匯編程序占空間大 在適當地方響應中斷 CPU電路較少 體積小功耗低

       ARM采用RISC精簡指令集  

  ThumbARM體系結構中一種16位的指令集。

       從ARMv4T之后,的ARM處理器有一種16-bit指令模式,叫做Thumb,較短的指令碼提供整體更佳的編碼密度,更有效地使用有限的內存帶寬。所有 ARM9 和后來的家族,包括 XScale 都納入了 Thumb 技術。

       即ARM有兩種指令集:RISCThumb

6. arm的思想

1) arm體系的總思想:

   在不犧牲性能的同時,盡量簡化處理器。同時從體系結構上靈活支持處理器擴展。采用RISC結構。RISC處理器簡化了處理器結構,減少復雜功能指令的同時,提高了處理器速度。

   ARMMIPS都是典型的RISC處理器

2) arm的流水線結構

   

   arm處理器使用流水線來增加處理器指令流的速度,這樣可以使幾個操作同時進行。并使處理和存儲器系統連續操作。

   arm處理器分為三級:取指->譯碼->執行

      取指:指令從存儲器中取出

      譯碼:對指令使用的寄存器進行譯碼

      執行:從寄存器組中讀取寄存器,執行移位和ALU操作,寄存器被寫回到寄存器組中

   

3) ARM處理器支持的類型

      字節  8

      半字  16

      字    32

      

      **所有數據操作都以字為單位

      **ARM指令的長度剛好是一個字,Thumb指令長度剛好是半個字

  

4) ARM處理器狀態

      ARM處理器內核使用ARM結構,該結構包含32位的ARM指令集和16Thumb指令集,因此ARM有兩種操作狀態

      ARM狀態:32

      Thumb狀態:16

  

5) 處理器模式

      ARM處理器共有7種運行模式:

      用戶:  正常程序工作模式,不能直接切換到其它模式

      系統:  用于支持操作系統的特權任務,可以直接切換到其它模式

      快中斷:支持高速數據傳輸及通道處理,FIQ異常響應時進入此模式

      中斷:  用于通用中斷處理,IRQ異常響應時進入此模式

      管理:  操作系統保護代碼,系統復位和軟件中斷響應時進入此模式

      中止:  用于支持虛擬內存或存儲器保護,用于MMU

      未定義:支持硬件協處理器的軟件仿真,未定義指令異常響應時進入此模式。

()、經典平臺硬件組成

    見圖:arm硬件組成圖

    開發板一般是由一塊組成的,有核心器件和外圍器件接口等,但是有的是由兩塊板子組成,主版和核心板,主版上主要是外圍接口,外圍器件等,核心板上主要是核心器件,還有一些晶振電路等 

      

  1.核心板(天嵌 2440)

    

    CPU處理器  S3C2440AL,主頻400MHz(最高可達533MHz) 

    SDRAM內存  板載64MB SDRAM(標準配置), 32bit數據總線SDRAM時鐘頻率高達100MHz(支持運行133MHz

    Nand Flash 板載64MB Nand Flash256MB Nand Flash(標準配置)

    Nor Flash  板載2MB Nor Flash(最高可升級到8MB

    CorePower  專業1.25V核心電壓供電

    Power      核心板采用3.3V供電 

    Powerled   核心板電源指示燈 

    核心板接口 接口型號為DC-2.0雙列直插

    SDRAM:隨機存儲器,普遍使用的內存。用作主存。

    NOR Flash和 NAND Flash是現在市場上兩種主要的非易失閃存。

          NOR的特點是芯片內執行,應用程序可以直接在flash 閃存內運行,不必再把代碼讀到系統RAM中。

          NAND結構能提供極高的單元密度,可以達到高存儲密度,并且寫入和擦除的速度也很快。

  

  2.主板

     電源

     并口線

     復位

     RTC電源

     RS232電平轉換DB9插座

     音頻IIS,AC97

     按鍵、PS/2IC接口

     數碼管

     觸摸屏

     以太網卡

     主USB HUB 14

  3.寄存器 

    見圖:ARM模塊和內核框圖

    寄存器是中央處理器內的組成部份。

    寄存器是有限存貯容量的高速存貯部件,用來暫存指令、數據和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器(PC)。在中央處理器的算術及邏輯部件中,包含的寄存器有累加器(ACC)

    IR 用于存儲指令

    PC 用于存儲程序運行的地址(即當前指令在內存中的位置)

    寄存器是由一個指令的輸出或輸入可以直接索引到的暫存器群組。所有的計算機指令都是進入寄存器后被直接讀取

    ARM的匯編編程,本質上就是針對CPU寄存器的編程。

    

    //*******重點  需要背訟*************************************************

    ARM寄存器分為2類:普通寄存器和狀態寄存器

   

      (1)通用寄存器和計數器:共32個,15個通用寄存器

         R0 -R7 未備份寄存器

                R0(a1)  R1(a1) R2(a3) R3(a4) R4(v1) R5(v2) R6(v3) R7(v4) 

         R8 -R12 備份寄存器

                R8(v5)  R9 (SB,v6) R10(SL,v7) R11(EP,v8) R12(IP)    數據寄存器

         R15(PC)    程序計數器 它的值是當前正在執行的指令在內存中的位置。

                    當指令執行結束后,CPU會自動將PC值加上一個單位,PC值指向下一條即將執行的指令的地址

                    如果通過匯編指令對PC寄存器賦值,就會完成一次程序的跳轉(如從子函數跳轉回主函數內)

                    

         R14(LR)    鏈接寄存器 存放子程序的返回地址

                    示例:在主函數內,如果調用子函數,程序會進入到子函數內執行。當子函數執行完畢后,需要回到

                主函數內,所以,在子函數調用前需要將這個地址先保存起來,否則無法找到這個地址。

                    LR用于保存這個地址,這個地址也稱為子程序返回地址。當子函數結束后,再將LR內的地址賦給PC即可。

                    

                    如果子程序再調用孫程序,LR如何保存地址呢?

                    先把當前LR內的值壓入內存的棧區,然后LR再保存孫程序的返回地址。當孫程序執行完后通過PC跳轉到

                子程序內,此時將棧區內的子程序返回地址取出保存在LR內。當子程序執行完后,再通過PC跳轉到主函數內。

         

         R13(SP)    棧指針寄存器 用于存放堆棧的棧頂地址。

                    SP相當于指針變量,保存的是棧頂的地址,出棧時,從SP指向的內存中取出數據,入棧時將新的內存地址

                壓入棧頂,而SP相當于鏈表的頭指針(head)

                

                    

         原則上說R0-R12可以保存任何數據。其中R0-R7用來臨時存儲數據,R8-R12  系統沒有用來做任何特殊用途,常用于中斷

         

         而在匯編與C語言的交互中,定制了ATPCS標準

            寄存器:R4-R11用來保存局部變量

            參數:  參數小于等于4,用R0-R3保存參數,參數多于4,剩余的傳入堆棧

            函數返回:結果為32位整數,通過R0返回

                      結果為64位整數,通過R0R1返回

                      對于位數更多的結果,通過內存傳遞

      (2)狀態寄存器:

         狀態寄存器用于保存程序的當前狀態

         

         CPSR   當前程序狀態寄存器

                一個寄存器為32位,每一位數據代表不同的狀態。分為三個部分(條件代碼標志位、控制位、保留區位)

                

                      31 32 29 28 .... 7 6 5 4  3  2  1  0

                      N  Z  C  V       I F T M4 M3 M2 M1 M0

                      

                      其中N Z C V稱為條件標志位(即保存的是條件的運算結果,真和假)

                      N=1 表示運算結果為負數,N=0 表示運算結果為正數。               

                      Z=1 表示運算結果為0, Z=0 表示運算結果為非零。

                      C=1 表示運算結果產生了進位。

                      V=1 運算結果的符號位發生了溢出。

                      

                      這4個位的組合,代表了各種條件,如下:

                      0000 EQ Z置位 相等/等于0

                      0001 NE Z0 不等

                      0010 CS/HS C置位 進位/無符號高于或等于

                      0011 CC/LO C0 無進位/無符號低于

                      0100 MI N置位 負數

                      0101 PL N0 非負數

                      0110 VS V置位 溢出

                      0111 VC V0 無溢出

                      1000 HI C置位且Z0 無符號高于

                      1001 LS C0Z置位 無符號低于或等于

                      1010 GE N等于V 有符號大于或等于

                      1011 LT N不等于V 有符號小于

                      1100 GT Z0N等于V
有符號大于

                      1101 LE Z置位或N不等于V 有符號小于或等于

                      1110 AL 任何狀態 總是(always

                      1111 NV 無 從不(never

                      

                      

                      IFT 稱為控制位

                      I   I=1 禁用IRO中斷

                      F   F=1 禁用FIQ中斷

                      T   表示CPU當前的狀態,代表正在Thumb指令集狀態,0表示正在ARM指令集狀態。

                      M0M4表示中斷類型(控制位內的模式位)

                      0b10000      User        用戶中斷

                      0b10001      FIQ         快速中斷

                      0b10010      IRQ         聲卡、調制解調器等外部設備產生的中斷

                      0b10011      Supervisor  管理程序或監控程序產生的中斷

                      0b10111      Abort       異常中斷 

                      0b11011      Undefined   未定義中斷

                      0b11111      System      系統中斷

          SPSR   保存的程序狀態寄存器,結構與CPSR完全一樣,用來保存CPSR的值。以便出現異常時恢復CPSR的值

      (3)流水線對pc值的影響 

         

         CPU內部的組成部分:指令寄存器,指令譯碼器,指令執行單元(包括ALU和通用寄存器組)

         CPU執行指令的步驟:取指->譯碼->執行

              取指:將指令從內存或指令cache中取入指令寄存器

              譯碼指令譯碼器對指令寄存器中的指令進行譯碼操作,辨識add,或是sub等操作

              執行:指令執行單元根據譯碼的結果進行運算并保存結果

         流水線操作:并發多條流水線(以3條為例)

         1 取指    譯碼     執行

         2         取指     譯碼    執行

         3                  取指    譯碼    執行

      

         提高時間效率

    

()、學習內容

    1.匯編(對裸板機的控制,以及驅動程序控制)

    2.內核移植(uboot移植、內核編譯、文件系統移植、應用程序移植)

    3.驅動程序編寫

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

()ADS的使用

        ADS是匯編或C語言編譯調試工具

        可生成的文件:.axf 含調試信息的可執行ELF文件

                      .bin 可燒寫的二進制映像文件    

                      .hex 可燒寫的十六進制映像文件

        ADS配置

        在磁盤中新建一個目錄 D:arm,用來存儲所寫的代碼

        

        點擊目錄:File->new,創建一個可執行的ARM映象工程

            ARM Executable Image 生成ELF格式映像(bin)

            ARM Object Library 生成armar格式目標庫文件

            Empty Project 創建不包含任何庫或源文件的工程

            Makefile Importer Wizard 用于Vc

            Thumb ARM Interworking Image 用于ARM thumb指令混合代碼生成的ELF映像

            Thumb Executable image thumb指令生成ELF格式映像

            Thumb Object Library 用于Thumb指令的代碼生成的armar格式文件

        

            選 ARM Executable Image,工程文件名2440ART   

        加源文件

            project -> Add Files

            新建

            填加已有

        生成目標的配置

            2440ART.mcp內雙擊

            TargetSettins:   post-linker選擇ArM fromELF

            Language Settins:Architecture or Processor選擇相應的編譯器ARM920T

            Arm Linker:output RO 0x30000000

                       optionsImage entry point 設為0x30000000 

                       layout Object 2440init.o Section Init   

                       Listings Image map

            Arm fromELF: output format內 Plain binary

                         output filename***.bin             

        編譯

            make

        調試  AXD是調試器

            設置,debug ->打開AXD調試界面,選擇option->config target 選項

                 選ARMUL(模擬調試器),然后選擇確定.進入調試界面.

                 ARMUL是虛擬調試環境(虛擬開發板)

                 如果用開發板真實環境調試,則需要使用JTAG連開發板后,在此處選H-JTAG

            用file-<lode image項,加載剛才編譯所的的 .axf調試文件.

                 execute->run to cousor 項.使程序進入用戶主程序

                 

            可以用F8來一條一條執行語句,也可用F10,可以設置斷點.

        

().匯編語言基本結構

      例:

          AREA Init,CODE,READONLY  ;AREA定義代碼段,段名Init;代碼段,只讀

          ENTRY                     ;偽操作,第一條指令的入口

      Start                         ;標號,一段代碼的開始,用于標記,無意義,必須頂格

          MOV  r0,#10               ;10存入r0寄存器,整型常量前面用#號 

          MOV  r1,#3                ;3存入r1寄存器,r0r1相當于兩個變量,只是名稱固定,在寄存器的存儲空間內

          ADD  r0,r0,r1             ;r0內數據與r1內數據相加,相加后數據放在r0

      ;Stop                          ;停止標號,下面的代碼用于停止運行程序

      ;    MOV  r0,#0x18             ;軟件異常中斷響應

      ;    LDR  r1,=0x20026          ;ADP停止運行,應用退出

      ;    SWI  0x123456             ;ARM半主機軟件中斷

      END

      

  1).基本概念    

   (2)寄存器:如 R0R1等  

      ARM的匯編編程,本質上就是針對CPU寄存器的編程。

   (3)指令:即操作碼,直接控制CPU,如MOV

      包括跳轉指令、數據處理指令、乘法指令、PSR訪問指令、加載或存儲指令、數據交換指令、移位指令等

   (4)偽操作:作用于編譯器,大多用于定義和控制。如AREA

      包括符號定義、數據定義、控制等

   (5)標號:僅是一種標識。在跳轉語句中,可以指向要跳轉到的標識號位置

            在ARM 匯編中,標號代表一個地址,段內標號的地址在匯編時確定,段外標號的地址值在連接時確定

   (6)符號:即標號(代表地址)、變量名、數字常量名等。符號的命名規則如下:

        a. 符號由大小寫字母、數字以及下劃線組成;

        b. 除局部標號以數字開頭外,其它的符號不能以數字開頭;

        c. 符號區分大小寫,且所有字符都是有意義的;

        d. 符號在其作用域范圍你必須是唯一的;

        e. 符號不能與系統內部或系統預定義的符號同名;

        f. 符號不要與指令助記符、偽指令同名。

  2).段定義

     在匯編語言中,以相對獨立的指令或數據序列的程序段組成程序代碼

     段的劃分:數據段、代碼段。一個匯編程序至少有一個代碼段

     (1)代碼段

        上面的例子為代碼段。

        AREA  定義一個段,并說明所定義段的相關屬性,CODE 用以指明為代碼段

        ENTRY 標識程序的入口點。END為程序結束。

     (2)數據段

        AREA   DATAAREADATA,BIINIT,ALLGN=2

        DISPBUF SPACE 200

        RCVBUF  SPACE 200

          

        DATA用以指明為數據段,

        SPACE分配200字節的存儲單元并初始化為0

        

        指令和偽操作在后面詳細描述

  3).匯編語言結構

      [標號] [指令或偽操作]

    

      所有標號必須在一行的頂格書寫,其后不加冒號

      所有指令均不能頂格寫

      指令助記符大小寫敏感,不能大小寫混合,只能全部大寫或全部小寫

      ;為注釋

      @代碼行注釋,同;

      #整行注釋或直接操作數前綴

      為換行符 

      ENTRY 為程序的入口

      END  為程序的結束

//************************************* arm體系結構 第二天***************************************

二、ARM的尋址方式

    最簡單的匯編指令格式是操作碼和操作數

    如:MOV  r0,#10

    操作碼:即CPU指令 如MOV ADD

    操作數:即表示數據是在寄存器中還是在內存中,是絕對地址還是相對地址

            操作數部分要解決的問題是,到哪里去獲取操作數,獲取操作數的方式就是尋址方式。

    ARM每一條指令都是32位機器碼,對應CPU的位數

    ARM指令格式:                 

       在32位中分為7個位域,每個位域分別存儲不同意義的編碼(二進制數)

        

       31 ~ 28 27~25   24 ~ 21  20   19~16    15~12    11 ~ 0 

       -----------------------------------------------------------

       | Cond | 001  | Opcode  | S |   Rn   |   Rd   |  Operand2 | 

       -----------------------------------------------------------

       對應輸入的一條指令為:

       <Opcode>{<cond>}{s} <Rd>,<Rn>,<Operand2>

       

       Opcode:指令操作碼   用 4個位存儲,如MOV,ADD  每一個操作碼和操作數最終都是以二進制形式存在

       Cond  :指令的條件碼 用 4個位來儲,可省略

              如:ADD   r0,r0,#1   ;計算r01后的值,再寫入到r0

                  ADDEQ r0,r0,#1   ;只有在CPSR寄存器條件標志位滿足指定條件時,才計算r01后的值,再寫入到r0

                  

              其中條件助記符如下:

                 EQ     相等

                 NE     不相等

                 MI     負數

                 VS     溢出

                 PL     正數或零

                 HI     無符號大于

                 LS     無符號小于或等于

                 CS     無符號大于或等于

                 CC     無符號小于

                 GT     有符號大于

                 GE     有符號大于或等于

                 LE     有符號小于或等于

                 LT     有符號小于

                 AL     無條件執行

 

       S     :決定指令的操作是否影響CPSR的值,用一個位存儲,省略則表示為0值,否則為1

              如SUBS  R0,R0,#1   ;R01,結果放入R0,同時響影CPSR的值

                SUB   R0,R0,#1   ;R01,結果放入R0,不影響CPSR的值

       Rd    :目標寄存器編碼   用4位存儲   

       Rn    :1個操作數的寄存器編碼 用4位存儲

       Operand2: 2個操作數   用12位存儲      

    尋址方式:是根據指令中給出的地址碼字段來實現尋找真實操作數地址的方式,8種尋址方式:

         寄存器尋址、立即尋址、寄存器間接尋址、基址尋址、多寄存器尋址、堆棧尋址、相對尋址、寄存器移位尋址

         

    //*******尋址方式是重點  需要理解背訟*************************************************

    

1.立即尋址

    操作數是常量,用#表示常量。例

    MOV  R0,#0xFF000   ;指令省略了第1個操作數寄存器。將立即數0xFF000(2操作數)裝入R0寄存器  

    SUB  R0,R0,#64     ;R064,結果放入R0

    #表示立即數   0x&表示16進制數  否則表示十進制數 

    立即數尋址指令中的地址碼就是操作數本身,可以立即使用的操作數。

    其中,#0xFF000#64都是立即數。該立即數位于32位機器碼中,占低位的12位。也就是說在ARM指令中以12位存儲一個數據

 那么,32位的數據(longfloat)如何存儲?

    32位的數據以一種特殊的方式來處理

    其中:高4位表示的無符號整數

          低8位補0擴展為32位,然后循環右移x位來代表一個數。x=4位整數*2

          所以,不是每一個32位數都是合法的立即數,只有能通過上述構造得到的才是合法的立好數。如:

          合法立即數:0xff,0x104,0xff0

          不合法立即數:ox1010x102,0xff1

2.寄存器尋址

    操作數的值在寄存器中,指令執行時直接取出寄存器值來操作

      例:MOV R1,R2         ;R2的值存入R1     在第1個操作數寄存器的位置存放R2編碼

          SUB R0,R1,R2      ;R1的值減去R2的值,結果保存到R0   在第2操作數位置,存放的是寄存器R2的編碼

          

    寄存器尋址是根據寄存器編碼獲取寄存器內存儲的操作數    

3.寄存器間接尋址

    操作數從寄存器所指向的內存中取出,寄存地存儲的是內存地址

      例:LDR R1,[R2]          ;R2指向的存儲單元的數據讀出,保存在R1中 R2相當于指針變量

          STR R1,[R2]          ;R1的值寫入到R2所指向的內存

          SWP R1,R1,[R2]       ;將寄存器R1的值和R2指定的存儲單元的內容交換

      [R2]表示寄存器所指向的內存,相當于 *p

      LDR 指令用于讀取內存數據

      STR 指令用于寫入內存數據      

//-----------------------------------------------------

4.寄存器基址尋址

    操作數從內存址偏移后讀取數據。常用于查表、數組操作、功能部件寄存器訪問等。

    基址:相當于首地址,地址偏移后取值作為操作數

    基址寄存器:用來存放基址的寄存器    

    變址尋址:基址尋址也稱為變址尋址

    

    1)前索引尋址

      例:LDR R2,[R3,#4]     ;讀取R3+4地址上的存儲單元的內容,放入R2

          LDR R2,[R3,#4]!    ;讀取R3+4地址上的存儲單元的內容,放入R2,然后R3內的地址變為R3+4,R3=R3+4

      

      [R3,#4] 表示地址偏移后取值,相當于*(p+4) 或 p[4]R3內保存的地址不變

      [R3,#4]! 表示地址偏移后取值,相當于*(p+4)  或 p[4],!表示回寫,即R3=R3-4 ,R3的值發生了改變 

    

     2)后索引尋址 

      例:LDR R2,[R3],#4     ;先讀取R3地址上的存儲單元的內容,放入R2,然后R3內的地址變為R3+4,R3=R3+4

        

      [R3],#4     類似于 *p++ 只不過自加的不是,而是指定的

      [R3,#4]!    類似于 *++p 只不過自加的不是1,而是指定的4                            

     

     3)寄存器的值作索引

      例:LDR R2,[R3,R0]     ;R0內存儲索引值,R3內存地址,讀取R3+R0地址上的存儲單元的內容,放入R2

      

      [R3,R0] 表示地址偏移后取值,相當于*(p+i) 或 p[i]

      

      

5.多寄存器尋址

    一次可傳送多個寄存器的值,也稱為塊拷貝尋址

        

    例:LDMIA R1!,{R2-R7,R12} ;R1指向的存儲單元中的數據讀寫到R2R7R12中,然后R1自加1

        STMIA R1!,{R2-R7,R12} ;將寄存器R2R7R12的值保存到R1指向的存儲單元中,然后R1自加1

        

        其中 R1是基址寄存器,用來存基址,R2-R7R12用來存數據 賦值編號小的寄存器與低地址相對應,與寄存器列表順序無關

             ! 為可選后綴,表示改變R1的值,則當數據傳送完畢之后,將最后的地址寫入基址寄存器

             基址寄存器不允許為R15,寄存器列表可以為R0R15 的任意組合。

             這里R1沒有寫成[R1]!,是因為這個位不是操作數位,而是寄存器位

             LDMIA 和 STMIA  是塊拷貝指令,  LDMIA是從R1所指向的內存中讀數據,STMIA是向R1所指向的內存寫入數據

             

             R1指向的是連續地址空間

6.寄存器堆棧尋址

    是按特定順序存取存儲區,按后進先出原則,使用專門的寄存器SP(堆棧指針)指向一塊存儲區

    例:LDMIA SP!,{R2-R7,R12} ;將棧內的數據,讀寫到R2R7R12中,然后下一個地址成為棧頂

        STMIA SP!,{R2-R7,R12} ;將寄存器R2R7R12的值保存到SP指向的棧中

        

        SP指向的是棧頂

7.相對尋址

    即讀取指令本身在內存中的地址。是相對于PC內指令地址偏移后的地址。

    

    由程序計數器PC提供基準地址,指令中的地址碼字段作為偏移量,兩者相加后得到的地址即為操作數的有效地址。

    例:

      B LOOP    ;B指令用于跳轉到標號LOOP指令處執行代碼

      ...

    LOOP

      MOV R6 #1

      

      其中LOOP僅僅是標號,而不是地址,不是CPU指令,所以在指令的機器碼中沒有標號的機器碼,而是

  計算出了從B指令到MOV指令之間內存地址的差值,這個差值相當于PC的偏移量,即相當于:add pc,pc,#偏移量

      B 指令引起了PC寄存器值的變化,PC內永遠保存將要運行指令在內存中的地址。

      

8.寄存器移位尋址

      操作數在被使用前進行移位運算

      例:MOV R0,R2,LSL #3     ;R2的值左移3位,結果放入R0,       ;即是R0=R2×8  

      

      LSL 是移位指令,用于將前面寄存器的數據 左移

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   ARM匯編語言語句由 指令、偽指令、偽操作、宏指令組成

.ARM指令集

    共包括6種類型:數據處理指令、跳轉指令、程序狀態指令、加載存取儲指令、協處理指令、軟中斷指令

(一)、數據處理指令

     數據處理指令,只能對寄存器內容進行操作,而不能對內存進行操作,所有數據處理指令均可使用S后綴,并影響狀態標志

 包括:數據傳送指令、算述指令、乘法指令、邏輯運算指令、比較指令、移位指令

1. 數據傳輸指令

  1) MOV 數據傳送指令 

     格式:MOV Rd,<op1>

     功能:Rd=操作數操作數可以是寄存器、被移位的寄存器或立即數。

     例:

      MOV  R0,#0xFF000  ;立即尋址,將立即數0xFF000(2操作數)裝入R0寄存器  

      MOV R1,R2         ;寄存器尋址,將R2的值存入R1 

      MOV R0,R2,LSL #3  ;移位尋址,R2的值左移3位,結果放入R0

  2) MVN 數據取反傳送指令

     格式:MVN <Rd>,<op1>; 

     功能:將操作數傳送到目的寄存器Rd中,但該值在傳送前被按位取反,即Rd=!op1

     例:

       MVN R0,0 ;R0=-1

       

     mvn 意為“取反傳輸”,它把源寄存器的每一位取反,將得到的結果寫入結果寄存器。

     movsmvns指令對pc寄存器賦值時有特殊含義,表示要求在賦值的同時從spsr中恢復cpsr

     movmvn指令,編譯器會進行智能的轉化。比如指令“mov r1, 0xffffff00”中的立即數是非法的。

  在編譯時,編譯器將其轉化為“mvn r1, 0xff”,這樣就不違背立即數的要求。所以對于movmvn指令,

  可以認為:合法的立即數反碼也是合法的立即數。

      

2.算術指令

  1) ADD 加法指令 

     格式:ADD{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn+op2

     例: 

       ADD R0,R1,5       ;R0=R1+5 

       ADD R0,R1,R2        ;R0=R1+R2 

       ADD R0,R1,R2,LSL5 ;R0=R1+R2左移5位 

  2) ADC 帶進位加法指令 

     格式:ADC{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn+op2+carry ,carry為進位標志值。該指令用于實現超過32位的數的加法。 

     示例: 

         第一個64位操作數存放在寄存器R2R3中; 

         第二個64位操作數存放在寄存器R4R5中; 

         64位結果存放在R0R1中。 

        

         ADDS R0,R2,R4 ;32位相加,S表示結果影響條件標志位的值 

         ADC R1,R3,R5 ;32位相加 

  3) SUB 減法指令

     格式:SUB{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn-op2 

     例: 

       SUB R0,R1,5 ;R0=R1-5 

       SUB R0,R1,R2 ;R0=R1-R2 

       SUB R0,R1,R2,LSL5 ;R0=R1-R2左移5位 

  4) SBC 帶借位減法指令 

     格式:SBC{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn-op2-!carry 

     SUBSBC生成進位標志的方式不同于常規,如果需要借位則清除進位標志,所以指令要對進位標志進行一個非操作。 

     例: 

        第一個64位操作數存放在寄存器R2R3中; 

        第二個64位操作數存放在寄存器R4R5中; 

        64位結果存放在R0R1中。 

        SUBS R0,R2,R4; 32位相減,S表示結果影響條件標志位的值 

        SBC R1,R3,R5; 32位相減 

   5) 其它減法指令

      RSB 反向減法指令,SUB指令,但倒換了兩操作數的前后位置,即Rdop2-Rn

      RSC 帶借位的反向減法指令,SBC指令,但倒換了兩操作數的前后位置,即Rdop2-Rn-!carry。 

      rsb r0, r1, r2 /* r0 = r2 – r1 */

      rsc r0, r1, r2 /* r0 = r2 – r1 + carry – 1 */

    

      addsadcs在進位時將cpsrC標志置1;否則置0

      subssbcs在產生借位時將cpsrC標志置0;否則置1

3.乘法指令 

   1) MUL 32位乘法指令 

      格式:MUL{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn×op2 

      該指令根據S標志,決定操作是否影響CPSR的值;其中op2必須為寄存器。Rnop2的值為32位的有符號數或無符號數。 

      例: 

         MULS R0,R1,R2 ;R0R1×R2,結果影響寄存器CPSR的值 

   2) MLA 32位乘加指令 

      格式:MLA{<cond>}{S} <Rd>,<Rn>,<op2>,<op3>; 

      功能:RdRn×op2+op3  op2op3必須為寄存器。Rnop2op3的值為32位的有符號數或無符號數。 

      例: 

         MLA R0,R1,R2,R3 ;R0R1×R2+R3 

   3) SMULL 64位有符號數乘法指令 

      格式:SMULL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:Rdh RdlRn×op2   RdhRdlop2均為寄存器。Rnop2的值為32位的有符號數。 

      例: 

         SMULL R0,R1,R2,R3 ;R0R2×R3的低32位 R1R2×R3的高32位 

   4) SMLAL 64位有符號數乘加指令 

      格式:SMLAL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:Rdh RdlRn×op2+Rdh Rdl  ;RdhRdlop2均為寄存器。Rnop2的值為32位的有符號數,Rdh Rdl的值為64位的加數。 

      例: 

         SMLAL R0,R1,R2,R3   ;R0R2×R3的低32+R0 R1R2×R3的高32+R1 

   5) UMULL 64位無符號數乘法指令 

      格式:UMULL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:同SMULL指令,但指令中Rnop2的值為32位的無符號數。 

      例: 

         UMULL R0,R1,R2,R3   ;R0R2×R3的低32位 R1R2×R3的高32位 其中R2R3的值為無符號數 

   6) UMLAL 64位無符號數乘加指令 

      格式:UMLAL {<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:同SMLAL指令,但指令中Rnop2的值為32位的無符號數,Rdh Rdl的值為64位無符號數。 

      例: 

         UMLAL R0,R1,R2,R3  ;R0R2×R3的低32+R0 R1R2×R3的高32+R1  

                            ;其中R2R3的值為32位無符號數 R1R0的值為64位無符號數 

4. 邏輯指令:andorreorbic

   1) AND 邏輯與指令 

      格式:AND{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn AND op2  一般用于清除Rn的特定幾位。

      例: 

         AND R0,R0,5  ;保持R0的第0位和第2位,其余位清

   2) ORR 邏輯或指令 

      格式:ORR{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn OR op2 一般用于設置Rn的特定幾位。

      例: 

        ORR R0,R0,5  ;R0的第0位和第2位設置為1,其余位不變 

   3) EOR 邏輯異或指令 

      格式:EOR{<cond>}{S} <Rd>,<Rn>,<op2>;

      功能:RdRn EOR op2  一般用于將Rn的特定幾位取反。 

      例: 

        EOR R0,R0,5  ;R0的第0位和第2位取反,其余位不變 

   4) BIC 位清除指令 

      格式:BIC{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn AND (!op2) 用于清除寄存器Rn中的某些位,并把結果存放到目的寄存器Rd

      例: 

        BIC R0,R0,5 ;R0中第0位和第2位清0,其余位不變

5. 比較指令

   1) CMP 比較指令

      格式:CMP{<cond>} <Rn>,<op1>; 

      功能:Rn-op1,根據結果更新CPSR中條件標志位的值。

      例:

         CMP R0,5        ;計算R0-5,根據結果設置條件標志位 

         ADDGT R0,R0,5   ;ADD為加法指令,GT為判斷條件標志位是 否大于5, 如果R0>5,則執行ADD指令

   2) CMN 反值比較指令 

      格式:CMN{<cond>} <Rn>,<op1>; 

      功能:同CMP指令,但寄存器Rn的值是和op1取負的值進行比較。

      例: 

         CMN R0,5 ;R0-5進行比較 

   3) TST 位測試指令

      格式:TST{<cond>} <Rn>,<op1>; 

      功能: Rn AND op1 按位與后,更新CPSR中條件標志位,用于檢查寄存器Rn是否設置了op1中相應的位。

      例: 

         TST R0,#5 ;測試R0中第0位和第2位是否為

   4) TEQ 相等測試指令 

      格式:TEQ{<cond>} <Rn>,<op1>; 

      功能:Rn EOR op1  按位異或后,更新CPSR中條件標志位,用于檢查寄存器Rn的值是否和op1所表示的值相等。

      例: 

         TEQ R0,5 ;判斷R0的值是否和5相等 

6.移位指令(位運算指令)

   1) LSL(ASL) 左移

      格式:寄存器,LSL(ASL ) 操作數

      功能:將寄存器內的數據左移,操作數是移位的位數在0-31之間

      例:

        MOV R0, R1, LSL#2;將 R1中的內容左移兩位后傳送到 R0中。

   2) LSR操作 右移

      格式寄存器 LSR 操作數

      功能將寄存囂內的數據右移

      例:

        MOV R0, R1, LSR#2;將 R1中的內容右移兩位后傳送到 R0中,左端用零來填充。

   3)其它移位

      ASR  右移,左端用第31位值來填充

      ROR  右移,循環右移,左端用右端移出的位來填充

      RRX  右移,循環右移,左端用進位標志位C來填充

      

//************************************* arm體系結構 第三天***************************************

()、跳轉指令

   1) B 跳轉指令 

      格式:B{<cond>} <addr>; 

      功能:跳轉到addr地址。

      例:

         B exit; 程序跳轉到標號exit處 

   2) BL 帶返回的跳轉指令 

      格式:BL{<cond>} <addr>; 

      功能:同B指令,但BL指令執行跳轉操作的同時,還將PC(寄存器R15)的值保存到LR寄存器(寄存器R14)中。

            該指令用于實現子程序調用,程序的返回可通過把LR寄存器的值復制到PC寄存器中來實現。 

      例: 

      BL func; 調用子程序func 

      … 

      func 

      … 

      MOV R15,R14; 子程序返回        

   3) 其它跳轉指令

      BLX 帶返回和狀態切換的跳轉指令,用于子程序調用和程序Thumb狀態的切換。 

      BX 帶狀態切換的跳轉指令,處理器跳轉到目標地址處,目標地址處的指令可以是ARM指令,也可以是Thumb 指令。 

   跳轉指令用于實現程序的跳轉和程序狀態的切換。 

   ARM程序設計中,實現程序跳轉有兩種方式:跳轉指令、直接向程序寄存器PC中寫入目標地址值。

//----------------------------------------------------------------------------------------

(三)、程序狀態指令

      用于狀態寄存器和通用寄存器間傳送數據。總共有兩條指令:MRSMSR。兩者結合可用來修改程序狀態寄存器的值。

   1) MRS 程序狀態寄存器到通用寄存器的數據傳送指令

      格式:MRS{<cond>} <Rd>,CPSR/SPSR; 

      功能:用于將程序狀態寄存器的內容傳送到目標寄存器Rd中。

      例: 

          MRS R0,CPSR  ;狀態寄存器CPSR的值存入寄存器R0中 

   2) MSR 通用寄存器到程序狀態寄存器的數據傳送指令 

      格式:MSR{<cond>} CPSR/SPSR_<field>,<op1>; 

      功能:用于將寄存器Rd的值傳送到程序狀態寄存器中。 

      <field>:用來設置狀態寄存器中需要操作的位。 

               32位的狀態寄存器可以分為4個域: 

               位[3124]為條件標志位域,用f表示。 

               位[2316]為狀態位域,用s表示。 

               位[158]為擴展位域,用x表示。 

               位[70]為控制位域,用c表示。 

      例:

          MSR CPSR_f,R0 ;R0的值修改CPSR的條件標志域 

          MSR CPSR_fsxc,#5; CPSR的值修改為

(四)、加載存儲指令

    該集合的指令使用頻繁,當數據存放在內存中時,必須先把數據從內存裝載到寄存器,執行完后再把寄存器

中的數據存儲到內存中

1.單數據訪存指令

  1)單數據加載指令 

      格式:LDR(LDRBLDRBTLDRHLDRSBLDRSHLDRTSTRSTRBSTRBTSTRHSTRT) <Rd>,<addr>; 

      功能:內存地址中的數據裝載到目標寄存器Rd中,同時還可以把合成的有效地址寫回到基址寄存器。 

      尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無符號數。

            LDR Rd,[Rn]             ;把內存中地址為Rn的字數據裝入寄存器Rd中 

            LDR Rd,[Rn,Rm]          ;將內存中地址為Rn+Rm的字數據裝入寄存器Rd中 

            LDR Rd,[Rn,index]     ;將內存中地址為Rn+index的字數據裝入Rd中 

            LDR Rd,[Rn,Rm,LSL5]   ;將內存中地址為Rn+Rm×32的字數據裝入Rd 

            LDR Rd,[Rn,Rm]!         ;將內存中地址為Rn+Rm的字數據裝入Rd,并將新地址Rn+Rm寫入Rn 

            LDR Rd,[Rn,index]!    ;將內存中地址為Rn+index的字數據裝入Rd,并將新地址Rn+index寫入Rn 

            LDR Rd,[Rn,RmLSL5]! ;將內存中地址為Rn+Rm×32的字數據裝入Rd,并將新地址Rn+Rm×32寫入Rn 

            LDR Rd,[Rn],Rm          ;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+Rm寫入Rn 

            LDR Rd,[Rn],index     ;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+index寫入Rn 

            LDR Rd,[Rn],Rm,LSL5   ;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+Rm×32寫入Rn 

      各指令的區別:

      (1) LDR  字數據加載指令  

          將內存地址中的字數據裝載到目標寄存器Rd

          例: LDR R0,[R1,R2,LSL5]! ;將內存中地址為R1+R2×32的字數據裝入寄存器R0,并將新地址R1+R2×32寫入R1 

          

      (2) LDRB 字節數據加載指令

          同LDR,只是從內存讀取一個8位的字節數據而不是一個32位的字數據,并將Rd的高24位清0。        

          例: LDRB R0,[R1]           ;將內存中起始地址為R1的一個字節數據裝入R0中 

      

      (3) LDRBT 用戶模式的字節數據加載指令

          同LDRB指令,但無論處理器處于何種模式,都將該指令當作一般用戶模式下的內存操作。 

      (4) LDRH 半字數據加載指令

          同LDR指令,但該指令只是從內存讀取一個16位的半字數據而不是一個32位的字數據,并將Rd的高16位清0

          例:LDRH R0,[R1]            ;將內存中起始地址為R1的一個半字數據裝入R0中 

      (5) LDRSB 有符號的字節數據加載指令

          同LDRB指令,但該指令將寄存器Rd的高24位設置成所裝載的字節數據符號位的值。 

          例:LDRSB R0,[R1] ;將內存中起始地址為R1的一個字節數據裝入R0中,R0的高24位設置成該字節數據的符號位 

      (6) LDRSH 有符號的半字數據加載指令

          同LDRH指令,但該指令將寄存器Rd的高16位設置成所裝載的半字數據符號位的值。

          例:LDRSH R0,[R1] ;將內存中起始地址為R1的一個16位半字數據裝入R0中,R0的高16位設置成該半字數據的符號位 

      (7) LDRT 用戶模式的字數據加載指令

          同LDR指令,但無論處理器處于何種模式,都將該指令當作一般用戶模式下的內存操作。有效地址必須是字對齊的

  2)單數據存儲指令

      格式:STR(STRSTRBSTRBTSTRHSTRT) <Rd>,<addr>; 

      功能:將寄存器數據寫入到內存中

      尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無符號數。

            STR Rd,[Rn]             ;將寄存器Rd中的字數據寫入到內存中地址為Rn內存中

            STR Rd,[Rn,Rm]          ;將寄存器Rd中的字數據寫入到內存中地址為Rn+Rm的內存中

            STR Rd,[Rn,index]     ;將寄存器Rd中的字數據寫入到內存中地址為Rn+index內存中

            STR Rd,[Rn,Rm,LSL5]   ;將寄存器Rd中的字數據寫入到內存中地址為Rn+Rm×32內存中

            STR Rd,[Rn,Rm]!         ;將寄存器Rd中的字數據寫入到內存中地址為Rn+Rm的內存中

            STR Rd,[Rn,index]!    ;將寄存器Rd中的字數據寫入到內存中地址為Rn+index的內存中,并將新地址Rn+index寫入Rn 

            STR Rd,[Rn,RmLSL5]! ;將寄存器Rd中的字數據寫入到內存中地址為Rn+Rm×32的內存中,并將新地址Rn+Rm×32寫入Rn 

            STR Rd,[Rn],Rm          ;將寄存器Rd中的字數據寫入到內存中地址為Rn的內存中,并將新地址Rn+Rm寫入Rn 

            STR Rd,[Rn],index     ;將寄存器Rd中的字數據寫入到內存中地址為Rn的內存中,并將新地址Rn+index寫入Rn 

            STR Rd,[Rn],Rm,LSL5   ;將寄存器Rd中的字數據寫入到內存中地址為Rn的內存中,并將新地址Rn+Rm×32寫入Rn 

   

      (1) STR 字數據存儲指令

          把寄存器Rd中的字數據(32位)保存到addr所表示的內存地址中,同時還可以把合成的有效地址寫回到基址寄存器。

          例: STR R0,[R1,5]!  ;R0中的字數據保存到以R1+5為地址的內存中,然后R1R1+5 

      (2) STRB 字節數據存儲指令

          把寄存器Rd中的低8位字節數據保存到addr所表示的內存地址中。

          例: STRB R0,[R1]  ;將寄存器R0中的低8位數據存入R1表示的內存地址中 

      (3) STRBT 用戶模式的字節數據存儲指令 

          同STRB指令,但無論處理器處于何種模式,該指令都將被當作一般用戶模式下的內存操作。

      (4) STRH 半字數據存儲指令

          把寄存器Rd中的低16位半字數據保存到addr所表示的內存地址中,而且addr所表示的地址必須是半字對齊的。 

          例:STRH R0,[R1]  ;將寄存器R0中的低16位數據存入R1表示的內存地址中 

      (5) STRT 用戶模式的字數據存儲指令

          同STR指令,但無論處理器處于何種模式,該指令都將被當作一般用戶模式下的內存操作。 

2.多數據訪存指令

   1) 批量數據加載指令 

      格式:LDM{<cond>}{<type>} <Rn>{!},<regs>{^}; 

      功能:從一片連續的內存單元讀取數據到各個寄存器中,內存單元的起始地址為基址寄存器Rn的值,各個寄存器由寄存

            器列表regs表示。 

      該指令一般用于多個寄存器數據的出棧。 

      type字段種類: 

          IA:每次傳送后地址加1。 

          IB:每次傳送前地址加1。 

          DA:每次傳送后地址減1。 

          DB:每次傳送前地址減1。 

          FD:滿遞減堆棧。 

          ED:空遞減堆棧。 

          FA:滿遞增堆棧。 

          EA:空遞增堆棧。 

      堆棧尋址的命令LDMFA/STMFALDMEA/STMEALDMFD/STMFDLDMED/STMED

      LDMSTM表示多寄存器尋址,即一次可以傳送多個寄存器值。

      LDM:一次裝載多個,這里用來出棧。

      STM:一次存儲多個,這里用來入棧。

      F/E表示指針指向的位置

      Ffull滿堆棧,表示堆棧指針指向最后一個入棧的有效數據項。

      Eempty空堆棧,表示堆棧指針指向下一個要放入的空地址。

      A/D表示堆棧的生長方式

      A:堆棧向高地址生長,即遞增堆棧。

      D:堆棧向低地址生長,即遞減堆棧。

      注意:有一個約定,編號低的寄存器在存儲數據或者加載數據時對應于存儲器的低地址。

            FDEDFAEA指定是滿棧還是空棧,是升序棧還是降序棧,用于堆棧尋址。

                一個滿棧的棧指針指向上次寫的最后一個數據單元

                空棧的棧指針指向第一個空閑單元。

                一個降序棧是在內存中反向增長而升序棧在內存中正向增長。

            {!}:若選用了此后綴,則當指令執行完畢后,將最后的地址寫入基址寄存器。 

            {^}:當regs中不包含PC時,該后綴用于指示指令所用的寄存器為用戶模式下的寄存器,

                 否則指示指令執行時,將寄存器SPSR的值復制到CPSR中。

                 

   2) 批量數據存儲指令 

      格式:STM{<cond>}{<type>} <Rn>{!},<regs>{^}; 

      功能:將各個寄存器的值存入一片連續的內存單元中,內存單元的起始地址為基址寄存器Rn的值

            各個寄存器由寄存器列表regs表示。 該指令一般用于多個寄存器數據的入棧。 

      {^}:指示指令所用的寄存器為用戶模式下的寄存器。其他參數用法同LDM指令。

      

      例:STMEA R13!,{R0-R12,PC}  ;將寄存器R0~R12以及程序計數器PC的值保存到R13指示的堆棧中 

3.數據交換指令

   1) 字數據交換指令

      格式:SWP <Rd>,<op1>,[<op2>]; 

      功能:Rd[op2][op2]op1 

            從op2所表示的內存裝載一個字并把這個字放置到目的寄存器Rd中,然后把寄存器op1的內容存儲到同一內存地址中。 

      

      例:SWP R0,R1,[R2] ;R2所表示的內存單元中的字數據裝載到R0,然后將R1中的字數據保存到R2所表示的內存單元中 

   2) 字節數據交換指令

      格式:SWPB <Rd>,<op1>,[<op2>]; 

      功能:從op2所表示的內存裝載一個字節并把這個字節放置到目的寄存器Rd的低8位中,Rd的高24位設置為0

            然后將寄存器op1的低8位數據存儲到同一內存地址中。 

      例:  SWPB R0,R1,[R2] ;R2所表示的內存單元中的一個字節數據裝載到R0的低8位,然后將R1中的低8位字節

         數據保存到R2所表示的內存單元中 

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

()、協處理指令

   1) CDP協處理器操作指令 

      格式:CDP{<cond>}<p>,<opcode1>,<CRd>,<CRm>,<CRn>,<opcode2>; 

      功能:用于傳遞指令給協處理器p,要求其在寄存器CRnCRm上,進行操作opcode1,并把結果存放到CRd中,

            可以使用opcode2提供與操作有關的補充信息。指令中的所有寄存器均為協處理器的寄存器,操作由協處理器完成。 

      指令中

            P為協處理器編號; 

            CRd為目的寄存器的協處理器寄存器; 

            CRmCRn為存放操作數的協處理器寄存器; 

            Opcode1opcode2為協處理器即將執行的操作。

             

      例:CDP p55c0c1c29;該指令用于通知協處理器p5,在c1c2上執行操作59,并將結果存放到c0中。 

   2) LDC協處理器數據讀取指令 

      格式:LDC {<cond>}{L}<p>,<CRd>,<addr>; 

      功能:將addr表示的內存地址中的連續數據傳送到目的寄存器CRd中。 

            L表示指令為長讀取操作,比如用于雙精度數據的傳輸; 

            目的寄存器CRd為協處理器的寄存器; 

            addr的尋址方式同LDR指令,其寄存器為ARM處理器的寄存器。 

     

      例:LDC p5c1[R1+5]:該指令用于將R15所對應的存儲單元中的數據,傳送到協處理器p5的寄存器c1中。 

   

   3) STC協處理器數據存儲指令 

      格式:STC{<cond>}{L}<p>,<CRd>,<addr>; 

      功能:將寄存器CRd的值傳送到addr表示的內存地址中。指令中各參數用法同LDC。 

      示例:STC p5c1[R1+5]  ;該指令用于將協處理器p5中寄存器c1的數據傳送到R15所對應的存儲單元中。 

   4) MCR ARM寄存器到協處理器寄存器的數據傳送指令 

      格式:MCR{<cond>}<p>,<op1>,<Rd>,<CRn>,<CRm>{,op2}; 

      功能:將ARM處理器的寄存器Rd中的數據傳送到協處理器p的寄存器CRnCRm中;op1op2為協處理器將要執行的操作。 

      例:MCR p5,5,R1,C1,C2,9;該指令將R1中的數據傳送到協處理器p5的寄存器C1C2中,協處理器執行操作59。 

      MRC 協處理器寄存器到ARM寄存器的數據傳送指令 

      格式:MRC {<cond>}<p>,<op1>,<Rd>,<CRn>,<CRm>{,op2}; 

      功能:將協處理器p的寄存器CRnCRm的數據傳送到ARM處理器的寄存器Rd中;op1op2為協處理器將要執行的操作。 

      例:MRC p5,5,R1,C1,C2,9;該指令將寄存器C1C2中的數據傳送到R1中,協處理器p5協處理器執行操作59。 

()、異常中斷指令

    異常中斷產生指令:用于系統調用和調試。 

   1) SWI 軟件中斷指令 

      格式:SWI {<cond>} 24位的立即數; 

      功能:用于產生軟件中斷,以使用戶程序調用操作系統的系統例程。 

            指令中24位的立即數指定用戶程序調用系統例程的類型,其參數通過通用寄存器傳遞。當24位的立即數

            被忽略時,系統例程類型由寄存器R0指定,其參數通過其他通用寄存器傳遞。 

      例:SWI 0X05; 調用編號為05的系統例程。 

   2) BKPT 斷點中斷指令 

      格式:BKPT 16位的立即數; 

      功能:用于產生軟件斷點中斷,以便軟件調試時使用。16位的立即數用于保存軟件調試中額外的斷點信息。 

      指令操作的偽代碼: 

()、信號量操作指令

      信號量操作指令:用于進程間的同步互斥,提供對信號量的原子操作。

()ARM程序常見結構

1.子函數和主函數

       使用BL 指令進行調用,該指令會把返回的PC 值保存在LR

           AREA Example,CODE,READONLY ;聲明代碼段Example

           ENTRY                      ;程序入口

       Start 

           MOV  R0,#0                 ;設置實參,將傳遞給子程騙子的實參存放在r0r1

           MOV  R1,#10

           BL   ADD_SUM               ;調用子程序ADD_SUM

           B    OVER                  ;跳轉到OVER標號處,進入結尾

       ADD_SUM

           ADD  R0,R0,R1              ;實現兩數相加

           MOV  PC,LR                 ;子程序返回,R0內為返回的結果

       OVER

           END

      運行過程

      (1)程序從ENTRY后面指令處開始運行(即主函數)

         R0R1先準備好數據令子函數運算時使用

      (2)BL  為跳轉指令,用于調用子函數 后面為函數標號。

         在調用函數的過程中,自動將PC內的值保存到LR(R14)內備份,PC內當前的值為下一條要執行的指令地址

         (即B OVER指令地址),在子函數結束前將該地址恢復到PC

      (3)B   為跳轉指令,用于跳轉到指定的標號后,此處跳轉到程序結尾

      (4)MOV PC,LR 是子函數內的最后一條語句,用于將LR內保存的地址恢復到PC

         PC(R15)  程序計數器 存儲要執行的指令在內存中的地址

                  PC的值 當前正在執行指令在內存中的地址 + 8

2.條件跳轉語句

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start

        MOV  R0,#2                 ;R0賦初值

        MOV  R1,#5                 ;R1賦初值5

        ADD  R5,R0,R1              ;R0R1內的值相加并存入R5

        CMP  R5,#10                

        BEQ  DOEQUAL               ;R510,則跳轉到DOEQUAL標簽處

    WAIT    

        CMP  R0,R1 

        ADDHI R2,R0,#10            ;R0 >R1 R2=R0+10

        ADDLS R2,R1,#5             ;R1<=R2 R2=R1+5

    DOEQUAL

        ANDS R1,R1,#0x80           ;R1=R1& 0x80,并設置相應標志位

        BNE  WAIT                  ;R1 d7位為1則跳轉到WAIT標簽

    OVER

        END 

        

    運行過程

      (1)程序從ENTRY后面指令處開始運行(即主函數)

         R0R1賦初值25

         將R0R1相加后存入R5

      (2)CMP 用于比較兩個數據,指令格式如下:

         CMP 操作數1,操作數2

         CMP用于把一個寄存器的內容和另一個寄存器或立即數進行比較,同時

      更新CPSR中條件標志位的值。標志位表示操作數1和操作數2的關系。然后執行后面的語句

      (3)條件助記符

         BEQ   B為跳轉指令,EQ為條件相等,讀取CPSR內的條件標志位,如相等則跳轉到所指定的標號處

         BNE   B為跳轉指令,NE為不相等(0),如不相等則跳轉到所指定的標號處

         ADDHI ADD為相加指令,HI為無符號大于,如大于則執行相加

         ADDLS ADD為相加指令,LS為無符號小于或等于,如小于或等于則相加

      (4)位運算

         ANDS  AND 按位與,0x80取出第7位, S為將該位與的值寫入標志位

3.循環語句

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start

        MOV  R1,#0                 ;R1賦初值0

    LOOP     

        ADD  R1,R1,#1

        CMP  R1,#10

        BCC  LOOP                  ;R1小于10則執行跳轉到LOOP處執行循環,即R1010后退出循環

        END 

    例:編寫一具有完整匯編格式的程序,實現冒泡法排序功能。

        設無符號字數據存放在從0x400004開始的區域,字數據的數目字存放在0x400000中。

        AREA   SORTCODEREADONLY

    ENTRY

    START

    MOV     R1#0x400000

    LP

    SUBS     R1R1#1

    BEQ     EXIT

    MOV     R7R1

    LDR     R0=0x400004

    LP1

    LDR     R2[R0]#4

    LDR     R3[R0]

    CMP     R2R3

    STRLO   R3[R0, # -4]

        STRLO   R2[R0]

      SUBS      R7R7#1

      BNE     LP1

      B     LP

    EXIT

      END

    練習:

      1. 編寫1+2+3++100的匯編程序。

      2. 實現子函數,該函數返回兩個參數中的最大值,在主函數內調用。

//************************************* arm體系結構 第四天***************************************

.偽操作和宏指令

   偽指令——是匯編語言程序里的特殊指令助記符,在匯編時被合適的機器指令替代。

   偽操作——為匯編程序所用,在源程序進行匯編時由匯編程序處理,只在匯編過程起作用,不參與程序運行。

   宏指令——通過偽操作定義的一段獨立的代碼。在調用它時將宏體插入到源程序中。也就是常說的宏。

   

   說明:所有的偽指令、偽操作和宏指令,均與具體的開發工具中的編譯器有關

1.宏定義(MACROMEND)    

    格式:MACRO

            {$標號名宏名{ $ 參數 , 參數 ,……}    

            指令序列    

          MEND 

          MACRO 、 MEND 偽指令可以將一段代碼定義為一個整體,稱為宏指令,在程序中通過宏指令多次調用該段代碼。

          {}為可選項

          $ 標號在宏指令被展開時,標號會被替換為用戶定義的符號

          在宏定義體的第一行應聲明宏的原型(包含宏名、所需的參數),然后就可以在匯編程序中通過宏名來調用該指令序列

          

          寫在代碼段或數據段前面

          MEXIT 跳出宏

          

    例:沒有參數的宏(實現子函數返回)

            MACRO

               MOV_PC_LR    ;宏名

               MOV PC,LR    ;子程序返回,R0內為返回的結果

            MEND

            

            AREA Example,CODE,READONLY ;聲明代碼段Example

            ENTRY                      ;程序入口

        Start 

            MOV  R0,#0                 ;設置實參,將傳遞給子程騙子的實參存放在r0r1

            MOV  R1,#10

            BL   ADD_NUM               ;調用子程序ADD_NUM

            BL   SUB_NUM               ;調用子程序SUB_NUM

            B    OVER                  ;跳轉到OVER標號處,進入結尾

        

            EXPORT ADD_NUM             

        ADD_NUM

            ADD  R0,R0,R1              ;實現兩數相加

            MOV_PC_LR                  ;調用宏,代表子函數結束

            

            EXPORT SUB_NUM             

        SUB_NUM

            SUB  R0,R1,R0              ;實現兩數相減

            MOV_PC_LR                  ;調用宏,代表子函數結束                            

        OVER

            END          

    例:有參數宏

        宏定義從MACRO 偽指令開始,到MEND 結束,并可以使用參數。類似于C#define

        MACRO                ;宏定義

           CALL $Function,$dat1,$dat2 ;宏名稱為CALL,個參數

           IMPORT $Function  ;聲明外部子程序 宏開始

           MOV R0,$dat1      ;設置子程序參數,R0=$dat1

           MOV R1,$dat2

           BL $Function      ;調用子程序 宏最后一句

        MEND                 ;宏定義結束

        

        CALL FADD1,#3,#2     ;宏調用,后面是三個參數

        

        匯編預處理后,宏調用將被展開,程序清單如下:

        IMPORT FADD1

        MOV R0,#3

        MOV R1,#3

        BL FADD1          

2.符號定義偽操作    

  1)定義常量(EQU)   

     

    格式:標號名稱 EQU 表達式 ,類型 }

          用于為程序中的常量、標號等定義一個等效的字符名稱,類似于 語言中的# define 

          其中 EQU 可用 代替。

       

          標號名稱:為常量名。

          表達式:寄存器的地址值、程序中的標號、32位地址常量、32位常量

                  當表達式為 32 位的常量時,可以指定表達式的數據類型,可以有以下三種類型:  

                  CODE16 、 CODE32 和 DATA

                      

    示例:EQU的使用

    X     EQU 45    ;#define X 45,必須頂格

    Y     EQU 64

    stack_top EQU 0x30200000CODE32    

     

         AREA Example,CODE,READONLY             

         CODE32     

         ENTRY                         

    Start     

         LDR SP,=stack_top ; stack_top內的值0x30200000是地址,=stack_top是取stack_top常量地址(即指針的指針

         MOV R0,#X    ;X替換為45

         STR R0,[SP]  ;R0內的45存入到SP所指向的內存中(SP此時是指針的指針)

         MOV R0,#Y

         LDR R1,[SP]  ;從內存中讀取數據到R1

         ADD R0,R0,R1

         STR R0,[SP]         

     END 

     注:X,Y,stack_top為標號,必須頂格寫,大多寫在代碼段外面

   

    

  2)定義變量

    常量:數字常量,有三種表示方式:十進制數、十六進制數、字符串常量、布爾常量(testno SETS {FALSE})

    變量:數字變量、邏輯變量、字符串變量

    (1)*** GBLAGBLLGBLS 定義全局變量

    格式:GBLA(GBLLGBLS) 全局變量名

           GBLA 偽指令用于定義一個全局的數字變量,并初始化為 ;    

           GBLL 偽指令用于定義一個全局的邏輯變量,并初始化為 F ();    

           GBLS 偽指令用于定義一個全局的字符串變量,并初始化為空;  

           

           由于以上三條偽指令用于定義全局變量,因此在整個程序范圍內變量名必須唯一。 

             

    示例:全局變量的定義與賦值    

          GBLA count    ;定義全局變量

    count SETA 2        ;給全局變量賦值為2,必須頂格

         AREA Example,CODE,READONLY             

         CODE32     

         ENTRY                     

    Start          

         MOV R0,#count  ;count內的值寫入R0

         ADD R0,R0,#2

         B Start

         END   

    注: 在賦值過程中,全局變量名必須頂格寫,全局變量常在代碼段外定義和賦值       

    

    示例:變量與內存地址 

        GBLA globv

    globv SETA 23

    

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start 

        LDR R0,=globv  ;golbv是全局變量,將內存地址讀入到R0

        LDR R1,[R0]    ;將內存數據值讀入到R1

        ADD R1,R1,#2

        STR R1,[R0]    ;將修改后數據再賦給變量

        MOV R0,#0   

    OVER

        END            

                

        注:#取變量值  =取變量地址  [R0]讀取R0內地址所指向的數據值

  

    (2)***LCLALCLL LCLS 定義局部變量

    格式:LCLA ( LCLL 或 LCLS ) 局部變量名    

                   LCLA 偽指令用于定義一個局部的數字變量,并初始化為 ;    

          LCLL 偽指令用于定義一個局部的邏輯變量,并初始化為 F ();    

          LCLS 偽指令用于定義一個局部的字符串變量,并初始化為空;    

          以上三條偽指令必須寫在宏定義內,用于聲明局部變量,宏結束,局部變量不再起作用

          

    示例:

         LCLA num ;聲明一個局部的數字變量,變量名為 num    

    num  SETA 0xaa ;將該變量賦值為 0xaa    

         LCLL isOk ;聲明一個局部的邏輯變量,變量名為 isOk    

    isOk SETL ;將該變量賦值為真    

         LCLS str1 ;定義一個局部的字符串變量,變量名為 str1    

    str1 SETS "Testing" ;將該變量賦值為"Testing"   

          

    示例:局部變量的定義與賦值

        MACRO

        MOV_START       ;宏名

            LCLA x      ;定義局部變量

            LCLA y

    x SETA 12           ;必須頂格寫

    y SETA 24        

            MOV R0,#2   

            MOV R1,#3

            ADD R0,R0,R1

        MEND

        

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start 

        MOV_START

        MOV  R0,#0   

    OVER

        END             

   

    注: 在賦值過程中,局部變量名必須頂格寫,局部變量必須在宏定義內使用

         

    (3)***SETASETL SETS  用于給一個已經定義的全局變量或局部變量賦值。    

        SETA 偽指令用于給一個數學變量賦值;    

        SETL 偽指令用于給一個邏輯變量賦值;    

        SETS 偽指令用于給一個字符串變量賦值;    

        其中,變量名為已經定義過的全局變量或局部變量,表達式為將要賦給變量的值。

    (4)變量代換 $

        $在數字變量前,將變值轉換為十六進制字符串

        $在邏輯變量前,將變量轉換為真或假

        $在字符串變量前,替換后面變量的字符串

        如:

          LCLS  Y1  ;定義局部字符串變量Y1Y2

          LCLS  Y2  

          Y1    SETS "WORLD!"

          Y2    SETS "LELLO,$Y1" ;將字符串Y2的值替換$Y1,形成新的字符串

  3)、定義一個寄存器(RN)    

         格式:名稱 RN 表達式    

               RN 偽指令用于給一個寄存器定義一個別名。

         示例:    

               Temp RN R0 ;將R0 定義一個別名Temp    

  

  4) 定義寄存器列表(RLIST)    

    格式:名稱 RLIST { 寄存器列表 }  

          用于對一個通用寄存器列表定義名稱,使用該偽指令定義的名稱可在 ARM 指令 LDM/STM 中使用。

          在LDM/STM指令中,寄存器列表中的寄存器訪問次序總是先訪問編號較低的寄存器,再訪問編號較高的寄存器,而不管寄存器列表中各寄存器的排列順序。

    

    示例:RegList RLIST {R0-R5 , R8 , R10} ;將寄存器列表名稱定義為 RegList,用于多寄存器尋址(后面詳解)

    

  5) 定義協處理器寄存器(CN)

     格式:名稱 CN  協處理器的寄存器編號

     

     示例:Power CN 6   ;將協處理器的寄存器6名稱定義為Power

  6) 定義協處理器(CP)

     格式名稱 CP  協處理器名

     示例:Dmu CP 6 ;將協處理器6名稱定義為Dmu 

  7) 定義浮點或精度寄存器(DN,SN,FN)

     格式:名稱 DN 雙精度寄存器編號   ;DN 為雙精度VFP寄存器定義名稱

     格式:名稱 SN 單精度寄存器編號   ;SN 為單精度VFP寄存器定義名稱

     格式:名稱 FN 浮點寄存器編號     ;FN 為浮點寄存器定義名稱

           

     示例:

     height DN  6  ;VFP雙精度寄存器6名稱定義為height

     width  SN  16 ;VFP單精度寄存器16名稱定義為width

     height FN  6  ;將浮點寄存器6名稱定義為height

   

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    

3.數據定義偽操作(申請內存)

    數據定義偽指令用于為特定的數據分配存儲單元,同時可完成已分配存儲單元的初始化。

    

    1) 按類型分配內存

       格式:標號 偽指令 表達式

          標號 

          表達式:初始化的值,表達式可以為程序標號或字符、數字表達式

          偽指令:如下

            (1) DCB   用于分配一片連續的字節存儲單元(字符數組),可用 =號代替

                Str DCB "This is a test!" ;分配一片連續的字節存儲單元并初始化。 

            (2) DCW(DCWU) 用于分配一片連續的半字存儲單元(16位 短整型數組)DCW 半字對齊,DCWU不嚴格半字對齊。

                DataTest DCW 1,2,3

            (3) DCD(DCDU) 用于分配一片連續的字存儲單元(32位,整型數組),DCD 可用 &代替,DCD字對齊的

                DataTest DCD 4,5,6

            (4) DCFD(DCFDU) 為雙精度的浮點數分配一片連續的字存儲單元,每個雙精度的浮點數占據兩個字單元。

                FDataTest DCFD 2E115,-5E7

            (5) DCFS(DCFSU) 為單精度的浮點數分配一片連續的字存儲單元,每個單精度的浮點數占據一個字單元。

                FDataTest DCFS 2E5,-5E-7

            (6) DCQ(DCQU) 用于分配一片以 個字節為單位的連續存儲區域(8字節為一個數據的數組)

                DataTest DCQ 100 ;分配一片連續的存儲單元并初始化為指定的值100

                

     2)申請連續內存

        (1) 申請一個連續內存(SPACE

             用于分配一片連續的存儲區域并初始化為 0,可用%代替

              格式標號 SPACE 表達式

                    表達式為要分配的字節數

                    

              例:DataSpace SPACE 100  ;分配連續 100 字節的存儲單元并初始化為 。    

       

        (2)聲明一個數據緩沖池的開始(LTORG)

              通常,把數據緩沖池放在代碼段的最后面,下一個代碼段之前,或END之前

              

           示例:           

              AREA Example,CODE,READONLY ;聲明代碼段Example

              ENTRY                      ;程序入口

           Start

              BL funcl

           funcl    

              LDR R0,=0x12345678

              ADD R1,R1,R0

              MOV PC,LR

              LTORG           ;定義緩沖池0x12345678 LTORG根據LDR確定內存地址

           data SPACE 4200     ;從當前位置開始分配4200字節內存

              END

        (3)定義一個結構化的內存表首地址(MAP

           格式:  MAP 表達式 ,基址寄存器 

                  用于定義一個結構化的內存表的首地址。 可用 代替。

                  表達式可以為程序中的標號或數學表達式,基址寄存器為可選項.

                  ** 當基址寄存器選項不存在時,表達式的值即為內存表的首地址,

                  ** 當該選項存在時,內存表的首地址為表達式的值與基址寄存器的和。    

           例:

              Datastruc   SPACE    280             ;分配280個字節單元

              MAP         Datastruc                ;內存表的首地址為 Datastruc內存塊

           

        (4) 定義一個結構化內存表的數據域(FILED

            用于定義一個結構化內存表中的數據域。可用#代替

            格式:標號 FILED  表達式

                   FIELD 偽指令常與 MAP 偽指令配合使用來定義結構化的內存表。表達式的值為當前數據域所占的字節數。

                   標號為數據域(字段、成員變量)名  

                   ** MAP 偽指令定義內存表的首地址, 

                   ** FIELD 偽指令定義內存表中的各個數據域,并可以為每個數據域指定一個標號供其他的指令引用。                (3)內存首地址 (MAP)

            MAP 偽指令通常與 FIELD 偽指令配合使用來定義結構化的內存表。    

           示例:    

              Datastruc   SPACE    280             ;分配280個字節單元

              MAP         Datastruc                ;內存表的首地址為 Datastruc內存塊

              consta      FIELD    4               ;字段consta長度4字節,相對地址0

              constab     FIELD    4               ;字段constab長度4字節,相對地址4

              x           FIELD    8               ;字段x長度8字節,相對地址8

              y           FIELD    8               ;字段y長度8字節,相對地址16

              string      FIELD    256             ;字段string長度256字節,相對地址24

              LDR  R6[R9,consta] ;引用內存表中的數據域

           注意:MAP偽操作和FIELD偽操作僅僅是定義數據結構,他們并不實際分配內存單元,而SPACE用于分配內存

          

4.匯編控制偽操作

    用于控制匯編程序的執行流程,常用的匯編控制偽指令包括以下幾條:

    

    (1) IF 邏輯表達式...ELSE...ENDIF 條件控制

    (2) WHILE 邏輯表達式 ...WEND 循環控制   

    

    例:條件編譯

          AREA Example,CODE,READONLY 

          CODE32                     

    Data_in *   100              ;定義標號Data_in的值為100 在 ENTRY入口之前

          GBLA count             ;定義全局變量

    count SETA 20            

          ENTRY                      

    Start

          IF count < Data_in      ;條件編譯

             MOV R0,#3

          ELSE                   

             MOV R1,#24

          ENDIF

          MOV R1,#12

          ADD R0,R0,R1      

          

          END 

   例:循環編譯

       GBLA Counter ;聲明一個全局的數學變量,變量名為 Counter    

       Counter SETA 3 ;由變量Counter 控制循環次數    

       ……    

       WHILE Counter < 10    

           指令序列    

           IF continue 

              MEXIT ;退出宏

           ENDIF

       WEND  

5. 其他常用的偽指令

    1)AREA    

       格式:AREA 段名 屬性 ,屬性 ,……

       AREA 偽指令用于定義一個代碼段或數據段。其中,段名若以數字開頭,則該段名需用 " | " 括起來,如 |1_test| 

       

       屬性字段表示該代碼段(或數據段)的相關屬性,多個屬性用逗號?

       — CODE 屬性:用于定義代碼段,默認為 READONLY 。    

       — DATA 屬性:用于定義數據段,默認為 READWRITE 。    

       — READONLY 屬性:指定本段為只讀,代碼段默認為 READONLY 。    

       — READWRITE 屬性:指定本段為可讀可寫,數據段的默認屬性為 READWRITE 。    

       — ALIGN 屬性:使用方式為 ALIGN 表達式。在默認時, ELF (可執行連接文件)的代碼段和數據段是按字對齊的

       — COMMON 屬性:定義一個通用的段,不包含任何的用戶代碼和數據。各源文件中同名的 COMMON 段共享同一段存儲單元

       

       一個匯編語言程序至少要包含一個段,當程序太長時,也可以將程序分為多個代碼段和數據段。    

       使用示例:    

       AREA Init , CODE , READONLY    

       該偽指令定義了一個代碼段,段名為 Init ,屬性為只讀    

    2)、 ENTRY    

       格式:ENTRY    

             用于指定匯編程序的入口點。一個源文件里最多只能有一個 ENTRY (可以沒有)

    3)、 END    

       格式:END    

             用于通知編譯器已經到了源程序的結尾。

             

    4)、 CODE16CODE32    

         格式:CODE16 (或 CODE32 )    

    

         CODE16 偽指令通知編譯器,其后的指令序列為 16 位的 Thumb 指令。    

         CODE32 偽指令通知編譯器,其后的指令序列為 32 位的 ARM 指令。    

         

         在使用 ARM 指令和 Thumb 指令混合編程的代碼里,可用這兩條偽指令進行切換

         示例:    

         AREA Init , CODE , READONLY    

         CODE32                        ;通知編譯器其后的指令為 32 位的 ARM 指令    

         LDR R0 ,= NEXT + 1  ;將跳轉地址放入寄存器 R0    

         BX R0                 ;程序跳轉到新的位置執行,并將處理器切換到 Thumb 工作狀態    

         ……    

         CODE16                ;通知編譯器其后的指令為 16 位的 Thumb 指令    

         NEXT LDR R3,=0x3FF    

         ……    

         END                   ;程序結束 

    

    5)、 EXPORT(GLOBAL)    

         格式:EXPORT 標號

               export 偽指令用于在程序中聲明一個全局的標號,該標號可在其他的文件中引用。

    6)、 IMPORT    

         格式:IMPORT 標號     

               IMPORT 偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用

               如果當前源文件實際并未引用該標號,該標號也會被加入到當前源文件的符號表中。

               

    7)、 EXTERN    

         格式:EXTERN 標號     

               EXTERN 偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用

               如果當前源文件實際并未引用該標號,該標號就不會被加入到當前源文件的符號表中。

   

    8)、 GET(INCLUDE)    

         格式:GET 文件名    

         GET 偽指令用于將一個源文件包含到當前的源文件中,并將被包含的源文件在當前位置進行匯編處理。

         

         匯編程序中常用的方法是在某源文件中定義一些宏指令,用 EQU 定義常量的符號名稱,用 MAP和 FIELD 定義結構化的數據類型,然后用 GET 偽指令將這個源文件包含到其他的源文件中。使用方法與 語言中的 "include" 相似。

         GET 偽指令只能用于包含源文件,包含目標文件需要使用 INCBIN 偽指令 

            

        示例:    

            AREA Init , CODE , READONLY    

            GET a1.s     ;通知編譯器當前源文件包含源文件a1.s    

            GET Ca2.s ;通知編譯器當前源文件包含源文件C a2.s ……    

            END    

    9)、 INCBIN    

         格式:INCBIN 文件名

            INCBIN 偽指令用于將一個目標文件或數據文件包含到當前的源文件中

        示例:    

            AREA Init , CODE , READONLY    

            INCBIN a1.dat ;通知編譯器當前源文件包含文件a1.dat    

            INCBIN Ca2.txt ;通知編譯器當前源文件包含文件Ca2.txt……    

            END   

                

    10)ROUT    

        格式: 名稱 } ROUT    

              ROUT 偽指令用于給一個局部變量定義作用范圍。

              在程序中未使用該偽指令時,局部變量的作用范圍為所在的 AREA 

              而使用 ROUT 后,局部變量的作為范圍為當前 ROUT 和下一個 ROUT 之間。

    11)、 ALIGN    

        格式:ALIGN { 表達式 ,偏移量 }}    

              ALIGN 偽指令可通過添加填充字節的方式,使當前位置滿足一定的對其方式 

              表達式的值用于指定對齊方式,可能的取值為 的冪,如 、 、 、 、 16 等。

              偏移量也為一個數字表達式,若使用該字段,則當前位置的對齊方式為: 的表達式次冪+偏移量。    

        示例:

              AREA Init , CODE , READONLY , ALIEN = ;指定后面的指令為 字節對齊。 

    

.ARM匯編偽指令(讀取內存地址)

  1)ADRADRL

    將PC相對偏移的地址或基于寄存器相對偏移的地址值讀取到寄存器中

       格式:ADR(ADRL) 寄存器,地址表達式

             ADR 小范圍的地址讀取偽指令,

             ADRL 中等范圍的地址讀取偽指令

       例:

       查表

          ADR     R0,D_TAB     ;加載轉換表地址

          LDRB   R1,[R0,R2]    ;使用R2作為參數,進行查表

               ……

       D_TAB

          DCB   0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92

      

  2)LDR

    用于加載32位立即數或一個地址值到指定的寄存器,大范圍的地址讀取偽指令.

    LDR通常都是作加載指令,但是它也可以作偽指令。作用是裝載一個32bit常數和一個地址到寄存器。

       

       格式:LDR  寄存器,=地址表達式

       COUNT EQU       0x56000054  ;COUNT是一個變量,地址為0x56000054

             LDR       R1,=COUNT   ;COUNT這個變量的值(地址),也就是0x56000054放到R1中。

             MOV       R0,#0

             STR       R0,[R1]     ;是一個典型的存儲指令,將R0中的值放到以R1中的值為地址的存儲單元去

       ;這三條指令是為了完成對變量COUNT賦值。

  3)NOP     

    空操作偽指令,可用于延時操作

       例:延時子程序

       Delay

              NOP       ;空操作

              NOP

              NOP

              SUBS   R1,R1,#1   ;循環次數減1

              BNE    Delay

              MOV    PC,LR

六、Thumb指令集

   有興趣的自學

   

//************************************* arm體系結構 第五天***************************************

七、C語言與匯編混合編程

    完全使用匯編語言來編寫程序會非常的繁瑣

    通常,只是使用匯編程序來完成少量必須由匯編程序才能完成的工作,而其它工作則由C語言程序來完成。

()ATPCS規則  

    混合編程中,雙方都須遵守ATPCS規則。這些基本規則包括:

    子程序調用過程中寄存器的使用規則、數據棧的使用規則和參數的傳遞規則。

1.寄存器使用規則

    寄存器:  R4-R11用來保存局部變量

        R0-R3 a1-a4)用于保存參數/返回結果/scratch(臨時寄存器)

        R4-R11v1-v8)用于保存ARM狀態局部變量

        R12IP) 子程序內部調用的scratch

        R13SP) 數據棧指針

        R14LR) 連接寄存器

        R15PC) 程序計數器

    

    R7 又可稱為 wr 用于Thumb狀態工作寄存器

    R9 又可稱為 sb 在支持RWPI ATPCS中為靜態基址寄存器

    R10又可稱為 sl 在支持PWPI ATPCS中為數據棧限制指針

    R11又可稱為 fp 用于幀指針

              

2.數據棧使用規則

    ATPCS標準規定,數據棧為FD(滿遞減類型),并且對數據棧的操作是8字節對齊。在進行出棧和入棧操作,則

必須使用ldmfdstrnfd指令(或ldmiastmdb)    

3.參數的傳遞規則

    參數:    參數小于等于4,用R0-R3保存參數,參數多于4,剩余的傳入堆棧

    函數返回:結果為32位整數,通過R0返回

              結果為64位整數,通過R0R1返回

              對于位數更多的結果,通過內存傳遞

    例:參數傳遞及結果返回(r0-r3做參數,r0做返回值)

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start 

        MOV  R3,#4                 ;設置實參,將參數寫入R0-R3

        MOV  R2,#3

        MOV  R1,#2

        MOV  R0,#1    

        BL   func1                 ;調用子程序ADD_SUM

        B    OVER                  ;跳轉到OVER標號處,進入結尾

    func1

        ADD  R0,R0,R1              ;實現兩數相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0內為返回的結果

    OVER

        END

        

    相當于如下C語言

    int func1(int a,int b,int c,int d){

        return a+b+c+d;

    }

    int main(){

        func1(1,2,3,4);

    }    

    例:多于4個參數,前4個通過寄存器R0-R3傳遞,其它參數通過數據棧傳遞

        AREA Example,CODE,READONLY ;聲明代碼段Example

        ENTRY                      ;程序入口

    Start 

        STMFD SP!,{R1-R4,LR}       ;先將R1-R4,及LR內原有數據壓入棧,需要使用這五個寄存器

        MOV  R0,#1                 ;準備好7個寄存囂存入7個數據 LR,IP,R4作臨時寄存器使用

        MOV  IP,#2

        MOV  LR,#3

        MOV  R4,#4

        MOV  R1,#5

        MOV  R2,#6

        MOV  R3,#7

        STMFD SP!,{R1-R3}          ;先將R1-R3數據從前向后入棧,然后將IP,LR,R4內的數據裝入R1-R3

        MOV  R3,R4                 ;其中IP,LR,R4 是臨時使用的寄存器

        MOV  R2,LR

        MOV  R1,IP     

        BL   func1                 ;調用子程序funcl R0是返回結果

        LDMFD SP!,{R1-R4,PC}       ;從棧中取出最初的數據,恢復原始值

        B    OVER                  ;跳轉到OVER標號處,進入結尾

    func1

        STMFD SP!,{R4,LR}          ;當調用函數時,LRR4都已發生了變化,其中LR是指令地址所以也壓入棧

        LDR  R4,[SP,#0x10]         ;目前共壓入5個數據,每一個數據占兩個字節,當前棧頂偏移10為前5個數據7

        ADD  LR,SP,#8              ;將前第4個數據的地址(棧項+偏移)賦給LR

        LDMIA LR,{IP,LR}           ;連續將LR地址處的兩個數據取出寫入IPLR內,從右向左寫,LDMIA即出棧指令

        ADD  R0,R0,R1              ;從此行開始相當于return a+b+c+d+e+f+g;   

        ADD  R0,R0,R2

        ADD  R0,R0,R3

        ADD  R0,R0,IP

        ADD  R0,R0,LR

        ADD  R0,R0,R4    

        LDMFD SP!,{R4,PC}          ;從棧內取數據加載入R4PC,PC跳轉回主函數

    OVER

        END        

     

    下面是棧頂 

        

    相當于如下C語言

    int func1(int a,int b,int c,int d,int e,int f,int g){

        return a+b+c+d+e+f+g;

    }

    int main(){

        int a=1,b=2,c=3,d=4,e=5,f=6,g=7;

        func1(a,b,c,d,e,f,g);

    }        

()CARM匯編程序間的相互調用

1. 匯編程序調用C子程序

   為保證程序調用時參數正確傳遞,必須遵守ATPCS

   在C程序中函數不能定義為static函數。在匯編程序中需要在匯編語言中使用IMPORT偽操作來聲明C子函數

   

   //C代碼

   int sum5(int a, int b ,int c, int d)

   {

       return (a+b+c+d);

   }

      

   //匯編代碼

         AREA Example,CODE,READONLY ;聲明代碼段Example

         IMPORT sum5                     ;

         ENTRY                      ;程序入口

   Start 

         MOV  R3,#4                 ;設置實參,將參數寫入R0-R3

         MOV  R2,#3

         MOV  R1,#2

         MOV  R0,#1    

         BL   sum5                 ;調用子程序sum5

         B    OVER                  ;跳轉到OVER標號處,進入結尾

   OVER

         END

        

2. 匯編程序訪問全局C變量

   匯編程序中可以通過C全局變量的地址來間接訪問C語言中定義的全局變量

   在編編程序中用IMPORT引入C全局變量,該C全局變量的名稱在匯編程序中被認為是一個標號。通過ldrstr指令訪問該編號所代表的地址

   //C代碼    

   int i=3;

   int sum5(int a, int b ,int c, int d)

   {

       return (a+b+c+d+i);

   }   

   //匯編代碼   

         AREA Example,CODE,READONLY ;聲明代碼段Example

         IMPORT sum5                     

         IMPORT i

         ENTRY                      ;程序入口

   Start 

         LDR  R1,i                  ;i讀入R1

         MOV  R0,#2

         ADD  R0,R0,R1

         STR  R0,i                  ;將寄存器值寫入i

         MOV  R3,#4                 ;設置實參,將參數寫入R0-R3

         MOV  R2,#3

         MOV  R1,#2

         MOV  R0,#1    

         BL   sum5                 ;調用子程序ADD_SUM

         B    OVER                  ;跳轉到OVER標號處,進入結尾

   OVER

         END

  

3. C語言中調用匯編子程序

   為保證程序調用時參數的正確傳遞,在匯編程序中需要使用EXPORT偽操作來聲明匯編子程序,同時在C語言中使用extern擴展聲明匯編子程序。

   

   //匯編代碼

        EXPORT func1               ;func1為子函數名

        AREA Example,CODE,READONLY ;聲明代碼段Example

   func1

        ADD  R0,R0,R1              ;實現兩數相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0內為返回的結果

        END

   //C代碼

   extern int func1(int a,int b,int c,int d);

   int main(int argc,char **argv){

       int a=1,b=2,c=3,d=4;

       int z=func1(a,b,c,d);

       printf("%d",z);   

       return 0;

   }

4. C語言中調用匯編全局變量

   匯編中用DCD為全局變量分配空間并賦值,并定義一個標號代表該存儲位置。

   在匯編中用EXPORT導出標號(這個標號就是全局變量),在C程序中用extern擴展聲明名該變量

   

   //匯編代碼 

        EXPORT func1

        EXPORT tmp

        AREA Example,CODE,READONLY ;聲明代碼段Example

   tmp              ;全局變量名

        DCD 0x0005  ;全局變量創建內存空間及賦初值      

   func1            ;子函數名

        ADD  R0,R0,R1              ;實現兩數相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0內為返回的結果

        END

   //C代碼     

   extern int func1(int a,int b,int c,int d);

   extern int tmp;

   int main(int argc,char **argv){

       int a=1,b=2,c=3,d=4;

       int z=func1(a,b,c,tmp);

       printf("%d",z);   

       return 0;

   }

5. C語言中內嵌匯編

   有些操作C語言程序無法實現,如改變CPSP寄存器值,初始化堆棧指針寄存器SP等,這些只能由匯編來完成。

   但出于編程簡潔等一些因素,有時需要在C源代碼中實現上述操作,此時就需要在C中嵌入少量匯編代碼。

   內嵌的匯編不能直接引用C的變量定義,必須通過ATPCS進行,語法格式如下:

      __asm{ 

        //內嵌匯編

      }

      

   例:在C語言中嵌入匯編

   int f(){ //C函數

      __asm{ //內嵌匯編,禁用中斷例子

         MRS R0,CPSR

         ORR R0,R0,#0x80

         MSR CPSR_c,R0

      }

      

   }

   int main(int argc,char **argv){

      int a;

      int z=f(a);

      printf("%d",z);

      return 0;

   }

   

   出地完整性考慮,內嵌匯編相對于一般匯編的不同特點如下:

   1)操作數可以是寄存器、常量或C表達式。可以是charshort、或int類型,而且是無符號數進行操作

   2)常量前的#號可以省略

   3)只有指令B可以使用C程序中的標號,指令BL不可以使用

   4)不支持匯編語言中用于內存分配的偽操作

   5)內嵌匯編不支持通過“.”指示符或PC獲取當前指令地址

   6)不支持LDR Rn,=expression 偽指令,而使用MOV Rn,expression指令向寄存器賦值

   7)不支持標號表達式

   8)不支持ADRADRL偽指令

   9)不支持BXBLX指令

   10)不可以向PC賦值

   11)使用0x前綴替代 &表示十六進制數

   12)不使用寄存尋址變量

   13)ldmstm指令的寄存器列表只允許物理寄存器

   14)必須小心使用物理寄存器,如R0-R3,LRPC

//------------------------------------------

()、裸機硬件的控制方法

1.裸板開發環境搭建

    1)J-Link

      (1)安裝Setup_JLinkARM_V408i.exe

      (2)連接開發板

         1 打開桌面快捷 J-Flash ARM V4.08i

         2 連接好開發板 開發板->jlink->pc(usb)

         3 將開發板置為NorFlash啟動

         4 打開菜單 file->open project->選擇TQ2440.jflash

         5 填加配置選項

           將Flash.csv復制到 安裝目錄的ETCJFlash

           打開菜單 options->project settings

           在彈出對話框內選 flash 后,點擊按鈕select flash device

           在彈出對話框內選擇 EN29LV160AB

         6.連接開發板

           重啟開發板,然后點擊菜單 target->connect 查看聯接信息

      (3)燒寫方法

         將j-link連接好后,在菜單 file->open內選擇要燒寫的程序。

         如:燒寫u-boot.bin

             然后在燒寫地址對話框內輸入燒寫地址,u-boot的地址設為0

             再點擊菜單 target->program進行燒寫

      (4)調試方法

         連接JLINK和開發板。

         打開程序 J-LINK ARM V4.08i->J-link GDB Server

                  設置信息 JTAG speed 500KH

                           所有選項勾選

         設置AXD調試環境 options->configure target填加JlinkARM目錄下的JLinkRDI.dll

                 然后在AXD內選則JLinkRDI.dll選項,同時點擊右側configure按鈕

                 在彈出對話框內General標簽:JTAG speed設為4000Khz

                               flash標簽  去掉 Enable flash programming選項

                               Breakpoints: 去掉 Use software breakpoints選項

                               CPU標簽    :勾選 Allowinstruction set simulation

                                                 Little endian

                                                 Reset strategy內選

                                                 Hardware,halt after reset(normal)1000ms

                 在AXD內,通過File->Load Image載入要調試的axf文件                                

                                                 

                 

                                    

()、軟件控制硬件的編程原理

   每一種硬件,在其控制器芯片上都有物理寄存器(不是CPU寄存器,是硬件上的寄存器)

   這些寄存器分為三類:命令寄存器、狀態寄存器、數據寄存器

   程序控制硬件的方法是,通過匯編str指令向命令寄存器寫入指令即可完成對硬件的配置操作或執行物理操作。

                         通過匯編ldr指令從數據寄存器中獲取數據,或從狀態寄存器獲取狀態。

   

   程序控制硬件,其實質就是對硬件的寄存器進行讀寫操作。

   

   程序中需要解決的問題:

   1)硬件寄存器的內存地址是多少?

   2)向哪一個寄存器寫入什么值?

   

   如想解決上述兩個問題,需要熟練查看硬件的手冊,閱讀硬件連線原理圖。

   

   數電的基本知識:

   1).電路符號

   2).基本原理

   

   以LED燈為例講解如何使用硬件手冊,和原理圖

   在開發板上有4LED燈,如果想點亮,必須先看硬件連線圖

   

1. 查看開發板LED管腳連線

   TQ2400開發板有兩塊電路板:核心板、主板

   LED燈在主板上,所以查看《TQ2440底板原理圖.pdf》,找到LED模塊(LED測試)

   從電路圖可看到如果讓LED1燈點亮,必須在nLED_1連線上輸出低電屏(即通電)

   

   如何讓LED1上輸出低電屏,需查看CPU相關引腳連線

2. 查看TQ2440核心板原理圖

   CPU在核心板上,所以查看《TQ2440_V2核心板原理圖.pdf

   找到nLED1連線,如下

       nLED1 CUPGPB5引腳上

       nLED2        GPB6

       nLED3        GPB7

       nLED4        GPB8

   

   如何讓CPUGPB5為低電屏,需查看CPU引腳模式

3.查看CPU用戶手冊

  TQ2440的微處理器芯片為S3C2440,所以查看《S3C2440.pdf》芯片手冊,找到 GPB5的管腳模式

  1)控制寄存器

      GPB5 [11:10] 00 = Input   01 = Output   

                   10 = nXBACK 11 = Reserved

      

      GPBCON 控制寄存器

      GPBDAT 數據寄存器

      GPBUP  上拉使能寄存器

      

      [11:10] 是所在位,每個管腳都是占兩個位

         

      其中 Input  是輸入值

           Output   輸出值

           reserved 保留值

           nXBACK  nXBACK/GPB5   

           

      即,如果將GPB5設為低電屏,則需要將控制寄存器設置為輸出模式。

          即需要CPU寄存器GPBCON的 1110位設置為 01  11位為0  10位為1

  2)數據寄存器

      GPB[10:0] [10:0] 當端口配置為輸入端口時,相應位為引腳狀態。

                       當端口配置為輸出端口時,相應位為引腳狀態。

                       當端口配置為功能引腳時,將讀取到未定義值  

      所以,當GPBCON為輸入狀態時,GPBDAT的相應位5則應為0才會輸出低電屏

  3)查看寄存器地址

      GPBCON 0x56000010 R/W  端口配置寄存器        復位值 0x0

      GPBDAT 0x56000014 R/W  端口數據寄存器        復位值 -

      所以,需要將地址位0x56000010的寄存器作為GPBCON寄存器

                  地址位0x56000014的寄存器作為GPBDAT寄存器

4.代碼編寫

   //ledtest.c

     #define GPBCON (*(volatile unsigned long *)0x56000010)

     #define GPBDAT (*(volatile unsigned long *)0x56000014)

     //volatile 影響編譯器編譯的結果,表示變量是隨時可能發生變化的

     //0x56000010是地址,強行轉換為unsigned long *類型,

     //然后前面加*代表這個地址所指向的寄存器變量

     #define LEDS (1<<5 | 1<< 6 | 1<< 7| 1<<8)  //即 11110000

     #define DELAYVAL (0xFFFF)

     

     extern int delay(int time); //聲明匯編函數

     int main(){

         int val;

         int i=0;

      GPBCON = (1<< 16 | 1<< 14 | 1<< 12 | 1<< 10); //010101010000000000 B5 B6 B7 B8的狀態寄存器

      val =val +1;

      for(i=0;i<4;i++){

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<6 | 1 << 7 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 7 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 6 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 6 | 1 << 7);

          delay(DELAYVAL);

      }

      return 0;

     }

   //delay.s

     ;匯編指令延時程序                                 

      EXPORT delay                                     

      AREA DELAY,CODE,READONLY                                      

     ;下面是延時子程序                                       

     delay                                                                   

      sub R0,R0,#1    ;r0=r0-1                             

      cmp R0,#0x0     ;r00比較                                       

      bne delay       ;比較的結果不為0,則繼續調用delay              

      mov PC,LR       ;返回                            

                                                       

      END                                              

   

5.調試代碼

     

   2440ART.mcp內雙擊

            TargetSettins:   post-linker選擇ArM fromELF

            Language Settins:Architecture or Processor選擇相應的編譯器ARM920T

            Arm Linker:output RO 0x30000000

                       optionsImage entry point 設為0x30000000 

                       layout Object 2440init.o Section Init   

                       Listings Image map

            Arm fromELF: output format內 Plain binary

                         output filename***.bin             

        編譯

            make

        調試  AXD是調試器

            設置,debug ->打開AXD調試界面,選擇option->config target 選項

                 選ARMUL(模擬調試器),然后選擇確定.進入調試界面.

                 ARMUL是虛擬調試環境(虛擬開發板)

                 如果用開發板真實環境調試,則需要使用JTAG連開發板后,在此處選H-JTAG

            用file-<lode image項,加載剛才編譯所的的 .axf調試文件.

                 execute->run to cousor 項.使程序進入用戶主程序

                 

            可以用F8來一條一條執行語句,也可用F10,可以設置斷點.  

 

arm匯編編程(示例)

七星彩走势图2元网官网