爱了爱了,这篇寄存器讲的有点意思(三)

简介: 什么是 Code SegmentCode Segment 即代码段,它就是我们上面聊到就是 CS 寄存器中存储的基础地址,也就是段地址,段地址其本质上就是一组内存单元的地址,例如上面的 「mov ax,0123H 、mov bx, 0003H」。我们可以将长度为 N 的一组代码,存放在一组连续地址、其实地址为 16 的倍数的内存单元中,我们可以认为,这段内存就是用来存放代码的。


什么是 Code Segment

Code Segment 即代码段,它就是我们上面聊到就是 CS 寄存器中存储的基础地址,也就是段地址,段地址其本质上就是一组内存单元的地址,例如上面的 「mov ax,0123H 、mov bx, 0003H」。我们可以将长度为 N 的一组代码,存放在一组连续地址、其实地址为 16 的倍数的内存单元中,我们可以认为,这段内存就是用来存放代码的。


DS 寄存器

CPU 在读写一个内存单元的时候,需要知道这个内存单元的地址。在 8086 CPU 中,有一个 DS 寄存器,通常用来存放访问数据的段地址。如果你想要读取一个 10000H 的数据,你可能会需要下面这段代码

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

上面这三条指令就把 10000H 读取到了 a1 中。


在上面汇编代码中,mov 指令有两种传送方式

  • 一种是把数据直接送入寄存器
  • 一种是将一个寄存器的内容送入另一个寄存器


但是不仅仅如此,mov 指令还具有下面这几种表达方式

描述 举例
mov 寄存器,数据 比如:mov ax,8
mov 寄存器,寄存器 比如:mov ax,bx
mov 寄存器,内存单元 比如:mov ax,[0]
mov 内存单元,寄存器 比如:mov[0], ax
mov 段寄存器,寄存器 比如:mov ds,ax


栈我相信大部分小伙伴已经非常熟悉了,是一种具有特殊的访问方式的存储空间。它的特殊性就在于,先进入栈的元素,最后才出去,也就是我们常说的 先入后出

它就像一个大的收纳箱,你可以往里面放相同类型的东西,比如书,最先放进收纳箱的书在最下面,最后放进收纳箱的书在最上面,如果你想拿书的话, 必须从最上面开始取,否则是无法取出最下面的书籍的。


栈的数据结构就是这样,你把书籍压入收纳箱的操作叫做压入(push),你把书籍从收纳箱取出的操作叫做弹出(pop),它的模型图大概是这样


image.png


入栈相当于是增加操作,出栈相当于是删除操作,只不过叫法不一样。栈和内存不同,它不需要指定元素的地址。它的大概使用如下

// 压入数据
Push(123);
Push(456);
Push(789);
// 弹出数据
j = Pop();
k = Pop();
l = Pop();

在栈中,LIFO 方式表示栈的数组中所保存的最后面的数据(Last In)会被最先读取出来(First Out)。


image.png


栈和 SS 寄存器

下面我们就通过一段汇编代码来描述一下栈的压入弹出的过程

8086 CPU 提供入栈和出栈指令,最基本的两个是 PUSH(入栈)POP(出栈)。比如 push ax 会把 ax 寄存器中的数据压入栈中,pop ax 表示从栈顶取出数据送入 ax 寄存器中。


这里注意一点:8086 CPU 中的入栈和出栈都是以字为单位进行的。


我这里首先有一个初始的栈,没有任何指令和数据。


image.png


然后我们向栈中 push 数据后,栈中数据如下


image.png


涉及的指令有

mov ax,2345H
push ax


注意,数据会用两个单元存放,高地址单元存放高 8 位地址,低地址单元存放低 8 位。


再向栈中 push 数据


image.png


其中涉及的指令有

mov bx,0132H
push bx


现在栈中有两条数据,现在我们执行出栈操作


image.png


其中涉及的指令有

pop ax
/* ax = 0132H */

再继续取出数据


image.png


涉及的指令有

pop bx
/* bx = */

完整的 push 和 pop 过程如下


image.png


现在 cxuan 问你一个问题,我们上面描述的是 10000H ~ 1000FH 这段空间来作为 push 和 pop 指令的存取单元。但是,你怎么知道这个栈单元就是 10000H ~ 1000FH 呢?也就是说,你如何选择指定的栈单元进行存取?


事实上,8086 CPU 有一组关于栈的寄存器 SSSP。SS 是段寄存器,它存储的是栈的基础位置,也就是栈顶的位置,而 SP 是栈指针,它存储的是偏移地址。在任意时刻,SS:SP 都指向栈顶元素。push 和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。


现在,我们可以完整的描述一下 push 和 pop 过程了,下面 cxuan 就给你推导一下这个过程。


image.png


上面这个过程主要涉及到的关键变化如下。

当使用 「PUSH」 指令向栈中压入 1 个字节单元时,SP = SP - 1;即栈顶元素会发生变化;

而当使用 「PUSH」 指令向栈中压入 2 个字节的字单元时,SP = SP – 2 ;即栈顶元素也要发生变化;

当使用 「POP」 指令从栈中弹出 1 个字节单元时, SP = SP + 1;即栈顶元素会发生变化;

当使用 「POP」 指令从栈中弹出 2 个字节单元的字单元时, SP = SP + 2 ;即栈顶元素会发生变化;


栈顶越界问题

现在我们知道,8086 CPU 可以使用 SS 和 SP 指示栈顶的地址,并且提供 PUSH 和 POP 指令实现入栈和出栈,所以,你现在知道了如何能够找到栈顶位置,但是你如何能保证栈顶的位置不会越界呢?栈顶越界会产生什么影响呢?


比如如下是一个栈顶越界的示意图


image.png


一开始,SS:SP 寄存器指向了栈顶,然后向栈空间 push 一定数量的元素后,SS:SP 位于栈空间顶部,此时再向栈空间内部 push 元素,就会出现栈顶越界问题。


栈顶越界是危险的,因为我们既然将一块区域空间安排为栈,那么在栈空间外部也可能存放了其他指令和数据,这些指令和数据有可能是其他程序的,所以如此操作会让计算机懵逼。


我们希望 8086 CPU 能自己解决问题,毕竟 8086 CPU 已经是个成熟的 CPU 了,要学会自己解决问题了。


然鹅(故意的),这对于 8086 CPU 来说,这可能是它一辈子的 夙愿 了,真实情况是,8086 CPU 不会保证栈顶越界问题,也就是说 8086 CPU 只会告诉你栈顶在哪,并不会知道栈空间有多大,所以需要程序员自己手动去保证。。。



相关文章
|
6月前
|
存储 程序员 存储控制器
【读书笔记】汇编语言(第四版)第二章 寄存器
【读书笔记】汇编语言(第四版)第二章 寄存器
|
6月前
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
|
5月前
|
编译器
8086 汇编笔记(八):转移指令的原理
8086 汇编笔记(八):转移指令的原理
8086 汇编笔记(十):标志寄存器
8086 汇编笔记(十):标志寄存器
|
7月前
|
存储
8086 汇编笔记(一):寄存器
8086 汇编笔记(一):寄存器
|
7月前
|
存储
8086 汇编笔记(二):寄存器(内存访问)
8086 汇编笔记(二):寄存器(内存访问)
|
6月前
|
程序员 索引
逆向学习汇编篇:内存管理与寻址方式
逆向学习汇编篇:内存管理与寻址方式
55 0
|
7月前
|
Linux
DMA寄存器与代码结合文档笔记
DMA寄存器与代码结合文档笔记
72 0
|
传感器 存储 人工智能
STM32第一章-寄存器你懂吗?
嵌入式系统是小型计算机的一个分支系统。平常用的PC,就属于功能比较专一的计算机,从核心的处理器来说,可以分成嵌入式微处理器和嵌入式微控制器,我们传统意义上的那种单片机,比如说像51、AVR还有按里面比较低配的一些,比如说像Cortex-M系列的这一类,我们都把它划分为微控制器,微处理器呢,就相对来说处理能力,运算能力要强一些,比如ARM9以上的系列和 Cortex-A以及以上系列。STM32属于一个微控制器,请大家牢牢记住微控制器这四个字。STM32自带了各种常用通信接口,比如USART、I2C、SPI等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有STM3
604 0
 STM32第一章-寄存器你懂吗?
|
Windows
[笔记] Windows内核课程:保护模式《二》段寄存器介绍
[笔记] Windows内核课程:保护模式《二》段寄存器介绍