第三章 寄存器(内存访问)
3.1 内存中字的存储
1.任何两个地址连续的内存单元,N号单元和N+1号单元,可以将他们看成两个存储单元
也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元
2.注意:在内存的表示中,从高到低,是从0号单元开始,然后逐渐变大,
即在书写时,低位写在高的地方,高位写在低的地方,
如上图所示:4E20H即是0号字节存储20,1号字节存储4E,如果是0号字则存的是4E20H(字=2字节)
3.2 DS和[address]
1.8086中有一个DS寄存器,通常用来存放要访问的数据的段地址
2.例如:我们要读取10000H单元的内容可以用如下程序段进行:
mov bx,1000H
mov ds,bx
mov al,[0]
上面的三条指令将10000H(1000:0)中的数据读到al(8bit)中
1.复习:已知mov指令可以完成的两种传送功能
1.将数据直接送入寄存器
2.将一个寄存器中的内容送入另一个寄存器中
2.除此之外,mov指令还可以将一个内存单元中的内容送入一个寄存器
mov指令格式:mov 寄存器名,内存单元地址
[...]表示一个内存单元,“[...]”中的...表示内存单元的【偏移地址】
执行指令时,8086CPU自动取DS中的数据为内存单元的【段地址】
3.如何把1000H放入DS中?
要通过通用寄存器把段地址传入到DS中
8086CPU不支持将数据直接送入段寄存器的操作,DS是一个段寄存器
即:mov ds,1000H 是非法的(硬件设计的问题)
数据->通用寄存器->段寄存器
3.写几条指令,将AL中的数据送入内存单元10000H?
mov bx,1000H
mov ds,bx
mov [0],al ;al中的字节型数据送入到1000H:0中
3.3 字的传送
1.8086CPU是16位结构,有16根数据线,所以可以一次性传送16位的数据
即:一次可以传送一个字
2.比如
mov bx,1000H
mov ds,bx
mov ax,[0] ;1000H:0处的字型数据送入ax中
mov [0],cx ;cx中的16位数据送入到1000H:0中
3.4 mov、add、sub指令
1.复习:已学mov指令的几个形式
1.mov 寄存器,数据 ;立即寻址
2.mov 寄存器,寄存器 ;寄存器寻址
3.mov 寄存器,内存单元 ;直接寻址
4.mov 内存单元,寄存器 ;寄存器寻址?
5.mov 段寄存器,寄存器 ;寄存器寻址
6.mov 寄存器,段寄存器 ;寄存器寻址
2.add、sub同mov一样,都有两个操作对象
1.add的用法
1.add 寄存器,数据 ;立即寻址
2.add 寄存器,寄存器 ;寄存器寻址
3.add 寄存器,内存单元 ;直接寻址
4.add 内存单元,寄存器 ;
2.sub的用法
【不带借位的减法】
指令格式 sub op1,op2 ;意为:op1=op1-op2
1.sub 寄存器,数据 ;立即寻址
2.sub 寄存器,寄存器 ;寄存器寻址
3.sub 寄存器,内存单元 ;直接寻址
4.sub 内存单元,寄存器 ;
3.5 数据段
如何访问数据段中的数据?
将一段内存当作数据段,是我们在编程时的一种安排
具体操作:用DS存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元
3.6 栈和cpu提供的栈机制
1.8086CPU提供相关的指令来以栈的方式访问内存空间
这意味着,我们在基于8086CPU编程的时候,可以将一段内存当作栈来使用
2.8086CPU提供入栈和出栈指令:(最基本的)
push(入栈)
pop(出栈)
1.push ax:将寄存器ax中的数据送入栈中
2.pop ax:从栈顶取出数据送入ax
3.8086CPU的入栈和出栈操作都是以【字(16位)】为单位进行的
4.pop和push可以在寄存器和内存之间传送数据
3.CPU如何知道一段内存空间被当做栈使用?
1.8086CPU中,有两个寄存器
1.段寄存器SS:存放栈顶的段地址
2.寄存器SP:存放栈顶的偏移地址【专用寄存器】
2.任意时刻SS:SP指向栈顶元素,当栈为空的时候,也就不存在栈顶元素
ss:sp也就指向栈最高地址单元的下一个单元
4.执行push和pop的时候,如何知道哪个单元是栈顶单元?
1.执行push ax时
1.sp=sp-2
2.将ax中的内容送入到ss:sp指向的内存单元
ss:sp此时指向新栈顶
2.执行pop ax时
1.将ss:sp指向的内存单元的内容送入到ax中
注意:这里取出的内容在内存中还是存在的,并没有被重置
下一轮push会覆盖
2.sp=sp+2
5.如果栈是空的,sp指向哪里?
sp指向最高地址单元的下一个单元
3.7 栈顶超界的问题 push/pop指令
ss、sp只记录了栈顶的地址,依靠ss、sp可以保证在入栈和出栈时找到栈顶
可以,如何能够保证在入栈、出栈时,栈顶不会超出栈空间?
1.8086CPU不保证栈的操作不会越界
2.当栈空的时候,再执行pop出栈 或者 当栈满的时候再使用push入栈
都会发生栈顶超界问题,会操作到栈以外的数据,
这些数据可能是其他用途的数据或者代码
栈顶超界是危险的!!!
3.8086CPU没有记录栈顶上下限的寄存器
push ds:将段寄存器ds中的数据送入栈中
pop es:从栈顶取出数据送入段寄存器es
push[0]:将内存单元 ds:0中的数据送入栈中
pop [2]:从栈顶取出数据送入ds:[2]内存单元
3.8 栈段
1.将一段内存当做栈段,仅仅是我们在编程时的一种安排,
2.ss:sp指向我们定义的栈段的栈顶;
3.当栈空时,sp指向最高地址的下一个单元
4.思考:一个栈段最大可以设为多少?
64KB
5.设栈顶的变化范围是0-FFFFH,从栈空时sp=0(最高地址单元FFFFH的下一个单元0000H)
一直压栈,直到栈满,sp=0;
如果再次压栈,栈顶将环绕,覆盖原来栈中的内容
6.一段内存,既可以是代码的存储空间,又可以是数据的存储空间,还可以是栈空间
也可以是什么都属实。
关键在于CPU中寄存器的设置,即:cs、ip、ss、sp、ds的设置
**可以通过mov直接给sp赋值【立即数寻址】,但是不能通过mov给cs、ip、ss、ds赋值
给cs和ip赋值需要使用jum指令
给ss和ds赋值需要使用mov ss或ds,寄存器 ;【寄存器寻址】
【实验二】
执行过程中发现mov sp,10 不见了,跟随mov ss,ax一起执行了。为什么会这样呢?要想彻底说清楚这里面的来龙去脉,在这里还为时过早,因为这涉及我们在以后的课程中要深入研究的内容:中断机制,它是我们后半部分课程中的一个主题。现在我们只要知道这一点就可以了:Debug 的T命令在执行修改寄存器SS 的指令时,下一条指令也紧接着被执行。
继续执行可以看到栈中数据放入了 3123,3366