机器指令
指令的一般格式
在第一章的时候有提到过,指令的组成分为两个部分:操作码+地址码:
操作码字段 | 地址码字段 |
---|---|
操作码
操作码是用来指明该指令所要完成的操作,如加法、减法、传送、移位、转移等等。
通常,其位数反映了机器的操作种类,也即机器允许的指令条数,如操作码占7位,则该机器最多包含27=128
条指令。
操作码的长度可以是固定的,也可以是变化的。前者将操作码集中放在指令字的一个字段内,如上所示。
操作码长度不固定的指令,其操作码分散在指令字的不同字段中。这种格式可有效地压缩操作码的平均长度,在字长较短的微机中被广泛采用。
为了缩短指令字长,出现了扩展操作码。下面是一种扩展操作码的安排示意。
所谓扩展操作码:操作码的位数随着地址数的减少而增加。
OP | A1 | A2 | A3 | 说明 | |
---|---|---|---|---|---|
四位操作码 | 0000 | A1 | A2 | A3 | 15条三地址指令 |
0001 | A1 | A2 | A3 | ||
… | … | … | … | ||
1110 | A1 | A2 | A3 | ||
八位操作码 | 1111 | 0000 | A2 | A3 | 15条二地址指令 |
1111 | 0001 | A2 | A3 | ||
… | … | … | … | ||
1111 | 1110 | A2 | A3 | ||
十二位操作码 | 1111 | 1111 | 0000 | A3 | 15条一地址指令 |
1111 | 1111 | 0001 | A3 | ||
… | … | … | … | ||
1111 | 1111 | 1110 | A3 | ||
十六位操作码 | 1111 | 1111 | 1111 | 0000 | 16条零地址指令 |
1111 | 1111 | 1111 | 0001 | ||
… | … | … | … | ||
1111 | 1111 | 1111 | 1111 |
这里说明部分在后面会有说明,这里只需要先看一下即可。
图中指令字长为16位,其中4位为基本操作码字段OP,另有三个4位长的地址字段为A1
、A2
、A3
。
在设计操作码不固定的指令系统时,应尽量考虑安排指令使用频度高的指令占用短的操作码,对使用频度低的指令可占用较长的操作码,这样可以缩短经常使用的指令的译码时间。当然,考虑操作码长度时也应考虑地址码的要求。
高频短码,低频长码。
地址码
地址码用来指出该指令的**源操作数**
的地址(一个或两个)、结果的地址以及下一条指令的地址。
这里的地址可以是主存的地址,也可以是寄存器的地址,甚至可以是I/O设备的地址。
下面以主存地址为例,分析指令的地址码字段。
(1)四地址指令
这种指令的地址字段有4个,其格式为:
OP | A1 | A2 | A3 | A4 |
---|---|---|---|---|
其中:
- OP为操作码
- A1为第一操作数地址
- A2为第二操作数地址
- A3为结果地址
- A4为下一条指令的地址
该指令完成(A**1)OP(A2)→A3的操作。
这种指令直观易懂,后续指令地址可以任意填写,可直接寻址的地址范围与地址字段的位数有关。
如果指令字长为32位,操作码占8位,4个地址字段各占6位,则指令的直接寻址范围为26=64。
如果地址字段均指示主存的地址**,刚完成一条四地址指令,共需访问四次存储器。
取指令一次,取两个操作数两次,存入结果一次。
因为程序中大多数指令是按顺序执行的,而程序计数器PC既能存放当前欲执行指令的地址,又有计数功能,因此它能自动形成下一条指令的地址。这样,指令字中的第四地址字段A4
便可省去,即得三地址指令格式。
(2)三地址指令
三地址指令中只有三个地址,其格式为:
OP | A**1** | A**2** | A**3** |
---|---|---|---|
它可完成(A1)OP(A2)→A3的操作,这种指令的地址隐含在程序计数器PC之中。
如果指令字长不变,设OP仍为8位,则三个地址字段各占8位,故三地址指令直接寻址范围可达28=256
。同理,若地址字段均为主存地址,则完成一条三地址指令也需访问四次存储器。
其实,机器在运行过程中,没有必要将每次运算结果都存入主存,中间结果可以暂时存放在CPU的寄存器(如ACC)中,这样又可省去一个地址字段A1,从而得出二地址指令。
(3)二地址指令
二地址指令中只含两个地址字段,其格式为:
OP | A1 | A2 |
---|---|---|
它可完成(A1)OP(A2) →A1的操作,即A**1**字段既代表源操作数的地址,又代表存放本次运算结果的地址。
有的机器也可以表示(A1)OP(A2) →A2的操作,此时A2除了代表操作数源地址外,还代表中间结果的存放地址。
这两种情况完成一条指令仍需访问四次存储器。
如果使其完成(A**1)OP(A2**) →ACC,此时,它完成一条指令只需三次访存,它的含义是中间结果暂存于累加器ACC中。
在不改变指令字长和操作码的位数前提下,二地址指令可直接寻访的主存地址数为2**12**=4K。
如果将一个操作数的地址隐含在运算器的ACC中,则指令字中只需给出一个地址码,构成了一地址指令。
(4)一地址指令
一地址指令的地址码字段只有一个,其格式为:
OP | A1 |
---|---|
它可完成(ACC)OP(A1) →ACC的操作,ACC既存放参与运算的操作数,又存放运算的中间结果,这样,完成一条一地址指令只需两次访存。
在指令字长仍为32位、操作码位数仍固定位8位时, 一地址指令可直接寻址的范围达224,即16M。
在指令系统中,还有一种指令可以不设地址字段,即所谓零地址指令。
(5)零地址指令
零地址指令在指令字中无地址码,例如进栈(PUSH)、出栈(POP)这类指令,其操作数的地址隐含在堆栈指针SP中。
零地址指令主要有两个:
- 空操作指令
- 停机指令
空操作指令就是该指令什么也不做,主要可以起到一个延迟其他指令执行的作用,所以无需地址码。
停机指令就是停止指令执行,也无需地址码。
零地址指令并不一定就不涉及到操作数的运算,有一种情况就是操作数都被隐含在某个寄存器当中。
指令字长
固定字长和可变字长
指令字长取决于操作码的长度、操作数地址的长度和操作数地址的个数。不同机器的指令字长是不相同的。
例如:四地址指令
OP | A1 | A2 | A3 | A4 |
---|---|---|---|---|
操作码长度一般为8位
,操作数地址个数就是4个
,每个6位
,指令字长就是8 + 4*6 = 32位
早期的计算机指令字长、机器字长和存储字长均相等,因此访问某个存储单元,便可取出一条完整的指令或一个完整的数据。这种机器的指令字长是固定的,控制方式比较简单。
同时,一台机器的指令系统可以采用位数不相同的指令,即指令字长是可变的,如单字长指令、多字长指令。
控制这类指令的电路比较复杂,而且多字长指令要多次访问存储器才能取出一条完整的指令,因此使CPU速度下降。
为了提高指令的运行速度和节省存储空间,通常尽可能把常用的指令(如数据传送指令、算逻运算指令等)设计成单字长或短字长格式的指令。
简单概括就是:
固定指令字长 | 可变指令字长 | |
---|---|---|
描述 | 字长固定 与机器字长和存储字长相等 |
字长不固定 按照字节的倍数变化 |
特点 | 控制方式简单 | 控制复杂 CPU速度下降 |
适用 | 不常用指令 | 常用指令 |
补充内容
指令字中的地址码字段可以使用一些硬件资源来代替,这样做的目的是:
- 扩大指令的寻址范围
- 缩短指令字长
- 减少访存次数
前面提到的地址码都是位于存储器当中的地址码,除了存储器,还可以是寄存器的编号。也就是根据寄存器的编号获取操作数。
由于寄存器的个数一定是小于存储器当中地址的个数的,所以把存储器地址码A
替换成寄存器编号R
可以有效缩短指令字长。
例如三地址:OP R1 R2 R3
并且这样做可以避免访存,提高指令执行的速度。
操作数类型和操作类型
操作数类型
常见的操作数有:地址、数字、字符、逻辑数据。
地址 | 一个无符号的整数 |
---|---|
数字 | 定点数、浮点数和十进制数 |
字符 | 采用ASCII编码 |
逻辑数据 | 把0和1的组合看做逻辑数(算数运算和逻辑运算) |
数据在存储器中的存放方式
通常计算机中的数据存放在存储器或寄存器中,而寄存器的位数便可反映机器字长。一般机器字长可取字节的1、2、4、8倍,这样便于字符处理。
在计算机当中,存储方式要么按照字节存储,要么按照字存储。
下面讨论的就是按照字存储。
边界对准
为了便于硬件实现,通常要求多字节的数据在存储器的存放方式能满足“边界对准”的要求,如下图所示:
存储字(32位) | 字地址 | |||
---|---|---|---|---|
字(地址0) | 0 | |||
字(地址4) | 4 | |||
字节(地址11) | 字节(地址10) | 字节(地址9) | 字节(地址8) | 8 |
字节(地址15) | 字节(地址14) | 字节(地址13) | 字节(地址12) | 12 |
半字(地址18) | 半字(地址16) | 16 | ||
半字(地址22) | 半字(地址20) | 20 | ||
双字(地址24) | 24 | |||
双字 | 28 | |||
双字(地址32) | 32 | |||
双字 | 36 |
看加粗红字的部分。这里假定存储字长为32位,一个字节8位,一个字就是4个字节,所以字在计算机中的地址为0,4,8....
。存放满一个完整的存储字(32位),就可以说这种存储方式为边界对准。
边界不对准
所谓边界不对准,出现在存储字分开存放的情况:
存储字(32位) | 字地址 | ||
---|---|---|---|
字(地址2) | 半字(地址0) | 0 | |
字节(地址7) | 字节(地址6) | 字(地址4) | 4 |
半字(地址10) | 半字(地址8) | 8 |
如图中浅绿色背景中,一个完整的存储字,被分为两个半字(16位),存储在地址2和地址4当中。
这种情况如果需要获取一个完整的存储字,就需要2次访存(分别访问两个地址)。
这就是边界不对准
操作类型
数据传送
数据传送就是把一个地方的数据传送到另一个地方:
源头 | 寄存器 | 寄存器 | 存储器 | 存储器 |
---|---|---|---|---|
目的地 | 寄存器 | 存储器 | 寄存器 | 存储器 |
算数逻辑操作
算数逻辑运算:加、减、乘、除、增1、减1、取负数即求补
逻辑运算:与、或、非、异或
对于低档机而言,一般算术运算只支持最基本的二进制加减、比较、求补等,高档机还能支持浮点运算和十进制运算。有些机器还具有操作功能,如位测试(测试指定位的值)、位清除(清除指定位)、位求反(对指定位求反)等。
移位
算数移位:对有符号数进行移位
例如:1,010 左移一位变为:1,100 这里的1表示符号位。
逻辑移位:对无符号数进行移位
例如:1010 左移一位变为:0100
循环移位:循环移位首位相连进行移位,这里还有两种方式:
- 携带进位进行移位
- 不携带进位进行移位
所以进位就是用作进1的位。
例如:1010 左移一位就是 0101
转移
在多数情况下,计算机是按顺序执行程序的每条指令的,但有时需要改变这种顺序,此刻可采用转移类指令来完成。
比如这里的指令300跳转至指令303进行。
转移有如下方式:
- 无条件转移(JMP)
- 条件转移
- 结果为0转移(Z = 1) JZ
- 结果溢转移(O=1) JO
- 结果有进位转移(C = 1) JC
- 跳过一条指令 SKP
- 调用和返回
- 陷阱和陷阱指令
- 一般不给用户直接使用(例如溢出判断指令)
- 出现事故的时候由CPU自动产生和执行(隐指令)
- 提供给用户的陷阱指令一般有:软中断指令
- 输入输出
- 输入:数据从端口地址存放到CPU的寄存器当中
- 输出:数据从CPU的寄存器存放到端口地址当中
输入输出分为:直接输入/出和间接输入/出。 直接输入就是直接把数据从端口地址存放到CPU的寄存器当中 间接输入会先把端口地址存放到寄存器当中再进行输入。
所谓调用和返回如图所示:
主程序可以先调用子程序的指令1,2,3,再返回主程序当中继续执行指令2和3。
寻址方式
所谓寻址,就是确定本条指令的操作数的地址或者下一条需要执行的指令的指令地址。
指令寻址
指令寻址有两种方式:
- 顺序寻址
- 跳跃寻址
顺序寻址可通过程序计数器PC加1,自动形成下一条指令的地址;
跳跃寻址则通过转移类指令实现。指令寻址过程如下图所示。
注意这里指令地址3
,在3之前都是依靠PC计数器按照地址的顺序执行指令,到指令地址3
的时候,跳转到指令地址7
,这个时候会先执行指令地址7
的内容,这个就是跳跃寻址。
数据寻址
数据寻址需要找到的就是操作数的地址。
数据寻址方式种类较多,在指令字中必须设一字段来指明属哪一种寻址方式。
指令的地址码字段,通常都不代表操作数的真实地址,把它称作形式地址,记作A
。
因为内存是动态管理的,所以数据的真实位置很难确定。
操作数的真实地址叫做有效地址,记作EA
,它是由寻址方式和形式地址共同来确定的。由此可得指令的格式应如下图所示。
操作码 | 寻址特征 | 形式地址A |
---|---|---|
其中之类的寻址特征就是用来表示用什么方式进行寻址。
在后面的讨论中,假设:
指令字长 = 存储字长 = 机器字长
机器字长:CPU一次能够处理数据的位数 存储字长:一次访存所能获取的信息量 指令字长:该指令所占用的二进制位数
立即寻址
在立即寻址里面,形式地址A就是操作数本身。此时的A也叫作立即数。
这样做的特点就是:
- 指令执行的时候无需访存(因为获取指令就直接获取了操作数)
- A的位数限制了立即数的范围
一般这种指令的格式为:
MOV AX,3
其中3是原操作数,AX是目标操作数,MOV表示移动指令,这句指令的意思是把3这个数移动到寄存器AX当中。原操作数3的寻址方式就是立即寻址。
直接寻址
形式地址和真实地址一样的时候,即可采用直接寻址,也就是EA = A
。
寻址的时候直接根据A去寻址即可。
优点:
- 寻找操作数比较简单
- 指令执行阶段对主存只访问一次
完整的指令周期里面,一共需要2两次访存:取指令需要访存一次,执行指令这里又要访存一次。
缺点:
- A的位数限制了指令的寻址范围
-
隐含寻址
隐含寻址是指指令字中不明显地给出操作数的地址,其操作数的地址隐含在操作码或某个寄存器中。
例如,一地址格式的加法指令只给出一个操作数的地址,另一个操作数隐含在累加器ACC中, 这样累加器ACC成了另一个数的地址。
由于隐含寻址在指令字中少了一个地址,所以这种寻址方式的指令有利于缩短指令字长。间接寻址
指令字中的形式地址不直接操作数的地址,而是指出操作数有效地址所在的存储单元地址,也就是说,有效地址是由形式地址间接提供的,故为间接寻址。即
**EA=(A)**
如图所示:
以左图为例,首先A在主存中寻址获取EA(有效地址),在由EA寻址到真正的操作数。所以在整个指令的执行过程中,需要2次访存(一次寻找EA,一次寻找操作数)。
右图分析同理,不过此时需要首位作为标志位,当标志位为**0**
的时候,代表找到了有效地址。
间接寻址的优点如下: 扩大了寻址范围
- 便于程序的编址
为什么扩大了寻址的范围?
因为我们前面规定了指令字长 = 存储字长,那么A的长度就一定小于存储字长,A所能寻址的范围也小于存储字的寻址范围,如果由A寻址到EA,此时EA的长度大于A,也就扩大了寻址范围。
为什么便于程序的编址?
看如下程序:
主程序两次调用相同的子程序,在子程序最后需要返回主程序,这里有个命令JMP @A。
@为间址特征位,A就表示返回的地址。
如果采用的是直接寻址,那么每次程序调用结束都会返回主程序中的同一个位置,比如这里的地址81
。但是由图可知,主程序调用两次子程序之后,返回的位置,一次是81,一次是202,所以A的位置是变化的。
此时只能使用间接寻址,第一次调用前,使[A]=81
, 第二次调用前;使[A]=202
。这样,当第一次子程序执行到最末条指令JMP @A,便可无条件转至81号单元。第二次执行完子程序后,便可返回到202号单元。
这里
[A]
就代表间接寻址找到的有效地址EA。
寄存器寻址
寄存器寻址十分简单,让实际地址码 = 寄存器编码
,也就是EA = R
。
此时在指令执行阶段就无需访存,这样做也可以让指令字较短,节省了存储空间。
寄存器寻址也有两种:
- 寄存器直接寻址
- 寄存器间接寻址(这里是需要访存的)
补充一点,寄存器间接寻址有利于提高程序编制的灵活性和编制循环程序的,但是老师讲的我听不懂我就不写了。
基址寻址
基址寻址需设有基址寄存器**BR**
,其操作数的有效地址EA等于指令字中的形式地址与基址寄存器中的内容(称作基地址)相加。即:**EA=A+(BR**)
。
这里的A被称为偏移量。
基址寄存器可采用隐式的和显式的两种。
所谓隐式是在计算机内专门设有一个基址寄存器BR,使用时用户不必明显指出该基址寄存器,只需由指令的寻址特征位反映出基址寻址即可。
显式是在一组通用寄存器里,由用户明确指出哪个寄存器用作基址寄存器,存放基地址。
变址寻址
变址寻址与基址寻址极为相似;其有效地址,EA等于指令字中的形式地址A与变址寄存器IX的内容相加之和。即EA=A+(IX)
,如图所示:
基址寻址:主要用于为程序或数据分配存储空间,故基址寄存器的内容通常由操作系统或管理程序确定,而指令字中的A是可变的。
变址寻址:变址寄存器的内容是由用户设定的,在程序执行过程中其值可变(也就是IX可变),而指令字中的A是不可变的。
变址寻址主要用于处理数组问题,在数组处理过程中,可设定A为数组的首地址, 不断改变变址寄存器IX的内容,便可很容易形成数组中任一数据的地址,特别适合编制循环程序。
相对寻址
相对寻址的有效地址是将程序计数器PC的内容(即当初前指令的地址)与指令字中的形式地址A相加而成。即:**EA=(PC)+A**
其中A是相当于当前指令的位移量。
相对寻址有如下的特点:
- A的位数决定操作数的寻址范围
- 程序浮动
-
堆栈寻址
在堆栈寻址的指令字中没有形式地址码字段,它是一种零地址指令。
硬堆栈:用寄存器组来实现
软堆栈:用主存的一部分空间作堆栈。
堆栈的运行方式为先进后出或先进先出两种,先进后出型堆栈的操作数只能从—个口进行读或写。
如图所示,**SP**
为堆栈指针,用于指向栈顶的地址。进栈的时候地址+1,出栈的时候地址-1。 按字编制
- 进栈:SP - 1 → SP
- 出栈:SP + 1 → SP
- 按字节编址
- 存储字长16位(2字节)
- 进栈:SP - 2 → SP
- 出栈:SP + 2 → SP
- 存储字长32位(4字节)
- 进栈:SP - 4 → SP
- 出栈:SP + 4 → SP
- 存储字长16位(2字节)
指令格式举例
设计指令时候考虑的因素
因素 | 说明 |
---|---|
兼容性 | 指令需要兼容不同的系统 |
操作类型 | 包括指令个数和操作的难易程度 |
数据类型 | 确定哪些数据类型可以参与操作 |
指令格式 | - 指令字长是否固定 - 操作码位数、是否采用扩展码技术 - 地址码位数、地址个数、寻址方式 |
寻址方式 | 指令地址、操作数寻址 |
寄存器个数 | 寄存器的多少会直接影响指令的执行时间 |
Intel 8086
Intel8086/80486
系列微机的指令字长为1~6个字节,即不定长。
地址格式有:零地址、一地址、二地址。
注意:在二地址中,Intel 8086不支持两个操作数都在存储器当中。目的是避免指令字长过长
RISC和CISC技术
RISC是精简指令系统计算机的英文缩写,即Reduced Instruction Set Computer,与其对应的是CISC,即复杂指令系统计算机(Complex Instruction Set Computer)。
RISC的产生和发展
80-20规律
首先需要了解一个定理。
80-20规律:典型程序中80%的语句仅仅使用处理机中20%的指令。
执行频率高的简单指令,会因为复杂指令的存在,执行速度无法提高。