汇编学习笔记(2) -- 寄存器(内存访问),数据段和栈

内存中字的存储
一个字两个字节
所以内存中用两个地址连续的内存单元存储一个字
该字的低位字节存放在低地址内存单元,高位字节存放在高地址内存单元
 
20000的十六进制是4E20H 高位就是4E,存在地址高一点的地址内存单元中
 
 
字单元
由此就有了 字单元: 由两个地址连续的内存单元组成
将起始地址为N的字单元简称为N地址字单元
如:地址为2,3的两个内存单元组成的字单元,称之为 2地址字单元
 
总结:
任何两个地址连续的内存单元,N号单元和N+l号单元
可以将它们看成两个内存单元
也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元
 
 
 
DS和[address]
DS是一个寄存器,通常用于存放要访问数据的地址,称为段寄存器
1 mov bx,1000H
2 mov ds,bx
3 mov al,[0]

 

将10000H[1000:0]中的数据读到al寄存器中
 
mov al,[0]
mov 寄存器名,内存单元地址
[...]表示一个内存单元,其中0表示偏移地址
而段地址是8086CPU 自动取ds中的数据为内存单元的段地址
 
注意,不能直接:mov ds 1000H
因为8086不支持将数据直接送入段寄存器,只能先送到一个一般的寄存器(如bx),再将bx内容送入ds
 
 
问题:将al中的数据送入内存单元10000H
答案:
1 mov bx,1000H
2 mov ds,bx   10000H的地址送入ds中
3 
4 mov [0],al  [0]是偏移地址,段地址从ds自动获取

 

字的传送
8086是16位结构,有16根数据线
所以一次可以传输16位数据,即一次传一个字
例如
1 mov bx,1000H
2 mov ds,bx
3 mov ax,[0]  1000:0处的字形数据送入ax
4 mov [0],cs  cs中的16位数据送到1000:0处

 

例题 执行后,ax,bx,cx的值是多少
ax = 1123H, bx = 6622H, cx = 8833H
 
 
 
数据段
将一组地址连续起始地址是16的倍数 的内存单元 当作专门存储数据的内存空间,从而定义了数据段
例如:
将123B0H ~ 123B9H 这段内存单元作为专门存储数据的内存空间
这段内存就是一个段地址为123BH,长为10个字节的数据段
 
将123B0H ~ 123B9H定义为数据段,将三个单元的数据累加到al
1 mov ax,123B
2 mov ds,ax
3 mov al,0
4 mov al,[0]
5 mov al,[1]
6 mov al,[2]

 

栈是一种具有特殊访问方式的存储空间。
特殊处在于:最后进入这个空间的数据,最先出去
 
入栈:将一个新的元素放到栈顶
出栈:从栈顶取出一个元素
 
栈顶的元素总是最后入栈
需要出栈时,又最先被从栈顶取出
 
操作规则:LIFO(先进后出)
 
 
 
可以将一段内存当作栈来使用
 
 
指令:
push 入栈
push ax:将寄存器ax中的数据送入栈中
 
pop 出栈
pop ax 从栈顶取出数据送入ax
8086中入栈和出栈都是以字为单位进行(8086是16位,酷睿是64位)
 
 
1 mov ax,0123H
2 push ax
3 mov bx,2266H
4 push bx
5 mov cx,1122H
6 push cx
7 pop ax
8 pop bx
9 pop cx

 

注意,字形单元用两个单元存放,高地址单元存放高8位,低地址单元存放低8位
 
 
SS:SP
任意时刻,SS:SP指向栈顶元素
段寄存器SS 存放栈顶的段地址
寄存器SP 存放栈顶的偏移地址
 
执行push,pop时,cpu从SS和SP中得到栈顶的地址
 
 
push ax执行过程
1 SP = SP - 2
2 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶
 
将10000H ~ 10005H 这段空间作为栈,初始状态栈为空
则SS:SP指向 栈空间最高地址单元 下一个单元(100006H)
 
由于SS:SP在任意时刻指向栈顶,而栈为空时 栈中无元素 也就没有栈顶
所以SS:SP 只能指向 栈的 最底部单元 下面的单元 (栈的顺序可以理解为地址小的在上面)
该单元的偏移地址为 栈最底部的 字 单元 的 偏移地址 +2
 
栈最底部字单元的地址为1000:0004,所以栈空时,SP = 0006H
 
 
pop指令执行过程:
 
 
栈顶超界
执行push后栈顶超出栈空间
 
执行pop后栈顶超出栈空间
 
 
栈空间之外可能是其它重要用途的数据代码等
而超出栈空间后的 数据会覆盖 原数据
也就是栈溢出
 
例题
1 将10000H~1000FH 这段空间当作栈,初始状态栈是空的,将AX、BX、DS中的数据入栈。
1 mov ax,1000H
2 mov ss,ax        设置栈的段地址,ss=1000H,不能直接向段寄存器ss中送入数据,所以用ax中转。
3 mov sp,0010H     设置栈顶的偏移地址,因栈为空,所以sp=0010H。
4 push ax
5 push bx
6 push ds

 

 
2 在10000H处写入字型数据2266H
 1 mov ax,1000H
 2 mov ds,ax
 3 mov ax,2266H
 4 mov [0],ax
 5 
 6 mov ax,1000H
 7 mov ss,ax
 8 mov sp,2   执行push时,cpu会SP=SP-2
 9 
10 mov ax,2266H
11 push ax

 

由于push,pop执行时只修改SP,所以栈顶变化范围为0~FFFFH
 
 
栈段
我们可以将长度为N(N≤64KB)的一组
地址连续、起始地址为16的倍数的内存单元,当作栈空间来用
从而定义了一个栈段
 
比如,我们将10010H~1001FH 这段长度为16字节的内存空间当作栈来用
这段空间就可以称为栈段,段地址为1001H,大小为16字节
使用SS:SP指向我们自定义的栈段
 
一个栈段最大为64kb
因为push,pop指令只修改sp,栈段变化范围为0~FFFFH,所以有65536个字节,即64kb
 
问题
如果将10000H~1FFFFH 这段空间当作栈段,初始状态栈是空的,此时,
SS=1000H,SP=?
栈最底部的字单元地址为1000:FFFE。
栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2。
加2后SP=10000,数字太大,1不能存入SP,所以SP=0。
 
 
 
段的总结
对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时
CPU就将我们定义的数据段中的内容当作数据来访问
 
对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中
这样CPU就将执行我们定义的代码段中的指令;
 
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中
这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当作栈空间来用。
 
比如我们将1000OH~1001FH 安排为代码段,并在里面存储如下代码:
 1 mov ax,1000H
 2 mov ss,ax
 3 mov sp,0020H        ;初始化栈顶
 4 mov ax,cs        
 5 mov ds,ax        ;设置数据段段地址
 6 mov ax,[0]
 7 add ax,[2]
 8 mov bx,[4]
 9 add bx,[6]
10 push ax
11 push bx
12 pop ax
13 pop bx

 

 
设置 CS=1000H,IP-0,这段代码将得到执行。
可以看到,在这段代码中,我们又将10000H~1001FH安排为栈段和数据段。
10000H~1001FH 这段内存,既是代码段,又是栈段和数据段。
 
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不是。
关键在于 CPU中寄存器的设置,即CS、IP,SS,SP,DS的指向
 
参考: 王爽 - 汇编语言 和 小甲鱼零基础汇编
 
 
posted @ 2020-09-08 21:24  周佳豪  阅读(996)  评论(0)    收藏  举报