Strubs – ein Precompiler für Basic-Programme (Teil 2)
In der letzten Ausgabe haben Sie den Unterschied zwischen einem Compiler und einem Interpreter erfahren und sich kurz über die Vorteile von Strubs informieren können. Hier nun werden die in Strubs implementierten Befehlsstrukturen erläutert und das Objektprogramm von Strubs selbst vorgestellt.
An anderer Stelle in dieser Zeitschrift (oder auch in den unten aufgeführten Büchern) können Sie sich ausführlich über die Grundlagen der strukturierten Programmierung informieren. Aus diesem Grund werden wir uns hier darauf beschränken, einige Aspekte kurz anzusprechen und im übrigen vorzustellen, was Strubs in dieser Hinsicht zu bieten hat.
Gehören Sie auch zu denjenigen, die sich manchmal ein Programm aus einer Zeitschrift vornehmen, um zu analysieren, wie es arbeitet oder um eventuell Teile des Programms für eigene Programmprojekte zu verwenden? Dann erinnern Sie sich bestimmt an Programme, bei denen Sie sich verzweifelt von Sprung zu Sprung bewegen und nach nicht allzu langer Zeit vollkommen den Überblick verlieren. Oder vielleicht kennen Sie folgende Situation: Sie schreiben ein Programm und erinnern sich angesichts eines bestimmten Problems, daß Sie ein ganz ähnliches Problem schon einmal in einem anderen Programm gelöst haben. Aber sobald Sie sich den alten Programmtext vornehmen, um den entsprechenden Programmteil in ihr neues Programm zu übernehmen, müssen Sie enttäuscht feststellen, daß diese spezielle Problemlösung so sehr in das Programmgeflecht verwoben ist, daß es Ihnen weitaus einfacher scheint, den entsprechenden Programmteil vollkommen neu zu entwickeln.
Die Ursache für solche Erscheinungen liegt zum Teil darin, daß viele Basic-Programme mehr oder weniger aus der Sicht des Computer der »Basic-Maschine« — direkt am Computer nach dem Verfahren von Versuch und Irrtum entwickelt werden. Das kann in Einzelfällen sogar soweit führen, daß man zum Schluß zwar sieht, daß das Programm läuft, aber selbst nicht so recht weiß, warum eigentlich und wie es funktioniert. Der Hauptgrund für solche Unübersichtlichkeit aber liegt in der Verwendung zahlreicher wilder Sprünge und ausgefallener Programmier-Tricks. (Daß die Verwendung von GOTO-Anweisungen den mathematischen Beweis für die Korrektheit von Programmen praktisch unmöglich macht, ist für den Informatiker interessant, braucht uns hier aber nicht zu interessieren).
Den entgegengesetzten Weg geht die strukturierte Programmierung. Sie bedeutet vor allem sorgfältige Planung und den Verzicht auf GOTOs und unübersichtliche Programmiertricks. Hier steht die systematische Analyse des Problems im Vordergrund. Die eigentliche Codierung, das heißt die Formulierung des Programmtextes in einer bestimmten Programmiersprache, spielt nur eine untergeordnete Rolle.
In der Problemanalyse geht es darum, ein gegebenes Problem in relativ selbständige Teilprobleme zu zerlegen und deren Beziehungen zueinander festzulegen. Den Aufbau des Programms Strubs mit den jeweiligen Zeilennummern können Sie Bild 1 entnehmen. Das komplette Objektprogramm ist ebenfalls abgedruckt (siehe Listing).

Entsprechend setzt sich das strukturierte Programm aus einer Reihe möglichst selbständiger Programmeinheiten zusammen. Dieses Vorgehen spiegelt sich im Konzept der Blöcke und Module.
Em Block ist eine Anweisung oder eine Folge von Anweisungen mit genau einem Eingang und genau einem Ausgang. Das heißt man darf weder in einen solchen Block hineinspringen, noch aus diesem Block herausspringen. Solche Blöcke können entweder aneinander gereiht oder beliebig tief ineinander geschachtelt werden; sie dürfen sich aber nicht überschneiden. In letzterer Hinsicht verhält es sich mit diesen Blöcken also genauso, wie bei den bekannten FOR-Schleifen in Basic.
Ein strukturiertes Programm besteht nun ausschließlich aus einer geordneten Hierarchie solcher Blöcke. Der kleinste mögliche Block besteht aus einer einzelnen Anweisung, wie zum Beispiel PRINT "Text". Der größte, umfassendste Block besteht aus dem Programm selbst.
Da ist zunächst einmal die einfache IF-Anweisung, die schon von Basic her bekannt ist. Dieses normale Basic-IF kann natürlich wie alle Basic-Befehle weiterhin benutzt werden. Zusätzlich bietet Strubs aber eine erweiterte Form, bei welcher der THEN-Teil nicht auf den Rest einer Programmzeile begrenzt ist, sondern beliebig viele Zeilen umfassen kann, die durch den Befehl '!FI' — einfach ein umgedrehtes IF — abgeschlossen werden. Ein Beispiel:
10 ! IF X=Y THEN 20 : PRINT "X und Y" 30 : PRINT SIND GLEICH" ... 99 !FI
Ist die Bedingung hinter IF erfüllt, so werden die Zeilen zwischen der IF- und der FI-Anweisung ausgeführt, ansonsten wird das Programm sofort hinter der FI-Zeile fortgesetzt.
Daneben existiert selbstverständlich auch die vollständige Form
10 !IF X=Y THEN 20: PRINT "GLEICH’ 50 !ELSE 60 : PRINT "UNGLEICH" 99 !FI
Ist die Bedingung erfüllt, dann wird der Block zwischen IF und ELSE ausgeführt, sonst der Block zwischen ELSE und FI.
Für den Fall, daß mehr als nur zwei Fälle zu unterscheiden sind, bietet Strubs die CASE-Anweisung:
10 !CASEOF X<0 THEN 15 : PRINT "KLEINER ALS 0” ... 40 ! OF X=0 THEN 45 : PRINT "GLEICH 0" ... 60 ! OF X>0 AND Y<XTHEN 65 : PRINT "X>0 UND Y<X" ... 80 ! ELSE 85 : PRINT "KEINER DER FÄLLE TRIFFT ZU" 99 ! ECASE
Mit dieser Struktur können beliebig viele Fälle unterschieden werden, wobei jedes OF mit einer beliebigen Bedingung verbunden werden kann. Es sollte aber darauf geachtet werden, daß sich die Bedingungen gegenseitig ausschließen (sonst wird das erste Auftreten einer erfüllten Bedingung gewählt). Nach der Bearbeitung des entsprechenden Falles wird das Programm immer hinter ECASE fortgesetzt. Die Möglichkeit, daß keiner der Fälle zutrifft, kann mit Hilfe der ELSE-Anweisung behandelt werden. Ist dies nicht erforderlich, kann der ELSE-Teil auch entfallen.
Damit kommen wir nun zu den Schleifen. Die FOR-Schleife kann wie bisher benutzt werden. Die WHILE-Schleife wird durchlaufen, solange die Bedingung erfüllt ist. Anschließend wird das Programm hinter EWHILE fortgesetzt. Da die Bedingung am Anfang der Schleife abgefragt wird, kann es vorkommen, daß die Schleife auch überhaupt nicht durchlaufen wird. Ein Beispiel:
10 ! WHILE X<5 !DO 20 : PRINT "IMMER NOCH KLEINER ALS 5" 30 : X = X + 1 ... 99 !EWHILE
Von der WHILE-Schleife unterscheidet sich die REPEAT-Schleife in zwei Punkten: Erstens wird die Schleife durchlaufen, bis die Bedingung erfüllt ist, also solange sie nicht erfüllt ist. Zweitens wird die Bedingung erst am Ende der Schleife abgefragt, so daß die Schleife immer mindestens einmal durchlaufen wird. In diesem wie im nächsten Beispiel bezieht sich die Zeile 30 auf den Fall, daß X beim Eintritt in die Schleife größer als 5 ist:
10 ! REPEAT 20 : PRINT "X KLEINER ALS 5" 30 : PRINT "VIELLEICHT ABER AUCH NICHT" 40 : X = X+1 ... 99 ! UNTIL X >= 5
Eine weniger weit verbreitete, aber sehr mächtige Schleifenstruktur stellt die LOOP-Schleife dar (sie befindet sich zum Beispiel in der Programmiersprache ADA):
10 ! LOOP 30 : PRINT "EVENTUELL GROESSER ALS 5" 40 : IF X>=5 THEN !EXIT 50 : PRINT"KLEINER ALS 5" 60 : X = X+1 99 !ELOOP
Verlassen einer Endlosschleife
Es handelt sich dabei um eine Endlosschleife, welche mit Hilfe des Befehls EXIT verlassen werden kann. Diese Schleife bietet im wesentlichen zwei Vorteile: Zum einen muß die Bedingung nicht entweder am Anfang oder am Ende der Schleife stehen, sondern kann an jeder beliebigen Stelle innerhalb des Blockes abgefragt werden. Darüber hinaus ist das Beenden der Schleife nicht nur von einer Bedingung abhängig, sondern die LOOP-Schleife kann beliebig viele EXIT-Anweisungen enthalten (dadurch wird nicht die oben erwähnte Forderung nach nur einem Ausgang verletzt, da das Programm in allen Fällen hinter dem ELOOP fortgesetzt wird). Damit eignet sich diese Konstruktion insbesondere gut für die Behandlung von Ausnahmen wie zum Beispiel von Eingabebefehlen etc. (eine Angelegenheit, die zum Beispiel in Pascal recht umständlich sein kann, falls man auf GOTOs verzichten will oder muß).
In Bild 2 (das Zeichen ' kennzeichnet Kommentare) sehen Sie ein Beispiel für geschachtelte LOOP-Schleifen. Die Ausführung einer EXIT-Anweisung bewirkt die Fortsetzung des Programms bei der ersten Zeile hinter derjenigen Schleife, welch© diese EXIT-Anweisung am nächsten umschließt. Im Beispiel enthält die äußere Schleife zwei EXIT-Anweisungen — eine davon vor, die andere hinter der inneren Schleife. Die innere Schleife enthält eine EXIT-Anweisung. Grafisch lassen sich blockstrukturierte Programme am besten durch Struktogramme — anstelle der verbreiteten Flußdiagramme — darstellen. Das Struktogramm für die LOOP Schleifen finden Sie in Bild 3. Über die Diagramme der anderen Strukturen und den Umgang mit Struktogrammen können Sie sich an anderer Stelle in dieser Zeitschrift oder in den unten aufgeführten Büchern informieren. Kommen wir nun zu den Modulen. Dabei handelt es sich um besondere Blöcke, die ein bestimmtes Teilproblem — beispielsweise das Zeichnen einer Linie in einem Grafikprogramm — unter möglichst weitgehender Unabhängigkeit vom restlichen Programmtext bearbeiten. Stellen Sie sich vor, Sie finden in einer Zeitschrift ein Pascal-Programm zur Einstellung von Grafiken. Dieses Programm benutzt zum Beispiel die Anweisung PLOT (X,Y) zum Zeichnen eines Punktes mit den Koordinaten X und Y. Ihr Freund möge eine Sprache Super-Pascal besitzen, die diese Anweisung standardmäßig enthält. Er tippt das Programm ein, es läuft — fertig. Sie selbst besitzen aber nur ein mageres Mini-Pascal, das diesen Befehl nicht kennt. Nun, mit Pascal ist das kein Problem: Sie schreiben sich eine Procedur PLOT (X,Y) fügen diese in das Programm ein — fertig. An dem Programmtext selbst brauchen Sie nicht die geringste Änderung vorzunehmen. Ja, brauchen ihn nicht einmal näher anzusehen. Woran liegt das?
510 '********************************* 520 '* geschachtelte loop-bloecke * 530 '********************************* 540 ' 620 !loop 'l1 630 : print"aeussere loop1" 640 : if x=1 then !exit 'loop1 650 : !loop 'l2 660 : print "innere loop2" 670 : if x=0 then !exit 'loop2 680 : !eloop ' l2 690 ' hier wird progr. nach exit loop2 fortgesetzt 700 : x=x+1 710 : !if x=2 then 720 : print "loop1 verlassen:":!exit 'loop1 730 : !fi 740 : x=x+1 750 !eloop 'l1 760 print"hier wird programm nach exit loop 1 fortgesetzt" 770 '

Vom Problem her — dem Erstellen einer Grafik — ist das Zeichnen eines Punktes das Zeichnen eines Punktes. Das einzige, was interessiert, ist, daß dazu zwei Koordinaten erforderlich sind. Dieser Tatsache trägt die Sprache Pascal dadurch Rechnung, daß sie keinen Unterschied macht zwischen dem Aufruf von vorgegebenen Standardanweisungen und selbst definierten Prozeduren.
Wenn Sie in einem Basic-Programm irgendwo eine Zeile PRINT "TEXT” stehen haben, erwarten Sie selbstverständlich, daß dadurch nicht 50 Zeilen weiter der Wert der Variablen A verändert wird. Entsprechend sorgt nun Pascal dafür, daß eine selbst definierte Prozedur genausowenig Auswirkungen auf andere Programmteile hat wie der Aufruf einer Standard-Anweisung. Die interne Arbeitsweise einer solchen Prozedur wird vor der Programmumgebung genauso versteckt, wie dies bei der internen Arbeitsweise von im Sprachumfang enthaltenen Anweisungen der Fall ist. Entsprechend nennt man dieses Konzept auch »Information Hiding«. Programmiersprachen wie ADA, MODULA oder SIMULA bieten in dieser Hinsicht noch sehr viel weitergehende Möglichkeiten als Pascal.
Schnittstellen:
Der Datenaustausch mit der Umgebung eines Moduls erfolgt über genau definierte Schnittstellen. Bei einer solchen Schnittstelle handelt es sich um eine Menge derjenigen Annahmen, die die Programmumgebung über ein Modul macht — das heißt welche Daten es als Eingabe erwartet, welche Daten es daraufhin wieder ausgibt und welche anderen Module es seinerseits benötigt.
Modulbibliothek:
Die relative Eigenständigkeit solcher Module sorgt nun nicht nur für einfache Änderbarkeit und Erweiterbarkeit, sondern ermöglicht auch das Anlegen einer sogenannten Modulbibliothek. Eine solche Bibliothek enthält eine Reihe von Programmbausteinen, die je nach Bedarf in zu entwickelnde Programme eingefügt werden können. Dabei kann es sich um Sortierroutinen, Grafik-Routinen, mathematische und statistische Routinen und so weiter handeln. Aber auch die Entwicklung von Spielen läßt sich auf diese Weise vereinfachen: Man kann Bibliotheken fertiger Sprites, von eigenen Zeichensätzen oder von diversen Soundroutinen anlegen.
Das wichtigste Hilfsmittel zur Unterstützung modularer Programmentwicklung stellen sicherlich die lokalen Variablen dar. Leider gibt es solche nicht in Basic und auch Strubs kann keine lokalen Variablen bieten. So ist es auch weiterhin erforderlich, beim Einsetzen oder Ändern eines Moduls darauf zu achten, ob und an welchen Stellen Variablen des Moduls in anderen Programmteilen benutzt werden, und gegebenenfalls Umbenennungen vorzunehmen. Der zweite große Nachteil von Basic — die leidigen Zeilennummern — braucht uns dagegen nur noch wenig zu beschäftigen. Strubs bietet alle Möglichkeiten, die erforderlich sind, um ein Programm vollkommen unabhängig von Zeilennummern zu schreiben Als erstes sind da natürlich die oben besprochenen Kontrollstrukturen zu nennen. Darüber hinaus können bei allen Sprüngen Zeilennummern durch Labels (Marken) ersetzt werden. Solche Labels werden durch das Zeichen »£« gekennzeichnet und abgeschlossen durch ein Leerzeichen, Doppelpunkt, Komma oder Zeilenende. Die dürfen zwar reservierte Basic-Worte enthalten, dann können sich aber wegen der in der letzten Folge erwähnten Tokens bei der Ausgabe der Markentabelle seltsame Effekte ergeben. Die Labels werden definiert, indem sie an den Anfang einer Zeile gesetzt werden und können beliebig lang sein:
10 AUSGEBEN: 20 : PRINT "X:";X 30 RETURN ... 200 X=1:GOSUB £X-AUSGEBEN 210 X=2:GOSUB £X-AUSGEBEN
Schließlich bietet Strubs noch die Möglichkeit relativer Sprünge. Diese dienen vor allem dazu, kurze Schleifen innerhalb einer einzigen Zeile zu konstruieren, ohne dafür extra ein Label zu definieren:
90 NC=NC+1:C=PEEK(NC):IF C>0 THEN Z$+CHR$(C): GOTO £THIS
Der Befehl GOTO £THIS bewirkt einen Sprung an den Anfang derjenigen Zeile, in der dieser Befehl steht.
Da bei der Arbeit mit Strubs Quellprogramme in der Regel weit umfangreicher als die Objektprogramme sind, bietet Strubs die EXTERN-DEKLARATION, die es ermöglicht, Module und Programmteile getrennt zu übersetzen und erst auf der Objektprogrammebene zusammenzufügen. Hierbei müssen die einzelnen Programmteile allerdings verschiedene Zeilennummern belegen. In der Extern-Deklaration wird ein Name vereinbart, unter dem ein Programm ein externes Modul ansprechen kann. Diesen Namen wird die Einsprungadresse (bei Maschinenprogrammen) beziehungsweise die Zeilennummer bei Basic-Routinen zugewiesen:
20 REM VEREINBARUNG: 30 ! EXT: £MAPRO:740,£PLOT: 50000 ... 90 REM AUFRUF: 99 SYS £MAPRO: X=13:Y=90:GOSUB £PLOT
Kommen wir abschließend zur Dokumentation: Vom Hobby-Programmierer kann kein Mensch erwarten, daß er Berge von Dokumentationsmaterial anlegt, die den Umfang des Programmtextes um ein Vielfaches übersteigen. Deshalb ist es gerade hier wichtig, Programme weitgehend selbstdokumentierend zu schreiben. Im Gegensatz zu höheren Programmiersprachen mit ihren zahlreichen Deklarationspflichten ist der Basic-Programmierer nahezu ausschließlich auf Kommentare angewiesen. Da Strubs Kommentare bei der Übersetzung eleminiert, stehlen diese weder Speicherplatz noch Laufzeit. Der Programmierer kann also ohne Bedenken einen exzessiven Gebrauch von Kommentaren machen.
Kommentare werden gekennzeichnet durch das Zeichen »'«. Steht dieses Zeichen direkt am Zeilenanfang, so wird die ganze Zeile gelöscht. Sonst wird der Programmtext bis zum zweiten »'« oder bis zum Zeilenende überlesen. Außer innerhalb von Befehls- und Markennamen können Kommentare an jeder beliebigen Programmstelle eingefügt werden. Kommentare, die in das Objektprogramm übernommen werden sollen, können wie bisher mit REM in den Programmtext eingefügt werden. Beispiel:
10 'DIESE ZEILE WIRD VOLLSTÄNDIG GELÖSCHT 20 A'US'G'ABE'$="ENTSPRICHT AG$" 'KOMMENTAR
Die Lesbarkeit von strukturierten Programmen wird verbessert durch das Einrücken von Zeilen entsprechend der Blockstruktur. Hierzu dient der Tabulator (Bild 2): Ein Doppelpunkt am Zeilenanfang gefolgt von Leerzeichen. Für die Ungeduldigen ist das Opjektprogramm von Strubs bereits abgedruckt (Listing). In der nächsten Ausgabe werden wir auf die praktische Programmentwicklung mit Hilfe von Strubs eingehen.
(Matthias Törk)Literatur
* N. Wirth: Systematisches Programmieren, Teubner, Stuttgart 1978
* Kimm, R./Koch,W./Simonsmeier,W./Tontsch,F.: Einführung in Software Engineering, De Gruyter, Berlin, New York 1979
* Schnupp, P./FLoyd, C.: Software: Programmentwicklung und Projektorganisation, De Gruyter, Berlin, New York 1976
* Nagl, M.: Einführung in die Programmiersprache ADA, Vieweg, Braunschweig, Wiesbaden 1982
51 print"{clr}";" *****************" 52 printt " * --strubs.4 -- *" 55 printt " * m.toerk *" 57 printt " * 4352 herten *" 58 printt " *****************" 70 ifnot(peek(46)<46or(peek(46)=46andpeek(45)<3))then75 73 poke46,46:poke45,3:poke46*256,0:clr 75 : 80 ea=40*256+1 8860 print"{clr}{down}{down}{down}{down}{down}" 8870 print "*********************" 8880 print "** zurueck mit: **" 8882 print "** ' ! ' [return] **" 8940 print "*********************"
NEXTCHAR | 00250 |
HOLNAME | 00750 |
ERROR | 08050 |
ABBRUCH | 50000 |
WARTEN | 49550 |
5 remstrubs4/4.9.83 51 print"{clr}";tab(10);"*****************" 52 printtab(10);"* --strubs.4 -- *" 55 printtab(10);"* m.toerk *" 57 printtab(10);"* 4352 herten *" 58 printtab(10);"*****************" 70 ifnot(peek(46)<40or(peek(46)=40andpeek(45)<3))then75 73 poke46,40:poke45,3:poke40*256,0:clr 75 : 80 ea=40*256+1 100 gosub45060 140 goto40050 250 ifpeek(nc)=blthennc=nc+1:goto250 260 c=peek(nc) 265 ifc<>kothen320 280 nc=nc+1:c=peek(nc):ifcandc<>kothen280 290 ifcthennc=nc+1:c=peek(nc) 295 ifc=blthen250 320 ifc<>tethennc=nc+1:return 350 z$=z$+chr$(c):nc=nc+1:c=peek(nc):ifcandc<>tethen350 370 nc=nc+1 390 return 550 iflen(z$)<4thenreturn 555 printfnad(za+2) 560 aa=aa+len(z$)+2 565 h%=aa/256 570 print#1,chr$(aa-256*h%);chr$(h%);z$; 580 return 750 t$="" 790 : 795 c=peek(nc):ifc=dporc=kmorc=blorc=0then811 800 nc=nc+1:t$=t$+chr$(c) 810 goto790 811 : 820 nc=nc+1:ifc=blthengosub250 830 return 1050 gosub750 1120 ifnot(t$="this")then1131 1125 h=fnad(za+2) 1130 goto1175 1131 : 1140 fori=0tomp:ifma$(i)<>t$thennext 1160 ifi>mpthener=2:goto8050: 1170 h=ma%(i)+di 1175 : 1180 z$=z$+mid$(str$(h),2) 1190 return 1550 gosub750 1560 fori=0tobm:ift$<>be$(i)thennext 1565 ifi>bmthener=0:goto8050 1567 b$=be$(i):ifi=3thenb$="if" 1569 i=i+1 1570 onigosub1600,1680,1640,2010,2040,2100,2160,2210,2260,2400,1710,1740,1810,1860 1574 printfnad(za+2); 1575 ifin=0thenprinttab(ta);b$:return 1577 ifin=1thenprinttab(ta);b$:ta=ta+1:return 1579 ifin=2thenprinttab(ta-1);b$:return 1581 ifin=3thenta=ta-1:printtab(ta);b$:return 1586 return 1600 ifsp>smthener=3:goto50000 1605 iflp>lmthener=5:goto50000 1610 s%(sp)=lp:sp=sp+1:lo%(lp,0)=fnad(za+2)-di:lp=lp+1 1615 in=1:return 1640 sp=sp-1:ifsp<0thener=1:goto50000 1650 lo%(s%(sp),1)=fnad(za+2)-di 1660 in=3:return 1680 in=0:return 1710 gosub1600:return 1740 gosub1640:return 1810 gosub1600:return 1860 gosub1640:return 2010 ifsp>smthener=3:goto50000 2011 ifip>imthener=4:goto50000 2020 s%(sp)=ip:ip=ip+1:sp=sp+1 2025 in=1:return 2040 ifsp<1thener=1:goto50000 2041 ifip>imthener=4:goto50000 2044 i%(s%(sp-1))=fnad(za+2)+1-di 2050 s%(sp-1)=ip:ip=ip+1 2052 in=2:return 2100 ifsp<1thener=1:goto50000 2105 sp=sp-1:i%(s%(sp))=fnad(za+2)-di 2107 in=3:return 2160 ifsp>smthener=3:goto50000 2165 s%(sp)=-1:sp=sp+1 2170 gosub2010 2180 in=1:return 2210 gosub2040 2230 gosub2010 2240 in=2:return 2260 h=fnad(za+2)-di 2270 : 2275 ifsp<1thener=1:goto50000 2280 sp=sp-1:i=s%(sp) 2290 ifi<0then2311 2300 i%(i)=h 2310 goto2270 2311 : 2320 in=3:return 2400 : 2410 ifmp>mmthener=6:goto50000 2415 ifcandc<>lathengosub250:goto2415 2420 ifcthengosub750 2423 ifcthengosub250 2425 ifc<48orc>57thener=9:goto8050 2430 ma$(mp)=t$:h=c 2440 gosub750 2450 ma%(mp)=val(chr$(h)+t$)-di 2460 mp=mp+1 2470 ifc=0then2481 2480 goto2400 2481 : 2485 in=0:return 2550 gosub750 2560 fori=0tobm:ift$<>be$(i)thennext 2565 ifi>bmthener=0:goto8050 2568 i=i+1 2570 onigosub2590,2685,2630,3010,3090,3190,3260,3310,3360,3400,3450,3550,3580,3600 2575 return 2590 ifc=0thenz$=z$+":" 2595 s%(sp)=lp:sp=sp+1:lp=lp+1 2597 return 2630 sp=sp-1 2640 z$=z$+gt$+mid$(str$(lo%(s%(sp),0)+di),2)+nu$ 2642 gosub550 2647 l=peek(za+2)+1:h=peek(za+3):ifl>255thenl=0:h=h+1 2648 z$=chr$(l)+chr$(h)+":" 2650 return 2685 b$="":ifright$(z$,1)<>chr$(167)thenb$=gt$ 2693 z$=z$+b$+mid$(str$(lo%(s%(sp-1),1)+di+1),2) 2695 return 3010 z$=z$+ic$+no$+"("+chr$(c) 3020 gosub250:ifc<>thandcthenz$=z$+chr$(c):goto3020 3030 z$=z$+")"+chr$(th)+mid$(str$(i%(ip)+di),2) 3036 ip=ip+1:c=0:return 3090 z$=z$+gt$+mid$(str$(i%(ip)+di),2)+nu$ 3100 gosub550 3120 l=peek(za+2)+1:h=peek(za+3):ifl>255thenl=0:h=h+1 3130 z$=chr$(l)+chr$(h)+":" 3140 ip=ip+1:return 3190 l=peek(za+2):h=peek(za+3) 3200 z$=chr$(l)+chr$(h)+":" 3210 return 3260 gosub3010:return 3310 gosub3090 3320 z$=left$(z$,len(z$)-1) 3330 gosub3010 3340 return 3360 gosub3190 3370 return 3400 z$="":c=0:return 3450 gosub2590 3460 z$=z$+ic$+no$+"(" 3470 ifc<>beandcthenz$=z$+chr$(c):gosub250:goto3470 3480 z$=z$+")"+chr$(th) 3490 z$=z$+mid$(str$(lo%(s%(sp-1),1)+di+1),2) 3495 c=0:return 3550 gosub2630:return 3580 gosub2590:return 3600 z$=z$+ic$+no$+"(" 3610 ifcthenz$=z$+chr$(c):gosub250:goto3610 3620 sp=sp-1:in=3 3630 z$=z$+")"+chr$(th)+mid$(str$(lo%(s%(sp),0)+di),2) 3640 return 4060 z$=chr$(peek(za+2))+chr$(peek(za+3)) 4080 nc=za+4:gosub250 4090 ifc=dpthengosub250 4100 ifnot(c=la)then4110 4105 gosub750:ifc=dpthengosub250 4108 ifc=0thenz$=z$+":" 4110 : 4115 nc=nc-1:ifc=0thenz$=z$+nu$ 4130 :ifc=0then4397 4132 gosub250 4150 ifnot(c=be)then4359 4155 gosub2550 4358 goto4378 4359 : 4360 ifc=lathengosub1050 4378 : 4380 z$=z$+chr$(c) 4396 goto4130 4397 : 4398 return 5050 print"{clr} ***** uebersetzen ****{down}{down}{down}" 5052 ifnot(fnad(ea)<ea+5orfnad(ea)>ea+83)then5054 5053 print"kein programm vorhanden":gosub49550:return 5054 : 5058 print"bitte disk einlegen {down}{down} " 5060 print"name fuer objekt-programm" 5065 poke198,1:poke631,34 5070 inputf$ 5080 open1,8,1,f$+",p,w":open15,8,15 5090 input#15,e,e$:ife=0then5101 5095 print"disk err:";e;e$ 5096 input"neuer versuch";z$ 5098 close1:close15 5099 ifz$<>"j"thenreturn 5100 goto5060 5101 : 5120 aa=ea 5130 print#1,chr$(aaand256);chr$(aa/256); 5135 print"1.lauf" 5136 ta=7 5140 gosub5555 5143 ifsp>0thenprintsp;:er=8:goto50000 5145 print"2.lauf" 5150 gosub6550 5160 print#1,chr$(0);chr$(0); 5180 close1:print"{down}**";ep;" errors **":gosub49550 5190 return 5555 za=ea 5570 ifnot(za<>0)then5931 5580 nc=za+4:c=peek(nc):nc=nc+1 5585 ifc=dpthengosub250 5590 ifc=lathengosub6050:ifc=dpthengosub250 5620 ifc=bethengosub1550 5920 za=fnad(za) 5930 goto5570 5931 : 5935 return 6050 ifmp>mmthener=6:goto50000 6070 gosub750 6100 ma$(mp)=t$:ma%(mp)=fnad(za+2)-di:mp=mp+1 6120 return 6550 za=ea:z1=fnad(za) 6560 lp=0:sp=0:ip=0 6580 : 6585 ifnot(peek(za+4)<>ko)then6650 6590 gosub4060 6600 gosub550 6650 : 6655 za=z1:z1=fnad(z1) 6660 ifnot(z1=0)then6580 6680 return 8050 print"error in";fnad(za+2),er$(er) 8060 ifep<emthener%(ep,0)=fnad(za+2)-di:er%(ep,1)=er:ep=ep+1 8080 z$=left$(z$,2)+"***** err:"+er$(er)+"********" 8090 c$=nu$:c=0 8099 return 8860 print"{clr}{down}{down}{down}{down}{down}" 8870 printtab(9);"*********************" 8880 printtab(9);"** zurueck mit: **" 8882 printtab(9);"** ' ! ' [return] **" 8940 printtab(9);"*********************" 8950 poke44,ea/256:pokeea-1,0:clr:end 8990 end 40050 print"{clr}";tab(10);"*****************" 40052 printtab(10);"* -- strubs -- *" 40053 printtab(10);"* precompiler *" 40055 printtab(10);"* bitte waehlen *" 40058 printtab(10);"*****************" 40060 print"{down}{down}{down}{rvon}e{rvof}dit" 40070 print"{down}{rvon}u{rvof}ebersetzen" 40080 print"{down}{rvon}m{rvof}arken-tabelle ausgeben" 40090 print"{down}{rvon}f{rvof}ehler-tabelle ausgeben" 40100 print"{down}{rvon}s{rvof}chluss" 40160 getz$:ifz$=""then40160 40170 ifz$="e"then8860 40180 ifz$="u"thengosub5050:goto40050 40190 ifz$="s"thensys64738 40195 ifz$="m"thengosub48050:goto40050 40200 ifz$="f"thengosub49050:goto40050 40495 goto40050 45060 mm=99:dimma$(mm),ma%(mm):mp=0 45135 lm=140:dimlo%(lm,1):lp=0 45145 im=270:dimi%(im):ip=0 45190 sm=60:dims%(sm):sp=0 45220 di=32766 45250 dp=asc(":"):ko=asc("'"):la=asc("\"):nu$=chr$(0):bl=asc(" ") 45253 be=asc("!"):te=34:gt$=chr$(137) 45254 ic$=chr$(139):th=167:no$=chr$(168):km=44 45265 bm=13:dimbe$(bm) 45270 fori=0tobm:readbe$(i):next 45271 be$(3)=ic$ 45272 dataloop,exit,eloop,if,else,fi 45273 datacaseof,of,ecase,ext 45274 datawhile,ewhile,repeat,until 45410 deffnad(x)=peek(x)+256*peek(x+1) 45480 em=40:dimer%(em,1):ep=0:dimer$(40) 45500 fori=0to9:reader$(i):next 45510 data"falscher befehl","blockschachtelung: anfang fehlt" 45511 data"undefinierte marke","stack voll" 45512 data"zu viele if/else/case/of","zu viele loop/while/repeat" 45513 data"zu viele marken",,"block nicht geschlossen" 45514 data"extern declaration" 45600 i=0:readw 45610 poke704+i,w:i=i+1:readw:ifw<256then45610 45620 data32,115,0,8,201,33,240,4,40,76,231,167 45630 data169,8,133,44,169,138,76,231,167,999 45650 fori=0to10:readw:poke750+i,w 45660 next 45670 sys750 45680 data169,192,141,8,3,169,2,141,9,3,96 45999 return 48050 ifmp=0thenreturn 48055 h=0 48057 print"{clr}{down} ** markentabelle ausgeben **" 48060 input"{down}{down} auf drucker (j/n)";b$ 48070 ifnot(b$="j")then48091 48075 print" drucker an?":gosub49550 48080 open1,4 48090 goto48104 48091 : 48100 open1,3 48102 h=-1 48104 : 48120 fori=0tomp-1 48140 print#1,ma%(i)+di,ma$(i) 48150 ifi-int(i/10)*10=0thenifiandhthengosub49550 48180 next 48185 close1:gosub49550 48190 return 49050 ifep=0thenreturn 49055 h=0 49057 print"{clr}{down} ** fehlertabelle ausgeben **" 49060 input"{down}{down} auf drucker (j/n)";b$ 49070 ifnot(b$="j")then49091 49075 print"{down} drucker an?{down}{down}":gosub49550 49080 open1,4 49090 goto49104 49091 : 49100 open1,3 49102 h=-1 49104 : 49110 print#1,ep;" errors" 49120 fori=0toep-1 49140 print#1,er%(i,0)+di;er$(er%(i,1)) 49150 ifi-int(i/10)*10=0thenifiandhthengosub49550 49180 next 49185 close1 49191 gosub49550 49190 return 49550 print"->{left}{left}"; 49560 getb$:ifb$=""then49560 49570 return 50000 print"{down}* fehler beheben, dann neu versuchen *" 50008 print:printer$(er);" in ";fnad(za+2) 50010 print#1,chr$(0);chr$(0); 50020 close1 50030 gosub49550 50040 gosub49050 50050 run