ARM匯編和內嵌匯編
首先要判斷我們用的是ldr arm指令還是偽指令。 當我們用的是arm指令時(shí),它的作用不是向寄存器里加載立即數,而是將某個(gè)地址里 的內容加載到寄存器。而偽指令ldr的作用就是向寄存器里加載立即數。
(1) ldr偽指令
ldr偽指令的格式是 ldr Rn, =expr
其中,expr是要加載到Rn中的內容,一般可以是立即數或者label。
如果expr可以用8bit數據向右移偶數位得到,那么這條偽指令就被編譯器翻譯成mov指令。具體的移位情況可以去查閱資料。反之如果立即數很大,超過(guò)了12bit的表示范疇,那么就不能用一條mov指令了,畢竟arm指令最大只有32bit的空間可用(RISC的arm所有的指令長(cháng)度是一致的,效率較高,當然我們并不關(guān)心16bit的thumb指令)。如果不能用一條32bit的指令乘下來(lái),那么就只能另辟蹊徑了,新開(kāi)一段緩沖,將立即數expr放到里面,然后將其地址(暫時(shí)標記為addr)拿來(lái)使用:
ldr Rn, addr
xxx (xxx就是expr)
xxx
由于編譯器一般來(lái)說(shuō)新安排的存儲這個(gè)立即數expr的緩沖的位置是在相應代碼的附近(這個(gè)應該可以控制,好像是使用.ltorg偽指令)。我們從addr地址加載數據到Rn不就可以了。
(2)ldr arm 指令
就是將一個(gè)地址的內容加載到寄存器。不能用mov,因為arm里的mov只是在寄存器之間傳輸數據,不支持在寄出器和memory之間傳遞數據。因此就出現了ldr/str指令。如ldr Rn, addr,注意這里的addr的值也是有限制的。這個(gè)label應該距離當前指令的距離不超過(guò)4k。因為我們知道label在具體使用的時(shí)候應該是被翻譯成了相對偏移,如果這個(gè)label長(cháng)度不超過(guò)12bit,那么就不應超過(guò)4k,我們可以這樣做:
ldr pc, _start_armboot
_start_armboot: .word arm_startboot
這樣label _start_armboot就在指令下方,因此肯定是合法的。
ldr r0, [r1, #4] 的含義就是把r1+4 這個(gè)地址處的DOWRD 加載到r0,而尋址后,r1 的內容并不改變。
ldr r0, [r1, #4]! 這種變址方式有點(diǎn)類(lèi)似于++i的含義,尋址前先對基地址寄存器進(jìn)行運算,然后尋址. 其基本的語(yǔ)法是在尋址符[]后面加上一個(gè)"!" 來(lái)表示
二、.world
說(shuō)說(shuō)這個(gè) .word 的作用。
word expression 就是在當前位置放一個(gè) word 型的值,這個(gè)值就是expression
舉例來(lái)說(shuō),
_rWTCON:
.word 0x15300000
就是在當前地址,即 _rWTCON 處放一個(gè)值0x15300000
不是把地址0x1530 0000 上的內容傳遞到r1,是把地址_rWTCON上的內容放到r1,而地址_rWTCON上的內容是0x15300000。實(shí)際上就是把r1設置為0x15300000
三 bic
BIC(位清除)指令對 Rn 中的值 和 Operand2 值的反碼按位進(jìn)行邏輯“與”運算。 (注意:ARM官方網(wǎng)站有誤, 寫(xiě)的是補碼)
BIC 是 邏輯”與非” 指令, 實(shí)現的 Bit Clear的功能
舉例:
BIC R0, R0 , #0xF0000000
#將 R0 高4位清零
BIC R1, R1, #0x0F
#將R1 低4位清0
RSB 反向減法
Rn, Operand2
RSB(反向減法)指令可從 Operand2 中的值減去 Rn 中的值。
這是很有用的,因為有了該指令,Operand2 的選項范圍就會(huì )更大。
例如:
RSB r4, r4, #1280
從1280中減去 R4
RSB R4, R0, #0×46
從0×46 中 減去 R0, 放入R4
四、ADR
ADR的定義為:小范圍的地址讀取偽指令,ADR指令將基于PC相對偏移的地址值讀取到寄存器中,在編譯源程序時(shí)ADR偽指令被編譯器 替換成一條合適的指令。通常,編譯器用一條ADD指令或SUB指令來(lái)實(shí)現該ADR偽指令的功能,若不能用一條指令實(shí)現,剛產(chǎn)生錯誤。
在如上的定義中,有兩個(gè)關(guān)鍵信息:⑴將基于PC相對偏移的地址值讀取到寄存器中;⑵被編譯器替換成一條合適的指令。ADR指令只能將地址值讀取到寄存器中,而不能是其它的立即數,并用只能用一條指令。
如果在匯編程序中使用ADR R1,ResetHandel語(yǔ)句,其中ResetHandel是匯編程序中的一個(gè)標簽,此條偽指令的作用是把ResetHandel標簽所在的指令地址 讀取到寄存器R0中
根據上面的分析,可以看到,編譯器在編譯的時(shí)候把ADR偽指令編譯成一個(gè)ADD R1,PC,Immediate指令,其中Immediate是一個(gè)立即數,數值是ResetHandel語(yǔ)句和此條偽指令之間的差值,由編譯器自動(dòng)算 出。由于立即數尋址的約束,這個(gè)Immediate存在一定的約束,所以會(huì )出現定義中所說(shuō)的不能用一條指令實(shí)現。
五、ldmia 和 stmia
所有的示例指令執行前:
mem32[0x1000C] = 0x04
mem32[0x10008] = 0x03
mem32[0x10004] = 0x02
mem32[0x10000] = 0x01
r0 = 0x00010010
r1 = 0x00000000
r3 = 0x00000000
r4 = 0x00000000
1) ldmia r0!, {r1-r3} 2) ldmib r0!, {r1-r3}
執行后: 執行后:
r0 = 0x0010001C r0 = 0x0010001C
r1 = 0x01 r1 = 0x02
r2 = 0x02 r2 = 0x03
r3 = 0x03 r3 = 0x04
至于DA 和DB 的模式,和IA / IB 是類(lèi)似的,不多說(shuō)了。
最后要說(shuō)的是,使用ldm 和stm指令對進(jìn)行寄存器組的保護是很常見(jiàn)和有效的功能。配對方案:
stmia / ldmdb
stmib / ldmda
stmda / ldmib
stmdb / ldmia
繼續來(lái)看兩個(gè)例子:
執行前:
r0 = 0x00001000
r1 = 0x00000003
r2 = 0x00000002
r3 = 0x00000001
執行的指令:
stmib r0!, {r1-r3}
mov r1, #1 ; These regs have been modified
mov r2, #2
mov r3, #3
當前寄存器狀態(tài):
r0 = 0x0000100C
r1 = 0x00000001
r2 = 0x00000002
r3 = 0x00000003
ldmia r0!, {r1-r3}
最后的結果:
r0 = 0x00001000
r1 = 0x00000003
r2 = 0x00000002
r3 = 0x00000001
另外,我們還可以利用這個(gè)指令對完成內存塊的高效copy:
loop
ldmia r9!, {r0-r7}
stmia r10!, {r0-r7}
cmp r9, r11
bne loop
六、CMP
CMP指令的格式為:
CMP{條件} 操作數1,操作數2
CMP指令用于把一個(gè)寄存器的內容和另一個(gè)寄存器的內容或立即數進(jìn)行比較,同時(shí)更新CPSR中條件標志位的值。該指令進(jìn)行一次減法運算,但不存儲結果,只 更改條件標志位。標志位表示的是操作數1與操作數2的關(guān)系(大、小、相等),例如,當操作數1大于操作操作數2,則此后的有GT 后綴的指令將可以執行。
指令示例:
CMP R1,R0 ;將寄存器R1的值與寄存器R0的值相減,并根據結果設置CPSR的標志位
CMP R1,#100 ;將寄存器R1的值與立即數100相減,并根據結果設置CPSR的標志位
cmp r0, #0
beq 1f ; 如果r0==0那么向前跳轉到B處執行
bne 1b ; 否則向后跳轉到A處執行1: ;
1b,1f里的b和f表示backward和forward,1表示局部標簽1
TST R0, #0X8
BNE SuspendUp ;BNE指令是“不相等(或不為0)跳轉指令”:
LDR R1,#0x00000000
先進(jìn)行and運算,如果R0的第四位不為1,則結果為零,則設置zero=1(繼續下面的LDR指令);
否則,zero=0(跳到SuspendUp處執行)
評論