C 64
Wettbewerb: Dokumentationshilfe

»Dokumentationshilfe«: Cross-Referenz-Liste C 64

Die Dokumentation von Basic-Programmen ist manchmal eine mühevolle Aufgabe. Trotzdem ist sie sinnvoll oder sogar notwendig. Aus diesem Grund schlugen wir einen Programmierwettbewerb zur Dokumentationshilfe vor, dessen Ergebnis hier vor Ihnen liegt.

Jeder Basic-Programmierer kennt das Problem: Man schreibt lange und auch komplizierte Programme und merkt, daß langsam aber sicher der Überblick verloren geht. Oder auch, wenn versucht wird, ein fremdes, nicht selbst erstelltes Programm zu verstehen: Was bedeutet diese Variable, und wo taucht sie im Programm noch auf? Von wo und wie oft wird diese Programmzeile angesprungen?

Bei gut strukturierten Programmen findet man relativ schnell durch, aber die sind selten zu finden. Doch selbst dann ist eine gute Beschreibung eine hilfreiche Sache und erleichtert den späteren Wiedereinstieg.

Eine Crossreferenzliste unterstützt bei der Erstellung einer Dokumentation. Gerade bei kommerziellen Software-Projekten wird nicht auf sie verzichtet. Sie enthält dabei nicht nur eine Aufzählung aller Sprünge (sofern bei höheren Programmiersprachen überhaupt vorhanden), sondern vor allem eine komplette Variablenliste. Und diese Funktionen hat auch unser Programm. Im einzelnen enthält sie:

  1. Liste aller Zeilen, in denen Sprungbefehle enthalten sind. Angegeben wird die Zeilennummer und die angesprungene Zeile.
  2. Liste aller Zeilen, die angesprungen werden. Angegeben sind die Zeilennummer und alle Zeilen, von denen aus diese Zeile angesprungen wird. Eine sehr nützliche Information.
  3. Eine Liste aller im Programm vorkommenden Variablen. Angegeben wird, alphabetisch sortiert, der Variablenname und die Zeilen, in denen sie vorkommt. Zusätzlich hat man die Möglichkeit, zu jeder Variablen einen kurzen Kommentar mit einzugeben.
  4. Liste aller Variablen mit dem vorhin genannten Kommentar, aber ohne Hinweise auf die Zeilen, in denen sie vorkommen.

Mit diesen Informationen kann man schon etwas anfangen. Doch jetzt etwas für Programmierer selbst:

Wie funktioniert das?

Wie sucht man nach Variablen? Wie nach Sprungbefehlen? Wie so oft, ist die Lösung ganz einfach.

Wie ein Basic-Programm im Speicher steht, wurde ausführlich im 64’er, Ausgabe 2/85, Seite 87, beschrieben. Fast in der gleichen Form wird ein Programm auch auf Diskette gespeichert. Lediglich zwei Byte für die Ladeadresse des Programms kommen dazu. Beim absoluten Laden eines Programms (LOAD"NAME",8,1) holt sich der Basic-Interpreter diese zwei Byte, um festzustellen, an welcher Stelle im Speicher das Programm beginnt. Beim normalen LOAD-Befehl, also ohne die ",1" am Ende, wird jedes Programm ab Basic-Anfang gesetzt, also ab $801 ( = dezimal 2049).

An einem kleinen Beispiel soll gezeigt werden, wie man sich alle Zeilen anzeigen lassen kann, die den Befehl GOTO enthalten. Sehen Sie sich dazu das Flußdiagramm (Bild 1) an.

Bild 1. So funktioniert im Prinzip dos Suchen von Basic-Befehlen aus Programmen, die auf Diskette stehen.

Als erstes muß eine »Programmdatei« geöffnet werden. Das geht zum Beispiel mit OPEN2,8,2,"NAME,P,R". »P« steht dabei für Programm und »R« für READ, also lesen.

Dann folgt das Lesen der Byte. Dazu nimmt man den GET#-Befehl. Der INPUT #-Befehl ist zwar wesentlich schneller, »schafft« aber nur maximal 88 Zeichen, benötigt ein Carriage Return (CHR$(13)) als Endekennzeichen (bei Programmen sind es Null-Byte) und verträgt keine Kommata (in Basic-Programmen oft vorhanden). Außerdem erlaubt uns der GET#-Befehl die Überprüfung einzelner Zeichen direkt nach dem Laden.

Lesen wir jetzt also die ersten Byte eines Basic-Programms. Am Anfang, das sind die ersten zwei Bytes, steht die Startadresse. Bei Basic-Programmen ist sie 2049, hexadezimal ausgedrückt $0801. Darauf folgen zwei Byte für die Anfangsadresse der nächsten Basic-Zeile (gemeint ist hier die Adresse im RAM, die natürlich auch gespeichert wird). Erst jetzt beginnt die wirkliche Basic-Zeile: Zwei Byte für die Zeilennummer, aufgetrennt in Low- und High-Byte, gefolgt vom Basic-Text. Das Ende einer Basic-Zeile erkennen Sie (und der Basic-Interpreter) an einem Null-Byte. Danach folgt wieder die Anfangsadresse der nächsten Basic-Zeile (Low- und High-Byte) und der nächsten Zeilennummer (auch Low- und High-Byte), danach der Basic-Text, etc., etc.. Das Ende eines Basic-Programms ist gekennzeichnet durch drei aufeinanderfolgende Null-Byte, und auch die Statusvariable ST wird auf 64 gesetzt.

Das war’s schon. Mit diesem Wissen sind Sie in der Lage, ein Basic-Programm komplett zu analysieren. Eine vollständige Liste aller Token, das sind die Abkürzungen, die Codes der Basic-Befehle, finden Sie in Bild 2.

Befehl Token Befehl Token Befehl Token
DEZ HEX DEZ HEX DEZ HEX
END 128 80 CONT 154 9A SGN 180 B4
FOR 129 81 LIST 155 9B INT 181 B5
NEXT 130 82 CLR 156 9C ABS 182 B6
DATA 131 83 CMD 157 9D USR 183 B7
INPUT# 132 84 SYS 158 9E FRE 184 B8
INPUT 133 85 OPEN 159 9F POS 185 B9
DIM 134 86 CLOSE 160 A0 SQR 186 BA
READ 135 87 GET 161 Al RND 187 BB
LET 136 88 NEW 162 A2 LOG 188 BC
GOTO 137 89 TAB 163 A3 EXP 189 BD
RUN 138 8A TO 164 A4 COS 190 BE
IF 139 8B FN 165 A5 SIN 191 BF
REST. 140 8C SPC 166 A6 TAN 192 C0
GOSUB 141 8D THEN 167 A7 ATN 193 C1
RETURN 142 8E NOT 168 A8 PEEK 194 C2
REM 143 8F STEP 169 A9 LEN 195 C3
STOP 144 90 + 170 AA STR$ 196 C4
ON 145 91 171 AB VAL 197 C5
WAIT 146 92 * 172 AC ASC 198 C6
LOAD 147 93 / 173 AD CHR$ 199 C7
SAVE 148 94 174 AE LEFT$ 200 C8
VERIFY 149 95 AND 175 AF RIGHT$ 201 C9
DEF 150 96 OR 176 B0 MID$ 202 CA
POKE 151 97 > 177 Bl GO 203 CB
PRINT# 152 98 = 178 B2
PRINT 153 99 < 179 B3
Bild 2. Basic-Befehlswörter und deren Token.

Doch nun zum Programm »Cross-Ref 64«.

Cross-Ref 64

Das hier vorgestellte Programm Cross-Ref 64 (Listing 2) arbeitet auf allen Commodore-Computern, mit einem Floppy- oder Kassetten-Laufwerk und einem Drucker.

Nach dem Start mit RUN kann die Ausgabe der fertigen Listen auf Drucker oder Bildschirm gewählt werden (siehe auch Zeile 200 bis 260). Die Variable DV enthält die jeweils gültige Gerätenummer. Danach müssen Sie den Dateinamen des zu durchsuchenden Files eingeben.

Ihr Computer liest sich nun das Programm wie ein sequentielles File Byte für Byte durch. Das Programm steht auf der Diskette im gleichen Format wie im RAM, lediglich die ersten beiden Bytes geben den Start des Programms an. Liegt Ihr Programm nicht ab 2049, zum Beispiel bei Maschinenprogrammen üblich, so bricht das Programm die Bearbeitung ab. Sollten Sie das Programm auf einem anderen Commodore-Computer betreiben, so geben Sie einfach Listing 3 ein und tauschen die auf dem Bildschrim erscheinende Zahl gegen die in Zeile 290 stehende 2049 aus.

Beim Durchsuchen »hangelt« sich der Computer durch die Zeilen, indem er, immer wenn er auf einen Charakter 0 trifft, eine neue Zeile beginnt. Im ersten Paß durchsucht er das Programm auf die Token für die Sprungbefehle GOTO, RUN, GOSUB und THEN (Zeile 330). Sie haben, in der gleichen Reihenfolge, die ASCII-Werte 137, 138, 141 und 167. Gefundene Sprünge werden auf zwei Arten gespeichert. Als erstes im Format ZEILENNUMMER : SPRUNGZIEL (Bild 3) in den Zeilen 400 bis 420, darauf im Format SPRUNGZIEL : ZEILENNUMMERN (Bild 3). Wobei SPRUNGZIEL immer die Zahl ist, die hinter GOTO oder THEN steht. ZEILENNUMMER ist jeweils die Zeile, in der der Sprungbefehl auftaucht.

Bild 3. Diese Sprungtabelle wird ausgegeben, wenn das Programm »Listing 1« untersucht wird.

Die Zeilennummern werden in den Feldern ps$( und sp$( gespeichert. Wird in einem der Felder die Maximallänge von 70 Zeichen überschritten, so wird ein neues Feld angelegt (Zeile 410 und 450).

Haben Sie den ersten Pass glücklich überstanden, so wird die Liste auf das von Ihnen gewählte Gerät übertragen (Zeile 580 bis 680). Die aufsteigend sortierten Zeilennummern werden so ausgegeben, daß nie zwei gleiche Zeilennummern untereinanderstehen. Statt dessen werden sechs Leerzeichen gedruckt (Zeile 660).

Der zweite Pass läuft im Prinzip wie der erste ab. Die Variablen mit zugehörigen Zeilennummern werden lediglich im Feld VA$( gespeichert und sortiert. Selbstverständlich wird der Text hinter REM und DATA sowie Anführungszeichen überlesen (Zeile 750-790).

In diesem Stadium kommt Arbeit auf Sie zu. Jetzt haben Sie die Möglichkeit, die Variablen mit einem Text zu versehen. Dabei müssen Sie sich allerdings auf 25 Zeichen beschränken.

Antworten Sie auf die Frage »WOLLEN SIE ZU DEN VARIABLEN BEMERKUNGEN EINGEBEN« mit »J«, so können sie mit Cursor UP und DOWN durch die Liste wandern. Die momentane Variable wird jeweils angezeigt. Haben Sie die richtige Stelle gefunden, so drücken Sie »RETURN« und können Ihren Text eingeben. Durch nochmaliges Drücken der »RETURN«-Taste wird der Text gespeichert.

Haben Sie Ihre Texte verteilt, so beginnt das Programm nach Drücken der »N«-Taste zu drucken (Zeilen 1180 bis 1280). Auch hier können Sie den Druckvorgang wiederholen (Bild 4 und 5).

Bild 4. Diese Variablenliste erstellt »Cross-Ref 64« nach Untersuchung von »Listing 1«. Die Kommentare müssen natürlich zusätzlich eingegeben werden.
Bild 5. Auch eine Variablenliste ohne Zeilennummern ist möglich.

Umstellungshinweise

Dieses Programm ist auf allen Computern lauffähig, die ihren Basic-Text nach Commodoreart speichern. Potentielle »Umschreiber« können sich daher auf die Zeile 330 beschränken. Auch die Druckausgabe ist leicht modifizierbar. Die Sekundäradresse der OPEN-Befehle in den Zeilen 580 und 1180 stellen den Epson MX-80 über das Print-64-Interface auf Klein/Großschrift um. Dies kann auch durch normale »OPENs« mit nachfolgenden Steuercodes erfolgen.

Sogar bei einem Kassettenlaufwerk ist das Programm verwendbar. Auch hier ändern Sie in den Zeilen 290 und 730 die OPEN-Befehle (Listing 4).

(Stefan Becker/gk)
5 REM X=2
10 DIMA$(100),B(20),C%(20)
20 FORI=1TO20:B(I)=I:NEXT
30 FORJ=1TO20
40    PRINT" WERT NR."J;:INPUTA$(J)
50    IF A$(J)="X" THEN J=20
60 NEXT J
70 INPUT"PRG.NAME = "; NA$
80 OPEN2,8,2,NA$+"P,W"
90 FORX=1TOJ
100    PRINT#2,A$(I)
110 NEXT
120 CLOSE2
130 INPUT"AUSWAHL =";AW$:AW=VAL(AW$)
140 ON AW GOSUB 1000,2000,3000
150 END
1000 PRINTAW:X=1:RETURN
2000 PRINTAW:X=2:RETURN
3000 PRINTAW:X=3:RETURN
Listing 1. Dieses Programm liefert die Listen Bild 3 bis 5.
10 rem*********************************
20 rem* programmname : xref           *
30 rem*     c-64                      *
40 rem*     floppy 1541 o. aehnliche  *
50 rem*     drucker (z.b. mps 801)    *
60 rem* von stefan becker             *
70 rem*********************************
80 clr:goto150
90 get#1,a$:x=asc(a$+n$):if(64andst)=0thenreturn
100 close1:ifpathenpa=0:goto520
110 goto960
120 gosub90:x1=x:gosub90:x=x1+256*x:return
130 ifsp<obandva<obandps<obthenreturn:rem*** grenzen erreicht ? ***
140 print:print"Bitte die Variable ob in Zeile 150 vergroessern.":goto1310
150 ob=500:dimsp$(ob),ps$(ob),va$(ob)
160 rem*** ob ist obergrenze der anzahl der spruenge und variablen ***
170 poke53280,6:poke53281,6
180 n$=chr$(0)
190 le$="                                                  "
200 printchr$(147)chr$(9)chr$(14)chr$(8)chr$(144);
210 print"{rvon}          Cross-Referenz-Lister         "
220 print"{down}{down}Ausgabe auf ":print"{down}{rvon}B{rvof}ildschirm oder {rvon}D{rvof}rucker{up}{up}{up}"
230 printspc(12);:poke204,0
240 geta$:ifa$<>"b"anda$<>"d"goto240
250 ifa$="b"thenprint"Bildschirm.":dv=3:goto270
260 print"Drucker.":dv=4
270 print"{down}                       {up}"
280 open1,0:print"Programmname: ";:input#1,na$:close1:print
290 open2,8,15:open1,8,2,na$+",p,r":gosub1290:gosub120:ifx=2049then310
300 print:print"Das Programm muss ab 2049 liegen.":goto1310
302 :
304 :
305 rem********************************
306 rem     pass 1 sprungtabelle
308 rem********************************
309 :
310 print"{clr}Pass 1 (Suchen der Spruenge){down}":pa=1
320 gosub120:gosub120:ze$=right$("     "+str$(x),5):print"{home}{down}{down}"ze$
330 gosub90:ifx=137orx=138orx=141orx=167thens1$="":goto360
340 ifx=0goto320
350 goto330
360 gosub90:ifx=32goto360
370 ifx>=48andx<=57thens1$=s1$+a$:goto360
380 ifs1$=""goto470
390 s1$=right$("      "+s1$,6)
400 ifleft$(sp$(sp),5)<>ze$thensp=sp+1:gosub130:sp$(sp)=ze$+":"
410 iflen(sp$(sp))>70thensp=sp+1:gosub130:sp$(sp)=ze$+":"
420 ifright$(sp$(sp),6)<>s1$thensp$(sp)=sp$(sp)+s1$
430 fori=1tops
440 ifleft$(ps$(i),6)<>s1$thennext:ps=i:gosub130:ps$(i)=s1$+":"
450 iflen(ps$(i))>70thennext:ps=ps+1:i=ps:gosub130:ps$(i)=s1$+":"
460 ifright$(ps$(i),5)<>ze$thenps$(i)=ps$(i)+" "+ze$
470 ifx=0goto320
480 ifx=44thens1$="":goto360
490 ifx=58orx>=127or(x>=65andx<=90)goto330
500 print:print"{down}Fehler im Quellprogramm. Zeile:"ze$:goto1310
502 :
504 :
506 rem -------------------------------
510 rem*** sortieren der sprungziele (feld ps$( ***
515 rem -------------------------------
516 :
520 fori=1tops
530 forj=itops
540 ifleft$(ps$(i),5)<left$(ps$(j),5)goto560
550 ps$(0)=ps$(i):ps$(i)=ps$(j):ps$(j)=ps$(0)
560 nextj
570 nexti
574 :
575 rem--------------------------------
576 rem   ausgabe spruenge + sprungziele
577 rem--------------------------------
578 :
580 open1,dv,7:print#1,"Programmname: "na$:print#1
590 print#1,"sprungtabelle           "
600 print#1,"--------------------------"
605 print#1,"zeile :   sprung auf zeile      ":print#1
610 fori=1tosp
620 ifleft$(sp$(i-1),5)=left$(sp$(i),5)thenprint#1,spc(7)mid$(sp$(i),7):goto640
630 print#1," "sp$(i)
640 nexti:print#1
645 print#1,"zeile :   wird angesprungen von":print#1
650 ps$(0)="":fori=2tops
660 ifleft$(ps$(i-1),6)<>left$(ps$(i),6)thenprint#1,ps$(i):goto680
670 print#1,spc(6);mid$(ps$(i),7)
680 nexti:close1
690 print"{down}{rvon}N{rvof}ochmals/{rvon}W{rvof}eiter"
700 geta$:ifa$<>"n"anda$<>"w"goto700
710 ifa$="n"goto580
712 :
714 :
715 rem********************************
716 rem     pass 2 variable suchen
718 rem********************************
719 :
720 print"{clr}Pass 2 (Suchen der Variablen){down}"
730 open1,8,2,na$+",p,r":gosub1290:gosub120
740 gosub120:gosub120:ze$=right$("     "+str$(x),5):print"{home}{down}{down}"ze$:y=0
750 gosub90
760 ifx=0goto740
770 ifx=34orx=131orx=143goto800
780 ifx>64andx<91goto850
790 goto750
800 ifx=131theny=1
810 gosub90:ifx=0goto740
820 ifx=34goto750
830 ifx=58andy=1goto750
840 goto810
850 v1$=a$
860 gosub90
870 ifx=36orx=37or(x>47andx<58)or(x>64andx<91)thenv1$=v1$+a$:goto860
880 ifx=40thenv1$=v1$+a$
890 v1$=left$(v1$+"    ",4)
900 fori=1tova
910 ifleft$(va$(i),4)<>v1$thennext:va=i:gosub130:va$(i)=v1$+"  :"
920 iflen(va$(i))>50thennext:va=va+1:i=va:gosub130:va$(i)=va$+"  :"
930 ifright$(va$(i),5)<>ze$thenva$(i)=va$(i)+" "+ze$
940 ifx=0goto740
950 goto750
952 :
954 :
956 rem -------------------------------
960 rem *** sortieren der variablen (feld va$( ***
965 rem -------------------------------
968 :
970 fori=1tova
980 forj=itova
990 ifleft$(va$(i),4)>left$(va$(j),4)goto1010
1000 va$(0)=va$(i):va$(i)=va$(j):va$(j)=va$(0)
1010 nextj
1020 nexti:va=va-1:i=va
1025 :
1030 print"{clr}Wollen Sie zu den Variablen":print"Bemerkungen eingeben?  ";
1040 geta$:ifa$="n"goto1180
1050 ifa$<>"j"goto1040
1060 print"Ja{down}{down}{down}"
1070 print"{home}{down}{down}{down}{down}{down}{down}Variablenname: ";left$(va$(i),4)
1073 printle$
1075 iflen(va$(i))>70thenprint"{up}{up}"right$(va$(i),25)
1080 geta$:ifa$<>"{up}"anda$<>"{down}"anda$<>chr$(13)goto1080
1090 ifa$="{up}"theni=i+1:ifi>vatheni=va
1100 ifa$="{down}"theni=i-1:ifi<1theni=1
1110 ifa$<>chr$(13)goto1070
1120 vr$=left$(va$(i),4):open1,0
1130 print"Text:";:input#1,te$:print:close1:te$=left$(te$,25)
1140 fori=vato1step-1
1150 ifleft$(va$(i),4)<>vr$thennext:print"Nicht vorhanden.":goto1070
1160 va$(i)=left$(va$(i)+le$,55)+right$(le$+te$,25):goto1030
1170 vr$=left$(vr$,4)
1174 ;
1175 rem-------------------------------
1176 rem     ausgabe variable
1177 rem-------------------------------
1180 print:open1,dv,7
1190 print#1,"{down}{down}liste der variablen     :"
1200 print#1,"-------------------------"
1210 fori=vato1step-1
1220 ifleft$(va$(i+1),4)<>left$(va$(i),4)thenprint#1,va$(i):goto1240
1230 print#1,spc(6);mid$(va$(i),7)
1240 nexti
1250 print"{down}{rvon}n{rvof}ochmals/{rvon}w{rvof}eiter"
1260 geta$:ifa$<>"n"anda$<>"w"goto1260
1263 ifa$="n"goto1190
1266 print"Variablenliste ohne Zeilennummer (j/n)"
1267 getr$:ifr$=""then1267
1268 ifr$<>"j"thenprint:print"ende":goto1310
1269 print#1:print#1:print#1,"Variablenliste ohne Zeilennummer"
1270 print#1,"-------------------------------"
1271 fori=vato1step-1
1272 iflen(va$(i))>70thenprint#1,left$(va$(i),8);right$(va$(i),25):goto1274
1273 print#1,left$(va$(i),8)
1274 nexti
1280 goto1310
1290 input#2,a$,b$:ifa$="00"thenreturn
1300 print:print"Disk-Error: ";b$
1310 close1:close2:end
Listing 2. Cross-Referenz-Liste 64
Eingetippt von vicjack
10 input"name";a$
20 open 1,8,2,a$+",p,r"
30 get#1,a$,b$
40 close 1
50 print "anfangsadresse: " ; asc(a$+chr$(0))+256*asc(b$+chr$(0))
60 :
70 rem fuer kassettenbetrieb:
80 rem zeile 20: open1,1,0,a$
Listing 3. Cross-Ref 64 für andere Commodore-Computer
290 open 1,1,0,na$:gosub 120:if x=2049 then 310
730 open 1,1,0,na$:gosub 120
999 rem die zeilen 1290 bis 1310 entfallen
Listing 4. Anpassung von Cross-Ref 64 an die Datasette
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →