DISA-Z is a Z80 disassembler written in BASIC that reads machine code from memory and prints human-readable mnemonics to a printer. It supports the full Z80 instruction set including all prefix bytes: CB (bit operations), DD (IX-indexed), FD (IY-indexed), and ED (extended instructions), as well as relative jump target resolution. A configuration flag at line 190 (`TS2=0` or `TS2=1`) switches hex digit output between the ZX81/TS1000 and TS2068 character sets, since the two platforms place A–F at different character code offsets. The program uses octal decomposition of opcodes (breaking each byte into two 3-bit fields and one further subdivision) to classify and decode instructions, a technique that mirrors the Z80’s own internal opcode structure. Output is sent via LPRINT and includes the address, raw hex bytes, and mnemonic for each instruction.
Program Analysis
Program Structure
The program is organised as a collection of subroutines branched to from a main loop starting at line 3500. After an INPUT J to set the start address, the loop at lines 3520–3700 repeatedly disassembles one instruction, prints it, and advances. The top-level flow per instruction is:
- Clear state variables (
DD,FD,CB) and the hex stringH$(lines 3520–3580). - Format the address via
GOSUB 350(address hexer). - Determine instruction length via
GOSUB 420(lines 430–960). - Peek and hex-format each byte of the instruction (lines 3630–3660).
- Generate the mnemonic string via
GOSUB 970(lines 970–3410). - Send the completed line to the printer with
LPRINT H$.
Platform Switch
Line 190 sets TS2=0 for ZX81/TS1000 or TS2=1 for TS2068. The constant NCOD (line 200) evaluates to either 28 (ZX81, where digit characters start at code 28) or 48 (TS2068, standard ASCII). The hex formatter at line 330 uses NCOD+Y+7*(Y>9)*TS2 to select the correct character code for A–F, compensating for the gap between digit and letter character codes on each platform.
Opcode Decoding via Octal Decomposition
The subroutine at line 230 decomposes an opcode byte into octal fields. It finds K (the top 2 bits, 0–3), then extracts C (bits 5–3, the middle octet) and T (bits 2–0, the low triad). This directly mirrors the Z80 designers’ own encoding scheme, where the three octal digits of an opcode determine its instruction group, register operand, and operation type respectively.
Instruction Length Calculation
Before disassembling mnemonics, the program must know how many bytes the instruction occupies so it can fetch operand bytes. The subroutine at line 420 handles prefix chaining: DD (0xDD) and FD (0xFD) prefixes call a helper (line 480) that peeks the next byte without permanently advancing the pointer, and recursively re-evaluates. The length is built up in variable L, with separate paths for:
- CB-prefixed bit instructions (line 550 → line 940)
- ED-prefixed extended instructions (lines 560–700)
- Unprefixed main-table instructions (lines 710–910)
The helper at line 920 handles two-byte operands (adding 2 to L) and line 940 adds 1 for single-byte operands; line 950 always adds 1 for the opcode itself.
Hex Formatting
The byte hexer (lines 300–340) peeks address J and appends two hex characters to H$. The address hexer (lines 360–410) calls the byte hexer twice for high and low bytes of a 16-bit address and appends a space. Operand bytes embedded in H$ at fixed character positions are later extracted by substring references such as H$(8 TO 9) (low byte) and H$(10 TO 11) (high byte), allowing the mnemonic builders to reuse already-formatted hex data without re-peeking memory.
Mnemonic Generation
The mnemonic subroutine (line 970) pads H$ to at least 14 characters, then dispatches by opcode group. Lookup tables stored as compact strings are used throughout:
| Variable | Contents | Usage |
|---|---|---|
O$ | ADDADCSUBSBCANDXOROR CP | ALU operation names (3 chars each) |
D$ | BCDEHLSPAFIXIY | 16-bit register pair names (2 chars each) |
R$ | BCDEHLXA | 8-bit register names (1 char each) |
E$ | NZZ NCC POPEP M | Condition code names (2 chars each) |
F$ | RLCARRCARLA RRA DAA CPL SCF CCF | Misc. first-group mnemonics (4 chars each) |
B$ | BITRESSET | CB-group bit instruction names (3 chars each) |
C$ | RSLRC AL | CB shift/rotate sub-field characters |
G$ | LDCPINOT | ED block instruction base names (2 chars each) |
Index Register Substitution
The subroutine at line 1170 checks the DD and FD flags. When neither is set, the “H” register slot (R$ position 6, labelled "X" internally) is replaced by (HL). When a prefix is active, the helper at line 1210 builds an (IX+d) or (IY+d) string by reading the displacement byte already formatted in H$(10 TO 11), neatly avoiding a second PEEK call.
Relative Jump Resolution
Lines 2180–2220 resolve JR and DJNZ target addresses. The displacement byte is read with PEEK (J-1) and sign-extended using the idiom (byte AND (byte<128)) + ((byte-256) AND (byte>127)), which converts an unsigned 0–255 value to a signed −128 to +127 integer without any bit-manipulation keywords. The absolute target address is then formatted as hex via the address hexer.
RST Instruction Formatting
Lines 1990–2010 compute the RST vector from octet field C: W=INT(C/2) gives the upper digit and 8*(C-2*W) the lower, producing values like RST 38 rather than the more conventional RST 38H. The result is decimal, not hex — a cosmetic anomaly compared to standard Z80 assembler notation.
ED-Prefix Block Instructions
Lines 2740–2840 handle the LDI, LDD, LDIR, LDDR, CPI, CPD, CPIR, CPDR, INI, IND, INIR, INDR, OUTI, OUTD, OTIR, and OTDR instructions by building suffixes: the I/D direction from the even/odd column, and the optional R repeat suffix from whether C>5.
Anomalies and Notes
- Line 2510 has no handler for
C=0(opcode 0xC3 is caught earlier at line 830) orC=1(handled by theT=1path). The label at 2520 is missing from the listing — line 2520 does not appear, so there is a gap between 2510 and 2530 that skips no code but may confuse a reader. - Line 2100 outputs
EX AF,AF"with a literal trailing double-quote character embedded in the string to represent the conventionalAF'prime notation. - The
OUT (C),0undocumented ED instruction is not specifically handled; theC=7column of the ED 0x70/0x78 row falls through silently. - The variable
CBis set at line 3180 but never tested elsewhere in the listing — it appears to be a flag reserved for future use or a remnant from development. - The main loop at line 3700 uses
GOTO 3520(not3510), so it does not re-prompt for an address; the disassembler runs continuously from the initial address without stopping.
Content
Source Code
10 REM *********************** DISA-Z
20 REM *********************** A Z80 DISASSEMBLER FOR TS COMPUTERS
30 REM ***********************
40 REM *********************** COPYRIGHT, 1983 RAY KINGSLEY
50 REM *********************** RUN AND ENTER START ADDRESS IN DECIMAL
60 REM ***********************
70 REM ***********************
90 REM DATA STRINGS
100 LET O$="ADDADCSUBSBCANDXOROR CP "
110 LET D$="BCDEHLSPAFIXIY"
120 LET B$="BITRESSET"
130 LET C$="RSLRC AL"
140 LET R$="BCDEHLXA"
150 LET E$="NZZ NCC POPEP M "
160 LET F$="RLCARRCARLA RRA DAA CPL SCF CCF "
170 LET G$="LDCPINOT"
180 REM FOR TS2068 SET TS2=1
190 LET TS2=0
200 LET NCOD=28+20*TS2
220 GOTO 3500
230 REM OCTAL DIGITS
240 FOR K=0 TO 3
250 IF X0>=K*64 AND X0<(K+1)*64 THEN LET X=X0-K*64
260 NEXT K
270 LET C=INT (X/8)
280 LET T=X-C*8
290 RETURN
295 REM BYTE HEXER
300 LET X=PEEK J
310 LET Y=INT (X/16)
320 LET Z=X-Y*16
330 LET H$=H$+CHR$ (NCOD+Y+7*(Y>9)*TS2)+CHR$ (NCOD+Z+7*(Z>9)*TS2)
340 RETURN
350 REM ADDRESS HEXER
360 LET X=INT (J/256)
370 GOSUB 310
380 LET X=J-256*X
390 GOSUB 310
400 LET H$=H$+" "
410 RETURN
420 REM GET INSTRUCTION LENGTH
430 LET L=0
440 IF X<>221 AND X<>253 THEN GOTO 540
450 IF L THEN RETURN
460 GOSUB 480
470 GOTO 440
480 LET L=1
490 LET J=J+1
500 GOSUB 300
510 LET J=J-1
520 LET H$=H$( TO LEN H$-2)
530 RETURN
535 REM I-LENGTH FOR PREFIXES
540 IF L THEN LET L=L+((X>51) AND (X<55) OR X=203)
550 IF X=203 THEN GOTO 940
560 IF X<>237 THEN GOTO 710
570 GOSUB 480
580 IF X<64 OR X>188 THEN RETURN
590 IF X<160 AND X>123 THEN RETURN
600 IF X<124 THEN GOTO 630
610 IF X-INT (X/8)*8<5 THEN GOTO 950
620 RETURN
630 IF X=78 OR X=102 OR X=110 OR X=112 OR X=113 OR X=118 OR X=119 THEN RETURN
640 LET W=X-67
650 IF NOT (W-INT (W/8)*8) THEN GOTO 920
660 LET W=X-76
670 IF W>=0 AND NOT (W-INT (W/8)*8) THEN RETURN
680 LET W=X-85
690 IF W>=0 AND NOT (W-INT (W/8)*8) THEN RETURN
700 GOTO 950
705 REM WITHOUT PREFIXES
710 IF X<64 OR X>191 THEN GOTO 740
720 IF L THEN LET L=L+(X-8*INT (X/8)=6)
730 GOTO 950
740 IF X>191 THEN GOTO 830
750 IF Z=1 THEN GOTO 920
760 LET W=X-34
770 IF W>=0 AND NOT (W-INT (W/8)*8) THEN GOTO 920
780 LET W=X-6
790 IF W>=0 AND NOT (W-INT (W/8)*8) THEN GOTO 940
800 LET W=X-16
810 IF W>=0 AND NOT (W-INT (W/8)*8) THEN GOTO 940
820 GOTO 950
830 IF X=195 OR X=205 THEN GOTO 920
840 IF X=211 OR X=219 THEN GOTO 940
850 LET W=X-194
860 IF NOT (W-INT (W/8)*8) THEN GOTO 920
870 LET W=W-2
880 IF NOT (W-INT (W/8)*8) THEN GOTO 920
890 LET W=W-2
900 IF NOT (W-INT (W/8)*8) THEN GOTO 940
910 GOTO 950
920 IF L=2 THEN GOTO 940
930 LET L=L+1
940 LET L=L+1
950 LET L=L+1
960 RETURN
970 REM MNEMONICS
980 LET H$=H$+" "
990 IF LEN H$<14 THEN GOTO 980
1000 IF X0=118 THEN GOTO 1290
1010 GOSUB 230
1020 IF X0<64 OR X0>191 THEN GOTO 1400
1030 IF X0>127 THEN GOTO 1310
1035 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 GOSUB 1170
1070 LET H$=H$+"LD "+P$+","+Q$
1080 RETURN
1085 REM ADD POINTER REGS TO H$
1090 IF NOT DD THEN GOTO 1120
1100 LET H$=H$+"IX"
1110 RETURN
1120 IF NOT FD THEN GOTO 1150
1130 LET H$=H$+"IY"
1140 RETURN
1150 LET H$=H$+"HL"
1160 RETURN
1165 REM CHECK FOR FD OR DD
1170 IF FD OR DD THEN GOTO 1210
1180 IF P$="X" THEN LET P$="(HL)"
1190 IF Q$="X" THEN LET Q$="(HL)"
1200 RETURN
1205 REM ADD INDEX REGS TO H$
1210 IF FD THEN LET W$="(IY+"
1220 IF DD THEN LET W$="(IX+"
1230 IF P$="X" THEN LET P$=W$+H$(10 TO 11)+")"
1240 IF Q$="X" THEN LET Q$=W$+H$(10 TO 11)+")"
1250 RETURN
1260 GOSUB 1170
1270 LET H$=H$+P$+","+R$(1+T)
1280 RETURN
1290 LET H$=H$+"HLT"
1300 RETURN
1310 REM ARITH/LOGIC OPS
1320 LET I=C*3+1
1330 LET Q$=R$(T+1)
1340 GOSUB 1170
1350 LET I$=" "
1360 IF I<7 OR I=10 THEN LET I$=" A,"
1370 IF I>18 THEN LET I$=""
1380 LET H$=H$+O$(I TO I+2)+I$+Q$
1390 RETURN
1395 REM 1ST AND 4TH GROUPS
1400 IF X0=203 THEN GOTO 3170
1410 IF X0=253 OR X0=221 THEN GOTO 3420
1420 IF X0>191 THEN GOTO 1570
1425 REM 1ST GROUP
1430 IF T=1 THEN GOTO 2020
1440 IF T=3 THEN GOTO 2240
1450 IF T=4 OR T=5 THEN GOTO 2300
1460 IF T=2 THEN GOTO 2370
1470 IF NOT T THEN GOTO 2080
1480 IF T<>6 THEN GOTO 1540
1490 LET Q$=R$(C+1)
1500 GOSUB 1170
1510 LET W=4*(FD OR DD)
1520 LET H$=H$+"LD "+Q$+","+H$(8+W TO 9+W)
1530 RETURN
1540 LET W=4*C+1
1550 LET H$=H$+F$(W TO W+3)
1560 RETURN
1570 REM DIRECT ARITHMETIC
1580 IF T<>6 THEN GOTO 1660
1590 LET I=3*C+1
1600 LET I$=" "
1610 IF I<7 OR I=10 THEN LET I$=" A,"
1620 IF I>18 THEN LET I$=""
1630 LET H$=H$+O$(I TO I+2)+I$
1640 LET H$=H$+H$(8 TO 9)
1650 RETURN
1660 REM 4TH GROUP
1670 IF X0=237 THEN GOTO 2700
1680 IF T=1 AND NOT (C-INT (C/2)*2) THEN GOTO 1920
1690 IF T=7 THEN GOTO 1990
1700 IF T=5 AND NOT (C-INT (C/2)*2) THEN GOTO 1940
1710 LET FL=0
1720 IF X0=205 OR T=4 THEN GOSUB 1830
1730 IF X0=195 OR T=2 THEN GOSUB 1800
1740 IF X0=201 OR NOT T THEN GOSUB 1870
1750 IF NOT T OR T=2 OR T=4 THEN GOSUB 1890
1760 IF T=3 AND NOT FL THEN GOTO 2510
1770 IF T=1 AND NOT FL THEN GOTO 2610
1780 IF FL THEN GOSUB 1850
1790 RETURN
1795 REM MAIN CONDITIONALS
1800 LET H$=H$+"JP "
1810 LET FL=1
1820 RETURN
1830 LET H$=H$+"CALL "
1840 GOTO 1810
1850 LET H$=H$+H$(10 TO 11)+H$(8 TO 9)
1860 RETURN
1870 LET H$=H$+"RET "
1880 GOTO 1810
1890 LET H$=H$+E$(2*C+1 TO 2*(C+1))
1900 IF NOT (C=1 OR C=3 OR C>5) THEN LET H$=H$+" "
1910 RETURN
1915 REM GROUP 4:C EVEN,T=1 OR 5
1920 LET H$=H$+"POP "
1930 GOTO 1950
1940 LET H$=H$+"PUSH "
1950 IF C=6 THEN LET C=8
1960 IF C=4 THEN LET C=C+6*(FD OR DD)
1970 LET H$=H$+D$(1+C TO 2+C)
1980 RETURN
1985 REM GROUP 4:T=7
1990 LET W=INT (C/2)
2000 LET H$=H$+"RST "+STR$ (W)+STR$ (8*(C-2*W))
2010 RETURN
2020 REM GROUP 1:T=1
2030 IF C-2*INT (C/2) THEN GOTO 2060
2040 LET H$=H$+"LD "+D$(C+1 TO C+2)+","
2050 GOTO 1850
2060 LET H$=H$+"ADD HL,"+D$(C TO C+1)
2070 RETURN
2080 REM GROUP 1:T=0 (JRS)
2090 IF C=0 THEN LET H$=H$+"NOP"
2100 IF C=1 THEN LET H$=H$+"EX AF,AF"""
2110 IF C=2 THEN LET H$=H$+"DJNZ"
2120 IF C>1 THEN GOTO 2140
2130 RETURN
2140 IF C>2 THEN LET H$=H$+"JR "
2150 LET W=C*2-7
2160 IF C>3 THEN LET H$=H$+E$(W TO W+1)
2170 IF NOT (C-2*INT (C/2)) THEN LET H$=H$+" "
2180 LET W=(PEEK (J-1) AND (PEEK (J-1)<128))+((PEEK (J-1)-256) AND (PEEK (J-1)>127))
2190 LET J0=J
2200 LET J=J+W
2210 GOSUB 350
2220 LET J=J0
2230 RETURN
2240 REM GROUP 1:T=3
2250 LET Q$="INC "
2260 IF C-2*INT (C/2) THEN LET Q$="DEC "
2270 LET W=2*INT (C/2)+1
2280 LET H$=H$+Q$+D$(W TO W+1)
2290 RETURN
2300 REM GROUP 1:T=4 OR 5
2310 LET Q$=R$(C+1)
2320 GOSUB 1170
2330 LET P$="INC "
2340 IF T-2*INT (T/2) THEN LET P$="DEC "
2350 LET H$=H$+P$+Q$
2360 RETURN
2370 REM GROUP 1:T=2
2380 LET H$=H$+"LD "
2390 IF NOT (C-2*INT (C/2)) THEN GOTO 2460
2400 IF C=5 THEN GOSUB 1090
2410 IF C=5 THEN GOTO 2430
2420 LET H$=H$+"A"
2430 IF C>3 THEN LET H$=H$+",("+H$(10 TO 11)+H$(8 TO 9)+")"
2440 IF C<4 THEN LET H$=H$+",("+D$(C TO C+1)+")"
2450 RETURN
2460 IF C<4 THEN LET H$=H$+"("+D$(C+1 TO C+2)+"),"
2470 IF C>3 THEN LET H$=H$+"("+H$(10 TO 11)+H$(8 TO 9)+"),"
2480 IF C=4 THEN GOTO 1090
2490 LET H$=H$+"A"
2500 RETURN
2510 REM GROUP 4:T=3
2530 IF C=2 THEN LET H$=H$+"OUT "+H$(8 TO 9)+",A"
2540 IF C=3 THEN LET H$=H$+"IN A,"+H$(8 TO 9)
2550 IF C=4 THEN LET H$=H$+"EX(SP),HL"
2560 IF C=5 THEN LET H$=H$+"EX DE,HL"
2580 IF C=6 THEN LET H$=H$+"DI"
2590 IF C=7 THEN LET H$=H$+"EI"
2600 RETURN
2610 REM GROUP 4:T=1
2620 IF C=3 THEN LET H$=H$+"EXX"
2630 IF C=5 THEN LET H$=H$+"JP("
2640 IF C=7 THEN LET H$=H$+"LD SP,"
2650 IF C<5 THEN RETURN
2660 GOSUB 1090
2670 IF C=7 THEN RETURN
2680 LET H$=H$+")"
2690 RETURN
2700 REM ED PREFIXES TO 3610
2710 IF L=1 THEN GOTO 3460
2720 LET X0=PEEK (J-L+1)
2730 GOSUB 230
2740 IF X0<160 OR X0>187 THEN GOTO 2850
2750 IF T>3 THEN RETURN
2760 IF T=3 AND C<6 THEN LET Q$="OUT"
2770 IF T=3 AND C<6 THEN GOTO 2800
2780 LET W=2*T+1
2790 LET Q$=G$(W TO W+1)
2800 IF (C-2*INT (C/2)) THEN LET Q$=Q$+"D"
2810 IF NOT (C-2*INT (C/2)) THEN LET Q$=Q$+"I"
2820 IF C>5 THEN LET Q$=Q$+"R"
2830 LET H$=H$+Q$
2840 RETURN
2850 IF X0<64 OR X0>123 THEN RETURN
2860 IF T>1 THEN GOTO 2910
2870 LET Q$=R$(C+1)
2880 IF T THEN LET H$=H$+"OUT(C),"+Q$
2890 IF NOT T THEN LET H$=H$+"IN "+Q$+",(C)"
2900 RETURN
2910 LET W=INT (C/2)
2920 IF T<>2 THEN GOTO 2960
2930 IF C-2*W THEN LET H$=H$+"ADD HL,"+D$(2*W+1 TO 2*W+2)
2940 IF NOT (C-2*W) THEN LET H$=H$+"SBC HL,"+D$(2*W+1 TO 2*W+2)
2950 RETURN
2960 IF T<>3 THEN GOTO 3020
2970 IF C-2*W THEN GOTO 3000
2980 LET H$=H$+"LD("+H$(12 TO 13)+H$(10 TO 11)+"),"+D$(2*W+1 TO 2*W+2)
2990 RETURN
3000 LET H$=H$+"LD "+D$(2*W+1 TO 2*W+2)+",("+H$(12 TO 13)+H$(10 TO 11)+")"
3010 RETURN
3020 IF T<>7 THEN GOTO 3120
3030 IF C>3 THEN GOTO 3090
3040 IF C=2*W THEN LET Q$="I"
3050 IF C<>2*W THEN LET Q$="R"
3060 IF C>1 THEN LET H$=H$+"LD A,"+Q$
3070 IF C<2 THEN LET H$=H$+"LD "+Q$+",A"
3080 RETURN
3090 IF C=4 THEN LET H$=H$+"RRD"
3100 IF C=5 THEN LET H$=H$+"RLD"
3110 RETURN
3120 IF T=6 AND C<4 AND C<>1 THEN LET H$=H$+"IM "+STR$ ((C>0)*(C-1))
3130 IF X0=68 THEN LET H$=H$+"NEG"
3140 IF X0=69 THEN LET H$=H$+"RETN"
3150 IF X0=77 THEN LET H$=H$+"RETI"
3160 RETURN
3170 REM BIT OPS
3180 LET CB=1
3190 LET X0=PEEK (J-1)
3200 LET W=INT (X0/64)
3210 LET X0=X0-W*64
3220 IF W THEN GOTO 3350
3230 LET B1=1+INT (X0/32)
3240 LET B2=INT (X0/16)
3250 LET X0=X0-B2*16
3260 LET B3=INT (X0/8)+3
3270 LET B2=B2+5
3280 LET X0=1+X0-(B3-3)*8
3290 LET Q$=R$(X0)
3300 GOSUB 1170
3310 LET H$=H$+C$(B1)+C$(B3)+C$(B2)
3320 IF B2<>6 THEN LET H$=H$+" "
3330 LET H$=H$+Q$
3340 RETURN
3350 LET W=3*W-2
3360 LET D=INT (X0/8)
3370 LET X0=X0-D*8+1
3380 LET Q$=R$(X0)
3390 GOSUB 1170
3400 LET H$=H$+B$(W TO W+2)+" "+STR$ (D)+","+Q$
3410 RETURN
3420 REM FD,DD PREFIXES
3430 LET DD=X0=221
3440 LET FD=X0=253
3450 IF L>1 THEN GOTO 3480
3460 LET H$=H$+"DATA"
3470 RETURN
3480 LET X0=PEEK (J-L+1)
3490 GOTO 1000
3500 REM MAIN LOOP
3510 INPUT J
3520 LET H$=""
3530 LET P$=H$
3540 LET Q$=H$
3550 LET DD=0
3560 LET FD=DD
3570 LET CB=DD
3580 GOSUB 350
3590 GOSUB 300
3600 GOSUB 420
3610 LET H$=H$( TO LEN H$-2)
3620 LET X0=PEEK J
3630 FOR K=1 TO L
3640 GOSUB 300
3650 LET J=J+1
3660 NEXT K
3670 GOSUB 970
3680 LPRINT H$
3700 GOTO 3520
3710 SAVE "1008%3"
3720 LIST
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
