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

2019-11-15  本文已影响0人  forty_seven

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

其中,

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汇编程序的常量、变量

常量

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

数字表达式

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)伪操作主要用于定义变量定义寄存器名称等。包括以下伪操作:

7.2 数据定义伪操作

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

7.3 汇编控制伪操作

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

7.4 框架描述伪操作

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

7.5 信息报告伪操作

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

7.6 其它伪操作

这些杂类的伪操作包括:

8. 伪指令

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

上一篇下一篇

猜你喜欢

热点阅读