美文网首页
RAM学习路线03-RAM汇编语言

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

作者: forty_seven | 来源:发表于2019-11-15 10:18 被阅读0次

    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汇编语言

          本文链接:https://www.haomeiwen.com/subject/dujsbctx.html