第二讲 寄存器
学习汇编语言时所作的笔记。
寄存器\color{blue}{\huge{寄存器}}寄存器
寄存器以及数据存储
①. CPUCPUCPU的组成
- 运算器:进行信息处理。
- 寄存器:进行信息存储。
- 控制器:协调各种器件进行工作。
- 内部总线:实现CPUCPUCPU内各个器件之间的联系。
②. 寄存器是CPUCPUCPU内部的信息存储单元
③. 通用寄存器——AX
一个161616位的寄存器存储一个161616位的数据。
将输入的数据转化为二进制的数据之后存放到AXAXAX中。
问题:
808680868086上一代CPUCPUCPU中的寄存器都是888位的,那么在上一代编写的程序,如何才能保证对于下一代CPUCPUCPU的兼容?
解决:
将多位数的通用寄存器进行分位操作,分成两个独立的888位寄存器进行使用。高八位分成AHAHAH,低八位分成ALALAL。分别进行存储即可。
④. "字"在寄存器中的存储
808680868086是161616位的CPUCPUCPU。
那么808680868086的字长就是161616位。
一个字可以存储在一个161616位寄存器中,这个字的高位存储在高888位寄存器,低位存储在低888位寄存器。
MovMovMov和addaddadd指令
示例:
- Mov ax 18Mov \space ax\space18Mov ax 18:将181818送入AXAXAX寄存器中。
- Add ax bxAdd \space ax \space bxAdd ax bx:将AXAXAX和BXBXBX中的内容相加,结果存储到AXAXAX中。
❗❗AddAddAdd指令存在高位截断,溢出的时候截去溢出的位。并且这个高位截断并不只是对于整个寄存器,如果只对寄存器的低位进行操作,那么就算可以进位到高位那么也不会进位到高位,也会进行高位截断。
物理地址
所有的内存单元构成的存储空间是一个一维的线性空间,不过对于线性空间的划分使得不同的存储空间存储不同的数据。
如果CPUCPUCPU想要访问内存单元的时候要给出对应内存单元的地址。每一个内存单元在这个线性的存储空间里面都有一个唯一的地址,这个唯一的地址就是物理地址。
①. 寻址法出现背景
808680868086有202020位地址总线,所以它的寻址能力是1M1M1M。但是808680868086本身是161616位结构的CPUCPUCPU,里面的运算器一次只能处理161616位的数据,也就是说内部处理的数据对应的寻址能力只有64KB64KB64KB,内外寻址能力不同,如何弥补这个差距?
②. 8086CPU8086CPU8086CPU物理地址寻址法
物理地址 = 段地址(基地址)∗16 + 偏移地址物理地址 \space = \space 段地址(基地址) * 16 \space + \space 偏移地址物理地址 = 段地址(基地址)∗16 + 偏移地址
可以使用坐标进行理解,选定好基坐标之后,添加偏移量就可以访问到物理地址,当然选定不同的基坐标,访问同一物理地址对应的偏移量也不同。
这个合成地址的位置在CPUCPUCPU中的地址加法器中进行操作,待合成的地址一起送入到地址加法器中,然后地址加法器输出合成后的地址。
③. 寻址法本质(∗16)(*16)(∗16)
∗16*16∗16的意义就是将原本161616位的基地址,左移444位,这样就变成了202020位的数据,之后加上偏移量,就使用了两个161616位的地址来确定了202020位的地址。
内存的分段表示法
用分段的方式管理内存
物理地址 = 段地址(基地址)∗16 + 偏移地址物理地址 \space = \space 段地址(基地址) * 16 \space + \space 偏移地址物理地址 = 段地址(基地址)∗16 + 偏移地址
这是808680868086进行物理寻址的方法,但是实际上内存并没有进行分段,就只是一块连续的存储地址,每个段落的划分(段地址的起始点选择)是由CPUCPUCPU进行确定的。
事实
- 一个段的起始地址必然是161616的倍数。
- 偏移地址是161616位。161616位地址的寻址能力是64K64K64K,所以一个段的长度最大为64K64K64K。
- 不同的段地址和偏移地址可以形成同一个物理地址。所以对于一个物理地址是由哪个基地址和偏移量构成的需要进行说明。

- 有专门的寄存器存放段地址。

CSCSCS和IPIPIP与代码段
CSCSCS:代码段寄存器(存储基地址)
IPIPIP:指令指针寄存器(存储偏移量)
CS:IPCS:IPCS:IP:CPUCPUCPU将内存中的CS:IPCS:IPCS:IP指向的内容当作指令进行执行。
CS:IP = 基地址:偏移量CS:IP \space = \space 基地址:偏移量CS:IP = 基地址:偏移量
808680868086PC读取和执行指令演示
- 从CS:IPCS:IPCS:IP指向的内存单元读取指令,读取到的指令输入到指令缓冲器。
- IP=IP+❗(所读取指令的长度)IP = IP + ❗(所读取指令的长度)IP=IP+❗(所读取指令的长度),从而指向下一条命令。
- 111和222重复循环执行直到指令读取完毕。

jmp指令
修改CSCSCS和IPIPIP指令
CS:IPCS:IPCS:IP可以确定一条指令的位置,但是除了执行完一条指令CPUCPUCPU会自动跳转到下一条指令之外,还有没有其他的方法来修改CS:IPCS:IPCS:IP呢?
❗❗❗MovMovMov指令不能够对CS:IPCS:IPCS:IP进行修改,Mov cs 2000H ×Mov \space cs \space 2000H \space ×Mov cs 2000H × 这是错误的!CPUCPUCPU不允许将立即数作为更改CS:IPCS:IPCS:IP的数据,即使将立即数放置到其他的寄存器再进行转移也不可以。
转移指令jmpjmpjmp
- 同时修改CSCSCS和IPIPIP的内容:
jmp 段地址:偏移地址jmp \space 段地址:偏移地址jmp 段地址:偏移地址
jmp 2AE3:3jmp \space 2AE3:3jmp 2AE3:3
jmp 3:0B16jmp \space 3:0B16jmp 3:0B16
用输入的CSCSCS和IPIPIP去替换以前的数据。 - 仅仅修改IPIPIP的内容
jmp 某一合法寄存器jmp \space 某一合法寄存器jmp 某一合法寄存器
表示将该寄存器中的数据输入到IPIPIP,来修改IPIPIP原有的数据。
jmp ax(Mov IP,ax)jmp \space ax (Mov \space IP,ax)jmp ax(Mov IP,ax)
jmp bx(Mov IP,bx)jmp \space bx (Mov \space IP,bx)jmp bx(Mov IP,bx)
示例(死循环)
内存中字的存储
8086CPU8086CPU8086CPU,161616位作为一个字,高888位放高位字节,低888位放低位字节,并且在物理存储中低位字节放在低地址单元,高位字节放在高地址单元。
字单元
字单元就是存放在其中的数据单位类型是字,由两个地址连续的内存单元组成,存放一个字型的数据(161616位)

字节与字的区分
字节单元内存放的就是一个字节,子单元内存放的就是一个字。但是在物理存储中一个地址对应一个字节,但是一个字要看由多少个字节构成进而确定一个字的地址。
DSDSDS和[address][address][address]实现字的传送
CPUCPUCPU读取内存单元的时候,必须先给出这个内存单元的地址。
读取数据:DSDSDS和[address][address][address]集合
- 用DSDSDS寄存器存放要访问的数据的段地址。
- 偏移地址用[...][...][...]形式直接给出。
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
8086CPU8086CPU8086CPU不支持将数据直接送入到段寄存器中,只能从数据→通用寄存器→段寄存器\color{red}{数据→通用寄存器→段寄存器}数据→通用寄存器→段寄存器这条路径。
字的传送
8086CPU8086CPU8086CPU也可以一次性传送一个字(161616位的数据)
mov bx, 1000H
mov ds, bx
mov ax, [0] //(1000:0)处的字型数据送入到ax中
mov [0], cx //cs中的16位数据送到(1000:0)处

❗❗❗传递字型数据的时候读取的使用一定从高位向低位读两个字节(是10001H → 10000H)
DSDSDS与数据段
数据段
8086CPU8086CPU8086CPU访问内存中的数据的时候需要使用:
物理地址=段地址∗16+偏移地址物理地址 = 段地址 * 16 + 偏移地址物理地址=段地址∗16+偏移地址
于是地址段\color{red}{地址段}地址段:就是一组长度为NNN、地址连续、起始地址为161616倍数的内存单元当作专门存放存储数据的内存空间。
数据段表示方法
DS:([address])DS : ([address])DS:([address])
DSDSDS:存放数据段的段地址。
addressaddressaddress:指令访问数据段中具体单元的时候访问的内存地址。
MovMovMov指令操作数据

❗❗❗❗事实证明除了不能够将数据MovMovMov到段寄存器中\color{red}{段寄存器}中段寄存器中,其他的MovMovMov使用方法都是正确的。
addaddadd和subsubsub指令
①. addaddadd
下列操作的参数都是可以的。
add 段寄存器, 存储器 ❌
//add操作会将后面的值加入到前面,但是段存储器不允许值随便改变
add 内存单元, 内存单元 ❌
//add两个参数不能都是内存单元
②. subsubsub

栈以及栈操作的实现
CPUCPUCPU中包含有栈机制,并且直接提供相关的栈操作,支持使用栈的方式直接访问内存空间。8086CPU8086CPU8086CPU中可以直接将一段内存当作栈来使用。
栈操作
//ax:通用寄存器等..
//操作的时候以字为单位
push ax //将ax中的数据压入栈中
pop ax //从栈顶取出数据存放到ax中
举例:
上图是执行到了mov cx 1122H后内存中栈空间的情况,之后向下操作,每次弹出一个字,最后的结果就是:ax = 1122H, bx = 2266H,cx = 0123H。
疑问?
- CPUCPUCPU如何确定栈在内存空间的位置?
- 如何确定栈内存中的栈顶位置?
解决
与栈相关的寄存器:
- SSSSSS:栈段寄存器,存放栈顶的段地址(起始地址)
- SPSPSP:栈顶指针寄存器,存放栈顶的偏移地址(标明栈顶位置)
- 任何时候SS:SPSS:SPSS:SP都指向栈顶元素。
两数交换(栈操作)
①. 定义好初始的栈内存空间
mov ax,1000H
mov ss,ax
mov sp,0010H

②. 向axaxax和bxbxbx中存放要交换的两个数据
mov ax,001AH
mov bx,0018H

③. 两数分别入栈
push ax
push bx

④. 逆顺序出栈,axaxax先接收poppoppop出来的值,然后才是bxbxbx。
pop ax
pop bx

最后完成了两数的交换。
❗❗❗❗出栈之后栈顶指针会发生改变,改变指向的位置但是原有的数据弹出之后还是会保存到内存中,但是因为栈顶指针已经不在指向那些空间了,所以那些数字也就没有意义了。
pushpushpush指令和poppoppop指令的执行过程
①. pushpushpush
- SP=SP−2SP = SP - 2SP=SP−2:将栈顶指针移动到新的位置。
- 将axaxax中得内容送到SS:SPSS:SPSS:SP指向的内存单元。
②. poppoppop
- 先将SS:SPSS:SPSS:SP指向的内存单元的数据送入到axaxax中。
- SP=SP+2SP = SP + 2SP=SP+2退回到栈顶下面的单元,并更改栈顶
越界问题
栈空还要poppoppop操作可能会发生栈顶越界(高地址方向),栈满还要进行pushpushpush操作可能发生栈顶越界(低地址方向)。但是8086CPU8086CPU8086CPU并没有相关的栈情况检查操作,不知道程序安排的栈空间有多大,只能够自己仔细小心操作。
💥💥💥poppoppop与pushpushpush操作的本质就是寄存器与内存进行的数据交换,但是这个交换地址的确定不是直接附加的,需要通过SS:SPSS:SPSS:SP来进行确定。
段总结
- 数据段
①. 段地址放在DSDSDS中
②. 使用subsubsub、addaddadd、movmovmov等指令的时候,CPUCPUCPU将指向的内存中的内容当作数据进行使用。 - 代码段
①. 段地址放在CSCSCS,偏移地址在IPIPIP中。
②. CPUCPUCPU将CS:IPCS:IPCS:IP指向的内存单元中的数据当作指令来进行使用。 - 栈段
①. 段地址放在SSSSSS中,栈顶指针在SPSPSP中。
②. CPUCPUCPU进行poppoppop和pushpushpush操作的时候把定义的栈段当作栈空间来使用。
例题:
按图分配内存空间,并交换两个数据的位置
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
更多推荐

所有评论(0)