搞不懂为什么学校开嵌入式用汇编……
第一次实验:寻址方式使用练习 
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,  MOV R1, LOOP  CMP R1,R2  BHI ADD_END  ADD R0,R0,R1  ADD R1,R1,  B LOOP ADD_END  LDR R4,=0X30000000  STR R0,[R4]  MOV R1,  STR R1,[R4,  MOV R2,R1,LSL   MOV R3,R1,LSR   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> :第二个操作数,可是寄存器也可是立即数等。
指令助记符编译后的机器码格式如下:
1、条件码
几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件地执行。当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示如下表所示。
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、加载/存储指令 
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、数据处理指令 
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、书写实验报告。
三、实验原理 
上次实验中在调试过程中可利用反汇编查看编译后的对应的机器语言编码。
下图中的Ro Base的值就是设置编译后机器码存放的只读存储器的首地址。
ARM指令的机器语言编码由32位二进制代码组成,这些二进制代码的含义如下图所示:
数据处理的指令的opcode([24:21])域二进制代码表示的含义如下图所示,其中s域表示要不要修改CPSR的标志位,Rn代表目录寄存器编号,Rd代表着第一个操作数的寄存器编号,operand2代表第2个操作数。
2、汇编语言
汇编语言用助记符书写,助记符由指令助记符、伪操作助记符和伪指令助记符组成。指令助记符有一一对应的机器编码,伪指令助记符由编译器编译成机器编码,而伪操作助记符只是起到控制编译的作用,并不对应的机器编码。有时把伪操作和伪指令统称为伪指令。
ADS中常用的伪指令
上次实验中在调试过程中可利用反汇编查看对应的机器码。例如“LDR R0,=N” 编译后用“mov r0,#9”替代;“LDR R4,=0X30000000”编译后用“ mov  r4,#0xc0, 10 ”替代;“LDR SP,=0X3200000C ”编译后用“ ldr      r13,0x0000804c ”替代。但程序中的AREA、END等伪操作找不到有对应的机器码。
3、汇编语言程序结构
按程序执行的流程可分为顺序程序、分支程序、循环程序和子程序等类型。任何复杂的程序结构可看作这些基本结构的组合。例如实验6的例程就属于顺序程序结构。
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为堆栈:
由于程序一般放在只读存储器中,但只读存储器只能读不能写,而且读取速度慢,故程序在运行时往往需要把只读存储器的程序和数据加载到读写存储器中,并设置堆区及堆栈区,以便存放程序运行时产生的数据。
ADS在运行时,会自动加系统定义的main函数,该函数主要完成程序和数据的复制、以及产生ZI;然后系统建立堆和堆栈、初始化库函数后才进入用户编写的主函数。
为了能系统顺利进入用户编写的主函数,需要用汇编语言编写启动引导代码(有些软件如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达到亮灭的目的。
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、编译和链接项目工程
5、调试
打开反汇编,可以看到存储器的0x8000处的指令是bl ledmain,并且给此存储地址起名为start。程序代码地址与编译器设置有关。
存储器分配情况的:
0x8004-0x8018:portinit子程序
0x801c-0x8040:ledon子程序
0x8044-0x808c:ledoff子程序
0x8090-0x80a4:ledonoff子程序
0x80a8-0x80bc:ledmain程序
用单步运行,可以看到c程序与对应汇编程序之间的关系以及GPF端口数据变化情况。
五、实验报告要求 
1、以报告的形式总结实验过程;
2、总结如何使用ADS调试C程序。