1、LDR/STR
LDR指令用于从内存中读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下:
LDR{cond}{T} Rd,<地址>; 加载指定地址上的数据(字),放入Rd中 STR{cond}{T} Rd,<地址>; 存储数据(字)到指定地址的存储单元,要存储的数据在Rd中
LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式:
(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R0,=0X123 ; 将0X123存入r0中 LDR R0,=label ; 将label_1所指向的地址值存入r0中 LDR R1,[R0] ; 将 R0 地址处的数据读出,保存到R1中 (R0 的值不变) LDR R1,[R0,#0x12] ; 将 R0+0x12 地址处的数据读出,保存到R1中 (R0 的值不变) LDR R1,[R0,#-0x12]; 将 R0-0x12 地址处的数据读出,保存到R1中 (R0 的值不变)
(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:
LDR R1,[R0,R2] ; 将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变) LDR R1,[R0,-R2] ; 将R0-R2 地址的数据计读出,保存到R1中(R0 的值不变)
(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变) LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)
一组代码示例:
NumCount EQU 0x40003000 ;定义变量NumCount … LDR R0,=NumCount ;使用LDR 伪指令装载NumCount的地址到R0 LDR R1,[R0] ;取出变量值 ADD R1,R1,#1 ;NumCount=NumCount+1 STR R1,[R0] ;保存变量值 … GPIO 设置 GPIO-BASE EQU 0Xe0028000 ;定义GPIO 寄存器的基地址 … LDR R0,=GPIO-BASE LDR R1,=0x00FFFF00 ;装载32 位立即数,即设置值 STR R1,[R0,#0x0C] ;IODIR=0x00FFFF00, IODIR 的地址为0xE002800C MOV R1,#0x00F00000 STR R1,[R0,#0x04] ;IOSET=0x00F00000,IOSET 的地址为0xE0028004
2、LDM/STM
批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM 为加载多个寄存器,STM 为存储多个寄存器。允许一条指令传送16 个寄存器的任何子集或所有寄存器。指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^} STM{cond}<模式> Rn{!},reglist{^}
LDM /STM 的主要用途是现场保护、数据复制、参数传送等。其模式有8种,如下所列:(前面4 种用于数据块的传输,后面4 种是堆栈操作)。
(1) IA:每次传送后地址加4 (2) IB:每次传送前地址加4 (3) DA:每次传送后地址减4 (4) DB:每次传送前地址减4 (5) FD:满递减堆栈 (6) ED:空递增堆栈 (7) FA:满递增堆栈 (8) EA:空递增堆栈
其中,寄存器Rn 为基址寄存器,装有传送数据的初始地址,Rn 不允许为R15;后缀“!”表示最后的地址写回到Rn中;寄存器列表reglist 可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”后缀不允许在用户模式呈系统模式下使用,若在LDM 指令用寄存器列表中包含有PC 时使用,那么除了正常的多寄存器传送外,将SPSR 拷贝到CPSR 中,这可用于异常处理返回;使用“^”后缀进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。 地址对准――这些指令忽略地址的位[1:0]。 批量加载/存储指令举例如下:
LDMIA R0!,{R3-R9} ;加载R0 指向的地址上的多字数据,保存到R3~R9中,R0 值更新 STMIA R1!,{R3-R9} ;将R3~R9 的数据存储到R1 指向的地址上,R1值更新 STMFD SP!,{R0-R7,LR} ;现场保存,将R0~R7、LR入栈 LDMFD SP!,{R0-R7,PC}^;恢复现场,异常处理返回
在进行数据复制时,先设置好源数据指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB 进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用SP 然后使用堆栈寻址指令STMFD/LDMFD、STMED。LDMED、STMFA/LDMFA、STMEA/LDMEA实现堆栈操作。 使用LDM/STM 进行数据复制例程如下:
… LDR R0,=SrcData ;设置源数据地址 LDR R1,=DstData ;设置目标地址 LDMIA R0,{R2-R9} ;加载8 字数据到寄存器R2~R9 STMIA R1,{R2-R9} ;存储寄存器R2~R9 到目标地址 使用LDM/STM 进行现场寄存器保护,常在子程序中或异常处理使用: SENDBYTE STMFD SP!,{R0-R7,LR} ;寄存器入堆 … BL DELAY ;调用DELAY 子程序 … LDMFD SP!,{R0-R7,PC} ;恢复寄存器,并返回
值得注意的是一些诸如原子操作的指令:STREX/LDREX;
3、MRS/MSR
特殊寄存器 CPSR 通过 MRS 和 MSR 指令进行读写操作:
MRS:读状态寄存器指令。在ARM 处理器中,只有 MRS 指令可以状态寄存器CPSR或SPSR读出到通用寄存器中。
MRS{cond} Rd ,psr
Rd 目标寄存器。Rd 不允许为R15
举例:
MRS R1,CPSR ;将CPSR状态寄存器读取,保存到R1 中 MRS R2,SPSR ;将SPSR状态寄存器读取,保存到R2 中
MRS 指令读取CPSR,可用来判断ALU 的状态标志,或IRQ、FIQ中断是否允许等;在异常处理程序中,读SPSR 可知道进行异常前的处理器状态等。MRS 与MSR 配合使用,实现CPSR 或SPSR 寄存器的读—修改---写操作,可用来进行处理器模式切换(),允许/禁止IRQ/FIQ中断等设置。另外,进程切换或允许异常中断嵌套时,也需要使用MRS 指令读取SPSR 状态值。保存起来
举例:
使能IRQ 中断例程: ENABLE_IRQ MRS R0,CPSR BIC R0,R0,#0x80 MSR CPSR_c,R0 MOV PC,LR 禁能IRQ 中断例程: DISABLE_IRQ MRS R0,CPSR ORR R0,R0,#0x80 MSR CPSR_c,R0 MOV PC,LR
MSR:写状态寄存器指令。在ARM 处理器中。只有MSR 指令可以直接设置状态寄存器CPSR或SPSR。指令格式如下:
MSR{cond} psr_fields,#immed_8r MSR{cond} psr_fields,Rm
psr CPSR 或 SPSR,
fields 指定传送的区域。Fields 可以是以下的一种或多种(字母必须为小写):
c 控制域屏蔽字节(psr[7…0]) x 扩展域屏蔽字节(psr[15…8]) s 状态域屏蔽字节(psr[23。…16]) f 标志域屏蔽字节(psr[31…24]) immed_8r 要传送到状态寄存器指定域的立即数,8 位。 Rm 要传送到状态寄存器指定域的数据的源寄存器。
MSR 指令举例如下:
MSR CPSR_c,#0xD3 ;CPSR[7…0]=0xD3,即切换到管理模式。 MSR CPSR_cxsf,R3 ;CPSR=R3
注意:只有在特权模式下才能修改状态寄存器!
程序中不能通过MSR 指令直接修改CPSR 中的 T 控制位来实现ARM 状态/Thumb状态的切换,必须使用 BX 指令完成处理器状态的切换(因为BX 指令属转移指令,它会打断流水线状态,实现处理器状态切换)。MRS 与MSR 配合使用,实现CPSR或SPSR 寄存器的读-修改-写操作,可用来进行处理器模式切换、允许/禁止IRQ/FIQ 中断等设置。 堆栈指令实始化例程: INITSTACK MOV R0,LR ;保存返回地址 ;设置管理模式堆栈 MSR CPSR_c,#0xD3 LDR SP,StackSvc ;设置中断模式堆栈 MSR CPSR_c,#0xD2 LDR SP,StackIrq