*STJ-OBERON-2 * *Eine Oberon-2 Implementation f•r Atari ST/STe/TT * von Stephan Junker 28. November 1993 Inhaltsverzeichnis ================== 1) Einleitung 1.1) Stand der Entwicklung 1.2) Geplante Entwicklung 1.3) Benutzungsbedingungen 1.4) Fehlermeldungen 1.5) Kontakt 2) Von Modula-2 nach Oberon-2 2.1) Streichungen 2.1.1) Datentypen 2.1.2) Module und Import-/Exportregeln 2.1.3) Anweisungen 2.1.4) Low-Level Anweisungen 2.2) Neue Features 2.2.1) Typerweiterung 2.2.2) Typgebundene Prozeduren 2.2.3) Typinklusion 2.3) Sonstige Unterschiede 3) Allgemeine Erl„uterungen 3.1) Suchpfade 3.2) Dateien 3.3) Einstellung der Shell 4) Installation 4.1) Start 5) Der Compiler 5.1) Aufruf, Parameter 5.2) Environmentvariablen 5.3) Optionen im Quelltext 5.4) Ausgabe 5.5) Vordefinierte Prozeduren 5.6) Unterschiede zum Standard-Oberon-2 5.7) Bekannte Fehler 6) Der Assembler 6.1) Symbolkonventionen 6.1.1) Formelausdr•cke 6.2) Pseudobefehle 6.2.1) SET und EQU 6.2.2) CHARSET 6.2.3) CPU 6.2.4) SUPMODE 6.2.5) SEGMENT 6.2.6) DC,DS 6.2.7) ALIGN 6.2.8) MACRO 6.2.9) IRP 6.2.10) REPT 6.2.11) Bedingte Assemblierung 6.2.12) Lokale Label 6.3) Hochsprachenelemente 6.3.1) IF cond THEN ... ELSIF ... ELSE ... END 6.3.2) REPEAT ... UNTIL cond 6.3.3) LOOP ... END 6.3.4) EXIT [(Zahl)] 6.4) Diverses 6.4.1) INCLUDE 6.4.2) MESSAGE, WARNING, ERROR und FATAL 6.5) Zugriff auf Oberon-Bezeichner 7) Der Linker 7.1) Aufruf, Parameter 7.2) Environmentvariablen 8) Die Lader 8.1) Aufruf, Parameter 8.2) Ausgabe 9) Das Make-Utility 9.1) Aufruf, Parameter 9.2) Environmentvariablen 9.3) Hinweise 10) Der Scanner 10.1) Aufruf, Parameter 10.2) Environmentvariablen 11) Debugging 11.1) DB 11.2) Bugaboo 11.3) Tips 12) Utilities 12.1) Der Browser 12.2) Inline-Zeilen erzeugen 13) Speicherverwaltung 13.1) Benutzung in Programmen 13.2) Implementierung 14) Die Bibliotheken 14.1) Betriebssystem 14.1.1) BIOS 14.1.2) GEMDOS 14.1.3) MiNT 14.1.4) XBIOS 14.2) Abstrakte Datenstrukturen 14.2.1) BinTree 14.2.2) CDCL 14.2.3) DCL 14.2.4) FIFO 14.2.5) LRU 14.2.6) Stack 14.3) Standardmodule 14.3.1) Buffers 14.3.2) CommandLine 14.3.3) Cookie 14.3.4) Datum 14.3.5) Environment 14.3.6) Error 14.3.7) Exceptions 14.3.8) Execute 14.3.9) File 14.3.10) FileBuffer 14.3.11) Filename 14.3.12) IO 14.3.13) Kernel 14.3.14) Key 14.3.15) MathCom 14.3.16) MathLib0 14.3.17) Memory 14.3.18) Modules 14.3.19) MVC 14.3.20) NumStr 14.3.21) Paths 14.3.22) Strings 14.3.23) Supervisor 14.3.24) Sys 14.3.25) Task 14.3.26) VA 14.4) VDI-Module 14.4.1) VDI 14.4.2) VDIAttributes 14.4.3) VDIControl 14.4.4) VDIExcapes 14.4.5) VDIInput 14.4.6) VDIInquiry 14.4.7) VDIOutput 14.4.8) VDIRaster 14.5) AES-Module 14.5.1) AES 14.5.2) Appl 14.5.3) Evnt 14.5.4) Form 14.5.5) Fsel 14.5.6) Graf 14.5.7) Menu 14.5.8) Objc 14.5.9) Rsrc 14.5.10) Wind 14.6) Erweiterte AES-Module 14.6.1) Dialogs 14.6.2) FontSelect 14.6.3) GemApp 14.6.4) GEMIO 14.6.5) Menus 14.6.6) TermWin 14.6.7) WindowDialog 14.6.8) WinView 15) Tutorial 15.1) Die Resourcedatei 15.2) Der Rumpf 15.3) Resourcedatei laden 15.4) Die Men•zeile 15.5) Ein Fenster ”ffnen 15.6) Einen Dialog darstellen 15.7) Das fertige Programm 15.8) Zusammenfassung 16) Anhang 16.1) Literatur 16.2) Danksagungen 1) Einleitung ============= Die Entstehung dieses Oberon-Systems begann kurz vor Weihnachten als Frank Storm mir die Quelltexte zum Oberon-1 Compiler von Niklaus in '92, Wirth die Hand drckte. Zun„chst mužte ich den in Oberon geschriebenen Compiler in Modula umschreiben und den Codegenerator auf 68000 „ndern. Der n„chste Schritt war dann nat•rlich, das ganze wieder in Oberon umzuschreiben. Dies war bis M„rz '93 geschafft, seitdem programmiere ich nur noch in Oberon. Inzwischen sind etliche Monate vergangen, in denen ich fleižig Fehler behoben und Erweiterungen eingebaut habe. Trotzdem wird noch einige Zeit vergehen, bis diese Software den Betateststatus verliert. 1.1) Stand der Entwicklung -------------------------Der Kern einer Programmiersprache ist nat•rlich der Compiler. Dieser wird erg„nzt durch Linker, Lader, Make und Scanner. Zus„tzlich gibt es noch einige Utilities. Alles wird als Objektmodule ausgeliefert und vom Installationsprogramm zu ausf•hrbaren Programmen gelinkt. Aber erst durch umfangreiche Libraries wird eine Programmiersprache benutzbar. Die meisten Module sind von mir im Laufe von mehreren Jahren programmiert worden. Einige sind auch von anderen Autoren, und weitere Implementationen sind nat•rlich erw•nscht. Der Compiler hat bereits starke Erweiterungen gegen•ber dem von Wirth erfahren. Dies ist zwar unsch”n, weil dadurch wieder Inkompatibilit„ten zu anderen Compilern entstehen, aber sie erleichtern die Programmierung. Wenn man portable Programme schreiben m”chte, muž man auf diese Erweiterungen verzichten. 1.2) Geplante Entwicklung ------------------------Zun„chst steht die Entwicklung einer Load-Time-Linking Entwicklungsumgebung mit Chatwin auf dem Plan. Dann fehlt noch ein Source-Level-Debugger. Da werde ich mich irgenwann mal drangeben. Die Codequalit„t k”nnte auch noch verbessert werden, dazu geh”rt auch 68030- und FPU-Unterst•tzung. Ansonsten werden natrlich st„ndig noch Fehler verbessert. behoben und Bibliotheksmodule 1.3) Benutzungsbedingungen -------------------------STJ-Oberon-2 ist Shareware. Die ”ffentliche Version darf in Mailboxen hochgeladen und auf PD-Disketten bis 5 DM pro Diskette vertrieben werden. Wenn man dauerhaft damit arbeiten m”chte, muž man den Sharewarebeitrag von 50 DM entrichten. Daf•r bekommt man die neueste Version auf Diskette zugeschickt (also Adresse nicht vergessen!). Diese enth„lt eine private Version, die nicht weitergegeben werden darf. Darauf ist zus„tzlich diese Anleitung als DVI-File sowie der fehlende optimierende Linker. Auf Wunsch kann ich die Anleitung auch in eine Postscriptdatei umwandeln. M”chte jemand eine gedruckte Anleitung (Laserdrucker) mit Ringbindung, kostet dies 20 DM extra. Wer am System mitarbeitet oder mitgearbeitet hat, individuellen Rabatt erhalten. Einfach mal anfragen. kann einen 1.4) Fehlermeldungen -------------------Das ganze System ist noch in der Entwicklungs- und Betatestphase. Ich weiž, daž es noch einige Macken hat. Ich bem•he mich, sie noch alle zu entfernen. Ich bernehme keine Garantie fr die Funktionsf„higkeit dieses Programms und hafte nicht fr Sch„den, die dieses Programm verursacht. Falls Fehler entdeckt werden, sollte man mich m”glichst genau dar•ber informieren. 1.5) Kontakt -----------Stephan Junker Heuvel 1A NL-6291 CP Vaals E-Mail, MausNet : Stephan Junker @ AC2 Sparkasse Aachen Kontonummer : 16013351 Bankleitzahl: 390 500 00 2) Von Modula-2 nach Oberon-2 ============================= Fr diejenigen, die Modula kennen und evtl. umsteigen m”chten, werde ich hier kurz die Unterschiede zwischen Modula-2 und Oberon-2 auflisten. Es soll keineswegs eine Sprachbeschreibung ersetzen, sondern nur einen Eindruck von den neuen M”glilchkeiten vermitteln, um das Interesse zu wecken. 2.1) Streichungen ----------------Niklaus Wirth hatte den Mut, einige Features von Modula-2 ersatzlos zu streichen. Dahinter stand das Konzept, die Sprache Oberon auf das Wichtigste zu konzentrieren und damit einen kleinen, schnellen Compiler zu realisieren. Diese Streichungen sind nicht bei allen Modula-Programmieren auf Gegenliebe gestožen, aber man kann damit leben. 2.1.1) Datentypen ................. _Variante Records_ wurden eliminiert, da sie die Implementation einer Speicherverwaltung mit Garbage Collector im Wege stand. Ihre Funktionalit„t wurde durch erweiterbare Typen erhalten[1]. _Opake Typen_ wurden •berfl•ssig, da man Records nicht mehr komplett exportieren muž. Die Auswahl einzelner Feldelemente ist wesentlich flexibler. _Aufz„hlungstypen_ standen der Erweiterung •ber Modulgrenzen hinweg im Wege. Aužerdem f•hrte ein Import des Typbezeichners zum impliziten Import aller assoziierten Konstantenbezeichner. _Unterbereichstypen_ hatten nicht den Nutzen, der die dafr n”tige zus„tzliche Komplexit„t des Compilers rechtfertigt. _Mengen_ wurden zu einem einzigen Typ reduziert: SET. Diese Menge beinhaltet alle Zahlen von 0 bis zu einem implementationsabh„ngigen Maximum[2]. Dies resultiert aus der Elimination von Aufz„hlungs- und Unterbereichstypen. Der Typ _CARDINAL_ wurde eliminiert, da die Benutzung von 32-Bit Werten dies unn”tig macht[3]. _Zeigertypen_ k”nnen nur noch auf Records und Arrays zeigen. Dies h„ngt damit zusammen, das der Zeigeroperator '^' nicht mehr benutzt werden muž. _Indextypen_ k”nnen nicht mehr angegeben werden. Stattdessen gibt man nur noch die Anzahl Elemente des Arrays an. --------------[1] nicht ganz, wie ich meine [2] bei STJ-Oberon-2 ist das Maximum 31 [3] sind man beachte, daž damit keine vorzeichenlosen Vergleiche m”glich 2.1.2) Module und Import-/Exportregeln ...................................... _Lokale Module_ wurden eliminiert, da sie nur selten benutzt wurden und den Compiler unn”tig schwierig machten. _Unqualifizierter Export_ ist nicht mehr m”glich, d.h. man gibt nur noch den Namen eines Moduls in der Importliste an und muž immer mit vorangestelltem Modulbezeichner auf die exportierten Bezeichner zugreifen. Um die Tipparbeit ein wenig zu reduzieren, ist es m”glich, den Import umzubenennen. Daf•r schreibt man beim Import 'Abk:=Abkuerzung', z.B. 'WDial:=WindowDialog'. Das _Hauptmodul_ als solches gibt es nicht mehr. Alle Module sind gleichberechtigt, d.h. daž auch alle Module linkf„hig sind. _Definitionsmodule_ sind fr den Compiler nicht mehr n”tig, aber man sollte sie nat•rlich f•r den Programmierer erzeugen. Daf•r ist der Browser vorgesehen. Der Export geschieht jetzt, indem man hinter einem Bezeichner einen Stern f•r normalen oder ein Minuszeichen (bei Variablen und Feldelementen) f•r Read-Only Export angibt. 2.1.3) Anweisungen .................. Die _WITH-Anweisung_ wurde eliminiert, be- da der qualifizierte Zugriff zu vorzugen ist. Diese Anweisung hat in Oberon jetzt eine andere Funktion. 2.1.4) Low-Level Anweisungen ............................ _ADDRESS_ und _WORD_ wurden durch BYTE ersetzt. _Typumwandlungsfunktionen_ mit vorangestelltem Typbezeichner und _absolute Addressierung_ wurden eliminiert. 2.2) Neue Features -----------------2.2.1) Typerweiterung ..................... Es ist m”glich, einen bestehenden Recordtyp zu erweitern. Beispiel: Wenn Typ Point so Point = RECORD x,y : INTEGER END definiert ist, kann man ihn so Rect = RECORD(Point) w,h : INTEGER END erweitern. Typ Rect hat dann die Elemente x,y,w,h. Dann ist es m”glich, Prozeduren zu definieren, die einen Typ Point als Parameter erwarten, und diesen einen Typ Rect zu •bergeben. Analog gilt dies f•r Zeiger auf solche Typen. Mit einem Typeguard der Form var(typ) kann man zur Compilezeit den Typen festlegen. Zur Laufzeit wird dann •berpr•ft, ob die Variable var tats„chlich den Typ typ hat. Wenn nicht, wird das Programm abgebrochen. Es ist auch m”glich, den Typ einer Variablen abzufragen. Der Ausdruck var IS typ liefert TRUE, wenn var vom (dynamischen) Typ typ ist. Diese Abfrage inklusive eines regionalen WITHStatement: Typeguards WITH var : Point DO (* wenn var vom typ Point ist *) var.x := 0 (* dann x l”schen *) | var : Rect DO (* ist es ein Rect *) var.w := 0 (* dann w l”schen *) ELSE (* sonst *) END; (* nichts *) leistet das 2.2.2) Typgebundene Prozeduren .............................. Es ist m”glich, Prozeduren an einen Recordtyp zu binden. In der Sprache des OOP sind dies dann Methoden, die auf dem Typ arbeiten, an den sie gebunden sind. Im Gegensatz zu anderen Ans„tzen werden bei Oberon jedoch keine Bezeichner implizit •bergeben, sondern explizit angegeben. Eine solche Prozedur wird so PROCEDURE (VAR p : Point) Clear; BEGIN p.x := 0; p.y := 0; END; definiert und mit point.Clear aufgerufen. Statt VAR Point k”nnte man auch einen Zeiger auf Point ohne VAR angeben. Man kann jetzt hingehen und eine Prozedur Clear an Rect binden. Der Name ist nicht etwa schon benutzt, denn er ist ja nur innerhalb eines Typs Point sichtbar. PROCEDURE (VAR r : Rect) Clear; BEGIN r.w := 0; r.h := 0; r.Clear^; END; Dieses Clear l”scht erst w und h, um dann mit r.Clear^die geerbte Prozedur Clear aufzurufen, die x und y l”scht. 2.2.3) Typinklusion ................... Die Integer- und Realtypen sind jeweils Teilmengen des n„chst gr”žeren Bereichs. Daher ergibt sich folgende Hierarchie: LONGREAL > REAL > LONGINT > INTEGER > SHORTINT Das bedeutet, daž ein Wert eines untergeordneten Typs einem h”heren zugewiesen werden kann. 2.3) Sonstige Unterschiede -------------------------- _Prozedurtypen_ werden mit Dummynamen f•r die Parameter angegeben. _Globale Variablendeklarationen_ m•ssen immer vor der ersten Prozedur erfolgen. 3) Allgemeine Erl„uterungen =========================== 3.1) Suchpfade -------------Suchpfade sind solche Pfade, in denen nach Dateien gesucht wird. Viele der Systemprogramme ben”tigen Suchpfade, aber alle bauen auf einem Modul namens Paths auf. Daher verlangen alle Programme den gleichen Aufbau der Suchpfade. Definiert werden sie mit einer Environmentvariablen. Mehrere Suchpfade m•ssen mit Komma oder Semikolon getrennt werden. Ein Backslash am Ende ist m”glich, aber nicht n”tig. Eine kleine M”glichkeit f•r Wildcards ist vorgesehen: Der letzte Order einer Suchpfaddefinition darf ein '*' sein, so daž alle Ordner in diesem Directory durchsucht werden. Die Suche beginnt immer mit dem zuletzt angegebenen Pfad. Beispiel: MODPATH=E:\OBERON\GEM,E:\OBERON\STD\ Damit k”nnen alle Dateien gefunden werden, die den Mustern E:\OBERON\GEM\*.*und E:\OBERON\STD\*.*gen•gen. Beispiel: MODPATH=E:\OBERON\LIB\* Damit k”nnen alle Dateien gefunden werden, die den Mustern E:\OBERON\LIB\*\*.*gen•gen. Wenn im folgenden also von 'Suchpfaden' die Rede ist, sind Environmentvariable mit obigem Inhalt gemeint. Eine Besonderheit der Suchpfade m”chte ich hier schon erw„hnen: Eine sinnvolle Einstellung ist es, nur MODPATH zu definieren. Dann wird immer dort gesucht und Ausgaben des Compilers werden bei den Sourcen gespeichert. In dieser Form werden die Dateien auch ver”ffentlicht. Es ist aber m”glich, die verschiedenen Dateitypen in getrennten Ordnern zu bewahren. Dazu muž man f•r jeden Dateityp einen Suchpfad definieren und die Ausgaben des Compilers werden in den letzten Suchpfad geschrieben. Diese Suchpfade heižen OBJPATH, SYMPATH und INFPATH. 3.2) Dateien -----------Das System unterscheidet vier verschiedene Dateien: 1) Die Quelltexte mit der Extension MOD. 2) Die Objektmodule mit der Extension OBJ. eines Moduls auf. Diese nehmen den Code 3) Die Symboldateien mit der Extension SYM. Diese enthalten Informationen •ber exportierte Bezeichner. 4) Die Infodateien mit der Extension INF. Diese enthalten zus„tzliche Informationen zu Modulen. Momentan sind das zum Beispiel ein Programmname und Optimierungsinformationen. Sp„ter sollen die Infodateien dann auch Informationen f•r den Source Level Debugger aufnehmen. 5) Die Fehlerdateien mit der Extension ERR. Darin werden Fehler, die der Compiler findet, in einem frei w„hlbaren Format notiert. 3.3) Einstellung der Shell -------------------------DIe Shell, mit der man arbeitet, sollte man mittels Batchfile beim Starten schon vern•nftig konfigurieren. Dies ist ein Beispiel-Setup f•r Chatwin: alias make e:\oberon\Make.TTP $* alias opt e:\oberon\OPTIMIZE.TTP $* alias scan e:\oberon\SCAN.TTP $* alias lt e:\oberon\LOAD.TTP $* alias lg e:\oberon\LOAD.PRG $* alias browse e:\oberon\browse.ttp $* env OC=e:\oberon\compile.ttp env MODPATH=E:\OBERON\LIB\* env LINKALSO=Exceptio Desweiteren empfiehlt es sich, f•r jedes Projekt eine Batchdatei einzurichten, in der Pfade, Namen, Extension etc. definiert werden. Hier ist mal ein Beispiel, daž ich f•r das Installationsprogramm verwende: env MODPATH=E:\OBERON\LIB\*,E:\OBERON\INSTALL\ env PRGEXT=PRG env PRGPATH=E:\OBERON\ env LINKALSO=Exceptio dellist * addlist e:/oberon/install/install.mod addlist e:/oberon/install/linker.mod addlist e:/oberon/install/syminld.mod 4) Installation =============== Das Installationsprogramm INSTALL.PRG ist das einzig lauff„hige Programm in der Distribution. Es erzeugt alle Programme, die f•r die Arbeit mit STJOberon-2 ben”tigt werden. 4.1) Start ---------Beim Start von Install erscheint eine Dialogbox. Dort sieht man zun„chst neun Buttons mit jeweils einem Namen dran. Selektiert sind alle Programme aužer Oberon[4]. W„hlen sie die Programme, die sie haben wollen. Stellen sie nun ein, ob die Programme eine Symboltabelle haben oder optimiert[5] werden sollen. Die Symboltabelle kostet nur unn”tig Platz, also verzichten sie lieber darauf. Als letztes k”nnen sie einen Zielpfad f•r die Programme Dieser wird anfangs auf den Pfad gesetzt, in dem auch das Installationsprogramm steht. angeben. Wenn sie nun den Knopf 'Installieren' anw„hlen, verschwindet die Dialogbox und ein Terminalfenster wird ge”ffnet. Dort erscheinen die Ausgaben des Linkers, der alle selektierten Programme linkt. Dabei sind die Suchpfade so eingestellt, wie sie in der Distribution sind, also LIB\*\,SYS\ und TOOLS\, jeweils vom Directory ausgehend, in dem das Installationsprogramm steht. Wenn sie also gerade erst das Archiv ausgepackt haben und dabei die Ordnerstruktur nicht verloren ging, m•ssen die Module auch gefunden werden. Hinweis: des Im Prinzip ist der Linker auch in der Lage, die T„tigkeit Installationsprogramms durchzuf•hren. Lediglich die Lader und Oberon k”nnen nicht vom normalen Linker gelinkt werden. --------------[4] denn Oberon gibt's noch nicht [5] Die Optimierung funktioniert leider noch nicht 5) Der Compiler =============== Der Compiler des Oberon-Systems (COMPILE.OBJ/TTP) entstand aus dem Oberon-1 Compiler von Niklaus Wirth. Er wurde unter anderem um einen Makroassembler erweitert und ist jetzt weitgehend auf Oberon-2-Standard. 5.1) Aufruf, Parameter ---------------------Der Compiler sollte von einer Shell aus benutzt werden, die zumindest in der Lage ist, Environmentvariablen zu setzen, denn der Compiler liest dort seine Optionen. Beim Aufruf kann man als Kommando einige Optionen sowie eine Liste von Namen, getrennt mit Leerzeichen, •bergeben. Diese Module werden nacheinander •bersetzt. Die Syntax sieht also so aus: compile {<Option>} <Name> {<Name>} Das Format des Namens wird flexibel gehandhabt. Ein Name mit Pfadangabe wird dort zuerst gesucht. Danach wird er wie ein Name ohne Pfad in den Suchpfaden gesucht. Wird keine Datei gefunden, wird der Compiler wieder verlassen. In jedem Fall wird der Name mit der Extension MOD versehen. Die Optionen haben die allgemeine Syntax: -<Option><Parameter> Die Art der Option wird mit einem Buchstaben (grož oder klein) angegeben, eventuelle Parameter folgen ohne Leerzeichen. Einige Optionen sind sowohl •ber Environmentvariablen als auch •ber Kommandozeile setzbar. Dabei gilt: Die Option in der Kommadozeile hat h”here Priorit„t. Folgende Optionen sind implementiert: -e: Weist den Compiler an, bei dem ersten gefundenen Fehler den Programmlauf abzubrechen. Normalerweise wird die Datei komplett •bersetzt und die Fehler in einer Datei gespeichert. -w: Schaltet die Ausgabe von Warnungen ein. Warnungen werden erzeugt, wenn ein Fehler des Programmierers vorliegen k”nnte, die der Compiler aber •bersetzen kann. Dies wird zum Beispiel bei Schreibzugriffen auf globale Variable eines anderen Moduls getan. Warnungen werden normalerweise unterdr•ckt. -i: Schaltet den Indexcheck aus, da er defaultm„žig eingeschaltet ist. Indexcheck bewirkt eine šberpr•fung der Arraygrenzen von Indizes zur Laufzeit und ist mit einer geringfgigen Verl„ngerung des Codes verbunden. Ein falscher Index bewirkt eine CHK-Exception. -t: Schaltet den Typcheck aus, der ebenfalls normalerweise eingeschaltet ist. Wenn er eingeschaltet ist, wird bei jedem Typeguard gepr•ft, ob es sich auch um den korrekten Recordtyp handelt. Ein falscher Typ bewirkt die Ausgabe einer Fehlermeldung und anschlieženden Programmabbruch. -a: Schaltet den Arithmetikcheck aus, der defaultm„žig eingeschaltet ist. Dieser Check soll šber- und Unterlauf von Realzahlen •berpr•fen, ist aber noch nicht implementiert. -s|<pos>|: Diese Option bewirkt, daž der Compiler bei Erreichen der Position pos im erzeugten Code den Programmlauf mit einer Meldung abbricht. Mit dieser Funktion l„žt der Scanner die Absturzstelle im Quelltext finden. -o: Wenn die Option -s benutzt wird und der Compiler bricht ab, so gibt er einige Zeilen vor und nach dem Abbruchpunkt aus, falls diese Option gesetzt wird. 5.2) Environmentvariablen ------------------------Der Compiler wertet auch einige Environmentvariablen aus. Sie m•ssen immer grožgeschrieben und von einem = gefolgt sein. Gesetzt werden sie in der Shell und jedem Programm bergeben, daž von dieser Shell aufgerufen wird. Es werden folgende Variablen ausgewertet: MODPATH: Gibt die Suchpfade (Kap.??) an, in denen nach dem zu •bersetzenden Modul gesucht wird. SYMPATH: Gibt die Suchpfade an, in denen nach den Symboldateien der importierten Module gesucht wird. Ist SYMPATH definiert, wird ein evtl. erzeugtes SYM-File in den letzten Pfad geschrieben, der bei SYMPATH angegeben ist. Ist SYMPATH nicht definiert, werden die Suchpfade von MODPATH •bernommen und das SYM-File wird in denselben Ordner geschrieben, in dem die Source war. Anmerkung: Eine Symboldatei wird nur erzeugt, nicht existiert oder sich ge„ndert hat. wenn sie noch OBJPATH: Die erzeugte Objektdatei wird in den letzten Pfad geschrieben, der mit dieser Variablen definiert wird. Ist OBJPATH nicht definiert, werden die Suchpfade von MODPATH •bernommen und das OBJ-File wird in denselben Ordner geschrieben, in dem die Source war. INFPATH: Die erzeugte Infodatei wird in den letzten Pfad geschrieben, der mit dieser Variablen definiert wird. Ist INFPATH nicht definiert, werden die Suchpfade von MODPATH •bernommen und das INF-File wird in denselben Ordner geschrieben, in dem die Source war. Anmerkung: Ein Info-File wird nur erzeugt, wenn es n”tig ist. INXCHK: Der Inhalt der Variablen darf die Werte ON oder OFF haben. Damit wird der Indexcheck ein- oder ausgeschaltet, der normalerweise eingeschaltet ist. TYPCHK: Wie INXCHK, jedoch f•r den Typcheck. ARICHK: Wie INXCHK, jedoch f•r den Arithmetikcheck. ERRDIST: Der Inhalt der Variablen muž eine Dezimalzahl sein, die den Abstand zwischen zwei Fehlermeldungen in Zeichen angibt. Dieser Abstand bewirkt, daž weniger Folgefehler eines Fehlers ausgegeben werden. Der Standardwert ist 20, das bedeutet: Wenn nach einem erkannten Fehler innerhalb der n„chsten 20 Zeichen nochmal ein Fehler auftritt, wird er nicht ausgegeben. MAXERR: Der Inhalt ist wieder eine Dezimalzahl, die angibt, nach wievielen ausgegebenen Fehlern keine weiteren Fehler mehr ausgegeben werden sollen. Normalerweise sind dies 100 Fehler. ERRFORM: Der Inhalt dieser Variablen ist ein String. Damit ist es m”glich, das Format einer Fehlermeldung einzustellen. Prinzipiell kann man sich den Dateinamen mit \d, die Zeilennummer mit \z, die Spalte mit \s (beide z„hlend ab 1), die absolute Position mit \p und nat•rlich die Fehlermeldung selbst mit \f ausgeben lassen. Diese Teile werden dort in der Zeile eingef•gt, wo die K•rzel mit '\' stehen. Der standardm„žig gesetzte String lautet: "Error \d \z : \s \f" Damit sieht eine Fehlermeldung so aus: Error DATEI ZEILE : SPALTE FEHLERMELDUNG WARNOUT: Werte ON/OFF sind erlaubt. Schaltet die Ausgabe von Warnungen ein oder aus. Normalerweise werden keine Warnungen ausgegeben. 5.3) Optionen im Quelltext -------------------------F•r die Angabe von Optionen in Quelltexten ist die •bliche Konstruktion (*$...*) reserviert. Solche Optionen haben die h”chste Priorit„t. kann Es immer nur eine Option angegeben werden. Zwischen $ und dem Kennbuchstaben darf kein Leerzeichen sein. Hinter den Parametern darf noch beliebiger Kommentar folgen. (*$I?*): Damit kann der Indexcheck bestimmt werden. Das Fragezeichen darf '+', '-' oder '=' sein. Hinter dem I darf kein Leerzeichen sein. Ein '+' schaltet den Indexcheck ein, '-' aus und '=' stellt den vorigen Zustand wieder her. Diese Option ist nur im Oberon-Teil verf•gbar. (*$T?*): Wie I f•r den Typcheck. Eingeschalteter Typcheck bewirkt eine Typ•berpr•fung bei jedem Zugriff auf den gesamten Record. Diese Option ist nur im Oberon-Teil verf•gbar. (*$A?*): Wie I f•r den Arithmetikcheck. Wird im Moment noch nicht unterst•tzt. Die Routinen im Modul System (Grundrechenarten) melden arithmetische Fehler, auch ohne diesen Check. Diese Option ist nur im Oberon-Teil verf•gbar. (*$N?*) Diesmal gibt das Fragezeichen einen Dateinamen an. Unter diesem Namen wird sp„ter das gelinkte Programm gespeichert. Vor dem Namen d•rfen ausnahmsweise auch Leerzeichen stehen. Wird der Name mit Pfad angegeben, wird das Programm dort gespeichert, ansonsten im Pfad PRGPATH oder beim Modul. Der Name wird dem Linker •ber die Infodatei mitgeteilt. Diese Option ist nur im Oberon-Teil verf•gbar. (*$O?*) Wenn ein '-' angegeben wird, wird der folgende Code bei der Optimierung nicht anger•hrt. Bei O+ wird die Optimierung wieder zugelassen. Es darf keine Verschachtelung stattfinden. Diese Option ist auch im Assembler verf•gbar. (*$V+?*) Das Fragezeichen muž den Namen einer Environmentvariablen angegeben. Es bedeutet: Der nun folgende Code soll bei der Optimierung nur dann im Programm gelassen werden, wenn die Environmentvariable zum Zeitpunkt der Optimierung definiert ist. Der Wert ist dabei beliebig. Der Compiler k•mmert sich nicht weiter darum und •bersetzt alles, es ist also keine bedingte Compilierung. Es ist n„mlich wesentlich flexibler: Wenn man die Form des Codes (z.B. f•r verschiedene Zielrechner) „ndern m”chte, muž man nicht irgendwo einen Wert „ndern und alle Module neu •bersetzen und linken. Stattdessen braucht man nur die entsprechenden Variablen zu setzen oder zu l”schen und die Optimierung zu starten. Der Linker erzeugt dann das gew•nschte Programm. Diese Option ist auch im Assembler verf•gbar. Beispiel: (*V+ DEBUG *) (* drinlassen wenn Debugversion *) IO.WriteString(...) (*V=*) Wenn die Environmentvariable DEBUG definiert ist, Optimieren die zus„tzliche Ausgabe dringelassen. wird beim (*$V-?*) Wie oben, jedoch wird der folgende Code nur dann wenn die Variable definiert ist. entfernt, Diese Option ist auch im Assembler verf•gbar. (*$V+?=...*) und (*$V-?=...*) Wie oben, jedoch wird auch der Inhalt der Variablen angegeben. Die Bedingung ist also erf•llt, wenn die Variable definiert ist und den angegebenen Wert hat (beliebiger String ohne Leerzeichen). Diese Option ist auch im Assembler verf•gbar. (*$V=*) Damit wird die Abh„ngigkeit von allen vorher angegebenen Variablen ausgeschaltet. Diese Option ist auch im Assembler verf•gbar. 5.4) Ausgabe ------------ Der Compiler erzeugt eine neue Objektdatei, wenn die šbersetzung fehlerfrei war. Ist die dabei erzeugte Symboldatei anders als die bisherige oder existierte bisher Keine, so wird die neue Symboldatei abgespeichert. War die šbersetzung fehlerhaft, wird die Fehlerdatei abgespeichert. Die Symboldateien ben”tigt der Compiler, um beim Import die dort exportierten Bezeichner zu lesen. Die Objektdateien ben”tigt der Linker, wenn er ein Programm zusammensetzt. Evtl. wird auch eine Infodatei erzeugt, die unter anderem einen Programmnamen aufnimmt. Das Format der Objektdateien enspricht fast dem eines normalen Programms. Es hat einen 28 Byte langen Programmheader, es folgen der Code, die Daten, die Symboltabelle und die Reloziertabelle. Die Symboltabelle entspricht dem erweiterten GST-Format. Durch dieses Format der Objektdateien ist es m”glich, sowohl komplette Module mit einem beliebigen Assembler zu schreiben, als auch vom Compiler erzeugte Objektdateien zu disassemblieren und zu •berarbeiten. Letzteres kann f•r die Geschwindigkeitsoptimierung hilfreich sein, denn es ist einfacher, einen bestehenden Assemblertext zu verbessern als etwas direkt in Assembler zu schreiben. Das Format des Symboltabellen ist nicht mehr kompatibel zu dem von N. Wirth, da einige zus„tzliche Informationen ben”tigt wurden. 5.5) Vordefinierte Prozeduren ----------------------------Die folgenden Tabellen zeigen die vordefinierten Funktionen und Prozeduren von Oberon-2 inklusive der Erweiterungen bei STJ-Oberon-2. Diese sind mit einem Stern markiert und sind nicht portabel. v steht f•r eine Variable, x und n f•r Ausdr•cke, a f•r Adresse und T f•r einen Typ. Integer bedeutet einen der Integertypen SHORTINT, INTEGER oder LONGINT. _Funktionen:_ +-------------+--------------+-------------+-----------------------+ | Name | Argumenttyp | Ergebnistyp | Funktion | +-------------+--------------+-------------+-----------------------+ | ABS(x) | Numerische | Typ von x | Absolutwert | | | Typen | | | | ASH(x,n) | x,n: Integer | Typ von x | x * 2^n | | CAP(x) | CHAR | CHAR | Grožbuchstabe | | CHR(x) | Integer | CHAR | Zahl in CHAR | | | | | umwandeln | | ENTIER(x) | Realtyp | LONGINT | Gr”žtes Integer nicht | | | | | gr”žer als x | | TRUNC(x) * | Realtyp | LONGINT | Integeranteil | | LEN(v,n) | v: Array; | INTEGER | L„nge von v in | | | n: Integer- | | Dimension n | | | konstante | | (0 = erste Dim.) | | LEN(v) | v: Array | INTEGER | entspricht LEN(v,0) | | LONG(x) | SHORTINT | INTEGER | erweitern | | | INTEGER | LONGINT | | | | REAL | LONGREAL | | | MAX(T) | T = Basistyp | T | Maximalwert von T | | | T = SET | INTEGER | Maximales | | | | | Mengenelement | | MIN(T) | T = Basistyp | T | Minimalwert von T | | | T = SET | INTEGER | 0 | | ODD(x) | Integer | BOOLEAN | x MOD 2 = 1 | | ORD(x) | CHAR | INTEGER | Ordinalzahl von x | | SHORT(x) | LONGINT | INTEGER | n„chst kleinerer Typ | | | INTEGER | SHORTINT | | | | LONGREAL | REAL | | | SIZE(T) | jeder Typ | Integer | Anzahl Bytes, | | | | | die T belegt | +-------------+--------------+-------------+-----------------------+ _Prozeduren:_ +--------------------+-------------------------------+--------------------+ | Name | Argumenttyp | Funktion | +--------------------+-------------------------------+--------------------+ | COPY(x,v) | x: Char. Array, String; | v := x | | | v: Char. Array | | | DEC(v) | Integer | v := v-1 | | DEC(v,n) | v,n: Integer | v := v-n | | EXCL(v,x) | v: SET; x: Integer | v := v - {x} | | HALT(x) | Integerkonstante | Programm beenden | | INC(v) | Integer | v := v+1 | | INC(v,n) | v,n: Integer | v := v+n | | INCL(v,x) | v: SET; x: Integer | v := v + {x} | | NEW(v) | Zeiger | v^ allozieren | | NEW(v,x_0,...,x_n) | v : Zeiger auf offenes Array; | v^ mit L„ngen | | | x_i: Integer | x_0..x_n allozieren | +--------------------+-------------------------------+--------------------+ _Funktionen in SYSTEM:_ +-------------+------------------+-------------+------------------------+ | Name | Argumenttyp | Ergebnistyp | Funktion | +-------------+------------------+-------------+------------------------+ | ADR(v) | alle | LONGINT | Adresse von v | | ANL(a,b) * | a,b: Integer | wie a,b | bitweise Und | | BIT(a,n) | a: LONGINT | BOOLEAN | Bit n von Mem[a] | | | n: Integer | | | | CC(n) | Integerkonstante | BOOLEAN | Bedingung n (0ónó15) | | LONG(a) * | SHORTINT | INTEGER | vorzeichenlos erweitern | | | INTEGER | LONGINT | | | LSH(x,n) | x: Integer, | Typ von x | logischer Shift | | | CHAR,BYTE | | | | | n: Integer | | | | NTL(a) * | a: Integer | wie a | bitweise invertieren | | ORL(a,b) * | a,b: Integer | wie a,b | bitweise Oder | | ROT(x,n) | x: Integer | Typ von x | Rotation | | | CHAR,BYTE | | | | | n: Integer | | | | VAL(T,x) | T,x: alle Typen | T | x als Typ T auffassen | | XOL(a,b) * | a,b: Integer | wie a,b | bitweise Exklusiv Oder | +-------------+------------------+-------------+------------------------+ _Prozeduren in SYSTEM:_ +----------------+---------------------+------------------------+ | Name | Argumenttyp | Funktion | +----------------+---------------------+------------------------+ | DISPOSE(p) | Zeiger | gibt den Speicher frei | | GET(a,v) | a: LONGINT | v := Mem[a] | | | v: einfache Typen | | | GETREG(n,v) | n: Integerkonstante | v := Register n | | | v: einfache Typen | (0ónó15) | | INLINE(...) * | Wortkonstanten | f•gt die Konstanten | | | | in den Code ein | | MOVE(a0,a1,n) | a0,a1: LONGINT | n Bytes bei a0 | | | n: Integer | nach a1 kopieren | | NEW(v,n) | v: Zeiger, LONGINT | n Bytes allozieren | | | n: Integer | und Adresse nach v | | PUT(a,v) | a: LONGINT | Mem[a] := v | | | v: einfache Typen | | | PUTREG(n,v) | n: Integerkonstante | Register n := v | | | v: einfache Typen | (0ónó15) | +----------------+---------------------+------------------------+ 5.6) Unterschiede zum Standard-Oberon-2 --------------------------------------- Der Compiler wurde erweitert um AND und NOT, und ~sind. die identisch mit & - SYSTEM.ADR kann auch die Adressen von Prozeduren und konstanten Strings zur•ckgeben. - Es gibt eine Abart von Prozeduren, die f•r Betriebssystemaufrufe benutzt werden. Bei Wirth wurden sie anders benutzt. Beispiel : PROCEDURE- Fclose(Handle : INTEGER) : INTEGER 62,1; Bei Benutzung dieser Prozedur wird das Handle und die Funktionsnummer 62 auf den Stack geschrieben, TRAP #1 aufgerufen und der Stack korrigiert. Da das Betriebssystem genau wie normale Prozeduren den Returnwert in Re- gister D0 zur•ckgeben, funktioniert dies also auch. Reihenfolge der Parameter m•ssen vertauscht werden. Lediglich die - Bei Wirth m•ssen Prozeduren, die einer Prozedurvariablen zugewiesen werden, eine Funktionsnummer haben. Prozeduren bekommen Funktionsnummern, wenn sie einen Stern hinter dem Namen haben (dann sind sie exportiert), wenn sie einen Stern hinter 'PROCEDURE' stehen haben (soll wohl einen Far-Aufruf erzwingen) oder wenn sie forward deklariert werden (ein '^' hinter 'PROCEDURE'). Bei STJ-O2 ist keine Funktionsnummer mehr n”tig f•r die Zuweisung an eine Prozedurvariable. - Laut Wirth waren Strings nur in G„nsefžchen zul„ssig. Dabei wird ein Zeichen als CHAR, mehr oder weniger Zeichen als ARRAY OF CHAR erkannt. Um nun auch einzelne Zeichen als Strings zu deklarieren, kann man sie in Hochkommata einschliežen. 5.7) Bekannte Fehler -------------------- Nicht f•r alle Fehlermeldungen ist eine Klartextmeldung gespeichert. Dann erscheint nur eine Fehlernummer. Einige Fehlermeldungen passen nicht immer ganz zu dem bemerkten Fehler. Man m”ge mir das verzeihen. - Zeiger auf offene Arrays in komplexen Strukturen (Arrays, Records) machen Probleme. Besonders der Indexcheck funktioniert dann nicht. - Die Anzahl Prozeduren, die an einen Typ und dessen Erweiterungen gebunden werden, ist noch auf 100 begrenzt. - Zeiger auf mehrdimensional offene Arrays sind noch nicht m”glich. - LEN liefert nur INTEGER zur•ck statt LONGINT, da die Indizes auf 32K begrenzt sind. - Wenn schon eine Infodatei existiert, deren Inhalt aber Unsinn ist oder deren Format veraltet ist oder die 0 Bytes lang ist, st•rzt der Compiler nach der šbersetzung ab. 6) Der Assembler ================ Im Compiler ist ein Makroassembler integriert. Dieser ist urspr•nglich als eigenst„ndiges Programm zur Assemblerprogrammierung gedacht gewesen und daher sehr viel leistungsf„higer als n”tig. Daher ist er auch nur gering mit dem Oberon-Teil des Compilers verbunden. Er hat einen eigenen Parser und arbeitet im Gegensatz zum Compiler mit 2 Passes. Bei der Programmierung habe ich mich an AS orientiert, ein PD-Assembler fr PC's, so daž hier einige Žhnlichkeiten bestehen. Mit dem Befehl ASSEMBLER ... END; wird der Assembler aktiviert. Alles zwischen ASSEMBLER und END wird dann nicht mehr vom Compiler, sondern vom Assembler bearbeitet. Dabei k”nnen die meisten Symbole des Oberon-Teils verwendet werden, jedoch kann der OberonTeil nicht auf im Assembler definierte Symbole zugreifen. Der Assembler kann soweit mit Oberon-Strukturen arbeiten, solange kein Code dabei erzeugt werden muž. F•r A7 kann alternativ auch SP verwendet werden. Alle Befehle sind auch ohne L„ngenangabe definiert. Wenn der Befehl in Wortgr”že existiert, wird dies als Gr”že angenommen. Der Assembler ist genau wie der Compiler streng Case-Sensitiv. Alle Maschinenbefehle und Pseudobefehle mssen grož geschrieben werden. Bei Labeln wird zwischen Grož- und Kleinschreibung unterschieden. Achtung : Es sind l„ngst nicht alle Maschinenbefehle getestet bei Problemen sollte ein Disassembler pr•fen, ob der Assembler nicht vielleicht Unsinn kodiert hat. und 6.1) Symbolkonventionen ----------------------Symbole werden mit einer L„nge von 22 Byte gespeichert, alle weiteren Zeichen werden ignoriert. Wenn ein Label definiert werden soll, muž es in der ersten Spalte beginnen, darf kein vordefinierter Bezeichner sein und muž mit einem Buchstaben beginnen. Alle weiteren Zeichen k”nnen Buchstaben, Ziffern und Unterstrich sein. Ein Doppelpunkt hinter einem Label ist erlaubt, aber nicht erforderlich. Alle vordefinierten Bezeichner d•rfen auch in der ersten Spalte anfangen. Die Parameterliste eines Makros muž mit einem Zeilenende oder Kommentar beendet werden. Aužer diesen beiden Forderungen ist in einem Quelltext alles erlaubt, auch mehrere Befehle in einer Zeile. Sicherheitshalber sollten sie mit ';;' getrennt werden, auch wenn es nicht immer n”tig ist. Ein Semikolon leitet Kommentar ein, zwei hingegen trennen Befehle. Die Spalte hinter dem zweiten Semikolon wird wieder als erste Spalte einer neuen Zeile interpretiert, so daž dort auch ein Label definiert werden kann. Neben dem •blichen Kommentar mit einem Semikolon bis zum Zeilenende kann man auch mehrere Zeilen mit der in Oberon •blichen Konstruktion (* ...*) Kommentar definieren. Bei Labels und Befehlen wird immer zwischen Grož- und Kleinschreibung unterschieden. Alle Maschinenbefehle, Register und Pseudooperationen m•ssen grož geschrieben werden. Folgende Symbole definiert der Assembler vor: +------+------------------------+ | Name | Bedeutung | +------+------------------------+ | * | mom. Programmz„hler | | CPU | der gew„hlte Prozessor | +------+------------------------+ Desweiteren einige Pseudobefehle, deren Parameter und alle 68000 Befehle. Befehle der anderen 680X0 Prozessoren sind implementiert. nat•rlich z.T. 6.1.1) Formelausdr•cke ...................... An den meisten Stellen, an denen der Assembler Zahlenangaben erwartet, k”nnen nicht nur einfache Symbole oder Konstanten angegeben werden, sondern ganze Formelausdr•cke. Bei den Komponenten der Formelausdr•cke kann es sich sowohl um ein einzelnes Symbol als auch um eine Konstante handeln. Die Schreibweise von Integerkonstanten kann in verschiedenen Zahlensystemen erfolgen: +-------------+-------------------------------------+ | dezimal | direkt | | hexadezimal | nachgestelltes H, vorangestelltes $ | | bin„r | nachgestelltes B, vorangestelltes % | | oktal | nachgestelltes O, vorangestelltes @ | +-------------+-------------------------------------+ Damit hexadezimale Kostanten im Intel-Modus nicht als Symbolnamen fehlinterpretiert werden k”nnen, m•ssen sie immer mit einer Ziffer beginnen; anstelle z.B. F0H muá also 0F0H geschrieben werden. Die Werte A-F m•ssen grožgeschrieben werden, ebenso die nachgestellten Buchstaben. Beim Motorola-Modus entf„llt dies. Integerkonstanten k”nnen auch als ASCIIWerte geschrieben werden, so entsprechen 'A' == $00000041 'AB' == $00004142 'ABC' == $00414243 'ABCD' == $41414244 Dabei m•ssen die Zeichen in Hochkommata eingeschlossen von sein, um sie Strings zu unterscheiden. Ihre L„nge darf maximal 4 Zeichen betragen. Um nun aber auch G„nsef•áchen und Sonderzeichen ohne Verrenkungen in Strings (und als Ascii-Werte geschriebene Integerkonstanten) schreiben zu k”nnen, wurde ein "Escape-Mechanismus" eingebaut, der C-Programmierer(inne)n bekannt vorkommen d•rfte: Schreibt man einen Backslash (\) mit einer maximal dreiziffrigen Zahl im String, so versteht der Assembler dies als Zeichen mit dem entsprechenden dezimalen ASCII-Wert. So kann man mit \0ein NULZeichen definieren. Einige besonders h„ufig gebrauchte Steuerzeichen kann man auch folgenden Abk•rzungen erreichen: \b : Backspace \t : Tabulator \\ : Backslash mit \a : Klingel \e : Escape \n : Zeilenvorschub \r : Wagenr•cklauf \' : Hochkomma \" : G„nsef•áchen Die Kennbuchstaben d•rfen sowohl groá als auch klein geschrieben werden. šber dieses Escape-Zeichen k”nnen sogar Formelausdr•cke in den String eingebaut werden, wenn sie in geschweifte Klammern eingefaát werden: z.B. bewirkt Wert1 equ 1 Wert2 equ 2 message "Wert = \{Wert1+Wert2}" die Ausgabe von 'Wert = 3'. Der Assembler stellt zur Verkn•pfung folgende Operanden zur Verf•gung: +--------------------------------------------------+ | Operand Funktion #Operanden Rang | +--------------------------------------------------+ | ~ log. NOT 1 hoch | | ~~ bin„res NOT 1 ^ | +--------------------------------------------------+ | * Produkt 2 | | | / Quotient 2 | | | # Modulodivision 2 | | | ^ Potenz 2 | | | !,!! bin„res XOR 2 | | | &,&& bin„res AND 2 | | +--------------------------------------------------+ | Differenz 2 | | | + Summe 2 | | | |,|| bin„res OR 2 | | +--------------------------------------------------+ | <> Ungleichheit 2 | | | >= gr”áer oder gleich 2 | | | <= kleiner oder gleich 2 | | | < echt kleiner 2 | | | > echt gr”áer 2 v | | = Gleichheit 2 niedrig | +--------------------------------------------------+ Die angedeuteten Gruppen haben jeweils gleichen Rang. Die Reihenfolge der Evaluierung l„át sich durch Klammerung neu festlegen. Die Vergleichsoperatoren liefern TRUE, falls die Bedingung zutrifft, und FALSE falls nicht. F•r die logischen Operatoren ist ein Ausdruck TRUE, falls er ungleich 0 ist, ansonsten FALSE. Deshalb ist auch ein separater "logisch Nicht" Operator n”tig, denn eine Zahl ungleich 0 kann bin„r invertiert immer noch ungleich 0 sein. Beim "logisch Nicht" wird eine 0 zur 1, eine Zahl ungleich 0 zur 0. Alle anderen logischen Operationen sind mit den Bin„ren identisch. F•r Strings sind alle Vergleichsoperatoren sowie die Summe definiert. Die Summe zweier Strings ergibt einen String, der die beiden aneinandergeh„ngt erh„lt. Vergleiche von Strings liefern 0 (FALSE) oder 1 (TRUE). šberall, wo Zahlen erwartet werden, d•rfen also auch Stringvergleiche benutzt werden. Als einzige Funktion, die ein Stringargument zul„át, ist die Funktion UPSTRING definiert. Sie wandelt alle Zeichen in Groábuchstaben um. Dabei werden auch Umlaute in Grožbuchstaben gewandelt, aber Žnderungen des Zeichensatzes mit CHARSET werden nicht korrekt ber•cksichtigt. Wer nur ein einzelnes Zeichen (als Integer gespeichert) umwandeln will, kann dies mit der Funktion TOUPPER tun. 6.2) Pseudobefehle ------------------ 6.2.1) SET und EQU .................. SET und EQU erlauben die Definition typenloser Konstanten, d.h. sie werden keinem Segment zugeordnet und ihre Verwendung erzeugt in keinem Fall eine Warnung wegen Segmentverquickung. W„hrend EQU Konstanten definiert, die nicht wieder (mit EQU) ge„ndert werden k”nnen, erlaubt SET die Definition von Variablen, die sich w„hrend des Assemblerlaufes ver„ndern lassen. Intern werden Konstanten und Variablen identisch gespeichert, der einzige Unterschied ist, daá sie mit SET umdefiniert werden k”nnen und mit EQU nicht. Es ist daher m”glich, ein Symbol mit EQU zu definieren und es mit SET zu „ndern (auch wenn das nicht der Sinn der Sache ist). 6.2.2) CHARSET .............. Einplatinensysteme, zumal wenn sie LCDs ansteuern, benutzen h„ufig einen anderen Zeichensatz als ASCII, und daá die Umlautkodierung mit der im Befehl •bereinstimmt, d•rfte wohl reiner Zufall sein. Um nun aber keine fehlertr„chtigen Handumkodierungen vornehmen zu mssen, enth„lt der Assembler eine Umsetzungstabelle f•r Zeichen, die jedem Quellcode ein Zielzeichen zuordnet. Zur Modifikation dieser Tabelle (die initial 1:1 •bersetzt), dient der Befehl CHARSET. Der Befehl erwartet eine Bereichsangabe f•r die zu •bersetzenden Zeichen als ersten bzw. ersten/zweiten Parameter und als letzten Parameter den Bereich, in den die Zeichen umgemappt werden sollen. Zur Klarstel- lung zwei Beispiele: CHARSET '„',128 bedeutet, daá das Zielsystem das „ mit der Zahl 128 kodiert. das Zielsystem keine Kleinbuchstaben untersttzt, k”nnen mit CHARSET 'a','z','A' Falls alle Kleinbuchstaben auf die passenden Groábuchtaben automatisch umgemappt werden. ACHTUNG! CHARSET beeinfluát nicht nur im Speicher abgelegte Stringkonstanten, sondern auch als 'ASCII' formulierte Integerkonstanten. Dies bedeutet, daá eine evtl. bereits modifizierte Umsetzungstabelle in den obigen Beispielen zu anderen Ergebnissen f•hrt! 6.2.3) CPU .......... Speichert die nachfolgende Zahl als Bezeichnung f•r eine CPU. Kann wie jeder andere Bezeichner in Ausdr•cken verwendet werden und ist bei der bedingten Assemblierung verwendbar. Der Assembler pr•ft, ob ein Maschinenbefehl auf der gew„hlten CPU verf•gbar ist und verweigert sie wenn nicht. Defaultwert ist 68000. 6.2.4) SUPMODE .............. Diese Variable kann nur ein- oder ausgeschaltet werden. Sie teilt dem Assembler mit, ob der Supervisormode gerade eingeschaltet ist oder nicht. Am Anfang ist die Variable ausgeschaltet. Beispiel : SUPMODE ON MOVE #0,SR ; nur im Supervisormode zul„ssig SUPMODE OFF MOVE #0,SR ; f•hrt zu einer Warnung des Assemblers 6.2.5) SEGMENT .............. Der Atari unterscheidet verschiedene Adreábereiche, die nicht miteinander mischbar sind und jeweils auch verschiedene Befehle zur Ansprache ben”tigen. Um auch diese verwalten zu k”nnen, stellt der Assembler mehrere Programmz„hler zur Verf•gung, zwischen denen mit dem SEGMENT-Befehl hinund hergeschaltet werden kann. Dies erlaubt es, sowohl in mit INCLUDE eingebun- denen Unterprogrammen als auch im Hauptprogramm ben”tigte Daten an der Stelle zu definieren, an denen sie benutzt werden. Im einzelnen werden folgende Segmente mit folgenden Namen verwaltet: CODE: Programmcode DATA: Datenbereich BSS: Block storage segment, zu 0 initialisierte Daten, die nicht im Programmcode auftauchen, sondern vom TOS angeh„ngt werden. Labels, die in einem Segment eines bestimmten Typs definiert werden, erhalten diesen Typ als Attribut. Damit hat der Assembler eine begrenzte Prfm”glichkeit, ob mit den falschen Befehlen auf Symbole in einem Segment zugegriffen wird. In solchen F„llen sollte der Assembler eine Warnung ausgeben. Achtung : Die Segmente werden nat•rlich auseinandergezogen und vom Linker richtig zusammengesetzt, daher darf man nat•rlich keine PC-relative Adressierung •ber Segmentgrenzen anwenden. 6.2.6) DC,DS ............ Damit werden Konstanten im Code oder im Datensegment abgelegt oder Speicher reserviert. Als L„ngen sind .B, .W und .L m”glich, keine Angabe wird als Wortl„nge interpretiert. Bei allen dreien sind Strings erlaubt, evtl. wird ein String mit Nullen verl„ngert, um auf ein Vielfaches der Bytezahl zu kommen. Eine Reservierung von Speicher wird durch DS gemacht: DS.B 10 DS.W DS.L ; reserviert 10 Bytes 1 ; reserviert ein Wort 2,$FF ; reserviert 2 Langworte ; mit Inhalt $FF Speicherreservierung ohne Inhaltsangabe ist in allen Segmenten erlaubt. Der Inhalt ist dann jeweils 0. Eine Inhaltsangabe ist nat•rlich nur im Code und im Datensegment erlaubt. 6.2.7) ALIGN ............ ALIGN mit einem dahinterstehenden Integerausdruck erlaubt es, den Programmz„hler auf eine bestimmte Adresse auszurichten. Die Ausrichtung erfolgt dergestalt, daá der Programmz„hler so weit erh”ht wird, daá er ein ganzzahliges vielfaches des Arguments wird : align 2 macht den Programmz„hler gerade. Der Freiraum wird mit 0 gef•llt. Stattdessen kann man auch EVEN ohne Wert benutzen. 6.2.8) MACRO ............ Dies ist der wohl wichtigste Befehl zur Makroprogrammierung. Befehlsfolge <Name> Mit der MACRO [Parameterliste] <Befehle> ENDM wird das Makro <Name>als die eingeschlossene Befehlsfolge definiert. Diese Definition alleine erzeugt noch keinen Code! Daf•r kann fortan die Befehlsfolge einfach durch den Namen abgerufen werden, das ganze stellt also eine Schreiberleichterung dar. Um die ganze Sache etwas n•tzlicher zu machen, kann man bei der Makrodefinition eine Parameterliste mitgeben. Die Parameternamen werden wie •blich durch Kommas getrennt und m•ssen - wie der Makroname selber - den Konventionen f•r Symbolnamen gen•gen. Beim Aufruf eines Makros werden die beim Aufruf angegebenen Parameternamen •berall textuell im Befehlsblock eingesetzt und der sich so ergebende Assemblercode wird normal assembliert. Sollten beim Aufruf zu wenige Parameter angegeben werden, werden sie als leere Strings •bergeben. Soll mittendrin ein Parameter weggelassen werden, kann man zwei aufeinanderfolgende Kommas schreiben. F•r die •bergebenen Parameter gelten besondere Regeln : Eine zusammenh„ngende Kette von Zeichen ohne Komma gilt als ein Parameter, egal um welche Zeichen es sich handelt. Es k”nnen also auch spezielle Adressierungsarten wie (A0)+ bergeben werden. Wenn bewužt Strings bergeben werden sollen, m•ssen sie in Hochkommata eingeschlossen werden, der Parameter besteht dann aus dem String mit Hochkommata. Wird ein String in G„nsefžchen eingeschlossen, besteht der Parameter nur aus dem String ohne G„nsefžchen. So ist es auch m”glich, Kommas und Leerzeichen in einem Parameter unterzubringen. Beispiele : mac1 mac2 MACRO MOVE ENDM mac1 MACRO par2 ENDM mac2 par1 D0,par1 A0 par2,par3 par3 MOVE,"D0,A0" ; entspricht "MOVE D0,A0" ; entspricht wiederum ; "MOVE D0,A0" Es kann also praktisch alles durch Makroparameter ersetzt werden, Befehle ! auch In Makror•mpfen definierte Labels werden immer als lokal betrachtet, ein expliziter LOCAL-Befehl ist also nicht erforderlich. Sollen Label global bekannt sein, m•ssen sie mit einem Stern gekennzeichnet sein. Da auf diese Weise das Label mit jedem Makroaufruf neu definiert wird, darf es sich nur um Definitionen mit 'SET' handeln, damit keine Fehlermeldungen wie 'Label schon definiert' kommen. Aus technischen Grnden ist es momentan n”tig, ein Makro vor der ersten Benutzung zu deklarieren. Wenn ein Makroparameter in G„nsefžchen eingeschlossen wird, wird er ebenfalls ersetzt, so daž das Aussehen des Parameters •berpr•ft werden kann. Beispiel : Test MACRO Par IF "Par" = "A" ... Wenn als Parameter 'a' oder 'A' •bergeben wurde, ergibt der Vergleich true. Es wird aber nicht generell in Strings ersetzt, sondern nur, wenn der gesamte String gleich einem Makroparameter ist. Der Parameter wird *immer* in Grožbuchstaben umgewandelt. 6.2.9) IRP .......... Dies ist die eine vereinfachte Form von Makrodefinitionen f•r den Fall, daá eine Befehlsfolge einmal auf mehrere Operanden angewendet werden soll und danach nicht mehr gebraucht wird. IRP ben”tigt als ersten Parameter ein Symbol f•r den Operanden, und danach eine (fast) beliebige Menge von Parametern, die nacheinander in den Befehlsblock eingesetzt werden. Um eine Menge von Registern auf den Stack zu schieben, kann man z.B. schreiben IRP op, D0,D1,D3 MOVE op,-(SP) ENDM was in folgendem resultiert: MOVE D0,-(SP) MOVE D1,-(SP) MOVE D3,-(SP) Benutzte Labels sind wieder f•r jeden Durchgang automatisch lokal. Soll ein Label global sichtbar sein, muž es einen Stern hinter dem Namen haben. Dies geht nur bei Labels, die mit SET definiert werden, denn andere wrden eine Fehlermeldung erzeugen, daž das Label schon definiert ist. 6.2.10) REPT ............ Dies ist die einfachste Form der Makrobenutzung. Der im Rumpf angegebene Code wird einfach sooft assembliert, wie der Integerparameter von REPT angibt. Dieser Befehl wird h„ufig in kleinen Schleifen anstelle einer programmierten Schleife verwendet, um den Schleifenoverhead zu sparen. Der Vollst„ndigkeit halber ein Beispiel: REPT 3 ROR #1,(A0) ENDM rotiert den Wert um 3 Stellen nach rechts. Symbole sind wiederum f•r jede einzelne Repetition lokal. 6.2.11) Bedingte Assemblierung .............................. Der Assembler unterst•tzt die bedingte Assemblierung mit Hilfe der Befehle IFC.. / ELSIFC / ENDC. Diese Befehle wirken zur Assemblierzeit, indem entsprechend der Bedingung Teile •bersetzt oder •bersprungen werden. Diese Befehle sind also nicht mit den IF-Statements h”herer Programmiersprachen zu vergleichen. Die allgemeine Form eines IF-Befehles ist folgendermaáen: IFC <Ausdruck> THEN . . <Block 1> . . ELSIFC . . <Block 2> . . ELSEC . . <Block 3> . . ENDC Falls der hinter IFC angegebene Ausdruck wahr (d.h. ungleich 0) ist, wird Block 1 assembliert. Es k”nnen dann beliebig viele ELSIFC folgen, mit denen genauso verfahren wird. Falls keine Bedingung zutrifft, wird der ELSECZweig assembliert, falls er vorhanden ist. IF-Anweisungen d•rfen beliebig verschachtelt werden, ein ELSEC bezieht sich immer auf das letzte vorangegangene, noch nicht abgeschlossene IFC. Wenn in der Bedingung Symbole auftauchen, m•ssen diese unbedingt vorher definiert worden sein, damit im Pass 1 der richtige Block •bersetzt wird. F•r den Test, ob ein Symbol definiert ist, wurde die Funktion DEF eingef•hrt. Sie gibt TRUE (=1), wenn das angegebene Symbol definiert ist, sonst FALSE (=0). Dies ist n•tzlich f•r Include Dateien : IFC NOT DEF(thisfile) THEN; wenn nicht definiert thisfile EQU 1 ; dann definieren ... ; und •bersetzen ENDC 6.2.12) Lokale Label .................... Die bedeutendste Erweiterung zu AS sind lokale Label. Damit k”nnen innerhalb eines Bereichs alle Label eingekapselt werden, so daž sie in der Umgebung nicht mehr sichtbar sind. Beispiel : LOCAL Proc1*: ... Loop: ... END LOCAL Proc2*: ... Loop: ... END k”nnen so in derselben Datei stehen. Loop ist jeweils nur innerhalb von LOCAL und END sichtbar. Ein * hinter einem Label (vor dem Doppelpunkt falls einer gesetzt wird) bedeutet, daž das Label global sein soll. Egal auf welcher Verschachtelungsebene von LOCAL man sich befindet, ein solches Label ist •berall sichtbar. Includedateien sollten alle Label lokal machen und nur diese Label global definieren, die auch von anderen benutzt werden sollen. Damit vermeidet man die Doppelbenutzung von Labels, die unwissentlich in einer Includedatei definiert sind. Alle Label ohne Stern sind aužerhalb der ASSEMBLER ... END Struktur nicht bekannt, d.h. sie sind automatisch lokal. Durch die Lokalisierung innerhalb von ASSEMBLER bis END ist dieser Befehl nicht n”tig, aber f•r reine Assemblerprojekte ist er wichtig. 6.3) Hochsprachenelemente ------------------------Der Assembler beherrscht auch einige der Oberon-Strukturen, wenn auch wesentlich primitiver. Trotzdem kann man mit ihnen ein wenig Struktur in ein Assemblerprogramm bringen und auch Labeldefinitionen wie Loop o.„. sparen. 6.3.1) IF cond THEN ... ELSIF ... ELSE ... END .............................................. Die •bliche If-Abfrage darf nat•rlich nicht fehlen. Als Bedingungen sind aber lediglich die •blichen Condition Codes HI, LS, CC, HS, CS, LO, NE, EQ, VC, VS, PL, MI, GE, LT, GT und LE zugelassen. Beispiel : CMP D0,D1 IF EQ THEN ... ELSIF LO THEN ... ELSE ... END ; ; ; ; wenn D0 = D1 tu dies wenn D1 < D0 tu dies ; sonst dies 6.3.2) REPEAT ... UNTIL cond ............................ Entspricht einer Repeat-Schleife in Oberon. Bedingungen wie bei IF. Beispiel : REPEAT SUBQ #1,D0 UNTIL EQ ; z„hlt D0 bis auf 0 runter 6.3.3) LOOP ... END ................... Entspricht LOOP in Oberon. 6.3.4) EXIT [(Zahl)] .................... Mit Hilfe der Exit-Anweisung kann man REPEAT-Schleifen und LOOPSchleifen mittendrin verlassen. Es wird ein Branch an das Ende der Struktur eingef•gt. Wenn man eine Zahl in Klammern angibt, kann man gleich mehrere Strukturen verlassen. Dabei einspricht EXIT einem EXIT(0). Beispiel : LOOP REPEAT TST D0 IF EQ THEN EXIT END ; ; IF MI THEN EXIT(1) END; ; UNTIL PL END verl„žt die REPEAT-Schleife verl„žt die LOOP-Schleife 6.4) Diverses ------------6.4.1) INCLUDE .............. Dieser Befehl f•gt die im Parameter angegebene Datei so im Text ein, als ob sie dort stehen w•rde. Dieser Befehl ist sinnvoll, um Quelldateien aufzuspalten, die alleine nicht in den Speicher passen w•rden oder um sich "Toolboxen" zu erzeugen. Aus Kompatibilit„tsgr•nden ist G„nsef•áchen zu schreiben, es erlaubt, den Dateinamen in include stddef51.asm und include "stddef51.asm" sind also „quivalent. 6.4.2) MESSAGE, WARNING, ERROR und FATAL ........................................ Der Assembler prft zwar die Quelltexte so streng wie m”glich und liefert diffenzierte Fehlermeldungen, je nach Anwendung kann es aber sinnvoll sein, unter bestimmten Bedingungen zus„tzliche Fehlermeldungen auszugeben, mit denen sich logische Fehler automatisch pr•fen lassen. Der Assembler unter- scheidet drei Typen von Fehlermeldungen, dem Programmierer zug„nglich sind: die •ber drei Befehle auch - WARNING : Fehler, die auf m”glicherweise falschen oder ineffizienten Code hinweisen. Die Assemblierung l„uft weiter, eine Codedatei wird erzeugt. - ERROR : Echte Fehler im Programm. Die Assemblierung l„uft weiter, um m”gliche weitere Fehler in einem Durchgang entdecken und korrigieren zu k”nnen. Eine Codedatei wird nicht erzeugt. - FATAL : Schwerwiegende Fehler, die einen sofortigen Abbruch des Assemblers bedingen. Eine Codedatei kann m”glicherweise entstehen, ist aber unvollst„ndig. Alle Befehle erwarten eine String als Argument. Diese Anweisungen ergeben nur in Zusammenhang mit bedingter Assemblierung Sinn. So kann man fehlerhafte Bedingungen abtesten und mit einer Fehlermeldung abbrechen. Der String einer Fehlermeldung wird anstatt einer Assemblermeldung in die Fehlerdatei geschrieben und mit Zeile und Spalte versehen, in der der Befehl steht. Der Befehl MESSAGE gibt den angegebenen String lediglich aus und erzeugt einen Zeilenvorschub. 6.5) Zugriff auf Oberon-Bezeichner ---------------------------------Der Assembler hat eine begrenzte Zugriffsm”glichkeit auf Bezeichner, die in Oberon definiert wurden. Dabei gilt allgemein, daž nur solche Zugriffe untersttzt werden, die keinen zus„tzlichen Code erfordern. Beispiel : CONST con = 10; TYPE rec = RECORD var1 : INTEGER; var2 : ARRAY 10 OF CHAR; END; arr = ARRAY 10 OF LONGINT; VAR a : rec; b : POINTER TO rec; c : arr; PROCEDURE proc1; ... PROCEDURE proc2*; ... Dann sind folgende Zugriffe m”glich: MOVE #con,D0 ; l„dt D0 mit 10 MOVE.L a,A0 ; l„dt Adresse von a in A0 ; (geht auch mit Prozeduren ; und Stringkonstanten) MOVE a.var1,D0 ; geht weil a globale ; Variable ist MOVE.B a.var2[5],D1 ; dito MOVE.L c[8],D2 ; dito BSR proc ; innerhalb eines Moduls JSR proc2 ; bei importierten und ; exportierten Prozeduren Dagegen geht dies nicht: MOVE b.var1,D0 ; b ist ein Zeiger und muž erst ; geladen werden Dies kann so gel”st werden: MOVE.L b,A0 ; Inhalt von b = Zeiger auf rec MOVE rec.var1(A0),D0 ; typ.var ergibt Offset von var MOVE.B rec.var2[5](A0),D0; genauso MOVE.L arr[2](A1),D0 ; geht genauso mit Arrays Man kann also mit dem Typbezeichner die Offsets der Variablen innerhalb des Records bekommen. Dies sollte immer der direkten Angabe von Zahlen vorgezogen werden, damit bei einer Žnderung der Datenstruktur nicht alle Zahlen ge„ndert werden m•ssen. 7) Der Linker ============= Der Oberon-Linker (LINK.OBJ/TTP) dient dazu, vom Compiler erzeugte Objektmodule zu einem lauff„higen Programm zusammenzubinden. Daf•r ben”tigt er nur die Objektmodule, die Symboldateien nicht. 7.1) Aufruf, Parameter ---------------------Der Linker sollte von einer Shell aus benutzt werden, die zumindest in der Lage ist, Environmentvariablen zu setzen, denn der Linker liest dort seine Optionen. Beim Aufruf kann man als Kommando einige Optionen sowie einen Namen eines Objektmoduls •bergeben. Dieses Modul wird mit den von ihm importierten Modulen zusammengelinkt und unter seinem Namen mit passender Extension gespeichert. Die Syntax sieht also so aus: link {<Option>} <Name> Es wird nur der Name ohne Extension beachtet, ein eventueller Pfad wird abgeschnitten. Dieser Name wird mit der Extension OBJ in den Suchpfaden gesucht. Wird keine Datei gefunden, wird der Linker wieder verlassen. Die Optionen haben die allgemeine Syntax: -<Option><Parameter> Die Art der Option wird mit einem Buchstaben (grož oder klein) angegeben, eventuelle Parameter folgen ohne Leerzeichen. Einige Optionen sind sowohl •ber Environmentvariablen als auch •ber Kommandozeile setzbar. Dabei gilt: Die Optionen in der Kommadozeile haben h”here Priorit„t. Folgende Optionen sind implementiert: -t: Schaltet die Erzeugung einer Symboltabelle aus, die normalerweise immer an das Programm angeh„ngt wird. Eine Symboltabelle ist wichtig, wenn man ein Programm debuggen muž. Sowohl Bugaboo als auch DB verstehen das Format der Symboltabelle. -e: Schaltet die Erzeugung einer erweiterten Symboltabelle ab. Normalerweise wird eine Symboltabelle im erweiterten GST-Format erzeugt, die eine Symboll„nge von 22 Zeichen zul„žt, w„hrend das Standardformat nur 8 Zeichen hat. Diese Option stellt also nur das Format ein, die Option -t schaltet die Symboltabelle ganz aus. -s|<size>|: Normalerweise erh„lt ein Programm einen Stack von Gr”že. Mit dieser Option kann man den Stack beliebig einstellen. 32K -x|<ext>|: Damit kann die Extension eingestellt werden, die das Programm erhalten soll. Normalerweise ist das PRG, aber bei TOS-Programmen kann man TOS oder TTP angeben. Ist von einem Modul eine Infodatei vorhanden und darin ist ein Programmname definiert, wird dieser beim Speichern des Programmes benutzt. Wenn ein Pfad angegeben ist, wird dort gespeichert, sonst in PRGPATH bzw. beim Modul. Der Name kann im Quelltext mit (*$N ...*) gesetzt werden, siehe Kap. ??. 7.2) Environmentvariablen ------------------------Der Linker wertet auch einige Environmentvariablen aus. Sie m•ssen immer grožgeschrieben sein und von einem '=' gefolgt sein. Gesetzt werden sie in der Shell und werden jedem Programm bergeben, daž von dieser Shell aufgerufen wird. Es werden folgende Variablen ausgewertet: OBJPATH: Gibt die Suchpfade an, in denen nach importierten Modulen gesucht wird. Zum Linken werden nur die Objektdateien ben”tigt. Wenn OBJPATH nicht definiert ist, wird MODPATH genommen. INFPATH: Gibt die Suchpfade an, in denen nach Infodateien gesucht wird. Wenn INFPATH nicht definiert ist, wird MODPATH genommen. TOSPATH: Gibt ebenfalls Suchpfade an, in denen Module stehen, die in TOSProgrammen benutzt werden d•rfen. Diese Pfade werden vor denen in OBJPATH durchsucht. Die Unterscheidung erfolgt anhand der Extension, die das zu erzeugende Programm erh„lt. F„ngt sie mit 'T' an, werden die Module aus TOSPATH und OBJPATH gelinkt. Ist TOSPATH nicht definiert, wird wie bisher nur OBJPATH bzw. MODPATH genommen. GEMPATH: Gibt ebenfalls Suchpfade an, in denen Module stehen, die in GEMProgrammen benutzt werden d•rfen. Diese Pfade werden vor denen in OBJPATH durchsucht. Die Unterscheidung erfolgt anhand der Extension, die das zu erzeugende Programm erh„lt. F„ngt sie nicht mit 'T' an, werden die Module aus GEMPATH und OBJPATH gelinkt. Ist GEMPATH nicht definiert, wird wie bisher nur OBJPATH bzw. MODPATH genommen. PRGPATH: Gibt einen mit Backslash beendeten Pfad an, in den das erzeugte Programm geschrieben werden soll. Wenn die Variable nicht existiert, wird das Programm in denselben Pfad geschrieben, in dem das Objektmodul stand. SYMTAB: Darf als Werte ON und OFF annehmen. Damit wird die Ausgabe einer Symboltabelle ein- oder ausgeschaltet. Normalerweise wird eine Symboltabelle erzeugt. EXTSYM: Wieder Werte ON/OFF. Schaltet das erweiterte GST-Format der Symboltabelle ein oder aus. Ist normalerweise eingeschaltet. PRGEXT: Gibt die Extension an, die das gelinkte Programm erhalten soll. Beispiel : 'PRGEXT=TOS' erzeugt ein TOS-Programm (der Code muž daf•r natrlich geeignet sein). Standardm„žig wird die Extension PRG benutzt. STACKSIZE: Der Inhalt dieser Variablen muž eine Dezimalzahl sein, die Gr”že des Stacks angibt. Normalerweise ist dies 32K. die LINKALSO: Diese Variable darf eine Liste von Dateinamen, getrennt mit Kommata, enthalten. Diese Objektmodule werden dann auch gelinkt, wenn sie nicht importiert werden. Damit ist es z.B. m”glich, w„hrend der Testphase ein Debugmodul mitzulinken, daž sich in Exceptions einklinkt oder „hnliches. Ein Name sollte immer nur 8 Zeichen lang sein, Extensions sind nicht n”tig. Es sind nur maximal 5 Module m”glich. M•ssen es mehr sein, so muž man ein Leermodul definieren, daž diese Module importiert, und dieses kann man bei LINKALSO angeben. LINK_ ...: Alle Variablen, die mit LINK_ anfangen, dienen zum Umbenennen von importierten Modulen. Wird z.B. LINK_IO=GEMIO definiert, wird bei jedem Import von IO stattdessen GEMIO gelinkt. Diese Module m•ssen nat•rlich gleiche Schnittstellen haben. 8) Die Lader ============ Die Lader wurden zu einer Zeit entwickelt, als es noch keine Shell f•r Load-Time-Linking gab. Sie sind daher in der Lage, Objektmodule direkt zu linken und zu starten. Es gibt zwei Lader, einen f•r GEMApplikationen (LOAD.PRG) und einen f•r TOS-Applikationen (LOAD.TTP). Inzwischen gibt es allerdings keinen Unterschied mehr. Man braucht nur jeweils die richtige Extension, damit beim Start die richtige Umgebung gew„hlt wird. 8.1) Aufruf, Parameter ---------------------Die Lader erhalten mindestens den Namen eines Moduls als Argument. Dieses Modul darf keine Extension haben, also nur der Name. Das Modul wird zuerst im Lader selbst gesucht, dann in den Suchpfaden, die in der Environmentvariablen OBJPATH definiert sind. Ist sie nicht definiert, wird MODPATH genommen. Wenn ein Modul nicht gefunden wird, wird mit einer Meldung abgebrochen. Sind alle Module geladen und gelinkt, wird das Hauptmodul gestartet. Dabei ist die Variable Sys.Loader TRUE, so daž man erkennen kann, wenn man unter dem Lader l„uft. Folgt auf den Namen des Moduls ein Punkt und ein weiterer Name, so wird dieser Name in der Liste der exportierten Prozeduren gesucht und eine gefundene Prozedur wird gestartet. Dies realisiert die unter Wirths Oberon •bliche Art des Load-Time-Linking. Das geladene Modul kann dies erkennen, denn die Variable Sys.ModuleCall wird auf TRUE gesetzt, wenn nur das Modul gestartet wurde. In so einem Fall muž der Modulrumpf die Aktion ausl”sen. Sie wird auf FALSE gesetzt, wenn eine Prozedur angegeben wurde. Dann dient der Modulrumpf nur zur Initalisierung. Alles was hinter dem Modul- bzw. Prozedurnamen folgt, wird als Kommando an das Modul weitergegeben. Man findet dies wie immer in Sys.Basepage.Command, genau so als w„re das Modul als Programm gestartet worden. 8.2) Ausgabe -----------Die Lader machen keine Ausgaben, solange keine Fehler auftreten. Eine m”gliche Fehlermeldung lautet: deutet auf einen Versionskonflikt hin. 'Objectmodule defect'. Dies Wird ein Modul mit Extension •bergeben (z.B. Icon auf Loader gezogen), so wird das Modul normal gestartet mit Sys.ModuleCall FALSE. Nach Beendigung des Moduls erscheint dann die Meldung 'Procedure OBJ not found', falls die Extension OBJ war. Dies ist nicht weiter sch„dlich. Die Lader haben ein angepažtes Modul f•r die Exceptionbehandlung integriert. Es gibt die Art des Fehlers, die Adresse, das Modul, die Prozedur mit Offset und die Parameter aus, mit denen der Compiler aufgerufen werden muž. Beispiel: Bus error Address: 1ABEA0 Module: Test Procedure: test + 8 Call Compiler with '-e-o-s64' Der Absturz fand also in der Prozedur Test.test statt, 8 Bytes vom Anfang der Prozedur entfernt. Wenn man jetzt den Compiler mit '-e-o-s64 Test' aufruft, sucht er im Modul Test die Position 64 (64 Bytes vom Anfang entfernt, hexadezimal!) und gibt ein paar Zeilen rund um die Position aus. Die Bedeutung der Optionen ist in Kap. ?? erkl„rt. 9) Das Make-Utility =================== Der Begriff Make d•rfte von C her bekannt sein. Hier ist es jedoch nicht n”tig, ein Makefile zu erzeugen. Make (MAKE.OBJ/TTP) liest die importierten Module aus dem Quelltext und •bersetzt alle Dateien, bei denen dies n”tig ist. 9.1) Aufruf, Parameter ---------------------Make kann ohne Parameter oder mit Wird Make ohne Parameter gestartet, so •bersetzenden Modulen durchsucht. Wird ein Module •berpr•ft, die in den Suchpfaden angegebenen Moduls ben”tigt werden. einem Modulnamen gestartet werden. werden alle Suchpfade nach zu Modul genannt, werden nur solche stehen und f•r das Linken des 9.2) Environmentvariablen ------------------------Mit der Environmentvariablen OC kann man den Compiler angeben. Beispiel: OC=E:\OBERON\SYS\COMPILE.TTP Ist OC nicht definiert, wird 'COMPILE.TTP' im aktuellen Verzeichnis gesucht. Die Suchpfade f•r Sourcen, Objekt- und Symboldateien werden •ber die Environmentvariable 'MAKEPATH' definiert. 9.3) Hinweise ------------Wenn Module sich gegenseitig importieren, bleibt Make in einer Endlosschleife h„ngen. Man erkennt dies daran, daž st„ndig dieselben Dateinamen ausgegeben werden. Mit Control-C kann man die Ausf•hrung abbrechen. 10) Der Scanner =============== Der Scanner (SCAN.OBJ/TTP) dient dazu, von einer Position in einem Programm die Stelle im richtigen Quelltext zu finden. Dies erfordert eine erweiterte Symboltabelle am Programm. Diese erzeugt der Linker normalerweise, wenn nichts anderes verlangt wird. 10.1) Aufruf, Parameter ----------------------Scan erwartet zwei Parameter in der Kommandozeile, getrennt sind: die mit Leerzeichen 65) Der Name des Programms. Der Name muž so angegeben werden, daž Scan das Programm auch findet. 66) Die Position im Programm. Sie wird hexadezimal ohne Zus„tze wie $ oder H angegeben. Mit Position ist der Abstand von dem Codeanfang gemeint. Diese wird zum Beispiel von Exceptions ausgegeben. Scan liest das Programm, bestimmt mit Hilfe der Symboltabelle das Modul und die Position relativ zum Modulanfang und ruft den Compiler auf. Als Optionen werden -e, -o und -s<pos>angegeben. Der Compiler •bersetzt das bestimmte Modul, bis er an die Position gelangt, an der der Absturz stattfand. Es werden einige Zeilen davor und dahinter sowie eine Kennzeichnung der Stelle ausgegeben. Diese Stelle wird auch als Fehlermeldung ausgegeben und kann in der Fehlerdatei nachgelesen werden. Dann beenden sich Compiler und Scan. Falls das Programm keine Symboltabelle hat, und terminiert. macht Scan eine Meldung 10.2) Environmentvariablen -------------------------Mit der Environmentvariablen OC kann man den Compiler angeben. Beispiel: OC=E:\OBERON\SYS\COMPILE.TTP Ist OC nicht definiert, wird 'COMPILE.TTP' im aktuellen Verzeichnis gesucht. 11) Debugging ============= Bis jetzt gibt es leider keinen Source Level Debugger f•r STJ-Oberon2. Dies ist aber geplant und wird irgendwann kommen. Bis dahin muž man sich mit Low Level Debuggern herumplagen. 11.1) DB -------Der Debugger DB von Atari eignet sich einigermažen zum Debuggen. Ich m”chte hier besonders auf den Befehl 'stack' hinweisen: Dieser Befehl versucht •ber den Stack die Aufrufkette von Subroutinen zur•ckzuverfolgen. Dies geht auch mit Oberon-Programmen, da diese genau wie C-Programme mit Register A6 lokale Stacks aufbauen. 11.2) Bugaboo ------------Bugaboo (aus dem TurboAss-Paket) eignet sich ebenfalls zum Debuggen. Er kennt leider den Befehl 'stack' nicht, ist daf•r aber wesentlich komfortabler. Leider arbeitet er nicht auf dem TT mit Grožbildschirm. 11.3) Tips ---------Zum Debuggen wird immer eine Symboltabelle ben”tigt. Eine globale Variable kann man •ber den Namen ansprechen, lokale Variable einer Prozedur findet man bei Adressen ab (A6) abw„rts, deren Parameter ab 8(A6) aufw„rts. Die Namen der Symboltabelle sind nicht immer eindeutig. Wenn man also ein Symbol ansprechen will, muž man erst pr•fen, ob es das Gew•nschte ist. Beispielsweise findet man die Prozedur 'Read' in File und in Paths. Einen Befehl sollte man immer im Hinterkopf haben: Wenn n„mlich ein Programm abgestrzt ist und man aus dem Debugger raus m”chte, sollte man noch die Exitprozedur aufrufen, damit alle Betriebsmittel freigegeben werden. Dazu muž man den PC auf das Symbol 'Exit' stellen und die Ausf•hrung starten. Wenn der Exit nicht abstrzt, mžte eine Meldung ber die Terminierung des Programms kommen. Bei DB lautet der Befehl 'x pc .Exit', bei Bugaboo 'let pc=.Exit'. 12) Utilities ============= 12.1) Der Browser ----------------- Ein Browser ist ein Programm, daž aus der Symboldatei und der Source eine Definitionsdatei erzeugt. Die vorliegende Version ist noch in der Entwicklung. Sie wertet lediglich die Symboldatei aus, so daž keine Kommentare in der Definitionosdatei sind. Der Browser wurde von Dirk Theisen geschrieben. 12.2) Inline-Zeilen erzeugen ---------------------------F•r den Fall, daž jemand eine Datei als INLINE-Zeilen in eine Source integrieren will (z.B. eine Resourcedatei), kann man dies mit Inline tun. Inline fragt nach der zu konvertierenden Datei und speichert die erzeugte Datei mit der Extension INL ab. Im Editor muž man dann noch den Modulnamen SYSTEM bzw. eine Abk•rzung davon mittels Suchen und Ersetzen vor INLINE setzen. 13) Speicherverwaltung ====================== Die Speicherverwaltung implementiert Funktionen zum Allozieren und Freigeben dynamischen Speichers. Solcher Speicher ist grunds„tzlich nicht initialisiert[6]. --------------[6] in- wie auch lokale Variable, lediglich globale Variable werden zu 0 itialisiert 13.1) Benutzung in Programmen ----------------------------F•r die dynamische Speicherverwaltung stehen mehrere Funktionen zur Verf•gung: |NEW(<pointer>)|: Wenn als Argument ein Zeiger auf einen Record oder ein konstantes Array •bergeben wird, wird diese Struktur alloziert. Bei Records werden 4 Bytes zus„tzlich alloziert, die den Typdeskriptor auf- nehmen. Dieser steht immer _vor_ dem Record, d.h. der Zeiger zeigt immer auf das erste Element des Records, wohingegen er bei anderen Compilern auf den Deskriptor zeigt. |NEW(<pointer>,<len>)|: Eine L„nge kann nur angegeben werden, wenn es sich bei dem Zeiger um einen Zeiger auf ein offenes Array handelt. Dabei ist len die Anzahl Indizes. |SYSTEM.NEW(<pointer>,<len>)|: SYSTEM.NEW k•mmert sich nicht um den Typ des Zeigers. Es wird soviel Speicher alloziert, wie len in Bytes angibt. So allozierter Speicher wird niemals am Garbage Collect teilnehmen. |SYSTEM.DISPOSE(<pointer>)|: Gibt den Speicher, auf den der •bergebene Zeiger zeigt, wieder frei. Der Garbage Collector wird mit Kernel.GC aufgerufen. Dies f•hrt einen kompletten Collect durch. Es gibt auch eine M”glichkeit, den Collect in kleine Teile zerhackt nebenher laufen zu lassen. Dies w•rde mit Oberon.Collect unter Chatwin gehen. Dort sehe ich auch die einzige sinnvolle Anwendung. Im Moment kann ich von beidem nur abraten, da es noch nicht ausgereift ist. Hinweis: Am Ende eines Programms oder Modulstarts wird automatisch aller Speicher freigegeben. 13.2) Implementierung --------------------Die Implementierung versucht den Kompromiž zwischen Geschwindigkeit und Overhead zu finden. Der Overhead wird minimal, wenn bei jeder Speicheranforderung genau der angeforderte Speicher alloziert wird (plus ein Eintrag in einer Liste). Die Verwaltung der Liste hat aber so viel Zeit ben”tigt, daž der Compiler merklich langsamer wurde. Effizienter wird das, wenn man den ben”tigten Speicher ein wenig aufrundet und in einem Array unter- bringt. Das kostet nat•rlich Speicher. Um diesen Overhead nicht zu grož werden zu lassen, habe ich Speicheranforderungen in 16 Klassen unterteilt[7]. Gr”žere Objekte ab 256 Byte werden in einer Liste verwaltet. Objekte zwischen 2 und 256 Byte werden in Arrays verwaltet, deren Elementgr”že zwischen 16 und 256 Byte in 15 Stufen unterteilt ist. Gegen•ber GEMDOS stellt sich die Speicherverwaltung anders dar. Dort wird immer mindestens 32K alloziert, so daž die Anzahl GEMDOS-Bl”cke nicht allzu grož wird und damit die Geschwindigkeit abnimmt. Es kann auch nicht passieren, daž die Speicherverwaltung des GEMDOS keine Eintr„ge mehr hat. --------------[7] Mombergs Oberon verwendet nur 5 Klassen und hat damit mehr Overhead 14) Die Bibliotheken ==================== Die Bibliotheken erm”glichen eigentlich erst ein vern•nftiges Programmieren mit einer Programmiersprache. Hier soll eine šbersicht der vorhandenen Module gegeben werden, n„here Informationen m•ssen den Definitionsdateien entnommen werden. 14.1) Betriebssystem -------------------Alle Betriebssystemmodule implementieren die Aufrufe als Makros, d.h. der Code f•r den Trap wird beim Aufruf in den Code integriert. Es erfolgt kein Prozeduraufruf. Der Nachteil dieser Methode ist, daž alle Parameter in der Reihenfolge vertauscht sind. Der Oberon-Compiler legt den ersten Parameter auch als ersten auf den Stack, w„hrend C-Compiler den letzten Parameter als erstes auf den Stack legen. 14.1.1) BIOS ............ Die •blichen BIOS-Funktionen. 14.1.2) GEMDOS .............. Die •blichen GEMDOS-Funktionen. 14.1.3) MiNT ............ Die neuen Funktionen unter MiNT sind eigentlich auch GEMDOS-Aufrufe. Sie sind aber hier separat verf•gbar. Sys.MiNT gibt an, ob MiNT installiert ist. 14.1.4) XBIOS ............. Die •blichen XBIOS-Funktionen. Es fehlen Erweiterungen von TT und Falcon, •ber die ich keine Informationen habe. 14.2) Abstrakte Datenstrukturen ------------------------------14.2.1) BinTree ............... BinTree implementiert einen bin„ren Baum. BinTree wurde von H. M”ssenb”ck und Dirk Theisen geschrieben. 14.2.2) CDCL ............ CDCL steht f•r Circular Double Chained List. Es handelt sich also um eine doppelt verkettete Liste, deren erstes und letztes Element aufeinander zeigen. 14.2.3) DCL ........... DCL steht f•r Double Chained List. doppelt verkettete Liste. Es handelt sich also um eine 14.2.4) FIFO ............ FIFO (First in first out) implementiert eine Liste, an deren Anfang Elemente eingefgt und an deren Ende sie wieder entnommen werden k”nnen. FIFO wurde von Dirk Theisen geschrieben. 14.2.5) LRU ........... LRU (Least recently used) implementiert eine Priorit„tenliste dem Prinzip 'am l„ngsten nicht mehr benutzt zuerst'. LRU wurde von Dirk Theisen geschrieben. nach 14.2.6) Stack ............. Stack implementiert eine Liste, an deren Anfang Elemente eingef•gt und wieder entnommen werden k”nnen. Stack wurde von Dirk Theisen geschrieben. 14.3) Standardmodule -------------------Unter diesem Abschnitt werden alle Module zusammengefažt, die in beliebigen Applikationen (TOS oder GEM) benutzt werden k”nnen. 14.3.1) Buffers ............... Buffers implementiert einen einfachen flexiblen Puffer, Grundlage f•r gepufferte Ausgabe dienen soll. der als 14.3.2) CommandLine ................... CommandLine implementiert die Auswertung eines per ARGV •bergebenen Kommandos, kann aber auch mit der normalen Commandline arbeiten. CommandLine wurde von Dirk Theisen geschrieben. 14.3.3) Cookie .............. Suchen, Setzen und L”schen von Eintr„gen im Cookie Jar. 14.3.4) Datum ............. Das Modul Datum arbeitet mit Daten. Datum wurde von Wolfgang Radtke geschrieben. 14.3.5) Environment ................... Suchen von Eintr„gen im Environment. 14.3.6) Error ............. Standardisierte Ausgabe von Fehlermeldungen, Betriebssystems, •ber eine Alertbox. insbesondere des 14.3.7) Exceptions .................. Exceptions f„ngt Softwarefehler wie Bus Error etc. ab und gibt eine passende Meldung aus. Es gen•gt, Exceptions zu importieren oder mit LINKALSO hinzuzulinken. 14.3.8) Execute ............... Execute ist immer dann zu verwenden, wenn man nicht genau weiž, in welcher Form (Modul oder Programm) etwas gestartet werden soll. Daher gibt es ein Execute im Lader, das Module startet, und ein kompatibles zum Linken, das Programme startet. 14.3.9) File ............ Hoffnungslos veraltetes Modul zur Dateibehandlung. 14.3.10) FileBuffer ................... Ebenfalls veraltetes Modul zwischengeschaltetem Puffer. zur Dateibehandlung mit 14.3.11) Filename ................. Zusammensetzen und Aufteilen von Dateinamen. 14.3.12) IO ........... IO implementiert Standardprozeduren zur Ein- und Ausgabe auf dem TOSBildschirm. Die Benutzung in GEM-Programmen sollte vermieden werden, da dann unsch”n auf den Bildschirm geschrieben wird. Im Lader ist ein kompatibles Modul mit anderer Implementierung integriert, CLIFenster von Chatwin umlenkt. daž die Ausgabe in das 14.3.13) Kernel ............... Kernel stellt haupts„chlich die einige Hilfsprozeduren zur Verf•gung. Speicherverwaltung, aber auch 14.3.14) Key ............ War mal die Grundlage f•r die Zuweisung Tastenkombinationen. Wird im Moment nicht benutzt. von Prozeduren zu 14.3.15) MathCom ................ Grundlegende Prozeduren f•r mathematische Funktionen. Entstammt LPRModula. 14.3.16) MathLib0 ................. šbliche mathematische Funktionen. Entstammt LPR-Modula. 14.3.17) Memory ............... Sehr schnelle Prozeduren zum Kopieren und F•llen von Speicher. 14.3.18) Modules ................ Modules ist fr das Nachladen von Modulen in den Ladern zust„ndig. 14.3.19) MVC ............ MVC steht f•r Model View Controller. Es implementiert Viewer, deren Ausgabe vom Inhalt eines Models abh„ngen. WinView baut darauf auf. Theoretisch ist es aber nicht an Fenster gebunden, man k”nnte auch Pseudofenster auf einem TOS-Bildschirm darauf aufbauen. 14.3.20) NumStr ............... Umwandlung von Zahlen in Strings und umgekehrt. 14.3.21) Paths .............. Suchpfadverwaltung. 14.3.22) Strings ................ Was man so braucht um mit Strings umzugehen. 14.3.23) Supervisor ................... Ein- und Ausschalten des Supervisormodus. 14.3.24) Sys ............ Die Grundlage aller Programme. Enth„lt alle Standardfunktionen des Compilers sowie die Programminitialisierung. 14.3.25) Task ............. Enth„lt den Mechanismus zur sauberen Terminierung von Programmen oder Programmteilen. Normalerweise uninteressant f•r Anwender. 14.3.26) VA ........... VA enth„lt die Konstanten, Venus) ben”tigt werden. die f•r das AV-Protokoll (Accessory <-> 14.4) VDI-Module ---------------Das VDI implementiert alles zur Ausgabe auf beliebigen Ger„ten. Es ist mir nicht bekannt, ob es auch in TOS-Programmen benutzt werden darf. 14.4.1) VDI ........... Die grundlegenden Strukturen und Variablen zur Arbeit mit dem VDI. 14.4.2) VDIAttributes ..................... Einstellen von Attributen f•r die Ausgabe. 14.4.3) VDIControl .................. Kontrollfunktionen des VDI. Es existiert zum Test, ob GDOS installiert ist. eine zus„tzliche Funktion 14.4.4) VDIExcapes .................. Escaperoutinen des VDI. 14.4.5) VDIInput ................ Eingaberoutinen des VDI. 14.4.6) VDIInquiry .................. Abfrageroutinen des VDI. 14.4.7) VDIOutput ................. Ausgaberoutinen des VDI. 14.4.8) VDIRaster ................. Rasterfunktionen des VDI. Zus„tzlich gibt es hier ein paar Kopierroutinen zum Scrollen von Bildschirmausschnitten. 14.5) AES-Module ---------------Das AES implementiert das, was man so liebgewonnen hat auf dem Atari: Men•s, Fenster und und und. Bis auf Form.Alert f•hren diese Prozeduren in einem TOS-Programm zum Absturz. 14.5.1) AES ........... Die grundlegenden Strukturen und Variablen zur Arbeit mit dem AES. 14.5.2) Appl ............ Die Funktionen der Application Library. 14.5.3) Evnt ............ Die Funktionen der Event Library. 14.5.4) Form ............ Die Funktionen der Form Library. Programmen verwendet werden. Form.Alert darf auch in TOS- 14.5.5) Fsel ............ Fileselectbox darstellen. Die neue Funktion mit Titel ist auch vorhanden. 14.5.6) Graf ............ Die Funktionen der Graphics Library. Graf.Mouse wurde auf vier Prozeduren verteilt und Graf.Mkstate in Evnt verlegt. 14.5.7) Menu ............ Die Funktionen der Menu Library. 14.5.8) Objc ............ Die Funktionen der Object Library, stark erweitert um Funktionen zum Umgang mit den Objekten. 14.5.9) Rsrc ............ Die Funktionen der Resource Library. Rsrc.Load ist auch in der Lage, selbst„ndig zu suchen, wenn ein Resourcefile nicht gefunden wurde. 14.5.10) Wind ............. Die Funktionen der Window Library. 14.6) Erweiterte AES-Module --------------------------Zum einfacheren Umgang mit dem AES wurden folgende Module entwickelt: 14.6.1) Dialogs ............... Dialogs enth„lt alle Prozeduren, um ganz einfach darzustellen und den Dialog mit dem Anwender zu f•hren. eine Dialogbox 14.6.2) FontSelect .................. FontSelect bietet eine einfache M”glichkeit, mit einer Dialogbox den Benutzer einen Font ausw„hlen zu lassen. 14.6.3) GemApp .............. GemApp bildet die Grundlage zur Programmierung von GEM-Applikationen. Es muž unbedingt benutzt werden, wenn die erweiterten AES-Module verwendet werden. 14.6.4) GEMIO ............. GEMIO ist das Pendant zu IO f•r TOS. Die Schnittstellen sind identisch, so daž man statt IO auch GEMIO linken kann. 14.6.5) Menus ............. Menus automatisiert den Aufruf von Prozeduren •ber Men•punkte oder Tastenkombinationen. Die Tastenkombinationen werden aus dem Eintrag des Men•s gelesen und k”nnen dadurch von Benutzern individuell ge„ndert werden! 14.6.6) TermWin ............... TermWin erweitert WinView derart, daž mit •blichen Schreibbefehlen (WriteString...) Text in einem Fenster ausgegeben werden kann. Desweiteren wird ein Event STRING eingef•hrt. 14.6.7) WindowDialog .................... WindowDialog verlegt einen Dialog in ein Fenster. 14.6.8) WinView ............... WinView ist die Grundlage f•r Fensterapplikationen. Fast das gesamte Handling von Fensters ist automatisiert. Im einfachsten Fall muž man nur noch eine Redrawprozedur implementieren. 15) Tutorial ============ Im nun folgenden Kapitel soll ein GEM-Programm entwickelt werden, daž eine Menleiste hat, ber einen Dialog in einem Fenster Eingaben erm”glicht und diese Eingaben graphisch in beliebig vielen Fenstern ausgeben kann. Eine neue Eingabe muž dann nat•rlich in allen Fenstern gezeichnet werden. 15.1) Die Resourcedatei ----------------------Die Resourcedatei wird mit einem Resource Construction Set erstellt. Die Resourcedatei fr unser Projekt heižt GEMDEMO.RSC. Wer noch nie eine Resourcedatei erzeugt hat, sollte sich erstmal mit einem RCS GEMDEMO.RSCangucken und mal etwas damit spielen. Aber bitte nicht ver„ndert abspeichern und mir dann eine Mail schicken, das Demo w•rde nicht funktionieren! 15.2) Der Rumpf --------------[hbpt] MODULE Tutor1; IMPORT GemApp; VAR myApp : GemApp.Application; BEGIN NEW( myApp); myApp.Init; myApp.Run; myApp.Exit END Tutor1. Abb. ?? zeigt den prinzipiellen Aufbau einer Applikation: Definition einer Variablen vom Typ Application, deren Initialisierung und Aufruf der daran gebundenen Prozeduren Init, Run und Exit. Init initialisiert das Programm. Dazu geh”rt z.B. die Anmeldung beim AES. Run implementiert die Event-Schleife, also warten auf Events vom AES und Aufruf von HandleEvent, das ebenfalls an Application gebunden ist. Exit meldet die Applikation dann wieder ab und sorgt auch f•r eine saubere Terminierung, z.B. Schliežen evtl. offen gebliebener Fenster etc. Sollte jemand dieses Programm starten, wird er in die Eventschleife gelangen. Diese kann mit ControlQverlassen werden, diese Taste wird immer ausgewertet. 15.3) Resourcedatei laden ------------------------Als n„chstes m•ssen wir die Resourcedatei laden. Abb. ?? zeigt die Erweiterungen. [hbpt] TYPE Application = POINTER TO ApplDesc; ApplDesc = RECORD(GemApp.ApplDesc) END; VAR myApp : Application; PROCEDURE (app : Application) Init; BEGIN app.Init^; Graf.ChangeMouse( Graf.ARROW); IF NOT Rsrc.Load("GEMDEMO.RSC") THEN app.Exit END; END Init; Zun„chst wurde eine Erweiterung von GemApp.ApplDesc definiert, damit eine neue Prozedur Init daran gebunden werden kann. Diese muž unbedingt als erstes die geerbte Prozedur Init aufrufen. Als n„chstes wird der Mauszeiger als Pfeil dargstellt, da er beim Programmstart immer auf 'Biene' steht. Der Aufruf von Rsrc.Load bewirkt das Laden und evtl. auch Suchen der Resource- datei. Wenn der Benutzer in der Fileselectbox 'Abbruch' anklickt, gibt Rsrc.Load FALSE zur•ck und die Applikation wird terminiert. app.Exit wird nicht mehr verlassen, deshalb geht das. Wenn man das Programm startet, verh„lt es sich wie Tutor1, nur daž die Maus auf Pfeil umgeschaltet wird. 15.4) Die Men•zeile ------------------Nun wollen wir die Men•zeile anzeigen. von Init. Abb. ?? zeigt die Erweiterung [hbpt] TYPE Application = POINTER TO ApplDesc; ApplDesc = RECORD(GemApp.ApplDesc) END; PROCEDURE ShowInfo; VAR d : INTEGER; BEGIN d := Form.Alert(1, "[1][Tutor3 by Stephan Junker][Ok]"); END ShowInfo; PROCEDURE Exit; BEGIN GemApp.exit := TRUE; END Exit; PROCEDURE (app : Application) Init; VAR menu : Menus.Menu; BEGIN [...] NEW(menu); menu.Init( Rsrc.GetAddr(MENU) ); menu.Set( FILE, QUIT, Exit ); menu.Set( DESK, INFO, ShowInfo ); menu.Show; END Init; Zun„chst alloziert man das Objekt menu und initialisiert es. Sodann empfiehlt es sich, eine Prozedur f•r den Men•punkt QUIT anzumelden, damit man das Programm auch wieder verlassen kann. Dann kann man mit menu.Show die Men•zeile darstellen. Beim Start werden sie feststellen, daž sowohl beim Anklicken von 'Quit' als auch bei Dr•cken von Control-Qdas Programm verlassen wird. Wie funktioniert das? Ganz einfach: Menus hat mit GemApp.StoreEventHandler eine Prozedur angemeldet, die Events verarbeitet. Diese wird immer aufgerufen, wenn ein Ereignis vom AES gemeldet wird, und filtert die Ereignisse heraus, die fr das Men ben”tigt werden. 15.5) Ein Fenster ”ffnen -----------------------Beim Aufruf des Men•punktes 'Ausgabefenster' soll nun ein Fenster ge”ffnet werden. Der Einfachheit halber wird es nur eine weiže Fl„che darstellen. Abb. ?? zeigt die Žnderungen. TYPE Viewer ViewDesc = POINTER TO ViewDesc; = RECORD(WinView.ViewDesc) END; MyModel = POINTER TO ModelDesc; ModelDesc = RECORD(MVC.ModelDesc) END; VAR myModel : MyModel; Station : INTEGER; Workout : VC.workout; PROCEDURE(v : Viewer) Redraw(x,y,w,h : INTEGER); VAR x2, y2 : INTEGER; BEGIN x2 := x+w-1; y2 := y+h-1; VC.VsClip( Station, TRUE, x, y, x2, y2); VO.VBar( Station, x, y, x2, y2 ); END Redraw; PROCEDURE OpenOutput; VAR outWin : Viewer; BEGIN NEW( outWin); outWin.Init; outWin.model := myModel; outWin.SetTitle("Objektfenster"); outWin.SetFullSize( 0, 19, Workout.MaxX - 1, Workout.MaxY - 20); outWin.Open; END OpenOutput; PROCEDURE (app : Application) Init; VAR Workin : VC.workin; menu : Menus.Menu; BEGIN [...] NEW( menu); menu.Init( Rsrc.GetAddr(MENU) ); menu.Set( FILE, QUIT, Exit ); menu.Set( DESK, INFO, ShowInfo ); menu.Set( WORK, OUTPUT2, OpenOutput ); menu.Show; Station := 1; Workin.Id := 1; Workin.LineType := 1; Workin.LineColor := 1; Workin.MarkType := 1; Workin.MarkColor := 1; Workin.Font := 1; Workin.TextColor := 1; Workin.FillStyle := 0; Workin.FillPat := 0; Workin.FillColor := 1; Workin.KoorType := 2; VC.VOpnvwk(Workin,Station,Workout); VA.VswrMode(Station,VA.REPLACE); VA.VsfPerimeter(Station,FALSE); NEW( myModel); myModel.Init; END Init; [hbpt] Zun„chst wird die Prozedur OpenOutput f•r den Men•punkt angemeldet. Es folgt die ™ffnung einer virtuellen Workstation, die zum Zeichnen des Fensterinhaltes ben”tigt wird. Als letztes wird noch ein Model initialisiert, das sp„ter die Daten fr die Ausgabefenster aufnimmt. OpenOutput muž ein Fenster initialisieren, das Model, Titel und maximale Gr”že festlegen. Dann kann es ge”ffnet werden. outWin ist tats„chlich eine lokale Variable! Wie das gehen soll? Ganz einfach, die Verwaltung der Fenster •bernimmt WinView! Das einzige, was wir noch machen m•ssen, ist den Inhalt neuzeichnen. Dies •bernimmt die Prozedur Redraw, die an einen Typ Viewer gebunden werden muž. Diese wird von WinView immer automatisch aufgerufen, wenn das AES eine Redraw-Message verschickt. Die Ereignisse, die Fenster betreffen, werden wieder durch einen EventHandler bearbeitet, der bei GemApp angemeldet wurde. Deshalb funktionieren auch Mover, Closer, Fuller etc. ohne das wir uns darum gek•mmert haben! 15.6) Einen Dialog darstellen ----------------------------Nun wollen wir einen Dialog mit dem Benutzer in einem Fenster Die Žnderungen sind in Abb. ?? skizziert. f•hren. [hbpt] VAR infoDial : WDial.Viewer; PROCEDURE ShowInfo; BEGIN infoDial.Open; END ShowInfo; PROCEDURE (app : Application) Init; BEGIN [...] NEW( infoDial); infoDial.InitDialog( Rsrc.GetAddr(BOX) , 0, TRUE); infoDial.SetWork(OK, NIL, { WDial.DESELECT, WDial.EXITONLY } ); infoDial.SetWork(OUTPUT1, OpenOutput, { WDial.DESELECT, WDial.REDRAWOBJ } ); infoDial.SetTitle("Information"); END Init; Die Initialisierung eines Dialog erfolgt wieder in Init. Mit InitDialog wird der Viewer initialialisiert. Dabei wird ihm auch der zugeh”rige Objektbaum mitgeteilt. Mit SetWork werden den Buttons im Dialog, die den Status Exit haben, Prozeduren zugewiesen, die beim Anklicken aufgerufen werden sollen. An dieser Stelle ist auch schon eine zweite M”glichkeit vorgesehen, ein Ausgabefenster zu ”ffnen. Ge”ffnet wird der Dialog •ber den Eintrag 'Information'. 15.7) Das fertige Programm -------------------------Was noch fehlt, ist eine Dialogbox zur Eingabe von Objekten und der Redraw dieser Objekte. Abb. ?? zeigt das komplette Programm. MODULE GemDemo; IMPORT S:=SYSTEM, GemApp, MVC, WinView, Evnt, Graf, VC:=VDIControl, VA:=VDIAttributes, VO:=VDIOutput, Menus, Rsrc, Form, Objc, WDial:=WindowDialog, NumStr; CONST BOX OK INPUT1 OUTPUT1 = = = = 0; 4; 5; 6; MENU DESK FILE WORK INFO QUIT INPUT2 OUTPUT2 = = = = = = = = 1; (* menu *) 3; (* TITLE in tree MENU *) 4; (* TITLE in tree MENU *) 5; (* TITLE in tree MENU *) 8; (* STRING in tree MENU *) 17; (* STRING in tree MENU *) 19; (* STRING in tree MENU *) 20; (* STRING in tree MENU *) INPUTBOX CIRCLE RECT XPOS YPOS RADIUS WIDTH HEIGHT DRAW = = = = = = = = = 2; 2; 3; 4; 5; 6; 7; 8; 9; TYPE Viewer ViewDesc (* (* (* (* (* (* (* (* (* (* (* (* (* form/dialog *) BUTTON in tree BOX *) BUTTON in tree BOX *) BUTTON in tree BOX *) form/dialog *) BUTTON in tree INPUTBOX *) BUTTON in tree INPUTBOX *) FTEXT in tree INPUTBOX *) FTEXT in tree INPUTBOX *) FTEXT in tree INPUTBOX *) FTEXT in tree INPUTBOX *) FTEXT in tree INPUTBOX *) BUTTON in tree INPUTBOX *) = POINTER TO ViewDesc; = RECORD(WinView.ViewDesc) END; Application = POINTER TO ApplDesc; ApplDesc = RECORD(GemApp.ApplDesc) END; Object = POINTER TO ObjDesc; ObjDesc = RECORD next : Object; x,y : INTEGER; END; Circle = POINTER TO CircleDesc; CircleDesc= RECORD(ObjDesc) r : INTEGER; END; Rect RectDesc = POINTER TO RectDesc; = RECORD(ObjDesc) w,h : INTEGER; END; MyModel = POINTER TO ModelDesc; ModelDesc = RECORD(MVC.ModelDesc) objects : Object; END; VAR myApp : Application; infoDial,inputDial : WDial.Dialog; myModel : MyModel; Station : INTEGER; Workout : VC.workout; PROCEDURE(o : Object) Draw(v : Viewer); BEGIN END Draw; PROCEDURE(c : Circle) Draw(v : Viewer); BEGIN VO.VArc( Station, v.x - SHORT( v.xOff) + c.x, v.y - SHORT( v.yOff) + c.y, c.r, 0, 3600 ); END Draw; PROCEDURE(r : Rect) Draw(v : Viewer); VAR Edges : ARRAY 10 OF INTEGER; BEGIN Edges[0] := v.x - SHORT( v.xOff) + r.x; Edges[1] := v.y - SHORT( v.yOff) + r.y; Edges[2] := Edges[0]; Edges[3] := Edges[1] + r.h - 1; Edges[4] := Edges[0] + r.w - 1; Edges[5] := Edges[3]; Edges[6] := Edges[4]; Edges[7] := Edges[1]; Edges[8] := Edges[0]; Edges[9] := Edges[1]; VO.VPline( Station, 5, Edges); END Draw; PROCEDURE(v : Viewer) Redraw(x,y,w,h : INTEGER); VAR x2, y2 : INTEGER; obj : Object; BEGIN x2 := x+w-1; y2 := y+h-1; VC.VsClip( Station, TRUE, x, y, x2, y2); VO.VBar( Station, x, y, x2, y2 ); obj := myModel.objects; WHILE obj # NIL DO obj.Draw(v); obj := obj.next; END; END Redraw; PROCEDURE(m : MyModel) Init; BEGIN m.objects := NIL; m.Init^; END Init; PROCEDURE ShowInfo; BEGIN infoDial.Open; END ShowInfo; PROCEDURE Exit; BEGIN GemApp.exit := TRUE; (* die saubere Methode *) END Exit; PROCEDURE OpenInput; BEGIN inputDial.Open; END OpenInput; PROCEDURE SetDWH(v : Viewer); VAR obj : Object; maxX, maxY, dw, dh : INTEGER; BEGIN obj := myModel.objects; dw := SHORT(v.dw); dh := SHORT(v.dh); WHILE obj # NIL DO IF obj IS Rect THEN maxX := obj.x + obj(Rect).w; maxY := obj.y + obj(Rect).h; ELSE maxX := obj.x + obj(Circle).r; maxY := obj.y + obj(Circle).r; END; IF maxX > dw THEN dw := maxX END; IF maxY > dh THEN dh := maxY END; obj := obj.next; END; IF dw # v.dw THEN v.dw := dw; v.HSlider END; IF dh # v.dh THEN v.dh := dh; v.VSlider END; END SetDWH; PROCEDURE OpenOutput; VAR outWin : Viewer; BEGIN NEW( outWin); outWin.Init; outWin.model := myModel; SetDWH(outWin); outWin.SetTitle("Objektfenster"); outWin.SetFullSize( 0, 19, Workout.MaxX - 1, Workout.MaxY - 20); outWin.Open; END OpenOutput; PROCEDURE(v : Viewer) Update( aspect : LONGINT); BEGIN v.Update^( aspect); SetDWH(v); END Update; (*$T- wegen NEW( obj(Rect) ) bzw. NEW( obj(Circle) ), denn Typcheck geht nur wenn das Objekt schon alloziert ist ... *) PROCEDURE EnterNewObject; VAR x,y : INTEGER; obj : Object; tep : Objc.tedinfoptr; BEGIN IF Objc.SELECTED IN Objc.GetState( inputDial.objTree, RECT) THEN NEW( obj(Rect) ); tep := Objc.GetSpec( inputDial.objTree, WIDTH); obj(Rect).w := NumStr.ToInt( 10, tep.Text^); tep := Objc.GetSpec( inputDial.objTree, HEIGHT); obj(Rect).h := NumStr.ToInt( 10, tep.Text^); ELSE NEW( obj(Circle) ); tep := Objc.GetSpec( inputDial.objTree, RADIUS); obj(Circle).r := NumStr.ToInt( 10, tep.Text^); END; tep := Objc.GetSpec( inputDial.objTree, XPOS); obj.x := NumStr.ToInt( 10, tep.Text^); tep := Objc.GetSpec( inputDial.objTree, YPOS); obj.y := NumStr.ToInt( 10, tep.Text^); obj.next := myModel.objects; myModel.objects := obj; myModel.Changed( 0); END EnterNewObject; (*$T= *) PROCEDURE EnableCircle; BEGIN inputDial.SetCursor( XPOS); Objc.SetFlags( inputDial.objTree, WIDTH, {Objc.EDITABLE, Objc.HIDDEN} ); inputDial.RedrawObj( WIDTH); Objc.SetFlags( inputDial.objTree, HEIGHT, {Objc.EDITABLE, Objc.HIDDEN} ); inputDial.RedrawObj( HEIGHT); Objc.SetFlags( inputDial.objTree, RADIUS, {Objc.EDITABLE} ); inputDial.RedrawObj( RADIUS); END EnableCircle; PROCEDURE EnableRect; BEGIN inputDial.SetCursor( XPOS); Objc.SetFlags( inputDial.objTree, RADIUS, {Objc.EDITABLE, Objc.HIDDEN} ); inputDial.RedrawObj( RADIUS); Objc.SetFlags( inputDial.objTree, WIDTH, {Objc.EDITABLE} ); inputDial.RedrawObj( WIDTH); Objc.SetFlags( inputDial.objTree, HEIGHT, {Objc.EDITABLE} ); inputDial.RedrawObj( HEIGHT); END EnableRect; PROCEDURE(app: Application) Init; VAR menu : Menus.Menu; Workin : VC.workin; BEGIN app.Init^; (* must come first! *) Graf.ChangeMouse( Graf.ARROW); IF NOT Rsrc.Load("GEMDEMO.RSC") THEN app.Exit END; NEW(menu); menu.Init( Rsrc.GetAddr(MENU) ); menu.Set( FILE, QUIT, Exit ); menu.Set( DESK, INFO, ShowInfo ); menu.Set( WORK, OUTPUT2, OpenOutput ); menu.Set( WORK, INPUT2, OpenInput ); menu.Show; Station := 1; Workin.Id := 1; Workin.LineType := 1; Workin.LineColor := 1; Workin.MarkType := 1; Workin.MarkColor := 1; Workin.Font := 1; Workin.TextColor := 1; Workin.FillStyle := 0; Workin.FillPat := 0; Workin.FillColor := 1; Workin.KoorType := 2; VC.VOpnvwk(Workin,Station,Workout); VA.VswrMode(Station,VA.REPLACE); VA.VsfPerimeter(Station,FALSE); NEW( myModel); myModel.Init; NEW( infoDial); infoDial.InitDialog( Rsrc.GetAddr(BOX) , 0, TRUE); infoDial.SetWork(OK, NIL, { WDial.DESELECT, WDial.EXITONLY } ); infoDial.SetWork(INPUT1, OpenInput, { WDial.DESELECT, WDial.REDRAWOBJ } ); infoDial.SetWork(OUTPUT1, OpenOutput, { WDial.DESELECT, WDial.REDRAWOBJ } ); infoDial.SetTitle("Information"); NEW( inputDial); inputDial.InitDialog( Rsrc.GetAddr(INPUTBOX), XPOS, TRUE); inputDial.SetWork(DRAW, EnterNewObject, { WDial.DESELECT, WDial.REDRAWOBJ } ); inputDial.SetWork(CIRCLE, EnableCircle, {} ); inputDial.SetWork(RECT, EnableRect, {} ); inputDial.SetTitle("Neues Objekt"); inputDial.SetText( XPOS, ""); inputDial.SetText( YPOS, ""); inputDial.SetText( WIDTH, ""); inputDial.SetText( HEIGHT, ""); inputDial.SetText( RADIUS, ""); Objc.SetState( inputDial.objTree, RECT, {Objc.SELECTED} ); END Init; BEGIN NEW(myApp); myApp.Init; myApp.Run; myApp.Exit END GemDemo. [hbpt] Was hat sich getan? Nun, der Typ ModelDesc nimmt jetzt die Objekte auf, die dargestellt werden sollen. Redraw wurde erweitert, damit es die Objekte zeichnen kann. Eine weitere Dialogbox, mit der die Objekte vom Anwender eingegeben werden, wurde erzeugt. EnterNewObject liest die Eingaben aus dieser Box und erzeugt daraus ein neues Objekt. Mit EnableCircle bzw. EnableRect wird die Darstellung der Box ge„ndert, je nachdem ob der Benutzer Kreis oder Rechteck verlangt. SetDWH pažt die Gr”že der Zeichnung (also dessen was insgesamt dargestellt werden soll) immer an die Gr”že der eingegebenen Objekte an. So kann man auch Zeichnungen darstellen, die gr”žer als ein Fenster sind. Die Slider, Pfeile etc. werden alle automatisch durch WinView bedient. Man kann praktisch unbegrenzt Fenster ”ffnen (zumindest wenn man Winx oder MultiTOS installiert hat), und bei Eingabe eines neuen Objektes werden alle auf den neuesten Stand gebracht. Fenster kann nat•rlich einen anderen Ausschnitt darstellen. Aber jedes 15.8) Zusammenfassung --------------------Dieses Kapitel sollte einige M”glichkeiten von Oberon und der Module zeigen. Das Ergebnis war ein 300 Zeilen langes Programm, daž hoffentlich einen guten Eindruck hinterlassen hat. GEM- 16) Anhang ========== 16.1) Literatur --------------N. Wirth, J. Gutknecht: <Project Oberon: The design of an Operating System and Compiler,> Addison-Wesley (1992), ISBN 0-201-54428-8. M. Reiser: <The Oberon System: Usesr Guide and Programmer's Manual> Addison-Wesley (1991), ISBN 0-201-54422-9. M. Reiser, N. Wirth: <Programming in Oberon: and Modula>, Addison-Wesley (1992) Steps beyond Pascal 16.2) Danksagungen -----------------Dank an Frank Storm, der mich darauf gebracht hat, statt einem Modulaeinen Oberon-Compiler zu schreiben und anfangs auch die E-Mail erledigt hat. Dank an den Chefbetatester Dirk Theisen f•r den Browser und sonstige Unterst•tzung. Dank an alle, die •ber Fehler berichten und neue Module implementieren. Dank an Christian Strunk f•r seine TeX-Implementierung. Dank an Roman Hodek f•r TeX2TXT, mit dem diese Anleitung in einen ganz passablen Asciitext konvertiert werden konnte. Ach ja, dan- dann sollte ich wohl auch noch Niklaus Wirth und seinem Team ken, daž er Oberon erdacht hat und die Sourcen frei weitergibt.