当CPU在访问内存单元的时候,需要给出内存单元的地址。所有内存单元构成了一个一维的线性地址空间,每个内存单元在整个空间都有一个唯一的地址,这个唯一的地址被称之为物理地址。
CPU通过地址总线送入存储器中的地址,必须是一个物理地址,存储器才能读写该内存单元。
不同的CPU形成物理地址的方法不同,本篇讨论的是8086形成物理的过程,并且涉及到内存地址的数字皆为16进制。
16位的CPU拥有下面几种特性
即在8086内部,能够一次性处理,传输、暂时存储的信息长度最大为16位。
但是8086的地址总线宽度却为20位,可以传送20位的地址,寻址能力达到 1 MB。但是从8086的内部构造来看,它只能送出16位的地址,寻址能力仅仅只有64KB,CPU内部的信息处理能力和外部的硬件最大传送能力不匹配,这无疑是对地址总线宽度的浪费。
8086CPU内部为了匹配地址总线的寻址能力,采用了两位16位的地址合成一个20位的地址。
即使用了段地址 + 偏移地址的概念。
当CPU需要读写内存时,需要提供20位的物理地址,地址合成如下:
地址加法器采用物理地址 = 段地址 * 16 + 偏移地址的方式对物理地址进行合成。
计算机中的所有信息通过二进制的方式进行存储,16位的地址扩展为20位,那么只需要将段地址左移4位,也就是段地址 * 16
合成过程如下:
如果计算机需要访问一个内存地址为123C8H的内存单元,这是一个20位的地址
因为地址是通过16进制的方式表示,在16进制中,一个数码需要使用4个bit表示。123C8H拥有5个数码,H是16进制的后缀,所以5个16进制的数码 = 5 * 4 = 20位
将这个内存地址进行拆分,123C8H = 12300H + 00C8H
得到物理地址后将物理地址由输入输出电路输出即可。
二进制的 100 * 2 = 1000
十进制的 100 * 10 = 1000
同理
十六进制的 100 * 16 = 1000
段地址 + 偏移地址本质上就是利用一个基础的地址和一个相对于基础地址的偏移地址相加,得到具体的物理地址
在8086CPU中,通过基础地址(段地址 * 16)+ 相对于段地址的偏移地址得到一个具体的物理地址。这里的基础地址是通过段地址 * 16获取。
但是可能在其他的CPU中,基础地址可能是通过基础地址 * 32 实现,也有可能通过段地址 * 64实现。
因为段地址这个概念的出现,可能有人认为内存是分成一个一个段的,每一个段有一个段地址,但是内存并没有分段,分段的划分来自CPU,内存是连续的,并没有分段。
在一维坐标轴中,想要表示一个范围内的数据,也就是将坐标轴分段,通常使用区间进行表示。
想要表示 3 到 7 这个区间,使用 [3, 7] 进行表示,但是还可以使用另外一种方法进行表示
起点是 3, 范围大小区间为 [0, 4](正反向),也能表示 [3, 7] 这个范围。
而段地址 + 偏移地址和第二种方法神似。
所以偏移地址的大小决定了一个段最大的长度。
在8086中,偏移地址是16位的,所以一个段的最大长度位 216 Byte,也就是64KB
CS寄存器(代码段寄存器), IP寄存器(指令指针寄存器),这两个寄存器是CPU中最重要的两个寄存器。
它们指示了CPU当前需要读取的指令在内存上的物理地址。通过上面的物理地址的合成,需要段地址和偏移地址对一个物理地址进行合成,CS中就存放着段地址,IP中就存放着偏移地址。
在冯诺伊曼机器中,我们知道计算机指令应该是顺序执行的,计算机应当知道需要执行的下一条执行的指令的地址,那下一条指令的地址如何而来,下一条指令的地址就存放于 CS * 16 + IP 这个物理地址上。
每当CPU需要执行指令的时候
如果CPU需要执行其他位置的指令,修改CS和IP寄存器中的内容,即可挑选CPU需要执行的指令的地址。
在内存中,我们知道指令和数据没有任何的区别,都是二进制数据,那么CPU在工作的时候,肯定不能将指令看为数据,肯定不能将数据看为指令,在我们看来完全一样的数据,CPU是怎么区分的呢?也就是什么时候将二进制数据看为指令,什么时候将二进制数据看为数据。
其实我们只需要负责CS:IP寄存器指向的内容就好,也就是CPU将永远认为CS:IP寄存器指向的内存单元中的二进制数据就是指令。
当我们想要修改AX,BX,CX,DX等通用寄存器中的内容的时候,我们需要使用mov指令进行修改。
但是CS寄存器和IP寄存器中的内容不能使用mov指令修改。
示范:
;错误
MOV CS, 037F
;错误
MOV AX, 037F
MOV CS, AX
一、同时修改CS和IP
使用jmp指令可以修改CS和IP寄存器中的内容
格式:jmp 段地址:偏移地址
例如:
假设 CS = 7777, IP = 8888
现在执行指令 jmp 9999:0000
执行了指令之后CS = 9999,IP = 0000
二、只修改IP寄存器
使用jmp指令加上通用寄存器,即可修改IP寄存器中的内容
格式:jmp 寄存器
例如:
假设 CS = 7777, IP = 8888,AX = 0000
现在执行指令 jmp AX
执行了指令之后 CS = 7777, IP = 0000
1000:0000 为程序入口
执行:mov ax, 1000 ;mov bx, 2000
执行后 ax = 1000, bx = 2000
执行: jmp 2000:0000
执行后:CS = 2000, IP = 0000
跳转到内存中的 2000:0000 单元
执行: add ax, bx
执行后:ax = 3000,
执行: jmp ax
执行后: CS = 2000,IP = 3000
跳转到内存中的2000:3000
执行:mov cx, 1234
执行后:cx = 1234