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 下完成)。