Thumb状态切换#
Thumb是16位指令的状态,可以使用16位指令,在进行切换时不会改变通用寄存器。
;现在是ARM指令集MOV R0,#1 ;将1放进r0寄存器BX R0 ;跳转的最低位是1,ARM指令集下地址最低位都是0,也就是从ARM指令集跳到Thumb指令集
;现在是Thumb指令集MOV R0,#0 ;将0放进r0寄存器BX R0 ;跳转的最低位是0,Thumb指令集下地址最低位都是1,也就是从Thumb指令集跳到ARM指令集16位Thumb地址一般位于奇数地址,所以跳转到1就进入了Thumb状态。
ARM工作模式切换#
- 用户模式(Usr) 最普通的模式,资源和指令都受限
接下来的都是特权模式,可以对CPSR寄存器所有的位直接进行读写,而用户模式只能进行间接访问,如使用MRS指令将CPSR寄存器读入另一个寄存器。
-
系统模式(Sys) 特权模式,和用户模式共用一套寄存器但是权限更高
-
一般中断模式(IRQ) 通用的中断模式。IRQ异常响应时进入该模式
-
快速中断模式(FIQ) 用来处理优先级更高的中断 。FIQ异常响应时进入该模式
-
管理模式(SVC) CPU上电后默认的模式,用来做系统的初始化。系统复位或开机、软中断时进入到该模式
-
终止模式(ABT) 用户程序访问非法地址或者没有权限的地址时会进入。
-
未定义模式(UND) CPU在译码阶段不能够识别指令时会进入未定义模式
除了系统模式之外的5种特权模式,都是异常模式,异常模式只有在对应的异常发生时才会进入对应的模式。
在发生异常或者中断时,会有一个异常向量表或者中断向量表
+++
ARM寄存器#
ARM一共有37个用户可见寄存器。
通用寄存器#
- R0-R15 全模式下可用,其中R13是SP堆栈指针,R14是LR链接寄存器,R15是PC寄存器。
- R13_SVC、R14_SVC 管理模式专用寄存器
- R13_ABT、R14_ABT 终止模式专用寄存器
- R13_UND、R14_UND 未定义模式专用寄存器
- R13_IRQ、 R14_IRQ 中断模式专用寄存器
- R8_FIQ~R14_FIQ 快速中断模式专用寄存器,为了减少保护寄存器使用的时间而进行快速中断,直接多几个专用寄存器
状态寄存器#
- CPSR当前程序状态寄存器,具有四位条件标志位,两位中断屏蔽(包含快速中断),一位当前工作状态。
最后五位是处理器工作模式。可以通过修改这五位进行工作模式的改变。
- CPSR的条件标志位:
**N(Negative)**是否为负数,N=1就代表结果为负数。
Z(Zero) Z=1表示运算的结果为零。
C(Carry) 加法指令中结果产生了进位,则C=1,反之则为0;减法指令中发生了借位则C=0,没有借位则为1;移位操作中,C是最后被移出的位。
V(Overflow) 在有符号数的加减法指令中,发生了符号位溢出,则V=1
- SPSR 分别对应五种异常模式,五种异常模式发生时可以用来记录CPSR的值,然后跳转到对应的异常模式后再使用CPSR。
在通用寄存器中R0-R12是完全的通用寄存器,不会被用来做特殊用途,所有的针对普通寄存器的指令都可以用于他们。
特殊寄存器的操作#
- R13(SP)
往后看,有堆栈寻址的详细解释。堆栈往往用来实现函数跳转等操作。
- R14(LR)
执行BL、BLX指令时,会将PC-4(也就是要执行的下一条指令)的值自动记录到LR中,以便函数返回时将LR的值送回PC。但是在其他跳转时并不会自动记录,而是需要手动记录PC-4并存储。
一般可以这样用,也就是进行多重嵌套时,将LR压栈才能进入下一层嵌套,回到上一层嵌套就将寄存器出栈。
STMFD SP!, {<registers>, LR} LDMFD SP!, {<registers>, PC}在处理异常时会将异常处理的返回地址放在对应模式下的LR寄存器中,但是这个返回地址应该是发生异常代码的下一条代码也就是PC+4,而在返回时应该去执行发生异常时正在执行的那条代码,因为那条代码并没有执行完,也就需要LR减去一个偏移量。在发生异常时,所有的跳转地址都是自动配置好的,不需要我们程序员来关心。
- R15(PC)
arm采用了三级流水线的结构,每一条指令的执行都会经过取指、译码、执行三个阶段,所以正在执行的代码往后两条指令是正在取指的代码,也就是说PC的值是当前正在执行的代码地址+8。
+++
ARM存储器体系#
ARM的存储器支持大端模式和小端模式,默认是小端模式,也就是数据的低位存储在低地址内存单元中。
ARM处理器的ROM一般是FLASH,所有程序都存储在FLASH中,分为片内和片外两种。
ARM外设有统一的存储器映射编址,也就是访问对应的内存单元就可以访问特定外设的寄存器。这些内存地址不会被缓存。
STM32存储架构详解
+++
指令#
ARM汇编指令遵循着相对固定的格式,如**< opcode > {< cond >} {S}< Rd >,< Rn > {,< OP2 >}**。
在其中
- opcode就是指令的操作码的助记符,例如ADD、MOV。
- cond是可选的条件执行,可以是EQ、NE等,满足条件则执行这条指令,条件就是一个CPSR的标志位。
- S也是一个可选的指令,就是S,表示是否刷新CPSR中的条件标志位。如果不使用就仍是原来的值。
- Rd、Rn分别代表第一个操作数和第二个操作数。
- OP2表示可选的第三操作数。
条件执行的条件码和对应的条件
- EQ Z=1 相等
- NE Z=0 不相等
- CS/HS C=1 无符号数大于或者等于
- CC/LO C=0 无符号数小于
- MI N=1 负数
- PL N=0 正数或者零
- VS V=1 溢出
- VC V=0 未溢出
- HI C=1且Z=0 无符号数大于
- LS C=0或Z=1 无符号数小于或等于
- GE N=V 带符号数大于或等于
- LT N!=V 带符号数小于
- GT Z=0且N=V 带符号数大于
- LE Z=1或N!=V 带符号数小于或等于
- AL 无条件执行
- NV (ARMV3之前)从不执行
寻址#
- 立即数寻址 arm汇编一般在立即数前面加上#,x86汇编不需要加
- 寄存器寻址 寄存器中的值直接作为操作数
MOV r1, r2 ;将r2的值传到r1ADD r1, r2, r3 ;将r2的值加上r3的值并赋给r1MOV r2, r1, LSL #3 ;将r1<<3位并赋给r1ADD r3, r2, r1, LSL #3 ;将r1<<3 + r2赋给r3ADD r3, r2, r1, LSL r0 ;将r1<<r0 + r2赋给r3- 寄存器间址 寄存器中的值作为内存地址,使用内存地址上的数据作为操作数。
LDR r1, [r2] ;以r2中的内存地址加载数据进r1STR r1, [r2] ;将r1中的值写入r2寄存器中的内存地址- 基址寻址 使用寄存器中的地址生成新地址并且寻址,一般用于查表和数组访问。
LDR R1, [R0, #2] ;(前变址,不修改基址)将R0中的值加2作为新地址LDR R1, [R0, #2]! ;(自变址,修改基址)从r0 + 2的位置加载完数据后,R0寄存器的值会增加2。LDR R1, [R0, R2] ;(前变址,不修改基址)将R0+R2作为新地址LDR R1, [R0, R2, LSL #2] ;(前变址,不修改基址)将R0+R2<<2作为新地址LDR R1, [R0], #2 ;(后变址,修改基址)从r0的位置加载完数据后,R0寄存器的值会增加2。与上述第二条不同。STR R1, [R0, #-2] ;(前变址,不修改基址)将R0中的值减2作为新地址。STR R1, [R0], #-2 ;(后变址,修改基址)将R0中的值作为地址,将R1中的值写入该地址,然后R0寄存器值减2- 多寄存器寻址 STM/LDM指令完成多个寄存器的寻址
- 堆栈寻址 一般使用
STMFD SP! {R0-R3}这样的指令进行寻址,最后SP指向栈顶元素。
常用指令#
基础指令
- ADD 加法运算
- MOV 将一个值从一个寄存器传入另一个寄存器,或者将立即数传入一个寄存器
- SUB 减法运算
- RSB 反向减法运算
- ADC 带进位的加法运算,多用来进行大数的加法运算
- SBC 带借位的减法运算,减法会减去(1-C)进行借位操作,多用来进行大数的减法运算
- RSC 反向的带借位减法运算
- CMP 两数相减不保存结果只更新条件标志位
- CMN 两数相加不保存结果只更新条件标志位,常用于反向比较两个数,将其中一个数取负数进行比较
- MUL 将寄存器中的有符号数值相乘,并且只保留低32位到一个寄存器中
- MLA 和MUL类似但是将乘法的结果加上第三个寄存器的值
- UMULL 高精度的无符号数乘法,将结果存储到两个寄存器中
- UMLAL 高精度的无符号加和乘法,将结果加和到两个寄存器
- SMULL 高精度的有符号数乘法
- SMLAL 高精度的有符号加和乘法
位操作指令
- LSL 逻辑左移,空位补零
- LSR 逻辑右移,空位补零
- ASL 算数左移,空位补零
- ASR 算数右移,空位根据符号来补0或1
- ROR 循环右移,将最右侧的位移到最左侧
- RRX 循环右移,将CPSR中进位标志位放在最高位并将最低位移进进位标志位
- MVN 按位取反然后移入到通用寄存器
- AND 按位与
- ORR 按位或
- EOR 按位异或
- TST 按位与但是不保存结果只更新条件标志位
- TEQ 按位异或但是不保存结果只更新条件标志位
- BIC 按位清零
操作内存指令
当偏移地址为非字对齐时,会自动舍弃掉不满一个字的部分。半字同理
- LDR 从内存中取得数据传入寄存器
- STR 向内存中写入数据
- LDRB 从内存中读取一个字节写入寄存器并且空位补零
- STRB 向内存中写入一个字节
- LDRSB 从内存中读取一个字节写入寄存器并且空位按符号扩展
- LDRH 从内存中读取一个半字写入寄存器并且空位补零
- STRH 向内存中写入一个半字
- LDRSH 从内存中读取一个半字写入寄存器并且空位按符号扩展
- LDM 从内存中取得数据放到多个寄存器中
- STM 将多个寄存器的输入写入到内存中
LDMIA R0, {R1-R5} ;以ro为基址读取五个数据单元加载到r1-r5STMIA R6, {R1-R5} ;以r6为基址,将r1-r5的数据写入多寄存器寻址和堆栈寻址时,需要加上不同的后缀,向不同的寻址。
IA 操作后递增IB 操作前递增DA 操作后递减DB 操作前递减堆栈寻址时,堆栈指针指向的是栈顶元素就是满栈,堆栈指针指向栈顶元素的下一个元素就是空栈。
每入栈一个元素,栈指针SP都会往栈增长的方向移动一个存储单元。如果栈指针SP从高地址往低地址移动,那么这个栈就是递减栈;如果栈指针SP从低地址往高地址移动,那么这个栈就是递增栈,ARM处理器使用的一般都是满递减堆栈。
SP!代表着在操作之后递增SP指针。
FA 满递增堆栈FD 满递减堆栈EA 空递增堆栈ED 空递减堆栈STMFD SP! {R0,R1,R2} ;会自动将高位寄存器先入栈LDMFD SP! {R0,R1,R2} ;按照相对应的顺序出栈-
MRS 从特殊寄存器中取数据到一个普通寄存器
-
MSR 从普通寄存器写入数据到一个特殊寄存器,这里的特殊寄存器也就是CPSR
-
BX,BLX,B,BL四种跳转指令,带L的会自动记录PC的值到LR中,带X的可以用于跳转Thumb模式。
伪指令#
声明全局变量
GBLA amount ;定义一个全局数字变量,默认值为0GBLL logic ;定义一个全局逻辑变量,默认值为0GBLS string ;定义一个全局字符串变量,默认值为null声明局部变量
LCLA amount ;定义一个局部数字变量LCLL logic ;定义一个局部逻辑变量LCLS string ;定义一个局部字符串变量变量赋值
amount SETA 10 ;给amount赋值10logic SETL 1 ;给logic赋值1string SETS "你好" ;给string赋值"你好"定义寄存器列表
RegList RLIST {R0-R4} ;将R0到R4定义为RegListSTMFD sp!, RegList ;将R0到R4寄存器入栈数据定义
LTORG ;在代码段最后或者无条件跳转后面或者子程序返回指令,定义数据缓冲池,存储常量信息 MAP 0x40000000 ;一个段中只能有一个map,定义map的基地址A FIELD #4 ;定义A占4个字节,A的起始位置就是MAP的地址B FIELD #4 ;定义B占4个字节,B的起始位置就是MAP加4
string DCB "hello" ;分配一些字节单元给标号stringDCD ;分配字单元,字对齐DCDU ;分配子单元,非字对齐程序控制
IF 逻辑表达式 程序段1ELSE 程序段2ENDIFWHILE 逻辑表达式 程序段 WEND其他伪指令
ALIGN 4,3 ;4个字节对齐并且在上一个位置之后偏移3个字节AREA 段名,CODE,READWRITE ;段定义CODE16 ;16位指令CODE32 ;32位指令ENTRY ;程序入口END ;EXPLORT ;和GLOBAL一样,向外部声明IMPORT ;引入其他源文件中的符号并加入符号表EXTERN ;引入其他源文件中的符号但是如果没有调用,就不会加入符号表EQU ;将常量和标号等效另一个名字GET ;和INCLUDE一样,包含源文件ADR ;取一个标号的地址,在非字对齐255字节之内,字对齐1020之内ADRL ;和ADR一样但是范围不同,在非字对齐64k之内,字对齐256k之内LDR ;取地址,但是后面要加一个=,全局范围读取宏
MACRO$MyMacro MacroName $value1,$value2 LDR R1,#3 LDR R0,#0$MyMacro.LOOP SUB R1,#1 CMP R1,R0 BGT LOOP MEND运算符
x + y ;相加x - y ;相减x * y ;相乘x / y ;整除x :MOD: y ;取余x :ROL: y ;循环左移x :ROR: y ;循环右移x :SHL: y ;左移x :SHR: y ;右移x :AND: y ;按位与x :OR: y ;按位或:NOT: y ;按位取反x :EOR: y ;按位异或;逻辑表达式x = yx > yx < yx >= yx <= yx /= y ;不等于x <> y ;不等于x :LAND: y ;逻辑与x :LOR: y ;逻辑或x :LEOR: y ;逻辑异或:LNOT: y ;逻辑取反:LEN: x ;取长度(字符长度):CHR: x ;将ASCII码转成字符