找人做网站服务器不是自己的怎么办软件应用开发
        我们在学习 STM32  的时候几乎没有用到过汇编,可能在学习  UCOS 、 FreeRTOS  等  RTOS  
 
 类操作系统移植的时候可能会接触到一点汇编。但是我们在进行嵌入式  Linux  开发的时候是绝  
 
 对要掌握基本的  ARM  汇编,因为  Cortex-A  芯片一上电  SP  指针还没初始化, C  环境还没准备  
 
 好,所以肯定不能运行  C  代码,必须先用汇编语言设置好  C  环境,比如初始化  DDR 、设置  SP  
 
 指针等等,当汇编把  C  环境设置好了以后才可以运行  C  代码。所以  Cortex-A  一开始肯定是汇  
 
 编代码,其实  STM32  也一样的,一开始也是汇编,以  STM32F103  为例,启动文件  
 
 startup_stm32f10x_hd.s  就是汇编文件,只是这个文件  ST  已经写好了,我们根本不用去修改,所  
 
 以大部分学习者都没有深入的去研究。汇编的知识很庞大,本章我们只讲解最常用的一些指令,  
 
 满足我们后续学习即可。 
 
 
一.GNU 汇编语法
 如果大家使用过  STM32  的话就会知道  MDK  和  IAR  下的启动文件  startup_stm32f10x_hd.s  
  其中的汇编语法是有所不同的,将  MDK  下的汇编文件直接复制到  IAR  下去编译就会出错,因  
  为  MDK  和  IAR  的编译器不同,因此对于汇编的语法就有一些小区别。我们要编写的是  ARM  
  汇编,编译使用的  GCC  交叉编译器,所以我们的汇编代码要符合  GNU  语法。  
  GNU  汇编语法适用于所有的架构,并不是  ARM  独享的, GNU  汇编由一系列的语句组成,  
  每行一条语句,每条语句有三个可选部分,如下:  
  label : instruction @ comment  
  label  即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到  
  指令的地址,标号也可以用来表示数据地址。注意  label  后面的“:”,任何以“:”结尾的标识  
  符都会被识别为一个标号。  
  instruction  即指令,也就是汇编指令或伪指令。  
  @ 符号,表示后面的是注释,就跟  C  语言里面的“ /* ”和“ */ ”一样,其实在  GNU  汇编文  
  件中我们也可以使用“ /* ”和“ */ ”来注释。  
  comment  就是注释内容。  
  比如如下代码:  
  add:  
  MOVS R0, #0X12 @ 设置  R0=0X12  
  上面代码中“ add: ”就是标号,“ MOVS R0,#0X12 ”就是指令,最后的“ @ 设置  R0=0X12 ”就是  
  注释。  
  .data  初始化的数据段。  
  .bss  未初始化的数据段。  
  .rodata  只读数据段。  
  我们当然可以自己使用 .section  来定义一个段,每个段以段名开始,以下一段名或者文件结  
  尾结束,比如:  
  .section .testsection @ 定义一个  testsetcion  段  
  汇编程序的默认入口标号是 _start ,不过我们也可以在链接脚本中使用  ENTRY  来指明其它  
  的入口点,下面的代码就是使用 _start  作为入口标号:  
  .global _start  
  _start:  
  ldr r0, =0x12 @r0=0x12  
  上面代码中 .global  是伪操作,表示 _start  是一个全局标号,类似  C  语言里面的全局变量一  
  样,常见的伪操作有:  
  .byte  
  定义单字节数据,比如 .byte 0x12 。  
  .short  
  定义双字节数据,比如 .short 0x1234 。  
  .long  
  定义一个  4  字节数据,比如 .long 0x12345678 。  
  .equ  
  赋值语句,格式为: .equ  变量名,表达式,比如 .equ num, 0x12 ,表示  num=0x12 。  
  .align  数据字节对齐,比如: .align 4  表示  4  字节对齐。  
  .end  
  表示源文件结束。  
  .global  定义一个全局符号,格式为: .global symbol ,比如: .global _start 。  
  GNU  汇编还有其它的伪操作,但是最常见的就是上面这些,如果想详细的了解全部的伪操  
  作,可以参考《 ARM Cortex-A(armV7) 编程手册  V4.0.pdf 》的  57  页。  
  GNU  汇编同样也支持函数,函数格式如下:  
  函数名 :  
  函数体  
  返回语句  
  GNU  汇编函数返回语句不是必须的,如下代码就是用汇编写的  Cortex-A7  中断服务函数:  
  示例代码 7.1.1.1 汇编函数定义  
  /*  未定义中断  */  
  Undefined_Handler :  
  ldr r0 , = Undefined_Handler  
  bx r0  
  /* SVC  中断  */  
  SVC_Handler :  
  ldr r0 , = SVC_Handler  
  bx r0  
  /*  预取终止中断  */  
  PrefAbort_Handler :  
  ldr r0 , = PrefAbort_Handler  
  bx r0 
  上述代码中定义了三个汇编函数: Undefined_Handler 、 SVC_Handler  和  
  PrefAbort_Handler 。以函数  Undefined_Handler  为例我们来看一下汇编函数组成,  
  “ Undefined_Handler ”就是函数名,“ ldr r0, =Undefined_Handler ”是函数体,“ bx r0 ”是函数  
  返回语句,“ bx ”指令是返回指令,函数返回语句不是必须的。 
  二.处理器内部数据传输指令
 三个指令:  
  1 、 MOV  指令  
  MOV  指令用于将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄  
  存器里面,使用示例如下:  
  MOV R0 , R1  
  @ 将寄存器  R1  中的数据传递给  R0 ,即  R0=R1  
  MOV R0, #0X12  
  @ 将立即数  0X12  传递给  R0  寄存器,即  R0=0X12  
  2 、 MRS  指令  
  MRS  指令用于将特殊寄存器 ( 如  CPSR  和  SPSR) 中的数据传递给通用寄存器,要读取特殊  
  寄存器的数据只能使用  MRS  指令!使用示例如下:  
  MRS R0, CPSR  
  @ 将特殊寄存器  CPSR  里面的数据传递给  R0 ,即  R0=CPSR  
  3 、 MSR  指令  
  MSR  指令和  MRS  刚好相反, MSR  指令用来将普通寄存器的数据传递给特殊寄存器,也就  
  是写特殊寄存器,写特殊寄存器只能使用  MSR ,使用示例如下:  
  MSR CPSR, R0  
  @ 将  R0  中的数据复制到  CPSR  中,即  CPSR=R0 
  三.存储器访问指令
 1 、 LDR  指令  
  LDR  主要用于从存储加载数据到寄存器  Rx  中, LDR  也可以将一个立即数加载到寄存器  Rx  
  中, LDR  加载立即数的时候要使用“ = ”,而不是“ # ”。在嵌入式开发中, LDR  最常用的就是读  
  取  CPU  的寄存器值,比如  I.MX6UL  有个寄存器  GPIO1_GDIR ,其地址为  0X0209C004 ,我们  
  现在要读取这个寄存器中的数据,示例代码如下:  
  示例代码 7.2.2.1 LDR 指令使用  
  1  LDR R0 , = 0X0209C004  @ 将寄存器地址  0X0209C004  加载到  R0  中,即  R0 = 0X0209C004  
  2  LDR R1 , [ R0 ]  @ 读取地址  0X0209C004  中的数据到  R1  寄存器中  
  上述代码就是读取寄存器  GPIO1_GDIR  中的值,读取到的寄存器值保存在  R1  寄存器中,  
  上面代码中  offset  是  0 ,也就是没有用到  offset 。  
  2 、 STR  指令  
  LDR  是从存储器读取数据, STR  就是将数据写入到存储器中,同样以  I.MX6UL  寄存器  
  GPIO1_GDIR  为例,现在我们要配置寄存器  GPIO1_GDIR  的值为  0X20000002 ,示例代码如下:  
  示例代码 7.2.2.2 STR 指令使用  
  1  LDR R0 , = 0X0209C004  @ 将寄存器地址  0X0209C004  加载到  R0  中,即  R0 = 0X0209C004  
  2  LDR R1 , = 0X20000002  @R1  保存要写入到寄存器的值,即  R1 = 0X20000002  
  3  STR R1 , [ R0 ]  @ 将  R1  中的值写入到  R0  中所保存的地址中  
  LDR  和  STR  都是按照字进行读取和写入的,也就是操作的  32  位数据,如果要按照字节、  
  半字进行操作的话可以在指令“ LDR ”后面加上  B  或  H ,比如按字节操作的指令就是  LDRB  和  
  STRB ,按半字操作的指令就是  LDRH  和  STRH 。  
  四. 压栈和出栈指令
         通常会在 A  函数中调用  B  函数,当  B  函数执行完以后再回到  A  函数继续执行。要想  
  再跳回  A  函数以后代码能够接着正常运行,那就必须在跳到  B  函数之前将当前处理器状态保存  
  起来 ( 就是保存  R0~R15  这些寄存器值 ) ,当  B  函数执行完成以后再用前面保存的寄存器值恢复  
  R0~R15  即可。保存  R0~R15  寄存器的操作就叫做现场保护,恢复  R0~R15  寄存器的操作就叫做  
  恢复现场。在进行现场保护的时候需要进行压栈 ( 入栈 ) 操作,恢复现场就要进行出栈操作。压栈  
  的指令为  PUSH ,出栈的指令为  POP , PUSH  和  POP  是一种多存储和多加载指令,即可以一次  
  操作多个寄存器数据 
 