当前位置: 首页 > news >正文

ARM裸机开发(基础汇编指令)Day02

一、Keil软件

1、警告

2、报错

3、keil编译界面分析

4、keil仿真界面分析

5、修改编译格式为Chinese

6、汇编文件 ( .s 文件) 分析

7、map.lds链接脚本文件

在一个单片机工程中,必然会一个.s汇编文件和一个.lds连接脚本文件
这两个文件一般做单片机开发时,不需要手动编写,通过一些软件或者工具会自动生成

二、汇编指令

1、汇编指令的分类

基础汇编指令:1、数据操作指令1.1 数据搬移指令(赋值指令=)1.2 移位运算指令(<<、>>)1.3 位运算指令(&、|、~、^)1.4 算术运算指令(+ - * /)1.5 比较指令(< > == !=)2、跳转指令
进阶汇编指令:3、单寄存器内存读写指令4、栈指针寄存器内存读写指令(包含了多寄存器内存读写指令)5、CPSR特殊功能寄存器内存读写指令6、swi软中断指令

2、汇编指令的基本格式

{opcode}{cond}{s}        Rd, Rn, oprand_shifter2|      |   |          |   |        |    -------> 第二操作数(相当于又操作数)|      |   |          |   |        |    -------> 第二操作数可以是:|      |   |          |   |        |             1.立即数|      |   |          |   |        |             2.普通寄存器       |      |   |          |   |        |             3.位移后的寄存器|      |   |          |   |                |      |   |          |   | --------> 第一操作寄存器(相当于左操作数)(非必要)|      |   |          |   |      |   |          | ---------> 目标寄存器,用于存放当前汇编指令结果的寄存器|      |   |          |      |   | ---------> 状态位,在这个位置加上 s ,当前指令的执行结果会修改CPSR寄存器的NZCV位|      |                                  不加上 s ,当前指令的执行结果不会影响CPSR寄存器的NZCV位|      |                    |      | ---------> 表示条件码,用于让汇编指令有条件的指令| | ----------> 表示指令码,也就是汇编指令注意:1.{opcode}{cond}{s} 指令码、条件码、状态位 是连在一起写的,不能用空格隔开2.Rd,Rn,oprand_shifter2 目标寄存器、第一操作寄存器、第二操作数要用 逗号(,) 隔开3.{opcode}{cond}{s} 和 Rd,Rn,oprand_shifter2 之间要用空格隔开4.汇编指令没有分隔符(;),所以每行只能写一个汇编指令5.汇编指令不区分大小写6.在汇编文件中,使用的编译器支持的注释方式 @(等于C语言中的//) , /*  */(和C语言中用法一致).if 0/1 .else .endif 
=========================================================================================

2.1数据搬移指令(赋值指令)

指令码

mov     --------      直接赋值指令

mvn     ------         按位取反后赋值

指令格式

mov/mvn Rd,operand_shifter2

赋值指令时没有第一操作寄存器的,只有目标寄存器和第二操作数

测试代码
.text.global _start_start:          mov r0,#oxff  @解释:将0xff赋值到R0寄存器中@如果使用赋值指令时,第二个操作数是一个立即数@需要在他的前面加上#mvn r1,#0xff  @解释:将0xff全部按位取反后,赋值给r1寄存器@r1=~(0xff)=0xffffff00mov r2,#(-0xff)     @-0xff(存储的补码,正数源码和补码是一致的,负责需要计算)@源码:0x800000ff@反码:0xffffff00@补码:0xffffff01stop: b stop
.end

3、立即数

 在ARM中,立即数是指直接嵌入在指令编码中的常量,无需从寄存器或内存中读取,处理器执行时可以直接从指令中提取并使用的值。由于一条汇编指令共4字节,其中表示立即数的空间只有 12位,分别是 8位原始值和4位旋转值。

1)立即数的基本格式

在ARM汇编的语法中,立即数通常是 以 # 为前缀 的二进制、十进制、十六进制数

MOV R0, #10           十进制立即数10,将R0赋值为10
ADD R1, R1, #0x20     十六进制立即数0x20(即32),R1 = R1 + 32
AND R2, R2, #0b1110   二进制立即数0b1110(即14),R2与14做“与”操作

2)立即数的编码结构

在ARM32中立即数的编码结构分为两个部分:

  1. 高四位的旋转值(rot):指原始值右移的位数
  2. 低八位的原始值(imm8):指 0x00 ~ 0xFF 中的一个值

3)立即数的编码约束

        编码约束也就是编码规则,最终立即数必须符合编码规则才是合法的立即数,也就是最终立即数必须可以通过原始值循环右移得到。

最终立即数 = (imm8) 循环右移 (2xrot)次

4)总结

以mov这条汇编指令为例,不同的汇编指令的立即数是不一样的
以mov r0, #0xff为例,一条汇编指令占四个字节的空间

立即数是指可以被指令直接编码、无需从内存或者寄存器读取的常数。它的核心特点是“能被指令格式直接表示“。
立即数的判断步骤:
                     1、确定带判断数a;
                      2、在0x00~0xff范围内找一个数b;
                      3、将b循环右移偶数位(0、2、4......30位),若结果等于a,则是立即数
eg:
          0xff是否为立即数
          在0x00~0xff中找到b=0xff;
          将b循环右移32位(等价于右移0位,因为32是偶数),结果仍为0xff,因此0xff是立即数
            0xfff是否是立即数
            在0x00~0xff中找不到任何数循环右移等于0xfff的,所以0xfff不是立即数

判断0x1fe是否是立即数
数a:0x1fe = 0b 0000 0000 0000 0000 0000 0001 1111 1110
数b:0xff  = 0b 0000 0000 0000 0000 0000 0000 1111 1111
循环右移31位
0x1fe不是一个立即数

4、有效数

         有效数是指,一个立即数全部取反后,可以得到一个合法的立即数,这个数就是有效数,有效数,可以当作立即数使用。

5、伪指令ldr

ldr伪指令可以实现0x00000000~0xffffffff之间任意数的赋值操作
指令格式:ldr Rd,=任意数

如果以后遇到一个数,不知道是否是立即数,可以直接使用ldr进行赋值操作
eg:
     ldr r0,=0x12345678

三、基础汇编指令

1、数据操作指令(运算指令)

1)数据搬移指令(mov、mvn)

指令码:mov    直接赋值指令mvn    按位取反后赋值指令指令格式:mov/mvn  Rd, oprand_shifter2赋值指令是没有第一操作寄存器的,只有目标寄存器和第二操作数伪指令:ldr    任意数赋值
指令格式: ldr Rd, =任意数

2)位移运算指令(lsl、lsr、ror、asr)

指令码:lsl    逻辑左移/无符号数左移lsr    逻辑右移/无符号数右移ror    循环右移asr    算术右移/有符号数右移指令格式:lsl/lsr/ror/asr Rd, Rn, oprand_shifter2

/***********************2、移位运算指令**************************/ /*mov r0, #0xfflsl r1, r0, #8              @ 将r0寄存器中的值逻辑左移8位后赋值给r1寄存器@ r1 = r0 << 8 = 0xff00lsr r2, r1, #12            @ 将r1寄存器中的值逻辑右移12位后赋值给r2寄存器@ r2 = r1 >> 12 = 0xfror r3, r2, #4 @ 将r2寄存器中的值循环右移4位后赋值给r3寄存器@ r3 = 0xf0000000asr r4, r3, #4 @ 将r3寄存器中的值算术右移4位后赋值给r4寄存器@ r4 = 0xff@ 高位补符号位指的是所有高位都需要补符号位,而不是只有最高位补符号位*/ /***********************2、第二操作数的所有情况**************************/
@ 第二操作数可以是一个立即数
mov r0, #0xff @ 解释:将0xff赋值给r0寄存器中@ 第二操作数可以是一个普通寄存器
mov r1, r0 @ 解释:将r0寄存器中的值赋值给r1寄存器中@ 第二操作数可以是一个经过移位的寄存器
mov r2, r1, lsl #4                       @ 解释:将r1寄存器中的值逻辑左移4位后赋值给r2寄存器@ 上述汇编指令的作用和这条lsl r2, r1, #4汇编指令的作用是一致的

3)位运算指令

指令码:and    按位与orr    按位或eor    按位异或mvn    按位取反指令格式:and/orr/eor/mvn    Rd, Rn, oprand_shifter2口诀:与0清0,与1不变或1置1,或0不变异或1取反,异或0不变

测试代码01
/***********************4、位运算操作指令**************************/@ 32位数:@ 31                                    0                                                                                                                            @ **** **** **** **** **** **** **** ****mov r0, #0xff@ 目的:将r0寄存器中的第[3]位清0,其他位不变,最后赋值给r0寄存器@ c语言写法:r0 = r0 & (~(0x1 << 3))and r0, r0, #(~(0x1 << 3))   @ r0 = 0xf7@ 目的:将r0寄存器中的第[3]位置1,其他位不变,最后赋值给r0寄存器orr r0, r0, #(0x1 << 3)
练习代码
@假设你不知道r0寄存器中的值ldr r0, =0x12345678@ 31 0@ **** **** **** **** **** **** **** ****@ 1> 将r0寄存器的第[3]位清0,保持其他位不变and r0, r0, #(~(0x1 << 3))@ 2> 将r0寄存器的第[29]位置1,保持其他位不变orr r0, r0, #(0x1 << 29)@ 3> 将r0寄存器的第[7:4]位清0,保持其他位不变and r0, r0, #(~(0xf << 4))@ and r0, r0, #(~(0b1111 << 4))@ 4> 将r0寄存器的第[15:8]位置1,保持其他位不变orr r0, r0, #(0xff << 8) @ orr r0, r0, #(0x0000ff00)@ 5> 将r0寄存器的第[3:0]位按位取反,保持其他位不变eor r0, r0, #(0xf << 0)@ 6> 将r0寄存器的第[11:4]位修改为10101011,保持其他位不变@ 推荐使用先清0再置1and r0, r0, #(~(0xff << 4))orr r0, r0, #(0b10101011 << 4) @ orr r0, r0, #(0xab << 4)@ 也可以先置1再清0orr r0, r0, #(0xab << 4)and r0, r0, #(~(0x54 << 4))

4)算术运算指令

1.指令码
add -----基础加法指令(不涉及CPSR寄存器中的c位的使用)
adc-------进阶加法指令(涉及CPSR寄存器中的c位使用)
sub--------基础减法指令(不涉及CPSR寄存器中的c位的使用)
sbc------进阶减法指令(涉及CPSR寄存器中的c位使用)
mul-------- 乘法指令
div-------除法指令(使用除法指令需要架构ARM-V8架构之上)
2指令格式
add/adc/sub/abc/mul/div     Rd,Rn,oprand_shifter2
3测试代码
mov r1, #0x1
mov r2, #0x3
add r3, r1, r2       @r3=0x1+0x3=0x4
sub r4, r2, r1       @r4=0x3-0x1=0x2
@模拟两个64位数相减
@r1寄存器存放第一个64位数的高32位,r2寄存器存放第一个64位数的低32位
@r3寄存器存放第二个64位数的高32位,r4寄存器存放第二个64位数的低32位
@r5寄存器中存放相减后的高32位数,r6寄存器存放相减后的低32位数
mov r1, #0x5
mov r2, #0x2
mov r3, #0x1
mov r4, #0x8
@先低位相减
subs r6, r2, r4           @此时需要使用到s状态位@由于当前指令的执行结果产生了借位@并且高32位运算时,需要用到产生借位后的c位@r6=r2-r4=0x2-0x8=0x2-0x2-0x6=0x0-0x6=0x0-0x1-0x5@=0xffffffff-0x5=0xfffffffa
注:-1 mod 2³² = 2³² - 1 = 0xffffffff(因为 0xffffffff + 1 = 0x100000000,对 2³² 取模后等于 0)。同样也可以这样理解当你10-1的时候是不是等于九是不是个位数里面最大的数同理当你0x0-0x1时也是一样的等于最大值0xffffffffsbc  r5, r1, r3          此时需要使用到sbc由于此处高32位相减时,需要使用到产生借位后的位r5= r1- r3- C位(注:无论是进位还是借位的c位都是0x1)r5=r1-r3-c位=0x5-0x1-0x1=0x3注意:不管C位存放的是0/1只要c位产生了借位,就需要多减一个0x1只要c位产生了进位,就要多加一个0x1
注:为什么加s,subs中的s是为了强制更新 CPSR 的借位标志(C 位),让后续的高位运算能正确处理低 32 位产生的借位,当指令后加s(即 “set flags”)时,会在执行运算后自动更新 CPSR 的关键标志位(N、Z、C、V 等),其中:C 位(进位 / 借位标志):对于减法,C=0表示有借位,C=1表示无借位,这是 64 位减法中处理高位运算的核心依据。@模拟两个64位数相加
@r1寄存器存放第一个64位数的高32位,r2寄存器存放第一个64位数的低32位
@r3寄存器存放第二个64位数的高32位,r4寄存器存放第二个64位数的低32位
@r5寄存器中存放相加后的高32位数,r6寄存器存放相加后的低32位数
mov r1, #0x1mov r2, #0xfffffffe
mov r3, #0x2
mov r4, #0x4
adds  r6, r2, r4                            r6=0xfffffffe+0x4=0xfffffffe+0x1+0x3=0xfffffffff+0x3=0xffffffff+0x1+0x2=0x0+0x2=0x2
注:可以这样理解当9+1的时候是不是等于十是不是十位数里面最小的数同理当你0xffffffff+0x1时也是一样的等于0x0
adc r5, r1, r3                               r5=r1+r3+c位=0x1+0x2+0x1=0x4
注:为什么加c位是因为发生了进位上面已经解释了。

5)比较运算指令

2、跳转指令

http://www.xdnf.cn/news/1415485.html

相关文章:

  • 【AI智能体】LLM记账智能体+MCP服务-实现步骤与效果展示
  • 分布式锁和分布式事务
  • 使用yt-dlp下载网页视频
  • 国内大型银行数据模型实践案例
  • 2025年跨领域职业发展证书选择指南
  • 漫谈《数字图像处理》之基函数与基图像
  • Linux wlan 之网络问题定位分析 实例一
  • 面向对象中—类
  • 「数据获取」《中国工会统计年鉴》(1991-2013)(获取方式看绑定的资源)
  • EtherCAT主站IGH-- 50 -- 搭建xenomai系统及自己的IGH主站遇见的BUG
  • Android Studio 9.png制作
  • Java与分布式系统的集成与实现:从基础到应用!
  • Linex进程管理
  • Unity核心概念②:帧、生命周期函数
  • 【开题答辩全过程】以 基于微信小程序的教学辅助系统 为例,包含答辩的问题和答案
  • SAP报工与收货的区别(来自deepseek)
  • Library cache lock常见案例分析(二)
  • 技能补全之Python操作MongoDB
  • 基于FOA与BP神经网络分类模型的特征选择方法研究(Python实现)
  • 订单后台管理系统-day05用户模块查看与删除
  • Kubernetes 存储
  • 【语法】C++的异常
  • IIC接口的mpu6050六轴模块(8针脚)引脚使用说明
  • Java中的异常,枚举,泛型,代理
  • 单表查询-group by rollup优化
  • 责任链模式实践-开放银行数据保护及合规
  • 一键获取电商平台商品原数据:item_get_app接口实操讲解
  • [Plecs基础知识系列]建立自定义模块/子系统(Subsystem)
  • 基于路测点云标注生成OpenDrive地图的全流程解析
  • 微服务01