- 嵌入式系統Linux內核開發實戰指南(ARM平臺)
- 王洪輝編著
- 59字
- 2018-12-27 18:21:35
第3章 ARM指令及其尋址方式
3.1 ARM處理器的程序狀態寄存器(PSR)
ARM中的當前程序狀態寄存器(Current Program Status Register,CPSR)可以在任何處理器模式下被訪問,而在5種異常中斷(exception)模式中又各自擁有一個獨立的備份程序狀態寄存器(Saved Program Status Register,SPSR)用來保存CPSR中的內容。當特定的異常中斷發生時,SPSR_xxx用來保存CPSR,當異常中斷程序退出時,可以用SPSR_xxx中保存的值來恢復CPSR。
由于用戶(user)模式和系統(system)模式不是異常模式,所以它們沒有SPSR,當在用戶模式或系統模式下訪問SPSR時,將會產生不可預知的結果。
當前程序狀態寄存器共32位,當前只實現了16位。該寄存器的各位定義說明如下:
表3-1 ARM中的程序狀態寄存器(PSR)

3.2 ARM指令的條件碼

3.3 ARM指令介紹
3.3.1 跳轉指令
跳轉指令是程序跳轉到-32MB~+32MB范圍內地址處執行。
b{<cond>} <target_address>
程序跳轉到<target_address>處執行。
bl{<cond>} <target_address>
程序跳轉到<target_address>處執行,將當前pc的值保存到lr中。
blx <target_address>
程序跳轉到<target_address>處執行,將程序狀態從ARM態切換到Thumb態,將當前pc的值保存到lr中。
blx{<cond>} <rm>
程序跳轉到rm值所指的地址處執行,目標地址處的指令可以是ARM指令也可以是Thumb指令,將當前pc的值保存到lr中。
bx{<cond>} <rm>
程序跳轉到rm值所指的地址處執行,目標地址處的指令可以是ARM指令(rm[bit0=0]),也可以是Thumb指令(rm[bit0=1])。
3.3.2 數據處理指令
mov{<cond>}{s} <rd>, <shifter_operand>
將<shifter_operand>表示的數據傳遞給rd。
mvn{<cond>}{s} <rd>, <shifter_operand>
將<shifter_operand>表示的數據的反碼傳遞給rd。
add{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值相加,并把結果保存到<rd>中。
adc{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值相加,再加上CPSR中C標志的值,并把結果保存到<rd>中。
sub{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<rn>的值減去<shifter_operand>表示的數據,并把結果保存到<rd>中。
sbc{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<rn>的值減去<shifter_operand>表示的數據,再減去CPSR中C標志的值,并把結果保存到<rd>中。
rsb{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據減去<rn>的值,并把結果保存到<rd>中。
將<shifter_operand>表示的數據減去<rn>的值,再減去CPSR中C標志的值,并把結果保存到<rd>中。
and{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值按位做邏輯與操作,并把結果保存到<rd>中。
orr{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值按位做邏輯或操作,并把結果保存到<rd>中。
eor{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值按位做邏輯異或操作,并把結果保存到<rd>中。
bic{<cond>}{s} <rd>, <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值的反碼按位做邏輯與操作,并把結果保存到<rd>中。
cmp{<cond>} <rn>, <shifter_operand>
將<rn>的值減去<shifter_operand>表示的數據,根據操作結果更新CPSR中相應的條件標志位。
cmn{<cond>} <rn>, <shifter_operand>
將<rn>的值加上<shifter_operand>表示的數據,根據操作結果更新CPSR中相應的條件標志位。
tst{<cond>} <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值按位做邏輯與操作,根據操作結果更新CPSR中相應的條件標志位。
teq{<cond>} <rn>, <shifter_operand>
將<shifter_operand>表示的數據與<rn>的值按位做邏輯異或操作,根據操作結果更新CPSR中相應的條件標志位。
3.3.3 乘法指令
考慮到操作結果,指令中所有操作數都放在寄存器中。
mul{<cond>}{s} <rd>, <rm>, <rs>
將兩個32位(有/無)符號操作數相乘,將結果保存到32位寄存器<rd>中。
mla{<cond>}{s} <rd>, <rm>, <rs>, <rn>
將兩個32位(有/無)符號操作數相乘,再將乘積加上第3個操作數,將結果保存到32位寄存器<rd>中。
smull{<cond>}{s} <rdLo>, <rdHi>, <rm>, <rs>
將兩個32位有符號操作數相乘,將結果的高32位保存到寄存器<rdHi>中,將結果的低32位保存到寄存器<rdLo>中。
smlal{<cond>}{s} <rdLo>, <rdHi>, <rm>, <rs>
將兩個32位有符號操作數的64位乘積結果與<rdHi>和<rdLo>中原來的64位數相加,將最后加法結果的高32位保存到寄存器<rdHi>中,低32位保存到寄存器<rdLo>中。
umull{<cond>}{s} <rdLo>, <rdHi>, <rm>, <rs>
將兩個32位無符號操作數相乘,將結果的高32位保存到寄存器<rdHi>中,將結果的低32位保存到寄存器<rdLo>中。
umlal{<cond>}{s} <rdLo>, <rdHi>, <rm>, <rs>
將兩個32位無符號操作數的64位乘積結果與<rdHi>和<rdLo>中原來的64位數相加,將最后加法結果的高32位保存到寄存器<rdHi>中,低32位保存到寄存器<rdLo>中。
3.3.4 雜類算術指令
clz{<cond>} <rd>, <rm>
計算寄存器<rm>的值的最高端0的個數,如果bit31為1,那么指令返回0,如果<rm>=0,則指令返回32。
3.3.5 狀態寄存器訪問指令
mrs{<cond>} <rd>, cpsr
將當前狀態寄存器的內容傳送到<rd>中。
mrs{<cond>} <rd>, spsr
將處理器當前工作模式的保存狀態寄存器的內容傳送到<rd>中。
msr{<cond>} cpsr_<fields>, #<immediate>
將立即數傳給當前狀態寄存器。
msr{<cond>} cpsr_<fields>, <rm>
將<rm>的值傳給當前狀態寄存器。
msr{<cond>} spsr_<fields>, #<immediate>
將立即數傳給處理器當前工作模式的保存狀態寄存器。
msr{<cond>} spsr_<fields>, <rm>
將<rm>的值傳給處理器當前工作模式的保存狀態寄存器。
說明:
<fields>設置狀態寄存器中需要操作的位。狀態寄存器的32位可以分為4個8位的域:bits[31:24]為條件標志位域,用f表示;bits[23:16]為狀態標志位域,用s表示;bits[15:8]為擴展位域,用x表示;bits[7:0]為控制標志位域,用c表示。4部分狀態寄存器對應的表示方法為:cpsr_f、cpsr_s、cpsr_x、cpsr_c及spsr_f、spsr_s、spsr_x、spsr_c。
3.3.6 Load/Store內存訪問指令
ldr{<cond>} <rd>, <addressing_mode>
將<addressing_mode>尋址模式表示的內存地址處的32位字讀取到<rd>中,如果地址不是字對齊的,那么從內存中讀出的數據要進行循環右移操作,移位的位數為地址的bits[1:0]表示的值的8倍,這樣對于little-endian的內存模式,指令要讀取的字節數據存放在<rd>的低8位;對于big-endian的內存模式,指令要讀取的字節數據存放在<rd>的bits[31:24]。
ldr{<cond>}b <rd>, <addressing_mode>
從內存中將一個8位字節數讀取到<rd>中,并將<rd>的高24位清0。
ldr{<cond>}bt <rd>, <post_indexed_addressing_mode>
從內存中將一個8位字節數讀取到<rd>中,并將<rd>的高24位清0;當在特權級處理器模式下使用本指令時,內存系統將該操作當作一般用戶模式下的內存訪問操作。
ldr{<cond>}h <rd>, <addressing_mode>
從內存中將一個16位數據讀取到<rd>中,并將<rd>的高16位清0。如果內存地址不是半字對齊的,將產生不可預知的結果。
ldr{<cond>}sb <rd>, <addressing_mode>
從內存中將一個8位有符號字節數讀取到<rd>中,并將<rd>的高24位設置成該字節數據符號位的值。
ldr{<cond>}sh <rd>, <addressing_mode>
從內存中將一個16位有符號數據讀取到<rd>中,并將<rd>的高16位設置成該半字數據的符號位的值。如果內存地址不是半字對齊的,將產生不可預知的結果。
ldr{<cond>}t <rd>, <post_indexed_addressing_mode>
從內存中將一個32位字讀取到<rd>中,如果地址不是字對齊的,那么從內存中讀出的數據要進行循環右移操作,移位的位數為地址的bits[1:0]表示的值的8倍,這樣對于little-endian的內存模式,指令要讀取的字節數據存放在<rd>的低8位;對于big-endian的內存模式,指令要讀取的字節數據存放在<rd>的bits[31:24]。當在特權級處理器模式下使用本指令時,內存系統將該操作當作一般用戶模式下的內存訪問操作。
str{<cond>} <rd>, <addressing_mode>
將<rd>中的32位字寫入到<addressing_mode>尋址模式表示的內存地址處。
str{<cond>}b <rd>, <addressing_mode>
將<rd>中的低8位字節數據寫入到<addressing_mode>尋址模式表示的內存地址處。
str{<cond>}h <rd>, <addressing_mode>
將<rd>中的低16位半字數據寫入到<addressing_mode>尋址模式表示的內存地址處。如果內存地址不是半字對齊的,將產生不可預知的結果。
str{<cond>}t <rd>, <addressing_mode>
將<rd>中的32位字寫入到<addressing_mode>尋址模式表示的內存地址處。當在特權級處理器模式下使用本指令時,內存系統將該操作當作一般用戶模式下的內存訪問操作。
將<rd>中的低8位字節數據寫入到<addressing_mode>尋址模式表示的內存地址處。當在特權級處理器模式下使用本指令時,內存系統將該操作當作一般用戶模式下的內存訪問操作。
str{<cond>}bt <rd>, <addressing_mode>
3.3.7 批量Load/Store內存訪問指令
ldm{<cond>}<addressing_mode> <rn>!, <registers>
將數據從連續的內存單元中讀取32位字到寄存器列表中。
ldm{<cond>}<addressing_mode> <rn>!, <registers_without_pc>^
將數據從連續的內存單元中讀取32位字到寄存器列表中,但r15/pc寄存器不在寄存器列表中,此時指示指令中所有的寄存器為用戶模式下的寄存器。
ldm{<cond>}<addressing_mode> <rn>!, <registers_and_pc>^
將數據從連續的內存單元中讀取32位字到寄存器列表中,且r15/pc寄存器必須在寄存器列表中,同時將當前處理器模式對應的SPSR寄存器的內容復制到CPSR寄存器中。
stm{<cond>}<addressing_mode> <rn>!, <registers>
將寄存器列表中的32位字數據寫入連續的內存單元中。
stm{<cond>}<addressing_mode> <rn>!, <registers>^
將寄存器列表中的32位字數據寫入連續的內存單元中,并指示指令中所有的寄存器為用戶模式下的寄存器。
3.3.8 LDREX和STREX指令
■ 獨占加載和存儲寄存器
指令語法格式如下所示:
LDREX{cond} Rt, [Rn {, #offset}] STREX{cond} Rd, Rt, [Rn {, #offset}] LDREXB{cond} Rt, [Rn] STREXB{cond} Rd, Rt, [Rn] LDREXH{cond} Rt, [Rn] STREXH{cond} Rd, Rt, [Rn] LDREXD{cond} Rt, Rt2, [Rn] STREXD{cond} Rd, Rt, Rt2, [Rn]
說明:
con可選的條件代碼。
Rd存放返回狀態的目標寄存器。
Rt要加載或存儲的寄存器。
Rt2進行雙字加載或存儲時要用到的第二個寄存器。
Rn內存地址所基于的寄存器。
offset要應用到Rn中的值的可選偏移量。offset只可用于Thumb-2指令中。如果省略offset,則認為偏移量為0。
■ LDREX(獨占裝載指令)
LDREX可從內存中加載數據。
如果物理地址有共享TLB屬性,那么LDREX會將該物理地址標記為由當前處理器獨占訪問,并且會清除該處理器對其他任何物理地址的獨占訪問標簽。否則,會標記為“執行處理器已經標記了一個物理地址,但訪問尚未完畢”。
■ STREX(獨占存儲指令)
STREX可在一定條件下向內存中存儲數據。條件具體如下:
● 如果物理地址沒有共享TLB屬性,執行處理器有一個已標記物理地址,但尚未訪問完畢,那么將會進行存儲,清除該標記,并向Rd中返回值0;
● 如果物理地址沒有共享TLB屬性,且執行處理器也沒有已標記但尚未訪問完畢的物理地址,那么將不會進行存儲,而會向Rd返回值1;
● 如果物理地址有共享TLB屬性,且已被標記為由執行處理器獨占訪問,則將進行存儲,清除標簽,并向Rd返回值0;
● 如果物理地址有共享TLB屬性,但卻沒有標記為由執行處理器獨占訪問,則不會進行存儲,而會向Rd中返回值1。
■ 限制
offset不可用于ARM指令中;offset的值可為0~1020范圍內的任意一個4的倍數。
r15不可用于Rd、Rt、Rt2或Rn。
對于STREX,Rd一定不能與Rt、Rt2或Rn為同一寄存器。
對于LDREX,Rt和Rt2不可為同一寄存器。
在ARM指令中,Rt必須是一個編號為偶數的寄存器,且不能為r14;同時Rt2必須為R(d+1)。
■ 用法
利用LDREX和STREX可在多處理器和共享內存系統間實現進程間通信。
出于性能方面的考慮,請將相應LDREX指令和STREX指令間的指令數控制到最少。
■ 注意
STREX指令中所用的地址必須要與近期執行次數最多的LDREX指令所用的地址相同。如果使用不同的地址,則STREX指令的執行結果將不可預知。
■ 適用體系結構
ARM LDREX和STREX可用于ARMv6及更高版本中。
ARM LDREXB、LDREXH、LDREXD、STREXB、STREXD和STREXH可用于ARMv6K及更高版本中。
所有這些32位Thumb指令均可用于ARMv6T2和ARMv7,但LDREXD和STREXD不可用于ARMv7-M架構。
這些指令均無16位版本。
■ 示例
MOV r1, #0x1 try LDREX r0, [LockAddr] CMP r0, #0 STREXEQ r0, r1, [LockAddr] CMPEQ r0, #0 BNE try ...
3.3.9 信號量操作指令
swp{<cond>} <rd>, <rm>, [<rn>]
將<rn>的值所指的內存地址處的字節讀取到<rd>中,同時將<rm>的值寫入到<rn>的值所指的內存地址中,當<rd>和<rm>是同一寄存器時,指令交換該寄存器<rd>和內存單元的內容。
swp{<cond>}b <rd>, <rm>, [<rn>]
將<rn>的值所指的內存地址處的字節數據讀取到<rd>的低8位中,<rd>的高24位清0,同時將<rm>的低8位寫入到<rn>的值所指的內存地址中,當<rd>和<rm>是同一寄存器時,指令交換該寄存器<rd>的低8位和內存單元的內容。
3.3.10 異常中斷產生指令
swi{<cond>} <immed_24>
產生軟中斷。ARM通過swi中斷機制來實現在用戶模式下對操作系統中特權模式程序的調用。<immed_24>又叫軟中斷號,被操作系統用來判斷用戶程序請求的服務類型。
bkpt <immed_16>
產生軟件斷點中斷。軟件調試程序可以使用該中斷,當系統使用硬件調試器時可以忽略該中斷。<immed_16>被調試軟件用來保存額外的斷點信息。
3.3.11 ARM協處理器指令
cdp{<cond>} <coproc>, <opcode_1>, <crd>, <crn>, <crm>, <opcode_2>
ARM處理器通知ARM協處理器執行特定的操作,該操作由協處理器完成,如果協處理器不能成功執行該操作,將產生未定義的指令異常中斷。
說明:
<coproc> 協處理器的編號。
<opcode_1> 協處理器將執行的操作的操作碼。
<crd> 作為目標寄存器的協處理器寄存器。
<crn> 存放第1個操作數的協處理器寄存器。
<crm> 存放第1個操作數的協處理器寄存器。
<opcode_2> 協處理器將執行的操作的操作碼。
示例
cdp p5, 2, c12, c10, c3, 4 ;初始化協處理器p5,操作碼1為2,操作碼2為4,目標 寄存器為c12,源操作數寄存器為c10和c3
ldc{<cond>}{L} <coproc>, <crd>, <addressing_mode>
從一系列連續的內存單元中將數據讀取到協處理器的寄存器中,如果協處理器不能成功執行該操作,將產生未定義的指令異常中斷。
說明:
{L} 指示指令為長讀取操作,比如用于雙精度的數據傳送。
示例
ldc p6, cr4, [r2, #4] ;讀取內存單元[r2+4]的字數據并寫入到協處理器p6的cr4的 寄存器中 stc{<cond>}{L} <coproc>, <crd>, <addressing_mode>
將協處理器的寄存器中的數據寫入到一系列連續的內存單元中,如果協處理器不能成功執行該操作,將產生未定義的指令異常中斷。
示例
stc p6, cr6, [r2, #4]! ;將協處理器p6的cr6的寄存器中的字數據寫入到內存單元 [r2+4]中,然后r2=r2+4 mcr{<cond>} <coproc>, <opcode_1>, <rd>, <crn>, <crm>, {<opcode_2>}
將ARM處理器寄存器中的數據寫到協處理器的寄存器中,如果協處理器不能成功執行該操作,將產生未定義的指令異常中斷。
示例
mcr p14, 3, r7, c7, c11, 6 ;將r7中的數據寫到協處理器p14的寄存器c7和c11中,操作碼1為3,操作碼2為6 mrc{<cond>} <coproc>, <opcode_1>, <rd>, <crn>, <crm>, {<opcode_2>}
將協處理器的寄存器中的數據寫到ARM處理器的寄存器中,如果協處理器不能成功執行該操作,將產生未定義的指令異常中斷。
示例
mrc p15, 2, r5, c0, c2, 4 ;將協處理器p15的寄存器中的數據寫到r5中,操作 碼1為2,操作碼2為4
3.4 ARM指令尋址方式
3.4.1 數據處理指令的操作數的尋址方式
數據處理指令的一般編碼格式如下所示:

數據處理指令的一般語法格式如下所示:
<opcode>{<cond>}{S} <Rd>,<Rn>,<shifter_operand>
數據操作指令的操作數的尋址方式是針對第2個操作數shifter_operand的,粗分為3種尋址方式,具體細分為11種尋址方式。
數據處理指令的(第2個)操作數的尋址方式粗分為以下3種方式:
■ 立即數方式
立即數是32位的,但它只能占用指令格式中12位的<shifter_operand>,所以在匯編代碼時是要將32位的立即數編碼成12位的<shifter_operand>。立即數編碼有固定的規則,每個立即數由一個8位的常數循環右移偶數位得到,右移的位數由一個4位的二進制數的兩倍表示,所以立即數尋址方式的指令編碼格式如下:

其中4位的roate_imm是二進制移位數的一半,8位的immed_8是常數,兩者剛好組成了指令中的第2個操作數<shifter_operand>。如果立即數記作<immediate>,那么:
<immediate> = immed_8循環右移(2*rotate_imm)
千萬不要錯誤直觀地認為:
<immediate> = immed_8 |(rotate_imm<<8)
由以上立即數的編碼規則可以判斷立即數的合法性,舉例說明:
0xFF、0x104、0xFF0、0xFF00等都是合法的立即數;而0x101、0x102、0xFF1等則是非法的立即數,因為無法根據上面的編碼規則對它們進行編碼。
另外同一個合法立即數可能有若干種編碼方法,比如0x3F可以有以下兩種編碼方法:
由于這種立即數的構造方法中包含了循環移位操作,而循環移位操作會影響CPSR的條件標志位C,因此同一個合法立即數采用不同的編碼方式將使某些指令的執行產生不同的結果,這是不允許的。ARM匯編編譯器遵守下面的規則:
immed_8=0x3F,rotate_imm=0或者immed_8=0xFC,rotate_imm=0xF
當立即數數值在0~0xFF范圍時,令immed_8=<immediate>,rotate_imm=0;
在其他情況下,匯編編譯器選擇使rotate_imm數值最小的編碼方式。
心得:如何快速判斷一個32位立即數是否合法?只要看該立即數能否通過右移偶數位把該立即數的所有非0位都移到最低8位中,如果能,那么這個立即數肯定能通過上面的編碼規則進行編碼,是合法立即數;否則該立即數一定是非法立即數。
■ 寄存器方式
第2個操作數即是Rm寄存器的值。
■ 寄存器移位方式
移位的方式有5種,它們的助記符和含義說明如下:
ASR 算術右移
LSL 邏輯左移
LSR 邏輯右移
ROR 循環右移
RRX擴展的循環右移
數據處理指令的(第2個)操作數的尋址方式具體細分為以下11種方式:
① #<immediate>
指令編碼格式:

示例:
mov r0, #0xfc0
② <Rm>
指令編碼格式:

示例:
mov r3,r2
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8,見前面“ARM流水線操作”說明。
③ <Rm>,LSL #<shift_imm>
指令編碼格式:

示例:
mov r0,r0,LSL #n ;將r0的值左移n位,右邊空位用0補充,循環器進位標志的值等于 最后從寄存器r0中移出位的值,如果沒有移位,那么循環器進位標志的值等于CPSR中C標志位的值
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
④ <Rm>,LSL <Rs>
指令編碼格式:

注意:當R15/pc用作Rn、Rm、Rd或Rs時,會產生不可預知的結果。
⑤ <Rm>,LSR #<shift_imm>
指令編碼格式:

示例:
mov r0,r0,LSR #n ;將r0的值右移n位,左邊空位用0補充,循環器進位標志的值等于 最后從寄存器r0中移出位的值,如果n等于0,那么右移32位
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
⑥ <Rm>,LSR <Rs>
指令編碼格式:

注意:當R15/pc用作Rn、Rm、Rd或Rs時,會產生不可預知的結果。
⑦ <Rm>,ASR #<shift_imm>
指令編碼格式:

示例:
mov r0,r0,ASR #n ;將r0的值右移n位,左邊空位用R0[31]位的值補充,循環器進位標志的值等于最后從寄存器r0中移出位的值,如果n等于0,則沒有移位,那么循環器進位標志的值等于CPSR中C標志位的值 R0[31]
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
⑧ <Rm>,ASR <Rs>
指令編碼格式:

注意:當R15/pcP用作Rn、Rm、Rd或Rs時,會產生不可預知的結果。
⑨ <Rm>,ROR #<shift_imm>
指令編碼格式:

示例:
mov r0,r0,ROR #n ;將r0的值右移n位,移出的位右移入寄存器的左邊空位,循環器進位 標志的值等于最后從r0寄存器右邊移出位的值;如果n等于0,那么操作與下面的RRX移位操作相同
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
⑩ <Rm>,ROR <Rs>
指令編碼格式:

注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
? <Rm>,RRX
指令編碼格式:

示例:
mov r0,r0,RRX ;將r0的值右移1位,將CPSR的C標志填充左邊移出的位,而CPSR的C 標志位用從右邊移出位的值替代
注意:當R15/pc用作第1個源操作數Rn或者第2個操作數Rm時,操作數即為當前指令地址加常數8。
3.4.2 字及無符號字節的Load/Store指令的尋址方式
字及無符號字節Load/Store指令的一般編碼格式如下所示:

字及無符號字節Load/Store指令的一般語法格式如下所示:
ldr|str{<cond>}{B} {T}<Rd>, <addressing_mode>
各種類型的Load/Store指令的尋址方式由兩部分組成:一部分為一個基址寄存器,另一部分為一個地址偏移量。基址寄存器可以是任一通用寄存器,地址偏移量則有以下3種格式:
● 立即數
● 寄存器
● 寄存器加一個移位常數
同樣,尋址方式的地址計算方法有如下3種:
● 不更新基址寄存器方法
● 事先更新基址寄存器方法
● 事后更新基址寄存器方法
字及無符號字節Load/Store指令的操作數的尋址方式是針對第2個操作數Addressing_mode的,具體細分為以下9種尋址方式。
① [<Rn>, #+/-<offset_12>]
指令編碼格式:

示例:
ldr r0, [r1,-#4] ;將內存單元r1-4中的字讀取到r0寄存器中
注意:當R15/pc用作Rn時,內存基地址為當前指令地址加8字節偏移量。
② [<Rn>, +/-<Rm>]
指令編碼格式:

示例:
ldr r0, [r1,-r2] ;將內存單元r1-r2中的字讀取到r0寄存器中
注意:當R15/pc用作Rn時,內存基地址為當前指令地址加8字節偏移量;當R15/pc用作索引寄存器Rm時,會產生不可預知的結果。
③ [<Rn>, +/-<Rm>, <shift>#<shift_imm>]
指令編碼格式:

示例:
ldr r0, [r1, r2, LSL #2] ;將內存單元(r1+r2*4)中的字讀取到r0中
注意:當R15/pc用作Rn時,內存基地址為當前指令地址加8字節偏移量;當R15/pc用作索引寄存器Rm時,會產生不可預知的結果。
④ [<Rn>, #+/-<offset_12>]!
指令編碼格式:

示例:
ldr r0, [r1,#4]! ;將內存單元(r1+4)中的字讀取到r0中,同時r1=r1+4
注意:當R15/pc用作Rn時,會產生不可預知的結果。
⑤ [<Rn>, +/-<Rm>]!
指令編碼格式:

示例:
ldr r0, [r1, r2]! ;將內存單元(r1+r2)中的字讀取到r0中,同時r1=r1+r2
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一個寄存器時,會產生不可預知的結果。
⑥ [<Rn>, +/-<Rm>, <shift>#<shift_imm>]!
指令編碼格式:

示例:
ldr r0, [r1, r2, LSL #2]! ;將內存單元(r1+r2*4)中的字讀取到r0中,同時 r1=r1+r2*4
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一寄存器時,會產生不可預知的結果。
⑦ [<Rn>], #+/-<offset_12>
指令編碼格式:

示例:
ldr r0, [r1], #4 ;將內存單元r1中的字讀取到r0中,然后r1=r1+4,這叫事后更新 方法
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果。
⑧ [<Rn>], +/-<Rm>
指令編碼格式:

示例:
ldr r0, [r1], r2 ;將內存單元r1中的字讀取到r0中,然后r1=r1+r2,這叫事后更新 方法
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一寄存器時,會產生不可預知的結果。
⑨ [<Rn>], +/-<Rm>, <shift>#<shift_imm>
指令編碼格式:示例:

ldr r0, [r1], r2, LSL #2 ;將內存單元r1中的字讀取到r0中,然后 r1=r1+r2*4,這叫事后更新方法
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一寄存器時,會產生不可預知的結果。
3.4.3 雜類Load/Store指令的尋址方式
雜類Load/Store指令包括半字、帶符號字節、雙字的Load/Store指令。
雜類Load/Store指令的一般編碼格式如下所示:

雜類Load/Store指令的一般語法格式如下所示:
ldr|str{<cond>}H|SH|SB|D <Rd>, <addressing_mode>;addressing_mode為bit[11:0];
雜類Load/Store指令操作數的尋址方式是針對第2個操作數Address_mode的,具體細分為以下6種尋址方式:
① [<Rn>, #+/-<offset_8>];offset_8=(offsetH<<4)|offsetL
指令編碼格式:示例:

ldrsb r0, [r1, #3] ;將內存單元(r1+3)中的有符號字節讀取到r0中,r0中高24 位為字節的符號位
注意:當R15/pc用作Rn時,內存基地址為當前指令地址加8字節偏移量。
② [<Rn>, +/-<Rm>]
指令編碼格式:

示例:
strh r0, [r1, r2] ;將r0中的低16位數據保存到內存單元(r1+r2)中
注意:當R15/pc用作Rn時,內存基地址為當前指令地址加8字節偏移量;當R15/pc用作索引寄存器Rm時,會產生不可預知的結果。
③ [<Rn>, #+/-<offset_8>]!;offset_8=(offsetH<<4)|offsetL
指令編碼格式:

示例:
ldrsh r0, [r1, #2]! ;將內存單元(r1+2)中的有符號半字讀取到r0中,r0中高16 位為半字的符號位,同時r1=r1+2
注意:當R15/pc用作Rn時,會產生不可預知的結果。
④ [<Rn>, +/-<Rm>]!
指令編碼格式:
示例:

ldrh r0, [r1, r2]! ;將存單元(r1+r2)中的半字數據讀取到r0中,r0中的高16 位設置為0,同時r1=r1+r2
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一寄存器時,會產生不可預知的結果。
⑤ [<Rn>], #+/-<offset_8>;offset_8=(offsetH<<4)|offsetL
指令編碼格式:示例:

strh r0, [r1], #2 ;將r0中低16位數據保存到內存單元(r1)中,然后r1=r1+2
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果。
⑥ [<Rn>], +/-<Rm>
指令編碼格式:

示例:
strh r0, [r1], r2 ;將r0中低16位數據保存到內存單元(r1)中,然后r1=r1+r2
注意:當R15/pc用作Rn或Rm時,會產生不可預知的結果;當Rn和Rm是同一寄存器時,會產生不可預知的結果。
3.4.4 批量Load/Store指令的尋址方式
批量Load/Store指令的一般編碼格式如下所示:

批量Load/Store指令的一般語法格式如下所示:
ldm|stm{<cond>}<addressing_mode> <Rn>{!}, <registers>{^};addressing_mode為bit[11:0];
一條批量Load/Store指令可以實現在一組寄存器和一塊連續的內存單元之間傳輸數據,指令中的寄存器組和內存單元的對應關系有固定的規則,即:低編號的寄存器對應于內存單元中低地址單元,高編號的寄存器對應于內存中高地址單元,基址寄存器<Rn>中存放地址塊的起始地址值,可能是最高地址值,也可能是最低地址值。
指令中的<addressing_mode>表示地址的變化方式,共有如下4種方式:
① IA-Increment After 事后遞增方式
指令編碼格式如下所示:

② IB-Increment Before 事先遞增方式
指令編碼格式如下所示:

③ DA-Decrement After 事后遞減方式
指令編碼格式如下所示:

④ DB-Decrement Before 事先遞減方式
指令編碼格式如下所示:

除了可以在一組寄存器和一塊連續的內存單元之間批量傳輸數據外,還可以在數據棧中批量傳輸數據。數據棧的棧指針通常可以指向不同的位置,棧指針指向棧頂元素(即最后一個入棧的數據元素)的叫Full棧。棧指針指向棧頂元素相鄰的一個可用數據單元的稱為Empty棧。
另外數據棧的增長方向也可以不同,數據棧向內存地址減小的方向增長叫Descending棧;數據棧向內存地址增加的方向增長的叫Ascending棧。
綜合以上兩種特點,總共有下面4種數據棧:
● FD Full Descending
● ED Empty Descending
● FA Full Ascending
● EA Empty Ascending
不同數據棧對應的批量Load/Store指令的尋址方式如表3-2所示。
表3-2 Load/Store尋址方式

3.4.5 協處理器Load/Store指令的尋址方式
雜類Load/Store指令的一般編碼格式如下所示:

協處理器Load/Store指令的一般語法格式如下所示:
mcr|mrc{<cond>}{L} <coproc>,<Rd>,<addressing_mode>
其中<addressing_mode>表示地址變化方式,有以下4種格式:
① 偏移量[<Rn>, #+/-<offset_8>*4]
指令編碼格式如下所示:

注意:當R15/pc作為Rn時,其值為當前指令的地址加8。
② 事前更新[<Rn>, #+/-<offset_8>*4]!
指令編碼格式如下所示:

注意:當R15/pc作為Rn時,會產生不可預知的結果。
③ 事后更新[<Rn>], #+/-<offset_8>*4
指令編碼格式如下所示:

注意:當R15/pc作為Rn時,會產生不可預知的結果。
④ 非索引[<Rn>], <option>;<option>未被ARM使用,可用作協處理器來擴展指令。
指令編碼格式如下所示:

注意:當R15/pc作為Rn時,其值為當前指令的地址加8。
3.4.6 ARM指令的尋址方式總結
所謂尋址方式就是處理器根據指令中給出的地址信息來尋找物理地址的方式,總結上面的詳細描述,可以把ARM指令的尋址方式歸納成以下7種:

3.5 ARM匯編偽操作(Directive)
ARM匯編語言源程序的語句由指令、偽操作和宏指令組成,偽操作叫Derective,宏指令叫pseudo-instruction,宏指令也是通過偽操作來定義的。
偽操作是由匯編器在對源程序匯編期間進行處理的;宏則是一段獨立的程序代碼,在程序中通過宏指令調用宏,當程序被匯編時,匯編器對每個宏調用進行展開,用宏定義體取代源程序中的宏指令。
ARM偽操作主要包括以下幾類:
1)符號定義(Symbol definition)偽操作;
2)數據定義(Data definition)偽操作;
3)匯編控制(Assembly control)偽操作;
4)棧中數據幀描述(Frame description)偽操作;
5)信息報告(Reporting)偽操作;
6)其他(Miscellaneous)偽操作。
3.5.1 符號定義偽操作

3.5.2 數據定義偽操作

續表

3.5.4 棧中數據幀描述偽操作
主要用于調試,此處不做介紹。
3.5.5 信息報告偽操作

表3-2 opt偽操作選項編碼

ttl
在列表文件每一頁的開頭插入一個標題,它將作用于其后的每一頁直到遇到新的ttl。
語法格式
ttl title
subt
在列表文件每一頁的開頭插入一個子標題,它將作用于其后的每一頁直到遇到新的subt。
語法格式
subt title
3.5.6 其他偽操作

續表

3.6 ARM匯編偽指令
ARM偽指令不是真正的ARM指令或Thumb指令,偽指令在匯編編譯器對源程序進行匯編處理時被替換成對應的ARM或Thumb指令序列。

3.7 Thumb指令介紹
Thumb指令集是將ARM指令集的一個子集重新編碼形成的一個指令集。ARM指令長度為32位而Thumb指令長度為16位,所以使用Thumb指令集可以得到密度更高的代碼,這對于需要嚴格控制產品成本的設計是非常有意義的。
與ARM指令集相比,Thumb指令集有以下局限:
1)完成相同的操作,通常需要更多的Thumb指令,所以在對系統運行時間要求苛刻的應用場合ARM指令集更為合適;
2)Thumb指令集沒有包含進行異常處理的一些指令,因此在異常處理低級處理時,還是需要ARM指令,這就使得Thumb指令必須與ARM指令配合使用。
Thumb指令集有兩個版本:版本1用于ARMv4的T變種;而版本2用于ARMv5及以上的T變種。
Thumb指令集的版本2相對于版本1有以下特點:
1)通過增加指令和對已有指令的修改,提高ARM指令和Thumb指令混合使用時的效率;
2)增加了軟件斷點指令;
3)更嚴格定義了Thumb乘法指令對條件標志位的影響。