ARM处理器汇编语言

HarderHeng Lv5

Thumb状态切换

Thumb是16位指令的状态,可以使用16位指令,在进行切换时不会改变通用寄存器。

1
2
3
4
5
6
7
;现在是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 全模式下可用,其中R13SP堆栈指针R14LR链接寄存器R15PC寄存器
  • 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压栈才能进入下一层嵌套,回到上一层嵌套就将寄存器出栈。

1
2
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汇编不需要加
  • 寄存器寻址 寄存器中的值直接作为操作数
1
2
3
4
5
MOV r1, r2 ;将r2的值传到r1
ADD r1, r2, r3 ;将r2的值加上r3的值并赋给r1
MOV r2, r1, LSL #3 ;将r1<<3位并赋给r1
ADD r3, r2, r1, LSL #3 ;将r1<<3 + r2赋给r3
ADD r3, r2, r1, LSL r0 ;将r1<<r0 + r2赋给r3
  • 寄存器间址 寄存器中的值作为内存地址,使用内存地址上的数据作为操作数。
1
2
LDR r1, [r2] ;以r2中的内存地址加载数据进r1
STR r1, [r2] ;将r1中的值写入r2寄存器中的内存地址
  • 基址寻址 使用寄存器中的地址生成新地址并且寻址,一般用于查表和数组访问。
1
2
3
4
5
6
7
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 将多个寄存器的输入写入到内存
1
2
LDMIA R0, {R1-R5} ;以ro为基址读取五个数据单元加载到r1-r5
STMIA R6, {R1-R5} ;以r6为基址,将r1-r5的数据写入

多寄存器寻址和堆栈寻址时,需要加上不同的后缀,向不同的寻址。

1
2
3
4
IA 操作后递增
IB 操作前递增
DA 操作后递减
DB 操作前递减

堆栈寻址时,堆栈指针指向的是栈顶元素就是满栈,堆栈指针指向栈顶元素的下一个元素就是空栈

每入栈一个元素,栈指针SP都会往栈增长的方向移动一个存储单元。如果栈指针SP从高地址往低地址移动,那么这个栈就是递减栈;如果栈指针SP从低地址往高地址移动,那么这个栈就是递增栈,ARM处理器使用的一般都是满递减堆栈

SP!代表着在操作之后递增SP指针。

1
2
3
4
5
6
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模式。

伪指令

声明全局变量

1
2
3
GBLA amount ;定义一个全局数字变量,默认值为0
GBLL logic ;定义一个全局逻辑变量,默认值为0
GBLS string ;定义一个全局字符串变量,默认值为null

声明局部变量

1
2
3
LCLA amount ;定义一个局部数字变量
LCLL logic ;定义一个局部逻辑变量
LCLS string ;定义一个局部字符串变量

变量赋值

1
2
3
amount SETA 10 ;给amount赋值10
logic SETL 1 ;给logic赋值1
string SETS "你好" ;给string赋值"你好"

定义寄存器列表

1
2
RegList RLIST {R0-R4} ;将R0到R4定义为RegList
STMFD sp!, RegList ;将R0到R4寄存器入栈

数据定义

1
2
3
4
5
6
7
8
LTORG ;在代码段最后或者无条件跳转后面或者子程序返回指令,定义数据缓冲池,存储常量信息
MAP 0x40000000 ;一个段中只能有一个map,定义map的基地址
A FIELD #4 ;定义A占4个字节,A的起始位置就是MAP的地址
B FIELD #4 ;定义B占4个字节,B的起始位置就是MAP加4

string DCB "hello" ;分配一些字节单元给标号string
DCD ;分配字单元,字对齐
DCDU ;分配子单元,非字对齐

程序控制

1
2
3
4
5
IF	逻辑表达式
程序段1
ELSE
程序段2
ENDIF
1
2
3
WHILE 逻辑表达式
程序段
WEND

其他伪指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 ;取地址,但是后面要加一个=,全局范围读取

1
2
3
4
5
6
7
8
9
	MACRO
$MyMacro MacroName $value1,$value2
LDR R1,#3
LDR R0,#0
$MyMacro.LOOP
SUB R1,#1
CMP R1,R0
BGT LOOP
MEND

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 = y
x > y
x < y
x >= y
x <= y
x /= y ;不等于
x <> y ;不等于
x :LAND: y ;逻辑与
x :LOR: y ;逻辑或
x :LEOR: y ;逻辑异或
:LNOT: y ;逻辑取反
:LEN: x ;取长度(字符长度)
:CHR: x ;将ASCII码转成字符

  • Title: ARM处理器汇编语言
  • Author: HarderHeng
  • Created at : 2024-04-08 08:32:37
  • Updated at : 2024-06-05 22:33:37
  • Link: https://harderheng.life/2024/04/08/ARM汇编语言/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments