• 汇编寄存器(内存访问)基础知识之四----栈


    1:栈是一种先进后出的操作

      栈(比喻:碟盘子)。

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

      也就是说:在基于8086cpu编程 的时候,可以将一段内存当作栈来使用

    2:入栈和出栈指令:
      PUSH 入栈
      POP 出栈
    例:
      push ax : 将寄存器ax中的数据送入栈中
      pop ax: 从栈顶取出数据送入ax

    8086cpu的入栈和出栈操作以 为单元

    3:8086cpu的栈操作:   

         

    出栈操作pop ax ,ax里面的值变为1122,此时,1000AH和1000BH里的数据还存在的,只是栈顶指针移动了,里面的数据下一次push操作的时候将会覆盖它。

    出栈操作pop bx ,bx里面的值变为2266,

    出栈操作pop cx ,cx里面的值变为0123,

    4:

    困惑?

    1:cpu怎么栈顶一段内存空间被当做栈使用?

    2:执行push和pop的时候,如何知道那个单元是栈顶单元?

    根据前面所学知道,cpu怎么知道当前要执行的指令的位置?答: 寄存器CS和IP中存放当前指令的段地址和偏移地址

    还有  DS:偏移地址  ----指向---> 数据

    所以栈也有段寄存器SS和寄存器SP.

      堆栈段寄存器 SS(Stack Segment) :存放栈顶的段地址

      寄存器SP:    存放栈顶的偏移地址

    任意时刻,SS:SP指向栈顶元素

    当执行push和pop的时候,栈顶单元改变,cpu怎么知道当前的栈顶单元?

    例如:push ax(先偏移地址减-2,后放数据)

      (1)SP=SP-2

      (2)将ax中的内容送入SS:SP指向的内存单元,SS:SP此时是新栈顶

    pop ax  则是相反,先取出ss:sp指向的内存单元的数据放到ax里面,然后SP+2;

    任意时刻,SS:SP指向栈顶元素,当栈位空的时候,栈中没有元素,也就不存在栈顶元素

    所以,SS:SP只能指向栈的最底部单元下面的单元,此单元的偏移地址为栈最底部的字单元的偏移地址+2

    例如:栈最底部字单元的地址为1000:000E,所以栈空时,SP=0010H.

    5:栈顶越界的问题:

      当栈满的时候在push指令入栈,或者栈空的时候在使用pop指令出栈,都将发生栈顶超界的问题,栈顶超界是危险的

    所以,在编程的时候要小心操作栈顶超界的问题,要根据可能用到的最大栈空间,安排栈的大小,防止栈数据太多导致的越界;

     堆(heap):是由程序员分配的,是零碎的内存,堆最后一定要释放内存。

    栈与内存:

      栈空间是内存空间的一部分,它只是一段可以特殊(先进后出FILO(First-In/Last-Out))方式进行访问的内存空间

    6: push和pop指令

    push和pop指令可以在寄存器和内存直接传送数据的。

     push和pop指令的格式(1)

      push  寄存器:将一个寄存器中的数据入栈     例如:push ax

      pop  寄存器:出栈,用一个寄存器接受出栈的数据   例如:pop bx

     push和pop指令的格式(2)

      push  段寄存器:将一个段寄存器中的数据入栈   例如: push ds

        pop   段寄存器:出栈,用一个段寄存器接受出栈的数据 例如:pop es  (ES:附加段寄存器)

     push和pop指令的格式(3)

      push  内存单元:将一个内存单元的字入栈 (栈操作都是以字为单元).   例如:push [0] (也就是 push ds:0)

        pop   内存单元:出栈,用一个内存单元接受出栈的数据.     例如:pop [2]       (也就是 pop ds:2)

    执行指令时,cpu要知道内存单元的地址,可以在push,pop指令中给出内存单元的偏移地址,段地址在指令执行时,cpu从ds中取得.

    7:问题:如果要在10000H处写入字型数据2266H,可以用下面的代码实现:

      mov ax,1000H

      mov ds,ax    //把内存单元的地址通过ax中介放到段地址中

      mov  ax,2266H   

      mov [0],ax   //把寄存器的数据 放入内存单元(1000:0)中

      还可以用下面的代码完成这个功能,不能使用"mov 内存单元,寄存器 ’这类指令

      mov ax,1000H

      mov ss,ax       //栈的寄存器段地址是1000H

      mov sp,2      //栈的栈顶偏移量要在0的下面,就是高,因为push入栈,先是sp=sp-2,然后在放入数据到栈中,ss:sp指向栈顶元素

        mov ax,2266H

        push ax

    结论:

    push,pop实质上是一种内存传送指令,可以在寄存器和内存直接传送数据,

    与mov指令不同的是,push和mov指令访问的内存的单元地址不是在指令中给出的,而是由SS:SPd指出的。

    同时,push和pop还要改变sp中内容.

    执行push时,先改变sp=sp-2,后向SS:SP处传送

    执行pop, 先读取SS:SP处的数据,后改变SP=sp+2

    8注意:

    (1)栈的变化范围最大为:0~FFFFH(FFFFH=65535(D),栈的操作是16位模式,2^16=64KB)

    (2)SS:SP指向栈顶,改变SP(sp=sp-2)后写入内存 是入栈指令 ;读取内存后改变SP(sp=sp+2) 是出栈指令

    9栈的小结:

    (1)8086cpu提供了栈操作机制:

      在SS:SP存放栈顶的段地址和偏移地址;

      提供入栈和出栈指令,根据SS:IP指向的地址,按照栈的方式访问内存单元

    (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实质上是一种内存传送指令

    10: 栈段

      前面讲过,对于8086cpu,在编程时,可以根据需要,在一组内存单元中定义一个段

      所以,我们可以将长度为N(N<=64KB)的一组地址连续,起始地址为16的倍数的内存单元,当做栈的空间使用,从而定义一个栈段。

    cpu如何把我们定义的栈段当做栈空间来访问呢?   就要指定 SS:SP指向我们定义的栈段.

  • 相关阅读:
    Module build failed: TypeError: this.getResolve is not a function 安装node-sass运行报错
    RMAN Catalog 和 Nocatalog 的区别
    oracle 闪回区故障
    mysql windows 安装 错误
    git rejected
    oracle 计算机改名后监听无法启动
    mysql GTID
    java程序员技术范围
    activiti
    spring mybatis mysql 事务不起作用
  • 原文地址:https://www.cnblogs.com/DonAndy/p/6091043.html
Copyright © 2020-2023  润新知