事实场景:
对8086CPU,16位作为一个字, 表明一个包含两个字节,对应的便需要两个存储单元来存放;
那么两个字节在内存单元中,具体的存放顺序如何?
问题
16位的字存储在一个16位的寄存器中,如何存储?
回答:
高8位放高字节,低8位放低字节;
问题
16位的字在内存中需要2个连续字节存储,怎么存放?
回答
低位字节存在低地址单元,高位字节存在高地址单元
例:20000D(4E20H)存放0、1两个单元,
18D(0012H)存放在2、3两个单元
注意到, 0号是低地址单元,1号是高地址单元
字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
原理:在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节;
在起始地址为0的单元中,存放的是4E20H
在起始地址为2的单元中,存放的是0012H
问题检测:
(1)0地址单元中存放的字节型数据是( 20H)
(2)0地址字单元中存放的字型数据是( 4E20H )
(3)2地址单元中存放的字节型数据是( 12H )
(4)2地址字单元中存放的字型数据是( 0012H )
需求:
CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址;
原理
在8086PC中,内存地址由段地址和偏移地址组成
(段地址:偏移地址)
解决方案:DS和[address]配合
注意,当直接使用[..] 的表示形式,则本身的含义代表的是使用的是 DS:[..], 以数据段 DS寄存器中的内容,作为段地址,偏移地址是[..]
将10000H(1000:0)中的数据读到al中,
mov bx,1000H
mov ds,bx
mov al, [0]
mov bx,1000H
mov ds,bx
mov [0],a
8086CPU不支持将数据直接送入段寄存器
(硬件设计的问题)
:套路:数据 --> 一般的寄存器 —> 段寄存器
注意到, 当使用DS数据段寄存器时, 则表明此时,从内存单元中取出的内容, cpu将会把该内容当做数据来处理, 因为DS数据段寄存器,表明的便是该内存单元中的内容是作为数据来处理的
8086CPU可以一次性传送一个字(16位的数据)
mov bx, 1000H
mov ds, bx
mov ax, [0] ;1000:0处的字型数据送入ax
mov [0], cx ;cx中的16位数据送到1000:0处
指令
mov ax, 1000H
mov ds, ax
mov ax, [0]
mov bx, [2]
mov cx, [1]
add bx, [1]
add cx, [2]
指令
mov ax, 1000H
mov ds, ax
mov ax, 2C31
mov [0], ax
mov bx, [0]
sub bx, [2]
mov [2], bx
对于8086PC机,可以根据需要将一组内存单元定义为一个段;
物理地址=段地址×16+偏移地址;
数据段: 将一组长度为N(N≤64K)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
用123B0H~123B9H的空间来存放数据
段地址:123BH 起始偏移地址:0000H 长度:10字节
段地址:1230H 起始偏移地址:00B0H 长度:10字节
…
处理方法:DS:[address]
用DS存放数据段的段地址
用相关指令访问数据段中的具体单元,单元地址由[address]指出
将123B0H~123BAH的内存单元定义为数据段;
累加数据段中的前3个单元中的数据,
mov ax, 123BH
mov ds, ax
mov al, 0
add al, [0]
add al, [1]
add al, [2]
这里,需要注意到字型数据和 字节型数据之间的区别
即,一个字数据,占用了两个字节型数据, 每个字节型数据占用一个存储单元。
累加数据段中的前3个字型数据
mov ax, 123BH
mov ds, ax
mov ax, 0
add ax, [0]
add ax, [2]
add ax, [4]
给出00000H-0001F的数据, 请写出下面代码的执行结果:
预设数据, 预设代码, 寄存器值, 执行代码;
指令形式 | 例示 |
---|---|
mov 寄存器,数据 | mov ax, 8 |
mov 寄存器,寄存器 | mov ax, bx |
mov 寄存器,内存单元 | mov ax, [0] |
mov 内存单元,寄存器 | mov [0], ax |
mov 段寄存器,寄存器 | mov ds, ax |
已知:mov 段寄存器,寄存器
推测1 mov 寄存器,段寄存器
: 已知:mov 内存单元,寄存器
推测2 mov 内存单元,段寄存器
推测3 mov 段寄存器,内存单元
已知:mov 寄存器,数据
推测4 mov 段寄存器,数据
add指令形式 | 例示 |
---|---|
add 寄存器,数据 | add ax, 8 |
add 寄存器,寄存器 | add ax, bx |
add 寄存器,内存单元 | add ax, [0] |
add 内存单元,寄存器 | add [0], ax |
sub指令形式 | 例示 |
---|---|
sub 寄存器,数据 | sub ax, 8 |
sub 寄存器,寄存器 | sub ax, bx |
sub 寄存器,内存单元 | sub ax, [0] |
sub 内存单元,寄存器 | sub [0], ax |
mov ax, 1000H
mov ds, ax
mov ax, 11316
mov [0], ax
mov bx, [0]
sub bx, [2]
mov [2], bx
结合体验品味:
(1)字在内存中存储时 ,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放再高地址单元中。
(2)用 mov 指令要访问内存单元,可以在mov指令中只给出单元的偏 移地址,此时,段地址默认在DS寄存器中。
(3)[address]表示一个偏移地址为address的内存单元。
(4)在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。
(5)mov、add、sub是具有两个操作对象的指令,访问内存中的数据段(对照:jmp是具有一个操作对象的指令,对应内存中的代码段)。
(6)可以根据自己的推测,在Debug中实验指令的新格式。
栈是一种只能在一端进行插入或删除操作的数据结构。
栈有两个基本的操作:入栈和出栈。
入栈:将一个新的元素放到栈顶;
出栈:从栈顶取出一个元素。
CPU提供的栈机制
现今的CPU中都有栈的设计。
8086CPU提供相关的指令,支持用栈的方式访问内存空间。
基于8086CPU的编程,可以将一段内存当作栈来使用
PUSH(入栈)和 POP(出栈)指令
push ax:将ax中的数据送入栈中
pop ax:从栈顶取出数据送入ax
ax是16位通用寄存器,表明(以字为单位对栈进行操作)
设将10000H~1000FH内存当作栈来使用,
mov ax,0123H
push ax
mov bx,2266H
push bx
mov cx,1122H
push cx
pop ax
pop bx
pop cx
问题:
1、CPU如何知道一段内存空间被当作栈使用?
2、执行push和pop的时候,如何知道哪个单元是栈顶单元?
回答:
8086cpu 中,有两个与栈相关的寄存器:
栈段寄存器ss, 用于存放栈顶的段地址
,
栈顶指针寄存器sp, 用于存放栈顶的偏移地址
,
而在任意时刻, ss:sp 是指向栈顶元素;
mov ax, 1000H
mov ss, ax
mov sp, 0010H
mov ax, 001AH
mov bx, 001BH
push ax
push bx
pop ax
pop b
push ax :压栈, 默认的是先将sp栈顶指针代表的偏移地址减去2, 然后将ax寄存器中的内容输入到 ss:sp 指向的内存单元中;
这里加减2的原因,是ax寄存器是16位,代表了两个字节的内容,所以是加减二;
pop ax: 出栈,默认的是先将ss:sp 指向的内存单元中的内容输入到寄存器 ax中,然后将栈顶指针sp+2;
注意到,push压栈的过程中,栈顶是逐渐上移动,从高地址向低地址移动;
pop 出栈的过程中,栈顶逐渐向下移动,从低地址到高地址移动。
push ax
(1)SP=SP–2;
(2)将ax中的内容送入SS:SP指向的内存单
元处,SS:SP此时指向新栈顶。
:pop ax
(1)将SS:SP指向的内存单元处的数据送入ax中;
(2)SP = SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
mov ax, 1000H
mov ss, ax
mov sp, 0010H
mov ax, 001AH
mov bx, 001BH
push ax
push bx
pop ax
pop bx
:栈顶超界问题
如何能够保证在入栈、出栈时,栈顶不会超出栈空间?
栈顶超界问题的解决
:8086CPU不保证对栈的操作不会超界。
8086CPU 只知道栈顶在何处(由SS:SP指示),不知道程序安排的栈空间有多大。
我们在编程的时候要自己操心栈顶超界的问题 ,要根据可能用到的最大栈空间,来安排栈的大小,
防止入栈的数据太多而导致的超界;
防止出栈时栈空了仍然继续出栈而导致的超界
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,
与mov指令不同的是, push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。
执行push和pop指令时,SP 中的内容自动改变。
8086CPU提供的栈操作机制:
在ss, sp 中存放栈定的段地址和偏移地址, 入栈和出栈指令根据ss:sp 指示的地址,按照栈的方式访问内存单元。
push 指令的执行步骤:
pop 指令的执行步骤:
基础
物理地址=段地址×16+偏移地址
- 编程时,可以根据需要将一组内存单元定义为一个段。
- 可以将起始地址为16的倍数,长度为N(N ≤64K )的一组地址连续的内存单元,定义为一个段。
将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元——在程序中可以完全由程序员安排
数据段 DS
. 将段地址放在 DS中
. 用mov、add、sub等访问内存单元的指令时,
CPU将我们定义的数据段中的内容当作数据段来访问;
代码段 CS
. 将段地址放在 CS中,将段中第一条指令的偏移地址放在IP中。
. CPU将我们定义的代码段中的内容当做指令来执行;
栈段
. 将段地址放在SS中,将栈顶单元的偏移地置放在 SP 中
. CPU在需要进行栈操作(push、pop)时,就将我们定义的栈段当作栈空间来用。
mov bx, 1000H
mov ds, bx
mov bx, 1001H
mov ss, bx
mov sp, 10H
mov ax, [0]
mov bx, [2]
push ax
push bx
pop ax
pop bx
mov [0], ax
mov [2], bx
mov bx, 1000H
mov ds, bx
mov ss, bx
mov sp, 20H
mov ax, [0]
mov bx, [2]
push ax
push bx
pop ax
pop bx
mov [0], ax
mov [2], bx