---
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
```
