https://www.youtube.com/watch?v=7i1XD2yN4Ug ...
Manchmal hat man schon bestimmte Routinen, die man direkt woanders wiederverwenden möchte. Oder es sieht einfach schöner im Programm aus, wenn man bestimmte Zusatzberechnungen quasi auslagert. Oder man hat ein Betriebssystem oder gar bereits eine Programmbibliothek, die einem bestimmte Dinge abnehmen und erledigen können.
Für all diese Fälle bieten sich Unterprogramme an, sogenannte Subroutinen.
Damit man diese verwenden kann, muß man natürlich wissen, wo im Speicher sie beginnen. Man benötigt also ihre Adresse.
Dann könnte man vermuten, daß man dort einfach mit einem direkten Sprung hingelangt. Das ginge prinzipiell auch - aber woher soll die Subroutine denn, wenn sie einmal durchgelaufen und fertig ist, wissen, wo es dann weitergeht ? Der normale Sprung springt eben nur und macht sonst gar nichts weiter.
Damit man trotzdem wieder vom Ende der Subroutine an die Stelle gelangt, von wo der Aufruf kam, muß also in irgendeiner Form die letzte Adresse gemerkt werden, die wo der Sprung herkam; besser noch: die Adresse, wo es dann weitergehen soll.
Genau so einen Sprungbefehl, der das kann, gibt es dann auch (meistens).
Im Fall vom 6502 heißt der auch so : Jump (to) Sub-Routine , JSR
Woanders, z.B. beim großen "Nebenbuhler" Z80, benutzt man : Call
auf den ARMs wäre das Equivalent ein : Branch (with) Link , BL
Die Herkunftsadresse wird oft einfach auf den Stack (den Stapelspeicher) gelegt, eine Art Speicher, der es erlaubt auch aus der Subroutine heraus noch eine weitere Unterroutine anzuspringen, also eine Sub-Sub-Routine, und trotzdem beide Rücksprungadressen wiederzufinden.
Am Ende der jeweiligen Subroutine muß man sich daher um Adressen nicht kümmern, sondern gibt man einfach einen Rückkehrbefehl (, ein RETURN). Dieser lädt i.P. einfach die gespeicherte Adresse und die CPU macht dann dort weiter, wo der Subroutinen-Aufruf stand.
Beachten muß man aber, daß dieser Service i.a. nicht mehr bietet.
Alle Werte die noch in Registern stehen, oder auch Flags, oder Werte in Adressen, die man als Zwischenspeicher benutzt werden NICHT automatisch gesichert.
Ändert man nun in der Subroutine Werte in z.B. Registern, dann wird beim Zurückkehren ins Hauptprogramm auch der geänderte Wert mit zurückkommen !
Darum muß man sich gut überlegen, welche Strukturen man in einer Subroutine "frei" beschreiben darf und von welchen man besser vorher eine Kopie anlegt, die man am Ende wieder in die Register lädt - bevor man mit RETURN zurückkehrt.
Eine weitere Schwierigkeit ist, daß der Stack zwar bequem zu benutzen ist, aber nicht beliebig groß sein kann. Das kann er schon prinzipiell nicht, weil ja das RAM des Rechners nicht beliebig groß ist, im Speziellen ist es aber oft so, daß der Stack auf eine bestimmte fixe Maximalgröße ausgelegt ist. Wenn nun zu viele Subroutinen hintereinander aufgerufen werden, legt jede ihre Rücksprungadresse auf den Stack - und das geht nur so lange gut, bis dieser komplett gefüllt ist. Danach bricht das System i.a. einfach mit einem Fehler in sich zusammen.
Besonders beachten muß man dies, wenn man sogenannte rekursive Strukturen benutzt - also Routinen, die sich selbst als ihre eigene Unterroutine aufrufen. Solche Konstrukte sind daher zwar interessant und auch mächtig, aber nicht beliebig oft hintereinander verwendbar (es sei denn man kümmert sich selbst um die Rücksprungadressen).
Zusätzlich wird auf dem Stack meist auch noch anderes gespeichert, was diesen Platz dann nochmal reduziert.
Deshalb auch muß man sich gut überlegen, ob man - was man durchaus machen kann, und was auch üblich ist - die Register, die man zwischenspeichert damit die Werte für die Hauptroutine erhalten bleiben, auf dem Stack abspeichern will oder vielleicht doch woanders.
Diese Eingrenzung der möglichen Sprungzahl ist es auch, weshalb ich beim direkten Sprung geschrieben habe, daß er der einzige universale Sprung ist.