ARM 的組合語言

本文當中,我們將說明 ARM 組合語言的特性,以便讓讀者能比較 ARM 與 IA32 兩種CPU在組合語言上的差別。

ARM的定址模式

由於 ARM 是典型的精簡指令集處理器,因此只有載入儲存指令可以存取記憶體。然而,為了速度的原因,ARM的定址法還可分為『前置索引』與『後置索引』,索引值還可以分為『常數』或『暫存器』等,另外,針對前置索引,還有『寫回運算』可以使用。表一顯示了ARM 處理器的定址模式的一些範例。

表一、ARM 處理器的定址的範例

範例 語意 說明
LDR Rd, [Rn,+-offset] Rd=[Rn+-offset] 前置索引
LDR Rd, [Rn,+-offset]! Rd=[Rn+-offset] Rn=Rn+-offset 寫回運算 (!)
LDR Rd, [Rn],+-offset Rd=[Rn] Rn=Rn+-offset 後置索引
LDR Rd, [Rn,+-Rm] Rd=[Rn+Rm] 前置索引 (使用 Rm)
LDR Rd, [Rn,+-Rm]! Rd=[Rn+Rm] Rn=Rn+-Rm 前置索引+寫回運算
LDR Rd, [Rn], +-Rm Rd=[Rn] Rn=Rn+-Rm 後置索引 (使用 Rm)
LDR Rd, [Rn,+-Rm LSL Cx] Rd=[Rn+Rm < < Cx] 移位後載入
LDR Rd, [Rn,+-Rm LSL Rx] Rd=[Rn+Rm < < Rx] 移位後載入 (移位量由暫存器 Rx 指定)
LDR Rd, [+-offset] Rd=[PC+-offset] 相對於程式計數器的定址

前置索引會先進行加法後,再取得記憶體內容。而後置索引則會先取得記憶體內容之後,再做加法動作。

前置索引的語法為 [Rn,+-offset] 或 [Rn,+-Rm shift Cx],其中 Rn, Rm 是暫存器,offset 是位移,而 Cx 則是移位的位元數,shift 代表移位運算,可能是左移 LSL或右移 LSR。

後置索引的語法為 [Rn],+-offset 或 [Rn],+-Rm shift Cx,由於中括號並沒有框住後置式,以此代表先取得記憶體 [Rn] 後才進行加減法。

ARM 能先進行左右移位後再進行運算,這是 ARM 的 ALU 上方的滾筒移位器的功能,這使得 ARM 可以同時執行移位與運算,不需要分成兩個指令。

常數的表示

在 ARM 的指令中,常數的載入通常是利用旋轉立即數 (<rotated_immed>) 的方式進行的,所謂的旋轉立即數,是將一個 8 位元的無號數作左 (右) 偶數位旋轉而來的,也就是說, <rotated_immed>=<immed8> ROR (2*<immed4>)。因此,0xFF、0x104、0xe0000005、0xBC00000 等都是旋轉立即數,但是 0x0101 和 0x102都不是。

因此,在 ARM CPU 中,如果我們想將 R3 的初值設為 972,因為 972 是一個旋轉立即數,因此直接使用 mov R3, #972 即可,但是若要將 R3 設為 973,則必須使用 mov R3, #972, add R3, R3, #1 等兩個指令,才能達成。這也是為了將較大範圍的常數塞入指令中,所採用的特殊設計方式。

ARM 的組合語言範例

範例一顯示了一個 ARM 的組合語言程式,該程式會將 Array 陣列中的數值相加,儲存在 Sum變數中。

範例一、ARM 的組合語言範例

; 組合語言                          ; C 語言 (對照) 
    AREA    CODE                    ; void main()
    ENTRY                           ; {
    LDR R1, n                       ;   R1=n;
    LDR R2, ptr                     ;   R2=ptr;
    MOV R0, #0                      ;   R0=0;
loop:
    LDR R3, [R2], #4                ;   R3 = [R2]; R2 = R2+4;
    ADD R0, R0, R3                  ;   R0 = R0 + R3
    SUBS R1, R1, #1                 ;   R1 = R1 – 1; if (R1 > 0)
    BGT loop                        ;     goto Loop;
    STR R0, sum                     ;   sum = R0
                                    ; 
    AREA DATA                       ; 
sum   DCD 0                         ; int sum=0;
n     DCD 6                         ; int n=6;
ptr   DCD Array                     ; int *ptr = array;
array DCD 6, -3, 2, 9, -33, 100     ; int array[] = {6, -3, 2, 9, -33, 100};
    END                             ; }

ARM 的組合語言程式與 CPU0 相當類似,在此僅針對較特殊的部分進行說明,在範例一中,LDR R3, [R2], #4 乃是利用後置索引的方式,先做完 R3=[R2] 後,再將 R2 加上 4寫回。因此,相當於 R3=[R2];

R2=R2+4; 兩行的語義。另外,SUBS 指令當中的 S,代表運算後的結果會影響狀態暫存器 CPSR,於是後續的 BGT 才能根據CPSR的內容進行條件判斷,以決定是否要進行跳躍動作。

讀者可以比較 ARM 與 IA32 的架構,可以看出兩者有相當大的不同,不論是在暫存器或指令集上,都有明顯的差異,透過這樣的比較,應可感受到組合語言的多樣性。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License