----------------------------------------------------

Kapitel 7

ATARI BASIC

----------------------------------------------------

 

Dieses Kapitel behandelt den BASIC-Interpreter des ATARI™ Personal Computer Systems. Die vier Hauptthemen sind:

 

1. WAS IST ATARI BASIC - Eine Beschreibung dessen, was erforderlich ist, um das BASIC laufen zu lassen und eine Erläuterung seiner Stärken und Schwächen.

 

2. WIE DAS ATARI BASIC ARBEITET - Eine detaillierte Analyse über die "Tokenisierung" und Ausführung von Programmen.

 

3. VERBESSERUNG DER PROGRAMMAUSF‹HRUNG - Eine Liste von Methoden, um die Ausführungsgeschwindigkeit eines Programmes zu erhöhen und seine Länge zu verkürzen.

 

4. FORTGESCHRITTENE PROGRAMMIER-TECHNIKEN - Eine Reihe von Möglichkeiten, um verschiedenen Programm-Erfordernissen besser gerecht zu werden.

 

 

WAS IST ATARI BASIC?

 

ATARI BASIC ist wie andere BASICs eine interpretierte Sprache. Das bedeutet, Programme können gestartet werden, sobald sie eingegeben wurden, und nicht erst, nachdem sie "compiliert" und "gelinkt" (d.h. in Maschinensprache übersetzt und im Speicher plaziert) worden sind. Der ATARI BASIC INTERPRETER liegt in einem 8K-ROM-Modul, das in den linken Schacht des Systems gesteckt wird. Dieses Modul belegt die Adressen $A000 bis $BFFF. Der BASIC-Interpreter speichert die Programme des Benutzers im RAM, wofür mindestens 8K-RAM benötigt werden.

 

Um das ATARI BASIC effektiv benutzen zu können, muß man seine Stärken und Schwächen kennen. Mit folgenden Informationen ist es möglich, Programme zu schreiben, die sämtliche Vorteile aus den Fähigkeiten und Möglichkeiten des ATARI BASICs ziehen.

 

 

Stärken des ATARI BASICs:

 

1. ANSPRECHEN DER GRAPHIK-ROUTINEN DES OPERATING SYSTEMS - Mit einfachen Graphik-Befehlen ist es möglich, ansprechende Bilder auf den Schirm zu bringen.

 

2. ANSPRECHEN EINES TEILS DER HARDWARE - Dieses betrifft z.B. die Befehle SOUND, STICK oder PADDLE.

 

3. EINFACHER AUFRUF VON ASSEMBLER-ROUTINEN - Die USR-Funktion gestattet dem Benutzer den Zugriff auf Assembler-Routinen.

 

4. DER INTERPRETER LIEGT im ROM - Dieses schützt vor versehentlichen ƒnderungen des BASIC-Interpreters durch den Programmierer oder sein Programm.

 

5. ZUGRIFF AUF DAS DOS - Spezielle Befehle, wie z.B. NOTE oder POINT (DOS 2.0S) gestatten dem Benutzer über das Disk-Operating-System einen freien Zugriff auf die Diskettenstationen.

 

6. ZUGRIFF AUF DIE PERIPHERIE - Jedes vom OS erkannte und akzeptierte Peripheriegerät kann über BASIC angesteuert werden.

 

 

Schwächen des ATARI BASICs:

 

1. KEINE INTEGER-ARITHMETIK - Alle Zahlen werden als 6 Byte-BCD Fließkomma-Zahlen gespeichert.

 

2. LANGSAME MATHEMATISCHE BERECHNUNGEN - Da alle Zahlen im oben genannten Format gespeichert werden, sind die Berechnungen entsprechend langsam.

 

3. KEINE STRING-ARRAYS - Es können nur eindimensionale Strings aufgebaut werden.

 

 

WIE DAS ATARI-BASIC ARBEITET

 

Ein kurzer ‹berblick über die Arbeit des BASIC-Interpreters.

 

1.

Der Interpreter bekommt vom Benutzer eine Zeile eingegeben und wandelt diese in eine Token-Form um.

 

2.

Diese Token-Zeile wird in das bisherige Token-Programm eingefügt.

 

3.

Das Programm wird ausgeführt.

 

 

Die Details dieser Operationen werden in den folgenden 4 Abschnitten besprochen:

 

A. Tokenisierung

 

B. Struktur der tokenisierten Dateien

 

C. Programmausführung

 

D. BASIC und das Betriebssystem

 

 

A. TOKENISIERUNG

 

 

Die Tokenisierung einer Zeile in BASIC sieht wie folgt aus:

 

1.

BASIC nimmt eine Zeile entgegen.

 

2.

Die Zeilensyntax wird überprüft (SYNTAX CHECK).

 

3.

Während dieses Checks wird die Zeile tokenisiert.

 

4.

Die tokenisierte Zeile wird in das Token-Programm eingefügt.

 

5.

Wurde die Zeile im Direkt-Modus, d.h. ohne Zeilennummer eingegeben, so wird sie sofort ausgeführt.

 

Um den Tokenisierungs-Vorgang besser zu verstehen, müßen als erstes einige Begriffe geklärt werden:

 

TOKEN

Ein 8-Bit-Zeichen, das einen speziellen interpretierbaren Code enthält.

 

STATEMENT

Eine vollständige Folge von Tokens, die BASIC veranlassen, etwas auszuführen. Beim LISTen werden die einzelnen Statements durch Doppelpunkte voneinander getrennt.

 

ZEILE

Ein oder mehrere Statements, denen entweder eine Nummer von 0 bis 32767, oder keine (im Falle des direkten Ausführens) vorangeht.

 

KOMMANDO

Der erste ausführbare Token eines Statements, das BASIC mitteilt, wie die folgenden Tokens zu interpretieren sind.

 

VARIABLE

Ein Token, der ein indirekter Zeiger zu seinem augenblicklichen Wert ist; der Wert kann geändert werden, ohne den Token zu ändern.

 

KONSTANTE

Ein 6-Byte-BCD-Wert, dem ein spezieller Token vorangeht. Dieser Wert wird während der Programmausführung nicht geändert.

 

OPERATOR

Einer von 46 Tokens, die auf irgendeine Weise den folgenden Wert ändern oder bewegen.

 

FUNKTION

Ein Token, der bei Ausführung einen bestimmten Wert für das Programm erzeugt.

 

EOL

-End of Line- (Ende der Zeile). Ein Zeichen mit dem hexadezimalen Wert 98.

 

Der BASIC-Interpreter beginnt den Tokenisierungs-Vorgang mit Empfang einer vom Benutzer geschriebenen Zeile. Diese Eingabe geschieht über einen Handler des OS. Normalerweise ist dieses der Bildschirm-Editor, mit dem ENTER-Kommando ist allerdings die Festlegung eines beliebigen Gerätes möglich. Der von BASIC an irgendein Gerät gesendete Befehl wird als GET RECORD-Kommando bezeichnet.

 

Die hierdurch erhaltenen Daten stehen im ATASCII-Format und werden durch ein EOL-Zeichen abgeschloßen. Sie werden mittels CIO in den sog. Eingabe-Zeilen-Buffer ($580 bis $5FF) gespeichert.

 

Nachdem das GET RECORD-Kommando aus und ein Syntax-Check durchgeführt wurde, beginnt der Tokenisierungs-Vorgang. Als erstes überprüft der BASIC-Interpreter, ob eine Zeilennummer angegeben wurde. Ist dieses der Fall, so wird sie in eine 2-Byte-Integer-Zahl umgewandelt. Ist keine Zeilennummer vorhanden, so wird Direktmodus angenommen und die Zeilennummer wird intern auf $8000 gesetzt. Diese beiden Bytes sind die ersten Tokens der tokenisierten Zeile, die in den Token-Ausgabe-Puffer gebracht wird. Dieser Puffer besitzt eine Länge von 256 Bytes und liegt am Ende des fürs Operating-System reservierten RAMs.

 

Das nächste Token ist ein sog. "Dummy"-Byte. Es ist für das Zählen der Bytes vom Anfang dieser Zeile bis zum Anfang der nächsten (=Offset) nötig. Danach folgt ein weiteres Dummy-Byte, das die Anzahl der Bytes vom Anfang dieser Zeile bis zum Anfang des nächsten Statements abgeschloßen wurde. Der Sinn dieser Parameter wird im Abschnitt zum Programm-Ausführungs-Prozeß erläutert.

 

Der BASIC-Interpreter überprüft nun den Befehl des ersten Statements der eingegebenen Zeile mit Hilfe der im ROM befindlichen Liste aller zugelassenen Kommandos. Wird ein passendes Kommando gefunden, dann wird das nächste Byte der tokenisierten Zeile die Nummer des zum Befehl passenden Eintrages der Liste im ROM. Wird kein passender Befehl gefunden, so wird das "Syntax-Error"- (Syntax-Fehler)-Token eingesetzt und der Interpreter stoppt den Tokenisierungs-Vorgang. Die restliche Zeile im Eingabe-Buffer wird im ATASCII-Format in den Token-Ausgabe-Puffer kopiert, woraufhin die fehlerhafte Zeile ausgedruckt wird.

 

In einer zulässigen Zeile muß hinter einem Kommando eines der folgenden Dinge stehen: eine Variable, eine Konstante, ein Operator, eine Funktion, ein Anführungszeichen, ein anderes Statement oder ein EOL-Zeichen. Der BASIC-Interpreter testet, ob das nächste eingegebene Zeichen numerisch ist. Ist dieses nicht der Fall, dann wird das Zeichen (sowie die nachfolgenden) mit den bisherigen Einträgen in eine Variablenliste verglichen. (Bei der ersten eingegebenen Zeile kann hierbei natürlich nichts gefunden werden, da es noch keine Einträge gibt.) Die Zeichen werden dann mit den Funktions- und Operatortafeln verglichen. Wird hierbei ebenfalls nichts gefunden, so nimmt der Interpreter an, daß es sich um eine neue Variable handelt. (Da dieses in der ersten Zeile die erste Variable ist, ist es auch der erste Eintrag in die Variablenliste.) Die Zeichen werde aus dem Eingabe-Buffer in die Variablenliste kopiert, wobei das höchstwertigste Bit (MSB) des letzten Bytes vom Variablen-Namen gesetzt wird. Danach werden für diesen Eintrag 8 Bytes in der Variablentafel reserviert. (Siehe Erklärung der Variablentafel im Abschnitt über die Struktur der tokenisierten File) Das Token, das für eine Variable in eine tokenisierte Zeile eingesetzt wird, ist die Nummer derselben minus 1, wobei das MSB gesetzt ist. Das bedeutet, das Token der ersten eingegebenen Variablen wäre $80, der zweiten $81 usw., bis $FF, was insgesamt 128 verschiedene Variablen-Namen zuläßt.

 

Wird eine Funktion gefunden, dann nimmt das erste Token den zugehörigen Wert der Funktionstafel an. Funktionen erfordern bestimmte Folgen von Parameter; diese sind in Syntax-Tafeln enthalten. Stimmen sie nicht überein, so wird eine Syntax-Fehlermeldung ausgegeben.

 

Wird ein Operator gefunden, so enthält das Token die zugehörige Nummer der Operatortabelle. Operatoren können in sehr komplexer Form aufeinander folgen (z.B. verschachtelte Klammern), daher ist der entsprechende Syntax-Check etwas komplizierter.

 

Im Falle von Anführungszeichen nimmt der Interpreter an, daß ein String (= Reihe, Folge) von Zeichen folgt. Das Token enthält den hexadezimalen Wert 0F, wobei ein Dummy-Byte für die Länge des Strings reserviert wird. Die Zeichen werden dann aufeinanderfolgend von Eingabe-Puffer in den Ausgabe-Puffer übertragen, bis ein weiteres Anführungszeichen gefunden wird. Das Dummy-Byte erhält dann einen Wert, die Anzahl der Zeichen dieses Strings.

 

Ist das folgende Zeichen im Eingabe-Puffer numerisch, so wird es vom BASIC-Interpreter in eine 6-Byte-BCD-Konstante umgewandelt. Ein Token mit dem wert $0E, dem die 6-Byte-Konstante folgt, wird danach in den Ausgabe-Puffer gebracht.

 

Stößt der Interpreter auf einen Doppelpunkt, so wird eine $l4 als Token eingesetzt und in den Ausgabe-Puffer gebracht. Das vorher bereitgestellte Dummy-Byte erhält ebenfalls seinen Wert, d.h. die Bytes vom Anfang der Zeile bis zum Anfang des nächsten Statements. Danach wird ein weiteres Dummy-Byte reserviert und der BASIC-Interpreter beginnt wieder mit dem Abfragen eines Befehls.

 

Wenn ein EOL-Zeichen gefunden wird, dann wird ein $16-Token gespeichert und die Anzahl der Bytes vom Zeilenanfang in das für den Zeilen-Offset reservierte eingesetzt. Zu diesem Zeitpunkt ist die Tokenisierung abgeschloßen, und der BASIC-Interpreter bringt die tokenisierte Zeile in das Token-Programm. Als erstes wird hierbei nach der Nummer der eben tokenisierten Zeile gesucht. Wird die Nummer gefunden, so wird die betreffende Zeile durch die neue ersetzt. Andernfalls wird die neue Zeile an der (numerisch) richtigen Stelle in das tokenisierte Programm eingefügt. In beiden Fällen werden die Daten, die der Zeile folgen, im Speicher auf- oder abbewegt, wodurch das Programm weiter ausgedehnt bzw. verkleinert wird.

 

Der Interpreter prüft nun, ob die tokenisierte Zeile direkt ausgeführt werden soll. Ist dieses der Fall, dann führt er diese wie unter Interpretationsprozeß beschrieben aus. Andernfalls wartet der BASIC-Interpreter auf eine neue Zeile.

 

‹berschreitet die Länge der tokenisierten Zeile zu irgendeinem Zeitpunkt 256 Bytes, so wird eine "Fehler 14"-Meldung auf dem Bildschirm ausgegeben. Danach wartet der Interpreter wieder auf eine neue Zeile.

 

Die Tokenisierung einer Zeile sieht wie folgt aus (alle Werte stehen in hexadezimaler Notation.):

 

10 LET X=1:PRINT X

 

KOMMANDO             OPERANDEN                      FUNKTIONEN

Hex Dez         Hex Dez                       Hex Dez

---------------------------------------------------------------

00  0   REM     0E   14   (numer. Konstante)   3D   61   STR$

01  1   DATA    0F   15   (String-Konstante)   3E   62   CHR$

02  2   INPUT   10   16   "                    3F   63   USR

03  3   COLOR   11   17   (nicht belegt)       40   64   ASC

04  4   LIST    12   18   ,                    41   65   VAL

05  5   ENTER   13   19   $                    42   66   LEN

06  6   LET     14   20   : (Ende d. Statem.)  43   67   ADR

07  7   IF      15   21   ;                    44   68   ATN

08  8   FOR     16   22   (Ende der Zeile)          45   69    COS

09  9   NEXT    17   23   GOTO                 46   70   PEEK

0A  10  GOTO    18   24   GOSUB                47   71   SIN

0B  11  GO TO   19   25   TO                   48   72   RND

0C  12  GOSUB   1A   26   STEP                 49   73   FRE

0D  13  TRAP    1B   27   THEN                 4A   74   EXP

0E  14  BYE     1C   28   =                    4B   75   LOG

0F  15  CONT    1D   29   <= (Numerische)      4C   76   CLOG

10  16  COM     1E   30   <>                   4D   77   SGR

11  17  CLOSE   1F   31   >=                   4E   78   SGN

12  18  CLR     20   32   <                    4F   79   ABS

13  19  DEG     21   33   >                    50   80   INT

14  20  DIM     22   34   =                    51   81    PADDLE

15  21  END     23   35   .                    52   82   STICK

16  22  NEW     24   36   *                    53   83   PTRIG

17  23  OPEN    25   37   +                    54   84   STRIG

18  24  LOAD    26   38   -

19  25  SAVE    27   39   /

1A  26  STATUS  28   40   NOT

1B  27  NOTE    29   41   OR

1C  28  POINT   2A   42   AND

1D  29  XIO     2B   43   (

lE  30  ON      2C   44   )

1F  31  POKE    2D   45   = (Arithmetische)

20  32  PRINT   2E   46   = (Strings)

21  33  RAD     2F   47   <=(Strings)

22  34  READ    30   48   <>

23  35  RESTORE 31   49   >=

24  36  RETURN  32   50   <

25  37  RUN     33   51   >

26  38  STOP    34   52   =

27  39  POP     35   53   + (Vorzeichen)

28  40  ?       36   54   - (Vorzeichen)

29  41  GET     37   55   ( (String)

2A  42  PUT     38   56   ( (Array)

2B 43  GRAPHICS 39   57   ( (DIM Array)

2C  44  PLOT    3A   58   ( (Sonstige)

2D  45  POSITION 3B   59   ( (DIM String)

2E  46  DOS     3C   60   , (Array-Komma)

2F  47  DRAWTO

30  48  SETCOLOR

31  49  LOCATE

32  50  SOUND

33  51  LPRINT

34  52  CSAVE

35  53 CLOAD

36  54  (implizites LET)

37  55  ERROR (Syntax)

 

 

B. STRUKTUR DER TOKENISIERTEN DATEI

 

Die tokenisierte Datei enthält zwei Hauptbestandteile: 1) eine Gruppe von Zero-Page-Zeigern, die in die tokenisierte Datei zeigen und 2) die tokenisierte Datei selbst. Die Zero-Page-Zeiger sind 2-Byte-Werte, die auf verschiedene Abschnitte der Token-Datei zeigen. Es gibt 9 dieser 2-Byte-Zeiger. Sie liegen bei den Adressen $80 bis $91. Die folgende Liste reiht die Zeiger und die Abschnitte der Token-Datei auf, auf welche sie zeigen.

 

 

 

Zeiger (Hex)  Abschnitt der Token-Datei (Fortlaufende Blöcke)

------------  -----------------------------------------------

LOMEM   80,81 Token Ausgabe-Puffer - Diesen Puffer benutzt der

              BASIC-Interpreter, um eine Zeile zu codieren. Er

              ist 256 Bytes lang und liegt am Ende des vom OS

              benutzten RAMs.

 

VNTP    82,83 Variablen-Namentafel - Eine Liste aller in das

              Programm eingegebenen Variablen. Diese werden als

              ATASCII-Zeichen gespeichert und stehen in der

              gleichen Reihenfolge, in der sie in das Programm

              eingegeben wurden. Es gibt drei verschiedene

              Arten von Einträgen:

              1.numerische Variablen - MSB des letzten Zeichens

              des Namens gesetzt.

              2.String-Variablen - Das letzte Zeichen ist ein

              "$", dessen MSB gesetzt ist.

              3.Variablenfelder - Das letzte Zeichen ist ein

              "(", dessen MSB gesetzt ist.

 

VNTD    84,85 Ende der Variablen-Namenstafel - Der BASIC-

              Interpreter benutzt diesen Zeiger zum Anzeigen

              des Namenstafel-Endes. Dieser zeigt normalerweise

              auf ein Dummy-Byte mit dem Wert Null (weniger als

              128 Variablennamen). Gibt es 128 Variablen, so

              zeigt er auf das letzte Byte des letzten

              Variablennamens.

 

VVTP    86,87 Variablen-Wertetafel - Diese Tafel enthält den

              augenblicklichen Wert jeder Variablen. Die

              Information für den jeweiligen Variablentyp sieht

              wie folgt aus:

 

Eine numerische Variable enthält einen numerischen Wert. Ein Beispiel hierfür wäre X=l. X ist die numerische Variable und 1 ihr Wert (6-Byte BCD-Format). Ein Feld wird aus numerischen Werten zusammengesetzt, die im String/Array-Bereich gespeichert werden. Er besitzt einen Eintrag in die Wertetafel. Ein String, bestehend aus im String/Array-Bereich gespeicherten Zeichen, besitzt ebenfalls einen Eintrag in der Wertetafel.

 

Das erste Byte jedes Eintrages benennt den Variablentyp: 00 steht für eine Variable, 40 für ein Feld und 90 für einen String. Wurde das Feld oder der String dimensioniert, dann wird das LSB des ersten Bytes gesetzt.

 

Im Falle der numerischen Variablen enthalten die Bytes 3 bis 8 die 6-Byte-BCD-Zahl, die ihrem augenblicklichen Werten entsprechen.

 

Für Strings und Felder enthalten die Bytes 3 und 4 den Offset von Anfang des Strings/Array-Bereiches bis zum Anfang der Daten (siehe unten).

 

Das fünfte und sechste Byte eines Arrays enthalten den ersten Parameter des DIM-Befehls. Die Zahl ist ein 2-Byte-Integer, dessen Wert um 1 größer ist, als der vom Benutzer eigegebene. Das siebte und, achte Byte enthalten den zweiten Parameter des DIM-Befehls, dessen Wert ebenfalls um 1 größer ist, als der vom Benutzer eingegebene.

 

STMTAB  88,89 Statement-Tafel - Dieser Datenblock enthält

              alle vom Benutzer eingegebenen Zeilen, die von

              BASIC tokenisiert wurden. Zeilen im Direktmodus

              werden ebenfalls hier abgelegt. Das Format dieser

              Zeile wird im Abschnitt über den Tokenisierungs-

              prozeß beschrieben.

 

STMCUR  8A,8B Augenblickliches Statement - Dieser Zeiger wird

              vom BASIC-Interpreter benutzt, um auf bestimmte

              Tokens innerhalb einer Zeile der Statement-Tafel

              hinzuweisen. Erwartet der Interpreter eine

              Eingabe, so wird dieser Zeiger auf den Anfang der

              Direktmodus-Zeile gesetzt.

 

STARP   8C,8D String/Array-Bereich - Dieser Block enthält alle

              String- und Felddaten. String-Zeichen werden als

              jeweils ein Byte ATASCII-Code gespeichert, d.h.

              ein String von 20 Zeichen Länge benötigt 20 Bytes

              des Speichers. Felder werden im 6-Byte-BCD-Format

              gespeichert. Ein Feld mit 10 Elementen benötigt

              demnach 60 Bytes des RAMs.

 

              Dieser Bereich wird bei jedem neuen DIM-Befehl um

              die Länge bzw. das 6-fache der Feldgröße

              vergrößert.

 

RUNSTK  8E,8F RUN-TIME-STACK - Dieser Software-Stapel enthält

              GOSUB- und FOR/NEXT-Einträge. Der GOSUB-Eintrag

              besteht aus 4 Bytes. Das erste ist eines mit dem

              Wert 0, welches das GOSUB anzeigt. Danach folgt

              als 2-Byte-Integer die Zeilennummer, in welcher

              die Anweisung steht. Schließlich folgt ein Byte,

              welches den Offset des GOSUBs zum Zeilenanfang

              angibt, so daß der Interpreter wieder dorthin

              zurückspringen kann (RETURN), um das nächste

              Statement auszuführen.

 

              Jeder FOR/NEXT-Eintrag besteht aus 16 Bytes. Der

              erste Eintrag enthält den Endwert für den Zähler.

              Der zweite ist die Schrittweite, d.h. der Wert,

              um den der Zähler jedesmal erhöht wird. Jeder

              dieser beiden Werte wird im 6-Byte-BCD-Format

              gespeichert. Das 13. Byte ist die Nummer der

              Zählervariablen, deren MSB gesetzt ist. Das 14.

              und 15. Byte enthalten die Zeilennummer, und das

              16. Byte ist der Zeilenoffset des FOR-Statements.

 

MEMTOP  90,91 Oberstes Ende des verwendeten RAMs - Dieses ist

              das Ende des BASIC-Programms. Die Ausdehnung des

              Programms kann von hier aus bis zum Ende des

              freien RAMs, das durch den Anfang der

              Display-List festgelegt wird, geschehen. Die

              FRE-Funktion berechnet die Größe des freien RAMs,

              in dem MEMTOP von HIMEM ($2E5,$2E6) abgezogen

              wird. Anmerkung: MEMTOP des BASICs ist nicht

              identisch mit der OS-Variablen MEMTOP!

 

 

C. PROGRAMMAUSF‹HRUNG

 

Um eine Programmzeile auszuführen, ist es erforderlich, die, Tokens, die während dem Tokenisierungs-Vorgangs erzeugt werden, zu lesen. Jedes Token hat eine bestimmte Bedeutung und veranlaßt den Interpreter zur Ausführung einer Folge von Operationen. Hierfür ist es erforderlich, daß sich der BASIC-Interpreter jeweils ein Token aus dem tokenisierten Programm holt und dieses ausführt. Das Token ist ein Index zu einer Sprungtafel von Routinen. Das bedeutet, ein PRINT-Token zeigt auf eine Routine zur Durchführung eines solchen Befehls. Nach Abschluß der Routine(n) holt sich der Interpreter das nächste Token. Der Zeiger, der auf das neue Token zeigt, heißt STMCUR und liegt bei $8A und $8B.

 

Die erste Zeile, die innerhalb eines Programmes ausgeführt wird, ist die Direktmodus-Zeile. Sie enthält gewöhnlich ein RUN oder GOTO xxxxx. Im Falle eines RUN-Befehls holt der Interpreter sich die erste tokenisierte Zeile aus der Statement-Tafel und führt diese aus. Weisen die Tokens in der Zeile nicht auf eine andere, dann fährt der BASIC-Interpreter nach Durchführung dieser Zeile mit der Abarbeitung der nächsten fort.

 

Wird auf ein GOTO gestoßen, dann muß die anzuspringende Zeile gefunden werden; Die Statement-Tafel enthält eine verkettete Liste von Zeilennummern und Statements, wobei die niedrigste Nummer an erster und die höchste an letzter Stelle steht. Wird eine andere Zeile in der Mitte der Tafel benötigt, dann läuft der folgende Prozeß ab:

 

Die Adresse der ersten Zeile wird im STMTAB-Zeiger ($88,$89) gefunden und in einem Hilfszeiger gespeichert. Die ersten zwei Bytes der Zeile sind ihre Nummer. Diese wird mit der anzuspringenden Zeilennummer verglichen. Ist die Nummer kleiner, dann holt sich der Interpreter die nächste Zeile, indem der Wert des dritten Bytes der ersten Zeile zum Wert des Hilfszeigers addiert wird. Hierdurch zeigt der Hilfszeiger auf die zweite Zeile. Nun werden wieder die ersten beiden Bytes der neuen Zeile mit dem Wert der anzuspringenden verglichen. Sind die ersten beiden Bytes kleiner, so wird das dritte Byte zum Hilfszeiger addiert.

 

Ist die anzuspringende Zeilennummer gefunden, dann wird der Hilfszeiger nach STMCUR übertragen. Der Interpreter holt sich dann das nächste Token aus der neuen Zeile.

 

Wenn die gesuchte Zeile nicht gefunden wird, dann gibt der BASIC-Interpreter eine "ERROR 12"-Meldung aus ("LINE NOT FOUND" - Zeile nicht gefunden).

 

Die Ausführung eine GOSUBs erfordert mehr Arbeit als die eines

GOTOs. Die Routine für das Auffinden der gewünschten Zeile ist die gleiche. Bevor der Interpreter aber zur neuen Zeile geht, wird ein Eintrag auf den RUN-TIME-STACK gebracht. Dieser Eintrag umfaßt 4 Bytes am Ende den Stapels und speichert eine 0 im ersten Byte, um anzuzeigen, daß es sich um einen GOSUB-Befehl handelt. Als nächstes werden die beiden Bytes, welche die Zeilennummer angeben, in der der GOSUS-Befehl steht, auf den Stapel gebracht. Das letzte Byte enthält den Offset vom Anfang der Zeile bis zum GOSUB-Token. Danach führt der BASIC-Interpreter die angesprungene Zeile aus. Wenn er auf eine RETURN-Anweinung stößt, dann holt er den letzten Eintrag vom Stapel und geht zu der Zeile zurück, in welcher die GOSUB-Anweisung stand. Danach wird der nächste Befehl ausgeführt.

 

Das FOR-Kommando veranlaßt den Interpreter zu einer Reservierung von 16 Bytes auf dem RUN-TIME-STACK. Die ersten 6 Bytes geben den Endwert an, den die Zählervariable erreichen kann (6-Byte-BCD-Format). Die nächsten 6 Bytes enthalten die Schrittweite im gleichen Format. Danach wird die Variablennummer (mit gesetztem MSB) gespeichert. Als nächstes folgt die augenblickliche Zeilennummer (2 Bytes) und der Offset in die Zeile. schließlich wird der Rest der Zeile ausgeführt.

 

Findet der Interpreter das NEXT-Kommando, so ließt er den letzten Stapeleintrag. Es wird überprüft, ob die Variable, die durch das NEXT ausgegeben wird, die gleiche ist, die als letztes auf den Stapel gebracht wurde. Außerdem wird überprüft, ab der Zähler den Endwert erreicht oder überschritten hat. Ist dieses nicht der Fall, dann kehrt der BASIC-Interpreter zur Zeile mit der FOR-Anweisung zurück und fährt mit der Programm-Ausführung fort. Wird der Endwert des FOR-Befehls erreicht oder überschritten, so wird der FOR/NEXT-Eintrag vom Stapel geholt und die Programmausführung von diesem Punkt an fortgesetzt.

 

Stößt der Interpreter auf einen mathematischen Ausdruck, dann werden die Operatoren auf einen Operatoren-Stapel gebracht. Danach werden sie nacheinander wieder vom Stapel geholt und ausgeführt. Die Reihenfolge, in der sie auf den Stapel gebracht werden, kann beeinflußt werden. Werden die einzelnen Operatoren durch Klammern eingeschlossen so werden sie in der Reihenfolge, in der sie abgearbeitet worden sollen, auch auf den Stapel gebracht. Andernfalls "schaut" der Interpreter in einer ROM-Tafel "nach", in der die Prioritäten der einzelnen Operatoren verzeichnet sind, und bringt sie dementsprechend auf den Stapel (z.B. Punkt- vor Strichrechnung).

 

Wird zu irgendeinem Zeitpunkt die BREAK-Taste gedrückt dann wird vom OS ein Flag gesetzt, um dieses Ereignis festzuhalten. Der BASIC-Interpreter überprüft dieses Flag jeweils nach der Ausführung eines Tokens. Wenn dieses Flag gesetzt ist, wird die Nummer der Zeile, in der die Taste gedrückt wurde, gespeichert und eine "STOPPED AT LINE XXXXX"-Meldung ausgegeben, bei der dieser Wert eingesetzt wird. Danach wird das BREAK-Flag gelöscht und auf eine Eingabe vom Benutzer gewartet. Durch Eingabe eines "CONT"-Befehls kann der Benutzer erreichen, daß die Programmausführung ab der nächsten Zeile wieder aufgenommen wird.

 

 

D. BASIC UND DAS BETRIEBSSYSTEM

 

Der BASIC-Interpreter benutzt das OS hauptsächlich durch I/O-Aufrufe des CIO. Die folgende Liste zeigt eine Reihe durch den Benutzer ausführbare Aufrufe und die zugehörigen IOCBs des OS.

 

BASIC                          OS

-------------------            ---------------------------

OPEN #1,12,0,E:                IOCB=l

                               Kommando=3 (OPEN)

                               Aux1=12 (Ein-/Ausgabe)

                               Aux2=0

                               Pufferadresse=ADR("E:")

 

GET #1,X                       IOCB=!

                               Kommando=7 (GET CHARACTER)

                               Pufferlänge=0

                               => Zeichen wird im Akku-

                                  mulator übergeben

 

PUT #1,X                       IOCB=1

                               Kommando=ll (PUT CHARACTER)

                               Pufferlänge=0

                               => Zeichenausgabe aus dem

                                  Akkumulator

 

INPUT #1,A$                    IOCB=l

                               Kommando=5 (GET RECORD)

                               Pufferlänge=Größe von A$

                               (nicht über 120)

                               Pufferadresse=Eingabe-Zei-

                               len-Puffer

 

PRINT #I,A$                    IOCB=l

                               Der Interpreter benutzt

                               einen speziellen PUT BYTE-

                               Vektor im IOCB, um direkt

                               mit dem Handler zu

                               kommunizieren.

 

XIO 18,#6,12,0,"S:"            IOCB=6

                               Kommando=18 (FILL-Kommando)

                               Aux1=12

                               Aux2=0

 

 

SAVE/LOAD: Wenn ein Token-Programm auf einem Gerät mit SAVE gespeichert wird, dann werden zwei Informationsblöcke geschrieben. Der erste Block besteht aus 7 von 9 Zero-Page-Zeigern, die der Interpreter benutzt, um die Token-Files zu verwalten und aufrecht zu erhalten. Diese Zeiger sind LOMEM ($80,$81) bis STARP ($8C,$8D). Bei diesem Schreiben wird allerdings eine ƒnderung gemacht: die 2-Byte-Zeiger werden erst auf ein Gerät geschrieben, nachdem der Wert des LOMEM-Zeigers von jedem subtrahiert wurde. Dieses bedeutet, daß die ersten beiden gespeicherten Werte immer 0 und 0 sind.

 

Der zweite gespeicherte Informationsblock besteht aus folgenden Abschnitten der tokenisierten Datei:

1) der Namenstafal der Variablen,

2) der Wertetafel der Variablen,

3) dem tokenisierten Programm und

4) der Direktmodus-Zeile.

 

Wird dieses Programm wieder in den Speicher geladen (LOAD), dann "sieht" der Interpreter "auf" die OS-Variable MEMLO ($2E7,$2E8), und addiert deren Wert zu jedem der 2-Byte-Zero-Page-Zeiger, sobald diese gelesen werden. Die Zeiger werden wieder in der Zero-Page des Speichers plaziert, wonach die Werte von RUNSTK ($8E,$8F) und MEMTOP ($90,$91) auf den Wert von STARP gebracht werden.

 

Als nächstes werden 256 Bytes ab der Speicherstelle reserviert, die von MEMLO angegeben wird, um Platz für den Token-Ausgabe-Puffer zu schaffen. Danach wird die Information der tokenisierten Datei eingelesen. Diese Daten werden genau hinter dem Token-Ausgabe-Puffer plaziert.

 

 

OS- und BASIC-Zeiger (ohne DOS)

 

 

VERBESSERUNG DER PROGRAMMAUSF‹HRUNG

 

 

Um die Effektivität eines Programms zu steigern, können zwei Dinge getan werden. Zum einen kann die Ausführungszeit, zum anderen der benötigte Speicherplatz verringert werden. Um diese beiden Ziele zu erreichen, kann der Benutzer die nachfolgend Tips verwenden. Die einzelnen Methoden sind in einer Folge aufgereiht, bei der die erste die größte und die letzte die geringste Effektivität besitzt.

 

Methoden zur Erhöhung der Ausführungsgeschwindigkeit von BASIC-Programmen:

 

1. NEUCODIEREN - Da BASIC keine strukturierte Sprache ist, werden in dieser Sprache geschriebene Programme nicht ineffizient. Ein Programm kann auch nach mehreren ‹berarbeitungen noch immer nicht optimal sein. Es lohnt sich also immer, ein Programm noch einmal durchzusehen.

 

2. UBERPR‹FEN DER ALSORITHMISCHEN LOGIK - Der Programmteil für die Durchführung einer Operation muß so durchschlagend wie nur irgendmöglich sein.

 

3. Oft BENUTZTE UNTERPROGRAMME UND FOR/NEXT-SCHLEIFEN M÷GLICHST AN DEN ANFANG DES PROGRAMMES STELLEN - Der Interpreter beginnt mit der Suche nach einer Zeilennummer immer am Anfang des Programms, so daß weiter hinten stehende Zeilen erst sehr viel später erreicht werden.

 

4. MEHRFACH BENUTZTE OPERATIONEN INNERHLAB EINER SCHLEIFE DIREKT ALS UNTERPROGRAMM EINGEBEN - Der BASIC-Interpreter benötigt sehr viel Zeit, um den RUN-TIME-STACK zu aktualisieren.

 

5. DIE SICH AM HƒUFIGSTEN ƒNDERNDE SCHLEIFE SOLLTE BEI VERSCHACHTELTEN SCHLEIFEN DIE INNERSTE SEIN - Der RUN-STACK-TIME wird dadurch seltener benutzt.

 

6. VEREINFACHEN DER FLIEßKOMMA-ARITHMETIK INNERHALB VON SCHLEIFEN - Wird ein Ergebnis durch Multiplikation mit dem Index erreicht, so sollte diese statt dessen in eine Addition von Konstanten umgewandelt werden.

 

7. SCHLEIFEN IN EINE ZEILE BRINGEN - Der BASIC-Interpreter muß dadurch nicht erst in die nächste Zeile springen, um die Schleife fortzusetzen.

 

9. ABSCHALTEN DES BIDSCHIRMS - Ist eine Bildschirmanzeige (über einen Zeitraum) nicht unbedingt erforderlich, so können 30% der Ausführungszeit durch Setzen einer 0 in die Speicherstelle 559 gespart werden (POKE 559,0).

 

9. BENUTZEN EINES SCHNELLEREN GRAPHIKMODUS¥ BZW. EINER K‹RZEREN DISPLAY LIST - Ist nicht unbedingt ein voller Bildschirm erforderlich, so können hierdurch bis zu 25% der Ausführungszeit gespart werden.

 

10. BENUTZEN VON ASSEMBLER-UNTERPROGRAMMEN - Durch Aufrufen von Assembler-Unterprogrammen über die USR-Funktion kann die Ausführungszeit eines Programms verkürzt werden.

 

Sparen von Speicherplatz:

 

1. NEUCODIEREN - Wie schon genannt. Durch Neuorganisation das Programms kann Speicherplatz eingespart werden.

 

2. L÷SCHEN VON BEMERKUNGEN - REMs werden als ATASCII-Daten gespeichert und verbrauchen dementsprechend Speicherplatz.

 

3. ERSETZEN VON KONSTANTEN, DIE MEHR ALS DREIMAL BENUTZT WERDEN - Der BASIC-Interpreter benötigt für eine Konstante 7 Bytes, für einen Variablenaufruf aber nur 1 Byte. Durch das Einsetzen von Variablen können also jeweils 6 Bytes gespart werden.

 

4. INITIALISIEREN VON VARIABLEN MIT DER READ-ANWEISUNG - Ein Data-Statement wird als ATASCII-Code gespeichert, d.h. ein Byte pro Zeichen. Durch Gleichsetzen mit einer Konstanten gehen Jedesmal 7 Bytes verloren.

 

5. ERSETZEN VON KONSTANTEN DURCH AUSDRUCKE AUS BEREITS BENUTZTEN VARIABLEN - so kann eine 1 durch Zl, und eine 2 durch Z2 ersetzt werden. Wird die Zahl 3 benötigt, so schreibt man Z1+Z2.

 

6. HƒUFIG BENUTZTE ZEILENNUMMERN IN VARIABLEN UMWANDELN - Wird z.B. die Zeile 100 über einen GOSUB- oder GOTO-Befehl 50 mal angesprungen, so können ca. 300 Bytes gespart worden, indem man die 100 jedes Befehls durch ein Z100 ersetzt (GOTO Z100).

 

7. DIE ANZAHL DER VARIABLEN M÷GLICHST KLEIN HALTEN - Jeder neue Eintrag in die Namenstafel für Variablen verbraucht 8 Bytes, zuzüglich der Bytes des Namens.

 

8. LöSCHEN DER NICHT MEHR BEN÷TISTEN VARIABLEN - Variablen werden nicht durch Löschen aus dem Programm aus der Namenstafel entfernt. Um sie aus letzterer zu entfernen, muß das Programm auf Diskette oder Cassette geLISTet werden. Danach wird NEW eingegeben und das Programm über ENTER wieder geladen.

 

9. VARIABLENNAMEN KURZ HALTEN - Jeder Variablenname wird in der Namenstafel als ATASCII-Daten gespeichert. Je kürzer die einzelnen Namen sind, desto kürzer ist die Tafel, d.h. umso um so weniger Speicher wird verbraucht.

 

10. ERSETZEN VON MEHRFACH BENUTZTEM TEXT DURCH STRINGS - Durch diese Methode kann viel Speicherplatz gespart werden, da jeder Buchstabe eines Textes einem Byte im Speicher entspricht.

 

11. INITIALISIEREN EINES STRINGS DURCH GLEICHSETZEN - Ein Gleichsetzen mit Zeichen innerhalb zweier Anführungszeichen benötigt weniger Speicherplatz, als eine READ-Anweisung oder eine CHR$-Funktion.

 

12. ZUSAMMENFASSEN VON ZEILEN - 3 Bytes können gespart werden, wenn anstelle von zwei Zeilen mit jeweils einem Statement eine Zeile mit zwei durch Doppelpunkt getrennten Statements geschrieben wird.

 

13. VERMEIDEN NUR EINMAL BENUTZTER UNTERPROGRAMME - Nur einmal aufgerufene Unterprogramme sollten anstelle ihres Aufrufes direkt in den Programmcode eingefügt werden (GOSUB und RETURN verschwenden Bytes, wenn sie nur einmal benutzt werden).

 

14. ERSETZEN VON NUMERISCHEN FELDERN DURCH STRINGS, SOFERN DIE DATENWERTE NICHT GR÷ßER ALS 255 WERDEN - Einträge für numerische Felder verbrauchen jeweils 5 Bytes, wogegen Stringelemente nur ein Byte benötigen.

 

15. ERSETZEN VON SETCOLOR-BEFEHLEN DURCH POKE-KOMMANDOS - Dieses erspart jedesmal 9 Bytes.

 

16. ERSETZEN VON POSITION-ANWEISUNGEN DURCH CURSOR-STEUERCODES Die POSITION-Anweisung benötigt 15 Bytes für X- und Y-Parameter, wogegen ein Cursor-Steuerzeichen nur ein Byte verbraucht.

 

17. L÷SCHEN VON ZEILEN DURCH DAS PROGRAMM SELBST - Siehe Abschnitt "Fortgeschrittene Programmier-Techniken".

 

18. MODIFIZIEREN DES STRING/ARRAY-ZEIGBERS ZUM LADEN VON VORHER DEFINIERTEN DATEN - Siehe Abschnitt über fortgeschrittenel Programmier-Techniken.

 

19. VERKETTEN VON PROGRAMMEN - Ein Programmteil wird vom Benutzer gestartet und lädt dann nach seiner Ausführung selbsttätig den nächsten Teil von Diskette oder Cassette. Diese Technik läßt sich beliebig oft wiederholen.

 

 

FORTGESCHRITTENE PROGRAMMIER-TECHNIKEN

 

Wenn die Grundprinzipien des ATARI BASICs verstanden worden sind, können einige interessante Programme geschrieben werden, die diese Prinzipien anwenden. Es können sowohl reine BASIC-Programme sein, als auch solche, die die Möglichkeiten des OS mit einbeziehen.

 

BEISPIEL 1 -String-Initialisierung - Dieses Progamm setzt alle Bytes eines Strings (bliebiger Länge) auf den gleichen Wert. Der BASIC-Interpeter kopiert das erste Byte des Quellenstrings in das erste Byte des Zielstrings. Danach folgt das zweite, das dritte usw. Da der Zielstring das zweite Byte des Quellenstrings ist, wird das gleiche Zeichen in den gesamten String geschrieben.

 

BEISPIEL 2 - Löschen von Programmzeilen - Durch Benutzen einer Möglichkeit des OS kann ein Programm Zeilen selbst löschen oder ändern. Der Bildschirm-Editor kann so geschaltet werden, daß er Informationen vom Bildschirm annimmt, ohne auf die Eingabe des Benutzers warten zu müßen. Als erstes wird der Cursor am oberen Bildschirmrand positioniert und das Programm gestoppt. Hierdurch liest der Interpreter die auf dem Bildschirm stehenden Kommandos ein.

 

BEISPIEL 3 - Sichern eines String/Array-Bereiches - Besitzt ein String oder Array immer die gleiche Größe und den gleichen Inhalt, so kann ein großer Betrag des Speicherbereiches gespart werden, indem die entsprechende Information während des SAVEns gespeichert und die Initialisierung beim nächsten Programmstart gelöscht wird.

 

BEISPIEL 4 - Sichern von BCD-Zahlen auf Diskette - Immer wenn numerische Daten auf ein Gerät geschrieben werden, geschieht dies im ATASCII-Format. Das heißt: die Zahl 10 wird als ATASCII-Information 1, gefolgt von einer 0 ausgeschrieben. Dieses kann bei Records mit festgelegter Länge problematisch sein. Eine Möglichkeit, um dieses zu umgehen, wäre, die Zahlen in 6-Byte-BCD-Zahlen umzuformen und in Strings zu speichern. Letztere werden dann zum Lesen und Schreiben benutzt.

 

BEISPIEL 5 - Player/Missile-Graphik mit Strings - In diesem Beispiel wird eine Möglichkeit der schnellen Bewegung von Player/Missile-Bilddaten gezeigt. Man ändert den String/Array-Offset eines Strings, so daß er auf den Bereich für die Player/Missile-Bilddaten zeigt. Durch Schreiben von Daten in diesen String (Gleichsetzen) können die Player/Missile-Bilddaten mit einer Geschwindigkeit bewegt werden, die der von Maschinensprache gleichkommt.

 

 

10 REM Beispiel 1: String-Initialisierung

20 DIM A$(1000)

30 A$(1)="*": A$(1000)="*"

40 A$(2)=A$

 

10 REM Beispiel 2: Löschen von Zeilen

20 GRAPHICS 0:POSITION 2,4

30 ? 70:? 80:? 90:? "CONT"

40 POSITION 2,0

50 POKE 842,13:STOP

60 POKE 842,12

70 REM Diese Zeilen

80 REM werden

90 REM gelöscht

 

10 REM Beispiel 3: Sichern von Strings/Arrays

20 REM Beim ersten Lauf ein GOTO 50 ausführen

30 REM Beim zweiten Lauf Zeile 50 löschen

40 GOTO 110

50 DIM A$(10):A$="WWWWWWWWWW"

60 STARP=PEEK(140)+PEEK(141)*256

70 STARP=STARP+10

80 HI=INT(STARP/256):LO=STARP-HI*256

90 POKE 140,LO:POKE 141,HI

100 SAVE"0:STRING":STOP

110 STARP=PEEK(140)+PEEK(141)*256

120 STARP=STARP-10

130 HI=INT(STARP/256):LO=STARP-HI*256

140 POKE 140,LO:POKE 142,LO:POKE 144,LO

150 POKE 141,HI:POKE 142,HI:POKE 145,HI

160 DIM A$(10)

170 A$(10)="W"

180 STOP

 

10 REM Beispiel 4: Sichern und Laden von

20 REM BCD-Zahlen über die Diskettenstation

30 DIM A(0),B$(6)

40 B$(6,6)=CHR$(32)

50 VTAB=PEEK(134)+PEEK(135)*256

60 POKE VTAB+1,0

70 OPEN #1,8,0,"D:TEST"

80 FOR C=1 TO 15:A(0)=C:? #1;B$:NEXT C

90 CLOSE #1

100 OPEN #1,4,0,"D:TEST"

110 FOR C=1 TO 15:INPUT #1,B$:? A(0):NEXT C

120 CLOSE #1:END

 

10 REM Beispiel 5: Player/Missile-Graphik über Strings

20 DIM A$(512),B$(20)

30 X=X+1:READ A:IF A<>-l THEN B$(X,X)=CHR$(A):GOTO 30

40 DATA 0,255,129,129,129,129,129,129,129,129,255,0,-l

50 POKE 559,62:POKE 704,88

60 I=PEEK(106)-16:POKE 54279,I

70 POKE 53277,3:POKE 710,244

80 VTAB=PEEK(134)+PEEK(135)*256

90 ATAB=PEEK(140)+PEEK(141)*256

100 OFFS=I*256+1024-ATAB

110 HI=INT(OFFS/256):LO=OFFS-HI*256

130 Y=60:Z=100:V=1:H=l

140 A$(Y,Y+11)=B$:POKE 53248,Z

150 Y=Y+V:Z=Z+H

160 IF Y>213 OR Y<33 THEN V=-V

170 IF Z>206 OR Z<49 THEN H=-H

180 GOTO 140