VIC Tracing Disassembler
Peter Busby
Here's a handy tool to let you look at machine language programs — those in your VIC BASIC ROM chips, programs you've typed in from COMPUTE!, or some you've written yourself. If you don't know machine language, seeing it and watching how fast it executes might tempt you to learn more about it.
This article also describes how to adapt a special cassette technique to the VIC.
What is a disassembler? It is a program which looks at the machine code in memory, RAM or ROM, and prints the hexadecimal and decimal values or, more importantly, translates the hex code into 6502 mnemonics. These abbreviated words describe what the microprocessor is doing at that point in the machine language program being disassembled.
Why use a disassembler? Suppose you have Louis Sander's article on PET tape header machine language programs (COMPUTE!, July 1981, #14), which you wish to utilize with your computer. You would use the disassembler to discover how your computer handles tape headers and SAVEs. Hardly a week passes that I do not spend some time exploring the operating system.
The addressing modes are:
# | —Immediate |
Z | —Zero Page |
-A | —Accumulator |
(X) | —Indexed Indirect |
(Y) | —Indirect Indexed |
Z,X Z,Y | —Zero Page Indexed |
,X ,Y | —AbsoluteIndexed |
(I) | —Indirect |
No mode indicated represents either Absolute Addressing or an implied or relative operation. The addresses are shown first in hexadecimal, then in decimal, notation.
When disassembling, it is often convenient to jot down the mnemonics and assign descriptive labels to the branch targets and subroutine locations. At first these will be no more than "A", "B", etc.; but, as the purpose of each part becomes clear, the names evolve to "CHRGET", "SCNKEY", etc. Those of you with printers may wish to add a subroutine to print to hard copy.
As you work through a program, you may occasionally find "ILLEGAL" opcodes or perhaps misleading ones, particularly between subroutines. You have to follow the logic of the routines so that these traps do not prevent easy decoding. There may also be bytes of data interspersed with the program.
No one minds if you look at and disassemble a routine in order to decipher its workings and access points. If you discover that the routine you need is at $E165, by all means have your program go SYS 57701 and use the routine. On the other hand, you may not distribute any portion of copyrighted material, even with the (variable) names changed, without written permission. This program is intended only to explore the workings of your computer or to verify the assembly of your own programs.
Here's how to adapt the tape header machine language program ("MLP") routine to the VIC-20. Develop your MLP according to the precepts in Louis Sander's article. Prepare to load the program whose header you are using; place its title somewhere in RAM above the program (I use $333F on), and follow it immediately with the MLP. Generally, the title would be 16 characters and blanks and the MLP up to 171 bytes. With your monitor, display the six registers starting at $00B7 and place these values:
$ 00B7 - length of MLP + 16 for the title 00B8 - 0 00B9 - 0 00BA - 1 00BB - address of first byte of title, low byte first 00BC (e.g., 00BB-3f;00BC-33)
Now enter .G E156. Presto! Your program title and MLP, when LOADed, will start at $033F.
The VIC Disassembler will handle more than 10 subroutine levels with two adjustments that, however, do use extra memory: at the end of Line 520 may be added:
:DIM RE(99)
or however many you desire; also place this figure inside the parentheses in Line 280:
(SR<99)
or whatever.
10 PRINT"{CLEAR} 6502 DISASSEMBLER" 20 GOSUB 520 30 PRINT : PRINTSE$"ELP "RIGHT$(STR$(SR),2); :GOSUB70 40 GOSUB80 : FOR I = 1 TO 12 : IFA$<>MID$(SE$, I,1) THENNEXT : GOTO 40 50 PRINT" ";:GOSUB70 60 MODE$ = " ":LI = PA : ONIGOSUB 220, 220, 200, 630, 210, 180, 580, 999, 170, 190, 220, 600 : GOTO 30 70 FOR J = 0 TO 18 : PRINTCHR$(157); : NEXT : RETURN : REM CURSOR LEFT'S - APPLE USE CHR$(8) 80 POKE 198, 0 : WAIT 198, 1 : GETA$ : RETURN : REM A PPLE USE GETA$ : ON-(A$ = "")GOTO80 : RETURN 90 J = 3 + 2*(A<LI) : K = 16 100 A% = A + HI*(A>H) : AD$ = "" : FOR I = 0 TO J : AD$ = AD$ + MID$(H$,(A%/K↑(J-I) AND (K-1)) + 1, 1) : NEXT : RETURN 110 AD = 0 : FOR L = 1 TO R : GOSUB 260 : A = PEEK(X) : AD = AD + A*PA↑(L-1) : GOSUB90 : MN$ = AD$ + LEFT $ (MN$, 2) : NEXT 120 RETURN 130 J = 10 : A = 0 : K = A : L = 5 140 GOSUB80 : IF K = 0 THEN IF A$ = "$" ORA$ = "%"THEN PRINTA$; : K = 1 : J = 16 : IF A$ = "%"THEN J = 2 : L = 9 150 ON-(A$ = CHR$(13)) GOTO 120 : FOR I = 1 TO J : IFA$ <> MID$(H$, I, 1)THEN NEXT : GOTO140 160 PRINTA$; : A = A*J + I-1 : K = K + 1 : ON-(K<L) GOTO 140 : RETURN 170 GOSUB290 : X = Y-3 : GOTO 220 180 R =-1 : X = Z : GOTO 220 190 Y = BR : FL = 0 : IFR = -1 THEN X = X + 2*(X>1) : GOTO 220 200 IF FLAG<4 THEN ONFLGOSUB 270, 280, 290 : Z = Y 210 X = Z : R = 0 220 Z = X : A = X : LIMIT = 0 : GOSUB 90 : LI = PA : PRINTAD$ " "; : A = PEEK(X) : IFR>-1 THEN GOSUB 300 230 IFR = -1 THEN GOSUB 90 : PRINTAD$;A;IL$; : GOTO 260 240 PRINTMN$;MO$" ";IL$; : MN$ = " " 245 IF FL = 1 THEN GOSUB 110 : LI = 0 : A = Y : GOSUB ~ 90 : LI = PA : PRINT MN$" "AD$; : GOTO 260 250 IFR THEN GOSUB 110 : PRINTBS$(R);MN$;AD;BS$ (R); 260 IL$ = "" : X = X-(X<HI-1) : RETURN 270 BRANCH = X-2 : RETURN 280 SRTN = SR-(SR<10) : RETURN 290 SR = SR + (SR>0) : Y = RE(SR) : RETURN 300 R = 0 : FL = 4 : ONAAND 3 GOTO 430, 450, 460 : ONFNA(4) GOTO 370 : MN$ = MID$ (ZE$,(AAND248)/8*3+1, 3) 310 IF(A AND 31) = 16 THEN R = 1 : Y = PEEK(X + 1) : Y = X + 2 + Y-2*(Y AND 128) : FL = 1 320 ON-((A AND 31)>0)GOTO 120 : ON FNA(128)GOTO 340 : ON FNA(32)GOTO 350 330 IL$ = " END OF ROUTINE" : RETURN 340 ON-(A = 128)GOTO 460 : R = 1 : MO$ = " # " : RETURN 350 ON FNA(64)GOTO 360 : R = 2 : Y = FNX(0) : RE(SR) = X + 3 : FL = 2 : RETURN 360 FL = 3 : ON-(SR = 0)GOTO 330 : RETURN 370 ON FNA(128)GOTO 410 : IF(A AND 247) = 36 THENMN$ = "BIT":GOTO 420 380 ON-((A AND 223)<>76)GOTO 460 : R = 2 : FL = 0 : MN$ = "JMP" : Y = FNX(0) 390 IFA = 108 THEN MO$ = "(I)" : Y = PEEK(Y) + PEEK(Y+1)*PA 400 RETURN 410 MN$ = MID$(ZE$,(A AND 224)/8*3+1, 3) : IF(A AND 80) = 80 OR A = 156 THEN 460 420 MO$ = MID$(MD$,(A AND 28)/4*3+1, 3) : R = 1-((A AND 31) = 25)-((A AND 15)> 11) : RETURN 430 GOSUB 420 440 MN$ = MID$(OP$(A AND 3),(A AND 224)/32*3+1, 3) : ON-(A = 137)GOTO 460 : RETURN 450 ON FNA(4)GOTO 500 : ON FNA(8)GOTO 470 : MO$ = " ~# " : R = 1 : IFA = 162 THEN 440 460 R=-1 : IL$="ILLEGAL" : RETURN 470 ON FNA(16)GOTO 490 : ON FNA(128)GOTO 480 : MO$ = "-A " : GOTO 440 480 MN$ = MID$(TW$,(A AND 96)/32*3+1, 3) : RETURN 490 ON-((A AND 208)<>144)GOTO 460 : MN$ = "TSX" : ON FNA(32)GOTO 120 : MN$ = "TXS" : RETURN 500 GOSUB 430 : ON-((A AND 208)<>144)GOTO 120 : ON FNA(8)GOTO 510 : MO$ = "Z, Y" : RETURN 510 ON-(A = 158)GOTO 460 : MO$ = ",Y " : RETURN 520 H$ = "0123456789ABCDEF" : SELECT$ = " ABCDENQRU H" 530 ZERO $ = "BRKPHPBPLCLCJSRPLPBMISECRTIPHABVCCLIRTSPLABVSSEI" 540 ZE$ = ZE$ + "STYDEYBCCTYALDYTAYBCSCLVCPYINYBNECLDCPXINXBEQSED" 550 OP$(1) = "ORAANDEORADCSTALDACMPSBC" : OP$(2) = "ASLROLLSRRORSTXLDXDECINC" 560 MD$ = "(X) Z # (Y)Z, X, Y, X " : TWO$ = "TXATAXDEXNOP" : BS$(2) = CHR$(157) : REM CURSOR LEFT 570 HI = 65536 : H = 32767 : PAGE = 256 : DEFFNX(A) = PEEK(X + 1) + PEEK(X + 2)*PA : DEFFNA(B) = (A AND B)/B 580 PRINT"ENTER STARTING ADDRESS (PREFIX ‘$’ FOR HEX)" : GOSUB130 590 X = A*-(A<HI) : Z = X : BR = X : RE(0) = X + 3 600 PRINT : PRINT" A-DVANCE ONE STEP B-RANCH/GO SUBROUTINE C-ONVERT BASES 610 PRINT" D-ISASSEMBLE CODES E-XAMINE ~ADDRESSES N-EW START ADDRESS ~ Q-UIT 620 PRINT" R-ETURN SUBROUTINE U-NBRANCH/BACKSTEP UP (SUBROUTINE LEVEL)" : RETURN 630 PRINT"ENTER NUMBER (PREFIX ‘$’ = HEX, ‘%’ = BINARY)" : GOSUB 130 640 PRINT : IFA>HI-1 THEN PRINT"OUT OF RANGE"; : RETURN 650 GOSUB 90 : IF A>255 THEN PRINT "$"AD$;A; : RETURN 660 PRINT"$"AD$" % " ; : K = 2 : J = 7 : GOSUB100 : PRINT AD$;A; : RETURN 999 END