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.

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

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