DISA Z is a Z80 disassembler written in BASIC that decodes machine code bytes stored in memory and prints human-readable assembly mnemonics. The program accepts a decimal start address, then reads and decodes opcodes including all prefixed instruction groups: DD (IX), FD (IY), ED (extended), and CB (bit operations). It handles multi-byte instructions by computing instruction length before fetching operand bytes, and formats 16-bit addresses in big-endian hex by swapping high and low byte pairs. A configurable variable `TS2` at line 160 adjusts the hex character offset (`NCOD`) to accommodate different character code mappings between the TS1000 and TS2068 models. The main loop uses `POKE 23692,255` to suppress the automatic scroll prompt, allowing continuous output without user intervention.
Program Analysis
Program Structure
The program is organized as a collection of BASIC subroutines dispatched from a main loop beginning at line 3560. After prompting for a start address, the loop at lines 3580–3760 repeatedly fetches an instruction, computes its length, formats a hex dump string, decodes the mnemonic, prints the result, and advances to the next instruction. Control never exits this loop during normal operation; line 3770 (STOP) is unreachable in practice.
| Line range | Purpose |
|---|---|
| 10–50 | REM header block |
| 60–140 | Data string initialization (mnemonic tables) |
| 150–180 | Platform selection and branch to main loop |
| 190–250 | Octal digit decoder subroutine |
| 260–380 | Hex formatting subroutines (byte and address) |
| 390–950 | Instruction length calculator |
| 960–3220 | Mnemonic decoder (unprefixed + CB + ED groups) |
| 3230–3470 | CB-prefixed (bit/rotate/shift) instruction decoder |
| 3480–3550 | DD/FD (IX/IY) prefix handler |
| 3560–3780 | Main input/display loop |
Platform Adaptation via TS2 Flag
Line 160 sets TS2=1, and line 170 computes NCOD=28+20*TS2, giving 48 for TS2068 (ASCII ‘0’) or 28 for TS1000. This offset is used in line 300 when building the hex string: CHR$(NCOD+Y+7*(Y>9)*TS2). On the TS1000 the character set places digits at code 28; on the TS2068 they follow ASCII. The expression 7*(Y>9)*TS2 adds 7 for A–F only on the TS2068 model, bridging the gap between ‘9’ and ‘A’ in ASCII.
Mnemonic Data Encoding
Rather than storing one string per mnemonic, the program packs related mnemonics into fixed-width substrings within shared data strings, then slices them by computed index. This approach saves significant memory.
O$(line 70): 8 arithmetic/logic ops, 3 characters each — ADD, ADC, SUB, SBC, AND, XOR, OR, CPD$(line 80): register-pair names, 2 characters each — BC, DE, HL, SP, AF, IX, IYB$(line 90): BIT, RES, SET — 3 characters eachC$(line 100): rotate/shift direction qualifiersR$(line 110): single-character register names B, C, D, E, H, L, X (placeholder for (HL)), AE$(line 120): condition codes, 2 characters each — NZ, Z, NC, C, PO, PE, P, MF$(line 130): miscellaneous group-0/group-3 single-byte ops (RLCA, RRCA, RLA, RRA, DAA, CPL, SCF, CCF), 4 characters eachG$(line 140): block instruction roots — LD, CP, IN, OT
Instruction Length Calculation
The subroutine at lines 390–950 determines how many bytes the current instruction occupies before any decoding begins. It initializes L=0 and increments it based on opcode class. Prefix bytes (DD=221, FD=253) cause a recursive-style re-entry via the GO TO 410 loop at line 440. The ED prefix (237) triggers a separate path at line 540 that classifies the following byte to decide whether a 2-byte operand follows. CB prefix (203) always yields a 2-byte instruction body (plus any preceding prefix). Unprefixed opcodes in the range 64–191 (LD r,r block) take 1 byte; operand-bearing opcodes in the first and fourth groups are detected by modular arithmetic on the octal decomposition.
Octal Decomposition Idiom
The Z80 instruction set is most naturally described in octal. The subroutine at lines 190–250 decomposes an opcode X0 into three fields: the high octal digit pair K (0–3, the “group”), the middle octal digit C (0–7), and the low octal digit T (0–7). This decomposition is used throughout the decoder, e.g. group 1 (64–127) are LD r,r; group 2 (128–191) are arithmetic/logic on registers; group 3 (192–255) contains calls, jumps, and miscellaneous ops.
The modulo-8 test used in many conditional branches is expressed as NOT (W - INT(W/8)*8), which is true when W is a multiple of 8, equivalent to W MOD 8 = 0.
Hex String Construction and Address Formatting
The program builds a fixed-width display string H$ that holds the address, hex bytes, and mnemonic in a single concatenated string. The address hexer (lines 320–380) calls the byte hexer (lines 260–310) twice — first for the high byte, then for the low byte — placing them in big-endian print order. For 16-bit address operands embedded in instructions, the decoder retrieves them as H$(10 TO 11)+H$(8 TO 9), swapping the two hex-byte pairs back to little-endian Z80 format.
Line 3670 strips the last two characters of H$ after the initial address hex call, removing the byte that was PEEKed purely to test for prefix status but should not appear in the opcode dump at that position.
Index Register Substitution
When an IX or IY prefix is active (DD or FD flags set), the placeholder register "X" in P$ or Q$ is replaced by an indexed address string such as "(IX+nn)" at lines 1240–1280. The displacement byte nn is read directly from the pre-built hex string at positions 10–11. Without a prefix, "X" becomes "(HL)" (lines 1200–1210).
Scroll Suppression
Line 3750 executes POKE 23692,255 each iteration. Address 23692 is the system variable SCRCT (scroll counter), which normally halts output and prompts “scroll?” after a screenful of lines. Poking 255 resets the counter to its maximum, effectively disabling the pause and allowing the disassembly to scroll continuously.
Notable Bugs and Anomalies
- Line 3080 contains
IF T<>7 THEN GO TO 3180, but the ED-prefix decoder at lines 2760–3220 never routes execution to line 3080 — theT=7case is not reachable from the preceding branch structure, so LD A,I / LD A,R / LD I,A / LD R,A / RRD / RLD decode is dead code in practice (only lines 3090–3170 are ever reached via line 3080 if it were called). The correct dispatcher for T=7 would need an explicit check after line 3070. - The
EX AF,AF'mnemonic at line 2180 is rendered asEX AF,AF"due to BASIC string quoting — the closing single-quote of the alternate register notation cannot be represented inside a BASIC string literal and is approximated with a double-quote. This is a display inaccuracy rather than a functional bug. - Lines 2540–2570 handle certain LD (rr),A and LD A,(rr) forms. The branch for
C=4at line 2560 falls through toGO TO 1100, which appends a pointer register name but does not add the surrounding parentheses or comma, potentially producing malformed output for theLD (nn),HLclass of instructions when no IX/IY prefix is active.
Source Code
10 REM *********************** DISA Z
20 REM *********************** A Z80 DISASSEMBLER FOR TS COMPUTERS
30 REM *********************** COPYRIGHT,1983 ALGIS GEDRIS
40 REM *********************** RUN AND ENTER START ADDRESS IN DECIMAL
50 REM ***********************
60 REM DATA STRINGS
70 LET O$="ADDADCSUBSBCANDXOROR CP "
80 LET D$="BCDEHLSPAFIXIY"
90 LET B$="BITRESSET"
100 LET C$="RSLRC AL"
110 LET R$="BCDEHLXA"
120 LET E$="NZZ NCC POPEP M "
130 LET F$="RLCARRCARLA RRA DAA CPL SCF CCF "
140 LET G$="LDCPINOT"
150 REM FOR TS2000 SET TS2=1
160 LET TS2=1
170 LET NCOD=28+20*TS2
180 GO TO 3560
190 REM OCTAL DIGITS
200 FOR K=0 TO 3
210 IF X0 >=K*64 AND X0<(K+1)*64 THEN LET X=X0-K*64
220 NEXT K
230 LET C= INT (X/8)
240 LET T=X-C*8
250 RETURN
260 REM BYTE HEXER
270 LET X= PEEK J
280 LET Y= INT (X/16)
290 LET Z=X-Y*16
300 LET H$=H$+ CHR$ (NCOD+Y+7*(Y>9)*TS2)+ CHR$ (NCOD+Z+7*(Z>9)*TS2)
310 RETURN
320 REM ADDRESS HEXER
330 LET X= INT (J/256)
340 GO SUB 280
350 LET X=J-256*X
360 GO SUB 280
370 LET H$=H$+" "
380 RETURN
390 REM GET INSTRUCTION LENGTH
400 LET L=0
410 IF X <>221 AND X <>253 THEN GO TO 520
420 IF L THEN RETURN
430 GO SUB 450
440 GO TO 410
450 LET L=1
460 LET J=J+1
470 GO SUB 270
480 LET J=J-1
490 LET H$=H$( TO LEN H$-2)
500 RETURN
510 REM I-LENGTH FOR PREFIXES
520 IF L THEN LET L=L+((X>51) AND (X<55) OR X=203)
530 IF X=203 THEN GO TO 930
540 IF X <>237 THEN GO TO 700
550 GO SUB 450
560 IF X<64 OR X>188 THEN RETURN
570 IF X<160 AND X>123 THEN RETURN
580 IF X<124 THEN GO TO 610
590 IF X- INT (X/8)*8<5 THEN GO TO 940
600 RETURN
610 IF X=78 OR X=102 OR X=110 OR X=112 OR X=113 OR X=118 OR X=119 THEN RETURN
620 LET W=X-67
630 IF NOT (W- INT (W/8)*8) THEN GO TO 910
640 LET W=X-76
650 IF W >=0 AND NOT (W- INT (W/8)*8) THEN RETURN
660 LET W=X-85
670 IF W >=0 AND NOT (W- INT (W/8)*8) THEN RETURN
680 GO TO 940
690 REM WITHOUT PREFIXES
700 IF X<64 OR X>191 THEN GO TO 730
710 IF L THEN LET L=L+(X-8* INT (X/8)=6)
720 GO TO 940
730 IF X>191 THEN GO TO 820
740 IF Z=1 THEN GO TO 910
750 LET W=X-34
760 IF W >=0 AND NOT (W- INT (W/8)*8) THEN GO TO 910
770 LET W=X-6
780 IF W >=0 AND NOT (W- INT (W/8)*8) THEN GO TO 930
790 LET W=X-16
800 IF W >=0 AND NOT (W- INT (W/8)*8) THEN GO TO 930
810 GO TO 940
820 IF X=195 OR X=205 THEN GO TO 910
830 IF X=211 OR X=219 THEN GO TO 930
840 LET W=X-194
850 IF NOT (W- INT (W/8)*8) THEN GO TO 910
860 LET W=W-2
870 IF NOT (W- INT (W/8)*8) THEN GO TO 910
880 LET W=W-2
890 IF NOT (W- INT (W/8)*8) THEN GO TO 930
900 GO TO 940
910 IF L=2 THEN GO TO 930
920 LET L=L+1
930 LET L=L+1
940 LET L=L+1
950 RETURN
960 REM MNEMONICS
970 LET H$=H$+" "
980 IF LEN H$<14 THEN GO TO 970
990 IF X0=118 THEN GO TO 1320
1000 GO SUB 190
1010 IF X0<64 OR X0>191 THEN GO TO 1440
1020 IF X0>127 THEN GO TO 1340
1030 REM EIGHT-BIT REG LDS
1040 LET P$=R$(C+1)
1050 LET Q$=R$(T+1)
1060 IF P$="X" OR Q$="X" THEN GO SUB 1190
1070 LET H$=H$+"LD "+P$+","+Q$
1080 RETURN
1090 REM ADD POINTER REGS TO H$
1100 IF NOT DD THEN GO TO 1130
1110 LET H$=H$+"IX"
1120 RETURN
1130 IF NOT FD THEN GO TO 1160
1140 LET H$=H$+"IY"
1150 RETURN
1160 LET H$=H$+"HL"
1170 RETURN
1180 REM CHECK FOR FD OR DD
1190 IF FD OR DD THEN GO TO 1240
1200 IF P$="X" THEN LET P$="(HL)"
1210 IF Q$="X" THEN LET Q$="(HL)"
1220 RETURN
1230 REM ADD INDEX REGS TO H$
1240 IF FD THEN LET W$="(IY+"
1250 IF DD THEN LET W$="(IX+"
1260 IF P$="X" THEN LET P$=W$+H$(10 TO 11)+")"
1270 IF Q$="X" THEN LET Q$=W$+H$(10 TO 11)+")"
1280 RETURN
1290 GO SUB 1190
1300 LET H$=H$+P$+","+R$(1+T)
1310 RETURN
1320 LET H$=H$+"HLT"
1330 RETURN
1340 REM ARITH/LOGIC OPS
1350 LET I=C*3+1
1360 LET Q$=R$(T+1)
1370 GO SUB 1190
1380 LET I$=" "
1390 IF I<7 OR I=10 THEN LET I$=" A,"
1400 IF I>18 THEN LET I$=""
1410 LET H$=H$+O$(1 TO I+2)+I$+Q$
1420 RETURN
1430 REM 1ST AND 4TH GROUPS
1440 IF X0=203 THEN GO TO 3230
1450 IF X0=253 OR X0=221 THEN GO TO 3480
1460 IF X0>191 THEN GO TO 1620
1470 REM 1ST GROUP
1480 IF T=1 THEN GO TO 2100
1490 IF T=3 THEN GO TO 2320
1500 IF T=4 OR T=5 THEN GO TO 2380
1510 IF T=2 THEN GO TO 2450
1520 IF NOT T THEN GO TO 2160
1530 IF T <>6 THEN GO TO 1590
1540 LET Q$=R$(C+1)
1550 GO SUB 1190
1560 LET W=4*(FD OR DD)
1570 LET H$=H$+"LD "+Q$+","+H$(8+W TO 9+W)
1580 RETURN
1590 LET W=4*C+1
1600 LET H$=H$+F$(W TO W+3)
1610 RETURN
1620 REM DIRECT ARITHMETIC
1630 IF T <>6 THEN GO TO 1710
1640 LET I=3*C+1
1650 LET I$=" "
1660 IF I<7 OR I=10 THEN LET I$=" A,"
1670 IF I>18 THEN LET I$=""
1680 LET H$=H$+O$(I TO I+2)+I$
1690 LET H$=H$+H$(8 TO 9)
1700 RETURN
1710 REM 4TH GROUP
1720 IF X0=237 THEN GO TO 2760
1730 IF T=1 AND NOT (C- INT (C/2)*2) THEN GO TO 1990
1740 IF T=7 THEN GO TO 2070
1750 IF T=5 AND NOT (C- INT (C/2)*2) THEN GO TO 2010
1760 LET FL=0
1770 IF X0=205 OR T=4 THEN GO SUB 1890
1780 IF X0=195 OR T=2 THEN GO SUB 1860
1790 IF X0=201 OR NOT T THEN GO SUB 1930
1800 IF NOT T OR T=2 OR T=4 THEN GO SUB 1950
1810 IF T=3 AND NOT FL THEN GO TO 2590
1820 IF T=1 AND NOT FL THEN GO TO 2670
1830 IF FL THEN GO SUB 1910
1840 RETURN
1850 REM MAIN CONDITIONALS
1860 LET H$=H$+"JP "
1870 LET FL=1
1880 RETURN
1890 LET H$=H$+"CALL "
1900 GO TO 1870
1910 LET H$=H$+H$(10 TO 11)+H$(8 TO 9)
1920 RETURN
1930 LET H$=H$+"RET "
1940 GO TO 1870
1950 LET H$=H$+E$(2*C+1 TO 2*(C+1))
1960 IF NOT (C=1 OR C=3 OR C>5) THEN LET H$=H$+" "
1970 RETURN
1980 REM GROUP 4/C EVEN,T=1 OR 5
1990 LET H$=H$+"POP "
2000 GO TO 2020
2010 LET H$=H$+"PUSH "
2020 IF C=6 THEN LET C=8
2030 IF C=4 THEN LET C=C+6*(FD OR DD)
2040 LET H$=H$+D$(1+C TO 2+C)
2050 RETURN
2060 REM GROUP 4/T=7
2070 LET W= INT (C/2)
2080 LET H$=H$+"RST "+ STR$ (W)+ STR$ (8*(C-2*W))
2090 RETURN
2100 REM GROUP 1/T=1
2110 IF C-2* INT (C/2) THEN GO TO 2140
2120 LET H$=H$+"LD "+D$(C+1 TO C+2)+","
2130 GO TO 1910
2140 LET H$=H$+"ADD HL,"+D$(C TO C+1)
2150 RETURN
2160 REM GROUP 1/T=0 (JRS)
2170 IF C=0 THEN LET H$=H$+"NOP"
2180 IF C=1 THEN LET H$=H$+"EX AF,AF"""
2190 IF C=2 THEN LET H$=H$+"DJNZ"
2200 IF C>1 THEN GO TO 2220
2210 RETURN
2220 IF C>2 THEN LET H$=H$+"JR "
2230 LET W=C*2-7
2240 IF C>3 THEN LET H$=H$+E$(W TO W+1)
2250 IF NOT (C-2* INT (C/2)) THEN LET H$=H$+" "
2260 LET W=(PEEK (J-1) AND (PEEK (J-1)<128))+((PEEK (J-1)-256) AND (PEEK (J-1)>127))
2270 LET J0=J
2280 LET J=J+W
2290 GO SUB 320
2300 LET J=J0
2310 RETURN
2320 REM GROUP 1/T=3
2330 LET Q$="INC "
2340 IF C-2* INT (C/2) THEN LET Q$="DEC "
2350 LET W=2* INT (C/2)+1
2360 LET H$=H$+Q$+D$(W TO W+1)
2370 RETURN
2380 REM GROUP 1/T=4 OR 5
2390 LET Q$=R$(C+1)
2400 GO SUB 1190
2410 LET P$="INC "
2420 IF T-2* INT (T/2) THEN LET P$="DEC "
2430 LET H$=H$+P$+Q$
2440 RETURN
2450 REM GROUP 1/T=2
2460 LET H$=H$+"LD "
2470 IF NOT (C-2* INT (C/2)) THEN GO TO 2540
2480 IF C=5 THEN GO SUB 1100
2490 IF C=5 THEN GO TO 2510
2500 LET H$=H$+"A"
2510 IF C>3 THEN LET H$=H$+",("+H$(10 TO 11)+H$(8 TO 9)+")"
2520 IF C<4 THEN LET H$=H$+",("+D$(C TO C+1)+")"
2530 RETURN
2540 IF C>4 THEN LET H$=H$+"("+D$(C+1 TO C+2)+"),"
2550 IF C>3 THEN LET H$=H$+"("+H$(10 TO 11)+H$(8 TO 9)+"),"
2560 IF C=4 THEN GO TO 1100
2570 LET H$=H$+"A"
2580 RETURN
2590 REM GROUP 4/T=3
2600 IF C=2 THEN LET H$=H$+"OUT"+H$(8 TO 9)+",A"
2610 IF C=3 THEN LET H$=H$+"IN A,"+H$(8 TO 9)
2620 IF C=4 THEN LET H$=H$+"EX(SP),HL"
2630 IF C=5 THEN LET H$=H$+"EX DE,HL"
2640 IF C=6 THEN LET H$=H$+"DI"
2650 IF C=7 THEN LET H$=H$+"EI"
2660 RETURN
2670 REM GROUP 4/T=1
2680 IF C=3 THEN LET H$=H$+"EXX"
2690 IF C=5 THEN LET H$=H$+"JP("
2700 IF C=7 THEN LET H$=H$+"LD SP,"
2710 IF C<5 THEN RETURN
2720 GO SUB 1100
2730 IF C=7 THEN RETURN
2740 LET H$=H$+")"
2750 RETURN
2760 REM ED PREFIXES TO 3610
2770 IF L=1 THEN GO TO 3520
2780 LET X0= PEEK (J-L+1)
2790 GO SUB 190
2800 IF X0<160 OR X0>187 THEN GO TO 2910
2810 IF T>3 THEN RETURN
2820 IF T=3 AND C<6 THEN LET Q$="OUT"
2830 IF T=3 AND C<6 THEN GO TO 2860
2840 LET W=2*T+1
2850 LET Q$=G$(W TO W+1)
2860 IF (C-2* INT (C/2)) THEN LET Q$=Q$+"D"
2870 IF NOT (C-2* INT (C/2)) THEN LET Q$=Q$+"I"
2880 IF C>5 THEN LET Q$=Q$+"R"
2890 LET H$=H$+Q$
2900 RETURN
2910 IF X0<64 OR X0>123 THEN RETURN
2920 IF T>1 THEN GO TO 2970
2930 LET Q$=R$(C+1)
2940 IF T THEN LET H$=H$+"OUT(C),"+Q$
2950 IF NOT T THEN LET H$=H$+"IN "+Q$+",(C)"
2960 RETURN
2970 LET W= INT (C/2)
2980 IF T <>2 THEN GO TO 3020
2990 IF C-2*W THEN LET H$=H$+"ADC HL,"+D$(2*W+1 TO 2*W+2)
3000 IF NOT (C-2*W) THEN LET H$=H$+"SBC HL,"+D$(2*W+1 TO 2*W+2)
3010 RETURN
3020 IF T <>3 THEN GO TO 3180
3030 IF C-2*W THEN GO TO 3060
3040 LET H$=H$+"LD("+H$(12 TO 13)+H$(10 TO 11)+"),"+D$(2*W+1 TO 2*W+2)
3050 RETURN
3060 LET H$=H$+"LD "+D$(2*W+1 TO 2*W+2)+",("+H$(12 TO 13)+H$(10 TO 11)+")"
3070 RETURN
3080 IF T <>7 THEN GO TO 3180
3090 IF C>3 THEN GO TO 3150
3100 IF C=2*W THEN LET Q$="I"
3110 IF C <>2*W THEN LET Q$="R"
3120 IF C>1 THEN LET H$=H$+"LD A,"+Q$
3130 IF C<2 THEN LET H$=H$+"LD "+Q$+",A"
3140 RETURN
3150 IF C=4 THEN LET H$=H$+"RRD"
3160 IF C=5 THEN LET H$=H$+"RLD"
3170 RETURN
3180 IF T=6 AND C<4 AND C <>1 THEN LET H$=H$+"IM "+ STR$ ((C>0)*(C-1))
3190 IF X0=68 THEN LET H$=H$+"NEG"
3200 IF X0=69 THEN LET H$=H$+"RETN"
3210 IF X0=77 THEN LET H$=H$+"RETI"
3220 RETURN
3230 REM BIT OPS
3240 LET CB=1
3250 LET X0= PEEK (J-1)
3260 LET W= INT (X0/64)
3270 LET X0=X0-W*64
3280 IF W THEN GO TO 3410
3290 LET B1=1+ INT (X0/32)
3300 LET B2= INT (X0/16)
3310 LET X0=X0-B2*16
3320 LET B3= INT (X0/8)+3
3330 LET B2=B2+5
3340 LET X0=1+X0-(B3-3)*8
3350 LET Q$=R$(X0)
3360 GO SUB 1190
3370 LET H$=H$+C$(B1)+C$(B3)+C$(B2)
3380 IF B2 <>6 THEN LET H$=H$+" "
3390 LET H$=H$+Q$
3400 RETURN
3410 LET W=3*W-2
3420 LET D= INT (X0/8)
3430 LET X0=X0-D*8+1
3440 LET Q$=R$(X0)
3450 GO SUB 1190
3460 LET H$=H$+B$(W TO W+2)+" "+ STR$ (D)+","+Q$
3470 RETURN
3480 REM FD,DD PREFIXES
3490 LET DD=X0=221
3500 LET FD=X0=253
3510 IF L>1 THEN GO TO 3540
3520 LET H$=H$+"DATA"
3530 RETURN
3540 LET X0= PEEK (J-L+1)
3550 GO TO 990
3560 REM MAIN LOOP
3570 INPUT "START LINE NUMBER ";J
3580 LET H$=""
3590 LET P$=H$
3600 LET Q$=H$
3610 LET DD=0
3620 LET FD=DD
3630 LET CB=DD
3640 GO SUB 320
3650 GO SUB 270
3660 GO SUB 390
3670 LET H$=H$( TO LEN H$-2)
3680 LET X0= PEEK J
3690 FOR K=1 TO L
3700 GO SUB 270
3710 LET J=J+1
3720 NEXT K
3730 GO SUB 960
3740 PRINT H$
3750 POKE 23692,255
3760 GO TO 3580
3770 STOP
3780 SAVE "DISA Z" LINE 10Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
