RAM学习路线03-RAM汇编语言

1. ARM汇编程序的结构

1.1 段

        ARM的汇编语言程序由段组成,段是相对独立的指令或数据单位,每个段由AREA伪指令定义,并定义段的属性:READONLY(只读)或READWRITE(读写)。AREA用于定义一个代码段或数据段,若段名以数字开头则该段名需要用“|”括起来,如“1_data”。
        在一个汇编程序中,至少包含一个或多个代码段;0个或多个初始化数据段;0个或多个未初始化数据段。

如汇编程序Hello.S:

@ Hello.S

/**
 * 1.代码段
 * 由CODE属性定义,READONLY表示只读
 */
AREA |.text|,CODE,READONLY
    ENTRY
    EXPORT sqr
    EXPORT num
sqr
    MUL r1, r0, r0
    MOV r0, r1
    MOV lr, pc

/**
 * 2.数据段
 * 由DATA属性定义,READWRITE表示读写
 */
AREA |.data|,DATA,READWRITE
num
    DCD 10

/**
 * 3.未初始化数据段
 * 由NOINIT属性定义,READWRITE表示读写
 */
AREA |.bass|,NOINIT,READWRITE
data
    SPACE 1024
    END
1.2 标识符

        在汇编程序里标识符用于表示指令或数据的地址,如Hello.S中的sqr和num。局部标识符是0~99的十进制数,局部标识符的引用格式为:
%{F|B}{A|T} N(0~99)
如:

2
  CMP r3, r1
  STRCC r2, [r3], #4
  BCC  %B2

其中,

  • F——只向前(forwards)搜索局部标识符
  • B——只向后(backwards)搜索局部标识符
  • A——在所有的宏里搜索局部标识符
  • T——只在当前宏里搜索局部标识符
    如果F和B都没指定,那么搜索的顺序先向后再向前;如果A和T都没指定,那么搜索的顺序只从但前宏向高层宏搜索。
    局部标识符的作用范围在当前ROUT和下一个ROUT之间,如:
LocalStart ROUT
  ...
1
  ...
2
  ...
LocalEnd ROUT

所有的标识符必须在一行开头顶格写不能有空格,标识符后不能加“:“。ARM汇编器对标识符的大小写敏感,标识符大小写要一致。

1.3 程序入口

        在C语言中程序的入口是main,而在ARM汇编语言中,用ENTRY来表示程序的入口。在一个源文件中只能指定一个入口,但在一个完整的项目中至少有一个入口,可以有多个。当有多个入口时,程序的真正入口点由链接器通过entry参数指定。
注意:在ARM汇编语言里,用ENTRY(大小写均可)来表示程序的入口。
在用armlink链接时,可以用参数entry来指定入口。

1.4 程序出口

        在汇编源文件里,用END来表示汇编源程序的结束。

1.5 包含其它汇编源文件

        GET将一个源文件包含到当前源文件中,并将被包含的源文件在当前位置展开进行汇编处理。INCLUDEGET有相同的作用。GET/INCLUDE只能用于包含源文件,包含其它类型的文件(如目标文件或数据文件)则需要用到INCBIN伪指令。

1.6 引用外部标识符

        IMPORT告诉编译器它后面的这个标识符要在当前源文件中使用,但是在其它源文件中定义的。如果在链接时找不到该标识符,链接就会报错:

Error:L6218E:Undefined sysmbol xxxx (referred from param.o)

如果在IMPORT时使用WEAK参数,在链接时即使找不到也不会报错,如:

IMPORT printff, WEAK

如果该标识符是B或BL的地址,如果链接时找不到,B或BL就无处可跳,那么B或BL指令就是一条空指令:NOP。
除B或BL的其它情况如果链接时找不到,标识符的值会被置成0。

2. ARM汇编程序的常量、变量

常量

  • 数字常量:num SETA 100ram_start DCD 0xC000000LDR R1, =&1000FFFF
  • 字符串常量:DCB “Hello,World”, CR
  • 逻辑常量:{TRUE}、{FALSE}
  • 字符常量:LDR R4, = #'B'
    变量的定义和赋值
  • 数字变量:LCLS num_1GBLA num_g
  • 字符串变量:LCLS s_lGBLS s_g
  • 逻辑变量:LCLL debug_lGBLL debug_g
    变量替换
    变量的替换通过操作符“$”来实现。

3. ARM汇编程序的运算符和表达式

数字表达式

  • 算术运算符:加、减、乘、除
  • 移位运算符:ROL、ROR、SHL、SHR
  • 逻辑运算符:AND、OR、NOT、EOR
    逻辑表达式
    逻辑表达式

    字符串表达式
  • LEN: 字符串长度: LEN : S
  • CHA: 0~255的整数转换成一个字符: CHA : M
  • STR: 转换成一个字符串: STR : N
  • LEFT: 返回某个字符串左端的一个字符串S : LEFT : N
  • RIGHT: 返回某个字符串右端的一个字符串S : RIGHT : N
  • CC: 将两个字符串连成一个字符串S1 : CC : S2
  • DEF: 用于判断是否定义了某个符号: DEF : S

4. ARM汇编程序的数据定义

        数据定义的目的是为特定的数据分配存储单元,同时对分配的存储单元进行初始化。
LTORG
LTORG用于声明一个文字池(literal pool)。

文字池是什么?
它是镶嵌在代码中的一段存储空间,用来存放常量。
用它来做什么?
文字池不能远离LDR指令,它必须在LDR指令前后4KB地址范围。

编译器会在每一个段的末尾放一个缺省的文字池,如果这个段很长这个缺省文字池和LDR的距离可能会超过4KB,那么LDR就失去了装载的功能。这种情况下就要自己在适当的地方加入新的LTORG来生命一个新的文字池,当然,条件时LDR的周围。

DCB、DCW、DCD和SPACE
DCB:用于分配一片连续的字节存储单元,并用指定的表达式初始化。可用“=”代替。

str DCB "This is a test"

DCW:用于分配一片连续的半字存储单元,并用指定的表达式初始化。

Halfword DCW 1,2,3

DCD:用于分配一片连续的字存储单元,并用指定的表达式初始化。

word DCD 4,5,6

SPACE:用于分配一片连续的存储区域并初始化0。可用“%”代替。

space SPACE 100

MAP和FILED
MAP:用于定义一个结构化内存表的首地址。MAP可用“^”代替。

MAP 0x100, R9   @ 内存表的首地址为:R9 + 0x100

FIELD:用于定于一个机构话内存表的数据域。FIELD可用“#”代替。语法格式如下:

{label} FELD expr

其中lable是可选的,当指令中包含这一项时,label的值为当前内存表的位置计数器{VAR}的值。expr表示本数据域在内存中所占的字节数,当汇编编译器处理了FIELD伪操作后,内存表计数器的值将加上expr。

5. ARM汇编程序的控制结构

选择结构

IF 逻辑表达式
    指令1
    ELSE
    指令2
ENDIF

循环结构

WHILE 逻辑表达式
    指令
WEND

6. ARM汇编程序实例

(1)计算一个以0结束的字符串包含的字符个数

PRESERVE8                       @ 伪指令指示当前文件保持堆栈为 8 字节对齐
    AREA str, CODE, READONLY
    ENTRY

start
    LDR R0, =val1
    MOV R1, #-1

count
    ADD R1, R1, #1              @ R1 = R1 + 1
    LDRB R2, [R0], #1           @ 读取字节数据,源地址加 1
    CMP R2, #0
    BNE count

    STR R1, rel
    SWI &11


    AREA data1, DATA            @ 数据段1
val1
    DCB "This is a example", 0
    ALIGN


    AREA data2, DATA            @ 数据段2
rel
    DCD 0

    END

(2)把字符串前面的0用空格替换

PRESERVE8
    AREA zero, CODE, READONLY  @ 段zero
blank EQU ''                   @ EQU 伪指令为数字常量,基于寄存器的值和程序中的标号定义一个名称。*与EQU同义
zero  EQU '0'                  @ EQU 伪指令的作用类似于 C 语言中的#define
    ENTRY

/**
 * 读取字符串的地址
 * 把空格和0装载到寄存器
 */
start
    LDR R0, =data
    MOV R1, #zero
    MOV R3, #blank

/**
 * 读取字符串的字符
 * 判断是否为0
 */
replace
    LDRB R2, [R0}, #1
    CMP R2, R1
    BNE done           /* 不为0,直接中断 */

    /**
     * 为0,把空格装载到0的位置,然后跳转循环
     */
    SUB R0, R0, #1
    STRB R3, [R0]
    ADD R0, R0, #1
    BAL replace

done
    SWI &11


    AREA data, DATA    @ 段data
val1 DCB "00000001"
    ALIGN

    END

7. 伪操作

        伪操作不像机器指令那样在计算机运行期间由机器执行,它是在源程序汇编期间由汇编程序处理的。

7.1 符号定义伪操作

符号定义(Symbol definition)伪操作主要用于定义变量定义寄存器名称等。包括以下伪操作:

  • GBLA、GBLL和GBLS        声明全局变量
  • LBLA、LBLL和LBLS        声明局部变量
  • SETA、SETL和SETS        给变量赋值
  • RLIST        为通用寄存器列表定义名称
  • CN        为协处理器的寄存器定义名称
  • CP        为协处理器定义名称
  • DN和SN        为VFP的寄存器定义名称
  • FN        为FPA的浮点寄存器定义名称
7.2 数据定义伪操作

数据定义(Data definition)伪操作包括:

  • LTORG        声明一个数据缓冲池的开始
  • MAP        定义一个结构化的内存表的首地址
  • FIELD        定义结构化的内存表中的一个数据域
  • SPACE        分配一块内存单元,并用0初始化
  • DCB        分配一段字节的内存单元,并用执行的数据初始化
  • DCD及DCDU        分配一段字的内存单元,并用指定的数据初始化
  • DCDO        分配一段字的内存单元,并将该单元的内容初始化成该单元相对于静态基值寄存器的偏移量
  • DCFD及DCFDU       分配一段双字的内存单元,并用双精度的浮点数据初始化
  • DCFS及DCFSU        分配一段字的内存单元,并用单精度的浮点数据初始化
  • DCI        分配一段字节的内存单元,用指定的数据初始化,指定内存单元中存放的是代码,而不是数据
  • DCQ及DCQU        分配一段双字的内存单元,并用64位的整数数据初始化
  • DCW及DCWU        分配一段半字的内存单元,并用指定的数据初始化
  • DATA        在代码段中使用数据。现已不再使用,只用于向前兼容。
7.3 汇编控制伪操作

汇编控制(Assembly control)伪操作包括:

  • IF、ELSE及ENDIF
  • WHILE及WEND
  • MACRO及MEND
  • MEXIT
7.4 框架描述伪操作

栈中数据描述伪操作主要用于调试。

7.5 信息报告伪操作

信息报告(Reporting)伪操作如下:

  • ASSERT
  • INFO
  • OPT
  • TTL及SUBL
7.6 其它伪操作

这些杂类的伪操作包括:

  • ALIGN
  • AREA
  • CODE16及CODE32
  • ENTRY及END
  • EQU
  • EXPORT及GLOBAL
  • EXTERN
  • GET及INCLUDE
  • IMPORT
  • INCBIN
  • KEEP
  • NOFP
  • REQUIRE
  • REQUIRE8及PRESERVE8
  • RN
  • ROUT

8. 伪指令

        伪指令在汇编编译器对源程序进行汇编处理时,被替换成对应的ARM或者Thumb指令(序列)。ARM伪指令包括:ADR、ADRL、LDR、NOP。

  • ADR:小范围的地址读取伪指令。该指令将基于PC的地址值或基于寄存器的地址值读取到寄存器中。
  • ADRL:中等范围的地址读取伪指令。比ADR伪指令可以读取更大范围的地址。
  • LDR:大范围的地址读取伪指令。将一个32位的常数或者一个地址值读取到寄存器中。
  • NOP:空操作伪指令。在汇编时将被替换成一个空操作。

你可能感兴趣的:(RAM学习路线03-RAM汇编语言)