1. 内存中字的存储
如图,数据 4E20H 存放在内存中,8086CPU 中是低字节的存放在低地址,而高字节的存放在高地址,这是一种小端序的存储模式,当然还有大端序的存储模式,在其他的领域会存在。
当我们问 0 地址单元存放的字节型数据是多少呢,就是 20H,那 0 地址存放的字型数据应该是 4E20H,同理问 2 地址单元存放的字型数据就是 0012H,1 地址存放的字型数据就是 124EH,总之任何两个地址连续的内存单元,n 单元和 n+1 号单元,可以将它们看成两个内存单元,也可以看成一个地址为 n 的字单元中的高位字节单元和低位字节单元。
2.DS 和 [address]
前面我们知道,8086CPU 中内存地址由段地址和偏移地址组成,8086CPU 中有一个 DS 寄存器,用来存放要访问的内存的段地址。
例如我们要读取 10000H 单元的内容可以用如下程序段进行
上面三条指令将 10000H(1000:0)中的数据读到 al 中。为什么可以实现呢?
我们已经知道 mov 指令可完成两种传送功能:
1. 将数据直接送入寄存器
2. 将一个寄存器中的内容送入另一个寄存器中。
除此之外呢,mov 指令还可以将一个内存单元中的内存送入寄存器当中,格式是
[address] 表示一个内存单元,里面的数据表示的是偏移地址,如上面给出的汇编指令 [0] 就表示偏移地址为 0,而执行指令的时候,8086CPU 会自动取 DS 中的数据为内存单元的段地址。
上面的汇编指令中,我们事先将段地址 1000H 放入了 ds 寄存器当中,用 mov al,[0],mov 指令当中的 [ ] 说明操作对象是一个内存单元,[] 中的 0,说明这个内存单元的偏移地址为 0。它的段地址默认放在了 ds 寄存器当中,
将段地址送入 ds 寄存器当中其实有一个易错点,上面给出的汇编指令中,我们先将 1000H 送入了 bx 寄存器中,再送入 ds 寄存器当中,那么能不能直接送入 ds 寄存器当中呢?
答案是不可以,8086CPU 不支持将数据直接送入段寄存器 ds 当中。mov ds,1000H 这样的汇编指令就是非法的。数据必须先进入通用寄存器当中,才可以被送入段寄存器当中。
上面所给的汇编指令是将内存单元的数据送入寄存器当中,我们想将寄存器当中的数据送入特点的内存单元当中也可以用以下代码完成。
3. 字的传送
因为 8086CPU 是 16 位结构,有 16 根数据线,所以,可以一次性传送 16 位的数据,也就是一次性传送一个字
比如以下代码
将 1000:0 处的字型数据送入了 ax 当中,将 cx 中的 16 位数据送到 1000:0 处。因为 ax 是 16 位的寄存器,如果是 al,ah 啥的,就是字节型的数据。
做一道例题
首先段寄存器中的数据是 1000H,执行第三行汇编指令时,索引到 1000:0,即地址 10000H 处,因为 ax 是 16 位的数据,所以应该传送一个字大小的数据,数据 1123 被送入 ax 寄存器当中。
下一步同理 bx 中数据为 6622,cx 中数据为 2211。执行最后两行汇编指令时,bx=bx+2211=6622+2211=8833,cx=cx+6622=2211+6622=8833。这里的 bx 和 cx 都指寄存器里面存储的数据。
所以答案为 ax=1122,bx=8833,cx=8833。
4.mov、sub、add 指令
已学 mov 指令的几种形式如下
还有下面这种也是可以实现的
add 和 sub 指令同 mov 一样,都是两个操作对象。
所有正确的语法如图
但是要注意 add 和 sub 指令无法对段寄存器进行操作
5. 数据段
前面讲过我们可以根据需要将一组内存单元定义为一个段,前面讲了代码段,这里就谈谈数据段。
和代码段一样,可以将长度为 n (n<=64kb) 的一组代码,存在一组地址连续、起始地址为 16 的倍数的内存单元中,这段内存用来存放代码,从而定义了一个数据段。
如何访问数据段的数据呢?
和代码段同理,我们可以用 ds 存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
数据段其实没啥好说的,我们通常简单汇编指令处理就是经常处理的是数据,一个段我们不用什么指令进行处理,那就是存储的一个数据。
6. 栈
一个很重要的概念。
栈是一种具有特殊的访问方式的存储空间。她的特殊性就在于,最先进入这个空间的数据,最后出去。很形象的形容就是弹夹,最先进去的子弹一般最后打出去。
栈有两个基本的操作:入栈和出栈
入栈:将一个新的元素放到栈顶。
出栈:从栈顶取出一个元素。
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出来。栈的操作规则就是后进先出,先进后出。
8086CPU 提供了相关的指令来以栈的方式访问内存空间,这意味着我们在操作内存空间时,可以将一段内存当作栈来使用。就像我们之前用 CS 来指定代码段差不多。
关于栈的汇编指令如下
8086CPU 的入栈和出栈操作都是以字为单位进行的。
用下图来加深理解
我们将这段内存空间看作栈,当我们执行前两条指令时,寄存器 ax 中的数据 0123H 首先被送入栈中,高字节在高地址,这点不用再强调了,后面继续执行三条 push 指令,把三个大小为字的数据送入了栈中。
当我们执行后面三条的 pop 指令时,会首先将 1122H 送出栈,并送入寄存器 ax 当中,后面两条同理。
最后栈中为空,寄存器 ax 存储的数据为 1122H,寄存器 bx 存储的数据为 2266H,寄存器 cx 存储的数据为 0123H。
那么有两个问题,第一个问题和之前探讨数据段、代码段的时候差不多,CPU 如何知道一段内存空间被当作段来使用了呢?
依旧是依赖寄存器,8086CPU 当中有一个段寄存器 SS(Stack Segment),用于存放栈顶的段地址。同时又有一个寄存器 SP(Stack Pointer),用来存放栈顶的偏移地址。任意时刻,SS:SP 都指向栈顶元素。
比如上图,当栈中没用任何元素的时候,SS 中的地址就是指向 1000,SP 中的地址就是 F。
第二个问题就是当执行 push 和 pop 指令的时候,如何知道哪个单元是栈顶单元呢?
如图
如图,解释的很清楚了。要注意的是,要先将寄存器 SP 中的数据减了 2 之后,数据才会被送入栈中。
pop 呢,则是先把数据送出栈,然后再将寄存器 SP 的数据加 2。为什么是加 2 和减 2 当然是因为 push 和 pop 指令操作的单位都是一个字来的。
还有一个点就是,当栈为空时,SS:SP 其实指向的是最高地址单元的下一个单元。如上图就是 10010H.
还有我们 pop 指令将一个数据送出栈时,这个数据还在栈当中存在吗?比如上图,可能我们会认为对第三张图中的栈执行了 pop 指令之后,2266H 会被弹出栈,1000CH 和 1000DH 的空间就是空的了,这样的想法其实是错的,在 pop 之后,数据依旧存留在栈中,只是在 push 之后,这两个单元的数据是会被覆盖的。