不动笔墨不读书,抄书系列《深入浅出嵌入式底层软件开发》(不断更新……)

   日益感觉到忘性真的是要开始大于记性了,也要开始抱怨了。正如拉罗什弗科说:“人人都抱怨缺乏记忆力,但没有一个热抱怨缺乏健全的思想。”思想慢慢健全吧,通过抄书感觉记忆力大大提升了,文艺点的说法叫做“最淡的笔墨也胜于最强的记忆”,三俗点那就是“好记性不如烂笔头”

    文章内容99%参考《深入浅出嵌入式底层软件开发》,由于作者在大部分知识点上实在是写得完美,且结构安排合理,不管是对于初学者还是高手都有很好的借鉴意义。特别是本书前几章关于ARM指令部分讲解的非常精彩。虽然本文尽量包含全书所有的知识点,并且尽量保证原汁原味,但仍然强烈购买原书作为参考。

    本文基本上对《深入浅出嵌入式底层软件开发》的照搬,主要是为了想证明也曾认真学习过,为日后参考所用,也愿意把之比喻为课后家庭作业吧,就好像小学老师让小学生把课文抄10遍(当然现在是自愿抄的)。当然也包含了一些自己的见解,疑惑,记录了初看时的不解之处,最终通过各种办法弄懂的过程。学问这个东西,还真得是一边学,一边问的。

    请不要把抄书看成是一种低智商劳动。如果你热爱学习,那么从抄书(摘抄)开始吧!你将会有意想不到的收获。

    非常愿意把这篇文章看成是自己的一种学习指导,或者说是经典著作的评注版。如果您一不留神看到了这篇文章,不要呲之以鼻,还是一笑而过吧。当然,如果一不小心这篇文章也成为了您的学习指导,那自然是深感荣幸,说明咱们在空中达成了一种共识,您可以给我留言,愿共同交流,一起进步。

 

第零章 绪论

 

0.1 ARM简介

ARM是Advanced RISC Machines的缩写,它是一家微处理器行业的知名企业,据说成立于英国的某个谷仓中,差不多是与现在叱咤风云的Linux同时出现。

ARM公司定义了7种主要的ARM指令集体系结构版本,用版本号V1-V7表示。目前正在使用的版本是V4、V5、V6、V7,V4版本之前的版本已经很少看到。

V7体系定义了如下的内核型:

●A型应用于复杂的、基于虚拟内存的操作系统和用户应用软件。

●R型应用于实时操作系统。

●M型为微控制器和低成本的应用优化。

Cortex是基于V7架构新的产品系列,目前已经成为ARM公司主推的ARM处理器内核。

0.2 ARM指令系统

    ARM处理器是基于精简指令集计算机(RISC)原理设计的,指令集和相关译码机制较为简单。ARM具有32位ARM指令集和16位Thumb指令集,ARM指令集效率高,但是代码密度低;而Thumb指令集具有较高的代码密度,却仍然保持ARM的大多数性能上的优势,它是ARM指令集的子集。所有的ARM指令都是可以有条件执行的,而Thumb指令仅有一条指令(B)具备条件执行功能。ARM程序和Thumb程序可相互调用,相互之间的状态切换开销几乎为零。 

 

 第一章 ARM汇编编程基础

贵在坚持

1.1 ARM CPU寄存器

ARM的汇编编程,本质上就是针对CPU寄存器的编程,所以先要弄清楚ARM有哪些寄存器以及这些寄存器都是如何使用的。

普通寄存器总共有16个,分别为R0-R15;状态寄存器共两个,分别为CPSR和SPSR

1.1.1普通寄存器R0-R15

●R15别名PC(Program Counter),程序计数器

它的值是当前正在执行的指令在内存中的位置,当指令执行结束后,CPU硬件会自动将PC值加上一个单位,从而使得PC值为下一条即将执行的指令在内存中的位置。

对汇编程序编写而言,PC寄存器也是十分重要,因为当程序员通过汇编指令对PC寄存器进行赋值操作时,其实就是完成了一次无条件跳转,这点很重要,务必牢记。

●R14别名LR(Linked Register),链接寄存器

与子程序调用密切相关,用于存放子程序的返回地址。举例说明:

int main(void)

{

int k,i=1,j=2;

addsub(i,j);/*编译器会将这行编译为指令:BL addsub */

k=3;

 }

int addsub(int a,int b)

{

int c;

c=a+b;

return c;/*编译器会将这行编译为指令 MOV pc,lr */

 }

BL addsub详解:

①将子程序的返回地址保存到寄存器LR中;

②跳转到子程序addsub的第一条指令处。

还有一个问题需要注意:如果子程序又调用了孙子程序,那该怎么办?因为子程序的返回地址已经存放在了LR,不会再把孙子程序的返回地址存放到LR中吧?这是行不通的。那该怎么办呢?其实,如果我们编写的是C程序,那就不用担心,因为编译器会为我们考虑一切,针对这个问题,编译器会在孙子程序的入口处增加入栈操作将LR的值入栈,然后在孙子程序的返回处增加出栈操作,将LR的值恢复,从而解决这个难题。

但是我们一定要保持头脑清醒,因为我们现在是在编写汇编子程序,如果该子程序还要再调用孙子程序,那么请务必记住,一定要在子程序的入口地址处保存LR寄存器的值。

●R13别名SP(stack pointer),栈指针寄存器

用于存放堆栈的栈顶地址,也就是说,每次进行出栈和入栈的时候,都将根据该寄存器的值来决定访问内存的位置,同时在出栈和入栈操作完成后,SP寄存器的值也应该相应的增加或减少。特别说明的是,在32位的ARM指令集中没有专门的入栈指令和出栈指令,所以并不是一定要用SP来作为栈指针寄存器,除了PC外,任何普通寄存器均可作为栈指针寄存器,只不过约定俗成,都使用SP罢了。

●寄存器R0-R12为普通的数据寄存器,可用于任何地方。

1.1.2 状态寄存器CPSR与SPSR

●CPSR(Current Program Status Register),当前程序状态寄存器

用于保存程序的当前状态,那么程序的哪些状态需要保存?

                                      31 30   29   28   27  26                                            8   7    6     5    4    3    2     1    0

N

Z

C

V

-

-

              ...

I

F

T

         

 

28-31为标志位:

N:符号标志位,结果为负数时N=1,结果为整数或零时N=0;

Z:零标志位,指令结果为零时Z=1,否则Z=0;

C:进位标志位,当进行加法运算最高位产生进位时(或进行减法运算最高位产生借位 时)C=1,否则C=0;

V:当进行加法/减法运算产生溢出时,V=1,否则V=0;

8-27位保留;

0-7为控制位,控制CPU是否响应中断

I:中断禁止位,当I位置位时,IRQ中断被禁止;

F:快中断禁止位,当F位置位时,FIQ中断被禁止;

T:反应了CPU当前的状态,当T位置位时,处理器正在Thumb状态下运行;当T位清零时,处理器正在ARM状态下运行。

0-4为模式位,分别为M0-M4,这些位决定了处理器的模式(关于处理器模式详见“ARM处理器模式与异常初步”)

总共有七种模式:快中断、中断、中止、用户、管理、未定义、系统,分别会用于不同的情况和异常。由此可见,并不是所有模式位的组合都定义了有效的处理器模式,如果使用了错误的模式,将引起一个无法恢复的错误。

●SPSR(Saved Program Status Register),保存的程序状态寄存器

该寄存器结构与CPSR完全一样,在异常发生时,由硬件自动将异常发生前的CPSR的值存放到SPSR中,以便将来在异常处理结束后,程序能恢复原来CPSR的值。

1.1.3 流水线对PC的值得影响

CPU在执行一条指令的时候,主要有3个步骤:取指、译码、执行。

流水线的本质是利用指令运行的不同阶段使用的CPU硬件互不相同,并发的运行多条指令,从而提高时间效率。

流水线的引入的确提高了CPU运行指令的时间效率,但却为汇编语言程序编写引入了新的问题。那就是:

指令执行时,PC的值=当前正在执行指令在内存中的地址+8;

牢记这个结论。(通过查看反汇编的方式理解伪指令和编译器的行为时很有帮助)

提问:为什么+8?

首先,因为是流水线,当第1条指令执行的时候,第3条指令已经被取指了,且因为每条ARM指令的长度为32bit,占4B,所以两条指令当然是8啦!

最后说明一点:其实ARM现在的CPU流水线级数早就已经突破了3级。但ARM出于同一和前后兼容的考虑,上述的结论在所有的流水线级数上都是相同的。作为编程人员,只需要知道这个结论即可。

1.2 寻址方式与基本指令

何为操作码?何为操作数?

MOV pc,lr

MOV即为操作码,pc,lr即为操作数。

操作数部分需要解决的问题是:到哪里去获得操作数?因此就有了寻址方式的分类。

1.2.1 基本上来讲,ARM共有8种寻址方式

●寄存器寻址

MOV pc,lr即为寄存器寻址

●寄存器间接寻址

LDR R0,[R2]即为寄存器间接寻址

将R2中存放的数作为内存地址,到该内存地址处取出存放的数送到RO中。

●寄存器移位寻址

MOV R0,R2,LSL #3 ;R2的值左移3位,结果放入R0,即R0=R2*8

ANDS R1,R1,R2,LSL R3 ;R2的值左移R3位,然后和R1相与操作,结果放入R1。

可采用的移位操作如下:

LSL 逻辑左移,寄存器中字的低端空出的位补0;

LSR 逻辑右移,寄存器中字的高端空出的位补0;

ASR 算术右移,移位过程中保持符号位不变,若源操作数为正数,则字的高端空出的位补0,否则补1;

ROR 循环右移,由字的低端移出的位填入字的高端空出的位;

RRX 带扩展的循环右移,操作数右移1位,高端空出的位用原C标志值(指的是CPSR的C位)填充;

记住:向左的只有一个,那就是LSL。

●多寄存器寻址

多寄存器寻址是指一次可传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。

LDMIA R1!,{R2-R4,R6} ;将R1指向的单元中的数据读出到R2-R4、R6中(R1自动加1)

 

R6

0x??

R4

0x??

R3

0x??

R2

0x??

R1

0x4000000

 

0x04

0x4000000C

0x03

0x40000008

0x02

0x40000004

0x01

0x40000000

                                                                                              存储器

                                                                                       LDMIA指令执行前

 

R6

0x04

R4

0x03

R3

0x02

R2

0x01

R1

0x4000010

 

0x04

0x4000000C

0x03

0x40000008

0x02

0x40000004

0x01

0x40000000

                                                                                                存储器

                                                                                          LDMIA指令执行后

 STMIA R0!,{R2-R4,R6} ;将R2-R4、R6的值保存到R0指向的存储单元中(R0自动加1);

说明:

a,寄存器列表{R2-R4,R6}中的顺序并不要紧。最终寄存器与内存地址的对应关系是:编号小的寄存器与内存的低地址相对应。

b,其实多寄存器加载指令ldm总共有4个:ldmia、ldmib、ldmda、ldmdb。

ia的意思是increase after

ib的意思是 increase before

da的意思是 decrease after

db的意思是 decrease before

4条类似地多寄存器存储指令分别是stmia、stmib、stmda、stmdb

●立即数寻址

MOV pc,#64即为立即数寻址

这里,可能大家会看出一个问题:由于立即数是位于32为机器码中的,而32位机器码中除了操作数外还有操作码,这就意味着不可能用全部的32位来表示立即数。事实上,ARM机器指令中,仅用了最低的12bit来表示立即数。那么立即数的范围就是-2048-2047,这意味着“MOV pc,#8912”这样的指令是非法的。但是事实并非如此,这样的指令还就是合法的。真实情况是,ARM机器指令可以表示的立即数范围为--1,只不过它只能表示这其中的个数字而已。

ARM是这样用12bit来表示一个立即数的:将12bit划分为两部分--高4位和低8位,将低8位补0扩展为32位,然后循环右移X位(X=高4位表示的无符号整数*2),例如:如果32位机器码中低12bit为0x512,则表示的立即数为0x04800000。

 ●基址寻址

基址寻址就是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址,基址寻址用于访问基址附近的内存单元,常用于查表、数组操作、功能部件寄存器访问等。

LDR R2,[R3,#0x0C] ;读取R3+0x0C地址上的存储单元的内容,存入R2;

STR R1,[R0,#-4]! ;先R0=R0-4,然后把R1的值保存到R0指定的存储单元;(注意:STR这个指令跟LDM指令类似,都是把前者的内容读到后者,例如上面的ldmia、ldmib、ldmda、ldmdb和下面要讲的ldmea、ldmed、ldmfa、ldmfd)

STR R1,[R0,#-4] ;把R1的值保存到R0-4存储单元;

STR R1,[R0],#-4 ;把R1的值保存到R0地址上的存储单元,并且指令结束时,R0=R0-4;

LDR R1,[R0,R3,LSL #1] ;将R0+R3*2地址上的存储单元的内容读出,存入R1;★★ 

●堆栈寻址

递增堆栈(向上生长)和递减堆栈(向下生长):以入栈后SP的值是增加还是减少为依据;

满堆栈和空堆栈:先调整指针再写入数据(SP所指向的内存处存放的是栈顶元素)为满堆栈;先写入数据再调整指针(SP所指向的内存处存放的是下一次要入栈的元素)为空堆栈;

因此堆栈的类型共有四种:空递减、空递增、满递减、满递增;

stm指令为入栈操作,ldm指令为出栈操作;

入栈

说明

STMED

空递减

STMEA

空递增

STMFD

满递减

STMFA

满递增

 

 

出栈

说明

LDMED

空递减

LDMEA

空递增

LDMFD

满递减

LDMFA

满递增

ED:empty descend(空递减)

EA:empty ascend(空递增)

FD:full descend(满递减)

FA:full ascend(满递增)

例如:

STMFD SP!,{R1-R7,LR} ;将R1-R7、LR入栈,满递减堆栈

LDMFD SP!,{R1-R7,LR} ;数据出栈放到R1-R7、LR寄存器,满递减堆栈

●相对寻址

相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。例如:

B LOOP

LOOP MOV R6,#1

1.2.2最常见的指令精解

●单寄存器加载指令

①加载字指令

LDR r0,[r1],将内存中的一个字(4字节)加载到寄存器r0中。

LDR r0,label,将标号label代表的内存地址处存放的内容加载到r0中。

②加载字节指令

LDRB r0,[r1],将内存中的一个字节加载到寄存器r0中。

③有符号数加载字节指令

LDRSB r0,[r1],由于加载的是一个字节而不是一个字,所以需要确定寄存器r0的高24bit是什么。对于上一条指令,r0的高24bit补0,而本条指令,r0的高24位补符号位,也就是补r0的bit7。

④半字加载指令

LDRH

⑤有符号半字加载指令

LDRSH

●单寄存器存储指令

①存储字指令

STR r0,[r1],将r0中的值存储到地址为r1中的数的内存的4个字节中。

②存储字节指令

STRB r0,[r1],将r0的低8位存储到地址为r1中的数的内存的1个字节中。

③半字存储指令

STRH

●分支指令

①B Label

跳转到标号label处;

②BL Label

跳转到标号label处,并且将下一条指令的地址保存到lr中。

③BX r0

将r0的值作为地址,跳转到该地址处,并根据r0的值决定是否在ARM和thumb态之间进行切换。

特别说明:B和BL指令,其跳转范围限制在当前指令的正负32MB地址内(ARM指令为字对齐,最低2位地址固定为0)。(why?)

实际上,虽然B LOOP这条指令的意思是要跳转到标号LOOP所代表的指令处,但CPU根本不明白标号是个什么东西,那么B LOOP这条指令的机器码是:高8bit为操作码相关内容,低24bit为一个常数,表示从B指令到MOV指令之间的内存地址的差值(如果不考虑流水线的影响的话)。因为24bit常数要用1bit区别正负,还剩23bit,同时又由于ARM指令在内存中的地址的最低2bit一定是0,因此23bit中可以不必表示这2个0,所以23bit可以表示的范围是0-225,即:0-32MB。

●数据处理指令

MOV r0,r1:将r1的值赋给r0。

ADD(SUB):r0,r1,r2:将r1的值加上(减去)r2的值,结果存放到r0中。

AND(ORR,EOR):r0,r1,r2:将r1的值与(或、异或)r2的值,结果存放到r0中。

cmp r1,r2:比较r1与r2值得大小;

指令运行细节是:执行r1-r2的操作,如果结果为负数,则置位CPSR的N位,清零Z位;如果结果为0,则清零CPSR的N位,置位Z位;如果结果为正数,则清零CPSR的N位和Z位。但r1-r2的结果并不保存。CMP指令通常用于分支跳转。

思考:addeq,streq,beq这3条指令是什么意思呢?

分别是add,str,b的条件执行版本。

讲到这里就不得不讲解一下什么是条件执行了。ARM指令集的所有指令均支持条件执行,条件执行指的是,指令可以根据CPSR的条件代码标志位决定自身是否被执行。

Eq表示如果CPSR的Z位为1,该指令要执行,否则不执行。

其它条件助记符如下表:

操作码   条件助记符   标志     含义
0000    EQ     Z=1     相等 (EQual )
0001    NE     Z=0     不相等( Not Equal )
0010    CS/HS   C=1     无符号数大于或等于( Carry Set /HIgher Set )
0011    CC/LO   C=0     无符号数小于( Carry Clear /LOwer )
0100    MI     N=1     负数( MInus )
0101    PL     N=0     正数或零( PLus )
0110    VS      V=1     溢出( oVerflow Set )
0111    VC      V=0     没有溢出( oVerflow Clear )
1000    HI     C=1,Z=0   无符号数大于( HIgher )
1001    LS      C=0,Z=1   无符号数小于或等于( Lower or the Same )
1010    GE     N=V     有符号数大于或等于( Greater than or Equal )
1011    LT           N!=V     有符号数小于( Less Than )
1100    GT          Z=0,N=V   有符号数大于( Greater Than )
1101    LE           Z=1,N!=V   有符号数小于或等于( Less than or Equal )
1110    AL          任何       无条件执行 (指令默认条件) ( ALways )
1111    NV          任何     从不执行(不要使用) ( NeVer )

● 交换指令

助记符

说明

操作

SWP Rd,Rm,[Rn]

寄存器和存储器字进行数据交换

Rd←[Rn],

[Rn]←Rm,(Rn≠Rd或Rm)

SWPB Rd,Rm,[Rn]

寄存器和存储器字节进行数据交换

Rd←[Rn]

[Rn]←Rm,(Rn≠Rd或Rm)

 ● 数据传送指令

助记符

说明

操作

MVN Rd,operand2

数据非传送

Rd←(~operand2)

● 算术运算指令

助记符

说明

操作

RSB Rd,Rn,operand2

逆向减法指令

Rd←operand2-Rn

ADC Rd,Rn,operand2

带进位加法指令

Rd←Rn+operand2+Carry

SBC Rd,Rn,operand2

带进位减法指令

Rd←Rn-operand2-(N0T)Carry

RSC Rd,Rn,operand2

逆向带进位减法指令

Rd←operand2-Rn-(NOT)Carry

● 逻辑运算指令

助记符

说明

操作

BIC Rd,Rn,operand2

按位清除指令

Rd←Rn&(~operand2)

其实现的功能其实就是:将Rn中对应于operand2中为1的bit位全部清0,其它bit位保持不变,然后将结果保存到Rd中。

● 比较指令

助记符

说明

操作

CMN Rn,operand2

负数比较指令

标志N、Z、C、V←Rn+operand2

TST Rn,operand2

位测试指令

标志N、Z、C←Rn&operand2

TEQ Rn,operand2

相等测试指令

标志N、Z、C←Rn^operand2

TST指令测试的是:Rn中所有指定bit位是否全为0(指定的bit位是operand2中为1的所有位)。

TEQ指令测试的是:Rn和operand2是否相等。这点与CMP指令一样,区别在于CMP指令除了可以比较两个数是否相等外,也可以比较两个数谁大谁小,但TEQ不行。

● 乘法指令

助记符

说明

操作

MUL Rd,Rm,Rs

32位乘法指令

Rd←Rm*Rs(Rd≠Rm)

MLA Rd,Rm,Rs,Rn

32位乘加指令

Rd←Rm*Rs+Rn(Rd≠Rm)

UMULL RdLo,RdHi,Rm,Rs

64位无符号乘法指令

(RdLo,RdHi)←Rm*Rs

UMLAL RdLo,RdHi,Rm,Rs

64位无符号乘加指令

(RdLo,RdHi)←Rm*Rs+(RdLo,RdHi)

SMULL RdLo,RdHi,Rm,Rs

64位有符号乘法指令

(RdLo,RdHi)←Rm*Rs

SMLAL RdLo,RdHi,Rm,Rs

64位有符号乘加指令

(RdLo,RdHi)←Rm*Rs+(RdLo,RdHi)

● 协处理器指令

参见“MMU与内存保护的实现”

● 杂项指令

①SWI:软中断指令(参见“swi与system call的实现”)

产生软中断,处理器进入管理模式。

根据SWI指令传递的参数SWI异常处理程序可以作出相应的处理。SWI指令传递参数有以下两种方法:

α指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递。 

MOV    R0,#34 ;设置子功能号为34 

SWI    12 ;调用12号软中断 

β指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其它的通用寄存器传递。 

MOV    R0,#12 ;调用12号软中断 

MOV    R1,#34 ;设置子功能号为34 

②MRS、MSR:程序状态寄存器操作指令,参见“ARM异常处理”

1.2.3 数据,指令,地址

31                           23                            15                           7                            0

            12                             34                            56                           78

●数据

12345678(小端)78563412(大端)

●指令

指令解码器将12345678解码成指令,如果不是有效指令,出异常

●地址

代表12345678这个内存地址

●总结

计算机内部二进制数在不同位置具有不同的意义

 

1.3 ARM汇编伪操作

1.3.1 汇编伪操作在在汇编程序中的使用范例

    现在来看一个简单的汇编程序,该程序调用子程序完成了加法操作

    1;文件名:TEST.S

    2;功能:实现两个寄存器相加

    3 AREA Example,CODE,READONLY ;声明代码段Example

    4 ENTRY ;标识程序入口

    5 CODE32 ;声明32位ARM指令

    6 START MOV R0,#0 ;设置参数

    7   MOV R1,#10

    8 BL ADD_SUB ;调用子程序ADD_SUB

    9 LOOP    B LOOP ;跳转到LOOP

    10ADD_SUB

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

    12 MOV PC,LR ;子程序返回

    13 END ;文件结束

    START、LOOP、ADD_SUB是标号,最经常用于跳转指令B和BL,由于汇编语法要求的缘故,标号必须顶格写(即:不能在行首有空格),否则编译器会报错。与之对应的是,汇编指令一定不能顶格写。

    很明显分号(;)在汇编程序中时注释符号,相当于C语言的//和/*...*/号。

    3,4,5,13行是ARM汇编伪操作。

    AREA表示定义一个段,其段名为Example,CODE表明是代码段,READONLY表示属性为只读

    ENTRY表示整个程序的入口点是第6行的MOV指令。

    CODE32表示第6-12行的程序代码是ARM指令,而不是thumb指令。

    END表示源代码文件结束。其背后的含义就是:如果程序员在第13行后还写有汇编指令,编译器也根本不会理会这些代码,更不会去编译它们,当然这些代码也就不可能出现在最后的可执行文件中。

    第9行的含义是要让程序在运行结束后,在第9行进行死循环,从而让整个程序定格在第9行。这一点也许让人很困惑:在写应用程序时,程序结束就结束了,源代码根本不需要再去写个死循环。但现在要弄清楚:在写应用程序时,有OS为处理程序结束后的若干事情。可是,现在已经得不到OS服务。如果不自己写第9行代码,那么当认为程序已经运行结束(第8行执行完成)的时候,CPU不会聪明的停下来,它会继续任劳任怨地去取指第11行,继续运行,这不是大家所希望的。其实这还不是最糟糕的,最糟糕的是,如果程序没有11-13行,那么CPU任劳任怨取出的指令其实是内存中的随机数,但CPU却会把它当做指令来执行,那么,此时会出现什么情况呢?

1.3.2 最常见的汇编伪操作精解

●GBLA:声明一个全局算术变量,例如:GBLA testval

     GBLL:声明一个全局逻辑变量,

    GBLS:声明一个全局字符串变量

●SETA:给一个全局算术变量赋值,例如:testval SETA 9

     SETL:给一个全局逻辑变量赋值

     SETS:给一个全局字符串变量赋值

●DCB:分配一段字节内存单元,例如:DCB 'a'

     DCW/DCWU:分配一段半字对齐的半字内存单元

     DCD/DCDU:分配一段字对齐的内存单元,DCD可能在分配的第1个内存单元前插入填补字节(padding),以保证分配的内存是字对齐的,DCDU则不需要对齐

     DCQ/DCQU:分配一段以双字(8个字节)为单位的内存

     DCFD/DCFDU:为双精度的浮点数分配字对齐的内存单元

     DCFS/DCFSU:为单精度的浮点数分配字对齐的内存单元

     DCI:在ARM代码中分配一段字对齐的内存单元;在Thumb代码中分配一段半字对齐的半字内存单元

●LTORG:声明一个数据缓冲池的开始,语法格式:LTORG

     MAP:定义一个结构化内存表(Storage Map)的首地址

    FIELD:定义一个结构化内存表中的数据域

     SPACE:分配一块连续内存单元,并用0初始化

●IF,ELSE及ENDIF:相当于C语言的条件编译,例如:

  GBLA testval

  testval  SETA 9

  IF testval<5

  mov r0,#testval

  ELSE

  mov r1,#testval

  ENDIF

  IF:DEF:testval

  mov r2,#testval

  ELSE

  INFO 4,"you should define testval"

  ENDIF

  编译器编译该段代码的结果是:

  mov r1,#9

  mov r2,#9

●WHILE及WEND:例如:

  GBLA testval

  testval  SETA 1

  WHILE testval<=3

T   testval SETA testval+1

  mov r0,#testval

  WEND

  编译器编译该段代码的结果是:

  mov r0,#2

  mov r0,#3

  mov r0,#4

●MACRO、MEND及MEXIT:相当于C语言的宏替换,MACRO标识宏定义的开始,MEND标识宏定义的结束。MEXIT用于从宏中跳转出去。用MACRO和MEND定义的一段代码,称为宏定义体。通过宏名称来调用宏。

语法格式:

MACRO

{$label}macroname{$param...}

...;宏代码

MEND

●EQU:相当于C语言的宏定义,例如:testval EQU 4。

●EXPORT:参见“ATPCS与混合编程”。

●IMPORT:参见“ATPCS与混合编程”。

1.4 RealView MDK开发环境的使用

    ADS1.2集成开发工具ARM公司在2001年已经停止对其维护和支持,ARM公司目前推出了新的、功能更加强大的、用户界面更友好的MDK作为其硬件平台的集成开发工具。

MDK(Microcontroller Development Kit)开发工具源自德国Keil公司,由于其拥有流畅的用户界面与强大的方针功能而被全球超过10万的嵌入式开发工程师验证和使用,是ARM公司面前最新推出的针对各种嵌入式处理器的软件开发工具。RealView MDK集成了业内最领先的技术,融合了中国多数软件开发工程师所需的特点和功能。它支持ARM7、ARM9和最新的Cortex-M3核处理器,自动配置启动代码,集成Flash烧写模块,强大的Simulation设备模拟,性能分析等功能,与ARM之前的工具包ADS等相比,RealView编译器的最新版本可将性能改善超过20%。

posted on 2012-05-14 01:13  西海水鬼  阅读(1083)  评论(1)    收藏  举报

导航