1.0 Copyright 1.1 Vorbemerkung 2.0 Einleitung 2.1 Eigenschaften 2.2 Compilieren und Ausführen von Programmen 3.0 Der Aufbau von Gofolio-Programmen 3.1 Beispiel-Programm 3.2 Bezeichner in Gofolio 3.2.1 Preprocessor-Anweisungen 3.3 Variablen 3.4 Darstellung von Zahlenkonstanten 3.5 Arrays 3.6 Strings 3.7 Dateien 3.8 Funktionen 3.8.1 Die Deklaration von Funktionen 3.8.2 Parameterübergabe 3.9 Zusammenfassung zum Aufbau von Gofolio-Programmen 4.0 Operatoren in Gofolio 4.1 Additive Operatoren 4.2 Multiplikative Operatoren 4.3 Relationale Operatoren 4.4 Der Zuweisungs-Operator := 5.0 Beschreibung der Gofolio-Funktionen in alphabetischer Reihenfolge 6.0 Fehlermeldungen des Compilers (GF.EXE) 7.0 Versionsinformationen 8.0 Tips 8.1 Maschinenprogramme unter Gofolio 8.2 Unterprogramme zur Stringbearbeitung 8.3 Geschwindigkeitsoptimierung
GOFOLIO ist Freeware. D.h., sie können mit GOFOLIO kostenlos arbeiten und es beliebig weitergeben. Haben Sie Fragen oder Anregungen zu GOFOLIO, so können sie sich gerne an den Autoren wenden:
Lutz Herrmann Lorichsstrasse 35 22307 Hamburg. E-Mail : windkraft@hotmail.com Hompage: http://www.bitpilot.de
Sollten Sie ein interessantes Programm mit GOFOLIO entwickelt haben, so können Sie es mir gerne zusenden. Ich werde es dann in die Sammlung der Beispielprogramme mitaufnehmen.
Bei den weiteren Ausführungen ist die folgende Terminologie zu beachten:
Ausdruck, Ausdruck,…. oder Ausdruck,….
Zu beachten ist hierbei auch immer das Zeichen, welches die einzelnen Wiederholungen voneinander trennt (in diesem Fall das Komma).
Gofolio ist ein leistungsfähiger Semi-Compiler (GF.EXE) mit Runtime-Modul (GO.EXE). Die mit GF.EXE compilierten Programme enthalten nur den reinen Objekt-Code, sie können mit GO.EXE gestartet werden. Diese Methode ist gegenüber der Generierung von EXE-Dateien sehr sparsam mit Massenspeicher, da die Laufzeitbibliothek für alle Programme nur einmal in GO.EXE vorliegt.
Gofolio wurde ursprünglich für den Atari-Portfolio entwickelt; Compiler und Runtime-Modul sind daher vom Codeumfang sehr klein gehalten. Gofolio eignet sich daher besonders für alle Dos-Rechner mit kleinem Hauptspeicher bzw. Massenspeicher (Palmtop-/Notebook-Computer).
Zu den meisten heute erhältlichen Compilern gehören riesige Laufzeitbibliotheken, Includedateien, Entwicklungsumgebungen usw., weshalb diese auf diesen kleinen Computern häufig nicht lauffähig sind bzw. zu umständlich zu handhaben sind (abgesehen davon, daß diese Entwicklungssysteme häufig einen 80286 oder 80386 Prozessor voraussetzen). Für denjenigen, der z.B. auf seinem Atari-Portfolio, Hewlet-Packart HP 95LX, SHARP PC-3000 oder PT-30 kleine bis mittlere Programme entwickeln möchte, ohne umständliches Laden bzw. Linken von Bibliotheken, für den ist Gofolio gedacht. Der Sprachumfang von Gofolio wurde bewußt auf das Wesendliche beschränkt. Trotzdem können sie aber mit Gofolio ihren Computer vollständig beherrschen; d.h. auf DOS-Funktionen und Hardware zugreifen. Dies ist wichtig, wenn Sie z.B. Ihren Computer für Steuerungen, Messungen und Regelungen einsetzen. Ein Einsatz auf jeden beliebigen DOS-Rechner ist aber ohne Einschränkung ebenfalls möglich.
Gofolio ist eine eigenständige Programmiersprache. Die Syntax wurde an die Form der Funktionen von Tabellenkalkulations-Arbeitsblättern angelehnt. D.h. alle Befehle und Kontrollstrukturen werden in Form von Funktionen ausgedrückt. Gofolio unterstützt strukturierte Programmierung mit der Möglichkeit zur Definition eigener Funktionen (welche auch rekursiv verwendet werden können), der Bildung von Blöcken sowie der Anwendung von Kontrollstrukturen wie IF…. ELSE ….-Entscheidungen und WHILE-Schleifen. Es stehen die folgenden Datentypen zur Verfügung:
Von diesen Typen können jeweils beliebig-dimensionale Arrays (Felder) gebildet werden. Arrays vom Typ Char können als Strings (Zeichenketten) verwendet werden. Weiterhin können über vordefinierte Variablen vom Typ Integer die Prozessorregister geladen und gelesen werden (_AX, _BX, _CX, _DX,_SI, _DI, _FI). Gofolio verwaltet maximal 32000 Byte für Daten.
Gofolio beinhaltet eine Menge Funktionen, welche zum Sprachumfang gehören. U.a. zur formatierten Ein- und Ausgabe von Daten (SCAN, PRINT), zur systemnahen Programmierung (PEEK, POKE, IN, OUT, INTR) sowie mathematische Funktionen (z.B.: SIN, ASIN, COS, ACOS, TAN, ATAN, EXP, LN). Die Ein- und Ausgabefunktionen, Stringverarbeitung sowie die Array-Darstellung arbeiten ähnlich wie die Äquivalente in der Programmiersprache C.
Der Programmquelltext muß als reiner ASCII-Text vorliegen. Zur Erstellung eines Programms kann z.B. der DOS-Editor Edit vewendet werden. Der Progamm-Quelltext wird compiliert, indem die Quelltext-Datei GF.EXE als Kommandozeilenparameter übergeben wird. GF.EXE hat nach erfolgreicher Compilierung eine Datei mit der Erweiterung .GFO erstellt. Die Datei mit der Erweiterung .GFO wird ausgeführt, indem sie GO.EXE als Kommandozeilenparameter übergeben wird. Beispiel:
Ihre Datei mit dem Programmquelltext heißt myprog.txt. Um myprog.txt zu compilieren geben Sie folgendes ein:
gf myprog.txt
GF.EXE hat (wenn kein Fehler aufgetreten ist) die Datei myprog.gfo erzeugt. Um myprog.gfo zu starten geben Sie folgendes ein:
go myprog
Das Editieren, Compilieren und Ausführen kann durch die folgende Batch-Datei automatisiert werden:
@echo off edit %1 gf %1.txt if not errorlevel 1 go %1
Der typische Aufbau eines Gofolio-Programms wird im folgenden anhand eines dokumentierten Beispiels erläutert. Das Programm füllt ein Integer-Array mit Zufallszahlen und sortiert das Array dann aufsteigend. Das Sortieren wird am Bubble-Sort-Algorithmus und am Quick-Sort-Algorithmus demonstriert. Schlüsselworte, welche zur Gofolio-Syntax gehören, sind groß geschrieben.
1: DEFINT(intfeld[1000],anzahl); 2: 3: VOIDTYPE #FillFeld(DEFINT(n)); 4: BLOCK( 5: DEFINT(nr), 6: nr:=0, 7: WHILE(nr <= n, BLOCK( 8: intfeld[nr]:=RND()%100, 9: INC(nr)) 10: ), 11: RETURN() 12: ); 13: 14: VOIDTYPE #QuickSort(DEFINT(l,r)); 15: BLOCK( 16: DEFINT(i,j,x,w), 17: i:=l, j:=r, 18: x:=intfeld[(l+r)/2], 19: WHILE(i <= j, BLOCK( 20: WHILE(intfeld[i] < x, INC(i)), 21: WHILE(x < intfeld[j], DEC(j)), 22: IF(i <= j, BLOCK( 23: w:=intfeld[i], 24: intfeld[i]:=intfeld[j], 25: intfeld[j]:=w, 26: INC(i), DEC(j)) 27: )) 28: ), 29: IF(l < j, QuickSort(l,j)), 30: IF(i < r, QuickSort(i,r)), 31: RETURN() 32: ); 33: 34: VOIDTYPE #PrintFeld(DEFINT(n)); 35: BLOCK( 36: DEFINT(nr), 37: nr:=0, 38: WHILE(nr <= n, BLOCK( 39: PRINT("%i ",intfeld[nr]), 40: INC(nr)) 41: ), 42: RETURN() 43: ); 44: 45: VOIDTYPE #BubbleSort(DEFINT(n)); 46: BLOCK( 47: DEFINT(i,j,x), 48: i:=1, 49: WHILE(i <= n, BLOCK( 50: j:=n, 51: WHILE(j >= i, BLOCK( 52: IF(intfeld[j-1] > intfeld[j], BLOCK( 53: x:=intfeld[j-1], 54: intfeld[j-1]:=intfeld[j], 56: intfeld[j]:=x) 57: ), 58: DEC(j)) 59: ), 60: INC(i)) 61: ), 62: RETURN() 63: ); 64: 65: CLS(); 66: PRINT("\nBitte Anzahl eingeben: "); scan("%i",anzahl); 67: FillFeld(anzahl); 68: QuickSort(0,anzahl); 69: @ BubbleSort(anzahl); 70: PrintFeld(anzahl).
In Zeile 1 werden die globlalen Variablen deklariert. Die globalen Variablen sind vom Typ Integer und werden mit der Funktion DEFINT(Variable,<Variable>,….) deklariert.
Bei der Variablen intfeld handelt es sich um ein eindimensionales Integer-Array mit 1001 Elementen.
Die Variable anzahl ist eine einfache Integer-Variable.
Die ersten Anweisungen in einem Gofolio-Programm müssen immer die Deklarationen der globalen-Variablen sein. Eine Anweisung wird in Gofolio immer mit einem Semikolon beendet.
In Zeile 3 wird die Funktion FillFeld deklariert. VOIDTYPE gibt den Typ der Funktion an. VOIDTYPE bedeutet, daß die Funktion keinen Rückgabewert liefert (Funktionen können weiterhin vom Typ CHARTYPE, INTTYPE und REALTYPE sein, wobei sie den entsprechenden Daten-Typ zurückliefern). Der Name der Funktion wird in der Deklaration immer mit dem #-Zeichen eingeleitet. Sollen der Funktion Parameter übergeben werden können, so werden in Klammern hinter dem Funktionsnamen Variablen deklariert, welche beim Aufruf der Funktion die Parameter aufnehmen. Der Funktion FillFeld kann somit beim Aufruf ein Parameter vom Typ Integer übergeben werden.
Sollen mehrere Parameter übergeben werden, so muß die Reihenfolge der Übergabe der Deklarations-Reihenfolge entsprechen. Die Funktions-Deklaration (Zeile 3) wird mit einem Semikolon abgeschlossen.
Die Nachfolgende Anweisung stellt den Programm-Code der Funktion FillFeld dar. Wenn der Programm-Code mehrere Anweisungen umfassen soll, so muß er zu einem BLOCK zusammengefaßt werden. Dies geschieht in Zeile 4 durch die Funktion BLOCK(Anweisung, <Anweisung> ,….). Die erste Anweisung, welche zum Anweisungsblock der Funktion FillFeld gehöhrt, ist die Deklaration der lokalen Integer-Variablen nr mittels DEFINT() in Zeile 5.
Die in einem BLOCK befindlichen Anweisungen werden durch Kommata voneinander getrennt. In Zeile 6 wird der lokalen Variablen nr der Wert 0 zugewiesen. Der Doppelpunkt mit dem Gleichheitszeichen := ist hierbei der Zuweisungsoperator.
In Zeile 7 beginnt eine WHILE-Schleife. Die Funktion WHILE enthält eine Bedingung sowie eine Anweisung bzw. mehrere in einem BLOCK zusammengefaßte Anweisungen: WHILE(Bedingung, Anweisung). Die Anweisung wird in einer WHILE-Schleife sooft wiederholt, bis die Bedingung nicht mehr erfüllt ist.
Die Anweisungen in den Zeilen 8 und 9 sind in einen BLOCK zusammengefaßt und werden sooft wiederholt bis nr < = n (Inhalt der Variablen nr kleiner gleich dem Inhalt der Variablen n) ist. Wobei die Variable n der Übergabeparameter an die Funktion FillFeld ist.
In Zeile 8 wird dem Array-Element des Arrays intfeld mit der Nummer entsprechned dem Inhalt der Variablen nr eine Zufallszahl zwischen 0 und 99 zugewiesen. Die Funktion RND() erzeugt Zufallszahlen zwischen 0 und 30000. Das Prozentzeichen (%) ist der Modulo-Operator, liefert also den Ganzzahligen Divisionsrest zurück. Die Modulo-Operation kann nur auf Daten vom Typ Integer angewendet werden.
In Zeile 9 wird der Inhalt der Variablen nr durch die Funktion INC(Variable) um 1 erhöht (incrementiert). Die Variable nr hätte auch durch die Anweisung nr:=nr+1 um 1 erhöht werden können. Die Variante über INC() wird aber wesendlich schneller ausgeführt. Nach dem die Klammer der Funktion INC() geschlossen wurde, wird die Klammer der Funktion BLOCK() geschlossen.
In Zeile 10 wird die Klammer der Funktion WHILE() geschlossen. Da WHILE() eine Anweisung innerhalb des BLOCK´s von Zeile 4 ist, wird die nächste Anweisung in Zeile 11 mit einem Komma getrennt.
In Zeile 11 wird die Funktion FillFeld durch Aufruf der Funktion RETURN() verlassen. Da FillFeld eine VOIDTYPE Funktion ist, also keinen Wert zurückliefert, wird der Funktion RETURN() kein Parameter übergeben. Bei anderen Funktionstypen wird der Rückgabewert über RETURN(Ausdruck) übergeben.
In Zeile 12 wird der Anweisungsblock der Funktion FillFeld geschlossen. Es folgt ein Semikolon, da sämtliche Blöcke verlassen sind.
In Zeile 14 wird die Funktion QuickSort deklariert. Beim Aufruf von QuickSort können 2 Parameter vom Typ Integer übergeben werden, auf welche innerhalb von QickSort über die Varialen l und r zugegriffen werden kann.
In Zeile 22 wird die Funktion IF() verwendet. IF() enthält eine Bedingung und mindestens eine Anweisung: IF(Bedingung, Anweisung). Ist die Bedingung erfüllt so wird die Anweisung ausgeführt, wenn nicht, so wird die Anweisung übersprungen. IF() kann aber auch mit einer Bedingung und 2 Anweisungen aufgerufen werden: IF(Bedingung, Anweisung1, <Anweisung2>). Wenn in diesem Fall die Bedingung erfüllt ist, so wird Anweisung1 ausgeführt, und wenn die Bedingung nicht erfüllt ist, wird Anweisung2 ausgeführt.
QuickSort ist eine rukursive Funktion, dies zeigen die Zeilen 29 und 30, in denen sich QuickSort selbst aufruft.
In Zeile 34 wird die Funktion PrintFeld deklariert. PrintFeld enthält in der Zeile 39 die Funktion PRINT(). PRINT() wird verwendet, um Daten auf dem Bildschirm, auf dem Drucker oder in eine Datei auszugeben. In Zeile 39 soll eine Ausgabe auf dem Bildschirm erfolgen. PRINT() hat die folgende Aufrufsyntax: PRINT(<Ausgabeeinheit>, String, <Ausdruck>). Wenn keine Ausgabeeinheit angegeben ist (wie in Zeile 39), erfolgt die Ausgabe auf dem Bildschirm. Der String kann eine Text enthalten, welcher einen Platzhalter enthält, der angibt, wo der Ausdruck innerhalb des Textes auf dem Bildschirm erscheinen soll. Der Platzhalter besteht aus dem Prozentzeichen und einem Buchstaben, welcher den Typ des Ausdrucks angibt, der ausgegeben werden soll. In Zeile 39 befindet sich ein i hinter dem Prozentzeichen, d.h. der Ausdruck (die Variable intfeld[nr]) wird nach dem Format integer ausgegeben. Die Funktion PRINT() wird später noch eingehender beschrieben.
In Zeile 65 wird die Funktion CLS() aufgerufen. CLS() bewirkt ein Löschen des Bildschirmes.
Zeile 66 einthält die Funktion SCAN(). SCAN() ermöglicht ein Einlesen von Daten von der Tastatur oder aus einer Datei in eine Variable. Es gilt die folgende Aufrufsyntax: SCAN(<Eingabeeinheit>, String, Variable). Wenn keine Eingabeeinheit angegeben ist, wird von der Tastatur eingelesen (wie in Zeile 66). Der String enthält analog zur Funktion PRINT() einen Platzhalter, welcher angibt, nach welchem Format eingelesen werden soll. In Zeile 66 wird ein Datum nach dem Format integer von der Tastatur eingelesen und der Variable anzahl zugewiesen.
In Zeile 67 wird die oben definierte Funktion FillFeld() aufgerufen, und hierbei der Inhalt der Variablen anzahl als Parameter übergeben.
In Zeile 68 wird die oben definierte Funktion QuickSort() aufgerufen, und es werden zwei Parameter übergeben.
Zeile 69 wird vom Compiler ignoriert, da sie mit dem @-Zeichen beginnt. Stößt der Compiler auf das @-Zeichen so wird der folgende Programmtext bis zum Zeilenende überlesen. Diese Methode bietet also die Möglichkeit Kommentare in den Programmtext einzufügen.
Zeile 70 endet mit einem Punkt. Gofolio-Programme enden immer mit einem Punkt.
Wenn Sie die Geschwindigkeit der Sortierfunktionen QuickSort() und BubbleSort() vergleichen wollen, so stellen Sie Zeile 69 wieder her und machen Zeile 68 zur Kommentarzeile.
Gofolio unterscheidet nicht zwischen Groß- und Kleinschreibung. Es bleibt also Ihnen überlassen, ob Sie Ihre Variablen-Bezeichner, Funktions-Bezeichner oder die Gofolio-Funktionen groß bzw. klein schreiben. Leerzeichen werden vom Compiler grundsätzlich überlesen. Variablen- und Funktions-Bezeichner dürfen aus den folgenden Buchstaben und Zahlen bestehen: a-z und 0-9. Wobei die Bezeichner allerdings nicht mit einer Zahl beginnen dürfen. Ein Bezeichner darf aus maximal 20 Zeichen bestehen.
Eine besondere Art von Bezeichnern sind die sog. Preprocessoranweisungen. Dies sind konstante Bezeichner, die am Beginn des Programmtextes stehen und durch das „&“ Zeichen deklariert werden. Sie haben somit die Möglichkeit Konstante zu deklarieren.
Schreiben Sie z.B. in die erste Zeile ihres Programmtextes
&Pi=3.14;
so ist im folgenden Quelltext der Bezeicher &Pi immer gleichbedeutend mit 3.14. Bitte beachten Sie,
Beispiel:
&Pi=3.14; &rMax=10; defreal(r[&rMax]); defint(n); n:=0; while(n < &rMax, block( r[n]:=&Pi*real(n), inc(n)) ); n:=0; while(n < &rMax, block( print("%f\n",r[n]), inc(n)) ).
Zum Erzeugen von Variablen bietet Gofolio die folgenden Funktionen:
Gofolio besitzt eine strenge Typen-Überprüfung. Wollen Sie einer Variablen den Wert einer anderen zuweisen, wobei die andere Variable einen unterschiedlichen Typ hat, so muß dieser Typ in den Typ der Ziel-Variablen gewandelt werden. Andernfalls erhalten Sie einen Syntaxfehler. Lediglich Variablen vom Typ Byte und Integer sind zuweisungskompatibel. Wollen Sie z.B. den Wert der Real-Variable r der Integer-Variablen i zuweisen, so gilt:
i:=INT(r)
Um Typenwandlungen vorzunehmen, bietet Gofolio die folgenden Funktionen:
Zahlenkonstanten können in Gofolio in dezimaler und hexadezimaler Schreibweise dargestellt werden. Variablen vom Typ Real können nur Zahlen zugewiesen werden, welche dezimal mit Dezimalpunkt angegeben sind. Variablen vom Typ Integer und Byte können Zahlen in dezimaler Darstellung (ohne Dezimalpunkt !) sowie in hexadezimaler Darstellung zugewiesen werden.
Hexadezimale Konstanten beginnen immer mit den Zeichen 0x, worauf die Hex-Ziffern folgen (analog zur Programmiersprache C).
Beispiel:
DEFINT(i,n); DEFREAL(r); i:=0xEF11; n:=100; r:=100.0; print("i=%i\n",i); print("n=%i\n",n); print("r=%f\n",r).
In Gofolio können Array-Variablen von beliebiger Dimension erzeugt werden. Deklariert werden Array-Variablen mit den Gofolio-Funktionen DEFINT(), DEFCHAR(), DEFBYTE() und DEFREAL(). Wollen Sie ein eindimensionales Integer-Array mit 10 Elementen erzeugen, so geben Sie DEFINT(i[9]) ein. Hinter dem Variablen-Bezeichner schließt für jede Dimension ein Paar eckiger Klammern eine Zahl ein, welche die Anzahl der Elemente für die entsprechende Dimension minus 1 angibt. Anzahl-1 deshalb, weil das erste Element den Index 0 hat. Das letzte Element hat einen Index entsprechend der angegebenen Zahl.
Ein 3-dimensionales Real-Array mit jeweils 20 Elementen pro Dimension, wird wie folgt deklariert:
DEFREAL(raum[19][19][19])
Strings (Zeichenketten) werden in Gofolio als Arrays vom Typ Charakter aufgefaßt. Die Behandlung von Strings erfolgt ähnlich wie in der Programmiersprache C; d.h. das letzte Element eines Strings muß immer den Wert 0 (binäre Null) besitzen. Gofolio-Funktionen wie PRINT() oder STRCPY() erkennen so das Ende der Zeichenkette. Das folgende kleine Programm erzeugt einen String und gibt ihn auf dem Bildschirm aus:
DEFCHAR(str[20]); STRCPY(ADDR(str),"Hallo Welt"); PRINT("%s\n",ADDR(str)).
Die Gofolio-Funktion:
STRCPY(Adresse-Zielstring, Adresse-Quellstring oder String-Const)
kopiert die String-Constante an die Startadresse von str. Um die Adresse einer Variablen zu ermitteln, steht die Gofolio-Funktion ADDR(Variable) zur Verfügung. ADDR() gibt einen Integer-Wert zurück (welcher den Offset der Variablen im Datensegment darstellt). ADDR(str) ist mit ADDR(str[0]) identisch. Soll das Wort „Welt“ in dem String durch das Wort „Hans“ überschrieben werden, so kann dies durch STRCPY(addr(str[6]),„Hans“) geschehen.
Die Gofolio-Funktion PRINT() erwartet, um einen String auszugeben, immer die Adresse des Strings. Von dieser Adresse an werden dann solange Zeichen ausgegeben, bis PRINT() auf eine binäre Null stößt. Soll aus obigem String z.B. nur „Hallo“ ausgegeben werden so müßte hinter „Hallo“ eine binäre Null folgen: str[5]:=CHAR(0). Damit z.B. das 5.Zeichen in dem String kein ´o´sondern ein ´i´ enthält, geben Sie folgendes ein: str[4]:=´i´.
Zeichenketten werden also immer zwischen „“ und einzelne Buchstaben zwischen ´´ angegeben. Steuerzeichen werden in einem String durch den Gegenstrich (\) eingeleitet. Folgt in einem String dem Gegenstrich z.B. ein n (\n), so entspricht dies dem ASCII-Code 10, d.h. es erfolgt ein Zeilenvorschub. Gofolio unterstützt die folgen Zeichen zur Angabe von Steuerzeichen:
\n Zeilenvorschub \r Wagenrücklauf \t Tabulator \f Seitenvorschub \v Vertikaltabulator.
Ferner kann, wenn hinter dem Gegenstrich ein x folgt, der ASCII-Code in hexadezimaler Form direkt eingegeben werden.
Beispiel:
DEFCHAR(st1[10], st2[10]); STRCPY(ADDR(st1), "1234\n"); STRCPY(ADDR(st2), "\x31\x32\x33\x34\x0a"); PRINT("%s",ADDR(st1)); PRINT("%s",ADDR(st2))
Das Einlesen einer Zeichenkette von der Tastatur wird mit der Gofolio-Funktion SCAN() durchgeführt. SCAN(„%s“,str) bewirkt, daß str vom Element mit dem Index 0 an mit Zeichen von der Tastatur gefüllt wird. SCAN(„%s“,str[5]) bewirkt, daß str vom Element mit dem Index 5 an mit Zeichen von der Tastatur gefüllt wird. Im Gegensatz zur Funktion PRINT() benötigt SCAN() nicht die Adresse des Strings, sondern die Variable wird direkt angegeben.
Die Bearbeitung von Dateien unterstützt Gofolio mit den folgenden Funktionen:
Geschrieben und gelesen wird in bzw. aus Dateien mit den Funktionen PRINT() bzw. SCAN(), wobei als Aus-/Eingabeeinheit FILE angegeben wird.
In dem folgenden Beispiel werden die beiden Worte „Hallo Welt“ in die Datei d1.txt geschrieben und anschließend wieder aus der Datei gelesen und auf dem Bildschirm ausgegeben.
DEFCHAR(str[10]); IF(OPENOUT("d1.txt"), BLOCK( PRINT("Fehler beim Öffnen der Datei"), HALT()) ); PRINT(FILE,"Hallo\n"); PRINT(FILE,"Welt\n"); CLOSEOUT(); IF(OPENIN("d1.txt"), BLOCK( PRINT("Fehler beim Öffnen der Datei"), HALT()) ); SCAN(FILE,"%s",str); PRINT("%s\n",ADDR(str)); SCAN(FILE,"%s",str); PRINT("%s\n",ADDR(str)); CLOSEOUT().
Anstatt Zeichen und Zeichenketten können natürlich auch Zahlen vom Typ Integer, Byte und Real in Dateien geschrieben bzw. aus Dateien gelesen werden.
Die Deklaration von Funktionen in Gofolio muß immer direkt der Deklaration der globalen Programm-Variablen (wenn denn welche deklariert wurden) folgen. Funktionen können die folgenden Rückgabe-Typen haben:
Beispielprogamm mit Funktionsdeklaration:
DEFCHAR(str[20]); INTTYPE #Pos(DEFCHAR(ch)); BLOCK( DEFINT(n), n:=0, WHILE(str[n] <> ch, INC(n)), RETURN(n) ); STRCPY(ADDR(str),"Dies ist ein Test"); PRINT("Position=%i\n",Pos(´T´)).
In Zeile 3 des Programms wird die Funktion Pos() deklariert. Pos() liefert einen Rückgabewert vom Typ Interger. Die Funktionsdeklaration wird deshalb mit dem Gofolio-Bezeichner INTTYPE eingeleitet. In der Deklarationsphase folgt jetzt das #-Zeichen, welches die Deklaration des Funktionsnamen einleitet. Zwischen den Klammern hinter dem Funktionsnamen werden die Variablen für eventuelle Funktionsparameter deklariert. Im obigen Fall ist es die Variable mit dem Namen ch, und sie ist vom Typ Charakter. Die Zeilen 4-9 enthalten den Progamm-Code der Funktion Pos(). Dieser kann - wie im vorliegenden Fall - zu einem Block zusammengefaßt sein. Er kann aber auch nur aus einer einzelnen Anweisung bestehen - der Gofolio-Funktion RETURN().
Funktionen müssen immer mit RETURN() beendet werden. RETURN() wird verwendet, um eine Funktion zu verlassen sowie um einen Rückgabewert zurückzugeben. Auch VOIDTYPE-Funktionen müssen mit RETURN() verlassen bzw. beendet werden. Allerdings ohne Rückgabewert, welcher ansonsten zwischen den Klammern steht. Folgende Funktions-Deklaration ist daher auch möglich:
REALTYPE #Quadrat(defreal(x)); RETURN(x*x); PRINT("%f\n",Quadrat(1.234))
Eine Funktion kann wiederum eine andere Funktion aufrufen, wenn diese vor der rufenden Funktion deklariert wurde. Wie an dem kommentierten Programmbeispiel gezeigt, kann eine Funktion sich auch selbst aufrufen (Rekursion). In Gofolio ist es nicht möglich innerhalb einer Funktion weitere Funktionen - also funktionslokale Funktionen - zu deklarieren.
Beispiel zum Thema Funktion ruft Funktion:
REALTYPE #HochZwei(DEFREAL(x)); RETURN(x*x); REALTYPE #HochVier(DEFREAL(x)); RETURN(HochZwei(x)*HochZwei(x)); PRINT("%f\n",HochVier(1.234))
Den Funktionen können beliebig viele Parameter (Argumente) übergeben werden. Hierzu werden - wie oben schon gezeigt - zwischen den Klammern hinter dem Funktionsbezeichner Variablen deklariert, über die auf die Übergabeparameter zugegriffen werden kann. Diese Variablen werden als formale Parameter bezeichnet. Die formalen Parameter können vom Typ Charakter, Byte, Integer sowie Real sein.
Bei den Typen Integer und Byte ist es bezüglich der Parameterübergabe egal welchen Sie verwenden. Die Verwendung von Byte ist hier nicht sparsamer. Ich empfehle für den Fall der Formalparameter-Deklaration eine Beschränkung auf die Typen Charakter, Integer und Real. Die Parameter-übergabe erfolgt immer als Wertübergabe. D.h. wird beim Funktionsaufruf als Parameter eine Variable aus dem rufenden Progammteil übergeben, so wird deren Inhalt nicht durch die aufgerufene Funktion verändert. Die Positionen der Übergabeparameter beim Aufruf der Funktion müssen immer mit denen der formalen Parameter exakt übereinstimmen. Übergeben werden die Parameter jeweils getrennt durch ein Komma.
Beispiel:
VOIDTYPE #Test(DEFINT(i1,i2),DEFCHAR(ch),DEFREAL(r1,r2)); BLOCK( PRINT("i1=%i\n",i1), PRINT("i2=%i\n",i2), PRINT("r1=%f\n",r1), PRINT("r2=%f\n",r2), PRINT("ch=%c\n",ch), RETURN() ); Test(123,234,´w´,3.14,0.0034)
Der Compiler überprüft hierbei nicht, ob Sie beim Aufruf der Funktion die Parameter bezüglich Anzahl und Position korrekt übergeben haben. Sollte dies nicht der Fall sein, so stürzt Ihr Programm mit Sicherheit ab.
Innnerhalb von Funktionen können Variablen deklariert werden. Die Deklarationen dieser lokalen Variablen müssen die ersten Anweisungen in einer Funktion sein. Die so erzeugten Variablen haben nur Gültigkeit innerhalb der Funktion, in welcher sie deklariert wurden. Es kann also weder vom Hauptprogramm noch von einer evt. rufenden Funktion auf sie zugegriffen werden. Die Namen der lokalen Variablen können identisch mit globalen Variablen aus dem Hauptprogramm sein. Ein Zugriff auf eine solche Variable innerhalb einer Funktion bezieht sich dann immer auf die lokale Variable. Grundsätzlich kann aber von Funktionen aus auf jede globale Programm-Variable zugegriffen werden (wenn sie nicht den selben Namen wie eine lokale Variable trägt).
Ruft eine Funktion eine andere Funktion, so kann die gerufene Funktion nicht auf die lokalen Variablen der rufenden Funktion zugreifen. Ferner ist zu beachten, daß ein Name einer lokalen Variablen nicht identisch sein darf mit einem Namen eines formalen Parameters der entsprechenden Funktion. Denn die formalen Parameter sind praktisch ebenfalls lokale Variablen.
Beim Aufbau eines Gofolio-Programms muß also die folgende Reihenfolge beachtet werden:
Dieser Operator kann auf die Typen Byte, Integer und Real angewendet werden. Die Operanden werden addiert. Z.B.: PRINT(„%i\n“,1+1).
Dieser Operator kann auf die Typen Byte, Integer und Real angewendet werden. Vom linken Operand wird der rechte subtrahiert.
Z.B.: PRINT(„%i\n“,2-1).
Dieser Operator kann auf die Typen Byte, Integer und Real angewendet werden. Die Operanden werden multipliziert. Z.B.: PRINT(„%i\n“,2*2).
Dieser Operator kann auf die Typen Byte, Integer und Real angewendet werden. Der linke Operand wird durch den rechten dividiert.
Z.B.: PRINT(„%i\n“,6/3).
Dieser Operator kann nur auf die Typen Byte und Integer angewendet werden. Modulo (Divisionsrest wird gebildet) Z.B.: PRINT(„%i\n“,10%3).
Dieser Operator kann nur auf den Typ Real angewendet werden. Der linke Operand entspricht der Basis, und der rechte dem Exponenten.
Z.B.: PRINT(„%f\n“,2.0^0.5).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn diese gleich sind.
Z.B.: IF(3 = 4, PRINT(„wahr“), PRINT(„falsch“)).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn der linke größer-gleich dem rechten ist. Z.B.: IF(3 >= 4, PRINT(„wahr“), PRINT(„falsch“)).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn der linke kleiner-gleich dem rechten ist. Z.B.: IF(3 < = 4, PRINT(„wahr“), PRINT(„falsch“)).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn der linke größer dem rechten ist. Z.B.: IF(3 > 4, PRINT(„wahr“), PRINT(„falsch“)).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn der linke kleiner dem rechten ist. Z.B.: IF(3 < 4, PRINT(„wahr“), PRINT(„falsch“)).
Kann auf alle Typen angewendet werden. Bildet mit einem linken und einem rechten Operanden (müssen vom gleichen Typ sein) eine Bedingung, welche wahr ist, wenn diese ungleich sind.
Z.B.: IF(3 <> 4, PRINT(„wahr“), PRINT(„falsch“)).
Eine Bedingung gibt für den Fall, daß sie wahr ist, den Wert 1 zurück. Für den Fall, daß die Bedingung nicht wahr ist, wird 0 zurückgegeben. Es ist daher möglich das Ergebnis einer Bedingung einer Variablen zuzuweisen. Der Rückgabewert ist immer vom Typ Integer.
Beispiel:
DEFINT(bool); bool:=(10.1 > 10.0); PRINT("%i\n",bool).
Der Zuweisungsoperator kann auf alle Typen angewendet werden. Dem linken Operanden wird der rechte zugewiesen. Beide Operanden müssen vom gleichen Typ sein. Bei dem linken Operanden muß es sich um eine Variable handeln. Der rechte Operand kann eine Variable oder ein Ausdruck sein.
Z.B.:
DEFCHAR(ch); DEFINT(i,a); DEFREAL(r); ch:=´k´; i:=135; r:=(0.199)^2.0; a:=i; a:=i+1; i:=INT(ch)-10; a:=INT(r); r:=REAL(ch).
In diesem Abschnitt werden sämtliche Gofolio-Funktionen nach folgendem Schema beschrieben:
PRINT("%f\n",ABS(-1.23))
PRINT("%f\n,ACOS(3.14))
IF(AND(1 >= 0, 2 = 2), PRINT("wahr"), PRINT("falsch"))
PRINT("%i\n",ANDB(2,18))
PRINT("%f\n",ASIN(1.552))
PRINT("%f\n",ATAN(1.23))
DEFINT(i); i:=0; WHILE(i < 100, BLOCK( PRINT("%i\n"), i:=i+1) )
BLOCK( DEFINT(i), i:=0, WHILE(i < 100, BLOCK( PRINT("%i\n"), i:=i+1) )
DEFINT(i); i:=10000; PRINT("Low-Byte %i\n", PEEK(DS(),ADDR(i)); PRINT("High-Byte %i\n", PEEK(DS(),ADDR(i)+1)
IF(x < y, BLOCK( print("x ist kleiner y"), y:=y-1), ELSE( print("x ist größer gleich y"), x:=x-1 );
_AX:=256*0x2a; INTR(0x21); PRINT("Tag= %i\n",ANDB(_DX,0x00ff)); PRINT("Monat=%i\n",ANDB(_DX,0xff00)/256)
Hier noch ein paar Erläuterungen zum Umgang mit den Prozessorregistern unter GoFolio:
Die Register AX, BX usw. sind 16 Bit breit. Sie werden prozessorintern aber noch unterteilt in ein High-Byte (8-Bit) und ein Low-Byte (ebenfalls 8-Bit).
Viele Programmiersprachen erlauben es z.B. das Register AX auf drei Arten anzusprechen:
Aus Performence-Gründen gibt es unter Gofolio die Register AL, AH, BL, BH usw. nicht. Sondern sie müssen mit folgendem Trick angesprochen werden (das Beispiel betrifft wieder das AX-Register, gleiches gilt aber für alle weiteren Register):
Typ | Ausdruck | Ausgabe |
---|---|---|
i | Integer | signed integer (dezimal) |
o | Integer | unsigned integer (oktal) |
u | Integer | unsigned integer (dezimal) |
x | Integer | unsigned integer (hexadezimal) |
f | Real | Gleitkommawert, die Anzahl der Nachkommastellen wird durch Präzision festgelegt |
e | Real | Gleitkommawert, welcher mit Exponent ausgegeben wird |
c | Charakter | Zeichen |
s | String | Zeichenkette. |
Beispiel:
DEFINT(i); DEFREAL(r); DEFCHAR(ch[20]); i:=-1234; r:=SIN(1.0/3.0); STRCPY(ADDR(ch),"Gofolio ist super"); print("signed integer: %i\n",i); print("unsigned integer: %u\n",i); print("unsigned integer (oktal): %o\n",i); print("unsigned integer (hexadezimal): %x\n",i); print("Gleitkomma: %f\n",r); print("Gleitkomma: %2.3f\n",r); print("Gleitkomma: %e\n",r); print("String: %s\n",ADDR(ch)); print("Zeichen: %c\,ch[3])
% <Breite> <[Eingabefeld]> Typ.
Beispiele:
Typ | Eingabe | Variablen-Typ |
---|---|---|
i | Integer (dezimal) | Integer |
o | Integer (oktal) | Integer |
x | Integer (hexadezimal) | Integer |
f | Real | Real |
s | String | Array of Charakter |
c | Zeichen | Charakter |
Beispiel:
DEFCHAR(ch[20]); DEFINT(i); DEFREAL(r); PRINT("Bitte String eingeben: "); SCAN("%s",ch); PRINT("\nBitte Integerwert eingeben: "); SCAN("%i",i); PRINT(\nBitte Realwert eingeben: "); SCAN("%f",r); PRINT("%s\n",ADDR(ch)); PRINT("%i\n",i); PRINT("%f\n",r)
Auch SCAN() stimmt nahezu mit der C-Funktion scanf() überein. Der Unterschied zur C-Funktion besteht bei SCAN() darin, daß nur eine Variable angegeben werden darf und der Format-String nur als Constante (d.h. zwischen Anführungszeichen) angegeben werden darf.
DEFCHAR(c1[20], c2[20]); STRCPY(ADDR(c1), "Hallo Welt"); STRCPY(ADDR(c2), ADDR(c1)); PRINT("String c1: %s\n", ADDR(c1)); PRINT("String c2: %s\n", ADDR(c2))
DEFCHAR(ch[10]); DEFREAL(r); STRCPY(ADDR(ch), "1.2345"); r:=VAL(ADDR(ch)); r:=r-1.0; PRINT("%f\n",r)
Beispiel:
DEFINT(n); n:=0; WHILE(n < 100, BLOCK( PRINT("%i\n",n), INC(n)) )
Fehlernummer | Bedeutung |
---|---|
1 | „(“ Klammer auf erwartet |
2 | „)“ Klammer zu erwartet |
3 | „,“ Komma erwartet |
4 | Type-Error (Typen sind nicht kompatibel) |
5 | Der verwendete Bezeichner wurde schon deklariert |
6 | „:=“ Zuweisungs-Operator erwartet |
7 | „]“ eckige Klammer zu erwartet |
8 | Bezeichner erwartet |
9 | String erwartet |
10 | Unbekannter Bezeichner |
11 | „.“ Punkt erwartet |
12 | „;“ Semikolon erwartet |
13 | Zahlen-Konstante erwartet |
Version 1.0 von GF.EXE und GO.EXE (1994)
In den Versionen 1.0 gab es einen Fehler beim Vergleichen von Real-Zahlen, dieser wurde behoben;
Auf dem ATARI Portfolio arbeitet GF.EXE teilweise nicht korrekt. Ursache waren hier Compilereinstellungen beim Übersetzen des Programms GF.EXE.
GF.EXE wurde erneut übersetzt und arbeitet jetzt korrekt auf dem ATARI Portfolio.
Auf älteren ATARI Portfolio Versionen funktioniert das Überschreiben von Dateien nicht korrekt. Dies hatte zur Folge, daß, wenn mit GF.EXE ein Programm erneut übersetzt und somit eine bestehende *.GFO Datei überschrieben wurde, die neu erzeugte *.GFO Datei fehlerhaft war. Man kann dieses Problem umgehen, indem vor einer neuen Compilation die entsprechende *.GFO Datei gelöscht wird (es wird dann also keine Datei überschrieben). Die Version 1.2 von GF.EXE löscht vor dem Schreiben der *.GFO Datei eine evt. vorhandene *.GFO Datei automatisch, weshalb „manuelles“ Löschen nicht mehr erforderlich ist.
Die Ausgabe von Byte-Arrays mit der Funktion PRINT() funktionierte teilweise nicht richtig. Dieser Fehler wurde behoben.
An GO.EXE wurde eine kleine interne Änderung vorgenommen. Das Programm wurde dadurch etwas kleiner und schneller.
Der interne Puffer für String-Konstenten wurde bei GF.EXE vergrößert.
Ein Fehler der bei der Verwendung der Funktionen AND() und OR() auftrat wurde behoben.
GO.EXE wurde überarbeitet. Das Programm wurde dadurch schneller.
Fehler, die sich bei der Überarbeitung von GO.EXE eingeschlichen haben wurden bereinigt. Die Funktionen PEEK(), POKE() und OUT() arbeiten wieder korrekt.
GoFolio hat einen Preprocessor erhalten. Sprachumfang und Ausführungsgeschwindigkeit haben sich gegenüber der Version 1.51 nicht geändert.
Genaue Erläuterungen siehe Punkt 3.2.1.
Der Preprocessor legt eine temporäre Quelltextdatei an. In der Version 2.0 wurde diese Datei auch dann angelegt, wenn wenn keine Preprocessoranweisungen vorhanden waren. In der Version 2.1 wird diese temporäre Datei nur angelegt, wenn tatsächlich Preprocessoranweisungen vorliegen.
Ein Fehler im Preprocessor wurde behoben.
Sie haben die Möglichkeit Maschinencode in Ihre Gofolio-Programme zu integrieren. Hierzu schreiben Sie die Hex-Codes des Maschinenprogramms in ein Char-Array (String). Gestartete wird das Maschinenprogramm, indem ein Interrupt-Vektor eines unbenutzten Interrupts auf die Adresse des Strings mit dem Maschinencode „umgebogen“ wird, und anschließend der Interrupt aufgerufen wird. Zum Umbiegen des Interrupt-Vektors wird die Funktion 25h des Interrupt 21h „Setze Interrupt-Vektor“ verwendet. Hierzu wird das AH-Register mit der Funktionsnummer 25h geladen und das AL-Register mit der Nummer des Interrupts, welcher „umgebogen“ werden soll. In das DX-Register wird die Offset-Adresse des Maschinencodes (also des Strings) geschrieben (das DS-Register muß nicht gesetzt werden, da es schon die korrekte Segment-Adresse enthält). Nach dem nun der Interrupt 21h ausgeführt wurde, zeigt jetzt der „verbogene“ Vektor auf den Maschinen-Code.
Es ist darauf zu achten, daß die letzte Anweisung in dem Maschinenprogramm immer IRET (Interrupt-Return) ist.
In dem folgenden Gofolio-Programm wird der (freie) Interrupt 63h auf das Beispiel-Maschinenprogramm, welches einen Signalton ausgibt, gesetzt.
Beispiel-Maschinenprogramm:
PUSH AX PUSH DX MOV DL,07 MOV AH,02 INT 21 POP DX POP AX IRET
Gofolio-Programm:
defchar(ch[20]); defint(off,seg); off:=addr(ch); strcpy(off,"\x50\x52\xb2\x07\xb4\x02\xcd\x21\x5a\x58\xcf"); _ax:=0x25*256+0x63; _dx:=off; intr(0x21); intr(0x63)
Mit dem Befehl intr(0x63) wird das Maschinenprogramm gestartet.
Der Datenaustausch zwischen Maschinen- und Hauptprogramm kann über die Registervariablen _AX, _BX, _CX usw. erfolgen.
Gofolio beinhaltet nur wenige Funktionen zur Stringbehandlung. Anhand der Funktionen len() (berechnet die Länge eines Strings) und pos() (berechnet die Position eines Zeichens in einem String) soll demonstriert werden, wie eigene Funktionen zur Stringbehandlung effektiv selbstgeschrieben werden können.
Beispiel:
defchar(c[50],ch); inttype #len(defint(x)); block( defint(n), n:=0, while(peek(ds(),x+n) <> 0, inc(n)), return(n) ); inttype #pos(defchar(c),defint(x)); block( defint(n), n:=0, while(char(peek(ds(),x+n)) <> c,inc(n)), if(len(x) < n, return(-1), return(n)), ); print("String?: "); scan("%s",c); print("Zeichen?: "); scan("%s",ch); print("\nlen=%i",len(addr(c))); print("\npos=%i",pos(ch,addr(c))).
Besondes bei der Programmierung der Hardware (z.B. beim Ein-/Auslesen von Ports oder Prozessorregistern) ist es wichtg, daß das GOFOLIO-Programm hinterher kommt. Hier einige Tips:
Laufvariablen von WHILE-Schleifen immer als Integer definieren und mit den Funktionen INC() bzw. DEC() erhöhen bzw. erniedrigen. Dies ist wesentlich schneller als z.B.: i:=i+1 bzw. i:=i-1.
Beim Umgang mit Variablen gibt es beträchtliche Geschwindigkeitsunterschiede: