Parameterübergabe an Subroutinen

Es gibt 16 Antworten in diesem Thema, welches 2.528 mal aufgerufen wurde. Der letzte Beitrag (3. Oktober 2020 um 10:21) ist von daybyter.

  • Hi,

    ich lerne gerade 6502 Assembler und schreibe meine ersten Subroutinen. Da die CPU so wenig Register hat, bleibt mir irgendwie nichts anders übrig als über den Speicher die Parameter an eine Subroutine zu übergeben und das mit dem Rückgabewert genauso zu machen. Nun sind das bei 16Bit Adressen oder Werten schon eine Menge LDA und STA. Ich habe mich als Speicher für die Parameter und die Rückgabe für die Zeropage entschieden, da hierdurch Speicher und Rechenzeit gespart werden kann.

    Wie macht ihr Profis das so mit der Parameterübergabe?

  • Genau so. Bei wenigen Parametern über Register ansonsten über die Zeropage.

    Wobei die Parameterübergabe über Register auch nicht immer praktisch ist, wenn die in der aufrufenden Routine benötigt und jedesmal vor dem Aufruf gesicherten werden müssen. Deswegen bestimmt bei mir meistens die aufrufende Routine, wie die Parameter übergeben werden.

  • Vielen Dank, dann bin ich ja auf dem richtigen Weg.

  • Man kann sich auch über einen Zeiger in der Zeropage einen eigenen Stack für die Parameter basteln. Das ist natürlich deutlich aufwändiger, erlaubt aber rekursive Programmierung - wenn man so etwas mal brauchen sollte.

  • Du kannst auch vor dem Aufruf eine oder mehrere Speicherstellen in der Zielprozedur setzen:

    Ist natürlich weniger übersichtlich und kann verwirren.

    Bitte melde dich an, um diesen Link zu sehen. (Bitte melde dich an, um diesen Link zu sehen.)Bitte melde dich an, um diesen Link zu sehen.Bitte melde dich an, um diesen Link zu sehen.
  • Ja stimmt danke, selbstmodifizierender Code ist auch eine Möglichkeit.:emojiSmiley-106:

  • Nö, selbstmodifizierender Code ist ganz schlechter Programmierstil. Ungefähr vergleichbar mit einem GOTO in einer Hochsprache. :D

    Spätestens, wenn du die Routinen mal in eine Cartridge packen willst, siehst du damit ziemlich alt aus.

  • Bei 6502 Assembler das im RAM läuft aber durchaus eine gute Option.

    Bitte melde dich an, um diesen Link zu sehen.
    Bitte melde dich an, um diesen Link zu sehen.
    Bitte melde dich an, um diesen Link zu sehen.

  • Klar ist das eine Option. Ändert aber nichts am schlechten Stil. ;)

    Zur Geschwindigkeitsoptimierung an ausgesuchten Stellen (wo man die Geschwindigkeit wirklich braucht), finde das ok. Aber als generelle Programmiertechnik sollte man sich das nicht angewöhnen.

  • Hat alles Vor-und Nachteile:

    Wenn man die Selbstmodifizierungsstrukuren in Makros packt (cc65 hat z.B. ein Macro Package dafür), kann man den Code trotzdem noch gut strukturieren.

    Auf selbstmodifizierenden Code zu verzichten benötigt dafür mehr Zero Page-Adressen. Das wird mit der Zeit auch teuflisch, wenn man dann mehrere Codeteile hat (ggf. Fremdcode) und nicht genau weiß welche ZP-Adressen verwendet werden. Insbesondere wenn BASIC und Kernal daneben noch laufen sollen, ist die ZP schon sehr belegt.

    Dafür verbau ich mir mit Selbstmodifizierungsstrukuren meine Chancen als ROM-Programmierer.

    Reentrant ist leider beides nicht...

  • Aber zur eigentlichen Frage bzgl. Parameterübergabe: alles was noch Platz hat (3 Byte + Carrybit) in die Register, wenn es ein 16-Bit-Wert ist in AX. Bei einem String würde ich den Pointer übergeben. Wenn es mehr ist als in die Register passt und der Zugriff darauf zeitkritisch ist in die ZP. Wenn die Ausführungszeit nicht so kritisch ist, dann in eine Struktur im Hauptspeicher direkt bei der Routine, ggf. bau ich mir dann auch Makros zum Setzen einzelner Parameter, sodass ich bei einer Änderung der Struktur nur die Makros anpassen muss.

  • Der Stack, natürlich, warum ich da nicht drauf gekommen bin? :platsch:

    Das machen ja Compiler nicht anders wenn sie Funktionsaufrufe übersetzen.

  • Der Stack, natürlich, warum ich da nicht drauf gekommen bin? :platsch:

    Das machen ja Compiler nicht anders wenn sie Funktionsaufrufe übersetzen.

    Die Stackvariante habe ich immer als recht umständlich empfunden. Wenn ich im aufrufenden Programm meine Argumente auf den Stack pushe und danach das Unterprogramm aufrufe, sind die obersten Bytes am Stack ja die Rücksprungadresse. Um an die Daten ranzukommen muss ich demnach den Stackpointer ins X-Register holen und über diesen den Stack auslesen. Nach dem Aufruf des Unterprogramms muss das Hauptprogramm den Stack aufräumen. Callee cleans wäre noch aufwendiger weil die Rücksprungadresse am Stack im Weg ist. Insgesamt ist das ist langsamer als ein Ablegen im normalen Speicher und erfordert relativ viel Code im aufrufenden Program .

    Das mit dem lda $103,x ist auch nicht gerade intuitiv, zumindest ich selbst vertue mich da oft beim richtigen Offset.

    Der Aufwand für Übergabe im Stacke lohnt sich meiner Meinung nach nur für Unterprogramme die rekursiv und reentrant sein müssen. Wobei der Hardware-Stack bei rekursiven Routinen mit Parametern sehr schnell zum Bottleneck wird.

    Um diese Limitierungen zu umgehen verwendet die C-Implementierung in cc65 daher zum Beispiel einen zweiten, in Software realisierten Stack für die Parameterübergabe.

  • Die Stackvariante habe ich immer als recht umständlich empfunden. Wenn ich im aufrufenden Programm meine Argumente auf den Stack pushe und danach das Unterprogramm aufrufe, sind die obersten Bytes am Stack ja die Rücksprungadresse.

    Daran habe ich gar nicht gedacht, ein gutes Argument gegen die Stacknutzung bei kleinen schnellen Routinen.