Logic Disassembler
6/84, S. 108-109

Logic Disassembler

Wer hat nicht schon nach einem guten Einstieg in das Betriebssystem oder in ein selbstgeschriebenes Assembler-Programm gesucht, um dem Verlauf der Programmlogik folgen zu können?

Mit diesem ganz in Basic geschriebenen Programm besteht nun die Möglichkeit, bei einer beliebigen Startadresse einzusteigen und von dort aus das Programm zu verfolgen.
Auch mit einem kompletten Disassembler-Listing war dies bisher eine äußerst aufwendige und zeitraubende Angelegenheit. Bei je dem Sprung-Befehl (sei es BRANCH, JSR oder JMP) ging das große Blättern los. Das Verfolgen von mehrstufigen Unterroutinen mit unterschiedlichen Ausstiegsmöglichkeiten ist schon ganz unmöglich.

Der Logic-Disassembler hingegen wartet bei jedem Sprungbefehl auf die Eingabe von Y, N oder X. Y steht für »yes«, das heißt dem Sprungbefehl folgen, N steht für »no«, also zum Beispiel eine bekannte Unterroutine einfach zu übergehen, damit das Listing nicht zu unübersichtlich wird. X schließlich steht für »exit«, wodurch der Disassembler bei einer neuen Adresse gestartet werden kann.

Das Listing der Basic-Routine »NEW« (Bild 1) zeigt als Beispiel, wie ein solcher Disassembler-Lauf vor sich geht.

Bild 1. Logische Disassemblierung der Basic-Routine »NEW«

Die Einsprungadresse liegt bei $C642.

In Zeile 1 wird der BNE-Befehl mit der Eingabe von »N« übergangen. Der JSR-Befehl in Zeile 14 wird mit »Y« verfolgt, und es erfolgt eine Verzweigung in die Routine $C68E. Mit der Anzeige von STACK 0 wird über die Tiefe der Unterprogramm-Verschachtelung informiert. Bei Erreichen von RTS in Zeile 22 wird in das Hauptprogramm (STACK 0) zurückgesprungen und dieses ab der Adresse $C65C fortgeführt.

Der darauffolgende BRANCH-Befehl in Zeile 24 wird wieder mit der Eingabe von »N« übergangen. In Zeile 25 folgt dann ein RTS, und da der STACK auf 0 steht (man befindet sich also im Hauptprogramm), wird der Disassembler-Vorgang beendet und auf die erneute Eingabe einer Startadresse gewartet.

Bei der Eingabe von »END« statt einer Startadresse wird das Programm beendet.

Dabei wird jede Stackänderung angezeigt.

Es können alle Sprungbefehle verfolgt werden, sogar solche, die sich auf eine indirekte Adressierung beziehen. Als Beispiel hierfür kann das Disassembler-Protokoll der Abfrage der Stop-Taste dienen (Bild 2).

Bild 2. Die Betriebssystemroutine zur Abfrage der STOP-Taste disassembliert.

In der Zeile 1 ist der Befehl JMP ($0328) gefunden worden, jetzt wird der Inhalt der Speicherstelle $0328 und $0329 ausgelesen und die Sprungadresse errechnet. Dann erfolgt ein Speichertest an der errechneten Sprungadresse, und erst, wenn dieser Test positiv verläuft, wird ab dieser Adresse weiter disassembliert. Verläuft der Test jedoch negativ, erfolgt ein »LOGIC FLOW ERROR«, und es wird zum Ende der Disassemblierung gesprungen.

Das Programm »Logic-Disassembler« ist in der vorliegenden Form für den VC 20 geschrieben, es ist jedoch nach Änderung der Zeilen 100 und 530 (die POKE-Befehle ändern lediglich die Farben des Bildschirms) sofort auf dem Commodore 64 und den anderen CBM-Computern lauffähig.

(Fred Hammer)
A$ Tabelle für Sprungadressen
AC% Tabelle für 6502-Mnemonics
SS% Tabelle für Stack
SS% Index für Stack
I Index für Mnemonic-Tabelle
H$ Werte für Hexa-Umrechnung
FE$ Zwischenspeicher für Anfangsadresse
A$ Startadresse in Hexa
X$ Hexa-Wert bei Umrechnung
DE Dezimal-Wert bei Umrechnung
Z0
N0
Z1
Indexfelder für Umrechnung
A Anfangsadresse ln Dezimal
AN$ KZ für Druckerausgabe
N Zeilennummer
AD aktuelle Adresse bei der Disassemblierung
P Zwischenspeicher für Adreßrechnung
EA Dezimal-Wert des laufenden Befhles
S$ Hexa-Werte der laufenden Disassembler-Zeile
MN$ laufende Disassembler Zeile
K Anzahl der Bytes für das Befehlswort
AA$ Eingabefeld bei Sprungbefehlen
FF$ Überschrift bei Druckerausgabe
A1 Arbeitsfeld zur Adreßberechnung bei JMP ind.
A2 wie A1, jedoch zur Kontrolle ob Memory vorhanden
Variablen-Liste vom »Logic Disassembler«
100-250 Programmstart und Tabelleninitialisierung
260-290 Umrechnung Hexa in Dezimal
300-350 Umrechnung Dezimal in Hexa
360-510 DATA-Satements mit den 6502-Mnemonics
520-550 Eingabe der Startadresse
560-620 Abfrage auf Druckerausgabe
630-870 Disassembler—Schleife
880-970 BRANCH-Befehle
980-1040 Eingabe (Y)es, (N)o, e(X)it
1050-1080 RTS-Befehl mit Stack-Abfrage
1090-1170 JSR-Befehl mit Stack-Erhoehung
1180-1200 JMP-Befehl absolut
1210-1250 JMP-Befehl indirekt
1260-1300 Ende
Die wichtigsten Programmteile vom »Logic Disassembler«
100 poke36879,233:print"{clr}{down} *** disassembler ***"
110 print"{down}{down} logic - disassembler"
120 print"{down}   vc 20 - version"
130 print"{down}         by         "
140 print"{down}{down}  {$a0}fred hammer      "
150 print"{down}   obere bergstr. 12"
160 print"{down}   5419 leuterod    "
170 print"{down}   tel. 02602/60372 "
180 print"{down}{down}{down}   press  any  key   ";
190 geta$:ifa$=""then190
200 clr:poke36879,25:dimss(100):ss%=0:dimac$(255)
210 print"{clr}< disassembler-start >{down}"
220 fori=0to255:readac$(i):next
230 poke36879,25:print"{clr}< disassembler-start >{down}"
240 h$="0123456789abcdef"
250 goto520
260 rem hex-umrechnung
270 de=0:forz0=1to4:n0=0:forz1=1to16
280 ifmid$(x$,z0,1)=mid$(h$,z1,1)thenn0=z1-1:z1=16
290 next:de=de+16^(4-z0)*n0:next:return
300 rem dez-umrechnung
310 x$="":forz0=4to1step-1:z1=int(de/16^(z0-1))
320 ifz1=0thenx$=x$+"0":goto350
330 ifz1<10thenx$=x$+right$(str$(z1),len(str$(z1))-1):goto350
340 x$=x$+chr$(z1+55)
350 de=de-z1*16^(z0-1):next:return
360 rem der 6502-befehlssatz
370 data brk,ora (x,,,,ora $,asl $,,php,ora #,asl,,,ora,asl?,,bpl,ora (y,,,
380 data ora $x,asl $x,,clc,ora y,,,,ora x,asl x,,jsr?,and (x,,,bit $,and $
390 data rol $,,plp,and #,rol,,bit?,and?,rol?,,bmi,and (y,,,,and $x,rol $x,
400 data sec,and y,,,,and x,rol x,,rti,eor (x,,,,eor $,lsr $,,pha,eor #
410 data lsr,,jmp?,eor?,lsr?,,bvc,eor (y,,,,eor $x,lsr $x,,cli,eor y,,,,eor x
420 data lsr x,,rts,adc (x,,,,adc $,ror $,,pla,adc #,ror,,jmp (,adc?,ror?,
430 data bvs,adc (y,,,,adc $x,ror $x,,sei,adc y,,,,adc x,ror x,,,sta (x,,
440 data sty $,sta $,stx $,,dey,,txa,,sty?,sta?,stx?,,bcc,sta (y,,,sty $x
450 data sta $x,stx $y,,tya,sta y,txs,,,sta x,,,ldy #,lda (x,ldx #,,ldy $
460 data lda $,ldx $,,tay,lda #,tax,,ldy?,lda?,ldx?,,bcs,lda (y,,, ldy $x
470 data lda $x,ldx $y,,clv,lda y,tsx,,ldy x,lda x,ldx y,,cpy #,cmp (x,,
480 data cpy $,cmp $,dec $,,iny,cmp #,dex,,cpy?,cmp?,dec?,,bne,cmp (y,,,
490 data cmp $x,dec $x,,cld,cmp y,,,,cmp x,dec x,,cpx #,sbc (x,,,cpx $
500 data sbc $,inc $,,inx,sbc #,nop,,cpx?,sbc?,inc?,,beq,sbc (y,,,,sbc $x
510 data inc $x,,sed,sbc y,,,,sbc x,inc x,
520 fe$=ad$:input"{down}start (hex)";a$
530 ifa$="end"thenclr:poke36879,29:end
540 iflen(a$)<>5orleft$(a$,1)<>"$"then520
550 x$=right$(a$,4):gosub260:a=de
560 print"{down}printer ? y/n":poke198,0:wait198,1:getan$:ifan$<>"y"then600
570 input"comment ";ff$
580 open4,4:cmd4
590 printchr$(14);ff$;chr$(15):print
600 print"{clr}<logic - disassembler>":print:print"line# loc    code           statement
610 print:print"      0000                  *="a$:print:n=0
620 ad=a-1
630 ad=ad+1:n=n+1:de=ad:gosub300
640 printright$("0000"+right$(str$(n),len(str$(n))-1),4)"  "x$"  ";
650 p=peek(ad):de=p:ea=p:gosub300:s$=right$(x$,2):restore
660 mn$=ac$(p):iflen(mn$)=3then880
670 ifmn$=""then940
680 iflen(mn$)=4then830
690 ifright$(mn$,1)="#"thenmn$=mn$+"$":goto770
700 ifright$(mn$,1)="$"then770
710 ifea=108then840
720 ifmid$(mn$,5,1)="("then790
730 ifmid$(mn$,5,1)="$"then810
740 gosub750:mn$=left$(mn$,4)+"$"+a$(2)+a$(1)+","+right$(mn$,1):goto940
750 fork=1to2:ad=ad+1:p=peek(ad):de=p:gosub300:a$(k)=right$(x$,2)
760 s$=s$+" "+right$(x$,2):next:return
770 ad=ad+1:p=peek(ad):de=p:gosub300:s$=s$+" "+right$(x$,2)
780 mn$=mn$+right$(x$,2):goto940
790 ad=ad+1:p=peek(ad):de=p:gosub300:s$=s$+"{$a0}"+right$(x$,2)
800 mn$=left$(mn$,5)+"$"+right$(x$,2)+"),"+right$(mn$,1):goto940
810 ad=ad+1:p=peek(ad):de=p:gosub300:s$=s$+"{$a0}"+right$(x$,2)
820 mn$=left$(mn$,5)+right$(x$,2)+","+right$(mn$,1):goto940
830 gosub750:mn$=left$(mn$,3)+"{$a0}$"+a$(2)+a$(1):goto940
840 ad=ad+2:p=peek(ad):de=p:gosub300:s$=s$+"{$a0}"+right$(x$,2)
850 mn$=left$(mn$,5)+"$"+right$(x$,2)
860 p=peek(ad-1):de=p:gosub300:s$=s$+" "+right$(x$,2)
870 mn$=mn$+right$(x$,2)+")":goto940
880 rem branch-befehl
890 ifmn$="brk"orleft$(mn$,1)<>"b"then940
900 ad=ad+1:p=peek(ad):de=p:gosub300:s$=s$+"{$a0}"+right$(x$,2)
910 ifp>=130thena0=ad-(255-p):goto930
920 a0=(ad-1)+p+2
930 de=a0:gosub300:mn$=mn$+"{$a0}$"+x$:kz=1
940 ifea=32thenmn$=mn$+"             stack"+str$(ss%+1)
950 ifea=96andss%>0thenmn$=mn$+"                   stack"+str$(ss%-1)
960 printleft$(s$+"                ",16)mn$:ifea=96thenprint
970 ifkz=0then1030
980 getaa$:ifaa$=""then980
990 ifan$="y"thencmd4,chr$(15);
1000 kz=0:ifaa$="n"then1030
1010 ifaa$="x"goto1260
1020 print:ad=a0-1:kz=0
1030 ifea=32orea=76orea=108orea=96then1050
1040 goto630
1050 ifea<>96then1090
1060 rem "befehl: rts"
1070 ifss%=0then1260
1080 ss%=ss%-1:ad=ss(ss%):goto630
1090 rem "befehl: jsr"
1100 getaa$:ifaa$=""then1100
1110 ifan$="y"thencmd4,chr$(15);
1120 ifaa$="n"then630
1130 ifaa$="x"goto1260
1140 ifea<>32then1180
1150 ss(ss%)=ad:ss%=ss%+1:print
1160 ad=peek(ad)*256+peek(ad-1)-1
1170 goto630
1180 rem "befehl: jmp abs."
1190 ifea<>76then1210
1200 ad=peek(ad)*256+peek(ad-1)-1:print:goto630
1210 rem "befehl: jmp ind."
1220 a1=peek(ad)*256+peek(ad-1)
1230 a2=peek(ad)*256+peek(ad-1)
1240 ifa1<>a2thenprint:print"logic flow error":goto1260
1250 ad=peek(a1+1)*256+peek(a1)-1:print:goto630
1260 de=ad:gosub300
1270 print:print"start-adr.{$a0}= "a$:print"end-adr.   ={$a0}$"x$
1280 print:print"< disassembler - end >"
1290 ifan$="y"thenprint#4,:close4
1300 goto520
Listing zum »Logic Disassembler«
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →