【vfp精品源码栏目提醒】:网学会员--在 vfp精品源码编辑为广大网友搜集整理了:【精品】5-6章 汇编语言程序设计-final - 小学课件绩等信息,祝愿广大网友取得需要的信息,参考学习。
第 5 章 ARM 汇编语言程序设计 5.1 ARM 汇编语言伪指令 伪指令并不是真正的 arm 指令或者 thumb 指令,编译时伪指令会被处理成对应的 arm或 thumb 指令序列。
不同的编译器提供的伪指令不同,这里主要介绍 ARM 编译器常用的伪指令。
5.1.1 符号定义伪指令1 全局变量声明 GBLA 声明一个全局算术变量,并将其初始化为 0。
GBLL 声明一个全局逻辑变量,并将其初始化为FALSE。
GBLS 声明一个全局字符串变量,并将其初始化为 NULL。
语法格式: GBLA variable GBLL variable GBLS variable 例: GBLA num1 GBLL logic_x GBLS string_y2 局部变量声明 LCLA 声明一个局部算术变量,并将其初始化为 0。
LCLL 声明一个局部逻辑变量,并将其初始化为FALSE。
LCLS 声明一个局部字符串变量,并将其初始化为 NULL。
语法格式: LCLA variable LCLL variable LCLS variable 例: LCLA num1 LCLL logic_x LCLS string_y3 变量赋值 SETA 给一个算术变量赋值。
SETL 给一个逻辑变量赋值。
SETS 给一个字符串变量赋值。
语法格式: variable SETA expr variable SETL expr variable SETS expr 例: num1 SETA 0xff logic_x SETL TRUE string_y SETS “stringtest”4 RLIST 为通用寄存器列表定义名称,定义的名称可以在 LDM/STM 指令中使用。
列表中的寄存器以逗号分隔,连续编号的寄存器可以用“-”包括,例如:r7-r9 表示寄存器 r7、r8、r9。
语法格式: name RLIST list of registers 例: reg_list RLIST r0-r4r6r7-r9 STMFD sp reg_list 将 ; r0-r4r6r7-r9 的内容压入满降序栈中保存5 RN 为寄存器定义名称。
语法格式: name RN expr 例: sp RN r136 CN、CP CN 为协处理器的寄存器定义名称。
CP 为协处理器定义名称。
语法格式: name CN expr name CP expr 例: power CN 6 dmu CP 67 DN、SN、FN DN 为一个双精度的
VFP 寄存器定义名称。
SN 为一个单精度的
VFP 寄存器定义名称。
FN 为一个 FPA 浮点寄存器定义名称。
语法格式: name DN expr name SN expr name FN expr 例: height DN 6 width SN 16 lower FN 68 EQU 为数字常量、标号或寄存器定义一个字符名称。
语法格式: name EQU expr type 当 expr 表示绝对地址时,可以使用 type 选项,type 可以为:CODE16(表示 Thumb 指令地址) 、CODE32(表示 ARM 指令地址)或 DATA(表示数据区地址) 。
例如: addr_a EQU 0x20 CODE32 ;将绝对地址 0x20 赋值给 addr_a,并标记为32 位地址。
5.1.2 空间分配伪指令1 SPACE SPACE 伪指令常常用来分配一块内存区域供程序使用,并且将这个内存区域初始化为0。
该伪指令可以用“”代替。
语法格式: label SPACE expr ;其中 label 为可选项 例: dataroom SPACE 256 ;以 dataroom 为起始地址,分配 256 个字节的内存单元,并初始化为 02 DCB DCB 用于定义并初始化一个或多个字节的内存区域。
该伪指令可以用“”代替。
语法格式: label DCB expr expr … 例: data1 DCB 135 string DCB “hello”0 ;构造一个以 NULL 结尾的字符串,字符串的起始地址为 string 注意,当 DCB 后面紧跟一个指令时,为了使指令保持字对齐,需要用到 ALIGN 伪指令,ALIGN 伪指令将在下面讲到。
3 DCD、DCDU DCD 和 DCDU 用于定义并初始化一个或多个字的内存区域,其中 DCD 可以用“amp”代替。
DCD 和 DCDU 的区别在于 DCD 可以保证分配的内存单元是严格的字对齐的,而DCDU 不能保证。
语法格式: label DCD expr expr … 例: data1 DCD 0246 data2 DCDU 1354 DCDO DCDO 用于定义并初始化一个或多个字的内存区域,且保证分配的内存单元是字对齐的。
语法格式: label1 DCDO label2 DCDO 与 DCD 的区别在于 DCDO 将内存单元 label1 初始化为 DCDO 后面的标号 label2相对于静态基址寄存器 R9(sb)的偏移量。
例: IMPORT externlabel data1 DCDO externlabel ;将地址为 data1 的字单元初始化为标号 externlabel 相对于 R9 的偏移量5 DCFD、DCFDU、DCFS、DCFSU DCFD 用于为双精度的浮点数分配字对齐的内存单元, 每个双精度浮点数占据两个字单元。
语法格式: label DCFD fpliteral fpliteral … DCFDU 与 DCFD 的不同之处在于 DCFDU 分配的内存单元并不是严格字对齐的。
DCFS 用于为单精度的浮点数分配字对齐的内存单元, 每个单精度浮点数占据 1 个字单元。
DCFSU 与 DCFS 的不同之处在于 DCFSU 分配的内存单元并不是严格字对齐的。
例: data1 DCFD 2.1E204-3E-100 data2 DCFDU 236-.53E25 data3 DCFS 2.1E2-3E-106 DCI DCI 用于分配并初始化一段内存单元,且认为内存单元中的数值为指令数据。
当 DCI位于 ARM 代码中的时候,分配的内存是严格字对齐的;当 DCI 位于 Thumb 代码中的时候,分配的内存是半字对齐的。
语法格式: label DCI expr expr …7 DCQ、DCQU 以及 DCW、DCWU DCQ 和 DCQU 用于分配双字(8 个字节)的内存单元,其中 DCQ 是严格字对齐的,DCQU 不能保证严格字对齐。
DCW 和 DCWU 用于分配半字(2 个字节)的内存单元,其中 DCW 是严格字对齐的,DCWU 不能保证严格字对齐。
语法格式: label DCQ expr expr …8 LTORG LTORG 用于声明一个数据缓冲池 (literal pool)的开始。
通常放在无条件跳转指令之后,或者子程序返回指令之后,以免处理器错误的将数据缓冲池中的数据作为指令来执行。
例: func1 : ;代码 MOV PC lr LTORG data SPACE 256 ;从 data 标号开始预留 256 个字节的内存单元 END9 MAP、FIELD MAP 和 FIELD 用于描述结构化的内存表。
其中 MAP 用于定义结构化内存表的首地址,MAP 也可以用“”来表示;FIELD 用于定义结构化内存表中的数据域,FIELD 可以用“”来表示。
语法格式: MAP expr base-register ;内存表的起始地址为 exprbase-register,其中 base-register 为可选项,若没有此项,则内存表的起始地址为 expr。
例: MAP 4000 ;内存表的起始地址为 4000Integer FIELD 4 ;从 4000 开始为 Integer 分配 4 个字节String FIELD 64 ;从 4004 开始为 String 分配 64 个字节Doublea FIEDL 8 ;从 4068 开始为 Doublea 分配 8 个字节5.1.3 汇编控制伪指令1 IF、ELSE 和 ENDIF IF、ELSE 和 ENDIF 用于控制汇编器是否将一段汇编指令的源代码包含在汇编语言程序内。
例: IF FLAG1 : ;如果条件满足,则包含此段代码 ELSE : ;如果条件不满足,则包含此段代码 ENDIF2 WHILE、WEND WHILE 和 WEND 用来控制编译器根据条件重复汇编一段源代码。
例: WHILE FLAG1 : ;如果条件满足,则重复汇编此段代码 WEND3 MACRO、MEND、MEXIT MACRO 和 MEND 用来定义一段宏体。
所谓宏实际上是一段汇编语言指令序列,将多条指令定义成一条宏语句,编译时宏语句被展开。
例: MACRO label submac sum num1num2 label CMP num1num2 label.1 SUBGE sum num1num2 label.2 SUB sum num2num1 MEND 在这个例子中定义了一个名为“submac”的宏,其中label、label.1、label.2 为宏中语句的标号,label 并不是必需的,只有当宏中的其他语句需要标号时,label 才是必需的。
其他语句的标号可以根据需要设置成其他的名字, 如label.1、label.2 可以定义成label.sub、label.ressub 等。
sum、num1、num2 是宏的参数,宏中所有的变量都是以“”开头的。
若在程序中引用宏 submac: submac r0 r1 r2 则编译后展开为下面的语句: CMP r1 r2 SUBGE r0 r1 r2 SUB r0 r2 r1 使用宏时应注意,宏操作中不遵循 APCS 原则,寄存器也不会受到保护。
MEXIT 为从宏中跳出的伪指令。
5.1.4 格式控制伪指令1 ALIGN 通过填充 0 将当前的位置以某种形式对齐。
语法格式: ALIGN exproffset expr 通常为 2 的整数次幂,表示对齐的方式。
例如,当 expr 为 4 时,表示其后的指令对齐到下一个字的边界处;offset 表示在上述对齐方式下的偏移量。
若 ALIGN 后不带任何参数,则表示把当前位置对齐到下一个字的起始位置。
例如: ALIGN 4 1 (4 表示其后的指令以字 个字节) 为起始位置,且偏移量为 1。
若下一个字地址为 0xC0004,则下面指令的起始地址为 0xC0005。
2 CODE16、CODE32 CODE16 表示其后的指令为 16 位的 THUMB 指令。
CODE32 表示其后的指令为 32 位的 ARM 指令。
语法格式: CODE16 CODE323 AREA 定义一个代码段或数据段及其属性。
语法格式: AREA sectionname attrattr… sectionname 为代码段或数据段的名称。
若该名称以数字开头,则必须用“”括起来,如“2_name”。
还有一些代码段有约定的名称,如.text表示 C 语言编译器产生的代码段或者是 C 语言库相关的代码段。
attr 表示代码段的属性,各属性间用逗号分隔。
常用的属性有: CODE:指定为代码段。
DATA:指定为数据段。
READONLY:只读段,代码段的默认属性为 READONLY。
READWRITE:可读可写段,数据段的默认属性为 READWRITE。
通常可以用 AREA 将程序分为多个 ELF 格式的段。
段名称可以相同,这时这些同名的段被放在同一个 ELF 段中。
一个大的程序可以包含多个代码段和数据段,一个汇编程序至少应包含一个段。
例如: AREA name1,CODE,READONLY AREA name1,DATA,READWRITE4 ENTRY 指定程序的入口点。
语法格式: ENTRY 注意:一个程序(可以包含多个源文件)中可以有多个 ENTRY,但一个源文件中最多只能有一个 ENTRY,可以没有。
例如: AREA name1,CODE,READONLY ENTRY :5 END 告知编译器源程序结束。
语法格式: END6 EXPORT 或 GLOBAL、IMPORT EXPORT 申明一个符号可以被其他文件引用, 与 相当于申明了一个全局变量, GLOBAL同义。
IMPORT 引入其他源文件中定义的符号。
7 ROUT 定义局部标号的有效范围。
详细使用见下一小节的标号。
语法格式: routname ROUT ;routname 为作用范围的名称,可选项5.1.5 ARM 伪指令1 ADR 小范围的地址读取伪指令。
ADR 伪指令将基于 PC 相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。
对于非字对齐地址,ADR 的取值范围为 255 个字节,对于字对齐地址,ADR 的取值范围为 1020 个字节255 个字。
语法格式: ADR Rn label 汇编器通常将 ADR Rnlabel 伪指令处理成一条 ADD 指令或 SUB 指令,如果其地址不能通过一条 ADD 或 SUB 指令产生的话则汇编器报错。
2 ADRL 中等范围的地址读取伪指令。
ADRL 伪指令将基于 PC 相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。
对于非字对齐地址,ADRL 的取值范围为 64KB,对于字对齐地址,ADRL 的取值范围为 256KB。
语法格式: ADRL Rn label 汇编器通常将 ADRL Rnlabel 伪指令替换成两条合适的数据处理指令, 如果地址不能通过两条指令构造获得,则汇编器报错。
注意,ADR 或 ADRL 伪指令与引用的标号必须在同一个代码段中。
3 LDR 大范围的地址读取伪指令。
LDR 伪指令用于加载一个 32 位的立即数或一个地址值到寄存器中。
与 ADR 和 ADRL 不同,LDR 允许标号不在当前的代码段中。
语法格式: LDR Rn expr/label LDR 伪指令加载的立即数可以是一个任意的 32 位常数,汇编器会将其生成合适的指令。
主要有两种情况: (1)如果常数可以通过MOV或MVN指令获得,则编译器生成合适的MOV或MVN指令。
(2)如果常数不能通过 MOV 或 MVN 指令获得,则编译器首先检查这个常数是否在前面的任何一个数据池(LTORG 伪指令生成)中出现过,若出现过,则以此地址为常数寻址地址;若没有出现,则将数据放入紧邻的数据池中,然后产生一个基于 PC 的 LDR 指令,从数据池中读取数据。
此时必须保证数据池位于 LDR 伪指令 4KB 的寻址范围内,否则编译器报错。
下面以 loadcon.s 为例(位于安装路径 examplesarmasm 中),分号后面解释的指令为ARM 汇编器生成的指令。
例5.1: AREA Loadcon CODE READONLY ENTRY ;程序入口start BL func1 ;调用子程序func1 BL func2 ;调用子程序func2stop MOV r0 0x18 ;程序中止 LDR r1 0x20026 SWI 0x123456func1 LDR r0 42 ;gt MOV R0 42 LDR r1 0x55555555 ;gt LDR R1PC距数据池1的偏移量 LDR r2 0xFFFFFFFF ;gt MVN R2 0 MOV pc lr LTORG ;数据池1,汇编器将数据0x55555555放入此处func2 LDR r3 0x55555555 ;gt LDR R3 PC 距数据池1的偏移量 LDR r4 0x66666666 如 ; 果 0x66666666没有出现过,则编译失败, ;因为数据池2超出了LDR的寻址范围。
MOV pc lrLargeTable 4200 ;从当前地址起,分配4200个字节的内存空间并清零 END ;数据池2为空4 NOP 空操作伪指令。
语法格式: NOP5.1.6 Thumb 伪指令1 ADR 小范围的地址读取伪指令。
ADR 伪指令将基于 PC 相对偏移的地址值读取到寄存器中,偏移量必须是正数并小于 1KB。
Thumb 状态的 ADR 伪指令只能产生字对齐的地址。
语法格式: ADR Rn label2 LDR 大范围的地址读取伪指令。
LDR 伪指令用于加载一个 32 位的立即数或一个地址值到寄存器中。
语法格式: LDR Rn expr/label 其使用注意见 ARM 伪指令,不同之处在于数据池应保证位于 LDR 伪指令 1KB 的寻址范围内。
3 NOP 空操作伪指令。
语法格式: NOP 5.2 ARM 汇编语言语句格式 学习完 ARM 的指令系统后,让我们系统地看一下 ARM 汇编语言的语句格式。
标号指令或伪指令;注释 其中: 在 ARM 汇编语言中,符号必须从一行的行头开始,并且符号中不能包含空格。
符号可以为地址标号,也可以是某些伪指令中的变量或常量。
在 ARM 汇编语言中,指令不能从一行的行头开始,前面必须有空格或者符号。
注释以“;”开头,可以单独占据一行。
汇编器在汇编时忽略所有的注释。
在 ARM 汇编语言中,指令助记符必须统一用大写或者小写,不能在一个助记符中既有大写又有小写字母。
伪指令必须大写。
语句之间可以插入空行,以增加程序的可读性。
如果一条语句很长,可以分成若干行来写。
在一行的末尾用“”表示下一行将续在本行之后。
注意在“”之后不能再有其他任何字符。
5.2.1 ARM 汇编语言中的符号 上面提到 ARM 汇编语言中的符号可以为地址标号,也可以为变量或者是常量。
对于地址标号,当以数字开头时,其作用范围为当前段(当没有使用 ROUT 伪操作时) ,这种标号又称为局部标号。
符号的命名满足如下规则: (1)符号由大小写字母、数字以及下划线组成; (2)局部标号以数字开头,其他的符号均不能以数字开头; (3)符号区分大小写; (4)符号中所有的字符都是有意义的; (5)符号在其作用范围内必须唯一,即在其作用范围内不可有同名的符号; (6)程序中的符号不能与系统内部变量或者系统预定义的符号同名; (7)程序中的符号通常不要与指令助记符或者伪指令同名。
当程序中的符号与指令助记符或伪指令同名时,用双竖线将符号括起来,如require,这时的双竖线并不是符号的组成部分。
1 常量 ARM 中的常量有数字常量、逻辑常量、串常量和字符常量。
在 ARM 汇编语言中,使用 EQU 来定义常量,常量一经定义,其数值就不能再修改。
数字常量是一个 32 位的整数。
当作为无符号整数时,取值范围为 0 到 232-1;当作为有符号整数时,取值范围为-231~231-1。
汇编编译器并不区分一个数是无符号数还是有符号数,事实上-n 与 232-n 在内存中是同一个数。
进行大小比较时,认为数字常量都是无符号数。
数字常量常采用三种表示方式: (1)十进制表示。
例如:123。
(2)16 进制表示。
例如:0x7c。
其 (3) 进制表示 n_XXX, 中 n 为 2 到 9 之间的基数。
n 例如 8_123 表示的十进制数 83。
逻辑常量包括 TRUE 和 FALSE 两个取值,使用时必须写成TRUE和FALSE两种形式。
串常量是用双引号括起来的字符串, 中间可以包含空格。
如果串常量本身含有双引号或符,则必须书写两次。
例如: “abccba”表示的是字符串“abccba”。
字符常量是用单引号括起来的单一的字符。
2 变量及变量替换 ARM 中的变量有数字变量、逻辑变量和串变量三种,变量的类型在程序中不能改变。
数字变量的取值范围为数字常量和数字表达式所能表示的数值的范围。
逻辑变量的取值为TRUE和FALSE。
串变量的取值范围为串表达式可以表示的范围,如“abcd” 。
用 在 ARM 汇编语言中, GBLA、 GBLL、 用 GBLS 申明全局变量, LCLA、LCLL、LCLS申明局部变量,用 SETA、SETL、SETS 为变量赋值。
如果在串变量前面有一个字符,汇编时编译器会用该串变量的数值取代该串变量。
例如下面的指令汇编后 STR2 的值为 This is a pen。
GBLS STR1 GBLS STR2 STR1 SETS “pen” STR2 SETS “This is a STR1” 如果数字变量的前面有一个字符,汇编时编译器会将该数字变量的数值转换成十六进制的串,用它来取代后面的数字变量。
如果逻辑变量的前面有一个字符,汇编时编译器会将该逻辑变量替换成它的取值(TRUE 或者 FALSE) 。
如果程序中需要字符,则用来表示。
3 标号及局部标号 符号表示地址时常被称为标号,汇编器根据标号在程序中的位置为标号赋予相应的地址值。
根据地址值的生成方式可以把标号分为以下三种: (1)基于 PC 的标号:在汇编时处理成 PC 值加上或减去一个偏移量。
(2)基于寄存器的标号:在结构化的内存表(MAP)中通常含有标号,程序中内存表的首地址常常放在某一个寄存器中,则标号在汇编时被处理成寄存器的值加上一个偏移量,这种类型的标号称为基于寄存器的标号。
(3)绝对地址:连接时生成的标号地址值。
局部标号为 0-99 之间的十进制数字。
其语法格式如下: Nroutname ;其中 N 为 0-99 之间的数字,routname 为局部.
上一篇:
【精品】ARM集成开发环境介绍
下一篇:
思想品德教学中学生创新能力的培养