初析
ARM 和 8086这两种架构有很多方面的区别,包括指令集、寻址模式、性能、功耗、市场等。
指令集类型
ARM 和 8086 属于不同的指令集类型。ARM 是一种精简指令集(RISC),它的指令数量较少,长度相同,执行速度较快,适合流水线操作。8086 是一种复杂指令集(CISC),它的指令数量较多,长度不同,执行速度较慢,适合高级语言编译。
例如,下面是一个实现两个 32 位无符号整数相加的 ARM 汇编代码:
MOV R0, #0x12345678 ; R0 = 0x12345678
MOV R1, #0x87654321 ; R1 = 0x87654321
ADD R2, R0, R1 ; R2 = R0 + R1
下面是一个实现同样功能的 8086 汇编代码:
MOV AX, 0x5678 ; AX = 0x5678
MOV BX, 0x4321 ; BX = 0x4321
ADD AX, BX ; AX = AX + BX
MOV CX, 0x1234 ; CX = 0x1234
MOV DX, 0x8765 ; DX = 0x8765
ADC CX, DX ; CX = CX + DX + CF
可以看出,ARM 代码只需要三条指令,而 8086 代码需要六条指令。ARM 代码中的每条指令都可以在一个时钟周期内完成,而 8086 代码中的每条指令都需要多个时钟周期。ARM 代码中的每条指令都可以直接操作 32 位的寄存器,而 8086 代码中的每条指令都只能操作 16 位的寄存器,所以需要用两个寄存器来表示一个 32 位的数,并且需要用进位标志(CF)来处理高位的进位。
寻址方式
ARM 和 8086 有不同的寻址模式。寻址模式是指定操作数在存储器中的位置的方式。ARM 支持的寻址模式较少,主要有立即数寻址、寄存器寻址、寄存器间接寻址、基址变址寻址等。8086 支持的寻址模式较多,主要有立即数寻址、寄存器寻址、直接寻址、寄存器间接寻址、基址变址寻址、相对寻址、段寄存器寻址等。
例如,下面是一个使用基址变址寻址的 ARM 汇编代码:
LDR R0, [R1, R2] ; R0 = Memory[R1 + R2]
下面是一个使用基址变址寻址的 8086 汇编代码:
MOV AX, [BX+SI] ; AX = Memory[BX + SI]
可以看出,ARM 代码中的基址变址寻址可以使用任意两个寄存器作为基址和变址,而 8086 代码中的基址变址寻址只能使用 BX 或 BP 作为基址,SI 或 DI 作为变址。ARM 代码中的基址变址寻址可以直接访问 32 位的存储器地址,而 8086 代码中的基址变址寻址需要加上段寄存器的值来形成 20 位的存储器地址。
性能和功耗
ARM 和 8086 有不同的性能和功耗特点。ARM 的性能优势在于它的指令执行速度快,流水线效率高,寄存器数量多,可以减少存储器访问次数。8086 的性能劣势在于它的指令执行速度慢,流水线效率低,寄存器数量少,需要频繁访问存储器。
例如,下面是一个实现斐波那契数列的 ARM 汇编代码:
MOV R0, #10 ; R0 = 10, 要计算的项数
MOV R1, #0 ; R1 = 0, 第一项
MOV R2, #1 ; R2 = 1, 第二项
MOV R3, #0 ; R3 = 0, 循环
loop:
CMP R3, R0 ; 比较 R3 和 R0
BGE end ; if R3 >= R0, 分支结束
ADD R4, R1, R2 ; R4 = R1 + R2, 下一项
MOV R1, R2 ; R1 = R2, 更新第一项
MOV R2, R4 ; R2 = R4, 更新第二项
ADD R3, R3, #1 ; R3 = R3 + 1, 使循环数加1
B loop ; 分支循环
end:
; R1包含斐波拉契数列的最后一项
下面是一个实现斐波那契数列的 8086 汇编代码:
MOV CX, 10 ; CX = 10, 要计算的项数
MOV AX, 0 ; AX = 0, 第一项
MOV BX, 1 ; BX = 1, 第二项
loop:
CMP CX, 0 ; 比较 CX 和 0
JE end ; if CX == 0, 跳转到尾
ADD DX, AX, BX ; DX = AX + BX, 第二项
MOV AX, BX ; AX = BX, 更新第一项
MOV BX, DX ; BX = DX, 更新第二项
DEC CX ; CX = CX - 1, 减去循环数
JMP loop ; 跳转循环
end:
; AX 包含斐波那契数列的最后一项
可以看出,ARM 代码只需要八条指令,而 8086 代码需要九条指令。ARM 代码中的每条指令都可以在一个时钟周期内完成,而 8086 代码中的每条指令都需要多个时钟周期。ARM 代码中的每条指令都可以直接操作 32 位的寄存器,而 8086 代码中的每条指令都只能操作 16 位的寄存器,所以需要用 DX 来存放下一项的高位。
ARM 代码中使用了条件分支指令,可以减少无用的跳转,而 8086 代码中使用了无条件跳转指令,会增加流水线的开销。
ARM 的功耗优势在于它的指令集简单,电路结构简洁,运行频率低,可以节省电能。8086 的功耗劣势在于它的指令集复杂,电路结构复杂,运行频率高,会消耗更多的电能。
例如,下面是一个比较 ARM 和 8086 的功耗的表格:
CPU | Frequency | Power | Energy per Instruction |
---|---|---|---|
ARM | 25 MHz | 0.5 W | 0.02 nJ |
8086 | 5 MHz | 1.5 W | 0.3 nJ |
可以看出, ARM 的功耗是 8086 的三分之一,而每条指令的能耗是 8086 的十五分之一。这意味着 ARM 可以在相同的电池容量下运行更长的时间,也可以在相同的时间内执行更多的指令。这就是为什么 ARM 适合用于移动设备和嵌入式系统,而 8086 适合用于个人电脑和服务器。
具体分析
下面,我将结合具体实例,从10个方面解析8086和 ARM 之间的区别。
跳转指令 (Jump)
跳转指令用于改变程序的执行流程,根据条件或地址进行转移。
ARM 和 8086 的跳转指令有以下区别:
- ARM 的跳转指令有 B 和 BL 两种,B 用于无条件跳转,BL 用于子程序调用。8086 的跳转指令有 JMP、CALL、RET 等,JMP 用于无条件跳转,CALL 用于子程序调用,RET 用于子程序返回。
- ARM 的跳转指令可以在指令后加上条件码,表示只有在满足条件时才执行跳转,例如 BEQ 表示等于时跳转,BNE 表示不等于时跳转。8086 的跳转指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
- ARM 的跳转指令可以使用寄存器或标号作为跳转目标,例如 B R0 表示跳转到 R0 寄存器中的地址,B loop 表示跳转到 loop 标号处。8086 的跳转指令只能使用标号或内存地址作为跳转目标,例如 JMP loop 表示跳转到 loop 标号处,JMP [BX] 表示跳转到 BX 寄存器中的地址。
例如,下面是一个使用跳转指令实现循环打印 10 个字符的 ARM 汇编代码:
MOV R0, #10 ; R0 = 10, 循环数
MOV R1, #65 ; R1 = 65, ASCII 码 中的'A'
loop:
CMP R0, #0 ; 比较 R0 和 0
BEQ end ; if R0 == 0, 分支结束
BL print ; 调用打印子列
ADD R1, R1, #1 ; R1 = R1 + 1, 增加 ASCII 码值
SUB R0, R0, #1 ; R0 = R0 - 1,减少循环数
B loop ; 分支循环
end:
;结束
下面是一个使用跳转指令实现同样功能的 8086 汇编代码:
MOV CX, 10 ; CX = 10, 循环数
MOV AL, 65 ; AL = 65, ASCII 值中的'A'
loop:
CMP CX, 0 ;比较 CX 和 0
JE end ; if CX == 0, 跳转到尾
CALL print ; 调用打印子列
INC AL ; AL = AL + 1, 增加 ASCII 值
DEC CX ; CX = CX - 1, 减少循环数
JMP loop ; 跳转循环
end:
; 结束
数据传送指令 (Data Transfer)
数据传送指令用于在寄存器、存储器和端口之间传送数据。ARM 和 8086 的数据传送指令有以下区别:
- ARM 的数据传送指令有 LDR 和 STR 两种,LDR 用于从存储器加载数据到寄存器,STR 用于从寄存器存储数据到存储器。8086 的数据传送指令有 MOV、IN、OUT 等,MOV 用于在寄存器、存储器和立即数之间传送数据,IN 用于从端口输入数据到寄存器,OUT 用于从寄存器输出数据到端口。
- ARM 的数据传送指令可以使用不同的寻址模式,例如立即数寻址、寄存器寻址、寄存器间接寻址、基址变址寻址等。8086 的数据传送指令也可以使用不同的寻址模式,例如立即数寻址、寄存器寻址、直接寻址、寄存器间接寻址、基址变址寻址、相对寻址、段寄存器寻址等。
- ARM 的数据传送指令可以在指令后加上条件码,表示只有在满足条件时才执行数据传送,例如 LDREQ 表示等于时加载,STRNE 表示不等于时存储。8086 的数据传送指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用数据传送指令实现从存储器读取一个字节并输出到端口的 ARM 汇编代码:
LDR R0, =data ; R0 = 数据地址
LDRB R1, [R0] ; R1 = 数据的字节值
STR R1, =port ; 将 R1 输出到端口
下面是一个使用数据传送指令实现同样功能的 8086 汇编代码:
MOV SI, data ; SI = 数据地址
MOV AL, [SI] ; AL = 数据的字节值
OUT port, AL ; 将 AL 输出到端口
算术运算指令 (Arithmetic Operations)
算术运算指令用于在寄存器或存储器中进行加、减、乘、除等运算。ARM 和 8086 的算术运算指令有以下区别:
- ARM 的算术运算指令有 ADD、SUB、MUL、DIV 等,ADD 用于加法运算,SUB 用于减法运算,MUL 用于乘法运算,DIV 用于除法运算。8086 的算术运算指令有 ADD、SUB、MUL、DIV、INC、DEC、NEG、CMP 等,ADD 用于加法运算,SUB 用于减法运算,MUL 用于无符号数乘法运算,DIV 用于无符号数除法运算,INC 用于增量运算,DEC 用于减量运算,NEG 用于取负运算,CMP 用于比较运算。
- ARM 的算术运算指令可以使用不同的操作数,例如立即数、寄存器、存储器等。8086 的算术运算指令也可以使用不同的操作数,例如立即数、寄存器、存储器等。
- ARM 的算术运算指令可以在指令后加上条件码,表示只有在满足条件时才执行算术运算,例如 ADDEQ 表示等于时加,SUBNE 表示不等于时减。8086 的算术运算指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用算术运算指令实现计算两个数的最大公约数的 ARM 汇编代码:
; 假设 R0 和 R1 存放两个要求最大公约数的数,R2 存放余数,R3 存放最大公约数
; 先比较 R0 和 R1 的大小,确保 R0 大于等于 R1
CMP R0, R1 ; 比较 R0 和 R1
BGE loop ; 如果 R0 大于等于 R1,跳转到 loop
MOV R2, R0 ; 否则,交换 R0 和 R1
MOV R0, R1
MOV R1, R2
loop:
UDIV R2, R0, R1 ; R2 = R0 / R1,无符号除法,得到余数
CMP R2, #0 ; 比较 R2 和 0
BEQ done ; 如果 R2 等于 0,跳转到 done
MOV R0, R1 ; 否则,将 R1 赋值给 R0
MOV R1, R2 ; 将 R2 赋值给 R1
B loop ; 跳转到 loop
done:
MOV R3, R1 ; 将 R1 赋值给 R3,此时 R3 就是最大公约数
下面是一个使用算术运算指令实现同样功能的 8086 汇编代码:
; 假设 AX 和 BX 存放两个要求最大公约数的数,DX 存放余数,CX 存放最大公约数
; 先比较 AX 和 BX 的大小,确保 AX 大于等于 BX
CMP AX, BX ; 比较 AX 和 BX
JAE loop ; 如果 AX 大于等于 BX,跳转到 loop
MOV DX, AX ; 否则,交换 AX 和 BX
MOV AX, BX
MOV BX, DX
loop:
XOR DX, DX ; 清空 DX
DIV BX ; DX:AX / BX,无符号除法,商在 AX,余数在 DX
CMP DX, 0 ; 比较 DX 和 0
JE done ; 如果 DX 等于 0,跳转到 done
MOV AX, BX ; 否则,将 BX 赋值给 AX
MOV BX, DX ; 将 DX 赋值给 BX
JMP loop ; 跳转到 loop
done:
MOV CX, BX ; 将 BX 赋值给 CX,此时 CX 就是最大公约数```
注:计算两个数的最大公约数的一种常用方法是辗转相除法,即用较大的数除以较小的数,然后用较小的数除以余数,重复这个过程,直到余数为零,此时的除数就是最大公约数。
逻辑运算指令 (Logical Operations)
逻辑运算指令用于在寄存器或存储器中进行与、或、非、异或等运算。ARM 和 8086 的逻辑运算指令有以下区别:
- ARM 的逻辑运算指令有 AND、ORR、EOR、MVN 等,AND 用于按位与运算,ORR 用于按位或运算,EOR 用于按位异或运算,MVN 用于按位取反运算。8086 的逻辑运算指令有 AND、OR、XOR、NOT 等,AND 用于按位与运算,OR 用于按位或运算,XOR 用于按位异或运算,NOT 用于按位取反运算。
- ARM 的逻辑运算指令可以使用不同的操作数,例如立即数、寄存器、存储器等。8086 的逻辑运算指令也可以使用不同的操作数,例如立即数、寄存器、存储器等。
- ARM 的逻辑运算指令可以在指令后加上条件码,表示只有在满足条件时才执行逻辑运算,例如 ANDEQ 表示等于时与,ORRNE 表示不等于时或。8086 的逻辑运算指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用逻辑运算指令实现计算两个数的奇偶性的 ARM 汇编代码:
MOV R0, #12 ; R0 = 12, 第一个数字
MOV R1, #18 ; R1 = 18, 第二个数字
AND R2, R0, #1 ; R2 = R0 & 1, R0 的最低有效位
AND R3, R1, #1 ; R3 = R1 & 1, R1 的最低有效位
CMP R2, #0 ; 比较 R2 和 0
BEQ even1 ; if R2 == 0, 分支到 even1
BL odd1 ; else, 调用 odd1 子例
even1:
BL even2 ; 调用 even2 子例程
CMP R3, #0 ; 比较 R3 和 0
BEQ even2 ; if R3 == 0, 分支到 even2
BL odd2 ; else, 调用 odd2 子例程
even2:
BL even3 ; 调用 even3 子例程
下面是一个使用逻辑运算指令实现同样功能的 8086 汇编代码:
MOV AX, 12 ; AX = 12, 第一个数字
MOV BX, 18 ; BX = 18, 第二个数字
AND AL, 1 ; AL = AX & 1, AX 的最低有效位
AND BL, 1 ; BL = BX & 1, BX 的最低有效位
CMP AL, 0 ; 比较 AL 和 0
JE even1 ; if AL == 0, 跳转到 even1
CALL odd1 ; else, 调用 odd1 子例程
even1:
CALL even2 ; 调用 even2 子例
CMP BL, 0 ; 比较 BL 和 0
JE even2 ; if BL == 0, 跳转到even2
CALL odd2 ; else, 调用 odd2 子例
even2:
CALL even3 ; 调用 even3 子例
移位运算指令 (Shift Operations)
移位运算指令用于在寄存器或存储器中进行左移、右移、循环移位等运算。ARM 和 8086 的移位运算指令有以下区别:
- ARM 的移位运算指令有 LSL、LSR、ASR、ROR 等,LSL 用于逻辑左移运算,LSR 用于逻辑右移运算,ASR 用于算术右移运算,ROR 用于循环右移运算。8086 的移位运算指令有 SHL、SHR、SAR、ROR、ROL 等,SHL 用于逻辑左移运算,SHR 用于逻辑右移运算,SAR 用于算术右移运算,ROR 用于循环右移运算,ROL 用于循环左移运算。
- ARM 的移位运算指令可以使用不同的操作数,例如立即数、寄存器、存储器等。8086 的移位运算指令也可以使用不同的操作数,例如立即数、寄存器、存储器等。
- ARM 的移位运算指令可以在指令后加上条件码,表示只有在满足条件时才执行移位运算,例如 LSLEQ 表示等于时左移,LSRNE 表示不等于时右移。8086 的移位运算指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用移位运算指令实现计算一个数的二进制表示中有多少个 1 的 ARM 汇编代码:
MOV R0, #12 ; R0 = 12, 要计数的数字
MOV R1, #0 ; R1 = 0, 计数器
loop:
CMP R0, #0 ; 比较 R0 和 0
BEQ end ; if R0 == 0, 分支结束
AND R2, R0, #1 ; R2 = R0 & 1, R0 的最低有效位
ADD R1, R1, R2 ; R1 = R1 + R2, 如果 R2 为 1,则递增计数
LSR R0, R0, #1 ; R0 = R0 >> 1, 右移 R0 1 位
B loop ; 分支循环
end:
; R1 包含 R0 中的 1 个
下面是一个使用移位运算指令实现同样功能的 8086 汇编代码:
MOV AX, 12 ; AX = 12, 要计数的数字
MOV CX, 0 ; CX = 0, 计数器
loop:
CMP AX, 0 ; 比较 AX 和 0
JE end ; if AX == 0, 跳转到尾
AND AL, 1 ; AL = AX & 1, AX 的最低有效位
ADD CX, AX ; CX = CX + AX, 如果 AL 为 1,则递增计数
SHR AX, 1 ; AX = AX >> 1, 右移 AX 1 位
JMP loop ; 跳转循环
end:
; CX 包含 AX 中的 1 个
位操作指令 (Bit Operations)
位操作指令用于在寄存器或存储器中进行位测试、位设置、位清除等运算。ARM 和 8086 的位操作指令有以下区别:
- ARM 的位操作指令有 TST、TEQ、BIC 等,TST 用于按位与测试运算,TEQ 用于按位异或测试运算,BIC 用于按位清除运算。8086 的位操作指令有 BT、BTS、BTR、BTC 等,BT 用于位测试运算,BTS 用于位设置运算,BTR 用于位清除运算,BTC 用于位反转运算。
- ARM 的位操作指令可以使用不同的操作数,例如立即数、寄存器、存储器等。8086 的位操作指令也可以使用不同的操作数,例如立即数、寄存器、存储器等。
- ARM 的位操作指令可以在指令后加上条件码,表示只有在满足条件时才执行位操作,例如 TSTEQ 表示等于时测试,BICNE 表示不等于时清除。8086 的位操作指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用位操作指令实现判断一个数是否为 2 的幂的 ARM 汇编代码:
MOV R0, #16 ; R0 = 16, 要检查的号码
SUB R1, R0, #1 ; R1 = R0 - 1, 从 R0 中减去 1
TST R0, R1 ; test R0 & R1
BEQ power ; if R0 & R1 == 0, 分支power
BL notpower ; else, 调用 NotPower 子例
power:
BL power2 ; 调用 Power2 子例程
下面是一个使用位操作指令实现同样功能的 8086 汇编代码:
MOV AX, 16 ; AX = 16, 要检查的号码
DEC AX ; AX = AX - 1, 将 AX 减 1
BT AX, 0 ; 测试 AX 的第 0 位
JC notpower ; if CF == 1, 跳转到 NotPower
JZ power ; if ZF == 1, 跳到power
CALL notpower ; else, 调用 NotPower 子例
power:
CALL power2 ; 调用 Power2 子例
栈操作指令 (Stack Operations)
栈操作指令用于在栈中进行数据的压入和弹出。ARM 和 8086 的栈操作指令有以下区别:
- ARM 的栈操作指令有 PUSH 和 POP 两种,PUSH 用于将一个或多个寄存器的值压入栈中,POP 用于将一个或多个寄存器的值从栈中弹出。8086 的栈操作指令有 PUSH 和 POP 两种,PUSH 用于将一个寄存器或存储器的值压入栈中,POP 用于将一个寄存器或存储器的值从栈中弹出。
- ARM 的栈操作指令可以使用不同的寄存器,例如 R0-R15 等。8086 的栈操作指令只能使用 SP 和 BP 两个寄存器作为栈指针和基指针。
- ARM 的栈操作指令可以在指令后加上条件码,表示只有在满足条件时才执行栈操作,例如 PUSHEQ 表示等于时压栈,POPNE 表示不等于时弹栈。8086 的栈操作指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用栈操作指令实现将两个数的和和差保存到栈中的 ARM 汇编代码:
MOV R0, #12 ; R0 = 12, 第一个数字
MOV R1, #18 ; R1 = 18, 第二个数字
ADD R2, R0, R1 ; R2 = R0 + R1, 和
SUB R3, R0, R1 ; R3 = R0 - R1, 差
PUSH {R2, R3} ; 将 R2 和 R3 推送到堆栈
下面是一个使用栈操作指令实现同样功能的 8086 汇编代码:
MOV AX, 12 ; AX = 12, 第一个数字
MOV BX, 18 ; BX = 18, 第二个数字
ADD CX, AX, BX ; CX = AX + BX, 和
SUB DX, AX, BX ; DX = AX - BX, 差
PUSH CX ; 将 CX 推送到堆栈
PUSH DX ; 将 DX 推送到堆栈
输入输出指令 (Input/Output)
输入输出指令用于在 CPU 和外部设备之间进行数据的交换。ARM 和 8086 的输入输出指令有以下区别:
- ARM 的输入输出指令没有专门的指令,而是使用数据传送指令和存储器映射的方式来实现,即将外部设备的地址映射到存储器空间中,然后使用 LDR 和 STR 指令来进行数据的读写。8086 的输入输出指令有 IN 和 OUT 两种,IN 用于从端口输入数据到寄存器,OUT 用于从寄存器输出数据到端口。
- ARM 的输入输出指令可以使用不同的寄存器,例如 R0-R15 等。8086 的输入输出指令只能使用 AL 或 AX 作为数据寄存器,DX 或立即数作为端口号。
- ARM 的输入输出指令可以在指令后加上条件码,表示只有在满足条件时才执行输入输出,例如 LDREQ 表示等于时加载,STRNE 表示不等于时存储。8086 的输入输出指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用输入输出指令实现从键盘输入一个字符并显示到屏幕的 ARM 汇编代码:
LDR R0, =keyboard ; R0 = 键盘的地址
LDRB R1, [R0] ; R1 = 键盘的字节值
LDR R0, =screen ; R0 = 屏幕地址
STRB R1, [R0] ; 将 R1 输出到屏幕
下面是一个使用输入输出指令实现同样功能的 8086 汇编代码:
MOV DX, keyboard ; DX = 键盘的端口号
IN AL, DX ; AL = 键盘的字节值
MOV DX, screen ; DX = 屏幕的端口号
OUT DX, AL ; 将 AL 输出到屏幕
中断指令 (Interrupt)
中断指令用于触发或响应中断,中断是一种特殊的事件,可以打断程序的正常执行流程,转而执行一段中断服务程序。ARM 和 8086 的中断指令有以下区别:
- ARM 的中断指令有 SVC、BKPT、WFI、WFE 等,SVC 用于产生软件中断,BKPT 用于产生断点中断,WFI 用于等待中断发生,WFE 用于等待事件发生。8086 的中断指令有 INT、IRET、CLI、STI 等,INT 用于产生软件中断,IRET 用于从中断服务程序返回,CLI 用于禁止中断,STI 用于允许中断。
- ARM 的中断指令可以使用不同的参数,例如 SVC #n 表示产生编号为 n 的软件中断,BKPT #n 表示产生编号为 n 的断点中断。8086 的中断指令只能使用立即数作为参数,例如 INT 21h 表示产生编号为 21h 的软件中断。
- ARM 的中断指令可以在指令后加上条件码,表示只有在满足条件时才执行中断,例如 SVCNE #n 表示不等于时产生软件中断,WFEQ 表示等于时等待事件。8086 的中断指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用中断指令实现从键盘输入一个字符并显示到屏幕的 ARM 汇编代码:
SVC #0 ; 生成软件中断以从键盘读取字符
; 该字符在 R0 中返回
SVC #1 ; 生成软件中断以将字符写入屏幕
; 该字符取自 R0
下面是一个使用中断指令实现同样功能的 8086 汇编代码:
MOV AH, 1 ; 将功能代码设置为1,从键盘读取一个字符
INT 21h ; 生成软件中断以调用 DOS 服务
; 字符在 AL 中返回
MOV AH, 2 ; 将功能代码设置为2,在屏幕上写入一个字符
INT 21h ; 生成软件中断以调用 DOS 服务
; 状态取自DL
协处理器指令 (Coprocessor)
协处理器指令用于在 CPU 和协处理器之间进行数据的交换和运算,协处理器是一种辅助的处理器,可以增强 CPU 的功能,例如浮点运算、图形处理、加密解密等。ARM 和 8086 的协处理器指令有以下区别:
- ARM 的协处理器指令有 MRC、MCR、CDP 等,MRC 用于从协处理器寄存器读取数据到 CPU 寄存器,MCR 用于从 CPU 寄存器写入数据到协处理器寄存器,CDP 用于在协处理器上执行运算。8086 的协处理器指令有 FLD、FST、FADD、FSUB 等,FLD 用于从存储器或寄存器加载数据到协处理器栈,FST 用于从协处理器栈存储数据到存储器或寄存器,FADD 用于在协处理器栈上执行浮点加法运算,FSUB 用于在协处理器栈上执行浮点减法运算。
- ARM 的协处理器指令可以使用不同的协处理器编号,例如 MRC p15, 0, R0, c1, c0, 0 表示从编号为 15 的协处理器的 c1 寄存器的第 0 位读取数据到 R0 寄存器。8086 的协处理器指令只能使用 8087 协处理器,它有八个 80 位的浮点寄存器,组成一个栈结构,称为 ST0-ST7。
- ARM 的协处理器指令可以在指令后加上条件码,表示只有在满足条件时才执行协处理器指令,例如 MRCEQ 表示等于时读取,MCRNE 表示不等于时写入。8086 的协处理器指令没有条件码,而是使用单独的条件转移指令,例如 JE 表示等于时跳转,JNE 表示不等于时跳转。
例如,下面是一个使用协处理器指令实现计算两个浮点数的和和差的 ARM 汇编代码:
LDR R0, =num1 ; R0 = num1 的地址
LDR R1, =num2 ; R1 = num2 的地址
VLDR S0, [R0] ; S0 = num1 的浮点值
VLDR S1, [R1] ; S1 = num2 的浮点值
VADD F32 S2, S0, S1 ; S2 = S0 + S1, 和
VSUB F32 S3, S0, S1 ; S3 = S0 - S1, 差
下面是一个使用协处理器指令实现同样功能的 8086 汇编代码:
FLD num1 ; 将 num1 的浮点值加载到 ST0
FLD num2 ; 将 num2 的浮点值加载到 ST0,ST1 = num1
FADD ; 将 ST0 和 ST1 相加,ST0 = num1 + num2,即总和
FST sum ; 将 ST0 存储到总和
FSUB ; 减去 ST0 和 ST1,ST0 = num1 - num2,差值
FST diff ; 将 ST0 存储到 diff
总结
在进行以上的基于实例的分析以后,来做个小总结。
基于arm的微机原理与接口技术和基于8086的微机原理与接口技术的代码和指令方面有很多区别,主要体现在以下几个方面:文章来源:https://www.toymoban.com/news/detail-838181.html
- 架构类型:arm是一种精简指令集(RISC)的架构,而8086是一种复杂指令集(CISC)的架构。RISC架构的指令数量较少,格式统一,执行速度快,但需要更多的寄存器和存储器访问。CISC架构的指令数量较多,格式不一,执行速度慢,但可以减少编程的难度和存储器的占用。
- 指令长度:arm的指令长度为32位,而8086的指令长度为8位到16位不等。这意味着arm的指令可以携带更多的信息,而8086的指令需要更多的前缀和后缀来扩展功能。
- 寻址模式:arm的寻址模式较少,主要有立即数寻址、寄存器寻址、寄存器间接寻址、基址变址寻址等。而8086的寻址模式较多,除了上述寻址模式外,还有直接寻址、相对寻址、段寻址、堆栈寻址等。寻址模式的多少影响了指令的灵活性和复杂性。
- 寄存器数量和功能:arm的寄存器数量较多,有16个通用寄存器,其中一个作为程序计数器,另一个作为当前程序状态寄存器。而8086的寄存器数量较少,只有14个寄存器,其中有8个通用寄存器,4个段寄存器,一个程序计数器,一个标志寄存器。arm的寄存器功能较为统一,可以用于数据操作、地址计算、条件判断等。而8086的寄存器功能较为分散,有些寄存器只能用于特定的用途,如段寄存器只能用于段寻址,标志寄存器只能用于条件判断等。
- 数据类型和操作:arm支持的数据类型有字节(8位)、半字(16位)、字(32位)和双字(64位),可以进行逻辑、算术、移位、乘法、除法等操作。而8086支持的数据类型有字节(8位)、字(16位)和双字(32位),可以进行逻辑、算术、移位、乘法、除法、字符串处理等操作。arm的数据操作一般都是寄存器到寄存器的,而8086的数据操作可以是寄存器到寄存器、寄存器到存储器、存储器到寄存器或存储器到存储器的。
除了代码和指令方面的区别外,基于ARM和基于8086的微机原理与接口技术还有其他方面的区别,如:文章来源地址https://www.toymoban.com/news/detail-838181.html
- 系统结构:arm的系统结构一般是基于片上系统(SOC)的,将CPU、存储器、外设等集成在一个芯片上,实现高度的集成和低功耗。而8086的系统结构一般是基于总线的,将CPU、存储器、外设等通过总线连接起来,实现高度的扩展和兼容。
- 中断处理:arm的中断处理一般是基于向量的,将不同的中断源分配不同的中断向量,通过中断向量表来定位中断服务程序。而8086的中断处理一般是基于优先级的,将不同的中断源分配不同的优先级,通过中断控制器来选择优先级最高的中断服务程序。
- 存储器管理:arm的存储器管理一般是基于分页的,将存储器划分为固定大小的页,通过页表来实现逻辑地址到物理地址的映射。而8086的存储器管理一般是基于分段的,将存储器划分为不同大小的段,通过段基址和段偏移量来实现逻辑地址到物理地址的转换。
参考
- 阎波. 微处理器系统结构与嵌入式系统设计-3版 [M]. 北京:电子工业出版社,2020.8 ISBN
978-7-121-35822-7 - Intel 公司. IA-32 Intel Architecture Software Developer’s Manual. Volume 1. Basic Architecture.
- 周明德,张淑玲. 80X86、80X87结构与汇编语言程序设计. 北京:清华大学出版社,1993.
到了这里,关于全面的介绍——基于ARM的微机和基于8086的微机的代码层面的区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!