不动笔墨不读书,抄书系列《深入浅出嵌入式底层软件开发》(不断更新……)
日益感觉到忘性真的是要开始大于记性了,也要开始抱怨了。正如拉罗什弗科说:“人人都抱怨缺乏记忆力,但没有一个热抱怨缺乏健全的思想。”思想慢慢健全吧,通过抄书感觉记忆力大大提升了,文艺点的说法叫做“最淡的笔墨也胜于最强的记忆”,三俗点那就是“好记性不如烂笔头”
文章内容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%。
浙公网安备 33010602011771号