ARM7TMDI是目前使用最广泛的32位嵌入式处理器,属低端ARM处理器核
TDMI的基本含义为
T:支持16位压缩指令集Thumb
D:支持片上Debug
M:内嵌硬件乘法器(Multiplier)
I:嵌入式ICE,支持片上断点和调试点
流水线支持:ARM7 - 3级, ARM9 - 5级, ARM10 - 6级, ARM11 - 8级。
ARM7TDMI处理器有两种操作状态:(两个状态之间的切换并不影响处理器模式或寄存器内容。)
ARM状态:32位,这种状态下执行的是字方式的ARM指令;
Thumb状态:16位,这种状态下执行半字方式的ARM指令。
处理器状态的切换:
;从Arm状态切换到Thumb状态 (地址最低位为1,表示切换到Thumb状态)
LDR R0,=Lable+1
BX R0
;从Thumb状态切换到ARM状态 (地址最低位为0,表示切换到ARM状态)
LDR R0,=Lable
BX R0
ARM处理器模式:
用户模式
特权模式:除下用户模式之外的其它6种都是特权模式。
异常模式:除下用户模式和系统模式之外的其它5种都是异常模式。
模式的切换:
切换到系统模式
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0x1f
msr cpsr_cxfs,r0
ARM各状态下的寄存器:
通用寄存器:在汇编语言中寄存器R0~R13为保存数据或地址值的通用寄存器。
未分组的寄存器:R0~R7,也就是说对于任何处理器模式,这些寄存器都对应于相同的32位物理寄存器。
分组寄存器:R8~R14,它们所对应的物理寄存器取决于当前的处理器模式。
寄存器R13常作为堆栈指针(SP)
R14为链接寄存器(LR):在结构上有两个特殊功能:
- 在每种模式下,模式自身的R14版本用于保存子程序返回地址;
- 当发生异常时,该异常模式特定的物理R14(例如R14_irq)被设置成该异常模式将要返回的地址(异常有一个小的固定偏移量)。
寄存器R15为程序计数器(PC):它指向正在取指的地址。即当前正在执行指令的地址加上8个字节(两条ARM指令的长度)。由于ARM指令总是以字为单位,所以R15寄存器的最低两位总是为0。
CPSR为程序状态寄存器:
程序状态保存寄存器(SPSR):种异常都有自己的SPSR,在进入异常时它保存CPSR的当前值,异常退出时可通过它恢复CPSR。
在Thumb状态中,高寄存器(R8~R15)不是标准寄存器集的一部分。汇编语言程序员对它们的访问受到限制。
异常的入口和出口处理,中断处理代码的开始部分和退出部分:
SUB LR,LR,#4 ;计算返回地址, 注意这里的常数值要根据具体异常来确定!
STMFD SP!,{R0-R3,LR} ;保存使用到的寄存器
. . .
LDMFD SP!,{R0-R3,PC}^ ;中断返回 ----> “^”符号表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时(PC是最后恢复的),CPSR也得到恢复。这里使用的堆栈指针SP(R13)是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。这个堆栈指针应必须在系统启动时初始化。
进入异常该做什么处理?(一下都是由内核处理)
1.在适当的LR中保存下一条指令的地址,当异常入口来自:
- ARM状态,那么ARM7TDMI将当前指令地址加4或加8复制(取决于异常的类型,因为当从异常返回时有些异常是返回当前指令继续执行,有些异常是返回到当前指令的下一条继续执行)到LR中;
- Thumb状态,那么ARM7TDMI将当前指令地址加2、4或加8 (取决于异常的类型)复制到LR中;异常处理器程序不必确定状态。
2.将CPSR复制到适当的SPSR中;
3. 将CPSR模式位强制设置为与异常类型相对应的值;
4.强制PC从相关的异常向量处取指。
下面是一个IRQ中断的例子的处理过程:
发生IRQ中断后的处理:
1.将CPSR寄存器的内容存入IRQ模式的SPSR寄存器;
2.置位I位,禁止IRQ中断;
3.清零T位,进入ARM状态;
4.设置MOD位,切换处理器模式至irq模式;
5.将下一条指令的地址(PC)存入IRQ模式的LR寄存器(R14_irq);
6.将跳转地址存入PC(中断向量表中已经定义的地址),实现跳转,进入IRQ中断处理程序。
IRQ中断退出的处理:
1.将SPSR寄存器的值复制会CPSR寄存器;
2.将LR寄存器的值减去一个常量后复制到PC寄存器,跳转到被中断的用户程序。
ARM指令中立即数产生方式:立即数由8位常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。
寄存器移位方式:移位的位数可以用立即数方式或者寄存器方式表示。
ASR:算术右移 使用要被移位的寄存器(Rx)的第 31 位的值来填充高位,用来保护补码表示中的符号
LSL:逻辑左移 最低有效位(LSB)用0来填充
LSR:逻辑右移 最高有效位(MSB)用0来填充
ROR:循环右移
RRX:扩展的循环右移 它向右移动一个位置 - 不同之处是,它使用处理器的进位标志来提供一个要被移位的 33 位的数量。
这里没有算术左移和循环左移,这是因为算术左移(ASL)可以用逻辑左移(LSL)来实现,而循环左移又可以用循环右移来实现。
寄存器的寻址方式:
1. 立即寻址: MOV R0,#0xFF00
2. 寄存器寻址:MOV R1,R2
3. 寄存器移位寻址: MOV R0,R2,LSL #3
4. 寄存器简介寻址: LDR R0, [R2] R2中的值是一个地址,将这个地址中的值赋给R0
5. 基址寻址:将基址寄存器中的内容与指令中给出的偏移量(<4K)相加减
LDR R2,[R3,#0x0C] ; 读取R3+0x0C地址上的存储单元的内容,放入R2(前索引基址寻址)。
STR R1,[R0,#-4]! ;"!"表示更新基址寄存器的内容(设置W位,表示回写)。 R1 = [R0 - 4], R0 = R0 - 4
LDR R0, [R1], #4 ;将地址为R1的内存单元数据读取到R0中(R0 = [R1]),然后R1 = R1 + 4 (后索引基址寻址)
LDR R0,[R1,R2] ; R0=[R1+R2] R1值不发生变化
6. 多寄存器寻址:
LDMIA R1!,{R2-R7,R12} ;将R1指向的单元中的数据读出到R2~R7、R12中(R1自动加4)
STMIA R0!,{R2-R7,R12} ;将寄存器R2~R7、R12的值存到R0指向的存储; 单元中(R0自动加4)
7. 堆栈寻址:堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针-SP)指向一块存储区域(堆栈),指针所指向的存储单元即是堆栈的栈顶。
递增堆栈:向上生长,向高地址方向生长
递减堆栈:向下生长,向低地址方向生长
满堆栈:堆栈指针指向最后压入的栈顶的有效数据项。
空堆栈:堆栈指针指向下一个带压入数据的空位置。
由上面的情况可以组和成四种类型的堆栈:
满递增:堆栈向上增长,堆栈指针指向内含有效数据项的最高地址。指令如LDMFA、STMFA等;
空递增:堆栈向上增长,堆栈指针指向堆栈上的第一个空位置。指令如LDMEA、STMEA等;
满递减:堆栈向下增长,堆栈指针指向内含有效数据项的最低地址。指令如LDMFD、STMFD等;
空递减:堆栈向下增长,堆栈指针向堆栈下的第一个空位置。指令如LDMED、STMED等。
8. 块拷贝寻址:多寄存器传送指令用于将一块数据从存储器的某一位置拷贝到另一位置。
STMIA R0!,{R1-R7} ;将R1~R7的数据保存到存储器中。存储指针R0在保存第一个值之后增加;增长方向为向上增长。
STMIB R0!,{R1-R7} ;将R1~R7的数据保存到存储器中。存储指针R0在保存第一个值之前增加;增长方向为向上增长。
STMDA:事后递减方式。
STMDB:事先递减方式。
9. 相对寻址:相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
LDR Rd,labe1
一个相对寻址的例子:
BL SUBR1 ;调用到SUBR1子程序
BEQ LOOP ;条件跳转到LOOP标号处
...
LOOP MOV R6,#1 有效地址由 loop 和 PC 决定, 下面的SUBR1也是一样的情况
...
SUBR1 ...
ARM指令集:
1. ARM指令集 - 条件码:所有的ARM指令都可以条件执行,而Thumb指令只有B(跳转)指令具有条件执行 功能。如果指令不标明条件代码,将默认为无条件(AL)执行。
2. ARM指令集 - 存储器访问指令:ARM处理器是典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。存储器访问指令分为单寄存器操作指令和多寄存器操作指令。
单寄存器加载和存储:
LDR: 加载字数据
LDRB:加载无符号字节数据
LDRT:以用户模式加载字数据
LDRBT:以用户模式加载无符号字节数据
LDRH:加载无符号半字数据
LDRSB:加载有符号字节数据
LDRSH:加载有符号半字数据
几点说明:
位 B 用于控制指令操作的数据类型。当 B = 1, 指令访问的是无符号的字节数据;当 B = 0, 指令访问的是字数据。
位 L 控制内存操作的方向, 当 L = 1,指令执行Load操作;当 L = 0, 指令执行Store操作。
位 S 用于控制半字访问时数据类型, 当 S = 1, 数据为带符号; 当 S = 0, 数据为无符号。
位 H 用于控制半字的访问, 当 H = 1, 访问的数据为半字。
位 W :W = 1, 表示回写。
位 P : 表示前后变址。
位 U :表示加/减。
后缀 T:T为可选后缀。若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是在用户模式下进行的。T在用户模式下无效,不能与前索引偏移一起使用T。
STR: 存储字数据
STRB:存储字节数据
STRT:以用户模式存储字数据
STRBT:以用户模式存储字节数据
STRH: 存储半字数据
多寄存器加载和存储:
LDM为加载多个寄存器;STM为存储多个寄存器。
多寄存器加载/存储指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
cond:指令执行的条件;
模式:控制地址的增长方式,一共有8种模式;
!:表示在操作结束后,将最后的地址写回Rn中;
reglist :表示寄存器列表,可以包含多个寄存器,它们使用“,”隔开,如{R1,R2,R6-R9},寄存器由小到大排列;
^(对应于位 S ):可选后缀。允许在用户模式或系统模式下使用。它有以下两个功能:
1)若op是LDM且寄存器列表包含R15时,那么除了正常的多寄存器传送外,还将SPSR也复制到CPSR中。这用于异常处理返回,仅在异常模式下使用。
2)数据传入或传出的是用户模式下的寄存器,而不是当前模式的寄存器。
使用堆栈指令进行堆栈操作比使用数据块传送指令进行堆栈操作更好。
STMED R13!,{R5-R6} 对应 LDMED R13!,{R5-R6}
STMDA R0!,{R5-R6} 对应 LDMIB R0!,{R5-R6}
3. ARM指令集 - 寄存器和存储器交换指令:指令格式:SWP{cond}{B} Rd,Rm,[Rn] 其中,B为可选后缀,若有B,则交换字节,否则交换32位字;SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中。 Rm:源寄存器, Rd:目标寄存器, Rn:基址寄存器。
SWP R1,R1,[R0] ;将R1的内容与R0指向的存储单元的内容进行互换
SWPB R1,R2,[R0] ;将R0指向的存储单元低字节数据读取到R1中(高24位清零),并将R2的内容写入到该内存单元中(最低字节有效)
3. ARM指令集 - 数据处理指令:分为三类:数据传送指令、算术逻辑运算指令、比较指令。有ARM数据处理指令均可选择使用S后缀,以使指令影响状态标志。
>>数据传送指令:
MOV Rd,operand2 数据传送 Rd←operand2
MVN Rd,operand2 数据非传送 Rd←(~operand2) //这里的“非”表示取反。
注:当使用后缀S时,这些指令根据结果更新标志N和Z,在计算Operand2时更新标志C,不影响标志V。
>>算术逻辑运算指令:
算术运算:
ADD Rd, Rn, operand2 加法运算指令 Rd←Rn+operand2
SUB Rd, Rn, operand2 减法运算指令 Rd←Rn-operand2
RSB Rd, Rn, operand2 逆向减法指令 Rd←operand2-Rn
ADC Rd, Rn, operand2 带进位加法 Rd←Rn+operand2+Carry
SBC Rd, Rn, operand2 带进位减法指令 Rd←Rn-operand2-(NOT)Carry
RSC Rd, Rn, operand2 带进位逆向减法指令 Rd←operand2-Rn-(NOT)Carry
注:这些指令影响N,Z,C和V标志位, 不过要在后面加上后缀 S 才能改变CPSR中的相应位。
逻辑运算:
AND Rd, Rn, operand2 逻辑与操作指令 Rd←Rn & operand2
ORR Rd, Rn, operand2 逻辑或操作指令 Rd←Rn | operand2
EOR Rd, Rn, operand2 逻辑异或操作指令 Rd←Rn ^ operand2
BIC Rd, Rn, operand2 位清除指令 Rd←Rn & (~operand2)
>>比较指令:
CMP Rn, operand2 比较指令 标志N、Z、C、V←Rn-operand2
CMN Rn, operand2 负数比较指令 标志N、Z、C、V←Rn+operand2
TST Rn, operand2 位测试指令 标志N、Z、C、V←Rn & operand2
TEQ Rn, operand2 相等测试指令 标志N、Z、C、V←Rn ^ operand2
注:这些指令影响N,Z,C和V标志位,都不保存运算结果。
CMN R0,#1指令则表示R0与-1比较,若R0为-1(即1的补码),则Z置位;否则Z复位。
TST R0,#0x01 ; 判断R0的最低位是否为0
TST R1,#0x0F ; 判断R1的低4位是否为0 TST指令通常与EQ、NE条件码配合使用,当所有测试位均为0时,EQ有效(Z=1),而只要有一个测试位不为0,则NE有效(Z=0) 。
TEQ R0,R1 ; 比较R0与R1是否相等 (不影响V位和C位)。
4. ARM指令集 - 乘法指令:
MUL Rd,Rm,Rs 32位乘法指令 Rd←Rm*Rs (Rd≠Rm)
MLA Rd,Rm,Rs,Rn 32位乘加指令 Rd←Rm*Rs+Rn (Rd≠Rm)
UMULL RdLo,RdHi,Rm,Rs 64位无符号乘法指令 (RdLo,RdHi) ←Rm*Rs
UMLAL RdLo,RdHi,Rm,Rs 64位无符号乘加指令 (RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi)
SMULL RdLo,RdHi,Rm,Rs 64位有符号乘法指令 (RdLo,RdHi) ←Rm*Rs
SMLAL RdLo,RdHi,Rm,Rs 64位有符号乘加指令 (RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi)
注意:RdLo/Hi、Rm、Rs不能为R15。
UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,而高32位保存到RdHi中。 例子:UMULL R0,R1,R5,R8 ; (R1、R0)=R5×R8
UMLAL指令将Rm和Rs中的值作无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中。例子:UMLAL R0,R1,R5,R8 ;(R1、R0)=R5×R8+(R1、R0)
5. ARM指令集 - 分支指令:
B label 分支指令 PC←label
BL label 带链接的分支指令 LR←PC-4,PC←label
BX Rm 带状态切换的分支指令 PC←Rm,切换处理器状态
BLX 带返回和状态切换的跳转指令
上面四种跳转指令的跳转范围为 向前或向后 32 MB的地址空间。 跳转指令的目标地址为一个24位有符号的立即数。
跳转指令的目标地址怎么计算???
1.取出24位有符号的偏移地址(指令中的 0 ~ 23 位就是偏移地址);
2.将偏移地址左移两位(因为ARM指令是字对齐的,最后两位总是0b00,所以左移两位)
3.将移位后的偏移地址和PC(PC在这里是作为基址地址)相加,这里得到的地址即为目标处的地址值(目标地址进入取址,流水线前面2级(ARM7流水线为3级)清空)。
6. ARM指令集 - 杂项指令:ARM指令集中有三条指令作为杂项指令,在实际应用中这三条指令非常重要。
SWI immed_24 软中断指令 产生软中断,处理器进入管理模式
MRS Rd,psr 读状态寄存器指令 Rd←psr,psr为CPSR或SPSR
MSR psr_fields,Rd/#immed_8r 写状态寄存器指令 psr_fields←Rd/#immed_8r,psr为CPSR或SPSR
>>软中断指令 SWI :
SWI指令用于产生SWI异常,使得CPU模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。不影响条件码标志。该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。
根据SWI指令传递的参数SWI异常处理程序可以作出相应的处理。SWI指令传递参数有以下两种方法,
<1> 指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递。
MOV R0,#34 ;设置子功能号为34
SWI 12 ;调用12号软中断
<2> 指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其它的通用寄存器传递。
MOV R0,#12 ;调用12号软中断
MOV R1,#34 ;设置子功能号为34
SWI 0
>>状态寄存器读指令 MRS:在ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。
MRS R1,CPSR ; 将CPSR状态寄存器读取,保存到R1中
MRS R2,SPSR ; 将SPSR状态寄存器读取,保存到R2中
>>状态寄存器写指令 MSR:在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。
7. ARM指令集 - 伪指令:ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令一样使用,但在编译时这些指令将被等效的一条或多条ARM指令所代替。ARM伪指令有四条,分别为ADR伪指令、ADRL伪指令、LDR伪指令、NOP伪指令。
>>小范围的地址读取 ADR:ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。当地址值是字节对齐时,其取指范围为-255~255;当地址值是字对齐时,其取指范围为-1020~1020。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。
>>中等范围的地址读取 ADRL:ADRL伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比ADR伪指令可以读取更大范围的地址 。在汇编编译器编译源程序时,ADRL伪指令被编译器替换成两条合适的指令。当地址值是字节对齐时,其取指范围为-64K~64K;当地址值是字对齐时,其取指范围为-256K~256K。
>>大范围地址的读取 LDR:LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
注意:1.从指令位置到文字池的偏移量必须小于4KB;
2.与ARM指令的LDR相比,伪指令的LDR的参数有“=”号。
加载常量:
LDR R2, =0xFF0 ;MOV R2, #0xFF0
LDR R0, =0xFF000000 ;MOV R0, #0xFF000000
LDR R1, =0xFFFFFFFE ;MVN R1, #0x1
加载地址:
LDR R1,=InitStack
...
InitStack
MOV R0, LR
...
>>空操作指令 NOP:NOP伪指令在汇编时将会被代替成ARM中的空操作,比如可能是“MOV R0,R0”指令等。NOP可用于延时操作。