本文主要是介绍Intel8086处理器-段寄存器ES/DS/CS/SS与寻址,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
注意:本文代码在微软visual studio 2019下完成
一:段的概念
比方说有这样一段汇编程序
.data
abc DWORD 666
.code
call abcdef
可以肤浅的理解为abc DWORD 666
这句代码存在的内存区域,叫做数据段,call abcdef
这句代码存在的内存区域,叫做代码段,调用call指令的时候,还需要堆栈,而堆栈占用的那片内存区域,叫做堆栈段,那么大致的内存图如下,从图中可以看出,每个程序都有自己的段,而且代码段,堆栈段,数据段之间有可能连续,也有可能不连续
二:基址(段的起始位置)与段长度
段的起始位置也叫基址,与长度一样,是段的属性之一,从上图中可以看到
程序1的数据段,基址是10004,程序1的数据段长度是2
程序2的堆栈段,基址是10014,程序2的堆栈段长度是3
注意,上述只是举例子,实际情况是段起始地址必须是以0x00结尾的地址
三:地址总线的寻址能力
CPU如果要想获取某个内存处的地址,比如上图中,CPU想获取10003处的值,则CPU会将10003放到地址总线中,这样内存中的33这个数据,就会通过数据总线传递给CPU,这有个前提,就是地址总线中必须能放得下10003这个数才行,比方说,地址总线只有两条,每条线肯定只有高电平(1)或者低电平(0)两种选择,那么这个地址总线的表达能力最多是3,只能获取内存中0-3之间的数据,而8086CPU有20条地址总线,它的表达能力是220,内存中0-220之间位置都能获取到,也就是1MB,这种能力,叫做地址总线的寻址能力,8086的地址总线寻址能力是1MB
四:CPU的寻址能力,段地址,偏移地址
8086CPU是16位CPU,定义为一次处理,传输,暂存的数据最大值是216,也就是64KB,如果这样,那么8086的CPU寻址能力将是64KB
CPU的寻址能力导致的问题
从上文可以看出,CPU的寻址能力可以说是远远不如地址总线的寻址能力,这就好比地址总线是一个很宽的高速公路,而CPU却是一台特别慢的汽车,根本配不上这条高速公路,假如现在要获取内存位置65KB处的数据,CPU根本无法表达65KB这个信息给地址总线,这就很尴尬,因为这个原因,CPU使用地址加法器,让自己的输出从16位变成了20为,从而使CPU的寻址达到最大化,地址加法器的做法是取1个16位寄存器,将每个bit值,依次放到16跟地址总线中,这个寄存器里的值,就叫做段地址
取另外一个16为寄存器中的四个数据,放到剩下的4跟地址总线中,这叫做偏移地址(理解偏移长度更好一些)
说白了就是用两个16位寄存器,来表达20位地址总线中的数据
五:地址加法器
假设段地址16进制是:2000H
假设偏移长度16进制是:1H
经过地址加法器合成之后,CPU实际查询的内存是:20001H,该结果可以用以下公式计算
真实地址=段地址乘以16+偏移长度
注意:段地址乘以16,就是上文的基址(段的起始位置)
所以也可以说真实地址=基址(段的起始位置)+偏移长度
在8086架构中
CS寄存器存储代码段的段地址,IP寄存器存放代码段的偏移长度
DS寄存器存储数据段的段地址,多数情况下用DX存放数据段的偏移长度
SS寄存器存储堆栈段的段地址,SP寄存器存放堆栈段的偏移长度
CPU运行过程如下图
在8086中,设CS中的内容为M,IP中的内容为N,则CPU从M*16+N单元开始读取一条指令并执行,任意时刻,CPU都将CS:IP指向的内容当做指令执行
CS+IP通过地址加法器,合成一个内存地址,这个内存地址中的内容,就是CPU要执行的指令
注:8086中加电启动或复位后,CS和IP被设置成CS=FFFF,IP=0000,所以8086电脑开机之后执行的第一条指令是:FFFF0H存储单元的指令
四个段寄存器:CS/DS/SS/ES
语法:
mov 段寄存器,通用寄存器
mov 段寄存器,[address]
下面的写法是错误的
mov 段寄存器,666
CS代码段寄存器
CS寄存器总是和IP寄存器配合使用的,在CS与IP中已经提到过,mov指令叫做传送指令,传送指令不可以修改cs寄存器,因为CPU就不提供这种功能,需要使用转移指令进行修改,一个典型转移指令就是jmp指令
jmp 段地址:偏移地址
//执行这条指令之后,CS=2AE3,IP=0003,接下来CPU将执行2AE33存储单元内的指令
jmp 2AE3:3
jmp 某一寄存器
// 将ax中的值放到ip寄存器中
jmp ax
日记:使用CALL指令将EIP内容读取到栈,这样我们就能获取到EIP中的内容了,然后使用RET/IRET指令修改栈,这样就实现了函数注入
七:DS数据段寄存器
汇编代码有时候会遇见中括号的写法,这种写法叫做[address],这种方式是以字为单位进行操作的,千万要注意,"以字为单位"
这句话,并且address只能是一个常量,或者一些指定的寄存器,它们分别是bx,bp,si,di
,例如下面的汇编代码是正确的
mov ax,[666];使用ds作为段寄存器,默认就是ds
mov ax,[bx];使用ds作为段寄存器,因为中括号[]中有bx寄存器
mov ax,[bx+si];使用ds作为段寄存器,因为中括号[]中有bx寄存器
mov ax,[bx+di];使用ds作为段寄存器,因为中括号[]中有bx寄存器
mov ax,[bp];使用ss作为段寄存器,因为中括号[]中有bp寄存器
mov ax,[bp+si];使用ss作为段寄存器,因为中括号[]中有bp寄存器
mov ax,[bp+di];使用ss作为段寄存器,因为中括号[]中有bp寄存器
下面的汇编代码是错误的
mov ax,[bx+bp];bx和bp不能同时出现在同一个中括号里
mov ax,[cx]
mov ax,[dx]
mov ax,[ds]
假设DS=2000H,并且内存如下图
则执行mov ax,[1]
这条汇编指令的时候,根据CPU位数不同,其处理也是不同的
8位CPU:将一个内存地址20001处的值B8挪到ax中
16位CPU:将一个内存地址20001处的值B8,20002处的值23,合并成一个16位数23B8H,然后将23B8H挪到ax中
32位CPU:将一个内存地址20001处的值B8,20002处的值23,20003处的01,20004出的B8,合并成一个32位数B80123B8H,挪到ax中
如何修改DS寄存器中的值?
由于CPU硬件设计问题,DS寄存器只能接收其他寄存器的值,而不接受立即数,例如下面的汇编语言是错误的
mov ds,1111H
要想让ds的值为1111H,则必须同一个一个寄存器进行中转,下面的汇编语言是正确的
mov ax,1111H
mov ds,ax
别问为什么,8086CPU就是这么设计的,我也不知道为什么
八:ES数据段附加段寄存器
与DS功能相同,也叫附加段寄存器,是为了弥补在某些情况下数据段寄存器不够使用的情况,如果因为数据段寄存器默认就是DS,所以要想使用ES,则需要如下写法,下面的写法也叫做段超越前缀
mov ax,0xb800
mov ds,ax
mov [ds:0x10],666 ;将数字666存放到物理地址0xB8010处
九:SS栈段寄存器和SP寄存器
确定栈空间,就是确定栈顶在哪个位置,任意时刻,SS和SP都指向栈顶,段寄存器SS用于存放段地址,寄存器SP用于存放偏移地址,执行push和pop指令的时候,CPU根据SS:SP获取栈顶位置
当执行下面push指令时候
push ax
SP首先-2(8086CPU),将栈顶往上挪动2个存储单元,然后将ax中的内容放到栈内
相对的,pop指令的时候,sp首先会自动+2
注意:执行修改SS指令的时候,比如mov ss,xxx,此时CPU强制屏蔽中断,直到下一条指令执行完毕,所以,mov ss,xxx之后,下面的指令我们必须要修改sp才行
这篇关于Intel8086处理器-段寄存器ES/DS/CS/SS与寻址的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!