关键字:汇编指令基础


 

首先 ARM 是一家公司,它成立于 1990 年。ARM 公司主要是设计 ARM 系列的 RISC 处理器内核,并将这些内核授权给合作伙伴进行生产与销售。ARM 公司是一家只负责设计内核而不生产芯片的公司。

 

ARM 芯片的产品线主要分为三种:

1、应用级

应用于高端产品的芯片,如智能手机等。其芯片代号为 A 系列,如 Cortex-A8 , Cortex-A9。

2、实时嵌入式

应用于一些对性能要求稍低一些的设备。其芯片代号为 R 系列,如 Cortext-R4。

3、MCU、FPGA

主要应用于对成本要求较高但性能要求较弱的产品。其芯片代号为 Cortext-M 系列。

 

ARM 的体系架构主要有以下四种:

1、ARMv4

2、ARMv5

3、ARMv6

4、ARMv7

不同体系架构拥有不同的指令集。

 

一个典型的 ARM 设备至少需要包含三个模块:1、FLASH;2、内存;3、CPU。

 

FLASH 负责存储程序,内存负责临时管理程序的运行状态而CPU则负责做计算处理。

 

CPU 中又有三个很重要的组成:1、控制器;2、运算器;3、寄存器。

 

控制器负责去 FLASH 中准确读取程序内容。运算器负责执行程序代码,或者说负责执行计算需求,而寄存器则负责存储需要计算的数据。

 

什么是交叉编译工具链?

交叉编译工具链是嵌入式行业常听的词汇。嵌入式设备的芯片通常都是 ARM 架构的,而我们用于开发嵌入式程序的电脑却通常都是 X86 架构的。两种芯片架构不同,所编译出来的程序自然也不能通用。那为了能在电脑上编译出嵌入式设备能用的程序来,就必须要用到这个交叉编译工具链了。它的作用就类似于一个“翻译官”,将在电脑上写的代码翻译成能在 ARM 架构的芯片上运行的程序出来。

 

ARM 的工作模式

ARM 主要有七种工作模式:

1、User

非特权模式,或者称为普通模式。大部分的任务执行都在这一模式下完成。

2、FIQ

中断模式(Fast)。当一个高优先级的中断发生时,就会进入这个模式。

3、IRQ

中断模式(normal)。当一个低优先级的中断发生时,就会进入这个模式。

4、Supervisor

超级用户模式。当复位或软中断指令执行时会进入这种模式。

5、Abort

当需要存取异常信息时会进入这个模式。

6、Undef

当执行未定义指令时会进入这个模式。

7、System

几乎与 User 模式一样,只是其权限会比 User 稍高一些。

 

ARM 有 37 个寄存器,其中 1 个是 PC 寄存器,1个是CPSR(Currnet program status register)寄存器,5个是SPSR(Saved program status register)寄存器以及30个通用寄存器。

 

汇编指令基础

MOV指令

MOV指令就是 move 的缩写。它表示移动寄存器的值的意思。

 

mov r1,#13

mov r2,#0xff

以上两条指令表示将十进制数值 13 寄存到 r1 寄存器中以及将十六进制数值 0xff 寄存到 r2 寄存器中。

 

mov r1,r2,LSL#2

这条指令表示将寄存器 r2 中的值向左移动两位以后再寄存到 r1 寄存器中。LSL 即 left shift logic 的缩写。 假设 r2 寄存器中的值是1,那么指执行了这条指令以后 r1 中的值将会是 4。与之相对应的还有 LSR#2,表示向右移动2位。

 

MRS 指令

MOV指令只能操作普通的寄存器,如果要操作特殊寄存器,就得使用 MRS 指令。

 

mrs r1,cpsr

mrs cpsr,r0

以上两条指令分别表示将 CPSR 寄存器中的值读取到普通寄存器 r1 中以及将普通寄存器 r0 中的值写入到 CPSR 寄存器中。

 

逻辑指令

and

逻辑与,例如 and r0,r1 #0xff 相当于 r0 = r1 & 0xff。

 

orr

逻辑或,例如 orr r3,r0,#0xf 相当于 r3 = r0 | 0xf。

 

cmp

比较。 例如 cmp r1,r0,比较 r1 与 r0 是否相等。

 

bic

清除指令。例如 bic r0,r0,#0x03。清除 r0 寄存器中的第 0 号位与第 3 号位。

 

tst

测试。例如 tst r0,#0x20。用于测试第 6 位的值是否为0,为0则Z标志位置1。

 

算术指令

add

加法指令。例如:add r0,r1,r2 表示将 r1 与 r2 两寄存器的值相加后保存在 r0 寄存器中。

 

sub

减法指令。例如:sub r0,r1,#3 表示将 r1 寄存器中的值减 3 后保存在 r0 寄存器中。

 

mul

乘法指令。例如: mul r0, r1, r2 表示将 r1 与 r2 中的值相乘后保存到 r0 寄存器中。

 

跳转指令

b

普通跳转指令。常见的用法为: b label。表示将程序指针跳转到标签为 label 的代码处。即 C 语言中的 goto 语句。

 

bl

记忆跳转指令。常见用法为: bl func。这条指令会将下一条指令的地址保存到 LR 寄存器,然后将程序指针跳到 func 标签处执行。待 func 标签处的代码全部执行完毕后可以通过 mov pc,lr 指令将程序指针跳回到先前跳转func前的位置继续执行。这就是单片机中的中断的概念,执行完以后还要回来的那种。

 

beq

bne

 

 以下是一个实例,将一段普通 C 语言程序翻译成汇编语言:

void main()
{
    int ret = 0;
    func1(2);
    while(1);
}

int func1(int a)
{
    if(a == 2)
        return func2(a);
    else
        return func3(a);
}

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

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

 

.text

main:
    mov r5,#0
    mov r0,#2
    b1 func1
main_end:
    b main_end


func1:
    mov r12,lr
    cmp r0,#2
    bleq func2
    blne func3
func_end:
    mov pc,r12


func2:
    add r0,#3
    mov pc,lr
func2_end:


func3:
    sub r0,r0,#1
    mov pc,lr
func3_end:


.end

 

Load/Store 指令

load/store 架构规定,存储器之间不能直接拷贝,必须通过CPU中的寄存器作中转。

load指令的汇编语句为: ldr。它的作用是将指定地址中的数据加载进CPU的寄存器中。

例如:

ldr r0,=buf

ldr r1,[r0]

第一句的意思是将 'buf' 标签的地址存到 r0 寄存器中。其中 buf 标签是定义在 .data 区段的 .byte 数组。

第二句的意思就是将 r0 寄存器中的值作为地址,将这个地址中的内容加载到 r1 寄存器中。

 

ldr r1,[r0,#8]  相当于C语言的 r0 = *(r0 + 8)

 

store指令的汇编语句为:str。与 ldr 相反,将CPU寄存器中的值写到指定地址中的内存中去。

例如:

mov r0,#9
mov r1,=dest_buf
str r0,[r1]

第一句先准备好一个寄存器中的数据。

第二句确定好内存中的数据段地址。

第三句将 r0 寄存器中的数据输出到 dest_buf 内存段中去。

 

GNU汇编伪指令

GNU汇编伪指令是辅助汇编程序编程用的指令,它不参与实际代码逻辑的构建,仅作辅助标识之用。

.text  将定义符开始的代码编译到代码段

.end  文件结束

.data  将定义符开始的代码编译到数据段

.equ  定义宏

.byte  定义1个字节的变量,定义字节数组的指令为: .byte 0x11,'a',0

.word  定义word变量,长度为4个字节。

.string  定义字符串,如  .string "hello worl\0"

.global  声明全局符号,如 .global =start

 

批量操作指令

前面提到的 ldr 与 str 指令都是单寄存器操作的。如果想要一次性操作多个寄存器,则可以使用以下指令

ldmia 与 stmia。

例如:

ldmia r0!,{r7 - r10}。这条指令执行完毕后,会自动将 r7 ~ r10 寄存器中的数据加载到 r0 ~ r3 寄存器中。

stmia r0!,{r7 - r10}。与上一条指令相反。

 

堆栈操作指令

stmfd sp!,{r0 - r12,lr}  将寄存器 r0 ~ r12 lr中的值存入栈中,常用于中断保护现场,'!' 号表示会自动偏移。

ldmfd sp1,{r0-r12,pc}^  与上一条指令相反,常用于中断恢复现场。

 

软中断

swi

该指令可以产生一个软中断,用法为:swi 0x02,表示产生一个中断号为 2 的软中断。