段
1 将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元
2 种类
2.1 代码段
定义
-
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
-
可以将长度为 N( N≤64KB )的一组代码,存在一组地址连续、起始地址为 16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段。
例如
如何使得代码段中的指令被执行
2.2 数据段
定义
DS和[address]
mov 指令可以将一个内存单元中的内容送入一个寄存器。
- 10000H表示为1000:0(段地址:偏移地址)
- 将段地址1000H放入ds
- 用mov al,[0]完成传送(mov指令中的[]说明操作对象是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中)
例如
2.3 栈段
栈
操作方式
- 栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出
- 栈的操作规则:LIFO(Last In First Out,后进先出)
- 8086CPU的入栈和出栈操作都是以字为单位进行的。
- 注意:字型数据用两个单元存放,高地址单元放高 8 位,低地址单元放低8 位。
8086CPU提供入栈和出栈指令
SS:SP
8086CPU中,有两个寄存器:
SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶。
栈顶超界的问题
种类
8086CPU不保证对栈的操作不会超界。
解决办法
push、pop指令
定义
PUSH(入栈)
push ax:将寄存器ax中的数据送入栈中;
执行过程
push ax
图示
POP(出栈)
pop ax :从栈顶取出数据送入ax。
执行过程
图示
注意:
- 出栈后,SS:SP指向新的栈顶 1000EH,pop操作前的栈顶元素,1000CH 处的2266H 依然存在 ,但是,它已不在栈中。
- 当再次执行push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖。
格式
(1)
(2)
(3)
- push 内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)
- pop 内存单元:出栈,用一个内存字单元接收出栈的数据
- 例如:push [0] pop [2]
- 指令执行时 ,CPU 要知道内存单元的地址,可以在 push、pop 指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得。
注意
-
与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。
-
CPU执行mov指令只需一步操作,就是传送,而执行push、pop指令却需要两步操作
- 执行push时:先改变SP,后向SS:SP处传送。
- 执行pop时: 先读取SS:SP处的数据,后改变SP。
-
push、pop 等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。
-
提供:SS、SP指示栈顶;改变SP后写内存的入栈指令;读内存后改变SP的出栈指令。
栈段定义
思考
- 我们将10000H~1FFFFH这段空间当作栈段 ,SS=1000H ,栈空间大小为64KB ,栈最底部的字单元地址为1000:FFFE。
- 任意时刻,SS:SP指向栈顶,当栈中只有一个元素的时候,SS=1000H,SP=FFFEH。
- 栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2。
- SP原来为FFFEH,加2后SP=0,所以,当栈为空的时候,SS=1000H,SP=0。
- 任意时刻,SS:SP指向栈顶元素,当栈为空的时候 ,栈中没有元素 ,也就不存在栈顶元素,所以SS:SP只能指向栈的最底部单元下面的单元 ,该单元的偏移地址为栈最底部的字单元的偏移地址+2 ,栈最底部字单元的地址为1000:FFFE,所以栈空时,SP=0000H。
3 访问
-
对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
-
对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;
-
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,这样CPU在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们定义的栈段当作栈空间来用。
-
可见,不管我们如何安排 ,CPU 将内存中的某段内存当作代码 ,是因为CS:IP指向了那里;CPU将某段内存当作栈 ,是因为 SS:IP 指向了那里
比如我们将10000H~1001FH安排为代码段,并在里面存储如下代码:
4 段前缀
在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、“cs:”、“ss:”或“es:”
应用
场景1
问题:将内存ffff:0~ffff:b段元中的数据拷贝到 0:200~0:20b单元中。
分析
- 将ffff:X单元中的数据送入0020:X(需要用一个寄存器中转)
- X=X+1
-
(4)我们用将0:200~0:20b用0020:0~0020:b描述,就是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0开始。
问题
解决
- 我们可以使用两个段寄存器分别存放源单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。
- 改进的程序中,使用 es 存放目标空间0020:0~0020:b的段地址,用ds存放源空间ffff:0~ffff:b的段地址。
- 在访问内存单元的指令“mov es:[bx],al”中 ,显式地用段前缀 “es:” 给出单元的段地址,这样就不必在循环中重复设置ds