【发布时间】:2010-11-30 15:29:46
【问题描述】:
我正在准备微处理器考试。如果程序计数器的用途是保存下一条指令的地址,那么堆栈指针有什么用呢?
【问题讨论】:
标签: stack microprocessors
我正在准备微处理器考试。如果程序计数器的用途是保存下一条指令的地址,那么堆栈指针有什么用呢?
【问题讨论】:
标签: stack microprocessors
堆栈是一种 LIFO(后进先出 - 压入堆栈的最后一个条目是弹出时返回的第一个条目)数据结构,通常用于保存堆栈帧(堆栈的位)属于当前函数)。
这包括但不限于:
您将项目压入堆栈并弹出它们。在微处理器中,堆栈既可以用于用户数据(例如局部变量和传递的参数)也可以用于 CPU 数据(例如调用子程序时的返回地址)。
堆栈的实际实现取决于微处理器架构。它可以在内存中向上或向下增长,并且可以在 push/pop 操作之前或之后移动。
通常会影响堆栈的操作是:
考虑一下我的(虚构的)汇编语言中的以下程序:
Addr Opcodes Instructions ; Comments
---- -------- -------------- ----------
; 1: pc<-0000, sp<-8000
0000 01 00 07 load r0,7 ; 2: pc<-0003, r0<-7
0003 02 00 push r0 ; 3: pc<-0005, sp<-7ffe, (sp:7ffe)<-0007
0005 03 00 00 call 000b ; 4: pc<-000b, sp<-7ffc, (sp:7ffc)<-0008
0008 04 00 pop r0 ; 7: pc<-000a, r0<-(sp:7ffe[0007]), sp<-8000
000a 05 halt ; 8: pc<-000a
000b 06 01 02 load r1,[sp+2] ; 5: pc<-000e, r1<-(sp+2:7ffe[0007])
000e 07 ret ; 6: pc<-(sp:7ffc[0008]), sp<-7ffe
现在让我们跟随执行,描述上面 cmets 中显示的步骤:
希望从那个描述中,它会变得清晰。底线是:堆栈对于以 LIFO 方式存储状态很有用,这通常是大多数微处理器执行子程序调用的理想方式。
当然,除非您是 SPARC,在这种情况下,您可以为堆栈使用循环缓冲区 :-)
更新: 只是为了阐明在上述示例中推送和弹出值时所采取的步骤(无论是显式还是通过调用/返回),请参见以下示例:
LOAD R0,7
PUSH R0
Adjust sp Store val
sp-> +--------+ +--------+ +--------+
| xxxx | sp->| xxxx | sp->| 0007 |
| | | | | |
| | | | | |
| | | | | |
+--------+ +--------+ +--------+
POP R0
Get value Adjust sp
+--------+ +--------+ sp->+--------+
sp-> | 0007 | sp->| 0007 | | 0007 |
| | | | | |
| | | | | |
| | | | | |
+--------+ +--------+ +--------+
【讨论】:
堆栈指针存储最近被压入堆栈的条目的地址。
要将值压入堆栈,堆栈指针会递增以指向下一个物理内存地址,并将新值复制到内存中的该地址。
要从堆栈中弹出一个值,从堆栈指针的地址复制该值,并将堆栈指针递减,指向堆栈中的下一个可用项。
硬件堆栈最典型的用途是存储子程序调用的返回地址。当子程序执行完毕后,返回地址从栈顶弹出并放入程序计数器寄存器中,使处理器在子程序调用之后的下一条指令处恢复执行。
http://en.wikipedia.org/wiki/Stack_%28data_structure%29#Hardware_stacks
【讨论】:
你有更多[为考试]做准备;-)
堆栈指针是一个寄存器,它保存堆栈上下一个可用点的地址。
栈是内存中的一块区域,保留用于存储栈,这是一种 LIFO(后进先出)类型的容器,我们在其中存储局部变量和返回地址,从而可以对嵌套的嵌套进行简单的管理典型程序中的函数调用。
有关堆栈管理的基本说明,请参阅Wikipedia article。
【讨论】:
对于 8085:堆栈指针是微处理器中一个特殊用途的 16 位寄存器,它保存堆栈顶部的地址。
计算机中的堆栈指针寄存器可供以低于中断处理程序的特权级别执行的程序进行通用使用。此类程序中的一组指令(不包括堆栈操作)将堆栈指针以外的数据(例如操作数等)存储在堆栈指针寄存器中。当在中断上将执行切换到中断处理程序时,当前执行程序的返回地址数据被压入中断处理程序特权级别的堆栈中。因此,将其他数据存储在堆栈指针寄存器中不会导致堆栈损坏。此外,这些指令可以将数据存储在当前堆栈指针之外的堆栈段的临时部分中。
阅读这篇文章了解更多信息。
【讨论】:
堆栈是用于保存临时数据的内存区域。 CALL 指令使用堆栈来保存过程的返回地址。 return RET 指令从堆栈中获取该值并返回到该偏移量。当 INT 指令调用中断时也会发生同样的情况。它在堆栈中存储标志寄存器、代码段和偏移量。 IRET 指令用于从中断调用返回。
堆栈是后进先出 (LIFO) 存储器。数据通过 PUSH 指令放入堆栈,并通过 POP 指令删除。堆栈存储器由两个寄存器维护:堆栈指针 (SP) 和堆栈段 (SS) 寄存器。当一个数据字被压入堆栈时,高 8 位字节被放置在位置 SP-1 中,低 8 位字节被放置在位置 SP-2 中。然后 SP 减 2。SP 添加到 (SS x 10H) 寄存器,以形成物理堆栈内存地址。当数据从堆栈中弹出时,会发生相反的顺序。当从堆栈中弹出一个数据字时,在位置 SP-1 中获得高位 8 位字节,在位置 SP-2 中获得低位 8 位字节。然后 SP 增加 2。
【讨论】:
堆栈指针保存堆栈顶部的地址。堆栈允许函数将存储在堆栈中的参数相互传递,并创建作用域变量。此上下文中的范围意味着当堆栈帧消失和/或函数返回时,变量从堆栈中弹出。如果没有堆栈,您将需要为所有内容使用显式内存地址。这将使为架构设计高级编程语言变得不可能(或至少非常困难)。 此外,每种 CPU 模式通常都有自己的存储堆栈指针。因此,当发生异常(例如中断)时,异常处理程序例程可以使用自己的堆栈,而不会破坏用户进程。
【讨论】:
如果您渴望更深入的了解,我衷心推荐Patterson and Hennessy 作为介绍,Hennessy and Patterson 作为中高级文本。它们很贵,但确实是无与伦比的;我只是希望当我获得硕士学位并进入为他们设计芯片、系统和系统软件部分的工作时,其中一个或两个都可用(但是,唉!那是太久以前的事了;-)。堆栈指针是如此重要(微处理器和任何其他类型的 CPU 之间的区别在这种情况下非常有意义......或者,就此而言,在任何其他情况下,在过去的几十年里......!-)我怀疑除了一些彻底的从头开始的复习会有所帮助!-)
【讨论】:
在某些 CPU 上,有一组专用的堆栈寄存器。当执行调用指令时,一个寄存器加载程序计数器,同时第二个寄存器加载第一个寄存器的内容,第三个寄存器加载第二个,第四个加载第三个,等等. 当执行返回指令时,程序计数器被第一个堆栈寄存器的内容锁存,同时该寄存器从第二个寄存器被锁存;第二个寄存器是从第三个寄存器加载的,依此类推。请注意,此类硬件堆栈往往相当小(例如,许多较小的 PIC 系列微控制器具有两级堆栈)。
虽然硬件堆栈确实具有一些优势(例如,推送和弹出不会为调用/返回增加任何时间)但具有可以加载两个源的寄存器会增加成本。如果堆栈变得非常大,用可寻址存储器替换推挽寄存器会更便宜。即使为此使用了一个小的专用存储器,拥有 32 个可寻址寄存器和一个具有递增/递减逻辑的 5 位指针寄存器也比拥有 32 个寄存器每个都有两个输入更便宜。如果应用程序可能需要比 CPU 容易容纳的更多堆栈,则可以使用堆栈指针以及逻辑从主 RAM 存储/获取堆栈数据。
【讨论】:
栈指针是一个存储栈顶地址的小寄存器。用于指向栈顶地址。
【讨论】: