概述
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
最后
以上就是老迟到镜子为你收集整理的arm第九天(汇编之指令下)的全部内容,希望文章能够帮你解决arm第九天(汇编之指令下)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复