ARM体系结构2:汇编指令集

一、汇编文件中的主要符号

1> 汇编指令: 编译器可以将其编译生成机器码,执行汇编指令可以完成一定的功能,占用内存中代码段空间。

2> 伪指令:伪指令本身不是一条指令,但是编译器可以将其编译生成多条指令,共同完成一条伪指令的功能,伪指令占用内存中代码段的空间。

3> 伪操作:伪操作不占用代码段的内存空间,只是告诉编译器如何对代码进行编译的。

比如:.text .end .global .glabl .data .if .else .endif .byte .short .word

编译器不同,伪操作也是不同的。

4> 注释:编译器的不同,代码的注释方式也是不同的。

当行注释:@

多行注释:/**/ .if 0/1 .else .endif

二、最基本的汇编指令的语法格式

格式:

        {cond}{s}  Rd, Rn, oprand_shifter 

解释:

        :指令码,汇编指令对应的指令名字,比如mov  add   sub

        {cond}:条件码,实现汇编指令的有条件执行

        {s}:状态位,指令后边加s,指令的执行结果影响CPSR的NZCV位,

                              指令后边不加是,指令的执行结果不影响CPSR的NZCV位。

        Rd:目标寄存器,存放指令的执行结果,只能是普通的寄存器R0-R15

        Rn:第一个操作寄存器,只能是普通的寄存器R0-R15

        Oprand_shifter:第二个操作数

                1> 可以是一个普通的寄存器

                2> 可以是一个立即数

                3> 可以是一个经过移位操作的寄存器

注意:

        1》1条汇编指令占用1行

        2》汇编指令不区分大小写

        3》{cond}{s} 连到一起写

        4》Rd,Rn,Oprand_shifter 使用英文逗号隔开

        5》{cond}{s}和Rd之间使用空格隔开

三、汇编指令的分类

1> 数据操作指令

        数据搬移指令

        算数运算指令

        移位操作指令

        位运算指令

        比较指令 

2> 跳转指令

3> load/store内存读写指令

        单寄存器操作指令

        多寄存器操作指令

        栈操作指令

4> 软中断指令

5> 特殊功能寄存器读写指令

四、数据操作指令

4.1 数据搬移指令

4.1.1 指令码

mov ---> 将第二个操作数赋值给目标寄存器

mvn ---> 将第二个操作数按位取反赋值给目标寄存器 

4.1.2 指令的格式

{cond}{s}  Rd,Oprand_shifter 

4.1.3 立即数相关的概念

mov r0, #0xFF ==>编译生成32位的机器码 ==>包含mov, r0, 立即数的信息


如何判断一个数是否为立即数

首先根据需要判断的那个数,找到一个0-255之间的数,然后将这个0-255之间的数循环右移偶数位,如果循环右移偶数位之后的结果和要判断的那个数相等,则那个数就是一个立即数。 

4.1.4 ldr 伪指令

格式:

        ldr Rd, = number  @将number数赋值给Rd寄存器 

4.2 算数运算指令

4.2.1 指令码

add ----> 普通的加法指令,不需要考虑进位标志位(CPSR的C位)

adc ----> 带进位的加法指令,需要考虑进位标志位(CPSR的C位)

sub ----> 普通的减法指令,不需要考虑借位标志位(CPSR的C位)

sbc ----> 带借位的减法指令,需要考虑借位标志位(CPSR的C位)

mul ----> 乘法指令

div  ----> 除法指令,arm-v8架构中引入了除法指令。

4.2.2 指令格式

{cond}{s}  Rd, Rn,  Oprand_shifter 

4.3 移位操作指令

4.3.1 指令码

lsl ---> 逻辑左移/无符号数左移

lsr ---> 逻辑右移/无符号数右移

asr---> 算数右移/有符号数右移

ror ---> 循环右移

4.3.2 指令格式

{cond}{s}  Rd,  Rn,  Oprand_shifter 

 4.4 位运算指令

4.4.1 指令码

and : 按位与运算   &

orr :按位或运算   |

eor:  按位异或运算^

bic:按位清除

口诀:

        与0清0,与1不变

        或1置1,或0不变

        异或1取反,异或0不变

4.4.2 指令格式

{cond}{s}  Rd,  Rn,  Oprand_shifter 

4.5 比较指令

4.5.1 指令码

cmp ---> 比较两个数的大小

4.5.2 指令格式

        cmp{cond} Rn,oprand_shifter

注:

1> 比较指令没有目标寄存器,指令的执行结果影响的是cpsr的NZCV位,并且不需要加s。

2> 比较指令的本质做减法运算。

3> 比较指令和条件码配合使用。

五、跳转指令

5.1 指令码

b  --> 不保存返回地址的跳转指令

        执行跳转指令时,不保存跳转指令的下一条指令的地址到LR寄存器中

bl --> 保存返回地址的跳转指令

        执行跳转指令时,保存跳转指令的下一条指令的地址到LR寄存器中

5.2 指令格式

b{cond}label(标签/汇编函数名)

        --> 跳转到标签下的第一条指令执行。

bl {cond} label(标签/汇编函数名)

        ---> 跳转到标签下的第一条指令执行,并保存返回地址到LR中。

label(标签):

        汇编指令

        跳转指令的本质就是修改PC寄存器的值。

        有去无回就用b跳转指令,比如while(1){ }死循环;

        有去有回就用bl跳转指令,比如函数的调用。

5.3 实现跳转的其他的方式 

mov  pc,lr   ---> 可以实现跳转,不保存返回地址

ldr pc, =lable

六、特殊功能寄存器读写指令

6.1 指令码

msr ---> 将普通寄存器中的数据写到特殊功能寄存器中

mrs ---> 将特殊功能寄存器中的数据写到普通寄存器中

对于特殊工程寄存器cpsr的读写访问只能使用 msr 和 mrs指令。 

6.2 指令格式

msr cpsr, 普通寄存器/立即数  @spsr = 普通寄存器/立即数

mrs Rd, cpsr                           @Rd = cpsr

七、Load/Store 内存读写指令

7.1 单寄存操作指令

7.1.1 指令码

ldr --> 将内存中的数据读到普通的寄存器中,读4个字节的大小。

str --> 将普通寄存器中的数据写到内存中,写4个字节的大小


ldrb --> 将内存中的数据读到普通的寄存器中,读1个字节的大小

strb --> 将普通寄存器中的数据写到内存中,写1个字节的大小


ldrh --> 将内存中的数据读到普通寄存器中,读2个字节的大小

strh --> 将普通寄存器中的数据写到内存中,写2个字节的大小 


ld: Load

st:Store

r :Register

b :  Byte

h :  Half

7.1.2 指令格式

ldr / ldrh / ldrb   Rd,[Rm]

        Rd是一个普通的寄存器,

        [Rm]:Rm寄存器中的数据被当成内存的地址

        功能:将[Rm]指向地址空间中的数据读到Rd普通寄存器中。


str / strh  /strb    Rn,[Rm]

        Rn是一个普通的寄存器,

        [Rm]:Rm寄存器中的数据将被当成内存的地址。

        功能:将Rn寄存器中的数据,写到[Rm]指向的地址空间中。

其他用法:

ldr /ldrh /ldrb  Rd, [Rm,  #offset]

        将[Rm + offset]地址中的内容读到Rd寄存器中,Rm中的值不变

ldr/ ldrh /ldrb  Rd, [Rm], #offset

        将[Rm]地址中的内容读到Rd寄存器中,同时更新Rm中的地址:Rm = Rm+offset

ldr/ ldrh / ldrb  Rd, [Rm, #offset]!

        将[Rm + offset]地址中的内容读到Rd寄存器中,同时更新Rm中的地址:Rm=Rm+offset

        !:更新地址


str/ strh/ strb  Rn,[Rm, #offset]

        将Rn寄存器中的数据写到[Rm + offset]地址中,Rm中的值不变

str/ strh/ strb  Rn,  [Rm], #offset

        将Rn寄存器中的数据写到[Rm]地址中,同时更新Rm中的地址:Rm = Rm+offset

str/ strh/ strb  Rn,  [Rm,  #offset]

        将Rn寄存器中的数据写到[Rm + offset]地址中,同时更新Rm中的地址:Rm=Rm+offset

offset:偏移地址,偏移地址的大小是Load/Store指令可访问空间大小的整数倍。

7.2 多寄存器操作指令

7.2.1 指令码

ldm

stm

ld:Load

st:Store

m:mutil

7.2.2 指令格式

ldm  Rm, {寄存器列表}

        将Rm指向的连续的地址空间中的数据读到寄存器列表的每个寄存器中。

stm  Rm, {寄存器lieb}

        将寄存器列表中的每个寄存器中的数据写到Rm指向的连续的地址空间中 

注意:

1. Rm寄存器中的数据被当成一个地址看待

2. 寄存器列表中的寄存器如果是连续的,则使用"-" 隔开

3. 寄存器列表中的寄存器如果不连续则使用“ , ”隔开

4. 寄存器列表中的寄存器要求从小到大依次书写,如果从大到小书写,要求依次用逗号隔开书写,编译器会报警告。

 7.3 栈操作指令

7.3.1 栈的种类

1> 增栈:压栈之后,栈指针向高地址方向移动。

2> 减栈:压栈之后,栈指针向低地址方向移动。

3> 空栈:当前栈指针指向的空间没有有效的数据,因此可以先压栈,压栈之后栈指向的空间就为有效的数据,因此需要移动栈指针,让栈指针指向一个没有有效数据的空间。

4> 满栈:栈指针指向的空间有有效的数据,需要先移动栈指针,让栈指针指向一个空的位置,再进行压栈的操作,压栈之后,此时栈指针指向的空间又为有效的数据。

 7.3.2 栈的操作方式

满增栈:Full Ascending  ---> stmfa/ ldmfs

满减栈:Full Descending ---> stmfd/ ldmfd

空增栈:Empty Ascending---> stmea/ldmea

空减栈:Empty Descending-->stmed/ ldmed

ARM处理器默认采用的是满减栈,ARM指令集本身是支持以上4种栈的操作方式的所有的指令

7.2.3 指令码

ldmfd

stmfd

7.3.4 指令格式

ldmfd  sp!, {寄存器列表}

        将sp指向的栈空间中的数据读到寄存器列表的每个寄存器中。

stm  Rm, {寄存器列表}

        将寄存器列表中的每个寄存器中的数据写到sp指向的栈空间中 

注意:

0. !:压栈和出栈之后都需要更新栈指针

1. sp寄存器中的数据被当成一个栈空间的地址看待

2. 寄存器列表中的寄存器如果是连续的,则使用"-" 隔开

3. 寄存器列表中的寄存器如果不连续则使用“,”隔开

4. 寄存器列表中的寄存器要求从小到大依次书写,如果从大到小书写,要求依次用逗号隔开书写,编译器会报警告。

八、C和汇编的混合编程

 8.1  混合编程相关的概念

C和汇编的混合编程需要遵循ATPCS规范,ATPCS规范是ARM公司指定的。

ARPCS:主要有以下规范:

1> 处理器默认使用满减栈

2> 参数的传递通过R0-R3进行传递,如果参数的个数少于4个通过r0-r3传递,

​ 如果参数的个数超过4个,则超过的部分使用压栈的方式传递。

3> 函数的返回值通过R0进行返回。如果超过4个字节通过r0-r1返回。

8.2 内联汇编

 C代码中嵌套一段汇编代码,这种方式叫做内联汇编或者叫做内嵌汇编。

asm volatile(

指令列表

"汇编指令\n\t"
    .........
    :输出列表
    :输入列表
    :破坏列表
    );

你可能感兴趣的:(ARM体系结构及接口技术,arm,嵌入式硬件)