4. BASIC-Befehls-Referenz

 

Dieses Kapitel hat gewissermaßen die Funktion einer Formelsammlung, in der Sie wichtige Dinge nachschlagen können. In gewisser Weise ist also dieses Kapitel ein Nachschlagewerk, das Sie verwenden können, wenn Sie mal einen Befehl oder das zugehörige Format vergessen haben.

In diesem Kapitel habe ich nun sämtliche Befehle und Operatoren aufgelistet, und zusätzlich bei BASIC-Kommandos auch die Token-Nummer angegeben, die BASIC für die Befehle verwendet. Ein Token ist hierbei ein Schlüsselwort (wie z.B. IF und FOR), das sofort nach Drücken der RETURN-Taste durch ein einziges Byte ersetzt wird. Diese Maßnahme dient zur Erhöhung der Ausführungsgeschwindigkeit und zum Sparen von Speicher. Damit BASIC die Tokens schnell findet, und von normalen Zeichen unterscheiden kann, ist bei den Token-Zeichen immer das oberste Bit gesetzt. Die Token-Nummern fangen also bei 128 an.

Ich habe nun die Tokens alphabetisch aufgelistet, aber diese in drei Bereiche eingeteilt: Die Standard-Tokens, die Operatoren (diese werden ebenfalls durch Tokens dargestellt) und die internen, fest verdrahteten Funktionen, die Sie in BASIC auch nicht neu definieren oder überladen können.

 

4.1 Standard-Tokens

 

CLOSE (Token 160)

Format:

CLOSE [Dateinummer]

 

CLOSE schließt eine Datei und entfernt alle temporären Puffer. Daten im Schreibpuffer, die noch nicht geschrieben wurden, werden vorher noch geschrieben. CLOSE Verwendet die Dateinummer der zu schließenden Datei als Referenz, weshalb CLOSE auch für Drucker und andere Geräte benutzt wird, nicht nur für Disketten-Laufwerke. Sämtliche Dateien, die geöffnet wurden, sollten auch wieder mit CLOSE geschlossen werden, da sonst der BASIC-Speicher knapp werden kann oder sogar korrupte Dateien entstehen können.

 

CLR (Token 156)

Format:

CLR

 

CLR löscht die Variablentabelle und sämtliche Arrays und String, so, als ob gerade RUN eingegeben wurde. Allerdings wird durch CLR ein laufendes Programm nicht unterbrochen, deshalb kann mit CLR vorher belegter Speicher wieder freigegeben werden. Wenn Sie die Meldung OUT OF MEMORY erhalten haben, können Sie versuchen, mit CLR Speicher freizugeben, bevor Sie Ihre Datei auf Diskette speichern. Es gibt aber keine Garantie dafür, dass CLR hier noch sauber arbeitet.

 

CMD (Token 157)

Format:

CMD [Befehlsnummer]

 

CMD sendet ein bestimmtes Kommando an ein Gerät. Hierfür wird eine bestimmte Befehlsnummer verwendet, die vom Gerät abhängig ist. CMD funktioniert deshalb auch nur im Zusammenhang mit einer geöffneten Datei, und auch nur für das Gerät, das mit der aktuellen Dateinummer verknüpft ist, die zuletzt verwendet wurde. Dies macht die Anwendung von CMD auch so schwierig. Ein Standardbeispiel ist hier die Weiterleitung der Ausgabe von LIST an einen textfähigen, Epson-kompatiblen Drucker, der hier die Gerätenummer 4 hat (über die Gerätenummer 3 werden stattdessen einzelne (Kommando-)Bytes zu einem Drucker übertragen):

 

OPEN 3,4

CMD 3

LIST

CLOSE 3

 

CONT (Token 154)

Format:

CONT

 

CONT setzt die Programmausführung nach einer Unterbrechung fort, aber nur dann, wenn das Programm mit einer STOP-Anweisung oder durch die RUN/STOP-Taste beendet wurde, und BASIC deshalb die nächst gültige Zeilennummer noch finden kann. Dies ist aber nur dann der Fall, wenn das Programm nicht durch einen Fehler unterbrochen, oder nicht mit END endgültig beendet wurde. Leider gibt es BASIC-Referenzen im Internet, oder auch alte Bücher, die CONT falsch beschreiben, und behaupten, dass CONT auch nach END funktioniert. Dies ist aber nicht immer der Fall, und ein Erfolg ist hier sogar von der verwendeten Kernal-Version abhängig. Am besten, Sie verwenden CONT erst gar nicht und versuchen von vorn herein, gut strukturierten Code zu erstellen.

 

DATA (Token 131)

Format:

DATA [Wert 1], [Wert 2], … , [Wert n]

 

Mit DATA können Werte definiert werden, die dann an späterer Stelle im Programm verwendet werden können. Die Werte können Zahlen, aber auch Zeichenketten sein. Allerdings muss in diesem Fall darauf geachtet werden, dass die Datentypen, die in den DATA-Zeilen stehen, mit dem Datentyp der Variable übereinstimmen, die hinter der READ-Anweisung steht, mit der Sie die Daten auslesen. So können Sie z.B. mit

 

READ A

 

keine Daten einlesen, die z.B. mittels

 

DATA "HALLO","WELT"

 

definiert wurden. Die Konsequenz ist hier die Fehlermeldung

 

?TYPE MISSMATCH  ERROR

 

DATA-Zeilen werden sehr oft dazu verwendet, Maschinenprogramme in den Speicher zu schreiben, die Sie dann später mit der SYS-Anweisung (siehe auch dort) von BASIC aus aufrufen können. So bietet der Editor z.B. von Haus aus keine Möglichkeit, den Bildschirm automatisch nach unten zu scrollen. Mit DATA-Zeilen und der SYS-Anweisung können Sie dies trotzdem realisieren, müssen aber das Maschinenprogramm vorher mit einem Assembler-Monitor erzeugen. Ein guter Monitor, den Sie auch frei im Internet in Form einer D64-Datei herunterladen können, ist Hypra-Ass.

 

DEF (Token 150)

Format:

DEF FN [Funktionsname]([Variable])=[Ausdruck]

 

DEF wird ausschließlich zusammen mit dem Schlüsselwort FN verwendet und dient der Definition von benutzereigenen Funktionen. DEF FN bewirkt, dass einer bestimmten Variablen ein Funktionsname und ein BASIC-Ausdruck zugeordnet wird, der immer dann angewendet wird, wenn die Funktion später mit FN aufgerufen wird. Mit DEF FN können also keine wirklich neuen mathematischen Funktionen erstellt werden, die dann in BASIC als neue Befehle verwendet werden können. Allerdings können mit DEF FN kürzere Programme erreicht werden, in denen nicht immer wieder dieselbe Formel in mehreren Programmzeilen auftaucht. So können Sie z.B. den Satz des Pythagoras wie folgt als Funktion benutzen:

 

10 DEF FN P(X)=SQR(A^2+B^2)

20 A=2: B=3

30 PRINT FN P(X)

 

Es ist übrigens völlig unerheblich, welche Variable in den Funktionsklammern steht, deshalb wird auch im letzten Beispiel korrekt A und B als Seitenlänge des rechtwinkligen Dreiecks eingesetzt, obwohl die Funktion P(X) die Variable X nicht benutzt.

 

DIM (Token 134)

Format:

DIM [Array-Variable] ([Größe Dimension 1], [Größe Dimension 2], … [Größe Dimension n])

 

DIM legt ein Array im Speicher an und verwendet dazu die angegebene Variable als Array-Variable. Diese Variable kann dann natürlich nicht mehr als normale Fließkomma-Variable verwendet werden, sondern der Variablenname wird für das Array reserviert. Die Größe der einzelnen Dimensionen des Arrays wird innerhalb von runden Klammern als einzelne Parameter angegeben. In diesem Sinne ist dann DIM auch eher eine Funktion, als ein Standard-BASIC-Befehl.

Der Zugriff auf die einzelnen Array-Elemente erfolgt durch Indizes, die bei 0 für jede Dimension beginnen, und die dann ebenfalls als Parameter innerhalb runder Klammern angegeben werden müssen. Diese Parameter müssen in Anzahl und Größe mit dem Array übereinstimmen, auf das Sie zugreifen wollen, sonst wird die Fehlermeldung

 

?BAD SUBSCIPT  ERROR

 

ausgegeben.

 

END (Token 128)

Format:

END

 

END beendet das Programm und kehrt in den Eingabemodus zurück. Wenn sich der Editor im erweiterten Modus befindet, in dem auch die Steuerzeichen angezeigt werden, wird dieser deaktiviert. END löscht aber nicht die Variablentabelle oder die Strings, diese werden nur gelöscht, wenn das Programm erneut mit RUN gestartet oder CLR aufgerufen wird. Jedoch kann das Programm nach END nicht mit CONT fortgesetzt werden (oder zumindest ist nicht reproduzierbar, wann dies gelingt, und wann nicht).

 

FN (Token 165)

Format:

FN [Funktionsname]([Variable])

 

FN ruft eine Funktion auf, die vorher mit DEF FN definiert wurde, und gibt den Funktionswert zurück. Als Referenz dient hierbei ein beliebig wählbarer Funktionsname mit bis zu 2 Zeichen. Wenn z.B. vorher der Satz des Pythagoras durch DEF FN unter dem Funktionsnamen P(X) definiert wurde, und diese Funktion die Variablen A und B als Seitenlängen eines rechtwinkligen Dreiecks verwendet, dann gibt die BASIC-Zeile

 

C=P(X)

 

Die Seitenlänge der Hypotenuse zurück. Anders, als es die Vermutung nahelegen würde, wird die Variable X in dem Beispiel, das auch schon im Falle des DEF-Befehls erläutert wurde, ignoriert, obwohl diese bei der Funktionsdefinition als Parameter angegeben wurde.

 

FOR (Token 129)

Format:

FOR [Variable]=[Startwert] TO [Endwert] STEP [Schrittweite]

 

FOR definiert den Kopf einer Schleife, als Schleifenzähler wird die Variable verwendet, die direkt hinter FOR angegeben wird. Hinter dem Gleichheitszeichen muss dann der Startwert und der Endwert des Schleifenzählers angegeben werden, der vorher durch die FOR-Variable definiert wurde. Wird die Schrittweite (Schlüsselwort STEP) weggelassen, ist die Schrittweite 1. Startwert, Endwert und Schrittweite können auch durch einen komplexen Ausdruck ersetzt werden. Es ist also z.B. die folgende FOR-Schleife erlaubt:

 

FOR I=2*A TO 3*B STEP C/10

 

GET (Token 161)

Format:

GET [String-Variable]

 

GET liest die Tastatur aus, und schaut nach, ob eine Taste gedrückt wurde. Wenn keine Taste gedrückt wurde, wird ein leerer String zurückgegeben, wenn eine Taste gedrückt wurde, wird der entsprechende ASCII-Wert aus dem Tastaturpuffer ausgelesen und als String mit einer Länge von 1 zurückgegeben. GET arbeitet nur zusammen mit Strings und der Tastatur, nicht mit Fließkommazahlen oder zusammen mit Dateien (hierfür müssen Sie GET# verwenden). Die aus höheren BASIC-Versionen (ab 4.0) bekannten GET#- und PUT#- Anweisungen für Dateien gibt es für den C64 nicht, es sei denn, Sie erwerben ein neues Kernal-ROM und setzen dies auf der Platine ein.  Um auf eine beliebige Taste zu warten, kann die folgende BASIC-Zeile benutzt werden:

 

10 GET A$: IF A$="" THEN GOTO 10

(am Ende befindet sich die gedrückte Taste in dem String A$)

 

GOSUB (Token 141)

Format:

GOSUB [Zeilennummer]

 

GOSUB führt einen direkten Sprung zu der Zeile mit der angegebenen Zeilennummer aus, und ist wie GOTO (siehe auch dort) nicht von einer Bedingung abhängig. Anders, als GOTO, merkt sich GOSUB jedoch die Stelle, von der aus es aufgerufen wurde, und kann auf diese Weise zum Aufruf von Unterprogrammen verwendet werden. Sie müssen jedoch beachten, dass jedes Unterprogramm irgendwann mit RETURN (siehe auch dort) beendet werden muss.

 

GOTO (Token 137)

Format:

GOTO [Zeilennummer]

 

GOTO führt einen direkten Sprung zu der angegebenen Zeilennummer aus. BASIC fährt also direkt mit der Programmausführung der hinter GOTO angegebenen Zeile fort, unabhängig von irgendeiner Bedingung. Natürlich bricht das Programm nach dem Sprung ab, wenn sich in der Zeile, zu der GOTO springt, ein Fehler befindet.

 

IF (Token 139)

Format:

IF [Ausdruck] THEN [Anweisungen]

 

IF prüft den hinter IF angegebenen Ausdruck, der mathematisch korrekt sein muss, darauf, ob er logisch wahr ist. Wenn dieser Ausdruck logisch wahr ist, dann wird der Rest der Programmzeile ausgeführt, die hinter THEN steht. Hinter THEN können sich auch mehrere Befehle befinden, die durch einen Doppelpunkt getrennt werden. IF führt also stets sämtliche Befehle aus, die in derselben Zeile wie THEN stehen, wenn der Ausdruck hinter IF logisch wahr ist. Leider unterstützt Commodore BASIC in der Version 2.0 noch kein ENDIF und kann deshalb keine Funktionsblöcke, die mehrere Zeilen enthalten, abhängig von einer Bedingung ausführen. Ebenfalls noch nicht enthalten ist der ELSE-Befehl, der immer dann ausgeführt wird, wenn der hinter IF angegebene Ausdruck logisch falsch ist.

 

INPUT (Token 133)

Format:

INPUT [Variable]

 

INPUT liest eine Variable von der Tastatur ein. INPUT erkennt dabei automatisch, ob es sich bei der Zielvariablen um eine Zahl oder einen String handelt. Wenn Sie das Fragezeichen entfernen wollen, das INPUT vor jeder Eingabe anzeigt, müssen Sie eine Dateinummer (z.B. 1) wählen, und diese mit OPEN (siehe auch dort) an die Gerätenummer 0 binden.  Dadurch kann die entsprechende Variable mit INPUT# (siehe auch dort) von der Tastatur eingelesen werden, ohne ein Fragezeichen anzuzeigen. Viele Spiele (z.B. Adventures, Quiz- oder Ratespiele) verwenden deswegen INPUT# anstatt INPUT, oder auch die entsprechende Kernal-Funktion.

Die INPUT-Anweisung ist alles andere, als intuitiv, und überdies recht fehleranfällig. Wenn Sie eine falsche Eingabe machen (z.B. einen String anstatt einer Nummer eingeben), wird schlicht die Meldung REDO FROM START ausgegeben, ohne dass der Benutzer darüber informiert wird, was er denn nun falsch gemacht hat.

 

INPUT# (Token 132)

Format:

INPUT# [Dateinummer],[Variable]

 

INPUT# (das Nummernzeichen wird hier direkt ohne Leereichen an das Schlüsselwort INPUT angehängt) liest eine Variable aus einer Datei, die vorher mit OPEN geöffnet wurde. Die Dateinummern können Sie beliebig zwischen 1 und 15 wählen, allerdings müssen Sie vorher bei OPEN (siehe auch dort) zusätzlich eine Gerätenummer angeben.  Die Gerätenummern sind im Gegensatz zu den Dateinummern beim C64 fest vorgegeben. So ist z.B. die Dateinummer 0 für die Tastatur und die Dateinummer 3 oder 4 für den Drucker vorgesehen. Diskettenlaufwerke erhalten die Dateinummern 8, 9 und 10, zusätzliche Drucker oder Scanner die Dateinummern 5, 6 und 7. Das Kassettenlaufwerk muss allerdings die Gerätenummer 1 verwenden, und es kann auch nur ein einziges Kassettenlaufwerk angesprochen werden.

Leider ist die INPUT#-Anweisung alles andere als intuitiv, denn sämtliche Werte werden (zumindest im Fall einer Floppy-Diskette) standardmäßig als Strings abgelegt. Fließkommazahlen werden also nicht direkt als Bitmaske gespeichert (wie z.B. in C), sondern müssen in aufwendiger Weise in Zeichenketten konvertiert werden.

 

LET (Token 136)

Format: LET [Variable]=[Wert oder Ausdruck]

 

Es gibt ältere BASIC-Versionen, die durch das Schlüsselwort LET eine Variablendeklaration oder eine Wertzuweisung einleiten. Mathematisch entspricht dies in etwa einer Definition der Form „sei a=1234“. Commodore BASIC 2.0 ist kompatibel zu älteren BASIC-Versionen, indem es LET ignoriert, und anschließend sofort mit der Wertzuweisung fortfährt. Außerdem müssen sämtliche Variablen außer Arrays nicht extra deklariert werden, und haben standardmäßig nach Eingabe von RUN den Wert 0.

 

LIST (Token 155)

Format:

LIST [Start]-[Ende]

 

LIST kann mit keinem, einem oder zwei Parametern aufgerufen werden, wobei dann der zweite Parameter durch ein Minuszeichen vom ersten Parameter getrennt werden muss. Der Parameter Start bestimmt die Startzeile, ab der das Listing auf dem Bildschirm ausgegeben werden soll, Ende die Endzeile. Wenn das Minuszeichen und der zweite Parameter weggelassen werden, wird nur eine einzige Zeile ausgegeben. Wird der Parameter Ende wegelassen, wird das gesamte Listing ab der Startzeile ausgegeben. LIST alleine bewirkt die Ausgabe des gesamten Listings auf dem Bildschirm. Beim C64 gibt es leider von Haus aus keinen Texteditor, der sich alle ausgegebenen Zeilen merken kann, deshalb scrollt auch der Bildschirm nach Eingabe von LIST stets automatisch nach unten, wenn die Ausgabe über die letzte Zeile hinausgeht. Mit der Taste CTRL können Sie erreichen, dass das Listing langsamer ausgegeben wird, aber Sie können die Ausgabe selbst nur mit der RUN/STOP-Taste abbrechen. Die Lösung ist hier entweder, sich eine schnelle Reaktionszeit anzutrainieren, wenn dann die gesuchte Zeile vorbeiflimmert, oder aber ein Zusatzprogramm wie nano64 zu verwenden, dass einige Funktionen des Linux-Texteditors Nano auf dem C64 implementiert.

 

LOAD (Token 147)

Format:

LOAD "[Dateiname]",[Gerätenummer],[Funktionsnummer]

 

LOAD lädt ein Programm von Kassette, Diskette oder einem externen Speichermedium in den Hauptspeicher. Wenn Sie hinter LOAD keine Gerätenummer und keine Funktionsnummer angeben, dann wird die Datei von der Kassette geladen, und die Meldung PRESS PLAY ON TAPE erscheint. Nach Drücken der PLAY-Taste am Kassettenlaufwerk wird das Programm erst gesucht, und wenn es gefunden wurde, wird es in den Hauptspeicher geladen.

Wenn ein Programm allerdings von Diskette geladen werden soll, dann ist die Gerätenummer 8, oder für ein zweites und drittes Laufwerk 9 und 10. Wird zusätzlich keine Funktionsnummer angegeben, so wird das zu ladende Programm als BASIC-Listing betrachtet und an die Speicheradresse 2049 geladen. Ist die Funktionsnummer hingegen 1, so wird das zu ladende Programm als Maschinenprogramm betrachtet, und die ersten 2 Bytes in der Datei bestimmen die Startadresse.

Alle Funktionsnummern ab 2 werden von jedem Laufwerk anders behandelt. So gibt es z.B. Festplattenlaufwerke (die Gerätenummern sind hier oft frei wählbar), die zusätzliche Funktionen, wie z.B. das Einlesen oder Schreiben einzelner Blöcke anbieten. Leider gibt es beim C64 noch keinen Standard, wie es z.B. bei PCs der Fall ist. Wenn Sie also vorhaben, sich ein Festplattenlaufwerk für den C64 anzuschaffen, sollten Sie auf jeden Fall darauf achten, dass ein ausführliches Handbuch mit Programmbeispielen beiliegt.

 

NEW (Token 162)

Format:

NEW

 

NEW löscht das aktuelle BASIC-Programm, und entfernt auch sämtliche Arrays und Strings aus dem Speicher. Sämtliche Variablen werden auf den Wert 0 zurückgesetzt. NEW gibt ebenfalls den BASIC-Speicher wieder frei, deshalb kann NEW nicht mit einfachen Mitteln rückgängig gemacht werden. Es gibt hier durchaus Tricks, diese erfordern jedoch tiefgreifendes Wissen über die Speicherverwaltung beim C64.

 

NEXT (Token 130)

Format: NEXT [Variable]

 

NEXT bewirkt, dass eine Schleife erneut ausgeführt wird (BASIC springt dann zu der Anweisung direkt nach dem letzten FOR zurück). Die Schleife wird aber nur dann erneut ausgeführt, wenn die zu dem Schleifenzähler (angegeben als Variable direkt hinter NEXT) addierte Schrittweite noch unterhalb des Endwertes liegt, der in der letzten FOR-Anweisung angegeben wurde. Ist dies nicht der Fall, wird die Schleife abgebrochen.

NEXT hat jedoch eine kleine Eigenheit, die Sie kennen sollten, und die auch oft für kürzere Programmcodes benutzt wird: Wenn Sie die Schleifenzähler-Variable hinter NEXT weglassen, wird automatisch die Variable aus der letzten FOR-Anweisung als Zähler verwendet. Da sich die Schleifen eh nicht überlappen dürfen, kann im Endeffekt die Variable hinter NEXT stets weggelassen werden. Viele Programmierer tun genau dies, um Speicher zu sparen, jedoch führt dies zu unübersichtlicherem Code, da ein fremder Leser immer die weggelassenen Zähler im Kopf ergänzen muss.

 

NOT (Token 168)

Format:

IF NOT [Ausdruck] THEN [Ausdruck]

oder

[Variable]=NOT [Ausdruck]

 

NOT entspricht dem logischen Operator „nicht“, und kann dazu verwendet werden, einen logischen Ausdruck hinter IF (ein sogenanntes IF-Statement) zu invertieren. Eine Invertierung entspricht der Bedingung, dass ein Ausdruck eben nicht wahr, sondern logisch falsch ist. Ein Beispiel wäre z.B. die BASIC-Zeile

 

IF NOT A>10 THEN GOSUB 1000

 

,die identisch ist mit der BASIC-Zeile

 

IF A<=10 THEN GOSUB 1000

 

NOT kann jedoch auch auf eine Zahl (die dann aber als Ganzzahl betrachtet wird) angewendet werden. In diesem Fall werden die einzelnen Bits umgeklappt (invertiert). Ein Ausdruck, der den Wert -1 (alle Bits sind 1) zurückliefert, wird hierbei genauso als logisch wahr betrachtet, wie ein Ausdruck, der ungleich 0 ist. Ein Ausdruck, der den Wert 0 zurückliefert (alle Bits sind 0) wird als logisch falsch betrachtet. So liefert z.B.

 

A=0: PRINT A=0

 

den Wert -1 zurück, und

 

A=1: PRINT A=0

 

den Wert 0. NOT kann deshalb auch zusammen mit IF dazu verwendet werden, zu prüfen, ob in einer bestimmten Speicherstelle ein gestimmtes Bit gelöscht ist. Z.B. wird, wenn Sie Ihren Joystick an Port 1 in eine bestimmte Richtung bewegen, ein Schalter in den Stromkreis zwischen dem CIA-Chip und der Tastatur eingehängt, was dazu führt, dass der Spannungsabfall ein bestimmtes Bit in der Speicheradresse 56321 löscht. Wenn Sie den Joystick loslassen, wird das entsprechende Bit wieder gesetzt. Mit NOT und IF können Sie nun den Joystick in der folgenden Weise sehr einfach abfragen:

 

10 IF NOT PEEK(56321) AND 1 THEN GOSUB [Unterprogramm oben]

20 IF NOT PEEK(56321) AND 2 THEN GOSUB [Unterprogramm unten]

30 IF NOT PEEK(56321) AND 4 THEN GOSUB [Unterprogramm links]

40 IF NOT PEEK(56321) AND 8 THEN GOSUB [Unterprogramm rechts]

50 IF NOT PEEK(56321) AND 16 THEN GOSUB [Unterprogramm Feuer]

(AND bewirkt hier, dass nur das Bit abgefragt wird, dass Sie abfragen wollen)

 

ON (Token 145)

Format:

ON [Variable] GOTO/GOSUB [Zeile 1], [Zeile 2], … , [Zeile n]

 

ON ist die BASIC-Variante des C-Konstruktes switch…case. Folgendes Beispiel verdeutlicht dies:

 

C-Variante:

switch (A)

{

    case 0: printf(“A hat den Wert 0\n“); break;

    case 1: printf(“A hat den Wert 1\n“); break;

    case 2: printf(“A hat den Wert 2\n“); break;

    default: printf(“A hat keinen Wert zwischen 0 und 2\n“);

}

 

BASIC-Variante:

10 ON A GOTO 100,110,120

20 PRINT"A hat keinen Wert zwischen 0 und 2"

30 END

100 PRINT“A hat den Wert 0“:END

110 PRINT“A hat den Wert 1“:END

120 PRINT“A hat den Wert 2“:END

 

Während Sie in C beliebige Werte innerhalb des switch…case-Blocks benutzen können, fängt der BASIC-Befehl ON immer bei 0 an, und lässt auch keine Lücken in der Wertereihe zu. Wenn Sie an dieser Stelle einen Blick auf C werfen wollen, müssen Sich in den kostenlosen C-Compiler cc65 herunterladen, und auf eine C64-Diskette übertragen (bzw. das D64-Image in Ihren Emulator einbinden).

 

OPEN (Token 159)

Format:

OPEN [Dateinummer],[Gerätenummer],[Kanal],"[Kommando]"

 

OPEN öffnet eine Datei mit einer frei wählbaren Dateinummer zwischen 1 und 15, die Dateinummer 0 ist für die Tastatur reserviert. Die Gerätenummer ist beim C64 dagegen immer eindeutig, und kann auch am Gerät selbst durch DIP-Schalter eingestellt werden (bei mehreren Disketten-Stationen muss man dies sogar tun, damit sich diese nicht in die Quere kommen). Auch der Kanal ist hier direkt vom Gerät abhängig, und kann vom Benutzer nicht geändert werden.

Disketten-Laufwerke benutzen beispielsweise stets den Kanal 1 für die Übertragung von binären Daten, und Kanal 15 zur Übertragung von Befehlen. Der Umgang mit Dateinamen ist hier nicht immer intuitiv, und man muss sich im Zweifelsfall die Floppy-Kommandos merken. Um z.B. eine Datei für den Schreibzugriff anzulegen, wird folgender OPEN-Befehl verwendet:

 

OPEN 1,8,1,"[Dateiname]"

 

OPEN unterscheidet hier leider nicht zwischen Lese- und Schreibberechtigungen, deshalb können Sie auf einer einmal geöffneten Datei sowohl Lese- als auch Schreib-Operationen ausführen. Allerdings hat zumindest die alte 1541-Floppy hier die Eigenheit, dass die Datei nicht zum Schreiben geöffnet wird, wenn eine Datei mit demselben Namen schon existiert.

Wenn allerdings eine Datei erfolgreich neu angelegt wurde, können Daten mit PRINT# (siehe auch dort) in die entsprechende Datei geschrieben werden, allerdings müssen Sie sich in diesem Fall die Dateinummern, und auch, für welches Gerät diese gelten, gut merken. Sonst kann es durchaus geschehen, dass Sie Ihren Text versehentlich an den Drucker senden, anstatt ihn in eine Datei zu sichern. Ebenfalls sollten Sie darauf achten, dass Sie wirklich jede Datei, die Sie mit OPEN öffnen, auch wieder mit CLOSE schließen. Da nämlich BASIC für jede Datei extra einen Schreibe- und Lesepuffer anlegt, kann es durchaus zu der Fehlermeldung OUT OF MEMORY  ERROR kommen, wenn Sie zu viele Dateien gleichzeitig geöffnet haben.

Wenn Sie eine Datei nicht mehr benötigen, dann können Sie diese allerdings nicht einfach durch eine leere Datei ersetzen, die keine Zeichen enthält. Stattdessen müssen Sie den Kommando-Kanal der Floppy auf folgende Weise zusammen mit einer OPEN-Anweisung benutzen:

 

OPEN 1,8,15,"S:[Dateiname]"

CLOSE 1

 

Das S ist die Abkürzung für „scratch“. Dieses Wort bedeutet eigentlich „kratzen“, aber bei der 1541 und 1571 bedeutet das Wort „scratch“ löschen. Wahrscheinlich kommt „scratch“ daher, dass sich der Löschvorgang zumindest bei den ersten Floppys so anhörte, als ob der Lesekopf über die Diskette kratzt. In der Tat sind die alten Floppy-Stationen alles andere als leise. Sie sollten übrigens stets Scratch in Kombination mit einem anschließenden SAVE benutzen, um eine Datei durch eine neue Version zu ersetzen, denn folgender Befehl besitzt Bugs:

 

SAVE“@:[Dateiname]“ (sogenannter Replace-Bug bei 1541, 1571)

 

POKE (Token 151)

Format:

POKE [Speicheradresse],[Wert], wobei 0<=Wert<=255 gelten muss

 

POKE ist zusammen mit PEEK der wichtigste Befehl beim C64, denn mit POKE können die Inhalte von Speicheradressen geändert werden. Das Wort „poke“ bedeutet eigentlich „hämmern/klopfen“, und ist einer vielen freakigen Ausdrücken, die Programmierer im Laufe der Zeit erfunden haben. Das englische Wort „poke“ soll hierbei ausdrücken, dass mit einer Art Hammer ein Wert fest in eine Speicherstelle geknallt wird, und dort auch so lange bleibt, bis ein neuer POKE-Befehl angewendet wird. POKE schreibt einen Byte-Wert im Bereich zwischen 0 und 255 in eine bestimmte Speicheradresse, deshalb können Sie auch mit dem Befehl

 

POKE 1024,1

 

das Zeichen A in der linken oberen Ecke des Bildschirms erscheinen lassen. Dies ist deshalb möglich, weil der Zeichenspeicher die Adressen 1024-2023 belegt, und die Zeichen von oben nach unten angeordnet sind. Mit POKE können Sie jedoch auch auf jede beliebige andere Speicheradresse zugreifen, auch auf Register von Geräten, wie dem Grafikchip. So ändert z.B. die BASIC-Zeile

 

POKE 53280,0:POKE 53281,0

 

die Rahmenfarbe und die Hintergrundfarbe auf Schwarz, da der VIC die Register für die Rahmenfarbe und die Hintergrundfarbe in die Speicheradressen 53280 und 53281 einblendet. POKE akzeptiert jedoch auch jeden beliebigen BASIC-Ausdruck als Parameter, deshalb können Sie mit der richtigen Kombination von FOR, POKE, READ und DATA auch Maschinenprogramme direkt in den Speicher schreiben. Allerdings müssen Maschinenprogramme vorher mit einem Assembler-Monitor wie z.B. Hypra-Ass erstellt werden.

 

PRINT (Token 153)

Format:

PRINT [Variable 1][, oder + oder ;][Variable 2][, oder + oder ;] … [Variable n][, oder + oder ;]

oder

PRINT "[Text]"[, oder + oder ;] [Variable 1][, oder + oder ;]"[Text]" …

[, oder + oder ;] [Variable n]

 

PRINT schreibt den Inhalt einer oder mehrerer Variablen auf den Bildschirm, oder aber in eine entsprechende Datei, wenn die Ausgabe vorher umgeleitet wurde. Die Ausgaben von PRINT können Sie aber nur dann in einfacher Weise (also durch einen separaten Befehl) eine Datei umleiten, wenn Sie Commodore BASIC der Version 4.0 oder höher verwenden. Da PRINT normalerweise für Bildschirmausgaben benutzt wird, werden Sie PRINT auch fast immer in der Standard-Form benutzen.

PRINT ist der vielfältigste und flexibelste Befehl des BASIC-Befehlssatzes, da dieser sowohl Variablen, als auch Texte akzeptiert und sogar mit einer Kombination aus beidem umgehen kann. Das Einzige, dass Sie hierbei beachten müssen, ist, dass Fließkommazahlen und Strings nicht in gemischter Weise durch den +-Operator verknüpft werden können. PRINT hat jedoch einige Eigenheiten, die man sich einfach merken muss.

Die erste Eigenheit ist, dass die Ausgabe von Variablen, die in der Parameterliste durch Kommata getrennt werden, Zwischenräume beinhaltet. So bewirkt z.B. die Zeile

 

PRINT A,B,C

 

Die Ausgabe

 

1          2          3

 

(A=1, B=2 und C=3). Es werden hier also normalerweise 10 Zeichen zwischen den Zahlen eingefügt. Um den Abstand zu ändern, muss hier TAB() (siehe auch dort) benutzt werden.

Eine andere Eigenheit von PRINT ist, dass normalerweise immer ein Zeilenumbruch am Ende der ausgegebenen Zeile eingefügt wird, und der Cursor dadurch an den Anfang der nächsten Zeile springt. Um dies zu unterdrücken, kann am Ende der PRINT-Anweisung, oder aber auch am Ende eines Textes, der durch Anführungszeichen begrenzt wird, ein Semikolon angehängt werden. Auch für die Parameterliste von PRINT gilt diese Aussagen, so bewirkt z.B. die Zeile

 

PRINT A;B;C

 

die Ausgabe

 

 1 2 3

 

Allerdings ist es nicht in einfacher Weise möglich, die Abstände zwischen den Zahlen zu verändern, die durch ein Semikolon getrennt werden, was in Spielen, in denen Bildschirmausgaben genau platziert werden müssen, andere Lösungen benötigt, die nicht immer trivial sind.

Eine andere Eigenheit von PRINT ist, dass bei der Eingabe von Texten nach der Eingabe des ersten Anführungszeichens nicht mehr der Cursor gesteuert wird, sondern sämtliche Steuereichen direkt in Text übernommen werden. Auch PRINT selbst verwendet später diese Steuerzeichen bei der Textausgabe, was von großem Vorteil, aber auch von Nachteil sein kann. So können Sie z.B. die Textfarbe oder die Cursorposition direkt mit PRINT-Befehlen beeinflussen, aber leider auch viel falsch machen.

 

PRINT# (Token 152)

Format:

PRINT# [Dateinummer],[Variable]

 

PRINT# schreibt den Inhalt einer Variablen in eine Datei, die vorher mit OPEN (siehe auch dort) angelegt und einer Gerätenummer zugeordnet wurde. Da PRINT# ähnlich arbeitet, wie PRINT (siehe auch dort), können mit PRINT# beliebige Texte in eine Datei geschrieben werden, inklusive Zeilenumbruch und Steuerzeichen. Manche Drucker akzeptieren sogar die Steuerzeichen des PRINT#-Befehls, zumindest dann, wenn diese einen internen Puffer haben. Allerdings werden von PRINT# auch Fließkommazahlen als Text abgelegt, deshalb kann mit PRINT# nicht die Bitmaske von Fließkomma-Variablen direkt in eine Datei geschrieben werden. Allerdings können mit PRINT# einzelne Bytes auf die folgende Weise direkt in eine Datei geschrieben werden (DN ist die Dateinummer, BY der Byte-Wert in der Variablen BY):

 

PRINT#DN,CHR$(BY);

 

Das Semikolon muss hierbei unbedingt mit eingegeben werden.

 

READ (Token 135)

Format:

READ [Variable 1], [Variable 2], … , [Variable n]

 

Mit READ können Daten aus DATA-Zeilen in eine oder mehrere Variablen eingelesen werden. Allerdings müssen die Datentypen bei sämtlichen Variablen in der Liste hinter READ mit den Datentypen in der entsprechenden DATA-Zeile übereinstimmen. Für DATA-Zeilen benutzt BASIC einen separaten Speicherbereich, in dem sämtliche Werte abgelegt werden, die Sie mit DATA definieren. Da DATA-Zeilen auf diese Weise über das gesamte Programm verteilt werden können, sollte darauf geachtet werden, dass DATA-Zeilen der Übersichtlichkeit zuliebe in hohen Zeilennummern zusammenhängend abgelegt und mit Kommentaren versehen werden. READ kann zusammen mit der DATA-Anweisung (siehe auch dort) dazu benutzt werden, um Maschinenprogramme im Speicher abzulegen, die dann später mit SYS (siehe auch dort) von BASIC aus ausgeführt werden können. Auf diese Weise können Sie BASIC um Funktionen erweitern, die es nicht von Haus aus gibt. Dies geht z.B. so:

 

10 FOR I=S TO E:READ A:POKE I,A:NEXT I (S=Startadresse, E=Endadresse)

100 SYS [Startadresse] (Aurfuf des Maschinenprogramms von BASIC aus)

(Byte-Daten des Maschinenprogramms)

10000 DATA …

10010 DATA …

 

REM (Token 143)

Format:

REM [Kommentar]

 

Wenn BASIC das Token REM findet, dann wird der Rest der Zeile ignoriert. Deshalb kann REM dazu verwendet werden, Kommentare in das Programm einzufügen. Um die Funktionsfähigkeit einzelner Zeilen zu testen (z.B. ob der Programmfluss durch eine bestimmte Zeile behindert wird), wird oft die Technik des Auskommentierens verwendet. Dies bedeutet, dass der offensichtlich fehlerhaften Zeile ein REM vorangestellt wird. Wenn danach das Programm wieder einwandfrei funktioniert, kann die fehlerhafte Zeile anschließend korrigiert werden.

 

RESTORE (Token 140)

Format:

RESTORE

 

RESTORE setzt den Datenzeiger für die READ-Anweisung (siehe auch dort) zurück auf den ersten Eintrag in der ersten DATA-Zeile im Programm. Auf diese Weise können DATA-Zeilen auch mehrmals eingelesen werden, was aber nicht oft benutzt wird.

 

RETURN (Token 142)

Format:

RETURN

 

RETURN wird dazu verwendet, um ein Unterprogramm, das vorher mit GOSUB aufgerufen wurde, ordnungsgemäß zu beenden. BASIC verwendet hierzu einen internen Stapel, auf dem sämtliche Zeilennummern, die GOSUB im Laufe der Zeit verwendet, abgelegt werden. RETURN zieht also die Zeile im Programm vom Stapel, die der letzte GOSUB-Befehl verwendet hat, sowie die Position in der Zeile direkt hinter dem GOSUB. Die Verwendung eines internen Stapels für GOSUB führt natürlich dazu, dass Unterprogramme auch wieder Unterprogramme aufrufen können, allerdings ist in diesem Fall darauf zu achten, die richtige Rückkehr-Reihenfolge einzuhalten. Ein RETURN-Befehl, der ohne ein voriges GOSUB aufgerufen wird, führt zu der Fehlermeldung

 

?RETURN WITHOUT GOSUB  ERROR

 

und eine zu tiefe Verschachtelung (mehr als 10 GOSUB-Befehle ohne RETURN) sogar zu OUT OF MEMORY. Diesen Zustand (einen sogenannten Stack Overflow) sollten Sie vermeiden, da dadurch das System instabil werden kann.

 

RUN (Token 138)

Format:

RUN [Zeilennummer]

 

RUN startet ein Programm ab einer bestimmten Zeilennummer. Wenn die Angabe der Zeilennummer fehlt, wird das Programm ab der ersten Programmzeile, also von Anfang an, ausgeführt. RUN führt intern einen GOTO-Befehl aus und setzt anschließend den BASIC-Interpreter-Status von „Direktmodus“ auf „Ausführungsmodus“. Im Gegensatz zu GOTO löscht aber RUN sämtliche Arrays, Strings, und Schleifenzähler, und setzt sämtliche Inhalte der Variablentabelle auf den Wert 0 zurück.

 

SAVE (Token 148)

Format:

SAVE "[Dateiname]",[Gerätenummer],[Funktionsnummer]

 

SAVE speichert ein Programm auf Kassette, Diskette oder einem externen Speichermedium. Wenn Sie hinter SAVE keine Gerätenummer und keine Funktionsnummer angeben, dann wird die Datei auf dem Kassettenlaufwerk abgelegt, und die Meldung

 

PRESS PLAY AND RECORD ON TAPE

 

erscheint. Nach Drücken der PLAY-Taste zusammen mit der RECORD-Taste am Kassettenlaufwerk wird dann das Programm erst gesucht, und wenn es gefunden wurde, wird es zusammen mit der entsprechenden Meldung überschrieben. In diesem Fall muss allerdings sorgfältig darauf geachtet werden, dass vor dem nächsten Programm noch genug Freiraum auf dem Band ist.

Wenn ein Programm allerdings auf einer Diskette gespeichert werden soll, dann ist die Gerätenummer 8, oder für ein zweites und drittes Laufwerk 9 und 10. Wird dann zusätzlich keine Funktionsnummer angegeben, so wird das zu speichernde Programm als BASIC-Listing betrachtet, das an der Speicheradresse 2049 anfängt. Die Endadresse bestimmt dann die letzte BASIC-Zeile im Listing. Leider kann mit dem SAVE-Befehl für die BASIC-Version 2.0 nicht so einfach ein Maschinenprogramm auf der Diskette abgelegt werden, da der SAVE-Befehl in diesem Fall die Anfangs- und Endadresse nicht kennt. Sie müssen also, um Maschinenprogramme Byte für Byte auf einer Diskette abzulegen, den Befehl PRINT# (siehe auch dort) verwenden.

Alle Funktionsnummern ab 2 werden (wie auch bei LOAD) von jedem Laufwerk anders behandelt. So gibt es z.B. Festplattenlaufwerke (die Gerätenummern sind hier oft frei wählbar), die zusätzliche Funktionen, wie das Einlesen oder Schreiben einzelner Blöcke anbieten. Leider gibt es beim C64 noch keinen Standard, wie es z.B. bei PCs der Fall ist. Wenn Sie also vorhaben, sich ein Festplattenlaufwerk für den C64 anzuschaffen, sollten Sie auf jeden Fall darauf achten, dass ein ausführliches Handbuch mit Programmbeispielen beiliegt.

 

SPC()-Funktion (Token 166)

Format:

SPC([Anzahl Leerzeichen])

 

SPC wird zusammen mit PRINT dazu benutzt, eine bestimmte Anzahl Leerzeichen auszugeben. Nach Ausgabe der Leerzeichen verbleibt SPC in der aktuellen Cursorzeile und führt keinen Zeilenumbruch aus. Während also die TAB-Funktion (siehe auch dort) genau an die x-Position in der entsprechende Cursorzeile springt, die als Parameter übergeben wurde, und auch eventuell davorstehende Zeichen nicht überschreibt, gibt SPC wirklich Leerzeichen aus. SPC überschreibt also im Gegensatz zu TAB eventuell Zeichen in der aktuellen Cursorzeile. SPC ist somit die Abkürzung für folgende BASIC-Zeile:

 

PRINT“[Leerzeichen einer bestimmten Anzahl]“;

 

STEP (Token 169)

Format:

STEP [Schrittweite]

 

STEP ist Bestandteil eines FOR-Schleifenkopfes und kann nicht für sich alleine verwendet werden. STEP legt die Schrittweite fest, die beim nächsten NEXT-Befehl zu dem aktuellen FOR-Schleifenzähler addiert werden soll.

 

STOP (Token 144)

Format:

STOP

 

STOP beendet das Programm, jedoch merkt sich BASIC in diesem Fall die nächste Programmzeile, die dem STOP-Befehl folgt. Das bedeutet, dass STOP die gleiche Wirkung hat, wie die Taste RUN/STOP, und das Programm anschließend mit CONT (siehe auch dort) fortgesetzt werden kann. STOP löscht auch nicht die Variablentabelle oder die Arrays, deshalb können Sie sich mit PRINT Testwerte für einzelne Variablen anzeigen lassen. STOP wird deshalb oft dazu benutzt, Fehler zu finden, die an einer bestimmten Stelle im Programm vermutet werden. Leider ist diese Methode für längere Programme nicht so gut geeignet, weswegen es hier auch professionelle Lösungen, sogenannte Tracer gibt. Ein Tracer kann nach Wunsch das laufende Programm unterbrechen und, z.B. die Variablentabelle oder den Inhalt einzelner Arrays anzeigen. Wenn dann die entsprechenden Werte gefunden wurden, kann das Programm anschließend ganz normal fortgesetzt werden. Auch der ursprüngliche Bildschirm wird hierbei wieder hergestellt.

Der Königsweg ist jedoch ein Debugger, mit dem Programme sogar schrittweise ausgeführt werden können. Professionelle Debugger zeigen sogar die Registerinhalte und Flags des Prozessors an, sind aber nur für absolute Profis geeignet.

 

SYS (Token 158)

Format:

SYS [Programmadresse]

 

SYS dient ursprünglich dazu, die Maschinensprache-Routinen des Kernals (das ist das Betriebssystem des C64, das sich im Kernal-ROM befindet) auch von BASIC aus nutzen zu können. Dies geschieht einfach dadurch, dass der BASIC-Interpreter (der ja ebenfalls nur ein Programm ist) von SYS unterbrochen wird, damit der Prozessor an die Adresse springen kann, die hinter SYS steht. Da sich das Kernal-ROM niemals ändert, stehen auch die Betriebssystem-Routinen immer an derselben Stelle, und können so von BASIC aus aufgerufen werden. Mit SYS können Sie allerdings auch Programme ausführen, die nicht im Kernal-ROM stehen, vorausgesetzt, Sie haben die einzelnen Bytes, die das Programm ausmachen, vorher in den Speicher geschrieben. Die Verwendung von SYS ist allerdings nicht trivial und erfordert zwei Dinge: Erstens müssen Sie sich in der Assembler-Programmierung auskennen, und zweitens müssen Sie auch einen Assembler-Monitor oder Ähnliches besitzen, mit dem Sie Maschinensprache-Programme erstellen können.

 

TAB()-Funktion (Token 163)

Format:

TAB([x-Position des nächsten PRINT-Befehls])

 

TAB kann zusammen mit PRINT dazu benutzt werden, um einen Text an einer bestimmten Stelle in der aktuellen Cursorzeile auszugeben. Das heißt, TAB kann an eine bestimmte x-Position (0=ganz links) springen, und den auszugebenden Text dort platzieren. Um z.B. das Wort „HALLO“ an der 10. Position in der aktuellen Zeile auszugeben, verwenden Sie folgende BASIC-Zeile:

 

PRINT TAB(9);”HALLO”

 

THEN (Token 167)

 

THEN ist Bestandteil eines IF-THEN-Konstrukts und kann nicht für sich alleine verwendet werden. Die hinter THEN angegebenen Anweisungen werden immer dann ausgeführt, wenn der hinter dem dazugehörigen IF angegebene Ausdruck logisch wahr ist.

 

TO (Token 164)

 

TO ist Bestandteil eines FOR- NEXT- Blocks, und kann nicht für sich alleine verwendet werden. TO gibt den Endwert an, ab der eine FOR-Schleife nicht mehr ausgeführt wird.

 

VERIFY (Token 149)

Format:

VERIFY "[Dateiname]",[Gerätenummer],[Funktionsnummer]

 

SAVE speichert ein Programm auf Kassette, Diskette oder einem externen Speichermedium. VERIFY dagegen überprüft, ob die Datei richtig abgelegt wurde, indem der Speicherinhalt mit dem Inhalt der Datei auf dem Datenträger verglichen wird. Schlägt dieser Vergleich fehl, wird die Meldung

 

?VERIFY  ERROR

 

ausgegeben. VERIFY wird eigentlich nur bei Kassettenlaufwerken verwendet, weil diese (z.B. bei Knicken im Band) nicht immer zuverlässig arbeiten. Bei Floppy-Stationen oder externen Festplatten wird VERIFY kaum noch benutzt, es sei denn, es wird ein Fehler auf dem Datenträger vermutet, oder aber das Sichern einer bestimmten Datei ist so wichtig, dass Sie stets überprüfen müssen, dass alles glatt verlaufen ist.

Wenn Sie ein Programm vorher auf einer Diskette abgespeichert haben, dann ist die Gerätenummer 8, oder für ein zweites und drittes Laufwerk 9 und 10. Wird dann zusätzlich keine Funktionsnummer angegeben, so wird das zu speichernde Programm als BASIC-Listing betrachtet, das an der Speicheradresse 2049 anfängt. Die Endadresse bestimmt dann die letzte BASIC-Zeile im Listing, es werden also von VERIFY in diesem Fall sämtliche Bytes im BASIC-Programm mit den Bytes der Datei auf dem Datenträger verglichen. Leider gelten die letzten Aussagen nicht für Maschinenprogramme, denn VERIFY kennt in diesem Fall die Startadresse des Programms nicht.

Alle Funktionsnummern ab 2 werden (wie auch bei LOAD) von jedem Laufwerk anders behandelt. So gibt es z.B. Festplattenlaufwerke (die Gerätenummern sind hier oft frei wählbar), die zusätzliche Funktionen, wie z.B. das Einlesen oder Schreiben einzelner Blöcke anbieten. Leider gibt es beim C64 noch keinen Standard, wie es z.B. bei PCs der Fall ist. VERIFY funktioniert deshalb auch nicht zusammen mit externen Laufwerken, weil es nicht vorhersehbare Wirkungen auf externe Dateisysteme hat, die nicht von Commodore stammen. So können z.B. Laufwerke, die mit dem Microsoft-Dateisystem FAT oder NTFS formatiert wurden, nach der Verwendung von VERIFY unlesbar werden.

 

WAIT (Token 146)

Format:

WAIT [Speicheradresse],[Wert]

 

WAIT wartet so lange, bis sich in einer bestimmten Speicheradresse ein bestimmter Wert befindet. Solange dies nicht der Fall ist, wird das Programm allerdings unterbrochen. Deshalb wird WAIT oft dazu verwendet, auf eine bestimmte Taste zu warten, die, wenn sie dann gedrückt wurde, als Wert in der Speicheradresse 640 auftaucht. Allerdings müssen Sie in diesem Fall den Tastaturpuffer, in dem BASIC die letzten 10 gedrückten Tasten ablegt, selbst verwalten, was nicht ganz trivial ist.

 

4.2   Operatoren

 

Operatoren werden intern durch Tokens ab der Token-Nummer 170 dargestellt, das heißt, dass der BASIC-Interpreter immer sämtliche Operatoren in ASCII-Zeichen mit Werten über 128 verwandelt, nachdem Sie eine BASIC-Zeile eingegeben haben. Dies gilt allerdings nicht für das Schlüsselwort NOT, das gesondert betrachtet wird (Token 168). Die folgenden Operatoren werden nun durch Tokens mit einer Token-Nummer zwischen 170 und 179 ersetzt:

 

·       Operator + (Token 170)

·       Operator – (Token 171)

·       Operator * (Token 172)

·       Operator / (Token 173)

·       Operator ^ (Token 174)

·       Operator AND (Token 175)

·       Operator OR (Token 176)

·       Operator > (Token 177)

·       Operator = (Token 178)

·       Operator < (Token 179)

 

Warum BASIC so vorgeht, kann nicht auf einfache Weise erklärt werden. Ein Grund ist, dass ein Programmierer oft Bit-Verknüpfungen und logische Aussagen kombinieren muss, und deshalb auch die Operatoren Schlüsselwörter sein müssen, die durch ein gesetztes Bit 7 gekennzeichnet sind. Außerdem rufen Operatoren intern Funktionen auf, und diese Funktionen arbeiten auch ausschließlich mit der Verknüpfung von Tokens. Klammern, die keine Tokens sind, dienen dann zur Gruppierung und Strukturierung von Teilausdrücken.

 

4.3   Standardfunktionen

 

Die meisten Funktionen, die Sie im Laufe der Zeit benutzen, sind in BASIC fest verdrahtet, mit Ausnahme der benutzerdefinierten Funktion, die durch die USR()-Funktion definiert werden kann. Deshalb werden auch den meisten Funktionen Tokens zugeordnet, außer eben den Funktionen, die Sie mit DEF FN definiert haben, und der USR()-Funktion. Hierbei gilt die folgende Regel: Alle Tokens mit einer Nummer von >=180 gelten als Funktion. In diesem Fall müssen deshalb die Parameter von runden Klammern eingeschlossen werden. Parameter sind hierbei die Werte, mit denen Funktionen beginnen, ihre Arbeit aufzunehmen. Man spricht in diesem Fall auch von Initialisierungswerten für die entsprechenden Algorithmen, die sich hinter einer Funktion verbergen. So führt z.B. der Aufruf der Funktion SIN() mit dem Parameter 2 zu Berechnung einer Taylor-Reihe, die das Ergebnis so lange dem Sinus von 2 annähert, bis die ersten 8 Nachkommastellen korrekt berechnet sind. Danach bricht der Algorithmus ab, und gibt den Sinus von 2 als Rückgabewert an eine Variable oder an PRINT weiter.

Funktionen nehmen in BASIC genau einen Parameter entgegen und geben auch genau einen Rückgabewert zurück. Dies gilt aber nicht für die String-Funktionen, deshalb behandle ich die String-Funktionen auch separat. Schauen wir uns zunächst die Funktionen an, die mit Fließkommazahlen arbeiten. Da hier der Funktionsparameter eine Zahl, eine Variable, oder auch ein komplexer BASIC-Ausdruck sein kann (der allerdings kein NEXT, IF, THEN oder FOR enthalten darf), gebrauche ich im Folgenden stets das Wort „Ausdruck“, und nicht das Wort „Zahl“ oder „Variable“. Da Funktionen Parameter in runden Klammern entgegennehmen, gebe ich bei den Funktionen die runden Klammern mit an.

 

ABS()-Funktion (Token 182)

Format:

[Variable]=ABS([Ausdruck])

 

ABS() liefert den Absolutwert einer Zahl oder eines Ausdrucks zurück. Dies bedeutet, dass das Vorzeichen entfernt wird. ABS(0) ist hierbei 0.

 

ATN()-Funktion (Token 193)

[Variable]=ATN([Ausdruck])

 

ATN() berechnet den Arcus-Tangens und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Für den Arcus-Tangens muss hier das Bogenmaß benutzt werden.

 

COS()-Funktion (Token 190)

[Variable]=COS([Ausdruck])

 

COS() berechnet den Cosinus und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Für den Cosinus muss hier das Bogenmaß benutzt werden. Die Umkehrfunktion von COS() gibt es allerdings nicht, da der Algorithmus für diese Berechnung auf dem 6510-Prozessor nicht effizient genug umgesetzt werden kann. Aber auch die COS()-Funktion ist im Gegensatz zur SIN()-Funktion schon sehr langsam in der Ausführungsgeschwindigkeit. Dies liegt vor Allem daran, dass die Taylor-Reihe, die den Cosinus berechnet, sehr viel langsamer konvergiert, als die Taylor-Reihe, die den Sinus berechnet. Hier gibt es einige Optimierungsmöglichkeiten, die aber in der Version 2.0 von BASIC noch nicht enthalten sind.

 

EXP()-Funktion (Token 189)

Format:

[Variable]=EXP([Ausdruck])

 

EXP() berechnet ex und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Die Umkehrfunktion von EXP() ist LOG() (also verwendet LOG() immer die Eulerzahl e als Basis und entspricht ln(x)). Logarithmen und Exponenten mit anderen Basen als e können jedoch nicht effizient berechnet werden. Sie können zwar beim ^-Operator beliebige Basen verwenden, allerdings leidet darunter Ihre Ausführgeschwindigkeit enorm. Sie können dieses Manko auch nicht in einfacher Weise beheben, da der 6510-Prozessor zu langsam ist, um in effizienter Weise Exponentialfunktionen oder Logarithmen zu berechnen.

 

FRE()-Funktion (Token 184)

Format:

[Variable]=FRE([Ausdruck])

 

FRE() liefert unabhängig vom übergebenen Ausdruck oder Wert die Größe des noch freien BASIC-Speichers zurück. Allerdings ist der zurückgegebene Wert negativ, damit dieser zu dem Gesamtspeicher (z.B. 64k=65536 Bytes) addiert werden kann, um auf diese Weise einen aussagekräftigen positiven Wert zu erhalten. Der C64 liefert also direkt nach dem Einschalten ohne installierten Zusatzspeicher mit der BASIC-Zeile

 

PRINT 65536-FRE(0)

 

den Wert 38.911 zurück. Mit 64kB zusätzlichem Speicher ist dieser Wert 104.439. Dies gilt allerdings nur, wenn Sie eine erweiterte BASIC-Version verwenden, die den zusätzlichen Speicher auch nutzen kann, sonst erscheint hier ebenfalls der Wert 38.911. Wenn Sie den 6510-Prozessor benutzen, dann können Sie mit zusätzlichen Speichermodulen bis zu 64 kB zusätzlichen Seicher ansprechen, wenn Sie die Möglichkeit haben, einen 68000-Prozessor in einen separaten Sockel einzubauen, können bis zu 1 MB Speicher benutzt werden, allerdings nur mit aktuelleren Kernal-Versionen oder separaten Treibern. Der beste und schnellste Prozessor ist hier sicherlich der H65A-Prozessor, der aber nicht mehr leicht zu bekommen ist, und außerdem inkompatibel zum 6510-Prozessor ist. Der H65A-Prozessor verwendet eine reine 32-Bit-Architektur, und kann auf den gesamten Speicher direkt zugreifen.

 

INT()-Funktion (Token 181)

Format:

[Variable]=INT([Ausdruck])

 

INT() liefert den ganzzahligen Anteil einer Zahl zurück, bzw. den in eine ganze Zahl konvertierten Wert eines Ausdrucks. INT() schneidet jedoch den Kommaanteil einer Zahl einfach ab, ohne auf oder ab zu runden. INT() wird oft zusammen mit anderen Funktionen verwendet, oder auch innerhalb komplexer, verschachtelter Ausdrücke benutzt. Mit der folgenden BASIC-Zeile kann z.B. ein Wurf mit einem Würfel simuliert werden (die höchst mögliche Augenzahl ist hierbei Z):

 

W=INT(Z*RND(1))+1

(Z=6 simuliert einen 6-er-Würfel, 8 einen 8-er-Würfel, und 20 einen 20-er-Würfel)

 

LOG()-Funktion (Token 188)

Format:

[Variable]=LOG([Ausdruck])

 

LOG() berechnet den natürlichen Logarithmus zur Basis e (=2,781828) und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Leider gibt es in BASIC der Version unter 4.0 keinen Logarithmus zur Basis 2 oder 10. Dies liegt daran, dass auf dem 6510-Prozessor nur der Logarithmus zur Basis e effizient berechnet werden kann.

 

PEEK()-Funktion (Token 194)

Format:

[Variable]=PEEK([Speicheradresse])

 

PEEK() liest eine Speicheradresse aus, und liefert den 8-Bit-Wert an eine Variable oder an PRINT zurück. Anstatt der Speicheradresse kann auch ein BASIC-Ausdruck eingesetzt werden, der die Adresse berechnet, allerdings dürfen in diesem Fall die Werte, die der Ausdruck darstellt, die höchst mögliche Speicheradresse nicht überschreiten. Diese ist normalerweise 65.535, und kann nur in Ausnahmefällen überschritten werden, z.B. durch einen anderen Prozessor, als den 6510. Auch bei zusätzlich installiertem Speicher macht BASIC hier Probleme, da der 6510-Prozessor maximal Adressen bis 65.535 verwalten kann. Es gibt Zusatzmodule, die zusätzlich die PEEK()-Funktion modifizieren, leider kann man sich nicht immer darauf verlassen, dass dies fehlerfrei funktioniert.

 

POS()-Funktion (Token 185)

Format:

[Variable]=POS([Ausdruck])

 

POS() kann nur einen Wert zurückgeben, nämlich die aktuelle Spalte, in der sich der Cursor befindet. Leider kann die aktuelle Bildschirmzeile nicht so einfach festgestellt werden, und Sie müssen in diesem Fall ein entsprechendes Maschinenprogramm für eine USR()-Funktion erstellen, die dies leistet.

 

RND()-Funktion (Token 187)

Format:

[Variable]=RND([Ausdruck])

 

RND ist die Abkürzung von „random number“. RND() liefert also eine Zufallszahl zurück. Allerdings liegt der zurückgegebene Wert im Bereich zwischen 0 und 1, und muss deshalb fast immer skaliert werden. Ferner verwendet der Algorithmus, der hinter RND() steckt, eine spezielle Zahlenreihe, und benötigt deshalb einen Startwert. Dieser Startwert, der sogenannte Seed, bestimmt jedoch alle weiteren Ergebnisse, deshalb liefert RND() direkt nach dem Einschalten des C64 auch stets dieselben Werte zurück. Sie können beim C64 den Startwert zwar nicht direkt beeinflussen, was Sie jedoch tun können, ist RND() zunächst mit dem Parameter 0 aufzurufen. In diesem Fall wird der Wert der Uhr dazu verwendet, um den Startwert neu einzustellen, was dazu führt, dass die Werte nun nicht mehr so leicht vorhersagbar sind. Um den Benutzer in einem Spiel schwer vorhersagbare Augenzahlen würfeln zu lassen, können Sie wie folgt vorgehen:

 

10 PRINT"BITTE EINE TASTE DRUECKEN"

20 GET A$: IF A$="" THEN 20

30 T=RND(0): REM diesen Wert verwerfen

40 W=INT(6*RND(1))+1: REM hier wird wirklich gewürfelt

 

Warnung: Verwendung von RND() für kryptographische Algorithmen

Verwenden Sie RND() keinesfalls für Verschlüsselungsverfahren oder andere kryptographische Algorithmen, die Zufallszahlen z.B. für die Schlüsselerzeugung benutzen. Der RND()-Algorithmus ist hierfür zu schwach, und die Ausgabewerte sind trotz der Benutzung der internen Uhr und weiterer Tricks für einen professionellen Angreifer immer noch gut vorhersagbar. Diese Aussage gilt auch für Zufallszahlen, die Sie mit den SID-Registern erzeugen, denn auch die Ausgabewerte des für die Rausch-Wellenform verwendeten LFSR (linear feedback shift register) sind für wirklich professionelle Angreifer leicht vorhersagbar. Für Spiele ist der interne Zufallsgenerator aber oft bestens geeignet.

 

SGN()-Funktion (Token 180)

Format:

[Variable]=SGN([Ausdruck])

 

SGN() wertet den in Klammern stehenden Ausdruck aus, und gibt die Zahl -1 zurück, wenn der Ausdruck einen negativen Wert hat. SGN(0) liefert hingegen 0, und alle positiven Ausdrücke den Wert +1 zurück. SGN() ist die Abkürzung des englischen Wortes „sign“ (Vorzeichen).

 

SIN()-Funktion (Token 191)

[Variable]=SIN([Ausdruck])

 

SIN() berechnet den Sinus und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Für den Sinus muss hier das Bogenmaß benutzt werden. Die Umkehrfunktion von SIN() gibt es allerdings nicht, da der Algorithmus für diese Berechnung auf dem 6510-Prozessor nicht effizient genug umgesetzt werden kann. Aber auch die SIN()-Funktion ist schon sehr langsam in der Ausführungsgeschwindigkeit.

 

SQR()-Funktion (Token 186)

Format:

[Variable]=SQR([Ausdruck])

 

SQR() berechnet die Quadratwurzel (squareroot) und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Um x3 zu berechnen, muss allerdings der ^-Operator verwendet werden, mit dem die n. Wurzel wie folgt berechnet werden kann:

 

X=A^(1/n)

 

BASIC gibt aber stets nur reelle Zahlen zurück, das ist also hier die erste reelle Lösung der eigentlich n-1 Lösungen, die es bei der n. Wurzel normalerweise gibt. Das C64-BASIC kann übrigens überhaupt keine komplexen Zahlen berechnen, und SQR(-1) endet z.B. mit der Meldung

 

?ILLEGAL QUANTITY  ERROR

 

TAN()-Funktion (Token 192)

[Variable]=TAN([Ausdruck])

 

TAN() berechnet den Tangens und liefert das Ergebnis mit einer Genauigkeit von mindestens 8 Nachkommastellen zurück. Für den Tangens muss hier das Bogenmaß benutzt werden. Die Umkehrfunktion von TAN() gibt es hier allerdings nicht, da der Algorithmus für diese Berechnung auf dem 6510-Prozessor nur sehr effizient umgesetzt kann. Aber auch die TAN()-Funktion ist sehr langsam in der Ausführungsgeschwindigkeit.

 

USR()-Funktion (Token 183)

Format:

[Variable]=USR([Ausdruck])

 

Die USR()-Funktion ist normalerweise nicht belegt, und liefert bei Benutzung nur die Fehlermeldung SYNTAX ERROR zurück. Sie können dieses Verhalten auch nur ändern, indem Sie ein Maschinenprogramm verwenden, das folgendes leistet:

Der Wert des BASIC-Ausdrucks, der innerhalb der runden Klammern als Parameter übergeben wird, wird automatisch in den Gleitpunkt-Akkumulator kopiert (Speicheradresse 80-83). Zunächst wird also der Wert in den Speicheradressen 783 und 784 als 16-Bit-Zahl ausgelesen und als Sprungadresse in die USR()-Funktion benutzt. Wenn dieser Zeiger nicht verändert wurde, springt BASIC direkt in die Routine, die die Meldung SYNTAX ERROR ausgibt. Wenn jedoch der Zeiger in den Adressen 783 und 784 vorher umgebogen wurde, wird eine entsprechende Benutzerroutine ausgeführt, die natürlich vorher mit DATA-Anweisungen und READ in den Speicher geschrieben werden muss.

Eine eventuelle Benutzerroutine muss nun die Fließkommazahl in den Adressen 80-83 auswerten, mit diesem Wert einen benutzerdefinierten Algorithmus ausführen, und das Ergebnis anschließend auch wieder als Fließkommazahl in den Adressen 80-83 ablegen. Dieser Vorgang ist nicht trivial und erfordert tiefgreifende Kenntnisse in Maschinensprache.

 

4.4   String-Funktionen

 

String-Funktionen sind explizit zur Manipulation von String-Variablen entwickelt worden, deshalb werden diese auch separat behandelt. In BASIC 2.0 gibt es die folgenden String-Funktionen:

 

ASC()-Funktion (Token 198)

Format:

[Variable]=ASC([String])

 

ASC() gibt für das erste Zeichen einer Zeichenkette, die sich in einer String-Variablen befinden muss, den entsprechenden ASCII-Code als Zahl zurück. ASC() kann auch zusammen mit in einer PRINT-Anweisung verwendet werden.

 

PRINT ASC(“A“)

 

gibt die Zahl 65 aus.

 

CHR$()-Funktion (Token 199)

Format:

[String]=CHR$([ASCII-Code])

 

CHR$() erzeugt eine Zeichenkette der Länge 1, die das Zeichen enthält, das durch den entsprechenden ASCII-Code angegeben wird. CHR$() ist also die Umkehrung der ASC()-Funktion. CHR$() akzeptiert als Parameter auch beliebig komplexe BASIC-Ausdrücke, die aber numerisch sein müssen und keine Werte zurückliefern dürfen, die <0 oder >255 sind. Andernfalls erscheint die Meldung

 

?ILLEGAL QUANTITY  ERROR

 

 CHR$() kann auch zusammen mit in einer PRINT-Anweisung verwendet werden.

 

PRINT CHR$(65)

 

gibt das Zeichen A aus.

 

LEFT$()-Funktion (Token 200)

Format:

[Ziel-String]=LEFT$([Quell-String],[End-Position])

 

LEFT$() speichert den linken Teil eines Quell-Strings in einem Ziel-String. Hierbei kann der Ziel-String auch identisch mit dem Quell-String sein, was dann dazu führt, dass der rechte Teil entfernt wird. LEFT$() ist eine der wenigen Funktionen, die zwei Parameter übergeben bekommt, nämlich einen Quell-String und einen BASIC-Ausdruck, der die Position angibt, bis zu der der Quell-String von links aus kopiert werden soll.

 

LEN()-Funktion (Token 195)

Format:

[Variable]=LEN([String])

 

LEN() gibt die Länge eines Strings (dies ist die Anzahl Zeichen im String) an eine Variable zurück. Diese Variable muss eine normale Fließkomma-Variable sein, ansonsten wird die Fehlermeldung

 

?TYPE MISSMATCH  ERROR

 

ausgegeben. Diese Meldung wird auch dann ausgegeben, wenn der LEN()-Funktion kein String übergeben wird. LEN() kann auch Strings verwalten, die sich in verschachtelten Ausdrücken befinden, deshalb ist z.B. folgende BASIC-Zeile erlaubt:

 

L=LEN(A$+B$+LEFT$(C$,5))

 

MID$()-Funktion (Token 202)

Format:

[Ziel-String]=MID$([Quell-String],[Start-Position],[Länge])

 

MID$() speichert den mittleren Teil eines Quell-Strings in einem Ziel-String. Hierbei kann der Ziel-String auch identisch mit dem Quell-String sein, was dann dazu führt, dass der rechte und linke Anteil entfernt wird. MID$() ist die einzige Funktion, die drei Parameter übergeben bekommt, nämlich einen Quell-String, einen BASIC-Ausdruck, der die Start-Position angibt, und einen BASIC-Ausdruck der die Anzahl an Zeichen angibt, die aus dem Quell-String kopiert werden sollen. Einzelne Zeichen ab der Position I können mit der folgenden BASIC-Zeile ausgelesen werden:

 

B$=MID$(A$,I,1)

 

RIGHT$()-Funktion (Token 201)

Format:

[Ziel-String]=RIGHT$([Quell-String],[End-Position])

 

RIGHT$() speichert den rechten Teil eines Quell-Strings in einem Ziel-String. Hierbei kann der Ziel-String auch identisch mit dem Quell-String sein, was dann dazu führt, dass der linke Teil entfernt wird. RIGHT$() ist eine der wenigen Funktionen, die zwei Parameter übergeben bekommt, nämlich einen Quell-String und einen BASIC-Ausdruck, der die Position angibt, bis zu der der Quell-String von rechts aus kopiert werden soll.

 

STR$()-Funktion (Token 196)

Format:

[String]=STR$([Fließkomma-Variable])

 

STR$() wandelt den Wert einer Fließkomma-Zahl oder Standardvariablen in einen String-Ausdruck um. Mit STR$() können also Zahlen in Strings umgewandelt werden, um anschließend z.B. für die Bildschirmausgabe mit PRINT weiterverarbeitet werden zu können. STR$() ist die einzige Funktion, die PRINT-Ausgaben spieletauglich auf dem Bildschirm ausgeben kann, z.B. in der folgenden Scoreline-Zeile, die ohne störende Leerzeichen vor und hinter den Variablen SC (für score) und LI (für lives) angezeigt wird:

 

PRINT“[CLR/HOME]SCORE:“+STR$(SC)+” LIVES:”+STR$(LI)

 

Zusammen mit POS und TAB können die Ausgaben sogar zeichengenau positioniert werden.

 

VAL()-Funktion (Token 197)

Format:

[Variable]=VAL([String])

 

VAL() ist die Umkehrung von STR$() und wandelt einen String in eine Fließkomma-Zahl um. Mit VAL() können also Strings in Zahlen umgewandelt werden, wenn diese Strings vorher korrekt über die Tastatur eingegeben oder aus einer Datei ausgelesen wurden. Strings, die Zeichen enthalten, die keine Ziffern, kein Dezimalpunkt, oder kein + oder – sind, werden in die Zahl 0 konvertiert. VAL() entspricht somit der C-Funktion atof().