最近学习了X86汇编,其实无论是古老的8086还是现在i3/5/7/9,Xeon3/5,在最基本原理上,都是相通的,只是CPU位数,寻址空间,寄存器个数,指令集的扩充等方面有所不同,对于学习,8086永不过时。

1.内存中字的存储
8086CPU中,用16位寄存器来存储一个字,高8位存放高字节,低8位存放低字节。在内存中存储时,由于内存单元是字节单元,一个单元存放一个字节,那么一个字(2字节,16位)应该用两个连续的存储单元(内存地址)来存储,低字节存放在低地址,高字节存放在高地址,这就是我们所说的小端序,大端序与之相反。
字单元:存放一个字型数据(16位)的内存单元,它由两个连续内存单元组成。
8086CPU不支持将数据直接存入段寄存器(DS),需要先将数据存放到通用寄存器,然后再MOV到段寄存器。
“[address]”做为一个整体表示一个内存单元,中括号中的数字表示内存单元的偏移地址。如:
假设DS中为1000H,那么:
MOV Al, [0] ;将10000H内存地址中的数据存入AX
MOV [0], Al ;将AX中的数据存入内存地址10000H
eg.1读取10000H中内存单元的内容
mov bx, 1000H
mov ds, bx
mov al, [0]
eg.2将al中的数据存入10000H内存地址中
mov bx, 1000H
mov ds, bx
mov [0], al

字的传送:
mov bx, 1000H
mov ds, bx
mov ax, [0] ;1000:0处的数据存入AX寄存器
mov [0], ax ;AX寄存器中的值存入1000:0

2.指令学习:mov、add、sub
(1)mov 指令格式:
mov 寄存器, 数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器
mov 寄存器,段寄存器
mov 段寄存器,内存单元
(2)add指令格式:
add 寄存器, 数据
add 寄存器,寄存器
add 寄存器,内存单元
add 内存单元,寄存器
(3)sub指令格式:
sub 寄存器, 数据
sub 寄存器,寄存器
sub 寄存器,内存单元
sub 内存单元,寄存器

3.数据段
前面讲过段的概念,也讲过代码段,那么我们知道指令和数据都是二进制形式,既然有代码段,那么也应该有数据段。
我们可以将一组长度为N、地址连续、起始地址为16倍数的内存单元当做专门存储数据的内存段,即:数据段。如:123B0H~123B9H这段内存空间做数据段,那么它的段地址为123BH,长度为10个字节。
相比于代码段有代码段寄存器:CS,那么数据段也有数据段寄存器:DS
数据段的数据访问:可以用ds作为数据段的地址,再根据需要访问数据段中的具体单元
如:123B0H~123B9H作为一个数据段
mov ax, 123BH
mov ds, ax ;设置段地址
mov al, 0 ;清零al寄存器,保存累加结果
add al, [0] ;累加数据段第0偏移地址
add al, [1] ;累加数据段第1偏移地址
add al, [2] ;累加数据段第2偏移地址

4.栈
8086CPU提供操作把一段内存以栈的方式进行操作,入栈指令:push,出栈指令:pop。
(1)push 指令格式:
push 寄存器
push 段寄存器
push 内存地址
(2)pop指令格式:
pop 寄存器
pop 段寄存器
pop 内存地址
eg1.
push ax ;把ax寄存器中的数据存入栈
eg2.
pop ax ;从栈顶取出数据存入ax

8086CPU的入栈和出栈操作都可以以字为单位进行。
8086CPU提供段寄存器SS和寄存器SP,栈顶的段地址存放在SS,偏移地址粗放在SP,任意时刻:SS:SP指向栈顶元素。posh和pop指令被执行时,CPU从SS和SP中取到栈顶地址
push ax的执行可分2部分完成:
(1)SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶。因为栈地址是从高地址向低地址移动使用的。如:栈地址10000H~1000FH,栈底是1000FH,栈顶是10000H,初始时SS是1000H,SP是0010H,栈为空,由于此时要压入数据,SP=SP-2,则SP变成了000EH。
(2)将ax中的内容存入SS:SP指向的内存单元,即:执行push ax后,第一个数据被压入1000EH地址单元,SSH和SP的值不变,等待下一次入栈。
总结:入栈是从高地址向低地址方向增长,此外:任意时刻SS:SP指向栈顶元素
pop ax的执行过程与入栈相反,也可分2部分完成:
(1)将SS:SP指向的内存单元处的数据送入ax中
(2)SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
总结:出栈是从低地址高地址方向减少的,此外:细心读者可以看一下,出栈后,栈内数据并不清零,也就是垃圾数据,所以有时候我们依然能在某些时候读到正确数据,但是千万别侥幸,因为栈随时会增长,覆盖这些数据,更危险的是,此时入果向该地址写入数据,那么程序将立即崩溃。

栈顶越界和栈底越界的问题:栈底越界,我们会读到栈外的数据;栈顶越界,我们会向超出栈空间外的内存空间写入数据,这是非常危险的。但是8086CPU并不提供栈保护机制,这需要程序员自己来完成。

栈段:我们可以将长度为N的一组地址连续、起始地址为16倍数的内存单元,当做栈空间来使用,从而定义一个栈段,但是这仅仅是一种安排,CPU并不会由于这种安排,就在执行pop和push指令时,自动为将我们定义的栈空间当做栈空间访问,需要我们通过SS:SP来控制。此外,pop和push本质上是一种特殊的内存访问指令,pop和push修改的是SP。