Zilog Z80 Stack

寄存器 sp 用作堆栈指针,指向最后存储的值到堆栈(堆栈的顶部)。所以 EX (sp),hl 会将 hl 的值与堆栈顶部的值进行交换。

顶部字相反,堆栈通过减少 sp 在内存中增长,并通过增加 sp 来释放(pops)值。

对于 sp = $4844,其值为 123 存储在堆栈中(3 作为最后一个值被压入堆栈,因此位于其顶部),内存将如下所示:

|    address | value bytes | comment (btw, all numbers are in hexadecimal)
| ---------- | ----------- | ---------------------------------
|       4840 | ?? ??       | free stack spaces to be used by next push/call
|       4842 | ?? ??       | or by interrupt call! (don't expect values to stay here)
| sp -> 4844 | 03 00       | 16 bit value "3" on top of stack
|       4846 | 02 00       | 16 bit value "2"
|       4848 | 01 00       | 16 bit value "1"
|       484A | ?? ??       | Other values in stack (up to it's origin)
|       484C | ?? ??       | like for example return address for RET instruction

示例,指令如何使用堆栈:

    LD   hl,$0506
    EX   (sp),hl           ; $0003 into hl, "06 05" bytes at $4844
    POP  bc                ; like: LD c,(sp); INC sp; LD b,(sp); INC sp
                           ; so bc is now $0506, and sp is $4846
    XOR  a                 ; a = 0, sets zero and parity flags
    PUSH af                ; like: DEC sp; LD (sp),a; DEC sp; LD (sp),f
                           ; so at $4844 is $0044 (44 = z+p flags), sp is $4844
    CALL $8000             ; sp is $4842, with address of next ins at top of stack
                           ; pc = $8000 (jumping to sub-routine)
                           ; after RET will return here, the sp will be $4844 again
    LD   (L1+1),sp         ; stores current sp into LD sp,nn instruction (self modification)
    DEC  sp                ; sp is $4843
L1  LD   sp,$1234          ; restores sp to $4844 ($1234 was modified)
    POP  de                ; de = $0044, sp = $4846
    POP  ix                ; ix = $0002, sp = $4848
    ...

    ...
    ORG  $8000
    RET                    ; LD pc,(sp); INC sp; INC sp
                           ; jumps to address at top of stack, "returning" to caller

总结PUSH 将值存储在堆栈顶部,POP 将从堆栈顶部获取值,它是一个 LIFO后进先出)队列。CALLJP 相同,但它也会在 CALL 之后在堆栈顶部推送下一条指令的地址。RET 也类似于 JP,从堆栈中弹出地址并跳转到它。

警告 :当中断使能时,sp 必须在中断信号期间有效,并为中断处理程序例程保留足够的可用空间,因为中断信号将在调用处理程序例程之前存储返回地址(实际 pc),这可能会存储更多数据。堆栈也是。如果发生中断,则可以意外地修改 sp 之前的任何值。

高级技巧 :在 Z80 上,PUSH 需要 11 个时钟周期(11t),POP 需要 10t,展开的 POP / PUSH 通过所有寄存器,包括用于阴影变体的 EXX,是复制内存块的最快方法,甚至比展开的 LDI 更快。但是你必须在中断信号之间计时,以避免内存损坏。同时展开的 PUSH 是在 ZX Spectrum 上填充特定值的内存的最快方式(再次考虑到中断损坏的风险,如果没有正确计时,或者在 DI 下完成)。