osaet/content/posts/arm-learning-summary.md
yarnom f0b50d13ea feat: add admin publishing workflow and yar theme
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.
2026-06-01 15:48:04 +08:00

868 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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←Rnoperand2 |
| 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←Rnoperand2 |
| 14 | TST Rn,operand2 | 位测试指令 | 标志 N、Z、C、V←Rnoperand2 |
| 15 | TEQ Rn,operand2 | 相等测试指令 | 标志 N、Z、C、V←Rnoperand2 |
| 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位R2R3构成一个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位R2R3构成一个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=1C=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位R2R3构成一个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=1C=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=1C=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 | Z1 | 相等 |
| NE | **N**ot **e**qual. | Z0 | 不相等 |
| 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=1Z=0 | 无符号数大于 |
| LS | Unsigned **l**ower or **s**ame. | C=0Z=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=0N=V | 带符号数大于 |
| LE | Signed **l**ess than or **e**qual. | Z=1N=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 Rdaddressing | 以用户模式加载字数据 | Rd←[addressing] |
| LDRBT Rdaddressing | 以用户模式加载无符号字数据 | Rd←[addressing] |
| LDRH Rdaddressing | 加载无符半字数据 | Rd←[addressing] |
| LDRSB Rdaddressing | 加载有符字节数据 | Rd←[addressing] |
| LDRSH Rdaddressing | 加载有符半字数据 | Rd←[addressing] |
| | | |
| STR Rdaddressing | 存储字数据 | [addressing]←Rd |
| STRB Rdaddressing | 存储字节数据 | [addressing]←Rd |
| STRT Rdaddressing | 以用户模式存储字数据 | [addressing]←Rd |
| SRTBT Rdaddressing | 以用户模式存储字节数据 | [addressing]←Rd |
| STRH Rdaddressing | 存储半字数据 | [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
```