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

ARM-进阶汇编指令

目录

一、比较指令

1.1指令码

1.2指令格式

1.3测试代码

二、跳转指令

2.1指令码

2.2指令格式

2.3测试代码

2.4练习题

三、单寄存器内存读写指令

3.1前言

3.2指令码

3.3指令格式

3.4测试代码

3.5地址偏移

3.6地址偏移测试代码

3.7练习题

四、栈指针寄存器内存读写指令

4.1栈的分类

4.2指令码

4.3指令格式

4.4寄存器列表的编写规则

4.5测试代码1

4.6测试代码2

4.7测试代码3

五、CPSR  特殊功能寄存器读写指令

5.1指令码

5.2指令格式

5.3测试代码

六、swi软中断指令

6.1介绍

6.2指令格式

6.3软中断指令的作用

6.4测试代码


一、比较指令

1.1指令码

cmp  --- 比较指令指令码

1.2指令格式

cmp Rn,oprand_shifter2


注意:

1.比较指令就相当于c语言语句中的if语句

2.比较指令不需要实用目标寄存器,比较完的值不需要存储

3.比较指令的本质是减法,也就是Rn - oprand_shifer2

4、比较指令的执行结果会直接影响CPSR寄存器的NZCV位,使用比较指令时不需要+s

5、比较指令通常会搭配条件码({cond})进行使用(并不是在cmp这条汇编指令后+cond)

1.3测试代码

.text 
.globl _start_start:mov r1,#0x1mov r2, #0x2cmp r1,r2addcs r3,r1,r2     @解释:当r1>=r2时,r3=r1+r2subcc r3,r1,r2     @解释:当r1<r2时,r3=r1-r2stop:b stop.end

二、跳转指令

2.1指令码

b ------   有去无回的跳转

bl ------  有去有回的跳转

l:LR寄存器

2.2指令格式

b/bl   标签(函数名)

2.3测试代码

.text 
.globl _start_start:mov r1, #0x1mov r2, #0x2bl funcadd r3,r1,r2func:mov r1, #0x5mov r2, #0x5add r3,r1,r2mov pc,lrstop:b stop.end

2.4练习题

.text 
.globl _start_start:mov r0, #0x1mov r1, #0x2func:cmp r0, r1    beq stopblne func1func1:cmp r0,r1subhi r0,r0,r1subls r1,r1,r0b funcstop:b stop.end

三、单寄存器内存读写指令

3.1前言

int a=10;

int *p=&a;

*p=20;

3.2指令码

ldr    -----    从内存空间中读取4个字节的数据

ldrh  -----    从内存空间中读取2个字节的数据

ldrb  -----     从内存空间中读取1个字节的数据

str  ------     向内存空间中写入4个字节的数据

strh  -----    向内存空间中写入2个字节的数据

shrb  -----   向内存空间中写入1个字节的数据

ld: load(加载,从内存地址空间中去读取数据)

st: store(存储,向内存地址空间中写入数据)

r:   register(4个字节)

h:half word(2个字节)
b:byte(1个字节)

注:1个字=4个字节

3.3指令格式

ldr/ldrh/ldrb  R0,[Rn]

解释:

                将[Rn]寄存器中的值看做是一片内存空间地址

                从Rd寄存器中的4个/2个/1个字节的数据写入到【Rn】这片内存空间地址中

注意:

                1、当汇编指令中寄存器编号用[]修饰时,当前寄存器中的值会被看做是一片内存空间的地址

3.4测试代码

.text 
.globl _start_start:ldr r0,=0x40000820ldr r1,=0x12345678str r1,[r0]   @将r0寄存其中的值看做是一片内存空间地址@将r1寄存器中4个字节的数据写到[r0]这片空间地址中ldr r2,[r0]		@将r0寄存器中的值看做是一片内存空间地址@将r0这片内存空间地址中的值读取到r2寄存器中stop:b stop.end

3.5地址偏移

int arr[] = {12,1,2,3,4}

int *p=arr;

printf("%d\n",*(p+1));

三种内存空间地址偏移的写法

1、
        ldr/ldrh/ldrb Rd,[Rn],#offset

        解释:

                        将Rn寄存器中的值看做是一片内存空间地址

                        从[Rn]这片内存空间地址中读取4个/2个/1个字节的数据到Rd寄存器中

                        注意:

                                        当读取完数据后,Rn寄存器中的地址发生变化Rn=Rn+#offset

2、

        ldr/ldrh/ldrb  Rd,[Rn, #offset]

        解释:

                将Rn+#offset的值看做一片内存空间地址

                从Rn+#offset这片内存空间地址中读取4个/2个/1个字节的数据到Rd寄存器中

                注意:

                                当读取完整数据后,Rn寄存器中的地址不会发生变化

3、ldr/ldrh/ldrb  Rd,[Rn,#offset]!

             解释:

                        将Rn+#offset的值看做是一片内存空间地址

                        从Rn+#offset这片内存空间地址中读取4个/2个/1个字节的数据到Rd寄存器中

                        注意:
                              当使用!时,当读取完数据后,Rn寄存器中的地址发生改变r0=r0+#offset

注意:

        1、#offset就是一个值,就是一个地址偏移量(#1,#4,#8)

        2、str/strh/strb地址偏移的效果和上述效果类似(从读取变为写入)

        3、使用str/ldr地址偏移时,#offset地址偏移量必须要是4的倍数

        4、使用strh/ldrh地址偏移时,#offset地址偏移量必须要是2的倍数

        5、使用strb/ldrb地址偏移时,#offset地址偏移量必须要是1的倍数

3.6地址偏移测试代码

/**********************9、单寄存器内存读写指令 - 地址偏移**************************/ldr r0, =0x40000820ldr r1, =0x12345678str r1, [r0]	@ 执行完上述操作,0x40000820地址中存在0x12345678这个值ldr r2, [r0], #4/*	解释:将r0寄存器中的值(0x40000820)看作是一片内存空间地址从0x40000820这片内存空间地址读取4个字节的数据(0x12345678)到r2寄存器中r2 = 0x12345678读取完数据后,r0寄存器中的地址发生变化 r0 = r0 + 4 = 0x40000820 + 4 = 0x40000824*/ldr r0, =0x4000081Cldr r3, [r0, #4]/*  解释:将r0+#4看作是一片内存空间地址(0x4000081C+4=0x40000820)从0x40000820这片内存空间地址中读取4个字节的数据到r3寄存器中r3 = 0x12345678r0 = 0x4000081C*/ldr r4, [r0, #4]!/*	解释:将r0+#4看作是一片内存空间地址(0x4000081C+4=0x40000820)从0x40000820这片内存空间地址中读取4个字节的数据到r3寄存器中r4 = 0x12345678r0 = r0 + #4 = 0x4000081C + 4 = 0x40000820*/

3.7练习题

	/*0x40000820这片内存空间中存储了0x12345678这个数据通过ldrb和地址偏移的三种方法,将0x40000820这片内存空间地址中的值读取到r1(0x12), r2(0x34), r3(0x56), r4(0x78)这四个寄存器中将r1, r2, r3, r4 这四个寄存器中的值拼接到r5寄存器中(r5 = 0x12345678)*/ldr r0, =0x40000820ldr r6, =0x12345678str r6, [r0]@ 1、从内存空间读取数据@ 1.1 ldrb Rd, [Rn], #offset/*ldrb r4, [r0], #1	@ r0 = 0x40000820	ldrb r3, [r0], #1	@ r0 = 0x40000821ldrb r2, [r0], #1	@ r0 = 0x40000822ldrb r1, [r0], #1	@ r0 = 0x40000823*/@ 1.2 ldrb Rd, [Rn, #offset]/*ldrb r4, [r0, #0]	@ 读取的内存空间地址:0x40000820ldrb r3, [r0, #1]	@ 读取的内存空间地址:0x40000821ldrb r2, [r0, #2]	@ 读取的内存空间地址:0x40000822ldrb r1, [r0, #3]	@ 读取的内存空间地址:0x40000823*/@ 1.1 ldrb Rd, [Rn, #offset]!ldrb r4, [r0, #0]!	@ 读取的内存空间地址:0x40000820, r0 = 0x40000820ldrb r3, [r0, #1]!	@ 读取的内存空间地址:0x40000821, r0 = 0x40000821ldrb r2, [r0, #1]!	@ 读取的内存空间地址:0x40000822, r0 = 0x40000822ldrb r1, [r0, #1]!	@ 读取的内存空间地址:0x40000823, r0 = 0x40000823@ 2、将数据拼接到r5寄存器中@ 2.1 左移+按位或操作/*lsl r4, r4, #0lsl r3, r3, #8lsl r2, r2, #16lsl r1, r1, #24orr r5, r5, r4orr r5, r5, r3orr r5, r5, r2orr r5, r5, r1*/orr r5, r5, r4, lsl #0orr r5, r5, r3, lsl #8orr r5, r5, r2, lsl #16orr r5, r5, r1, lsl #24@ 2.2 左移+加法运算lsl r4, r4, #0lsl r3, r3, #8lsl r2, r2, #16lsl r1, r1, #24add r5, r5, r4add r5, r5, r3add r5, r5, r2add r5, r5, r1

四、栈指针寄存器内存读写指令

4.1栈的分类

增栈:

        当进行压栈操作时,栈指针的指向向高地址方向移动

        当进行出栈操作时,栈指针的指向向低地址方向移动

减栈:

        当进行压栈操作时,栈指针的指向向低地址方向移动

        当进行出栈操作时,栈指针的指向向高地值方向移动

满栈:

        当前栈指针指向的地址空间内存在有效数据

        当使用满栈进行压栈操作时,需要先移动栈指针的指向,在压入新的数据(防止有效数据被新的数据覆盖)
 

空栈:

        当前栈指针指向的地址空间内不存在有效数据

        当使用空栈进行压栈操作时,可以直接压入新的有效数据

栈的分类:

        满减栈(Full Descending Stack)  ------  对于ARM架构而言,使用的是满减栈

        满增栈(Full Ascending Stack)

        空减栈(Emoty Descending Stack)

        空增栈(Emoty Descending Stack)

4.2指令码

满减栈(Full Descending Stack)  ------  ldmfd/stmfd

满增栈(Full Ascending Stack)------ldmfa/stmfa

空减栈(Emoty Descending Stack)------ldmed/stmed

空增栈(Emoty Descending Stack)------ldmea/stmea

m:mutiple(多个、多种)

4.3指令格式

满减栈(Full Descending Stack):当进行压栈操作时,栈指针的指向先向低地址方向移动,在压入新的数据

ldmfd sp!,{寄存器列表}

!的作用:用于更新栈指针的指向

解释:
        从栈指针指向的一片内存空间地址中读取数据到寄存器列表中

stmfd  sp! , {寄存器列表}
!的作用:用于更新栈指针的指向

解释:

        将寄存器列表中的值写入到栈指针的一片内存空间地址中

        当进行压栈操作时,sp的指向先向低地址方向移动,在压入新的数据

        sp=0x40000820

        16=0x10

        sp=0x40000820-0x10=0x40000810

4.4寄存器列表的编写规则

1、寄存器列表中用于存放多个寄存器的编号

2、当寄存器列表中寄存器编号连续时,此时使用-进行分割,如{r0-r5}

3、当寄存器列表中寄存器编号不连续时,此时使用,进行分隔,如{r1,r3,r5,r7}

4、寄存器列表中可以存在部分连续,部分不连续的寄存器编号,如{r1-r4,r6,r8-r9}

5、寄存器列表中的寄存器编号一般采用从小到大的方式编写

        也可以从大到小写,但是如果从大到小写,只能使用,分隔,如{r4,r3,r2,r1}

4.5测试代码1

.text 
.globl _start_start:ldr sp, =0x40000820ldr r1, =0x11111111ldr r2, =0x22222222ldr r3, =0x33333333ldr r4, =0x44444444stmfd sp!,{r1-r4}@解释:@将r1-r4寄存器中的值写入到sp栈指针指向的空间地址中@由于使用的是满减栈,栈指针会先移动指向(向低地址方向移动),在压入数据@sp=0x40000820-0x10(16个字节)=0x40000810ldmfd sp!, {r5-r8}@解释:@将sp栈指针指向的内存空间地址中的数据读取到r5-r8寄存器中@此时在做出栈操作,sp的指向会向高地址方向移动@sp=0x40000810+0x10(16个字节)=0x40000820stop:b stop.end

4.6测试代码2

int main(void)
{
        int r1=0x1,r2=0x2;

        func();

        add r3,r1,r2
}
void  func(void)
{
        int r1=0x5,r2=0x5;

        sub r3,r1,r2
}

.text 
.globl _start_start:@使用栈指针寄存器本质是为了实现c语言中的局部变量的功能@为实现局部变量的功能,需要将r1和r2寄存器中的值先压入到栈空间地址中ldr sp, =0x40000820mov r1,#0x1mov r2,#0x2@此处可以进行压栈操作@压栈操作取决于是否二次使用这个寄存器@只有在还没有二次使用这个寄存器的位置进行压栈即可bl funcadd r3,r1,r2b stopfunc:@此处压栈stmfd sp!,{r1-r2}mov r1, #0x5mov r2, #0x5sub r3,r1,r2@出栈和上面一样的ldmfd sp!,{r1-r2}mov pc,lrstop:b stop.end

4.7测试代码3

int main(void)

{
        int r1=0x1,r2=0x2;

        func1();
        add r3,r1,r2
}

void func1(void)
{
        int r1=0x5,r2=0x5;

        func2();
        sub r3,r1,r2
}

void func2(void)
{
        int r1=0x9,r2=0x1;

        func3();
        mul r3,r1,r2;
}

void func3(void)
{
        int r1=0x1,r2=0x6;

        add r3,r1,r2;5


}

.text 
.globl _start_start:@使用栈指针寄存器本质是为了实现c语言中的局部变量的功能@为实现局部变量的功能,需要将r1和r2寄存器中的值先压入到栈空间地址中ldr sp, =0x40000820mov r1,#0x1mov r2,#0x2@此处可以进行压栈操作@压栈操作取决于是否二次使用这个寄存器@只有在还没有二次使用这个寄存器的位置进行压栈即可bl func1add r3,r1,r2func1:@此处压栈stmfd sp!,{r1-r2,lr}mov r1, #0x5mov r2, #0x5bl func2sub r3,r1,r2@出栈和上面一样的ldmfd sp!,{r1-r2,pc}@mov pc,lr@洗这个存在问题因为在多次调用时lr的值会被覆盖,解决办法用寄存器存储lrd的值func2:stmfd sp!,{r1-r2,lr}mov r1, #0x9mov r2, #0x1bl func3mul r3,r1,r2ldmfd sp!,{r1-r2,pc}func3:stmfd sp!,{r1-r2,lr}mov r1, #0x1mov r2, #0x6add r3,r1,r2ldmfd sp!, {r1-r2,pc}stop:b stop.end

五、CPSR  特殊功能寄存器读写指令

5.1指令码

msr  ----- 向cpsr寄存器中写入数据

mrs ----- 从cpsr寄存器中读取数据
 

5.2指令格式

msr CPSR, oprand_shifter2

解释:将oprand_shifter2第二操作数写入到CPSR寄存器中


mrs Rd,CPSR

解释:从CPSR寄存器中读取数据到Rd寄存器中

!!!!!!!只有mrs和msr这两条汇编指令可以操作CPSR寄存器,其他指令的汇编指令无法直接操作CSR寄存器

5.3测试代码

.text 
.globl _start_start:@已知CPSR寄存器中的值为0xD3(正常情况下我们是不知道的)@目的:我们需要将当前工作模式从SVC模式切换到user模式@我们可以改变cpsr寄存器中的M[4:0]位,其他的位不变@此时我们需要用到位运算相关的汇编指令@问题:普通的汇编指令无法直接操作CSPR寄存器,需要通过msr和mrs这条汇编指令来操作CPSR寄存器@1、可以先使用mrs将cpsr寄存器中的值读取的普通寄存器中@2、在通过and和orr修改普通寄存器中的值@3、将修改完的值通过msr写入到cpsr寄存器中mrs r0, cpsrand r0,r0, #(~(0x1f<<0))orr r0,r0, #(0x1<<4)msr cpsr,r0stop:b stop.end

六、swi软中断指令

6.1介绍

软中断:软件代码产生的中断

swi:software interrupt

6.2指令格式

指令格式:swi  软中短号

软中短号就是一个数字(0-255)

6.3软中断指令的作用

作用:不管当前外设处于那种工作模式下,当执行软中断指令时,默认切换到SVC模式下

6.4测试代码

软中断相关的异常在单片机课程中的中断实验详细讲解

mrs r0,cpsr
and r0,r0,#(~(0x1f<<0))
orr r0,r0,#(0x1<<4)
msr cpsr,r0
@软中断指令
swi 10
@当执行完这条汇编指令,工作模式默认切换到SVC模式下
mov r1,#0x1

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

相关文章:

  • linux安装gitlab详细教程,本地管理源代码
  • 存储掉电强制拉库引起ORA-01555和ORA-01189/ORA-01190故障处理---惜分飞
  • 英伟达Newton与OpenTwins如何重构具身智能“伴随式数采”范式
  • 【ElasticSearch实用篇-04】Boost权重底层原理和基本使用
  • Ruoyi项目MyBatis升级MyBatis-Plus指南
  • linux:离线/无网环境安装docker
  • 从Java全栈开发到微服务架构:一次真实的面试实录
  • (Arxiv-2025)HunyuanCustom:一种面向多模态驱动的定制化视频生成架构
  • vizard-将长视频变成适合社交的短视频AI工具
  • 【JavaWeb】之HTML(对HTML细节的一些总结)
  • vue3使用路由router
  • 大规模异构数据挖掘与数据架构
  • C++ STL序列容器-------list
  • 【LeetCode】3524. 求出数组的 X 值 I (动态规划)
  • 机器学习(四)KNN算法-分类
  • 13 选 list 还是 vector?C++ STL list 扩容 / 迭代器失效问题 + 模拟实现,对比后再做选择
  • MVC、三层架构
  • 手写MyBatis第46弹:多插件责任链模式的实现原理与执行顺序奥秘--MyBatis插件架构深度解析
  • 2025 数字化转型期,值得关注的 10 项高价值证书解析
  • T507 音频调试
  • Redis--Lua脚本以及在SpringBoot中的使用
  • 基于STM32设计的宠物寄养屋控制系统(阿里云IOT)_276
  • 【python+requests】告别繁琐XML解析!用xmltodict.parse像处理JSON一样轻松操作XML
  • MySQL下载及安装(Windows 11)
  • 【图论】 Graph.jl 操作汇总
  • Qt Widgets 之 QAbstractButton
  • 每周读书与学习->认识性能测试工具JMeter
  • Kafka Connect + Streams 用到极致从 CDC 到流处理的一套落地方案
  • UCIE Specification详解(十二)
  • Git中批量恢复文件到之前提交状态