0.指令格式
为了理解相关的寻址方式,首先对指令的格式进行简单的说明。(需要注意的是下面的说明只是从所包含的信息角度进行的说明,实际指令码的格式要复杂得多)
0.1 指令信息
一个指令需要包含下面四个方面的信息:
-
指令操作码
指示完成的功能。如
MOV
指令用来进行数据传送。 -
操作数地址
指令中指出操作数的地址从而处理器可以获得操作数。而操作数的地址有多种形式,常见的有:
- 存储单元
- I/O接口
- CPU内部寄存器
-
操作结果的存放地址
对操作数进行相关处理后,处理结果可能会存放在某处,存放地址和操作数地址类似,常见的有:
- 存储单元
- I/O接口
- CPU内部寄存器
-
下一条指令的地址
程序顺序执行时,下一条指令的地址由CPU中的程序计数器(PC)给出。如果遇到程序转移或者调用子程序时,下一条地址由指令给出。
上述的4种信息不一定在一条指令中都存在,具体有哪些信息由具体指令而定。示意图如下:
这里将示意图画成上图所示,并不代表指令的真实构成就是上面信息的直译形式。实际上,一条指令码的构成格式要比上图要复杂得多。上图只是从包含信息的角度,说明了指令包含哪些必要信息。
0.2 指令格式
常见指令格式有如下几种:
0.2.1 三地址指令
OP A1 A2 A3
A1
:存放结果的地址A2, A3
:两个操作数的地址
含义为:(A1) OP (A2)
→
\rightarrow
→A3
,即将A1 OP A2的结果放在A3
处。
这里A1 A2 A3的地址形式可以是上述说明的存储单元,I/O接口和CPU内部寄存器中的一种。
0.2.2 二地址指令
OP A1 A2
,含义为:(A1) OP (A2) → A1
。A1 A2可以是存储单元,I/O接口和CPU内部寄存器中的一种。
如:MOV DX, A2
,即将内存地址单元A2
的内容传送到DX
中,这里的DX
相当于A1
。
0.2.3 一地址指令
简单可以猜到形式如下:
例子:INC AX;
意为将AX中的内容加1。
0.2.4 零地址指令
OP
,比较特殊,这种指令只有操作码,没有地址码。形式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dl6Gtssm-1665668906523)(https://cdn.jsdelivr.net/gh/Holmes233666/blogImage@main/img/image-20221013191442338.png)]
常见的有停机指令,处理器暂停指令HLT
等。
8086CPU的寻址方式
以下的寻址方式以8086处理器为例。其中寻址方式包括:决定操作数地址的寻址方式和决定转移地址的寻址方式:
寻址方式
{
决定操作数地址:
e
.
g
.
mov xxx, xxx
决定转移地址:
e
.
g
.
jmp xxx, xxx
寻址方式\begin{cases}决定操作数地址:e.g. \text{ mov xxx, xxx}\\ 决定转移地址:e.g. \text{ jmp xxx, xxx}\end{cases}
寻址方式{决定操作数地址:e.g. mov xxx, xxx决定转移地址:e.g. jmp xxx, xxx
即,mov指令是决定操作数地址的寻址方式。
另外,补充一点寻址是在何时进行的。这里涉及到一条指令的执行过程:首先CPU根据程序计数器PC访问内存,在内存中取出待执行的指令,接着该指令被译码(指令译码:CPU需要理解这条指令到底是要做什么,对它进行翻译,根据指令含义获得相关的操作数),译码后,指令正式执行。
具体的,译码过程中获得操作数的过程涉及寻址,必要的话要进行访存,找到源操作数,目的操作数等。至于为什么说“必要的话”,因为有些指令不需要访存就可以得到操作数,如下面要说的立即寻址:两个操作数一个直接包含在指令中,另一个是寄存器中的值,都不需要访存就可以获得。这点在1.1高亮部分也有说明。
指令执行的基本过程有下面的几种形式:
基本过程
{
取指令
,
执行
取指令
,
取操作数
,
执行
取指令
,
取操作数
,
执行
,
写结果
基本过程\begin{cases}取指令,执行\\ 取指令,取操作数,执行\\ 取指令,取操作数,执行,写结果\end{cases}
基本过程⎩
⎨
⎧取指令,执行取指令,取操作数,执行取指令,取操作数,执行,写结果
1.1决定操作数的寻址方式
细致分类有下面几种:
为什么说是内存寻址,是因为这类寻址的指令对应的操作数是存储在存储器中的。在执行指令的过程中包含的对指令进行译码,取操作数的过程是需要访存取出操作数的,不像上面的寄存器寻址,操作数直接就在寄存器中,不需要访存。
1.1.1 立即寻址
特点:操作数直接包含在指令中,是和操作码一起放在代码区域中的;操作数可以是8位或者16位。
如:
MOV AL, 05H
MOV DX, 8000H
在计算机中,以第二条指令为例,这条指令在内存中的存储方式为:
这里你可能会好奇,DX并没有在这里存储,指令是怎么知道另一个操作数是DX的?这里涉及到指令码构成格式的问题,因为过程比较复杂,这里就不过多展开了。可以简单认为指令中会存放寄存器的编号,根据编号译码得出是哪个寄存器。
1.1.2 寄存器寻址
操作数包含在CPU的内部寄存器中,例如:
MOV DS, AX
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JYb09vA-1665668906523)(https://cdn.jsdelivr.net/gh/Holmes233666/blogImage@main/img/image-20221013202418041.png)]
1.1.3 直接寻址
在直接寻址中,操作数存放在存储器中,而操作数的16位段内偏移地址直接包含在指令中,它与操作码一起放在代码段区域。
操作数一般在数据段区域,他的地址为数据段寄存器DS加上这16位的段内偏移地址。
详细说明一下这一段的意思:我们需要区分的是操作数和操作数地址的概念。在直接寻址中,指令中直接给出的是操作数的地址的部分信息(即上述的16位段内偏移地址,另一部分信息即段地址存储在DS寄存器中),我们在指令译码阶段,会根据指令中的操作数地址进行访存,在内存中的数据段区域相应地址处找到该操作数,并取出。
例子:MOV AX, [2000H]
那么操作数的地址可以由下面的计算得出:
即操作数的地址是32000H,译码时需要从地址32000H和32001H中取出实际的操作数放入由AH和AL组成的寄存器AX中。
1.1.4 寄存器间接寻址
在寄存器间接寻址方式中,操作数放在存储器中,操作数的16位段内偏移地址放在SI, DI, BP, BX这四个寄存器之一。
SI, DI, BP, BX四个寄存器对应的段寄存器是不同的,其中若使用寄存器BP进行间接寻址,那么操作数存放在堆栈段区域;否则使用其他三个寄存器寻址,那么操作数存放在数据段中。
例子:MOV AX, [SI]
例子:MOV AX, [BP]
计算的形式如下:
1.1.5 寄存器相对寻址
在寄存器相对寻址方式中,操作数存放在存储器中,操作数的16位段内偏移地址是由SI, DI, BX, BP之一的内容,再加上指令中所指出的8位或者16位相对地址偏移量而得到的。
与寄存器间接寻址类似,在一般情况下,若用SI、DI或者BX进行寻址时,以数据段寄存器DS作为基址;使用BP寻址时,使用堆栈寄存器SS作为基址。
例子:MOV AX, DISP[SI]
1.1.6 基址、变址寻址
在8086/8088中,通常把BX,BP作为基址寄存器,而把SI、DI作为变址寄存器。将这两种寄存器联合起来进行的寻址就称为基址、变址寻址。
在基址、变址寻址方式中,操作数存放在存储器中,操作数的16位段内偏移地址是由基址寄存器内容(BX或BP内容),再加上变址寄存器内容(SI或DI内容)而得到的。
同理,若使用BX作为基址,那么DS作为段址寄存器;如果使用BP作为基址,那么SS作为段址寄存器。
1.1.7 基址、变址、相对寻址
基址、变址、相对寻址方式实际上是基址、变址寻址方式的扩充。即操作数存放在存储器中,操作数的16位段内偏移地址是由基址、变址方式得到的地址再加上由指令指明的8位或16位的相对偏移地址而得到的。
1.1.8 隐含寻址
在有些指令的指令码中,不仅包含有操作码信息,而且还隐含了操作数地址的信息。例如乘法指令MUL的指令码中只需指明一个乘数的地址,另一个乘数和积的地址是隐含固定的。这种将操作数的地址隐含在指令操作码中的寻址方式称为隐含寻址。