搞不懂为什么学校开嵌入式用汇编……

第一次实验:寻址方式使用练习

https://pic.lingjun.life/Qexo/2023/4/image_805a5465eaf2b8e0623b4e7356985c5c.png

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
N EQU 9
AREA Init,CODE,READONLY
ENTRY
START
LDR R0,=N
MOV R2,R0
MOV R0,#0
MOV R1,#0
LOOP
CMP R1,R2
BHI ADD_END
ADD R0,R0,R1
ADD R1,R1,#1
B LOOP
ADD_END
LDR R4,=0X30000000
STR R0,[R4]
MOV R1,#11
STR R1,[R4,#4]
MOV R2,R1,LSL #2
MOV R3,R1,LSR #2
LDR SP,=0X3200000C
STMFD SP!,{R1-R3}
LDMFD SP!,{R5-R7}
P_END
B P_END
END

第二次实验:汇编指令使用练习

一、实验目的:

1、 寻址方式巩固练习

2、 练习汇编指令

3、 掌握指令的条件

二、实验要求:

1、认真阅读电子版教材;

2、按照课件学习汇编指令;

3、书写实验报告。

三、实验原理

ARM指令助记符格式如下:

<opcode> {<cond>} {S} <Rd>, <Rn>, <shift\_op2>

< >内项目是必需的, { }内项目是可选的

:操作码域,指令编码的助记符。

② {}:条件码域,指令允许执行的条件助记符。

③ {S}:指令后加S,在指令完毕后会自动更新CPSR中的条件码标志位的值。

:目的操作数,总是一个寄存器。

:存放第一个操作数的寄存器。

⑥ <shift_op2> :第二个操作数,可是寄存器也可是立即数等。

指令助记符编译后的机器码格式如下:

https://pic.lingjun.life/Qexo/2023/4/image_358de116abbc0ae5cee67bbcaa0036ac.png

1、条件码

几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件地执行。当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示如下表所示。

https://pic.lingjun.life/Qexo/2023/4/image_15b9c3204d3be60204e8a38c3ad1f0a8.png

2、指令中第2个操作数<shift_op2>

第二个操作数,可以是一个寄存器,也可以是一个立即数,还可以是一个寄存器与移位操作组成,移位操作包括如下5种类型。

LSL:逻辑左移(Logical Shift Left)。寄存器中字的低端空出的位补0。

LSR:逻辑右移(Logical Shift Right)。寄存器中字的高端空出的位补0。

ASR:算术右移(Arithmetic Shift Right)。算术移位的对象是带符号数。在移位过程中必须保持操作数的符号不变。若源操作数为正数,则字的高端空出的位补0;若源操作数为负数,则字的高端空出的位补1。

ROR:循环右移(ROtate Right)。从字的最低端移出的位填入字的高端空出的位。

RRX:扩展为1的循环右移(Rotate Right eXtended by 1 place)。操作数右移1位,空位(位[31])用原C标志填充。

3、加载/存储指令

格式:LDM(或STM){类型} 基址寄存器{!},寄存器列表{∧}

功能:LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。其中,{类型}为以下几种情况。

IA:每次传送后地址加4。

IB:每次传送前地址加4。

DA:每次传送后地址减4。

DB:每次传送前地址减4。

FD:满递减堆栈。

ED:空递减堆栈。

FA:满递增堆栈。

EA:空递增堆栈。

四、实验内容

1、指令条件实验练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 AREA EXAMPLE,CODE,READONLY
ENTRY
start
MOV R0,#10
MOV R1,#3
ADDEQ R2,R0,R1,LSR#1
ADDNE R3,R0,R1,LSR#1
ADDCS R4,R0,R1,LSR#1
ADDCC R5,R0,R1,LSR#1
ADDMI R6,R0,R1,LSR#1
ADDPL R7,R0,R1,LSR#1
ADDVS R8,R0,R1,LSR#1
ADDVC R9,R0,R1,LSR#1
ADDHI R10,R0,R1,LSR#1
ADDLS R11,R0,R1,LSR#1
ADDGE R12,R0,R1,LSR#1
ADDLT R13,R0,R1,LSR#1
ADDGT R14,R0,R1,LSR#1
ADDLE R0,R0,R1,LSR#1
B start
END
2、加载/存储指令

https://pic.lingjun.life/Qexo/2023/4/image_4f612a7b88fef2e47595a40111ff51b9.png

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 example,CODE,READONLY
ENTRY
START
LDR R0,=0X30000000
MOV R1,#4
LDR R3,=0X12345678
LDR R4,=0X9ABCDEF0
STR R3,[R0]
STR R4,[R0,R1]
STR R3,[R0,#R8]
STR R4,[R0],#16
STR R3,[R0,#-4]
STR R4,[R0,#16]!
STR R3,[R0],#-16
STR R4,[R0,R1,LSL#1]
STRB R3,[R0]
STRH R4,[R0,#4]
LDRH R5,[R0,#-16]
LDRH R6,[R0,#-16]
LDR R7,[R0,#-8]!
LDMIA R0,{R8,R9}
LDMIB R0,{R10,R11}
LDMDA R0,{R11,R12}
LDMDB R0,{R13,R14}
LDR R13,=0X20000000
STMFA SP,{R0-R4}
STMEA SP!,{R0-R4}
STMFD SP,{R0,R1}
STMED SP,{R0,R1}
LDMEA R13!,{R0-R3,R5}
B START
END


3、数据处理指令

https://pic.lingjun.life/Qexo/2023/4/image_7a4c441de1c86e1427f344a26a6d6b5b.png

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
40
41
42
 area example,code,readonly
entry
start
movs r0,#0
mov r0,#16
mov r1,#4
mvn r2,#4
add r3,r1,r0
add r4,r2,r0
adds r3,r2,r0
sub r4,r1,#240
subs r3,r1,#240
rsb r4,r0,#18
cmp r0,#10
adchi r11,r0,r3
rsccss r5,r2,#0x4
and r11,r2,#0x4
tst r2,#0x4
orreq r7,r0,r5
eor r8,r0,r3,ror r1
eors r5,r7,#0x15
teq r1,#4
bic r11,r8,#0xff
cmn r2,r3
movs r13,r3,asr r1
movs r12,r3,lsr r1
sbcs r6,r2,r3
mul r3,r0,r1
mla r4,r0,r1,r3
muls r5,r0,r2
umulllts r6,r7,r0,r2
umull r8,r9,r0,r2
umlals r8,r9,r0,r2
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0x11
msr cpsr_c,r0
bic r0,r0,#0x1f
orr r0,r0,#0x13
msr cpsr_c,r0
b start
end

第三次试验汇编程序设计练习

一、实验目的:

1、 使用ADS调试汇编程序

2、 学习汇编程序设计

二、实验要求:

1、认真阅读电子版教材;

2、按照课件学习汇编程序设计;

3、书写实验报告。

三、实验原理

上次实验中在调试过程中可利用反汇编查看编译后的对应的机器语言编码。

https://pic.lingjun.life/Qexo/2023/4/图片_d61a31856799ef6c8774fb79bce2df20.png

下图中的Ro Base的值就是设置编译后机器码存放的只读存储器的首地址。

https://pic.lingjun.life/Qexo/2023/4/图片_d22b1cde5b4d32186f1b6e3b67e55108.png

ARM指令的机器语言编码由32位二进制代码组成,这些二进制代码的含义如下图所示:

https://pic.lingjun.life/Qexo/2023/4/图片_1d52df8b0d2f9eaa9cbf53b52ea6d66b.png

数据处理的指令的opcode([24:21])域二进制代码表示的含义如下图所示,其中s域表示要不要修改CPSR的标志位,Rn代表目录寄存器编号,Rd代表着第一个操作数的寄存器编号,operand2代表第2个操作数。

https://pic.lingjun.life/Qexo/2023/4/图片_75ceaf42e121363b093be21a342e8ac8.png

2、汇编语言

汇编语言用助记符书写,助记符由指令助记符、伪操作助记符和伪指令助记符组成。指令助记符有一一对应的机器编码,伪指令助记符由编译器编译成机器编码,而伪操作助记符只是起到控制编译的作用,并不对应的机器编码。有时把伪操作和伪指令统称为伪指令。

ADS中常用的伪指令

https://pic.lingjun.life/Qexo/2023/4/图片_d8d0cd2f3d6e0395b84b0bc7ba2051c6.png

https://pic.lingjun.life/Qexo/2023/4/图片_cd15c5ae68e25efc041284446bed2edf.png

https://pic.lingjun.life/Qexo/2023/4/图片_7709b49cf8c9a35f78c045f851d239c8.png

https://pic.lingjun.life/Qexo/2023/4/图片_682421b488dfea9775b247333ab4de6c.png

https://pic.lingjun.life/Qexo/2023/4/图片_57e4a2494983b6162df402b0c294b2e3.png

上次实验中在调试过程中可利用反汇编查看对应的机器码。例如“LDR R0,=N” 编译后用“mov r0,#9”替代;“LDR R4,=0X30000000”编译后用“ mov r4,#0xc0, 10 ”替代;“LDR SP,=0X3200000C ”编译后用“ ldr r13,0x0000804c ”替代。但程序中的AREA、END等伪操作找不到有对应的机器码。

https://pic.lingjun.life/Qexo/2023/4/图片_2690d51278eba1495aab030aba1342d2.png

3、汇编语言程序结构

按程序执行的流程可分为顺序程序、分支程序、循环程序和子程序等类型。任何复杂的程序结构可看作这些基本结构的组合。例如实验6的例程就属于顺序程序结构。

https://pic.lingjun.life/Qexo/2023/4/图片_feec7a0989c48d256bbdc7e296d1f36e.png

ARM汇编中大部分的指令都支持条件执行,因此类似C语言中的if-else分支很容易实现。

例: CMP R1,#3 ; 比较R1和#3

1
ADDHI R0,R0,R1     ; if R1>3 then R0=R0+R1

ADDLS R0,R0,#3 ; if R1<3 then R0=R0+3

B、BL可以条件执行,从而构成复杂的分支架构。

例:

CMP R1,#3 ; 比较R1和#3

BHI END ; if R1>3 then END

ADD R0,R0,#3 ; R0=R0+3

END

用预先设定的标号与B、BL结合可以设计各种循环结构。

例:

LOOP

ADD R0,R0,R1 ; R0=R0+R1

CMP R0,#3 ; 比较R0和#3

BLS LOOP ; if R0<3 then 跳转到LOOP 循环

在ARM汇编语言程序中,子程序的调用一般是通过BL指令来实现的。

格式:BL 子程序名

该指令在执行时完成如下操作:将子程序的返回地址存放在连接寄存器LR中,同时将程序计数器PC指向子程序的入口点,当子程序执行完毕需要返回调用处时,只需要将存放在LR中的返回地址重新复制给程序计数器PC即可。在调用子程序的同时,也可以完成参数的传递和从子程序返回运算的结果,通常可以使用寄存器R0~R3完成。

例: 调用子程序。子程序doadd完成加法运算,操作数放在R0和R1寄存器中,结果放在R0中。

1
2
3
4
5
6
7
8
9
10
11
12
  ENTRY
Start
MOV R0, #10 ;R0设置输入参数
MOV R1, #3 ;R1设置输入参数
BL doadd ;调用子程序doadd
Stop
B stop
doadd
ADD r0, r0, r1 ;子程序实体
MOV pc,lr;从子程序中返回
END

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
【例】  实现1+2+……+N。
N EQU 5 ; 常量的定义
AREA Example,CODE,READONLY ; 定义段名属性等
ENTRY ; 程序入口
CODE32 ; ARM代码
START ; 行标定义
LDR R0,=N ; R0赋值
MOV R2,R0 ; R2充当计数器
MOV R0,#0 ; R0←0
MOV R1,#0 ; R1←0
LOOP ; 行标
CMP R1,R2 ; 比较R1 R2
BHI ADD_END ; 如果R1>R2 跳转到 ADD_END
ADD R0,R0,R1 ; R0←R0+R1
ADD R1,R1,#1 ; R1←R1+1
B LOOP ; 无条件跳转至LOOP,循环的实现
ADD_END ; 行标定义
B ADD_END ; 无条件跳转ADD_END
END ; 代码结束

四、实验内容

1、把实验原理中的程序补全后在ADS中验证;

2、验证伪指令;

3、打开C:\Program Files\ARM\ADSv1_2\Examples\asm文件夹,查看其中的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
【例1】
AREA ARMex, CODE, READONLY
//Name this block of code ARMex
ENTRY //Mark first instruction to execute
Start:
MOV r0, #10 ; Set up parameters
MOV r1, #3
ADD r0, r0, r1 ; r0 = r0 + r1
Stop:
MOV r0, #0x18 //angel_SWIreason_ReportException
LDR r1, =0x20026 //ADP_Stopped_ApplicationExit
SWI 0x123456 //ARM semihosting (formerly SWI)
END //Mark end of file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【例2
AREA subrout, CODE, READONLY ; Name this block of code
ENTRY ; Mark first instruction to execute
start
MOV r0, #10 ; Set up parameters
MOV r1, #3
BL doadd ; Call subroutine
Stop
MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SWI 0x123456 ; ARM semihosting (formerly SWI)
doadd ADD r0, r0, r1 ; Subroutine code
BX lr ; Return from subroutine
END ; Mark end of file

第四次实验:ARM C程序设计

一、实验目的:

1、 学习ARM的C语言程序编写方法

2、 学会使用ADS软件调试C语言程序

二、实验要求:

1、认真阅读电子版教材;

2、按照课件学习学习在ADS中调试C程序;

3、书写实验报告。

三、实验原理

映像文件(image):是指一个可执行文件,在执行的时候被加载到处理器中。一个映像文件有多个线程,是ELF(Exccutable and linking format)格式的。

段(Scction):描述映像文件的代码或数据块。

RO段包括程序中的指令和常量,属性是readonly
RW段包括程序中的已初始化变量 ,属性是read/write
ZI段是程序中的以0初始化的变量 。

ADS1.2手册给出的默认存储器映像如下图所示,其中heap为堆,stack为堆栈:

https://pic.lingjun.life/Qexo/2023/4/图片_5809bc9633511cdec616cede7cdb962a.png

由于程序一般放在只读存储器中,但只读存储器只能读不能写,而且读取速度慢,故程序在运行时往往需要把只读存储器的程序和数据加载到读写存储器中,并设置堆区及堆栈区,以便存放程序运行时产生的数据。

ADS在运行时,会自动加系统定义的main函数,该函数主要完成程序和数据的复制、以及产生ZI;然后系统建立堆和堆栈、初始化库函数后才进入用户编写的主函数。

https://pic.lingjun.life/Qexo/2023/4/图片_04aa7d1b98cd74762842ea0df6002969.png

为了能系统顺利进入用户编写的主函数,需要用汇编语言编写启动引导代码(有些软件如keil可针对不同芯片可以自动加载启动代码),当然用户代码的主函数也可以不用main命名,有些书为了加以区分系统主函数,以Main命名,本次实验是以ledmain命名。

1
2
3
4
5
6
7
IMPORT      ledmain  
AREA init, CODE, READONLY
global start
start
bl ledmain
END

IMPORT伪操作告诉编译器ledmain在别的文件中定义,在此文件中引用,用global 伪操作表示start是一个全局标号。

本次实验是用C语言编写控制led小灯的程序,硬件电路连接如下图, s3c2410的GPF端口的7:4位接LED小灯,arm处理器相应端口输出低电平,LED灯亮,高电平灯灭。首先需要将这个端口配置成输出端口,然后通过配置去掉内部的上拉阻,最后通过给端口送0或1达到亮灭的目的。

https://pic.lingjun.life/Qexo/2023/4/图片_5753e0f625db1c8bef567e68ea17d41f.png

s3c2410的GPF端口的

配置寄存器的地址为:0x56000050

数据寄存器地址为:0x56000054

上拉电阻控制寄存器地址为:0x56000058

四、实验内容

1、创建工程项目

2、添加设计文件

(1)先用编写如下的汇编语言文件,并起名为init.s并添加到工程中。

1
2
3
4
5
6
7
8
    IMPORT      ledmain
AREA init, CODE, READONLY
global start
CODE32
start
bl ledmain
END

(2)编写C语言源程序

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
#define rGPFCON (*(volatile unsigned*)0x56000050)
#define rGPFDAT (*(volatile unsigned*)0x56000054)
#define rGPFUP (*(volatile unsigned*)0x56000058)

void portinit(void){
rGPFCON = 0x55aa;
rGPFUP = 0xff;
}

void ledon(){
int nout;
nout = 0xF0;
rGPFDAT = nout & 0x70;
rGPFDAT = nout & 0x30;
rGPFDAT = nout & 0x10;
rGPFDAT = nout & 0x00;
}
void ledoff(){
int nout;
nout=0;
rGPFDAT=0;
rGPFDAT = nout | 0x80;
rGPFDAT = nout | 0x40;
rGPFDAT = nout | 0x20;
rGPFDAT = nout | 0x10;
}
void ledonoff(void){
rGPFDAT=0;
rGPFDAT=0xF0;
}

void ledmain(void){
portinit();
while(1){
ledon();
ledoff();
ledonoff();
}
}

3、编译和链接前设置

部分设置可参考实验六。

4、编译和链接项目工程

https://pic.lingjun.life/Qexo/2023/4/图片_4a7f40d45ea108780f7ec4b7408e50d4.png

5、调试

打开反汇编,可以看到存储器的0x8000处的指令是bl ledmain,并且给此存储地址起名为start。程序代码地址与编译器设置有关。

https://pic.lingjun.life/Qexo/2023/4/图片_66aea7f67ee7a4b9e5aa2648ef185eb4.png

存储器分配情况的:

0x8004-0x8018:portinit子程序

0x801c-0x8040:ledon子程序

0x8044-0x808c:ledoff子程序

0x8090-0x80a4:ledonoff子程序

0x80a8-0x80bc:ledmain程序

用单步运行,可以看到c程序与对应汇编程序之间的关系以及GPF端口数据变化情况。

https://pic.lingjun.life/Qexo/2023/4/图片_8224cc1628d147a1719429a38305c72f.png

五、实验报告要求

1、以报告的形式总结实验过程;

2、总结如何使用ADS调试C程序。