04ARM汇编

汇编语言中的符号

  • 1.指令: 能够编译生成一条32位的机器码,且能被CPU识别和执行
  • 2.伪指令:本身不是指令,编译器可以将其替换成若干条等效指令
  • 3.伪操作:不会生成代码,只是在编译之前告诉编译器怎么编译

ARM指令

  • 1.数据处理指令: 数学运算、逻辑运算
  • 2.跳转指令: 实现程序的跳转,本质就是修改了PC寄存器
  • 3.Load/Srore指令: 访问(读写)内存
  • 4.状态寄存器传送指令:访问(读写)CPSR寄存器
  • 5.软中断指令: 触发软中断异常
  • 6.协处理器指令: 操控协处理器的指令

随手写一个例子

.text			            @表示当前段为代码段
.global _start		        @声明_start为全局符号
_start:				        @汇编程序的入口
    MOV R1, #1              @ R1 = 1
    MOV R2, R1              @ R2 = R1

STOP:	
    B STOP		            @死循环,防止程序跑飞	

.end				        @汇编程序的结束

数据处理指令

指令:能够编译生成一条32位的机器码,且能被CPU识别和执行
数据处理指令:数学运算、逻辑运算

  • 数据搬移指令

    MOV R1, #1          @ R1 = 1
    MOV R2, R1          @ R2 = R1
    MVN R0, #0xFF       @ R0 = ~0xFF(R0 = 0xFFFFFF00)
    
  • 立即数
    立即数的本质就是包含在指令当中的数,属于指令的一部分
    立即数的优点:取指的时候就可以将其读取到CPU,不用单独去内存读取,速度快
    立即数的缺点:不能是任意的32位的数字,有局限性

    @ MOV R0, #0x12345678 错误:不是立即数
    MOV R0, #0x12
    
    @ 编译器替换
    MOV R0, #0xFFFFFFFF @ MVN R0,#0x00000000
    
  • 数据运算指令基本格式
    <操作码> <目标寄存器> <第一操作寄存器> <第二操作数>
    操作码: 指示执行哪种运算
    目标寄存器: 存储运算结果
    第一操作寄存器: 第一个参与运算的数据(只能是寄存器)
    第二操作数: 第二个参与运算的数据(可以是寄存器或立即数)

  • 加法指令

    MOV R2, #5
    MOV R3, #3
    ADD R1, R2, R3      @ R1 = R2 + R3
    ADD R1, R2, #5      @ R1 = R2 + 5
    
  • 减法指令

    SUB R1, R2, R3      @ R1 = R2 - R3
    SUB R1, R2, #3      @ R1 = R2 - 3
    
  • 逆向减法指令

    RSB R1, R2, #3      @ R1 = 3 - R2
    
  • 乘法指令 注意:乘法指令只能是两个寄存器相乘

    MUL R1, R2, R3      @ R1 = R2 * R3
    
  • 按位与指令

    AND R1, R2, R3      @ R1 = R2 & R3
    
  • 按位或指令

    ORR R1, R2, R3      @ R1 = R2 | R3
    
  • 按位异或指令

    EOR R1, R2, R3      @ sR1 = R2 ^ R3
    
  • 左移指令

    LSL R1, R2, R3      @ R1 = (R2 << R3)
    
  • 右移指令

    LSR R1, R2, R3      @ R1 = (R2 >> R3)
    
  • 位清零指令 第二操作数中的哪一位为1,就将第一操作寄存器的中哪一位清零,然后将结果写入目标寄存器

    MOV R2, #0xFF
    BIC R1, R2, #0x0F   @ 把R2最后四位清零,结果存储到R1中
    
  • 数据运算指令的格式扩展

    MOV R2, #3
    MOV R1, R2, LSL #1  @ R1 = (R2 << 1)
    
  • 数据运算指令对条件位(N、Z、C、V)的影响
    默认情况下数据运算不会对条件位产生影响,在指令后加 后缀"S" 才可以影响

  • 带进位的加法指令
    两个64位的数据做加法运算

    @ 第一个数
    @ 0x00000001 FFFFFFFF
    @ 第二个数
    @ 0x00000002 00000005
    
    MOV R1, #0xFFFFFFFF     @ 第一个数的低32位放在R1
    MOV R2, #0x00000001     @ 第一个数的高32位放在R2
    MOV R3, #0x00000005     @ 第二个数的低32位放在R3
    MOV R4, #0x00000002     @ 第二个数的高32位放在R4
    ADDS R5, R1, R3         @ 运算结果的低32位放在R5
    ADC  R6, R2, R4         @ 运算结果的高32位放在R6
                            @ 本质:R6 = R2 + R4 + 'C'
    
  • 带借位的减法指令

    @第一个数
    @0x00000002 00000001
    @第二个数
    @0x00000001 00000005
    
    MOV R1, #0x00000001
    MOV R2, #0x00000002
    MOV R3, #0x00000005
    MOV R4, #0x00000001
    SUBS R5, R1, R3
    SBC  R6, R2, R4         @ 本质:R6 = R2 - R4 - '!C'
    

跳转指令

直接修改PC寄存器的值 (不建议使用,需要自己计算目标指令的绝对地址)

MAIN:
	MOV R1, #1
	MOV R2, #2
	MOV R3, #3
	MOV PC, #0x18
	MOV R4, #4
	MOV R5, #5	
FUNC:
	MOV R6, #6
	MOV R7, #7
	MOV R8, #8
  • 不带返回的跳转指令 本质就是将PC寄存器的值修改成跳转标号下指令的地址

    MAIN:
            MOV R1, #1
            MOV R2, #2
            MOV R3, #3
            B   FUNC
            MOV R4, #4
            MOV R5, #5	
    FUNC:
            MOV R6, #6
            MOV R7, #7
            MOV R8, #8
    
  • 带返回的跳转指令 本质就是将PC寄存器的值修改成跳转标号下指令的地址,同时将跳转指令下一条指令的地址存储到LR寄存器

    MAIN:
            MOV R1, #1
            MOV R2, #2
            MOV R3, #3
            BL  FUNC
            MOV R4, #4
            MOV R5, #5	
    FUNC:
            MOV R6, #6
            MOV R7, #7
            MOV R8, #8
            MOV PC, LR
    

比较指令

  • CMP指令的本质就是一条减法指令(SUBS),只是没有将运算结果存入目标(N Z C V)寄存器
        MOV R1, #1
        MOV R2, #2
        CMP R1, R2
        
        @ BEQ FUNC  @ 执行逻辑:if(EQ){B FUNC}	本质:if(Z==1){B FUNC}
        BNE FUNC    @ 执行逻辑:if(NQ){B FUNC}	本质:if(Z==0){B FUNC}
    
        MOV R3, #3
        MOV R4, #4
        MOV R5, #5
    FUNC:
        MOV R6, #6
        MOV R7, #7
    
  • ARM指令集中大多数指令都可以带条件码后缀
    MOV R1, #1
    MOV R2, #2
    CMP R1, R2
    MOVGT R3, #3
    

访问内存指令

  • 写内存
    MOV R1, #0xFF000000
    MOV R2, #0x40000000
    STR R1, [R2] 
    @ -->将R1寄存器中的数据写入到R2指向的内存空间
    
  • 读内存
    LDR R3, [R2]
    @ <--将R2指向的内存空间中的数据读取到R3寄存器
    
  • 读/写指定的数据类型
    MOV R1, #0xFFFFFFFF
    MOV R2, #0x40000000
    STRB R1, [R2]   @ 将R1寄存器中的数据的Bit[7:0]写入到R2指向的内存空间    (Byte)
    STRH R1, [R2]   @ 将R1寄存器中的数据的Bit[15:0]写入到R2指向的内存空间   (halfword)
    STR  R1, [R2]   @ 将R1寄存器中的数据的Bit[31:0]写入到R2指向的内存空间   (word)
    
    LDR指令同样支持以上后缀,以读取指定的数据类型

寻址方式

  • 立即寻址
    (立即数寻址)(参数来自于机器码)
    MOV R1, #1      @r1 = 1
    @ 或者
    ADD R1, R2, #1  @ r1 = r2 + 1
    
  • 寄存器寻址
    参数来自于寄存器
    ADD R1, R2, R3
    
  • 寄存器移位寻址
    先移位再使用
    MOV R1, R2, LSL #1
    
  • 寄存器间接寻址
    通过寄存器来操作内存
    STR R1, [R2]
    
  • 基址加变址寻址
    MOV R1, #0xFFFFFFFF
    MOV R2, #0x40000000
    MOV R3, #4
    STR R1, [R2,R3]         @ 将R1寄存器中的数据写入到R2+R3指向的内存空间
    STR R1, [R2,R3,LSL #1]  @ 将R1寄存器中的数据写入到R2+(R3<<1)指向的内存空间
    
    • 基址加变址寻址的索引方式
      • 前索引
        MOV R1, #0xFFFFFFFF
        MOV R2, #0x40000000
        STR R1, [R2,#8]     @ 将R1寄存器中的数据写入到R2+8指向的内存空间
        
      • 后索引
        MOV R1, #0xFFFFFFFF
        MOV R2, #0x40000000
        STR R1, [R2],#8     @ 将R1寄存器中的数据写入到R2指向的内存空间,然后R2自增8
        
      • 自动索引
        MOV R1, #0xFFFFFFFF
        MOV R2, #0x40000000
        STR R1, [R2,#8]!    @ 将R1寄存器中的数据写入到R2+8指向的内存空间,然后R2自增8
        
      以上寻址方式和索引方式同样适用于LDR

专用指令

  • 状态寄存器传送指令:访问(读写)CPSR寄存器

    @ 读CPSR
    MRS R1, CPSR        @ R1 = CPSR
    
    @ 写CPSR
    MSR CPSR, #0x10     @CPSR = 0x10
    
    @ 在USER模式下不能随意修改CPSR,因为USER模式属于非特权模式
    MSR CPSR, #0xD3
    
  • 软中断指令:触发软中断

    @ 异常向量表********************************
    	B MAIN                  @ 入口为main
    	B .                     @ B .跳转到自身
    	B SWI_HANDLER
    	B .
    	B .
    	B .
    	B .
    	B .
    
    @ 应用程序******************************
    MAIN:
    	MOV SP, #0x40000020     @ 初始化"SVC模式"下的栈指针
    	MSR CPSR, #0x10         @ 切换成USER模式,开启FIQ、IRQ
    	MOV R1, #1
    	MOV R2, #2
    	SWI #1                  @ 触发软中断异常
    	ADD R3, R2, R1
    	B STOP
    
    @ 异常处理程序*************************
    SWI_HANDLER:
    	STMFD SP!,{R1,R2,LR}    @ 压栈保护现场
    	MOV R1, #10
    	MOV R2, #20
    	SUB R3, R2, R1
    	LDMFD SP!,{R1,R2,PC}^   @ 出栈恢复现场
    
    	@ 将压入到栈中的LR(返回地址)出栈给PC,实现程序的返回
    	@ ^ 表示出栈的同时将SPSR的值传递给CPSR,实现CPU状态的恢复
    
  • 协处理器指令:操控协处理器的指令

     @ 1.协处理器数据运算指令
     CDP
     @ 2.协处理器存储器访问指令
     STC	    @ 将协处理器中的数据 写入到 存储器
     LDC	    @ 将存储器中的数据 读取到 协处理器
     @ 3.协处理器寄存器传送指令
     MRC	    @ 将协处理器中寄存器中的数据 传送到 ARM处理器中的寄存器
     MCR	    @ 将ARM处理器中寄存器中的数据 传送到 协处理器中的寄存器
    
  • 伪指令:本身不是指令,编译器可以将其替换成若干条等效指令

    @ 空指令
    NOP
    
    @ 指令
    LDR R1, [R2]
    @ 将 R2指向的内存空间中的数据 读取到 R1寄存器
    
    @ 伪指令
    LDR R1, =0x12345678     @ R1 = 0x12345678	
    @ LDR伪指令 可以将 任意一个32位的数据 放到 一个寄存器
    
    LDR R1, =STOP
    @ 将STOP表示的地址 写入 R1寄存器
    
    LDR R1, STOP
    @ 将STOP地址中的内容 写入 R1寄存器
    
  • 伪操作:不会生成代码,只是在编译之前告诉编译器怎么编译

    @ GNU的伪操作一般都以‘.’开头
    
    .global symbol      @ 将symbol声明成全局符号
    .local symbol       @ 将symbol声明成局部符号
    
    .equ DATA, 0xFF     @ #define
    MOV R1, #DATA
    
    .macro FUNC         @ 封装一些语句(函数)
        MOV R1, #1
        MOV R2, #2
    .endm
    FUNC
    
    .if 0               @ 条件编译
        MOV R1, #1
        MOV R2, #2
    .endif
    
    .rept 3             @ 重复3遍
        MOV R1, #1
        MOV R2, #2
    .endr
    
    
    @ .weak <symbol>    @ 弱化一个符号
    .weak func          @ 即告诉编译器即便没有这个符号也不要报错   
    B func              @ 编译成为NOP
    
    @ .word VALUE         
    MOV R1, #1
    .word 0xFFFFFFFF    @ 在当前地址申请一个字的空间 并 将其初始化为0xFFFFFFFF
    MOV R2, #2
    
    @.byte VALUE	            
    MOV R1, #1
    .byte 0xFF          @ 在当前地址申请一个字节的空间并将其初始化为VALUE
    @ MOV R2, #2        @ 直接写会存储在不对的位置
    @ .align N          @ 告诉编译器后续的代码2的N次方对齐
    .align 4
    MOV R2, #2
    
    .arm                @ 告诉编译器后续的代码是ARM指令
    .thumb              @ 告诉编译器后续的代码是Thumb指令
    
    .text               @ 定义一个代码段
    .data               @ 定义一个数据段
    .end                @ 汇编程序的结束
    
    @ .space N, VALUE   @ 在当前地址申请N个字节的空间并将其初始化为VALUE
    MOV R1, #1
    .space 12, 0x12
    MOV R2, #2
    
    
    

不同的编译器伪操作的语法不同

C语言和汇编混合编程

C和汇编的混合 编程原则 :在哪种语言环境下符合哪种语言的语法规则

  1. 在汇编中将C中的函数当做标号处理
  2. 在C中将汇编中的标号当做函数处理
  3. 在C中内联的汇编当做C的语句来处理

例子

  • 方式一:汇编语言调用C语言
    MOV R1, #1
    BL  func_c          @ 跳转到C函数
    MOV R2, #2
    
  • 方式二:C语言调用汇编语言
    .global FUNC_ASM    @ 定义为全局,这样才能被C程序调用
    FUNC_ASM:
        MOV R3, #3
        MOV R4, #4
    
  • 方式三:C内联(内嵌)汇编
    asm
    (
        "MOV R5, #5\n"
        "MOV R6, #6\n"
    );
    

ATPCS协议(ARM-THUMB Procedure Call Standard)

ATPCS协议主要内容

  • 栈的种类
    • 使用满减栈
  • 寄存器的使用
    • R15(PC)用作程序计数器,不能作其他用途
    • R14(LR)用作链接寄存器,不能作其他用途
    • R13(SP)用作栈指针,不能作其他用途
    • 当函数的参数不多于4个时使用R0-R3传递,当函数的参数多于4个时,多出的部分用栈传递
    • 函数的返回值使用R0传递
    • 其它寄存器主要用于存储局部变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值