BackGroundKnowledge

一条汇编指令格式应该为:Operation{cond}{s} Rd,Rn, Operand2

操作码 - 条件指令 - 状态码 - 目标寄存器 - 源寄存器 - 后续附加数据

like:ADD EQ N R1 R2 #100

写出来就是 → ADDEQN R1,r2,#100

其中{}看情况用,不是必须

条件指令

一般是跟在 B,LDR 等指令后的:

EQ : 等于

如果一次比较之后设置了 Z 标志。

NE : 不等于

如果一次比较之后清除了 Z 标志。

VS : 溢出设置

如果在一次算术操作之后设置了 V 标志,计算的结果不适合放入一个 32bit 目标寄存器中。

VC : 溢出清除

如果清除了 V 标志,与 VS 相反。

HI : 高于(无符号)

如果一次比较之后设置了 C 标志清除了 Z 标志。

LS : 低于或同于(无符号)

如果一次比较操作之后清除了 C 标志设置了 Z 标志。

PL : 正号

如果一次算术操作之后清除了 N。出于定义‘正号’的目的,零是正数的原因是它不是负数…

MI : 负号

如果一次算术操作之后设置了 N 标志。

CS : 进位设置

如果一次算术操作或移位操作之后设置了 C 标志,操作的结果不能表示为 32bit。你可以把 C 标志当作结果的第 33 位。

CC : 进位清除

与 CS 相反。

GE : 大于或等于(有符号)

如果一次比较之后…
设置了 N 标志设置了 V 标志
或者…
清除了 N 标志清除了 V 标志。

GT : 大于(有符号)

如果一次比较之后…
设置了 N 标志设置了 V 标志
或者…
清除了 N 标志清除了 V 标志
并且
清除了 Z 标志。

LE : 小于或等于(有符号)

如果一次比较之后…
设置了 N 标志清除了 V 标志
或者…
清除了 N 标志设置了 V 标志
并且
设置了 Z 标志。

LT : 小于(有符号)

如果一次比较之后…
设置了 N 标志清除了 V 标志。
或者…
清除了 N 标志设置了 V 标志。

AL : 总是

缺省条件,所以不用明显声明。

状态码

状态码保存在 CPU 内部寄存器 CPSR 中,用于记录特定的信息

状态码

N(NEGATIVE)标志 31 位

CPSR 的第 31 位是 N,符号标志位。它记录相关指令执行后,其结果是否为负.如果为负 N = 1,如果是非负数 N = 0.

注意:在 ARM64 的指令集中,有的指令的执行时影响状态寄存器的,比如 add\sub\or 等,他们大都是运算指令(进行逻辑或算数运算);

Z(ZERO)标志 30 位

CPSR 的第 30 位是 Z,0 标志位。它记录相关指令执行后,其结果是否为 0.如果结果为 0.那么 Z = 1.如果结果不为 0,那么 Z = 0.

注意:对于 Z 的值,我们可以这样来看,Z 标记相关指令的计算结果是否为 0,如果为 0,则 N 要记录下是 0 这样的肯定信息.在计算机中 1 表示逻辑真,表示肯定.所以当结果为 0 的时候 Z = 1,表示结果是 0.如果结果不为 0,则 Z 要记录下不是 0 这样的否定信息.在计算机中 0 表示逻辑假,表示否定,所以当结果不为 0 的时候 Z = 0,表示结果不为 0。

当结果为 0 时 Z = 1 当结果不为 0 时 Z = 0

C(CARRY)标志 29 位

  • CPSR 的第 29 位是 C,进位标志位。一般情况下,进行无符号数的运算。
  • 加法运算:当运算结果产生了进位时(无符号数溢出),C=1,否则 C=0。
  • 减法运算(包括 CMP):当运算时产生了借位时(无符号数溢出),C=0,否则 C=1。

对于位数为 N 的无符号数来说,其对应的二进制信息的最高位,即第 N - 1 位,就是它的最高有效位,而假想存在的第 N 位,就是相对于最高有效位的更高位。
img
进位
我们知道,当两个数据相加的时候,有可能产生从最高有效位想更高位的进位。比如两个 32 位数据:0xaaaaaaaa + 0xaaaaaaaa,将产生进位。由于这个进位值在 32 位中无法保存,我们就只是简单的说这个进位值丢失了。其实 CPU 在运算的时候,并不丢弃这个进位制,而是记录在一个特殊的寄存器的某一位上。ARM 下就用 C 位来记录这个进位值。比如,下面的指令

1
2
3
4
5
mov w0,#0xaaaaaaaa;0xa 的二进制是 1010
adds w0,w0,w0; 执行后 相当于 1010 << 1 进位1(无符号溢出) 所以C标记 为 1
adds w0,w0,w0; 执行后 相当于 0101 << 1 进位0(无符号没溢出) 所以C标记 为 0
adds w0,w0,w0; 重复上面操作
adds w0,w0,w0

借位

  • 当两个数据做减法的时候,有可能向更高位借位。

  • 两个 32 位数据:0x00000000 - 0x000000ff,将产生借位,借位后,相当于计算 0x100000000 - 0x000000ff。得到 0xffffff01 这个值。由于借了一位,所以 C 位 用来标记借位。C = 0.比如下面指令:

    1
    2
    3
    4
    mov w0,#0x0
    subs w0,w0,#0xff ;
    subs w0,w0,#0xff
    subs w0,w0,#0xff

V(OVERFLOW)溢出标志 28 位

CPSR 的第 28 位是 V,溢出标志位。在进行有符号数运算的时候,如果超过了机器所能标识的范围,称为溢出。

正数 + 正数 为负数 溢出
负数 + 负数 为正数 溢出
正数 + 负数 不可能溢出

寻址方式

由于数据保存在:内存/寄存器中,因此 ARM 难点就在于如何从前两者容器中获取数据,转移数据

寻址方式

前三种比较常见,看看第四个寄存器移位地址

表示:R1 寄存器左移 2 位,加上 R2,再赋给 R3

  • 相对寻址:
    • BL NEXT; 跳转到 NEXT(自己写的函数)
    • MOV PC,LR; LR 的值赋给程序计数器 PC

ARM 堆栈

  • ◎ Full descending 满递减堆栈

堆栈首部是高地址,堆栈向低地址增长。栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。

ARM-Thumb 过程调用标准和 ARM、Thumb C/C++ 编译器总是使用 Full descending 类型堆栈。

  • ◎ Full ascending 满递增堆栈

堆栈首部是低地址,堆栈向高地址增长。栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。

  • ◎ Empty descending 空递减堆栈

堆栈首部是低地址,堆栈向高地址增长。栈指针总是指向下一个将要放入数据的空位置。

  • ◎ Empty ascending 空递增堆栈

堆栈首部是高地址,堆栈向低地址增长。栈指针总是指向下一个将要放入数据的空位置。

操作指令 - ALU 操作

数据操作表 1:https://blog.csdn.net/dengjin20104042056/article/details/106794349

数据操作表 2:https://blog.csdn.net/dengjin20104042056/article/details/106794861

主要分:数据操作 + 逻辑操作 + 比较操作

  • 就像 java 中 加减乘除 对应 数据操作;
  • 或与非对应逻辑操作;
  • = <对应比较操作;
  • if else,whilefor 循环对应流程操作

逻辑操作

比较操作通过运算,将结果不保存在寄存器而是 S 标识符中用于判断

比较操作

内存操作 - 读取内存

重点在于研究清楚:LDR STR 存储顺序

内存操作,可以理解为将内存器内容读取到寄存器中,或将寄存器数据读取到内存里。即存储器和寄存器数据交流

主要分三类:

  1. 单寄存器读写指令:单寄存器与内存数据传输
  2. 多寄存器内存访问指令:连续的内存数据读到单寄存器;或是多个内存器数据读到单个内存块中
  3. 数据交换指令:内存寄存器字交换,字节交换

LDR 指令

LDR - 寄存器和内存数据通信

  • LDR 指令的格式: LDR{条件} 目的寄存器 <存储器地址>

  • 作用:将 存储器地址 所指地址处连续的 4 个字节(1 个字)的数据传送到目的寄存器中。

LDR R0,[R1] ;将存储器地址为 R1 的字数据读入寄存器 R0。

LDR R0,[R1,R2] ;将存储器地址为 R1+R2 的字数据读入寄存器 R0。

LDR R0,[R1,#8] ;将存储器地址为 R1+8 的字数据读入寄存器 R0。

LDR R0,[R1],R2 ;将存储器地址为 R1 的字数据读入寄存器 R0,并将 R1+R2 的值存入 R1。

LDR R0,[R1],#8 ;将存储器地址为 R1 的字数据读入寄存器 R0,并将 R1+8 的值存入 R1。

LDR R0,[R1,R2]! ;将存储器地址为 R1+R2 的字数据读入寄存器 R0,并将 R1+R2 的值存入 R1。

LDR R0,[R1,LSL #3] ;将存储器地址为 R1*8 的字数据读入寄存器 R0。

LDR R0,[R1,R2,LSL #2] ;将存储器地址为 R1+R2*4 的字数据读入寄存器 R0。

LDR R0,[R1,,R2,LSL #2]! ;将存储器地址为 R1+R24 的字数据读入寄存器 R0,并将 R1+R24 的值存入 R1。

LDR R0,[R1],R2,LSL #2 ;将存储器地址为 R1 的字数据读入寄存器 R0,并将 R1+R2*4 的值存入 R1。

LDR R0,Label ;Label 为程序标号,Label 必须是当前指令的-4~4KB 范围内。

LDRB 指令(LDR + B)

  • LDRB 指令的格式为:LDR{条件}B 目的寄存器,<存储器地址>

  • LDRB 指令的作用:LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。该指令通常用于从存储器中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器`PC``作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。```

指令示例:

LDRB R0,[R1] ;将存储器地址为 R1 的字节数据读入寄存器 R0,并将 R0 的高 24 位清零。

LDRB R0,[R1,#8] ;将存储器地址为 R1 + 8 的字节数据读入寄存器 R0,并将 R0 的高 24 位清零。

LDRH 指令

LDRH 指令的格式为:

LDR{条件}H 目的寄存器,<存储器地址>

LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。该指令通常用于从存储器中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器`PC``作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。```

指令示例:

LDRH R0,[R1] ;将存储器地址为 R1 的半字数据读入寄存器 R0,并将 R0 的高 16 位清零。

LDRH R0,[R1,#8] ;将存储器地址为 R1 + 8 的半字数据读入寄存器 R0,并将 R0 的高 16 位清零。

LDRH R0,[R1,R2] ;将存储器地址为 R1 + R2 的半字数据读入寄存器 R0,并将 R0 的高 16 位清零。

STR 指令

寄存器和内存之间数据通信

  • STR 指令的格式为:STR{条件} 源寄存器,<存储器地址>

  • 作用:STR 指令用于从源寄存器中将一个 32 位的数据传送到存储器中。(四个字节)

STR R0,[R1],#8 ;将 R0 中的字数据写入以 R1 为地址的存储器中,并将新地址 R1 + 8 写入 R1。
STR R0,[R1,#8] ;将 R0 中的字数据写入以 R1 + 8 为地址的存储器中。

STR r1, [r0] ;将 r1 寄存器的值,传送到地址值为 r0 的(存储器)内存中

STRB(STR + B)

  • 字节,将源寄存器数据输入到寄存器地址的内存中

多寄存器内存访问指令

  • LDM 和 STM
模式 说明 模式 说明
IA - Increase After 每次传送后地址加 4 FD 满递减堆栈
IB - Increase Before 每次传送前地址加 4 ED 空递减堆栈
DA - Decrease After 每次传送后地址减 4 FA 满递增堆栈
DB - Decrease Before 每次传送前地址减 4 EA 空递增堆栈
数据块传送操作 堆栈操作
  • LDMIA R1,{R2,R3,R4}:以 R1 寄存器为基址,将 R1→ R2 ,然后 R1+4 读一个新的数据,写到 R3,再加 4 读新值写到 R4;

下图是使用了小端地址,四个字节一组;

LDMIA-实例

首先将 R1 寄存器地址首地址设为【0x0000000F】(从后往前读)

那么 R1 中的数据为:E800E800E8,该数据会给到 R2

R1+4,挪到第二行,此时的数据为:E7FF0010,给到 R3

再+4,数据位 E800E800,给到 R4

表面上看起来是从寄存器中给数据到存储器,但实质其实是从存储器读数据到寄存器!

  • 首先调用 LDMIB R1,{R2-R9}进行写,然后 SIMIA R1,{R2-R9}:将 R2 地址所在的值读到 R1,R1 地址+4,读 R2….

STEP 1

STEP 2

数据交换指令

数据交换指令

swp指令

MOV R1,#0x0f

MOV R2,#0x12

SWP R0,R1,[R2]:

  1. 把 R2 对应的 0x12 中数据:FFE71011 写到 R0 里,观察到 R0 寄存器从 0x00000000 → 0XFFE71011;
  2. 把 R1 的内容 0x0f,写到 R2 对应的四个字节块中,从 10 00 FF E7 → FF 00 00 00

SUB 指令

  • SUB 指令的格式为: SUB{条件}{S} 目的寄存器,操作数 1,操作数 2

  • 作用:SUB 指令用于把操作数 1 减去操作数 2,并将结果存放到目的寄存器中。

    • 操作数 1 应是一个寄存器
    • 操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。

    该指令可用于有符号数或无符号数的减法运算

SUB指令

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
	AREA example,CODE,READONLY
ENTRY
start
mov r11,#9
outer
str r11,[sp]
ldr r0,=buf
ldr r12,[sp]
cmp r12,#0
beq L1
inner
ldrb r1,[r0]
ldrb r2,[r0,#1]
cmp r1,r2
swpccb r2,r2,[r0] ; 将r2 的内容与r0 指向的存储单元的内容进行交换
strccb r1,[r0,#1] ; 将r1内容放到r0 + 1寄存器处
add r0,r0,#1 ; r0 + 1
subs r12,r12,#1 ;r12 - 1
bne inner ; 回到内层循环
subs r11,r11,#1 ;r11 - 1
bne outer ;回到
L1
mov r0,#0x18
ldr r1,=0x20026
swi 0xAB
buf
DCB 1,10,8,2,5,4,3,9,7,6
END

跳转指令

跳转,相当于函数调用,goto,break 语句…

B 指令 - 理解成 goto

ARM 汇编伪指令

ARM-伪指令1

混合编程

C 语言 + ARM 汇编

主要分为三种:

  1. C/C++ 嵌入 汇编
  2. 汇编调用 C/C++
  3. 汇编,C/C++ 程序间相互调用

C - 嵌入汇编

  • 格式:__asm[volatile]{instruction [; instruction]}

  • 【myMain.c】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void my_strcpy(char *src, char *dest)
{
char ch;
__asm {
loop:
LDRB ch,[src],#1
STRB ch,[dest],#1
CMP ch,#0
BNE loop

}

}

int main()
{
char *a = "hello sundy";
char b[64];
my_strcpy(a,b);
}
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
29
30
31
32
33
34
35
36
37
38
39
#include<stdio.h>
void score_sum(int arr[], float* sumArr) {
__asm {
mov r4, #4;
mov r2, #0;
start:
ldrb r3, [r0];
add r2, r2, r3;
add r0, r0, #4;
sub r4, r4, #1;
cmp r4, #0;
bne start;

str r2, [r1];
}

}

int main() {

int arr[5][4] = {
{3,1,5,4},
{3,4,5,4},
{2,2,5,4},
{3,2,5,4},
{3,2,5,4}
};

float sumArr[4];

int i;

for (i = 0; i < 4; i++) {
score_sum(arr[i], &sumArr[i]);
float avg = sumArr[i] / 4;
printf("avg is = %.2f\n", avg);
}
return 0;
}

C - 调用汇编

  • 调用汇编的步骤:
  1. 汇编 export
  2. c 语言定义 extern function
  3. c 语言使用
  • 具体原理

C 语言的汇编之间的参数传递,以【R0-R3】作为媒介,like:R0 传递第一个参数,R1 传递第二个…and so on,如果多余【4】个,就需要借助【栈】来辅助完成,函数的返回值通过【R0】传递,这是规定。

编程这东西理论固然重要,还是要看实践:

以下两个文件放在同一个 Project 里面运行

  • 【myArm.S】
1
2
3
4
5
6
7
8
9
10
11
12
    AREA myARM,CODE,READONLY
EXPORT my_strcpy ;这里进行了导入

my_strcpy ;具体内容如下
loop
LDRB R4,[R0],#1
CMP R4,#0
BEQ over 
STRB R4,[R1],#1
B loop
over
END
  • 【myMain.c】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
void my_strcpy(char *src, char *dest)
{
char ch;
__asm {
loop:
LDRB ch,[src],#1
STRB ch,[dest],#1
CMP ch,#0
BNE loop

}

}
*/
extern void my_strcpy(char *src,char *dest);
int main()
{
char *a = "hello sundy";
char b[64];
my_strcpy(a,b);
}

执行结果

汇编 - 调用 C【核心】

编程步骤

  1. C 语言去实现函数
  2. 汇编文件通过【import】函数名
  3. BL 函数名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	AREA myARM,CODE,READONLY
EXPORT my_strcpy ;这里进行了导入
IMPORT cFunc
ENTRY
start
MOV R0,#1
MOV R1,#2
MOV R2,#3
BL cFunc
MOV R4,R0

my_strcpy ;具体内容如下
loop
LDRB R4,[R0],#1
CMP R4,#0
BEQ over
STRB R4,[R1],#1
B loop
over

END
1
2
3
4
int cFunc(int a, int b, int c)
{
return a + b + c;
}

混合

考试相关

考试

以下多

一维数组和二维数组按照某种规律求和

一维数组求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	AREA t1,CODE,READONLY   ;一维数组求和
ENTRY
start
LDR R0,=arr ;RO数组第一个元素地址
MOV R1,#0 ;R1为sum
MOV R2,#10 ;数字个数
loop1
LDR R3,[R0],#4 ;读取元素,并将指针后移
ADD R1,R1,R3 ;累加及写回
SUB R2,R2,#1 ;计数器减1
CMP R2,#0
BNE loop1
arr DCD 1,2,3,4,5,6,7,8,9,10
end

二维数组求和

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
29
30
31
32
33
34
	AREA t11,CODE,READONLY  ;二维数组求和 先求每行的和,再求总和 2*5
ENTRY
start
LDR R0,=arr ;数组第一个地址
LDR R1,=sum ;结果数组
MOV R2,#0 ;总和
MOV R3,#2 ;行
MOV R4,#5 ;列
MOV R5,#0 ;临时变量
loop1
LDR R6,[R0],#4 ;读取+移动指针
ADD R5,R5,R6 ;
SUB R4,R4,#1 ;列减少1
CMP R4,#0
BNE loop1
;如果R4等于0 则说明这一行遍历完成
STR R5,[R1],#4 ;写回
SUB R3,R3,#1 ;行减少1
;重置
MOV R4,#5 ;列
MOV R5,#0 ;临时变量
CMP R3,#0
BNE loop1 ;不为0则 处理下一行
MOV R3,#2 ;行
LDR R1,=sum ;结果数组
getSum
;汇总结果
LDR R5,[R1],#4
ADD R2,R2,R5 ;汇总
SUB R3,R3,#1 ;行减少1
CMP R3,#0
BNE getSum
arr DCD 1,2,3,4,5,6,7,8,9,10
sum DCD 0,0

求两个数的最大公约数

辗转相减法(更相减损术):

第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用 2 约简;若不是则执行第二步。

第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数,继续这个操作,直到所得的减数和差相等为止。第一步中约掉的若干个 2 的积与第二步中等数的乘积就是所求的最大公约数。

1
2
3
4
5
6
7
8
9
10
11
12
	AREA Test,CODE,READONLY
ENTRY
START
MOV R1,#78 ;78放到R1
MOV R2,#64 ;64放到R2
LOOP
CMP R1,R2 ;比较两个数大小
SUBHI R1,R1,R2;R1大,则R1 - R2,结果放R1
SUBLS R2,R2,R1;R2大,则R2 - R1,结果放R2
CMP R1,R2;再比R1和R2/比较被减数和差
BNE LOOP;如果为0,跳出循环,此时的R1 = R2 = 最大公约数
END

求两个数的最小公倍数

通过求出最大公约数,用两个数的乘积去除最大公约数就可以得到最小公倍数,由于 ARM 汇编不支持除法,因此用循环减法进行模拟除法操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	area getlcm, code, readonly
entry

start
mov r1, #50 ; r1 = 50
mov r2, #30 ; r2 = 30
mul r3, r1, r2 ; r3 = r1 * r2
loop
cmp r1, r2
subhi r1, r1, r2 ; 若r1 > r2, r1 = r1 - r2
subls r2, r2, r1 ; 若r1 < r2, r2 = r2 - r1
cmp r1, r2
bne loop ; 若 r1 = r2, 退出循环
lcm ; lcm = a * b / gcd(a, b)
sub r3, r3, r1 ; arm 没有出发操作,因此使用模拟除法
add r4, r4, #1
cmp r3, #0
bgt lcm
stop
b stop
end

对一维数组进行排序

【选择排序】

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
29
    AREA Sort,CODE,READONLY
ENTRY
START
MOV R0,#0
LDR R0,=addr ;RO存放首地址
LDR R5,=9 ;数组长度 - 1
ADD R4,R0,R5 ; 最后一个数地址
ADD R6,R4,#1 ; 最后一个数字下一个存储单元地址
outer
ADD R1,R0,#1 ; 初始化j为i+1
inner
LDRB R2,[R0] ;取a[i]到R2
LDRB R3,[R1] ;取a[i + 1]到R3

CMP R3,R2 ;比较a[i + 1]和a[i]
STRLTB R3,[R0]; 如果后一个数小,则交换位置 → 小的放前面,大的放后面,注意这里源用的是R0,下面用的R1
STRLTB R2,[R1] ;

ADD R1,R1,#1 ;j++
CMP R1,R6 ;与最后一个数字的下一个存储单元地址进行比较
BLT inner ;小于的话继续内层循环

ADD R0,R0,#1 ; i++
CMP R0,R4 ;比较与最后一个数地址
BLT outer ;
MOV R7,#0 ;
addr
DCB 10,9,8,7,6,5,4,3,1,2
END

【冒泡排序 - mfy】

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
29
30
31
; 冒泡排序
area sort, code, readonly
entry
start
mov R4, #0 ; R4=0
ldr R6, =src ; R6=src ,此时R6指向数组的开头
add R6, R6, #len ; R6=R6+len ,此时R6指向数组的末尾
outer
ldr R1, =src ; 开始外围循环
inner
ldr R2, [R1] ; 获取地址R1中的数字 R2=a[i]
ldr R3, [R1, #4] ; 获取R1的下一个地址的数字 R3=a[i+1]
cmp R2, R3 ; 根据下一条指令来对R2,R3进行比较
strgt R3, [R1] ; 如果R2>R3:
strgt R2, [R1, #4] ; 交换R2,R3地址
add R1, R1, #4 ; 地址指针R1后移一位
cmp R1, R6 ; 根据下一条指令来对R1,R6进行比较(R1: 当前位置, R6: 数组末尾)
blt inner ; blt: 小于(有符号数)如果R1<R6, 则跳转至inner处继续循环执行inner

add R4, R4, #4 ; R4=R4+4 全局指针后移一位
cmp R4, #len ; 根据下一条指令来对R4,len进行比较(R4: 当前位置, len: 数组长度)
suble R6, R6, #4 ; 如果没有走到末尾
ble outer ; 跳转至outer处继续循环执行outer
stop
b stop

area array, data, readwrite
src dcd 2, 5, 3, 1, 11, 10, 20
len equ 7 * 4

end

【hpg - 手写】

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
29
30
	AREA BUBBLE,CODE,READONLY
ENTRY
start
LDR R0,=arr
MOV R1,#0
LDR R2,=arr
ADD R2,R2,#len
outer
LDR R0,=arr
inner
LDR R3,[R0]
LDR R4,[R0,#4]
CMP R3,R4
STRGT R4,[R0]
STRGT R3,[R0,#4]

ADD R0,R0,#4
CMP R0,R2
BLT inner

ADD R1,R1,#4
CMP R1,#len
SUBLE R2,R2,#4
BLE outer
stop
b stop
area array, data, readwrite
arr DCD 10,8,9,7,6,5,4,3,2,1
len equ 10 * 4
END

求一维数组中的最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	AREA t5,CODE,READONLY
ENTRY
start
LDR R0,=arr ;R0=数组首地址
MOV R1,#9 ;R1=数字个数-1
LDR R2,[R0],#4 ;R2为当前最大值
loop1
LDR R3,[R0],#4 ;待比较的值
CMP R2,R3
MOVLT R2,R3 ;如果R2<R3 则R2= R3
SUB R1,R1,#1 ;循环次数-1
CMP R1,#0 ;R1=0 结束循环
BEQ end
B loop1
end

arr DCD 1,2,3,4,5,6,7,8,9,10

求一维数组中的最小值

与上面的区别就在于:MOVLT → MOVGT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	AREA t5,CODE,READONLY
ENTRY
start
LDR R0,=arr ;R0=数组首地址
MOV R1,#9 ;R1=数字个数-1
LDR R2,[R0],#4 ;R2为当前最小值
loop1
LDR R3,[R0],#4 ;待比较的值
CMP R2,R3
MOVGT R2,R3 ;如果R2>R3 则R2= R3
SUB R1,R1,#1 ;循环次数-1
CMP R1,#0 ;R1=0 结束循环
BEQ end
B loop1
end

arr DCD 1,2,3,4,5,6,7,0,9,10

字符串的复制

1
2
3
4
5
6
7
8
9
10
11
12
13
	AREA t7,CODE,READONLY
ENTRY
start
LDR R0,=src ;R0为源数组首地址
LDR R1, =des ;R1为目的数组首地址
loop1
LDRB R2,[R0],#1
STRB R2,[R1],#1
CMP R2,#0
BNE loop
end
src DCB "abcdefg\0"
des DCB ""