ARM指令集

數(shù)據(jù)處理指令:

  • 數(shù)據(jù)處理指令只可用于寄存器之間或寄存器與立即數(shù)之間


  • 移位指令

  1. Rotate left(ROL) 可以通過 rotate right (ROR)(32-number)來實(shí)現(xiàn), e.g. ROL 10 相當(dāng)于 ROR 22.
  2. 立即數(shù):只要該數(shù),可以通過0x00-0xFF中某個(gè)數(shù),循環(huán)右移偶數(shù)位而產(chǎn)生,就是合法的mov的操作數(shù),否則就是非法的mov的操作數(shù)。
    mov r0, #256       ; mov r0, #0x100
    mov r1, #0x40, 30  ; mov r1, #0x100
    
  3. 偽指令LDR可以用來MOV任意32位的常數(shù)到寄存器中


  • 例如:
 SUB    r0,r1,#5         ;r1-5->r0    (立即尋址)
 ADD    r2,r3,r3,LSL #2  ;R3x4+r3->r2
 ANDS   r4,r4,#0x20      ;r4+0x20->r4,更新條件碼標(biāo)志位
 ADDEQ  r5,r5,r6         ;r5+r6->r5(條件-相等)(寄存器尋址)

存儲(chǔ)器存取指令:

  • 存儲(chǔ)器存取指令用于通用寄存器與內(nèi)存單元之間


  • 例如
 LDR    r0,[r1],#4      ;r1+4->r0(基址變址尋址)
 STRNEB r2,[r3,r4]      ;NE符合-將r2低8位數(shù)寫到[r3+r4]內(nèi)存單元(寄存器間接尋址)
 LDRSH  r5,[r6,#8]!     ;[r6+8]->r5(半字節(jié)),r5中高16位設(shè)置成該字節(jié)的符號(hào)位
 STMFD  sp!,{r0,r2-r7,r10}  ;出棧(多寄存器尋址)
  • STR :從寄存器中將32bits字?jǐn)?shù)據(jù)存儲(chǔ)到內(nèi)存中
  • LDM/STM(出棧/入棧) 用于基址寄存器所指的一片連續(xù)內(nèi)存到寄存器列表中的多個(gè)寄存器之間的數(shù)據(jù)傳送。

PSR 讀寫指令

  • MRS 將CPSR/SPSR的內(nèi)容讀取到通用寄存器中
  • MSR 將通用寄存器的值寫到CPSR/SPSR的特定位域(f,c)中
  • 例如
MRS R0, CPSR     ;R0 = CPSR 
MSR CPSR_c, R0   ;CPSR [c] = R0

ARM 跳轉(zhuǎn)分支指令

  • B <label>
    PC 相對尋址
  • BL <子程序>
    保存返回地址到 LR
    返回時(shí)從 LR 恢復(fù) PC
    對于 non-leaf 函數(shù)(func1), LR 必須壓棧保存

ARM條件執(zhí)行與標(biāo)志位

  • ARM指令可以通過添加合適的條件碼后綴的方式使其條件執(zhí)行
  • 通常情況下,數(shù)據(jù)處理指令不影響條件碼標(biāo)志位,但可以添加"S"后綴來改變標(biāo)志位狀態(tài)。

常用條件碼如下:


  • 例如:
  1. if (a==0) func(1);
CMP      r0,#0?
MOVEQ    r0,#1
?BLEQ     func
  1. if (a==0) x=0;?
    if (a>0) x=1;
CMP      r0,#0
?MOVEQ    r1,#0?
MOVGT    r1,#1?
  1. if (a==4 || a==10) x=0;
CMP      r0,#4
?CMPNE    r0,#10?
MOVEQ    r1,#0

軟件中斷指令SWI

  • SWI 指令用于產(chǎn)生軟件中斷,以便用戶程序能調(diào)用操作系統(tǒng)的系統(tǒng)例
    程。
  • 操作系統(tǒng)在SWI 的異常處理程序中提供相應(yīng)的系統(tǒng)服務(wù),指令中
    24 位的立即數(shù)指定用戶程序調(diào)用系統(tǒng)例程的類型,相關(guān)參數(shù)通過通用
    寄存器傳遞。
  • 當(dāng)指令中24 位的立即數(shù)被忽略時(shí),用戶程序調(diào)用系統(tǒng)例
    程的類型由通用寄存器R0 的內(nèi)容決定。

ARM 偽指令

1. 符號(hào)定義偽指令

符號(hào)定義偽指令用于定義ARM 匯編程序中的變量、對變量賦值以及定
義寄存器的別名等操作

  • GBLA、GBLL 和GBLS 偽指令用于定義一個(gè)ARM 程序中的全局變量,
    并將其初始化。
    GBLA Number         ;定義一個(gè)全局的數(shù)字變量并初始化為0,變量名為Number
    Number SETA 0xaa    ;將該變量賦值為0xaa
    GBLL Logical        ;定義一個(gè)全局的邏輯變量并初始化為F(False),變量名為Logical
    Logical SETL {TRUE} ;將該變量賦值為真
    GBLS Str            ;定義一個(gè)全局的字符串變量并初始化為空,變量名為Str
    Str SETS “Testing”  ;將該變量賦值為“Testing”
    
  • LCLA、LCLL 和LCLS 偽指令用于定義一個(gè)ARM 程序中的局部變量,
    并將其初始化。
  • SETA、SETL、SETS 偽指令用于給一個(gè)已經(jīng)定義的全局變量或局部變
    量賦值。
  • RLIST 偽指令可用于對一個(gè)通用寄存器列表定義名稱,使用該偽指令定
    義的名稱可在ARM 指令LDM/STM 中使用。LDM/STM 指令中,列
    表中的寄存器訪問次序?yàn)楦鶕?jù)寄存器的編號(hào)由低到高,而與列表中的寄
    存器排列次序無關(guān)。
    RegList RLIST {R0-R5,R8,R10} ;將寄存器列表名稱定義為RegList,可在ARM 指令LDM/STM 中通過該名稱訪問寄存器列表。
    

2. 數(shù)據(jù)定義偽指令

數(shù)據(jù)定義偽指令一般用于為特定的數(shù)據(jù)分配存儲(chǔ)單元,同時(shí)可完成已分
配存儲(chǔ)單元的初始化。

  • DCB 偽指令用于分配一片連續(xù)的字節(jié)存儲(chǔ)單元并用偽指令中指定的表
    達(dá)式初始化。
    Str DCB “This is a test!”  ;為Str分配一片連續(xù)的字節(jié)存儲(chǔ)單元并初始化。
    Str = “This is a test!”
    
  • SPACE 偽指令用于分配一片連續(xù)的存儲(chǔ)區(qū)域并初始化為0。
    DataSpace SPACE 100;分配連續(xù)100 字節(jié)的存儲(chǔ)單元并初始化為0。
    DataSpace % 100
    
  • MAP 偽指令用于定義一個(gè)結(jié)構(gòu)化的內(nèi)存表的首地址。MAP 也可用“^”
    代替
  • FIELD 偽指令用于定義一個(gè)結(jié)構(gòu)化內(nèi)存表中的數(shù)據(jù)域。FILED 也可用
    “#”代替。
    MAP 0x100, R0   ;定義結(jié)構(gòu)化內(nèi)存表首地址的值為0x100+R0。
    A FIELD 16  ;定義A 的長度為16 字節(jié),位置為0x100+R0
    B FIELD 32  ;定義B 的長度為32 字節(jié),位置為0x110+R0
    S FIELD 256 ;定義S 的長度為256 字節(jié),位置為0x130+R0
    

3. 匯編控制偽指令

匯編控制偽指令用于控制匯編程序的執(zhí)行流程。

  • IF、ELSE、ENDIF 偽指令能根據(jù)條件的成立與否決定是否執(zhí)行某個(gè)指
    令序列。
    GBLL Test   ;聲明一個(gè)全局的邏輯變量,變量名為Test
    IF Test = TRUE
      指令序列1
    ELSE
      指令序列2
    ENDIF
    
  • WHILE、WEND 偽指令能根據(jù)條件的成立與否決定是否循環(huán)執(zhí)行某個(gè)
    指令序列。
    GBLA Counter   ;聲明一個(gè)全局的數(shù)學(xué)變量,變量名為Counter
    Counter SETA 3 ;由變量Counter 控制循環(huán)次數(shù)
    ……
    WHILE Counter < 10
    指令序列
    WEND
    
  • MACRO、MEND 偽指令可以將一段代碼定義為一個(gè)整體,稱為宏指令,然后就可以在程序中通過宏指令多次調(diào)用該段代碼。MEXIT 用于從宏定義中跳轉(zhuǎn)出去。
    MACRO
    $HandlerLabel HANDLER $Handlerpara;宏的名稱為HANDLER,有1 個(gè)參數(shù)$Handlerpara, $HandlerLabel為標(biāo)號(hào),在宏指令被展開時(shí)會(huì)被替換為用戶定義的符號(hào)。
    sub sp,sp,#4 ;decrement sp(to store jump address)
    stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)
    ldr r0,=$Handlerpara;load the address of HandleXXX to r0
    ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
    str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
    ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
    MEND
    

4. 其他常用偽指令

  • AREA 偽指令用于定義一個(gè)代碼段或數(shù)據(jù)段。
  • ALIGN 偽指令可通過添加填充字節(jié)的方式,使當(dāng)前位置滿足一定的對
    其方式。
  • CODE16 偽指令通知編譯器其后的指令序列為16 位的Thumb 指令,
  • CODE32 偽指令通知編譯器其后的指令序列為32 位的ARM 指令。
  • ENTRY 偽指令用于指定匯編程序的入口點(diǎn)
  • END 偽指令用于通知編譯器已經(jīng)到了源程序的結(jié)尾。
  • EQU 偽指令用于為程序中的常量、標(biāo)號(hào)等定義一個(gè)等效的字符名稱,
    類似于C 語言中的#define。其中EQU 可用“*”代替。
  • EXPORT 偽指令用于在程序中聲明一個(gè)全局的標(biāo)號(hào),該標(biāo)號(hào)可在其他的
    文件中引用。EXPORT 可用GLOBAL 代替。
  • IMPORT 偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義,
    但要在當(dāng)前源文件中引用,而且無論當(dāng)前源文件是否引用該標(biāo)號(hào),該標(biāo)
    號(hào)均會(huì)被加入到當(dāng)前源文件的符號(hào)表中。
  • EXTERN 偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義,
    但要在當(dāng)前源文件中引用,如果當(dāng)前源文件實(shí)際并未引用該標(biāo)號(hào),該標(biāo)
    號(hào)就不會(huì)被加入到當(dāng)前源文件的符號(hào)表中。
  • GET 偽指令用于將一個(gè)源文件包含到當(dāng)前的源文件中,并將被包含的
    源文件在當(dāng)前位置進(jìn)行匯編處理??梢允褂肐NCLUDE 代替GET。
  • INCBIN 偽指令用于將一個(gè)目標(biāo)文件或數(shù)據(jù)文件包含到當(dāng)前的源文件
    中。
  • RN 偽指令用于給一個(gè)寄存器定義一個(gè)別名
AREA Init,CODE,READONLY,ALIGN=3 ;該偽指令定義了一個(gè)代碼段,段名為Init,屬性為只讀, 并指定后面的指令為8 字節(jié)對齊。
ENTRY      ;指定應(yīng)用程序的入口點(diǎn)
Temp RN R0 ;將R0 定義一個(gè)別名Temp
GET a1.s   ;通知編譯器當(dāng)前源文件包含源文件a1.s
GET C:\a2.s ;通知編譯器當(dāng)前源文件包含源文件C:\ a2.s
INCBIN a1.bin;通知編譯器當(dāng)前源文件包含文件a1.bin
EXPORT Stest ;聲明一個(gè)可全局引用的標(biāo)號(hào)Stest
IMPORT Main  ;通知編譯器當(dāng)前文件要引用標(biāo)號(hào)Main,但Main 在其他源文件中定義
Test EQU 50  ;定義標(biāo)號(hào)Test 的值為50
CODE32       ;通知編譯器其后的指令為32 位的ARM 指令
LDR R0,=NEXT+1 ;將跳轉(zhuǎn)地址放入寄存器R0
BX R0            ;程序跳轉(zhuǎn)到新的位置執(zhí)行,并將處理器切換到Thumb 工作狀態(tài)
……
CODE16    ;通知編譯器其后的指令為16 位的Thumb 指令
NEXT LDR R3,=0x3FF
……
END   ;指定應(yīng)用程序的結(jié)尾

C與ARM匯編混合編程

1. C/C++中嵌入?yún)R編程序

void enable_IRQ(void) //使能中斷程序  
{  
    int tmp;              //定義臨時(shí)變量,后面使用  
    __asm                 //內(nèi)嵌匯編程序的關(guān)鍵詞  
    {  
        MRS tmp, CPSR     //把狀態(tài)寄存器加載給tmp  
        BIC tmp, tmp, #80 //將IRQ控制位清0  
        MSR CPSR_c, tmp   //加載程序狀態(tài)寄存器  
    }  
}  
  
void disable_IRQ(void) //禁止中斷程序  
{  
    int tmp;              //定義臨時(shí)變量,后面使用  
    __asm                 //內(nèi)嵌匯編程序的關(guān)鍵詞  
    {  
        MRS tmp, CPSR     //把狀態(tài)寄存器加載給tmp  
        ORR tmp, tmp, #80 //將IRQ控制位置1  
        MSR CPSR_c, tmp   //加載程序狀態(tài)寄存器  
    }  
}  

2. 匯編與C/C++程序的變量相互訪問

  • 在C/C++程序中聲明的全局變量可以被匯編程序通過地址間接訪問。
    AREA Example, CODE, READONLY  
       EXPORT AsmAdd  
       IMPORT g_cVal      @聲明外部變量g_cVal,在C中定義的全局變量  
    Add  
       LDR R1, =g_cVal    @裝載變量地址  
       LDR R0, [R1]       @從地址中讀取數(shù)據(jù)到R0  
       ADD R0, R0, #1     @加1操作  
       STR R0, [R1]       @保存變量值  
       MOV PC, LR         @程序返回  
    END  
    
  • 在匯編程序中聲明的數(shù)據(jù)可以被C/C++程序所訪問。在匯編程序中用EXPORT/GLOBAL偽指令聲明該符號(hào)為全局標(biāo)號(hào),C/C++程序中定義相應(yīng)數(shù)據(jù)類型的指針變量
    匯編:
      EXPORT Message        @聲明全局標(biāo)號(hào)  
      Message DCB "HELLO$"  @定義了5個(gè)有效字符,$為結(jié)束符 
    
    C/C++:
    extern char* Message;  
    int MessageLength()  
    {  
        int Length = 0;  
        char *pMessage;         //定義字符指針變量  
        pMessage = Message;     //指針指向Message 內(nèi)存塊的首地址  
        
        /*while循環(huán),統(tǒng)計(jì)字符串的長度*/  
        while(*pMessage != '$') //$為字符串的結(jié)束符  
        {  
            Length++;  
            pMessage++;  
        }  
        return(Length); //返回字符串的長度  
    }  
    

3. 匯編與C/C++程序的函數(shù)相互調(diào)用(APTCS規(guī)則)

匯編程序設(shè)置要遵循APTCS規(guī)則,保證程序調(diào)用時(shí)參數(shù)的正確傳遞。

  • C程序調(diào)用匯編程序。
    匯編程序中使用EXPORT偽指令聲明本子程序可外部使用,使其他程序可調(diào)用該子程序;在C語言程序中使用extern關(guān)鍵字聲明外部函數(shù)(聲明要調(diào)用的匯編子程序),才可調(diào)用此匯編的子程序。
    strcopy實(shí)現(xiàn)匯編代碼:

    AREA  Example, CODE, READONLY    @聲明代碼段Example  
          EXPORT strcopy          @聲明strcopy,以便外部函數(shù)調(diào)用  
    
    strcopy     @ R0為目標(biāo)字符串的地址, R1為源字符串的地址  
    
             LDRB R2, [R1], #1    @讀取字節(jié)數(shù)據(jù),源地址加1  
             STRB R2, [R0], #1    @保存讀取的1字節(jié)數(shù)據(jù),目標(biāo)地址加1  
             CMP R2, #0           @判斷字符是否復(fù)制完畢  
             BNE strcopy          @沒有復(fù)制完,繼續(xù)循環(huán)復(fù)制  
             MOV PC, LR  
    END 
    

    C/C++:

    #include <stdio.h>  
    extern void strcopy(char *d, const char *s); //聲明外部函數(shù),即要調(diào)用的匯編子程序  
    int main(void)  
    {  
      const char *srcstr = "First ource";          //定義字符串常量  
      char dststr[] = "Second string-destination"; //定義字符串變量  
      printf("Before copying: \n");  
      printf("src=%s, dst=%s\n", srcstr, dststr);  //顯示源字符串和目標(biāo)字符串的內(nèi)容  
      strcopy(dststr, srcstr);                     //調(diào)用匯編子程序R0=dststr, R1=srcstr  
      printf("After copying: \n");  
      printf("src=%s, dst=%s\n", srcstr, dststr);  //顯示復(fù)制后的結(jié)果  
      return(0);  
    }  
    
  • 匯編程序調(diào)用C程序
    在匯編程序中使用IMPORT偽指令聲明將要調(diào)用的C程序函數(shù)。在調(diào)用C程序時(shí),要正確設(shè)置入口參數(shù),然后使用BL指令調(diào)用。
    C/C++ sum函數(shù):

    int sum(int a, int b, int c, int d, int e)  
    {  
        return(a+b+c+d+e); //返回5個(gè)變量的和  
    }  
    

    匯編調(diào)用sum函數(shù):

     AREA Example, CODE, READONLY  
       IMPORT sum      @ 聲明外部標(biāo)號(hào)sum,即C函數(shù)sum()  
       EXPORT CALLSUM  
    UM  
       STMFD SP!, {LR}    @LR寄存器入棧  
       MOV R0, #1         @設(shè)置sum函數(shù)入口參數(shù),R0為參數(shù)a  
       MOV R1, #2         @R1為參數(shù)b  
       MOV R2, #3         @R2為參數(shù)c  
       MOV R3, #5         @參數(shù) e=5,保存到堆棧中  
       STR R3, {SP, #-4}!  
       MOV R3, #4         @R3為參數(shù)d, d=4  
       BL sum             @調(diào)用C程序中的sum函數(shù),結(jié)果放在R0中  
       ADD SP, SP, #4     @調(diào)整堆棧指針  
       LDMFD SP, {PC}     @程序返回  
    END  
    

寄存器最多傳遞4個(gè)參數(shù)(R0-R3),超出四個(gè)的需要用到堆棧。
以上程序使用了5個(gè)參數(shù),分別使用寄存器R0存儲(chǔ)第1個(gè)參數(shù),R1存儲(chǔ)第2個(gè)參數(shù),R2存儲(chǔ)第3個(gè)參數(shù),R3存儲(chǔ)第4個(gè)參數(shù),第5個(gè)參數(shù)利用堆棧傳送。
由于利用了堆棧傳遞參數(shù),在程序調(diào)用結(jié)束后要調(diào)整堆棧指針。匯編程序中調(diào)用了C程序的sum子函數(shù),實(shí)現(xiàn)了1+2+3+4+5,最后相加結(jié)果保存在R0寄存器中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容