微处理器中的堆栈指针有何用途?

发布于 2024-08-06 08:35:55 字数 52 浏览 7 评论 0原文

我正在准备微处理器考试。如果程序计数器的用途是保存下一条指令的地址,那么堆栈指针有什么用?

I am preparing for a microprocessor exam. If the use of a program counter is to hold the address of the next instruction, what is use of stack pointer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

月野兔 2024-08-13 08:35:55

堆栈是一种 LIFO 数据结构(后进先出,这意味着您压入堆栈的最后一个条目是弹出时返回的第一个条目)。它通常用于保存堆栈帧(属于当前函数的堆栈位)。

这可能包括但不限于:

  • 退货地址。
  • 返回值的地方。
  • 传递的参数。
  • 局部变量。

您将项目推入堆栈并弹出它们。在微处理器中,堆栈可用于存储用户数据(例如局部变量和传递的参数)和 CPU 数据(例如调用子例程时的返回地址)。

堆栈的实际实现取决于微处理器架构。它可以在内存中向上或向下增长,并且可以在推入/弹出操作之前或之后移动。

通常影响堆栈的操作有:

  • 子程序调用和返回。
  • 中断调用并返回。
  • 代码显式推送和弹出条目。
  • 直接操作堆栈指针寄存器sp

考虑我的(虚构的)汇编语言中的以下程序:

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

现在让我们跟踪执行过程,描述上面注释中显示的步骤:

  1. 这是起始条件,其中 pc (程序计数器)为0sp8000(所有这些数字都是十六进制)。

  2. 这只是用立即值 7 加载寄存器 r0 并将 pc 移动到下一条指令(我假设您了解除非另有说明,否则默认行为将移至下一条指令)。

  3. 这会将 sp 减少 2,然后将寄存器的值存储到该位置,从而将 r0 压入堆栈。

  4. 这调用了一个子例程。下一步中pc会以与上一步r0类似的方式压入堆栈,然后pc 设置为其新值。这与用户级推送没有什么不同,只是它更多地作为系统级的事情来完成。

  5. 这将从堆栈指针计算出的内存位置加载r1 - 它展示了一种将参数传递给函数的方法。

  6. return 语句从 sp 指向的位置提取值并将其加载到 pc 中,同时调整 sp 。这就像系统级 pop 指令(请参阅下一步)。

  7. r0 弹出堆栈涉及从 sp 当前指向的位置提取值,然后向上调整 sp

  8. halt 指令只是将 pc 留在原处,形成无限循环。

希望从这个描述中,一切都会变得清楚。底线是:堆栈对于以 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  |
     |        |      |        |      |        |
     |        |      |        |      |        |
     |        |      |        |      |        |
     +--------+      +--------+      +--------+

A stack is a LIFO data structure (last in, first out, meaning last entry you push on to the stack is the first one you get back when you pop). It is typically used to hold stack frames (bits of the stack that belong to the current function).

This may include, but is not limited to:

  • the return address.
  • a place for a return value.
  • passed parameters.
  • local variables.

You push items onto the stack and pop them off. In a microprocessor, the stack can be used for both user data (such as local variables and passed parameters) and CPU data (such as return addresses when calling subroutines).

The actual implementation of a stack depends on the microprocessor architecture. It can grow up or down in memory and can move either before or after the push/pop operations.

Operation which typically affect the stack are:

  • subroutine calls and returns.
  • interrupt calls and returns.
  • code explicitly pushing and popping entries.
  • direct manipulation of the stack pointer register, sp.

Consider the following program in my (fictional) assembly language:

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

Now let's follow the execution, describing the steps shown in the comments above:

  1. This is the starting condition where pc (the program counter) is 0 and sp is 8000 (all these numbers are hexadecimal).

  2. This simply loads register r0 with the immediate value 7 and moves pc to the next instruction (I'll assume that you understand the default behavior will be to move to the next instruction unless otherwise specified).

  3. This pushes r0 onto the stack by reducing sp by two then storing the value of the register to that location.

  4. This calls a subroutine. What would have been pc in the next step is pushed on to the stack in a similar fashion to r0 in the previous step, then pc is set to its new value. This is no different to a user-level push other than the fact it's done more as a system-level thing.

  5. This loads r1 from a memory location calculated from the stack pointer - it shows a way to pass parameters to functions.

  6. The return statement extracts the value from where sp points and loads it into pc, adjusting sp up at the same time. This is like a system-level pop instruction (see next step).

  7. Popping r0 off the stack involves extracting the value from where sp currently points, then adjusting sp up.

  8. The halt instruction simply leaves pc where it is, an infinite loop of sorts.

Hopefully from that description, it will become clear. Bottom line is: a stack is useful for storing state in a LIFO way and this is generally ideal for the way most microprocessors do subroutine calls.

Unless you're a SPARC of course, in which case you use a circular buffer for your stack :-)

Update: Just to clarify the steps taken when pushing and popping values in the above example (whether explicitly or by call/return), see the following examples:

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  |
     |        |      |        |      |        |
     |        |      |        |      |        |
     |        |      |        |      |        |
     +--------+      +--------+      +--------+
埋葬我深情 2024-08-13 08:35:55

堆栈指针存储被推入堆栈的最新条目的地址。

要将值压入堆栈,堆栈指针会递增以指向下一个物理内存地址,并将新值复制到内存中的该地址。

要从堆栈中弹出一个值,请从堆栈指针的地址复制该值,然后递减堆栈指针,将其指向堆栈中的下一个可用项。

硬件堆栈最典型的用途是存储子例程调用的返回地址。当子例程执行完毕时,返回地址将从堆栈顶部弹出并放入程序计数器寄存器中,从而使处理器在调用子例程后的下一条指令处恢复执行。

http://en.wikipedia.org/wiki/Stack_%28data_struct%29#Hardware_stacks

The stack pointer stores the address of the most recent entry that was pushed onto the stack.

To push a value onto the stack, the stack pointer is incremented to point to the next physical memory address, and the new value is copied to that address in memory.

To pop a value from the stack, the value is copied from the address of the stack pointer, and the stack pointer is decremented, pointing it to the next available item in the stack.

The most typical use of a hardware stack is to store the return address of a subroutine call. When the subroutine is finished executing, the return address is popped off the top of the stack and placed in the Program Counter register, causing the processor to resume execution at the next instruction following the call to the subroutine.

http://en.wikipedia.org/wiki/Stack_%28data_structure%29#Hardware_stacks

冧九 2024-08-13 08:35:55

你还有更多的准备工作要做;-)

堆栈指针是一个寄存器,它保存堆栈上下一个可用位置的地址。

堆栈是内存中保留用于存储堆栈的区域,堆栈是一种 LIFO(后进先出)类型的容器,我们在其中存储局部变量和返回地址,从而可以简单地管理函数调用的嵌套一个典型的程序。

请参阅这篇维基百科文章,了解堆栈管理的基本说明。

You got more preparing [for the exam] to do ;-)

The Stack Pointer is a register which holds the address of the next available spot on the stack.

The stack is a area in memory which is reserved to store a stack, that is a LIFO (Last In First Out) type of container, where we store the local variables and return address, allowing a simple management of the nesting of function calls in a typical program.

See this Wikipedia article for a basic explanation of the stack management.

愁杀 2024-08-13 08:35:55

对于8085: 堆栈指针是微处理器中一个特殊用途的16位寄存器,它保存堆栈顶部的地址。

计算机中的堆栈指针寄存器可供以低于中断处理程序的特权级别执行的程序用于通用用途。此类程序中的一组指令(不包括堆栈操作)将除堆栈指针之外的数据(例如操作数等)存储在堆栈指针寄存器中。当在中断上将执行切换到中断处理程序时,当前正在执行的程序的返回地址数据被推送到中断处理程序的特权级别的堆栈上。因此,在堆栈指针寄存器中存储其他数据不会导致堆栈损坏。此外,这些指令还可以将数据存储在当前堆栈指针之外的堆栈段的暂存部分中。

阅读这篇文章以获取更多信息。

堆栈指针寄存器的通用用途

For 8085: Stack pointer is a special purpose 16-bit register in the Microprocessor, which holds the address of the top of the stack.

The stack pointer register in a computer is made available for general purpose use by programs executing at lower privilege levels than interrupt handlers. A set of instructions in such programs, excluding stack operations, stores data other than the stack pointer, such as operands, and the like, in the stack pointer register. When switching execution to an interrupt handler on an interrupt, return address data for the currently executing program is pushed onto a stack at the interrupt handler's privilege level. Thus, storing other data in the stack pointer register does not result in stack corruption. Also, these instructions can store data in a scratch portion of a stack segment beyond the current stack pointer.

Read this one for more info.

General purpose use of a stack pointer register

寻找我们的幸福 2024-08-13 08:35:55

堆栈是用于保存临时数据的内存区域。 CALL 指令使用堆栈来保存过程的返回地址。返回 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。

The Stack is an area of memory for keeping temporary data. Stack is used by the CALL instruction to keep the return address for procedures The return RET instruction gets this value from the stack and returns to that offset. The same thing happens when an INT instruction calls an interrupt. It stores in the Stack the flag register, code segment and offset. The IRET instruction is used to return from interrupt call.

The Stack is a Last In First Out (LIFO) memory. Data is placed onto the Stack with a PUSH instruction and removed with a POP instruction. The Stack memory is maintained by two registers: the Stack Pointer (SP) and the Stack Segment (SS) register. When a word of data is PUSHED onto the stack the the High order 8-bit Byte is placed in location SP-1 and the Low 8-bit Byte is placed in location SP-2. The SP is then decremented by 2. The SP addds to the (SS x 10H) register, to form the physical stack memory address. The reverse sequence occurs when data is POPPED from the Stack. When a word of data is POPPED from the stack the the High order 8-bit Byte is obtained in location SP-1 and the Low 8-bit Byte is obtained in location SP-2. The SP is then incremented by 2.

深者入戏 2024-08-13 08:35:55

堆栈指针保存堆栈顶部的地址。堆栈允许函数将存储在堆栈上的参数相互传递,并创建作用域变量。此上下文中的范围意味着当堆栈帧消失和/或函数返回时,变量将从堆栈中弹出。如果没有堆栈,您将需要对所有内容使用显式内存地址。这将使得为该架构设计高级编程语言变得不可能(或者至少非常困难)。
此外,每种 CPU 模式通常都有自己的存储堆栈指针。因此,当发生异常(例如中断)时,异常处理程序例程可以使用自己的堆栈,而不会破坏用户进程。

The stack pointer holds the address to the top of the stack. A stack allows functions to pass arguments stored on the stack to each other, and to create scoped variables. Scope in this context means that the variable is popped of the stack when the stack frame is gone, and/or when the function returns. Without a stack, you would need to use explicit memory addresses for everything. That would make it impossible (or at least severely difficult) to design high-level programming languages for the architecture.
Also, each CPU mode usually have its own banked stack pointer. So when exceptions occur (interrupts for example), the exception handler routine can use its own stack without corrupting the user process.

酷炫老祖宗 2024-08-13 08:35:55

如果您渴望更深入的了解,我衷心推荐 Patterson 和 Hennessy 作为介绍和 Hennessy 和 Patterson 作为中级到高级文本。它们价格昂贵,但确实无与伦比;我只是希望当我获得硕士学位并进入为他们设计芯片、系统和部分系统软件的工作队伍时,其中之一或两者都可用(但是,唉!,那是太久以前的事了;-)。堆栈指针是如此重要(微处理器和任何其他类型的 CPU 之间的区别在这种情况下非常有意义……或者就此而言,在任何其他情况下,在过去的几十年里……!-)我怀疑除了一些彻底的从头开始的复习能有所帮助之外,还有什么可以帮助的!-)

Should you ever crave deeper understanding, I heartily recommend Patterson and Hennessy as an intro and Hennessy and Patterson as an intermediate to advanced text. They're pricey, but truly non-pareil; I just wish either or both were available when I got my Masters' degree and entered the workforce designing chips, systems, and parts of system software for them (but, alas!, that was WAY too long ago;-). Stack pointers are so crucial (and the distinction between a microprocessor and any other kind of CPU so utterly meaningful in this context... or, for that matter, in ANY other context, in the last few decades...!-) that I doubt anything but a couple of thorough from-the-ground-up refreshers can help!-)

爱你是孤单的心事 2024-08-13 08:35:55

在某些 CPU 上,有一组专用于堆栈的寄存器。当执行调用指令时,一个寄存器会加载程序计数器,同时第二个寄存器会加载第一个寄存器的内容,第三个寄存器会加载第二个寄存器,第四个寄存器会加载第三个寄存器,依此类推当执行返回指令时,程序计数器将锁存第一个堆栈寄存器的内容,并同时锁存第二个堆栈寄存器的内容;第二个寄存器是从第三个寄存器加载的,等等。请注意,此类硬件堆栈往往相当小(例如,许多较小的 PIC 系列微控制器具有两级堆栈)。

虽然硬件堆栈确实有​​一些优点(例如,推入和弹出不会增加调用/返回的时间),但拥有可以加载两个源的寄存器会增加成本。如果堆栈变得很大,用可寻址存储器替换推挽寄存器会更便宜。即使为此使用小型专用存储器,拥有 32 个可寻址寄存器和一个具有递增/递减逻辑的 5 位指针寄存器也比拥有 32 个寄存器(每个寄存器有两个输入)更便宜。如果应用程序可能需要更多的堆栈,而 CPU 无法轻松容纳,则可以使用堆栈指针以及逻辑来从主 RAM 存储/获取堆栈数据。

On some CPUs, there is a dedicated set of registers for the stack. When a call instruction is executed, one register is loaded with the program counter at the same time as a second register is loaded with the contents of the first, a third register is be loaded with the second, and a fourth with the third, etc. When a return instruction is executed, the program counter is latched with the contents of the first stack register and the same time as that register is latched from the second; that second register is loaded from a third, etc. Note that such hardware stacks tend to be rather small (many the smaller PIC series micros, for example, have a two-level stack).

While a hardware stack does have some advantages (push and pop don't add any time to a call/return, for example) having registers which can be loaded with two sources adds cost. If the stack gets very big, it will be cheaper to replace the push-pull registers with an addressable memory. Even if a small dedicated memory is used for this, it's cheaper to have 32 addressable registers and a 5-bit pointer register with increment/decrement logic, than it is to have 32 registers each with two inputs. If an application might need more stack than would easily fit on the CPU, it's possible to use a stack pointer along with logic to store/fetch stack data from main RAM.

雪化雨蝶 2024-08-13 08:35:55

堆栈指针是一个存储堆栈顶部地址的小寄存器。它的用途是指向栈顶地址。

A stack pointer is a small register that stores the address of the top of stack. It is used for the purpose of pointing address of the top of the stack.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文