Hi, ich weiss nicht ob dieses problem vorher schon einmal behandelt wurde aber ich habe folgende idee mit dem stack und rcall zu arbeiten: Vorerst ein Term wie er unter Windows aussehen würde... z.b C Syntax: call xyz ( arg1, arg2, arg3, arg4, arg5, .... ) Windows Assembler: push argx push arg... push arg5 push arg4 push arg3 push arg2 push arg1 call xyz So idee ist nun folgende falls ich das richtig in tutorials gelesen habe legt mir rcall die rücksprung adresse auf den stack was bedeutet ich hätte folgendes Scenario : push argx push arg... push arg5 push arg4 push arg3 push arg2 push arg1 rcall xyz ich hätte also nach meinem "push arg1" anschliessend noch die rücksprung adresse auf dem stack ganz oben liegen was ein wenig unschön ist. Da ich ein etwas grösseres Projekt plane wo auch einige Funktionen zustande kommen werden möchte ich oben beschriebenes System nutzen um mir später auch keine grosse sorgen machen zu müssen über eventuell freie register. Nun meine frage hat vielleicht einer von euch dazu schon einmal eine elegante Lösung gefunden ? Eine recht stupide idee wäre nun die rücksprung adresse vom stack zu holen irgendwo zu sichern und am ende meines calls(funktion) wieder auf den Stack zu legen. Allerdings frage ich mich ob das wirklich eine effektive lösung ist. Für ideen oder anregungen bzw erfahrungen hier wäre ich dankbar. Danke schon mal im vorraus Wishmaster
"Allerdings frage ich mich ob das wirklich eine effektive lösung ist." Nein, ganz und garnicht ! Die übliche Methode ist die Übergabe in Registern. Wozu sonst hat man denn die 32 Register, wenn nicht zum Benutzen. Und bei großen Datenfeldern übergibt man einfach den Zeiger auf dessen Anfang, z.B. im X-,Y- oder Z-Registerpaar. Schau Dir einfach mal andere Programme an. Peter
Für viele oder lange Argumente benutzen Compiler gelegentlich auch die Übergabe via Stack. In dem Fall ist es üblich, sich einen Framepointer zu berechnen (rr28..29 bieten sich an) und danach die Argumente als Offset zum Framepointer anzugeben. (Negative Offsets wären lokale Variablen innerhalb des aktuellen Stackframes.) Aber das kann man natürlich dann gleich einen C-Compiler machen lassen...
Hi, Sicherlich ist es effektiv wenn ich grosse Datenfelder(Arrays) beispielsweise strings via einem Pointer übergebe das war auch in meinen überlegungen es wäre unsinnig beispielsweise jeden buchstaben einzeln zu pushen. Peter: Vielleicht gebe ich vorher eine kleine erklärung was ich so grob vorhabe. Ich habe einen AVR128 mit einem FTDI chip und zusätzlich Sind LEDs und Taster und ein LCD angeschlossen. Die kommunikation wird später ausschliesslich über USB stattfinden worüber ich Funktionen auf den AVR ansprechen kann. Alleine für diesen Datentransfer und Auswertung werde ich einige register brauchen um die auswertung effektiv ausführen zu können. Da nun aber weiterhin noch sachen wie Taster, LEds und LCD mit einspielen so wie interne Algorithmen auf dem AVR später möchte ich ungerne jedes gerade freie register im auge behalten müssen daher die überlegung mit dem stack und einer vernünftigen Kapeselung von Funktionen die ich dann später in "librarys" ablegen kann. Auf diese art könnte ich z.b sagen ich nehme mir sagen wir mal 5 feste register für die Funktionen und hole mir meine argumente wie ich sie brauche. Ich wäre aber trotzdem mal daran interessiert diese realisierung in einem Programm zu sehen weisst du da zufällig ein nettes Beispiel ? Jörg: Die idee mit dem Framepointer gefällt mir. Sicherlich könnten wir jetzt eine diskussion anfangen über C und Assembler ich habe mich allerdings dafür entschieden in Assembler zu programmieren. Hast du eventuell zu deiner Idee irgendeinen kleinen Beispielcode oder ist dir ein bekanntest programm bekannt was diese technik nutzt ? Ich bin mir noch nicht so ganz klar wie das zu realisieren ist. Wishmaster
Nimm einfach mal einen C-Compiler (AVR-GCC in diesem Falle, die anderen benutzen meines Wissens alle getrennte Daten- und Return-Stacks) und laß Dir eine simple Funktion compilieren. Grob gesagt: beim Aufruf der Funktion wird durch den Call die Rückkehradresse auf den Stack gelegt. Danach rettet man den alten frame pointer, liest sich den stack pointer ein (CLI nicht vergessen!), speichert ihn als neuen frame pointer ab und subtrahiert vom stack pointer den Platz, den man für die lokalen Variablen benötigt. Danach kann man frame pointer mit positivem Offset (Y+n) für die Argumentliste benutzen, mit negativem Offset (Y-n) für die lokalen Variablen. Beim Verlassen der Funktion dann umgekehrt: stack pointer wieder auf den Wert des frame pointers rücksetzen (damit werden alle lokalen Variablen ,,vernichtet''), pop framepointer, return.
"möchte ich ungerne jedes gerade freie register im auge behalten müssen" Das macht man ja auch nicht. Entweder Du machst es wie die Compiler, d.h es gibt grundsätzlich keine "freien" Register. Variablen, die der Aufrufer noch danach wirklich benötigt, muß er entweder pushen oder im SRAM anlegen. Oder Du machst es codeoptimiert und definierst Dir 16 Register für häufige globale Variablen und die anderen 16 Register sind dann die nicht "freien" Arbeitsregister, die der Aufrufer je nach Bedarf sichern muß. Optimieren bedeutet, daß man Funktionen so schreibt, daß sie in 80..90% aller Fälle optimal sind und nicht, daß sie in 80..90% aller Fälle nur einen Haufen unnötigen Code enthalten. Und in der Regel werden die Variablen, die man einer Funktion übergeben hat, eben nicht mehr benötigt, sondern nur das Ergebnis daraus. Deshalb hat es sich auch eingebürgert, daß man in den selben Registern, die den ersten Operanden enthalten, auch das Ergebnis wieder zurück liefert. Anders gesagt, übertriebenes Pushen und Popen kostet dich nur unnötig viel Rechenzeit und Codespeicher. Und je mehr man pusht und popt, umso häufiger macht man Fehler dabei, z.B. ungleiches Push/Pop oder Registervertauschung, d.h. Dein Code wird fehleranfälliger. Peter
> Entweder Du machst es wie die Compiler, d.h es gibt grundsätzlich > keine "freien" Register. Variablen, die der Aufrufer noch danach > wirklich benötigt, muß er entweder pushen oder im SRAM anlegen. Ich kenne zwar nur den GCC, aber der macht es zumindest nicht so. Er hat eine Reihe von scratch registers, die auch zum Weiterreichen der Parameter genutzt werden (erst wenn sie nicht mehr ausreichen, wird der Stack genommen), und eine Reihe von Registern, die eine gerufene Funktion nicht zerstören darf (d. h. wenn sie mehr Register braucht, muß sie selbst push/pop'en). Prinzipiell könnte man sich außerdem in den 14 nicht freien Registern häufig benötigte globale Variablen ablegen. Allerdings ist das für ein größeres Projekt nur in den seltensten Fällen codeoptimal, da man diese Register dann permanent dem Compiler entzieht, so daß er u. U. auf SRAM für andere Dinge ausweichen muß, obwohl er dort mit noch einem verfügbaren Register mehr (und einem push/pop pro Funktionsaufruf) besseren Code erzeugen könnte.
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.