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().