计算机组成原理指令部分
指令寻址 vs 数据寻址
为什么要寻址,寻址的目的是找到指令中操作数所在的地址(位置)。好比我要联系你,我得知道你的手机号码一样。
要理解寻址,先要看数据可能存在与哪些地方?1.指令代码中可能就含有数据,必然MOV AX, 2000H, 这个2000H是一个常数存在与指令代码中,通过指令立即可以得到,所以称为立即寻址;2.CPU内部的寄存器。寄存器存在于CPU内部,是CPU的左右手,如果数据放在寄存器里,比如MOV AX,BX, 数据放在BX寄存器中,所以是寄存器寻址;3.I/O端口寻址。要读写I/O口必须使用总线,也就是需要有总线周期来完成数据读写,端口寻址简单点,使用IN,OUT指令。按8086CPU来讲,如果端口号小于255则可以直接读写,比如IN AL, 30H,表明我要从30H地址读一个字节。如果端口号大于255,则必须通过寄存器DX来周转一下,称为寄存器间接寻址,比如要从300H端口读一个字节,就不能使用IN AL,300H,而是使用MOV DX,300H, IN AL,DX;
指令寻址
数据寻址
寻址方式
直接寻址
直接在指明的地址中查找元素
间接寻址
间接寻址:指令的地址字段给出的形式地址不是操作数的真正地址,而是操作数有效地址所在的存储单元的地址,也就是操作数地址的地址,即EA=(A)。
寄存器寻址
寄存器间接寻址
立即寻址
我们想要的操作数直接包含在指令中
立即寻址:形式地址A就是操作数本身,又称为大立即数一般采用补码形式。# 表示立即寻址特征。
隐含取址
偏移寻址
基址寻址
注:基址寄存器是面向操作系统的,其内容由操作系统或管理程序确定。。在程序执行过程中,基址寄存器的内容不变(作为基地址),形式地址可变(作为偏移量)。当采用通用寄存器作为基址寄存器时,可由用户决定哪个寄存器作为基址寄存器但其内容仍由操作系统确定
优点:可扩大寻址范围>(基址寄存器的位数大于形式地址A的位数);用户不必考虑自己的程序存于主存的哪一空间区域,故有利于多道程序设计,以及可用于编制浮动程序(整个程序在内存里边的浮动)
变址寻址
变址寻址:有效地址EA等于指令字中的形式地址A与变址寄存器α的内容相加之和,即EA= (IX)+A,其中Ix可为变址寄存器(专用),也可用通用寄存器作为变址寄存器。
一般寻址的多种寻址的符和
相对寻址
相对寻址:把程序计数器PC的内容加上指令格式中的形式地址A而形成操作数的有效地址,即EA=(PC)+A,其中A是相对于PC所指地址的位移量,可正可负,补码表示
优点:操作数的地址不是固定的,它随着PC值的变化而变化,并且与指令地址之间总是相差一个固定值,因此便于程序浮动(一段代码在程序内部的浮动)。相对寻址广泛应用于转移指令。
堆栈寻址
本段Markdown文本旨在介绍堆栈寻址的概念和基本原理。堆栈寻址是一种在计算机程序中管理内存和执行操作的方式,它利用了堆栈的数据结构特性来实现。
堆栈寻址的概念
在计算机科学中,堆栈是一种线性数据结构,其中元素的添加和移除只能在结构的一端进行,这个端点被称为栈顶。堆栈寻址利用了这种后进先出(LIFO,Last In First Out)的数据访问模式来管理程序执行期间的内存地址。
堆栈的基本操作
堆栈寻址通常支持以下基本操作:
- 压栈(Push):将一个元素添加到栈顶。
- 弹出(Pop):移除栈顶的元素。
- 读取栈顶元素(Peek):获取栈顶元素的值,但不移除它。
- 检查堆栈是否为空(IsEmpty):确定堆栈是否不包含任何元素。
堆栈寻址的应用
堆栈寻址在许多计算机程序设计领域中有着广泛的应用,例如:
- 函数调用和返回地址管理:在执行函数调用时,函数的参数和返回地址通常被压入堆栈中,以便于处理器进行正确的参数传递和返回地址管理。
- 表达式求值:在计算中缀或后缀表达式时,堆栈可以用来存储操作数和操作符,从而有效地进行求值过程。
- 递归处理:在递归算法中,堆栈寻址用于存储递归调用的上下文信息,确保正确地恢复和继续递归过程。
通过理解堆栈寻址的基本原理和操作,程序员可以更有效地设计和优化计算机程序,特别是在处理需要高效内存管理的任务时。
机器级代码
结合C语言描绘汇编语言,
指令格式: 操作码 + 地址码
mov指令
mov 目的操作数d , 资源操作数s
# mov 指令功能,将源操作数s复制到目的操作数d所在位置
mov eax,ebx #将寄存器 ebx 的值复制到寄存器 eax
mov eax 5 #将立即数5 复制到寄存器eax
mov eax,dword ptr [af996h] #将内存地址 af996h 所指的32bit 值复制到寄存器 eax
mov byte ptr [af996h], 5 #将立即数 5 复制到内存地址 af996h所指的一字节中
指明内存读写长度: dword ptr 双字节 32bit
word ptr 单字节 16bit
byte ptr 字节 8bit
寄存器的类型
EAX EBX ECX EDX ESI EDI EBP ESP
去掉 E 寄存器只存储后16位更低的位置
更多例子
; 将 ebx 所指向的内存地址中的 32 位(4 字节)数据复制到 eax 寄存器中
mov eax, dword ptr [ebx]
; 将 eax 寄存器中的内容复制到 ebx 所指向的内存地址的 32 位(4 字节)位置
mov dword ptr [ebx], eax
; 将 ebx 所指向的内存地址中的 8 位(1 字节)数据复制到 eax 寄存器的低 8 位中,其余位清零
mov eax, byte ptr [ebx]
; 将 ebx 所指向的内存地址中的 32 位(4 字节)数据复制到 eax 寄存器中
; (这里未指明内存读写长度,但默认是 32 位,因为 eax 是 32 位寄存器)
mov eax, [ebx]
; 将 eax 寄存器中的内容复制到地址 0xAF996h 所指向的内存位置的 32 位(4 字节)中
; (这里未指明长度,但默认是 32 位)
mov [0xAF996h], eax
; 将 ebx+8 所指向的内存地址中的 32 位(4 字节)数据复制到 eax 寄存器中
mov eax, dword ptr [ebx+8]
常用的x86的机器指令
功能 | 指令 | 注释 |
---|---|---|
加 | add d,s | 将s的值加到d上,结果存储在d中 |
减 | sub d,s | 从d中减去s的值,结果存储在d中 |
乘 | mul d,s 或 imul d,s | d乘以s,结果存储在d中(imul 为带符号乘法) |
除 | div s 或 idiv s | 将d除以s,结果存储在d中(idiv 为带符号法) |
取负数 | neg d | 将d的值取反(即乘以-1),结果存储在d中 |
自增++ | inc d | 将d的值加1 |
自减– | dec d | 将d的值减1 |
以下是包含左移和右移指令的x86逻辑指令表:
功能 | 指令 | 描述 |
---|---|---|
逻辑与 | AND | 执行逻辑与操作,将目标操作数和源操作数进行位与运算,结果存储在目标操作数中 |
逻辑或 | OR | 执行逻辑或操作,将目标操作数和源操作数进行位或运算,结果存储在目标操作数中 |
逻辑异或 | XOR | 执行逻辑异或操作,将目标操作数和源操作数进行位异或运算,结果存储在目标操作数中 |
逻辑非 | NOT | 执行逻辑非操作,对目标操作数进行位取反运算,结果存储在目标操作数中 |
位测试 | TEST | 执行位测试操作,比较目标操作数和源操作数的位,根据比较结果设置标志寄存器中的相关位,但不存储结果 |
无符号左移 | SHL | 将目标操作数的所有位向左移动指定的位数,右侧用0填充 |
有符号左移 | SAL | 同SHL ,通常用于有符号整数 |
无符号右移 | SHR | 将目标操作数的所有位向右移动指定的位数,左侧用0填充(逻辑右移) |
有符号右移 | SAR | 将目标操作数的所有位向右移动指定的位数,左侧用符号位填充(算术右移) |
左移指令(SHL
和SAL)将操作数的所有位向左移动指定的位数,右侧用0填充。右移指令(SHR
和SAR
)将操作数的所有位向右移动指定的位数。SHR
是逻辑右移,左侧用0填充;而SAR
是算术右移,左侧用符号位(最高位)填充,通常用于保持有符号整数的符号。
AT & T格式 vs Intel 格式
选择语句在汇编语言的表示
section .data
a dd 10
b dd 5
section .text
global _start
_start:
; 将a和b加载到寄存器中
mov eax, [a]
mov ebx, [b]
; 比较a和b
cmp eax, ebx
; 如果a > b,则跳转到greater_than_label
jg greater_than_label
; 如果a不大于b,则执行这里的代码
; ...
; 跳转到程序结束
jmp end_of_program
greater_than_label:
; 如果a > b,则执行这里的代码
; ...
end_of_program:
; 程序结束
; ...
C/C++语言程序
if(a > b) {
c = a;
} else {
c = b;
}
转换为汇编后
mov eax,7 # 假设变量 a = 7 , 存放于 eax
mov ebx, 6 # 假设变量 b = 6 , 存放于 ebx
cmp eax, ebx # 比较变量 a 和 b
jg NEXT # 如果a > b 转移到 NEXT 否命题 jle
mov ecx, ebx # 假设用 ecx 存储变量c 另 c = b
jmp END #无条件跳转到 END:
NEXT:
mov ecx,eax # 假设用 ecx 存储变量c 另 c = a
END:
用条件转移指令实现循环
int result = 0;
for (int i = 1; i <= 100; i++) {
result += i;
}
转换为汇编
mov eax, 0 #用 eax 保存 result 初始值为0
mov edx, 1 #用 edx 保存 i, 初始化 为 1
cmp edx, 100 #比较 i 和 100
jg L2 #若 i > 100,跳转到 L2 执行
L1: #循环主体
add eax,edx; #实现 result += i
inc edx #inc 自增指令, 实现 i++
cmp edx, 100 # i 和 100 比较
jle L1 # i <= 100 跳转到 L1
L2: #跳出循环
loop 指令实现循环
movecx,500 #用ecx作为循环计数器
Looptop: #循环的开始
做某些处理
.....
loop Looptop #ecx--,若ecx!=0,跳转到Looptop
函数调用机器即表示
call
, ret
指令
函数的调用是压栈的过程
Push 指令与 Pop 指令
-
Push 指令将数据压入栈顶,栈顶指针 esp 向下移动 4 个字节。
-
Pop 指令将栈顶数据弹出,栈顶指针 esp 向上移动 4 个字节。
-
Push 指令与 Pop 指令的顺序必须一致,否则会导致栈帧数据错误。
mov 指令与 esp, ebp 指令访问栈帧数据
- mov 指令将数据从寄存器或主存地址移动到寄存器或主存地址。
- esp 指令将栈顶指针 esp 移动到寄存器 eax。
- ebp 指令将栈帧基址 ebp 移动到寄存器 eax。
- 栈帧基址 ebp 存储了栈帧的起始地址,栈顶指针 esp 存储了栈帧的结束地址。
- 栈帧最底部一定是上一层栈帧。
如何访问栈帧里面的数据
方法一:
Push * //先让esp 减少 4 在将 * 压入
Pop ! // 栈顶元素出栈写入!, 再让 esp 加 4
//Ps: *可以是立即数, 寄存器 主存地址
// Ps: !可以是寄存器, 主存地址
方法二:
mov 指令, 结合 esp, ebp 指令访问栈帧数据
//Ps: 可以减法/加法指令, 即 sub/add 修改栈顶指针 esp 的值
函数返回时,如何切换 栈帧
-
通常将局部变量集中存储在栈帧底部区域
-
通常将调用参数集中存储在栈帧顶部区域栈帧最底部一定是上一层
-
栈帧基址上(ebpl旧值)
-
栈帧最顶部一定是返回地址上(当前函数的栈帧除外)
-
调用参数。参数列表中越靠前的参数越靠近栈顶
CISC 和 RISC
CISC设计思想: 可变指令集
- 允许指令集的扩展
- 允许指令集的收缩
- 允许指令集的优化
RISC设计思想: 固定指令集
- 允许指令集的扩展
- 允许指令集的收缩
- 允许指令集的优化
- 允许指令集的替换
允许指令集的扩展: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许指令集的扩展, 例如: 允许
80 - 20 规律: 80%的指令集是固定的, 20%的指令集是可变的, 即80%的语句仅仅使用处理机中 20%的指令,
汇编语言中指令的分类
- 逻辑指令
- 存储指令
- 跳转指令
- 栈指令
- 输入输出指令
- 函数调用指令
- 函数返回指令
- 运算指令
CISC 与 RISC 的比较列出:
特性 | CISC指令集 | RISC指令集 |
---|---|---|
指令数量 | 多 | 少 |
指令集可变性 | 可变 | 固定 |
指令集扩展性 | 可扩展 | 不可扩展 |
指令集收缩性 | 可收缩 | 不可收缩 |
指令集优化性 | 可优化 | 不可优化 |
指令集可替换性 | 可替换 | 不可替换 |
运算器的基本储存
cd.控制单元,分析指令,给出控制信号
IR:指令寄存器,存放当前执行的指令
PC:程序计数器,存放下一条指令地址,有自动加1功能
中央处理器
CPU的功能
1.指令控制: 完成取指令,分析指令和执行指令的操作
2.操作控制: 一条指令的功能往往是由若干操作信号的组合来实现的。CPU管理并产生由内存取出的每条指令的操作信号,把各种操作信号送往相应的部件,从而控制这些部件按指令的要求进行动作。
3.数据加工: CPU负责将指令中的数据取出,并把数据送到相应的部件,由部件完成数据的加工,并把加工后的数据送到相应的部件。
4.时间控制: CPU负责控制各个部件的时间,使各个部件按指令要求完成工作。
5.中断处理: CPU负责中断处理,中断处理是CPU与外设之间的一种通信方式,CPU收到中断信号后,会暂停当前指令的执行,然后根据中断信号向外设请求数据,然后根据请求的数据,执行相应的操作。
运算器和控制器的功能
1.运算器: 运算器负责完成指令中的运算操作。
2.控制器: 控制器负责控制运算器、存储器、输入输出部件、中断控制器等部件的时间,使各个部件按指令要求完成工作。
堆栈寄存器
堆栈寄存器是CPU中一个特殊的寄存器,用于存放函数调用时,函数的局部变量。
专用数据通路方式:根据指令执行过程中的数据和地址的流动方向安排连接线路
多路选择器(MUX) :多路选择器是CPU中一个特殊的部件,用于根据不同的条件选择不同的数据或地址。
三态门: 三态门是CPU中一个特殊的部件,用于根据不同的条件选择不同的数据或地址。
CPU内部单总线方式:将所有寄存器的输入端和输出端都连接到一条公共的通路上
运算器的基本结构
运算器由两个部分组成:运算单元和控制单元。
运算单元:运算单元负责完成指令中的运算操作。
控制单元:控制单元负责控制运算单元的时间,使运算单元按指令要求完成工作。
一条指令分为操作码和地址码
1.操作码:操作码是CPU中一个特殊的寄存器,用于存放当前执行的指令的操作码。
2.地址码:地址码是CPU中一个特殊的寄存器,用于存放当前执行的指令的地址码。
1.程序计数器:程序计数器是CPU中一个特殊的寄存器,用于存放下一条指令的地址。
2.指令寄存器:指令寄存器是CPU中一个特殊的寄存器,用于存放当前执行的指令。
3.指令译码器: 指令译码器是CPU中一个特殊的部件,用于根据指令的格式,将指令翻译成相应的操作码和地址码。
4.微操作信号发生器:微操作信号发生器是CPU中一个特殊的部件,用于根据指令的格式,将指令翻译成相应的微操作信号。
指令在CUP的内部的结构
IR :指令寄存器,存放当前执行的指令
PC :程序计数器,存放下一条指令地址,有自动加1功能
ALU :算术逻辑单元,完成指令中的运算操作
MUX :多路选择器,根据不同的条件选择不同的数据或地址
MUX :三态门,根据不同的条件选择不同的数据或地址
指令周期
1.取指令:从内存中取出下一条指令,并放到指令寄存器中
2.译码:根据指令的格式,将指令翻译成相应的操作码和地址码
3.执行:根据操作码和地址码,执行相应的操作
4.更新PC:更新程序计数器,将下一条指令的地址放到程序计数器中
5.等待:等待下一条指令的执行
时钟周期
1.时钟信号:时钟信号是CPU中一个特殊的部件,用于控制CPU的时间。
2.时钟信号发生器:时钟信号发生器是CPU中一个特殊的部件,用于产生时钟信号。
单指令周期
1.取指令:从内存中取出下一条指令,并放到指令寄存器中
2.译码:根据指令的格式,将指令翻译成相应的操作码和地址码
3.执行:根据操作码和地址码,执行相应的操作
4.更新PC:更新程序计数器,将下一条指令的地址放到程序计数器中
5.等待:等待下一条指令的执行
多指令周期
对不同类型的指令选用不同的步骤完成
单指令周期
指令是穿行指令
寄存器与寄存器之间的数据联通:
1. 寄存器之间数据传送(比如把PC内容送至MAR)
流程:
- (PC)→Bus:PC的内容发送到总线Bus。
- PCout有效:控制信号PCout变为有效状态,允许PC的内容发送到总线。
- Bus→MAR:总线Bus的内容传送到MAR。
- MARin有效:控制信号MARin变为有效状态,允许总线的内容被MAR接收。
控制信号及操作顺序:
- PCout有效
- (PC)→Bus
- MARin有效
- Bus→MAR
2. 主存与CPU之间的数据传送(比如CPU从主存读取指令)
流程:
- (PC)→Bus→MAR:PC的内容首先发送到总线Bus,然后总线的内容被传送到MAR。
- PCout和MARin有效:这两个控制信号需要同时有效,以便完成PC到MAR的数据传送。
- MAR1→RCU:MAR中的内容(即指令的地址)被发送到RCU(可能是寄存器控制单元或其他相关单元,用于生成控制信号)。
- RCU发读命令:RCU通过控制总线发出读命令。
- MEM(MAR)→MDR:主存根据MAR中的地址读取指令,并将指令发送到MDR(内存数据寄存器)。
- MDRin有效:控制信号MDRin变为有效状态,允许主存发送的指令被MDR接收。
- Bus→MAR:MDR中的内容(即读取的指令)首先发送到总线Bus,然后总线的内容被传送到IR(指令寄存器)。
- MDRout和IRin有效:这两个控制信号需要同时有效,以便完成MDR到IR的数据传送。
控制信号及操作顺序:
1. PCout和MARin有效
2. (PC)→Bus→MAR
3. RCU发读命令
4. MEM(MAR)→MDR
5. MDRin有效
6. MDR→Bus
7. MDRout和IRin有效
8. Bus→IR
注意:在某些处理器设计中,可能不直接涉及Bus→IR的传输,而是直接从MDR到IR。这取决于具体的处理器架构。上面的步骤是基于您提供的描述。
硬布线控制器
- 寄存器之间数据传送(比如把PC内容送至MAR)
- 主存与CPU之间的数据传送(比如CPU从主存读取指令)
Cu 内部的电路
分析每个阶段的微操作序列
取值周期
PC -> MAR -> MEM -> MDR -> IR
OP -> ID -> PC + 1 -> PC
ID : 指令译码器
间址周期:
微程序控制器
安排微操作时序的原则:
- 尽量减少微操作信号的切换次数
- 微操作的先后顺序不能随意更改
- 尽量减少微操作信号的切换次数
微程序控制器的工作原理
微指令的编码方式
- 直接编码:直接将微指令的二进制表示写入微程序内存中。
- 字段直接编码方式: 将微指令的各个字段(如操作码、寄存器号等)分别编码为一个二进制数,然后将这些二进制数按顺序写入微程序内存中。
- 字段间接编码方式: 将微指令的各个字段(如操作码、寄存器号等)分别编码为一个二进制数,然后将这些二进制数按顺序写入微程序内存中,同时将微程序内存中的地址作为微指令的参数。
微指令的地址形成方式
- 微指令下地址
- 根据机器指令的操作码形成
- 增量计数器
- 分支转移
- 通过测试网络
- 硬件产生微程序入口地址
断定法 和 微程序计数法
断定法:根据微程序内存中微指令的编码,判断是否需要执行微指令。
微程序计数法:根据微程序内存中微指令的编码,判断是否需要执行微指令。
微程序控制单元的设计
设计步骤:
-
分析每个阶段的微操作序列
-
写出对应机器指令的微操作命令及节拍安排
(1)写出每个周期所需要的微操作:(参照硬布线)
(2)补充微程序控制器特有的微操作:
a.取指周期:
行Ad(CMDR)→CMAROP(IR)→微地址形成部件→CMAR
b.执行周期行
Ad(CMDR)→CMAR3 -
确定微指令格式
-
编写微指令码点
微程序设计分类
-
静态程序设计和动态程序设计
静态: 微程序无需改变,采用ROM
动态: 通过改变微指令 和 微程序 改变 机器指令 有利于仿真, 采用 EPROM -
豪微程序设计:
豪微程序设计的基本概念
微程序控制器 与 硬部线控制器 的区别
硬布线控制器与微程序控制器的区别: 硬布线控制器是CPU的硬件部分,而微程序控制器是CPU的控制部分。硬布线控制器是CPU的硬件部分,而微程序控制器是CPU的控制部分。
指令流水的定义: 指令流水是CPU执行指令的顺序,它包括指令的取指、译码、执行、访存、写回等阶段。
本文作者:2c237c6
本文链接:https://www.cnblogs.com/27dCnc/p/18568600
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步