02.汇编指令

    按照编译器不同,汇编分为两大量:一类是ADS的汇编程序,一类是GNU汇编格式任。

    以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。

    GNU汇编:http://web.mit.edu/gnu/doc/html/as_toc.html#SEC127


一、ARM指令  (带点的一般都是ARM GNU伪汇编指令)

        1. ".title xxx":指定汇编列表的标题。

            ".list":用来输出列表文件。

            ".type xxx":指定符号的类型。


        2. ".text":代码段。已编译程序的可执行机器码。

            ".rodata":只读数据段。const全局变量。比如printf语句中的格式串和开关(switch)语句的跳转表。

            ".data":数据段。已初始化的非零全局变量和静态变量。它会在main函数之前被处理。

            ".bss":未初始化(或初值为0)的全局变量。它在目标文件中不占实际的空间(不保存在bin文件中),是一个占位符。目标文件格式区分初始化和未初始化变量是为了提高空间效率。启动代码完成两方面:①未初始化变量的清0。②设置已初始化变量的初值。保存了BSS段和COMMON段(存放注释)的内容。

            ".symtab":符号表。存放被定义和引用的函数及全局变量。每个可重定位目标文件在.symtab中都有一张符号表。与编译器中的符号表不同,.symtab不包含局部变量的表目。不一定要通过-g编译程序,得到符号表信息。

            注释:为了让启动代码简单,编译链接器会把已初始化的变量放.data段,这个段的映像(包含了各个变量的初值)保存在“只读数据段”。启动代码复制这个映像到 .data 段,初始化所有变量。初始化为0的变量保存在bss段,未初始化变量保存在common段,链接时再将其放入bss段。启动代码调用 memset 把所有未初始化变量清0。

            ".rel.text":保存着一系列在.text中的位置的列表。这些位置在链接时被修改。这些位置通常保存着引用全局变量,或外部函数的指令。在运行时并不需要它以及下面的.rel.data。生成可执行文件ELF object时会去掉。除非使用者去显式指示链接器包含这些信息。

            ".rel.data":保存全局变量的重定位信息。如果一个全局变量的初始化值,是另一个全局变量的地址,或者是外部函数的地址时,它就需要被重定位。

            ".debug":调试符号表。其可以是程序中定义的:局部变量、类型定义、全局变量的定义和引用、C源文件。只有" -g "选项才会生成这张表。

            ".line":源程序中的行号和.text中机器指令间的映射。只有以-g选项调用编译驱动程序时才会生成。

            ".strtab":字符串表。包含.symtab和.debug中的符号表,和各个section的名字。字符串表是以null结尾的字符串序列。.debug和.symtab中,保存name的域,等于保存了一个偏移值。可以通过这个偏移值在字符串表里面找到相应字符串。(https://www.veryarm.com/23019.html)


        3. ".section":自定义一个段。.section section_name [, "flags"[, %type[,flag_specific_arguments]]]。每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。


        4. 注释:代码行中的注释符号: ‘@’。整行注释符号: ‘#’。语句分离符号: ‘ ; ’。直接操作数前缀: ‘#’ 或 ‘$’。


        5. "_start":汇编程序的缺省入口。如果想更改,到相应的链接脚本中去用ENTRY指明其他入口标志。标号可以直接认为是地址。

        6. ".globl/.global":定义一个全局符号,通常为ld使用。例如 .global _start(定义 _start 为外部程序可以访问的标签)。


        7. ".abort":停止汇编。

        8. ".string/.asciz/.ascii"、".byte"、".short"、".int"、".long"、".float"、".word"、".quad":定义一个类型并分配空间(字符串、字节(1 byte)、短整型(2 byte)、整型(4 byte)、长整型(4 byte)、浮点数、字(与系统位数有关。16位系统中是2字节)、quard word(4字))。用".string/.asciz"定义时要加双引号,用".ascii"定义时还要在末尾手动添加"\0"。

        9. ".align xxx, yyy":对齐方式。xxx表示对齐方式,4, 8,16或32。 yyy 表示填充的值。一般用于定义完字符串等类型之后的代码对齐。


        10. ".if" ".else" ".endif":条件预编译。if的变种如下:

        11. ".include "file" ":包含指定文件。可以把汇编常量定义放在头文件中。

        12. ".incbin "file"[,skip[,count]]":将二进制文件编译到当前文件中。skip是以字节为单位,从文件头部读取的偏移量。count是读取的字数。

        13. ".macro .endm":.macro定义的宏代码的开始, .endm宏代码的结束,.exitm跳出宏。若宏使用参数,则宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值。


        14. ".comm symbol, length":在bss段申请一段叫symbol的命名空间,长度为length。Ld连接器连接时会为它留出空间。

        15. ".rept x":重复定义x次其内的定义。用 .endr结束。

        16. "(.equ/.set) xxx, yyy":把符号定义成值。它不分配空间,相当于#define。或者用“ xxx equ yyy ”。

        17. ".req":为寄存器定义别名。

              ".unreq":取消寄存器别名。

        18. ".code":.code [16|32]: 指定指令代码产生的长度, 16表示Thumb指令, 32表示ARM指令。

        19. ".ltorg/.pool":当前往下的定义在归于当前段,并为之分配空间。即声明数据缓冲池。

        20. ".space <x> {,<aaa>}":分配x字节空间,并用aaa填充。缺省填充0。



二、进制的表示

        1. 二进制数以0b开头,其中字母也可以为大写

        2. 八进制数以0开始,如:0456,0123

        3. 十进制数以非0数字开头,如:123和9876

        4. 十六进制数以0x开头,如:0xabcd,0X123f

        5. 字符串常量用引号括起来,中间也可以使用转义字符。如: “You are welcome!\n”

        6. 当前地址以" . "表示

        7. 表达式:汇编程序中表达式可使用常数或数值。" - "取负数, " ~ "取补," < > "不相等," +、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| "跟C语言用法相似。



三、操作寄存器

        "ldr    r0,    [ r1 ]":读取地址 [ r1 ] 上的数据,并保存到 R0 寄存器中,长度为4字节。(方括号内的表示地址)

        "ldr    r0,    [ r1, #4 ]":将地址为 r1+4 的内存单元数据读取到 r1 中。

        "ldr    r0,    [ r1 ],    #4:将地址为 [ r1 ] 的内存单元数据读取到 r0 中,然后 r1 = r1+4。

        ★.在32位ARM指令中会用某些位表示当前执行的指令(LDR等)及寄存器(R0等)。剩下的位数不足以表示任意值,所以引入伪指令。伪指令会被拆分成ARM指令。

        ldr 伪指令的三种用法:

                ①. ldr r0, = 0x10:给 r0 赋值为0x10

                ②. ldr pc, =Label:将 Label 符号放入pc寄存器中。编译时会替换成一条 ldr 指令和一条 dcd 伪指令。符号地址由编译器指定。

                ③. ldr pc, Label_addr:读取存储器中 Label_addr 符号所表示的地址中的值放入 pc 。多读一次存储器。


        "str    r0,    [ r1 ]":把 r0 寄存器上的值,保存到地址 [ r1 ] 的地方,长度为4字节。

        "str    r0,    [ r1, #4 ]":将 r0 的数据保存到地址为 r1+4 的内存单元中。

        "str    r0,    [ r1 ],    #4":将 r0 的数据保存到地址为 r1 的内存单元中,然后 r1=r1+4。


        "ldmia    r0,    { r1, r2, r3, r4 }   stmdb    r0!,    { r1, r2, r3, r4 }":一次设置多个寄存器。后增,即先操作后增加。32位芯片的话,增加/减少的单位是4。按寄存器编号依次操作寄存器,高编号寄存器存放在高地址。(ld/st) + m + (i/d/f/e) + (a/b) = (读取/设置)+ 多个寄存器 +(增加/减少/满/空)+(之后/之前)。" "表示此寄存器的值等于最终被修改后的值。否则最终值等于初始值。ldm命令末尾的" "表示将spsr写回到cpsr,用于异常返回后工作状态的恢复,不允许在用户模式和系统模式下运行。

        当堆栈指针指向最后压入的数据时为满堆栈,当指针指向下一个将要放入数据的空位置时为空堆栈。当堆栈由低地址向高地址生成时称递增堆栈,当堆栈由高地址向低地址生成时称递减堆栈。下列是入栈和出栈的指令对:

                Full descending :    stmdb--ldmia  或  stmfd--ldmfd  

                Full ascending :      stmib--ldmda  或  stmfa--ldmfa  

                Empty descending :stmda--ldmib  或  stmed--ldmed  

                Empty ascending :  stmia--ldmdb  或  stmea--ldmea  

        注意:虽然ARM处理器核对于两种生长方式的堆栈均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈。所以STMFD等指令用的最多。

        "adr    r0,    xxx":将基于PC相对偏移的地址值读取到 r0。编译时ADR伪指令会被替换。通常是用ADD或SUB指令。若不能用一条指令实现,则会产生错误,编译失败。


        "mrs    r1,    cpsr":读出 cpsr 中的值到 r1 中。

        "msr    cpsr,    r1":将 r1 寄存器中的值恢复到 r1 中。

        "swi    xxx":执行软中断。xxx 为24位的二进制数,表示处理不同软中断。


        "mov    R0,    R1":把 R1 的值赋给 R0 。

        "mov    R0,    [ R1 ]":把 R1 地址的值赋给 R0 。

        "mov    R0,    #0x10":把立即数 0x10 赋给 R0 。立即数是小于0xff(65535)的数,如果大于65535,则用ldr指令赋值。

        "mvn    r0,    #0":把0取反(即-1)传给 r0。

        注意:ARM是RISC结构,数据在内存和CPU间的移动只能通过ldr/str指令。mov只是在寄存器间移动数据,或把立即数移动到寄存器中。而X86中没有ldr这种指令。因为X86的mov指令可以将数据从内存中移到寄存器中。


        "nop":空操作指令。可以用作延时。

        "add    r0,    r1,    r2":r0 = r1 + r2。

        "adds    r0,    r1,    r2":r0 = r1 + r2。s表示把进位结果写入cpsr。sub同理。

        "sub    r0,    r1,    r2":r0 = r1 - r2。

        "mul    r0,    r1,    r2": r0 = r1 * r2。


<opcode>{<cond>}{S} <Rd>,<Rn>{,<shifter_operand>}

        对shifter_operand参数的位移。

        左右移:http://www.eeworld.com.cn/mcu/2015/0930/article_22693.html


        "tst    r0,    r1":位比较。先按位与并把结果写入CPSR,供下一句使用。结果为1,则设置标志位zero=0,会执行bne语句。否则不执行bne。beq则相反。

        "cmp    xxx,    yyy":两值相减,只改标志位。标志位存入CPSR供下一句语句使用。

        (分支指令)

        "b    xxx":简单的跳转,即调用子程序。跳转到到目标标号处执行。跳转范围是当前指令的前后32M。

        "bl    xxx":带链接的程序跳转,即要带返回地址。跳转前将当前PC-4(返回地址)保存至R14(LR)。子程序返回时会执行 MOV PC, LR。跳转范围是当前指令的前后32M。

        "bx{<condition>}  <rx>":<condition>为指令执行的条件码,缺省时无条件执行。<rx>寄存器中为跳转的目标地址。当<rx>寄存器bit[0]为0时,目标地址处的指令为ARM指令;当bit[0]为1时,则为Thumb指令。(ARM汇编)

        注意:反汇编文件里,用 B或BL 等跳某个值,只是方便查看,并不是真的跳转。

        (条件执行指令)

        "bne    xxx":数据跳转指令,标志寄存器中Z标志位不等于零时,跳转到BNE后标签处。

        "beq    xxx":数据跳转指令,标志寄存器中Z标志位等于零时,跳转到BEQ后标签处。

        注意:bne 1f,或beq 1b。1f:程序之后的"1"标号(forward)。1b:程序之前的"1"标号(before)。

         bvs:  Branch if overflow(溢出) Set

         bvc:  Branch if overflow(溢出) Clear

         bhi:   Branch if HIgher

         bls:   Branch if Lower or the Same

         bpl:   Branch if Plus

         bmi:   Branch if MInus

         bcs:   Branch if Carry Set(进位设置)

         bcc:   Branch if Carry Clear(进位清除)

         bge:   Branch if Greater than or Equal

         bgt:   Branch if Greater Than

         ble:   Branch if Less than or Equal

         blt:   Branch if Less Than

         bleq:  Branch with Link if Equal(带返回链接的判断型跳转)

         bllt:  Branch with Link if Less Than(带返回链接的判断型跳转)

        "moveq    r1, #0": 如果上一句条件执行码<condition>判断为相等,则执行mov指令。ARM汇编指令。

        "movgt    r1, #0":如果上一句条件执行码<condition>判断左边大,则执行mov指令。ARM汇编指令。


        "and    r0,    r1":r0 &= r1。按位与。

        "and    r0,    r1,    r2":r0 = r1 & r2。

        "orr    r0,    r1":r0 |= r1。按位或。

        "orr    r0,    r1,    r2":r0 = r1 | r2。

        "not    r0":r0 = ! r0。按位取反。指令的执行不影响任何标志位。

        "bic    r0,    r1":r0 &= ~r1。按位清零。

        "bic    r0,    r1,    #%1011":r0 = r1 & 4。%表示二进制,0x表示十六进制。

        "orn    r0,    r1":r0 = r0 | ~r1。按位或反。

        "eor    r0,    r1":r0 ^= r1。按位异或(不同为1,相同为0)。


1、注释行以“@”代替“;”

2、伪操作符替换:INCLUDE 替换成 .INCLUDE

TCLK2 EQU PB25 替换成 .equ TCLK2, PB25

EXPORT 替换成 .global

IMPORT 替换成 .extern

DCD 替换成 .long

IF :DEF: 替换成 .IFDEF

ELSE 替换成 .ELSE

ENDIF 替换成 .ENDIF

:OR: 替换成 |

:SHL: 替换成 <<

END 替换成 .end

符号定义后要加":"号

AREA Word, CODE, READONLY --> .text

AREA Block, DATA, READWRITE --> .data

CODE32 --> .arm

CODE16 --> .thumb

LTORG --> .ltorg

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270