官术网_书友最值得收藏!

3.4 匯編語言的程序結構及在ADS環境下調試

在ARM(Thumb)匯編語言程序中,以程序段為單位組織代碼。段是相對獨立的指令或數據序列,具有特定的名稱。段可以分為代碼段和數據段,代碼段的內容為執行代碼,數據段存放代碼運行時需要用到的數據。一個匯編程序至少應該有一個代碼段,當程序較長時,可以分割為多個代碼段和數據段,多個段在程序編譯鏈接時最終形成一個可執行的映象文件。

3.4.1 匯編語言程序結構

以下是一個匯編語言源程序的基本結構。

      AREA  Init, CODE, READONLY
      ENTRY
      ……
      END
      ;匯編語言源程序的基本結構,分號為注釋
      AREA  EX2, CODE, READONLY
      ;AREA指令定義一個名為EX2程序段,屬性為只讀

例3.1 定義一個代碼段arm,其屬性為只讀,首先分別給3個變量賦值,給x、y賦兩個值,給stack_top賦一個地址,程序代碼如下,請閱讀程序寫出最后r0的值及調試程序及地址0x1000上的內容,程序名為ex3_1.s。

        AREA  arm , CODE , READONLY ;要有空格
       x  EQU 45   ; 不能有空格
       y  EQU 64
       stack_top EQU 0x1000
       ENTRY
        MOV sp, #stack_top
            MOV r0, #x
            STR r0, [sp]
            MOV r0, #y
            LDR r1, [sp]
            ADD r0, r0, r1
            STR r0, [sp]

      Stop
       B Stop
          END

注意:

標簽必須在一行的開頭頂格寫,不能有空格;

ARM指令前要留有空格,可全部大寫或小寫,但不要大小寫混合使用;

注釋使用“; ”。

3.4.2 匯編語言編輯、運行與調試

(1)運行ADS1.2集成開發環境,點擊File|New,在New對話框中,選擇Project中的ARM Executable Image選項,在Project name欄中輸入項目的名稱ex3_1,點擊“確定”按鈕保存項目,如圖3.6所示。

圖3.6 輸入工程名

(2)在新建的工程中,選擇Debug版本,如圖3.7所示,使用Edit|Debug Settings菜單對Debug版本進行參數設置。

圖3.7 選擇Debug

(3)在圖3.8中,點擊Debug Setting按鈕,彈出如圖3.9所示的窗口,選中Target Setting項,在Post-linker欄中選中ARM fromELF項,如圖3.9所示。按OK確定。這是為生成可執行的代碼的初始開關。

圖3.8 點擊Debug Settings圖標

圖3.9 選擇ARM from ELF

(4)在圖3.10中,點擊ARM Assembler,在Architecture or Processer欄中選ARM9TDMI,這是要編譯的CPU核。

圖3.10 選擇設備的處理器

(5)在圖3.11中,點擊ARM linker,在output欄中設定程序的代碼段地址,以及數據使用的地址。圖中的RO Base欄中填寫程序代碼存放的起始地址,RW Base欄中填寫程序數據存放的起始地址。該地址是屬于SDRAM的地址。

圖3.11 存儲器讀寫地址設置

在Options欄中,如圖3.12所示,Image entry point要填寫程序代碼的入口地址,其他保持不變,如果是在SDRAM中運行,則可在0x0c000000—0x0cffffff中選值,這是16MSDRAM的地址,但是這里用的是起始地址,所以必須把你的程序空間給留出來,并且還要留出足夠的程序使用的數據空間,而且還必須是4字節對齊的地址(ARM狀態)。通常入口點Image entry point為0xc100000, ro_base也為0xc100000。

圖3.12 程序執行入口地址設置

在Layout欄中,如圖3.13所示,在Place at beginning of image框內,需要填寫項目的入口程序的目標文件名,如,整個工程項目的入口程序是ex3_1.s,那么應在Object/Symbol處填寫其目標文件名ex3_1.o,在Section處填寫程序入口的起始段標號。它的作用是通知編譯器,整個項目的開始運行是從該地址段開始的。

圖3.13 輸入目標文件名及標號

(7)在圖3.14中,在Debug Setting對話框中點擊左欄的ARM fromELF項,在Output file name欄中設置輸出文件名*.bin,前綴名可以自己取,在Output format欄中選擇Plain binary,這是設置要下載到Flash中的二進制文件。圖3.14中使用的是ex3_1.bin.

圖3.14 輸入可在開發板上執行的文件名

(8)輸入匯編源程序

點擊File|New,在New對話框中,選擇File選項,在File name欄中輸入文件名ex3_1.s,如圖3.15所示,點擊“確定”按鈕后輸入源程序并保存。

圖3.15 輸入匯編程序名

(9)裝載文件ex3_1.s

在如圖3.16所示工程ex3_1.mcp中File的空白處點擊右鍵,選擇菜單項Add Files,導入文件ex3_1.s,并按圖3.17的選項,點擊按鈕[ OK ]。

圖3.16 導入文件ex3_1.s

圖3.17 選擇調試類型

(10)在ADS1.2集成開發環境(CodeWarrior for ARM Developer Suite)選擇菜單Project|Debug。

(11)如果是模擬調試,選擇AXD中菜單Options→Configure Target→選擇ARMUL,如圖3.18所示。

圖3.18 選擇仿真調試

ADS開發工具中分別支持兩種情況的目標調試。ARMUL目標環境配置,此是AXD鏈接到用軟件模擬的目標機;第二是選擇ADP目標環境配置,它是AXD使用Angel調試協議鏈接到開發板硬件進行的調試。

(12)程序調試

① 查看/修改存儲器內容

在AXD窗口中,點擊Processor Views|Memory,可以在Memory start address輸入存儲器的起始地址,查看存儲器某地址上的內容,雙擊某一數據,可以修改此存儲單元的內容。

② 在命令行窗口執行AXD命令

在AXD窗口中,點擊System Views|Command Line Interface,在提示符>下可以輸入命令進行調試。

③ 監視變量變化

在AXD窗口中,點擊Processor Views|Watch,用鼠標選中某變量,單擊鼠標右鍵,在彈出的菜單中選中Add to watch,此變量顯示在watch窗口中。

④ 設置斷點

將光標定位在要設置斷點的某語句處,按F9鍵。調試的結果如圖3.19所示,圖中顯示了斷點、Watch中寄存器的值、存儲器從起始地址0x1000開始的存儲內容。

圖3.19 程序ex3_1.s調試情況

思考:在ADS環境下調試下列源程序(ASM.S)代碼。

      rGPFCON  EQU  0x56000050
      rGPFDAT  EQU  0x56000054
      rGPFUP   EQU  0x56000058
      AREA  Init , CODE , READONLY
      ENTRY
      ResetEntry

      ldr  r0, =rGPFCON
      ldr  r1, =0x4000
      str  r1, [r0]
      ldr  r0, =rGPFUP
      ldr  r1, =0xffff
      str  r1, [r0]
      ldr  r2, =rGPFDAT
      ledloop
      ldr  r1, =0x1ffff
      str  r1, [r2]
      bl   delay
      ldr  r1, =0x0
      str  r1, [r2]
      bl   delay
      b  ledloop
      延時子程序
      delay
      ldr  r3, =0x1ffff
      delay1
      sub r3, r3, #1
      cmp r3, #0x0
      bne  delay1
      mov pc, lr
      END

3.5 匯編語言與C/C++的混合編程

在應用系統的程序設計中,若所有的編程任務均用匯編語言來完成,其工作量是可想而知的,同時,也不利于系統升級或應用軟件移植。事實上,ARM體系結構支持C/C++以及與匯編語言的混合編程。在一個完整的程序設計中,除了初始化部分用匯編語言完成以外,其主要的編程任務一般都用C/C++ 完成。

匯編語言與C/C++的混合編程通常有以下幾種方式:

(1)在C/C++代碼中嵌入匯編指令;

(2)在匯編語言程序和C/C++的程序之間進行變量的互訪;

(3)匯編語言程序、C/C++程序間的相互調用。

在以上的幾種混合編程技術中,必須遵守一定的調用規則,如物理寄存器的使用、參數的傳遞等,這對于初學者來說,無疑顯得過于煩瑣。在實際的編程應用中,使用較多的方式是:程序的初始化部分用匯編語言完成,然后用C/C++完成主要的編程任務,程序在執行時首先完成初始化過程,然后跳轉到C/C++程序代碼中,匯編語言程序和C/C++程序之間一般沒有參數的傳遞,也沒有頻繁的相互調用,因此,整個程序的結構顯得相對簡單,容易理解。

3.5.1 C語言程序調用匯編語言程序

1.在C語言中內嵌匯編

在C語言中內嵌的匯編指令包含大部分的ARM和Thumb指令,不過其使用方式與匯編文件中的指令有些不同,存在一些限制,主要有下面幾個方面:

(1)不能直接向PC寄存器賦值,程序跳轉要使用B或者BL指令;

(2)在使用物理寄存器時,不要使用過于復雜的C語言表達式,避免物理寄存器沖突;

(3)R12和R13可能被編譯器用來存放中間編譯結果,計算表達式值時可能將R0到R3、R12及R14用于子程序調用,因此要避免直接使用這些物理寄存器;

(4)一般不要直接指定物理寄存器,而讓編譯器進行分配。

(a)匯編指令以語句塊形式嵌入在C語言程序的函數中,其使用格式為:

          _asm
          {
        匯編語句
        }

(b)匯編指令以函數形式嵌入在C語言程序的函數中,其使用格式為:

          _asm int函數名(形式參數表)
          {
           匯編代碼
          }

例3.2 在C語言程序中嵌入匯編語句的例子,即將匯編指令以語句塊形式嵌入在C語言程序中。

      #include<stdio.h>
      int add(int i, int j)
      {
       int sum;
       _asm
       {

    ADD  sum, i, j
   }
    return sum;
  }
  int main()
  {
     int x, y;
     scanf("%d %d", &x, &y);
     printf("%d+%d=%d\n", x, y, add(x, y));
     return 0;
  }

請建立一個工程,調試上述程序。

例3.3 請在ADS環境中調試下列程序,程序表明如何在C語言程序中內嵌匯編語言。

  #include<stdio.h>
  void my_strcpy(const char *src, char *dest)
  {
  char ch;
  _asm
   {
  loop :
  ldrb ch , [src], #1
  strb ch , [dest], #1
  cmp ch, #0
  bne loop
   }
  }
  int main()
  {
  char *a = "forget it and move on! ";
  char b[64];
  my_strcpy(a, b);
  printf("original: %s", a);
  printf("copyed: %s", b);
  return 0;
  }

2.在C語言程序中調用以函數形式構成的匯編指令

C語言程序調用匯編語言程序時,匯編語言程序的書寫也要遵循ATPCS規則,以保證程序調用時參數正確傳遞。在C語言程序中調用匯編語言子程序的方法為:首先在匯編程序中使用EXPORT偽指令聲明被調用的子程序,表示該子程序將在其他文件中被調用;然后在C語言程序中使用extern關鍵字聲明要調用的匯編子程序為外部函數。

例如:在一個匯編源文件中定義了如下求和函數。

      EXPORT add ; //聲明add子程序將被外部函數調用
      ……
      add ; //求和子程序add
      ADD r0, r0, r1
      MOV pc, lr
      ……

在一個C語言程序的main()函數中對add匯編子程序進行了調用:

      extern int add (int x, int y); //聲明add為外部函數
      void main( )
      {
      int a=1, b=2, c;
      c=add(a, b); //調用add子程序
      ……
      }

當main()函數調用add匯編語言子程序時,變量a、b的值會給了r0和r1,返回結果由r0帶回,并賦值給變量c。函數調用結束后,變量c的值變成3。

例如:

建立文件add.s,代碼如下:

            EXPORT add
        AREA add, CODE, READONLY
        ENTRY
        ADD r0, r0, r1
        MOV pc, lr
        END
        C程序代碼為:
          #include<stdio.h>
        extern int add(int x, int y);
        int main()
        {
          int x, y;
          scanf("%d %d", &x, &y);
          printf("%d+%d=%d\n", x, y, add(x, y));

          return 0;
        }

程序調試方法

首先建立一個工程test。

按照圖3.20、圖3.21所示建立源程序main.c與add.s,請注意選中“Add to Project”,并分別輸入C語言源程序與匯編語言源程序。

圖3.20 編輯匯編文件add.s

圖3.21 編輯C程序文件main.c

3.在圖3.22中設置ARM Linker中Output的RO Base地址設為0x400000

圖3.22 程序調試

4.設置程序開始執行的地址,如圖3.23所示。在ARM Linker的Options標簽中

圖3.23 程序執行起始地址

5.如圖3.24所示,設置程序從main.o開始執行

圖3.24 設置起始執行程序main.o

6.執行菜單Procject下的make命令,編譯程序

7.執行程序run,如圖3.25所示

圖3.25 程序執行結果

3.5.2 匯編程序調用C語言程序

在匯編程序中調用C語言程序,格式較為簡單。其格式為:

BL C函數名

例如:

第一步:新建一個工程項目test3.mcp后再新建一個init.s匯編語言程序,這個程序是該項目文件的入口程序,程序代碼為:

        AREA   asm, CODE, READONLY
        IMPORT  add
        ENTRY
        LDR r0, =0x1
        LDR r1, =0x20
        LDR r2, =0x2
        BL      add             ; result saved in r0
        B .
        END

第二步:新建一個main.c程序,程序代碼為:

          int add(int a, int b, int c)
        {
           int sum=0, i;
           for(i=a; i<=b; i=i+c)
              sum=sum+i;
        return sum;
        }

第三步:在ADS1.2集成開發環境(CodeWarrior for ARM Developer Suite)選擇微處理器、RO Base地址、程序執行的首地址、程序開始執行的函數Init.o等環境參數。

第四步:選擇菜單Project|Make后,點擊Project|Debug,轉入AXD環境。

第五步:在AXD環境中,點擊Ecxute|Go,然后進行單步調試。

第六步:在調試過程中把變量r0、r1、r2、i、sum添加到Watch窗口,觀察這些變量的變化情況。

思考:請調試下列程序。

(1)建立文件init.s,代碼如下:

      AREA  Init , CODE, READONLY
      ENTRY
      ResetEntry
      IMPORT  Main
      EXPORT delay
      delay
      sub r0, r0, #1
      cmp r0, #0x0
      bne delay
      mov pc, lr
      END

(2)C語言程序代碼為:

      #include<stdio.h>
      #define rGPFCON ( * ( volatile unsigned *)0x56000050)
      #define rGPFDAT ( * ( volatile unsigned *)0x56000054)
      #define rGPFUP ( * ( volatile unsigned *)0x56000058)
      extern delay(int time);
      int main()
      {
        rGPFCON=0x4000 ;
        rGPFUP=0xffff ;
        while(1)
        {
          rGPFDAT=0xff ;
          delay(0xbffff);
          rGPFDAT=0x0 ;
          delay(0xbffff);
          rGPFUP=0xffff ;
        }
        return 0;
      }

思考與實驗

一、判斷題

1.ARM中有下列匯編語言語句:

ldr r0, =rGPFCON

表示將寄存器rGPFCON的內容存放到寄存器r0中。( )

2.ARM中有下列匯編語言語句:

ldr r1, =0x4000

表示將立即數0x4000加載到r1寄存器中。( )

3.ARM中有下列匯編語言語句:

str r1 , [r0]表示將r1中的數據存放到寄存器r0中。( )

4.ARM中有下列匯編語言語句:

ldr r1 , [r2]

表示將r2中的數據作為地址,取出此地址中的數據保存在r1中。( )

5.ARM中有下列匯編語言語句:

ldr r0 , [r1, #4]

表示將寄存器r1的內容加上4,然后把此數保存在寄存器r0中。( )

6.ARM中有下列匯編語言語句:

b ledon

表示調用子程序ledon。( )

7.ARM中有下列匯編語言語句:

sub r0, r0, #1

表示r0+1地址上的內容存放到寄存器r0中。( )

8.ARM中有下列匯編語言語句:

cmp r0, #x0

表示將r0的值與0進行比較。( )

9.ARM中有下列匯編語言語句:

ldr r0, =rGPFCON

str r1 , [r0]

表示將r0中的數據存放到寄存器r1中。( )

二、程序調試

1.在ADS中調試下列程序。

      /* main.c */
      #include <stdio.h>
      extern void asm_strcpy(const char *src, char *dest);
      int main()

      {
      const char *s = "seasons in the sun";
      char d[32];
      asm_strcpy(s, d);
      printf("source: %s", s);
      printf(" destination: %s", d);
      return 0;
      }
      ;匯編語言程序作為函數調用
      AREA asmfile, CODE, READONLY
      EXPORT asm_strcpy
      asm_strcpy
      loop
      ldrb r4, [r0], #1 address increment after read
      cmp r4, #0
      beq over
      strb r4, [r1], #1
      b loop
      over
      mov pc, lr
      END

2.在匯編和C之間通過定義全局變量實現數據傳送,請調試程序。

    main.c文件
    #include <stdio.h>
    int gVar_1 = 12;
    extern asmDouble(void);
    int main()
    {
    printf("original value of gVar_1 is: %d", gVar_1);
    asmDouble();
    printf(" modified value of gVar_1 is: %d", gVar_1);
    return 0;
    }
    匯編語言文件
    AREA asmfile, CODE, READONLY
    EXPORT asmDouble
    IMPORT gVar_1
    asmDouble

    ldr r0, =gVar_1
    ldr r1, [r0]
    mov r2, #2
    mul r3, r1, r2
    str r3, [r0]
    mov pc, lr
    END

3.閱讀下列匯編程序,并在ADS環境下上機調試。

    AREA   LDR_STR_LSL_LSR, CODE, READONLY
      ENTRY
     ;********************************************************************
     ********
    ;             加載/存儲指令以及移位指令
     ;********************************************************************
     ********
    start
    PRO1
    LDR R0, =0x0000
    LDR R1, =0x0004
    LDR R0, [R1]      ;將存儲器地址為R1的字數據讀入寄存器R0
    LDR R0, =0x0000
    LDR R1, =0x0004
    LDR R2, =0x0008
    LDR R0, [R1, R2]   ;將存儲器地址為R1+R2的字數據讀入寄存器R0
    LDR R0, =0x0000
    LDR R1, =0x0004
    LDR R2, =0x0008
     LDR R0, [R1], R2  ;將存儲器地址為R1的字數據讀入寄存器R0,并將新地址R1+R2寫入
     R1
    LDR R0, =0x0000
    LDR R1, =0x0004
    LDR R0, [R1, #8]   ;將存儲器地址為R1+8的字數據讀入寄存器R0
    AND R0, R0, #0     ;保持R0的0位,其于位清0
    LDR R1, =0X0004
     LDR R0, [R1, #8]! ;將存儲器地址為R1+8的字數據讀入寄存器R0,并將新地址R1+8寫
     入R1
    LDR R0, =0x0000
    LDR R1, =0x0004

      LDR R0, [R1], #8  ;將存儲器地址為R1 的字數據讀入寄存器R0,并將新地址R1+8 寫入
      R1
     LDR R0, =0x0000
     LDR R1, =0x0004
     LDR R2, =0x0008
     LDR R0, [R1, R2, LSL#2]!
      ;將存儲器地址為R1+R2×4的字數據讀入寄存器R0,并將新地址R1+R2×4寫入R1
     LDR R0, =0x0000
     LDR R1, =0x0004
     LDR R2, =0x0008
     LDR R0, [R1], R2, LSR#2
     ;將存儲器地址為R1的字數據讀入寄存器R0,并將新地址R1+R2/4寫入R1
     PRO2
     LDR R0, =0x0000
     LDR R1, =0x0004
        STR R0, [R1], #8;將R0中的字數據寫入以R1為地址的存儲器中,并將新地址R1+8寫
      入R1
     STR R0, [R1, #8]   ;將R0中的字數據寫入以R1+8為地址的存儲器中
     B PRO1
     END

4.下列是匯編程序調用C語言程序一個示例,請分析程序。

      ;********************************************************************
      *****
     ; Institute of Automation, Chinese Academy of Sciences
     ;File Name:        Init.s
     ;Description:
     ;Author:          JuGuang, Lee
     ;Date:
      ;********************************************************************
      ****
     IMPORT Main   ;通知編譯器該標號為一個外部標號
     ;定義一個代碼段
     AREA   Init, CODE, READONLY
     ;定義程序的入口點
     ENTRY
     ;初始化系統配置寄存器
        LDR R0, =0x3FF0000

      LDR R1, =0xE7FFFF80
      STR R1, [R0]        ;初始化用戶堆棧
      LDR SP, =0x3FE1000  ;跳轉到Main( )函數處的C/C++代碼執行
      BL  Main           ;標識匯編程序的結束
      END
    以上的程序段完成一些簡單的初始化,然后跳轉到Main( )函數所標識的C/C++代碼處執行
    主要的任務,此處的Main僅為一個標號,也可使用其他名稱,與C語言程序中的main( )
    函數沒有關系。
    /********************************************************************
    * Institute of Automation, Chinese Academy of Sciences
    * File Name:       main.c
    * Description:     P0, P1 LED Flash.
    * Author:          JuGuang, Lee
    * Date:
    ********************************************************************/
    void Main(void)
    {
    int i;
    *((volatile unsigned long *) 0x3ff5000) = 0x0000000f;
    while(1)
    {
    *((volatile unsigned long *) 0x3ff5008) = 0x00000001;
     for(i=0; i<0x7fFFF; i++);
        *((volatile unsigned long *) 0x3ff5008) = 0x00000002;
     for(i=0; i<0x7FFFF; i++);
    }
    }
主站蜘蛛池模板: 古浪县| 江永县| 西乌珠穆沁旗| 德清县| 长治市| 准格尔旗| 东源县| 木兰县| 桐城市| 安宁市| 天镇县| 聂荣县| 崇明县| 九龙城区| 溧水县| 延庆县| 巴东县| 奉新县| 壶关县| 汕头市| 牟定县| 宾阳县| 康乐县| 平武县| 建水县| 彭水| 栾川县| 容城县| 密云县| 临颍县| 漳州市| 萨嘎县| 抚远县| 左贡县| 清涧县| 即墨市| 黄大仙区| 府谷县| 临夏市| 和平区| 万安县|