一、指令基本格式
{}{S} ,{,}
  其中,<>内的项是必须的,{}内的项是可选的,如是指令助记符,是必须的,
  而{}为指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。
Opcode  指令助记符,如LDR,STR 等
Cond    执行条件,如EQ,NE 等
S       是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响
Rd      目标寄存器
Rn      第一个操作数的寄存器
shifter_operand     第二个操作数

NZCV
条件码  助记符后缀 标志     含义
0000   EQ  Z置位     相等
0001   NE  Z清零     不相等
0010   CS  C置位     无符号数大于或等于
0011   CC  C清零     无符号数小于
0100   MI  N置位     负数
0101   PL  N清零     正数或零
0110   VS  V置位     溢出
0111   VC  V清零     未溢出
1000   HI  C置位Z清零   无符号数大于
1001   LS  C清零Z置位   无符号数小于或等于
1010   GE  N等于V    带符号数大于或等于
1011   LT  N不等于V     带符号数小于
1100   GT  Z清零且(N等于V)  带符号数大于
1101   LE  Z置位或(N不等于V) 带符号数小于或等于
1110   AL  忽略     无条件执行

几个特殊的名字 r13--sp r14--lr r15--pc

二、数据传送指令
2.1 LDR/STR 字传送32位
LDR  r1, [r0, #0x12]    ;将r0+12地址处的数据读出,保存到 r1中(r0的值不变)
LDR  r1, [r0]      ;将r0地址处的数据读出,保存到 r1中(零偏移)
LDR  r1, [r0, r2]     ;将r0+r2地址的数据读出,保存到 r1中(r0的值不变)
LDR  r1, [r0, r2, LSL #2]   ;将r0+r2×4 地址处的数据读出,保存到 r1中(r0,r2的值不变)
LDR  Rd, label      ;label为程序标号,label必须是当前指令的±4KB 范围内
LDR  Rd, [Rn], #0x04    ;Rn 的值用作传输数据的存储地址。在数据传送后,将偏移量 0x04 与
         Rn相加,结果写回到Rn中。Rn不允许是r15
2.2 类似的LDRB/STRB字节传送8位 LDRH/STRH半字传送16位,用户模式下的LDRT/STRT LDRBT/STRBT
LDRSB/STRSB有符号字节传送8位 LDRSH/STRSH有符号半字传送8位
2.3 LDM/STM
LDM/STM 的主要用途有现场保护、数据复制和参数传递等。其模式有8种,如下所示。前面4种用于数据块的传输,后面4种是堆栈操作。
Load from memory into register
Store from a register into memory
IA:每次传送后地址加4
IB:每次传送前地址加4
DA:每次传送后地址减4
DB:每次传送前地址减4
FD:满递减堆栈(常用)
ED:空递减堆栈
FA:满递增堆栈
EA:空递增堆栈

LDMIA  r0!,{r3~r9} ;加载r0指向的地址上的多字数据,保存到 r3~r9中,r0值更新
STMIA  r1!,{r3~r9} ;将r3~r9的数据存储到r1指向的地址上,r1值更新
使用 LDM/STM 进行数据复制
  LDR  r0, =SrcData ; 设置源数据地址
  LDR  r1, =DstData ; 设置目标地址
  LDMIA  r0, {r2~r9} ;加载8个字数据到寄存器r2~r9
  STMIA  r1, {r2~r9} ;存储寄存器r2~r9到目标地址
使用 LDM/STM 进行现场寄存器保护,常在子程序或异常处理使用。
SENDBYTE
     STMFD    SP!,{r0~r7,LR} ;寄存器压栈保护
…….
     BL       DELAY  ;调用DELAY子程序
…….
     LDMFD    SP!,{r0~r7,PC} ;恢复寄存器,并返回
2.4 SPW&SWPB
SWP  r1, r1, [r0] ;将r1的内容与r0指向的存储单元内容进行交换
SWPB  r1, r2, [r0] ;将r0指向的存储单元内容读取一字节数据到r1中(高24位清零),并将r2的内容写入到该内存单元中(最低字节有效)
2.5 MRS/MSR
MRS  r1, CPSR ;将CPSR 状态寄存器读取,保存到r1中
MRS  r2, SPSR ;将SPSR 状态寄存器读取,保存到r1中
在ARM处理器中,只有 MSR指令可以直接设置状态寄存器 CPSR 或SPSR。状态寄存器的 32 位可以分为 4个8 位的域(field )。
bits[31 :24]为条件标志位域,用 f 表示;
bits[23 :16]为状态位域,用 s 表示;
bits[15 :8] 为扩展位域,用 x 表示;
bits[7:0] 为控制位域,用 c 表示;
  MSR CPSR_c,#0xD3 ;CPSR[7:0]=0xD3 ,切换到管理模式
  MSR CPSR_cxsf,r3 ;CPSR=R3
只有在特权模式下才能修改状态寄存器。

三、数据处理指令
感觉这华清写的总是多少有些问题,不是全相信,这里就先不笔记了,只是过目了不少指令,以后用到了网上查
四、乘法指令
4.1 下面指令完成R1=R2 ×R3+10 的操作。
MOV    R0, #0x0A ;
MLA    R1, R2, R3, R0 ; 乘-累加指令
4.2 下面指令完成(R1,R0)=R5 ×R8操作。
UMULL   R0, R1, R5, R8; 无符号长乘指令 R0高32位 R1低32位
4.3下面的指令完成(R1,R0)= R5 ×R8+(R1,R0)操作。
UMLAL   R0, R1, R5,R8; 无符号长乘-累加指令
4.4 SMULL SMLAL 符号的
五、跳转指令
5.1 除了B、BL、BX、BLX,另一种实现指令跳转的方式是通过直接向PC寄存器中写入目标地址值,实现在4GB地址空间中任意跳转,
这种跳转指令又被称为长跳转。
5.2 BL指令用于实现子程序调用。子程序的返回可以通过将 LR寄存器的值复制到PC寄存器来实现。下面三种指令可以实现子程序返回。
   BX r14 (如果体系结构支持BX指令)。
   MOV PC ,r14
  当子程序在入口处使用了压栈指令:
   STMFD r13 !, {,r14}
  子程序结束后,可以使用出栈指令。
   LDMFD r13 !, {,PC}
  将子程序返回地址放入PC中。
5.3 BL&BLX完成状态切换式的跳转与返回
  CODE32  ;ARM 代码
  ……
  BLX     TSUB  ;调用Thumb子程序
  ……
  CODE16 ;Thumb代码开始
  TSUB
  ……
  BX      r14  ;返回ARM 状态
  
五、ARM汇编程序设计
5.1 ARM汇编器所支持的伪操作
5.1.1 符号定义伪操作
1. 全局变量定义伪操作
  GBLA 伪操作用于定义一个全局的数字变量并初始化为0。
  GBLL 伪操作用于定义一个全局的逻辑变量并初始化为F(假)。
  GBLS 伪操作用于定义一个全局的字符串变量并初始化为空。
   例子:
  1)使用伪操作声明全局变量
   GBLA Test1 ; 定义一个全局的数字变量,变量名为 Test1
   Test1 SETA   0xaa  ; 将该变量赋值为 0xaa
   GBLL   Test2  ; 定义一个全局的逻辑变量,变量名为 Test2
   Test2 SETL   {TRUE}  ; 将该变量赋值为真
   GBLS   Test3  ; 定义一个全局的字符串变量,变量名为 Test3
   Test3 SETS    "Testing"  ; 将该变量赋值为“Testing”
  2)声明变量objectsize并设置其值为 0xff ,为“SPACE”操作做准备
   GBLA        objectsize
   Objectsize      SETA    oxff
   SPACE     objectsize
2.局部变量定义伪操作
  LCLA 伪操作用于定义一个局部的数字变量并初始化为0。
  LCLL 伪操作用于定义一个局部的逻辑变量并初始化为 F(假)。
  LCLS 伪操作用于定义一个局部的字符串变量并初始化为空。
   例子:
  1)使用伪操作声明局部变量
   LCLA  Test4  ;声明一个局部的数字变量,变量名为 Test4
   Test3 SETA    0xaa  ;将该变量赋值为 0xaa
   LCLL  Test5  ;声明一个局部的逻辑变量,变量名为 Test5
   Test4 SETL    {TRUE}  ;将该变量赋值为真
   LCLS  Test6  ;定义一个局部的字符串变量,变量名为 Test6
   Test6 SETS  "Testing"  ;将该变量赋值为“Testing”
  2)下面的例子定义一个宏,显示了局部变量的作用范围。
   MACRO ;声明一个宏
  $label   message  $a  ;宏原型
   LCLS   err  ;声明局部字符串变量
  $label
   INFO       0,"err":CC::STR:$a
   MEND ;宏结束,局部变量不再起作用
3.变量赋值伪操作(SETA、SETL 和SETS见上)
4.通用寄存器列表定义伪操作RLIST
  将寄存器列表名称定义为 RegList,可在ARM指令LDM/STM 中通过该名称访问寄存器列表。
   RegList RLIST  {R0-R5 ,R8,R10}
5.协处理器寄存器名称定义伪指令CN
  将协处理器寄存器6 命名为 Power
   Power    CN  6
6.协处理器名称定义伪操作CP
  将协处理器6 命名为Dmu
   Dmu   CP  6
7.VFP 寄存器名称定义伪操作DN/SN
  将VFP 双精度寄存器6 定义为energy
   energy DN 6
  将VFP 单精度寄存器16 定义为mass
   mass SN  16
8.浮点寄存器FN
  为浮点寄存器6 指定名称为 Energy
   Energy   FN  6
5.1.2 数据伪操作
1.用于分配字节存储单元的伪操作DCB
  {label} DCB  expr{,expr} label程序标号
  分配一片连续的字节存储单元并初始化为指定字符串
   Str DCB  "This is a test !"
  与C中的字符串不同,ARM汇编中的字符串不以null结尾,下面指令以ARM汇编形成一个C语言风格的字符串
   C_string DCB  "C_string",0
2.用于分配半字存储单元的伪操作DCW(DCWU) U,不严格半字对齐
  分配一片连续的半字存储单元并初始化
   DataTest DCW 1, 2, 3
3.用于分配字存储单元的伪操作DCD(DCWU) U,不严格字对齐
  分配一片连续的字存储单元并初始化
   DataTest DCD 4, 5, 6
4.用于为单精度浮点数分配内存单元的伪操作DCFS(DCFSU) U,不严格字对齐  
  分配一片连续的字存储单元并初始化为指定的单精度浮点数
   FDataTest DCFS  2E5, -5E-7
5.用于为双精度浮点数分配内存单元的伪操作DCFD(或DCFDU) U,不严格字对齐  
  分配一片连续的字存储单元并初始化为指定的双精度浮点数
   FDataTest DCFD  2E115, -5E7
6.分配以8个字节为单位的连续存储区域的伪操作DCQ(或DCQU) U,不严格字对齐
  分配一片连续的存储单元并初始化为指定的值
   DataTest DCQ    100
7.内存单元分配伪操作SPACE
  分配连续100 字节的存储单元并初始化为 0
   DataSpace SPACE  100
  在Mydata段的开始可以 255 个初始化为 0 的字节单元
   AREA   Mydata,DATA,READWRITE
   data1 SPACE   255;
8.定义结构化内存表首地址伪操作MAP
  定义结构化内存表首地址的值为 0x100+R0
   MAP  0x100,R0
9.定义结构化内存表中数据域的伪操作FILED
  1)下面的例子定义了一个内存表,其首地址为固定地址 0x100,该结构化内存表包含 3 个域,A 的长度为
16个字节,位置为 0x100,B 的长度为 32 个字节,位置为 0x110,S 的长度为 256 个字节,位置为 0x130。
   MAP   0x100  ; 定义结构化内存表首地址的值为 0x100。
   A     FIELD   16  ; 定义A 的长度为16字节,位置为0x100
   B     FIELD   32  ; 定义B 的长度为32字节,位置为0x110
   S     FIELD   256  ; 定义S 的长度为256 字节,位置为0x130
  2)下面的例子显示了一个寄存器相关的首地址定义结构化内存表
   MAP 0,r9  ; 将结构化内存表的首地址设为 r9的值
   FIELD  4  ;
   LAB FIELD   4 ;
   LDR r0,LAB  ;
  最后的LDR 指令,相当于:
   LDR r0,[r9,#4]
10.声明数据缓存池的伪操作LTORG
  1)声明一个数据缓存池用来存储 0x12345678
   LDR r0,=0x12345678;
   ADD r1,r1,r0;
   MOV PC,LR;
   LTORG
   ……
  2)在代码段中使用数据缓冲池
   AREA Example,CODE,READONLY
  Start BL  func1;
  func1 ;程序主体
  ;程序代码
   LDR r1,=0x55555555
   MOV PC,LR  ;子程序结束
   LTORG
  Data   SPACE   4200  ;清除4200 个内存字节
  END
11.将内存单元的内容初始化为相对地址的伪操作DCDO
  分配32 位的字单元,其值为 externsym基于r9 的偏移量
   DCDO externsym
12.分配用于存放代码的内存单元伪指令DCI
  DCI 伪操作和DCD伪操作非常相似,不同之处在于DCI 分配的内存中的数据被表示为指令,可用于通过宏操作来定义处理器不支持的指令。
  DCI 伪操作要求内存对齐,对于ARM指令要求4 字节对齐,对于 Thumb 指令要求2 字节对齐。
  下面的程序通过宏操作来定义处理器不支持的指令。
   MACRO
   Newinst $Rd,$Rm    ;
   DCI    0xe16f0f10:OR($Rd:SHL:12):OR:$Rm
   MEND
13.用于分配由用户指定大小的内存单元的伪操作COMMON
  下面的例子定义大小为255 字节的内存单元,该内存单元是字对齐的。
   COMMON    xyz,255,4
5.1.3 汇编控制伪操作 伪操作的嵌套不可超过256
1.IF、ELSE、ENDIF
  IF {CONFIG}=16
   BNE_rt_udiv_1    ;
   LDR r0,=_rt_div0   ;
   BX r0             ;
  ELIF ...
   ...
  ELSE
   BEQ_rt_div()      ;
  ENDIF
2.WHILE、WEND
  count   SETA   1         ;
       WHILE  count<5    ;
   count   SETA   count+1   ;
   …
   …
  WEND
3.MACRO、MEND
  下面的程序显示了一个完整的宏定义和调用过程。
  ;宏定义
       MACRO  ; 开始宏定义
   $label mymacro  $p1,$p2
       ;代码段1
   $label.loop1  ;代码段2
       ;代码段3
   BGE $label.loop1
   $label.loop2  ;代码段4
       BL $p1
   BGT $label.loop2
       ;代码段5
       ADR $p2
       ;代码段6
  MEND
  ;程序汇编后,宏展开结果
  abc  mymacro  subr1,de ;使用宏
     ;代码段1
  abcloop1  ;代码段2
     ;代码段3
  BGE abcloop1
  abcloop2  ;代码段4
     BL subr1
  BGT abcloop2
     ;代码段5
     ADR de
     ;代码段6
4.MEXIT
  MACRO
   $abc   macro   abc   $paraml,$param2
   ;code
   WHILE   condition1
    ;code
    IF   condition2
     ;代码段
     MEXIT
    ELSE
     ;代码段
    ENDIF
   WEND
   ;代段段
  MEND
5.1.4 信息报告伪操作
1.断言错误伪操作ASSERT
  ASSERT 为断言错误伪操作。在汇编器对汇编程序进行第二遍扫描时,如果发现 ASSERT 条件不成立,汇编器将报告错误信息。
   1)下面的程序在 Top 和Temp相等时报告错误
    ASSERT       Top<>Temp
   2)当label1 代表的地址大于label2 所代表的地址时报告错误
    ASSERT       label1<=label2
2.诊断信息显示伪操作INFO 或“!”
  INFO numeric-expression,string-expression
  数字表达式,在汇编时计算。如果 numeric-expression 的值为0,则通过第一遍汇编并在第二遍汇编时报告“string-expression ”的内容;
如果 numeric-expression 的值不等于0 ,则在第一遍汇编过程中报告“string-expression ”的内容并中止汇编。
  下面的程序在第二遍汇编扫描时报告版本信息,并判断cont1 和cont2 的关系。
   INFO   0,”verion  1.0” ;在第二遍扫描时,报告版本信息
   IF cont1>cont2  ;如果cont1>cont2
    INFO   1,”cont1>cont2”  ;则在第一遍扫描时报告“cont1>cont2 ”
   ENDIF
  
3.设置列表选项伪操作OPT(略,见资料)
4.插入标题伪操作TTL和SUBT(略,见资料)
5.1.5 指令集选择伪操作
1.ARM和CODE32(意义相同)
  AREA   Init ,CODE,READONLY
  ……
  CODE32 ; 通知编译器其后的指令为 32位的ARM 指令
  LDR R0 ,=NEXT+1 ; 将跳转地址放入寄存器 R0
  BX R0  ; 程序跳转到新的位置执行,并将处理器切换到 Thumb工作状态
  ……
  CODE16 ; 通知编译器其后的指令为 16位的Thumb指令
  NEXT LDR R3 ,=0x3FF
  ……
  END ; 程序结束
2.THUMB
  AREA   ChangeState,CODE,READONLY
  ARM
  ;下面的指令在ARM 状态下开始执行
  LDR r0,=start+1  ;取出跳转地址,设置状态标志位
  BX r0 ;跳转并切换程序状态
  THUMB ;下面的指令序列为 Thumb-2指令
  Gsthing     PROC  ;
  B   {pc}+2  ;#0x8002
  B   {pc}+4  ;#0x8004
3.CODE16见上
5.1.6 杂项伪操作
1.ALIGN
2.AREA
  定义了一个代码段,段名为Init ,属性为只读。
   AREA   Init, CODE, READONLY
   ENTRY                   ; 指定应用程序的入口点
   ...
   END
3.END
4.ENTRY
5.EQU
  Test EQU 50 ;定义标号Test 的值为50
  Addr EQU 0x55,CODE32  ;定义Addr 的值为0x55,且该处为32位的ARM 指令。
6.EXPORT(或GLOBAL)
  AREA   Init,CODE ,READONLY
  EXPORT        Stest               ; 声明一个可全局引用的标号Stest
  ……
  END
7.EXPORTAS
  AREA data1, DATA  ;定义新的数据段 data1
  AREA data2, DATA  ;定义新的数据段 data2
  EXPORTAS data2, data1  ;data2 中定义的符号将会出现在data1的符号表中
  one EQU 2
  EXPORTAS one, two
  EXPORT one  ; 符号two 将在目标文件中以“2”的形式出现
8.EXTERN
  通知编译器当前文件要引用标号 Main,但Main在其他源文件中。
   AREA   Init ,CODE,READONLY
   EXTERN       Main  ; 通知编译器当前文件要引用标号 Main,但Main 在其他源文件中定义
   ……
   END
9.GET(或INCLUDE)
  格式 GET filename
   AREA   Init ,CODE,READONLY
   GET a1.s  ;通知编译器当前源文件包含 a1.s
   GET   C:\a2.s ;通知编译器当前源文件包含 C:\ a2.s
   ……
   END
10.IMPORT
  IMPORT 和EXTERN 用法相似
11.INCBIN
  使用INCBIN 可以包含任何格式的文件
   AREA   Init ,CODE,READONLY
   INCBIN       a1.dat  ; 通知编译器当前源文件包含文件 a1.dat
   INCBIN C:\a2.txt ; 通知编译器当前源文件包含文件 C:\a2.txt
   ……
   END
12.KEEP
  label   ADC r2,r3,r4
       KEEP   label     ;指示汇编器将label包含进符号表
       ADD r2,r2,r5
13.NOFP
  NOFP 伪操作用于告诉汇编器当前源文件中不包含浮点运算指令。
14.REQUIRE
  REQUIRE伪操作用于定义段之间的相互依赖关系。
15.REQUIRE8 (或PRESERVE8)
  REQUIRE8 伪操作指定当前文件堆栈要求8 字节对齐。它将传递 REQ8 连接选项到 ARM连接器。
  PRESERVE8 伪操作指定当前文件堆栈要求8 字节对齐。它将传递 RPES8 连接选项到 ARM连接器。
  连接器保证要求8 字节堆栈对齐的代码相互调用。
  语法格式如下。
   REQUIRE8 {bool}
   PRESERVE8 {bool}
  其中bool 的取值为{TRUE} 或{FALSE}
16.RN
  regname    RN  11  ; 将寄存器11命名为regname
  sqr4       RN  r6   ; 将寄存器6 定义为spr4
17.ROUT
  ROUT 伪操作用于给一个局部变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为所
在的AREA,而使用ROUT 后,局部变量的作为范围为当前ROUT 和下一个ROUT 之间。
     ; code
  routineA   ROUT  ;定义局部标号的有效范围,范围名称为 routineA
     ; code
  3routineA  ; code  ;routineA 范围内的局部标号 3
     ; code
     BEQ    %4routineA  ;若条件成立,跳转到 routineA 范围内的局部号4
     ; code
     BGE    %3  ;若条件成立,跳转到 routineA 范围内的局部标号3
     ; code
  4routineA  ; code  ;范围A 内的局部标号4
     ; code
  otherstuff ROUT  ;定义新的局部标号有效范围
5.1.7 结构描述伪操作 感觉有点抽象,先不做笔记,记下一些指令名词
FRAME ADDRESS、FRAME POP、FRAME PUSH、FRAME REGISTER、FRAME RESTORE、FRAME RETURN ADDRESS、FRAME SAVE、
FRAME STATE REMEMBER、FRAME STATE RESTORE、FRAME UNWIND ON、FRAME UNWIND OFF、FUNCTION或PROC、ENDFUNC或ENDP
5.2 ARM汇编器所支持的伪指令 略
5.3 汇编语言文件格式
①符号名不应与指令或伪指令同名。如果要使用和指令或伪指令同名的符号要用双斜杠“|| ”将其括起
来,如“||ASSERT|| ”。
②; 直接的变量替换
   GBLS   add4ff
   ;
  add4ff SETS   "ADD  r4,r4,#0xFF"  ;给变量add4ff 赋值
   $add4ff.00 ;引用变量
   ; codes
   ADD r4,r4,#0xFF00
  ; 有特殊符号的变量替换
    GBLS   s1
    GBLS   s2
    GBLS   fixup
    GBLA   count
    ;
  count     SETA    14
  s1         SETS   "a$$b$count"  ;s1 =a$b0000000E
  s2         SETS   "abc"
  fixup      SETS   "|xy$s2.z|"  ;fixup= |xyabcz|
  |C$$code|  MOV    r4,#16  ;label= C$$code
③当在字符串中包含“$ ”或引号时,可以用“$$”表示“$ ”, 用两个双引号表示一个双引号。
④下面列出了被ARM汇编器预定义的寄存器名。列出目前常见的部分
   r0 ~r15 和R0~R15(15个通用寄存器)。
   a1~a4(参数、结果或临时寄存器,同 r0 ~r3 )。
   v1~v8 (变量寄存器,同 r4 ~r11)。
   sb 和SB(静态基址寄存器,同r9 )。
   sl 和SL(栈顶指针寄存器,同 r10)。
   fp 和FP(帧指针寄存器,同r11)。
   ip 和IP (过程调用中间临时寄存器,同r12)。
   sp 和SP(栈指针寄存器,同r13)。
   lr 和LR(连接寄存器,同 r14)。
   pc 和PC(程序计数器,同r15)。
  cpsr 和CPSR(当前程序状态寄存器)。
  spsr 和SPSR(保留程序状态寄存器)。
⑤汇编语言的程序结构
  AREA  Init ,CODE,READONLY
  ENTRY
Start
  LDR R0,=0x3FF5000
  LDR R1,0xFF
  STR R1,[R0]
  LDR R0,=0x3FF5008
  LDR R1,0x01
  STR R1,[R0]
  BL  PRINT_TEXT ;子程序调用
  ……
  PRINT_TEXT
  ……
  MOV   PC,BL ;子程序返回
  ……
  END
5.4 ARM汇编编译器的使用
5.5 ARM汇编程序设计举例(参见pdf)

六、C、C++和汇编混合编程、
6.1 内联汇编和嵌入型汇编的使用
6.1.1 内联汇编
1.内联汇编支持大部分的ARM指令,但不支持带状态转移的跳转指令,如BX和BLX指令。其他一些注意事项可以见手册,或者未来遇到问题再记。
  错误示例
   int f(int x)
   {
    __asm  
    {
     STMFD sp!, {r0}  //保存r0不合法,因为在读之前没有对寄存器写操作
     ADD r0, x, 1
     EOR x, r0, x
     LDMFD sp!, {r0}  //不需要恢复寄存器
    }
    return x;
   }
  修改后,正确示例
   int f(int x)
   {
    int r0;
    __asm
    {
     ADD r0, x, 1
     EOR x, r0, x
    }
    return x;
   }
几个例子,见手册。
  ①字符串拷贝 ②中断使能 ③分隔符的计算
2.内联汇编中的限制 见手册
3.内联汇编中的虚拟寄存器
  内联汇编指令中使用了寄存器r0,但对于C编译器,指令中出现的r0只是一个变量,并非实际的物理寄存器r0,当程序运行时,
可能是由物理寄存器r1来存放r0所代表的值。
4.内联汇编中的指令展开
5.内联汇编中的常数
  如果在指令中使用了"#",则其后的表达式必为常数。
6.内联汇编指令对标志位的影响 需要多多注意
7.内联汇编指令中的操作数
8.函数调用和分支跳转
9.内嵌汇编中的标号
10.内嵌汇编器版本间的差异
  不同版本的ARM编译器对内联汇编程序的语法要求有显著差异。在具体使用时请参见相关文档。
6.1.2 嵌入式汇编
1.嵌入式汇编语言语法 注意区分和内嵌汇编的区别
  嵌入式汇编
   #include
   __asm void my_strcpy(const char *src, const char *dst) {
    loop
     LDRB r3, [r0], #1
     STRB r3, [r1], #1
     CMP  r3, #0
     BNE  loop
     MOV  pc, lr
   }
  内嵌汇编
   #include
   void my_strcpy(const char *src, char *dst)
   {
    int ch;
    __asm
    {
    loop:
     LDRB   ch, [src], #1
     STRB   ch, [dst], #1
     CMP    ch, #0
     BNE    loop
    }
   }
   int main(void)
   {
    const char *a = "Hello world!";
    char b[20];
    my_strcpy (a, b);
    printf("Original string: '%s'\n", a);
    printf("Copied string: '%s'\n", b);
    return 0;
   }
2.嵌入式汇编语言的使用限制
3.嵌入式汇编程序表达式和C或C++表达式之间的差异
4.嵌入式汇编函数的生成
5.关键字_cpp
  可用__cpp关键字从汇编代码中访问C或C++ 的编译时常量表达式,其中包括含有外部链接的数据或函数地址。
  LDR r0, =__cpp(&some_variable)
  LDR r1, =__cpp(some_function)
  BL __cpp(some_function)
  MOV r0, #__cpp(some_constant_expr)
6.手动重复解决方案
7.相关基类的关键字
8.成员类的关键字
9.调用非静态成员函数
10.嵌入式汇编版本间的差异
6.1.3 内联汇编中使用SP、LR和PC寄存器的遗留问题
使用RVCT v2.0 版本及其以后的编译器,要在C或C++代码中使用汇编访问SP、LR和PC寄存器可以使用下面几种方法。
  ①使用嵌入式汇编代码。嵌入式汇编支持所有的ARM指令,同时允许在代码中访问SP、LR和PC寄存器。
  ②在内联汇编中使用以下一些指令。
   __current_pc():访问PC寄存器。
   __current_sp():访问SP寄存器。
   __return_address():访问LR,返回地址寄存器。
下面给出了两个访问SP、LR和PC寄存器的典型实例程序。
  ①使用编译器给定的指令。
   void printReg()
   {
    unsigned int spReg, lrReg, pcReg;
    __asm {
     MOV spReg, __current_sp()
     MOV pcReg, __current_pc()
     MOV lrReg, __return_address()
    }
    printf("SP = 0x%X\n",spReg);
    printf("PC = 0x%X\n",pcReg);
    printf("LR = 0x%X\n",lrReg);
   }
  ②使用嵌入式汇编
   __asm void func()
   {
    MOV r0, lr
    ...
    BX lr
   }
6.1.4 内联汇编代码与嵌入式汇编代码之间的差异
6.2 从汇编代码访问C全局变量
对于无符号变量,使用:
  LDRB/STRB:用于char 型;
  LDRH/STRH:用于short 型(对于ARM体系结构 v3,使用两个 LDRB/STRB 指令);
  LDR/STR:用于int 型。
下面的例子将整型全局变量globvar的地址载入r1、将该地址中包含的值载入r0、将它与2相加,然后将新值存回globvar中。
  PRESERVE8
  AREA    globals,CODE,READONLY
  EXPORT   asmsubroutine
  IMPORT   globvar
asmsubroutine
  LDR  r1, =globvar   ;read address of globvar into
                   ;r1 from literal pool 从内存池中读取globvar变量的地址,加载到r1中
  LDR  r0, [r1]
  ADD  r0, r0, #2
  STR  r0, [r1]
  MOV  pc, lr
  END
6.3 在C++中使用C头文件
要包括标准的系统 C  头文件,如 stdio.h,不必进行任何特殊操作。
要包含自己的C 头文件,用户必须将#include 命令包在 extern "C"语句中。
6.4 C、C++和ARM汇编语言之间的调用
6.4.1 相互调用的一般规则
6.4.2 C++的特定信息
6.4.3 混合编程调用举例
(1)从C调用汇编语言
  下面的程序显示如何在C程序中调用汇编语言子程序,该段代码实现了将一个字符串复制到另一个字符串。
   #include
   extern void strcopy(char *d, const char *s);
   int main()
   {  const char *srcstr = "First string - source ";
    char dststr[] = "Second string - destination ";
   
    printf("Before copying:\n");
    printf(" %s\n  %s\n",srcstr,dststr);
    strcopy(dststr,srcstr);
    printf("After copying:\n");
     printf(" %s\n  %s\n",srcstr,dststr);
    return (0);
   }
  下面为调用的汇编程序
   PRESERVE8
   AREA   SCopy, CODE, READONLY
   EXPORT strcopy
  strcopy ;r0指向目的字符串
    ;r1指向源字符串
   LDRB r2, [r1],#1  ;加载字节并更新源字符串指针地址
   STRB r2, [r0],#1  ;存储字节并更新目的字符串指针地址
   CMP r2, #0  ;判断是否为字符串结尾
   BNE strcopy  ;如果不是,程序跳转到 strcopy继续拷贝
   MOV pc,lr  ;程序返回
   END
  按以下步骤从命令行编译该示例:
   ① 输入armasm -g scopy.s 编译汇编语言源代码
   ② 输入armcc -c -g strtest.c 编译C源代码
   ③ 输入armlink strtest.o sc opy.o -o strtest 链接目标文件。
   ④ 将ELF/DWARF2兼容调试器与相应调试目标配合使用,运行映像。
(2)汇编语言调用C程序
  下面的例子显示了如何从汇编语言调用C程序。
  下面的子程序段定义了C语言函数。
   int g(int a, int b, int c, int d, int e)
   {
               return a + b + c + d + e;
   }
  下面的程序段显示了汇编语言调用。假设程序进入f 时,r0中的值为i。
   ; int f(int i) { return g(i, 2*i, 3*i, 4*i, 5*i); }
   PRESERVE8
   EXPORT f
   AREA f, CODE, READONLY
   IMPORT g  // 声明C 程序g()
   STR lr, [sp, #-4]!  // 保存返回地址 lr
   ADD r1, r0, r0  // 计算2*i(第2 个参数)
   ADD r2, r1, r0  // 计算3*i(第3 个参数)
   ADD r3, r1, r2  // 计算5*i
   STR r3, [sp, #-4]!  // 第五个参数通过堆栈传递
   ADD r3, r1, r1  // 计算4*i(第4 个参数)
   BL g  // 调用C 程序
   ADD sp, sp, #4  // 从堆栈中删除第5 个参数
   LDR pc, [sp], #4  // 返回
   END
(3)从C++调用C
  下面的例子显示了如何从C++程序中调用C函数。
  下面的C++ 程序调用了C程序。
   struct S {  // 本结构没有基类和虚函数
    S(int s):i(s) { }
    int i;
   };
   extern "C" void cfunc(S *);
   // 被调用的C 函数使用extern “C”声明
   int f(){
    S s(2);  // 初始化 's'
    cfunc(&s); // 调用C 函数 'cfunc' 将改变 's'
    return si*3;
   }
  下面显示了被调用的C程序代码。
   struct S {
    int i;
   };
   void cfunc(struct S *p) {
   
    p->i += 5;
   }
(4)从C++中调用汇编
  下面的例子显示了如何从C++中调用汇编程序。
  下面的例子为调用汇编程序的C++代码。
   struct S {  // 本结果没有基类和虚拟函数
      //        
    S(int s) : i(s) { }
    int i;
   };
   extern "C" void asmfunc(S *);  // 声明被调用的汇编函数

   int f() {
    S s(2);  //  初始化结构体 's'
    asmfunc(&s); //  调用汇编子程序 'asmfunc'
    return s.i * 3;
   }
  下面是被调用的汇编程序。
   PRESERVE8
   AREA Asm, CODE
   EXPORT asmfunc
  asmfunc               // 被调用的汇编程序定义
   LDR r1, [r0]      
   ADD r1, r1, #5
   STR r1, [r0]
   MOV pc, lr
   END
(5)从C中调用C++  
  下面的例子显示了如何从C++代码中调用C程序。
  下面的代码显示了被调用C++代码。
   struct S {       //  本结构没有基类和虚拟函数
    S(int s) : i(s) { }
    int i;
   };
   extern "C" void cppfunc(S *p) {   
   // 定义被调用的C++代码
   // 连接了C功能
   p->i += 5;               //
   }
  调用了C++代码的C函数。
   struct S {
    int i;
   };
   extern void cppfunc(struct S *p);
   
   int f(void) {
    struct S s;
    s.i = 2;               
    cppfunc(&s);           
    return s.i * 3;
   }
(6)从汇编中调用C++程序
  下面的代码显示了如何从汇编中调用C++程序。
  下面是被调用的C++程序。
   struct S {          // 本结构没有基类和虚拟函数
    S(int s) : i(s) { }
    int i;
   };
   extern "C" void cppfunc(S * p) {
   // 定义被调用的C++ 功能
   // 功能函数体
    p->i += 5;
   }
  在汇编语言中,声明要调用的C++功能,使用带连接的跳转指令调用C++功能。
   AREA Asm, CODE
   IMPORT cppfunc  ; 声明被调用的 C++  函数名

   EXPORT  f
  f
   STMFD sp!,{lr}
   MOV   r0,#2
   STR   r0,[sp,#-4]!  ; 初始化结构体
   MOV   r0,sp  ; 调用参数为指向结构体的指针
   BL    cppfunc  ; 调用C++ 功能'cppfunc'

   LDR   r0, [sp], #4
   ADD   r0, r0, r0,LSL #1
   LDMFD sp!,{pc}
   END
(7)在C和C++函数间传递参数
  下面的例子显示了如何在C和C++函数间传递参数。
  下面的代码为C++函数。
   extern "C" int cfunc(const int&);
   // 声明被调用的C 函数
   extern "C" int cppfunc(const int& r) {
   // 定义将被C 调用的C++ 函数
    return 7 * r;
   }
   int f() {
    int i = 3;
    return cfunc(i);  // 相C 函数传参
   }
  下面为C函数。
   extern int cppfunc(const int*);   
   
   int cfunc(const int *p) {      
   
    int k = *p + 4;