ARM汇编程序设计学习笔记(二)

汇编语言程序设计笔记 (二)

为了学习android系统而努力。

这个笔记,记录arm汇编语言的伪指令。

伪指令的作用仅在完成汇编过程,一旦汇编结束,伪指令的使命就完成了。

有如下几种伪指令:

  • 符号定义伪指令
  • 数据定义伪指令
  • 汇编控制伪指令
  • 信息报告伪指令
  • 宏控制以及其他伪指令

2.1 符号定义伪指令

指示符功能
GBLA声明和初始化一个全局算术变量,初始值为0
GBLL声明和初始化一个全局的逻辑变量,初始值为{FALSE}
GBLS声明和初始化一个全局的字符串变量,初始值为空
LCLA声明和初始化一个局部算术变量,初始值为0,局部算术变量只能在宏中声明
LCLL声明和初始化一个局部逻辑变量,初始值为{FALSE},局部变量只能在宏中声明
LCLS声明和初始化一个局部字符串变量,初始字号为空,局部字符串变量只能在宏中进行声明
SETA给一个局部或全局算术变量赋值
SETL给一个局部或全局逻辑变量赋值
SETS给一个局部或全局字符串变量赋值
RLIST给寄存器集命名
CN给一个协处理器命名
CP给一个特定协处理命名,,协处理器号为0-15
DN给一个双精度VFP寄存器命名
SN给一个单精度VFP寄存器命名
FN给一个特定的浮点寄存器命名

例子:

        MACRO
$label  message $a
        LCLS    err;声明一个局部的字符变量
err     SETS    "error no: ";给这个字符变量赋值,注意顶格
$label
        INFO    0, "err":CC::STR:$a
        MEND
	GBLA test1
test1 SETA 0xaa;注意顶格
	GBLL test2
test2 SETL {TRUE};注意顶格
	GBLS test3
test3 SETS "test3";注意顶格

2.2 数据定义伪指令

用于为数据分配存储单元,同时也可以完成已分配存储单元的初始化

DCB:用于分配一片连续的字节存储单元并用伪指令中指定的表达式初始化。其中,表达式可以使用双引号的字符串或0-255的数字,DCB可以用“=”代替。内存分配字节数由表达式决定,例子如下:

str DCB "this is test!"

注意,它不像c字符串,arm汇编器不会末尾放入一个’\0’字符。为了构造一个c字符串,可以参考下面写法:

C_string DCB "C_string",0

DCW/DCWU:DCW分配一片连续的半字存储单元,存储空间半字对齐。DCWU跟DCW类似,只是分配的存储单元不严格半字对齐。

DCD/DCDU:分配连续的字存储单元,存储空间字对齐。DCD可以用&符号代替。DCDU跟DCD一样,只不过不严格字对齐。

DCQ/DCQU:DCQ分配连续的8字节存储单元,存储空间字对齐。DCQU跟DCQ一样,只不过不严格字对齐。

DCFD/DCFDU:DCFD用于为双精度浮点数分配一片连续的存储单元。存储空间字对齐,每个双精度浮点数占据两个字单元。DCFDU不严格按照字对齐

DCFS/DCFSU:DCFS为单精度浮点分配连续的存储单元,存储空间字对齐,每个单精度浮点数使用一个字单元。DCFSU不严格按照字对齐

SPACE:分配连续的存储区域并初始化为0,后面跟的是需要分配的字节数。SPACE可以用%代替。例子:

data1 SPACE 255;定义一个255字节的存储区域,存取区域以0值填充

MAP:定义一个结构化的内存表的首地址,可用‘^’代替。格式如下:

MAP 表达式  [,基址寄存器]

当基址寄存器不存在时,表达式的值即为内存表的首地址;

当基址寄存器存在时,内存表的首地址为表达式的值与基址寄存器的和。

FIELD:用于定义一个结构化内存表中的数据,可以用‘#’代替

注意:MAP和FIELD仅用于定义数据结构,不分配实际的存储单元。FIELD指令和MAP指令配合使用用来定义结构化的内存表。MAP指令定义内存表的首地址,FIELD指令定义内存表中的各个数据域,并可以为每个数据域指定一个标号供其他的指令使用

使用如下:

    MAP 0x30000000;注意前面的tab
A FIELD 4;定义数据A,长度为4字节
B FIELD 4;定义数据B,长度为4字节
C FIELD 4;定义数据C,长度为4字节

2.3 汇编控制指令

MACRO、MEND:宏定义指令,格式如下:

MACRO
[$标号] 宏名  [$参数1,$参数2。。。]
MEND

MACRO表示一个宏的开始,MEND表示一个宏的结束,MACRO,MEND之间的代码被视为一个整体,称为宏定义体。例子如下:

	MACRO
$label moveR1toten
	MOV R1,#10
	MEND

IF、ELSE、ENDIF:根据逻辑表达式的结果,决定是否编译时加入某个指令序列。格式如下:

IF 逻辑表达式
        代码段1
ELSE
        代码段2
ENDIF

IF,ELSE,ENDIF分别可以用’[’,’|’,’]'代替

例子如下:

        IF test2={TRUE}
		MOV R0,#1
	ELSE
		MOV R0,#0
	ENDIF

WHILE、WEND:根据逻辑表达式的结果,决定是否要循环执行这个代码。格式如下:

WHILE 逻辑表达式
        代码
WEND

例子如下:

	WHILE test1 <= 4
		MOV R0,#1
	WEND

MEXIT:用于从宏中退出。例子如下:

        MACRO
$abc    example abc     $param1,$param2
        ; code
        WHILE condition1
            ; code
            IF condition2
                ; code
                MEXIT
            ELSE
                ; code
            ENDIF
        WEND
        ; code
        MEND

2.4 其他伪指令

ASSERT:格式如下:

ASSERT 逻辑表达式

说明:如果逻辑表达式不满足,则编译器报错

例子如下:

        ASSERT label1 <= label2

ALIGN:可以通过填充字节使当前的位置满足一定的对齐方式。格式如下:

ALIGN [表达式[,偏移量]]

其中表达式的值为2的幂,用于指定对齐方式。如果没有指定表达式,则编译器会将当前位置对齐到下一个字的位置。如果存在偏移量,这当前位置的自动对齐到:2的表达式次方+偏移量。

        AREA    cacheable, CODE, ALIGN=3
rout1   ; code         ; aligned on 8-byte boundary
        ; code
        MOV     pc,lr  ; aligned only on 4-byte boundary
        ALIGN   8      ; now aligned on 8-byte boundary
rout2   ; code

AREA:格式如下:

AREA 段名  属性,。。。

说明:AREA用于定义一个代码段、数据段或者特定属性的段。如果段名以数字开头,那么该段名需要用“|”字符括起来,如|7wolf|。

常见属性如下:

  1. DATA:定义数据段,默认为READWRITE
  2. CODE:定义代码段,默认为READONLY
  3. NOINIT:指定本数据段仅仅保留内存单元,而没有将各初始化值写入内存单元
  4. READWRITE:表示本段可读写
  5. ALIGN=表达式:对齐方式为2的表达式次方,
  6. COMMON:定义一个通用段,这个段不包含代码和数据。

例子如下:

        AREA    Example,CODE,READONLY   ; An example code section.
            ; code

CODE16/CODE32:CODE16指示编译器后面的代码为16位的thumb指令。CODE32指示编译器后面的代码为32位的ARM指令

在使用ARM指令和Thumb指令混合编程的代码里,可用这两条伪指令进行切换。但他们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。

ENTRY:用于指定汇编程序的入口。在一个完整的汇编程序中至少要有一个ENTRY,程序中也可以有多个,此时,程序的真正入口点可在链接时指定,但在一个源文件里最多只能有一个ENTRY或者没有NETRY

例子:

        AREA   ARMex, CODE, READONLY
        ENTRY      ; Entry point for the application.
        EXPORT ep1 ; Export the symbol so the linker can find it
ep1                ; in the object file.
        ; code
        END

END: 告诉编译器已经到了源程序的结尾

EQU:用于将程序中的数字常量、标号、基于寄存器的值赋予一个等效的名称,这一点类似于C语言的#define。可用“*”代替

格式如下:

名称  EQU   表达式[,类型]

如果表达式为32位的常量,我们可以指定表达式的数据类型,类型域可以有如下三种:CODE16,CODE32,DATA

例如:

NUM EQU 100;

EXPORT:在程序中声明一个全局的标号,该标号可以再其他的文件中引用。用户也可以用GLOBAL代替EXPORT。标号在程序中区分大小写

	AREA asm,CODE,READONLY
	ENTRY
	EXPORT start
start
	MOV R0,#10

IMPORT:告诉编译器标号要在当前源文件中使用,但标号是在其他源文件中定义的。不管当前源文件是否使用过该标号,这个标号都会加入到当前源文件的符号表中。标号在程序中区分大小写。

	AREA asm,CODE,READONLY
	import start

EXTERN:告诉编译器所有使用的符号要在当前源文件中引用,但该标号是在其他源文件中定义的。与IMPORT不同的是,如果当前源文件实际没有引用该标号,该标号就不会被加入到当前文件的符号表中。

RN:用于给一个寄存器定义一个别名。格式如下:

名称   RN   表达式

例如:

Temp RN R0;

GET/INCLUDE:GET将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置展开进行汇编处理。INCLUDE和GET作用等效。

INCBIN:将一个可执行文件或任意数据文件包含到当前的源文件中,编译时被包含的文件不作任何变动的存放在当前文件中,不进行汇编处理,编译器从后面开始继续处理。

NOP:空操作伪指令

2.5 汇编语言的其他语法

语句格式

[标签] 指令/伪指令/伪操作   操作数   [;语句的注释]

说明:

  1. 所有的标签必须在一行的开头顶格写,前面不能留空格,后面也不能跟冒号
  2. arm汇编器对标识符的大小写敏感,书写标号及指令时字母的大小写要一致。
  3. 注释使用“;”,注释的内容从分号到该行的结尾结束

标签代表地址时,又称为标号,标号分为段内标号和段外标号。段内标号的地址值在汇编时确定,段外编号的地址在链接时确定

在程序段中,标号代表其所在位置与段首地址的偏移量。根据程序计数器和偏移量计算地址即程序相对寻址。

此外在宏定义中也可以使用局部符号。局部符号是0-99的十进制数开始,可以重复定义。

局部标号引用格式:

%{F|B}{A|T} N{routname}

%-表示局部标号引用操作
F-指示编译器只向前搜索
B-指示编译器只向后搜索
A-指示编译器搜索宏的所有嵌套层次
T-指示编译器搜索宏的当前层
N-局部标号,0~99
routname-局部标号作用范围的名称,由ROUT伪指令定义

数字常量3种表示方法:

  1. 十进制数,
  2. 十六进制数,如0xAA
  3. n进制数,形如n-xxx,n的范围为2-9,xxx是具体数字。

字符常量:如’a’,‘b’,由单引号括起来

字符串常量:如"name",由双引号括起来

逻辑常量:{TRUE},{FALSE},注意一定要带大括号

  1. 如果在字符串变量的前面有一个$符号,在汇编时编译器将用该字符串变量的内容来代替该字符串变量
  2. 如果在数字变量前面有一个$字符,在汇编时会将该数字变量的值转换为十六进制的字符串,并用该十六进制的字符串代替。
  3. 使用‘.’来表示字符串变量名的结束。例子如下:
  4. 如果需要使用$字符,则可以用“$$”代替
  5. 在两个’|'之间的‘$’并不进行变量的代换,但如果‘|’放在双引号内,则进行变量代换
S1 SETS "test!"
S2 SETS "this is a $S1";字符串变量S2的值代替为“this is a test!

这部分笔记已经是几个月之前所写,未做仔细校对,若发现问题,请指出,谢谢。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值