Dieses Thema hätte man auch gleich am Anfang angehen können; man hätte können.
Vielleicht schadet es aber auch nicht, daß nach den bisherigen Teilen evtl. schon ein wenig Verständnis für das Thema Zahlen, Register, Adressen und v.a. Hoch- und Runterzählen vorhanden ist. Es ist nämlich ziemlich theoretisch und erfordert ein bißchen Vorstellungskraft.
Die Überschrift ist übrigens doppeldeutig gemeint. Ein Stack hat eine herausragende Eigenschaft - er ist endlich !
Das Häßliche dabei ist nur, daß man oft nicht genau weiß, wie endlich, weil man es i.a. einfach drauf ankommen läßt und davon ausgeht, daß noch genug Platz darin vorhanden ist. Und wenn doch nicht - crasht die Maschine.
Stack - Stapelspeicher - Kellerspeicher , das meint alles das Gleiche.
Es ist letztlich einfach ein reserviertes Stück Speicher. In dieses kann man hineinschreiben oder daraus lesen, wie bei jeder normalen RAM Adresse sonst auch. Aber beim Stack gibt es eine Besonderheit: er wird verwaltet !
Das bedeutet, daß eine besondere Struktur exisitert, die genau weiß, an welcher Stelle der Lese-/Schreibzugriff erfolgen darf. Das ist der Stackpointer.
Man kann sich den Stackpointer wie eine Grenze oder Schwelle vorstellen, auf einer Seite davon befinden sich bereits gespeicherte Daten, auf der anderen Seite davon liegt freies RAM.
Möchte man etwas in den Stack schreiben, dann teilt man das mit einem Befehl mit. Nun wird der neue Inhalt in die freie RAM Stelle geschrieben, die durch den Stackpointer angezeigt wird. Sofort anschließend wird der Stackpointer so verändert, daß er wieder auf eine freie Position zeigt. Er wird also um eine Position in den Bereich des freien RAMs weitergerückt. Die "Grenze" ändert ihre Position und das freie RAM ist um einen Speicherplatz kleiner geworden.
Das schöne dabei - das passiert automatisch. Man muß also nur mitteilen: Bitte Speichern!, an welcher Stelle das genau geschieht, darum kümmert sich der Stack quasi selbst.
Beim Laden ist der Ablauf ähnlich - nur halt genau andersherum. Der Stackpointer wird auf die direkt neben ihm liegende Stelle gesetzt, von der bekannt ist, daß in dieser Richtung die bereits gespeicherten Daten liegen. Dann wird von dort der Inhalt gelesen. Anschließend steht der Inhalt zwar meist noch dort im RAM drin (es sei denn der Wert wird noch zusätzlich gelöscht), aber weil der Stackpointer ja nun auf diese Adresse zeigt, ist sie nun als "frei" bzw. "wieder benutzbar" markiert.
Wenn man etwas in den Stack hineinschreibt, verschiebt sich die Grenze in die eine Richtung, beim Lesen in die andere.
Problematisch sind nun die äußeren Begrenzungen des Bereiches, den der Stackpointer verwaltet. Wenn der Stapelspeicher bereits komplett gefüllt ist, müßte der Stackpointer ja eigentlich am Platz verharren und einen Fehler mitteilen - nämlich, daß alles voll ist. Zumindest im Homecomputerbereich passiert das aber meist nicht. Stattdessen wird der Stackpointer auf die nächstmögliche Stelle gesetzt und das ist dann der Wert, wo er eigentlich ansagt, daß der gesamte Stack frei ist.
Das ist genauso wie wenn ein Register die $00 nach unten überschreitet und dann im nächsten Schritt (dekrementieren) bei $FF landet.
In die andere Richtung an der oberen Grenze zeigt der Stackpointer z.B. auf die $FF und wird bei der nächsten Erhöhung auf $00 gesetzt, womit auch wieder die komplette Verwaltung in sich zusammenbricht, weil nun der eigentlich freie Speicher zum bereits gefüllten erklärt worden ist.
(Das ließe sich natürlich auch vorher abfragen und ausschließen, aber das wird i.a. nicht gemacht. Wenn es passiert gibt das allerschönsten Datenmüll, insbesondere wenn man z.B. zweimal über den kompletten Bereich Daten hineinschreibt. Man findet in dem Fall nämlich nur noch die zweite Hälfte wieder.)
Trotzdem ist es ein schöner angenehmer Zwischenspeicher, gerade weil man sich beim Speichern und Laden wenig Gedanken machen muß.
Eine Sache muß man trotzdem beachten: Es wird ja immer der Wert genau an die Stelle des Stackpointers geschrieben, und der rückt danach eins weiter. Beim Lesen geht das andersherum - dies bedeutet aber, daß dieser letzte Wert als erster wiedergefunden wird.
Das ist bei Einzelwerten völlig unproblematisch. Die legt man ab und holt sie wieder.
Aber (!) : Wenn man mehrere Werte hintereinander ablegt, die logisch irgendwie zusammengehören, dann wird der zuletzt abgelegte als erster wieder herausgeholt. Anschließend der, der direkt vor ihm gespeichert wurde und erst ganz zum Schluß der erste Wert aus dieser Reihe.
Dafür hat sich jemand den schönen Namen LIFO ausgedacht - Last IN First OUT !
(auf deutsch: Die Ersten werden die Letzten sein. (na ja, zumindest so ähnlich) )
Von dieser Eigenschaft hat er auch seinen Namen - Stapel.
Wie bei einem Stapel Hölzer oder Papier oder Bleiplatten, kann man ein weiteres Objekt oben auflegen, aber man kann die Einzelobjekte ohne weiteren Aufwand (wie Umdrehen des gesamten Stapels) nur in umgekehrter Reihenfolge dort einzeln wieder herunternehmen, um ihn abzubauen.
Es ist übrigens gar nicht gesagt, daß ein Stapel immer nach oben wachsen muß. Im RAM ist das nämlich sogar eigentlich ziemlich egal - es geht ja um Information und nicht um Bleiplatten. Oft wird daher sogar bei einer hohen Adresse begonnen und dann der Stackpointer zu kleineren Adressen bewegt, wenn etwas gespeichert wird. Dementsprechend wird dabei der Stackpointer selbst kleiner. Er wird in so einem Fall einfach zur untersten zulässigen Adresse addiert und diese Ergebnisadresse ist halt die erste freie (und oberhalb des Stackpointers liegen dann die bereits gespeicherten Daten).
Beispiel:
$1000 + $56 (Stackpointer) bei 8Bit ($FF) bedeutet
oberhalb von $1056 (d.h. ab $1057) bis zur maximalen Obergrenze bei $10FF liegen bereits Daten
speichert man nun etwas ab, kommt das nach $1056 und der Stackpointer wird um -1 auf $1055 verringert
es ist somit die Grenze nach unten gewandert und nun weniger Platz da
der Stapel "wächst" quasi (wie ein Stalaktit) von oben ($10FF) nach unten ($1000)
und so eine Art von Stapelspeicher nennt man dann auch einen Kellerspeicher
Es ist aber eben auch nicht gesagt, daß der Stapel im Adreßbereich nach unten wachsen muß.
Es ist ebenfalls nicht gesagt, ob der Stackpointer nun auf das erste freie Element zu zeigen hat, oder auf des letzte bereits gespeicherte.
Wie das genau geregelt ist, kann man nur in der Anleitung zum "jeweiligen Stack" erfahren, d.h. i.a. im Handbuch zur Maschine.
Übrigens: Wenn der Stackpointer auf das letzte gespeicherte Element zeigt, wäre es verständlicherweise überaus sinnvoll, ihn zu verändern BEVOR eine Schreiboperation geschieht (sonst würde man das Element ja überschreiben). Beim Lesen dagegen würde man einen solchen Stackpointer dagegen erst NACH dem Lesen verändert haben wollen.
Dieses Verhalten wäre also genau andersherum als bei einem Stackpointer, der auf die nächste freie Stelle zeigt.