寄存器 \color{blue}{\huge{寄存器}} 寄存器
寄存器以及数据存储
①. C P U CPU CPU的组成
- 运算器:进行信息处理。
- 寄存器:进行信息存储。
- 控制器:协调各种器件进行工作。
- 内部总线:实现 C P U CPU CPU内各个器件之间的联系。
②. 寄存器是 C P U CPU CPU内部的信息存储单元
③. 通用寄存器——AX
一个
16
16
16位的寄存器存储一个
16
16
16位的数据。
将输入的数据转化为二进制的数据之后存放到
A
X
AX
AX中。
问题:
8086 8086 8086上一代 C P U CPU CPU中的寄存器都是 8 8 8位的,那么在上一代编写的程序,如何才能保证对于下一代 C P U CPU CPU的兼容?
解决:
将多位数的通用寄存器进行分位操作,分成两个独立的
8
8
8位寄存器进行使用。高八位分成
A
H
AH
AH,低八位分成
A
L
AL
AL。分别进行存储即可。
④. "字"在寄存器中的存储
8086
8086
8086是
16
16
16位的
C
P
U
CPU
CPU。
那么
8086
8086
8086的字长就是
16
16
16位。
一个字可以存储在一个
16
16
16位寄存器中,这个字的高位存储在高
8
8
8位寄存器,低位存储在低
8
8
8位寄存器。
M o v Mov Mov和 a d d add add指令
示例:
- M o v a x 18 Mov \space ax\space18 Mov ax 18:将 18 18 18送入 A X AX AX寄存器中。
-
A
d
d
a
x
b
x
Add \space ax \space bx
Add ax bx:将
A
X
AX
AX和
B
X
BX
BX中的内容相加,结果存储到
A
X
AX
AX中。
❗❗ A d d Add Add指令存在高位截断,溢出的时候截去溢出的位。并且这个高位截断并不只是对于整个寄存器,如果只对寄存器的低位进行操作,那么就算可以进位到高位那么也不会进位到高位,也会进行高位截断。
物理地址
所有的内存单元构成的存储空间是一个一维的线性空间,不过对于线性空间的划分使得不同的存储空间存储不同的数据。
如果
C
P
U
CPU
CPU想要访问内存单元的时候要给出对应内存单元的地址。每一个内存单元在这个线性的存储空间里面都有一个唯一的地址,这个唯一的地址就是物理地址。
①. 寻址法出现背景
8086 8086 8086有 20 20 20位地址总线,所以它的寻址能力是 1 M 1M 1M。但是 8086 8086 8086本身是 16 16 16位结构的 C P U CPU CPU,里面的运算器一次只能处理 16 16 16位的数据,也就是说内部处理的数据对应的寻址能力只有 64 K B 64KB 64KB,内外寻址能力不同,如何弥补这个差距?
②. 8086 C P U 8086CPU 8086CPU物理地址寻址法
物理地址
=
段地址
(
基地址
)
∗
16
+
偏移地址
物理地址 \space = \space 段地址(基地址) * 16 \space + \space 偏移地址
物理地址 = 段地址(基地址)∗16 + 偏移地址
可以使用坐标进行理解,选定好基坐标之后,添加偏移量就可以访问到物理地址,当然选定不同的基坐标,访问同一物理地址对应的偏移量也不同。
这个合成地址的位置在
C
P
U
CPU
CPU中的地址加法器中进行操作,待合成的地址一起送入到地址加法器中,然后地址加法器输出合成后的地址。
③. 寻址法本质 ( ∗ 16 ) (*16) (∗16)
∗
16
*16
∗16的意义就是将原本
16
16
16位的基地址,左移
4
4
4位,这样就变成了
20
20
20位的数据,之后加上偏移量,就使用了两个
16
16
16位的地址来确定了
20
20
20位的地址。
内存的分段表示法
用分段的方式管理内存
物理地址
=
段地址
(
基地址
)
∗
16
+
偏移地址
物理地址 \space = \space 段地址(基地址) * 16 \space + \space 偏移地址
物理地址 = 段地址(基地址)∗16 + 偏移地址
这是
8086
8086
8086进行物理寻址的方法,但是实际上内存并没有进行分段,就只是一块连续的存储地址,每个段落的划分(段地址的起始点选择)是由
C
P
U
CPU
CPU进行确定的。
事实
- 一个段的起始地址必然是 16 16 16的倍数。
- 偏移地址是 16 16 16位。 16 16 16位地址的寻址能力是 64 K 64K 64K,所以一个段的长度最大为 64 K 64K 64K。
- 不同的段地址和偏移地址可以形成同一个物理地址。所以对于一个物理地址是由哪个基地址和偏移量构成的需要进行说明。
- 有专门的寄存器存放段地址。
C S CS CS和 I P IP IP与代码段
C
S
CS
CS:代码段寄存器(存储基地址)
I
P
IP
IP:指令指针寄存器(存储偏移量)
C
S
:
I
P
CS:IP
CS:IP:
C
P
U
CPU
CPU将内存中的
C
S
:
I
P
CS:IP
CS:IP指向的内容当作指令进行执行。
C
S
:
I
P
=
基地址
:
偏移量
CS:IP \space = \space 基地址:偏移量
CS:IP = 基地址:偏移量
8086 8086 8086PC读取和执行指令演示
- 从 C S : I P CS:IP CS:IP指向的内存单元读取指令,读取到的指令输入到指令缓冲器。
- I P = I P + ❗ ( 所读取指令的长度 ) IP = IP + ❗(所读取指令的长度) IP=IP+❗(所读取指令的长度),从而指向下一条命令。
-
1
1
1和
2
2
2重复循环执行直到指令读取完毕。
jmp指令
修改 C S CS CS和 I P IP IP指令
C
S
:
I
P
CS:IP
CS:IP可以确定一条指令的位置,但是除了执行完一条指令
C
P
U
CPU
CPU会自动跳转到下一条指令之外,还有没有其他的方法来修改
C
S
:
I
P
CS:IP
CS:IP呢?
❗❗❗
M
o
v
Mov
Mov指令不能够对
C
S
:
I
P
CS:IP
CS:IP进行修改,
M
o
v
c
s
2000
H
×
Mov \space cs \space 2000H \space ×
Mov cs 2000H × 这是错误的!
C
P
U
CPU
CPU不允许将立即数作为更改
C
S
:
I
P
CS:IP
CS:IP的数据,即使将立即数放置到其他的寄存器再进行转移也不可以。
转移指令 j m p jmp jmp
- 同时修改
C
S
CS
CS和
I
P
IP
IP的内容:
j m p 段地址:偏移地址 jmp \space 段地址:偏移地址 jmp 段地址:偏移地址
j m p 2 A E 3 : 3 jmp \space 2AE3:3 jmp 2AE3:3
j m p 3 : 0 B 16 jmp \space 3:0B16 jmp 3:0B16
用输入的 C S CS CS和 I P IP IP去替换以前的数据。 - 仅仅修改
I
P
IP
IP的内容
j m p 某一合法寄存器 jmp \space 某一合法寄存器 jmp 某一合法寄存器
表示将该寄存器中的数据输入到 I P IP IP,来修改 I P IP IP原有的数据。
j m p a x ( M o v I P , a x ) jmp \space ax (Mov \space IP,ax) jmp ax(Mov IP,ax)
j m p b x ( M o v I P , b x ) jmp \space bx (Mov \space IP,bx) jmp bx(Mov IP,bx)
示例(死循环)
内存中字的存储
8086
C
P
U
8086CPU
8086CPU,
16
16
16位作为一个字,高
8
8
8位放高位字节,低
8
8
8位放低位字节,并且在物理存储中低位字节放在低地址单元,高位字节放在高地址单元。
字单元
字单元就是存放在其中的数据单位类型是字,由两个地址连续的内存单元组成,存放一个字型的数据(
16
16
16位)
字节与字的区分
字节单元内存放的就是一个字节,子单元内存放的就是一个字。但是在物理存储中一个地址对应一个字节,但是一个字要看由多少个字节构成进而确定一个字的地址。
D S DS DS和 [ a d d r e s s ] [address] [address]实现字的传送
C
P
U
CPU
CPU读取内存单元的时候,必须先给出这个内存单元的地址。
读取数据:
D
S
DS
DS和
[
a
d
d
r
e
s
s
]
[address]
[address]集合
- 用 D S DS DS寄存器存放要访问的数据的段地址。
- 偏移地址用 [ . . . ] [...] [...]形式直接给出。
mov bx, 1000H
mov ds, bx
mov al, [0]
//bx通用寄存器将自己存储的值赋值给了ds寄存器,ds寄存其中存储就是现在的段地址(1000H),后面的[0]就是在ds段地址的基础之上的偏移地址
mov bx, 1000H
mov ds, bx
mov [0], al
//将al中的数据写入到(1000:0)中
//将段地址送入DS中的两种方式
(1) mov ds, 1000H ❌
(2) mov bx, 1000H ✔️
mov ds, bx
8086 C P U 8086CPU 8086CPU不支持将数据直接送入到段寄存器中,只能从 数据 → 通用寄存器 → 段寄存器 \color{red}{数据→通用寄存器→段寄存器} 数据→通用寄存器→段寄存器这条路径。
字的传送
8086 C P U 8086CPU 8086CPU也可以一次性传送一个字( 16 16 16位的数据)
mov bx, 1000H
mov ds, bx
mov ax, [0] //(1000:0)处的字型数据送入到ax中
mov [0], cx //cs中的16位数据送到(1000:0)处
❗❗❗传递字型数据的时候读取的使用一定从高位向低位读两个字节(是10001H → 10000H)
D S DS DS与数据段
数据段
8086
C
P
U
8086CPU
8086CPU访问内存中的数据的时候需要使用:
物理地址
=
段地址
∗
16
+
偏移地址
物理地址 = 段地址 * 16 + 偏移地址
物理地址=段地址∗16+偏移地址
于是
地址段
\color{red}{地址段}
地址段:就是一组长度为
N
N
N、地址连续、起始地址为
16
16
16倍数的内存单元当作专门存放存储数据的内存空间。
数据段表示方法
D
S
:
(
[
a
d
d
r
e
s
s
]
)
DS : ([address])
DS:([address])
D
S
DS
DS:存放数据段的段地址。
a
d
d
r
e
s
s
address
address:指令访问数据段中具体单元的时候访问的内存地址。
M o v Mov Mov指令操作数据
❗❗❗❗事实证明除了不能够将数据
M
o
v
Mov
Mov到
段寄存器中
\color{red}{段寄存器}中
段寄存器中,其他的
M
o
v
Mov
Mov使用方法都是正确的。
a d d add add和 s u b sub sub指令
①. a d d add add
下列操作的参数都是可以的。
add 段寄存器, 存储器 ❌
//add操作会将后面的值加入到前面,但是段存储器不允许值随便改变
add 内存单元, 内存单元 ❌
//add两个参数不能都是内存单元
②. s u b sub sub
栈以及栈操作的实现
C P U CPU CPU中包含有栈机制,并且直接提供相关的栈操作,支持使用栈的方式直接访问内存空间。 8086 C P U 8086CPU 8086CPU中可以直接将一段内存当作栈来使用。
栈操作
//ax:通用寄存器等..
//操作的时候以字为单位
push ax //将ax中的数据压入栈中
pop ax //从栈顶取出数据存放到ax中
举例:
上图是执行到了mov cx 1122H
后内存中栈空间的情况,之后向下操作,每次弹出一个字,最后的结果就是:ax = 1122H, bx = 2266H,cx = 0123H
。
疑问?
- C P U CPU CPU如何确定栈在内存空间的位置?
- 如何确定栈内存中的栈顶位置?
解决
与栈相关的寄存器:
- S S SS SS:栈段寄存器,存放栈顶的段地址(起始地址)
- S P SP SP:栈顶指针寄存器,存放栈顶的偏移地址(标明栈顶位置)
- 任何时候 S S : S P SS:SP SS:SP都指向栈顶元素。
两数交换(栈操作)
①. 定义好初始的栈内存空间
mov ax,1000H
mov ss,ax
mov sp,0010H
②. 向
a
x
ax
ax和
b
x
bx
bx中存放要交换的两个数据
mov ax,001AH
mov bx,0018H
③. 两数分别入栈
push ax
push bx
④. 逆顺序出栈,
a
x
ax
ax先接收
p
o
p
pop
pop出来的值,然后才是
b
x
bx
bx。
pop ax
pop bx
最后完成了两数的交换。
❗❗❗❗出栈之后栈顶指针会发生改变,改变指向的位置但是原有的数据弹出之后还是会保存到内存中,但是因为栈顶指针已经不在指向那些空间了,所以那些数字也就没有意义了。
p u s h push push指令和 p o p pop pop指令的执行过程
①. p u s h push push
- S P = S P − 2 SP = SP - 2 SP=SP−2:将栈顶指针移动到新的位置。
- 将 a x ax ax中得内容送到 S S : S P SS:SP SS:SP指向的内存单元。
②. p o p pop pop
- 先将 S S : S P SS:SP SS:SP指向的内存单元的数据送入到 a x ax ax中。
- S P = S P + 2 SP = SP + 2 SP=SP+2退回到栈顶下面的单元,并更改栈顶
越界问题
栈空还要 p o p pop pop操作可能会发生栈顶越界(高地址方向),栈满还要进行 p u s h push push操作可能发生栈顶越界(低地址方向)。但是 8086 C P U 8086CPU 8086CPU并没有相关的栈情况检查操作,不知道程序安排的栈空间有多大,只能够自己仔细小心操作。
💥💥💥 p o p pop pop与 p u s h push push操作的本质就是寄存器与内存进行的数据交换,但是这个交换地址的确定不是直接附加的,需要通过 S S : S P SS:SP SS:SP来进行确定。
段总结
- 数据段
①. 段地址放在 D S DS DS中
②. 使用 s u b sub sub、 a d d add add、 m o v mov mov等指令的时候, C P U CPU CPU将指向的内存中的内容当作数据进行使用。 - 代码段
①. 段地址放在 C S CS CS,偏移地址在 I P IP IP中。
②. C P U CPU CPU将 C S : I P CS:IP CS:IP指向的内存单元中的数据当作指令来进行使用。 - 栈段
①. 段地址放在 S S SS SS中,栈顶指针在 S P SP SP中。
②. C P U CPU CPU进行 p o p pop pop和 p u s h push push操作的时候把定义的栈段当作栈空间来使用。
例题:
按图分配内存空间,并交换两个数据的位置
mov bx, 1000H
mov dx, bx //设置代码段地址ds
mov bx, 1001H
mov ss, bx //设定栈段
mov sp, 10H //设定栈顶位置sp
//取出两个数并利用栈进行交换,之后放回内存中
mov ax, [0]
mov bx, [2]
push ax
push bx
pop ax
pop bx
mov [0], ax
mov [0], bx