第三章 寄存器(内存访问)

第三章 寄存器(内存访问)

3.1 内存中字的存储

  • CPU中,寄存器用16位寄存器来存储一个字(2个Byte),高8位存放高位字节,低8位存放低位字节

  • 内存单元是字节单元(我们之前提过),一个字要用两个地址连续的内存单元来存放


  • 字单元:即存放一个字型数据(16位)的单元,由两个地址连续的内内存单元构成
  • 高地址内存单元中放字型数据的高位字节,低地址内存单元中放字型数据的低位字节
  • 起始地址为N的字单元简称为N地址字单元

可以问自己几个问题,看看有没有熟悉相关概念:

字的组成?什么是内存单元?什么是字单元?存放数据的方式是什么?

3.2 DS和[address]

  • 8086CPU中由一个DS寄存器,通常用来存放要访问数据的段地址

使用MOV指令还可以将一个内存单元的内容送到一个寄存器中,如mov al,[0]

  • […] 括号中的数表示一个内存单元,而上面的[0]代表内存单元的偏移地址
  • 指令执行时,8086CPU自动取DS中的数据为内存单元的段地址

如何用MOV指令从10000H中读取数据?

  • 根据寻址方法:先把DS中的值设为1000H,再利用mov al,[0]即可
mov ds,1000H  ;这条指令是不合法的
mov al,[0]

但是根据8086CPU的硬件设计,你要这么写是非法的,8086CPU不支持将数据直接送入段寄存器

我们可以利用一般寄存器中转

mov bx,1000H
mov ds,bx
mov al,[0]

3.3 字的传送

字是两个字节组成的,那么一个字就有16位,而8086CPU是16位结构,有16根数据线,那么可以一次传送一个字

mov bx,1000H
mov ds,bx
mov ax,[0]      ;1000:0 处的字型数据送入ax
mov [0],cx      ;cx中的16位数据送到1000:0处

问题3.3

mov ax,1000H
mov ds,ax
mov ax,[0]     ;执行后ax值位1123H
mov bx,[2]     ;执行后bx值为6622H
mov cx,[1]     ;执行后cx值为2211H
add bx,[1]     ;执行后bx值为8833H
add cx,[2]     ;执行后cx值为8833H

问题3.4

mov ax,1000H
mov ds,ax
mov ax,11316  ;11316是十进制,转换为十六进制为2C34H
mov [0],ax  
mov bx,[0]
sub bx,2      ;bx=bx中的字型数据-1000:2处的字型数据
mov [2],bx

3.4 mov、add、sub指令

现在我们知道,mov指令可以有以下几种形式

mov 寄存器,数据         mov ax,8
mov 寄存器,寄存器       mov ax,bx
mov 寄存器,内存单元     mov ax,[0]
mov 内存单元,寄存器     mov [0],ax
mov 段寄存器,寄存器     mov ds,ax
  1. 那么,mov ax,ds这种 mov 段寄存器,寄存器 是否是正确指令呢?(正确的,自己验证一下)
  2. mov 内存单元,寄存器,那么 mov 内存单元,段寄存器 也应该有

3.5 数据段

  • 对于8086PC机,在编程时我们可以根据需要,将一组内存单元定义为一个段
  • 进一步的,我们可以将一组长度N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元当作一个专门存放数据的内存空间,即为数据段(其实这完全取决于我们的用途,想想前面代码段的定义)
; 将123B0H~123B9H内存单元定义为数据段,累加前三个单元中的数据
mov ax,123BH
mov ds,ax
mov al,0
add al,[0]
add al,[1]
add al,[2]

那么累加数据段中前三个字型数据呢?

mov ax,123BH
mov ds,ax
mov ax,0
add ax,[0]   ;一个字型数据占两个单元,所以偏移地址为0、2、4
add ax,[2]
add ax,[4]

3.6 栈

  • 栈是一种具有特殊的访问方式的存储空间,特殊性在于最后进入这个空间的数据,是最先出去的

  • 栈的这种操作规则为:LIFO(Last In First Out)

你可以这么理解,栈是一个袋子,里面放着封面朝上的一本一本的书,在不破坏袋子的情况下想拿下面的书就要先拿上面的书,而上面的书肯定是最近(最后)放进去的

3.7 CPU提供的栈机制

8086CPU提供相关的指令来以栈的方式访问内存空间

push ax ;将寄存器AX中的数据送入栈中
pop ax  ;从栈顶去除数据送入AX

这里强调,8086CPU的入栈和出栈操作都是以字为单位进行的

image-20221012210435937

有几个问题:

  • CPU怎么知道10000H-1000FH这段空间被当作栈来使用呢?
  • 入栈出栈针对是就是栈顶,那么怎么知道哪个是栈顶的字单元呢?

之前我们讨论过:

  • CPU如何知道当前要执行指令的位置?

答案是:CS:IP存放着当前指令的段地址和偏移地址,这是两个特殊功能寄存器

那么现在,也可以猜测到了,8086CPU中大概率也有相应的特殊功能寄存器来存放栈顶地址

  • 8086CPU中有段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中
  • 任意时刻,SS:SP指向栈顶元素,push,pop指令执行时,CPU就从这两个专门的位置来找到栈顶地址
; 描述push指令的执行
push ax  ;   1. SP=SP-2,  SS:SP指向栈顶前面的单元,让前面的单元成为新的栈顶
         ;   2.将AX中的内容送入SS:SP指向的内存单元,并且SS:SP此时指向新的栈顶
image-20221012211227405

为什么是SP=SP-2?因为一开始从底部往上,那么就是由高地址到低地址

问题3.6

  • 如果将10000H-1000FH当作栈,初始状态为空栈,此时SS=1000H,那么SP=?

image-20221012211448333

初始为栈空,那么就入栈一个字看看;如果SP=0FH,那么入栈以后SP=SP-2,数据会存放在1000DH和1000EH单元中,底部就空了一个出来,所以应该SP=10H

换一个角度,栈空为一个元素都没有,而任意时刻,SS:SP指向栈顶元素,那么只能指向底部内存单元的下面一个单元

; 描述pop ax的过程(与push ax刚好相反)
pop ax   ; 1. 将SS:SP指向的内存单元中的数据送入ax中
         ; 2. SP=SP+2,使得SS:SP指向栈顶下面的内存单元,使这个下面的内存单元成为新栈顶

image-20221012212040521

这里要注意:

  • 虽然1000CH处的2266H依然存在,但是已经不在栈中
  • 再次有push指令入栈之后新的数据会覆盖旧的数据

3.8 栈顶超界的问题

image-20221012212252373 image-20221012212304925
  • 栈顶超界是危险,因为这段空间被安排为栈的话,其他空间可能存了数据或者代码,可能会引发一连串的错误
  • 如果8086CPU可以有一个专门的寄存器来检测会不会栈顶超界就能解决这个问题了,但实际上并没有

3.9 push、pop指令

push、pop指令可以用一种特殊的方式来访问内存空间

push 寄存器        ; 将一个寄存器中的数据入栈
pop 寄存器         ;  出栈,用一个寄存器接收出栈的数据
push 段寄存器      ;  将一个段寄存器中的数据入栈
pop 段寄存器       ;  出栈,用一个段寄存器接收出栈的数据 
push 内存单元      ; 将一个内存字单元处的字入栈
pop 内存单元        ;  出栈,用一个内存字单元接收出栈的数据

问题3.7 将10000H-1000FH 这段空间当作栈,初始状态为栈空,将AX、BX、DS中的数据入栈

mov ax,1000H
mov ss,ax
mov sp,0010H ;注意栈空时SS:SP指向栈顶元素,此时栈顶没元素只能在最下面的内存单元之下
push ax
push bx
push ds

问题3.8 编程实现:

  1. 将10000H-1000FH这段空间当作栈,初始状态为栈空
  2. 设置AX=001AH,BX=001BH;
  3. 将AX、BX中的数据入栈
  4. 然后将AX、BX清零
  5. 从栈中回复AX、BX原来的内容
mov ax,1000H
mov ss,ax
mov sp,0010H

mov ax,001AH
mov bx,001BH

push ax
push bx

mov ax,0    ;sub ax,ax也可以,机器码为两个字节;mov ax,0 机器码为3个字节
mov bx,0

pop bx    ; 注意顺序即可
pop ax

问题3.9 实际就是调换出栈顺序

问题3.10 如果要在10000H处写入字型数据2266H,可以用以下代码完成

mov ax,1000H
mov ds,ax
mov ax,2266H
mov [0],ax

补充下面的代码:

mov ax,1000H
mov ss,ax       ;使用栈的存储方式来存储,注意SS和SP的值,当成此时为空栈SP应该最低内存单元的下一个单元
mov sp,0002H
mov ax,2266H
push ax
  • push、pop实际上就是一种内存传送指令,可以在寄存器与内存间传送数据
  • 与mov指令不同的是,push、pop访问内存单元的地址是SS:SP中给出的,同时还要改变SP中的值

那么我们再对比一下CPU执行mov指令和push、pop指令的过程:

  • CPU执行mov指令就只有一步:传送
  • CPU执行push指令:首先SP=SP-2,然后向SS:SP处传送
  • CPU执行pop指令:先读取SS:SP处的数据,然后再让SP=SP+2

总结一下栈的内容:

  1. 8086CPU中,SS、SP中存放栈顶的段地址和偏移地址
  2. push指令的执行步骤:SP=SP-2;向SS:SP指向的字单元中送入数据
  3. pop指令的执行步骤:向SS:SP指向的字单元中读取数据;SP=SP+2
  4. 任意时刻,SS:SP指向栈顶元素
  5. 8086CPU只记录栈顶,栈空间大小要自己注意
  6. 用栈暂存以后要恢复的寄存器的内容时,寄存器的出栈顺序要和入栈顺序相反
  7. push和pop实际上是一种内存传送指令

3.10 栈段

  • 对于8086PC机,在编程时我们可以根据需要将一组地址连续,起始地址为16的倍数的内存单元当作数据段
  • 那么我们现在同样取长度为N(N<=64KB)的满足上述条件的内存空间当作栈段

问题 3.11 如果将10000H-1FFFFH当作栈段,初始状态为空栈,此时SS=1000H,SP=?

  • 任意时刻,SS:SP指向栈顶元素
  • 8086PC机入栈和出栈的操作都是以字为单元进行的

分析:

  1. 这段栈空间为64KB,最底部的字单元地址为1000:FFFE;假设有最后一个元素出栈,出栈后栈为空,那么用pop语句出栈后SP=SP+2,SP=0

  2. 因为任意时刻SS:SP指向栈顶元素,栈为空的时候栈顶元素不存在,那么应该指向最底部的字单元的下面的字单元,最底部字单元为1000:FFFE,所以空栈的时候SP=0000H

问题3.12 一个栈段最大可以设为多少?为什么?

执行push和pop指令都是修改SP的值来改变指向的内存字单元,SP的值变化范围是多少就代表栈段最大可以设为多少,栈顶的变化范围是:0-FFFFH,栈空SP=0,栈满的时候SP=0,如果再次压栈那么栈顶会环绕覆盖原来的内容,所以一个栈段最大为64KB

  • 对于数据段,我们将段地址放在DS中
  • 对于代码段,我们将段地址放在CS中
  • 对于栈段, 我们将段地址放在SS中

image-20221015170729861

mov ax,1000H
mov ds,ax
mov ax,2000H
mov ss,ax
mov sp,0010H
push [0]
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]
image-20221015172113986
mov,ax 2000H
mov ds,ax
mov ax,1000H
mov ss,ax
mov sp,0000H
pop [E]
pop [C]
pop [A]
pop [8]
pop [6]
pop [4]
pop [2]
pop [0]
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值