arm第九天(彙編之指令下)
ARM七種工作模式
- 管理
- 快速中斷
- 中斷
- 中止
- 未定義
- 系統
- 使用者
ARM執行狀態
- ARM狀態 arm指令 32bit 地址字對齊(整除4),最後兩bit為0
- Thumb狀態 thumb指令 16bit 半字對齊,最後1bit為0
ARM暫存器
不同的狀態,訪問不同的暫存器
R0-R12 共用
R13(SP) 儲存棧頂指標
R14(LR) 用來儲存返回地址 如,BL,在跳轉前要儲存下一條指令的地址
R15(PC) 用來儲存需要取指指令的地址
取指、解碼、執行
CPSR(1個)
模式位 bit[4:0]
執行狀態bit[5]
FIQ禁止位 bit[6]
IRQ禁止位 bit[7]
條件標誌位bit[31:28] NZCV
SPSR(5個)
ARM支援資料型別:位元組、字、半字
ARM記憶體對齊方式:字、半字、位元組
跳轉指令
- B 可以用來構建迴圈執行
相對跳轉 PC(new) = PC(當前) +/- 偏移(32MB) 無返回 - BL 常用來呼叫子函式
相對跳轉 PC(new) = PC(當前) +/- 偏移(32MB) 帶返回
CPU會自動將下一條指令地址儲存到LR
MOV PC,LR - BX
絕對跳轉
CPSR[5] = Rm[0]
PC = Rm & 0xFFFFFFFE - BLX
資料處理指令
案例:
ADD R0,R1,#1 //R0 = R1+1
MOVEEQ R0,R2 //條件執行,在EQ(相等)條件下,招行R0 = R2
SUBEQS R0,R1,R2,LSR #1 // 在EQ條件下,R0 = R1-(R2>>1)
CMP R1,R3 //R1-R3
資料處理指令可細分為4類:
資料傳送指令(MOV,MVN)
算術運算指令(ADD,ADC,SUB,SBC,RSB)
位運算指令(AND,ORR,EOR,BIC)
比較指令(CMP,CMN,TST,TEQ)
資料處理指令的一般格式:
{cond} {S} ,,
:指令操作碼,如ADD,MOV,SUB
{cond}:條件,如EQ
{S} :分兩種情況
1,當目標暫存器不是PC時,根據目標暫存器的結果影響CPSR的NACV條件標誌位
MOVS R0,R2 //因為MOV 加了S,根據R0的結果影響NZCV
2,當目標暫存器是PC時,CPSR = SPSR,如:
MOVS PC,LR //PC=LR,CPSR = SPSR 異常的返回; 這種指令只能在異常模式下面使用
:目標暫存器
:第一源運算元,只能是暫存器
:第二源運算元
1,立即數:相當於常量,直接出現在指令中,不用儲存在暫存器或儲存器,如:
ADD R0,R1,#3
2,暫存器,如
ADD R0,R1,R2 //R0 = R1 + R2
3,暫存器移位,把暫存器裡面的值進行不到的移位操作得到的結果。
3.1 ,LSL #,如
ADD R0,R1,R2 LSL #1 //R0 = R1 + (R2 << 1) ,邏輯左移,空出低位用0填充
3.2 ,LSL ,如
ADD R0,R1,R2 R2 LSL R3 //邏輯左移,空出低位用1填充,左移的位數放在了R3暫存器中
3.3 ,LSR #,如
ADD R0,R1,R2 LSR #1 //R0 = R1 + (R2 >> 1) ,邏輯右移,空出的高位用0填充
3.4 ,LSR ,右移的位數放在了暫存器中
3.5 ,ASR #
3.6 ,ASR
3.7 ,ROR #
ROR迴圈右移:移出的低位用於填充空出的高位
3.8 ,ROR
3.9 RRX
RRX:擴充套件右移一位,將Rm暫存器的內容右移一位,空出的高位用原來的CPSR的位填充,
並Rm移出的1位填充CPSR的C位,
只有RRX,不需要指定移位位數
第一源運算元:暫存器
第二源運算元(11種):
1,立即數
2,暫存器
3,暫存器移位
1, lSL #shift
2,LSL Rs 空出的低位用0填充
3,LSR #shift
4,LSR Rs 空出的高位用0填充
5,ASR #shift
6,ASR Rs 空出的高位用符號位填充
7,ROR #shift
8,ROR Rs 迴圈右移,將移出的低位填充空出的高位
9, RRX 擴充套件右移一位
立即數(8點陣圖立即數):
CMP R1,#0x4800000
32 bit 機器碼
找到8bit立即數,然後將8bit立即數擴充套件為32bit,再將32bit的數採用迴圈右移的方式進行移位,移2X(rot)次,最終得到0x04800000,將0x12擴充套件成32bit,高位全用0填充,然後將迴圈右移2*5 = 10 次
0x04800000
5 0x12 迴圈右移10次
6 0x48 迴圈右移12次
如果找到的組合不唯一,取迴圈次數少的那組。
合法立即資料的判定?
1,0-0xFF 就是立即數
MOV R1,0x55 //R1 = 0x55
2,立即數中1的個數大於8的一定不合法
3,從兩個方向看1的間距大於8,非法
資料傳送指令
指令格式
MOV{cond}{S} ,
shifter_operand:立即數(8點陣圖)、暫存器、暫存器移位
示例:
MOVEQ R0,#0x80 //在EQ的條件下,R0=0x80
MOVS PC,LR //由於MOV後有S,PC = LR,CPSR= SPSR,用於異常返回;只能在異常模式下使用
MOVEQS R0,R1 //在EQ條件下,後面加有S,根據R0的結果去影響CPSR的NZCV,
//後面幾行程式碼是具體影響方法
N = R0[31] //符號位
Z = if R0 == 0 then 1 else 0 //為正?為負?
C = shifter_carry_out,
不影響V,V代表溢位
MOV R3,R4,LSL #2 //R3 = (R4 << 2)
MOV R0,R0 //R0 = R0
MOV PC,R14 //PC = R14
NZCV:
N:運算結果為負值的標誌位
N= 1,負值
N= 0,正值
Z:運算結果為0的標誌位
Z=1,結果為0
Z= 0,結果為非0
資料反傳送指令:MVN
指令格式:
MVN{cond}{S} ,
功能:Rd = 按位取反
shifter_operand:立即數(8點陣圖),暫存器,暫存器移位
示例:
MOVNEQ R0,#0x80 //EQ,將0x80按位取反 R0 = 0xFFFFFF7F
MVNS PC,R0 //後面加了S,PC = R0按位取反,CPSR = SPSR ;異常返回
MVNEQS R0,R1 //EQ,R0 = R1按位取反,R0影響CPSR的NZCV
N = Rd[31]
Z = if Rd ==0 then 1 else 0
C = shifter_carry_cout
不影響V
MVN R3,R4,LSL #2 // R3=(R4<<2)
案例:
MOV R0,#0x00 //R0 =0x00
MVN R0,#0x00 //R0 =0xFFFF,FFFF
MOV R0,#0xffffff00 //非法立即數
MVN R0,#0x000000FF //R0 = 0xffffff00
小結:
MOV
MVN
這兩指令沒有第一源運算元
S:
PC,CPSR= SPSR
不是PC,根據結果影響CPSR的NZCV
算術指令
加法指令:ADD
指令格式
ADD{cond}{S} ,,
功能:Rd = Rn +
示例 :
ADD R0,R1,#0x80 //
ADDEQ R0,R1,R3
ADDS R0,R1,R2 //R0 = R1+R2根據R0結果影響NZVC
N = Rd[31]
Z = if Rd ==0 then 1 else 0
C = CarryFrom(Rn+shifter_operand)
V = OVerflowFrom(Rn+shifter_operand)
ADDS PC,R1,#0 //PC = R1,CPSR = SPSR
NZCV:
N:運算結果為負值的標誌位
N= 1,負值
N= 0,正值
Z:運算結果為0的標誌位
Z=1,結果為0
Z= 0,結果為非0
C:運算結果產生進位的標誌位
進位針對無符號數來說
C= 0 ,有進位
C = 1,無進位
V:運算結果產生溢位的標誌位
溢位針對有符號數來說
V= 0 ,有溢位
V = 1,無溢位
8bit
有符號:-128~127
無符號:0~255
3AH + 7CH = B6H //H表示十進位制
無符號:58+124 = 182,沒有超過255,無進位
有符號:58+124 = 182,超過了127,溢位
AAH + 7CH = B6H //H表示十進位制
無符號:170+124 = 294,沒有超過255,有進位
有符號:-86+124 = 38,沒有超過了127,無溢位
帶進位的加法:ADC
指令格式:
ADC{cond}{S} ,,
功能: Rd = Rn + + C位
示例 :
ADC R0,R1,#0x80 //R0 = R1+0x80+C
ADCEQ R0,R1,R3 //EQ,R0 = ..
ADCS R0,R1,R3 //R0 = Ra+R2+C,根據R0的值,影響CPSR的NZCV
Z = if Rd ==0 then 1 else 0
C = CarryFrom(Rn+shifter_operand + C Flag)
V = OverflowFrom(Rn+shifter_operand + C Flag)
ADCS PC,R1,#0 //
案例:
加數(R1,R0)
被加數(R3,R2)
結果(R1,R0)
前者為高32位,後者為低32位
減法指令
指令格式:
SUB{cond}{S} ,,
示例:
SUB R0,R1,#0x80 //R0 = R1-0x80
SUBNE R0,R1,R3 //NE,R0 = R1-R3
SUBS R0,R1,R2 //R0 = R1-R2,根據結果R0影響CPSR的NZCV
Z = if Rd ==0 then 1 else 0
C = NOT BorrowFrom(Rn-shifter_operand)
//有借位 C= 0
//無借位 C= 1
V = OverflowFrom(Rn-shifter_operand )
SUBS PC,R14,#04 //PC = R14 - 4,CPSR = SPSR 異常返回
案例:
SUB R0,R1,R2
SUB R0,R1,#256
SUB R0,R2,R3,LSL #1 //R0 = R2 - (R3<<1);
SUBS R0,R1,R3 //R1 = R2-R3,影響NZCV
SUBS PC,R14,#4 //同上
帶借位的減法指令:SBC
指令格式:
SBC{S} ,,
功能: Rd= Rn- shifter_operand - Not C
示例:
SBC R0,R1,#0x80 //R0 = R1 - 0x80 - Not C
SBCNE R0,R1,R3 //NE,R0 = R1 - R3 - Not C
SBCS R0,R1,R2//R0 = R1-R2,根據結果R0影響CPSR的NZCV
Z = if Rd ==0 then 1 else 0
C = NOT BorrowFrom(Rn-shifter_operand - NOT(C Flag))
V = OverflowFrom(Rn-shifter_operand - NOT(C Flag))
SBCS PC,R1#0
反向減法指令:RSB
指令格式:
RSB{S} ,,
功能:
Rd = - Rn
示例:
RSB R0,R1,0x80 // r0= 0x80-r2
RSBNE R0,R1,R3 //NE,R0= R3-R1
RSBS R0,R1,R2 //R0= R2-R1,影響NZCV
....
PSBS PC,R1,#0
RSB R3,R1,#0 //R3 = -R1;
RSBS R1,R2,R2,LSL #2 //R1=(R2<<2)-R2 = R2*3,用反向減法實現乘法
//會影響標誌位
帶借位的反向減法指令:RSC
指令格式:
RSC{S} ,,
功能:Rd= shifter_operand - Rn - Not C
示例:
RSC R0,R1,#0x80 //R0 = 0x80 - R1- Not C
RSCNE R0,R1,R3 //R0 = R3 - R1- Not C
RSCS R0,R1,R3 //R0 = R3 - R1- Not C,由於加了S,會根據結果影響NZCV
N=Rd[31]
Z = ...
C = NOT BrowwFrom(shifter_operand - Rn - Not(C Flag))
...
總結
算術運算:
ADD 加法 Rd = Rn + C = 1 進位,C= 0無進位
ADC 帶進位的加法 Rd = Rn + +C 位
SUB 減法 Rd = Rn - , C = 0 有借位,C= 1無借位
SBC 帶借位的減法 Rd = Rn - - Not C
RSB 反向減法 Rd = - Rn
RSC帶借位的反向減法 Rd = - Rn - Not C
如果指令程式碼加了S:
1),目標PC,CPSR = SPSR 異常返回,只能在異常模式下進行
2),不是PC,根據Rd結果,影響CPSR的NZCV
位運算指令(與、或、異或、位清除)
位與指令:AND
指令格式:
AND{S} ,,
指令功能:
Rd = Rn &
AND R0,R1,0x80 //R0=R1&0x80,把R1的bit7保留,其餘清0
ANDNE R0,R1,R3//NE ,R0=R1&R3
ANDS R0,R1,R3 //R0=R1&R3,由於後面加了S,會根據R0的結果影響NZCV
N = Rd[31]
Z= if Rd == 0 ..
C = shifter_carry_out
ANDS PC,R1,#0xFFFFFFFF
ANDS R0,R1,R2,LSL #1 //R0 = R1& (R2<<1)
按位操作實現什麼目的?
&1 用來判斷某位是1還是0
&0 清除某一位
位與指令:ORR
指令格式:
ORR{S} ,,
指令功能:
Rd = Rn |
ORR R0,R1,#0x80//將R1的bit[7]置1
ORRNE R0,R1,R3
ORRS R0,R1,R2
N = Rd[31]
Z = if Rd == 0 then 1 else 0
C = shifter_carry_out
OPRS PC,R1,#0x0 //異常返回
ORR R0,R1,R2,LSL #2
用或操作實現什麼?
|1 ,將某位置1
|0,保留某些位,保持不變
異或指令:EOR
指令格式:
EOR{S} ,,
指令功能:
Rd = Rn^
示例
EOR R0,R1,#0x80
...
用異或操作實現什麼?
相同 為 0
不同 為1
1,判斷兩個數是否相等
EORS R3,R1,R2
R1^R2 == 0 表示 R1==R2
2,把一個變數的某位與1做異或可以實現取反
0 ^ 1 = 1
1^ 1 = 0
3,加密
0x80^ 0xFF = 1000,0000^ 1111,1111 = 0111,1111 = 0x7F
0x7F^ 0xFF = 0111,11111 = 1111,1111 = 1000,0000=0x80
這裡的0xFF就相當一個金鑰
EOR R0,R0,#FF
EOR R0,R0,R1
EORS R0,R0,#1
位清除指令:BIC
指令格式:
BIC{S} ,,
指令功能:
Rd = Rn&(~ )
BIC R0,R1,#0x80 // Ro = R1&(~0x80)
BICEQ R0,R1,R3
BICS R0,R1,R2
N = Rd[31]
Z = if Rd == 0 then 1 else 0
C = shifter_carry_out
BICS PC,R1,#0x0
總結
位運算
AND
ORR
EOR
BIC
有無S:
比較測試指令
比較指令:CMP
指令格式:
CMP ,
指令功能:
Rn -
CMP R0,#0x80
CMPEQ R0,R3
N = ALU_OUT[31]
Z = if ALU_out == 0 then 1 else 0
C = NOT BorrowFrom(Rn - shifter_operand)
V = OverflowFrom(Rn - shifter_operand)
cmp R1,#10 //R1-10 == 0 ,r1=10 z = 1
moveq r0,r1
bleq test //這裡的eq,用的cmp R1,#10裡面的影響結果
...
test:
...
負值比較指令:CMN
指令格式:
CMN ,
指令功能:
Rn +
CMN R0,#0x80 // R0 + 0x80
CMN R1,#5 // R0 + 5 == 0 ? Z = 1
BEQ SKIP //根據Z判斷是否為相等
....
SKIP:
...
位測試指令:TST
指令格式:
TST ,
指令功能:
Rn &
會自動影響NZCV,後面的指令可以根據NZCV來判斷結果
TST R0,#0x80
TESTEQ R0,R3
N = ALU_out[31]
Z = if ALU_out == 0 ...
C shifter_carray_out
TXT R0,#0x1
ADDEQ R1,R2,R3
TST R1,R4
相等測試指令:TEQ
指令格式:
TEQ ,
指令功能:
Rn ^
TEQ R0,#0x80
TEQNE R0,R3
N = ALU_out[31]
Z = if ALU_out == 0 then 1 else 0
C shifter_carray_out
比較
CMP -
CMN +
TST &
TEQ ^
總結
資料處理指令
- 資料傳送(MOV,MVN)
- 算術運算(ADD ADC SUB RSB RSC)
進位:C=1 有進位,C = 0,無進位
借位:C=0 有借位,C =1,無借位 - 位運算(AND ORR EOR BIC)
- 比較測試(CMP CMN TST TEQ)
一般格式:
操作碼{條件碼}S 目標暫存器,第一源運算元,第二源運算元
- 第一源運算元只能是暫存器
第二源運算元
1,8點陣圖立即資料 (#rot ,8bit立即數,ROR)
2,暫存器
3,暫存器移位(LSL,LSR,ASR,ROR,RRX).S
1,目標暫存器為pC, CPSR = SPSR
2,目標暫存器不是PC,根據運算結果影響CPSR的NZCV資料傳送指令沒有第一源運算元
- 比較測試指令沒有目標暫存器,不加S,結果預設影響NZCV
案例
用匯編指令實現1-10累加和計算。
思路:用B跳轉指令構建迴圈,用比較指令跳出迴圈,用加法指令累加。
sum.s
.text
.global _start
.global sum
.code 32
_start:
mov r0,#10 //儲存迴圈次數
mov r1,#0 //用來儲存結果
sum:
add r1,r1,r0 //r1 = r1+r0
subs r0,r0,#1 // r0--
cmp r0,#0//比較R0 與 0 ,如果不等於0,Z位就會為0
bne sum //條件跳轉,ne表示不等於,則執行sum,如果相等,執行 b .
sum_ok:
b .
.end
編譯、連結、除錯
arm-linux-as -g -o sum.o sum.s
arm-linux-ld -e _start,-o,sum sum.o
qemu-arm -g 1234 sum #除錯
用匯編指令求最大公約數?
20 12 8 = 20-12 4 = 12-8
5*4 4*3 4*2 4*1
R0=20,R1=12
while(R0!=R1){
if(R0>=R1){//cmp 預設影響NZCV
//beq
R0 = R0 - R1;//subcs
}else{
R1= R1-R0;//subcc
//b
}
}
gcd.s
.text
.code 32
.global gcd //標號
gcd:
//把兩個數先放到暫存器
mov r0,#20
mov r1,#12
loop:
cmp r0,r1
//以下幾行相當於if .. else if ..
beq boop_end
subcs r0,r0,r1 //如果cmp r0,r1條件滿足,執行本條;cs 表示無符號大於或等於
subcc r1,r1,r0 //如果cmp r0,r1條件不滿足,執行本條
b loop //無條件跳轉
boop_end:
b .
.end