Ich versuchete das Beispielprogramm "Parameterübergabe an Unterprogramme" aus dem AVR-Tutorial 3. Der Stack zu realisieren. Nur leider habe ich die Stelle an der der Zeiger Z auf das letzte freie Byte im Stack zeigen soll nicht hinbekommen. Der Wert den der Stack-Pointer tatsächlich hat wird nicht richtig von dem Z-Pointer übernommen. Kann mir bitte jemand das Programm korregieren?
An der Stelle, an der Z auf das aktuelle Stackelement zeigen soll, müsste es IN ZH, SPH IN ZL, SPL heißen. An der Stelle, an der Du den Z-Pointer um 6 inkrementierst, wäre ADIW ZH:ZL, 6 sinnvoller.
Die Parameterübergabe mit Stack sollte man aber nur im äußersten Notfall machen. In der Regel ist nämlich die Übergabe in Registern schneller, Codesparender und Nervenschonender. Nichts ist häßlicher als ein unbalancierter Stack und oft sauschwer die faule Stelle zu finden. Stackfehler wirken sich nämlich immer an einer Stelle aus, die vom eigentlichen Fehler möglichst weit entfernt liegt. Peter
Danke für die hilfe, ich habe gleichzeitig einen zweiten Fehler entdeckt. Der Pointer muss um 8 inkrementiert werden. In dem Tutorial wird die Parameterübergabe als eine Möglichkeit aufgezeig um Unterprogramme flexibler zu machen. Ich habe noch keine Erfahrung mit dieser Programiersprache und kann dieses nicht beurteilen.
Traue dem Tutorial. Sobald Unterprogramme etwas verschachtelt sind, kannst Du nur so Übersicht bewahren (Rekursionen gehen ohnehin nicht anders). Du mußt nur innerhalb des Unterprogramms darauf achten, den Stack richtig zu hinterlassen; das ist nicht wirklich ein Problem. Peter hat zwar recht, daß sich mit Registerübergabe ein paar Takte sparen lassen, aber man muß notwendigerweise an mehreren Stellen dieselben Register für die Übergabe benutzen ... behalte da mal den Überblick!
"Sobald Unterprogramme etwas verschachtelt sind, kannst Du nur so Übersicht bewahren" Da ist überhaupt kein Unterschied ! Egal ob Stack oder Register, man muß sich erstmal einige Konventionen überlegen und diese dann einhalten. Z.B. definiere ich mir Register A3..A0,B3..B0,T3..T0,I1,I0. A,B sind dann für die Operanden und Rückgabewerte, T,I für temporäre Variablen und Scheifenzähler. Schau Dir einfach mal einige Beispiele von mir in der Codesammlung an. Ich gebe zu, das ist nicht auf meinem Mist gewachsen, ich habs mir einfach von C-Compilern abgeguckt. Als ich angefangen habe, habe ich auch wie wild gepusht und gepopt, bis ich schließlich die Übersicht verloren hatte. Und dann die vielen Fälle, wo ich mir nen Wolf gesucht habe, nur weil irgendwo andersrum gepopt als gepusht wurde: push r16 push r17 push r18 push r19 ; mache was pop r19 pop r17 pop r18 pop r16 Register sind dazu da, um sie zu benutzen und nicht ständig zu pushen. Natürlich kann man sich einige Register für sehr häufige globale Variablen freihalten, aber deshalb hat man ja auch 32 Stück. Und auch für Interrupts reserviert man ein paar Register. Z.B. hat man ja 3 Pointer (X,Y,Z), benötigt aber höchstens 2 gleichzeitig. Also ist es eine gute Idee, X nur für Interrupts zu reservieren. Und da Interrupt fast immer das SREG zerstören, kann man R2 fest für das Sichern des SREG reservieren. "Rekursionen gehen ohnehin nicht anders" Rekursionen sollte man als Anfänger meiden, wie der Teufel das Weihwasser. Typisch für Rekursionen ist nämlich, daß sie bei ungünstigen Startwerten sehr gerne zum Stacküberlauf führen. Nur erfahrene Programmierer können da die Übersicht behalten (aber die benutzen in der Regel C). Für den Anfänger sind Schleifen statt Rekursionen wesentlich übersichtlicher, schneller und vor allem sicherer. Peter
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.