寄存器【内存访问】
内存中字的存储
4E20
是两个字节构成一个字。
上图中0地址单元中存放的字节型数据是
20
。
0地址字节单元中存放的字型数据是4E20
。
2地址字单元中存放的字节型数据是12
。
2地址单元中存放的字型数据是0012
。
1地址单元中存放的字型数据是124EH
。
结论:任何两个地址连续的内存单元,N
号单元和N+1
号单元,可以将它们看成两个内存单元,也可以看成一个地址为N
的字单元中的高位字节单元和低位字节单元。
DS和【address】
- CPU要读取一个内存单元的时候,必须先要给出这个内存单元的地址。
- 在
8086CPU
中,内存地址是由段地址和偏移地址组成。 8086CPU
中有一个DS
寄存器,通常用来存放要访问的数据的段地址。8086CPU
有4个段寄存器:CS
【代码地址】、DS
【数据地址】、SS
【堆栈地址】、ES
【前面不够放这个寄存器】。- 下面三条指令将
10000H【1000:0】
中的数据读到AL
中。
mov bx,1000H
mov ds,bx
mov al,[0] //偏移地址为0的内存单元送入寄存器,[]表示一个内存单元,其中的数字0表示内存单元的偏移地址
- 已知的
mov
指令可完成两种传送功能:(1)将数据直接送入寄存器;(2)将一个寄存器中的内容送入另一个寄存器中;(3)将一个内存单元中的内容送入一个寄存器。 - 从哪个内存单元到哪个寄存器?
mov指令的格式:mov 寄存器名,内存单元地址
- 执行指令时,
8086CPU
自动取DS
中的数据为内存单元的段地址。 CS:IP
读取到的东西会被CPU
认为是指令,而DS
读取到的东西会直接被CPU
认为是数据。CS
、DS
都是段寄存器,一个程序往往被分为好几个段,DS
保存的是数据段的基地址,而CS
保存的是代码段的基地址,IP
中保存的是要执行的下一条指令的地址。- 如何用mov指令从
10000H
中读取数据?
10000H
表示从1000:0
【段地址:偏移地址】- 将段地址
1000H
放入DS
- 用
mov al,[0]
完成传送【mov
指令中的[]
说明操作对象是一个内存单元,[]
中的0
说明这个内存单元的偏移地址是0
,它的段地址默认放在DS
中。】
- 8086CPU不支持将数据直接送入段寄存器的操作,如
mov ax,1
可以,但是mov ds,1000H
不可以。 - 数据 -> 通用寄存器 -> 段寄存器
- 如何将数据从寄存器送入内存单元?将
al
中的数据如何送入到内存单元10000H
中?
mov bx,1000H
mov ds,bx //前两句是确认数据的段地址
mov [0],al //这里的al是题目的要求
字的传送
- 因为
8086CPU
是16
位结构,有16
根数据线,所以可以一次性传输16
位的数据,也就是一个字。
mov bx,1000H
mov ds,bx //前两句是确认数据的段地址
mov ax,[0] //1000:0处的字型数据放入ax中,ax为16位数据
mov [0],cx //cx中的16位数据送到1000:0中
mov、add、sub指令
mov
指令的几种形式
mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器
mov 寄存器,段寄存器
add
和sub
指令同mov
一样,都有两个操作对象。
-
sub:左边=左边-右边
-
要用
mov
指令要访问内存单元,可以在mov
指令中只给出内存单元的偏移地址,此时,段地址默认在DS
寄存器中。
数据段
- 我们可以将一组长度为
N
【N<=64K
】,地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。 - 比如,我们用
123B0H~123B9H
这段空间来存放数据,段地址:123BH
,长度:10
字节。 - 如何访问数据段中的数据呢? 将一段内存中当作数据段,是我们在编程时的一种安排,我们可以在具体操作的时候,用
DS
存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
栈
- 栈是一种具有特殊的访问方式的存储空间。它的特殊性在于,最后进入这个空间的数据,最先出去。【
LIFO
】 - 栈的基本操作:入栈、出栈。
- 现今的
CPU
中都有栈的设计。 8086CPU
提供相关的指令以栈的方式访问内存空间,这意味着,我们在基于8086CPU
编程的时候,可以将一段内存当作栈来使用。- 入栈:
PUSH
,出栈:POP
。
push ax //将寄存器ax中的数据送入栈中
pop ax //从栈顶取出数据送入ax
8086CPU
的入栈和出栈操作都是以字为单位进行的。CS:IP
cpu认为是指令,DS:偏移地址
cpu认为是数据。8086CPU
中,段寄存器SS
:存放栈顶的段地址,寄存器SP
:存放栈顶的偏移地址。- 任意时刻,
SS:SP
指向栈顶元素。当栈为空的时候,栈中没有元素,也就不存在栈顶元素。 - 所以,
SS:SP
只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2
。
- 字型数据用两个内存单位存放,高地址放高
8
位,低地址放低8
位。
栈顶越界的问题
8086CPU
在工作过程中只考虑当前的情况:当前栈顶在何处?当前要执行的指令是哪一条?- 如何保证入栈、出栈时栈顶不会超出栈空间? 我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间来安排栈的大小,防止入栈的数据太多而导致的超界。执行出栈操作的时候也要注意。
PUSH和POP指令
push
和pop
指令是可以在寄存器和内存之间传送数据的。- 【段寄存器以
s
结尾,通用寄存器用x
结尾】
push
寄存器:将一个寄存器中的数据入栈
pop
寄存器:用一个寄存器接受出栈的数据。
push
段寄存器:将一个段寄存器中的数据入栈
pop
段寄存器:用一个段寄存器接受出栈的数据
push
内存单元:将一个内存单元处的字入栈
pop
内存单元:出栈,用一个内存字单元接收出栈的数据
push[0]
指令执行时,CPU
要知道内存单元的地址,可以在push
、pop
指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从DS
中取得。
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
sub ax,ax // mov ax,0 或者 xor ax,ax[相同则为0, 不同则为1]
sub bx,bx // mov bx,0
pop bx
pop bx
mov ax,1000H
mov ss,ax
mov sp,001H
mov ax,002AH
mov bx,002BH
push ax
push bx
pop ax //用寄存器ax接受出来的bx的数据,即可实现交换
pop bx
另一种写法:
mov ax,1000H
mov ss,ax
mov sp,2
mov ax,2266H
push ax //push指令会先执行 sp = sp -2,把栈顶指针指上去,所以sp = 2 而不是 0
栈的综述
- 栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
- push指令的执行步骤:
- sp = sp - 2
- 向
SS:SP
指向的字单元中送入数据
- pop指令执行步骤:
- 从
SS:SP
指向的字单元中读取数据;- sp = sp + 2
8086CPU
只记录栈顶,栈空间的大小我们要自己管理。- 可以用栈来暂存以后需要恢复的寄存器的内容。
- 栈顶的变化范围最大为
0~FFFFH
栈段【栈的综述】
-
对于
8086PC
机,在编程时,我们可以根据需要,将一组内存单元定义为一个段。 -
我们可以将长度为
N
【N<= 64K
】的一组地址连续,起始地址为16
的倍数的内存单元,当作栈来用,从而定义了一个栈段。 -
将一段内存当作栈段,仅仅是在编程时的一种安排,CPU不会由于这种安排,就在执行
push
、pop
等栈操作指令时就自动将我们定义的栈段当作栈空间来访问。CPU
只能看见栈顶。 -
栈段的最大可以设为:2的16次方,
64KB
。 -
从栈操作指令所完成的功能的角度来看,
push
、pop
指令在执行的时候只修改SP
。 -
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的大暖,这完全是我们自己的安排。
-
对于代码段,我们将它的段地址放在CS中,将段中的第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令。
-
对于栈段,我们将它的段地址放在
SS
中,将栈顶单元的偏移地址放在SP
中,这样CPU
在需要进行栈操作的时候,比如push
、pop
指令等,就将我们定义的栈段当作栈空间来用。 -
CPU
将内存中的某段内存当作代码,是因为CS:IP
指向那里;CPU
将某段内存当作栈,是因为SS:IP
指向了那里。 -
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是,关键在于CPU中寄存器的设置。