Newsgroup: it.comp.programmare
Titolo: Lo stack mi sta facendo impazzire

Guardate queste semplici istruzioni per una chiamata a funzione:
pushl   $1
call    func
.......
func:
movl    8(%esp),%eax // voglio accedere al dato "1"
Dopo la pushl lo sp viene decrementato di 4 byte, dopo la call di altri 4.
Tenendo presente che lo stack viene decrementato, ma che il dato si 
trova sotto lo stack(perchč se vale 4 byte va verso indirizzi piů 
grandi) il displacement per accedere ad 1 non dovrebbe essere 4?
Faccio un disegnino con paint, supponiamo che all'inizio lo sp sia 
all'indirizzo 9:
http://digilander.libero.it/maxxam99/Pics/2010-07-11_225928.jpg
Il dato "1" non si trova nei quattro byte che vanno da 5 a 9?
In tal caso il displacement dovrebbe essere 4, perchč il codice di 
sopra(trovato in un testo: 
http://digilander.libero.it/maxxam99/Pics/2010-07-11_230451.jpg
) dice 
che č 8?
-- 
Bonus di 25 euro su 
betfair.it
, codice da inserire: AAAACGWFG
Vai sul mio sito(in fondo alla pagina) per altri dettagli:
http://digilander.libero.it/maxxam99/


Allora, lo stack, a seconda dei sistemi, scende o sale.
Di solito stack e' quello che scende, mentre heap e' quello che sale.
Quando scende, se inizialmente lo stack punta a 6008, e fai un push di
un intero, il processore scrive l'intero in 6008, 6007, 6006, 6005, e
ricolloca lo stack a 6004.
Se invece lo stack fosse a salire, allora metterebbe l'intero in 6008,
6009, 6010, 6011, e poi ricollocherebbe lo stack a 6012.
Il pop fa l'operazione contraria.
La chiamata ad una subroutine equivale a mettere nello stack
l'indirizzo dell'istruzione successiva alla call, ed effettuare un
jump alla subroutine.
Il ritorno da una subroutine equivale a fare un pop dallo stack
dell'indirizzo di ritorno ed un jump allo stesso.
Il programma proseguira' all'istruzione successiva alla call.
In condizioni normali non dovresti preoccuparti di come funziona lo
stack: lo usi e basta.
L'unica accortezza e' quella di verificare che vi sia sufficiente
memoria libera per consentire allo stack di crescere quanto serve.
Un problema tipico che succedeva coi vecchi computer da 64K era quello
che lo stack cresceva fino a ricoprire un'area di dati, corrompendola,
o un'area di codice, con conseguenze catastrofiche.
Oggi, invece, che la memoria si misura in giga e il sistema operativo
la protegge pure, questo problema non esiste piu'.

 o un'area di codice, con conseguenze catastrofiche.
Agghiacciante che si siano potute consentire architetture del genere 
senza nessuna protezione per il codice sovrastante!
Grazie.


 senza nessuna protezione per il codice sovrastante!
E vabbè... il 6502 aveva solo 4000 transistor, lo Z80 il doppio, mentre 
l'8086 ne aveva 29000.
Per me è MOLTO più agghiacciante che l'8086 avesse un bug per cui il 
program counter "saltava" una locazione di memoria ogni 64K di codice, 
se non ricordo male, bug che per mere ragioni di compatibilità la Intel 
ha dovuto mantenere in tutti i processori dopo (80186, 80286, 386... non 
so se pure gli x86 di oggi) :-(

 so se pure gli x86 di oggi) :-(
Cioè siccome i software dovevano tener conto di quel salto erano tutti 
scritti per assecondare il bug, e quindi per farli girare su cpu più 
moderne il bug viene copiato anche nei successivi?
Ma è  straaghicciante! :-))


 Ma è  straaghicciante! :-))
Sara' anche agghiacciante, ma e' del tutto trasparente per quanto
riguarda la programmazione.
Cioe' tu inserisci valori nello stack, e li recuperi normalmente.
Che cosa poi faccia il processore non ti deve riguardare: ti basta
solo sapere che quando metti un valore nello stack, il sistema poi te
lo restituisce tal quale quando vai a recuperarlo (naturalmente
rispettando l'ordine di inserimento).
Se poi tu, per tua curiosita' e cultura, vuoi andare ad approfondire
la logica dei processori per vedere che cosa succede da un punto di
vista fisico sulla memoria, va benissimo, pero' non e' necessario ai
fini della programmazione.
Voglio dire: puoi scrivere in assembly senza sapere come funziona uno
stack da un punto di vista fisico, ma solo da quello logico.
Puoi scrivere in C senza sapere che il compilatore usa lo stack per
passare le variabili alle funzioni.
Puoi scrivere in linguaggi di livello piu' alto senza sapere in che
modo i dati vengono organizzati in memoria.
etc...

 Finalmente svelato l'arcano
testo che leggi confusione che trovi, guardate qui:
Now the parameters will have been pushed onto the stack, then the return
address is pushed by the call, and then the old value of EBP is pushed. This
means that the last parameter pushed now has 8 bytes of data on top of it,
and is at an oﬀset of 8 bytes from the top of the stack, which is the 
new EBP.
By using register indirect with an oﬀset of 8, we can locate the last 
parameter
pushed. The parameter before that, if any, will be at an oﬀset of 8 plus the
number of bytes occupied by the last parameter pushed, and so on. Thus, for
example, to grab the value of the last parameter pushed and store it in EAX,
we write:
movl
8(%ebp), %eax
Oh madonna mia, qui abbiamo due elemeti da 4 byte, e poi il nostro 
parametro, quindi se vale ancora il ragionamento di sopra, li byte più 
significativo di questo dato sarà con un displacement di 12, no?
Altrimenti contraddiciamo quanto discusso prima...


 
questo e' uno step in piu', caratteristico del "supporto dei linguaggi 
ad alto livello del microporocessore"
BP viene salvato (nello stack ) e gli viene caricato il valore dello SP 
  ed opzionalmente lo stack viene incrementato di un valore tale in modo 
da riservare spazio nello stack per le ev. variabili locali della subroutine
si usa per far cio' la seguente sequenza d'istruzioni
PUSH BP
MOV BP, SP
SUB SP, n
oppure ( dal 80186 in su )
ENTER n
dove n e' il numero di byte che si vogliono riservare per le variaibli 
locali
in questa maniera usando i vari modi d'indirizzamento based/indexed e' 
possibile accedere, tramite BP, alle varibili locali della subroutine ed 
ai suoi parametri ( il calcolo della posizione dei parametri o delle 
variabili e' abbastanza semplice ;-) basta tenere conto di cosa hai 
messo nello stack e quali valori assumono i registri interessati ( BP! )
all'uscita della subroutine ( PRIMA del RET ) si ripristina lo stack con 
la sequenza
MOV SP, BP
POP BP
oppure ( dal 80186 in su )
LEAVE
per inciso il 680x0 aveva le istruzioni LINK e ULNK con analogo 
comportamento

 comportamento
Grazie, comunque più o meno mi erano chiare queste cose...il dubbio è 
solo su come viene puntato un dato e le differenze tra big endian e 
little endian, vedi se ho capito bene(probabilmente no)
Consideriamo il dato all'indirizzo 5(tanto per dire un numero):
Se faccio un move  di quattro byte su questo indirizzo succede che:
1)Big endian - Se il dato viene memorizzato all'indirizzo 5,  quattro 
byte del dato(dal più significativo al meno significativo) andranno alle 
posizioni 5-6-7-8
In tal caso quando devo recuperare il dato, punto all'indirizzo 5 e mi 
copio i 4 byte con la ram a salire, quindi sempre nell'ordine 5-6-7-8
2)Little endian - Se il dato viene memorizzato all'indirizzo 5,  quattro 
byte del dato(dal meno significativo al più significativo) andranno alle 
posizioni 5-6-7-8
In tal caso quando devo recuperare il dato, punto all'indirizzo 8 e non 
5, mi copio i 4 byte a scendere sulla ram(come dicevi tu: 8-7-6-5, e 
ricostruisco il dato nel corretto ordine)
Nell'esempio di prima
http://digilander.libero.it/maxxam99/Pics/2010-07-11_225928.jpg
per accedere al dato "1", siccome l'architettura era little endian, si 
copia il dato ai byte 9-8-7-6 , quindi per riferirci all'indirizzo 9, 
facciamo sp+8 e copiamo il nostro dato(che si trova, come già detto ai 
byte 9-8-7-6)
Nel caso dell'ultimo esempio(quello in inglese appena postato) al 
momento di fare la chiamata a funzione abbiamo due parametri sullo 
stack(per semplicità dico che sono solo due), a questo punto viene 
"pushato" l'indirizzo di ritorno(call) e il vecchio ebp.
Ora(tenendo presente il ragionamento di prima sul funzionamento little 
endian) se voglio accedere all'ultimo parametro copiato(siccome non ha 
scostato lo sp con un displacement tipo come fa con link a questo punto, 
da come scrive, sp e ebp puntano entrambi alla cima dello stack) devo 
fare -4-4-4 per puntare all'ndirizzo del byte più significato del primo 
parametro...
insomma a me pare che questo discorso non faccia una grinza..ecco perchè 
mi arrabbio a non capire :-(
Ho espresso  i miei pensieri nella maniera più completa possibile, spero 
solo di non averti annoiato.
Grazie se hai letto fin qui.


 
mi hai ricordato cose che facevo TANTO tempo fa, poi ho levato mano e 
son passato al C anche sui microcontrollori "piccoli"
