目录
指令格式
指令的定义
指令(又称机器指令):是指示计算机执行某种操作的命令,是计算机运行的最小功能单位。
一台计算机的所有指令的集合构成了该机的指令系统,也称为指令集。
注:一台计算机只能执行自己的指令系统中的指令,不能执行其他系统的指令。
指令格式
一条指令就是机器语言的一个语句,它是一组有意义的二进制代码。
根据地址码数量不同分类
一条指定通常要包括操作码字段和地址字段两部分:
操作码:指明了用户要干什么? 地址码:指明了对谁进行操作?
一条指定可能包含0个、1个、2个、3个、4个地址码..
根据地址码数目的不同,可以将指令分为零地址指令、一地址指令、二地址指令..
零地址指令
- 不需要操作数,如空操作、停机、关中断等指令
- 堆栈计算机,两个操作数隐含存放在栈顶和次栈顶,计算结果压回栈顶(例如:数据结构中的后缀表达式)
一地址指令
- 只需要单操作数,如加1、减1、取反、求补等 指令含义:OP(A1)->A1, 完成一条指令需要3次访存:取指->读A1->写A1
- 需要两个操作数,但其中一个操作数隐含在某个寄存器中(如隐含在ACC) 指令含义:(ACC)OP(A1)->ACC 完成一条指定需要2次访存:取指->读A1
注:A1指某个主存地址(类比C语言中的指针),(A1)表示A1所指向的地址中的内容(指针所指位置的内容)
二地址指令
常用于需要两个操作数的算术运算、逻辑运算相关指令
指令含义:(A1)OP(A2)->A1
完成一条指令需要访存4次,取指->读A1->读A2->写A2
三地址指令
常用于需要两个操作数的算术运算、逻辑运算相关指令
指令含义:(A1)OP(A2)->A3
完成一条指令需要访存4次,取指->读A1->读A2->写A3
四地址指令
指令含义:(A1)OP(A2)->A3, A4=下一条将要执行指令的地址
完成一条指令需要访存4次,取指->读A1->读A2->写A3
正常情况下:取指令之后PC+1,指向下一条指令
四地址指令:执行指令后,将PC的值修改位A4所指地址(跳着执行指令)
若指令总长度固定不变,则地址码数量越多,寻址能力越差
根据指令长度分类
指令字长:一条指定的总长度(可能会变)
机器字长:CPU进行一次整数运算所能处理的二进制数据的位数(通常和ALU直接相关)
存储字长:一个存储单元中的二进制代码位数(通常和MDR位数相同)
半字长指令、单字长指令、双字长指令 --指令长度是机器字长的多少倍
指令字长会影响指令所需时间。如:机器字长=存储字长=16bit,则取一条双字长指令需要两次访存
定长指令结构:指令系统中所有指令的长度都相等
变长指令字结构:指令系统中各种指令的长度不等
根据操作码长度不同分类
定长操作码:指令系统中所有指令的操作码长度都相同 n位->条指令
控制器的译码电路设计简单,但灵活性较差
可变长操作码:指令系统中个指令的操作码长度可变
控制器的译码电路设计复杂,但灵活性较高
根据操作类型分类
- 数据传送 (进行主存与CPU之间的数据传送) LOAD 作用:把寄存器中的数据放到寄存器中 STORE 作用:把寄存器中的数据放到存储器中
- 算术逻辑操作 算术:加、减、乘、除、增1、减1、求补、浮点运算、十进制运算 逻辑:与、或、非、异或、位操作、位测试、位清除、位求反
- 移位操作 算术移位、逻辑移位、循环移位(带进制和不带进制)
- 转移操作(程序设计类-改变程序执行流) 无条件转移JMP 条件转移 JZ:结果为0; JO:结果溢出; JC:结果有进位 调用和返回CALL和RETURE 陷阱(Trap)与陷阱指令
- 输入输出操作 CPU寄存器与IO端口之间的数据传送(端口即IO接口中的寄存器)
扩展操作码指令格式
定长指令字结构+可变长操作码
扩展操作码举例1
指令字长为16位,每个地址码占4位:
前4位为基本操作码字段OP,另有3个4位长的地址字段A1、A2、A3
4位基本操作码若全部用于三地址指令,则有16条。 但至少须将1111留作扩展操作码之用,即 三地址指令为15条 ;
1111 1111留作扩展操作码之用,二地址指令为15条;
1111 1111 1111 留作扩展码之用,一地址指令为15条;
零地址指令为16条。
还有其他扩展操作码设计方法。
在设计扩展操作码指令格式时,必须注意以下两点:
- 不允许短码是长码的前缀部分的代码相同,即短操作码不能与长操作码的前面部分的代码相同。
- 各指令的操作码一定不能重复。
通过情况下,对使用频率较高的指令,分配较短的 操作码;对使用频率较低的指令,分配较长的操作码,从而尽可能减少指令译码和分析的时间。
扩展操作码举例2
设指令字长固定为16位,试设计一套指令系统满足:有15条三地址指令、12条二地址指令、62条一地址指令、32条零地址指令
根据每种指令的条数来设计,如下:
CPU如何分析指令:
CPU首先先看指令的前4为如果不是全1就按照三地址指令执行,如果前4位是1,5、6位不是1按二地址指令执行,如果前6位是1,接着5位不是1按一地址指令执行,如果前11位是1按零地址指令执行。
设地址长度为n,上一层留出m种状态,下一层可扩展出m×种状态,如下图:
定长操作码和扩展操作码的优缺点分析
定长操作码
定长操作码:在指令字的最高位部分分配固定的若干位(定长)表示操作码。
-一般n为操作码字段的指令系统最大能够表示条指令
- 优:定长操作码对于简化计算机硬件设计,提高指令译码和识别速度很有利
- 缺:指令数量增加时会占用更多固定位,留给表示操作数地址的位数受限
扩展操作码
扩展操作码(不定长操作码):全部指令的操作码字段的位数不固定,且分散地放在指令字的不同位置上。
-最常见的变长操作码方法是扩展操作码,使操作码的长度随地址码的减 少而增加,不同地址数的指令可以具有不同长度的操作码,从而在满足 需要的前提下,有效地缩短指令字长。
-优:在指令字长有限的前提下仍保持比较丰富的指令种类;
-缺:增加了指令译码和分析的难度,使控制器的设计复杂化。
指令寻址
顺序寻址
(PC)+"1"-->PC 这里的1理解为1个指令字长,实际加的值会因指令长度、编址长度而不同
定长指令字结构
下面系统采用定长指令字结构,指令字长=存储字长=16bit=2B 主存按字编址,每次只需要将指令寄存器的值+1即可
下面系统采用定长指令字结构,指令字长=存储字长=16bit=2B 主存按字节编址,每次只需要将指令寄存器的值+2即可
变长指令字结构
下面系统采用定长指令字结构,指令字长=存储字长=16bit=2B 主存按字节编址,每个颜色代表一个指令。读入一个字,根据操作码判断是几地址指令,然后判断这条指令的总字节数n,修改PC的值,根据指令的类型,CPU还要进行多次访存,每次读入一个字
跳跃寻址
由转移指令指出
下面系统采用定长指令字结构,指令字长=存储字长=16bit=2B 主存按字节编址
JMP:无条件转移把PC中的内容改为JMP地址码中的数据
数据寻址
确定本条指令的地址码指明的真实地址
由于寻址方式多种多样,需要在地址码当中加入寻址方式位,以指明用哪种寻址方式进行寻址
一地址指令形式:
二地址指令形式:
假设指令字长=机器字长=存储字长 、假设操作数为3,进行以下寻址方式的讲解
直接寻址
指令字中的形式地址A就是操作数的真实地址EA,即EA=A。
一条指令的执行:取指令访存一次 执行指令访存一次 暂不考虑存结果 共访存2次
优点:简单,指令执行阶段仅访问一次主存,不需专门计算操作数的地址。
缺点:A的位数决定了该指令操作数的寻址范围。操作数的地址不易修改。
间接寻址
指令的地址字段给出的形式地址不是操作数的真正的地址,而是操作数有效地址所在的存储单元的地址,也就是操作数地址的地址,即EA=(A)。
一次间址一条指令的执行:取指令 访存1次 执行指令 访存2次 暂不考虑存结果 共访存3次
优点:
可扩大寻址范围(有效地址EA的位数大于形式地址A的位数)。
便于编址程序(用间接寻址可以方便地完成子程序返回)。
缺点:
指令在执行阶段要多次访存(一次间址需两次访存,多次寻址需根据存储字的最高位确定几次访存)
寄存器寻址
在指令字中直接给出操作数所在的寄存器编号,即EA=Ri,其操作数在由Ri所指的寄存器内。
一条指令的执行:取指令 访存1次 执行指令 访存0次 暂不考虑存结果 共访存1次
优点:
指令在执行阶段不访存主存,只访问寄存器,
指令字短且执行速度块,支持向量/矩阵运算。
缺点:
寄存器加个昂贵,计算机中寄存器个数有限。
寄存器间接寻址
寄存器Ri中给出的不是一个操作数,而是操作数所在主存单元的地址,即EA=(Ri)。
一条指令的执行:取指令 访存1次 执行指令 访存1次 暂不考虑存结果 共访存2次.
特点:与一般间接寻址相比速度更快,但指令的执行阶段需要访问主存(因为操作数在主存中)。
隐含寻址
不是明显地给出操作数的地址,而是在指令中隐含着操作数的地址。
优点:有利于缩短指令字长。
缺点:需增加存储操作数或隐含地址的硬件。
立即寻址
形式地址A就是操作数本身,又称立即数,一般采用补码形式。#表示立即寻址特征。
一条指令的执行:取指令 访存1次 执行指令 访存0次 暂不考虑存结果 共访存1次
优点:指令执行阶段不访问主存,指令执行时间最短。
缺点:A的位数限制了立即数的范围,如A的位数是n,且立即数采用补码时,可表示的数据范围为-~-1
偏移寻址
基址寻址
将CPU中的基址寄存器(BR)的内容加上指令格式中的形式地址A,而形成操作数的有效地址,即EA=(BR)+A。
第一种采用专用寄存器BR作为基址寄存器
第二种采用通用寄存器作为基址寄存器
采用基址寻址无需修改指令中的地址码
优点:
- 便于程序“浮动”,方便实现多道程序并发执行
- 可扩大寻址范围(基址寄存器的位数大于形式A的位数)
- 用户不必考虑自己的程序存于主存的哪一空间区域,故有利于多道程序设计
注:基址寄存器是面向操作系统的,其内容由操作系统或管理程序确定。在程序执行过程中,基址寄存器的内容不变(作为基地址),形式地址可变(作为偏移量)。
当采用通用寄存器作为基址寄存器时,可由用户决定哪个寄存器作为基址寄存器,但其内容仍由操作系统确定。
变址寻址
有效地址EA等于指令字中的形式地址A与变址寄存器IX的内容相加之和,即EA=(IX)+A,其中IX可为变址寄存器(专用),也可用通用寄存器作为变址寄存器。
注:变址寄存器是面向用户的,在程序执行过程中,变址寄存器的内容可由用户改变(IX作为偏移量),形式地址A不变(作为基地址)
变址寄存器的作用:
看这个循环程序
for(int i=0;i<10;i++){
sum+=a[i];
}
在不使用变址寻址的时候
如果像这样一样,每次循环都会有一条加法指令,这就会导致编程的不方便。为解决这种情况引入了变址寻址。如下:
在数组处理过程中,可设A为数组的首地址,不断改变变址寄存器IX的内容,变可很容易形成数组中任一数据的地址,特别适合编制循环程序。
基址&变址复合寻址
相对寻址
把程序计数器PC的内容加上指令格式中的形式地址A形成操作数的有效地址,即EA=(PA)+A,其中A是相对于PC所指地址的位移量,可正可负,补码表示
相对寻址的作用
优点:
操作数的地址不是固定的,他随着PC值的变化而变化,并且与指令地址之间总是相差一个固定值,因此便于程序浮动(一段代码在程序内部的浮动)。
相对寻址广发应用于转移指令。
硬件如何实现数的比较
区别
这三种偏移寻址的区别在于偏移的“起点‘不一样。
基址寻址:以程序的起始存放地址作为”起点“
变址寻址:程序员自己决定从哪里作为”起点“
相对寻址:以程序计数器PC所指地址作为”起点“
堆栈寻址
堆栈寻址:操作数放在堆栈中,隐含使用堆栈指针(SP)作为操作数地址。
堆栈是存储器(或专用存储器组)中一块特定的按“后进先出(LIFO)”原则管理的存储区,该区域中被读/写单元的地址是用一个特定的寄存器给出的,该寄存器称为堆栈指针(SP)。
硬堆栈实现
使用一个专用的寄存器组作为堆栈。
假设SP指向顶元素,R0为栈顶。要完成一次加法操作如何实现:
记栈顶单元为Msp
POP(弹出指令) ACC (Msp)->ACC
(SP)+1->SP
POP X (Msp)->X
(SP)+1->SP
ADD(相加指令) Y (ACC)+(X)->Y
PUSH(压入指令) Y (SP)-1->SP (Y)->Msp
每次POP和PUSH指令都不需要访存,所以这种方式快,但是成本高。
软堆栈实现
从主存中划出一片存储区域作为堆栈
每次POP和PUSH指令都需要访存,所以这种方式慢,但是成本低,往往采用这种。
汇编语言
高级语言与机器级代码之间的对应
x86汇编语言指令基础
以mov指令为例
mov 目的操作数d,源操作数s
#mov指令功能:将源操作数s复制到目的操作数d所指的位置。
mov eax,ebx #将寄存器ebx的值复制到寄存器eax
mov eax,5 #将立即数5复制到寄存器eax
mov eax,dword ptr [ad996h] #将内存地址ad996h所指的32bit值复制到寄存器eax
mov byte ptr [ad996h],5 #将立即数5复制到内存地址ad996h所指的一字节中
如何指明内存的读写长度:
- dword ptr--双字,32bit
- word ptr--单字,16bit
- byte ptr--字节,8bit
x86架构CPU,有哪些寄存器
分为通用寄存器、变址寄存器、堆栈指针
通用寄存器使用非常灵活,可以使用其中的16bit或者8bit
mov eax, dword ptr [ebx] #将ebx所指主存地址的32bit复制到eax寄存器中
mov dword ptr [ebx], eax #将eax的内容复制到ebx所指主存地址的32bit
mov eax, byte ptr [ebx] #将ebx所指的主存地址的8bit复制到eax
mov eax, [ebx] #若未指明主存读写长度,默认32 bit
mov [af996h], eax #将eax的内容复制到af996h 所指的地址(未指明长度默认32bit)
mov eax, dword ptr [ebx+8] #将ebx+8所指主存地址的32bit复制到eax寄存器中
mov eax, dword ptr [af996-12h] #将af996-12所指主存地址的32bit复制到eax寄存器中
常用x86汇编指令
常见的算数运算指令
edx:eda/s的意思是,32位的被除数除32位的除数时,需要先将32位的被除数位扩展为64位,所以就需要edx、eda两个寄存器来存储
关于王道书的解释
常见的逻辑运算指令
其他指令
用于实现分支结构、循环结构的指令:cmp、test、jmp、jxxx
用于实现函数调用的指令:push、pop、call、ret
用于实现数据转移的指令:mov
AT&T格式v.s.Intel格式
AT&T格式是Unix、Linux的常用格式
Intel格式是Windows的常用格式
选择语句的机器级表示
程序中的选择语句(分支结构)、
程序寄存器PC的值都会每次取回一条指令,就会自动+”1“,指向下一条即将执行的指令,但是分支结构可能会改变程序的执行流。
无条件转移指令--jmp
由于地址不明确,为了灵活使用,可以使用以下指令jmp NEXT
条件转移指令--jxxx
在每一个条件转移指令之前,一般需要加上cmp指令
示例
如上图,这样一个选择语句的程序,有两种表示方法
第一种:
第二种:
循环语句的机器级表示
用条件转移指令实现循环
用条件转移指令实现循环,需要4个部分构成:
- 循环前的初始化
- 是否直接跳过循环
- 循环主体
- 是否继续循环
用loop指令实现循环
loop Looptop等价于:
dec ecx
cmp ecx,0
jne Looptop
理论上,能用loop指令实现的功能一定能用条件转移指令实现,使用loop指令可能会使代码更加清晰简洁
补充:loopx指令--如loopnz,loopz
loopnz--当ecx!=0&&ZF==0时,继续循环
loopz--当ecx!=0&&ZF==1时,继续循环
CISC和RISC
两种设计方式
CISC
设计思路:一条指令完成一个复杂的基本功能。
代表:x86架构,主要用于笔记本、台式机等
89-20规律:典型程序中80%的语句仅仅使用处理机中20%的指令
比如设计一套能实现整数、矩阵加/减/乘运算的指令集:
CISC的思路:除了提供整数的加减乘指令除之外,还提供矩阵的加法指令、矩阵的减法指令、矩阵的乘法指令
一条指令可以由一个专门的电路完成
有的复杂指令用纯硬件实现很困难->采用“存储程序‘的设计思想,由一个比较通用的电路配合存储部件完成一条指令
RISC
设计思路:一条指令完成一个基本”动作“;多条指令组合完成一个复杂的基本功能。
代表:ARM架构,主要用于手机、平板等
比如设计一套能实现整数、矩阵加/减/乘运算的指令集:
一条指令一个电路,电路设计相对简单,功耗更低
”并行“、”流水线“