一个汇编文件中包含以下部分:

1, 汇编指令, 被编译成一条一条的汇编指令存放在内存中,

2, 伪指令,在编译的时候会被编译器替换成多条可以识别的arm汇编指令

3, 伪操作,引导编译器进行相关的编译工作,不会被编译成汇编指令,也不占内存空间

4, 标号,起标识某一个地址的作用,不占内存空间

 

 

汇编指令:

1. 数据处理指令    --  对数据进行算术运算或者逻辑运算或者其他处理方法

2. 跳转指令  --  可以实现程序的跳转

3. load/store架构指令  --  对处理器的内存进行操作的指令

4. PSR数据传送指令     --  用来操作程序状态寄存器

5. 协处理器指令        --  用来操作协处理器

6. 软中断指令          --  用来触发一个软中断异常

 


【1】数据处理指令

语法:

<指令>{<cond>}{S} Rd, Rn, Operand2

<指令>:指令的名字

{<cond>}:条件码  如果本指令加了条件码,必须条件满足这条指令才会执行, 不加条件码表示无条件执行

{S}:状态标记,只有当前指令加了S,那么运算结果才会影响CPSR寄存器的条件位

 Rd:目的寄存器(比较指令没有目的寄存器)

Rn:第一操作寄存器(数据搬移指令没有第一操作寄存器)

Operand2:第二操作数(立即数,前面加#标识,16进制或者10进制),如果是寄存器的话,称之为第二操作寄存器

 

 

.text                      @指定一个代码段

.global _start             @把_start标号定义成全局的

_start:                        @定义一个标号_start, 是汇编程序的默认入口

 

mov r0, #0xff              @汇编指令,将0xff这个数据搬移到r0寄存器中

stop:                          @标号

b stop                     @跳转指令,相当于while(1), 防止程序跑飞

.end                       @伪操作,表示汇编程序的结束

 

【1】数据搬移: MOV  MVN

mov r0, #0xff      @r0 = 0xff

mvn r1, #0xff      @r1 = ~0xff

mov r2, r1         @r2 = r1

 

【2】算术指令: ADD ADC SUB SBC RSB RSC

mov r0, #4

add r1, r0, #1         @r1 = r0 + 1   

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

add r2, #1             @r2 = r2 + 1

@add r3, r0, #0xffffffff    不会改变C位

adds r3, r0, #0xffffffff    @C = 1   N = 0  Z = 0

adc r4, r0, #1              @带进位的加法指令 r4 = r0 + 1 + C

@两个64位的数据进行加法运算,第一个64位数据的高32位放在r0中,低32位放在r1中,另一个数的高32位放在r2中,低32位放在r3中

mov r0, #0x1

mov r1, #0xffffffff

mov r2, #0x4

mov r3, #0x5

adds r4, r1, r3        @产生进位  C = 1

adc r5, r0, r2         @r5 = r0 + r2 + C

 

 

mov r0, #4

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

sub r2, r0, r1           @r2 = r0 - r1

sub r0, #1               @r0 = r0 - 1

subs r4, r0, #5          @N = 1  C = 0  Z = 0

sbc r0, #1               @带借位的减法,r0 = r0 - 1 - !C

 

 

32位乘法

MUL{<cond>}{S}    R0, R1, R2                    R0 = R1 * R2

MLA{<cond>}{S}    R0,R1,R2,R3                            R0 = (R1 * R2) + R3    乘加指令

 

mul r4, r0, r1       @r4 = r0 * r1

@mul r4, r0, #4      报错,乘法指令不能使用操作数

mla  r5, r0, r1, r3  @r5 = r0 * r1 + r3

 

【2】比较指令  cmp

mov r0, #4

mov r1, #5

cmp r0, #4      @cmp指令本质进行的是减法指令,没有目的寄存器   r0 - 4 = 0  Z = 1  C = 1

 

注意:所有的指令只有比较指令不加S,运算结果会影响CPSR寄存器,其他指令必须加S才会影响

 

 

【3】逻辑指令: AND  ORR EOR BIC

mov r0, #0xff

and r1, r0, #0xfffffff0       @r1 = r0 & 0xfffffff0

orr r2, r0, #0xf00            @r2 = r0 | 0xf00

eor r3, r0, #0xf              @r3 = r0 ^ 0xf (取反)

bic r4, r0, #0xf              @位清零指令   哪些位为1就将哪些位清零   r4 = r0 & (~(0xf))

bic r5, r0, #0x50             @第4位和第6位清零

 

 

【4】跳转指令(分支指令)

Branch : B{<cond>} label

Branch with Link : BL{<cond>} subroutine_label     //带返回的跳转,本质会保存返回地址

 

跳转范围:± 32 Mbyte

如何执行长跳转?

          ldr pc, =lable

 

需求:将r0和r1中的数求和  然后求差, 最后相加

_start:

 

mov r0, #5

mov r1, #3

bl ad

bl sb

add r4, r2, r3

stop:

b stop

 

ad:

add r2, r1, r0

mov pc, lr

sb:

sub r3, r0, r1

mov pc, lr

 

【5】条件码

EQ: 相等     Z = 1

NE: 不相等   Z = 0

GT: 大于     N = 0 且 Z = 0

GE: 大于等于 N = 0

 

LT: 小于     N = 1

LE: 小于等于 N = 1 或 Z = 1

 

 

mov r0, #4

mov r1, #3

cmp r0, r1

bleq stop

addgt r3, r0, r1

 

 

指令练习:

if (a==0) func(1);

 

cmp r0, #0

mov r0, #1    //传递参数

bleq func

 

 

        if (a==0) x=0;

if (a>0)  x=1;

cmp r0, #0

moveq r1, #0

movgt r1, #1

 

 

if (a==4 || a==10) x=0;

 

cmp r0, #4

cmpne r0, #10

moveq r1, #0

 

 

【6】桶型移位器

LSL:  逻辑左移   高位丢弃,低位补0

LSR:  逻辑右移   低位丢弃,高位补0

ASR:  算术右移   低位丢弃,高位补符号位   如果移动多个位,高位全部补符号位

ROR:  循环右移   低位补高位

 

 

mov r0, #1

mov r1, r0, lsl #1       @r1 = r0 << 1

add r1, r0, lsl #1       @r1 = r1 + (r0 << 1)

mov r0, #0xf

mov r1, r0, ror #4

mov r0, #0xf0000000

mov r1, r0, asr #4

 

【7】立即数和有效数

立即数:如果一个数可以通过一个8位的数(0-255)ROR偶数位得到,那么这个数就是一个立即数

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

1. 展开成二进制,如果1的个数大于8,肯定不是一个立即数

 

0x3fc00000     是一个立即数

0011 1111 1100  ...

 

0x1fe00000     不是一个立即数

0001 1111 1110 0000 ...

 

0x3e800000     是

0011 1110 1000 ....

0x30100000     不是

0011 0000 0001 0000 ....

 

有效数:

如果一个数是立即数,那么这个数也是有效数

如果一个数安位取反之后得到的数是立即数,那么这个数就是有效数

 

 

ldr r0, =0x12345678    //ldr伪指令,将任意数据搬移到一个通用寄存器中

 

 

GCD练习:

mov r0, #9

mov r1, #15

loop:

cmp r0, r1

beq stop

subgt r0, r1

sublt r1, r0

b loop

 

load/store架构指令

1. 单寄存器指令

2. 多寄存器指令

3. 数据交换指令

 

 

1. 单寄存器指令

语法:

  LDR{<cond>}{<size>} Rd, <address>        //内存 ==> 寄存器

STR{<cond>}{<size>} Rd, <address>        //寄存器 ==> 内存

{<size>}: B  H  不写(W)

Rd:目的寄存器

<address>:地址有三种形式

1) 标号

ldr r0, lable

2)寄存器间接寻址

str r0, [r1]

3)基址变址寻址

   a. 前索引

str r0, [r1, #4]

   b. 自动索引

str r0, [r1, #4]!

   c. 后索引

str r0, [r1], #4

 

 

 

 

 

=============     标号    =============================

ldr r1, label    @将label这个标号下面的数据读取到r1寄存器中

ldrb r2, label

stop:

b stop

 

label:

.word 0x12345678

 

=================  寄存器间接寻址

mov r0, #0x41000000      @0x40000000,0x42000000

mov r1, #0xff

str r1, [r0]           @将r1里面的数据存储到r0指向的内存地址   *r0 = r1

 

=======================  基址变址寻址

======  前索引

mov r0, #0x41000000        @0x40000000,0x42000000

mov r1, #0xff

str r1, [r0, #4]           @将r1里面的数据存储到r0+4指向的内存地址   *(r0+4) = r1

=========  自动索引

mov r0, #0x41000000        @0x40000000,0x42000000

mov r1, #0xff

str r1, [r0, #4]!           @将r1里面的数据存储到r0+4指向的内存地址,然后r0自动加4   *(r0+4) = r1  r0 = r0+4

=========  后索引

mov r0, #0x41000000        @0x40000000,0x42000000

mov r1, #0xff

str r1, [r0], #4          @将r1里面的数据存储到r0指向的内存地址,然后r0自动加4   *r0 = r1  r0 = r0+4

 

 

 

数组求和:

ldr r0, =myarray

mov r4, #0

loop:

ldr r1, [r0], #4

cmp r1, #0

beq stop

add r4, r1

b loop

 

 

用汇编实现如下功能:

main()
{
int i = 0;
char src_buf[] = {1, 2, 3};
char dest_buf[8];
 
for(i = 0; i < 3; i++)
{
dest_buf[i] = src_buf[i];
}
}

 

 

.

text
.global _start
_start:
ldr r0, =src_buf
ldr r1, =dest_buf
mov r4, #0
loop:
cmp r4, #3
beq stop
add r4, #1
ldrb r2, [r0], #1
strb r2, [r1], #1
b loop
 
stop:
b stop
 
src_buf:
.byte 1, 2, 3
dest_buf:
.space 8

.end