C 64-Kurs
Strubs
5/84, S. 116-121,126

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

Bild 1. Aufbau von Strubs

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 '
Bild 2. Geschachtelte Loop-Schleife
Bild 3. Struktogramm der Loop-Schleife

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 "*********************"
Dies Änderung sind für die Anpassung von Strubs an den VC 20 (mit mindestens 16 KByte Erweiterung) erforderlich.
NEXTCHAR 00250
HOLNAME 00750
ERROR 08050
ABBRUCH 50000
WARTEN 49550
Allgemeine Routinen
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
Listing. Das Objektprogramm Strubs
5 rem strubs4/4.9.83
10 '*******************************
15 '**  ---- strubs.4.qp ---     **
20 '** 4.9.83                    **
22 '** strubs.2  -code           **
25 '** basic prog voruebersetzen **
30 '** uebersetzt marken in zei- **
32 '** lennr. ( \name)           **
35 '** loescht kommentare '...'  **
36 '**         und blanks        **
40 '** befehle: mit '!'          **
41 '**         loop,exit,eloop   **
43 '**         if,else,fi        **
45 '**         caseof,of,ecase   **
46 '**         while,ewhile      **
47 '**         repeat,until      **
48 '**         ext:              **
49 '*******************************
50 '
51 print"{clr}";tab(10);"*****************"
52 print tab(10);"* --strubs.4 -- *"
55 print tab(10);"*   m.toerk     *"
57 print tab(10);"* 4352 herten   *"
58 print tab(10);"*****************"
67 '
68 '
70 !if peek(46)<40 or (peek(46)=40 and peek(45)<3)then'kein prog in editbereich
71 '
72 '   ** init edit u. var. bereich:
73 :   poke46,40:poke45,3:poke 40*256,0:clr
75 !fi
78 '
80 ea=40*256+1'  ** edit-bereich
100 gosub \init
140 goto \menue
148 '
149 '
200 '******************************
205 '**  -- next zeichen   ---   **
208 '** holt ab adr nc naechstes **
210 '** relevantes zeichen       **
212 '** ueberliest blanks und    **
214 '** kommentare zwischen      **
215 '** ' und  ' bzw zeilende    **
217 '** kopiert strings unveraen-**
218 '** dert nach z$             **
220 '** ein: nc   -char adr      **
222 '**      code-variablen      **
224 '** aus: nc   -adr next char **
226 '**      c    -char-code     **
228 '** sef: z$   -zeilenstring  **
247 '******************************
248 '
250 \nexchar:if peek(nc)=bl'ank'  then nc=nc+1:goto \this ' **blanks ueberlesen
254 '
260 c=peek(nc)
265 if c<>ko'mmentar' then \teststring
267 '
270 ' ** kommentar  ueberlesen
280 nc=nc+1:c=peek(nc):if c and c<>ko then \this
290 if c then nc=nc+1:c=peek(nc)
295 if c=bl then \nexchar
298 '
320 \teststring:  if c<>te'xt' then nc=nc+1:return
340 '
345 ' ** string nach z$ uebertragen  **
350 z$=z$+chr$(c):nc=nc+1:c=peek(nc):if c and c<>te'xt' then \this
370 nc=nc+1
390 return
395 '
500 '******************************
505 '** -schreib zeile auf disk- **
510 '** ein: z$ - zeilenstring   **
512 '** e/a: aa - linkadresse    **
513 '**      darf ausserhalb die-**
514 '**      ser routine nicht!! **
515 '**      veraendert werden ! **
520 '** sef: h%                  **
525 '** imp: fnad - adressfunkt. **
547 '******************************
548 '
550 \schreibzeile:if len(z$)<4 then return' **leerzeile
555 printfnad(za+2)
560 aa=aa+len(z$)+2 ' ** linkadr
565 h%=aa/256
570 print#1,chr$(aa-256*h%);chr$(h%);z$;
580 return
595 '
700 '*****************************
704 '** --- holname   ----      **
706 '** liest name ab adr nc    **
708 '** bis ":", ",", blank     **
709 '**     oder zeilenende     **
710 '** ein: nc                 **
715 '** aus: nc -adr. next char **
720 '**      c  -letztes gelese-**
722 '**         -nes zeichen    **
728 '**      t$ -name           **
747 '*****************************
748 '
750 \holname:t$=""
780 ' **** name lesen
790 !loop
795 :   c=peek(nc):if c=dp or c=km or c=bl or c=0    then !exit
800 :   nc=nc+1:t$=t$+chr$(c)
810 !eloop
820 nc=nc+1:if c=bl'ank' then gosub \nexchar
830 return
835 '
1000 '*****************************
1004 '** -- uebersetze marke --  **
1020 '** ein: z$ -zeilenanfang   **
1022 '**      nc -akt.char adr   **
1030 '** aus: z$ -z$+sprungziel  **
1032 '**      nc -auf letztes    **
1033 '**          gelesenes char **
1038 '** sef: i,h,t$             **
1047 '*****************************
1048 '
1050 \marke:gosub \holname
1115 '
1120 !if t$="this" then
1125 :    h=fnad(za+2)
1130 !else    '** marke suchen  ****
1140 :    for i=0 to mp:if ma$(i)<>t$ then next
1160 :    if i>mp then er=2:goto \error:'undefined label
1170 :    h=ma%(i)+di
1175 !fi
1180 z$=z$+mid$(str$(h),2)
1190 return
1195 '
1495 '
1500 '*********************************
1504 '** --- befehle im 1.lauf ----  **
1510 '** sef: sp,s%() stack          **
1530 '**      i%()    if/case tabelle**
1532 '**      lo%(,)  looptabelle    **
1533 '**      er,er%(),ep -errortab. **
1535 '**      i,in,ta,b$,h,l         **
1540 '** imp: holname,error,abbruch  **
1547 '*********************************
1549 '
1550 \befehl.l1:gosub \holname
1551 '
1560 for i=0 to bm:if t$<>be$(i) then next
1565 if i>bm then er=0:goto \error ' falscher befehl
1567 b$=be$(i):if i=3 then b$="if"
1568 '
1569 i=i+1  ' ** verteiler **
1570 onigosub\l1,\ex1,\el1,\if1,\els1,\fi1,\ca1,\of1,\ec1,\et1,\w1,\n1,\r1,\u1
1571 '
1572 '** blockstrucktur ausgeben **
1574 printfnad(za+2);
1575 if in'dentmodus'=0 then print tab(ta);b$:return
1577 if in=1 then print tab(ta);b$:ta=ta+1:return
1579 if in=2 then print tab(ta-1);b$:return
1581 if in=3 then ta=ta-1:print tab(ta);b$:return
1585 '
1586 return
1588 '
1589 ' ****  loop  *****
1600 \l1: if sp'tr'>sm'ax' then er=3:goto \abbruch
1605 :  if lp>lm then er=5:goto \abbruch
1609 '  * zeilennr merken:
1610 :  s'tack'%(sp)=lp:sp=sp+1:lo%(lp,0)=fnad(za+2)-di:lp=lp+1
1615 :  in'dentmodus'=1:return
1628 '
1629 ' **** eloop  *****
1640 \el1:sp=sp-1:if s'tack'p'ointer'<0 then er=1:goto \abbruch
1649 '  * zeilennummern zu entsprechendem loop nach lo%(,)
1650 :  lo'op'%(s%(sp),1)=fnad(za+2)-di
1660 :  in'dentmodus'=3:return
1678 '
1679 ' ** exit     *****
1680 \ex1: in'dentmodus'=0:return
1688 '
1689 '
1700 ' ** while    *****
1710 \w1: gosub \l1 'loop':return
1715 '
1730 ' *** ewhile ******
1740 \n1: gosub \el1 'eloop':return
1745 '
1800 ' ** repeat *******
1810 \r1: gosub \l1 'loop':return
1815 '
1845 '
1850 ' ** until  *******
1860 \u1: gosub \el1 'eloop':return
1948 '
1990 '
2000 ' ****  if  *******
2005 ' listenplatz fuer spaeteren sprungzieleintrag merken:
2010 \if1: if sp>sm then er=3:goto \abbruch
2011 :     if ip>im then er=4:goto \abbruch
2020 :     s%(sp)=ip:ip=ip+1:sp=sp+1
2025 :     in'dentmodus'=1:return
2029 '
2030 ' **** else *******
2035 ' zeilennr.+1 als sprungziel fuer zugehoeriges if eintragen:
2040 \els1:if sp<1 then er=1:goto \abbruch
2041 :   if ip>im then er=4:goto \abbruch
2044 :   i%(s%(sp-1))=fnad(za+2)+1-di
2045 '   * index fuer spaeteren sprungzieleintrag merken:
2050 :   s%(sp-1)=ip:ip=ip+1
2052 :   in'dentmodus'=2:return
2058 '
2090 ' ****  fi  *******
2095 ' znr. als sprungziel bei if bzw. else eintragen
2100 \fi1: if sp<1 then er=1:goto \abbruch
2105 :     sp=sp-1:i%(s%(sp))=fnad(za+2)-di
2107 :     in'dentmodus'=3:return
2108 '
2110 '
2150 ' **** caseof *****
2160 \ca1: if sp>sm then er=3:goto \abbruch
2165 :     s%(sp)=-1:sp=sp+1
2170 :     gosub \if1
2180 :     in'dentm.'=1:return
2185 '
2200 ' ***** of   ******
2210 \of1: gosub \els1
2230 :     gosub \if1
2240 :     in'dentm.'=2:return
2245 '
2250 ' ***** ecase *****
2260 \ec1: h=fnad(za+2)-di ' * zeilennr
2269 '  ** ausgaenge eintragen
2270 :  !loop
2275 :      if sp<1 then er=1:goto \abbruch
2280 :      sp=sp-1:i=s%(sp)
2290 :      if i<0 then !exit
2300 :      i%(i)=h
2310 :  !eloop
2320 :  in'dentm.'=3:return
2330 '
2399 ' *** ext/const ***
2400 \et1: !loop
2410 :     if mp>mm then er=6:goto \abbruch
2415 :     if c and c<>la'bel' then gosub \nexchar:goto \this
2420 :     if c then gosub \holname
2423 :     if c then gosub \nexchar
2425 :     if c<48 or c>57 then 'keine ziffer' er=9:goto \error
2430 :     ma$(mp)=t$:h=c
2438 '
2439 '     ** wert des labels: **
2440 :     gosub \holname
2450 :     ma%(mp)=val(chr$(h)+t$)-di
2460 :     mp=mp+1
2470 :     if c=0 then !exit
2480 !eloop
2481 '
2485 in'dentm.'=0:return
2495 '
2497 '
2500 '*********************************
2504 '** --- befehle im 2.lauf ----  **
2510 '** sef: stack                  **
2530 '**      ip,lp - tab. pointer   **
2534 '**      z$  - zeilenstring     **
2540 '** imp: holname                **
2547 '*********************************
2549 '
2550 \befehl.l2:gosub \holname
2551 '
2560 for i=0 to bm:if t$<>be$(i) then next
2565 if i>bm then er=0:goto \error ' * falscher befehl
2567 '
2568 i=i+1  ' ** verteiler **
2570 onigosub\l2,\ex2,\el2,\if2,\els2,\fi2,\ca2,\of2,\ec2,\et2,\w2,\n2,\r2,\u2
2575 return
2576 '
2589 ' ****  loop  *****
2590 \l2:if c=0 then z$=z$+":"
2592 '   index von loop/eloop paar merken
2595 :   s%(sp)=lp:sp=sp+1:lp=lp+1
2597 :   return
2628 '
2629 ' **** eloop  *****
2630 \el2: sp=sp-1
2639 '   * sprung zu entspr. loop
2640 :   z$=z$+g'o't'o'$+mid$(str$(lo%(s%(sp),0)+di),2)+nu$
2642 :   gosub \schreibzeile
2645 '   * folgezeile als sprungziel generieren
2647 :   l=peek(za+2)+1:h=peek(za+3):if l>255 then l=0:h=h+1
2648 :   z$=chr$(l)+chr$(h) +":"
2650 :   return
2652 '
2680 ' ****  exit  *****
2685 \ex2:b$="":if right$(z$,1)<>chr$(167) 'then-code' then b$=g'o't'o'$
2689 '   * sprung zu naechstem eloop
2693 :   z$=z$+b$+mid$(str$(lo%(s%(sp-1),1)+di+1),2)
2695 :   return
2947 '
2955 '
3000 ' ****  if  ********
3010 \if2: z$=z$+i'f'c$+no't'$+"("+chr$(c)
3020 :    gosub \nexchar:if c<>th'en' and c then z$=z$+chr$(c): goto \this
3030 :    z$=z$+")"+chr$(th'en')+mid$(str$(i%(ip)+di),2)
3035 '
3036 :    ip=ip+1:c=0:return
3039 '
3080 ' **** else ********
3090 \els2: z$=z$+g'o't'o'$+mid$(str$(i%(ip)+di),2)+nu$
3100 :    gosub \schreibzeile
3110 '   * folgezeile als sprungziel generieren:
3120 :   l=peek(za+2)+1:h=peek(za+3):if l>255 then l=0:h=h+1
3130 :   z$=chr$(l)+chr$(h) +":"
3140 :   ip=ip+1:return
3149 '
3180 ' ****  fi  ********
3190 \fi2: l=peek(za+2):h=peek(za+3)
3195 '   * zeile als sprungziel generieren:
3200 :   z$=chr$(l)+chr$(h) +":"
3210 :   return
3255 '
3259 ' ***** caseof ****
3260 \ca2: gosub \if2:return
3299 '
3300 ' ***** of   ******
3310 \of2: gosub \els2
3320 :     z$=left$(z$,len(z$)-1) ' ":" weg
3330 :     gosub \if2
3340 :     return
3345 '
3350 ' ***** ecase *****
3360 \ec2: gosub \fi2
3370 :  return
3380 '
3385 '
3399 ' *** ext/const ***
3400 \et2: z$="":c=0:return  ' *zeile loeschen
3405 '
3448 '
3449 ' *** while   *****
3450 \w2: gosub \l2 'loop'
3460 :    z$=z$+i'f'c'ode'$+no't'$+"("
3469 '    ** bedingung kopieren:
3470 :    if c<>be'fehl' and c then z$=z$+chr$(c):gosub \nexchar:goto \this
3480 :    z$=z$+")"+chr$(th'en')
3488 '    ** analog exit:
3490 :    z$=z$+mid$(str$(lo%(s%(sp-1),1)+di+1),2)
3495 :    c=0:return
3497 '
3498 '
3549 ' *** ewhile  *****
3550 \n2: gosub \el2 'eloop':return
3555 '
3557 '
3579 ' *** repeat  *****
3580 \r2: gosub \l2 'loop':return
3585 '
3599 ' *** until   *****
3600 \u2: z$=z$+i'f'c$+no't'$+"("
3605 '
3609 '   * bedingung kopieren
3610 :   if c then z$=z$+chr$(c):gosub \nexchar:goto \this
3619 '   * analog eloop
3620 :   sp=sp-1:in'dent'=3
3630 :   z$=z$+")"+chr$(th'en')+mid$(str$(lo%(s%(sp),0)+di),2)
3640 :   return
4000 '*****************************
4004 '** - bearbeite zeile  -    **
4020 '** ein: za -zeilenadr      **
4028 '** aus: z$ -zeilenstring   **
4029 '**          uebersetzte z. **
4035 '**      left$(z$,2)=zeilnr **
4040 '** imp: \befehl.l2         **
4045 '**      \marke             **
4047 '*****************************
4048 '
4050 ' ** zeilennr:       **
4060 \zeile:z$=chr$(peek(za+2))+chr$(peek(za+3))
4080 nc=za+4:gosub \nexchar ' 1.zeichen der zeile
4082 '
4089 ' **    'tabulator'  **
4090 if c=dp then gosub \nexchar
4098 '
4099 ' ** marke ueberlesen:  **
4100 !if c=la'bel' then
4105 :   gosub \holname:if c=dp then gosub \nexchar
4108 :   if c=0 then z$=z$+":"
4110 !fi
4111 '
4115 nc=nc-1:if c=0 then z$=z$+nu'll'$
4119 '
4120 ' ********  zeile lesen   ********
4130 !loop: if c=0 then !exit
4131 '
4132 :   gosub \nexchar
4138 '
4150 :   !if c=be'fehl' then
4155 :      gosub \befehl.l2
4358 :   !else
4360 :      if c=la'bel' then gosub \marke
4378 :   !fi
4380 :   z$=z$+chr$(c)
4395 ' ********  bis  zeilenende  *****
4396 !eloop
4398 return
4399 '
5000 '*****************************
5005 '** --- uebersetzen ---     **
5047 '*****************************
5048 '
5049 '
5050 \uebersetzen:  print"{clr}   ***** uebersetzen    ****{down}{down}{down}"
5052 !if fnad(ea)<ea+5 or fnad(ea)>ea+83 then
5053 :    print"kein programm vorhanden":gosub \warten:return
5054 !fi
5057 '
5058 print"bitte disk einlegen {down}{down} "
5059 '
5060 !loop  print"name fuer objekt-programm"
5065 :   poke198,1:poke631,34 ' **  " fuer input
5070 :   input f$
5080 :   open 1,8,1,f$+",p,w":open 15,8,15
5090 :   input#15,e,e$:if e=0 then !exit
5095 :   print"disk err:";e;e$
5096 :   input"neuer versuch";z$
5098 :   close1:close15
5099 :   if z$<>"j" then return
5100 !eloop
5118 '
5119 '
5120 aa=ea
5130 print#1,chr$(aa and 256);chr$(aa/256);'  ** startadr.
5134 '
5135 print"1.lauf"
5136 ta'bulator'=7 'fuer blockstruktur ausgabe
5140 gosub \1.lauf
5142 '" ** alle bloecke geschlossen?
5143 if sp>0 then print sp;:er=8:goto \abbruch
5144 '
5145 print"2.lauf"
5150 gosub \2.lauf
5154 '
5160 print#1,chr$(0);chr$(0);'  **** prog.ende marke
5180 close1:print"{down}**";ep;" errors **":gosub \warten
5190 return
5198 '
5199 '
5500 '*****************************
5504 '**  --- 1.lauf   ---       **
5510 '** imp: \nexchar           **
5512 '**      \mardef            **
5514 '**      \befehl.l1         **
5547 '*****************************
5548 '
5550 '  *** zeilenad.=editbereich anf
5555 \1.lauf:   za=ea
5557 '
5560 ' ** while nicht progr.ende do ***
5570 !while  za<>0  !do
5580 :   nc=za+4:c=peek(nc):nc=nc+1  '1.zeichen der zeile
5584 '   ** tab ueberlesen:
5585 :   if c=d'oppel'p'unkt' then gosub \nexchar
5587 '
5589 '   ** marke definieren
5590 :   if c=la'bel' then gosub \mardef:if c=dp then gosub \nexchar
5599 '
5619 '   ** befehl:
5620 :   if c=be'fehl' then gosub \befehl.l1
5920 :   za=fnad(za)
5930 !ewhile
5935 return
5940 ' **** endwhile ******************
5995 '
5996 '
6000 '*******************************
6004 '** --- marke definieren  --- **
6015 '** ein: za -zeilenadr.       **
6020 '** aus: veraenderte marken-  **
6022 '**      liste ma$(),ma%(),mp **
6030 '** sef: nc,t$                **
6047 '*******************************
6048 '
6050 \mardef:    if mp>mm'ax' then er=6:goto \abbruch
6070 gosub \holname
6095 '
6100 ma$(mp)=t$:ma%(mp)=fnad(za+2)-di:mp=mp+1
6120 return
6130 '
6500 '*****************************
6504 '** --- 2.lauf     ---      **
6510 '** imp: \zeile             **
6512 '**      \schreibzeile      **
6547 '*****************************
6548 '
6550 \2.lauf: z'eilen'a'dresse'=e'ditbereich'a'nfang':z1=fnad(za) 'adr. 2.zeile
6560 lp=0:sp=0:ip=0  ' * pointer ruecksetzen
6575 '
6580 !repeat
6585 :  !if peek(za+4)<>ko'mmentar' then
6590 :    gosub \zeile  ' bearbeiten
6600 :    gosub \schreibzeile
6649 '
6650 :  !fi
6655 :  za=z1:z1=fnad(z1) ' adresse naechste zeile
6660 !until z1=0
6670 ' * progr. ende *
6680 return
6685 '
8000 '*****************************
8004 '** --- error      -----    **
8047 '*****************************
8050 \error:print"error in";fnad(za+2),er$(er)
8060 if ep<em then er%(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  'zeilenende setzen
8099 return
8799 '
8800 '*****************************
8805 '** umschalten edit bereich **
8840 '** basic-anfang umsetzen   **
8847 '*****************************
8849 '
8850 '
8860 \edit:    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:poke ea-1,0:clr:end
8990 end
40000 '****************************
40010 '**  --- menue ---         **
40048 '****************************
40049 '
40050 \menue:print"{clr}";tab(10);"*****************"
40052 print tab(10);"* -- strubs  -- *"
40053 print tab(10);"*  precompiler  *"
40055 print tab(10);"* bitte waehlen *"
40058 print tab(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"
40150 '
40160 get z$:if z$="" then \this
40170 if z$="e" then \edit
40180 if z$="u" then gosub \uebersetzen:goto \menue
40190 if z$="s" then sys 64738 '** kaltstart
40195 if z$="m" then gosub \markentab-aus:goto \menue
40200 if z$="f" then gosub \errortab-aus:goto \menue
40495 goto \menue
45000 '****************************
45010 '*  --- init  ---           *
45048 '****************************
45049 '
45050 ' ** marken-tabelle:
45060 \init: mm'ax'=99:dim ma$(mm),ma%(mm):mp=0
45069 '
45120 '
45130 ' ** loop-tabelle:
45131 ' *lo(..,0)=znr.loop
45132 ' *lo(..,1)=znr. zugehoeriges eloop
45135 l'oop'm'ax'=140:dim lo'op'%(lm,1):l'oop'p'ointer'=0
45138 '
45140 ' ** if-tabelle:
45145 im'ax'=270:dim i%(im):ip=0
45149 '
45188 '
45189 ' ** stack:
45190 sm'ax'=60:dim s'tack'%(sm):sp'tr'=0
45200 '
45209 '
45210 ' ** differenz fuer zeilennr. in integer-array
45220 di=32766
45225 '
45240 ' ** relevante zeichencodes **
45250 dp=asc(":"):ko'mmentar'=asc("'"):la'bel'=asc("\"):nu$=chr$(0):bl=asc(" ")
45253 be'fehl'=asc("!"):te'xt("")'=34:g'o't'o-code'$=chr$(137)
45254 i'f'c'ode'$=chr$(139):th'en-code'=167:no't'$=chr$(168):k'om'm'a-code'=44
45259 '
45260 '***** befehle:  ****************
45265 bm=13:dim be$(bm)
45270 for i=0 to bm:read be$(i):next
45271 be$(3)=i'f'c'ode'$
45272 data loop,exit,eloop,if,else,fi
45273 data caseof,of,ecase,ext
45274 data while,ewhile,repeat,until
45399 '
45400 ' ** adressberechnung:
45410 def fnad(x)=peek(x)+256*peek(x+1)
45412 '
45415 '
45470 ' ** error-tabelle:
45480 em=40:dim er%(em,1):ep=0:dim er$(40)
45490 ' ** fehlermeldungen
45500 fori=0to9:read er$(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"
45595 '
45599 ' ** interpretererw. '!' = poke44,8:run
45600 i=0:read w
45610 poke 704+i,w:i=i+1:read w:if w<256 then \this
45620 data 32,115,0,8,201,33,240,4,40,76,231,167
45630 data 169,8,133,44,169,138,76,231,167,999
45640 ' * umschalten:
45650 for i=0 to 10:read w:poke 750+i,w
45660 next
45670 sys 750
45680 data 169,192,141,8,3,169,2,141,9,3,96
45690 '
45999 return
48000 '********************************
48003 '** - markentabelle ausgeben - **
48048 '********************************
48049 '
48050 \markentab-aus:if mp=0 then return
48055 h=0 ' flag
48057 print"{clr}{down}      ** markentabelle ausgeben **"
48060 input"{down}{down} auf drucker (j/n)";b$
48070 !if b$="j" then
48075 :   print" drucker an?":gosub \warten
48080 :   open 1,4
48090 !else
48100 :   open 1,3 'bildschirm
48102 :   h=-1 ' flag
48104 !fi
48105 '
48120 for i=0 to mp-1
48140 :   print#1,ma%(i)+di,ma$(i)
48150 :   if i-int(i/10)*10 =0 then if i and h then gosub \warten
48180 next
48185 close1:gosub \warten
48190 return
48195 '
49000 '********************************
49003 '** - fehlertabelle ausgeben - **
49048 '********************************
49049 '
49050 \errortab-aus:if ep=0 then return
49055 h=0 ' flag
49057 print"{clr}{down}      ** fehlertabelle ausgeben **"
49060 input"{down}{down} auf drucker (j/n)";b$
49070 !if b$="j" then
49075 :   print"{down} drucker an?{down}{down}":gosub \warten
49080 :   open 1,4
49090 !else
49100 :   open 1,3 'bildschirm
49102 :   h=-1 ' flag
49104 !fi
49105 '
49110 print#1,ep;" errors"
49120 for i=0 to ep-1
49140 :   print#1, er%(i,0)+di;er$(er%(i,1))
49150 :   if i-int(i/10)*10 =0 then if i and h then gosub \warten
49180 next
49185 close1
49191 gosub \warten
49190 return
49195 '
49500 '********************************
49503 '** --- auf taste warten   --- **
49548 '********************************
49549 '
49550 \warten:print"->{left}{left}";
49560 getb$:if b$="" then \this
49570 return
49598 '
49599 '
49950 '********************************
49955 '** --- progr.abbruch      --- **
49958 '** schliesst file             **
49970 '** gibt fehlermeldung aus     **
49975 '** ein: er -fehlercode        **
49990 '********************************
50000 \abbruch: print "{down}* fehler beheben, dann neu versuchen *"
50008 print:print er$(er);" in ";fnad(za+2)
50010 print#1,chr$(0);chr$(0); '  **** prog.ende marke
50020 close1
50030 gosub \warten
50040 gosub \errortab-aus
50050 run
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →