Add Go/Postgres admin APIs, Angular admin UI, manual build flow, asset uploads, markdown import/export, configurable slug generation, and the Yar reading theme. Exclude local docs and generated development artifacts from version control.
868 lines
25 KiB
Markdown
868 lines
25 KiB
Markdown
---
|
||
id: ce774734-92c2-4e0d-986b-5d48b3bfe9f5
|
||
slug: arm-learning-summary
|
||
title: ARM 学习总结
|
||
summary: ""
|
||
status: published
|
||
tags: []
|
||
cover: ""
|
||
version: 1
|
||
slug_source: manual
|
||
slug_locked: true
|
||
published_at: "2022-11-23T11:13:27+08:00"
|
||
created_at: "2022-11-23T11:13:27+08:00"
|
||
updated_at: "2026-01-07T18:10:50+08:00"
|
||
---
|
||
|
||
### 二、寻址方式
|
||
|
||
每种寻址方式可能还有其他的变形,但是在这一章中不做过多说明,会在下面对应的章节中给出。
|
||
|
||
#### 寄存器寻址
|
||
|
||
```assembly
|
||
MOV R1,R2 ;R2 -> R1
|
||
```
|
||
|
||
#### 立即寻址
|
||
```assembly
|
||
MOV R0,#0x123 ;0x123 -> R0
|
||
```
|
||
|
||
#### 寄存器偏移寻址
|
||
```assembly
|
||
MOV R0,R1,LSL #2 ;R1 的值左移 2 位,结果送给R0,即 R2 * 4 -> R0
|
||
```
|
||
可采用的移位操作如下:
|
||
|
||
- **LSL**:逻辑左移(**Logical Shift Left**),寄存器中字的低端空出的位补 0
|
||
- **LSR**:逻辑右移(**Logical Shift Right**),寄存器中字的高端空出的位补 0
|
||
- **ASR**:算术右移(**Arithmetic Shift Right**),移位过程中保持符号位不变,即如
|
||
果源操作数为正数,则字的高端空出的位补 0,否则补 1
|
||
- **ROR**:循环右移(**Rotate Right**),由字的低端移出的位填入字的高端空出的位
|
||
- **RRX**:带扩展的循环右移(**Rotate Right eXtended by 1place**),操作数右移一位,
|
||
高端空出的位用原 C 标志值填充。
|
||
|
||
#### 寄存器间接寻址
|
||
```assembly
|
||
LDR R0,[R1] ;将 R1 中的数值作为地址,将这个地址的值取出给R0
|
||
```
|
||
|
||
#### 基址寻址
|
||
|
||
```assembly
|
||
LDR R2,[R3,#0x0F] ;将 R3 中的数值加 0x0F 作为地址,取出此地址的数值保存在 R2 中
|
||
```
|
||
|
||
#### 多寄存器寻址
|
||
#### 堆栈寻址
|
||
|
||
|
||
|
||
### 二、数据处理指令
|
||
|
||
#### 快速查阅表
|
||
|
||
| 编号 | 助记符号 | 说明 | 操作 |
|
||
| :--: | :-------------------: | :-----------------: | :---------------------------: |
|
||
| 0 | MOV Rd ,operand2 | 数据转送 | Rd←operand2 |
|
||
| 1 | MVN Rd ,operand2 | 数据非转送 | Rd←(~operand2) |
|
||
| 2 | ADD Rd,Rn operand2 | 加法运算指令 | Rd←Rn+operand2 |
|
||
| 3 | SUB Rd,Rn operand2 | 减法运算指令 | Rd←Rn-operand2 |
|
||
| 4 | RSB Rd,Rn operand2 | 逆向减法指令 | Rd←operand2-Rn |
|
||
| 5 | ADC Rd,Rn operand2 | 带进位加法 | Rd←Rn+operand2+carry |
|
||
| 6 | SBC Rd,Rn operand2 | 带进位减法指令 | Rd←Rn-operand2-(NOT)Carry |
|
||
| 7 | RSC Rd,Rn operand2 | 带进位逆向减法指令 | Rd←operand2-Rn-(NOT)Carry |
|
||
| 8 | AND Rd,Rn operand2 | 逻辑与操作指令 | Rd←Rn&operand2 |
|
||
| 9 | ORR Rd,Rn operand2 | 逻辑或操作指令 | Rd←Rn\\|operand2 |
|
||
| 10 | EOR Rd,Rn operand2 | 逻辑异或操作指令 | Rd←Rn^operand2 |
|
||
| 11 | BIC Rd,Rn operand2 | 位清除指令 | Rd←Rn&(~operand2) |
|
||
| 12 | CMP Rn,operand2 | 比较指令 | 标志 N、Z、C、V←Rn-operand2 C |
|
||
| 13 | CMN Rn,operand2 | 负数比较指令 | N、Z、C、V←Rn+operand2 |
|
||
| 14 | TST Rn,operand2 | 位测试指令 | 标志 N、Z、C、V←Rn&operand2 |
|
||
| 15 | TEQ Rn,operand2 | 相等测试指令 | 标志 N、Z、C、V←Rn^operand2 |
|
||
| 16 | MUL Rd,Rm,Rs | 32 位乘法指令 | Rd←Rm*Rs (Rd≠Rm) |
|
||
| 17 | MLA Rd,Rm,Rs,Rn | 32 位乘加指令 | Rd←Rm*Rs+Rn (Rd≠Rm) |
|
||
| 18 | UMULL RdLo,RdHi,Rm,Rs | 64 位无符号乘法指令 | (RdLo,RdHi)←Rm*Rs |
|
||
| 19 | UMLAL RdLo,RdHi,Rm,Rs | 64 位无符号乘加指令 | (RdLo,RdHi)←Rm*Rs+(RdLo,RdHi) |
|
||
| 20 | SMULL RdLo,RdHi,Rm,Rs | 64 位有符号乘法指令 | (RdLo,RdHi)←Rm*Rs |
|
||
| 21 | SMLAL RdLo,RdHi,Rm,Rs | 64 位有符号乘加指令 | (RdLo,RdHi)←Rm*Rs+(RdLo,RdHi) |
|
||
|
||
在介绍指令之前,我们首先先来介绍影响CPSR中的一些标志位
|
||
|
||
- V 溢出标志位
|
||
- C 进位或借位标志位
|
||
- 对于加法指令(ADDS 和 CMN)如果产生进位,则C = 1
|
||
- 对于减法指令 (SUBS 和 CMP )如果产生借位,则C = 0
|
||
- Z 结果为0标志位
|
||
- Z = 1 表示运算结果是 0
|
||
- 同理
|
||
|
||
- N 符号标志位
|
||
- N=1 表示运算结果为负数
|
||
- 同理
|
||
|
||
|
||
#### [0] MOV 数据转送指令
|
||
|
||
```assembly
|
||
MOV{cond}{S} Rd,operand2
|
||
|
||
MOV R1,#0x12 ;R1=0x12
|
||
MOV R2,R1,LSL #2 ;R2=R1 << 2
|
||
MOVS R3,R2,LSL #4 ;R3=R2 << 4,并影响标志位
|
||
```
|
||
|
||
|
||
|
||
#### [1] MVN 数据非转送指令
|
||
|
||
这个命令和MOV很像,只不过在传送之前,把操作数先取反了。
|
||
|
||
在使用这个命令的时候,请不要忘记 **ARM 的寄存器是 32位的**
|
||
|
||
```assembly
|
||
MVN{cond}{S} Rd,operand2
|
||
|
||
MVN R1,#0xFF ;R1=0xFFFFFF00,这里的 0xFF 实际上是 0x000000FF
|
||
```
|
||
|
||
|
||
|
||
#### [2] ADD 加法运算指令
|
||
|
||
```assembly
|
||
ADD R1,R1,#0x13 ;R1 = R1 + 0x13
|
||
ADDS R2,R1,#0x1 ;影响标志位
|
||
```
|
||
|
||
|
||
|
||
#### [3] SUB 减法运算指令指令
|
||
|
||
```assembly
|
||
SUB R0,R1,#0x12 ;R0=R1-0x12
|
||
```
|
||
|
||
|
||
|
||
#### [4] RSB 逆向减法指令
|
||
|
||
```assembly
|
||
RSB R3,R1,#0x12 ;R3=0x12-R1
|
||
```
|
||
|
||
|
||
|
||
#### [5] ADC 带进位加法
|
||
|
||
带进位加法指令.将 operand2 的数据与 Rn 的值相加,再加上 CPSR 中的 C 条件标志位.结果保存到 Rd 寄存器.
|
||
|
||
由于寄存器是32位的,所以这个指令常用于计算64位加法。
|
||
|
||
***这里需要注意的是,在进行 ADDS 运算的时候,如果出现了进位,CPSR中的 C=1,否则 C=0***
|
||
|
||
**例如有这样的两个64位数:**
|
||
|
||
**假设R0和R1构成一个64位数,R0存放低32位,R1存放高32位;R2,R3构成一个64位数,R2存放低32位,R3存放高32位.**
|
||
|
||
计算的方法就是,先让两个低32位寄存器相加,为了得到可能的进位,需要用到ADDS,它能影响标志位
|
||
|
||
接着使用ADC,两个高32位相加,再加上标志位中的C 进位
|
||
|
||
```assembly
|
||
LDR R0, =0XFFFFFFFF
|
||
LDR R1, =0X12
|
||
LDR R2, =0X1
|
||
LDR R3, =0X2
|
||
|
||
ADDS R0,R0,R2 ;R0 = R0 + R1 也就是0xFFFFFFFF + 0x1 这得到的是 0x00000000 ,因为溢出了,但是有进位 C = 1
|
||
ADC R1,R1,R3 ; R1 = R1 + R3 也就是 0x12 + 0x2 + 1 得到 0x15
|
||
```
|
||
|
||
|
||
|
||
#### [6] SBC 带进位减法指令
|
||
|
||
带进位减法指令。用寄存器 Rn 减去 operand2,再减去 CPSR 中的 C 条件标志位的反码
|
||
|
||
***这里需要注意的是,在进行 SUBS 运算的时候,如果出现了借位,CPSR中的 C=0,否则 C=1***
|
||
|
||
SBC与ADC指令类似,常用于计算64位的减法。
|
||
|
||
**例如有这样的两个64位数:**
|
||
|
||
**假设R0和R1构成一个64位数,R0存放低32位,R1存放高32位;R2,R3构成一个64位数,R2存放低32位,R3存放高32位.**
|
||
|
||
```assembly
|
||
LDR R0, =0X12
|
||
LDR R1, =0X9
|
||
LDR R2, =0X32
|
||
LDR R3, =0X2
|
||
|
||
SUBS R0,R0,R2 ;R0 = R0 - R2 也就是 0x12 - 0x32 这将得到 0xFFFFFFE0 ,因为不够减,CPSR 中的 N=1,C=0
|
||
SBC R1,R1,R3 ;R1 = R1 - R3 - !C 也就是 0x9 - 0x2 - !0 ,得到0x6
|
||
```
|
||
|
||
|
||
|
||
#### [7] RSC 带进位逆向减法指令
|
||
|
||
用寄存器 operand2 减去 Rn,再减去 CPSR 中的 C 条件标志位的反码
|
||
|
||
**例如有这样的两个64位数:**
|
||
|
||
**假设R0和R1构成一个64位数,R0存放低32位,R1存放高32位;R2,R3构成一个64位数,R2存放低32位,R3存放高32位.**
|
||
|
||
```assembly
|
||
LDR R0, =0X12
|
||
LDR R1, =0X9
|
||
LDR R2, =0X32
|
||
LDR R3, =0X2
|
||
|
||
RSBS R0,R0,R2 ;R0 = R2-R0 也就是 0x32 - 0X12 这将得到 0x20 ,没有借位 ,CPSR 中的 N=1,C=1
|
||
RBC R1,R1,R3 ;R1 = R3 - R2 - !C 也就是 0x2 - 0x9 - !1 ,得到0xFFFFFFF9
|
||
```
|
||
|
||
这里值得注意一下,在计算机中负数是用补码保存的。
|
||
|
||
**2 - 9 = -7**
|
||
|
||
这个 -7 的原码在八位寄存器中是 10000111 , 反码是 11111000,补码是 11111001 ,也就是0xF9 ,同理,在32位寄存器中就是 0xFFFFFFFF9
|
||
|
||
|
||
|
||
所以,在了解了SUBS 和 SBC 之后,**我们同样可以求出 64 位的负数**,和上面的例子是一样的
|
||
|
||
**假设R0和R1构成一个64位数,R0存放低32位,R1存放高32位 ,求它的负数**
|
||
|
||
```assembly
|
||
LDR R0, =0X12
|
||
LDR R1, =0X9
|
||
|
||
RSBS R0,R0,#0 ;R0 = 0-R0 也就是 0 - 0X12 这将得到 0xFFFFFFEE ,有借位 ,CPSR 中的 N=1,C=0
|
||
RBC R1,R1,#0 ;R1 = 0- R1 - !C 也就是 0 - 0x9 - !0 ,得到0xFFFFFFF6
|
||
```
|
||
|
||
|
||
|
||
这里还是算一下,-10 的 补码
|
||
|
||
在八位寄存器中,-10 的原码表示是 1000 1010 ,反码表示是 1111 0101,补码表示是 1111 0110 ,也就是0xF6
|
||
|
||
在32位寄存器中的表示就是,0xFFFF FFF6
|
||
|
||
|
||
|
||
#### [8] AND 逻辑与操作指令
|
||
|
||
```assembly
|
||
AND R0,R1,R2 ;R0=R1&R2
|
||
```
|
||
|
||
|
||
|
||
#### [9] ORR 逻辑或操作指令
|
||
|
||
```assembly
|
||
ORR R0,R1,R2 ;R0=R1|R2
|
||
```
|
||
|
||
|
||
|
||
#### [10] EOR 逻辑异或操作指令
|
||
|
||
#### [11] BIC 位清除指令
|
||
|
||
位清除指令.将寄存器Rn的值与operand2的值的反码按位作逻辑与操作,结果保存 到 Rd 中
|
||
|
||
|
||
|
||
#### [12] CMP 比较指令
|
||
|
||
本质是 做减法 ,结果一定影响标志位
|
||
|
||
```assembly
|
||
CMP R1,R2 ;R1-R2
|
||
```
|
||
|
||
|
||
|
||
#### [13] CMN 负数比较指令
|
||
|
||
本质是 做加法 ,结果一定影响标志位
|
||
|
||
```assembly
|
||
CMN R1,R2 ;R1+R2
|
||
```
|
||
|
||
|
||
|
||
#### [14] TST 位测试指令
|
||
|
||
指令将寄存器Rn的值与operand2的值按位作逻辑与操作,根据操作的 结果理新 CPSR 中相应的条件标志位
|
||
|
||
````ass
|
||
TST R0,#0x1 ;判断 R0 的最低位是否为 0
|
||
````
|
||
|
||
|
||
|
||
#### [15] TEQ 位相等测试指令
|
||
|
||
指令寄存器Rn的值与operand2的值按位作逻辑异或操作,根据操作 的结果理新 CPSR 中相应条件标志位
|
||
|
||
```assembly
|
||
TEQ R0,R1 ;比较 R0 与 R1 是否相等 ,与用cmp命令对比,它不影响(不影响 V 位和 C 位)
|
||
```
|
||
|
||
|
||
|
||
#### [16] MUL 32 位乘法指令
|
||
|
||
指令将 Rm 和 Rs 中的值相乘,结果的低 32 位保存到 Rd 中
|
||
|
||
**MUL{cond}{S} Rd,Rm,Rs**
|
||
|
||
```assembly
|
||
MUL R1,R2,R3 ;R1=R2×R3
|
||
MULS R1,R2,R3 ;R0=R2×R3,同时设置 CPSR 中的 N 位和 Z 位
|
||
```
|
||
|
||
|
||
|
||
#### [17] MLA 32 位乘加指令
|
||
|
||
指令将 Rm 和 Rs 中的值相乘,再将乘积加上第 3 个操作数,结果的低 32 位保存到 Rd 中
|
||
|
||
**MLA{cond}{S} Rd,Rm,Rs,Rn**
|
||
|
||
```ASS
|
||
MLA R1,R2,R3,R4 ;R1=R2×R3+R4
|
||
```
|
||
|
||
|
||
|
||
#### [18] UMULL 64 位无符号乘法指令
|
||
|
||
**U即 Unsigned 无符号**
|
||
|
||
指令将 Rm 和 Rs 中的值作无符号数相乘,结果的低 32 位保存 到 RsLo 中,而高 32 位保存到 RdHi 中
|
||
|
||
**UMULL{cond}{S} RdLo,RdHi,Rm,Rs**
|
||
|
||
```assembly
|
||
UMULL R0,R1,R2,R3 ;(R1:R0)=R2×R3 ;相当于 R0 = (R2*R3) 的低32位,R1 = (R2*R3) 的高32位
|
||
```
|
||
|
||
|
||
|
||
#### [19] UMLAL 64 位无符号乘加指令
|
||
|
||
**U即 Unsigned 无符号**
|
||
|
||
指令将 Rm 和 Rs 中的值作无符号数相乘,64 位乘积与 RdHi,RdLo 相加,结果的低 32 位保存到 RdLo 中,而高 32 位保存到 RdHi 中.
|
||
|
||
**UMLAL{cond}{S} RdLo,RdHi,Rm,Rs**
|
||
|
||
```assembly
|
||
UMLAL R0,R1,R2,R3 ;(R1,R0)=R2×R3+(R1,R0) ;相当于 R0 = (R2*R3) 的低32位 + R0,R1 = (R2*R3) 的高32位+ R1
|
||
```
|
||
|
||
|
||
|
||
#### [20] SMULL 64 位有符号乘法指令
|
||
|
||
**S即 Signed 有符号**
|
||
|
||
指令将 Rm 和 Rs 中的值作有符号数相乘,结果的低 32 位保存 到 RdLo 中,而高 32 位保存到 RdHi 中
|
||
```assembly
|
||
SMULL R0,R1,R2,R3 ;(R1:R0)=R2×R3 ;相当于 R0 = (R2*R3) 的低32位,R1 = (R2*R3) 的高32位
|
||
```
|
||
|
||
|
||
|
||
#### [21] SMLAL 64 位有符号乘加指令
|
||
|
||
指令将 Rm 和 Rs 中的值作有符号数相乘,64 位乘积与RdHi,RdLo,相加,结果的低 32 位保存到 RdLo 中,而高 32 位保存到 RdHi 中.
|
||
|
||
```assembly
|
||
SMLAL R0,R1,R2,R3 ;(R1,R0)=R2×R3+(R1,R0) ;相当于 R0 = (R2*R3) 的低32位 + R0,R1 = (R2*R3) 的高32位+ R1
|
||
```
|
||
|
||
|
||
|
||
### 三、ARM分支指令
|
||
|
||
在了解分支指令之前,我们首先得去了解一下**条件码**,否则我们就会没办法正确使用分支指令
|
||
|
||
在此给出条件码表格
|
||
|
||
| 条件码助记符 | 英文含义,助记符来源 | 查看的标志 | 中文含义 |
|
||
| :----------: | :--------------------------------------------------------: | :--------: | :------------------------: |
|
||
| EQ | **Eq**ual | Z=1 | 相等 |
|
||
| NE | **N**ot **e**qual. | Z=0 | 不相等 |
|
||
| CS/HS | Unsigned **h**igher or **s**ame (or **c**arry **s**et). | C=1 | 无符号数大于或等于/C位设置 |
|
||
| CC/LO | Unsigned **lo**wer (or **c**arry **c**lear). | C=0 | 无符号数小于/C位清除 |
|
||
| MI | Negative. The mnemonic stands for "**mi**nus". | N=1 | 负数 |
|
||
| PL | Positive or zero. The mnemonic stands for "**pl**us". | N=0 | 正数或零 |
|
||
| VS | Signed overflow. The mnemonic stands for "**V s**et". | V=1 | 溢出/V位设置 |
|
||
| VC | No signed overflow. The mnemonic stands for "**V c**lear". | V=0 | 没有溢出 /V位清除 |
|
||
| HI | Unsigned **hi**gher. | C=1,Z=0 | 无符号数大于 |
|
||
| LS | Unsigned **l**ower or **s**ame. | C=0,Z=1 | 无符号数小于或等于 |
|
||
| GE | Signed **g**reater than or **e**qual. | N=V | 带符号数大于或等于 |
|
||
| LT | Signed **l**ess **t**han. | N!=V | 带符号数小于 |
|
||
| GT | Signed **g**reater **t**han. | Z=0,N=V | 带符号数大于 |
|
||
| LE | Signed **l**ess than or **e**qual. | Z=1,N!=V | 带符号数小于或等于 |
|
||
| AL | Always executed. | 任何 | 无条件执行(指令默认条件) |
|
||
|
||
|
||
|
||
#### 快速记忆方法:
|
||
|
||
我们必须得结合英文才能快速地记住这些**“助记符”**
|
||
|
||
***尤其是无符号和有符号之间的比较。***
|
||
|
||
无符号一般会使用 **Lower 和 Higher**和 **Same**,有符号一般会使用 **Greater than** 和 **Less than** 和 **Equal**,
|
||
|
||
所以,**无符号的大于等于** HS = Higher + Same ,**无符号小于等于** LS = Lower + Same ,**无符号大于 HI** = Higher(前两个字母),**无符号小于** LO = Lower
|
||
|
||
同理的,**有符号大于等于** GE = Greater + Equal ,**有符号小于等于** LE = Less + Equal, **有符号大于**就是 GT = Greater + Than ,**有符号小于**就是 LT = Less + Than
|
||
|
||
|
||
|
||
所以,通过这个英文可以很快速地就记住。
|
||
|
||
|
||
|
||
#### 跳转分支指令
|
||
|
||
接下来,我们来说一下跳转指令,同样的,给出跳转指令快速查阅表:
|
||
|
||
| 助记符 | 说明 | 操作 |
|
||
| :------: | :------------------: | :-------------------: |
|
||
| B label | 跳转指令 | PC←label |
|
||
| BL label | 带链接的跳转指令 | LR←PC-4, PC←label |
|
||
| BX Rm | 带状态切换的跳转指令 | PC←label,切换处理状态 |
|
||
|
||
|
||
|
||
#### [1] B 跳转指令
|
||
|
||
**B{cond} label**
|
||
|
||
```assembly
|
||
B LOOP_Y1 ;跳转到 LOOP_Y1 标号处
|
||
```
|
||
|
||
|
||
|
||
#### [2] BL 带链接的跳转指令
|
||
|
||
**BL{cond} label**
|
||
|
||
这个跳转的操作是:**LR←PC-4, PC←label**,由于将PC地址保持到了LR寄存器里面,所以之后还能跳转回来
|
||
|
||
|
||
|
||
#### [3] BX 带状态切换的跳转指令
|
||
|
||
略
|
||
|
||
|
||
|
||
### 四、加载和存储指令
|
||
|
||
**Load and Store with register offset.**
|
||
|
||
他们最基础的指令是 LDR 和 STR,以下先给出这两个基础指令的用法:
|
||
|
||
| 助记符 | 说明 | 操作 |
|
||
| ------------------ | ---------- | --------------- |
|
||
| LDR Rd, addressing | 加载字数据 | Rd←[addressing] |
|
||
| STR Rd, addressing | 存储字数据 | [addressing]←Rd |
|
||
| | | |
|
||
|
||
#### 寄存器间接寻址
|
||
|
||
```
|
||
LDR R0,[R1] ;R0 <- [R1]
|
||
STR R0,[R1] ;[R1] <- R0
|
||
```
|
||
|
||
|
||
|
||
#### 基址加变址寻址
|
||
|
||
这里有几种方式,前变址法、后变址法、自动变址
|
||
|
||
- 前变址法,也就是先变化地址,再根据这个地址 存取。
|
||
|
||
```assembly
|
||
LDR R0,[R1,#4] ;R0 <- [R1 + 4]
|
||
```
|
||
|
||
- 后变址,也就是先存取,再变化地址
|
||
|
||
```assembly
|
||
LDR R0,[R1],#4 ;R0 <- [R1] 然后 R1<-R1+4
|
||
```
|
||
|
||
- 自动变址,综合上面两种,加一个 感叹号 !
|
||
|
||
```assembly
|
||
LDR R0,[R1,#4]! ;R0 <- [R1 + 4] 然后 R1<-R1+4
|
||
```
|
||
|
||
STR 指令也是同理的,这里不再赘述。
|
||
|
||
在理解了基础指令之后,我们可以尝试去看看这两个指令的更多用法:
|
||
|
||
以下依旧给出速查表:
|
||
|
||
| 助记符 | 说明 | 操作 |
|
||
| -------------------- | -------------------------- | --------------- |
|
||
| LDR Rd, addressing | 加载字数据 | Rd←[addressing] |
|
||
| LDRB Rd,addressing | 加载无符字节数据 | Rd←[addressing] |
|
||
| LDRT Rd,addressing | 以用户模式加载字数据 | Rd←[addressing] |
|
||
| LDRBT Rd,addressing | 以用户模式加载无符号字数据 | Rd←[addressing] |
|
||
| LDRH Rd,addressing | 加载无符半字数据 | Rd←[addressing] |
|
||
| LDRSB Rd,addressing | 加载有符字节数据 | Rd←[addressing] |
|
||
| LDRSH Rd,addressing | 加载有符半字数据 | Rd←[addressing] |
|
||
| | | |
|
||
| STR Rd,addressing | 存储字数据 | [addressing]←Rd |
|
||
| STRB Rd,addressing | 存储字节数据 | [addressing]←Rd |
|
||
| STRT Rd,addressing | 以用户模式存储字数据 | [addressing]←Rd |
|
||
| SRTBT Rd,addressing | 以用户模式存储字节数据 | [addressing]←Rd |
|
||
| STRH Rd,addressing | 存储半字数据 | [addressing]←Rd |
|
||
|
||
|
||
|
||
虽然看起来蛮多的,但主要就是
|
||
|
||
- 后缀带有B的,是无符字节数据
|
||
- 后缀带有H的,是无符半字数据
|
||
- 后缀带有SB的,是有符号字节数据
|
||
- 后缀带有SH的,是有符号半字数据
|
||
|
||
因为 字节是**B**yte ,半字是**H**alf Word,有符号是 **S**igned
|
||
|
||
|
||
|
||
### 五、加载和存储指令LDM 和 STM 批量加载和批量存储分析
|
||
|
||
这一段内容来自 http://blog.chinaunix.net/uid-29401328-id-5059312.html
|
||
|
||
这里是简单地进行搬运。
|
||
|
||
#### 普通用法和堆栈用法
|
||
|
||
|
||
|
||
> 当LDM/STM没有被用于堆栈,而只是简单地表示地址前向增加,后向增加,前向减少,后向减少时,由IA,IB,DA,DB控制。
|
||
|
||
- IA ----> Increment After 每次传送后地址加4
|
||
- IB ----> Increment Before 每次传送前地址加4
|
||
- DA ----> Decrement After 每次传送后地址减4
|
||
- DB ----> Decrement Before 每次传送前地址减4
|
||
|
||
> 堆栈请求格式,FD,ED,FA,EA定义了前/后向索引和上/下位
|
||
>
|
||
> F,E表示堆栈满或者空。
|
||
> A 和 D 定义堆栈是递增还是递减,如果递增,STM将向上,LDM向下,如果递减,则相反。
|
||
|
||
- FA ----> Full Ascending 满递增堆栈
|
||
- FD ----> Full Descending 满递减堆栈
|
||
- EA ----> Empty Ascending 空递增堆栈
|
||
- ED ----> Empty Descending 空递减堆栈
|
||
|
||
|
||
|
||
#### 普通用法
|
||
|
||
```assembly
|
||
STMIA R0!,{R1,R3,R5}
|
||
LDMDB R0!,{R1-R3}
|
||
```
|
||
|
||
保存的时候使用了 IA **后增加**的方式,取的时候就得用 DB **先减少** 的方式
|
||
|
||
这个例子的R0指向一段基地址
|
||
|
||
|
||
|
||
#### 堆栈用法
|
||
|
||
1. Full descending 满递减堆栈——FD 堆栈首部是高地址,堆栈向低地址增长。栈指针总是指向堆栈**最后一个元素**(最后
|
||
一个元素是最后压入的数据)。ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用Full descending 类型堆栈。
|
||
|
||
2. Full ascending 满递增堆栈——FA 堆栈首部是低地址,堆栈向高地址增长。栈指针总是指向堆栈**最后一个元素**(最后
|
||
一个元素是最后压入的数据)。
|
||
|
||
3. Empty descending 空递减堆栈——ED 堆栈首部是高地址,堆栈向低地址增长。栈指针**总是指向下一个将要放入数据的空位置**。
|
||
|
||
4. Empty ascending 空递增堆栈——EA 堆栈首部是低地址,堆栈向高地址增长。栈指针**总是指向下一个将要放入数据的空位置**。
|
||
|
||
**A 和D 定义堆栈是递增还是递减,如果递增,STM将向上,LDM向下,如果递减,则相反。**
|
||
|
||
**所以,LDMFD和STMFD是成对使用,因为堆栈方式和出栈方式要是相同的**
|
||
|
||
|
||
|
||
### 六、ARM 伪指令
|
||
|
||
| 伪指令助记符 | 说明 | 操作 |
|
||
| ----------------------------------- | ------------------------ | ------------------------------------------ |
|
||
| ADR{cond} register,exper | 小范围的地址读取伪指令 | register<-expr 指向的地址 |
|
||
| ADRL {cond} register,exper | 中等范围的地址读取伪指令 | register<-expr 指向的地址 |
|
||
| LDR{cond} register,=expr/label_expr | 大范围的地址读取伪指令 | register<-expr/label-expr 指定 的数据/地址 |
|
||
| NOP | 空操作伪指令 | |
|
||
|
||
|
||
|
||
### 七、简单介绍数据定义伪指令
|
||
|
||
#### 1、DCB 分配一段字节的内存单元
|
||
|
||
**{label} DCB expr{,expr}{,expr}…**
|
||
|
||
```
|
||
A
|
||
DCB 0x11,0x22,0x33,0x44
|
||
DCB 0x55,0x66,0x77,0x88
|
||
DCB "Hello World"
|
||
DCB "ABCDEFGHIJKLMN",0
|
||
```
|
||
|
||
|
||
|
||
#### 2 、DCW 和 DCWU 分配一段半字的内存单元
|
||
|
||
DCWU 需要半字对齐
|
||
|
||
```assembly
|
||
B
|
||
DCW 0x1122,0x3344,0x5566,0x7788
|
||
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
#### 3、 DCD 和 DCDU 分配一段字内存单元
|
||
|
||
DCD 需要字对齐
|
||
|
||
```assembly
|
||
C
|
||
DCW 0x11223344,0x55667788,0x99aabbcc,0xddeeff00
|
||
```
|
||
|
||
|
||
|
||
#### 4、SPACE 分配一片连续的字节内存单元,并初始化为0
|
||
|
||
**{label} SPACE expr**
|
||
|
||
```assembly
|
||
D
|
||
Space 500 ;分配 500 字节空间,并初始化为0
|
||
```
|
||
|
||
|
||
|
||
### 八、汇编程序设计
|
||
|
||
#### 程序 1 、使用跳转完成函数功能
|
||
|
||
首先,我们先来一个简单的跳转指令:
|
||
|
||
```assembly
|
||
... ;之前的一些操作
|
||
BL ADD_FUNCTION ;带连接的跳转,LR <- PC -4 ,PC <- ADD_FUNCTION
|
||
... ;完成ADD_FUNCTION 这个函数的操作
|
||
...
|
||
ADD_FUNCTION
|
||
... ;一些操作
|
||
MOV PC,LR ;函数返回,相当于RET、Return,总之就是 PC <- LR
|
||
```
|
||
|
||
|
||
|
||
下面这个例子是老师给的:
|
||
|
||
**设计一个函数,计算R0 和 R1 的值**
|
||
``` assembly
|
||
AREA Example1,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
LDR R0,=0X66
|
||
LDR R1,=0X88
|
||
BL ADD_FUNCTION ;带链接跳转
|
||
B RETURN ;跳到结束位置
|
||
ADD_FUNCTION
|
||
ADD R0,R0,R1
|
||
MOV PC,LR
|
||
RETURN
|
||
END
|
||
```
|
||
|
||
|
||
|
||
#### 程序 2 、计算数组第1项和第5项之和,并将结果保存在第9项中
|
||
|
||
```assembly
|
||
AREA Example1,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
LDR R0,=ARRAY
|
||
LDR R1,[R0]
|
||
LDR R2,[R0,#16]
|
||
ADD R1,R1,R2
|
||
|
||
STR R1,[R0,#32]
|
||
|
||
|
||
ARRAY
|
||
DCD 0X11,0X22,0X33,0X44
|
||
DCD 0X55,0X66,0X77,0X88
|
||
DCD 0X00,0X00,0X00,0X00
|
||
END
|
||
```
|
||
|
||
|
||
|
||
#### 程序 3、编写一个分支程序段,如果R5中的值等于10,就把R5中的数据存入R1,否则就把R5中的数据分别存入寄存器R0和R1
|
||
|
||
```assembly
|
||
AREA Example1,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
MOV R5,#9
|
||
CMP R5,#10
|
||
MOVNE R0,R5
|
||
MOV R1,R5
|
||
END
|
||
```
|
||
|
||
|
||
|
||
#### 程序 4、编写一个程序段,当R1中的数据大于R2中的数据时,将R2中的数据加10存入R1中,否则将R2中的数据加5存入R1中
|
||
|
||
```assembly
|
||
AREA Example1,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
MOV R1,#10
|
||
MOV R2,#5
|
||
CMP R1,R2
|
||
ADDHI R1,R2,#10
|
||
ADDLS R1,R2,#5
|
||
END
|
||
```
|
||
|
||
还记得吗?
|
||
|
||
> 无符号一般会使用 **Lower 和 Higher**和 **Same**,有符号一般会使用 **Greater than** 和 **Less than** 和 **Equal**,
|
||
>
|
||
> 所以,**无符号的大于等于** HS = Higher + Same ,**无符号小于等于** LS = Lower + Same ,**无符号大于 HI** = Higher(前两个字母),**无符号小于** LO = Lower
|
||
>
|
||
> 同理的,**有符号大于等于** GE = Greater + Equal ,**有符号小于等于** LE = Less + Equal, **有符号大于**就是 GT = Greater + Than ,**有符号小于**就是 LT = Less + Than
|
||
|
||
|
||
|
||
#### 程序 5、循环,将 src 中的10个字节的数据,传送到 dst 开始的区域
|
||
|
||
```assembly
|
||
AREA init,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
LDR R0,=src
|
||
LDR R1,=dst
|
||
MOV R2,#0
|
||
|
||
LOOP
|
||
LDRB R3,[R0,R2]
|
||
STRB R3,[R1,R2]
|
||
|
||
ADD R2,R2,#1
|
||
CMP R2,#10
|
||
BLO LOOP
|
||
|
||
src
|
||
DCB "0123456789"
|
||
dst
|
||
DCB "aaaaaaaaaa"
|
||
|
||
END
|
||
```
|
||
|
||
|
||
|
||
#### 程序 6、循环,将src中的所有小写字母变成大写字母,其他的ASCII码不变
|
||
|
||
我们需要知道 ascii 码中,
|
||
|
||
A的十六进制是41,能够推出Z的十六进制是5A
|
||
|
||
a的十六进制是61,能够推出z的十六进制是7A
|
||
|
||
|
||
|
||
```assembly
|
||
AREA init,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
LDR R0,=src
|
||
MOV R1,#0
|
||
|
||
LOOP
|
||
LDRB R2,[R0,R1]
|
||
CMP R2,#0X61
|
||
BLO NEXT
|
||
CMP R2,#0X7A
|
||
SUBLS R2,R2,#0X20
|
||
STRBLS R2,[R0,R1]
|
||
|
||
NEXT
|
||
ADD R1,R1,#1
|
||
CMP R1,#10
|
||
BNE LOOP
|
||
|
||
src
|
||
DCB "AabCdEfghI"
|
||
END
|
||
```
|
||
|
||
|
||
|
||
#### 程序 7、循环,将src中的所有大写字母变成小写字母,其他的ASCII码不变
|
||
|
||
和上一题同理
|
||
|
||
```assembly
|
||
AREA init,CODE,READONLY
|
||
ENTRY
|
||
start
|
||
LDR R0,=src
|
||
MOV R1,#0
|
||
|
||
LOOP
|
||
LDRB R2,[R0,R1]
|
||
CMP R2,#0X41
|
||
BLO NEXT
|
||
CMP R2,#0X5A
|
||
ADDLS R2,R2,#0X20
|
||
STRBLS R2,[R0,R1]
|
||
|
||
NEXT
|
||
ADD R1,R1,#1
|
||
CMP R1,#10
|
||
BNE LOOP
|
||
|
||
src
|
||
DCB "AabCdEfghI"
|
||
END
|
||
```
|