Zilog Z80 Stack
暫存器 sp
用作堆疊指標,指向最後儲存的值到堆疊(堆疊的頂部)。所以 EX (sp),hl
會將 hl
的值與堆疊頂部的值進行交換。
與頂部字相反,堆疊通過減少 sp
在記憶體中增長,並通過增加 sp
來釋放(pops
)值。
對於 sp
= $4844
,其值為 1
,2
,3
儲存在堆疊中(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 ( 後進先出)佇列。CALL
與 JP
相同,但它也會在 CALL
之後在堆疊頂部推送下一條指令的地址。RET
也類似於 JP
,從堆疊中彈出地址並跳轉到它。
警告 :當中斷使能時,sp
必須在中斷訊號期間有效,併為中斷處理程式例程保留足夠的可用空間,因為中斷訊號將在呼叫處理程式例程之前儲存返回地址(實際 pc
),這可能會儲存更多資料。堆疊也是。如果發生中斷,則可以意外地修改 sp
之前的任何值。
高階技巧 :在 Z80 上,PUSH
需要 11 個時鐘週期(11t),POP
需要 10t,展開的 POP
/ PUSH
通過所有暫存器,包括用於陰影變體的 EXX
,是複製記憶體塊的最快方法,甚至比展開的 LDI
更快。但是你必須在中斷訊號之間計時,以避免記憶體損壞。同時展開的 PUSH
是在 ZX Spectrum 上填充特定值的記憶體的最快方式(再次考慮到中斷損壞的風險,如果沒有正確計時,或者在 DI
下完成)。