无论是体系结构还是指令集,大家或多或少都应该对X86汇编有些了解,而对于嵌入式领域已被广泛采用的ARM 处理器,了解的可能并不多。如果你有兴趣从事嵌入式方面的开发,那么了解一些RISC 体系结构和ARM汇编的知识还是有必要的。这里,我们找出了这两种体系结构最明显的不同之处,并对此进行介绍,让大家对于RISC体系结构的汇编有一个基本的了解。首先,我们就来看一看基于RISC的ARM的体系结构。
基于RISC 的ARM CPU
ARM是一种RISC体系结构的处理器芯片。和传统的CISC体系结构不同,RISC 有以下的几个特点:
◆ 简洁的指令集——为了保证CPU可以在高时钟频率下单周期执行指令,RISC指令集只提供很有限的操作(例如add,sub,mul等),而复杂的操作都需要由这些简单的指令来组合进行模拟。并且,每一条指令不仅执行时间固定,其指令长度也是固定的,这样,在译码阶段就可以对下一条指令进行预取。
◆ Load-Store 结构——这个应该是RISC 设计中比较有特点的一部分。在RISC 中,CPU并不会对内存中的数据进行操作,所有的计算都要求在寄存器中完成。而寄存器和内存的通信则由单独的指令来完成。而在CSIC中,CPU是可以直接对内存进行操作的,这也是一个比较特别的地方。
◆ 更多的寄存器——和CISC 相比,基于RISC的处理器有更多的通用寄存器可以使用,且每个寄存器都可以进行数据存储或者寻址。
当然,作为RISC 领域最成功的处理器,ARM也遵从上面的特点。这里,我们不妨来看一看在user 模式下,ARM处理器的体系结构,这对于我们了解其汇编语言是有好处的。而其它模式下只是有一些寄存器分组略有不同,大家可以在ARM的手册上查到。这里要说明的是,尽管ARM处理器也支持16位指令,不过在下文中,我们都假定ARM处理器在32 位模式下工作。
图1:user模式下ARM处理器体系结构
从图1中我们看到,在user 模式下,ARM CPU 有16个数据寄存器,被命名为r0~r15(这个要比x86的多一些)。r13~r15有特殊用途,其中:
◆ r13 - 指向当前栈顶,相当于x86的esp,这个东西在汇编指令中要用sp 表示
◆ r14 - 称作链接寄存器,指向函数的返回地址。用lr表示,这和x86将返回地址保存在栈中是不同的
◆ r15 - 类似于x86的eip,其值等于当前正在执行的指令的地址+8(因为在取址和执行之间多了一个译码的阶段),这个用pc表示
另外,ARM处理器还有一个名为cspr的寄存器,用来监视和控制内部操作,这点和x86 的状态寄存器是类似的。具体的内容就用到再说了。
ARM 指令集
ARM处理器可以支持3种指令集——ARM,Thumb和Jazelle。
采用那种指令集,由cspr中的标志位来决定。大体说来:
◆ ARM——这是ARM自身的32 位指令集
◆ Thumb ——这是一个全16 位的指令集,在16 位外部数据总线宽度下,这个指令集的效率要比32 位的ARM指令高一些。
◆ Jazelle ——这是一个8位指令集,用来加速Java字节码的执行
整个ARM指令集由数据处理指令、分支指令、Load-Store指令、程序中断指令和一些系统控制指令构成,除了Load-Store指令外,其他部分和x86指令集是比较类似的。但和x86相比,ARM指令最显著的特点它们都是32-bit 定长的。另外,由于arm是基于RISC指令集的,所以CPU只处理在寄存器中的数据并通过独立的load-store指令在内存和寄存器之间进行数据的传递。
在使用方面,ARM指令的格式也要比Intel的复杂些。一般说来,一条ARM指令有如下的形式:
<Instruction> {S} [Rd], [Rn], [Rm]
其中:
* {S} —— 加上这个后缀的指令会更新cpsr 寄存器
* [Rd] —— 目的寄存器
* [Rn]/[Rm] —— 源寄存器
一般来说,arm 指令有3个操作数,其中Rm寄存器在执行指令前可以进入桶形移位器进行移位操作,而Rn则会直接进入ALU 单元。如果一条arm 指令只有2 个操作数,那么源寄存器按照Rm 来处理。例如,一条加法指令:
add r0, r1, #1
就会把r1+1的结果存放到r0中。
在熟悉了基本的汇编格式后,读者就可以自行去查询基本的ARM汇编指令了,下面,我们找出ARM中比较有特色部分——Load-Store指令结构,它是CPU 和内存进行通信的一个重要媒介。
Load-Store 指令体系
由于ARM CPU并不直接处理内存中的数据,这个指令体系就担起了在寄存器和内存之间交换数据的重要媒介。它要比x86 的内存访问机制复杂一些。该指令体系分成3 类:
◆ 单寄存器传输(这是与x86 最为相像的)
◆ 多寄存器传输
◆ 交换指令