该节内容以上一节的概述为路线用代码实际教学
(72条消息) 嵌入式学习LV9-ARM体系结构与接口技术- D5. ARM指令集仿真环境搭建_肉丸子QAQ的博客-CSDN博客
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R1, #1 @ 将1这个数搬到R1寄存器
MOV R2, #2
MOV R1, #3
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
调试现象
进入调试模式
1,2步复位再点单步调试
蓝色背景表示上一次执行指令后产生了变化
- 点击两次单步调试后可以看到R1.R2的值通过MOV指令改写了
- 黄色箭头表示程序运行到哪里了
也可以将别的寄存器值写入另外一个寄存器:MOV R1, R2
复位和点击一次单步调试可以看到PC寄存器自增了4
转换成二进制后可以知道机器码是32位的
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R1, #1 @ 将1这个数搬到R1寄存器
MOV R2, #2
MOV R3, #3
MOV PC, #0 @将PC修改为0,也就类似一个小循环,将程序跳转到了最开始的位置
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
可以看到将PC修改成0后,程序跳转到地址为0的位置,也就是第一条指令
MOV R1, #1
的位置
无论写什么值,PC最后都会变成4的整数倍
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MVN R1, #0xFF @ R1 = ~0xFF 值取反后写入
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R0, #0
MVN R0, #0
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
绿色:不同类型指令,相同寄存器,值相同——>机器码前缀不同
红色:相同指令,操作不同寄存器,值相同——>机器码中间不同
蓝色:相同指令,操作相同寄存器,值不同——>机器码后缀不同
总结:
汇编中每一条的指令的机器码都是一一对应的
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R0, #0x12345678 @报错
MOV R0, #12 @不报错
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
- 机器码32位
- 第一条指令数据太大,超过了32位,无法编译成32位机器码
- 立即数的本质就是包含在指令当中的数,属于指令的一部分
- 立即数和变量的有区别
- 立即数的优点:取指的时候就可以将其读取到CPU,不用单独去内存读取,速度快
- 立即数的缺点:不能是任意的32位的数字,有局限性
- 最直接辨别的方法:能编译通过的就是立即数
- 对于
MOV
指令0-255都是立即数- 不用背那些是立即数,理解本质
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R0, #0xFFFFFFFF
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
前面讲到立即数是有限制的,数据过大会导致编译失败,但是这里发现编译通过了
- 从反编译中可以看出指令是
MOV
变成了MVN
- 这两条的指令的结果是相同的
- MOV将R0全部写成了1
- MVN将R0写成0后取反
《操作码》《目标寄存器》《第一操作寄存器》《第二操作数》
ADD R1, R2, R3
@ R1 = R2 + R3
ADD R1, R2, #5
@ R1 = R2 + 5
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #5
MOV R3, #3
ADD R1, R2, R3
@R1 = R2 + R3
ADD R1, R2, #5
@R1 = R2 + 5
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
运算顺序:第一操作寄存器 — 第二操作数
- 寄减寄
- 寄减数
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #5
MOV R3, #3
@ 减法指令
SUB R1, R2, R3
@ R1 = R2 - R3
SUB R1, R2, #3
@ R1 = R2 - 3
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #5
MOV R3, #3
RSB R1, R2, R3
@ R1 = R3 - R2
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #5
MOV R3, #3
@ 乘法指令
MUL R1, R2, R3
@ R1 = R2 * R3
@ 乘法指令只能是两个寄存器相乘
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
注意!!!
- 乘法指令只能是两个寄存器相乘
- 在arm是精简指令集不支持除法
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0
MOV R3, #3
@ 按位与指令
AND R1, R2, R3
@ R1 = R2 & R3
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0
MOV R3, #3
MOV R1, #0
@ 按位或指令
ORR R1, R2, R3
@ R1 = R2 | R3
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
异或:相同为0,不同为1
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0
MOV R3, #3
MOV R1, #0
@ 按位异或指令
EOR R1, R2, R3
@ R1 = R2 ^ R3
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0xF0
MOV R3, #2
@ 左移指令
LSL R1, R2, R3 @R2左移R3个位
@ R1 = (R2 << R3)
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0xF0
MOV R3, #2
@ 右移指令
LSR R1, R2, R3
@ R1 = (R2 >> R3)
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
第二操作数中的哪一位为1,就将第一操作寄存器的中哪一位清零,然后将结果写入目标寄存器
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #0xFF
BIC R1, R2, #0x0F
@ 第二操作数中的哪一位为1,就将第一操作寄存器的中哪一位清零,然后将结果写入目标寄存器
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
注意!!
- 这里的清零不是将原本的寄存器清零,而是将原本的寄存器清零后的数据储存到另外一个寄存器
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
@ 格式扩展
MOV R2, #3
MOV R1, R2, LSL #1
@ R1 = (R2 << 1)
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
《操作码》《目标寄存器》《第一操作寄存器》《第二操作数》
遵循指令基本格式:第一位是操作码
- 所以这条指令最后是MOV指令:将绿框内容搬移到R1寄存器
- 绿框内容表示将R2寄存器左移一位
- 最后结果就是将R2寄存器左移一位后放入R1保存
- 011变为110
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #3
SUB R1, R2, #5
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
- 从结果能看到CPSR寄存器的N位没有受到影响
- 默认情况下数据运算不会对条件位产生影响,在指令后加后缀”S“才可以影响
.text @表示当前段为代码段
.global _start @声明_start为全局符号
_start: @汇编程序的入口
MOV R2, #3
SUBS R1, R2, #5 @指令后面加S
STOP:
B STOP @死循环,防止程序跑飞
.end @汇编程序的结束
- 好比低年级学生只会个位加法,当计算十位数时需要将数据拆开单个运算,计算机同理
@ 带进位的加法指令
@ 两个64位的数据做加法运算
@ 第一个数的低32位放在R1
@ 第一个数的高32位放在R2
@ 第二个数的低32位放在R3
@ 第二个数的高32位放在R4
@ 运算结果的低32位放在R5
@ 运算结果的高32位放在R6
@ 第一个数
@ 0x00000001 FFFFFFFF
@ 第二个数
@ 0x00000002 00000005
@ MOV R1, #0xFFFFFFFF
@ MOV R2, #0x00000001
@ MOV R3, #0x00000005
@ MOV R4, #0x00000002
@ ADDS R5, R1, R3
@ ADC R6, R2, R4 @ADC指令能自动加上低位的进位,如果用ADD计算机是不会加上进位数据
@ 本质: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' @取非
ADC
:ADC指令能自动加上低位的进位,如果用ADD计算机是不会加上进位数据
- ADC本质:R6 = R2 + R4 + ‘C’:加上了CPSR寄存器的C位
SBC
:同理
1.编程实现使用32bit的ARM处理器实现两个128位的数据的加法运算。
注:
第一个数的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R1、R2、R3、R4寄存器
第二个数的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R5、R6、R7、R8寄存器
运算结果的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R9、R10、R11、R12寄存器
.text @ 表示当前为代码段
.global _start @ 将_start定义为全局符号
_start: @ 汇编的入口
@ 带进位的加法运算(两个128位数据)
@ 第一个数 0xFFFFFFFF 00000001 00000001 00000001
@ 第二个数 0x00000002 00000002 00000002 00000002
@第一个数的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R1、R2、R3、R4寄存器
@第二个数的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R5、R6、R7、R8寄存器
@运算结果的bit[31:0]、bit[63:32]、bit[95:64]、bit[127:96]分别存储在R9、R10、R11、R12寄存器
MOV R1, #0xFFFFFFFF
MOV R2, #0x00000001
MOV R3, #0x00000001
MOV R4, #0x00000001
MOV R5, #0x00000002
MOV R6, #0x00000002
MOV R7, #0x00000002
MOV R8, #0x00000002
ADDS R9, R1, R5
ADCS R10, R2, R6
ADCS R11, R3, R7
ADC R12, R4, R8
stop: @死循环,防止程序跑飞
B stop
.end @ 汇编的结束