ARM指令

arm汇编指令与机器码对应分析

我们编写的汇编程序还是不够底层,CPU都是对机器码进行操作的,所以还需要用汇编器将汇编代码转换成机器码才能被CPU处理。下面举几个例子来说说分析ARM机器码的方法。
编译环境: 软件: keil5 ;交叉编译工具 arm-2011.09-70-arm-none-linux-gnueabi。

1: moveq r5,r1 
0x00000018  01A05001  MOVEQ     R5,R1

前者是汇编代码,后者是其对应的机器码。用第一个例子具体讲一下分析的方法。首先必须具备的一个手册就是ARM Architecture Reference Manual。这是学习ARM处理器绝对权威的一个参考资料。
在keil5中,ARM汇编手册在:点击菜单栏的 “Help” ->μVersion Help ->目录 ->Arm Compiler 5 User's Guides ->Assembler User Guide->ARM and Thumb Instructions

31--28是条件段,取值表如下所示:
0000(EQ)           Z = 1           Equal           相等                                                          
0001(NE)           Z = 0           Not Equal       不相等 
0010(CS/HS)        C = 1           Carry Set       有进位,无符号数 大于等于
0011(CC/LO)        C = 0           Carry Clear     无进位,无符号数 小于
0100(MI)           N = 1           Minus,negative  减 ,负数
0101(PL)           N = 0           Plus,positive or zero  加, 正数或零
0110(VS)           V = 1           Overflow Set    溢出
0111(VC)           V = 0           Overflow Clear  无溢出 
1000(HI)           C ==1 and Z ==0 Unsighed Higher 无符号数大于
1001(LS)           C ==0 or Z ==1  Unsigned lower or same  无符号数小于等于
1010(GE)           N ==V           Signed greater than or equal    
1011(LT)           N !=V           Signed Less Than  有符号数小于
1100(GT)           Z ==0 and N ==V Signed Greater Than 有符号数大于
1101(LE)           Z ==1 or N!=V   Signed less than or equal  有符号数小于等于
1110(none)         不启用条件判断

这里的例子中moveq后面跟的条件是相等,所以是0000(EQ),对应的机器码为0000

27--26为保留位,恒为00
25位:Distinguishes between the immediate and register forms of <shifter_operand>. 
标志shifter_operand段存放的是立即数还是寄存器。若为寄存器则置零,若为立即数则置一。
24--21为opcode,标明指令的类型,下面是opcode的取值表
Opcode                  Mnemonic
0000                    AND
0001                    EOR
0010                    SUB
0011                    RSB
0100                    ADD
0101                    ADC
0110                    SBC
0111                    RSC
1000                    TST
1001                    TEQ
1010                    CMP
1011                    CMN
1100                    ORR
1101                    MOV
1110                    BIC
1111                    MVN
这个例子中mov对应的为1101
20位:Signifies that the instruction updates the condition codes.
表明该指令是否会影响程序状态字寄存器。是则置一,否则置零
19--16位:Specifies the first source operand register.
标明第一个源操作数寄存器,见每个指令的格式,有的有Rd,有的没有。
由MOV指令的一般格式可以看出,他是没有使用Rd的,所以这几位填全0,其他使用到Rn的,这几位填通用寄存器标号的二进制值。如r2,则为0010
15--12位:Specifies the destination register. 
标明目的寄存器,填充方法同Rn
11--0位:Specifies the second source operand.  
标明第二个源操作数,若为立即数则填该立即数的二进制值,若为通用寄存器则填通用寄存器标号的二进制值。

第一条指令的机器码到这里就分析完了,下面具其他几个不同的情况来验证上面的说法,分析方法还是一样的,这里就不一一分析了。

2、 movne r2,r1
0001 00 0 1101 0 0000 0010 000000000001

 

3、 cmp r1,r2
1110 00 0 1010 1 0001 0000 000000000010

 

4、 add r0,r0,r1
1110 00 0 0100 0 0000 0000 000000000001

 

5、 bic r0,r1,#0b101
1110 00 1 1110 0 0001 0000 000000000101

搬移指令 mov mrs msr

mov   r13,#3		@把立即数3移动到r13寄存器中
mov   r0,r1	        @把寄存器r1移动到r0寄存器中
mov   r0,r1,LSL#2   @r1逻辑左移2位,存入r0中   
mov   r0,r1,LSR#2   @r1逻辑右移2位,存入r0中
mrs   r0,cpsr       @cpsr寄存器的数据,存入r0中
msr   cpsr,r0       @r0寄存器数据,存入cpsr中

立即数合法性:

4: mov r0,#256 
0x0000001C  E3A00C01  MOV       R0,#0x00000100

MOV 操作的立即数最大值为十进制的256 ,十六进制的0x100,超过则会报错
如果想赋值超过256的立即数到寄存器中,则用下面的伪指令进行替换:

ldr   r0,=0x12345678

逻辑指令 and orr bic tst cmp

and r0,r1,#0xFF         @ r0 = r1&0xFF 按位与
 
orr r3,r0,#0x0F         @ r3 = r0|0x0F 按位或
        
bic r0,r0,#0x03         @ 清除r0中的0号位和1号位 可简写成 bic r0,#0x03
tst r0,#0x20            @测试第6位是否为0 ,为0则Z标志置1 

cmp r1,r0               @将R1与R0相减做比较,并根据结果设置CPSR的标志位 若r1>r0 C=1;若r1<r0 N=1;若r1=r0 Z=1(疑似同时C=1). 

判断当前工作状态是否是ARM状态,是则切换到user 工作模式:

mrs   r0,cpsr
tst   r0,#0x20
andeq   r0,r0,#0xFFFFFFE0
orreq   r0,r0,#0x10
msreq   cpsr,r0	

使能中断和快速中断?

mrs   r0,cpsr
bic   r0,r0,#0xc0
msr   cpsr,r0

算术指令 add sub mul (arm 汇编没有除法指令)

add r0,r1,r2    @r0=r1+r2

sub  r0,r1,#3   @r0= r1 - 3

sub  r0,r1,r2,LSL#1       

mul  r1,r2,r3   @r1=r2*r3 

指令后缀 EQ NE LT LE GT GE

EQ=equal(相等)
NE=not equal(不等)
LT=little than(小于)
LE=little and equal(小于等于)
GT=great than(大于)
GE=great and equal(大于等于)

跳转指令

b     main      //跳转到标号为main地代码处 

bl     func     //保存下一条要执行的指令的位置到 LR寄存器,跳转函数func
               //当跳转代码结束后,用MOV PC,LR指令跳回来
beq    addr     //当CPSR寄存器中的Z条件码置位时,跳转到该地址处
bne  addr       //当不等时,跳转到地址addr

例:用汇编实现下面功能

void main(void)
{
     int ret=0;
     func1(2);
     while(1) {};   	
}	
func1(int a)
{
     if(a==2)
          return func2(a);
     else
          return func3(a);  
}

func2(int a) 
{    
     return a+3;
} 

func3(int a)
{    
     return a-1;
} 

版本v1

.text

	mov r3,#0
	mov r0,#2
	b func1

mainend:	b mainend

func1:
	cmp r0,#2
	beq func2
	bne func3
loop:	
	b mainend
	
func2:
	add r0,r0,#2
	b loop

func3:
	sub r0,r0,#1
	b loop

.end

版本v2

.text

	mov r3,#0
	mov r0,#2
	bl func1

mainend:	b mainend

func1:
	mov r8,LR
	cmp r0,#2
	bleq func2
	blne func3
	mov LR,r8	
	mov PC,LR
	
func2:
	add r0,r0,#2
	mov PC,LR

func3:
	sub r0,r0,#1
	mov PC,LR

.end

例:实现 延时1秒函数

@delay fos 1 second
delay1s:	
    ldr    r4,=0x3FFFF   //估值
loop_delay1s:
     sub    r4,r4,#1
	cmp    r4,#0         
	bne    loop_delay1s
delay1s_end:
    mov    pc,lr

存储器-寄存器 间存储指令 Load/Store

注:load/store 架构规定,存储器之间不能直接拷贝,需通过寄存器做中转

ldr r0,[r1]  (load)   //r0=*r1    r1里存放的是地址,把该地址里存放的内容读入到r0中   
                       //LDRB(byte)  LDRH(half word)    

ldr r0,[r1,#8]      //r0=*(r1+8)  存储器地址为r1+8的字数据读入寄存器0。
ldr r0,[r1,#8]!    //r0=*(r1+8)  存储器地址为r1+8的字数据读入寄存器0,并更新r1寄存器,r1 = r1+8。
ldr pc,_irq           // pc = *(_irq)  将标号中的内容放入pc中

str r0,[r1] (store)   // *r1 = r0   将寄存器r0中值写入到存储器地址为r1的空间中  

str r0,[r1],#4        // r0=*r1, r1=r1+4  将r0 中的字数据写入以r1为地址的内存中,并将新地址r1+4 写入r1

str r0,[r1,#4]        //*(r1+4)=r0  将r0 中的字数据写入以r1+4 为地址的内存中    
str r0,[r1,#4]!        //*(r1+4)=r0  将r0 中的字数据写入以r1+4 为地址的内存中,并将新地址r1+4 写入r1 

例:拷贝srcBuf里内容 到destBuf中

.text

	mov r0 ,#0
	ldr r1,=srcBuf
	ldr r3,=destBuf
loop:
	ldrb r2,[r1],#1
     strb r2,[r3],#1 
	cmp r0,#3
	add r0,#1
	bne loop

.data

srcBuf:  
     .byte 0x1,0x2,0x3,0x4
destBuf:
     .space 8
.end

例:拷贝字符串srcBuf里内容 到destBuf中

.text

	mov r0 ,#0
	ldr r1,=srcBuf
	ldr r3,=destBuf
loop:
	ldrb r2,[r1],#1
    strb r2,[r3],#1
	cmp r0,#7
	add r0,r0,#1
	bne loop

.data

srcBuf:  
     .string "abcdefg\0"
destBuf:
     .space 8
.end

批量操作指令

批量操作指令:

ia-Increment After             赋值之后再增加
ib-Increment Before            增加之后再赋值         
da-Dec After                   赋值之后再减少
db-Dec Before                  减少之后再赋值    
   ldmia  r0!, {r3 - r10}   //r0里地址指向的内容批量,load 到r3~r10寄存器中, r0里地址会自动加4
   stmia  r0!, {r3 - r10}   //把r3~r10寄存器中内容,store 到r0里地址执行空间中,r0里地址会自动加4

例:实现块数据批量拷贝

.text

	ldr	r12,=srcBuf
	ldr r13,=destBuf
	ldmia r12!,{r0 - r11}
	stmia r13!,{r0 - r11}


.data

srcBuf:  
     .string "abcd1234abcd1234abcd1234abcd1234abcd1234abcd123\0"
destBuf:
     .space 12*4
.end

堆栈操作指令

stmfd sp!,{r0-r12,lr}   //将寄存器r0~r12 lr中的值存入栈中,常用于中断保护现场,“!”表示会自动偏移                                       
ldmfd sp!,{r0-r12,pc}^  //将栈中值逐个弹出到寄存器r0~r12 pc中,常用于中恢复断现场,“^”表示会恢复spsr到cpsr

软中断指令

swi 0x02  @产生软中断, 软中断号为2