- 内存中的字存储
- 字单元
存放一个字型数据(16位)的内存单元,由两个地址梁旭的内存单元组成。高地址存高位字节,低地址存放地位字节。 - N地址字单元
起始地址为N的字单元。比如一个字单元由2,3两个内存单元组成,则这个字单元的起始地址为2,我们可以说这是2地址的字单元。 -
Example:
7FCA58B7-98C0-46C1-8850-7A3B59E22C4A.png
对于图中:
(1)0地址单元中存放的字节型数据是 20H
(2)0地址字单元中存放的字型数据 4E20H
(3)2地址单元中存放的字节型数据是 12H
(4)2地址字单元中存放的字型数据 0012H
(5)1地址字单元中存放的字型数据 124EH
- DS 和 [address]
- DS 8086CPU中的段寄存器,用来存储需要访问数据的地址段
- [address] 表示一个内存单元,其中address表示内存单元的偏移地址
- 内存单元的地址 = SA(ds中的数据为内存单元的段地址) * 16 + EA([address]偏移地址)
- 8086CPU不支持将数据直接送入段寄存器的操作。所以只能用寄存器进行中转。
- Example 1 将10000H(1000:0) 中的数据读到al中
mov bx,1000H
mov ds,bx
mov al,[0]
- Example 2 将 al 中的数据存入内存单元10000H(1000:0)中
mov bx,1000H
mov ds,bx
mov [0],al
字的传送
8086CPU是16位结构,有16根数据线,可以一次传送16位的数据(一个字),所以只要在mov指令中给出16位的寄存器就可以进行16位数据的传送了。mov add sub指令
mov 寄存器,数据 // mov ax,8
mov 寄存器,寄存器 // mov ax,bx
mov 寄存器,内存单元 // mov ax,[0]
mov 内存单元,寄存器 // mov [0],ax
mov 段寄存器,寄存器 // mov ds,ax
mov 寄存器, 段寄存器 // mov ax, ds
mov 内存单元, 段寄存器 // mov [0], ds
mov 段寄存器, 内存单元 // mov ds, [0]
add 寄存器,数据 // add ax,8
add 寄存器,寄存器 // add ax,bx
add 寄存器,内存单元 // add ax,[0]
add 内存单元,寄存器 // add [0],ax
sub 寄存器,数据 // sub ax,8
sub 寄存器,寄存器 // sub ax,bx
sub 寄存器,内存单元 // sub ax,[0]
sub 内存单元,寄存器 // sub [0],ax
- 数据段
编程时的一种安排,将一组长度为N(N<=64KB),地址连续,起始地址为16倍数的内存单元当作专门存储数据的内存空间。
- Example 将123B0H ~ 123B9H 的内存单元定义为数据段。
(1)累加这个数据段中的前3个单元中的数据
mov ax,123BH
mov ds,ax
mov al,0
add al,[0]
add al,[1]
add al,[2]
(2)累加这个数据段中的前3个字型数据
mov ax,123BH
mov ds,ax
mov ax,0
add ax,[0]
add ax,[2]
add ax,[4]
小结
(1)字在内存中存储时,要用两个地址连续的内存单元存放,字的地位放在低地址单元,高位放在高地址单元。
(2)用 mov 访问内存单元,可以再mov中只给出偏移地址,段地址默认在段寄存器DS中。
(3)[address]表示一个偏移地址为 address的内存单元
(4)在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器,低地址单元和低8位寄存器相对应
(5)mov,add,sub 是具有两个操作对象的指令,jmp是具有一个操作对象的指令练习
(1)在Debug中, 用 "d 0:0 1f" 查看内存,结果如下:
0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60
0000:0010 62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88
下面的程序执行前, AX = 0, BX = 0, 写出相关寄存器中的值
mov ax 1
mov ds ax // ds = 1
mov ax,[0000] // AX = 2662H
mov bx,[0001] // BX = E626H
mov ax,bx // AX = E626H
mov ax,[0000] // AX = 2662H
mov bx,[0002] // BX = D6E6H
add ax,bx // AX = FD48H
add ax,[0004] // AX = 2C14H
mov ax,0 // AX = 0H
mov al,[0002] // AX = 00E6H
mov bx,0 // BX = 0H
mov bl,[000C] // BX = 0026H
add al,bl // AX = 000CH
AF017D58-B4BA-4B5C-9652-BE2D971EA324.png
执行指令如下:
mov ax,6622H
jmp 0ff0:0100
mov ax,2000H
mov ds,ax
mov ax,[0008]
mov ax,[0002]
15C2843E-C084-4C73-9C72-449BAFB398F5.png
- 栈
一种数据结构。操作规则 LIFO(Last In First Out 后进先出)
7.CPU提供的栈机制
PUSH ax // 表示将寄存器ax中的数据送入栈中
POP ax //表示取出栈顶数据存入寄存器ax中
- 8086CPU入栈和出栈都是以字为单位进行的。
-
8086CPU中 段寄存器 SS 和寄存器 SP,段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP 指向栈顶元素, PUSH POP执行时,CPU从中取出栈顶地址
8FD03AE2-A6A2-43C9-8B70-564517009023.png -
8086CPU入栈时,栈顶从高地址向低地址方向增长。
D9DFF7D5-0FC9-4F0C-B7D7-AA03245D344B.png
- 栈顶超界问题
8086CPU只知道栈顶在何处(由SS:SP知识),而不知道安排的栈空间有多大,所以在编程的时候自己操作,根据可能用到的最大空间来安排栈的大小,执行出栈入栈的时候要注意可能导致栈超界问题。
9.PUSH POP指令
push 寄存器 //将一个寄存器中的数据入栈
pop 寄存器 //出栈,用一个寄存器中接受出栈的数据
push 段寄存器 //将一个段寄存器中的数据入栈
pop 段寄存器 //出栈,用一个段寄存器中接受出栈的数据
push 内存单元 //将一个内存单元中的字入栈(注意:战的操作以字为单位)
pop 内存单元 //出栈,用一个内存字单元接受出栈的数据
- push pop等栈操作指令修改的只是SP,也就是说栈顶的变化范围最大为 0~FFFFH
栈的综述:
(1)8086CPU提供了栈操作机制,方案如下:
在SS,SP中存放栈顶的段地址和偏移地址
提供入栈出栈指令,它们根据SS:SP指示的地址,按照栈的方向访问内存单元
(2)push指令执行的操作步骤:1.SP=SP-2; 2.向SS:SP指向的字单元中送入数据
(3)pop指令执行的操作步骤:1.从SS:SP指向的字单元中读取数据;2.SP=SP+2
(4)任意时刻,SS:SP 指向栈顶元素
(5)8086CPU只记录栈顶,栈空间的大小我们要自己管理
(6)PUSH POP实质上是一种内存传送指令
- 栈段
- 数据段 段地址存放在 DS中,用mov add sub 等访问内存单元指令,CPU就将我们定义的数据段的内容当做数据来访问
- 代码段 段地址存放在 CS中,将段中的第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义在代码段中的命令
- 栈段 段地址存放在 SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push,pop等指令,就将我们定义的栈段当做栈空间来用。
- Debug T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。