汇编:CPU结构 - 段寄存器

在这里插入图片描述8086在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。

是什么部件提供段地址?

段地址在8086的段寄存器中存放。
8086有4个段寄存器:CS、DS、SS、ES,当CPU需要访问内存时由这4个段寄存器提供内存单元的段地址:
CS (Code Segment):代码段寄存器
DS (Data Segment):数据段寄存器
SS (Stack Segment):堆栈段寄存器
ES (Extra Segment):附加段寄存器

每个段寄存器的具体作用是什么,是这一节的主要内容。

一、代码段

CS为代码段寄存器。
IP为指令指针寄存器。
它们指示了CPU当前要读取指令的地址。任意时刻,8086CPU都会将CS:IP指向的指令作为下一条需要取出执行的指令。

1、指令的执行过程

如下图已知:
1、8086CPU当前状态:CS中的内容为2000H,IP中的内容为0000H;
2、内存20000H~20009H 单元存放着可执行的机器码;
3、内存20000H~20009H 单元中存放的机器码对应的汇编指令如下:

地址:20000H~20002H,内容:B8 23 01,长度:3Byte,对应汇编指令:mov ax,0123H
地址:20003H~20005H,内容:BB 03 00,长度:3Byte,对应汇编指令:mov bx,0003H
地址:20006H~20007H,内容:89 D8,长度:2Byte,对应汇编指令:mov ax,bx
地址:20008H~20009H,内容:01 D8,长度:2Byte,对应汇编指令:add ax,bx
在这里插入图片描述

指令执行过程图:
在这里插入图片描述
指令执行过程描述:

1、CS、IP中的内容送入地址加法器(地址加法器完成:物理地址 = 段地址 x 16 + 偏移地址)
2、地址加法器将物理地址送入输入输出控制电路
3、 输入输出控制电路将物理地址 20000H 送上地址总线
4、从内存单元 20000H 开始存放的机器指令 B8 23 01 通过数据总线被送入 CPU
5、 输入输出控制电路将机器指令 B8 23 01 送入指令缓冲器
6、读取一条指令后,IP中的值自动增加,以使CPU可以读取下一条指令。因当前读入的指令 B82301 长度为3个字节,所以IP中的值加3。此时,CS:IP指向内存单元 2000:0003。
7、执行控制器执行指令 B8 23 01 (即 mov ax,0123H)。
8、执行指令 B8 03 00(即 mov bx,0003H)。
9、CPU 从内存 20006H 处读取指令 89 D8 入指令缓冲器(IP 中的值加2)
10、执行指令 89 D8(即 mov ax,bx)后,AX中的内容为 0003H
11、CPU从内存 20008H处读取指令 01 D8 入指令缓冲器(IP中的值加2)
12、执行指令 01 D8 (add ax,bx)后,AX中的内容为 0006H

扫描二维码关注公众号,回复: 14242582 查看本文章

通过上面的过程展示,8086CPU的工作过程可以简要描述如下:

1、从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
2、IP = IP + 所读取指令的长度,从而指向下一条指令;
3、执行指令。赚到步骤1,重复这个过程。
在8086CPU 加电启动或复位后(即 CPU 刚开始工作时)CS和IP被设置为 CS = FFFFH,IP = 0000H,即在 8086机刚启动时,CPU 从内存 FFFF0H单元中读取指令执行,FFFF0H单元中的指令是8086PC即开机后执行的第一条指令。

2、指令VS数据

1、在内存或者磁盘上,指令和数据没有任何区别,都是二进制信息。
2、CPU在工作的时候把有的信息看做指令,有的信息看做数据,为同样的信息赋予了不同的意义。
在这里插入图片描述
3、CPU根据什么将内存中的信息看做指令?

1、CPU将CS:IP指向的内存单元的内容看做指令。
2、如果内存中的某段内容曾被CPU执行过,那么它所在的内存单元必然被CS:IP指向过。

二、数据段

1、CPU内存单元读写

1、CPU要读写一个内存单元时,必须要先给出这个内存单元的地址,在8086中,内存地址由段地址和偏移地址组成。
2、8086中有一个DS段寄存器,通常用来存放要访问数据的段地址

mov bx,1000H
mov ds,bx
mov al,[0]

1、上面3条指令的作用将10000H(1000:0)中的内存数据赋值到al寄存器中
2、mov al,[address]的意思将DS:address中的内存数据赋值到al寄存器中
3、由于al是8位寄存器,所以是将一个字节的数据赋值给al寄存器。

3、8086不支持将数据直接送入段寄存器中,mov ds,1000H是错误的

4、将 al 中的数据送入内存单元 10000H 中:

mov bx,10000H
mov ds,bx
mov [0],al

示例:
写出下面的指令执行后寄存器ax,bx,cx中的值:
在这里插入图片描述

2、大小端

大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低\低高) (Big Endian)
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高\低低) (Little Endian)

在这里插入图片描述
在这里插入图片描述
示例:
写出下面的指令执行后内存中的值:
在这里插入图片描述

3、mov指令

mov 寄存器,数据			比如:mov ax,.8
mov 寄存器,寄存器		比如:mov ax,bx
mov 寄存器,内存单元		比如:mov ax,[0]
mov 内存单元,寄存器		比如:mov[O],ax
mov 段寄存器,寄存器		比如:mov ds,.ax
mov 寄存器,段寄存器

:mov 内存单元, 内存单元 是不允许的,比如mov [0], [1]

4、add和sub指令

add和sub指令同mov一样,都有两个操作对象。它们也可以有以下几种形式:

add 寄存器,数据				比如:add ax,8
add 寄存器,寄存器			比如:add ax,bx
add 寄存器,内存单元			比如:add ax,[O]
add 内存单元,寄存器			比如:add[O],ax
sub 寄存器,数据				比如:sub ax,9
sub 寄存器,寄存器			比如:sub ax,bx
sub 寄存器,内存单元			比如:sub ax,[O]
sub 内存单元,寄存器			比如:sub[0],ax

拓展:
1、编码:
各寄存器的初始值:CS=2000H,IP=0,DS=1000H,AX=0,BX=0;
①写出CPU执行的指令序列(用汇编指令写出)。
②写出CPU执行每条指令后,CS、IP和相关寄存器中的数值。
③再次体会:数据和程序有区别吗?如何确定内存中的信息哪些是数据,哪些是程序?
在这里插入图片描述

三、堆栈段

这里所说的堆栈段,即为栈段。
1、栈:是一种具有特殊的访问方式的存储空间(后进先出, Last In Out Firt,LIFO)
在这里插入图片描述2、8086会将CS作为代码段的段地址,将CS:IP指向的指令作为下一条需要取出执行的指令
3、8086会将DS作为数据段的段地址,mov ax,[address]就是取出DS:address的内存数据放到ax寄存器中
4、8086会将SS作为栈段的段地址,任意时刻,SS:SP指向栈顶元素
5、8086提供了PUSH(入栈)和POP (出栈)指令来操作栈段的数据

比如push ax是将ax的数据入栈,pop ax是将栈顶的数据送入ax

2、出入栈

2.1、push ax

在这里插入图片描述
push的执行,由以下两步完成:

1、SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶:
2、将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。

2.2、pop ax

在这里插入图片描述
pop ax的执行过程和push ax刚好相反,由以下两步完成:

1、将SS:SP指向的内存单元处的数据送入ax中;
2、SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

拓展:
如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?思考后看分析。
在这里插入图片描述
分析: 栈空,SS:SP指向栈空间最高地址单元的下一个单元:
在这里插入图片描述

3、栈顶超界

3.1、- push

在这里插入图片描述

3.2、- pop

在这里插入图片描述
总结:

上面描述了执行push、pop指令时,发生的栈顶超界问题。可以看到,当栈满的时候再使用push指令入栈,或栈空的时候再使用pop指令出栈,都将发生栈顶超界问题。
栈顶超界是危险的,因为我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己程序中的,也可能是别的程序中的(毕竟一个计算机系统中并不是只有我们自己的程序在运行)。但是由于我们在入栈出栈时的不小心,而将这些数据、代码意外地改写,将会引发一连串的错误。
我们当然希望CPU可以帮我们解决这个问题,比如说在CPU中有记录栈顶上限和栈底的寄存器,我们可以通过填写这些寄存器来指定栈空间的范围,然后,CPU在执行push指令的时候靠检测栈顶上限寄存器、在执行pop指令的时候靠检测栈底寄存器保证不会超界。
不过,对于8086CPU,这只是我们的一个设想(我们当然可以这样设想,如果CPU是我们设计的话,这也就不仅仅是一个设想)。实际的情况是,8086CPU中并没有这样的寄存器。
8086CPU不保证我们对栈的操作不会超界。这也就是说,8086CPU只知道栈顶在何处(由SS:SP指示),而不知道我们安排的栈空间有多大。这点就好像CPU只知道当前要执行的指令在何处(由CS:P指示),而不知道要执行的指令有多少。从这两点上我们可以看出8086CPU的工作机理,它只考虑当前的情况:当前的栈顶在何处、当前要执行的指令是哪一条。
我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

4、常用操作指令

在8086中,push、pop操作的数据都是2个字节的:
push和pop指令的格式可以是如下形式:
push 寄存器			;将一个寄存器中的数据入栈
pop 寄存器			;出栈,用一个寄存器接收出栈的数据
push 段寄存器		;将一个段寄存器中的数据入栈
pop 段寄存器			;出栈,用一个段寄存器接收出栈的数据
push 内存单元		;将一个内存字单元处的字入栈(注意:栈操作都是以字为单位)
pop 内存单元			;出栈,用一个内存字单元接收出栈的数据

mov ax,1000H
mov ds,ax 				;内存单元的段地址要放在ds中
push [0]				;将1000:0处的字压入栈中
pop [2] 				;出栈,出栈的数据送入1000:2处

拓展:
1、编程:
(1)将10000H~1000FH这段空间当作栈,初始状态栈是空的:
(2)设置AX=001AH,BX=001BH:
(3)利用栈,交换AX和BX中的数据。
在这里插入图片描述

2、补全下面的程序,使其可以将10000H~1000FH中的8个字,逆序复制到 20000H~2000FH中:

在这里插入图片描述

四、段的总结

1、我们可以用一个段存放数据,将它定义为数据段

对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据来访问

2、我们可以用一个段存放代码,将它定义为代码段

对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在P中,这样CPU就将执行我们定义的代码段中的指令

3、我们可以用一个段当作栈,将它定义为栈段:

对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当作栈空间来用

猜你喜欢

转载自blog.csdn.net/weixin_38633659/article/details/125166729