CPU的控制器大致有哪些功能和其具体原理?
——解析指令,根据指令指挥其他部件协调工作。
指令该如何设计?
4.1 指令格式
4.1.1 指令的基本格式
操作码、地址码的概念
-
指令(又称机器指令)是指计算机执行某种操作的命令,是计算机运行的最小功能单位
-
一台计算机的所有指令的集合构成该机的指令系统,也称指令集
-
一台计算机只能执行自己指令系统中的命令,不能执行其他系统的指令(X86架构、ARM架构)
-
指令=操作码(OP)+地址码(A),操作码指明了做什么,地址码指明了对谁动手
- 有的指令不需要地址码(如:停机)
根据地址码数目不同分类
根据地址码数目不同,可以将指令分为零地址指令、一地址指令、二地址指令
-
零地址指令:
-
不需要操作数的操作,如空操作、停机、关中断等指令
-
操作数隐含,如堆栈计算机,两个操作数隐含存放在栈顶和次栈顶,计算结果压回栈顶(参考后缀表达式求值)
-
-
一地址指令: + 操作数
-
只需要单操作数,如加1、减1、取反、求补等
-
指令含义: , 指某个主存地址, 表示 所指向的地址中的内容
-
完成一条指令需要 3 次访存:取指令,读出,写回
-
-
需要两个操作数,但其中一个操作数隐含在某个寄存器,如隐含在ACC
-
指令含义:
-
完成一条指令需要2次访存:取指令,读 ,写回寄存器不需要访存
-
-
-
二地址指令: +目的操作数+源操作数
-
需要两个操作数的算数运算、逻辑运算相关指令
-
指令含义:
-
完成一条指令需要 4 次访存:取指令,读,读,写
-
-
三地址指令: + 目的操作数 + 源操作数 + 结果
-
需要两个操作数的算数运算、逻辑运算相关指令
-
指令含义:
-
完成一条指令需要4次访存:取指令,读,读,写
-
-
四地址指令: + 目的操作数 + 源操作数 + 结果+下址
-
需要两个操作数的算数运算、逻辑运算相关指令
-
指令含义:,为下一条将要执行指令的地址
-
正常情况下取指令之后
PC+1,指向下一条指令;四地址指令中执行指令后,将PC的值修改为所指的地址 -
完成一条指令需要4次访存:取指令,读,读,写
-
-
若指令总长度固定不变,地址码数量越多,则单个地址码越短,寻址能力越差
根据指令长度分类
关于字长
-
指令字长是机器字长的多少倍:半字长指令、单字长指令、双字长指令。指令字长会影响到取指令所需要的时间
-
定长指令字结构:指令系统中所有指令的长度均相等;
-
变长指令字结构:指令系统中各种指令的长度可以不等;
根据操作码的长度不同分类
-
定长操作码:指令系统中所有指令的操作码长度都相同——控制器的译码电路设计简单,但灵活性较低
-
可变长操作码:指令系统中各种指令的操作码长度可变——控制器的译码电路设计复杂,但灵活性较高
-
位操作码字段的指令系统最大能够表示 条指令
根据操作类型分类
-
数据传送类:进行主存与CPU之间的数据传送
-
LOAD:把存储器中的数据放到寄存器中 -
STORE:把寄存器中的数据放到存储器中
-
-
运算类:算数逻辑操作+移位操作
-
算数:加、减、乘、除、加1、减1、求补、浮点运算、十进制运算
-
逻辑:与、或、非、异或、位操作、位测试、位清除、位求反
-
移位:算数移位、逻辑移位、循环移位
-
-
程序控制类:改变程序执行的顺序——转移操作
-
无条件转移
JMP -
条件转移
JZ、JO、JC -
调用和返回
CALL和RETURN -
陷阱
Trap与陷阱指令
-
-
输入输出类:进行CPU与IO设备之间的数据传送
- 输入输出操作:CPU寄存器与IO端口之间的数据传送(IO端口即IO接口中的寄存器)
4.1.2 定长操作码的指令格式
4.1.3 扩展操作码指令格式
-
设计扩展操作码指令格式需要注意以下两点:
-
不允许短码是长码的前缀,即短操作码不能与长操作码的前面部分代码相同
-
各指令的操作码一定不能重复
-
-
通常情况下,对使用频率较高的指令,分配较短的操作码,从而尽可能减少指令译码和分析的时间
设地址长度为 ,上一层留出了 种状态,则下一层可扩展出 种状态(类比IPv4地址的子网划分)。如:
例:设指令字长固定为16位,试设计一套指令系统满足有:15条三地址指令、12条2地址指令、62条一地址指令、32条零地址指令
| 指令数 | ||||
|---|---|---|---|---|
| 15条三地址指令 | 0000~1110(留1种) | |||
| 12条二地址指令 | 1111 | 0000~1011(留4种) | ||
| 62条一地址指令 | 1111 | 1100~1110 1111 | 0000~1111 0000~1101(留2种) | |
| 32条零地址指令 | 1111 | 1111 | 1110~1111 | 0000~1111 |
4.1.4 指令的操作类型
4.2 指令的寻址方式
一个操作数在内存中可能占多个单元,怎样在指令中给出操作数的地址?
寻找的是虚拟地址
PC中存放着虚拟地址,而MAR中存放着物理地址
-
现代计算机都采用字节编址的方式,即一个内存单元只能存放一个字节的信息。一个操作数可能占用多个内存单元,与多个内存地址对应。有两种不同的地址指定方式:大端方式和小端方式
-
大端方式:指令中给出的地址是操作数的最高有效字节(MSB)所在的地址
-
小端方式:指令中给出的地址是操作数的最低有效字节(LSB)所在的地址
-
4.2.1 指令寻址
指令寻址:确定下一条要执行的指令的存放地址。下一条欲执行指令的地址,始终由程序计数器PC给出
顺序寻址
-
读入一个字,根据操作码判断这条指令的总字数 ,修改PC的值,
-
根据指令的类型,CPU可能还要进行多次访存,每次读入一个字
跳跃寻址
-
每一条指令的执行都分为“取指令”和“执行指令”两个阶段
-
每次取指令后,PC一定会 ,之后执行到转移指令时会对PC的值进行修改
4.2.2 数据寻址
数据寻址:确定本条指令的地址码指明的地址(虚拟地址)
数据寻址的方式较多,为了区别各种方式,在指令字中设一个字段,用来指明属于哪种寻址方式。
多地址指令=操作码(OP)+寻址特征+形式地址()+寻址特征+形式地址()……
直接寻址
-
指令字中的形式地址 就是操作数的真实地址 ,即
-
取指令访存1次,执行指令访存1次,共访存2次

-
优点:简单,指令执行阶段仅访问一次主存,不需要专门计算操作数的地址
-
缺点: 的位数决定了该指令操作数的寻址范围;操作数的地址不易修改
间接寻址
-
指令的地址字段给出的形式地址不是操作数的真正地址,而是操作数的有效地址所在的存储单元的地址,也就是操作数地址的地址,即
-
取指令访存1次,执行指令访存2次,共访存3次

-
优点:可扩大寻址范围(有效地址EA的位数大于形式地址A的位数);多级间接寻址便于编制程序(用间接寻址可以方便地完成子程序返回)
-
缺点:指令在执行阶段要多次访存(一次间接寻址需要两次访存,多次寻址需要根据存储字的最高位确定几次访存)
寄存器寻址
-
在指令字中直接给出操作数所在的寄存器编号,即 ,其操作数在由 所指的寄存器内
-
取指令访存1次,执行指令访存0次,共访存1次

-
优点:指令在执行阶段不访问主存,只访问寄存器,指令字短且执行速度快,支持向量/矩阵运算
-
缺点:寄存器价格昂贵,计算机中寄存器个数有限
寄存器间接寻址
-
寄存器 中给出的不是一个操作数,而是操作数所在主存单元的地址,即
-
取指令访存1次,执行指令访存1次,共访存2次

- 特点:与一般间接寻址相比速度更快,但指令的执行阶段需要访问主存(因为操作数在主存中)
隐含寻址
-
不是明显地给出操作数的地址,而是在指令中隐含着操作数的地址(如:ACC或栈顶)
-
取指令访存1次,执行指令访存0次,共访存1次

-
优点:有利于缩短指令字长
-
缺点:需要增加存储操作数或隐含地址的硬件
立即寻址
-
形式地址 就是操作数本身,又称为立即数,一般采用补码形式,
#表示立即寻址特征 -
取指令访存1次,执行指令访存0次,共访存1次
-
优点:指令执行阶段不访问主存,指令执行时间最短
-
缺点: 的位数限制了立即数的范围
偏移寻址
偏移寻址:以某个地址作为起点,形式地址视为偏移量
基址寻址
以程序的起始存放地址作为起点
-
将CPU中的基址寄存器(BR, Base Address Register)的内容加上指令格式中的形式地址 而形成的操作数的有效地址,即 (OS课中的“重定位寄存器”就是“基址寄存器”,详见装入的三种方式)
-
也可以采用通用寄存器作为基址寄存器,并在指令中指明要将哪个通用寄存器作为基址寄存器使用

基址寄存器是面向操作系统的,其内容由操作系统或管理程序确定。在程序执行过程中,基址寄存器的内容不变(作为基地址),形式地址可变(作为偏移量);当采用通用寄存器作为基址寄存器时,可由用户决定哪个寄存器作为基址寄存器,但其内容仍然是由操作系统决定
优点:可以扩大寻址范围(基址寄存器的位数大于形式地址A的位数);用户不必考虑自己的程序存于主存的哪一空间区域,有利于多道程序设计,以及可用于编制浮动程序(整个程序在内存里面的浮动)
变址寻址
程序员自己决定从哪里作为起点
将变址寄存器(IX, Index Register)的内容加上指令格式中的形式地址A而形成的操作数的有效地址,即 EA=(IX)+A
也可以采用通用寄存器作为变址寄存器,并在指令中指明要将哪个通用寄存器作为变址寄存器使用

变址寄存器是面向用户的,在程序执行过程中,变址寄存器的内容可由用户改变(IX作为偏移量),形式地址A不变(作为基地址)
优点:在数组处理过程中,可设定A为数组的首地址,不断改变变址寄存器IX的内容,便很容易形成数组中任一数据的地址,特别适合编制循环程序
相对寻址
以程序计数器PC所指地址作为起点
将程序计数器(PC, Program Counter)的内容加上指令格式中的形式地址A而形成的操作数的有效地址,即 EA=(PC)+A,其中A是相对PC所指地址(当前指令地址的下一个地址)的位移量,可正可负,用补码表示

优点:如果操作数的地址不是固定的,它随着PC值的变化而变化,并且与指令地址之间总是相差一个固定值,因此便于程序浮动(一段代码在程序内部的浮动)。相对寻址广泛应用于转移指令
堆栈寻址
操作数存放在堆栈中,隐含使用堆栈指针(SP, Stack Pointer)作为操作数地址
堆栈是存储器(或专用寄存器组)中一块特定的按“后进先出(LIFO)”原则管理的存储区,该存储区中被读写单元的地址是用一个特定的寄存器给出的,该寄存器称为堆栈指针(SP)
-
硬堆栈:寄存器堆栈,速度快成本高,不访存
-
软堆栈:从主存中划出一段区域来做堆栈,速度慢成本低,访存1次
堆栈可用于函数调用时保存当前函数的相关信息
4.3 程序的机器级代码表示
4.3.1 常用汇编指令介绍
机器级代码考试要求
-
只需要关注X86汇编语言,其他语言会详细注释
-
题目中给出某段简单程序的C语言、汇编语言、机器语言表示,能结合C语言看懂汇编语言的关键语句(常见指令、选择结构、循环结构、函数调用)
-
汇编语言、机器语言一一对应,要能结合汇编语言分析机器语言指令的格式、寻址方式
-
不会考:将C语言人工翻译为汇编语言或机器语言
X86语言指令基础
汇编语言与机器语言一一对应,汇编语言与机器语言都属于机器级代码表示
指令的作用?
①改变程序执行流
②处理数据
指令格式
指令格式:操作码+地址码
-
操作码——怎么处理?
-
地址码——数据在哪?
-
寄存器里——在指令中给出“寄存器名”,X86架构的CPU有哪些寄存器?
-
主存里——在指令中给出“主存地址”,如何在指令中指明读写长度?
-
指令里——在指令中直接给出要操作的数,也就是立即寻址;汇编指令中可用十进制表示,也可用十六进制(常以h结尾)
-
以mov指令为例
mov 目的操作数d,源操作数s:将源操作数s(source)复制到目的操作数d(destination)所指的位置
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所指的一字节中
mov eax, dword ptr [ebx] #将 eax所指主存地址的32bit 复制到 eax寄存器中(寄存器间接寻址)
mov eax, [ebx] #未指名读写长度,默认32bit
mov eax, dword ptr [ebx+8] #将 ebx+8(数值加减)所指主存地址的32bit 复制到 eax寄存器如何指明读写长度?
-
dword ptr [地址]——双字,32bit -
word ptr [地址]——单字,16bit -
byte ptr [地址]——字节,8bit
寄存器
X86架构CPU有哪些寄存器?
-
EAX, EBX, ECX, EDX:通用寄存器(X=未知,E=Extended, 32bit)
-
AX, BX, CX, DX:只使用通用寄存器更低的16bit
-
两个高低字节还可分别作为两个8位寄存器单独使用,分别为AH和AL……
-
-
ESI, EDI:变址寄存器(I=Index, S=Source, D=Destination),可用于线性表、字符串的处理
-
EBP, ESP:堆栈基指针(Base Pointer),堆栈顶指针(Stack Pointer),用于实现函数调用
常用的X86汇编指令
最终结果将放在 d 的位置,所以目的操作数 d 不可以是常量,且不允许两个操作数都来自主存
算术运算类
-
加法(add)
add d, s:计算 ,结果存入d -
减法(substract)
sub d, s:计算 ,结果存入d -
乘法(multiply)
mul d, simul d, s:无符号数/有符号数 ,结果存入d -
除法(divide)
div sidiv s:无符号数/有符号数 ,商存入eax,余数存入edx(被除数被提前放入edx和eax寄存器中,并且进行了位数扩展,用64位被除数除以32位除数) -
取负数(negative)
neg d:将d取负数,结果存入d -
自增(increase)
inc d:执行d++,结果存入d -
自减(decrease)
dec d:执行d--,结果存入d
逻辑运算类
-
与(and)
and d, s:将d、s逐位相与,结果放回d -
或(or)
or d, s:将d、s逐位相或,结果放回d -
非(not)
not d:将d逐位取反,结果放回d -
异或(exclusive or)
xor d, s:将d、s逐位异或,结果放回d -
左移(shift left)
shl d, s:将d逻辑左移s位,结果放回d(通常s是常量) -
右移(shift right)
shr d,s:将d逻辑右移s位,结果放回d(通常s是常量)
其他
-
用于实现分支结构、循环结构的指令:
cmptestjmpjxxx -
用于实现函数调用的指令:
pushpopcallret -
用于实现数据转移的指令:
mov
AT&T格式 vs Intel格式
AT&T格式:Unix、Linux的常用格式
Intel格式:Windows的常用格式
| AT&T格式 | x86格式 | |
|---|---|---|
目的操作数 d,源操作数 s | op s, d | op d, s |
| 寄存器的表示 | mov %ebx, %eax | mov eax, ebx |
| 立即数的表示 | mov $985, %eax | mov eax 985 |
| 主存地址的表示 | mov %eax, (af996h) | mov (af996h), eax |
| 读写长度的表示 | movb $5, (af996h)movw $5, (af996h)movl $5, (af996h) | mov byte ptr [af996h], 5mov word ptr [af996h], 5mov dword ptr [af996h], 5 |
| 主存地址偏移量的表示 | movl -8(%ebx), %eaxmovl 4(%ebx, %ecx, 32), %eax | mov eax, [ebx - 8]mov eax, [ebx+ecx*32+4] |
4.3.2 过程调用的机器级表示【暂缺】
后续补上?
-
函数调用指令
call:将PC旧值压栈保存;设置PC的新值,无条件转移至被调用函数的第一条指令 -
函数返回指令
ret:从函数的栈帧顶部找到PC的旧值,将其出栈并恢复到程序计数器PC
假设过程 A(调用者)调用过程 B (被调用者),过程调用的执行步骤如下:
-
A 将入口参数(实参)放在 B 能访问到的地方
-
A 将返回地址存到特定的地方,然后将控制转移到 B (
call指令) -
B 保存 A 的现场(通用寄存器的内容),并为自己的非静态局部变量分配空间
-
执行过程Q
-
B 恢复 A 的现场,将返回结果放到 A 能访问到的地方,并释放局部变量所占空间
-
B 取出返回地址,将控制转移到 A (
ret指令)
4.3.3 选择语句的机器级表示
每取出一条指令,PC自动+“1”,指向下一条指令
X86架构中,程序计数器PC(Program Counter)通常被称为IP寄存器(Instruction Pointer)
-
无条件转移指令
jmp <地址>:PC无条件转移至<地址>,类似C语言里的goto语句-
<地址>可以用常数直接给出或者来自于寄存器或主存 -
可以使用“标号”锚定位置,名字可以自己取
-
mov eax, 7
mov ebx, 6
jum NEXT
mov ecx, ebx
NEXT:
mov ecx, eax-
条件转移指令
jXXX,一般要与cmp指令一起使用-
je <地址>:jump when equal,若a==b则跳转 -
jne <地址>:jump when not equal,若a!=b则跳转 -
jg <地址>:jump when greater than,若a>b则跳转 -
jge <地址>:jump when greater than or equal to,若a>=b则跳转 -
jl <地址>:jump when less than,若a<b则跳转 -
jle <地址>:jump when less than or equal to,若a<=b则跳转
-
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
jle NEXT #若a<b,转移到NEXT:
mov ecx, eax #假设用ecx存储变量c,令c=a
jmp END #无条件转移到END:
NEXT:
mov ecx, ebx #假设用ecx存储变量c,令c=b
END:bxxx:branch
写汇编语言代码时,一般会以函数名作为“标号”,标注该函数指令的起始地址,条件转移时便可以从函数的起始地址偏移
扩展:cmp指令的底层原理
cmp a, b 本质上是进行 减法运算,并生成标志位 OF(溢出标志)、ZF(零标志)、CF(进位/错位标志)、SF(符号标志),根据标志位结果来判断 之间的大小关系
-
无符号数减法关注:
ZF(零标志)、CF(进位/错位标志) -
有符号数减法关注:
ZF(零标志)、OF(溢出标志)、SF(符号标志)
ALU 的每次运算的标志位都自动存入PSW程序状态寄存器(Intel称其为标志寄存器)
je <地址> #若a==b则跳转,ZF==1 //a-b结果为0
jne <地址> #若a!=b则跳转,ZF==0 //a-b结果不为0
#SF==OF,代表大于等于0,即:结果为负且发生溢出 或 结果为不为负且未发生溢出
jg <地址> #若a>b则跳转,ZF==0 && SF==OF //a-b结果不为0且大于等于0
jge <地址> #若a>=b则跳转,SF==OF //a-b结果大于等于0
jl <地址> #若a<b则跳转,SF!=OF //a-b结果小于0
jle <地址> #若a<=b则跳转,ZF==1 || SF!=OF //a-b结果为0或小于04.3.4 循环语句的机器级表示
用条件转移指令实现循环
//求1+2+...+100
//用for实现
int result =0;
for (int i=1; i<=100; i++){
result = result + i;
}
//用while实现
int i=1;
int result =0;
while(i<=100){
result = result + i;
i++;
}mov eax, 0 #用eax保存result,初始值为0
mov edx, 1 #用edx保存i,初始值为1
cmp edx, 100 #若i>100,跳转到L2
jg L2
L1: #循环主体
add eax, edx #实现result = result + i
inc edx #inc 自增指令,实现i++
cpm edx, 100 #若i<=100,跳转到L1
jle L1
L2: #跳出循环主体用条件转移指令实现循环,需要4个部分构成:
-
循环前的初始化
-
是否直接跳过循环
-
循环主体
-
是否继续循环
用loop指令实现循环
for (int i=500; i>0; i--){
做某些处理;
}//循环500轮mov ecx 500 #用ecx作为循环计数器,这里是ecx的特殊功能,不能用其他寄存器
Looptop: #循环的开始
做某些处理
loop Looptop #ecx--,若ecx!=0,跳转到Looptop
#等价于下列代码
dec ecx
cpm ecx, 0
jne Looptop理论上,能用loop指令实现的功能一定能用条件转移指令实现
使用loop指令可能会使代码更清晰简洁
loopX指令
-
loopnz:当ecx!=0 && ZF==0时,继续循环 -
loopz:当ecx!= && ZF==1时,继续循环
4.4 CISC 和 RISC 的基本概念
CISC 和RISC是指令系统的两种设计方向
CISC:Complex Instruction Set Computer,复杂指令系统计算机
-
设计思路:一条指令完成一个复杂的基本功能
-
代表:X86架构,主要用于笔记本、台式机等
-
采用“存储程序”的设计思想,由一个比较通用的电路配合存储部件完成一条指令
RISC:Reduced Instruction Set Computer,精简指令系统计算机
-
设计思路:一条指令完成一个基本动作,多条指令组合完成一个复杂的基本功能
-
代表:ARM架构,主要用于手机、平板等
-
一条指令一个电路,电路设计相对简单,功耗更低
80-20规律:典型程序中80%的语句仅仅使用处理机中的20%指令