This program is a Z80 machine code disassembler written in BASIC that decodes opcode bytes at user-specified memory addresses and prints both to the screen and to a printer via LPRINT. It reads the byte at each address, looks it up against DATA statements listing multi-byte instruction prefixes (such as ED/&ED=237, CB/&CB=203), and dispatches to subroutine-like branches at lines 200, 300, 400, and 700 to handle one-byte, two-byte, and prefixed instructions. The program uses string arrays A$, B$, and C$ (presumably DIMmed and populated elsewhere, consistent with the GOTO 100 start warning) to hold mnemonic text indexed by opcode value. POKE 23692,255 at line 171 resets the BASIC scroll counter to prevent “scroll?” prompts interrupting long disassembly runs. Lines 5000–5040 form an independent memory dump utility that prints each byte’s numeric value and its CHR$ representation.
Program Analysis
Program Structure
The program is organised into several distinct functional blocks rather than true subroutines (no GOSUB/RETURN pairs are used; all branching is via GO TO):
- Initialisation guard (lines 5–21): A
REMheader and a deliberateSTOPprevent accidentalRUNfrom line 1, since the mnemonic string arrays must be set up before the disassembler loop begins. - Main decode loop (lines 100–180): Reads the byte at
ADDRESS, checks for special prefix opcodes, searches two DATA tables for multi-byte instructions, then advances the address pointer. - One-byte operand handler (lines 200–230): Handles opcodes with an immediate 8-bit operand, including sign-extension for relative jumps and offsets (line 210).
- ED-prefix handler (lines 305–390): Processes
ED-prefixed two-byte instructions using the B$ mnemonic array and a secondary DATA search. - Two-byte operand handler (lines 400–430): Reconstructs a 16-bit address or immediate from two successive bytes using
PEEK (ADDRESS+1)+256*PEEK (ADDRESS+2). - CB-prefix handler (line 700): Handles
CB-prefixed bit-manipulation instructions via the C$ array. - Standalone memory dumper (lines 5000–5040): An independent utility loop that prints each byte’s decimal value and its
CHR$equivalent, entirely separate from the disassembler.
Opcode Dispatch Mechanism
The main loop uses a three-level dispatch strategy:
- Zero byte: Line 115 catches opcode
0(NOP) as a special case, printing the mnemonic directly without an array lookup. - ED prefix (237): Detected at line 120, branching to line 300/305 for the extended instruction set.
- CB prefix (203): Detected at line 125, branching to line 700 for bit, rotate, and shift instructions.
- Two-byte operand opcodes: Line 130–150 searches a
DATAlist at line 2000 for opcodes requiring a 16-bit operand (e.g.LD rr,nn,JP cc,nn). - One-byte operand opcodes: Lines 160–165 search a secondary
DATAlist at line 4000 for opcodes requiring an 8-bit operand (e.g.LD r,n,ADD A,n). - All other opcodes: Fall through to line 170, treated as single-byte instructions looked up via A$.
Mnemonic Storage
Mnemonics are stored in string arrays A$, B$, and C$, indexed directly by opcode byte value (or byte+1 in the CB case at line 700). These arrays are not initialised within this listing — they must be populated by a companion program or setup block, which is why the header warns never to RUN the program and instead to GO TO 100 after setup.
Notable Techniques
- Scroll suppression:
POKE 23692,255at line 171 writes to the FRAMES/scroll counter system variable to prevent the “scroll?” prompt interrupting a long disassembly run — a standard BASIC idiom for continuous output. - Sign extension: Line 210 applies
ONEBIT=ONEBIT-256when the byte is ≥128 and the opcode is one of the relative-jump or indexed-offset instructions (opcodes 16, 24, 32, 40, 48, 56), correctly converting the unsigned byte to a signed 8-bit offset. - 16-bit reconstruction: Line 400 uses the standard little-endian formula
PEEK(ADDRESS+1)+256*PEEK(ADDRESS+2)to reconstruct a 16-bit word from two successive memory bytes. - Dual output: Every decoded line is sent to both the display (
PRINT) and the printer (LPRINT), making the tool useful for producing hardcopy disassembly listings. - DATA table searching:
RESTOREfollowed by aFOR/READloop is used instead of arrays for the opcode classification tables, conserving variable space at the cost of repeated sequential scanning.
DATA Tables
| Line | Purpose | Count | Sample values |
|---|---|---|---|
2000 | Opcodes with 16-bit operand (nn) | 34 entries (loop reads 26) | 1 (LD BC,nn), 17 (LD DE,nn), 195 (JP nn), 205 (CALL nn) |
3000 | ED-prefixed opcodes with 16-bit operand | 8 | 67 (LD (nn),BC), 75, 83, 91… |
4000 | Opcodes with 8-bit operand (n) | 24 | 6 (LD B,n), 14 (LD C,n), 198 (ADD A,n), 254 (CP n) |
Bugs and Anomalies
- Overlong DATA at line 2000: The
FORloop at line 130 reads only 26 items, but theDATAstatement at line 2000 contains 34 values. The extra 8 values (67, 75, 83, 91, 99, 107, 115, 123) are never read by the 26-iteration loop and appear to be the ED-prefix 16-bit operand opcodes accidentally duplicated from line 3000. - Missing line 300: The main loop at line 120 branches to
GO TO 300for the ED prefix, but line 300 does not exist in the listing. Execution would fall through to line 305, which is the intended target — this works correctly in practice because BASIC finds the next available line. - Variable shadowing: The variable
PEEKat line 110 storesPEEK ADDRESSinto a numeric variable also namedPEEK, shadowing the built-in function name. Subsequent use ofPEEK ADDRESSin lines 120–125 calls the function, whileIF PEEK=0andIF PEEK=237reference the variable — this is consistent but potentially confusing. - CB operand index off-by-one: Line 700 indexes C$ with
PEEK ADDRESS+1rather thanPEEK ADDRESS. This appears intentional to convert the 0-based opcode to a 1-based array index, but assumes C$ is dimensioned and loaded accordingly.
Content
Source Code
5 REM "DISASSYMBL" © by Ben H. Jackson, 1985; ALL RIGHTS RESERVED
10 REM SAVE "DISASSYMBL" LINE 1
20 PRINT "Never RUN this program ! Always GOTO 100 to RUN or re-start"
21 STOP
100 CLS : INPUT "ENTER BEGINNING ADDRESS ";ADDRESS
110 LET PEEK=PEEK ADDRESS
115 IF PEEK=0 THEN PRINT ADDRESS;" 0","NOP": LPRINT ADDRESS;" 0","NOP": GO TO 170
120 PRINT ADDRESS;" ";PEEK ADDRESS,A$(PEEK): LPRINT ADDRESS;" ";PEEK ADDRESS,A$(PEEK): IF PEEK=237 THEN GO TO 300
125 IF PEEK=203 THEN GO TO 700
130 RESTORE 2000: FOR I=1 TO 26
140 READ TWOBIT: IF PEEK=TWOBIT THEN GO TO 400
150 NEXT I
160 RESTORE 4000: FOR I=1 TO 24: READ OB: IF PEEK=OB THEN GO TO 200
165 NEXT I
170 LET ADDRESS=ADDRESS+1
171 POKE 23692,255
180 GO TO 110
200 LET BIT=PEEK ADDRESS: LET ADDRESS=ADDRESS+1: LET ONEBIT=PEEK ADDRESS
210 IF (BIT=16 OR BIT=24 OR BIT=32 OR BIT=40 OR BIT=48 OR BIT=56) AND ONEBIT>=128 THEN LET ONEBIT=ONEBIT-256
220 PRINT ADDRESS;" ";PEEK ADDRESS,ONEBIT: LPRINT ADDRESS;" ";PEEK ADDRESS,ONEBIT
230 GO TO 170
305 LET ADDRESS=ADDRESS+1: PRINT ADDRESS;" ";PEEK ADDRESS,B$(PEEK ADDRESS): LPRINT ADDRESS;" ";PEEK ADDRESS,B$(PEEK ADDRESS)
310 RESTORE 3000
320 FOR I=1 TO 8
330 READ TWOBIT: IF PEEK ADDRESS=TWOBIT THEN GO TO 400
340 NEXT I
390 GO TO 170
400 LET NUMBER=PEEK (ADDRESS+1)+256*PEEK (ADDRESS+2)
410 LET ADDRESS=ADDRESS+1: PRINT ADDRESS;" ";PEEK ADDRESS,NUMBER: LPRINT ADDRESS;" ";PEEK ADDRESS,NUMBER: LET ADDRESS=ADDRESS+1
420 PRINT ADDRESS;" ";PEEK ADDRESS: LPRINT ADDRESS;" ";PEEK ADDRESS
430 GO TO 170
700 LET ADDRESS=ADDRESS+1: PRINT ADDRESS,C$(PEEK ADDRESS+1): LPRINT ADDRESS,C$(PEEK ADDRESS+1)
710 GO TO 170
2000 DATA 1,17,33,34,42,49,50,58,194,195,196,202,204,205,210,212,218,220,226,228,234,236,242,244,250,252,67,75,83,91,99,107,115,123
3000 DATA 67,75,83,91,99,107,115,123
4000 DATA 6,14,16,22,24,30,32,38,40,46,48,54,56,62,198,206,211,214,219,222,230,238,246,254
5000 INPUT "ADDRESS? ";ADDRESS
5010 PRINT ADDRESS;" ";PEEK ADDRESS,CHR$ PEEK ADDRESS
5020 LET ADDRESS=ADDRESS+1
5040 GO TO 5010
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
