Assembly Language
The Stack
Ricerca…
Osservazioni
La pila di computer è come una pila di libri. PUSH ne aggiunge uno in cima e POP prende il primo. Come nella vita reale, lo stack non può essere infinito, quindi ha la dimensione massima. Lo stack può essere utilizzato per algoritmi di ordinamento, per gestire una maggiore quantità di dati o valori sicuri dei registri mentre si esegue un'altra operazione.
Zilog Z80 Stack
Lo sp
registro viene usato come puntatore dello stack , puntando all'ultimo valore memorizzato nello stack ("top" dello stack). Quindi EX (sp),hl
si scambieranno il valore di hl
con il valore in cima allo stack.
Contrariamente alla parola "top", lo stack cresce in memoria diminuendo la sp
e rilascia valori ("pop") aumentando la sp
.
Per sp
= $4844
con i valori 1
, 2
, 3
memorizzati nello stack (il 3
viene inserito nello stack come ultimo valore, quindi in cima ad esso), la memoria sarà simile a questa:
| 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
Esempi, come funzionano le istruzioni con lo stack:
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
Riepilogo : PUSH
memorizzerà il valore in cima allo stack, POP
recupererà il valore dall'alto dello stack, è una coda LIFO (last in, first out). CALL
è uguale a JP
, ma spinge anche l'indirizzo dell'istruzione successiva dopo CALL
in cima allo stack. RET
è simile a JP
, fa scoppiare l'indirizzo dallo stack e saltarci sopra.
Attenzione : quando gli interrupt sono abilitati, lo sp
deve essere valido durante il segnale di interruzione, con spazio libero sufficiente riservato alla routine del gestore di interrupt, poiché il segnale di interruzione memorizzerà l'indirizzo di ritorno ( pc
effettivo) prima di chiamare la routine del gestore, che può memorizzare ulteriori dati su impilare pure. Qualsiasi valore precedente a sp
può quindi essere modificato "in modo imprevisto", se si verifica un'interruzione.
Trucco avanzato : su Z80 con PUSH
con 11 cicli di clock (11t) e POP
con 10t, POP
/ PUSH
srotolato su tutti i registri, incluso EXX
per le varianti shadow, era il modo più veloce per copiare blocchi di memoria, anche più veloce di LDI
srotolato. Ma bisognava far passare la copia tra i segnali di interruzione per evitare il danneggiamento della memoria. Anche PUSH
srotolato è stato il modo più veloce per riempire la memoria con un particolare valore su ZX Spectrum (di nuovo con il rischio di corruzione da Interrupt, se non correttamente programmato, o eseguito sotto DI
).