文章目录
- 寄存器(内存访问)
- 1 内存中字的存储
- 2 DS和[address]
- 3 字的传送
- 4 mov、add、sub指令
- 5 数据段
- 6 栈
- 7 CPU提供的栈机制
- 8 栈顶超界的问题
- 9 push、pop指令
- 10 栈段
寄存器(内存访问)
1 内存中字的存储
CPU中,用16位寄存器来存储一个字。高8位存放高位字节,低8位存放低位字节。
在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),则一个字要用两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。
在0地址处开始存放20000(4E20H):
上图内存中字的存储元可以看作一个起始地址为0的字单元(存放一个字的内存单元,由0、1两个字节单元组成)。对于这个字单元来说,0号单元是低地址单元,1号单元是高地址单元,则字型数据4E20H的低位字节存放在0号单元中,高位字节存放在1号单元中。
我们提出字单元的概念:字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。
我们将起始地址为 N的字单元简称为N地址字单元。比如一个字单元由2、3两个内存单元组成,则这个字单元的起始地址为2,我们可以说这是2地址字单元。
2 DS和[address]
CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址;
在8086PC中,内存地址由段地址和偏移地址组成。
8086CPU中有一个DS寄存器,通常用来存放要访问的数据的段地址。
已知的mov指令可完成的两种传送功能:
(1)将数据直接送入寄存器;
(2)将一个寄存器中的内容送入另一个寄存器中。
除此之外,mov 指令 还可以将一个内存单元中的内容送入一个寄存器。
用 mov 指令要访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中。[address]表示一个偏移地址为address的内存单元。
例如:我们要读取10000H单元的内容可以用如下程序段进行:
mov bx,1000H
mov ds,bx
mov al,[0]
上面三条指令将10000H(1000:0)中的数据读到al中。
3 字的传送
因为8086CPU是16位结构,有16根数据线,所以,可以一次性传送16位的数据,也就是一次性传送一个字。
上述指令执行结果如下:
4 mov、add、sub指令
mov指令的几种形式:
mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器
add和sub指令同mov一样,都有两个操作对象。
5 数据段
我们可以将一组长度为N(N≤64K)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
比如我们用123B0H~123B9H这段空间来存放数据:
段地址:123BH
长度:10字节
我们现在要累加这个数据段中的前3个单元中的数据,代码如下:
6 栈
栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。
栈有两个基本的操作:入栈和出栈。
- 入栈:将一个新的元素放到栈顶;
- 出栈:从栈顶取出一个元素。
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。
栈的操作规则:LIFO (Last In First Out,后进先出)
7 CPU提供的栈机制
我们在基于8086CPU编程的时候,可以将一段内存当作栈来使用。
8086CPU提供入栈和出栈指令:
- PUSH(入栈)
- POP (出栈)
push ax:将寄存器ax中的数据送入栈中;
pop ax :从栈顶取出数据送入ax。
8086CPU的入栈和出栈操作都是以字为单位进行的。
8086CPU中,有两个寄存器:
- 段寄存器SS:存放栈顶的段地址
- 寄存器SP:存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素。
下图为一段指令的执行过程:
pop指令的实际执行过程:
注意:
出栈后,SS:SP指向新的栈顶 1000EH,pop操作前的栈顶元素,1000CH 处的2266H 依然存在 ,但是,它已不在栈中。
当再次执行push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖。
8 栈顶超界的问题
当栈满的时候再使用push指令入栈或栈空的时候再使用pop指令出栈都将发生栈顶超界问题。
栈顶超界是危险的。因为我们既然将一段空间安排为栈 ,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己的程序中的,也可能是别的程序中的。(毕竟一个计算机系统并不是只有我们自己的程序在运行)
8086CPU不保证对栈的操作不会超界。这就是说, 8086CPU 只知道栈顶在何处(由SS:SP指示),而不知道我们安排的栈空间有多大。
因此我们在编程的时候要自己操心栈顶超界的问题 ,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;
执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。
9 push、pop指令
push和pop指令是可以在寄存器和内存之间传送数据的。
push和pop指令的格式(1):
- push 寄存器:将一个寄存器中的数据入栈
- pop寄存器:出栈,用一个寄存器接收出栈的数据
例如:
push ax
pop bx
push和pop指令的格式(2):
- push 段寄存器:将一个段寄存器中的数据入栈
- pop段寄存器:出栈,用一个段寄存器接收出栈的数据
例如:
push ds
pop es
push和pop指令的格式(3)
- push内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)
- pop 内存单元:出栈,用一个内存字单元接收出栈的数据
例如:
push [0]
pop [2]
10 栈段
我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段。
比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。这段空间就可以成为栈段,段地址为1000H,大小为16字节。
访问时,我们需要将SS:SP指向我们定义的栈段。
栈顶的变化范围是0~FFFFH,从栈空时候的SP=0,一直压栈,直到栈满时SP=0;如果再次压栈,栈顶将环绕,覆盖了原来栈中的内容。
所以一个栈段的容量最大为64KB。
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。
我们可以用一个段存放数据,将它定义为“数据段”;
我们可以用一个段存放代码,将它定义为“代码段”;
我们可以用一个段当作栈,将它定义为“栈段”;