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

3.2 ARM指令的條件碼

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

協(xié)處理器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時,會產(chǎn)生不可預知的結果。
③ 事后更新[<Rn>], #+/-<offset_8>*4
指令編碼格式如下所示:

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

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

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

3.5.2 數(shù)據(jù)定義偽操作

續(xù)表

3.5.4 棧中數(shù)據(jù)幀描述偽操作
主要用于調(diào)試,此處不做介紹。
3.5.5 信息報告?zhèn)尾僮?/h4>
表3-2 opt偽操作選項編碼

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

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

續(xù)表

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

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