von Bernd Dongus/Ulf Petersen Obwohl der Stack eines der wichtigsten Bestandteile eines Computersystems ist, gehen Assemblerbücher kaum näher auf ihn ein. In dieser Assemblerecke legen wir vom USER-MAG Ihnen deshalb dar, was Sie schon immer über den Stack wissen wollten oder wissen sollten. ACHTUNG: Diesmal konnten wir die SOURCE-Files nur im ATMAS II-Format beilegen. Nächste Ausgabe folgen die BIBO-Assembler Versionen. In den Assemblerecken der letzten Aus- gaben des USER-MAG haben wir uns vor- nehmlich mit dem Laufwerk und dem Scrolling beschäftigt. Bisher haben wir uns aber noch nicht näher mit dem Stack befasst. In dieser Ausgabe des USER-MAG soll deshalb der Stack und seine Mög- lichkeiten näher vorgestellt werden. DER STACK - WAS IST DAS ? Der Stack, der auch oft als Stapel oder auch Ablagespeicher bezeichnet wird, befindet sich fest im Speicherbereich der Page 1 ($100-$1FF (entspricht 256-511)) des RAM-Bereichs. Würde man ihn mit einem Satz beschreiben wollen, so könnte man den Stack als das allwis- sende Gedächtnis des Rechners bezeich- nen, da er , sofern man ihn nicht mani- puliert, immer weiß, an welche Stelle der Prozessor bei Ende eines Unterpro- gramms zurückspringen muß. Die Funk- tionsweise des Stacks gleicht dabei praktisch dem eines grossen Büchersta- pels. Am Anfang besteht dieser Stapel nur aus einem Buch. Nach und nach wächst dieser Stapel jedoch, indem wir wieder und wieder ein weiteres Buch auf diesen Stapel legen. Soll nun wieder ein Buch vom Stapel entfernt werden, so ist das erste Buch, das entfernt wird, das was zuletzt auf den Stapel gelegt wurde. Beim Stack gilt also das Prin- zip, daß das, was zuletzt abgelegt wurde, das erste ist, was auch wieder entfernt wird, wobei das oberste Buch der Speicherstelle 256 und das unterste Buch der Speicherstelle 511 des Compu- terstacks entspricht. Durch den be- grenzten Speicherbereich des Stacks können folglich auch nur 256 Werte sich gleichzeitig im Stack befinden. Damit der Computer jederzeit weiß, wie groß der Stack ist, existiert ein sogenann- ter Stackpointer. Diesen kann man vom Programm aus mit den Befehlen TSX oder TXS abrufen beziehungsweise verändern. Initialisiert werden kann er mit den Befehlen: LDX #255 TXS Nach Ausführung dieser beiden Befehle ist der Stack praktisch leer, d.h. es befinden sich keinerlei Werte mehr auf dem Stapel (der Stackpointer zeigt auf die unterste Adresse des Stacks: 511). Diese Befehle sollte allerdings niemals innerhalb eines Unterprogramms ange- wandt werden, da ansonsten aufgrund der fehlenden Rücksprungadresse etc., ga- rantiert ein Absturz die Folge ist. KORREKTER ABLAUF Wie bereits vorhin beschrieben dient der Stack dem Rechner als Gedächtnis- stütze. In Anspruch genommen wird er, wenn ein NMI- oder IRQ-Interrupt auftritt oder ein Unterprogrammein- sprung, der mit dem Befehl RTS endet (beim Befehl JSR oder den Timer-Inter- rupts), erfolgt. An dieser Stelle wol- len wir uns den Aufbau bzw. die Nutzung des Stapels bei den verschiedenen Unterbrechungsarten näher ansehen. JSR: $A800: JSR $A900 $A803: INC 712 . . . $A900: RTS Bei diesem Beispiel wird die Programm- ausführung an $A900 quasi abgegeben und zwar solange, bis der allseits bekannte RTS-Befehl auftritt. Anschließend springt der Prozessor wieder nach $A803 zurück, da er sich die Rücksprung- adresse mit Hilfe des Stacks gemerkt hat. Dieses sei nur für Anfänger be- merkt. Im einzelnen geht das folgendermassen vor sich. Nehmen wir als Voraussetzung an, daß der Stack bei unserem Beispiel anfangs leer ist, der Stackpointer also auf die Adresse 511 ($1FF) zeigt. Trifft der Prozessor nun auf den JSR- Befehl, wird der PC (enthält die gerade bearbeitete Programmadresse), an der der JSR-Befehl (!) steht (in diesem Falle $A800), um die Zahl 2 erhöht (wichtig !), das High-Byte und das Low-Byte des solchermassen erhöhten PCs (in dieser Reihenfolge !) auf den Stack geschoben, der Stackpointer ent- sprechend um 2 vermindert und die Programmausführung an die hinter dem JSR stehende Adresse abgegeben. Wird nun das Unterprogramm mit dem RTS-Be- fehl beendet, holt der Rechner das Low-Byte und das High-Byte (in dieser Reihenfolge) der Rücksprungadresse vom Stapel und erhöht den Stackpointer dem- entsprechend um 2. Vorhin haben wir jedoch festgestellt, daß die Rück- sprungadresse nun $A802 beträgt, also noch auf das Byte des insgesamt 3 Byte umfassenden JSR-Befehls zeigt, das sich vor dem nächsten Befehl befindet (ent- spricht wirkliche Rücksprungadresse - 1). Damit das Programm jetzt also nicht abstürzt, korrigiert der Prozessor dieses Problem von selbst, indem er die Rücksprungadresse erst jetzt um 1 erhöht, der Rücksprung also korrekt nun nach $A803 erfolgen kann. Selbstverständlich ist es auch möglich mehrere Unterprogramme ineinander zu verschachteln. Da die Rücksprungadresse beim JSR-Befehl 2 Byte lang ist und der Stack-Bereich 256 Bytes umfasst, wäre es also rein theoretisch möglich 128 Unterprogramme ineinander zu verschach- teln. Der Stack würde dabei so aus- sehen: Stackpointadresse Inhalt 511 High 1 510 Low 1 509 High 2 508 Low 2 507 High 3 506 Low 3 . . . . . . Timer-Interrupts: Wir führen diese Interrupts extra auf, da sie die einzigen Interrupts des XL/XE sind, die mit einem RTS-Befehl enden. Der Aufbau des Stacks ist beim Auftreten eines TI genau gleich mit dem des JSR-Befehls. Der einzige Unter- schied besteht quasi darin, daß der TI von der Hardware aus aktiviert wird und nicht vom Auftreten eines Befehl ab- hängig ist wie bei JSR. Im Listingteil wird am zweiten Timerinterrupt (es gibt insgesamt zwei, die vom Anwenden verwendet werden können) dargelegt, wie man sie anspricht bzw. verwendet. IRQ- und NMI-Interrupts: Auch wenn diese vielen von Ihnen viel- leicht bereits bekannt sind, wollen wir sie in dieser Ausgabe nochmals aufzäh- len: Vertical Blank Interrupt (VBI) NMI Display-List Interrupt (DLI) NMI RESET-Interrupt (nur 400/800) NMI BRK-Interrupt IRQ BREAK-Tasten-Interrupt IRQ Tastatur-Interrupt IRQ POKEY-Timer-Interrupt IRQ Soweit wir diese Interrupts noch nicht näher vorgestellt haben, werden wir dieses im folgenden Textteil nachholen. Im Grunde genommen unterscheiden sich IRQ- und NMI-Interrupts eigentlich ja nur dadurch, daß IRQs im Gegensatz zu den NMIs durch Setzen eines Interrupt- Disable-Flags unterbunden werden kön- nen. Folglich ist auch der Stackge- brauch bei beiden sonst gleich. Tritt nun an einer der beiden Interrupt- leitungen ein Aktiv-Signal auf, so wird beim IRQ das oben genannte Interrupt- Disable-Flag abgefragt und im gesetzten Falle der Interrupt nicht ausgeführt. Andernfalls wird beim IRQ und NMI der Programmablauf unterbrochen und in dieser Reihenfolge das HIGH-Byte des PC, das LOW-Byte des PC und das Prozes- sorstatusregister auf den Stack gelegt und der Stackpointer um drei vermin- dert. Beim Statusregister handelt es sich i.ü. um ein Byte, das alle Flags enthält, die vor dem Interrupt gesetzt oder ungesetzt waren. Anschliessend wird die Programmabarbeitung an die im Interruptvektor stehende Adresse abgegeben. Wird dem Prozessor mit dem RTI-Befehl mitgeteilt, daß der Interrupt beendet ist, so werden das Statusbyte und der PC wieder vom Stack geholt und das Hauptprogramm an der abgebrochenen Stelle fortgesetzt. An dieser Stelle folgen nun die Stack- daten der oben aufgeführten Interrupts. Dabei werden die Daten und die Be- schreibungen immer in der Reihenfolge angegeben, wie man sie mit den PLA- Befehlen vom Stack herunterholt. DLI: PLA 1.Wert Akku(-mulator) PLA 2.Wert Statusbyte PLA 3.Wert PC Low PLA 4.Wert PC High VBI: 1.Wert Y-Register 2.Wert X-Register 3.Wert Akku 4.Wert Statusbyte 5.Wert PC Low 6.Wert PC High RESET-Interrupt: Bei den XL/XE Computern nicht mehr existent. Hier wird ein echter Hardwarereset ausgeführt. Vermutlich endet dieser Interrupt aber bei den 400/800 mit einem PLA, RTI. In diesem Falle siehe DLI. BRK-Interrupt: Da wir bisher diesen Interrupt noch nicht in der Assemblerecke vorgestellt haben, soll dies hiermit nachgeholt werden. Seine Benennung ist im Grunde genommen relativ irreführend, da sie nur halb richtig ist. Er wird nämlich nicht nur aktiviert, wenn der Prozessor auf den Assemblerbefehl BRK stösst, sondern immer dann, wenn ein NICHT-Assembler- befehl (Breakpoint) auftritt. Diese Eigenschaft macht sich z.B. der ATMAS zunutze, da sich der BRK-Interrupt her- vorragend zum Debuggen (entfehlern) eignet. Um den BRK-Interrupt für seine eigene Routine zu verwenden, muß man die Adresse in die Vektoren 518 & 519 schreiben. Seine Stackdaten lauten folgendermassen: 1.Wert Akku 2.Wert Statusbyte 3.Wert PC Low 4.Wert PC High Der PC zeigt hierbei auf das 4te Byte hinter dem Breakpoint. Dieses ist für die Stackmanipulation wichtig. Bei den anderen IRQs und NMIs zeigt der PC genau auf die Rücksprungadresse. Break-Tasten Interrupt: Wird bei Druck auf die Break-Taste aktiviert. 1.Wert Akku 2.Wert Statusbyte 3.Wert PC Low 4.Wert PC High Tastaturinterrupt: Siehe Break-Tasten Interrupt POKEY-Interrupt: Siehe Break-Tasten Interrupt LAGERPLATZ An dieser Stelle sei noch auf eine wei- tere Nutzbarkeit des Stacks verwiesen. Man kann ihn auch als Ablage für Werte benutzen, um der Benutzung von Variab- len aus dem Wege zu gehen. Folgendes Beispiel soll verdeutlichen, wie man es machen kann: ORG $A800 LDA #112 PHA LDA #15 PHA JSR EINSCHUB PLA STA 709 PLA STA 710 RTS EINSCHUB LDA #0 STA 710 RTS Falsch wäre es in diesem Fall, wenn man die beiden auf den Stack geschobenen Werte innerhalb des Abschnittes EINSCHUB vom Stapel holen würde. Wie wir im vorhergehenden Text gesehen haben, würden wir dann ja die Rück- sprungadresse vom Stack holen. Das Ergebnis wäre dann vermutlich ein un- vermeidbarer Absturz. Übrigens kann man den oberen Teil des Stackbereichs weitestgehend auch als Datenplatz verwenden, da der Stack nie sehr stark ausgefüllt wird. Damit wären wir auch dieses mal wieder am Ende der Assemblerecke angelangt. Hinter Ihnen liegen eine Menge Infor- mationen, von denen ich hoffe, daß sie Ihnen nützlich sind. So, nun aber viel Spass beim Ausprobieren und Manipu- lieren. Nächste Ausgabe beschäftigen wir uns dann mit der DISPLAY LIST.