3-汇编语言中与内存访问相关的寄存器/操作(DS+数据段+SS/SP+栈相关操作)

一、内存中字的存储

CPU中用16位寄存器来存储一个字。高8位存放高位字节,低8位存放低位字节。在内存中存储时,由于内存单元的大小是字节,则一个字要用两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。比如我们从0地址开始存放20000,如下图所示:
在这里插入图片描述
在上图中,我们用0、1两个内存单元存放数据20000(4E20H),这两个单元可以看作一个起始地址为0的字单元(存放一个字的内存单元,由0、1两个字节单元组成)。对于这个字单元来说,0号单元是低地址单元,1号单元是高地址单元。同理,将2、3号单元看作一个字单元,它的起始地址为2,在这个字单元中存放数据18(0012H,在2号单元中存放低位字节12H,在3号单元中存放高位字节00H)。

所以字单元的概念如下:字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。

我们将起始地址为N的字单元简称为N地址字单元。比如一个字单元由2、3两个内存单元组成,则这个字单元的起始地址为2,我们可以说这是2地址字单元。

二、DS和[address]

CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086CPU中,内存地址由段地址和偏移地址组成。8086CPU中的DS寄存器,通常用来存放要访问数据的段地址。比如我们要读取10000H单元的内容,可以用如下的程序段进行:

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

上面的三条指令将10000H(1000:0)内存单元中的数据读到al中。

在这里mov al,[0]指令将一个内存单元中的内容直接送入寄存器,格式为mov 寄存器名,内存单元地址

[...]表示一个内存单元,[0]中的0表示内存单元的偏移地址。只有偏移地址是不能定位一个内存单元的,指令执行时,8086CPU自动取ds中的值作为内存单元的段地址。

注意,8086CPU不支持将数据直接送入段寄存器的操作,ds是一个段寄存器,所以mov ds,1000H这条指令是非法的,我们需要用一个寄存器来进行中转,即先将1000H送入一个一般的寄存器,如bx,再将bx中的内容送入ds。

mov bx,1000H
mov ds,bx

8086CPU不支持数据直接送入寄存器是因为硬件设计的问题。

三、字的传送

因为8086CPU是16位结构,有16根数据线,所以,可以一次性传送16位的数据,也就是说可以一次性传送一个字。只要在mov指令中给出16位寄存器就可以进行16位数据的传送了。比如:

mov bx,1000H
mov ds,bx
mov ax,[0]
mov [0],cx

四、mov、add、sub指令

mov、add、sub指令,它们都带有两个操作对象。

mov指令可以有以下几种形式:

mov 寄存器,数据			比如:mov ax,8
mov 寄存器,寄存器		比如:mov ax,bx
mov 寄存器,内存单元		比如:mov ax,[0]
mov 寄存器,段寄存器		比如:mov ax,ds
mov 内存单元,寄存器		比如:mov [0],ax
mov 内存单元,段寄存器		比如:mov [0],cs
mov 段寄存器,内存单元		比如:mov ds,[0]
mov 段寄存器,寄存器		比如:mov ds,ax

注意以下这几点:

  • 不能使用mov 内存单元,数据
  • 不能使用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

五、数据段

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。具体来说,将一组长度为N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,就定义了一个数据段。比如用123B0H~123B9H这段内存空间来存放数据,我们就可以认为,123B0H~123B9H这段内存是一个数据段,它的段地址为123BH,长度为10字节。

将一段内存当作数据段,是我们在编程时的一种安排,可以在具体操作的时候,用ds存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。

比如,将123B0H~123B9H的内存单元定义为数据段,现在要累加这个数据段中的前3个单元中的数据,代码如下:

mov ax,123BH
mov ds,ax			;将123BH送入ds中,作为数据段的段地址
mov al,0			;用al存放累加结果
add al,[0]			;将数据段第一个单元(偏移地址为0)中的数值加到al中
add al,[1]			;将数据段第二个单元(偏移地址为1)中的数值加到al中
add al,[2]			;将数据段第三个单元(偏移地址为2)中的数值加到al中

六、CPU提供的栈机制

8086CPU提供相关的指令来以栈的方式访问内存空间,在基于8086CPU编程的时候,可以将一段内存当作栈来使用。

8086CPU提供入栈和出栈指令,最基本的两个是PUSH(入栈)和POP(出栈)。比如,push ax表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax。8086CPU的入栈和出栈操作都是以为单位进行的。

下面是一个代码例子(假设我们将10000H~1000FH这段内存当作栈来使用):

mov ax,0123H
push ax
mov bx,2266H
push bx
mov cx,1122H
push cx
pop ax
pop bx
pop cx

下图阐述了这段代码的执行过程:
在这里插入图片描述
注意,字型数据用两个单元存放,高地址单元存放高8位,低地址单元存放低8位。

我们了解了栈的操作后,还需要解决两个问题:CPU怎么知道某段空间被当作栈来使用,并如何追踪栈顶单元呢?

这两个问题在8086CPU中,是通过段寄存器SS和寄存器SP解决的,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。

现在,我们完整地描述push和pop指令的功能,例如push ax,这由以下两步完成:

  1. SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶
  2. 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶

下图描述了8086CPU对push指令的执行过程:
在这里插入图片描述
从图中我们可以看出,8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

接下来描述下8086CPU对于pop指令的执行过程:

  1. 将SS:SP指向的内存单元处的数据送入ax中
  2. SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶

在这里插入图片描述
在上图中,出栈后,SS:SP指向新的栈顶1000EH,pop操作前的栈顶元素,1000CH处的2266H依然存在,但是,它已不在栈中。当再次执行push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖。

七、栈顶超界的问题

现在还有一个新的问题,SS和SP只是记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶,但是如何保证在入栈、出栈时候不会超出栈空间?

图3.13描述了在执行push指令后,栈顶超出栈空间的情况,图3.14描述了执行pop指令后栈顶超出栈空间的情况:
在这里插入图片描述
在这里插入图片描述
从上面可以看出,当栈满的时候再使用push指令入栈,或栈空的时候再使用pop指令出栈,都将发生栈顶超界的问题。

注意,8086CPU不保证我们对栈的操作不会超界。这就是说,8086CPU只知道栈顶在何处(由SS:SP指示),而不知道我们安排的栈空间有多大。我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;执行出栈的时候也要注意,以防栈空的时候继续出栈而导致的超界。

八、push、pop指令

push和pop指令可以用来在寄存器和内存之间传递数据。

push和pop指令的格式可以是如下形式:

push 寄存器		;将一个寄存器中的数据入栈
pop 寄存器		;出栈,用一个寄存器接收出栈的数据

也可以是如下形式:

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

push和pop也可以在内存单元和内存单元之间传送数据:

push 内存单元		;将一个内存单元处的字入栈(注意:栈操作都是以字为单位)
pop 内存单元			;出栈,用一个内存字单元接收出栈的数据

CPU定位内存单元时,可以在push、pop指令中只给出内存单元的偏移地址,CPU会从ds寄存器中取得段地址。

九、栈段

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将长度为N(N<=64KB)的一组地址连续、起始地址为16的倍数的内存单元,当作栈空间来使用,从而定义了一个栈段。比如,我们将10010H~1001FH这段长度为16字节的内存空间当作栈来用,以栈的方式进行访问。这段空间就可以称为一个栈段,段地址为1001H,大小为16字节。

将一段内存当作栈段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就在执行push、pop等栈操作指令时自动地将我们定义的栈段当作栈空间来访问。所以我们需要将SS:SP指向我们定义的栈段。

十、思考问题

如果将10000H~1FFFFH这段空间当作栈段,初始状态栈是空的,此时,SS=1000H,SP=?

如果将10000H~1FFFFH这段空间当作栈段,SS=1000H,栈空间为64KB,栈最底部的字单元地址为1000:FFFE。任意时刻,SS:SP指向栈顶单元,当栈中只有一个元素的时候,SS=1000H,SP=FFFEH。栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2。

SP原来为FFFEH,加2后SP=0,所以,当栈为空的时候,SS=1000H,SP=0

一个栈段最大可以设为多少?

首先从栈操作指令所完成的功能的角度上来看,push、pop等指令在执行的时候只修改SP,所以对栈顶的变化范围是0~FFFFH,从栈空时候的SP=0,一直压栈,直到栈满时SP=0;如果再次压栈,栈顶将环绕,覆盖了原来栈中的内容。所以一个栈段的容量最大为64KB。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
32位CPU所含有的寄存器有: 4个数据寄存器(EAX、EBX、ECX和EDX) 2个变址和指针寄存器(ESI和EDI) 2个指针寄存器(ESP和EBP) 6个寄存器(ES、CS、SSDS、FS和GS) 1个指令指针寄存器(EIP) 1个标志寄存器(EFlags) 1、数据寄存器 数据寄存器主要用来保存操作数和运算结果等信息,从而节省读取操作数所需占用总线和访问存储器的时间。 32位CPU有4个32位的通用寄存器EAX、EBX、ECX和EDX。 对低16位数据的存取,不会影响高16位的数据。 这些低16位寄存器分别命名为:AX、BX、CX和DX,它和先前的CPU寄存器相一致。 4个16位寄存器又可分割成8个独立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每个寄存器都有自己的名称,可独立存取。 程序员可利用数据寄存器的这种“可分可合”的特性,灵活地处理字/字节的信息。 寄存器EAX通常称为累加器(Accumulator),用累加器进行的操作可能需要更少时间。可用于乘、 除、输入/输出等操作,使用频率很高; 寄存器EBX称为基地址寄存器(Base Register)。它可作为存储器指针来使用; 寄存器ECX称为计数寄存器(Count Register)。 在循环和字符串操作时,要用它来控制循环次数;在位操作,当移多位时,要用CL来指明移位的位数; 寄存器EDX称为数据寄存器(Data Register)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。 在16位CPU,AX、BX、CX和DX不能作为基址和变址寄存器来存放存储单元的地址, 在32位CPU,其32位寄存器EAX、EBX、ECX和EDX不仅可传送数据、暂存数据保存算术逻辑运算结果, 而且也可作为指针寄存器,所以,这些32位寄存器更具有通用性。 2、变址寄存器 32位CPU有2个32位通用寄存器ESI和EDI。 其低16位对应先前CPU的SI和DI,对低16位数据的存取,不影响高16位的数据寄存器ESI、EDI、SI和DI称为变址寄存器(Index Register),它们主要用于存放存储单元在内的偏移量, 用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。 变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。 它们可作一般的存储器指针使用。在字符串操作指令的执行过程,对它们有特定的要求,而且还具有特殊的功能。 3、指针寄存器 其低16位对应先前CPU的BP和SP,对低16位数据的存取,不影响高16位的数据。 32位CPU有2个32位通用寄存器EBP和ESP。 它们主要用于访问堆栈内的存储单元,并且规定: EBP为基指针(Base Pointer)寄存器,用它可直接存取堆栈数据; ESP堆栈指针(Stack Pointer)寄存器,用它只可访问顶。 寄存器EBP、ESP、BP和SP称为指针寄存器(Pointer Register),主要用于存放堆栈内存储单元的偏移量, 用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。 指针寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。 4、寄存器 寄存器是根据内存的管理模式而设置的。内存单元的物理地址寄存器的值和一个偏移量组合而成 的,这样可用两个较少位数的值组合成一个可访问较大物理空间的内存地址。 CPU内部的寄存器: ECS——代码寄存器(Code Segment Register),其值为代码值; EDS——数据寄存器(Data Segment Register),其值为数据值; EES——附加寄存器(Extra Segment Register),其值为附加数据值; ESS——堆栈寄存器(Stack Segment Register),其值为堆栈值; EFS——附加寄存器(Extra Segment Register),其值为附加数据值; EGS——附加寄存器(Extra Segment Register),其值为附加数据值。 ..................................

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值