GOSUB

Aus C64-Wiki
Zur Navigation springenZur Suche springen
GOSUB
Syntax: GOSUB <Zeilennummer>
Parameter
<Zeilennummer>: Zeilennumer des Unterprogramms
Einordnung
Typ: Anweisung
Kontext: Programmstruktur
Aufgabe: Aufruf eines Unterprogramms
Abkürzung: goS
Token: $8d (141)
Verwandte Befehle
GOTO, ON, RETURN

Anmerkung: Dieser Artikel beschreibt den BASIC-Befehl GOSUB unter BASIC V2 des Commodore 64.


Der BASIC-Befehl GOSUB springt zur angegebenen Zeilennummer (nur als Zahlenkonstante erlaubt) und arbeitet den dort angeführten Programmteil als Unterprogramm ab. Die Programmstelle des GOSUB-Aufrufs merkt sich der BASIC-Interpreter am BASIC-Stapelspeicher, um später dort wieder fortsetzen zu können. Das Unterprogramm endet sobald der BASIC-Befehl RETURN ausgeführt wird und die Programmabarbeitung setzt beim Programmcode hinter dem aufrufenden GOSUB fort.
Damit wird die Grundidee umgesetzt, Programmteile an unterschiedlichen Stellen des Programms immer wieder verwenden zu können und zu einer entsprechende Ersparnis im Umfang und in der Verbesserung der Übersichtlichkeit, aber auch Wartbarkeit des Programmcodes führt.

Eine Parameterübergabe an das aufgerufene Unterprogramm oder das Zurückliefern von Ergebnissen muss mittels Variablen gelöst werden, über deren Gebrauch sehr sorgfältig Buch geführt werden muss. Es kann sonst zu unerwarteten Nebenwirkungen im Programmablauf kommen, falls Variablen mehrfach verwendet werden.

Es können mit dem BASIC-Befehl GOSUB mehrere Unterprogramme hintereinander (also ineinander verschachtelt) aufgerufen werden, allerdings wird immer das zuletzt aufgerufenen Unterprogramm zuerst beendet. Ein Unterprogramm kann sich auch selbst aufrufen (Rekursion), wobei die Rekursionstiefe bzw. die Anzahl ineinander verschachtelten Aufrufe durch den begrenzten BASIC-Stapelspeicher limitiert ist (siehe Auswertung). Zu viele geöffnete bzw. ineinander verschachtelt Unterprogramme führen zur BASIC-Fehlermeldung ?OUT OF MEMORY ERROR. Das theoretische Limit von 26 Verschachtelungen bewegt sich in der Praxis im Bereich rund um 23, wenn man davon ausgeht, dass im Unterprogramm noch sinnvolle Aktionen durchgeführt werden sollen (welche in der Regel ebenso den BASIC-Stapelspeicher gebrauchen).
Existiert die entsprechende Zeilennummer nicht, so erscheint die BASIC-Fehlermeldung ?UNDEF'D STATEMENT ERROR.

Das Verlassen eines Unterprogramms bewirkt zwangsläufig auch, dass alle dort geöffneten FOR-NEXT-Schleifen beendet werden (und die Verwaltungsinformationen am BASIC-Stapelspeicher korrekt bereinigt werden).


Jeder GOSUB-Aufruf benötigt 5 Bytes auf dem BASIC-Stapelspeicher. Diese setzen sich zusammen aus:

  • Adresse auf den BASIC-Text unmittelbar nach dem GOSUB (2 Bytes)
  • Zeilennummer zur zuvor genannten BASIC-Text-Position gehörend (2 Bytes)
  • GOSUB-Token 141/$8D (1 Byte)

Die Werte werden wie am Stapel üblich ausgehend von hohen in Richtung niedriger Adressen abgelegt.

Die Kombination aus GOSUB und RETURN ist in etwa gleich schnell wie zwei GOTOs: Das GOSUB ist, da es die Rücksprungdaten auf den Stack legen muss, etwas langsamer als ein GOTO, dafür ist das korrespondierende RETURN schneller als ein GOTO, da es die Zielzeilennummer nicht erst suchen muss. Im Sinne der Lesbarkeit von Programmen bietet es sich also an, Unterprogrammaufrufe tatsächlich per GOSUB umzusetzen, auch wenn das Unterprogramm nur von einer Stelle aus aufgerufen werden sollte.

Beispiele[Bearbeiten | Quelltext bearbeiten]

10 PRINT CHR$(147)
20 SP = 20: ZE = 3: A$ = "Guten Tag !": GOSUB 1000: GOSUB 2000
30 SP = 10: ZE = 3: A$ = "Ich bin der Commdore 64": GOSUB 1000: GOSUB 2000
40 SP = 12: ZE = 6: A$ = "Und wie heissen Sie?": GOSUB 1000
100 END
1000 REM Cursorpositionierung
1010 POKE 211,SP :POKE 214, ZE: SYS 58640 : PRINT A$
1020 RETURN
2000 REM Warteschleife
2010 FOR X=0 TO 1500: NEXT X
2020 RETURN

Rekursion[Bearbeiten | Quelltext bearbeiten]

10  REM - DAS RAHMENPROGRAMM
100 INPUT "FAKTORIELLENBERECHNUNG: N=";FA
110 GOSUB 1000
120 PRINT " FAKTORIELLE VON ";FA;"=";FR
130 GOTO 100
1000 REM UNTERPROGRAMM FAKT(FA) -> FR - VERWENDET: I
1010 FR=1: I=1           : REM INITIALISIERUNG
1020 IF I>FA THEN RETURN : REM ABBRUCHBEDINGUNG
1030 FR=FR*I             : REM EIN BERECHNUNGSSCHRITT
1040 I=I+1
1050 GOSUB 1020          : REM REKURSION!
1060 RETURN

Im Grunde kompakter und effizienter mit einer FOR-NEXT-Schleife lösbar, aber hier wird demonstrativ der mathematischen Definition der entsprechenden Funktion (etwas holprig) genüge getan.
Maximaler Eingabewert ist 22, ohne dass der Fehler ?OUT OF MEMORY erscheint.


Experimentelles[Bearbeiten | Quelltext bearbeiten]

Die maximale Anzahl der Verschachtelungen: (oder "Wie viele GOSUB-Aufrufe verträgt der BASIC-Stapel?")

10 GOTO 30
20 READ A     : REM ANZAHL DER GOSUB AUFRUFE
30 GOSUB 20
40 END
50 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13
60 DATA 14,15,16,17,18,19,20,21,22,23
70 DATA 24,25,26,27,28,29,30,31,32,33

Testlauf:

RUN
?OUT OF MEMORY  ERROR IN 30
READY.
PRINT A
 26
 
READY.

Die Konstruktion mit READ-DATA ist eine Methode der Zählung, die den BASIC-Stapelspeicher nicht zusätzlich belastet. Etwaige Schleifenvariablen oder Ausgaben via PRINT würden eine Nutzung des BASIC-Stapelspeichers wegen der Berechnung des jeweiligen Ausdrucks in den Parametern bewirken. Der Überlauf des BASIC-Stapelspeichers träte nämlich dann zwei GOSUB-Aufrufe früher ein.

Fehler wegen Zeilennummeranomalie[Bearbeiten | Quelltext bearbeiten]

Provokation einer fehlerhafter Fehlerbehandlung:

GOSUB350720

führt zu einem unerwünschten Effekt (Absturz). Siehe GOTO Fehler-Abschnitt.

Kommentar ohne REM[Bearbeiten | Quelltext bearbeiten]

Der Code hinter der GOSUB-Zeilennummer bis zum nächsten BASIC-Befehl wird nicht interpretiert. Hier kann man daher einen Kommentar einfügen, ohne REM benutzen zu müssen:

100 GOSUB 200 EINGABE: GOSUB 300'16BIT
110 GOSUB 400 VERARBEITUNG
120 END
200 PRINT "EINGABE":RETURN
300 PRINT "16BIT":RETURN
400 PRINT "VERARBEITUNG":RETURN

Achtung: Ist das erste Zeichen des Kommentars eine Ziffer, so wird sie als Teil der Zeilennummer interpretiert. Das passiert sogar, wenn zwischen Zeilennummer und dem beabsichtigten Kommentar (mindestens) ein Leerzeichen steht, denn der BASIC-Interpreter ignoriert Leerzeichen außerhalb von Anführungszeichen. In Zeile 100 wird deshalb der zweite Kommentar, weil er mit einer Ziffer beginnt, mit einem trennenden Zeichen versehen. Hier ist es der von BASIC-Erweiterungen gern als REM-Ersatz eingesetzte Apostroph CHR$(39). Ein anderes Zeichen, wie z.B. Komma oder Semikolon, wäre aber auch möglich.